mirror of
https://github.com/neovim/neovim.git
synced 2026-02-20 09:19:43 +10:00
Remove `export` pramgas from defs headers as it causes IWYU to believe that the definitions from the defs headers comes from main header, which is not what we really want.
229 lines
5.3 KiB
C
229 lines
5.3 KiB
C
/// @file garray.c
|
|
///
|
|
/// Functions for handling growing arrays.
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "nvim/garray.h"
|
|
#include "nvim/log.h"
|
|
#include "nvim/memory.h"
|
|
#include "nvim/path.h"
|
|
#include "nvim/strings.h"
|
|
|
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
# include "garray.c.generated.h"
|
|
#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);
|
|
}
|
|
|
|
void *ga_append_via_ptr(garray_T *gap, size_t item_size)
|
|
{
|
|
if ((int)item_size != gap->ga_itemsize) {
|
|
WLOG("wrong item size (%zu), should be %d", item_size, gap->ga_itemsize);
|
|
}
|
|
ga_grow(gap, 1);
|
|
return ((char *)gap->ga_data) + (item_size * (size_t)gap->ga_len++);
|
|
}
|