undo.c
资源名称:vim53src.zip [点击查看]
上传用户:gddssl
上传日期:2007-01-06
资源大小:1003k
文件大小:29k
源码类别:
编辑器/阅读器
开发平台:
DOS
- /* vi:set ts=8 sts=4 sw=4:
- *
- * VIM - Vi IMproved by Bram Moolenaar
- *
- * Do ":help uganda" in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- */
- /*
- * undo.c: multi level undo facility
- *
- * The saved lines are stored in a list of lists (one for each buffer):
- *
- * b_u_oldhead------------------------------------------------+
- * |
- * V
- * +--------------+ +--------------+ +--------------+
- * b_u_newhead--->| u_header | | u_header | | u_header |
- * | uh_next------>| uh_next------>| uh_next---->NULL
- * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
- * | uh_entry | | uh_entry | | uh_entry |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ +--------------+ +--------------+
- * | u_entry | | u_entry | | u_entry |
- * | ue_next | | ue_next | | ue_next |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ NULL NULL
- * | u_entry |
- * | ue_next |
- * +--------|-----+
- * |
- * V
- * etc.
- *
- * Each u_entry list contains the information for one undo or redo.
- * curbuf->b_u_curhead points to the header of the last undo (the next redo),
- * or is NULL if nothing has been undone.
- *
- * All data is allocated with u_alloc_line(), thus it will be freed as soon as
- * we switch files!
- */
- #include "vim.h"
- static void u_getbot __ARGS((void));
- static int u_savecommon __ARGS((linenr_t, linenr_t, linenr_t));
- static void u_doit __ARGS((int count));
- static void u_undoredo __ARGS((void));
- static void u_undo_end __ARGS((void));
- static void u_freelist __ARGS((struct u_header *));
- static void u_freeentry __ARGS((struct u_entry *, long));
- static char_u *u_blockalloc __ARGS((long_u));
- static void u_free_line __ARGS((char_u *));
- static char_u *u_alloc_line __ARGS((unsigned));
- static char_u *u_save_line __ARGS((linenr_t));
- static long u_newcount, u_oldcount;
- /*
- * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
- * the action that "u" should do.
- */
- static int undo_undoes = FALSE;
- /*
- * save the current line for both the "u" and "U" command
- */
- int
- u_save_cursor()
- {
- return (u_save((linenr_t)(curwin->w_cursor.lnum - 1),
- (linenr_t)(curwin->w_cursor.lnum + 1)));
- }
- /*
- * Save the lines between "top" and "bot" for both the "u" and "U" command.
- * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
- int
- u_save(top, bot)
- linenr_t top, bot;
- {
- if (undo_off)
- return OK;
- if (top > curbuf->b_ml.ml_line_count ||
- top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
- return FALSE; /* rely on caller to do error messages */
- if (top + 2 == bot)
- u_saveline((linenr_t)(top + 1));
- return (u_savecommon(top, bot, (linenr_t)0));
- }
- /*
- * save the line "lnum" (used by :s command)
- * The line is replaced, so the new bottom line is lnum + 1.
- */
- int
- u_savesub(lnum)
- linenr_t lnum;
- {
- if (undo_off)
- return OK;
- return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
- }
- /*
- * a new line is inserted before line "lnum" (used by :s command)
- * The line is inserted, so the new bottom line is lnum + 1.
- */
- int
- u_inssub(lnum)
- linenr_t lnum;
- {
- if (undo_off)
- return OK;
- return (u_savecommon(lnum - 1, lnum, lnum + 1));
- }
- /*
- * save the lines "lnum" - "lnum" + nlines (used by delete command)
- * The lines are deleted, so the new bottom line is lnum, unless the buffer
- * becomes empty.
- */
- int
- u_savedel(lnum, nlines)
- linenr_t lnum;
- long nlines;
- {
- if (undo_off)
- return OK;
- return (u_savecommon(lnum - 1, lnum + nlines,
- nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
- }
- static int
- u_savecommon(top, bot, newbot)
- linenr_t top, bot;
- linenr_t newbot;
- {
- linenr_t lnum;
- long i;
- struct u_header *uhp;
- struct u_entry *uep;
- long size;
- /*
- * if curbuf->b_u_synced == TRUE make a new header
- */
- if (curbuf->b_u_synced)
- {
- /*
- * if we undid more than we redid, free the entry lists before and
- * including curbuf->b_u_curhead
- */
- while (curbuf->b_u_curhead != NULL)
- u_freelist(curbuf->b_u_newhead);
- /*
- * free headers to keep the size right
- */
- while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
- u_freelist(curbuf->b_u_oldhead);
- if (p_ul < 0) /* no undo at all */
- return OK;
- /*
- * make a new header entry
- */
- uhp = (struct u_header *)u_alloc_line((unsigned)
- sizeof(struct u_header));
- if (uhp == NULL)
- goto nomem;
- uhp->uh_prev = NULL;
- uhp->uh_next = curbuf->b_u_newhead;
- if (curbuf->b_u_newhead != NULL)
- curbuf->b_u_newhead->uh_prev = uhp;
- uhp->uh_entry = NULL;
- uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
- /* save changed and buffer empty flag for undo */
- uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
- ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
- /* save named marks for undo */
- mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(FPOS) * NMARKS);
- curbuf->b_u_newhead = uhp;
- if (curbuf->b_u_oldhead == NULL)
- curbuf->b_u_oldhead = uhp;
- ++curbuf->b_u_numhead;
- }
- else /* find line number for ue_bot for previous u_save() */
- u_getbot();
- size = bot - top - 1;
- #if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
- /*
- * With Amiga and MSDOS 16 bit we can't handle big undo's, because
- * then u_alloc_line would have to allocate a block larger than 32K
- */
- if (size >= 8000)
- goto nomem;
- #endif
- /*
- * add lines in front of entry list
- */
- uep = (struct u_entry *)u_alloc_line((unsigned)sizeof(struct u_entry));
- if (uep == NULL)
- goto nomem;
- uep->ue_size = size;
- uep->ue_top = top;
- uep->ue_lcount = 0;
- if (newbot)
- uep->ue_bot = newbot;
- /*
- * Use 0 for ue_bot if bot is below last line.
- * Otherwise we have to compute ue_bot later.
- */
- else if (bot > curbuf->b_ml.ml_line_count)
- uep->ue_bot = 0;
- else
- uep->ue_lcount = curbuf->b_ml.ml_line_count;
- if (size)
- {
- if ((uep->ue_array = (char_u **)u_alloc_line((unsigned)(sizeof(char_u *) * size))) == NULL)
- {
- u_freeentry(uep, 0L);
- goto nomem;
- }
- for (i = 0, lnum = top + 1; i < size; ++i)
- {
- if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
- {
- u_freeentry(uep, i);
- goto nomem;
- }
- }
- }
- uep->ue_next = curbuf->b_u_newhead->uh_entry;
- curbuf->b_u_newhead->uh_entry = uep;
- curbuf->b_u_synced = FALSE;
- undo_undoes = FALSE;
- return OK;
- nomem:
- if (ask_yesno((char_u *)"No undo possible; continue anyway", TRUE) == 'y')
- {
- undo_off = TRUE; /* will be reset when character typed */
- return OK;
- }
- do_outofmem_msg();
- return FAIL;
- }
- /*
- * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
- * If 'cpoptions' does not contain 'u': Always undo.
- */
- void
- u_undo(count)
- int count;
- {
- /*
- * If we get an undo command while executing a macro, we behave like the
- * original vi. If this happens twice in one macro the result will not
- * be compatible.
- */
- if (curbuf->b_u_synced == FALSE)
- {
- u_sync();
- count = 1;
- }
- if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
- undo_undoes = TRUE;
- else
- undo_undoes = !undo_undoes;
- u_doit(count);
- }
- /*
- * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
- * If 'cpoptions' does not contain 'u': Always redo.
- */
- void
- u_redo(count)
- int count;
- {
- if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
- undo_undoes = FALSE;
- u_doit(count);
- }
- /*
- * Undo or redo, depending on 'undo_undoes', 'count' times.
- */
- static void
- u_doit(count)
- int count;
- {
- u_newcount = 0;
- u_oldcount = 0;
- while (count--)
- {
- if (undo_undoes)
- {
- if (curbuf->b_u_curhead == NULL) /* first undo */
- curbuf->b_u_curhead = curbuf->b_u_newhead;
- else if (p_ul > 0) /* multi level undo */
- /* get next undo */
- curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
- /* nothing to undo */
- if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
- {
- /* stick curbuf->b_u_curhead at end */
- curbuf->b_u_curhead = curbuf->b_u_oldhead;
- beep_flush();
- break;
- }
- u_undoredo();
- }
- else
- {
- if (curbuf->b_u_curhead == NULL || p_ul <= 0)
- {
- beep_flush(); /* nothing to redo */
- break;
- }
- u_undoredo();
- /* advance for next redo */
- curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
- }
- }
- u_undo_end();
- }
- /*
- * u_undoredo: common code for undo and redo
- *
- * The lines in the file are replaced by the lines in the entry list at
- * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
- * list for the next undo/redo.
- */
- static void
- u_undoredo()
- {
- char_u **newarray = NULL;
- linenr_t oldsize;
- linenr_t newsize;
- linenr_t top, bot;
- linenr_t lnum;
- linenr_t newlnum = MAXLNUM;
- long i;
- struct u_entry *uep, *nuep;
- struct u_entry *newlist = NULL;
- int old_flags;
- int new_flags;
- FPOS namedm[NMARKS];
- int empty_buffer; /* buffer became empty */
- old_flags = curbuf->b_u_curhead->uh_flags;
- new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
- ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
- if (old_flags & UH_CHANGED)
- changed();
- else
- unchanged(curbuf, FALSE);
- setpcmark();
- changed_line_abv_curs(); /* need to recompute cursor posn on screen */
- /*
- * save marks before undo/redo
- */
- mch_memmove(namedm, curbuf->b_namedm, sizeof(FPOS) * NMARKS);
- curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
- curbuf->b_op_start.col = 0;
- curbuf->b_op_end.lnum = 0;
- curbuf->b_op_end.col = 0;
- for (uep = curbuf->b_u_curhead->uh_entry; uep != NULL; uep = nuep)
- {
- top = uep->ue_top;
- bot = uep->ue_bot;
- if (bot == 0)
- bot = curbuf->b_ml.ml_line_count + 1;
- if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
- {
- EMSG("u_undo: line numbers wrong");
- changed(); /* don't want UNCHANGED now */
- return;
- }
- if (top < newlnum)
- {
- newlnum = top;
- curwin->w_cursor.lnum = top + 1;
- }
- oldsize = bot - top - 1; /* number of lines before undo */
- newsize = uep->ue_size; /* number of lines after undo */
- empty_buffer = FALSE;
- /* delete the lines between top and bot and save them in newarray */
- if (oldsize)
- {
- if ((newarray = (char_u **)u_alloc_line((unsigned)(sizeof(char_u *) * oldsize))) == NULL)
- {
- do_outofmem_msg();
- /*
- * We have messed up the entry list, repair is impossible.
- * we have to free the rest of the list.
- */
- while (uep != NULL)
- {
- nuep = uep->ue_next;
- u_freeentry(uep, uep->ue_size);
- uep = nuep;
- }
- break;
- }
- /* delete backwards, it goes faster in most cases */
- for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
- {
- /* what can we do when we run out of memory? */
- if ((newarray[i] = u_save_line(lnum)) == NULL)
- do_outofmem_msg();
- /* remember we deleted the last line in the buffer, and a
- * dummy empty line will be inserted */
- if (curbuf->b_ml.ml_line_count == 1)
- empty_buffer = TRUE;
- ml_delete(lnum, FALSE);
- }
- }
- /* insert the lines in u_array between top and bot */
- if (newsize)
- {
- for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
- {
- /*
- * If the file is empty, there is an empty line 1 that we
- * should get rid of, by replacing it with the new line
- */
- if (empty_buffer && lnum == 0)
- 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]);
- }
- u_free_line((char_u *)uep->ue_array);
- }
- /* adjust marks */
- if (oldsize != newsize)
- {
- mark_adjust(top + 1, top + oldsize, (linenr_t)MAXLNUM,
- (long)newsize - (long)oldsize);
- if (curbuf->b_op_start.lnum > top + oldsize)
- curbuf->b_op_start.lnum += newsize - oldsize;
- if (curbuf->b_op_end.lnum > top + oldsize)
- curbuf->b_op_end.lnum += newsize - oldsize;
- }
- /* set '[ and '] mark */
- if (top + 1 < curbuf->b_op_start.lnum)
- curbuf->b_op_start.lnum = top + 1;
- if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
- curbuf->b_op_end.lnum = top + 1;
- else if (top + newsize > curbuf->b_op_end.lnum)
- curbuf->b_op_end.lnum = top + newsize;
- u_newcount += newsize;
- u_oldcount += oldsize;
- uep->ue_size = oldsize;
- uep->ue_array = newarray;
- uep->ue_bot = top + newsize + 1;
- /*
- * insert this entry in front of the new entry list
- */
- nuep = uep->ue_next;
- uep->ue_next = newlist;
- newlist = uep;
- }
- curbuf->b_u_curhead->uh_entry = newlist;
- curbuf->b_u_curhead->uh_flags = new_flags;
- if ((old_flags & UH_EMPTYBUF) && bufempty())
- curbuf->b_ml.ml_flags |= ML_EMPTY;
- /*
- * restore marks from before undo/redo
- */
- for (i = 0; i < NMARKS; ++i)
- if (curbuf->b_u_curhead->uh_namedm[i].lnum)
- {
- curbuf->b_namedm[i] = curbuf->b_u_curhead->uh_namedm[i];
- curbuf->b_u_curhead->uh_namedm[i] = namedm[i];
- }
- /*
- * If the cursor is only off by one line, put it at the same position as
- * before starting the change (for the "o" command).
- * Otherwise the cursor should go to the first undone line.
- */
- if (curbuf->b_u_curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum &&
- curwin->w_cursor.lnum > 1)
- --curwin->w_cursor.lnum;
- if (curbuf->b_u_curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
- curwin->w_cursor.col = curbuf->b_u_curhead->uh_cursor.col;
- else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
- beginline(BL_SOL | BL_FIX);
- /* We still seem to need the case below because sometimes we get here with
- * the current cursor line being one past the end (eg after adding lines
- * at the end of the file, and then undoing it). Is it fair enough that
- * this happens? -- webb
- */
- else
- curwin->w_cursor.col = 0;
- /* Make sure the cursor is on an existing line and column. */
- adjust_cursor();
- }
- /*
- * If we deleted or added lines, report the number of less/more lines.
- * Otherwise, report the number of changes (this may be incorrect
- * in some cases, but it's better than nothing).
- */
- static void
- u_undo_end()
- {
- if ((u_oldcount -= u_newcount) != 0)
- msgmore(-u_oldcount);
- else if (u_newcount > p_report)
- smsg((char_u *)"%ld change%s", u_newcount, plural(u_newcount));
- update_topline();
- update_curbuf(NOT_VALID); /* need to update all windows in this buffer */
- }
- /*
- * u_sync: stop adding to the current entry list
- */
- void
- u_sync()
- {
- if (curbuf->b_u_synced)
- return; /* already synced */
- u_getbot(); /* compute ue_bot of previous u_save */
- curbuf->b_u_curhead = NULL;
- }
- /*
- * Called after writing the file and setting b_changed to FALSE.
- * Now an undo means that the buffer is modified.
- */
- void
- u_unchanged(buf)
- BUF *buf;
- {
- struct u_header *uh;
- for (uh = buf->b_u_newhead; uh; uh = uh->uh_next)
- uh->uh_flags |= UH_CHANGED;
- buf->b_did_warn = FALSE;
- }
- /*
- * u_getbot(): compute the line number of the previous u_save
- * It is called only when b_u_synced is FALSE.
- */
- static void
- u_getbot()
- {
- struct u_entry *uep;
- if (curbuf->b_u_newhead == NULL ||
- (uep = curbuf->b_u_newhead->uh_entry) == NULL)
- {
- EMSG("undo list corrupt");
- return;
- }
- if (uep->ue_lcount != 0)
- {
- /*
- * the new ue_bot is computed from the number of lines that has been
- * inserted (0 - deleted) since calling u_save. This is equal to the old
- * line count subtracted from the current line count.
- */
- uep->ue_bot = uep->ue_top + uep->ue_size + 1 +
- (curbuf->b_ml.ml_line_count - uep->ue_lcount);
- if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
- {
- EMSG("undo line missing");
- uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will
- * get all the old lines back
- * without deleting the current
- * ones */
- }
- uep->ue_lcount = 0;
- }
- curbuf->b_u_synced = TRUE;
- }
- /*
- * u_freelist: free one entry list and adjust the pointers
- */
- static void
- u_freelist(uhp)
- struct u_header *uhp;
- {
- struct u_entry *uep, *nuep;
- for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
- {
- nuep = uep->ue_next;
- u_freeentry(uep, uep->ue_size);
- }
- if (curbuf->b_u_curhead == uhp)
- curbuf->b_u_curhead = NULL;
- if (uhp->uh_next == NULL)
- curbuf->b_u_oldhead = uhp->uh_prev;
- else
- uhp->uh_next->uh_prev = uhp->uh_prev;
- if (uhp->uh_prev == NULL)
- curbuf->b_u_newhead = uhp->uh_next;
- else
- uhp->uh_prev->uh_next = uhp->uh_next;
- u_free_line((char_u *)uhp);
- --curbuf->b_u_numhead;
- }
- /*
- * free entry 'uep' and 'n' lines in uep->ue_array[]
- */
- static void
- u_freeentry(uep, n)
- struct u_entry *uep;
- long n;
- {
- while (n)
- u_free_line(uep->ue_array[--n]);
- u_free_line((char_u *)uep);
- }
- /*
- * invalidate the undo buffer; called when storage has already been released
- */
- void
- u_clearall(buf)
- BUF *buf;
- {
- buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
- buf->b_u_synced = TRUE;
- buf->b_u_numhead = 0;
- buf->b_u_line_ptr = NULL;
- buf->b_u_line_lnum = 0;
- }
- /*
- * save the line "lnum" for the "U" command
- */
- void
- u_saveline(lnum)
- linenr_t lnum;
- {
- if (lnum == curbuf->b_u_line_lnum) /* line is already saved */
- return;
- if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */
- return;
- u_clearline();
- curbuf->b_u_line_lnum = lnum;
- if (curwin->w_cursor.lnum == lnum)
- curbuf->b_u_line_colnr = curwin->w_cursor.col;
- else
- curbuf->b_u_line_colnr = 0;
- if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
- do_outofmem_msg();
- }
- /*
- * clear the line saved for the "U" command
- * (this is used externally for crossing a line while in insert mode)
- */
- void
- u_clearline()
- {
- if (curbuf->b_u_line_ptr != NULL)
- {
- u_free_line(curbuf->b_u_line_ptr);
- curbuf->b_u_line_ptr = NULL;
- curbuf->b_u_line_lnum = 0;
- }
- }
- /*
- * Implementation of the "U" command.
- * Differentiation from vi: "U" can be undone with the next "U".
- * We also allow the cursor to be in another line.
- */
- void
- u_undoline()
- {
- colnr_t t;
- char_u *oldp;
- if (undo_off)
- return;
- if (curbuf->b_u_line_ptr == NULL ||
- curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
- {
- beep_flush();
- return;
- }
- /* first save the line for the 'u' command */
- if (u_savecommon(curbuf->b_u_line_lnum - 1,
- curbuf->b_u_line_lnum + 1, (linenr_t)0) == FAIL)
- return;
- oldp = u_save_line(curbuf->b_u_line_lnum);
- if (oldp == NULL)
- {
- do_outofmem_msg();
- return;
- }
- ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
- u_free_line(curbuf->b_u_line_ptr);
- curbuf->b_u_line_ptr = oldp;
- #ifdef SYNTAX_HL
- /* recompute syntax hl., starting with current line */
- syn_changed(curbuf->b_u_line_lnum);
- #endif
- t = curbuf->b_u_line_colnr;
- if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
- curbuf->b_u_line_colnr = curwin->w_cursor.col;
- changed_cline_bef_curs();
- curwin->w_cursor.col = t;
- curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
- approximate_botline(); /* w_botline may have changed a bit */
- update_screen(VALID_TO_CURSCHAR);
- }
- /*
- * storage allocation for the undo lines and blocks of the current file
- */
- /*
- * 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
- /*
- * Allocate a block of memory and link it in the allocated block list.
- */
- static char_u *
- u_blockalloc(size)
- long_u size;
- {
- struct m_block *p;
- struct m_block *mp, *next;
- p = (struct m_block *)lalloc(size + sizeof(struct m_block), 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 */
- 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 *buf;
- {
- struct m_block *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;
- }
- /*
- * Free a chunk of memory.
- * Insert the chunk into the correct free list, keeping it sorted on address.
- */
- static void
- u_free_line(ptr)
- char_u *ptr;
- {
- info_t *next;
- info_t *prev, *curr;
- info_t *mp;
- struct m_block *nextb;
- if (ptr == NULL || ptr == IObuff)
- return; /* illegal address can happen in out-of-memory situations */
- mp = (info_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 < (info_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 && (info_t *)nextb < mp)
- {
- curbuf->b_mb_current = nextb;
- curbuf->b_m_search = NULL;
- }
- while ((nextb = curbuf->b_mb_current->mb_next) != NULL &&
- (info_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;
- /* 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;
- 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 */
- }
- }
- /*
- * 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;
- {
- info_t *mp, *mprev, *mp2;
- struct m_block *mbp;
- int size_align;
- /*
- * Add room for size field and trailing NUL byte.
- * Adjust for minimal size (must be able to store info_t
- * plus a trailing NUL, so the chunk can be released again)
- */
- size += M_OFFSET + 1;
- if (size < sizeof(info_t) + 1)
- size = sizeof(info_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 space in free list */
- mprev = curbuf->b_m_search;
- mbp = curbuf->b_mb_current;
- mp = curbuf->b_m_search->m_next;
- if (mp == NULL)
- {
- if (mbp->mb_next)
- mbp = mbp->mb_next;
- else
- mbp = &curbuf->b_block_head;
- mp = curbuf->b_m_search = &(mbp->mb_info);
- }
- while (mp->m_size < size)
- {
- if (mp == curbuf->b_m_search) /* back where we started in free
- chunk list */
- {
- if (mbp->mb_next)
- mbp = mbp->mb_next;
- else
- mbp = &curbuf->b_block_head;
- mp = curbuf->b_m_search = &(mbp->mb_info);
- if (mbp == curbuf->b_mb_current) /* back where we started in
- block list */
- {
- int n = (size_align > (MEMBLOCKSIZE / 4)
- ? size_align : MEMBLOCKSIZE);
- mp = (info_t *)u_blockalloc((long_u)n);
- if (mp == NULL)
- return (NULL);
- mp->m_size = n;
- u_free_line((char_u *)mp + M_OFFSET);
- mp = curbuf->b_m_search;
- mbp = curbuf->b_mb_current;
- }
- }
- mprev = mp;
- if ((mp = mp->m_next) == NULL) /* at end of the list */
- mp = &(mbp->mb_info); /* wrap around to begin */
- }
- /* if the chunk we found is large enough, split it up in two */
- if ((long)mp->m_size - size_align >= (long)(sizeof(info_t) + 1))
- {
- mp2 = (info_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;
- mp = (info_t *)((char_u *)mp + M_OFFSET);
- *(char_u *)mp = NUL; /* set the first byte to NUL */
- return ((char_u *)mp);
- }
- /*
- * u_save_line(): allocate memory with u_alloc_line() and copy line 'lnum'
- * into it.
- */
- static char_u *
- u_save_line(lnum)
- linenr_t lnum;
- {
- char_u *src;
- char_u *dst;
- unsigned len;
- src = ml_get(lnum);
- len = STRLEN(src);
- if ((dst = u_alloc_line(len)) != NULL)
- mch_memmove(dst, src, (size_t)(len + 1));
- return (dst);
- }
- /*
- * Check if the 'modified' flag is set, or 'ff' has changed (only need to
- * check the first character, because it can only be "dos", "unix" or "mac").
- */
- int
- buf_changed(buf)
- BUF *buf;
- {
- return (buf->b_changed || *buf->b_p_ff != buf->b_start_ffc);
- }
- int
- curbuf_changed()
- {
- return (curbuf->b_changed || *curbuf->b_p_ff != curbuf->b_start_ffc);
- }