C API Documentation

This is the C API documentation for kastore.

Example program

/* This is a simple program to illustrate the kastore C API. With
 * no arguments it writes a file 'example.kas'. If called with
 * a single command line argument, reads this file in and prints.
 */
#include <err.h>
#include <stdlib.h>
#include <stdio.h>

#include <kastore.h>

static void
handle_kas_error(int line, int retval)
{
    errx(1, "Error at line %d: %s", line, kas_strerror(retval));
}

static void
write_example(const char *path)
{
    int ret;
    kastore_t store;
    const uint32_t a[] = { 1, 2, 3, 4 };
    size_t b_length = 10;
    uint32_t *b = calloc(b_length, sizeof(*b));

    if (b == NULL) {
        err(1, "Out of memory");
    }

    ret = kastore_open(&store, path, "w", 0);
    if (ret != 0) {
        handle_kas_error(__LINE__, ret);
    }
    /* This is the standard 'put' where the library takes a copy of the array.
     * This is the recommended approach unless working with very large arrays. */
    ret = kastore_puts_uint32(&store, "a", a, 4, 0);
    if (ret != 0) {
        handle_kas_error(__LINE__, ret);
    }
    /* This is the own-put variant, where the array is inserted into the store
     * and ownership of the buffer (which must be a pointer returned from
     * malloc/calloc) is passed to the store. The buffer will be freed by the
     * library when 'close' is called. */
    ret = kastore_oputs_uint32(&store, "b", b, b_length, 0);
    if (ret != 0) {
        /* The store only takes ownership of the buffer if oputs succeeds, so
         * we must free b here to avoid leaking memory in error conditions */
        free(b);
        handle_kas_error(__LINE__, ret);
    }

    ret = kastore_close(&store);
    if (ret != 0) {
        handle_kas_error(__LINE__, ret);
    }
}

static void
read_example(const char *path)
{
    int ret;
    kastore_t store;
    uint32_t *array;
    size_t j, k, array_len;
    const char *keys[] = { "a", "b" };

    ret = kastore_open(&store, path, "r", 0);
    if (ret != 0) {
        handle_kas_error(__LINE__, ret);
    }
    for (j = 0; j < sizeof(keys) / sizeof(*keys); j++) {
        ret = kastore_gets_uint32(&store, keys[j], &array, &array_len);
        if (ret != 0) {
            handle_kas_error(__LINE__, ret);
        }
        printf("key: %s = [", keys[j]);
        for (k = 0; k < array_len; k++) {
            printf("%d%s", array[k], k == array_len - 1 ? "]\n" : ", ");
        }
    }
    ret = kastore_close(&store);
    if (ret != 0) {
        handle_kas_error(__LINE__, ret);
    }
}

int
main(int argc, char **argv)
{
    if (argc == 1) {
        write_example("example.kas");
    } else {
        read_example(argv[1]);
    }
    return 0;
}

General principles

Error handling

Functions return 0 to indicate success or an error code to indicate a failure condition. Thus, the return value of all functions must be checked to ensure safety.

Array lengths

The length of arrays is specified in terms of the number of elements not bytes.

Top level

struct kastore_t

A file-backed store of key-array values.

int kastore_open(kastore_t * self, const char * filename, const char * mode, int flags)

Open a store from a given file in read (“r”), write (“w”) or append (“a”) mode.

In read mode, a store can be queried using the get functions and any attempts to write to the store will return an error. In write and append mode, the store can written to using the put functions and any attempt to read will return an error.

After kastore_open() has been called on a particular store, kastore_close() must be called to avoid leaking memory. This must also be done when kastore_open() returns an error.

When opened in read-mode, the default is to read key/array values from file on demand. This is useful when a subset of the data is required and we don’t wish to read the entire file. If the entire file is to be read, the KAS_READ_ALL flag may be specified to improve performance.

Flags

KAS_READ_ALL

If this option is specified, read the entire file at open time. This will give slightly better performance as the file can be read sequentially in a single pass.

KAS_GET_TAKES_OWNERSHIP

If this option is specified, all get operations will transfer ownership of the array to the caller. kastore will not free the array memory and this is the responsibility of the caller. If get is called on the same key multiple times, a new buffer will be returned each time. Note that second and subsequent get calls on a given key will result in seek operations even when the KAS_READ_ALL flag is set, and will therefore fail on unseekable streams.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • filename: The file path to open.

  • mode: The open mode: can be read (“r”), write (“w”) or append (“a”).

  • flags: The open flags.

