neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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 }