SVEC (Stable VEC) is a typesafe 2-level tree with per-instance configurable leaf size and an api very similar to VEC but with stable
element addresses. Elements are never moved even when the SVEC grows. SVECs do not shrink their internal allocations automatically.
An SVEC is declared using the following macro, where Tis the type and chunk_size is the size of each leaf of the tree.
Small values will lead to fragmentation while large values will lead to over-allocation.
SVEC(T, [size_t] chunk_size) var_name;
In this documentation, SVEC* will be used to refer to a pointer to an SVEC of unspecified type, which is used by all of the functions.
Usually this is the address of the actual variable, not a proper independent pointer to it. T will be used as the declared internal
type of the SVEC.
Examples:
SVEC(int, 100) numbers;
SVEC(char*, 100) names;
SVEC_push(&numbers, 56);
SVEC_push(&names, "Simon");
Due to C's semantics involving anonymous structs, you have to typedef a SVEC of some certain type if you want to declare
pointers to it. I am not aware of a workaround at this time.
typedef SVEC(char*, 200) StringList;
// a (string-keyed) hash table containing lists of nicknames for each person
HT(StringList*) nicknames;
char* longest_nickname(StringList* list) {
int longest_len = 0;
char* longest_str = NULL;
SVEC_EACH(list, i, nick) {
int len = strlen(nick);
if(len > longest_len) {
longest_len = len;
longest_str = nick;
}
}
return longest_str;
}
SVECs must be zero-initialized before use. There is a macro to do this if you value the semantics of it. Subsequent operations automatically
initialize internal variables and allocate memory as needed.
SVEC(float) heights = {}; // perfectly fine
SVEC_init(&names); // just zeroes out the memory
When you're done, free the internal memory with VEC_free(). Don't forget to also free memory held by the contents if necessary, there
are no cascading frees in C (nor should there be).
SVEC(char*) files = {};
for(int a = 0; a < argc; a++) {
if(argv[a][0] == '-' && argv[a][1] == 'f') {
if(argc > a + 1) {
// inserted values are not evaluated more than once internally.
SVEC_push(&files, strdup(argv[++a]);
}
}
}
// do some stuff
SVEC_EACH(&files, i, f) {
free(f)
}
SVEC_free(&files);
There is a magic loop macro for iterating over the contents. It works just like it appears to, including the use of break and continue, and
are a single statement internally, allowing constructs like if(foo) SVEC_EACHP(&bar, i, b) { printf("%d\n", *b); } or nesting loops without
braces on the outer ones.
SVEC_EACHP(SVEC* o, [size_t] iterator, [T*] elem_ptr) { ... }
iterator and value_ptr are both declared as local variables.
A pointer to the actual internal memory at SVEC_item(o, iterator) is copied into elem_ptr, allowing direct modification.
Evaluates to the current used length. Is an L-value and can be assigned to.
T SVEC_item(SVEC* o, size_t index)
Evaluates to the internal array element for the given index. Is an L-value and can be assigned to.
Whatever expression is used for index is evaluated multiple times.
T* SVEC_itemp(SVEC* o, size_t index)
Evaluates to the address of the internal array element for the given index.
The expression used for index is only evaluated once.
Equivalent to VEC_item(o, 0)
Equivalent to VEC_item(o, VEC_len(o) - 1)
void SVEC_push(SVEC* o, T value)
Inserts value at the end of the array, ie, at location SVEC_len(o) prior to the insert. Automatically checks allocation
size and grows the internal array if necessary.
Asserts a new value at the end of the array and zero-initializes it. Returns a pointer to the new element.
Automatically checks allocation size and grows the internal array if necessary.
ssize_t SVEC_pointer_index(SVEC* o, T* ptr)
Returns the index of the element corresponding to ptr, or -1 if ptr is not an element in the SVEC.