
Single-file public-domain/open source C libraries with minimal dependencies - networked
https://github.com/nothings/stb/blob/master/docs/other_libs.md
======
yorhel
Shameless plug: I also maintain a few small single-file C libs at
[https://dev.yorhel.nl/ylib](https://dev.yorhel.nl/ylib)

And, separately, a small-single-file-but-correct XML parser:
[https://dev.yorhel.nl/yxml](https://dev.yorhel.nl/yxml)

------
greenyoda
Here are some more...

Kazlib contains a few single-file (actually one .c file and one .h file) open
source C libraries. For example, there's a dictionary data structure based on
red-black trees, dict.c/dict.h, which I've found to be useful and reliable.
There's also a linked list library, a hash table library, etc., but I haven't
used these.

Home page:
[http://www.kylheku.com/~kaz/kazlib.html](http://www.kylheku.com/~kaz/kazlib.html)

Source code:
[http://git.savannah.gnu.org/cgit/kazlib.git/tree/](http://git.savannah.gnu.org/cgit/kazlib.git/tree/)

The latest version uses the BSD License.

------
jarcane
For parsing, there's the excellent _mpc_ , a parser combinator library in
highly portable C.
[https://github.com/orangeduck/mpc](https://github.com/orangeduck/mpc)

I even managed to use it drop in for a RiscOS project once.

------
morebetterer
What about:

* sqlite3 amalgamation: [https://www.sqlite.org/download.html](https://www.sqlite.org/download.html)

* duktape javascript interpreter: [http://duktape.org](http://duktape.org)

~~~
ithkuil
Shameless plug: [https://github.com/cesanta/v7](https://github.com/cesanta/v7)
embedded javascript interpreter

------
rwmj
[http://git.annexia.org/?p=miniexpect.git](http://git.annexia.org/?p=miniexpect.git)

It's an "expect"-like library written in C (and more sane than the Tcl
original). It's 3 files, because it has a header file and a manual page.

It's just 390 lines of code because it's implemented using some PCRE trickery.

------
wellwatch
_Why isn 't SQLite's amalgamated build on this list? _Come on.

I don't understand this. Can someone explain it to me?

~~~
zyxley
> The amalgamation is a single C code file, named "sqlite3.c", that contains
> all C code for the core SQLite library and the FTS3, FTS5, RTREE, DBSTAT,
> JSON1, and RBU extensions. This file contains about 184K lines of code (113K
> if you omit blank lines and comments) and is over 6.4 megabytes in size.
> Though the the various extensions are included in the "sqlite3.c"
> amalgamation file, they are disabled using #ifdef statements.

[https://www.sqlite.org/amalgamation.html](https://www.sqlite.org/amalgamation.html)

~~~
userbinator
In other words, it's stretching the definition of "single file" a bit... but
then again, I don't know any other SQL databases anywhere near as small as
SQLite.

~~~
srpeck
Smallest commercially viable SQL database is probably the latest k5/kdb
([http://kparc.com/](http://kparc.com/)), which is implemented in something
like 9KB of code (source: [http://kparc.com/o.htm](http://kparc.com/o.htm)).

------
djcapelis
Just proposed one of my projects for inclusion:
[https://github.com/djcapelis/atomic-
ring](https://github.com/djcapelis/atomic-ring)

Lock-free Single Producer, Single Consumer (SPSC) queue. The dependencies
include C11 and that's it. No POSIX required, should work on any arch you can
find a C11 compiler for.

~~~
haberman
I was just reading your code, which interests me since I've been interested in
lock-free programming for a while but only recently started learning about the
C11 and C++11 memory model and atomic operations.

I had some thoughts, which I'm offering in hopes of clarifying my
understanding, and possibly helping to improve your software in the process.

I don't see any correctness problems in your code, but I do see what look like
several opportunities for optimization. Though please take these with a slight
grain of salt, as I am still learning the C11 atomics.

Your aring_give() ends with a release barrier and your aring_take() begins
with an acquire barrier. That makes sense to me, as a way of ensuring the
sequencing of the reads/writes to aring->rb and to item. However I don't see
why aring_give() needs to begin with an acquire and aring_take() needs to end
with a release. I think both of these could be changed to memory_order_relaxed
with no change in correctness.

But I think we can go a step further actually. Your
atomic_fetch_add_explicit() and atomic_fetch_sub_explicit() operations operate
on a shared aring->items member. Both the reader and writer write to this
variable, which requires expensive locked operations and which will degrade
under contention. I don't think this is actually necessary.

Instead you could eliminate aring->items completely and simply compute it
based on aring->head and aring->tail. Only the writer writes to head and only
the reader writes to tail, so you could use just
atomic_load_explicit()/atomic_store_explicit() with memory_order_relaxed on
these variables to read and write them. Then calculate the number of items
present by comparing them. This could make the overall queue significantly
more efficient.

I'd be curious to hear your thoughts on this.

~~~
temac
Not the author, but anyway; I'm not sure you can release on one variable and
acquire on an other while having ordering guarantee between the two.

Anyway, I agree that having both acq and rel on both reader on writer side
seems weird. I guess you can come with a solution with only one rel for the
writer and only one acq for the reader.

UPDATE: I'm stupid, actually not possible if you don't want to overflow.

~~~
haberman
Interesting, it was my impression that the release/acquire are not specific to
a memory location. They are just barriers, aren't they? But as I said I'm just
learning.

~~~
temac
They are specific. See 5 in 5.1.2.4 Multi-threaded executions and data races
in n1570.

In term of real hardware it translates well in MESI/MESI-like protocol on
cache lines on ooo cores, without much more constraints (of course some arch
are sufficiently weak to still require special instructions, but on the other
hand x86 don't need anything for acq/rel). If the different cores never touch
the same cache line, they don't need to do interact at all even when both
execute unrelated acq/rel atomics.

~~~
haberman
Interesting. I bought C++ Concurrency in Action today and am learning all
these details of the memory model.

You mention of caches makes me realize that a single-reader single-writer
queue can probably also be optimized by putting the head pointer and the tail
pointer on different cache lines. The reader and writer can cache the other's
value on their own page, and only reload it when the queue otherwise looks
full or empty, respectively. This should allow the reader and writer to act
without needing to synchronize cache lines for many operations.

~~~
djcapelis
I think that works on some architectures but would not be guaranteed behavior.
In theory on a system with no memory ordering constraints, the pointers could
update before the cache lines for the underlying ring buffer. Which means that
without an acquire barrier at the beginning of aring_take, which without an
item count, would require writes to the shared head/tail pointers to sync
anyway, you can't ensure the the data written to the ring buffer is visible to
the thread, even though your pointers would indicate there's data there and so
you may load either torn or completely different data into the consumer
thread.

Which means that if your memory barriers are operating correctly, they're just
causing two cache lines to sync for the metadata instead of one, if the
pointers are split across two lines.

Whereas with an item count, only one line ever has to sync, even if the
pointers are split across two, since the pointers aren't shared between
threads.

In practice, the compiler might not be smart enough to realize this and might
be enforcing order for all side effects before the barriers though, even if
the other thread doesn't read them. So maybe this isn't a good approach.

------
rat87
Plug for a tiny header library I wrote

[https://github.com/rtaycher/debug_print_h](https://github.com/rtaycher/debug_print_h)

It's a little thing for simple printf debugging. I got tired as hell of using
formatting strings. It prints line, file, and function, expression and value.
Since it stands out /greppable it's easier to delete then random printfs.

It has support for different colors(to make things stand out) and uses c99
_Generic to support different types with a single "function" and c
initializers for named arguments. It supports basic c types and you can sort
of extend it by modyfing the header(unfortunately I couldn't find anything
cleaner using c).

C99 only (works on gcc>4.9 (due to use of designated initializers) and
llvm/clang >3.3 (maybe earlier but I tested it with 3.3) I was hoping to
support msvc but it looks like Microsoft hasn't implemented _Generic yet.

~~~
kyberias
But... isn't _Generic part of C11, not C99? You actually require C11. Big
difference.

~~~
rat87
Oops.

You're absolutely right it's C11.

~~~
kyberias
Yeah, I wondered about that because I thought VC's C99 implementation is
_relatively_ useful but again C11 might never arrive there.

~~~
rat87
Looks I'm wrong again, it's been a while since I wrote it it also uses typeof
gcc/llvm extension, there might be a way to fix that but since msvc lacks
_Generic I didn't bother.

I was also hoping to support c++ but c++ doesn't do designated initializers.

------
Tim61
Just proposed two of my C projects for inclusion:

TinyExpr - evaluate math from string -
[https://github.com/codeplea/tinyexpr](https://github.com/codeplea/tinyexpr)

minctest - very minimal C unit tests -
[https://github.com/codeplea/minctest](https://github.com/codeplea/minctest)

TinyExpr is a single source file + header, while minctest is a single header.
Both are zlib licensed.

Also, I'd appreciate feedback if anyone is interested.

------
kgabis
Shameless plug:
[https://github.com/kgabis/parson](https://github.com/kgabis/parson) (2-file
(.h and .c) json parser)

------
arcticgeek
Another good source of small utility libraries is CCAN.

List of available modules:
[https://ccodearchive.net/list.html](https://ccodearchive.net/list.html)

------
Sir_Cmpwn
Mostly related:
[https://github.com/blanham/PDCLib](https://github.com/blanham/PDCLib)

Not one file but public domain and a great project :)

------
chubot
Uh, this stb library [1] seems to follow a horrible pattern: not only
combining your code into a single .c file (which is fine, like the sqlite
amalgamation), but putting the code into a single .h file. [2]

I've never seen a C or C++ codebase that does this. Maybe you can rely on
link-time deduplication, but it will still cause duplicate compilation, and
thus increased compile times. I haven't thought it about it lately, but I
thought the common wisdom was not to depend on the linker to dedupe or strip
unused symbols. Actually it should cause link-time errors, not just duplicated
cause.

The justification is also somewhat ridiculous: _Why single-file headers?_

 _Windows doesn 't have standard directories where libraries live. That makes
deploying libraries in Windows a lot more painful than open source developers
on Unix-derivates generally realize. (It also makes library dependencies a lot
worse in Windows.)_

Really? Why not just lay out your source normally, and then munge it with a
simple script into something Windows can handle (similar to sqlite)? Because
writing trivial scripts on Windows is also a pain?

[1] [https://github.com/nothings/stb](https://github.com/nothings/stb)

[2]
[https://github.com/nothings/stb/blob/master/stb_c_lexer.h](https://github.com/nothings/stb/blob/master/stb_c_lexer.h)

~~~
mbrock
Note that the implementation code is conditionally compiled. By default you
only compile the headers, so it's up to you instantiate the implementation
somewhere by setting a preprocessor symbol before you include the library.
This is described in the comment at the top of the file.

~~~
chubot
OK, I missed that... it makes it a slightly less crazy pattern, but still
weird and offputting. If you really have problems adding one .h and one .c
file to your project, I think the development environment is broken.

(Admittedly, I last used Visual Studio C++ in 2007... I installed it from a
DVD, only to find it was broken, and I had to copy a .DLL from the Microsoft
website to get it to work.)

