simple library implementing several useful data structures for C programming
The vec.h functions are built on top of ordinary C arrays.
So, a vector of type Type would be declared as a normal array: Type *myvec.
These 'vectors' can interoperate with functions expecting normal arrays, but the reverse is not true.
The vec_ctor() macro will magically initialize an array as a vector for you.
Use it like: vec_ctor(myvec);
myvecis the null pointer if an allocation fails
to allocate a vector manually, just do: vec_alloc(&myvec, sizeof *myvec).
This function returns 0 on succes, ENOMEM if the allocation fails, or EOVERFLOW if you somehow manage to overflow a size_t somewhere.
Free a vector with vec_free(myvec)
important: every function that modifies its argument takes a pointer to a vector allocated with
vec_ctor() (or vec_alloc()) as their first argument.
If you just pass the vector itself, your program might blow up.
-
vec_append(void *vec_ptr, void *item) -
vec_insert(void *vec_ptr, void *item, size_t position)-
append, prepend, or insert elements into a vector
-
item(orarray) should be a pointer of the same type as the vector, that is:int *myvec=>int *item -
These functions return
0on succces,ENOMEMif they cannot allocate memory,EOVERFLOWif some value would overflow (this should never happen). -
vec_insert()can also returnEINVALif the position is out of bounds
-
-
vec_delete(void *vec_ptr, size_t which)-
delete
whichelement. -
Returns nothing, cannot fail.
-
-
vec_elim(void *vec_ptr, size_t index, size_t nmemb)-
eliminate
nmembelements fromindexon. -
Returns nothing, cannot fail.
-
-
vec_truncate(void *vec_ptr, size_t index)-
truncate the vector, starting from
index. -
Returns nothing, cannot fail.
-
-
vec_shift(void *vec_ptr, size_t offset)-
shift the vector by
offsetelements. -
If
offsetis 2, for example, vec[2] becomes vec[0], vec[3] becomes vec[1], and so on -
Returns nothing, cannot fail.
-
-
vec_slice(void *vec_ptr, size_t begin, size_t nmemb)-
slice
nmembelements, starting atbegin. -
After
vec_slice(&myvec, 2, 4), myvec consists of the four 4 elements it had starting at index 2. -
Returns nothing, cannot fail.
-
-
vec_concat(void *vec_ptr, void *array, size_t length) -
vec_splice(void *vec_ptr, size_t offset, void *array, size_t length)-
concatenate, or splice a vector with an array.
-
Array should be an array of the same type as the vector.
-
Length is the length of the array in type-units, not bytes ('concat'ing (or 'splice'ing) an array of five ints, length would be
5, not20or however big fives ints are). -
returns
0on succes,ENOMEMwhen out of memory,EOVERFLOWif something overflows.
-
-
vec_copy(void *dest_ptr, void *src) -
vec_transfer(void *dest_ptr, void *src, size_t nmemb)-
copy elements to a vector from a source vector (
vec_copy()) or a source array (vec_transfer()) -
returns
0on succes,ENOMEMwhen out of memory,EOVERFLOWif something overflows. -
note the functionality of
vec_transfer()is likely to be completely changed in the near future
-
-
vec_join(void *dest_ptr, void *src)-
join two vectors
-
behaves equivalently to
vec_concat(), assumingsrcis a valid vector -
returns
0on success,ENOMEMwhen out of memory,EOVERFLOWif something overflows
-
-
vec_clone(void *vec)- returns a copy of the vector, or the null pointer if allocation fails
vec_foreach(variable, vector)is a macro which loops over the elements ofvector, assigning the address of each one tovariblein sequence. e.g.
int
main()
{
int *foo;
vec_ctor(foo);
if (!foo) abort();
/* ... */
vec_foreach(int *each, foo) {
printf("%d * 2 == %d\n", *each, *each + *each);
}
/* ... */
return 0;
}- as seen above, you can declare variables inside vec_foreach(), like a for loop initializer
- furthermore, `continue` and `break` behave as expected
- finally, `vec_foreach()` contains no double evaluatation.
any correctly typed expression can be used as `vector` or `variable`, even if it has side-effects
-
forgeting the
&-
as stated above, the mutating functions (insert, delete, et al.) take a pointer to a vector, rather than the vector itself.
-
it's very easy to forget the
&and write something likevec_append(vec, foo)which gives strange results for a vector of pointers (and a build error otherwise)
-
-
passing a vector to a fucntion
- this seems like reasonable code
int
frobnicate(struct frob *target, long errata)
{
int err;
/* ... */
err = vec_append(&target, some_local)
if (err) return err;
/* ... */
return 0;
}- however, it has a fatal bug -- if target overflows it's capacity, it will be reallocated and
quite likely moved to another address. these changes will not propagate back to the caller, resulting in a user-after-free bug
the fix is simply pass a pointer the vector, as the vec.h functions do
-
using compound literals
- it's convenient to use compound literals, particular when the vector is of scalars -- the scope need not be polluted with temporary variables. however, there is wrinkle when using this idiom with structs or arrays
vec_insert(frob, index, (struct frob[]) {{ .a = res_a, .b = res_b, .c = res_c }});- this snippet looks reasonable, but will not compile. compilers vary in helpfulness, but the source error
comes from the preprocessor -- the commas are interpreted as argument delimiters, rather than a pat of a single initializer
the fix is simply to wrap it in paranthese
vec_insert(frob, index, ((struct frob[]) {{ .a = res_a, .b = res_b, .c = res_c }}));declare a crit-bit tree with struct set *instance
alloc & initialize:
instance = set_alloc()
free:
set_free(instance)
note that all of these functions have two variations ---- one takes a byte buffer and a length, and one takes a null-terminated c-string. Both of them have equivalent behavior, what differs is how length is calculated.
adding members:
-
set_add_bytes(struct set *set, void *elem, size_t length) -
set_add_string(struct set *set, char *elem)-
insert
elemintoset -
return
0if succesful,ENOMEMif out of memory,EEXISTifelemwas already inset,EILSEQifelemis a prefix of an item already inset,EOVERFLOWifelemis too large to be added,EFAULTif given the null pointer as an argument,EINVALif the length ofelemis zero,
-
-
set_remove_bytes(struct set *set, void *elem, size_t length) -
set_remove_string(struct set *set, char *elem)-
remove
elemfromset -
return
0if successfulENOENTif theelemis not inset,EFAULTif given the null pointer as an argument,
-
-
set_contains_bytes(struct set *set, void *elem, size_t length) -
set_contains_string(struct set *set, char *elem, size_t length)-
check if
elemis contained inset -
return
trueiffelemis inset,falseotherwise
-
-
set_prefix_bytes(struct set *set, void *prefix, size_t length) -
set_prefix_string(struct set *set, char *prefix)-
check if any elements of
sethave the prefixprefix -
return
trueiff so,falseotherwise
-
-
set_query_bytes(void ***out, size_t nmemb, struct set *set, void *prefix, size_t length) -
set_query_string(void ***out, size_t nmemb, struct set *set, char *prefix) -
set_query_vector(void ***out, size_t nmemb, struct set *set, void *prefix)-
return the number of elements in
setwith the prefixprefix -
if
outis not the null pointer, the array ofnmembelements it points to will be filled with the elements containing the prefix, potentially truncated ifnmembis less than the total number of elements with that prefix -
as a special case, if
outpoints to the null pointer, a sufficiently large array will be allocated and the pointer pointed to byoutwill be mutated to be the allocated array
-