Zix¶
Zix is a lightweight C library of portability wrappers and data structures.
1 Using Zix¶
1.1 Error Handling¶
Most functions return a ZixStatus
which describes whether they succeeded (with zero, or ZIX_STATUS_SUCCESS
)
or why they failed.
The human-readable message for a status can be retrieved with zix_strerror()
.
1.2 String Views¶
For performance reasons,
many functions in zix that take a string take a ZixStringView
,
rather than a bare pointer.
This forces code to be explicit about string measurement,
which discourages common patterns of repeated measurement of the same string.
For convenience, several macros and functions are provided for constructing string views:
Initializes a string view from a string literal. Note that this may only be used with inlined string literals, passing a pointer to an arbitrary string is undefined behaviour, for example:
static const ZixStringView hello = ZIX_STATIC_STRING("hello");
Constructs a view of an empty string, for example:
ZixStringView empty = zix_empty_string();
Constructs a view of an arbitrary string, for example:
ZixStringView view = zix_string(string_pointer);This calls
strlen
to measure the string. Modern compilers should optimize this away if the parameter is a literal.
Constructs a view of a slice of a string with an explicit length, for example:
ZixStringView slice = zix_substring(string_pointer, 4);This can also be used to create a view of a pre-measured string. If the length a dynamic string is already known, this is faster than
zix_string()
, since it avoids redundant measurement.
These constructors can be used inline when passing parameters, but if the same dynamic string is used several times, it is better to make a string view variable to avoid redundant measurement.
1.3 Allocation¶
Functions or objects that allocate memory take a pointer to a ZixAllocator
,
which can be used to customize the allocation scheme.
To simply use the system allocator,
callers can pass NULL
,
which is equivalent to zix_default_allocator()
.
Zix includes one implementation of a custom allocator,
a very simple bump-pointer allocator which can be created with zix_bump_allocator()
.
A convenience API for using custom allocators
(or the system allocator as a fallback)
is provided with functions like zix_malloc()
that reflect the standard C API.
Memory allocated with an allocator must be freed by calling zix_free()
with the same allocator.
2 Zix C API¶
2.1 Utilities¶
2.1.1 Attributes¶
-
ZIX_BEGIN_DECLS¶
Begin public API definitions.
-
ZIX_END_DECLS¶
End public API definitions.
-
ZIX_ALWAYS_INLINE_FUNC¶
Should absolutely always be inlined.
-
ZIX_PURE_FUNC¶
Only reads memory.
-
ZIX_CONST_FUNC¶
Only reads its parameters.
-
ZIX_MALLOC_FUNC¶
Allocates memory with no pointers in it.
-
ZIX_NODISCARD¶
Returns a value that must be used.
-
ZIX_PURE_API¶
A pure function in the public API that only reads memory.
-
ZIX_CONST_API¶
A const function in the public API that is pure and only reads parameters.
-
ZIX_MALLOC_API¶
A malloc function in the public API that returns allocated memory.
-
ZIX_LOG_FUNC(fmt, arg1)¶
A function with printf-like parameters.
-
ZIX_UNUSED(name)¶
An unused parameter.
-
ZIX_NONNULL¶
A non-null pointer.
-
ZIX_NULLABLE¶
A nullable pointer.
-
ZIX_ALLOCATED¶
An allocated (possibly null) pointer.
-
ZIX_UNSPECIFIED¶
A pointer with unspecified nullability.
2.1.2 Status Codes¶
-
enum ZixStatus¶
A status code returned by functions.
-
enumerator ZIX_STATUS_SUCCESS¶
Success.
-
enumerator ZIX_STATUS_ERROR¶
Unknown error.
-
enumerator ZIX_STATUS_NO_MEM¶
Out of memory.
-
enumerator ZIX_STATUS_NOT_FOUND¶
Not found.
-
enumerator ZIX_STATUS_EXISTS¶
Exists.
-
enumerator ZIX_STATUS_BAD_ARG¶
Bad argument.
-
enumerator ZIX_STATUS_BAD_PERMS¶
Bad permissions.
-
enumerator ZIX_STATUS_REACHED_END¶
Reached end.
-
enumerator ZIX_STATUS_TIMEOUT¶
Timeout.
-
enumerator ZIX_STATUS_OVERFLOW¶
Overflow.
-
enumerator ZIX_STATUS_NOT_SUPPORTED¶
Not supported.
-
enumerator ZIX_STATUS_UNAVAILABLE¶
Resource unavailable.
-
enumerator ZIX_STATUS_NO_SPACE¶
Out of storage space.
-
enumerator ZIX_STATUS_MAX_LINKS¶
Too many links.
-
enumerator ZIX_STATUS_SUCCESS¶
2.1.3 String View¶
-
struct ZixStringView¶
An immutable slice of a string.
This type is used for many string parameters, to allow referring to slices of strings in-place and to avoid redundant string measurement.
-
const char *data¶
Pointer to the first character.
-
size_t length¶
Length of string in bytes.
-
const char *data¶
-
ZixStringView zix_empty_string(void)¶
Return a view of an empty string.
-
ZixStringView zix_substring(const char *const str, const size_t len)¶
Return a view of a substring, or a premeasured string.
This makes either a view of a slice of a string (which may not be null terminated), or a view of a string that has already been measured. This is faster than zix_string() for dynamic strings since it does not call
strlen
, so should be used when the length of the string is already known.- Parameters:
str – Pointer to the start of the substring.
len – Length of the substring in bytes, not including the trailing null terminator if present.
-
ZixStringView zix_string(const char *const str)¶
Return a view of an entire string by measuring it.
This makes a view of the given string by measuring it with
strlen
.- Parameters:
str – Pointer to the start of a null-terminated C string, or null.
-
char *zix_string_view_copy(ZixAllocator *allocator, ZixStringView view)¶
Copy a string view into a newly allocated null-terminated string.
-
bool zix_string_view_equals(ZixStringView lhs, ZixStringView rhs)¶
Return true if both string views refer to equal strings.
This may be significantly faster than a full string comparison, because it has fast paths for when the operands have different lengths, or point to the same string data.
-
ZIX_STATIC_STRING(s)¶
Initialize a string view from a string literal.
2.2 Allocation¶
2.2.1 Allocator¶
-
struct ZixAllocatorImpl¶
Definition of
ZixAllocator
.-
ZixMallocFunc malloc¶
Allocate.
-
ZixCallocFunc calloc¶
Allocate and zero.
-
ZixReallocFunc realloc¶
Reallocate.
-
ZixFreeFunc free¶
Free.
-
ZixAlignedAllocFunc aligned_alloc¶
Allocate aligned.
-
ZixAlignedFreeFunc aligned_free¶
Free aligned.
-
ZixMallocFunc malloc¶
-
typedef struct ZixAllocatorImpl ZixAllocator¶
A memory allocator.
This object-like structure provides an interface like the standard C functions malloc(), calloc(), realloc(), free(), and aligned_alloc(). It contains function pointers that differ from their standard counterparts by taking a context parameter (a pointer to this struct), which allows the user to implement custom stateful allocators.
-
typedef void *(*ZixMallocFunc)(ZixAllocator *allocator, size_t size)¶
General malloc-like memory allocation function.
This works like the standard C malloc(), except has an additional handle parameter for implementing stateful allocators without static data.
-
typedef void *(*ZixCallocFunc)(ZixAllocator *allocator, size_t nmemb, size_t size)¶
General calloc-like memory allocation function.
This works like the standard C calloc(), except has an additional handle parameter for implementing stateful allocators without static data.
-
typedef void *(*ZixReallocFunc)(ZixAllocator *allocator, void *ptr, size_t size)¶
General realloc-like memory reallocation function.
This works like the standard C remalloc(), except has an additional handle parameter for implementing stateful allocators without static data.
-
typedef void (*ZixFreeFunc)(ZixAllocator *allocator, void *ptr)¶
General free-like memory deallocation function.
This works like the standard C remalloc(), except has an additional handle parameter for implementing stateful allocators without static data.
-
typedef void *(*ZixAlignedAllocFunc)(ZixAllocator *allocator, size_t alignment, size_t size)¶
General aligned_alloc-like memory deallocation function.
This works like the standard C aligned_alloc(), except has an additional handle parameter for implementing stateful allocators without static data.
-
typedef void (*ZixAlignedFreeFunc)(ZixAllocator *allocator, void *ptr)¶
General aligned memory deallocation function.
This works like the standard C free(), but must be used to free memory allocated with the aligned_alloc() method of the allocator. This allows portability to systems (like Windows) that can’t use the same free function in these cases.
-
ZixAllocator *zix_default_allocator(void)¶
Return the default allocator which simply uses the system allocator.
-
void *zix_malloc(ZixAllocator *const allocator, const size_t size)¶
Convenience wrapper that defers to malloc() if allocator is null.
-
void *zix_calloc(ZixAllocator *const allocator, const size_t nmemb, const size_t size)¶
Convenience wrapper that defers to calloc() if allocator is null.
-
void *zix_realloc(ZixAllocator *const allocator, void *const ptr, const size_t size)¶
Convenience wrapper that defers to realloc() if allocator is null.
-
void zix_free(ZixAllocator *const allocator, void *const ptr)¶
Convenience wrapper that defers to free() if allocator is null.
-
void *zix_aligned_alloc(ZixAllocator *const allocator, const size_t alignment, const size_t size)¶
Convenience wrapper that defers to the system allocator if allocator is null.
-
void zix_aligned_free(ZixAllocator *const allocator, void *const ptr)¶
Convenience wrapper that defers to the system allocator if allocator is null.
2.2.2 Bump Allocator¶
-
struct ZixBumpAllocator¶
A simple bump-pointer allocator that never frees.
This is about the simplest possible allocator that is useful in practice. It uses a user-provided memory buffer with a fixed size, and allocates by simply “bumping” the top offset like a stack. This approach is simple, extremely fast, and hard real-time safe, but at the cost of being limited to narrow use cases since there is (almost) no support for deallocation.
Using this allocator requires knowing up-front the total amount of memory that will be allocated (without reuse). Typically this makes sense in short scopes like a function call.
This allocator adheres to standard C semantics as much as possible, but is much more restrictive. Specifically:
All allocations are aligned to sizeof(uintmax_t).
Both free() and realloc() only work on the most recently allocated pointer, essentially serving as a cheap pop and pop-push, respectively.
Calling free() means that realloc() will fail and free() will do nothing until the next allocation. In other words, free() can’t be used twice in a row.
There is no relocation: realloc() always returns either the input pointer, or null.
-
ZixAllocator base¶
Base allocator instance.
-
void *buffer¶
User-owned memory buffer.
-
size_t last¶
Last allocation offset in bytes.
-
size_t top¶
Stack top/end offset in bytes.
-
size_t capacity¶
Size of buffer in bytes (the maximum top)
-
ZixBumpAllocator zix_bump_allocator(size_t capacity, void *buffer)¶
Return a bump allocator that works within a provided buffer.
2.3 Algorithms¶
2.3.1 Digest¶
Functions to generate a short “digest” of data with minimal collisions.
These are good general-purpose hash functions for indexing arbitrary data, but are not necessarily stable across platforms and should never be used for cryptographic purposes.
-
uint32_t zix_digest32(uint32_t seed, const void *buf, size_t len)¶
Return a 32-bit hash of a buffer.
This can be used for any size or alignment.
-
uint32_t zix_digest32_aligned(uint32_t seed, const void *buf, size_t len)¶
Return a 32-bit hash of an aligned buffer.
Both the buffer and size must be aligned to 32 bits. For data that fits these requirements, this is equivalent to, but faster than,
zix_digest32()
.
-
uint64_t zix_digest64(uint64_t seed, const void *buf, size_t len)¶
Return a 64-bit hash of a buffer.
This can be used for any size or alignment.
-
uint64_t zix_digest64_aligned(uint64_t seed, const void *buf, size_t len)¶
Return a 64-bit hash of an aligned buffer.
Both the buffer and size must be aligned to 64 bits. For data that fits these requirements, this is equivalent to, but faster than,
zix_digest64()
.
-
size_t zix_digest(size_t seed, const void *buf, size_t len)¶
Return a pointer-sized hash of a buffer.
This can be used for any size or alignment.
Internally, this simply dispatches to
zix_digest32()
orzix_digest64()
as appropriate.
-
size_t zix_digest_aligned(size_t seed, const void *buf, size_t len)¶
Return a pointer-sized hash of an aligned buffer.
Both the buffer and size must be aligned to the pointer size. For data that fits these requirements, this is equivalent to, but faster than,
zix_digest()
.Internally, this simply dispatches to
zix_digest32_aligned()
orzix_digest64_aligned()
as appropriate.
2.4 Data Structures¶
2.4.1 BTree¶
2.4.1.1 Setup¶
-
typedef struct ZixBTreeImpl ZixBTree¶
A B-Tree.
-
typedef int (*ZixBTreeCompareFunc)(const void *a, const void *b, const void *user_data)¶
Function for comparing two B-Tree elements.
-
typedef void (*ZixBTreeDestroyFunc)(void *ptr, const void *user_data)¶
Function to destroy a B-Tree element.
-
ZixBTree *zix_btree_new(ZixAllocator *allocator, ZixBTreeCompareFunc cmp, const void *cmp_data)¶
Create a new (empty) B-Tree.
The given comparator must be a total ordering and is used to internally organize the tree and look for values exactly.
Searching can be done with a custom comparator that supports wildcards, see
zix_btree_lower_bound()
for details.
-
void zix_btree_free(ZixBTree *t, ZixBTreeDestroyFunc destroy, const void *destroy_data)¶
Free
t
and all the nodes it contains.- Parameters:
t – The tree to free.
destroy – Function to call once for every value in the tree. This can be used to free values if they are dynamically allocated.
destroy_data – Opaque user data pointer to pass to
destroy
.
-
void zix_btree_clear(ZixBTree *t, ZixBTreeDestroyFunc destroy, const void *destroy_data)¶
Clear everything from
t
, leaving it empty.- Parameters:
t – The tree to clear.
destroy – Function called exactly once for every value in the tree, just before that value is removed from the tree.
destroy_data – Opaque user data pointer to pass to
destroy
.
-
ZIX_BTREE_MAX_HEIGHT¶
The maximum height of a
ZixBTree
.This is exposed because it determines the size of iterators, which are statically sized so they can used on the stack. The usual degree (or “fanout”) of a B-Tree is high enough that a relatively short tree can contain many elements. With the default page size of 4 KiB, the default height of 6 is enough to store trillions.
2.4.1.2 Iteration¶
-
struct ZixBTreeIter¶
An iterator over a B-Tree.
Note that modifying the tree invalidates all iterators.
The contents of this type are considered an implementation detail and should not be used directly by clients. They are nevertheless exposed here so that iterators can be allocated on the stack.
-
ZixBTreeNode *nodes[ZIX_BTREE_MAX_HEIGHT]¶
Node stack.
-
uint16_t indexes[ZIX_BTREE_MAX_HEIGHT]¶
Index stack.
-
uint16_t level¶
Current level.
-
ZixBTreeNode *nodes[ZIX_BTREE_MAX_HEIGHT]¶
-
typedef struct ZixBTreeNodeImpl ZixBTreeNode¶
An opaque node in a B-Tree.
-
const ZixBTreeIter zix_btree_end_iter¶
A static end iterator for convenience.
-
void *zix_btree_get(ZixBTreeIter ti)¶
Return the data at the given position in the tree.
-
ZixBTreeIter zix_btree_begin(const ZixBTree *t)¶
Return an iterator to the first (smallest) element in
t
-
ZixBTreeIter zix_btree_end(const ZixBTree *t)¶
Return an iterator to the end of
t
(one past the last element)
-
bool zix_btree_iter_equals(ZixBTreeIter lhs, ZixBTreeIter rhs)¶
Return true iff
lhs
is equal torhs
-
bool zix_btree_iter_is_end(const ZixBTreeIter i)¶
Return true iff
i
is an iterator at the end of a tree.
-
ZixStatus zix_btree_iter_increment(ZixBTreeIter *i)¶
Increment
i
to point to the next element in the tree.
-
ZixBTreeIter zix_btree_iter_next(ZixBTreeIter iter)¶
Return an iterator one past
iter
2.4.1.3 Modification¶
-
ZixStatus zix_btree_insert(ZixBTree *t, void *e)¶
Insert the element
e
intot
.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
on success,ZixStatus.ZIX_STATUS_EXISTS
, orZixStatus.ZIX_STATUS_NO_MEM
.
-
ZixStatus zix_btree_remove(ZixBTree *t, const void *e, void **out, ZixBTreeIter *next)¶
Remove the element
e
fromt
.- Parameters:
t – Tree to remove from.
e – Value to remove.
out – Set to point to the removed pointer (which may not equal
e
).next – On successful return, set to point at element immediately following
e
.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
on success, orZixStatus.ZIX_STATUS_NOT_FOUND
.
2.4.1.4 Searching¶
-
ZixStatus zix_btree_find(const ZixBTree *t, const void *e, ZixBTreeIter *ti)¶
Set
ti
to an element exactly equal toe
int
.If no such item exists,
ti
is set to the end.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
on success, orZixStatus.ZIX_STATUS_NOT_FOUND
.
-
ZixStatus zix_btree_lower_bound(const ZixBTree *t, ZixBTreeCompareFunc compare_key, const void *compare_key_data, const void *key, ZixBTreeIter *ti)¶
Set
ti
to the smallest element int
that is not less thane
.The given comparator must be compatible with the tree comparator, that is, any two values must have the same ordering according to both. Within this constraint, it may implement fuzzier searching by handling special search key values, for example with wildcards.
If the search key
e
compares equal to many values in the tree, thenti
will be set to the least such element.The comparator is always called with an actual value in the tree as the first argument, and
key
as the second argument.- Returns:
2.4.2 Hash¶
2.4.2.1 Datatypes¶
-
typedef void ZixHashKey¶
The type of a key within a record.
-
typedef void ZixHashRecord¶
The type of a hash table record.
-
typedef void ZixHashSearchData¶
User data for key comparison function.
-
typedef struct ZixHashImpl ZixHash¶
A hash table.
This is an open addressing hash table that stores pointers to arbitrary user data. Internally, everything is stored in a single flat array that is resized when necessary.
The single user-provided pointer that is stored in the table is called a “record”. A record contains a “key”, which is accessed via a user-provided function. This design supports storing large records (which may be expensive to allocate/construct) without requiring an entire record to search. Simple atomic values can be stored by providing a trivial identity function as a key function.
The table uses power of 2 sizes with a growth factor of 2, so that hash values can be folded into an array index using bitwise AND as a fast modulo. This means that only the necessary low bits of the hash value will be used, so the hash function must be well-balanced within this range. More or less any good modern hash algorithm will be fine, but beware, for example, hash functions that assume they are targeting a table with a prime size.
Since this doubles and halves in size, it may not be an optimal choice if memory reuse is a priority. A growth factor of 1.5 with fast range reduction may be a better choice there, at the cost of requiring 128-bit arithmetic on 64-bit platforms, and indexing operations being slightly more expensive.
-
typedef size_t ZixHashCode¶
A full hash code for a key which is not folded down to the table size.
2.4.2.2 Setup¶
-
typedef const ZixHashKey *(*ZixKeyFunc)(const ZixHashRecord *record)¶
User function for getting the key of a record.
-
typedef ZixHashCode (*ZixHashFunc)(const ZixHashKey *key)¶
User function for computing the hash of a key.
-
typedef bool (*ZixKeyEqualFunc)(const ZixHashKey *a, const ZixHashKey *b)¶
User function for determining if two keys are truly equal.
-
ZixHash *zix_hash_new(ZixAllocator *allocator, ZixKeyFunc key_func, ZixHashFunc hash_func, ZixKeyEqualFunc equal_func)¶
Create a new hash table.
- Parameters:
allocator – Allocator used for the internal array.
key_func – A function to retrieve the key from a record.
hash_func – The key hashing function.
equal_func – A function to test keys for equality.
2.4.2.3 Iteration¶
-
typedef size_t ZixHashIter¶
An iterator to an entry in a hash table.
This is really just an index, but should be considered opaque to the user and only used via the provided API and equality comparison.
-
ZixHashIter zix_hash_begin(const ZixHash *hash)¶
Return an iterator to the first record in a hash, or the end if it is empty.
-
ZixHashIter zix_hash_end(const ZixHash *hash)¶
Return an iterator one past the last possible record in a hash.
-
ZixHashRecord *zix_hash_get(const ZixHash *hash, ZixHashIter i)¶
Return the record pointed to by an iterator.
-
ZixHashIter zix_hash_next(const ZixHash *hash, ZixHashIter i)¶
Return an iterator that has been advanced to the next record in a hash.
2.4.2.4 Modification¶
-
struct ZixHashInsertPlan¶
A “plan” (position) to insert a record in a hash table.
This contains everything necessary to insert a record, except the record itself. It is exposed to split up insertion so that records only need to be allocated if an existing record is not found, but the contents should be considered opaque to the user.
-
ZixHashCode code¶
Hash code used to find this position.
-
ZixHashIter index¶
Index into hash table.
-
ZixHashCode code¶
-
typedef bool (*ZixKeyMatchFunc)(const ZixHashKey *key, const ZixHashSearchData *user_data)¶
User function for determining if a key matches in a custom search.
-
ZixHashInsertPlan zix_hash_plan_insert(const ZixHash *hash, const ZixHashKey *key)¶
Find the best position to insert a record with the given key.
This searches the hash table and returns either the position of an existing equivalent record, or the best available position to insert a new one. The difference can be determined with
zix_hash_record_at()
.If the returned position is not occupied, then it is valid to insert a record with this key using
zix_hash_insert_at()
until the hash table is modified (which invalidates the position).
-
ZixHashInsertPlan zix_hash_plan_insert_prehashed(const ZixHash *hash, ZixHashCode code, ZixKeyMatchFunc predicate, const ZixHashSearchData *user_data)¶
Find the best position to insert a record with a custom search.
This is an advanced low-level version of
zix_hash_plan_insert()
. It allows a precalculated hash code to be given, along with a custom search predicate. These must be compatible with the functions that manage the hash table: trivial usage would be to pass the equality function used by the hash and the key to search for.This is exposed to make it possible to avoid constructing a key at all when potentially inserting. For example, if keys are structures with a fixed header and a variably-sized body, and you have a separate header and body this can be used to find an insert position without having to allocate a contiguous key.
Note that care must be taken when using this function: improper use can corrupt the hash table. The hash code given must be correct for the key to be inserted, and the predicate must return true only if the key it is called with (the first argument) matches the key to be inserted.
-
ZixHashRecord *zix_hash_record_at(const ZixHash *hash, ZixHashInsertPlan position)¶
Return the record at the given position, or null.
This is used to determine if a position returned from
zix_hash_plan_insert()
can be used to insert a new record, or to access the existing matching record.
-
ZixStatus zix_hash_insert_at(ZixHash *hash, ZixHashInsertPlan position, ZixHashRecord *record)¶
Insert a record at a specific position.
The position must have been found with an earlier call to
zix_hash_plan_insert()
, and no modifications must have been made to the hash table since.- Parameters:
hash – The hash table.
position – The position for the new record.
record – The record to insert which, on success, can now be considered owned by the hash table.
- Returns:
ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS if a record already exists at this position, or ZIX_STATUS_NO_MEM if growing the hash table failed.
-
ZixStatus zix_hash_insert(ZixHash *hash, ZixHashRecord *record)¶
Insert a record.
This is a trivial wrapper for
zix_hash_plan_insert()
andzix_hash_insert_at()
that is more convenient when record construction is not expensive.- Parameters:
hash – The hash table.
record – The record to insert which, on success, can now be considered owned by the hash table.
- Returns:
ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS, or ZIX_STATUS_NO_MEM.
-
ZixStatus zix_hash_erase(ZixHash *hash, ZixHashIter i, ZixHashRecord **removed)¶
Erase a record at a specific position.
- Parameters:
hash – The hash table to remove the record from.
i – Iterator to the record to remove. This must be a valid iterator from an earlier call to
zix_hash_find()
, that is, the hash table must not have been modified since.removed – Set to the removed record, or null.
- Returns:
ZIX_STATUS_SUCCES or ZIX_STATUS_BAD_ARG if
i
does not point at a removable record.
-
ZixStatus zix_hash_remove(ZixHash *hash, const ZixHashKey *key, ZixHashRecord **removed)¶
Remove a record.
- Parameters:
hash – The hash table.
key – The key of the record to remove.
removed – Set to the removed record, or null.
- Returns:
ZIX_STATUS_SUCCES or ZIX_STATUS_NOT_FOUND.
2.4.2.5 Searching¶
-
ZixHashIter zix_hash_find(const ZixHash *hash, const ZixHashKey *key)¶
Find the position of a record with a given key.
- Parameters:
hash – The hash table to search.
key – The key of the desired record.
- Returns:
An iterator to the matching record, or the end iterator if no such record exists.
-
ZixHashRecord *zix_hash_find_record(const ZixHash *hash, const ZixHashKey *key)¶
Find a record with a given key.
This is essentially the same as
zix_hash_find()
, but returns a pointer to the record for convenience.- Parameters:
hash – The hash table to search.
key – The key of the desired record.
- Returns:
A pointer to the matching record, of null if no such record exists.
2.4.3 Ring¶
2.4.3.1 Setup¶
-
typedef struct ZixRingImpl ZixRing¶
A lock-free ring buffer.
Thread-safe (with a few noted exceptions) for a single reader and single writer, and realtime-safe on both ends.
-
ZixRing *zix_ring_new(ZixAllocator *allocator, uint32_t size)¶
Create a new ring.
- Parameters:
allocator – Allocator for the ring object and its array.
size – Minimum size of the ring in bytes (rounded up to a power of 2).
Note that one byte of the ring is reserved, so in order to be able to write
n
bytes to the ring at once,size
must ben + 1
.
-
void zix_ring_free(ZixRing *ring)¶
Destroy a ring.
This frees the ring structure and its buffer, discarding its contents.
-
ZixStatus zix_ring_mlock(ZixRing *ring)¶
Lock the ring data into physical memory.
This function is NOT thread safe or real-time safe, but it should be called after
zix_ring_new()
to lock all ring memory to avoid page faults while using the ring.
2.4.3.2 Reading¶
Functions that may only be called by the read thread.
-
uint32_t zix_ring_read_space(const ZixRing *ring)¶
Return the number of bytes available for reading.
This function returns at most one less than the ring’s buffer size.
-
uint32_t zix_ring_peek(ZixRing *ring, void *dst, uint32_t size)¶
Read from the ring without advancing the read head.
- Parameters:
ring – The ring to read data from.
dst – The buffer to write data to.
size – The number of bytes to read from
ring
and write todst
.
- Returns:
The number of bytes read, which is either
size
on success, or zero on failure.
-
uint32_t zix_ring_read(ZixRing *ring, void *dst, uint32_t size)¶
Read from the ring and advance the read head.
- Parameters:
ring – The ring to read data from.
dst – The buffer to write data to.
size – The number of bytes to read from
ring
and write todst
.
- Returns:
The number of bytes read, which is either
size
on success, or zero on failure.
2.4.3.3 Writing¶
Functions that may only be called by the write thread.
-
struct ZixRingTransaction¶
A transaction for writing data in multiple parts.
The simple
zix_ring_write()
can be used to write an atomic message that will immediately be visible to the reader, but transactions allow data to be written in several chunks before being “committed” and becoming readable. This can be useful for things like prefixing messages with a header without needing an allocated buffer to construct the “packet”.The contents of this structure are an implementation detail and must not be manipulated by the user.
-
uint32_t read_head¶
Read head at the start of the transaction.
-
uint32_t write_head¶
Write head if the transaction were committed.
-
uint32_t read_head¶
-
uint32_t zix_ring_write_space(const ZixRing *ring)¶
Return the number of bytes available for writing.
This function returns at most one less than the ring’s buffer size.
-
uint32_t zix_ring_write(ZixRing *ring, const void *src, uint32_t size)¶
Write data to the ring.
This writes a contiguous input buffer of bytes to the ring.
- Parameters:
ring – The ring to write data to.
src – The buffer to read data from.
size – The number of bytes to read from
src
and write toring
.
- Returns:
The number of bytes written, which is either
size
on success, or zero on failure.
-
ZixRingTransaction zix_ring_begin_write(ZixRing *ring)¶
Begin a write.
The returned transaction is initially empty. Data can be written to it by calling
zix_ring_amend_write()
one or more times, then finishing withzix_ring_commit_write()
.Note that the returned “transaction” is not meant to be long-lived: a call to this function should be (more or less) immediately followed by calls to
zix_ring_amend_write()
then a call tozix_ring_commit_write()
.- Parameters:
ring – The ring to write data to.
- Returns:
A new empty transaction.
-
ZixStatus zix_ring_amend_write(ZixRing *ring, ZixRingTransaction *tx, const void *src, uint32_t size)¶
Amend the current write with some data.
The data is written immediately after the previously amended data, as if they were written contiguously with a single write call. This data is not visible to the reader until
zix_ring_commit_write()
is called.If any call to this function returns an error, then the transaction is invalid and must not be committed. No cleanup is necessary for an invalid transaction. Any bytes written while attempting the transaction will remain in the free portion of the buffer and be overwritten by subsequent writes.
- Parameters:
ring – The ring this transaction is writing to.
tx – The active transaction, from
zix_ring_begin_write()
.src – Pointer to the data to write.
size – Length of data to write in bytes.
- Returns:
ZixStatus.ZIX_STATUS_NO_MEM
orZixStatus.ZIX_STATUS_SUCCESS
.
-
ZixStatus zix_ring_commit_write(ZixRing *ring, const ZixRingTransaction *tx)¶
Commit the current write.
This atomically updates the state of the ring, so that the reader will observe the data written during the transaction.
This function usually shouldn’t be called for any transaction which
zix_ring_amend_write()
returned an error for.- Parameters:
ring – The ring this transaction is writing to.
tx – The active transaction, from
zix_ring_begin_write()
.
- Returns:
2.4.4 Tree¶
2.4.4.1 Setup¶
-
typedef struct ZixTreeImpl ZixTree¶
A balanced binary search tree.
-
typedef int (*ZixTreeCompareFunc)(const void *a, const void *b, const void *user_data)¶
Function for comparing two Tree elements.
-
typedef void (*ZixTreeDestroyFunc)(void *ptr, const void *user_data)¶
Function to destroy a Tree element.
-
ZixTree *zix_tree_new(ZixAllocator *allocator, bool allow_duplicates, ZixTreeCompareFunc cmp, void *cmp_data, ZixTreeDestroyFunc destroy, const void *destroy_user_data)¶
Create a new (empty) tree.
2.4.4.2 Iteration¶
-
void *zix_tree_get(const ZixTreeIter *ti)¶
Return the data associated with the given tree item.
-
ZixTreeIter *zix_tree_begin(ZixTree *t)¶
Return an iterator to the first (smallest) element in
t
-
ZixTreeIter *zix_tree_end(ZixTree *t)¶
Return an iterator the the element one past the last element in
t
-
bool zix_tree_iter_is_end(const ZixTreeIter *i)¶
Return true iff
i
is an iterator to the end of its tree.
-
ZixTreeIter *zix_tree_rbegin(ZixTree *t)¶
Return an iterator to the last (largest) element in
t
-
ZixTreeIter *zix_tree_rend(ZixTree *t)¶
Return an iterator the the element one before the first element in
t
-
bool zix_tree_iter_is_rend(const ZixTreeIter *i)¶
Return true iff
i
is an iterator to the reverse end of its tree.
-
ZixTreeIter *zix_tree_iter_next(ZixTreeIter *i)¶
Return an iterator that points to the element one past
i
-
ZixTreeIter *zix_tree_iter_prev(ZixTreeIter *i)¶
Return an iterator that points to the element one before
i
2.4.4.3 Modification¶
-
ZixStatus zix_tree_insert(ZixTree *t, void *e, ZixTreeIter **ti)¶
Insert the element
e
intot
and pointti
at the new element.
-
ZixStatus zix_tree_remove(ZixTree *t, ZixTreeIter *ti)¶
Remove the item pointed at by
ti
fromt
2.4.4.4 Searching¶
-
ZixStatus zix_tree_find(const ZixTree *t, const void *e, ZixTreeIter **ti)¶
Set
ti
to an element equal toe
int
.If no such item exists,
ti
is set to NULL.
2.5 Threading¶
2.5.1 Semaphore¶
-
typedef struct ZixSemImpl ZixSem¶
A counting semaphore.
This is an integer that is never negative, and has two main operations: increment (post) and decrement (wait). If a decrement can’t be performed (because the value is 0) the caller will be blocked until another thread posts and the operation can succeed.
Semaphores can be created with any starting value, but typically this will be 0 so the semaphore can be used as a simple signal where each post corresponds to one wait.
Semaphores are very efficient (much moreso than a mutex/cond pair). In particular, at least on Linux, post is async-signal-safe, which means it does not block and will not be interrupted. If you need to signal from a realtime thread, this is the most appropriate primitive to use.
-
ZixStatus zix_sem_init(ZixSem *sem, unsigned initial)¶
Create
sem
with the giveninitial
value.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
, or an unlikely error.
-
ZixStatus zix_sem_destroy(ZixSem *sem)¶
Destroy
sem
.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
, or an error.
-
ZixStatus zix_sem_post(ZixSem *sem)¶
Increment and signal any waiters.
Realtime safe.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifsem
was incremented,ZixStatus.ZIX_STATUS_OVERFLOW
if the maximum possible value would have been exceeded, orZixStatus.ZIX_STATUS_BAD_ARG
ifsem
is invalid.
-
ZixStatus zix_sem_wait(ZixSem *sem)¶
Wait until count is > 0, then decrement.
Obviously not realtime safe.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifsem
was decremented, orZixStatus.ZIX_STATUS_BAD_ARG
ifsem
is invalid.
-
ZixStatus zix_sem_try_wait(ZixSem *sem)¶
Non-blocking version of wait().
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifsem
was decremented,ZixStatus.ZIX_STATUS_UNAVAILABLE
if it was already zero, orZixStatus.ZIX_STATUS_BAD_ARG
ifsem
is invalid.
-
ZixStatus zix_sem_timed_wait(ZixSem *sem, uint32_t seconds, uint32_t nanoseconds)¶
Wait for an amount of time until count is > 0, then decrement if possible.
Obviously not realtime safe.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifsem
was decremented,ZixStatus.ZIX_STATUS_TIMEOUT
if it was still zero when the timeout was reached,ZixStatus.ZIX_STATUS_NOT_SUPPORTED
if the system does not support timed waits, orZixStatus.ZIX_STATUS_BAD_ARG
ifsem
is invalid.
2.5.2 Thread¶
-
typedef pthread_t ZixThread¶
A thread.
-
typedef void *ZixThreadResult¶
Thread function return type.
-
typedef ZixThreadResult (*ZixThreadFunc)(void*)¶
A thread function.
For portability reasons, the return type varies between platforms, and the return value is ignored. Thread functions should always return
ZIX_THREAD_RESULT
. This allows thread functions to be used directly by the system without any wrapper overhead.“Returning” a result, and communicating with the parent thread in general, can be done through the pointer argument.
-
ZixStatus zix_thread_create(ZixThread *thread, size_t stack_size, ZixThreadFunc function, void *arg)¶
Initialize
thread
to a new thread.The thread will immediately be launched, calling
function
witharg
as the only parameter.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
on success, orZixStatus.ZIX_STATUS_ERROR
.
-
ZixStatus zix_thread_join(ZixThread thread)¶
Join
thread
(block untilthread
exits).- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
on success, orZixStatus.ZIX_STATUS_ERROR
.
-
ZIX_THREAD_RESULT¶
Result returned from a thread function.
-
ZIX_THREAD_FUNC¶
Thread function attribute.
2.6 File System¶
2.6.1 Operations¶
2.6.1.1 Creation and Removal¶
-
enum ZixCopyOption¶
Options to control filesystem copy operations.
-
enumerator ZIX_COPY_OPTION_NONE¶
Report any error.
-
enumerator ZIX_COPY_OPTION_OVERWRITE_EXISTING¶
Replace existing file.
-
enumerator ZIX_COPY_OPTION_NONE¶
-
typedef uint32_t ZixCopyOptions¶
Bitwise OR of
ZixCopyOptions
values.
-
ZixStatus zix_copy_file(ZixAllocator *allocator, const char *src, const char *dst, ZixCopyOptions options)¶
Copy the file at path
src
to pathdst
.If supported by the system, a lightweight copy will be made to take advantage of copy-on-write support in the filesystem. Otherwise, a simple deep copy will be made.
- Parameters:
allocator – Allocator used for a memory block for copying if necessary.
src – Path to source file to copy.
dst – Path to destination file to create.
options – Options to control the kind of copy and error conditions.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifdst
was successfully created, or an error.
-
ZixStatus zix_create_directory(const char *dir_path)¶
Create the directory
dir_path
with all available permissions.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifdir_path
was successfully created, or an error.
-
ZixStatus zix_create_directory_like(const char *dir_path, const char *existing_path)¶
Create the directory
dir_path
with the permissions of another.This is like
zix_create_directory()
, but will copy the permissions from another directory.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
ifdir_path
was successfully created, or an error.
-
ZixStatus zix_create_directories(ZixAllocator *allocator, const char *dir_path)¶
Create the directory
dir_path
and any parent directories if necessary.- Parameters:
allocator – Allocator used for a temporary path buffer if necessary.
dir_path – The path to the deepest directory to create.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
if all directories indir_path
were successfully created (or already existed), or an error.
-
ZixStatus zix_create_hard_link(const char *target_path, const char *link_path)¶
Create a hard link at path
link
that points to pathtarget
.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
, or an error.
-
ZixStatus zix_create_symlink(const char *target_path, const char *link_path)¶
Create a symbolic link at path
link
that points to pathtarget
.Note that portable code should use
zix_create_directory_symlink()
if the target is a directory, since this function won’t work for that on some systems (like Windows).- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
, or an error.
-
ZixStatus zix_create_directory_symlink(const char *target_path, const char *link_path)¶
Create a symbolic link at path
link
that points to the directorytarget
.This is a separate function from
zix_create_symlink()
because some systems (like Windows) require directory symlinks to be created specially.- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
, or an error.
-
char *zix_create_temporary_directory(ZixAllocator *allocator, const char *path_pattern)¶
Create a unique temporary directory at a given path pattern.
The last six characters of
pattern
must be “XXXXXX” and will be replaced with unique characters in the result.- Parameters:
allocator – Allocator used for the returned path.
path_pattern – A path pattern ending in “XXXXXX”.
- Returns:
The path of the created directory, or null.
2.6.1.2 Access¶
-
typedef void (*ZixDirEntryVisitFunc)(const char *path, const char *name, void *data)¶
Function for reading input bytes from a stream.
- Param path:
Path to the directory being visited.
- Param name:
Name of the directory entry.
- Param data:
Opaque user data.
-
void zix_dir_for_each(const char *path, void *data, ZixDirEntryVisitFunc f)¶
Visit every file in the directory at
path
.- Parameters:
path – A path to a directory.
data – Opaque user data that is passed to
f
.f – A function called on every entry in the directory. The
path
parameter is always the directory path passed to this function, thename
parameter is the name of the directory entry (not its full path).
-
bool zix_file_equals(ZixAllocator *allocator, const char *a_path, const char *b_path)¶
Return whether the given paths point to files with identical contents.
- Parameters:
allocator – Allocator used for a memory block for comparison if necessary.
a_path – Path to the first file to compare
b_path – Path to the second file to compare
- Returns:
True if the two files have byte-for-byte identical contents.
2.6.1.3 Resolution¶
-
char *zix_canonical_path(ZixAllocator *allocator, const char *path)¶
Return
path
as a canonical absolute path to a “real” file.This expands all symbolic links, relative references, and removes extra directory separators.
Since this function may return null anyway, it accepts a null parameter to allow easier chaining of path functions when only the final result is required, for example:
char* path = zix_path_join(alloc, "/some/dir", "filename.txt"); char* canonical = zix_canonical_path(path); if (canonical) { // Do something with the canonical path... } else { // No canonical path for some reason, we don't care which... }
- Returns:
A new canonical version of
path
, or null if it doesn’t exist.
2.6.1.4 Locking¶
-
enum ZixFileLockMode¶
A mode for locking files.
The same mode should be used for the lock and the corresponding unlock.
-
enumerator ZIX_FILE_LOCK_BLOCK¶
Block until the operation succeeds.
-
enumerator ZIX_FILE_LOCK_TRY¶
Fail if the operation would block.
-
enumerator ZIX_FILE_LOCK_BLOCK¶
-
ZixStatus zix_file_lock(FILE *file, ZixFileLockMode mode)¶
Set an advisory exclusive lock on
file
.- Parameters:
file – Handle for open file to lock.
mode – Lock mode.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
if the file was locked, or an error.
-
ZixStatus zix_file_unlock(FILE *file, ZixFileLockMode mode)¶
Remove an advisory exclusive lock on
file
.- Parameters:
file – Handle for open file to lock.
mode – Lock mode.
- Returns:
ZixStatus.ZIX_STATUS_SUCCESS
if the file was unlocked, or an error.
2.6.1.5 Queries¶
-
enum ZixFileType¶
A type of file.
Note that not all types may be supported, and the system may support additional types not enumerated here.
-
enumerator ZIX_FILE_TYPE_NONE¶
Non-existent file.
-
enumerator ZIX_FILE_TYPE_REGULAR¶
Regular file.
-
enumerator ZIX_FILE_TYPE_DIRECTORY¶
Directory.
-
enumerator ZIX_FILE_TYPE_SYMLINK¶
Symbolic link.
-
enumerator ZIX_FILE_TYPE_BLOCK¶
Special block file.
-
enumerator ZIX_FILE_TYPE_CHARACTER¶
Special character file.
-
enumerator ZIX_FILE_TYPE_FIFO¶
FIFO.
-
enumerator ZIX_FILE_TYPE_SOCKET¶
Socket.
-
enumerator ZIX_FILE_TYPE_UNKNOWN¶
Existing file with unknown type.
-
enumerator ZIX_FILE_TYPE_NONE¶
-
typedef ptrdiff_t ZixFileOffset¶
An offset into a file or a file size in bytes.
This is signed, and may be 64 bits even on 32-bit systems.
-
ZixFileType zix_file_type(const char *path)¶
Return the type of a file or directory, resolving symlinks.
-
ZixFileType zix_symlink_type(const char *path)¶
Return the type of a file or directory or symlink.
On Windows, a directory symlink (actually a “reparse point”) always appears as a directory.
-
ZixFileOffset zix_file_size(const char *path)¶
Return the size of a file.
Note that the returned value is signed and must be checked for errors. Non-negative values can be thought of as the “end” offset just past the last byte.
- Returns:
A non-negative size in bytes, or -1 on error.
2.6.1.6 Environment¶
-
char *zix_current_path(ZixAllocator *allocator)¶
Return the current working directory.
- Parameters:
allocator – Allocator used for the returned path.
-
char *zix_temp_directory_path(ZixAllocator *allocator)¶
Return the path to a directory suitable for making temporary files.
- Parameters:
allocator – Allocator used for the returned path.
- Returns:
A new path to a temporary directory, or null on error.
2.6.2 Paths¶
Functions for interpreting and manipulating paths.
These functions are purely lexical and do not access any filesystem.
2.6.2.1 Concatenation¶
-
char *zix_path_join(ZixAllocator *allocator, const char *a, const char *b)¶
Join path
a
and pathb
with a single directory separator between them.
2.6.2.2 Lexical Transformations¶
-
char *zix_path_preferred(ZixAllocator *allocator, const char *path)¶
Return
path
with preferred directory separators.The returned path will be a copy of
path
with any directory separators converted to the preferred separator (backslash on Windows, slash everywhere else).
-
char *zix_path_lexically_normal(ZixAllocator *allocator, const char *path)¶
Return
path
converted to normal form.Paths in normal form have all dot segments removed and use only a single preferred separator for all separators (that is, any number of separators is replaced with a single “" on Windows, and a single “/” everwhere else).
Note that this function doesn’t access the filesystem, so won’t do anything like case normalization or symbolic link dereferencing. For that, use
zix_canonical_path()
.
-
char *zix_path_lexically_relative(ZixAllocator *allocator, const char *path, const char *base)¶
Return
path
relative tobase
if possible.If
path
is not withinbase
, a copy is returned. Otherwise, an equivalent path relative tobase
is returned (which may contain up-references).
2.6.2.3 Decomposition¶
-
ZixStringView zix_path_root_name(const char *path)¶
Return the root name of
path
like “C:”, or null.
-
ZixStringView zix_path_root_directory(const char *path)¶
Return the root directory of
path
like “/” or “", or null.
-
ZixStringView zix_path_root_path(const char *path)¶
Return the root path of
path
, or null.The universal root path (in normal form) is “/”. Root paths are their own roots, but note that the path returned by this function may be partially normalized. For example, “/” is the root of “/”, “//”, “/.”, and “/..”.
On Windows, the root may additionally be an absolute drive root like “C:", a relative drive root like “C:”, or a network root like “//Host/”.
- Returns:
The newly allocated root path of
path
, or null if it has no root or allocation failed.
-
ZixStringView zix_path_relative_path(const char *path)¶
Return the relative path component of path without the root directory.
If the path has no relative path (because it is empty or a root path), this returns null.
-
ZixStringView zix_path_parent_path(const char *path)¶
Return the path to the directory that contains
path
.The parent of a root path is itself, but note that the path returned by this function may be partially normalized. For example, “/” is the parent of “/”, “//”, “/.”, and “/..”.
If
path
has a trailing separator, it is treated like an empty filename in a directory. For example, the parent of “/a/” is “/a”.If
path
is relative, then this returns either a relative path to the parent if possible, or null. For example, the parent of “a/b” is “a”.- Returns:
The newly allocated path to the parent of
path
, or null if it has no parent or allocation failed.
-
ZixStringView zix_path_filename(const char *path)¶
Return the filename component of
path
without any directories.The filename is the name after the last directory separator. If the path has no filename, this returns null.
-
ZixStringView zix_path_stem(const char *path)¶
Return the stem of the filename component of
path
.The “stem” is the filename without the extension, that is, everything up to the last “.” if “.” is not the first character.
-
ZixStringView zix_path_extension(const char *path)¶
Return the extension of the filename component of
path
.The “extension” is everything past the last “.” in the filename, if “.” is not the first character.
2.6.2.4 Queries¶
-
bool zix_path_has_root_path(const char *path)¶
Return true if
path
has a root path like “/” or “C:".
-
bool zix_path_has_root_name(const char *path)¶
Return true if
path
has a root name like “C:”.
-
bool zix_path_has_root_directory(const char *path)¶
Return true if
path
has a root directory like “/” or “".
-
bool zix_path_has_relative_path(const char *path)¶
Return true if
path
has a relative path “dir/file.txt”.
-
bool zix_path_has_parent_path(const char *path)¶
Return true if
path
has a parent path like “dir/”.
-
bool zix_path_has_filename(const char *path)¶
Return true if
path
has a filename like “file.txt”.
-
bool zix_path_has_stem(const char *path)¶
Return true if
path
has a stem like “file”.
-
bool zix_path_has_extension(const char *path)¶
Return true if
path
has an extension like “.txt”.
-
bool zix_path_is_absolute(const char *path)¶
Return true if
path
is an absolute path.
-
bool zix_path_is_relative(const char *path)¶
Return true if
path
is a relative path.
2.7 Environment¶
2.7.1 Variable Expansion¶
-
char *zix_expand_environment_strings(ZixAllocator *allocator, const char *string)¶
Expand shell-style variables in a string.
On Windows, this expands environment variable references like “%USERPROFILE%”. On POSIX systems, it expands environment variable references like “$HOME”, and the special path component “~”.
- Parameters:
allocator – Allocator used for returned string.
string – Input string to expand.
- Returns:
A newly allocated copy of
string
with variables expanded, or null.