Mercurial > vim
changeset 2230:290ee42cae85 vim73
Remove old and unused method to allocate memory for undo.
author | Bram Moolenaar <bram@vim.org> |
---|---|
date | Sat, 29 May 2010 15:40:47 +0200 |
parents | d45902a5c61c |
children | aa6412cab544 |
files | runtime/doc/todo.txt src/undo.c |
diffstat | 2 files changed, 49 insertions(+), 459 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1089,18 +1089,11 @@ restored. (Luc St-Louis) Vim 7.3: -- Win32 DOS and Win32 console version: test69 fails. - using NSIS 2.46: install on Windows 7 works, but no "Edit with Vim" menu. Use register_shell_extension()? (George Reilly, 2010 May 26) Ron's version: http://dev.ronware.org/p/vim/finfo?name=gvim.nsi - Persistent undo bugs / fixes: - Memory leak reproduced by Dominique Pelle, 2010 May 28. - - Compiling without FEAT_SPELL fails, functions used in undo code: - get2c, get4c, put_bytes (Ralf Schandl) - - binary distributed: ":wundo" always fails. - - Patch not to allocate extra byte in U_ALLOC_LINE() (Dominique Pelle, - 2010 May 25) - - Remove the old code when U_USE_MALLOC is not defined? - When there is no undo info (undolevels negative), delete the undo file. - Need to check all values for evil manipulation. - Add undofile(name): get undo file name for buffer "name".
--- a/src/undo.c +++ b/src/undo.c @@ -72,8 +72,7 @@ * etc. etc. * * - * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the - * buffer is unloaded. + * All data is allocated and will all be freed when the buffer is unloaded. */ /* Uncomment the next line for including the u_check() function. This warns @@ -88,9 +87,6 @@ #include "vim.h" -/* See below: use malloc()/free() for memory management. */ -#define U_USE_MALLOC 1 - static void u_unch_branch __ARGS((u_header_T *uhp)); static u_entry_T *u_get_headentry __ARGS((void)); static void u_getbot __ARGS((void)); @@ -113,15 +109,7 @@ static void serialize_pos __ARGS((pos_T static void serialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp)); #endif -#ifdef U_USE_MALLOC -# define U_FREE_LINE(ptr) vim_free(ptr) -# define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE) -#else -static void u_free_line __ARGS((char_u *ptr, int keep)); -static char_u *u_alloc_line __ARGS((unsigned size)); -# define U_FREE_LINE(ptr) u_free_line((ptr), FALSE) -# define U_ALLOC_LINE(size) u_alloc_line(size) -#endif +#define U_ALLOC_LINE(size) lalloc((long_u)(size), FALSE) static char_u *u_save_line __ARGS((linenr_T)); static long u_newcount, u_oldcount; @@ -404,7 +392,7 @@ u_savecommon(top, bot, newbot) * Make a new header entry. Do this first so that we don't mess * up the undo info when out of memory. */ - uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T)); + uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T)); if (uhp == NULL) goto nomem; #ifdef U_DEBUG @@ -597,7 +585,7 @@ u_savecommon(top, bot, newbot) /* * add lines in front of entry list */ - uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T)); + uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T)); if (uep == NULL) goto nomem; vim_memset(uep, 0, sizeof(u_entry_T)); @@ -624,7 +612,7 @@ u_savecommon(top, bot, newbot) if (size > 0) { if ((uep->ue_array = (char_u **)U_ALLOC_LINE( - (unsigned)(sizeof(char_u *) * size))) == NULL) + sizeof(char_u *) * size)) == NULL) { u_freeentry(uep, 0L); goto nomem; @@ -903,7 +891,7 @@ u_read_undo(name, hash) goto error; else if (str_len > 0) { - if ((line_ptr = U_ALLOC_LINE(str_len)) == NULL) + if ((line_ptr = U_ALLOC_LINE(str_len + 1)) == NULL) goto error; for (i = 0; i < str_len; i++) line_ptr[i] = (char_u)getc(fp); @@ -921,16 +909,18 @@ u_read_undo(name, hash) seq_cur = get4c(fp); seq_time = get8ctime(fp); - if (num_head < 0) - num_head = 0; - /* uhp_table will store the freshly created undo headers we allocate * until we insert them into curbuf. The table remains sorted by the - * sequence numbers of the headers. */ - uhp_table = (u_header_T **)U_ALLOC_LINE(num_head * sizeof(u_header_T *)); - if (uhp_table == NULL) - goto error; - vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *)); + * sequence numbers of the headers. + * When there are no headers uhp_table is NULL. */ + if (num_head > 0) + { + uhp_table = (u_header_T **)U_ALLOC_LINE( + num_head * sizeof(u_header_T *)); + if (uhp_table == NULL) + goto error; + vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *)); + } c = get2c(fp); while (c == UF_HEADER_MAGIC) @@ -942,7 +932,7 @@ u_read_undo(name, hash) goto error; } - uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T)); + uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T)); if (uhp == NULL) goto error; vim_memset(uhp, 0, sizeof(u_header_T)); @@ -958,7 +948,7 @@ u_read_undo(name, hash) { EMSG2(_("E825: Undo file corruption: invalid uh_seq.: %s"), file_name); - U_FREE_LINE(uhp); + vim_free(uhp); goto error; } uhp->uh_walk = 0; @@ -987,7 +977,7 @@ u_read_undo(name, hash) last_uep = NULL; while ((uep_len = get4c(fp)) != -1) { - uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T)); + uep = (u_entry_T *)U_ALLOC_LINE(sizeof(u_entry_T)); if (uep == NULL) { u_free_uhp(uhp); @@ -1005,22 +995,26 @@ u_read_undo(name, hash) uep->ue_lcount = get4c(fp); uep->ue_size = get4c(fp); uep->ue_next = NULL; - array = (char_u **)U_ALLOC_LINE( - (unsigned)(sizeof(char_u *) * uep->ue_size)); - if (array == NULL) + if (uep->ue_size > 0) { - u_free_uhp(uhp); - goto error; + array = (char_u **)U_ALLOC_LINE( + sizeof(char_u *) * uep->ue_size); + if (array == NULL) + { + u_free_uhp(uhp); + goto error; + } + vim_memset(array, 0, sizeof(char_u *) * uep->ue_size); } - vim_memset(array, 0, sizeof(char_u *) * uep->ue_size); uep->ue_array = array; for (i = 0; i < uep->ue_size; i++) { line_len = get4c(fp); - /* U_ALLOC_LINE provides an extra byte for the NUL terminator.*/ - line = (char_u *)U_ALLOC_LINE( - (unsigned)(sizeof(char_u) * line_len)); + if (line_len >= 0) + line = (char_u *)U_ALLOC_LINE(line_len + 1); + else + line = NULL; if (line == NULL) { u_free_uhp(uhp); @@ -1115,7 +1109,7 @@ u_read_undo(name, hash) curbuf->b_u_seq_last = seq_last; curbuf->b_u_seq_cur = seq_cur; curbuf->b_u_seq_time = seq_time; - U_FREE_LINE(uhp_table); + vim_free(uhp_table); #ifdef U_DEBUG u_check(TRUE); #endif @@ -1124,14 +1118,13 @@ u_read_undo(name, hash) goto theend; error: - if (line_ptr != NULL) - U_FREE_LINE(line_ptr); + vim_free(line_ptr); if (uhp_table != NULL) { for (i = 0; i < num_head; i++) if (uhp_table[i] != NULL) u_free_uhp(uhp_table[i]); - U_FREE_LINE(uhp_table); + vim_free(uhp_table); } theend: @@ -1156,7 +1149,7 @@ u_free_uhp(uhp) u_freeentry(uep, uep->ue_size); uep = nuep; } - U_FREE_LINE(uhp); + vim_free(uhp); } /* @@ -1994,7 +1987,7 @@ u_undoredo(undo) if (oldsize > 0) { if ((newarray = (char_u **)U_ALLOC_LINE( - (unsigned)(sizeof(char_u *) * oldsize))) == NULL) + sizeof(char_u *) * oldsize)) == NULL) { do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize)); /* @@ -2038,9 +2031,9 @@ u_undoredo(undo) ml_replace((linenr_T)1, uep->ue_array[i], TRUE); else ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE); - U_FREE_LINE(uep->ue_array[i]); + vim_free(uep->ue_array[i]); } - U_FREE_LINE((char_u *)uep->ue_array); + vim_free((char_u *)uep->ue_array); } /* adjust marks */ @@ -2578,7 +2571,7 @@ u_freeentries(buf, uhp, uhpp) #ifdef U_DEBUG uhp->uh_magic = 0; #endif - U_FREE_LINE((char_u *)uhp); + vim_free((char_u *)uhp); --buf->b_u_numhead; } @@ -2591,12 +2584,12 @@ u_freeentry(uep, n) long n; { while (n > 0) - U_FREE_LINE(uep->ue_array[--n]); - U_FREE_LINE((char_u *)uep->ue_array); + vim_free(uep->ue_array[--n]); + vim_free((char_u *)uep->ue_array); #ifdef U_DEBUG uep->ue_magic = 0; #endif - U_FREE_LINE((char_u *)uep); + vim_free((char_u *)uep); } /* @@ -2643,7 +2636,7 @@ u_clearline() { if (curbuf->b_u_line_ptr != NULL) { - U_FREE_LINE(curbuf->b_u_line_ptr); + vim_free(curbuf->b_u_line_ptr); curbuf->b_u_line_ptr = NULL; curbuf->b_u_line_lnum = 0; } @@ -2682,7 +2675,7 @@ u_undoline() } ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE); changed_bytes(curbuf->b_u_line_lnum, 0); - U_FREE_LINE(curbuf->b_u_line_ptr); + vim_free(curbuf->b_u_line_ptr); curbuf->b_u_line_ptr = oldp; t = curbuf->b_u_line_colnr; @@ -2694,26 +2687,6 @@ u_undoline() } /* - * There are two implementations of the memory management for undo: - * 1. Use the standard malloc()/free() functions. - * This should be fast for allocating memory, but when a buffer is - * abandoned every single allocated chunk must be freed, which may be slow. - * 2. Allocate larger blocks of memory and keep track of chunks ourselves. - * This is fast for abandoning, but the use of linked lists is slow for - * finding a free chunk. Esp. when a lot of lines are changed or deleted. - * A bit of profiling showed that the first method is faster, especially when - * making a large number of changes, under the condition that malloc()/free() - * is implemented efficiently. - */ -#ifdef U_USE_MALLOC -/* - * Version of undo memory allocation using malloc()/free() - * - * U_FREE_LINE() and U_ALLOC_LINE() are macros that invoke vim_free() and - * lalloc() directly. - */ - -/* * Free all allocated memory blocks for the buffer 'buf'. */ void @@ -2722,394 +2695,18 @@ u_blockfree(buf) { while (buf->b_u_oldhead != NULL) u_freeheader(buf, buf->b_u_oldhead, NULL); - U_FREE_LINE(buf->b_u_line_ptr); -} - -#else -/* - * Storage allocation for the undo lines and blocks of the current file. - * Version where Vim keeps track of the available memory. - */ - -/* - * Memory is allocated in relatively large blocks. These blocks are linked - * in the allocated block list, headed by curbuf->b_block_head. They are all - * freed when abandoning a file, so we don't have to free every single line. - * The list is kept sorted on memory address. - * block_alloc() allocates a block. - * m_blockfree() frees all blocks. - * - * The available chunks of memory are kept in free chunk lists. There is - * one free list for each block of allocated memory. The list is kept sorted - * on memory address. - * u_alloc_line() gets a chunk from the free lists. - * u_free_line() returns a chunk to the free lists. - * curbuf->b_m_search points to the chunk before the chunk that was - * freed/allocated the last time. - * curbuf->b_mb_current points to the b_head where curbuf->b_m_search - * points into the free list. - * - * - * b_block_head /---> block #1 /---> block #2 - * mb_next ---/ mb_next ---/ mb_next ---> NULL - * mb_info mb_info mb_info - * | | | - * V V V - * NULL free chunk #1.1 free chunk #2.1 - * | | - * V V - * free chunk #1.2 NULL - * | - * V - * NULL - * - * When a single free chunk list would have been used, it could take a lot - * of time in u_free_line() to find the correct place to insert a chunk in the - * free list. The single free list would become very long when many lines are - * changed (e.g. with :%s/^M$//). - */ - - /* - * this blocksize is used when allocating new lines - */ -#define MEMBLOCKSIZE 2044 - -/* - * The size field contains the size of the chunk, including the size field - * itself. - * - * When the chunk is not in-use it is preceded with the m_info structure. - * The m_next field links it in one of the free chunk lists. - * - * On most unix systems structures have to be longword (32 or 64 bit) aligned. - * On most other systems they are short (16 bit) aligned. - */ - -/* the structure definitions are now in structs.h */ - -#ifdef ALIGN_LONG - /* size of m_size */ -# define M_OFFSET (sizeof(long_u)) -#else - /* size of m_size */ -# define M_OFFSET (sizeof(short_u)) -#endif - -static char_u *u_blockalloc __ARGS((long_u)); - -/* - * Allocate a block of memory and link it in the allocated block list. - */ - static char_u * -u_blockalloc(size) - long_u size; -{ - mblock_T *p; - mblock_T *mp, *next; - - p = (mblock_T *)lalloc(size + sizeof(mblock_T), FALSE); - if (p != NULL) - { - /* Insert the block into the allocated block list, keeping it - sorted on address. */ - for (mp = &curbuf->b_block_head; - (next = mp->mb_next) != NULL && next < p; - mp = next) - ; - p->mb_next = next; /* link in block list */ - p->mb_size = size; - p->mb_maxsize = 0; /* nothing free yet */ - mp->mb_next = p; - p->mb_info.m_next = NULL; /* clear free list */ - p->mb_info.m_size = 0; - curbuf->b_mb_current = p; /* remember current block */ - curbuf->b_m_search = NULL; - p++; /* return usable memory */ - } - return (char_u *)p; -} - -/* - * free all allocated memory blocks for the buffer 'buf' - */ - void -u_blockfree(buf) - buf_T *buf; -{ - mblock_T *p, *np; - - for (p = buf->b_block_head.mb_next; p != NULL; p = np) - { - np = p->mb_next; - vim_free(p); - } - buf->b_block_head.mb_next = NULL; - buf->b_m_search = NULL; - buf->b_mb_current = NULL; + vim_free(buf->b_u_line_ptr); } /* - * Free a chunk of memory for the current buffer. - * Insert the chunk into the correct free list, keeping it sorted on address. - */ - static void -u_free_line(ptr, keep) - char_u *ptr; - int keep; /* don't free the block when it's empty */ -{ - minfo_T *next; - minfo_T *prev, *curr; - minfo_T *mp; - mblock_T *nextb; - mblock_T *prevb; - long_u maxsize; - - if (ptr == NULL || ptr == IObuff) - return; /* illegal address can happen in out-of-memory situations */ - - mp = (minfo_T *)(ptr - M_OFFSET); - - /* find block where chunk could be a part off */ - /* if we change curbuf->b_mb_current, curbuf->b_m_search is set to NULL */ - if (curbuf->b_mb_current == NULL || mp < (minfo_T *)curbuf->b_mb_current) - { - curbuf->b_mb_current = curbuf->b_block_head.mb_next; - curbuf->b_m_search = NULL; - } - if ((nextb = curbuf->b_mb_current->mb_next) != NULL - && (minfo_T *)nextb < mp) - { - curbuf->b_mb_current = nextb; - curbuf->b_m_search = NULL; - } - while ((nextb = curbuf->b_mb_current->mb_next) != NULL - && (minfo_T *)nextb < mp) - curbuf->b_mb_current = nextb; - - curr = NULL; - /* - * If mp is smaller than curbuf->b_m_search->m_next go to the start of - * the free list - */ - if (curbuf->b_m_search == NULL || mp < (curbuf->b_m_search->m_next)) - next = &(curbuf->b_mb_current->mb_info); - else - next = curbuf->b_m_search; - /* - * The following loop is executed very often. - * Therefore it has been optimized at the cost of readability. - * Keep it fast! - */ -#ifdef SLOW_BUT_EASY_TO_READ - do - { - prev = curr; - curr = next; - next = next->m_next; - } - while (mp > next && next != NULL); -#else - do /* first, middle, last */ - { - prev = next->m_next; /* curr, next, prev */ - if (prev == NULL || mp <= prev) - { - prev = curr; - curr = next; - next = next->m_next; - break; - } - curr = prev->m_next; /* next, prev, curr */ - if (curr == NULL || mp <= curr) - { - prev = next; - curr = prev->m_next; - next = curr->m_next; - break; - } - next = curr->m_next; /* prev, curr, next */ - } - while (mp > next && next != NULL); -#endif - - /* if *mp and *next are concatenated, join them into one chunk */ - if ((char_u *)mp + mp->m_size == (char_u *)next) - { - mp->m_size += next->m_size; - mp->m_next = next->m_next; - } - else - mp->m_next = next; - maxsize = mp->m_size; - - /* if *curr and *mp are concatenated, join them */ - if (prev != NULL && (char_u *)curr + curr->m_size == (char_u *)mp) - { - curr->m_size += mp->m_size; - maxsize = curr->m_size; - curr->m_next = mp->m_next; - curbuf->b_m_search = prev; - } - else - { - curr->m_next = mp; - curbuf->b_m_search = curr; /* put curbuf->b_m_search before freed - chunk */ - } - - /* - * If the block only contains free memory now, release it. - */ - if (!keep && curbuf->b_mb_current->mb_size - == curbuf->b_mb_current->mb_info.m_next->m_size) - { - /* Find the block before the current one to be able to unlink it from - * the list of blocks. */ - prevb = &curbuf->b_block_head; - for (nextb = prevb->mb_next; nextb != curbuf->b_mb_current; - nextb = nextb->mb_next) - prevb = nextb; - prevb->mb_next = nextb->mb_next; - vim_free(nextb); - curbuf->b_mb_current = NULL; - curbuf->b_m_search = NULL; - } - else if (curbuf->b_mb_current->mb_maxsize < maxsize) - curbuf->b_mb_current->mb_maxsize = maxsize; -} - -/* - * Allocate and initialize a new line structure with room for at least - * 'size' characters plus a terminating NUL. - */ - static char_u * -u_alloc_line(size) - unsigned size; -{ - minfo_T *mp, *mprev, *mp2; - mblock_T *mbp; - int size_align; - - /* - * Add room for size field and trailing NUL byte. - * Adjust for minimal size (must be able to store minfo_T - * plus a trailing NUL, so the chunk can be released again) - */ - size += M_OFFSET + 1; - if (size < sizeof(minfo_T) + 1) - size = sizeof(minfo_T) + 1; - - /* - * round size up for alignment - */ - size_align = (size + ALIGN_MASK) & ~ALIGN_MASK; - - /* - * If curbuf->b_m_search is NULL (uninitialized free list) start at - * curbuf->b_block_head - */ - if (curbuf->b_mb_current == NULL || curbuf->b_m_search == NULL) - { - curbuf->b_mb_current = &curbuf->b_block_head; - curbuf->b_m_search = &(curbuf->b_block_head.mb_info); - } - - /* Search for a block with enough space. */ - mbp = curbuf->b_mb_current; - while (mbp->mb_maxsize < size_align) - { - if (mbp->mb_next != NULL) - mbp = mbp->mb_next; - else - mbp = &curbuf->b_block_head; - if (mbp == curbuf->b_mb_current) - { - int n = (size_align > (MEMBLOCKSIZE / 4) - ? size_align : MEMBLOCKSIZE); - - /* Back where we started in block list: need to add a new block - * with enough space. */ - mp = (minfo_T *)u_blockalloc((long_u)n); - if (mp == NULL) - return (NULL); - mp->m_size = n; - u_free_line((char_u *)mp + M_OFFSET, TRUE); - mbp = curbuf->b_mb_current; - break; - } - } - if (mbp != curbuf->b_mb_current) - curbuf->b_m_search = &(mbp->mb_info); - - /* In this block find a chunk with enough space. */ - mprev = curbuf->b_m_search; - mp = curbuf->b_m_search->m_next; - for (;;) - { - if (mp == NULL) /* at end of the list */ - mp = &(mbp->mb_info); /* wrap around to begin */ - if (mp->m_size >= size) - break; - if (mp == curbuf->b_m_search) - { - /* back where we started in free chunk list: "cannot happen" */ - EMSG2(_(e_intern2), "u_alloc_line()"); - return NULL; - } - mprev = mp; - mp = mp->m_next; - } - - /* when using the largest chunk adjust mb_maxsize */ - if (mp->m_size >= mbp->mb_maxsize) - mbp->mb_maxsize = 0; - - /* if the chunk we found is large enough, split it up in two */ - if ((long)mp->m_size - size_align >= (long)(sizeof(minfo_T) + 1)) - { - mp2 = (minfo_T *)((char_u *)mp + size_align); - mp2->m_size = mp->m_size - size_align; - mp2->m_next = mp->m_next; - mprev->m_next = mp2; - mp->m_size = size_align; - } - else /* remove *mp from the free list */ - { - mprev->m_next = mp->m_next; - } - curbuf->b_m_search = mprev; - curbuf->b_mb_current = mbp; - - /* If using the largest chunk need to find the new largest chunk */ - if (mbp->mb_maxsize == 0) - for (mp2 = &(mbp->mb_info); mp2 != NULL; mp2 = mp2->m_next) - if (mbp->mb_maxsize < mp2->m_size) - mbp->mb_maxsize = mp2->m_size; - - mp = (minfo_T *)((char_u *)mp + M_OFFSET); - *(char_u *)mp = NUL; /* set the first byte to NUL */ - - return ((char_u *)mp); -} -#endif - -/* - * u_save_line(): allocate memory with u_alloc_line() and copy line 'lnum' - * into it. + * u_save_line(): allocate memory and copy line 'lnum' into it. + * Returns NULL when out of memory. */ static char_u * u_save_line(lnum) linenr_T lnum; { - char_u *src; - char_u *dst; - unsigned len; - - src = ml_get(lnum); - len = (unsigned)STRLEN(src); - if ((dst = U_ALLOC_LINE(len)) != NULL) - mch_memmove(dst, src, (size_t)(len + 1)); - return (dst); + return vim_strsave(ml_get(lnum)); } /*