int kastore_openf(kastore_t * self, FILE * file, const char * mode, int flags)

Open a store from a given FILE pointer.

Behaviour, mode and flags follow that of kastore_open(), except append mode is not supported. The file argument must be opened in an appropriate mode (e.g. “r” for a kastore in “r” mode). Files open with other modes will result in KAS_ERR_IO being returned when read/write operations are attempted.

The FILE will not be closed when kastore_close() is called. If the KAS_READ_ALL flag is supplied, no seek operations will be performed on the FILE and so streams such as stdin, FIFOs etc are supported. The FILE pointer will be positioned exactly at the end of the kastore encoded bytes once reading is completed, and reading multiple stores from the same FILE sequentially is fully supported.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • file: The FILE* to read/write the store from/to.

  • mode: The open mode: can be read (“r”) or write (“w”).

  • flags: The open flags.

int kastore_close(kastore_t * self)

Close an opened store, freeing all resources.

Any store that has been opened must be closed to avoid memory leaks (including cases in which errors have occured). It is not an error to call kastore_close multiple times on the same object, but kastore_open must be called before kastore_close.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

const char* kas_strerror(int err)

Returns a description of the specified error code.

Return

String describing the error code.

Parameters
  • err: The error code.

Contains functions

Contains functions provide a way to determine if a given key is in the store.

int kastore_contains(kastore_t * self, const char * key, size_t key_len)

Return 1 if the store contains the specified key and 0 if it does not.

Queries the store for the specified key and returns 1 if it exists. If the key does not exist, 0 is returned. If an error occurs (for example, if querying the store while it is in write-mode), a negative value is returned.

For keys that are standard NULL terminated strings, the kastore_containss() function may be more convenient.

Return

Return 1 if the key is present and 0 if it does not. If an error occurs, return a negative value.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • key_len: The length of the key.

int kastore_containss(kastore_t * self, const char * key)

Return 1 if the store contains the specified NULL terminated key and 0 if it does not.

Queries the store for the specified key, which must be a NULL terminated string, and returns 1 if it exists. If the key does not exist, 0 is returned. If an error occurs (for example, if querying the store while it is in write-mode), a negative value is returned. the array in the specified destination pointers.

Return

Return 1 if the key is present and 0 if it does not. If an error occurs, return a negative value.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

Get functions

Get functions provide the interface for querying a store. The most general interface is kastore_get(), but it is usually more convenient to use one of the typed get functions.

int kastore_get(kastore_t * self, const char * key, size_t key_len, void ** array, size_t * array_len, int * type)

Get the array for the specified key.

Queries the store for the specified key and stores pointers to the memory for the corresponding array, the number of elements in this array and the type of the array in the specified destination pointers. This is the most general form of get query in kastore, as non NULL-terminated strings can be used as keys and the resulting array is returned in a generic pointer. When standard C strings are used as keys and the type of the array is known, it is more convenient to use the typed variants of this function.

The returned array points to memory that is internally managed by the store and must not be freed or modified. The pointer is guaranteed to be valid until kastore_close() is called.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • key_len: The length of the key.

  • array: The destination pointer for the array.

  • array_len: The destination pointer for the number of elements in the array.

  • type: The destination pointer for the type code of the array.

int kastore_gets(kastore_t * self, const char * key, void ** array, size_t * array_len, int * type)

Get the array for the specified NULL-terminated key.

As for kastore_get() except the key is a NULL-terminated string.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • array: The destination pointer for the array.

  • array_len: The destination pointer for the number of elements in the array.

  • type: The destination pointer for the type code of the array.

Typed gets

The functions listed here provide a convenient short-cut for accessing arrays where the key is a standard NULL terminated C string and the type of the array is known in advance.

int kastore_gets_int8(kastore_t * self, const char * key, int8_t ** array, size_t * array_len)
int kastore_gets_uint8(kastore_t * self, const char * key, uint8_t ** array, size_t * array_len)
int kastore_gets_int16(kastore_t * self, const char * key, int16_t ** array, size_t * array_len)
int kastore_gets_uint16(kastore_t * self, const char * key, uint16_t ** array, size_t * array_len)
int kastore_gets_int32(kastore_t * self, const char * key, int32_t ** array, size_t * array_len)
int kastore_gets_uint32(kastore_t * self, const char * key, uint32_t ** array, size_t * array_len)
int kastore_gets_int64(kastore_t * self, const char * key, int64_t ** array, size_t * array_len)
int kastore_gets_uint64(kastore_t * self, const char * key, uint64_t ** array, size_t * array_len)
int kastore_gets_float32(kastore_t * self, const char * key, float ** array, size_t * array_len)
int kastore_gets_float64(kastore_t * self, const char * key, double ** array, size_t * array_len)

