Go to file
Attractive Chaos 2944549526 update kalloc
2024-06-03 00:40:19 -04:00
cpp fixed a bug in khashl; resolves #135 2020-01-13 20:05:38 -05:00
lua change to 0 indexed array 2011-06-03 22:14:16 -04:00
test Add missing #include for read(2) 2023-09-21 08:22:16 +12:00
.gitignore Add kputw() and kputl() tests 2013-07-22 16:07:45 +01:00
bgzf.c minor changes 2011-10-28 16:15:33 -04:00
bgzf.h improved backward compatibility 2011-10-28 16:17:07 -04:00
kalloc.c update kalloc 2024-06-03 00:40:19 -04:00
kalloc.h update kalloc 2024-06-03 00:40:19 -04:00
kavl-lite.h added kavll_size() 2021-03-25 22:58:27 -04:00
kavl.h fixed insert counting err; couting with del 2018-06-17 22:01:44 -04:00
kbit.h some basic bit operations 2012-04-08 19:16:22 -04:00
kbtree.h added 2015-09-29 08:28:48 -04:00
kdq.h added dequeue 2016-08-22 08:34:34 -04:00
keigen.c Eigenvalues for dense symmetric matrices 2018-05-11 13:03:25 -04:00
keigen.h Eigenvalues for dense symmetric matrices 2018-05-11 13:03:25 -04:00
ketopt.h Merge branch 'master' of github.com:attractivechaos/klib 2019-02-27 12:07:51 -05:00
kexpr.c kexpr: evaluate by return type 2016-08-16 17:08:06 -04:00
kexpr.h kexpr: evaluate by return type 2016-08-16 17:08:06 -04:00
kgraph.h Graph related routines. Unfinished. DON'T USE! 2011-12-28 17:24:24 -05:00
khash.h fixed a few typos in khash.h; resolves #110 2018-10-30 20:03:33 -04:00
khashl.h sync with khashl 2024-05-06 09:39:23 -04:00
khmm.c Added the khmm library 2011-01-13 12:55:01 -05:00
khmm.h Added the khmm library 2011-01-13 12:55:01 -05:00
klist.h Prevent unused function warnings in khash.h, klist.h 2015-07-23 11:31:17 +01:00
kmath.c removed MT19937-64. 2018-08-30 22:58:52 -10:00
kmath.h removed MT19937-64. 2018-08-30 22:58:52 -10:00
knetfile.c Don't call freeaddrinfo() when getaddrinfo() fails 2015-07-23 11:31:25 +01:00
knetfile.h Added the knetfile library 2011-01-13 12:53:26 -05:00
knhx.c Fixed output bug where branch length is not printed on branches to leaf nodes 2015-06-26 14:21:26 -04:00
knhx.h print tree in the Newick format 2012-12-18 20:18:20 -05:00
kopen.c Don't call freeaddrinfo() when getaddrinfo() fails 2015-07-23 11:31:25 +01:00
krmq.h more tests 2019-12-19 13:52:42 -05:00
krng.h Added a version of xoroshiro128+ 2018-08-30 23:05:18 -10:00
ksa.c Constructing suffix array for multi-sentinel str. 2011-08-19 18:08:48 -04:00
kseq.h Align backspace character [cosmetic] 2023-09-21 08:11:08 +12:00
kson.c kson_query() -> kson_by_path() for clarity 2014-11-30 01:15:00 -05:00
kson.h kson_query() -> kson_by_path() for clarity 2014-11-30 01:15:00 -05:00
ksort.h added radix sort 2021-03-25 22:08:19 -04:00
kstring.c Fix undefined behaviour and sign extension issues in kstrtok 2018-01-11 14:30:02 +00:00
kstring.h Add kgetline() to kstring.c/.h 2015-07-23 10:31:57 +01:00
ksw.c bugfix: point address changes 2013-02-20 20:14:04 -05:00
ksw.h added NW and SW-extension; backported from bwa 2013-02-12 16:20:36 -05:00
kthread.c use long for forpool 2017-03-04 09:03:57 -05:00
kthread.h kt_for with a mini thread pool; buggy 2016-12-16 15:20:24 -05:00
kurl.c default to following redirect & no SSL certificate 2014-11-28 11:49:11 -05:00
kurl.h allow to feed change key/secret/id-file 2013-11-21 00:22:33 -05:00
kvec.h Two bugs reported by istreeter and wanghc78 2013-01-26 18:09:06 -05:00
LICENSE.txt Added LICENSE; resolves #149 2021-04-06 19:38:03 -04:00
README.md fixed a few typos in khash.h; resolves #110 2018-10-30 20:03:33 -04:00

Klib: a Generic Library in C

Overview

Klib is a standalone and lightweight C library distributed under MIT/X11 license. Most components are independent of external libraries, except the standard C library, and independent of each other. To use a component of this library, you only need to copy a couple of files to your source code tree without worrying about library dependencies.

Klib strives for efficiency and a small memory footprint. Some components, such as khash.h, kbtree.h, ksort.h and kvec.h, are among the most efficient implementations of similar algorithms or data structures in all programming languages, in terms of both speed and memory use.

A new documentation is available here which includes most information in this README file.

Common components

Components for more specific use cases

Methodology

For the implementation of generic containers, klib extensively uses C macros. To use these data structures, we usually need to instantiate methods by expanding a long macro. This makes the source code look unusual or even ugly and adds difficulty to debugging. Unfortunately, for efficient generic programming in C that lacks template, using macros is the only solution. Only with macros, we can write a generic container which, once instantiated, compete with a type-specific container in efficiency. Some generic libraries in C, such as Glib, use the void* type to implement containers. These implementations are usually slower and use more memory than klib (see this benchmark).

