C Container Collection (CCC)
|
The Flat Double Ended Queue Interface. More...
Go to the source code of this file.
Initialization Interface | |
Initialize and create containers with memory, callbacks, and permissions. | |
#define | ccc_fdeq_init(mem_ptr, alloc_fn, aux_data, capacity, optional_size...) ccc_impl_fdeq_init(mem_ptr, alloc_fn, aux_data, capacity, optional_size) |
Initialize the fdeq with memory and allocation permission. | |
ccc_result | ccc_fdeq_copy (ccc_flat_double_ended_queue *dst, ccc_flat_double_ended_queue const *src, ccc_alloc_fn *fn) |
Copy the fdeq from src to newly initialized dst. | |
Insert and Remove Interface | |
Add or remove elements from the FDEQ. | |
#define | ccc_fdeq_emplace_back(fdeq_ptr, value...) ccc_impl_fdeq_emplace_back(fdeq_ptr, value) |
Write an element directly to the back slot of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required. | |
#define | ccc_fdeq_emplace_front(fdeq_ptr, value...) ccc_impl_fdeq_emplace_front(fdeq_ptr, value) |
Write an element directly to the front slot of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required. | |
void * | ccc_fdeq_push_back (ccc_flat_double_ended_queue *fdeq, void const *elem) |
Push the user type to the back of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required. | |
ccc_result | ccc_fdeq_push_back_range (ccc_flat_double_ended_queue *fdeq, size_t n, void const *elems) |
Push the range of user types to the back of the fdeq. O(N). | |
void * | ccc_fdeq_push_front (ccc_flat_double_ended_queue *fdeq, void const *elem) |
Push the user type to the front of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required. | |
ccc_result | ccc_fdeq_push_front_range (ccc_flat_double_ended_queue *fdeq, size_t n, void const *elems) |
Push the range of user types to the front of the fdeq. O(N). | |
void * | ccc_fdeq_insert_range (ccc_flat_double_ended_queue *fdeq, void *pos, size_t n, void const *elems) |
Push the range of user types before pos of the fdeq. O(N). | |
ccc_result | ccc_fdeq_pop_front (ccc_flat_double_ended_queue *fdeq) |
Pop an element from the front of the fdeq. O(1). | |
ccc_result | ccc_fdeq_pop_back (ccc_flat_double_ended_queue *fdeq) |
Pop an element from the back of the fdeq. O(1). | |
Container Types | |
Types available in the container interface. | |
typedef struct ccc_fdeq_ | ccc_flat_double_ended_queue |
A contiguous buffer for O(1) push and pop from front and back. | |
Deallocation Interface | |
Destroy the container. | |
ccc_result | ccc_fdeq_clear (ccc_flat_double_ended_queue *fdeq, ccc_destructor_fn *destructor) |
Set size of fdeq to 0 and call destructor on each element if needed. O(1) if no destructor is provided, else O(N). | |
ccc_result | ccc_fdeq_clear_and_free (ccc_flat_double_ended_queue *fdeq, ccc_destructor_fn *destructor) |
Set size of fdeq to 0 and call destructor on each element if needed. Free the underlying buffer setting the capacity to 0. O(1) if no destructor is provided, else O(N). | |
State Interface | |
Interact with the state of the FDEQ. | |
void * | ccc_fdeq_begin (ccc_flat_double_ended_queue const *fdeq) |
Return a pointer to the front element of the fdeq. O(1). | |
void * | ccc_fdeq_rbegin (ccc_flat_double_ended_queue const *fdeq) |
Return a pointer to the back element of the fdeq. O(1). | |
void * | ccc_fdeq_next (ccc_flat_double_ended_queue const *fdeq, void const *iter_ptr) |
Return the next element in the fdeq moving front to back. O(1). | |
void * | ccc_fdeq_rnext (ccc_flat_double_ended_queue const *fdeq, void const *iter_ptr) |
Return the next element in the fdeq moving back to front. O(1). | |
void * | ccc_fdeq_end (ccc_flat_double_ended_queue const *fdeq) |
Return a pointer to the end element. It may not be accessed. O(1). | |
void * | ccc_fdeq_rend (ccc_flat_double_ended_queue const *fdeq) |
Return a pointer to the start element. It may not be accessed. O(1). | |
void * | ccc_fdeq_at (ccc_flat_double_ended_queue const *fdeq, size_t i) |
Return a reference to the element at index position i. O(1). | |
void * | ccc_fdeq_front (ccc_flat_double_ended_queue const *fdeq) |
Return a reference to the front of the fdeq. O(1). | |
void * | ccc_fdeq_back (ccc_flat_double_ended_queue const *fdeq) |
Return a reference to the back of the fdeq. O(1). | |
bool | ccc_fdeq_is_empty (ccc_flat_double_ended_queue const *fdeq) |
Return true if the size of the fdeq is 0. O(1). | |
size_t | ccc_fdeq_size (ccc_flat_double_ended_queue const *fdeq) |
Return the size of the fdeq. O(1). | |
size_t | ccc_fdeq_capacity (ccc_flat_double_ended_queue const *fdeq) |
Return the capacity of the fdeq. O(1). | |
void * | ccc_fdeq_data (ccc_flat_double_ended_queue const *fdeq) |
Return a reference to the base of backing array. O(1). | |
bool | ccc_fdeq_validate (ccc_flat_double_ended_queue const *fdeq) |
Return true if the internal invariants of the fdeq. | |
The Flat Double Ended Queue Interface.
An FDEQ offers contiguous storage and random access, push, and pop in constant time. The contiguous nature of the buffer makes it well-suited to dynamic or fixed size contexts where a double ended queue is needed.
If the container is initialized with allocation permission it will resize when needed but support constant time push and pop to the front and back when resizing is not required, resulting in amortized O(1) operations.
If the FDEQ is initialized without allocation permission its behavior is equivalent to a Ring Buffer. This is somewhat unique in that it does not fail to insert elements when size is equal to capacity. This means that push front, push back, pop front, and pop back are O(1) operations. However, if any push exceeds capacity an element where the push should occur is overwritten.
To shorten names in the interface, define the following preprocessor directive at the top of your file.
All types and functions can then be written without the ccc_
prefix.
#define ccc_fdeq_emplace_back | ( | fdeq_ptr, | |
value... | |||
) | ccc_impl_fdeq_emplace_back(fdeq_ptr, value) |
Write an element directly to the back slot of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required.
[in] | fdeq_ptr | a pointer to the fdeq. |
[in] | value | for integral types, the direct value. For structs and unions use compound literal syntax. |
#define ccc_fdeq_emplace_front | ( | fdeq_ptr, | |
value... | |||
) | ccc_impl_fdeq_emplace_front(fdeq_ptr, value) |
Write an element directly to the front slot of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required.
[in] | fdeq_ptr | a pointer to the fdeq. |
[in] | value | for integral types, the direct value. For structs and unions use compound literal syntax. |
#define ccc_fdeq_init | ( | mem_ptr, | |
alloc_fn, | |||
aux_data, | |||
capacity, | |||
optional_size... | |||
) | ccc_impl_fdeq_init(mem_ptr, alloc_fn, aux_data, capacity, optional_size) |
Initialize the fdeq with memory and allocation permission.
[in] | mem_ptr | a pointer to existing memory or ((T *)NULL). |
[in] | alloc_fn | the allocator function, if allocation is allowed. |
[in] | aux_data | any auxiliary data needed for element destruction. |
[in] | capacity | the number of contiguous elements at mem_ptr |
[in] | optional_size | an optional initial size between 1 and capacity. |
typedef struct ccc_fdeq_ ccc_flat_double_ended_queue |
A contiguous buffer for O(1) push and pop from front and back.
A flat double ended queue can be initialized on the stack, heap, or data segment at compile time or runtime.
void * ccc_fdeq_at | ( | ccc_flat_double_ended_queue const * | fdeq, |
size_t | i | ||
) |
Return a reference to the element at index position i. O(1).
[in] | fdeq | a pointer to the fdeq. |
[in] | i | the 0 based index in the fdeq. |
Note that the front of the fdeq is considered index 0, so the user need not worry about where the front is for indexing purposes.
void * ccc_fdeq_back | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a reference to the back of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_begin | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a pointer to the front element of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
size_t ccc_fdeq_capacity | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return the capacity of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
ccc_result ccc_fdeq_clear | ( | ccc_flat_double_ended_queue * | fdeq, |
ccc_destructor_fn * | destructor | ||
) |
Set size of fdeq to 0 and call destructor on each element if needed. O(1) if no destructor is provided, else O(N).
[in] | fdeq | a pointer to the fdeq. |
[in] | destructor | the destructor if needed or NULL. |
Note that if destructor is non-NULL it will be called on each element in the fdeq. However, the underlying buffer for the fdeq is not freed. If the destructor is NULL, setting the size to 0 is O(1).
ccc_result ccc_fdeq_clear_and_free | ( | ccc_flat_double_ended_queue * | fdeq, |
ccc_destructor_fn * | destructor | ||
) |
Set size of fdeq to 0 and call destructor on each element if needed. Free the underlying buffer setting the capacity to 0. O(1) if no destructor is provided, else O(N).
[in] | fdeq | a pointer to the fdeq. |
[in] | destructor | the destructor if needed or NULL. |
Note that if destructor is non-NULL it will be called on each element in the fdeq. After all elements are processed the buffer is freed and capacity is 0. If destructor is NULL the buffer is freed directly and capacity is 0.
ccc_result ccc_fdeq_copy | ( | ccc_flat_double_ended_queue * | dst, |
ccc_flat_double_ended_queue const * | src, | ||
ccc_alloc_fn * | fn | ||
) |
Copy the fdeq from src to newly initialized dst.
[in] | dst | the destination that will copy the source fdeq. |
[in] | src | the source of the fdeq. |
[in] | fn | the allocation function in case resizing of dst is needed. |
Note that there are two ways to copy data from source to destination: provide sufficient memory and pass NULL as fn, or allow the copy function to take care of allocation for the copy.
Manual memory management with no allocation function provided.
The above requires dst capacity be greater than or equal to src capacity. Here is memory management handed over to the copy function.
The above allows dst to have a capacity less than that of the src as long as copy has been provided an allocation function to resize dst. Note that this would still work if copying to a destination that the user wants as a fixed size fdeq (ring buffer).
The above sets up dst as a ring buffer while src is a dynamic fdeq. Because an allocation function is provided, the dst is resized once for the copy and retains its fixed size after the copy is complete. This would require the user to manually free the underlying buffer at dst eventually if this method is used. Usually it is better to allocate the memory explicitly before the copy if copying between ring buffers.
These options allow users to stay consistent across containers with their memory management strategies.
void * ccc_fdeq_data | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a reference to the base of backing array. O(1).
[in] | fdeq | a pointer to the fdeq. |
This method is exposed for serialization or writing purposes but the base of the array may not point to valid data in terms of organization of the fdeq.
void * ccc_fdeq_end | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a pointer to the end element. It may not be accessed. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_front | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a reference to the front of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_insert_range | ( | ccc_flat_double_ended_queue * | fdeq, |
void * | pos, | ||
size_t | n, | ||
void const * | elems | ||
) |
Push the range of user types before pos of the fdeq. O(N).
[in] | fdeq | a pointer to the fdeq. |
[in] | pos | the position in the fdeq before which to push the range. |
[in] | n | the number of user types in the elems range. |
[in] | elems | a pointer to the array of user types. |
Note that if no allocation is permitted the fdeq behaves as a ring buffer. Therefore, pushing a range that will exceed capacity will overwrite elements at the start of the fdeq.
Pushing a range of elements prioritizes the range and allows the range to overwrite elements instead of pushing those elements over the start of the range. For example, push a range {3,4,5} over a fdeq with capacity 5 before pos with value 6.
Notice that 1 and 2 were NOT moved to overwrite the start of the range, the values 3 and 4. The only way the start of a range will be overwritten is if the range itself is too large for the capacity. For example, push a range {0,0,3,3,4,4,5,5} over the same fdeq.
Notice that the start of the range, {0,0,3,...}, is overwritten.
bool ccc_fdeq_is_empty | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return true if the size of the fdeq is 0. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_next | ( | ccc_flat_double_ended_queue const * | fdeq, |
void const * | iter_ptr | ||
) |
Return the next element in the fdeq moving front to back. O(1).
[in] | fdeq | a pointer to the fdeq. |
[in] | iter_ptr | the current element in the fdeq. |
ccc_result ccc_fdeq_pop_back | ( | ccc_flat_double_ended_queue * | fdeq | ) |
Pop an element from the back of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
ccc_result ccc_fdeq_pop_front | ( | ccc_flat_double_ended_queue * | fdeq | ) |
Pop an element from the front of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_push_back | ( | ccc_flat_double_ended_queue * | fdeq, |
void const * | elem | ||
) |
Push the user type to the back of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required.
[in] | fdeq | a pointer to the fdeq. |
[in] | elem | a pointer to the user type to insert into the fdeq. |
ccc_result ccc_fdeq_push_back_range | ( | ccc_flat_double_ended_queue * | fdeq, |
size_t | n, | ||
void const * | elems | ||
) |
Push the range of user types to the back of the fdeq. O(N).
[in] | fdeq | pointer to the fdeq. |
[in] | n | the number of user types in the elems range. |
[in] | elems | a pointer to the array of user types. |
Note that if no allocation is permitted the fdeq behaves as a ring buffer. Therefore, pushing a range that will exceed capacity will overwrite elements at the beginning of the fdeq.
void * ccc_fdeq_push_front | ( | ccc_flat_double_ended_queue * | fdeq, |
void const * | elem | ||
) |
Push the user type to the front of the fdeq. O(1) if no allocation permission amortized O(1) if allocation permission is given and a resize is required.
[in] | fdeq | a pointer to the fdeq. |
[in] | elem | a pointer to the user type to insert into the fdeq. |
ccc_result ccc_fdeq_push_front_range | ( | ccc_flat_double_ended_queue * | fdeq, |
size_t | n, | ||
void const * | elems | ||
) |
Push the range of user types to the front of the fdeq. O(N).
[in] | fdeq | a pointer to the fdeq. |
[in] | n | the number of user types in the elems range. |
[in] | elems | a pointer to the array of user types. |
Note that if no allocation is permitted the fdeq behaves as a ring buffer. Therefore, pushing a range that will exceed capacity will overwrite elements at the back of the fdeq.
void * ccc_fdeq_rbegin | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a pointer to the back element of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_rend | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return a pointer to the start element. It may not be accessed. O(1).
[in] | fdeq | a pointer to the fdeq. |
void * ccc_fdeq_rnext | ( | ccc_flat_double_ended_queue const * | fdeq, |
void const * | iter_ptr | ||
) |
Return the next element in the fdeq moving back to front. O(1).
[in] | fdeq | a pointer to the fdeq. |
[in] | iter_ptr | the current element in the fdeq. |
size_t ccc_fdeq_size | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return the size of the fdeq. O(1).
[in] | fdeq | a pointer to the fdeq. |
bool ccc_fdeq_validate | ( | ccc_flat_double_ended_queue const * | fdeq | ) |
Return true if the internal invariants of the fdeq.
[in] | fdeq | a pointer to the fdeq. |