Put functions

Put functions provide the interface for inserting data into store. The most general interface is kastore_put() which allows keys to be arbitrary bytes, but it is usually more convenient to use one of the typed put functions.

int kastore_put(kastore_t * self, const char * key, size_t key_len, const void * array, size_t array_len, int type, int flags)

Insert the specified key-array pair into the store.

A key with the specified length is inserted into the store and associated with an array of the specified type and number of elements. The contents of the specified key and array are copied unless the KAS_BORROWS_ARRAY flag is specified. If KAS_BORROWS_ARRAY is specified the array buffer must persist until the kastore is closed. Keys can be any sequence of bytes but must be at least one byte long and be unique. There is no restriction on the contents of arrays. This is the most general form of put operation in kastore; when the type of the array is known and the keys are standard C strings, it is usually more convenient to use the typed variants of this function.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • key_len: The length of the key.

  • array: The array.

  • array_len: The number of elements in the array.

  • type: The type of the array.

  • flags: The insertion flags, only KAS_BORROWS_ARRAY or 0 is a valid.

int kastore_puts(kastore_t * self, const char * key, const void * array, size_t array_len, int type, int flags)

Insert the specified NULL terminated key and array pair into the store.

As for kastore_put() except the key must be NULL-terminated C string.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • array: The array.

  • array_len: The number of elements in the array.

  • type: The type of the array.

  • flags: The insertion flags, only KAS_BORROWS_ARRAY or 0 is a valid.

Typed puts

The functions listed here provide a convenient short-cut for inserting key-array pairs where the key is a standard NULL terminated C string and the type of the array is known in advance.

int kastore_puts_int8(kastore_t * self, const char * key, const int8_t * array, size_t array_len, int flags)
int kastore_puts_uint8(kastore_t * self, const char * key, const uint8_t * array, size_t array_len, int flags)
int kastore_puts_int16(kastore_t * self, const char * key, const int16_t * array, size_t array_len, int flags)
int kastore_puts_uint16(kastore_t * self, const char * key, const uint16_t * array, size_t array_len, int flags)
int kastore_puts_int32(kastore_t * self, const char * key, const int32_t * array, size_t array_len, int flags)
int kastore_puts_uint32(kastore_t * self, const char * key, const uint32_t * array, size_t array_len, int flags)
int kastore_puts_int64(kastore_t * self, const char * key, const int64_t * array, size_t array_len, int flags)
int kastore_puts_uint64(kastore_t * self, const char * key, const uint64_t * array, size_t array_len, int flags)
int kastore_puts_float32(kastore_t * self, const char * key, const float * array, size_t array_len, int flags)
int kastore_puts_float64(kastore_t * self, const char * key, const double * array, size_t array_len, int flags)

Own-put functions

The ‘own-put’ functions are almost identical to the standard ‘put’ functions, but transfer ownership of the array buffer from the caller to the store. This is useful, for example, when client code wishes to write a large array to the store and wants of avoid the overhead of keeping a separate copy of this buffer in the store. By calling kastore_oput(), the user can put the key-array pair into the store and transfer responsibility for freeing the malloced array buffer to the store. See the Example program for an illustration.

int kastore_oput(kastore_t * self, const char * key, size_t key_len, void * array, size_t array_len, int type, int flags)

Insert the specified key-array pair into the store, transferring ownership of the malloced array buffer to the store (own-put).

A key with the specified length is inserted into the store and associated with an array of the specified type and number of elements. The contents of the specified key is copied, but the array buffer is taken directly and freed when the store is closed. The array buffer must be a pointer returned by malloc or calloc. Ownership of the buffer is not taken unless the function returns successfully.

Apart from taking ownership of the array buffer, the semantics of this function are identical to kastore_put().

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • key_len: The length of the key.

  • array: The array. Must be a pointer returned by malloc/calloc.

  • array_len: The number of elements in the array.

  • type: The type of the array.

  • flags: The insertion flags. Currently unused.

