refactor(extmarks): use a more efficient representation

marktree.c was originally constructed as a "generic" datatype,
to make the prototyping of its internal logic as simple as possible
and also as the usecases for various kinds of extmarks/decorations was not yet decided.
As a consequence of this, various extra indirections and allocations was
needed to use marktree to implement extmarks (ns/id pairs) and
decorations of different kinds (some which is just a single highlight
id, other an allocated list of virtual text/lines)

This change removes a lot of indirection, by making Marktree specialized
for the usecase. In particular, the namespace id and mark id is stored
directly, instead of the 64-bit global id particular to the Marktree
struct. This removes the two maps needed to convert between global and
per-ns ids.

Also, "small" decorations are stored inline, i.e. those who
doesn't refer to external heap memory anyway. That is highlights (with
priority+flags) are stored inline, while virtual text, which anyway
occurs a lot of heap allocations, do not. (previously a hack was used
to elide heap allocations for highlights with standard prio+flags)

TODO(bfredl): the functionaltest-lua CI version of gcc is having
severe issues with uint16_t bitfields, so splitting up compound
assignments and redundant casts are needed. Clean this up once we switch
to a working compiler version.
This commit is contained in:
Björn Linse
2021-10-25 21:51:29 +02:00
parent c09147aad9
commit 95ab979fde
15 changed files with 462 additions and 460 deletions

View File

@@ -85,12 +85,12 @@ const char *describe_ns(NS ns_id)
}
// Is the Namespace in use?
static bool ns_initialized(uint64_t ns)
static bool ns_initialized(uint32_t ns)
{
if (ns < 1) {
return false;
}
return ns < (uint64_t)next_namespace_id;
return ns < (uint32_t)next_namespace_id;
}
@@ -111,27 +111,27 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict)
PUT(dict, "end_col", INTEGER_OBJ(extmark.end_col));
}
if (extmark.decor) {
Decoration *decor = extmark.decor;
if (decor->hl_id) {
String name = cstr_to_string((const char *)syn_id2name(decor->hl_id));
PUT(dict, "hl_group", STRING_OBJ(name));
}
if (kv_size(decor->virt_text)) {
Array chunks = ARRAY_DICT_INIT;
for (size_t i = 0; i < decor->virt_text.size; i++) {
Array chunk = ARRAY_DICT_INIT;
VirtTextChunk *vtc = &decor->virt_text.items[i];
ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text)));
if (vtc->hl_id > 0) {
ADD(chunk,
STRING_OBJ(cstr_to_string((const char *)syn_id2name(vtc->hl_id))));
}
ADD(chunks, ARRAY_OBJ(chunk));
Decoration *decor = &extmark.decor;
if (decor->hl_id) {
String name = cstr_to_string((const char *)syn_id2name(decor->hl_id));
PUT(dict, "hl_group", STRING_OBJ(name));
}
if (kv_size(decor->virt_text)) {
Array chunks = ARRAY_DICT_INIT;
for (size_t i = 0; i < decor->virt_text.size; i++) {
Array chunk = ARRAY_DICT_INIT;
VirtTextChunk *vtc = &decor->virt_text.items[i];
ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text)));
if (vtc->hl_id > 0) {
ADD(chunk,
STRING_OBJ(cstr_to_string((const char *)syn_id2name(vtc->hl_id))));
}
PUT(dict, "virt_text", ARRAY_OBJ(chunks));
ADD(chunks, ARRAY_OBJ(chunk));
}
PUT(dict, "virt_text", ARRAY_OBJ(chunks));
}
if (decor->hl_id || kv_size(decor->virt_text)) {
PUT(dict, "priority", INTEGER_OBJ(decor->priority));
}
@@ -166,7 +166,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return rv;
}
if (!ns_initialized((uint64_t)ns_id)) {
if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
return rv;
}
@@ -191,7 +191,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
}
ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id);
ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id);
if (extmark.row < 0) {
return rv;
}
@@ -252,7 +252,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
return rv;
}
if (!ns_initialized((uint64_t)ns_id)) {
if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
return rv;
}
@@ -310,7 +310,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
}
ExtmarkInfoArray marks = extmark_get(buf, (uint64_t)ns_id, l_row, l_col,
ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col,
u_row, u_col, (int64_t)limit, reverse);
for (size_t i = 0; i < kv_size(marks); i++) {
@@ -417,14 +417,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
if (!ns_initialized((uint64_t)ns_id)) {
if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
goto error;
}
uint64_t id = 0;
uint32_t id = 0;
if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) {
id = (uint64_t)opts->id.data.integer;
id = (uint32_t)opts->id.data.integer;
} else if (HAS_KEY(opts->id)) {
api_set_error(err, kErrorTypeValidation, "id is not a positive integer");
goto error;
@@ -628,20 +628,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = 0;
}
Decoration *d = NULL;
if (ephemeral) {
d = &decor;
} else if (kv_size(decor.virt_text) || kv_size(decor.virt_lines)
|| decor.priority != DECOR_PRIORITY_BASE
|| decor.hl_eol) {
// TODO(bfredl): this is a bit sketchy. eventually we should
// have predefined decorations for both marks/ephemerals
d = xcalloc(1, sizeof(*d));
*d = decor;
} else if (decor.hl_id) {
d = decor_hl(decor.hl_id);
}
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
@@ -652,12 +638,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
d, right_gravity, end_right_gravity, kExtmarkNoUndo);
if (kv_size(decor.virt_lines)) {
redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(decor.virt_lines_above?0:1)));
}
extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2,
&decor, right_gravity, end_right_gravity, kExtmarkNoUndo);
}
return (Integer)id;
@@ -682,23 +664,23 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
if (!buf) {
return false;
}
if (!ns_initialized((uint64_t)ns_id)) {
if (!ns_initialized((uint32_t)ns_id)) {
api_set_error(err, kErrorTypeValidation, "Invalid ns_id");
return false;
}
return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id);
return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id);
}
uint64_t src2ns(Integer *src_id)
uint32_t src2ns(Integer *src_id)
{
if (*src_id == 0) {
*src_id = nvim_create_namespace((String)STRING_INIT);
}
if (*src_id < 0) {
return UINT64_MAX;
return (((uint32_t)1) << 31) - 1;
} else {
return (uint64_t)(*src_id);
return (uint32_t)(*src_id);
}
}
@@ -753,7 +735,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
col_end = MAXCOL;
}
uint64_t ns = src2ns(&ns_id);
uint32_t ns = src2ns(&ns_id);
if (!(line < buf->b_ml.ml_line_count)) {
// safety check, we can't add marks outside the range
@@ -773,10 +755,13 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In
end_line++;
}
Decoration decor = DECORATION_INIT;
decor.hl_id = hl_id;
extmark_set(buf, ns, NULL,
(int)line, (colnr_T)col_start,
end_line, (colnr_T)col_end,
decor_hl(hl_id), true, false, kExtmarkNoUndo);
&decor, true, false, kExtmarkNoUndo);
return ns_id;
}
@@ -808,7 +793,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
if (line_end < 0 || line_end > MAXLNUM) {
line_end = MAXLNUM;
}
extmark_clear(buf, (ns_id < 0 ? 0 : (uint64_t)ns_id),
extmark_clear(buf, (ns_id < 0 ? 0 : (uint32_t)ns_id),
(int)line_start, 0,
(int)line_end-1, MAXCOL);
}