libdrgn: add common vector implementation

drgn has enough open-coded dynamic arrays at this point to warrant a
common implementation. Add one inspired by hash_table.h. The API is
pretty minimal. I'll add more to it as the need arises.
This commit is contained in:
Omar Sandoval 2019-07-09 15:47:48 -07:00
parent e2a27e7f59
commit 8d52536271
3 changed files with 300 additions and 1 deletions

View File

@ -41,7 +41,9 @@ libdrgnimpl_la_SOURCES = binary_search_tree.h \
type.c \
type.h \
type_index.c \
type_index.h
type_index.h \
vector.c \
vector.h
libdrgnimpl_la_CFLAGS = -fvisibility=hidden -fopenmp $(libelf_CFLAGS) \
$(libdw_CFLAGS)

62
libdrgn/vector.c Normal file
View File

@ -0,0 +1,62 @@
// Copyright 2019 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
#include "vector.h"
bool vector_do_reserve(size_t new_capacity, size_t entry_size, void **data,
size_t *capacity)
{
size_t bytes;
void *new_data;
if (new_capacity <= *capacity || new_capacity == 0)
return true;
if (__builtin_mul_overflow(new_capacity, entry_size, &bytes))
return false;
new_data = realloc(*data, bytes);
if (!new_data)
return false;
*data = new_data;
*capacity = new_capacity;
return true;
}
void vector_do_shrink_to_fit(size_t size, size_t entry_size, void **data,
size_t *capacity)
{
void *new_data;
if (*capacity > size) {
/*
* We already have at least size * entry_size bytes allocated,
* so we don't need to worry about overflow.
*/
new_data = realloc(*data, size * entry_size);
if (new_data) {
*data = new_data;
*capacity = size;
}
}
}
bool vector_reserve_for_append(size_t size, size_t entry_size, void **data,
size_t *capacity)
{
size_t new_capacity, bytes;
void *new_data;
if (size < *capacity)
return true;
if (*capacity == 0)
new_capacity = 1;
else
new_capacity = 2 * *capacity;
if (__builtin_mul_overflow(new_capacity, entry_size, &bytes))
return false;
new_data = realloc(*data, bytes);
if (!new_data)
return false;
*data = new_data;
*capacity = new_capacity;
return true;
}

235
libdrgn/vector.h Normal file
View File

@ -0,0 +1,235 @@
// Copyright 2019 - Omar Sandoval
// SPDX-License-Identifier: GPL-3.0+
/**
* @file
*
* Dynamic arrays.
*
* See @ref Vectors.
*/
#ifndef DRGN_VECTOR_H
#define DRGN_VECTOR_H
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
/**
* @ingroup Internals
*
* @defgroup Vectors Vectors
*
* Dynamic arrays (a.k.a.\ vectors).
*
* This is a basic 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
/**
* 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 {
/**
* The underlying array of entries.
*
* This may be accessed directly. It may be reallocated as noted.
*
* A common pattern is using a @c vector to build an array and then
* returning the raw array. To do so, don't call @ref vector_deinit(),
* then return @c data and free it with @ref free().
*/
entry_type *data;
/** The number of entries in a @ref vector. */
size_t size;
/**
* The number of allocated elements in @ref vector::data.
*
* This should not be modified.
*/
size_t capacity;
};
/**
* Initialize a @ref vector.
*
* The new vector is empty.
*/
void vector_init(struct vector *vector);
/**
* Free memory allocated by a @ref vector.
*
* This frees @ref vector::data.
*/
void vector_deinit(struct vector *vector);
/**
* Increase the capacity of a @ref vector.
*
* If @p capacity is greater than the current capacity of the @ref vector, this
* reallocates @ref vector::data and increases @ref vector::capacity to at least
* @p capacity. Otherwise, it does nothing.
*
* @return @c true on success, @c false on failure.
*/
bool vector_reserve(struct vector *vector, size_t capacity);
/**
* Free unused memory in a @ref vector.
*
* This may reallocate @ref vector::data and set @ref vector::capacity to @ref
* vector::size. It may also do nothing.
*/
void vector_shrink_to_fit(struct vector *vector);
/**
* Append to a @ref vector.
*
* This increases @ref vector::size by one. It may reallocate @ref vector::data
* and change @ref vector::capacity.
*
* @return @c true on success, @c false on failure to allocate memory.
*/
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 to allocate memory.
*/
entry_type *vector_append_entry(struct vector *vector);
/**
* Remove and return the last entry in a @ref vector.
*
* The vector is assumed to be non-empty. This descreases @ref vector::size by
* one. It does not reallocate @ref vector::data.
*
* @return A pointer to the removed entry, which remains valid until another
* entry is inserted in its place or @ref vector::data is reallocated.
*/
entry_type *vector_pop(struct vector *vector);
#endif
bool vector_do_reserve(size_t new_capacity, size_t entry_size, void **data,
size_t *capacity);
void vector_do_shrink_to_fit(size_t size, size_t entry_size, void **data,
size_t *capacity);
bool vector_reserve_for_append(size_t size, size_t entry_size, void **data,
size_t *capacity);
/**
* 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; \
};
/**
* 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 bool vector##_reserve(struct vector *vector, size_t capacity) \
{ \
return vector_do_reserve(capacity, sizeof(*vector->data), \
(void **)&vector->data, &vector->capacity); \
} \
\
__attribute__((unused)) \
static void vector##_shrink_to_fit(struct vector *vector) \
{ \
vector_do_shrink_to_fit(vector->size, sizeof(*vector->data), \
(void **)&vector->data, &vector->capacity); \
} \
\
static vector##_entry_type *vector##_append_entry(struct vector *vector) \
{ \
if (!vector_reserve_for_append(vector->size, sizeof(*vector->data), \
(void **)&vector->data, \
&vector->capacity)) \
return NULL; \
return &vector->data[vector->size++]; \
} \
\
__attribute__((unused)) \
static bool vector##_append(struct vector *vector, \
const vector##_entry_type *entry) \
{ \
vector##_entry_type *new_entry; \
\
new_entry = vector##_append_entry(vector); \
if (!new_entry) \
return false; \
memcpy(new_entry, entry, sizeof(*entry)); \
return true; \
} \
\
__attribute__((unused)) \
static vector##_entry_type *vector##_pop(struct vector *vector) \
{ \
return &vector->data[--vector->size]; \
}
/**
* 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)
/** @} */
#endif /* DRGN_VECTOR_H */