libz1: Experience in writing my first library

08 Aug 2022

If you've been programming in C for any decent amount of time, it's inevitable that you'll end up with a bunch of "copy-paste" functions laying around somewhere in your disk. libz1 is basically my attempt at taking those copy-paste functions and turning them into a set of single header libraries.

While I have been programming in C for a decent bit now (maybe around an year) and have contributed to a bunch of libraries I hadn't yet written a library of my own. So now having written libz1, I'd like to share some of my experience.

As it turns out, writing a library is actually rather easy. The difficult part is designing it... and writing the documentation. Also keep in mind that libz1 barely had any design decision involved since most of the functions were fairly self-contained.

As of writing this post, libz1 contains the following libraries:

zstr.h

On a long enough timeline, the chances of a C programmer writing his own string library approaches 100.

Yup, I had to do it. But the good news is that unlike a lot of other string libraries which invent their own string type, zstr is designed to work with the good ol standard nul-terminated strings.

One of the goal of zstr was to complement the C standard string library instead of reinventing it. It contains a bunch of nice functions which aren't standardized, such as: find and replace characters within a string, search for a character case insensitively, find an arbitrary sized element from an array etc.

It also contains compatibility replacements for various useful, but non-standard functions such as memmem, mempcpy, strcasestr etc.

One thing which became somewhat of a pain was the fact that these compatibility functions had to be fast. It makes no sense to use wrappers if they're 10x slower than the system-libc ones.

Making these functions fast, in itself isn't too difficult of a task. What made it difficult was the fact that zstr is a single header library. Which means that there's no room for doing optimizations which rely on undefined behavior since the library will be build on environments on which I don't have much control over.

Regardless, the end result is quite decent. A lot of the functions compete fairly well against Glibc, which is one of the faster libcs out there.

I do plan on adding some other functions to this library, but for now I've only included functions which I'm confident in.

zvec.h

zvec is a fairly straight-forward implementation of a type-safe dynamic array. The twist here is the fact that unlike most other vector implementation I've come across, this one is small-buffer optimized.

Small buffer optimization means avoid doing dynamic allocation for small inputs. This is quite useful when you're expecting you're typical case to be small enough to avoid having to malloc, but at the same time you'd like to be able to deal with edge-cases where dynamic allocation is necessary.

Unlike zstr this library is pretty much complete. I don't see myself adding anything else to it.

zattr.h

The C type system is rather primitive and lacks fine grained expressiveness. A lot of errors which could've been caught at compile time goes unnoticed due to it.

Compiler attributes somewhat solves this. But if you use compiler attributes, then you're making your source code less portable since attributes aren't standard (yet). C23 does standardize the attribute syntax which should make things better. However C23 isn't even finalized yet, and it's going to take a while until it gets widespread adoption.

zattr basically is a bunch of macros which allow you to portably use compiler attributes with a focus on improving compiler warnings, static analysis and runtime performance.

It includes various attributes which improve compiler warnings and static analysis such as format, noreturn, returns_nonnull etc.

Some of the attributes for improving performance such as hot, cold, pure etc are included as well. It also includes some built-in such as __builtin_expect wrapped in linux style likely/unlikely macros.

One of the focus of this library is that the absence or presence of an attribute SHOULD NOT change the program behavior. As such, attributes such as aligned or cleanup are not included.

There are some more attributes which I plan on adding to the library in the future given that they fit within the project's goal.

Future direction

While this turned out to be far more work than I originally anticipated, it also turned out quite fun. And making a library instead of just having things as "copy-paste" functions forced me to think a lot harder about edge-cases and revealed some bugs as well.

There are a couple more libraries which I plan on adding. Some things I have on mind are a misc POSIX utility library, a tightly-packed linked list, a simple stack-based allocator, and a couple other things which I won't name now.

If implementing these turn out to be interesting, then I'll probably write a blog post about it. Otherwise if you'd still like to get updated then thankfully for you, CodeBerg recently updated their Gitea version which now supports RSS feed which you can subscribe to ;)

Some credits

Tags: [ c ]



RSS Feed