I started building Kraken (an HTTP server in C) and quickly realized I needed data structures that the C standard library doesn't give you — a hashmap for routing, a queue for the thread pool's task buffer, a linked list for dynamic storage. Rather than pulling in external libraries for each one, I wrote them myself and packaged them as a standalone library.
Collections
The hashmap uses open addressing with Robin Hood hashing for better cache locality. It ships with SipHash-2-4 and MurmurHash3 as built-in hash functions, and supports custom comparators so you can use any struct as a key. This ended up powering Kraken's route table — register a path and handler, and lookups are O(1).
owl_hashmap_t *map = owl_hashmap_new(
sizeof(struct user), 0, 0, 0,
user_hash, user_compare, NULL, NULL
);
owl_hashmap_set(map, &(struct user){.name = "Alice", .age = 30});
struct user *u = owl_hashmap_get(map, &(struct user){.name = "Alice"});The queue is a bounded FIFO with configurable capacity — exactly what you need for a producer-consumer pattern like dispatching HTTP connections to worker threads. The linked list is a straightforward singly-linked implementation for cases where you need dynamic insertion without knowing the size upfront.
Thread pool
The thread pool manages a set of worker threads and an internal task queue. You enqueue work, and the next available thread picks it up. Shutdown is graceful — it waits for all queued tasks to finish before freeing resources.
owl_thread_pool_t *tp = owl_thread_pool_init(4);
owl_worker_task_t task = owl_worker_task_init(process_request, NULL);
owl_thread_pool_enqueue_task(tp, &task);
owl_thread_pool_free(tp);This was the backbone of Kraken's concurrency model. Instead of spawning a thread per connection, the server dispatches to a fixed pool — simpler to reason about and no risk of unbounded thread creation under load.
Design decisions
Everything in Owl does deep copies on insert and returns heap-allocated pointers on remove (caller frees). This makes ownership explicit — you always know who's responsible for a piece of memory. It's more work than garbage-collected languages, but that's the point. The whole library has zero external dependencies.
The hashmap implementation currently wraps tidwall/hashmap.c — I plan to rewrite it with my own implementation at some point, but for now it works and the API is mine.