Library / SDK
faragon/libsrt avatar
faragon/libsrt

libsrt: a C container library where allocation is the caller's decision

libsrt is a C library for writing fast and safe C code, faster. It provides string, vector, bit set, set, map, hash set, and hash map handling. Suitable for soft and hard real-time. Allows both heap and stack allocation. *BETA* (API still can change: suggestions are welcome)

558 stars50 forksCBSD-3-Clause

At a glance

What is it?
libsrt gives C programmers string, vector, set, map, bit set, hash set and hash map types that work on both the heap and the stack, with predictable space and time behaviour for real-time work. It is BSD licensed, still marked beta, and its build system has more knobs than most libraries of its size.
Who is it for?
Adopt libsrt if you are writing C, you want container types with predictable space and time behaviour, and you need the option of putting a structure on the stack rather than the heap. Do not adopt it if you need a stable API, because the project marks itself beta and says the API can still change, and do not expect it to replace a general purpose standard library.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 82 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Containers that work on the heap or the stack

libsrt is a C library that provides string, vector, bit set, set, map, hash set, and hash map handling. It has been designed for avoiding explicit memory management when using dynamic-size data structures, allowing safe and expressive code, while keeping high performance. It is also suitable for low level and hard real-time applications, because functions are predictable in both space and time, assuming the OS and the underlying C library is also real-time suitable.

The six key points are the whole pitch. Safe means you can write high-level-like C code, and write code faster and safer. Fast means using O(n), O(log n) and O(1) state of the art algorithms. Rich means strings supporting raw data handling per byte, plus the vector, bit set, set, map, hash set and hash map data structures. Efficient means space optimized with minimum allocation calls, and heap and stack support for all data structures. Compatible means OS-independent, with built-in space-optimized UTF-8 support. And Predictable means it is intended for both general purpose and for hard real-time applications.

That heap and stack point is the one that distinguishes it from most C container libraries. A hash map that can live on the stack has a bounded, compile-time size and no allocator call at all, which is what makes it usable in an interrupt handler or on a microcontroller. The MINIMAL build option exists for exactly that case.

The project is BSD-3-Clause licensed, the primary language is C, the default branch is master, and the last push was on 2026-08-02. The repository publishes no GitHub releases, and the documentation site is generated rather than written, by running the make_test.sh script with an argument.

The build has a knob for every situation you can think of

The build instructions are the longest part of the README, and they read as a catalogue of the situations the author wanted the library to work in.

The base command on most POSIX systems is make -f Makefile.posix, and the Makefile.posix includes platform detection for corner cases. You can mix flags, and the ones documented cover the whole range.

For plain tuning, the default is equivalent to a specific CRC32 setting, and you can spawn up to 8 concurrent jobs with the -j flag for faster builds on multiprocessor systems. DEBUG=1 disables optimizations at -O0 with debug symbols, and PROFILING=1 turns on profiling and coverage flags.

For the compiler itself, you can select gcc, clang, the Tiny C Compiler, or g++ and clang++ to build the library in C++ instead of C, and you can pick the C standard from C89/90 through C99 and C11, or C++11. There is a pedantic build with PEDANTIC=1 that should give no warnings, cross compilation is supported for PPC and ARM with the appropriate gcc, and FORCE32=1 forces a 32 bit build on a 64 bit system.

Then there are the library-specific knobs. MINIMAL=1 produces a microcontroller-suitable build optimized for size and low memory usage. The CRC32 table size is selectable through a define, ranging from no CRC32 hash tables at 1 bit per loop up to a 16384 byte table, with throughput figures given for each and the 12288 byte setting identified as the default CRC32 mode. Two flags disable optimisations, both marked as not recommended: one for growth heuristics and one for map string optimisations, the latter excepted for benchmarking. And HAS_PNG=1 with HAS_JPG=1 enables the image conversion example, which needs the libpng and jpeg 6b development libraries installed.

The number of options is a sign of what the library is for. Every one of them exists because someone needed that exact configuration.

There is a CMake path as well as the Makefiles

Alongside the POSIX Makefile there is a CMake build, and the README gives two invocations, one static and one shared.

The static build is:

code
  * cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cd build && make && ./stest

and the shared library variant adds a flag for it:

code
  * cmake -S . -B build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release && cd build && make && ./stest

The final ./stest in both is the test executable, and running it is not optional in practice, because of the observation listed under the build notes: every make call, in addition to building the targets, does a full test for that build, with unit tests covering all the API function calls.

That is an unusual and rather good arrangement. You cannot build the library without also proving it works, for that specific set of flags. So a MINIMAL microcontroller build and a full debug build each come with their own test run, and a flag combination that breaks something fails the build rather than producing a library that breaks later.

The autotools path is also there, with configure.ac, bootstrap.sh and Makefile.am, so the library fits into the older GNU autotools conventions as well as into the two newer build systems.

The repository layout reflects the three of them: Makefile.posix, Makefile.am, CMakeLists.txt, bootstrap.sh, configure.ac, and a make_test.sh script at the root, with src/ for the code, test/ for the tests, doc/ for the documentation, utl/ for utilities and win/ for the Windows solution file.

Every build runs the tests, and make clean matters

The observations section in the README is short and full of practical warnings, and the first is the one about the tests.

