LibLS

LS Libraries

Lion’s Standard (LS) libraries is a collection of header-only ANSI C (C89) libraries.

These aim to be:

All of these libraries are MIT licensed.

Container libraries use macros to generate type-specific implementations. All generated code is macro-free. LS libraries support custom allocators and handle allocation failures gracefully (where applicable).


Vector

A dynamic array (vector).

Repo: GitHub or LibLS Git

Example usage:

LS_VEC_INLINE(int, int_vector)

// somewhere in the same file
int_vector vec;
int_vector_init(&vec);
int_vector_push(&vec, 42);
// use vec.data, vec.size, etc.
int_vector_free(&vec);

Queue

A static (non-allocating) queue.

Repo: GitHub or LibLS Git

Example usage:

LS_QUEUE_INLINE(int, int_queue, 32)

// somewhere in the same file
int_queue q;
int_queue_init(&q);
int_queue_push(&q, 42);
int val;
if (int_queue_pop(&q, &val)) {
    // do something with val
}

Args

A command-line argument parser. Single header, fully ANSI C compliant and unit-tested.

Repo: GitHub or LibLS Git

Example usage:

#define LS_ARGS_IMPLEMENTATION
#include "ls_args.h"
// ...
    ls_args args;
    ls_args_init(&args);
    args.help_description = "My program description.";
    int verbose = 0;
    ls_args_bool(&args, &verbose, "v", "verbose", "Enable verbose output", 0);

    const char* output = NULL;
    ls_args_string(&args, &output, "o", "output", "Output file", 0);

    const char* input;
    ls_args_pos_string(&args, &input, "Input file", LS_ARGS_REQUIRED);

    if (!ls_args_parse(&args, argc, argv)) {
        fprintf(stderr, "%s\n", args.last_error);
        // maybe also print help
        fprintf(stderr, "%s", ls_args_help(&args));
        exit(1);
    }

    // use verbose, output, input...
    ls_args_free(&args);

Test

A minimal unit testing framework.

Repo: GitHub or LibLS Git

Example usage:

#define LS_TEST_IMPLEMENTATION
#include "ls_test.h"

TEST_CASE(test_add) {
    ASSERT_EQ(add(1, 2), 3, "%d");
    return 0;
}

TEST_MAIN

AI usage notice: The extent to which AI/LLMs are used in the development and maintenance of these libraries is documented here.

Contribution Guide

The following are some rules for all contributed code.

Namespacing

The LibLS namespacing rules are:

  1. The public API must be prefixed with the header name, e.g. ls_args.h must only expose public API which is prefixed with ls_args_ or LS_ARGS_, and the literal library name ls_args might also be used (e.g. for a struct).

  2. All private code must be static where possible, and be prefixed with either the header name like (ls_args_...) or the underscore-prefixed version (_ls_args_...), or, more commonly, the short version _lsa_ or lsa_ (e.g. _lst_... for ls_test, etc).

  3. Public code generated by a library, e.g. LS_VEC_DECL does not fall under this requirement. For example LS_VEC_INLINE(int, myvec) can declare types and functions named myvec or myvec_. Private code generated should still follow the namespacing rules.

Code Style and Correctness

Use the provided .clang-format format and generally follow the format of surrounding code.

A couple rules:

AI Use

All AI use must fit within the AI use page, or the page must be adjusted.

Philosophy

The below are ramblings about LibLS. All useful documentation is found in the headers of each library, and nowhere else.

If the same requirement can be fulfilled by two solutions, prefer the simpler one.

LS libraries are written in adherance to this ethos; they must be complete (fulfill the expectations/requirements), but the code must be reasonably terse. Simple code also means minimized dependencies and abstractions. This means LibLS libraries avoid all libraries beyond a subset of the C standard library.

Does this result in NIH syndrome issues, of re-implementing a subset of common and well-tested libraries? Yes, however, with the strong focus on high- or full-branch coverage testing, in a language as terse as C89 (ANSI C), common NIH issues are avoided.

Further, LibLS libraries only expose the necessary API and are properly namespaced.