mirror of
https://github.com/neovim/neovim.git
synced 2025-10-07 02:16:31 +00:00
refactor(api): reduce temporary allocations when replacing lines
The way ml_replace_buf is implemented makes it unfriendly for being used in a loop: every call allocates a scratch buffer for putting the line into the "dirty" state. This then immediately needs to be freed as the next ml_replace_buf and/or ml_append_buf call will flush that buffer. It's better to later pay the price of allocating the scratch buffer only if the line is being immediately edited (likely when using the API to only change one line) with an extra memcpy, than allocating that buffer multiple times every time the API is called. Of course, a separate xmalloc/xfree cycle for each time the dirty line changes is unwanted to begin with. But fixing that is a later refactor.
This commit is contained in:
@@ -1694,7 +1694,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
|
||||
if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) {
|
||||
continue; // no file
|
||||
}
|
||||
ml_flush_line(buf); // flush buffered line
|
||||
ml_flush_line(buf, false); // flush buffered line
|
||||
// flush locked block
|
||||
ml_find_line(buf, 0, ML_FLUSH);
|
||||
if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
|
||||
@@ -1745,7 +1745,7 @@ void ml_preserve(buf_T *buf, bool message, bool do_fsync)
|
||||
// before.
|
||||
got_int = false;
|
||||
|
||||
ml_flush_line(buf); // flush buffered line
|
||||
ml_flush_line(buf, false); // flush buffered line
|
||||
ml_find_line(buf, 0, ML_FLUSH); // flush locked block
|
||||
int status = mf_sync(mfp, MFS_ALL | (do_fsync ? MFS_FLUSH : 0));
|
||||
|
||||
@@ -1862,7 +1862,7 @@ static char *ml_get_buf_impl(buf_T *buf, linenr_T lnum, bool will_change)
|
||||
siemsg(_(e_ml_get_invalid_lnum_nr), (int64_t)lnum);
|
||||
recursive--;
|
||||
}
|
||||
ml_flush_line(buf);
|
||||
ml_flush_line(buf, false);
|
||||
errorret:
|
||||
STRCPY(questions, "???");
|
||||
buf->b_ml.ml_line_lnum = lnum;
|
||||
@@ -1881,7 +1881,7 @@ errorret:
|
||||
// Don't use the last used line when 'swapfile' is reset, need to load all
|
||||
// blocks.
|
||||
if (buf->b_ml.ml_line_lnum != lnum) {
|
||||
ml_flush_line(buf);
|
||||
ml_flush_line(buf, false);
|
||||
|
||||
// Find the data block containing the line.
|
||||
// This also fills the stack with the blocks from the root to the data
|
||||
@@ -1964,7 +1964,7 @@ int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile)
|
||||
}
|
||||
|
||||
if (curbuf->b_ml.ml_line_lnum != 0) {
|
||||
ml_flush_line(curbuf);
|
||||
ml_flush_line(curbuf, false);
|
||||
}
|
||||
return ml_append_int(curbuf, lnum, line, len, newfile, false);
|
||||
}
|
||||
@@ -1984,7 +1984,7 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char *line, colnr_T len, bool newfi
|
||||
}
|
||||
|
||||
if (buf->b_ml.ml_line_lnum != 0) {
|
||||
ml_flush_line(buf);
|
||||
ml_flush_line(buf, false);
|
||||
}
|
||||
return ml_append_int(buf, lnum, line, len, newfile, false);
|
||||
}
|
||||
@@ -2423,7 +2423,7 @@ void ml_add_deleted_len_buf(buf_T *buf, char *ptr, ssize_t len)
|
||||
|
||||
int ml_replace(linenr_T lnum, char *line, bool copy)
|
||||
{
|
||||
return ml_replace_buf(curbuf, lnum, line, copy);
|
||||
return ml_replace_buf(curbuf, lnum, line, copy, false);
|
||||
}
|
||||
|
||||
/// Replace line "lnum", with buffering, in current buffer.
|
||||
@@ -2438,7 +2438,7 @@ int ml_replace(linenr_T lnum, char *line, bool copy)
|
||||
/// changed_lines(), unless update_screen(UPD_NOT_VALID) is used.
|
||||
///
|
||||
/// @return FAIL for failure, OK otherwise
|
||||
int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy)
|
||||
int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy, bool noalloc)
|
||||
{
|
||||
if (line == NULL) { // just checking...
|
||||
return FAIL;
|
||||
@@ -2450,12 +2450,13 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy)
|
||||
}
|
||||
|
||||
if (copy) {
|
||||
assert(!noalloc);
|
||||
line = xstrdup(line);
|
||||
}
|
||||
|
||||
if (buf->b_ml.ml_line_lnum != lnum) {
|
||||
// another line is buffered, flush it
|
||||
ml_flush_line(buf);
|
||||
ml_flush_line(buf, false);
|
||||
}
|
||||
|
||||
if (kv_size(buf->update_callbacks)) {
|
||||
@@ -2469,6 +2470,13 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy)
|
||||
buf->b_ml.ml_line_ptr = line;
|
||||
buf->b_ml.ml_line_lnum = lnum;
|
||||
buf->b_ml.ml_flags = (buf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
|
||||
if (noalloc) {
|
||||
// TODO(bfredl): this is a bit of a hack. but replacing lines in a loop is really common,
|
||||
// and allocating a separate scratch buffer for each line which is immediately freed adds
|
||||
// a lot of noise. A more general refactor could be to use a _fixed_ scratch buffer for
|
||||
// all lines up to $REASONABLE_SIZE .
|
||||
ml_flush_line(buf, true);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -2483,7 +2491,7 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char *line, bool copy)
|
||||
/// @return FAIL for failure, OK otherwise
|
||||
int ml_delete(linenr_T lnum, bool message)
|
||||
{
|
||||
ml_flush_line(curbuf);
|
||||
ml_flush_line(curbuf, false);
|
||||
return ml_delete_int(curbuf, lnum, message);
|
||||
}
|
||||
|
||||
@@ -2496,7 +2504,7 @@ int ml_delete(linenr_T lnum, bool message)
|
||||
/// @return FAIL for failure, OK otherwise
|
||||
int ml_delete_buf(buf_T *buf, linenr_T lnum, bool message)
|
||||
{
|
||||
ml_flush_line(buf);
|
||||
ml_flush_line(buf, false);
|
||||
return ml_delete_int(buf, lnum, message);
|
||||
}
|
||||
|
||||
@@ -2516,7 +2524,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
|
||||
set_keep_msg(_(no_lines_msg), 0);
|
||||
}
|
||||
|
||||
int i = ml_replace_buf(buf, 1, "", true);
|
||||
int i = ml_replace_buf(buf, 1, "", true, false);
|
||||
buf->b_ml.ml_flags |= ML_EMPTY;
|
||||
|
||||
return i;
|
||||
@@ -2724,7 +2732,7 @@ size_t ml_flush_deleted_bytes(buf_T *buf, size_t *codepoints, size_t *codeunits)
|
||||
}
|
||||
|
||||
/// flush ml_line if necessary
|
||||
static void ml_flush_line(buf_T *buf)
|
||||
static void ml_flush_line(buf_T *buf, bool noalloc)
|
||||
{
|
||||
static bool entered = false;
|
||||
|
||||
@@ -2798,10 +2806,13 @@ static void ml_flush_line(buf_T *buf)
|
||||
ml_delete_int(buf, lnum, false);
|
||||
}
|
||||
}
|
||||
xfree(new_line);
|
||||
if (!noalloc) {
|
||||
xfree(new_line);
|
||||
}
|
||||
|
||||
entered = false;
|
||||
} else if (buf->b_ml.ml_flags & ML_ALLOCATED) {
|
||||
assert(!noalloc); // caller must set ML_LINE_DIRTY with noalloc, handled above
|
||||
xfree(buf->b_ml.ml_line_ptr);
|
||||
}
|
||||
|
||||
@@ -3903,7 +3914,7 @@ int ml_find_line_or_offset(buf_T *buf, linenr_T lnum, int *offp, bool no_ff)
|
||||
// was never cached to start with anyway).
|
||||
bool can_cache = (lnum != 0 && !ffdos && buf->b_ml.ml_line_lnum == lnum);
|
||||
if (lnum == 0 || buf->b_ml.ml_line_lnum < lnum || !no_ff) {
|
||||
ml_flush_line(curbuf);
|
||||
ml_flush_line(curbuf, false);
|
||||
} else if (can_cache && buf->b_ml.ml_line_offset > 0) {
|
||||
return (int)buf->b_ml.ml_line_offset;
|
||||
}
|
||||
@@ -4030,7 +4041,7 @@ void goto_byte(int cnt)
|
||||
{
|
||||
int boff = cnt;
|
||||
|
||||
ml_flush_line(curbuf); // cached line may be dirty
|
||||
ml_flush_line(curbuf, false); // cached line may be dirty
|
||||
setpcmark();
|
||||
if (boff) {
|
||||
boff--;
|
||||
|
Reference in New Issue
Block a user