Text: re-allocate exact lengths for undo

Undo sometimes reserved too much space in the buffer,
now assert when this happens and allocate the exact size needed.

Note prepares for moving text editor undo out of the text block (D3113)
which will split the undo buffer into a list of undo steps.
This commit is contained in:
Campbell Barton 2018-03-24 14:18:58 +01:00
parent 3f9d5ea0ec
commit 977a4e7f5d
1 changed files with 84 additions and 54 deletions

View File

@ -191,8 +191,8 @@ int txt_get_undostate(void)
static void init_undo_text(Text *text)
{
text->undo_pos = -1;
text->undo_len = TXT_INIT_UNDO;
text->undo_buf = MEM_mallocN(text->undo_len, "undo buf");
text->undo_len = 0;
text->undo_buf = NULL;
}
/**
@ -1466,25 +1466,40 @@ void txt_insert_buf(Text *text, const char *in_buffer)
static bool max_undo_test(Text *text, int x)
{
while (text->undo_pos + x >= text->undo_len) {
if (text->undo_len * 2 > TXT_MAX_UNDO) {
/* XXX error("Undo limit reached, buffer cleared\n"); */
MEM_freeN(text->undo_buf);
init_undo_text(text);
return false;
}
else {
void *tmp = text->undo_buf;
text->undo_buf = MEM_callocN(text->undo_len * 2, "undo buf");
memcpy(text->undo_buf, tmp, text->undo_len);
text->undo_len *= 2;
MEM_freeN(tmp);
}
}
/* Normally over-allocating is preferred,
* however in this case the buffer is small enough and re-allocation
* fast enough for each undo step that it's not a problem to allocate each time.
* This also saves on some memory when we have many text buffers
* that would have an empty undo memory allocated.
*/
/* Add one for the null terminator. */
text->undo_len = text->undo_pos + x + 1;
if (text->undo_len > TXT_MAX_UNDO) {
/* XXX error("Undo limit reached, buffer cleared\n"); */
MEM_freeN(text->undo_buf);
init_undo_text(text);
return false;
}
else {
/* Small reallocations on each undo step is fine. */
text->undo_buf = MEM_recallocN(text->undo_buf, text->undo_len);
}
return true;
}
static void txt_undo_end(Text *text)
{
int undo_pos_end = text->undo_pos + 1;
BLI_assert(undo_pos_end + 1 == text->undo_len);
text->undo_buf[undo_pos_end] = '\0';
}
/* Call once undo is done. */
#ifndef NDEBUG
#endif
#if 0 /* UNUSED */
static void dump_buffer(Text *text)
{
@ -1672,21 +1687,21 @@ static void txt_undo_store_uint32(char *undo_buf, int *undo_pos, unsigned int va
(*undo_pos)++;
}
/* store the cur cursor to the undo buffer */
/* store the cur cursor to the undo buffer (6 bytes)*/
static void txt_undo_store_cur(Text *text)
{
txt_undo_store_uint16(text->undo_buf, &text->undo_pos, text->curc);
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, txt_get_span(text->lines.first, text->curl));
}
/* store the sel cursor to the undo buffer */
/* store the sel cursor to the undo buffer (6 bytes) */
static void txt_undo_store_sel(Text *text)
{
txt_undo_store_uint16(text->undo_buf, &text->undo_pos, text->selc);
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, txt_get_span(text->lines.first, text->sell));
}
/* store both cursors to the undo buffer */
/* store both cursors to the undo buffer (12 bytes) */
static void txt_undo_store_cursors(Text *text)
{
txt_undo_store_cur(text);
@ -1697,42 +1712,46 @@ static void txt_undo_store_cursors(Text *text)
static void txt_undo_add_blockop(Text *text, int op, const char *buf)
{
unsigned int length = strlen(buf);
if (!max_undo_test(text, length + 11 + 12))
return;
if (!max_undo_test(text, 2 + 12 + 4 + length + 4 + 1)) {
return;
}
/* 2 bytes */
text->undo_pos++;
text->undo_buf[text->undo_pos] = op;
text->undo_pos++;
/* 12 bytes */
txt_undo_store_cursors(text);
/* 4 bytes */
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
/* 'length' bytes */
strncpy(text->undo_buf + text->undo_pos, buf, length);
text->undo_pos += length;
/* 4 bytes */
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
/* 1 byte */
text->undo_buf[text->undo_pos] = op;
text->undo_buf[text->undo_pos + 1] = 0;
txt_undo_end(text);
}
/* store a regular operator */
void txt_undo_add_op(Text *text, int op)
{
if (!max_undo_test(text, 15))
if (!max_undo_test(text, 2 + 12 + 1)) {
return;
}
/* 2 bytes */
text->undo_pos++;
text->undo_buf[text->undo_pos] = op;
text->undo_pos++;
/* 12 bytes */
txt_undo_store_cursors(text);
/* 1 byte */
text->undo_buf[text->undo_pos] = op;
text->undo_buf[text->undo_pos + 1] = 0;
txt_undo_end(text);
}
/* store an operator for a single character */
@ -1741,35 +1760,41 @@ static void txt_undo_add_charop(Text *text, int op_start, unsigned int c)
char utf8[BLI_UTF8_MAX];
size_t i, utf8_size = BLI_str_utf8_from_unicode(c, utf8);
if (!max_undo_test(text, 3 + utf8_size + 12))
return;
text->undo_pos++;
if (utf8_size < 4) {
if (utf8_size < 4 && 0) {
if (!max_undo_test(text, 2 + 6 + utf8_size + 1)) {
return;
}
/* 2 bytes */
text->undo_pos++;
text->undo_buf[text->undo_pos] = op_start + utf8_size - 1;
text->undo_pos++;
/* 6 bytes */
txt_undo_store_cur(text);
/* 'utf8_size' bytes */
for (i = 0; i < utf8_size; i++) {
text->undo_buf[text->undo_pos] = utf8[i];
text->undo_pos++;
}
/* 1 byte */
text->undo_buf[text->undo_pos] = op_start + utf8_size - 1;
}
else {
if (!max_undo_test(text, 2 + 6 + 4 + 1)) {
return;
}
/* 2 bytes */
text->undo_pos++;
text->undo_buf[text->undo_pos] = op_start + 3;
text->undo_pos++;
/* 6 bytes */
txt_undo_store_cur(text);
/* 4 bytes */
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, c);
/* 1 byte */
text->undo_buf[text->undo_pos] = op_start + 3;
}
text->undo_buf[text->undo_pos + 1] = 0;
txt_undo_end(text);
}
/* extends Link */
@ -1791,30 +1816,35 @@ static void txt_undo_add_unprefix_op(
BLI_assert(BLI_listbase_count(line_index_mask) == line_index_mask_len);
/* OP byte + UInt32 count + counted UInt32 line numbers + UInt32 count + 12-bytes selection + OP byte */
if (!max_undo_test(text, 1 + 4 + (line_index_mask_len * 4) + 4 + 12 + 1)) {
if (!max_undo_test(text, 2 + 4 + (line_index_mask_len * 4) + 4 + 12 + 1)) {
return;
}
/* Opening buffer sequence with OP */
/* 2 bytes */
text->undo_pos++;
text->undo_buf[text->undo_pos] = undo_op;
text->undo_pos++;
/* Adding number of line numbers to read */
/* Adding number of line numbers to read
* 4 bytes */
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, line_index_mask_len);
/* Adding linenumbers of lines that shall not be indented if undoing */
/* Adding linenumbers of lines that shall not be indented if undoing.
* 'line_index_mask_len * 4' bytes */
for (idata = line_index_mask->first; idata; idata = idata->next) {
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, idata->value);
}
/* Adding number of line numbers to read again */
/* Adding number of line numbers to read again.
* 4 bytes */
txt_undo_store_uint32(text->undo_buf, &text->undo_pos, line_index_mask_len);
/* Adding current selection */
/* Adding current selection.
* 12 bytes */
txt_undo_store_cursors(text);
/* Closing with OP (same as above) */
/* Closing with OP (same as above).
* 1 byte */
text->undo_buf[text->undo_pos] = undo_op;
/* Marking as last undo operation */
text->undo_buf[text->undo_pos + 1] = 0;
txt_undo_end(text);
}
static unsigned short txt_undo_read_uint16(const char *undo_buf, int *undo_pos)