Resizable arrays

#include <libcork/ds.h>

This section defines a resizable array class, similar to C++’s std::vector or Java’s ArrayList classes. Our arrays can store any fixed-size element. The arrays automatically resize themselves as necessary to store the elements that you add.


A resizable array that contains elements of type element_type.

void cork_array_init(cork_array(T) *array)

Initializes a new array. You should allocate array yourself, presumably on the stack or directly within some other data type. The array will start empty.

void cork_array_done(cork_array(T) *array)

Finalizes an array, freeing any storage that was allocated to hold the arrays elements.

size_t cork_array_size(cork_array(T) *array)

Returns the number of elements in array.

bool cork_array_is_empty(cork_array(T) *array)

Returns whether array has any elements.

void cork_array_void(cork_array(T) *array)

Removes all elements from array.

T* cork_array_elements(cork_array(T) *array)

Returns a pointer to the underlying array of elements in array. The elements are guaranteed to be contiguous, just like in a normal C array, but the particular pointer that is returned in not guaranteed to be consistent across function calls that modify the contents of the array.

T cork_array_at(cork_array(T) *array, size_t index)

Returns the element in array at the given index. Like accessing a normal C array, we don’t do any bounds checking. The result is a valid lvalue, so it can be directly assigned to:

cork_array(int64_t)  array;
cork_array_append(array, 5, err);
cork_array_at(array, 0) = 12;
void cork_array_append(cork_array(T) *array, T element)

Appends element to the end of array, reallocating the array’s storage if necessary. If you have an init or reset callback for array, it will be used to initialize the space that was allocated for the new element, and then element will be directly copied into that space (using memcpy or an equivalent). If that is not the right copy behavior for the elements of array, then you should use cork_array_append_get() instead, and fill in the allocated element directly.

T *cork_array_append_get(cork_array(T) *array)

Appends a new element to the end of array, reallocating the array’s storage if necessary, returning a pointer to the new element.

int cork_array_ensure_size(cork_array(T) *array, size_t desired_count)

Ensures that array has enough allocated space to store desired_count elements, reallocating the array’s storage if needed. The actual size and existing contents of the array aren’t changed.

int cork_array_copy(cork_array(T) *dest, cork_array(T) *src, cork_copy_f *copy, void *user_data)

Copy elements from src to dest. If you provide a copy function, it will be called on each element to perform the copy. If not, we’ll use memcpy to bulk-copy the elements.

If you’ve provided callbacks for dest, then those callbacks will be called appropriately. We’ll call the remove callback for any existing entries (will be overwritten by the copy). We’ll call init or reuse on each element entry before it’s copied.

typedef int (*cork_copy_f)(void *user_data, void *dest, const void *src)
size_t cork_array_element_size(cork_array(T) *array)

Returns the size of the elements that are stored in array. You won’t normally need to call this, since you can just use sizeof(T).

Initializing and finalizing elements

You can provide callback functions that will be used to automatically initialize and finalize the elements of a resizable array.

void cork_array_set_init(cork_array(T) *array, cork_init_f init)
void cork_array_set_done(cork_array(T) *array, cork_done_f done)
void cork_array_set_reuse(cork_array(T) *array, cork_init_f reuse)
void cork_array_set_remove(cork_array(T) *array, cork_done_f remove)
void cork_array_set_callback_data(cork_array(T) *array, void *user_data, cork_free_f free_user_data)

Set one of the callback functions for array. There are two pairs of callbacks: init and done, and reuse and remove. Within each pair, one callback is used to initialize an element of the array, while the other is used to finalize it.

The init callback is used to initialize an element when its array entry is used for the first time. If you then shrink the array (via cork_array_clear(), for instance), and then append elements again, you will reuse array entries; in this case, the reset callback is used instead. (Having separate init and reuse callbacks can be useful when the elements are complex objects with deep memory requirements. If you use the init callback to allocate that memory, and use the reset callback to “clear” it, then you can reduce some of the memory allocation overhead.)

Similarly, the remove callback is used when an element is removed from the array, but the space that the element used isn’t being reclaimed yet. The done callback, on the other hand, is used when the array entry is reclaimed and freed.

All of the callbacks take in an additional user_data parameter, in addition to the array entries themselves. You provide that parameter by calling the cork_array_set_callback_data() function. If you pass in a free_user_data function, then we will use that function to free the user_data when the array itself is finalized.

typedef void (*cork_init_f)(void *user_data, void *value)
typedef void (*cork_done_f)(void *user_data, void *value)
typedef void (*cork_free_f)(void *value)