To effectively use klib, it is important to understand how it achieves generic programming. We will use the hash table library as an example:

#include "khash.h"
KHASH_MAP_INIT_INT(m32, char)        // instantiate structs and methods
int main() {
    int ret, is_missing;
    khint_t k;
    khash_t(m32) *h = kh_init(m32);  // allocate a hash table
    k = kh_put(m32, h, 5, &ret);     // insert a key to the hash table
    if (!ret) kh_del(m32, h, k);
    kh_value(h, k) = 10;             // set the value
    k = kh_get(m32, h, 10);          // query the hash table
    is_missing = (k == kh_end(h));   // test if the key is present
    k = kh_get(m32, h, 5);
    kh_del(m32, h, k);               // remove a key-value pair
    for (k = kh_begin(h); k != kh_end(h); ++k)  // traverse
        if (kh_exist(h, k))          // test if a bucket contains data
			kh_value(h, k) = 1;
    kh_destroy(m32, h);              // deallocate the hash table
    return 0;
}

In this example, the second line instantiates a hash table with unsigned as the key type and char as the value type. m32 names such a type of hash table. All types and functions associated with this name are macros, which will be explained later. Macro kh_init() initiates a hash table and kh_destroy() frees it. kh_put() inserts a key and returns the iterator (or the position) in the hash table. kh_get() and kh_del() get a key and delete an element, respectively. Macro kh_exist() tests if an iterator (or a position) is filled with data.

An immediate question is this piece of code does not look like a valid C program (e.g. lacking semicolon, assignment to an apparent function call and apparent undefined m32 'variable'). To understand why the code is correct, let's go a bit further into the source code of khash.h, whose skeleton looks like:

#define KHASH_INIT(name, SCOPE, key_t, val_t, is_map, _hashf, _hasheq) \
  typedef struct { \
    int n_buckets, size, n_occupied, upper_bound; \
    unsigned *flags; \
    key_t *keys; \
    val_t *vals; \
  } kh_##name##_t; \
  SCOPE inline kh_##name##_t *init_##name() { \
    return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \
  } \
  SCOPE inline int get_##name(kh_##name##_t *h, key_t k) \
  ... \
  SCOPE inline void destroy_##name(kh_##name##_t *h) { \
    if (h) { \
      free(h->keys); free(h->flags); free(h->vals); free(h); \
    } \
  }

#define _int_hf(key) (unsigned)(key)
#define _int_heq(a, b) (a == b)
#define khash_t(name) kh_##name##_t
#define kh_value(h, k) ((h)->vals[k])
#define kh_begin(h, k) 0
#define kh_end(h) ((h)->n_buckets)
#define kh_init(name) init_##name()
#define kh_get(name, h, k) get_##name(h, k)
#define kh_destroy(name, h) destroy_##name(h)
...
#define KHASH_MAP_INIT_INT(name, val_t) \
	KHASH_INIT(name, static, unsigned, val_t, is_map, _int_hf, _int_heq)

KHASH_INIT() is a huge macro defining all the structs and methods. When this macro is called, all the code inside it will be inserted by the C preprocess to the place where it is called. If the macro is called multiple times, multiple copies of the code will be inserted. To avoid naming conflict of hash tables with different key-value types, the library uses token concatenation, which is a preprocessor feature whereby we can substitute part of a symbol based on the parameter of the macro. In the end, the C preprocessor will generate the following code and feed it to the compiler (macro kh_exist(h,k) is a little complex and not expanded for simplicity):

typedef struct {
  int n_buckets, size, n_occupied, upper_bound;
  unsigned *flags;
  unsigned *keys;
  char *vals;
} kh_m32_t;
static inline kh_m32_t *init_m32() {
  return (kh_m32_t*)calloc(1, sizeof(kh_m32_t));
}
static inline int get_m32(kh_m32_t *h, unsigned k)
...
static inline void destroy_m32(kh_m32_t *h) {
  if (h) {
    free(h->keys); free(h->flags); free(h->vals); free(h);
  }
}

int main() {
	int ret, is_missing;
	khint_t k;
	kh_m32_t *h = init_m32();
	k = put_m32(h, 5, &ret);
	if (!ret) del_m32(h, k);
	h->vals[k] = 10;
	k = get_m32(h, 10);
	is_missing = (k == h->n_buckets);
	k = get_m32(h, 5);
	del_m32(h, k);
	for (k = 0; k != h->n_buckets; ++k)
		if (kh_exist(h, k)) h->vals[k] = 1;
	destroy_m32(h);
	return 0;
}

This is the C program we know.

From this example, we can see that macros and the C preprocessor plays a key role in klib. Klib is fast partly because the compiler knows the key-value type at the compile time and is able to optimize the code to the same level as type-specific code. A generic library written with void* will not get such performance boost.

Massively inserting code upon instantiation may remind us of C++'s slow compiling speed and huge binary size when STL/boost is in use. Klib is much better in this respect due to its small code size and component independency. Inserting several hundreds lines of code won't make compiling obviously slower.

Resources

  • Library documentation, if present, is available in the header files. Examples can be found in the test/ directory.
  • Obsolete documentation of the hash table library can be found at SourceForge. This README is partly adapted from the old documentation.
  • Blog post describing the hash table library.
  • Blog post on why using void* for generic programming may be inefficient.
  • Blog post on the generic stream buffer.
  • Blog post evaluating the performance of kvec.h.
  • Blog post arguing B-tree may be a better data structure than a binary search tree.
  • Blog post evaluating the performance of khash.h and kbtree.h among many other implementations. An older version of the benchmark is also available.
  • Blog post benchmarking internal sorting algorithms and implementations.
  • Blog post on the k-small algorithm.
  • Blog post on the Hooke-Jeeve's algorithm for nonlinear programming.