drgn/libdrgn/vector.h
Omar Sandoval d1a6350bdd libdrgn: revamp generic vector API
The current generic vector API is pretty minimal and exposes its
internal members as part of the public interface. This has worked well
but prevents us from changing the vector implementation. In particular,
I'd like to have "small vector" variants that can store some entries
directly in the vector structure, use a smaller integer type for the
size and capacity, or both.

So, let's make the generated vector type "private" and add accessor
functions. This is very verbose in some cases, but it'll grant us much
more flexibility. While we're changing every user anyways, let's also
make use of _cleanup_(vector_deinit) where possible.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
2023-08-16 14:13:38 -07:00

515 lines
15 KiB
C

// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later
/**
* @file
*
* Dynamic arrays.
*
* See @ref Vectors.
*/
#ifndef DRGN_VECTOR_H
#define DRGN_VECTOR_H
#include <stdbool.h>
#include <stdlib.h> // IWYU pragma: keep
#include <string.h> // IWYU pragma: keep
#include "minmax.h"
#include "util.h"
/**
* @ingroup Internals
*
* @defgroup Vectors Vectors
*
* Dynamic arrays (a.k.a.\ vectors).
*
* This is an implementation of generic, strongly-typed vectors.
*
* A vector is defined with @ref DEFINE_VECTOR(). Each generated vector
* interface is prefixed with a given name; the interface documented here uses
* the example name @c vector.
*
* @{
*/
#ifdef DOXYGEN
/**
* @struct vector
*
* Vector instance.
*
* There are no requirements on how this is allocated; it may be global, on the
* stack, allocated by @c malloc(), embedded in another structure, etc.
*/
struct vector;
/**
* Initialize a @ref vector.
*
* The new vector is empty.
*
* @sa VECTOR_INIT
*/
void vector_init(struct vector *vector);
/** Free memory allocated by a @ref vector. */
void vector_deinit(struct vector *vector);
/** Return the number of entries in a @ref vector. */
size_t vector_size(const struct vector *vector);
/** Return whether a @ref vector is empty. */
bool vector_empty(const struct vector *vector);
/**
* Maximum possible number of entries in a @ref vector.
*
* Attempts to increase the size or capacity beyond this will fail.
*/
const size_t vector_max_size;
/**
* Update the number of entries in a @ref vector.
*
* If @p size is greater than the current capacity, this increases the capacity
* to at least @p size and reallocates the entries.
*
* If @p size is greater than the current size, the entries between the old size
* and the new size are uninitialized.
*
* @return @c true on success, @c false on failure.
*/
bool vector_resize(struct vector *vector, size_t size);
/**
* Set the size of a @ref vector to zero.
*
* This does not change the capacity or free the entries.
*/
void vector_clear(struct vector *vector);
/** Return the number of allocated entries in a @ref vector. */
size_t vector_capacity(const struct vector *vector);
/**
* Increase the capacity of a @ref vector.
*
* If @p capacity is greater than the current capacity, this increases the
* capacity to at least @p capacity and reallocates the entries. Otherwise, it
* does nothing.
*
* @return @c true on success, @c false on failure.
*/
bool vector_reserve(struct vector *vector, size_t capacity);
/**
* Increase the capacity of a @ref vector to accomodate at least one append.
*
* If the current capacity is equal to the current size, this increases the
* capacity by at least one and reallocates the entries. Otherwise, it does
* nothing.
*
* @return @c true on success, @c false on failure.
*/
bool vector_reserve_for_append(struct vector *vector);
/**
* Increase the capacity of a @ref vector to accomodate at least @p n appends.
*
* If the current capacity minus the current size is not at least @p n, this
* increases the capacity by at least @p n and reallocates the entries.
* Otherwise, it does nothing.
*
* @return @c true on success, @c false on failure.
*/
bool vector_reserve_for_extend(struct vector *vector, size_t n);
/**
* Free unused memory in a @ref vector.
*
* This may reduce the capacity and reallocate the entries. It may also do
* nothing.
*/
void vector_shrink_to_fit(struct vector *vector);
/**
* Steal the array of entries from a @ref vector.
*
* This returns the internal array of entries. The vector can no longer be used
* except to be passed to @ref vector_deinit(), which will do nothing.
*
* This can be used to build an array when the size isn't known ahead of time
* but won't change after the array is built. For example:
*
* ```
* DEFINE_VECTOR(int_vector, int);
*
* bool primes_less_than(int n, int **array_ret, size_t *size_ret)
* {
*
* _cleanup_(int_vector_deinit) struct int_vector vector = VECTOR_INIT;
* for (int i = 2; i < n; i++) {
* if (is_prime(i) && !int_vector_push(&vector, &i))
* return false;
* }
* int_vector_shrink_to_fit(&vector);
* int_vector_steal(&vector, array_ret, size_ret);
* return true;
* }
* ```
*
* As demonstrated here, it may be desirable to call @ref vector_shrink_to_fit()
* first.
*
* @param[out] entries_ret Returned array. This must be freed with @c free().
* @param[out] size_ret Returned number of entries in array. May be @c NULL.
*/
void vector_steal(struct vector *vector, entry_type **entries_ret,
size_t *size_ret);
/**
* Return the array of entries in a @ref vector.
*
* The vector may be empty, in which case this is equal to `vector_end(vector)`.
*/
entry_type *vector_begin(struct vector *vector);
/**
* Return one past the last entry in a @ref vector.
*
* The vector may be empty, in which case this is equal to
* `vector_begin(vector)`.
*/
entry_type *vector_end(struct vector *vector);
/**
* Return the first entry in a @ref vector.
*
* This is equivalent to `vector_at(vector, 0)`. The vector must not be empty
* (in contrast to @ref vector_begin()).
*/
entry_type *vector_first(struct vector *vector);
/**
* Return the last entry in a @ref vector.
*
* This is equivalent to `vector_at(vector, vector_size(vector) - 1)`. The
* vector must not be empty.
*/
entry_type *vector_last(struct vector *vector);
/**
* Return the entry at the given index in a @ref vector.
*
* @param[in] i Entry index. Must be less than the size of the vector.
*/
entry_type *vector_at(struct vector *vector, size_t i);
/**
* Append to a @ref vector.
*
* This increases vector's size by one. If the current capacity is equal to the
* current size, this increases the capacity by at least one and reallocates the
* entries.
*
* @return @c true on success, @c false on failure.
*/
bool vector_append(struct vector *vector, const entry_type *entry);
/**
* Append an uninitialized entry to a @ref vector.
*
* Like @ref vector_append(), but return a pointer to the new (uninitialized)
* entry.
*
* @return The new entry on success, @c NULL on failure.
*/
entry_type *vector_append_entry(struct vector *vector);
/**
* Append all of the entries from one vector to another.
*
* @param[in] dst Vector to append to.
* @param[in] src Source vector. This is not modified.
* @return @c true on success, @c false on failure.
*/
bool vector_extend(struct vector *dst, const struct vector *src);
/**
* Remove and return the last entry in a @ref vector.
*
* The vector must not be empty. This decreases the size by one. It does not
* change the capacity or reallocate the entries.
*
* @return A pointer to the removed entry, which remains valid until another
* entry is inserted in its place or the entries are reallocated.
*/
entry_type *vector_pop(struct vector *vector);
#endif
/**
* Define a vector type without defining its functions.
*
* This is useful when the vector type must be defined in one place (e.g., a
* header) but the interface is defined elsewhere (e.g., a source file) with
* @ref DEFINE_VECTOR_FUNCTIONS(). Otherwise, just use @ref DEFINE_VECTOR().
*
* @sa DEFINE_VECTOR()
*/
#define DEFINE_VECTOR_TYPE(vector, entry_type) \
typedef typeof(entry_type) vector##_entry_type; \
\
struct vector { \
vector##_entry_type *_data; \
size_t _size; \
size_t _capacity; \
}; \
struct DEFINE_VECTOR_needs_semicolon
/**
* Define the functions for a vector.
*
* The vector type must have already been defined with @ref
* DEFINE_VECTOR_TYPE().
*
* Unless the type and function definitions must be in separate places, use @ref
* DEFINE_VECTOR() instead.
*
* @sa DEFINE_VECTOR()
*/
#define DEFINE_VECTOR_FUNCTIONS(vector) \
__attribute__((__unused__)) \
static void vector##_init(struct vector *vector) \
{ \
vector->_data = NULL; \
vector->_size = vector->_capacity = 0; \
} \
\
__attribute__((__unused__)) \
static void vector##_deinit(struct vector *vector) \
{ \
free(vector->_data); \
} \
\
__attribute__((__unused__)) \
static size_t vector##_size(const struct vector *vector) \
{ \
return vector->_size; \
} \
\
__attribute__((__unused__)) \
static bool vector##_empty(const struct vector *vector) \
{ \
return vector->_size == 0; \
} \
\
static const size_t vector##_max_size = \
PTRDIFF_MAX / sizeof(vector##_entry_type); \
\
static size_t vector##_capacity(const struct vector *vector) \
{ \
return vector->_capacity; \
} \
\
static bool vector##_reallocate(struct vector *vector, size_t capacity) \
{ \
void *new_data = realloc(vector->_data, \
capacity * sizeof(vector##_entry_type)); \
if (!new_data) \
return false; \
vector->_data = new_data; \
vector->_capacity = capacity; \
return true; \
} \
\
static bool vector##_reserve_for_extend(struct vector *vector, size_t n) \
{ \
size_t size = vector##_size(vector); \
if (n <= vector##_capacity(vector) - size) \
return true; \
if (n > vector##_max_size - size) \
return false; \
size_t new_capacity = size + max(size, n); \
if (new_capacity < size || new_capacity > vector##_max_size) \
new_capacity = vector##_max_size; \
return vector##_reallocate(vector, new_capacity); \
} \
\
__attribute__((__unused__)) \
static bool vector##_resize(struct vector *vector, size_t size) \
{ \
if (vector->_size < size \
&& !vector##_reserve_for_extend(vector, size - vector->_size)) \
return false; \
vector->_size = size; \
return true; \
} \
\
__attribute__((__unused__)) \
static void vector##_clear(struct vector *vector) \
{ \
vector->_size = 0; \
} \
\
__attribute__((__unused__)) \
static bool vector##_reserve(struct vector *vector, size_t capacity) \
{ \
if (capacity <= vector##_capacity(vector)) \
return true; \
if (capacity > vector##_max_size) \
return false; \
return vector##_reallocate(vector, capacity); \
} \
\
static bool vector##_reserve_for_append(struct vector *vector) \
{ \
return vector##_reserve_for_extend(vector, 1); \
} \
\
__attribute__((__unused__)) \
static void vector##_shrink_to_fit(struct vector *vector) \
{ \
size_t size = vector##_size(vector); \
if (vector->_capacity <= size) \
return; \
if (size > 0) { \
vector##_reallocate(vector, size); \
} else { \
free(vector->_data); \
vector->_data = NULL; \
vector->_capacity = 0; \
} \
} \
\
__attribute__((__unused__)) \
static void vector##_steal(struct vector *vector, \
vector##_entry_type **entries_ret, size_t *size_ret) \
{ \
*entries_ret = vector->_data; \
if (size_ret) \
*size_ret = vector->_size; \
vector->_data = NULL; \
} \
\
static vector##_entry_type *vector##_begin(struct vector *vector) \
{ \
return vector->_data; \
} \
\
__attribute__((__unused__)) \
static vector##_entry_type *vector##_end(struct vector *vector) \
{ \
return add_to_possibly_null_pointer(vector##_begin(vector), \
vector##_size(vector)); \
} \
\
__attribute__((__unused__)) \
static vector##_entry_type *vector##_first(struct vector *vector) \
{ \
return vector##_begin(vector); \
} \
\
__attribute__((__unused__)) \
static vector##_entry_type *vector##_last(struct vector *vector) \
{ \
return vector##_begin(vector) + vector##_size(vector) - 1; \
} \
\
__attribute__((__unused__)) \
static vector##_entry_type *vector##_at(struct vector *vector, size_t i) \
{ \
return vector##_begin(vector) + i; \
} \
\
static vector##_entry_type *vector##_append_entry(struct vector *vector) \
{ \
if (!vector##_reserve_for_append(vector)) \
return NULL; \
return vector##_begin(vector) + vector->_size++; \
} \
\
__attribute__((__unused__)) \
static bool vector##_append(struct vector *vector, \
const vector##_entry_type *entry) \
{ \
vector##_entry_type *new_entry = vector##_append_entry(vector); \
if (!new_entry) \
return false; \
memcpy(new_entry, entry, sizeof(*entry)); \
return true; \
} \
\
__attribute__((__unused__)) \
static bool vector##_extend(struct vector *dst, const struct vector *src) \
{ \
if (src->_size == 0) \
return true; \
if (!vector##_reserve_for_extend(dst, src->_size)) \
return false; \
memcpy(vector##_end(dst), vector##_begin((struct vector *)src), \
src->_size * sizeof(vector##_entry_type)); \
dst->_size += src->_size; \
return true; \
} \
\
__attribute__((__unused__)) \
static vector##_entry_type *vector##_pop(struct vector *vector) \
{ \
return vector##_begin(vector) + --vector->_size; \
} \
struct DEFINE_VECTOR_needs_semicolon
/**
* Define a vector interface.
*
* This macro defines a vector type along with its functions.
*
* @param[in] vector Name of the type to define. This is prefixed to all of the
* types and functions defined for that type.
* @param[in] entry_type Type of entries in the vector.
*/
#define DEFINE_VECTOR(vector, entry_type) \
DEFINE_VECTOR_TYPE(vector, entry_type); \
DEFINE_VECTOR_FUNCTIONS(vector)
/**
* Empty vector initializer.
*
* This can be used to initialize a vector when declaring it.
*
* @sa vector_init()
*/
#define VECTOR_INIT { NULL }
/**
* Iterate over every entry in a @ref vector.
*
* This is roughly equivalent to
*
* ```
* for (entry_type *it = vector_begin(vector), *end = vector_end(vector);
* it != end; it++)
* ```
*
* Except that @p vector is only evaluated once.
*
* @param[in] vector_type Name of vector type.
* @param[out] it Name of iteration variable.
* @param[in] vector Vector to iterate over.
*/
#define vector_for_each(vector_type, it, vector) \
for (vector_type##_entry_type *it, \
*it##__end = ({ \
struct vector_type *it##__vector = (vector); \
it = vector_type##_begin(it##__vector); \
vector_type##_end(it##__vector); \
}); \
it != it##__end; it++)
/** @} */
#endif /* DRGN_VECTOR_H */