// This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com /// @file garray.c /// /// Functions for handling growing arrays. #include #include "nvim/garray.h" #include "nvim/log.h" #include "nvim/memory.h" #include "nvim/path.h" #include "nvim/strings.h" #include "nvim/types.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "garray.c.generated.h" // IWYU pragma: export #endif /// Clear an allocated growing array. void ga_clear(garray_T *gap) { xfree(gap->ga_data); // Initialize growing array without resetting itemsize or growsize gap->ga_data = NULL; gap->ga_maxlen = 0; gap->ga_len = 0; } /// Clear a growing array that contains a list of strings. /// /// @param gap void ga_clear_strings(garray_T *gap) { GA_DEEP_CLEAR_PTR(gap); } /// Initialize a growing array. /// /// @param gap /// @param itemsize /// @param growsize void ga_init(garray_T *gap, int itemsize, int growsize) { gap->ga_data = NULL; gap->ga_maxlen = 0; gap->ga_len = 0; gap->ga_itemsize = itemsize; ga_set_growsize(gap, growsize); } /// A setter for the growsize that guarantees it will be at least 1. /// /// @param gap /// @param growsize void ga_set_growsize(garray_T *gap, int growsize) { if (growsize < 1) { WLOG("trying to set an invalid ga_growsize: %d", growsize); gap->ga_growsize = 1; } else { gap->ga_growsize = growsize; } } /// Make room in growing array "gap" for at least "n" items. /// /// @param gap /// @param n void ga_grow(garray_T *gap, int n) { if (gap->ga_maxlen - gap->ga_len >= n) { // the garray still has enough space, do nothing return; } if (gap->ga_growsize < 1) { WLOG("ga_growsize(%d) is less than 1", gap->ga_growsize); } // the garray grows by at least growsize if (n < gap->ga_growsize) { n = gap->ga_growsize; } // A linear growth is very inefficient when the array grows big. This // is a compromise between allocating memory that won't be used and too // many copy operations. A factor of 1.5 seems reasonable. if (n < gap->ga_len / 2) { n = gap->ga_len / 2; } int new_maxlen = gap->ga_len + n; size_t new_size = (size_t)gap->ga_itemsize * (size_t)new_maxlen; size_t old_size = (size_t)gap->ga_itemsize * (size_t)gap->ga_maxlen; // reallocate and clear the new memory char *pp = xrealloc(gap->ga_data, new_size); memset(pp + old_size, 0, new_size - old_size); gap->ga_maxlen = new_maxlen; gap->ga_data = pp; } /// Sort "gap" and remove duplicate entries. "gap" is expected to contain a /// list of file names in allocated memory. /// /// @param gap void ga_remove_duplicate_strings(garray_T *gap) { char **fnames = gap->ga_data; // sort the growing array, which puts duplicates next to each other sort_strings(fnames, gap->ga_len); // loop over the growing array in reverse for (int i = gap->ga_len - 1; i > 0; i--) { if (path_fnamecmp(fnames[i - 1], fnames[i]) == 0) { xfree(fnames[i]); // close the gap (move all strings one slot lower) for (int j = i + 1; j < gap->ga_len; j++) { fnames[j - 1] = fnames[j]; } gap->ga_len--; } } } /// For a growing array that contains a list of strings: concatenate all the /// strings with sep as separator. /// /// @param gap /// @param sep /// /// @returns the concatenated strings char *ga_concat_strings_sep(const garray_T *gap, const char *sep) FUNC_ATTR_NONNULL_RET { const size_t nelem = (size_t)gap->ga_len; const char **strings = gap->ga_data; if (nelem == 0) { return xstrdup(""); } size_t len = 0; for (size_t i = 0; i < nelem; i++) { len += strlen(strings[i]); } // add some space for the (num - 1) separators len += (nelem - 1) * strlen(sep); char *const ret = xmallocz(len); char *s = ret; for (size_t i = 0; i < nelem - 1; i++) { s = xstpcpy(s, strings[i]); s = xstpcpy(s, sep); } strcpy(s, strings[nelem - 1]); // NOLINT(runtime/printf) return ret; } /// For a growing array that contains a list of strings: concatenate all the /// strings with a separating comma. /// /// @param gap /// /// @returns the concatenated strings char *ga_concat_strings(const garray_T *gap) FUNC_ATTR_NONNULL_RET { return ga_concat_strings_sep(gap, ","); } /// Concatenate a string to a growarray which contains characters. /// When "s" is NULL does not do anything. /// /// WARNING: /// - Does NOT copy the NUL at the end! /// - The parameter may not overlap with the growing array /// /// @param gap /// @param s void ga_concat(garray_T *gap, const char *restrict s) { if (s == NULL) { return; } ga_concat_len(gap, s, strlen(s)); } /// Concatenate a string to a growarray which contains characters /// /// @param[out] gap Growarray to modify. /// @param[in] s String to concatenate. /// @param[in] len String length. void ga_concat_len(garray_T *const gap, const char *restrict s, const size_t len) FUNC_ATTR_NONNULL_ALL { if (len) { ga_grow(gap, (int)len); char *data = gap->ga_data; memcpy(data + gap->ga_len, s, len); gap->ga_len += (int)len; } } /// Append one byte to a growarray which contains bytes. /// /// @param gap /// @param c void ga_append(garray_T *gap, uint8_t c) { GA_APPEND(uint8_t, gap, c); }