| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- /*
- Based on SAMBA 7ce1356c9f571c55af70bd6b966fe50898c1582d.
- very efficient functions to manage mapping a id (such as a fnum) to
- a pointer. This is used for fnum and search id allocation.
- Copyright (C) Andrew Tridgell 2004
- This code is derived from lib/idr.c in the 2.6 Linux kernel, which was
- written by Jim Houston jim.houston@ccur.com, and is
- Copyright (C) 2002 by Concurrent Computer Corporation
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <ccan/idtree/idtree.h>
- #include <ccan/tal/tal.h>
- #include <stdint.h>
- #include <string.h>
- #define IDTREE_BITS 5
- #define IDTREE_FULL 0xfffffffful
- #if 0 /* unused */
- #define TOP_LEVEL_FULL (IDTREE_FULL >> 30)
- #endif
- #define IDTREE_SIZE (1 << IDTREE_BITS)
- #define IDTREE_MASK ((1 << IDTREE_BITS)-1)
- #define MAX_ID_SHIFT (sizeof(int)*8 - 1)
- #define MAX_ID_BIT (1U << MAX_ID_SHIFT)
- #define MAX_ID_MASK (MAX_ID_BIT - 1)
- #define MAX_LEVEL (MAX_ID_SHIFT + IDTREE_BITS - 1) / IDTREE_BITS
- #define IDTREE_FREE_MAX MAX_LEVEL + MAX_LEVEL
- #define set_bit(bit, v) (v) |= (1<<(bit))
- #define clear_bit(bit, v) (v) &= ~(1<<(bit))
- #define test_bit(bit, v) ((v) & (1<<(bit)))
- struct idtree_layer {
- uint32_t bitmap;
- struct idtree_layer *ary[IDTREE_SIZE];
- int count;
- };
- struct idtree {
- struct idtree_layer *top;
- struct idtree_layer *id_free;
- int layers;
- int id_free_cnt;
- };
- static struct idtree_layer *alloc_layer(struct idtree *idp)
- {
- struct idtree_layer *p;
- if (!(p = idp->id_free))
- return NULL;
- idp->id_free = p->ary[0];
- idp->id_free_cnt--;
- p->ary[0] = NULL;
- return p;
- }
- static int find_next_bit(uint32_t bm, int maxid, int n)
- {
- while (n<maxid && !test_bit(n, bm)) n++;
- return n;
- }
- static void free_layer(struct idtree *idp, struct idtree_layer *p)
- {
- p->ary[0] = idp->id_free;
- idp->id_free = p;
- idp->id_free_cnt++;
- }
- static int idtree_pre_get(struct idtree *idp)
- {
- while (idp->id_free_cnt < IDTREE_FREE_MAX) {
- struct idtree_layer *pn = talz(idp, struct idtree_layer);
- if(pn == NULL)
- return (0);
- free_layer(idp, pn);
- }
- return 1;
- }
- static int sub_alloc(struct idtree *idp, const void *ptr, int *starting_id)
- {
- int n, m, sh;
- struct idtree_layer *p, *pn;
- struct idtree_layer *pa[MAX_LEVEL+1];
- unsigned int l;
- int id, oid;
- uint32_t bm;
- memset(pa, 0, sizeof(pa));
- id = *starting_id;
- restart:
- p = idp->top;
- l = idp->layers;
- pa[l--] = NULL;
- while (1) {
- /*
- * We run around this while until we reach the leaf node...
- */
- n = (id >> (IDTREE_BITS*l)) & IDTREE_MASK;
- bm = ~p->bitmap;
- m = find_next_bit(bm, IDTREE_SIZE, n);
- if (m == IDTREE_SIZE) {
- /* no space available go back to previous layer. */
- l++;
- oid = id;
- id = (id | ((1 << (IDTREE_BITS*l))-1)) + 1;
- /* if already at the top layer, we need to grow */
- if (!(p = pa[l])) {
- *starting_id = id;
- return -2;
- }
- /* If we need to go up one layer, continue the
- * loop; otherwise, restart from the top.
- */
- sh = IDTREE_BITS * (l + 1);
- if (oid >> sh == id >> sh)
- continue;
- else
- goto restart;
- }
- if (m != n) {
- sh = IDTREE_BITS*l;
- id = ((id >> sh) ^ n ^ m) << sh;
- }
- if ((id >= MAX_ID_BIT) || (id < 0))
- return -1;
- if (l == 0)
- break;
- /*
- * Create the layer below if it is missing.
- */
- if (!p->ary[m]) {
- if (!(pn = alloc_layer(idp)))
- return -1;
- p->ary[m] = pn;
- p->count++;
- }
- pa[l--] = p;
- p = p->ary[m];
- }
- /*
- * We have reached the leaf node, plant the
- * users pointer and return the raw id.
- */
- p->ary[m] = (struct idtree_layer *)ptr;
- set_bit(m, p->bitmap);
- p->count++;
- /*
- * If this layer is full mark the bit in the layer above
- * to show that this part of the radix tree is full.
- * This may complete the layer above and require walking
- * up the radix tree.
- */
- n = id;
- while (p->bitmap == IDTREE_FULL) {
- if (!(p = pa[++l]))
- break;
- n = n >> IDTREE_BITS;
- set_bit((n & IDTREE_MASK), p->bitmap);
- }
- return(id);
- }
- static int idtree_get_new_above_int(struct idtree *idp,
- const void *ptr, int starting_id)
- {
- struct idtree_layer *p, *pn;
- int layers, v, id;
- idtree_pre_get(idp);
- id = starting_id;
- build_up:
- p = idp->top;
- layers = idp->layers;
- if (!p) {
- if (!(p = alloc_layer(idp)))
- return -1;
- layers = 1;
- }
- /*
- * Add a new layer to the top of the tree if the requested
- * id is larger than the currently allocated space.
- */
- while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDTREE_BITS)))) {
- layers++;
- if (!p->count)
- continue;
- if (!(pn = alloc_layer(idp))) {
- /*
- * The allocation failed. If we built part of
- * the structure tear it down.
- */
- for (pn = p; p && p != idp->top; pn = p) {
- p = p->ary[0];
- pn->ary[0] = NULL;
- pn->bitmap = pn->count = 0;
- free_layer(idp, pn);
- }
- return -1;
- }
- pn->ary[0] = p;
- pn->count = 1;
- if (p->bitmap == IDTREE_FULL)
- set_bit(0, pn->bitmap);
- p = pn;
- }
- idp->top = p;
- idp->layers = layers;
- v = sub_alloc(idp, ptr, &id);
- if (v == -2)
- goto build_up;
- return(v);
- }
- static int sub_remove(struct idtree *idp, int shift, int id)
- {
- struct idtree_layer *p = idp->top;
- struct idtree_layer **pa[1+MAX_LEVEL];
- struct idtree_layer ***paa = &pa[0];
- int n;
- *paa = NULL;
- *++paa = &idp->top;
- while ((shift > 0) && p) {
- n = (id >> shift) & IDTREE_MASK;
- clear_bit(n, p->bitmap);
- *++paa = &p->ary[n];
- p = p->ary[n];
- shift -= IDTREE_BITS;
- }
- n = id & IDTREE_MASK;
- if (p != NULL && test_bit(n, p->bitmap)) {
- clear_bit(n, p->bitmap);
- p->ary[n] = NULL;
- while(*paa && ! --((**paa)->count)){
- free_layer(idp, **paa);
- **paa-- = NULL;
- }
- if ( ! *paa )
- idp->layers = 0;
- return 0;
- }
- return -1;
- }
- void *idtree_lookup(const struct idtree *idp, int id)
- {
- int n;
- struct idtree_layer *p;
- n = idp->layers * IDTREE_BITS;
- p = idp->top;
- /*
- * This tests to see if bits outside the current tree are
- * present. If so, tain't one of ours!
- */
- if (n + IDTREE_BITS < 31 &&
- (id & ~(~0 << MAX_ID_SHIFT)) >> (n + IDTREE_BITS))
- return NULL;
- /* Mask off upper bits we don't use for the search. */
- id &= MAX_ID_MASK;
- while (n >= IDTREE_BITS && p) {
- n -= IDTREE_BITS;
- p = p->ary[(id >> n) & IDTREE_MASK];
- }
- return((void *)p);
- }
- bool idtree_remove(struct idtree *idp, int id)
- {
- struct idtree_layer *p;
- /* Mask off upper bits we don't use for the search. */
- id &= MAX_ID_MASK;
- if (sub_remove(idp, (idp->layers - 1) * IDTREE_BITS, id) == -1) {
- return false;
- }
- if ( idp->top && idp->top->count == 1 &&
- (idp->layers > 1) &&
- idp->top->ary[0]) {
- /* We can drop a layer */
- p = idp->top->ary[0];
- idp->top->bitmap = idp->top->count = 0;
- free_layer(idp, idp->top);
- idp->top = p;
- --idp->layers;
- }
- while (idp->id_free_cnt >= IDTREE_FREE_MAX) {
- p = alloc_layer(idp);
- tal_free(p);
- }
- return true;
- }
- struct idtree *idtree_new(void *mem_ctx)
- {
- return talz(mem_ctx, struct idtree);
- }
- int idtree_add(struct idtree *idp, const void *ptr, int limit)
- {
- int ret = idtree_get_new_above_int(idp, ptr, 0);
- if (ret > limit) {
- idtree_remove(idp, ret);
- return -1;
- }
- return ret;
- }
- int idtree_add_above(struct idtree *idp, const void *ptr,
- int starting_id, int limit)
- {
- int ret = idtree_get_new_above_int(idp, ptr, starting_id);
- if (ret > limit) {
- idtree_remove(idp, ret);
- return -1;
- }
- return ret;
- }
|