comparison src/textprop.c @ 29649:a3a966b8b7a7 v9.0.0165

patch 9.0.0165: looking up a text property type by ID is slow Commit: https://github.com/vim/vim/commit/e44336b00a6c51bef904450a8012e4982e38ba2d Author: Bram Moolenaar <Bram@vim.org> Date: Sun Aug 7 18:20:08 2022 +0100 patch 9.0.0165: looking up a text property type by ID is slow Problem: Looking up a text property type by ID is slow. Solution: Keep an array of property types sorted on ID.
author Bram Moolenaar <Bram@vim.org>
date Sun, 07 Aug 2022 19:30:03 +0200
parents dd96f3d8ed85
children aa83cf7bef1c
comparison
equal deleted inserted replaced
29648:49cdbb7997e9 29649:a3a966b8b7a7
9 9
10 /* 10 /*
11 * Text properties implementation. See ":help text-properties". 11 * Text properties implementation. See ":help text-properties".
12 * 12 *
13 * TODO: 13 * TODO:
14 * - Adjust text property column and length when text is inserted/deleted.
15 * -> search for changed_bytes() from misc1.c
16 * -> search for mark_col_adjust()
17 * - Perhaps we only need TP_FLAG_CONT_NEXT and can drop TP_FLAG_CONT_PREV?
18 * - Add an array for global_proptypes, to quickly lookup a prop type by ID
19 * - Add an array for b_proptypes, to quickly lookup a prop type by ID
20 * - Checking the text length to detect text properties is slow. Use a flag in 14 * - Checking the text length to detect text properties is slow. Use a flag in
21 * the index, like DB_MARKED? 15 * the index, like DB_MARKED?
22 * - Also test line2byte() with many lines, so that ml_updatechunk() is taken 16 * - Also test line2byte() with many lines, so that ml_updatechunk() is taken
23 * into account. 17 * into account.
24 * - Perhaps have a window-local option to disable highlighting from text 18 * - Perhaps have a window-local option to disable highlighting from text
40 #define HIKEY2PT(p) ((proptype_T *)((p) - offsetof(proptype_T, pt_name))) 34 #define HIKEY2PT(p) ((proptype_T *)((p) - offsetof(proptype_T, pt_name)))
41 #define HI2PT(hi) HIKEY2PT((hi)->hi_key) 35 #define HI2PT(hi) HIKEY2PT((hi)->hi_key)
42 36
43 // The global text property types. 37 // The global text property types.
44 static hashtab_T *global_proptypes = NULL; 38 static hashtab_T *global_proptypes = NULL;
39 static proptype_T **global_proparray = NULL;
45 40
46 // The last used text property type ID. 41 // The last used text property type ID.
47 static int proptype_id = 0; 42 static int proptype_id = 0;
48 43
49 /* 44 /*
50 * Find a property type by name, return the hashitem. 45 * Find a property type by name, return the hashitem.
51 * Returns NULL if the item can't be found. 46 * Returns NULL if the item can't be found.
52 */ 47 */
53 static hashitem_T * 48 static hashitem_T *
54 find_prop_hi(char_u *name, buf_T *buf) 49 find_prop_type_hi(char_u *name, buf_T *buf)
55 { 50 {
56 hashtab_T *ht; 51 hashtab_T *ht;
57 hashitem_T *hi; 52 hashitem_T *hi;
58 53
59 if (*name == NUL) 54 if (*name == NUL)
70 return NULL; 65 return NULL;
71 return hi; 66 return hi;
72 } 67 }
73 68
74 /* 69 /*
75 * Like find_prop_hi() but return the property type. 70 * Like find_prop_type_hi() but return the property type.
76 */ 71 */
77 static proptype_T * 72 static proptype_T *
78 find_prop(char_u *name, buf_T *buf) 73 find_prop_type(char_u *name, buf_T *buf)
79 { 74 {
80 hashitem_T *hi = find_prop_hi(name, buf); 75 hashitem_T *hi = find_prop_type_hi(name, buf);
81 76
82 if (hi == NULL) 77 if (hi == NULL)
83 return NULL; 78 return NULL;
84 return HI2PT(hi); 79 return HI2PT(hi);
85 } 80 }
89 * When not found return zero. 84 * When not found return zero.
90 */ 85 */
91 int 86 int
92 find_prop_type_id(char_u *name, buf_T *buf) 87 find_prop_type_id(char_u *name, buf_T *buf)
93 { 88 {
94 proptype_T *pt = find_prop(name, buf); 89 proptype_T *pt = find_prop_type(name, buf);
95 90
96 if (pt == NULL) 91 if (pt == NULL)
97 return 0; 92 return 0;
98 return pt->pt_id; 93 return pt->pt_id;
99 } 94 }
104 * When not found gives an error message and returns NULL. 99 * When not found gives an error message and returns NULL.
105 */ 100 */
106 static proptype_T * 101 static proptype_T *
107 lookup_prop_type(char_u *name, buf_T *buf) 102 lookup_prop_type(char_u *name, buf_T *buf)
108 { 103 {
109 proptype_T *type = find_prop(name, buf); 104 proptype_T *type = find_prop_type(name, buf);
110 105
111 if (type == NULL) 106 if (type == NULL)
112 type = find_prop(name, NULL); 107 type = find_prop_type(name, NULL);
113 if (type == NULL) 108 if (type == NULL)
114 semsg(_(e_type_not_exist), name); 109 semsg(_(e_type_not_exist), name);
115 return type; 110 return type;
116 } 111 }
117 112
728 curbuf->b_ml.ml_line_ptr = newtext; 723 curbuf->b_ml.ml_line_ptr = newtext;
729 curbuf->b_ml.ml_line_len += proplen; 724 curbuf->b_ml.ml_line_len += proplen;
730 curbuf->b_ml.ml_flags |= ML_LINE_DIRTY; 725 curbuf->b_ml.ml_flags |= ML_LINE_DIRTY;
731 } 726 }
732 727
728 /*
729 * Function passed to qsort() for sorting proptype_T on pt_id.
730 */
731 static int
732 compare_pt(const void *s1, const void *s2)
733 {
734 proptype_T *tp1 = *(proptype_T **)s1;
735 proptype_T *tp2 = *(proptype_T **)s2;
736
737 return tp1->pt_id == tp2->pt_id ? 0 : tp1->pt_id < tp2->pt_id ? -1 : 1;
738 }
739
733 static proptype_T * 740 static proptype_T *
734 find_type_by_id(hashtab_T *ht, int id) 741 find_type_by_id(hashtab_T *ht, proptype_T ***array, int id)
735 { 742 {
736 long todo; 743 int low = 0;
737 hashitem_T *hi; 744 int high;
738 745
739 if (ht == NULL) 746 if (ht == NULL)
740 return NULL; 747 return NULL;
741 748
742 // TODO: Make this faster by keeping a list of types sorted on ID and use 749 // Make the loopup faster by creating an array with pointers to
743 // a binary search. 750 // hashtable entries, sorted on pt_id.
744 751 if (*array == NULL)
745 todo = (long)ht->ht_used; 752 {
746 for (hi = ht->ht_array; todo > 0; ++hi) 753 long todo;
747 { 754 hashitem_T *hi;
748 if (!HASHITEM_EMPTY(hi)) 755 int i = 0;
749 { 756
750 proptype_T *prop = HI2PT(hi); 757 *array = ALLOC_MULT(proptype_T *, ht->ht_used);
751 758 if (*array == NULL)
752 if (prop->pt_id == id) 759 return NULL;
753 return prop; 760 todo = (long)ht->ht_used;
754 --todo; 761 for (hi = ht->ht_array; todo > 0; ++hi)
755 } 762 {
763 if (!HASHITEM_EMPTY(hi))
764 {
765 (*array)[i++] = HI2PT(hi);
766 --todo;
767 }
768 }
769 qsort((void *)*array, ht->ht_used, sizeof(proptype_T *), compare_pt);
770 }
771
772 // binary search in the sorted array
773 high = ht->ht_used;
774 while (high > low)
775 {
776 int m = (high + low) / 2;
777
778 if ((*array)[m]->pt_id == id)
779 return (*array)[m];
780 if ((*array)[m]->pt_id > id)
781 high = m;
782 else
783 low = m + 1;
756 } 784 }
757 return NULL; 785 return NULL;
758 } 786 }
759 787
760 /* 788 /*
770 dict_add_number(dict, "length", prop->tp_len); 798 dict_add_number(dict, "length", prop->tp_len);
771 dict_add_number(dict, "id", prop->tp_id); 799 dict_add_number(dict, "id", prop->tp_id);
772 dict_add_number(dict, "start", !(prop->tp_flags & TP_FLAG_CONT_PREV)); 800 dict_add_number(dict, "start", !(prop->tp_flags & TP_FLAG_CONT_PREV));
773 dict_add_number(dict, "end", !(prop->tp_flags & TP_FLAG_CONT_NEXT)); 801 dict_add_number(dict, "end", !(prop->tp_flags & TP_FLAG_CONT_NEXT));
774 802
775 pt = find_type_by_id(buf->b_proptypes, prop->tp_type); 803 pt = find_type_by_id(buf->b_proptypes, &buf->b_proparray, prop->tp_type);
776 if (pt == NULL) 804 if (pt == NULL)
777 { 805 {
778 pt = find_type_by_id(global_proptypes, prop->tp_type); 806 pt = find_type_by_id(global_proptypes, &global_proparray,
807 prop->tp_type);
779 buflocal = FALSE; 808 buflocal = FALSE;
780 } 809 }
781 if (pt != NULL) 810 if (pt != NULL)
782 dict_add_string(dict, "type", pt->pt_name); 811 dict_add_string(dict, "type", pt->pt_name);
783 812
794 proptype_T * 823 proptype_T *
795 text_prop_type_by_id(buf_T *buf, int id) 824 text_prop_type_by_id(buf_T *buf, int id)
796 { 825 {
797 proptype_T *type; 826 proptype_T *type;
798 827
799 type = find_type_by_id(buf->b_proptypes, id); 828 type = find_type_by_id(buf->b_proptypes, &buf->b_proparray, id);
800 if (type == NULL) 829 if (type == NULL)
801 type = find_type_by_id(global_proptypes, id); 830 type = find_type_by_id(global_proptypes, &global_proparray, id);
802 return type; 831 return type;
803 } 832 }
804 833
805 /* 834 /*
806 * prop_clear({lnum} [, {lnum_end} [, {bufnr}]]) 835 * prop_clear({lnum} [, {lnum_end} [, {bufnr}]])
1521 1550
1522 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) 1551 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
1523 return; 1552 return;
1524 dict = argvars[1].vval.v_dict; 1553 dict = argvars[1].vval.v_dict;
1525 1554
1526 prop = find_prop(name, buf); 1555 prop = find_prop_type(name, buf);
1527 if (add) 1556 if (add)
1528 { 1557 {
1529 hashtab_T **htp; 1558 hashtab_T **htp;
1530 1559
1531 if (prop != NULL) 1560 if (prop != NULL)
1537 if (prop == NULL) 1566 if (prop == NULL)
1538 return; 1567 return;
1539 STRCPY(prop->pt_name, name); 1568 STRCPY(prop->pt_name, name);
1540 prop->pt_id = ++proptype_id; 1569 prop->pt_id = ++proptype_id;
1541 prop->pt_flags = PT_FLAG_COMBINE; 1570 prop->pt_flags = PT_FLAG_COMBINE;
1542 htp = buf == NULL ? &global_proptypes : &buf->b_proptypes; 1571 if (buf == NULL)
1572 {
1573 htp = &global_proptypes;
1574 VIM_CLEAR(global_proparray);
1575 }
1576 else
1577 {
1578 htp = &buf->b_proptypes;
1579 VIM_CLEAR(buf->b_proparray);
1580 }
1543 if (*htp == NULL) 1581 if (*htp == NULL)
1544 { 1582 {
1545 *htp = ALLOC_ONE(hashtab_T); 1583 *htp = ALLOC_ONE(hashtab_T);
1546 if (*htp == NULL) 1584 if (*htp == NULL)
1547 { 1585 {
1667 { 1705 {
1668 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) 1706 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
1669 return; 1707 return;
1670 } 1708 }
1671 1709
1672 hi = find_prop_hi(name, buf); 1710 hi = find_prop_type_hi(name, buf);
1673 if (hi != NULL) 1711 if (hi != NULL)
1674 { 1712 {
1675 hashtab_T *ht; 1713 hashtab_T *ht;
1676 proptype_T *prop = HI2PT(hi); 1714 proptype_T *prop = HI2PT(hi);
1677 1715
1678 if (buf == NULL) 1716 if (buf == NULL)
1717 {
1679 ht = global_proptypes; 1718 ht = global_proptypes;
1719 VIM_CLEAR(global_proparray);
1720 }
1680 else 1721 else
1722 {
1681 ht = buf->b_proptypes; 1723 ht = buf->b_proptypes;
1724 VIM_CLEAR(buf->b_proparray);
1725 }
1682 hash_remove(ht, hi); 1726 hash_remove(ht, hi);
1683 vim_free(prop); 1727 vim_free(prop);
1684 } 1728 }
1685 } 1729 }
1686 1730
1712 { 1756 {
1713 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL) 1757 if (get_bufnr_from_arg(&argvars[1], &buf) == FAIL)
1714 return; 1758 return;
1715 } 1759 }
1716 1760
1717 prop = find_prop(name, buf); 1761 prop = find_prop_type(name, buf);
1718 if (prop != NULL) 1762 if (prop != NULL)
1719 { 1763 {
1720 dict_T *d = rettv->vval.v_dict; 1764 dict_T *d = rettv->vval.v_dict;
1721 1765
1722 if (prop->pt_hl_id > 0) 1766 if (prop->pt_hl_id > 0)
1816 void 1860 void
1817 clear_global_prop_types(void) 1861 clear_global_prop_types(void)
1818 { 1862 {
1819 clear_ht_prop_types(global_proptypes); 1863 clear_ht_prop_types(global_proptypes);
1820 global_proptypes = NULL; 1864 global_proptypes = NULL;
1865 VIM_CLEAR(global_proparray);
1821 } 1866 }
1822 #endif 1867 #endif
1823 1868
1824 /* 1869 /*
1825 * Free all property types for "buf". 1870 * Free all property types for "buf".
1827 void 1872 void
1828 clear_buf_prop_types(buf_T *buf) 1873 clear_buf_prop_types(buf_T *buf)
1829 { 1874 {
1830 clear_ht_prop_types(buf->b_proptypes); 1875 clear_ht_prop_types(buf->b_proptypes);
1831 buf->b_proptypes = NULL; 1876 buf->b_proptypes = NULL;
1877 VIM_CLEAR(buf->b_proparray);
1832 } 1878 }
1833 1879
1834 // Struct used to return two values from adjust_prop(). 1880 // Struct used to return two values from adjust_prop().
1835 typedef struct 1881 typedef struct
1836 { 1882 {