]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.0165: looking up a text property type by ID is slow v9.0.0165
authorBram Moolenaar <Bram@vim.org>
Sun, 7 Aug 2022 17:20:08 +0000 (18:20 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 7 Aug 2022 17:20:08 +0000 (18:20 +0100)
Problem:    Looking up a text property type by ID is slow.
Solution:   Keep an array of property types sorted on ID.

src/structs.h
src/textprop.c
src/version.c

index 5b05c6487cf549617d51f462c584f589e8287755..fcc9c79c3ad096b2a5a92d51d817590d4c8e1de1 100644 (file)
@@ -3084,6 +3084,7 @@ struct file_buffer
 #ifdef FEAT_PROP_POPUP
     int                b_has_textprop; // TRUE when text props were added
     hashtab_T  *b_proptypes;   // text property types local to buffer
+    proptype_T **b_proparray;  // entries of b_proptypes sorted on tp_id
     garray_T   b_textprop_text; // stores text for props, index by (-id - 1)
 #endif
 
index 8670c87c33393ac18f0398331636e3beeaf8682a..679535375ca38c86364959e31455989fb04cba6c 100644 (file)
  * Text properties implementation.  See ":help text-properties".
  *
  * TODO:
- * - Adjust text property column and length when text is inserted/deleted.
- *   -> search for changed_bytes() from misc1.c
- *   -> search for mark_col_adjust()
- * - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV?
- * - Add an array for global_proptypes, to quickly lookup a prop type by ID
- * - Add an array for b_proptypes, to quickly lookup a prop type by ID
  * - Checking the text length to detect text properties is slow.  Use a flag in
  *   the index, like DB_MARKED?
  * - Also test line2byte() with many lines, so that ml_updatechunk() is taken
@@ -42,6 +36,7 @@
 
 // The global text property types.
 static hashtab_T *global_proptypes = NULL;
+static proptype_T **global_proparray = NULL;
 
 // The last used text property type ID.
 static int proptype_id = 0;
@@ -51,7 +46,7 @@ static int proptype_id = 0;
  * Returns NULL if the item can't be found.
  */
     static hashitem_T *
-find_prop_hi(char_u *name, buf_T *buf)
+find_prop_type_hi(char_u *name, buf_T *buf)
 {
     hashtab_T  *ht;
     hashitem_T *hi;
@@ -72,12 +67,12 @@ find_prop_hi(char_u *name, buf_T *buf)
 }
 
 /*
- * Like find_prop_hi() but return the property type.
+ * Like find_prop_type_hi() but return the property type.
  */
     static proptype_T *
-find_prop(char_u *name, buf_T *buf)
+find_prop_type(char_u *name, buf_T *buf)
 {
-    hashitem_T *hi = find_prop_hi(name, buf);
+    hashitem_T *hi = find_prop_type_hi(name, buf);
 
     if (hi == NULL)
        return NULL;
@@ -91,7 +86,7 @@ find_prop(char_u *name, buf_T *buf)
     int
 find_prop_type_id(char_u *name, buf_T *buf)
 {
-    proptype_T *pt = find_prop(name, buf);
+    proptype_T *pt = find_prop_type(name, buf);
 
     if (pt == NULL)
        return 0;
@@ -106,10 +101,10 @@ find_prop_type_id(char_u *name, buf_T *buf)
     static proptype_T *
 lookup_prop_type(char_u *name, buf_T *buf)
 {
-    proptype_T *type = find_prop(name, buf);
+    proptype_T *type = find_prop_type(name, buf);
 
     if (type == NULL)
-       type = find_prop(name, NULL);
+       type = find_prop_type(name, NULL);
     if (type == NULL)
        semsg(_(e_type_not_exist), name);
     return type;
@@ -730,29 +725,62 @@ add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count)
     curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
 }
 
+/*
+ * Function passed to qsort() for sorting proptype_T on pt_id.
+ */
+    static int
+compare_pt(const void *s1, const void *s2)
+{
+    proptype_T *tp1 = *(proptype_T **)s1;
+    proptype_T *tp2 = *(proptype_T **)s2;
+
+    return tp1->pt_id == tp2->pt_id ? 0 : tp1->pt_id < tp2->pt_id ? -1 : 1;
+}
+
     static proptype_T *
-find_type_by_id(hashtab_T *ht, int id)
+find_type_by_id(hashtab_T *ht, proptype_T ***array, int id)
 {
-    long       todo;
-    hashitem_T *hi;
+    int low = 0;
+    int high;
 
     if (ht == NULL)
        return NULL;
 
-    // TODO: Make this faster by keeping a list of types sorted on ID and use
-    // a binary search.
-
-    todo = (long)ht->ht_used;
-    for (hi = ht->ht_array; todo > 0; ++hi)
+    // Make the loopup faster by creating an array with pointers to
+    // hashtable entries, sorted on pt_id.
+    if (*array == NULL)
     {
-       if (!HASHITEM_EMPTY(hi))
-       {
-           proptype_T *prop = HI2PT(hi);
+       long        todo;
+       hashitem_T  *hi;
+       int         i = 0;
 
-           if (prop->pt_id == id)
-               return prop;
-           --todo;
+       *array = ALLOC_MULT(proptype_T *, ht->ht_used);
+       if (*array == NULL)
+           return NULL;
+       todo = (long)ht->ht_used;
+       for (hi = ht->ht_array; todo > 0; ++hi)
+       {
+           if (!HASHITEM_EMPTY(hi))
+           {
+               (*array)[i++] = HI2PT(hi);
+               --todo;
+           }
        }
+       qsort((void *)*array, ht->ht_used, sizeof(proptype_T *), compare_pt);
+    }
+
+    // binary search in the sorted array
+    high = ht->ht_used;
+    while (high > low)
+    {
+       int m = (high + low) / 2;
+
+       if ((*array)[m]->pt_id == id)
+           return (*array)[m];
+       if ((*array)[m]->pt_id > id)
+           high = m;
+       else
+           low = m + 1;
     }
     return NULL;
 }
@@ -772,10 +800,11 @@ prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf)
     dict_add_number(dict, "start", !(prop->tp_flags & TP_FLAG_CONT_PREV));
     dict_add_number(dict, "end", !(prop->tp_flags & TP_FLAG_CONT_NEXT));
 
-    pt = find_type_by_id(buf->b_proptypes, prop->tp_type);
+    pt = find_type_by_id(buf->b_proptypes, &buf->b_proparray, prop->tp_type);
     if (pt == NULL)
     {
-       pt = find_type_by_id(global_proptypes, prop->tp_type);
+       pt = find_type_by_id(global_proptypes, &global_proparray,
+                                                               prop->tp_type);
        buflocal = FALSE;
     }
     if (pt != NULL)
@@ -796,9 +825,9 @@ text_prop_type_by_id(buf_T *buf, int id)
 {
     proptype_T *type;
 
-    type = find_type_by_id(buf->b_proptypes, id);
+    type = find_type_by_id(buf->b_proptypes, &buf->b_proparray, id);
     if (type == NULL)
-       type = find_type_by_id(global_proptypes, id);
+       type = find_type_by_id(global_proptypes, &global_proparray, id);
     return type;
 }
 
@@ -1523,7 +1552,7 @@ prop_type_set(typval_T *argvars, int add)
        return;
     dict = argvars[1].vval.v_dict;
 
-    prop = find_prop(name, buf);
+    prop = find_prop_type(name, buf);
     if (add)
     {
        hashtab_T **htp;
@@ -1539,7 +1568,16 @@ prop_type_set(typval_T *argvars, int add)
        STRCPY(prop->pt_name, name);
        prop->pt_id = ++proptype_id;
        prop->pt_flags = PT_FLAG_COMBINE;
-       htp = buf == NULL ? &global_proptypes : &buf->b_proptypes;
+       if (buf == NULL)
+       {
+           htp = &global_proptypes;
+           VIM_CLEAR(global_proparray);
+       }
+       else
+       {
+           htp = &buf->b_proptypes;
+           VIM_CLEAR(buf->b_proparray);
+       }
        if (*htp == NULL)
        {
            *htp = ALLOC_ONE(hashtab_T);
@@ -1669,16 +1707,22 @@ f_prop_type_delete(typval_T *argvars, typval_T *rettv UNUSED)
            return;
     }
 
-    hi = find_prop_hi(name, buf);
+    hi = find_prop_type_hi(name, buf);
     if (hi != NULL)
     {
        hashtab_T       *ht;
        proptype_T      *prop = HI2PT(hi);
 
        if (buf == NULL)
+       {
            ht = global_proptypes;
+           VIM_CLEAR(global_proparray);
+       }
        else
+       {
            ht = buf->b_proptypes;
+           VIM_CLEAR(buf->b_proparray);
+       }
        hash_remove(ht, hi);
        vim_free(prop);
     }
@@ -1714,7 +1758,7 @@ f_prop_type_get(typval_T *argvars, typval_T *rettv)
                return;
        }
 
-       prop = find_prop(name, buf);
+       prop = find_prop_type(name, buf);
        if (prop != NULL)
        {
            dict_T *d = rettv->vval.v_dict;
@@ -1818,6 +1862,7 @@ clear_global_prop_types(void)
 {
     clear_ht_prop_types(global_proptypes);
     global_proptypes = NULL;
+    VIM_CLEAR(global_proparray);
 }
 #endif
 
@@ -1829,6 +1874,7 @@ clear_buf_prop_types(buf_T *buf)
 {
     clear_ht_prop_types(buf->b_proptypes);
     buf->b_proptypes = NULL;
+    VIM_CLEAR(buf->b_proparray);
 }
 
 // Struct used to return two values from adjust_prop().
index be59f1be2e8ade8b241e40aba605a4edb9d3938a..bccf787ef800f8dd7981e8de974b57fe0691564f 100644 (file)
@@ -735,6 +735,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    165,
 /**/
     164,
 /**/