Every make call, in addition to building the targets, does a full test for that build, with unit tests covering all the API function calls, through the stest executable. So the cost of every configuration change is a full test run, which is deliberate, since the library's claim is about behaviour under a specific set of flags rather than about the code compiling.

The second observation is the one people get bitten by. Between make calls with different parameters, please use make clean. Without it, you can end up comparing a library built with one set of flags against one built with another, and the mismatch looks like a bug in the library rather than a stale object file.

The third observation sets the portability expectations. It should build with any GCC version of at least 2.95.2 from 1999, in any hardware platform, with the list given as x86, x86-64, MIPS32/64, MIPSLE, PPC/PPC64 and ARM. CLang should work in all versions, except in cases of reporting a wrong or missing compiler or C standard version.

Two smaller notes round it out. For Windows there is a.sln example provided just for building the test, used for the CI check. And in BSD systems that do not use GNU Make as the default, you use gmake instead of make.

The continuous integration setup is visible in the badges at the top of the README, with separate build badges for gcc, clang and tcc, for Ubuntu 16, for Windows, plus a Coverity analysis badge. The multi-compiler matrix is the point, since a C library that only builds with one compiler is not portable in any useful sense.

Unicode is a layer over raw bytes

The Unicode key point in the README is subtle and worth reading closely, because it explains an API asymmetry that would otherwise look like a mistake.

Although the string internal representation is raw data, meaning bytes, functions for handling Unicode interpretation, generation and transformation are provided. When Unicode-specific functions are used, the result of those functions is stored internally as UTF-8, with some operations cached, such as Unicode string length.

The example given is the length functions. ss_len() and ss_size() give the length in bytes, and ss_len_u() gives the length in Unicode characters. So the same string has two lengths depending on which function you ask, and the byte-oriented one is the raw truth while the Unicode one is a computed, cached view.

That is a deliberate design and it is the right one for a library aimed at embedded and real-time work. A string that is always valid UTF-8 internally has to be validated on every operation and cannot hold arbitrary bytes, and plenty of protocols need arbitrary bytes. Keeping the storage as bytes and making Unicode an explicit interpretation means a string type can be used for binary data and for text without the caller having to pick a different type.

The caching is what makes it affordable. Computing a Unicode character count means walking the string and decoding UTF-8 sequences, and if the same string is measured repeatedly that walk is repeated. Caching it on the string makes the second call free, at the cost of some state in the structure, which is a trade the author has evidently judged worth making.

A beta library with a long history

The repository description carries the warning itself: the library is marked beta, and the API can still change, with suggestions welcome.

That is worth weighing against the rest of the picture, because the surrounding details suggest a much older project than a typical beta. The CI configuration includes Travis for the master branch and Azure Pipelines for Ubuntu 16, and AppVeyor for win32, which places the original continuous integration in the era when those were the standard three. The Coverity analysis badge is on a project number, and the README mentions that libsrt has been included into Paul Hsieh's String Library Comparisons table.

So the picture is a library that has been developed and profiled for a long time, and whose API is still being adjusted. Those two facts are not in conflict. A container library that has been benchmarked against every implementation it could be compared with has had its interface refined many times, and the beta label reflects that the author still expects to change it.

For an adopter, the practical consequence is that you should pin a version and read the changelog before upgrading, rather than tracking a moving API. The upside of the maturity is the thing the documentation keeps returning to: the space and time predictability is measured rather than asserted, the build matrix covers the compilers and architectures you are likely to use, and every configuration you build is tested.

Editorial conclusion

Adopt libsrt if you are writing C, you want container types with predictable space and time behaviour, and you need the option of putting a structure on the stack rather than the heap. Do not adopt it if you need a stable API, because the project marks itself beta and says the API can still change, and do not expect it to replace a general purpose standard library. Before you build it, read the observation that every make call also runs the full test suite, and remember to use make clean between builds with different parameters, otherwise you will be comparing objects built with different flags.

Frequently asked questions

What data structures does libsrt provide?

String, vector, bit set, set, map, hash set and hash map handling. The library is designed for avoiding explicit memory management with dynamic-size structures, and every data structure supports both heap and stack allocation.

How do I build libsrt?

On most POSIX systems `make -f Makefile.posix` is enough, since the Makefile.posix includes platform detection. There is also a CMake path with `cmake -S . -B build -DCMAKE_BUILD_TYPE=Release && cd build && make && ./stest`, and an autotools path using bootstrap.sh and configure.

What does the MINIMAL build option do?

It produces a microcontroller-suitable build optimized for size and low memory usage. There are also compiler and standard selection flags, a FORCE32 option for a 32 bit build on a 64 bit system, DEBUG and PROFILING flags, and a CRC32 hash table size setting that ranges from no CRC32 tables up to a 16384 byte table.

Do I need to run make clean between builds with different flags?

Yes. The README asks you to use make clean between make calls with different parameters, and separately notes that every make call also runs the full unit test suite through the stest executable, so each configuration is verified as it is built.

Official sources

  1. faragon/libsrt on GitHub
  2. Issues
  3. License: BSD-3-Clause
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/faragon-libsrt.svg)](https://hysenlabs.com/projects/faragon-libsrt)

Was this useful?