This is an extension of the C standart library.
Download the cebus.h file and place it in your project directory.
In your C source file, include the library header and define the implementation as follows:
#define CEBUS_IMPLEMENTATION
#include "cebus.h"
You can add cebus as dependency in the pybuildc.toml
file.
[deps]
cebus = { dir = "path/to/cebus", type = "pybuildc" }
Include this file to include all the header files that are listed below.
#include <cebus.h>
To start using the dynamic array, you first need to create an instance of
Arena
for memory management and then initialize the dynamic array with the
specific type.
Arena arena = {0};
DA(int) vec = da_new(&arena);
Elements can be added to the dynamic array using da_push
, which automatically
resizes the array if additional space is required.
da_push(&vec, 69);
da_push(&vec, 420);
Access the first and last elements with da_first
and da_last
. Remove the
last element with da_pop
. Get any element with da_get
.
⚠️ These operations do not perform any bounds checks. So make sure your dynamic array has at least one element in it.
int first = da_first(&vec);
int last = da_last(&vec);
int nth = da_get(&vec, 3);
int popped = da_pop(&vec);
da_empty
: Use to check if the array has no elements.da_len
: Get the length of the dynamic array.da_clear
: Reset the length of the array to zero.da_init
: Initialize dynamic array.da_init_list
: Initialize dynamic array from a array.da_init_static
: Initialize dynamic array from a static array.da_copy
: Duplicate the contents of one dynamic array into another.
⚠️ These operations do not perform any bound checks.
da_insert
: Insert a value at a specified index.da_remove
: Remove a value at a specified index.
da_resize
: Adjust the capacity of the array to a specified size. Used for preallocating space.da_reserve
: Ensure there is enough space for additional elements. Used before adding multiple elements.
da_extend
: Add multiple elements from another array or list.da_map
: Transform elements of the array into another form and store them in a destination array.da_map_ctx
: Same but the mapping function also takes a context.da_filter
: Filter dynamic array with a filter function and place it into a destination.da_filter_ctx
: Filter dynamic array with a filter function, that takes avoid*
as a context, and place it into a destination.da_sort
: Sort the array using a comparison function.da_reverse
: Reverse the order of elements in the array.
My HashMap takes a unique approach: it stores only the hashes of keys, not the keys themselves. Most of the time, you don’t really need the original keys hanging around. If you find yourself in a situation where you do, just pair it with a dynamic array to cover those bases. See this example.
As for the values, the HashMap is set up to work with simple, primitive
data types. You can use pointers to handle more complex values. But make sure
they have the same lifetime as the HashMap
.
Creating a new HashMap
involves initializing an Arena
, then calling
hm_create
or hm_with_size
to initialize the hashmap with an optional initial
size:
Arena arena = {0};
HashMap* hm = hm_create(&arena);
Basic hashmap management includes clearing, copying, resizing, reserving capacity, and updating from another hashmap:
hm_clear
: Clears the hashmap.hm_copy
: Creates a copy of the hashmap.hm_resize
: Resizes the hashmap. Used for preallocating spacehm_reserve
: Reserves space in the hashmap. Used before adding multiple elements.hm_update
: Merges another hashmap into the current one.
Elements of various types can be inserted into the hashmap, including integers, floating-point numbers, and pointers:
hm_insert_<T>
: Insertu8
,i8
,u32
,i32
,u64
,i64
,usize
,f32
orf64
values.hm_insert_mut_ptr
,hm_insert_ptr
: Insert mutable or constant pointers.
Retrieve pointers to the values stored in the hashmap by their key hashes,
allowing for mutable or immutable access. Returns NULL
if key was not found:
⚠️ Avoid storing pointers from the hashmap for extended periods. Keeping these pointers beyond the immediate scope can lead to undefined behavior, as the underlying storage may change.
hm_get_<T>
: Getu8
,i8
,u32
,i32
,u64
,i64
,usize
,f32
orf64
pointers.hm_get_<T>_mut
: Getu8
,i8
,u32
,i32
,u64
,i64
,usize
,f32
orf64
pointers.
My Set
implementation follows the same principle as my HashMap
: it stores
only the hashes for lookup. This means you get efficient way to check
if something is in the set without the overhead of storing the actual elements.
It’s pretty handy for cases where you just need to keep track of existence
rather than the elements themselves.
To start using the set, you need to initialize an Arena
and create a new set:
Arena arena = {0};
Set set = set_create(&arena);
Perform basic set operations such as adding, removing, and extending sets with multiple elements:
set_add
: Add an element to the set. Returns false if the element already exists.set_remove
: Remove an element from the set.set_extend
: Add multiple elements to the set at once.
Copy and resize sets for handling dynamic set sizes:
set_copy
: Create a duplicate of a set.set_resize
: Adjust the capacity of the set. Used for preallocating space.set_reserve
: Reserves space in the set. Used before adding multiple elements
Query the set for the presence of elements, equality with another set, or subset and disjoint relationships:
set_contains
: Check if an element is in the set.set_eq
: Check if two sets are equal.set_subset
: Determine if one set is a subset of another.set_disjoint
: Determine if two sets have no elements in common.
Combine sets or find their differences using algebraic set operations:
set_intersection
: Find the intersection of two sets.set_difference
: Find the difference between two sets.set_union
: Combine two sets into a union.
The StringBuilder
provides functionality for efficiently constructing
strings.
⚠️ StringBuilder does not construct '\0' terminated strings.
-
StringBuilder sb_init(Arena *arena);
Initializes a newStringBuilder
instance, allocating its buffer using the provided memoryarena
. -
Str sb_to_str(StringBuilder *sb);
Converts the contents of theStringBuilder
to aStr
, effectively finalizing the string construction. -
void sb_append_parts(StringBuilder *sb, usize size, const char *s);
Appends parts of a string to theStringBuilder
, wheresize
specifies the number of characters to append, ands
points to the string parts to be appended. -
void sb_append_cstr(StringBuilder *sb, const char *cstr);
Appends a C-style null-terminated string to theStringBuilder
. -
void sb_append_str(StringBuilder *sb, Str str);
Appends aStr
type string to theStringBuilder
. -
void sb_append_fmt(StringBuilder *sb, const char *fmt, ...);
Appends a formatted string to theStringBuilder
, similar toprintf
style formatting. -
void sb_append_va(StringBuilder *sb, const char *fmt, va_list va);
Appends a formatted string and va_list to theStringBuilder
, similar tovprintf
style formatting.
To start using the library, initialize an Arena
struct:
Arena arena = {0};
Allocate memory from the arena using arena_alloc
or arena_calloc
for
uninitialized or zero-initialized memory, respectively:
int* i1 = arena_alloc(&arena, sizeof(int));
int* i2 = arena_alloc(&arena, sizeof(int));
int* i3 = arena_alloc(&arena, sizeof(int));
Deallocate all memory associated with an arena at once using arena_free
. This
operation frees all memory chunks allocated from the arena, providing a
significant performance advantage over individual deallocation:
arena_free(&arena);
The module also provides functions for more granular control over memory chunks within an arena:
arena_alloc_chunk
: Allocate a new chunk of memory.arena_calloc_chunk
: Allocates a new, zero initialized, chunk of memory.arena_realloc_chunk
: Reallocate a previously allocated chunk to a new size.arena_free_chunk
: Free a specific chunk of memory (advanced use cases).
arena_reset
: Reset all the allocations (does not free any memory).arena_size
: Gets the number of bytes allocated inside the arena.arena_real_size
: Gets the number of bytes allocated by the arena.
Use the assertion macros to validate conditions and log failures:
cebus_assert(1 == 1, "It needs a message");
cebus_assert(2 == 2, "It can even handle arguments: %d", 420);
cebus_assert(EXPR, FMT, ...)
: Asserts a condition and logs an error if the condition is false.cebus_assert_warn(EXPR, FMT, ...)
: Asserts a condition and logs a warning if the condition is false.cebus_assert_debug(EXPR, FMT, ...)
: Debug-only assert that logs a debug message if the condition is false.cebus_assert_return(EXPR, RETURN_VALUE)
: Asserts a condition and returns a value if the condition is false.
DEBUGBREAK()
: Uses platform specific debug break.UNREACHABLE()
: Prints error message in debug mode. In release mode it uses compiler intrensics.NOT_IMPLEMENTED()
: Prints error message
- Data Types: Defines types such as
u8
,i8
,u32
,i32
,usize
,f32
,f64
,Bytes
,Str
, andUtf8
. - Mathematical Constants: Defines
F64_PI
andF64_E
for mathematical operations. - Memory Units: Macros for
KILOBYTES
,MEGABYTES
, andGIGABYTES
to easily specify memory sizes. - Array Length:
ARRAY_LEN(A)
calculates the number of elements in an array. - Comparison Ordering: Enum
CmpOrdering
for less than, equal, and greater than comparisons. - Compiler Attributes: Macros such as
EXPORT
,NORETURN
,UNUSED
,PURE_FN
,CONST_FN
for compiler-specific attributes. - Likely and Unlikely:
LIKELY
andUNLIKELY
macros to hint the compiler about branch prediction. - Format Attribute:
FMT
macro to specify format strings for functions, enhancing type safety withprintf
-like functions.
ErrNew
: Initializes a new Error instance.ErrPanic
: Initializes an Error that will trigger a panic onerror_emit()
.ErrDefault
: Empty Error that will panic onerror_emit()
.
error_emit(E, code, fmt, ...)
initializes the passed in error.
void function_that_fails(Error *error) {
// ...
if (failure_condition) {
error_emit(error, error_code, "Error: %s", reason);
return;
}
}
error_context()
creates a context for you to handle the error. Panics if it falls through
Error error = ErrNew;
function_that_fails(&error);
error_context(&error, {
error_raise();
});
error_propagate()
creates a context. Does not panic if it falls through but also does not reset the error. :warning: if the error is never handled there will be a memory leak.
Error error = ErrNew;
function_that_fails(&error);
error_propagate(&error, {
return;
});
error_panic()
: Triggers a panic with the current error.error_except()
: Resets the error state.error_msg()
: Retrieves the error message.error_code(T)
: Retrieves the error code and casts it toT
.error_set_code()
: Sets a new error code.error_set_msg(fmt, ...)
: Sets a new error message and clears all notes.error_add_location()
: Adds current file and line location.error_add_note(fmt, ...)
: Adds a note to the error.
Call the function with the message format and arguments:
cebus_log_error("This is an error message: %s", error_details);
cebus_log_level(log_level, fmt, ...)
: Logs a message with the specified log level.cebus_log(fmt, ...)
: Logs a message.cebus_log_fatal(fmt, ...)
: Logs a fatal message.cebus_log_error(fmt, ...)
: Logs an error message.cebus_log_warning(fmt, ...)
: Logs a warning message.cebus_log_info(fmt, ...)
: Logs an info message.cebus_log_debug(fmt, ...)
: Logs a debug message (only in debug builds).cebus_log_trace(fmt, ...)
: Logs a trace message (only in debug builds).
- Platform Detection: Identifies the operating system, such as Linux or Windows.
- Architecture Detection: Determines the CPU architecture, such as x86_64 or ARM.
- Compiler Detection: Identifies the compiler used, such as GCC, Clang, or MSVC.
- CPU Bitness: Distinguishes between 32-bit and 64-bit environments.
- Byte Order: Defines the system's byte order (endianness).
cmd_exec(error, argc, argv)
: Executes a system command.cmd_exec_da(error, da)
: Executes a with a dynamic array.
Arena arena = {0};
Cmd cmd = cmd_new(&arena);
cmd_push(&cmd, STR("gcc"), STR("-o"), STR("main"));
Str cflags[] = {STR("-Wall"), STR("-Wextra")};
cmd_extend(&cmd, words);
DA(Str) files = da_new(&arena);
// collect files...
cmd_extend_da(&cmd, &files);
cmd_exec_da(ErrPanic, &cmd);
arena_free(&arena);
Defines CmdError
enum for different command execution outcomes:
CMD_OK
: Command executed successfully.CMD_FORK
: Error occurred while forking the process.CMD_NOT_FOUND
: Command not found, typically returns127
.
Error error = ErrNew;
Str args[] = {STR("/bin/echo"), STR("Hello, world!")};
cmd_exec(&error, 2, args);
error_context(&error, {
// Handle error
});
dll_load(path, error)
: Loads a dynamic link library.dll_close(handle)
: Closes an opened dynamic link library.dll_symbol(handle, symbol, error)
: Retrieves a symbol from the dynamic link library.
Error error = ErrNew;
Dll *myLib = dll_load(STR("myLibrary.dll"), &error);
error_context(&error, {
error_raise();
});
Function *myFunction = dll_symbol(myLib, "myFunctionName", &error);
// Use the function pointer as needed
dll_close(myLib);
-
Reading Files:
fs_file_read_bytes(filename, arena, error)
: Reads the entire file into a byte array.fs_file_read_str(filename, arena, error)
: Reads the entire file into a string.fs_file_read_utf8(filename, arena, error)
: Reads the entire file into UTF-8 format.
-
Writing Files:
fs_file_write_bytes(filename, bytes, error)
: Writes byte data to a file.fs_file_write_str(filename, content, error)
: Writes a string to a file.fs_file_write_utf8(filename, content, error)
: Writes UTF-8 formatted data to a file.
-
File Management:
fs_file_open(filename, mode, error)
: Opens a file with the specified mode.fs_file_close(file, error)
: Closes an open file.fs_rename(old_name, new_name, error)
: Renames a file.fs_remove(filename, error)
: Removes a file.fs_exists(filename)
: Checks if a file exists.
Arena arena = {0};
Error error = ErrNew;
Str content = fs_file_read_str(STR("filename.txt"), &arena, &error);
error_context(&error, {
error_raise();
});
arena_free(&arena);
int main(void) {
// initialize and configure iterator
FsIter it = fs_iter_begin(STR("."), true);
// iterate over directory with certain filters
while (fs_iter_next_suffix(&it, STR(".clangd"))) {
// every allocation in the scratch buffer gets reset after each iteration
Str data = fs_file_read_str(it.current.path, &it.scratch, &it.error);
// do not return before you call 'fs_iter_end'
error_propagate(&it.error, { break; });
// do something with data...
cebus_log_debug(STR_FMT, STR_ARG(data));
}
// collect errors and deinitializes iterator
Error err = ErrNew;
fs_iter_end(&it, &err);
error_context(&err, { error_panic(); });
}
-
Output:
io_write(file, fmt, ...)
: Writes a formated string into the fileio_write_bytes(file, bytes, error)
: Writes byte data to a file or stream.
-
Input:
io_read_bytes(file, size, buffer, error)
: Reads a specified amount of byte data from a file or stream into a buffer.io_read_line(file, size, buffer, error)
: Reads a line of text from a file or stream into a buffer.input(prefix)
: Displays a prompt and reads a line of text from standard input.
Error e = ErrNew;
io_write(stdout, BYTES_STR("Hello, World"), &e);
error_context(&e, { error_raise(); });
Str ret = input(STR(":> "));
printf("input: '"STR_FMT"'\n", STR_ARG(ret));
Output when running the example:
:> name
input: 'name'
-
Environment Variables:
os_getenv(env, error)
: Retrieves the value of an environment variable.
-
Current Working Directory:
os_getcwd(arena)
: Returns the current working directory, allocating memory from the specifiedArena
.os_chdir(path)
: Changes the current working directory to the specified path.
Error error = ErrNew;
Str cwd = os_getcwd(&arena);
Str home = os_getenv("HOME", &error);
error_context(&error, {
error_raise();
});
printf("Current working directory: " STR_FMT "\n", STR_ARG(cwd));
printf("Home directory: " STR_FMT "\n", STR_ARG(home));
-
Creating Byte Arrays:
BYTES
: Create a byte array from byte literals.BYTES_STR
: Create a byte array from a string literal.
-
Creating from Pointer and Size:
bytes_from_parts(size, data)
: Creates a byte array from given size and data pointer.
-
Copying Byte Arrays:
bytes_copy(bytes, arena)
: Performs a deep copy of a byte array, allocating memory from the specified arena.
-
Slicing and Taking Bytes:
bytes_slice(bytes, idx1, idx2)
: Returns a new byte array that is a slice from the original, fromidx1
toidx2
.bytes_take(bytes, count)
: Modifies the original byte array to keep only the firstcount
bytes, returning the removed portion.
-
Checking Equality:
bytes_eq(b1, b2)
: Checks if two byte arrays are equal.
-
Hexadecimal Conversion:
bytes_to_hex(bytes, arena)
: Converts a byte array into a hexadecimal string representation, using memory from the arena.bytes_from_hex(s, arena)
: Creates a byte array from a hexadecimal string, allocating memory from the arena.
Create new Bytes from a literal.
Bytes bytes = BYTES(0xff, 0x11);
Bytes bytes_str = BYTES_STR("Bytes from a string");
Or from a pointer with size.
int a = 69;
Bytes bytes = bytes_from_parts(sizeof(a), &a);
if you need to take ownership of the bytes you can copy it.
int a = 69;
Bytes bytes = bytes_from_parts(sizeof(a), &a);
Arena arena = {0};
Bytes owned_bytes = bytes_copy(bytes, &arena);
c_is_alnum(c)
: Checks if a character is alphanumeric.c_is_alpha(c)
: Checks if a character is alphabetic.c_is_lower(c)
: Checks if a character is lowercase.c_is_upper(c)
: Checks if a character is uppercase.c_is_space(c)
: Checks if a character is a whitespace character.c_is_cntrl(c)
: Checks if a character is a control character.c_is_print(c)
: Checks if a character is printable.c_is_graph(c)
: Checks if a character has a graphical representation.c_is_blank(c)
: Checks if a character is blank (space or tab).c_is_punct(c)
: Checks if a character is punctuation.c_is_digit(c)
: Checks if a character is a digit.c_is_xdigit(c)
: Checks if a character is a hexadecimal digit.
c_to_lower(c)
: Converts a character to lowercase.c_to_upper(c)
: Converts a character to uppercase.c_to_u8(c)
: Converts a character to an unsigned 8-bit integer.c_hex_to_u8(c)
: Converts a hexadecimal character to an unsigned 8-bit integer.c_u8_to_c(d)
: Converts an unsigned 8-bit integer to a character.c_u8_to_hex(d)
: Converts an unsigned 8-bit integer to a hexadecimal character (lowercase).c_u8_to_HEX(d)
: Converts an unsigned 8-bit integer to a hexadecimal character (uppercase).
These functions are available for f32
and f64
.
f32_eq(a, b)
: Tests if twof32
are equal.f32_eq_eps(a, b)
: Tests if twof32
are equal within a give epsilon.f32_isnan(value)
: Tests iff32
isnan
.f32_abs(value)
: Returns the absolute value.f32_min(a, b)
: Returns the min ofa
orb
.f32_max(a, b)
Returns the max ofa
or b.f32_clamp(min, max, value)
: Clamps af32
betweenmin
andmax
.f32_lerp(min, max, value)
: Linear interpolation betweenmin
andmax
.f32_rad(deg)
: Converts degrees to radians.f32_deg(rad)
: Converts radians to degrees.
All these functions are defined for these types: u8
, i8
, u16
, i16
,
u32
, i32
, u64
, i64
, usize
.
T_reverse_bits(T value)
: Reverses the bits invalue
.T_leading_ones(T value)
: Counts the number of leading ones invalue
.T_trailing_ones(T value)
: Counts the number of trailing ones invalue
.T_leading_zeros(T value)
: Counts the number of leading zeros invalue
.T_trailing_zeros(T value)
: Counts the number of trailing zeros invalue
.T_count_zeros(T value)
: Counts the number of zeros invalue
.T_count_ones(T value)
: Counts the number of ones invalue
.
T_swap_bytes(T value)
: Swaps the byte order ofvalue
.T_to_be(T value)
: Convertsvalue
to big endian format.T_from_be(T value)
: Convertsvalue
from big endian format.T_to_le(T value)
: Convertsvalue
to little endian format.T_from_le(T value)
: Convertsvalue
from little endian format.T_to_ne_bytes(T value)
: Convertsvalue
to native endian format.
Basic math operations including max, min, and clamp are provided.
T_max(T a, T b)
: Returns the maximum ofa
andb
.T_min(T a, T b)
: Returns the minimum ofa
andb
.T_clamp(T min, T max, T value)
: Clampsvalue
betweenmin
andmax
.
T_hash(T value)
: Generates a hash forvalue
.T_swap(T *v1, T *v2)
: Swaps the values ofv1
andv2
.T_compare_lt(T a, T b)
: Comparesa
andb
for less than.T_compare_gt(T a, T b)
: Comparesa
andb
for greater than.T_compare_qsort(CmpOrdering ordering)
: Provides a comparison function suitable forqsort
.
-
String Creation and Printing:
STR("Hello World")
: Create a new string from a string literal.STR_STATIC("Hello")
: Create a new static string from a string literal.str_from_cstr(str)
: Create new string from a char array.str_from_bytes(str)
: Create new string from bytes.str_format(fmt, ...)
: Create new string as formated.printf(STR_FMT"\n", STR_ARG(str))
: Print strings using macros.
-
String Manipulation:
str_lower(str, &arena)
,str_upper(str, &arena)
: Convert to lower or upper case.str_append(str, suffix, &arena)
,str_prepend(str, prefix, &arena)
: Append or prepend strings.str_wrap(str, wrap, &arena)
: Wrap a string.str_join(sep, count, strs, &arena)
: Join strings with a separator.
-
String Trimming and Slicing:
str_trim(str)
,str_trim_left(str)
,str_trim_right(str)
: Trim whitespace.str_chop_by_delim(str, delim)
,str_try_chop_by_delim(str, delim, &chunk)
: Chop strings by delimiter.str_substring(str, start, end)
: Extract a substring.
-
String Comparison and Search:
str_eq(s1, s2)
,str_eq_ignorecase(s1, s2)
: Check string equality.str_startswith(str, prefix)
,str_endswith(str, suffix)
: Check prefixes/suffixes.str_contains(haystack, needle)
: Check if string contains a substring.
-
Conversion and Utility:
str_to_u64(str)
,str_u64(n, &arena)
: Convert between strings and unsigned 64-bit integers.str_hash(str)
: Generate a hash value for a string.
Arena arena = {0};
Str greeting = STR("Hello World");
Str lower = str_lower(greeting, &arena);
printf(STR_FMT"\n", STR_ARG(lower));