Abstract data types for the C programming language.
Online documentation and API reference: c-adt.readthedocs.io
ADT provides platform-independent and compiler-independent abstract data types for the C programming language (C99 and later). It offers a rich suite of containers designed for both standard application development and resource-constrained embedded systems:
- Generic Object Containers (
void*): Dynamic pointer array (adt_ary_t), hash table with string keys (adt_hash_t), doubly-linked list (adt_list_t), LIFO stack (adt_stack_t), and binary heap priority queue (adt_heap_t). - Byte & String Buffers: Mutable resizable byte array (
adt_bytearray_t), immutable byte buffer (adt_bytes_t), and UTF-8/ASCII string container (adt_str_t). - Embedded & Zero-Heap Containers: Static element ring buffer (
adt_rbfs_t), embedded 16-bit ring buffer (adt_rbfu16_t), and sorted-array key-value map (adt_u16Map_t). - Specialized Value Containers: 32-bit integer linked list (
adt_u32List_t), unique 32-bit integer set (adt_u32Set_t), and dynamically resizing heap ring buffer (adt_rbfh_t).
| Name | Header | Storage Type | Requires Heap | Description |
|---|---|---|---|---|
adt_ary_t |
adt_ary.h |
Objects (void*) |
Yes | Contiguous pointer array with O(1) random access |
adt_bytearray_t |
adt_bytearray.h |
Bytes (uint8_t) |
Yes | Mutable byte array with geometric growth |
adt_bytes_t |
adt_bytes.h |
Bytes (uint8_t) |
Yes | Immutable byte array |
adt_str_t |
adt_str.h |
Characters (char*) |
Yes | Dynamic UTF-8 / ASCII string container |
adt_hash_t |
adt_hash.h |
Objects (void*) |
Yes | Hash table with string keys |
adt_u16Map_t |
adt_map.h |
Objects (void*) |
No | Sorted-array map for uint16_t keys |
adt_list_t |
adt_list.h |
Objects (void*) |
Yes | Doubly-linked list for mid-sequence edits |
adt_u32List_t |
adt_list.h |
Values (uint32_t) |
Yes | Specialized linked list for 32-bit integers |
adt_stack_t |
adt_stack.h |
Objects (void*) |
Yes | LIFO stack for generic pointers |
adt_heap_t |
adt_heap.h |
Objects (void*) |
Yes | Binary heap priority queue |
adt_rbfs_t |
adt_ringbuf.h |
Elements (uint8_t*) |
No | Static circular FIFO buffer (zero heap) |
adt_rbfu16_t |
adt_ringbuf.h |
Values (uint16_t) |
No | Embedded circular buffer for uint16_t (zero heap) |
adt_rbfh_t |
adt_ringbuf.h |
Elements (uint8_t*) |
Yes | Heap-allocated circular FIFO buffer |
adt_u32Set_t |
adt_set.h |
Values (uint32_t) |
Yes | Unique set of 32-bit integers |
Embedded Development Note: Data structures with Requires Heap: No (such as
adt_rbfs_t,adt_rbfu16_t, andadt_u16Map_t) or those initialized via in-place*_create/*_destroyfunctions operate completely without dynamic heap allocation (malloc/free), making them safe for microcontrollers, safety-critical code, and real-time systems.
None, except for a C compiler.
If you are looking for higher level data types in C you can check out the cogu/dtl_type project. It offers reference-counted variables with an easy to use API. It internally uses ADT for data storage.
# Run unit tests
cmake --preset clang-test
cmake --build --preset clang-test
ctest --preset clang-test
# Address and Undefined Behavior Sanitizers (ASan + UBSan)
cmake --preset clang-asan
cmake --build --preset clang-asan
ctest --preset clang-asan
# Static Analysis
cmake --preset clang-tidy
cmake --build --preset clang-tidyFor Windows, use a "Native tools command prompt" from your Visual Studio installation. It comes with a cmake binary that by default chooses the appropriate compiler version.
Configure:
cmake -S . -B build-test -GNinja -DUNIT_TEST=ONBuild:
cmake --build build-testRun test cases:
ctest --test-dir build-test --output-on-failureConfigure with --coverage compiler and linker flags:
cmake -S . -B build-cov -DUNIT_TEST=ON \
-DCMAKE_C_FLAGS="--coverage" \
-DCMAKE_EXE_LINKER_FLAGS="--coverage"Build and run tests:
cmake --build build-cov
ctest --test-dir build-cov --output-on-failureAnalyze coverage with gcov:
# For a single file:
gcov -o build-cov/CMakeFiles/adt.dir/src/adt_stack.c.o src/adt_stack.c
# For all source files:
for f in src/*.c; do gcov -o build-cov/CMakeFiles/adt.dir/src/$(basename $f).o $f; donegcov prints the summary percentage to stdout and produces annotated .gcov files (e.g. adt_stack.c.gcov) in the current working directory. Untested lines are highlighted with #####:.
Configure:
cmake -S . -B build -DCMAKE_BUILD_TYPE=ReleaseBuild:
cmake --build build --target adtCMake options can be set from command line or using a CMake GUI tool (such as ccmake for Linux).
| CMake Option | Usage | Description |
|---|---|---|
| LEAK_CHECK | -DLEAK_CHECK=ON | Enables memory leak check detection |
| UNIT_TEST | -DUNIT_TEST=ON | Activates UNIT_TEST preprocessor define |
| ADT_NO_HEAP_MEM | -DADT_NO_HEAP_MEM=ON | Disable heap memory allocation (zero-heap mode) |
| ADT_SANITIZERS | -DADT_SANITIZERS="address,undefined" | Enables sanitizers for GCC or Clang |
By default, all data structures (including adt_ringbuf.c and adt_map.c) are compiled into the ADT library with dynamic heap features enabled.
For small embedded microcontrollers or safety-critical software where dynamic heap allocation (malloc/free) is prohibited:
- Pass
-DADT_NO_HEAP_MEM=ONin CMake (or defineADT_NO_HEAP_MEM=1when compiling individual source files). - This strips out all heap-dependent APIs (
adt_rbfh_*,adt_u16Map_new,adt_u16Map_delete) while keeping all zero-heap containers and functions (adt_rbfs_*,adt_rbfu16_*,adt_u16Map_create, etc.) fully functional with zero dynamic allocation.
When -DUNIT_TEST=ON is set, a dedicated benchmark executable adt_perf is built. It benchmarks insertion, lookup, and sorting across adt_hash, adt_ary, adt_set, and adt_map.
Run directly:
./build-test/adt_perfOr run via CTest:
ctest --test-dir build-test -L benchmark --output-on-failure --verbose