int kastore_oputs(kastore_t * self, const char * key, void * array, size_t array_len, int type, int flags)

Insert the specified NULL terminated key and array pair into the store, transferring ownership of the malloced array buffer to the store (own-put).

As for kastore_oput() except the key must be NULL-terminated C string.

Return

Return 0 on success or a negative value on failure.

Parameters
  • self: A pointer to a kastore object.

  • key: The key.

  • array: The array. Must be a pointer returned by malloc/calloc.

  • array_len: The number of elements in the array.

  • type: The type of the array.

  • flags: The insertion flags. Currently unused.

Typed oputs

int kastore_oputs_int8(kastore_t * self, const char * key, int8_t * array, size_t array_len, int flags)
int kastore_oputs_uint8(kastore_t * self, const char * key, uint8_t * array, size_t array_len, int flags)
int kastore_oputs_int16(kastore_t * self, const char * key, int16_t * array, size_t array_len, int flags)
int kastore_oputs_uint16(kastore_t * self, const char * key, uint16_t * array, size_t array_len, int flags)
int kastore_oputs_int32(kastore_t * self, const char * key, int32_t * array, size_t array_len, int flags)
int kastore_oputs_uint32(kastore_t * self, const char * key, uint32_t * array, size_t array_len, int flags)
int kastore_oputs_int64(kastore_t * self, const char * key, int64_t * array, size_t array_len, int flags)
int kastore_oputs_uint64(kastore_t * self, const char * key, uint64_t * array, size_t array_len, int flags)
int kastore_oputs_float32(kastore_t * self, const char * key, float * array, size_t array_len, int flags)
int kastore_oputs_float64(kastore_t * self, const char * key, double * array, size_t array_len, int flags)

Constants

Errors

KAS_ERR_GENERIC -1

Generic error thrown when no other message can be generated.

KAS_ERR_IO -2

An error occured during IO.

KAS_ERR_BAD_MODE -3

An unrecognised mode string was passed to open().

KAS_ERR_NO_MEMORY -4

Out-of-memory condition.

KAS_ERR_BAD_FILE_FORMAT -5

Attempt to read an unknown file format.

KAS_ERR_VERSION_TOO_OLD -6

The file is in kastore format, but the version is too old for this version of the library to read.

KAS_ERR_VERSION_TOO_NEW -7

The file is in kastore format, but the version is too new for this version of the library to read.

KAS_ERR_BAD_TYPE -8

An unknown type key was specified.

KAS_ERR_EMPTY_KEY -9

A zero-length key was specified.

KAS_ERR_DUPLICATE_KEY -10

A duplicate key was specified.

KAS_ERR_KEY_NOT_FOUND -11

The requested key does not exist in the store.

KAS_ERR_ILLEGAL_OPERATION -12

The requestion function cannot be called in the current mode.

KAS_ERR_TYPE_MISMATCH -13

The requested type does not match the type of the stored values.

KAS_ERR_EOF -14

End of file was reached while reading data.

KAS_ERR_BAD_FLAGS -15

Unknown flags were provided to open.

Types

KAS_INT8 0
KAS_UINT8 1
KAS_INT16 2
KAS_UINT16 3
KAS_INT32 4
KAS_UINT32 5
KAS_INT64 6
KAS_UINT64 7
KAS_FLOAT32 8
KAS_FLOAT64 9

Version information

KAS_VERSION_MAJOR 2

The library major version. Incremented when breaking changes to the API or ABI are introduced. This includes any changes to the signatures of functions and the sizes and types of externally visible structs.

KAS_VERSION_MINOR 1

The library minor version. Incremented when non-breaking backward-compatible changes to the API or ABI are introduced, i.e., the addition of a new function.

KAS_VERSION_PATCH 1

The library patch version. Incremented when any changes not relevant to the to the API or ABI are introduced, i.e., internal refactors of bugfixes.

KAS_FILE_VERSION_MAJOR 1

The file version major number. Incremented when any breaking changes are made to the file format.

KAS_FILE_VERSION_MINOR 0

The file version minor number. Incremented when non-breaking backward-compatible changes are made to the file format.

Miscellaneous functions

struct kas_version_t

Library version information.

Public Members

int major

The major version number.

int minor

The minor version number.

int patch

The patch version number.

kas_version_t kas_version(void)

Returns the API version.

The API follows the semver convention, where the major, minor and patch numbers have specific meanings. The versioning scheme here also takes into account ABI compatability.