CRADIX(3) | Library Functions Manual | CRADIX(3) |
struct cradix;
struct cradix_node;
struct cradix_ops;
void
cradix_init(struct cradix *C, const struct cradix_ops *ops);
void
cradix_destroy(struct cradix *C);
struct cradix_node *
cradix_lookup(const struct cradix *C, const void *key, size_t keylen);
bool
cradix_insert_begin(struct cradix *C, struct cradix_insert *I);
void
cradix_insert_end(struct cradix *C, struct cradix_insert *I);
struct cradix_node *
cradix_insert(struct cradix *C, struct cradix_node *N, struct cradix_insert *I);
void
cradix_delete_begin(struct cradix *C, struct cradix_delete *D);
void
cradix_delete_end(struct cradix *C, struct cradix_delete *D);
void
cradix_delete(struct cradix *C, struct cradix_node *N, struct cradix_delete *D);
struct cradix_node *
cradix_delete_key(struct cradix *C, uint64_t key, struct cradix_delete *D);
However, insertion and deletion must be serialized.
The caller must allocate storage for a struct cradix object and one struct cradix_node object for every element in the cradix tree. These objects should otherwise be treated as opaque, and not inspected, modified, or copied. Each element has a corresponding key, which is 64-bit integer.
The caller must define operations collected in a struct cradix_ops object to retrieve the key of an element, and optionally to allocate, free, and ‘recycle' memory. ‘Recycling' here means waiting until all readers have finished using the memory before freeing it.
struct cradix_ops { const uint64_t (*key)(const struct cradix *C, const struct cradix_node *N); void * (*alloc)(const struct cradix *C, size_t size); void (*free)(const struct cradix *C, void *ptr, size_t size); void (*recycle)(const struct cradix *C, void *ptr, size_t size); };
The operations are:
This operation is not necessary, but it provides a debugging aid by asserting that the tree is in a consistent state and preventing subsequent use.
If I is nonnull, it must have been initialized by cradix_insert_begin(), and cradix_insert() will use memory preallocated in I, and will never fail. Otherwise, if I is NULL, then cradix_insert() may call the alloc() operation to allocate a branch structure if necessary; if that fails, then cradix_insert() will return NULL to indicate memory allocation failure.
If D is nonnull, it must have been initialized by cradix_delete_begin(), and cradix_delete() stashes any memory no longer used by C in D so that a subsequent cradix_delete_end() can recycle it. Otherwise, if I is NULL, then cradix_delete() may call the recycle() operation to recycle a branch structure if necessary.
If D is nonnull, it must have been initialized by cradix_delete_begin(C, D), and cradix_delete(C, key, D) stashes any memory no longer used by C in D so that a subsequent cradix_delete_end(C, D) can recycle it. Otherwise, cradix_delete_key(C, key) NULL may call the recycle() operation to recycle a branch structure if necessary.
struct widget { uint64_t key; struct cradix_node node; ... }; struct { kmutex_t lock; struct cradix tree; struct pserialize *psz; } widgets __cacheline_aligned; static uint64_t widget_key(const struct cradix *C, const struct cradix_node *N) { const struct widget *W = container_of(N, struct widget, node); KASSERT(C == &widgets.tree); return W->key; } static void widget_recycle(struct cradix *C, void *ptr, size_t size) { KASSERT(C == &widgets.tree); /* Wait for all pserialize read sections to complete. */ pserialize_perform(widgets.psz); /* Memory is no longer in use; free it. */ kmem_free(ptr, size); } static const struct cradix_ops widget_cradix_ops = { .key = &widget_key, .recycle = &widget_recycle, };
Lookup:
uint64_t key = ...; struct widget *W; int result = -1; int s; s = pserialize_read_enter(); W = cradix_lookup(&widgets.tree, key); if (W == NULL) goto out; result = bromble(W); out: pserialize_read_exit(s);
Insert:
uint64_t key = ...; struct widget *W, *W0; struct cradix_insert insert; W = alloc_widget(); W->key = key; W->... = ...; if (!cradix_insert_begin(&widgets.tree, &insert)) { free_widget(W); return ENOMEM; } mutex_enter(&widgets.lock); W0 = cradix_insert(&widgets.tree, W, &insert); mutex_exit(&widgets.lock); cradix_insert_end(&widgets.tree, &insert); if (W0 != W) { /* Failed to insert -- key already used. */ free_widget(W); return EEXIST; }
Delete:
uint64_t key = ...; struct widget *W; struct cradix_delete delete; cradix_delete_begin(&widgets.tree, &delete); mutex_enter(&widgets.lock); W = cradix_delete_key(&widgets.tree, &key, sizeof key); mutex_exit(&widgets.lock); cradix_delete_end(&widgets.tree, &delete); if (W != NULL) free_widget(W);
Daniel J. Bernstein, Crit-bit trees, https://cr.yp.to/critbit.html.
December 10, 2019 | NetBSD 6.1_STABLE |