mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
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:
parent
e2a27e7f59
commit
8d52536271
@ -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
62
libdrgn/vector.c
Normal 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
235
libdrgn/vector.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user