garray.c (5116B)
1 /// @file garray.c 2 /// 3 /// Functions for handling growing arrays. 4 5 #include <stdint.h> 6 #include <string.h> 7 8 #include "nvim/garray.h" 9 #include "nvim/log.h" 10 #include "nvim/macros_defs.h" 11 #include "nvim/memory.h" 12 #include "nvim/path.h" 13 #include "nvim/strings.h" 14 15 #include "garray.c.generated.h" // IWYU pragma: keep 16 17 /// Clear an allocated growing array. 18 void ga_clear(garray_T *gap) 19 { 20 xfree(gap->ga_data); 21 22 // Initialize growing array without resetting itemsize or growsize 23 gap->ga_data = NULL; 24 gap->ga_maxlen = 0; 25 gap->ga_len = 0; 26 } 27 28 /// Clear a growing array that contains a list of strings. 29 /// 30 /// @param gap 31 void ga_clear_strings(garray_T *gap) 32 { 33 GA_DEEP_CLEAR_PTR(gap); 34 } 35 36 /// Initialize a growing array. 37 /// 38 /// @param gap 39 /// @param itemsize 40 /// @param growsize 41 void ga_init(garray_T *gap, int itemsize, int growsize) 42 { 43 gap->ga_data = NULL; 44 gap->ga_maxlen = 0; 45 gap->ga_len = 0; 46 gap->ga_itemsize = itemsize; 47 ga_set_growsize(gap, growsize); 48 } 49 50 /// A setter for the growsize that guarantees it will be at least 1. 51 /// 52 /// @param gap 53 /// @param growsize 54 void ga_set_growsize(garray_T *gap, int growsize) 55 { 56 if (growsize < 1) { 57 WLOG("trying to set an invalid ga_growsize: %d", growsize); 58 gap->ga_growsize = 1; 59 } else { 60 gap->ga_growsize = growsize; 61 } 62 } 63 64 /// Make room in growing array "gap" for at least "n" items. 65 /// 66 /// @param gap 67 /// @param n 68 void ga_grow(garray_T *gap, int n) 69 { 70 if (gap->ga_maxlen - gap->ga_len >= n) { 71 // the garray still has enough space, do nothing 72 return; 73 } 74 75 if (gap->ga_growsize < 1) { 76 WLOG("ga_growsize(%d) is less than 1", gap->ga_growsize); 77 } 78 79 // the garray grows by at least growsize 80 n = MAX(n, gap->ga_growsize); 81 82 // A linear growth is very inefficient when the array grows big. This 83 // is a compromise between allocating memory that won't be used and too 84 // many copy operations. A factor of 1.5 seems reasonable. 85 n = MAX(n, gap->ga_len / 2); 86 87 int new_maxlen = gap->ga_len + n; 88 89 size_t new_size = (size_t)gap->ga_itemsize * (size_t)new_maxlen; 90 size_t old_size = (size_t)gap->ga_itemsize * (size_t)gap->ga_maxlen; 91 92 // reallocate and clear the new memory 93 char *pp = xrealloc(gap->ga_data, new_size); 94 memset(pp + old_size, 0, new_size - old_size); 95 96 gap->ga_maxlen = new_maxlen; 97 gap->ga_data = pp; 98 } 99 100 /// Sort "gap" and remove duplicate entries. "gap" is expected to contain a 101 /// list of file names in allocated memory. 102 /// 103 /// @param gap 104 void ga_remove_duplicate_strings(garray_T *gap) 105 { 106 char **fnames = gap->ga_data; 107 108 // sort the growing array, which puts duplicates next to each other 109 sort_strings(fnames, gap->ga_len); 110 111 // loop over the growing array in reverse 112 for (int i = gap->ga_len - 1; i > 0; i--) { 113 if (path_fnamecmp(fnames[i - 1], fnames[i]) == 0) { 114 xfree(fnames[i]); 115 116 // close the gap (move all strings one slot lower) 117 for (int j = i + 1; j < gap->ga_len; j++) { 118 fnames[j - 1] = fnames[j]; 119 } 120 121 gap->ga_len--; 122 } 123 } 124 } 125 126 /// For a growing array that contains a list of strings: concatenate all the 127 /// strings with "sep" as separator. 128 /// 129 /// @param gap 130 /// @param sep 131 /// 132 /// @returns the concatenated strings 133 char *ga_concat_strings(const garray_T *gap, const char *sep) 134 FUNC_ATTR_NONNULL_RET 135 { 136 const size_t nelem = (size_t)gap->ga_len; 137 const char **strings = gap->ga_data; 138 139 if (nelem == 0) { 140 return xstrdup(""); 141 } 142 143 size_t len = 0; 144 for (size_t i = 0; i < nelem; i++) { 145 len += strlen(strings[i]); 146 } 147 148 // add some space for the (num - 1) separators 149 len += (nelem - 1) * strlen(sep); 150 char *const ret = xmallocz(len); 151 152 char *s = ret; 153 for (size_t i = 0; i < nelem - 1; i++) { 154 s = xstpcpy(s, strings[i]); 155 s = xstpcpy(s, sep); 156 } 157 strcpy(s, strings[nelem - 1]); // NOLINT(runtime/printf) 158 159 return ret; 160 } 161 162 /// Concatenate a string to a growarray which contains characters. 163 /// When "s" is NULL does not do anything. 164 /// 165 /// WARNING: 166 /// - Does NOT copy the NUL at the end! 167 /// - The parameter may not overlap with the growing array 168 /// 169 /// @param gap 170 /// @param s 171 void ga_concat(garray_T *gap, const char *restrict s) 172 { 173 if (s == NULL) { 174 return; 175 } 176 177 ga_concat_len(gap, s, strlen(s)); 178 } 179 180 /// Concatenate a string to a growarray which contains characters 181 /// 182 /// @param[out] gap Growarray to modify. 183 /// @param[in] s String to concatenate. 184 /// @param[in] len String length. 185 void ga_concat_len(garray_T *const gap, const char *restrict s, const size_t len) 186 FUNC_ATTR_NONNULL_ALL 187 { 188 if (len == 0) { 189 return; 190 } 191 ga_grow(gap, (int)len); 192 char *data = gap->ga_data; 193 memcpy(data + gap->ga_len, s, len); 194 gap->ga_len += (int)len; 195 } 196 197 /// Append one byte to a growarray which contains bytes. 198 /// 199 /// @param gap 200 /// @param c 201 void ga_append(garray_T *gap, uint8_t c) 202 { 203 GA_APPEND(uint8_t, gap, c); 204 } 205 206 void *ga_append_via_ptr(garray_T *gap, size_t item_size) 207 { 208 if ((int)item_size != gap->ga_itemsize) { 209 WLOG("wrong item size (%zu), should be %d", item_size, gap->ga_itemsize); 210 } 211 ga_grow(gap, 1); 212 return ((char *)gap->ga_data) + (item_size * (size_t)gap->ga_len++); 213 }