X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=ccan%2Fintmap%2Fintmap.c;fp=ccan%2Fintmap%2Fintmap.c;h=d27b645695c43d4c52d66ab37358f92f624c5d06;hb=d9e93014a999102aa1cc9979e041cd58e6aca724;hp=0000000000000000000000000000000000000000;hpb=3f642347378afc9e1db1768d88c9f5b2baffe9ba;p=ccan diff --git a/ccan/intmap/intmap.c b/ccan/intmap/intmap.c new file mode 100644 index 00000000..d27b6456 --- /dev/null +++ b/ccan/intmap/intmap.c @@ -0,0 +1,212 @@ +/* CC0 license (public domain) - see LICENSE file for details */ +/* This code is based on ccan/strmap.c. */ +#include +#include +#include +#include +#include +#include +#include + +struct node { + /* These point to strings or nodes. */ + struct intmap child[2]; + /* The bit where these children differ (0 == lsb) */ + u8 bit_num; +}; + +/* Closest member to this in a non-empty map. */ +static struct intmap *closest(struct intmap *n, intmap_index_t index) +{ + /* Anything with NULL value is a node. */ + while (!n->v) { + u8 direction = (index >> n->u.n->bit_num) & 1; + n = &n->u.n->child[direction]; + } + return n; +} + +void *intmap_get_(const struct intmap *map, intmap_index_t index) +{ + struct intmap *n; + + /* Not empty map? */ + if (!intmap_empty_(map)) { + n = closest((struct intmap *)map, index); + if (index == n->u.i) + return n->v; + } + errno = ENOENT; + return NULL; +} + +bool intmap_add_(struct intmap *map, intmap_index_t index, const void *value) +{ + struct intmap *n; + struct node *newn; + u8 bit_num, new_dir; + + assert(value); + + /* Empty map? */ + if (intmap_empty_(map)) { + map->u.i = index; + map->v = (void *)value; + return true; + } + + /* Find closest existing member. */ + n = closest(map, index); + + /* Find highest bit where they differ. */ + bit_num = ilog64(n->u.i ^ index); + if (bit_num == 0) { + errno = EEXIST; + return false; + } + bit_num--; + + assert(bit_num < CHAR_BIT*sizeof(index)); + + /* Which direction do we go at this bit? */ + new_dir = (index >> bit_num) & 1; + + /* Allocate new node. */ + newn = malloc(sizeof(*newn)); + if (!newn) { + errno = ENOMEM; + return false; + } + newn->bit_num = bit_num; + newn->child[new_dir].v = (void *)value; + newn->child[new_dir].u.i = index; + + /* Find where to insert: not closest, but first which differs! */ + n = map; + while (!n->v) { + u8 direction; + + /* Subtle: bit numbers are "backwards" for comparison */ + if (n->u.n->bit_num < bit_num) + break; + + direction = (index >> n->u.n->bit_num) & 1; + n = &n->u.n->child[direction]; + } + + newn->child[!new_dir] = *n; + n->u.n = newn; + n->v = NULL; + return true; +} + +void *intmap_del_(struct intmap *map, intmap_index_t index) +{ + struct intmap *parent = NULL, *n; + u8 direction; + void *value; + + /* Empty map? */ + if (intmap_empty_(map)) { + errno = ENOENT; + return NULL; + } + + /* Find closest, but keep track of parent. */ + n = map; + /* Anything with NULL value is a node. */ + while (!n->v) { + parent = n; + direction = (index >> n->u.n->bit_num) & 1; + n = &n->u.n->child[direction]; + } + + /* Did we find it? */ + if (index != n->u.i) { + errno = ENOENT; + return NULL; + } + + value = n->v; + + if (!parent) { + /* We deleted last node. */ + intmap_init_(map); + } else { + struct node *old = parent->u.n; + /* Raise other node to parent. */ + *parent = old->child[!direction]; + free(old); + } + errno = 0; + return value; +} + +intmap_index_t intmap_first_(const struct intmap *map) +{ + const struct intmap *n; + + if (intmap_empty_(map)) { + errno = ENOENT; + return UINTMAP_NONE; + } + + n = map; + /* Anything with NULL value is a node. */ + while (!n->v) + n = &n->u.n->child[0]; + errno = 0; + return n->u.i; +} + +intmap_index_t intmap_after_(const struct intmap *map, intmap_index_t index) +{ + const struct intmap *n, *prev = NULL; + + /* Special case of unincrementable value, or empty map */ + if (index == UINTMAP_NONE || intmap_empty_(map)) { + errno = ENOENT; + return UINTMAP_NONE; + } + + /* Follow down, track the last place where we could have set a bit + * instead of clearing it: this is the higher alternative tree. */ + n = map; + while (!n->v) { + u8 direction = (index >> n->u.n->bit_num) & 1; + if (!direction) + prev = n; + n = &n->u.n->child[direction]; + } + + /* Found a successor? */ + if (n->u.i > index) { + errno = 0; + return n->u.i; + } + + /* Nowhere to go back up to? */ + if (!prev) { + errno = ENOENT; + return UINTMAP_NONE; + } + + /* Get first one from that other branch. */ + return intmap_first_(&prev->u.n->child[1]); +} + +static void clear(struct intmap n) +{ + if (!n.v) { + clear(n.u.n->child[0]); + clear(n.u.n->child[1]); + free(n.u.n); + } +} + +void intmap_clear_(struct intmap *map) +{ + if (!intmap_empty_(map)) + clear(*map); + intmap_init_(map); +}