13b3a8eb9SGleb Smirnoff /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3fe267a55SPedro F. Giffuni * 43b3a8eb9SGleb Smirnoff * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa 53b3a8eb9SGleb Smirnoff * All rights reserved 63b3a8eb9SGleb Smirnoff * 73b3a8eb9SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 83b3a8eb9SGleb Smirnoff * modification, are permitted provided that the following conditions 93b3a8eb9SGleb Smirnoff * are met: 103b3a8eb9SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 113b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 123b3a8eb9SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 133b3a8eb9SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 143b3a8eb9SGleb Smirnoff * documentation and/or other materials provided with the distribution. 153b3a8eb9SGleb Smirnoff * 163b3a8eb9SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 173b3a8eb9SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 183b3a8eb9SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 193b3a8eb9SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 203b3a8eb9SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 213b3a8eb9SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 223b3a8eb9SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 233b3a8eb9SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 243b3a8eb9SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 253b3a8eb9SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 263b3a8eb9SGleb Smirnoff * SUCH DAMAGE. 273b3a8eb9SGleb Smirnoff */ 283b3a8eb9SGleb Smirnoff 293b3a8eb9SGleb Smirnoff /* 303b3a8eb9SGleb Smirnoff * Binary heap and hash tables, used in dummynet 313b3a8eb9SGleb Smirnoff * 323b3a8eb9SGleb Smirnoff * $FreeBSD$ 333b3a8eb9SGleb Smirnoff */ 343b3a8eb9SGleb Smirnoff 353b3a8eb9SGleb Smirnoff #include <sys/cdefs.h> 363b3a8eb9SGleb Smirnoff #include <sys/param.h> 373b3a8eb9SGleb Smirnoff #ifdef _KERNEL 383b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$"); 393b3a8eb9SGleb Smirnoff #include <sys/systm.h> 403b3a8eb9SGleb Smirnoff #include <sys/malloc.h> 413b3a8eb9SGleb Smirnoff #include <sys/kernel.h> 423b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/dn_heap.h> 433b3a8eb9SGleb Smirnoff #ifndef log 443b3a8eb9SGleb Smirnoff #define log(x, arg...) 453b3a8eb9SGleb Smirnoff #endif 463b3a8eb9SGleb Smirnoff 473b3a8eb9SGleb Smirnoff #else /* !_KERNEL */ 483b3a8eb9SGleb Smirnoff 493b3a8eb9SGleb Smirnoff #include <stdio.h> 503b3a8eb9SGleb Smirnoff #include <dn_test.h> 513b3a8eb9SGleb Smirnoff #include <strings.h> 523b3a8eb9SGleb Smirnoff #include <stdlib.h> 533b3a8eb9SGleb Smirnoff 543b3a8eb9SGleb Smirnoff #include "dn_heap.h" 553b3a8eb9SGleb Smirnoff #define log(x, arg...) fprintf(stderr, ## arg) 563b3a8eb9SGleb Smirnoff #define panic(x...) fprintf(stderr, ## x), exit(1) 57e38e277fSLuigi Rizzo #define MALLOC_DEFINE(a, b, c) volatile int __dummy__ ## a __attribute__((__unused__)) 583b3a8eb9SGleb Smirnoff static void *my_malloc(int s) { return malloc(s); } 593b3a8eb9SGleb Smirnoff static void my_free(void *p) { free(p); } 603b3a8eb9SGleb Smirnoff #define malloc(s, t, w) my_malloc(s) 613b3a8eb9SGleb Smirnoff #define free(p, t) my_free(p) 623b3a8eb9SGleb Smirnoff #endif /* !_KERNEL */ 633b3a8eb9SGleb Smirnoff 643b3a8eb9SGleb Smirnoff static MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap"); 653b3a8eb9SGleb Smirnoff 663b3a8eb9SGleb Smirnoff /* 673b3a8eb9SGleb Smirnoff * Heap management functions. 683b3a8eb9SGleb Smirnoff * 693b3a8eb9SGleb Smirnoff * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2. 703b3a8eb9SGleb Smirnoff * Some macros help finding parent/children so we can optimize them. 713b3a8eb9SGleb Smirnoff * 723b3a8eb9SGleb Smirnoff * heap_init() is called to expand the heap when needed. 733b3a8eb9SGleb Smirnoff * Increment size in blocks of 16 entries. 743b3a8eb9SGleb Smirnoff * Returns 1 on error, 0 on success 753b3a8eb9SGleb Smirnoff */ 763b3a8eb9SGleb Smirnoff #define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 ) 773b3a8eb9SGleb Smirnoff #define HEAP_LEFT(x) ( (x)+(x) + 1 ) 783b3a8eb9SGleb Smirnoff #define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; } 793b3a8eb9SGleb Smirnoff #define HEAP_INCREMENT 15 803b3a8eb9SGleb Smirnoff 813b3a8eb9SGleb Smirnoff static int 823b3a8eb9SGleb Smirnoff heap_resize(struct dn_heap *h, unsigned int new_size) 833b3a8eb9SGleb Smirnoff { 843b3a8eb9SGleb Smirnoff struct dn_heap_entry *p; 853b3a8eb9SGleb Smirnoff 864d85bfebSLuigi Rizzo if ((unsigned int)h->size >= new_size ) /* have enough room */ 873b3a8eb9SGleb Smirnoff return 0; 883b3a8eb9SGleb Smirnoff #if 1 /* round to the next power of 2 */ 893b3a8eb9SGleb Smirnoff new_size |= new_size >> 1; 903b3a8eb9SGleb Smirnoff new_size |= new_size >> 2; 913b3a8eb9SGleb Smirnoff new_size |= new_size >> 4; 923b3a8eb9SGleb Smirnoff new_size |= new_size >> 8; 933b3a8eb9SGleb Smirnoff new_size |= new_size >> 16; 943b3a8eb9SGleb Smirnoff #else 953b3a8eb9SGleb Smirnoff new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT; 963b3a8eb9SGleb Smirnoff #endif 97*454529cdSPedro F. Giffuni p = mallocarray(new_size, sizeof(*p), M_DN_HEAP, M_NOWAIT); 983b3a8eb9SGleb Smirnoff if (p == NULL) { 993b3a8eb9SGleb Smirnoff printf("--- %s, resize %d failed\n", __func__, new_size ); 1003b3a8eb9SGleb Smirnoff return 1; /* error */ 1013b3a8eb9SGleb Smirnoff } 1023b3a8eb9SGleb Smirnoff if (h->size > 0) { 1033b3a8eb9SGleb Smirnoff bcopy(h->p, p, h->size * sizeof(*p) ); 1043b3a8eb9SGleb Smirnoff free(h->p, M_DN_HEAP); 1053b3a8eb9SGleb Smirnoff } 1063b3a8eb9SGleb Smirnoff h->p = p; 1073b3a8eb9SGleb Smirnoff h->size = new_size; 1083b3a8eb9SGleb Smirnoff return 0; 1093b3a8eb9SGleb Smirnoff } 1103b3a8eb9SGleb Smirnoff 1113b3a8eb9SGleb Smirnoff int 1123b3a8eb9SGleb Smirnoff heap_init(struct dn_heap *h, int size, int ofs) 1133b3a8eb9SGleb Smirnoff { 1143b3a8eb9SGleb Smirnoff if (heap_resize(h, size)) 1153b3a8eb9SGleb Smirnoff return 1; 1163b3a8eb9SGleb Smirnoff h->elements = 0; 1173b3a8eb9SGleb Smirnoff h->ofs = ofs; 1183b3a8eb9SGleb Smirnoff return 0; 1193b3a8eb9SGleb Smirnoff } 1203b3a8eb9SGleb Smirnoff 1213b3a8eb9SGleb Smirnoff /* 1223b3a8eb9SGleb Smirnoff * Insert element in heap. Normally, p != NULL, we insert p in 1233b3a8eb9SGleb Smirnoff * a new position and bubble up. If p == NULL, then the element is 1243b3a8eb9SGleb Smirnoff * already in place, and key is the position where to start the 1253b3a8eb9SGleb Smirnoff * bubble-up. 1263b3a8eb9SGleb Smirnoff * Returns 1 on failure (cannot allocate new heap entry) 1273b3a8eb9SGleb Smirnoff * 1283b3a8eb9SGleb Smirnoff * If ofs > 0 the position (index, int) of the element in the heap is 1293b3a8eb9SGleb Smirnoff * also stored in the element itself at the given offset in bytes. 1303b3a8eb9SGleb Smirnoff */ 1313b3a8eb9SGleb Smirnoff #define SET_OFFSET(h, i) do { \ 1323b3a8eb9SGleb Smirnoff if (h->ofs > 0) \ 1333b3a8eb9SGleb Smirnoff *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i; \ 1343b3a8eb9SGleb Smirnoff } while (0) 1353b3a8eb9SGleb Smirnoff /* 1363b3a8eb9SGleb Smirnoff * RESET_OFFSET is used for sanity checks. It sets ofs 1373b3a8eb9SGleb Smirnoff * to an invalid value. 1383b3a8eb9SGleb Smirnoff */ 1393b3a8eb9SGleb Smirnoff #define RESET_OFFSET(h, i) do { \ 1403b3a8eb9SGleb Smirnoff if (h->ofs > 0) \ 1413b3a8eb9SGleb Smirnoff *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16; \ 1423b3a8eb9SGleb Smirnoff } while (0) 1433b3a8eb9SGleb Smirnoff 1443b3a8eb9SGleb Smirnoff int 1453b3a8eb9SGleb Smirnoff heap_insert(struct dn_heap *h, uint64_t key1, void *p) 1463b3a8eb9SGleb Smirnoff { 1473b3a8eb9SGleb Smirnoff int son = h->elements; 1483b3a8eb9SGleb Smirnoff 1493b3a8eb9SGleb Smirnoff //log("%s key %llu p %p\n", __FUNCTION__, key1, p); 1503b3a8eb9SGleb Smirnoff if (p == NULL) { /* data already there, set starting point */ 1513b3a8eb9SGleb Smirnoff son = key1; 1523b3a8eb9SGleb Smirnoff } else { /* insert new element at the end, possibly resize */ 1533b3a8eb9SGleb Smirnoff son = h->elements; 1543b3a8eb9SGleb Smirnoff if (son == h->size) /* need resize... */ 1553b3a8eb9SGleb Smirnoff // XXX expand by 16 or so 1563b3a8eb9SGleb Smirnoff if (heap_resize(h, h->elements+16) ) 1573b3a8eb9SGleb Smirnoff return 1; /* failure... */ 1583b3a8eb9SGleb Smirnoff h->p[son].object = p; 1593b3a8eb9SGleb Smirnoff h->p[son].key = key1; 1603b3a8eb9SGleb Smirnoff h->elements++; 1613b3a8eb9SGleb Smirnoff } 1623b3a8eb9SGleb Smirnoff /* make sure that son >= father along the path */ 1633b3a8eb9SGleb Smirnoff while (son > 0) { 1643b3a8eb9SGleb Smirnoff int father = HEAP_FATHER(son); 1653b3a8eb9SGleb Smirnoff struct dn_heap_entry tmp; 1663b3a8eb9SGleb Smirnoff 1673b3a8eb9SGleb Smirnoff if (DN_KEY_LT( h->p[father].key, h->p[son].key ) ) 1683b3a8eb9SGleb Smirnoff break; /* found right position */ 1693b3a8eb9SGleb Smirnoff /* son smaller than father, swap and repeat */ 1703b3a8eb9SGleb Smirnoff HEAP_SWAP(h->p[son], h->p[father], tmp); 1713b3a8eb9SGleb Smirnoff SET_OFFSET(h, son); 1723b3a8eb9SGleb Smirnoff son = father; 1733b3a8eb9SGleb Smirnoff } 1743b3a8eb9SGleb Smirnoff SET_OFFSET(h, son); 1753b3a8eb9SGleb Smirnoff return 0; 1763b3a8eb9SGleb Smirnoff } 1773b3a8eb9SGleb Smirnoff 1783b3a8eb9SGleb Smirnoff /* 1793b3a8eb9SGleb Smirnoff * remove top element from heap, or obj if obj != NULL 1803b3a8eb9SGleb Smirnoff */ 1813b3a8eb9SGleb Smirnoff void 1823b3a8eb9SGleb Smirnoff heap_extract(struct dn_heap *h, void *obj) 1833b3a8eb9SGleb Smirnoff { 1843b3a8eb9SGleb Smirnoff int child, father, max = h->elements - 1; 1853b3a8eb9SGleb Smirnoff 1863b3a8eb9SGleb Smirnoff if (max < 0) { 1873b3a8eb9SGleb Smirnoff printf("--- %s: empty heap 0x%p\n", __FUNCTION__, h); 1883b3a8eb9SGleb Smirnoff return; 1893b3a8eb9SGleb Smirnoff } 1903b3a8eb9SGleb Smirnoff if (obj == NULL) 1913b3a8eb9SGleb Smirnoff father = 0; /* default: move up smallest child */ 1923b3a8eb9SGleb Smirnoff else { /* extract specific element, index is at offset */ 1933b3a8eb9SGleb Smirnoff if (h->ofs <= 0) 1943b3a8eb9SGleb Smirnoff panic("%s: extract from middle not set on %p\n", 1953b3a8eb9SGleb Smirnoff __FUNCTION__, h); 1963b3a8eb9SGleb Smirnoff father = *((int *)((char *)obj + h->ofs)); 1973b3a8eb9SGleb Smirnoff if (father < 0 || father >= h->elements) { 1983b3a8eb9SGleb Smirnoff panic("%s: father %d out of bound 0..%d\n", 1993b3a8eb9SGleb Smirnoff __FUNCTION__, father, h->elements); 2003b3a8eb9SGleb Smirnoff } 2013b3a8eb9SGleb Smirnoff } 2023b3a8eb9SGleb Smirnoff /* 2033b3a8eb9SGleb Smirnoff * below, father is the index of the empty element, which 2043b3a8eb9SGleb Smirnoff * we replace at each step with the smallest child until we 2053b3a8eb9SGleb Smirnoff * reach the bottom level. 2063b3a8eb9SGleb Smirnoff */ 2073b3a8eb9SGleb Smirnoff // XXX why removing RESET_OFFSET increases runtime by 10% ? 2083b3a8eb9SGleb Smirnoff RESET_OFFSET(h, father); 2093b3a8eb9SGleb Smirnoff while ( (child = HEAP_LEFT(father)) <= max ) { 2103b3a8eb9SGleb Smirnoff if (child != max && 2113b3a8eb9SGleb Smirnoff DN_KEY_LT(h->p[child+1].key, h->p[child].key) ) 2123b3a8eb9SGleb Smirnoff child++; /* take right child, otherwise left */ 2133b3a8eb9SGleb Smirnoff h->p[father] = h->p[child]; 2143b3a8eb9SGleb Smirnoff SET_OFFSET(h, father); 2153b3a8eb9SGleb Smirnoff father = child; 2163b3a8eb9SGleb Smirnoff } 2173b3a8eb9SGleb Smirnoff h->elements--; 2183b3a8eb9SGleb Smirnoff if (father != max) { 2193b3a8eb9SGleb Smirnoff /* 2203b3a8eb9SGleb Smirnoff * Fill hole with last entry and bubble up, 2213b3a8eb9SGleb Smirnoff * reusing the insert code 2223b3a8eb9SGleb Smirnoff */ 2233b3a8eb9SGleb Smirnoff h->p[father] = h->p[max]; 2243b3a8eb9SGleb Smirnoff heap_insert(h, father, NULL); 2253b3a8eb9SGleb Smirnoff } 2263b3a8eb9SGleb Smirnoff } 2273b3a8eb9SGleb Smirnoff 2283b3a8eb9SGleb Smirnoff #if 0 2293b3a8eb9SGleb Smirnoff /* 2303b3a8eb9SGleb Smirnoff * change object position and update references 2313b3a8eb9SGleb Smirnoff * XXX this one is never used! 2323b3a8eb9SGleb Smirnoff */ 2333b3a8eb9SGleb Smirnoff static void 2343b3a8eb9SGleb Smirnoff heap_move(struct dn_heap *h, uint64_t new_key, void *object) 2353b3a8eb9SGleb Smirnoff { 2363b3a8eb9SGleb Smirnoff int temp, i, max = h->elements-1; 2373b3a8eb9SGleb Smirnoff struct dn_heap_entry *p, buf; 2383b3a8eb9SGleb Smirnoff 2393b3a8eb9SGleb Smirnoff if (h->ofs <= 0) 2403b3a8eb9SGleb Smirnoff panic("cannot move items on this heap"); 2413b3a8eb9SGleb Smirnoff p = h->p; /* shortcut */ 2423b3a8eb9SGleb Smirnoff 2433b3a8eb9SGleb Smirnoff i = *((int *)((char *)object + h->ofs)); 2443b3a8eb9SGleb Smirnoff if (DN_KEY_LT(new_key, p[i].key) ) { /* must move up */ 2453b3a8eb9SGleb Smirnoff p[i].key = new_key; 2463b3a8eb9SGleb Smirnoff for (; i>0 && 2473b3a8eb9SGleb Smirnoff DN_KEY_LT(new_key, p[(temp = HEAP_FATHER(i))].key); 2483b3a8eb9SGleb Smirnoff i = temp ) { /* bubble up */ 2493b3a8eb9SGleb Smirnoff HEAP_SWAP(p[i], p[temp], buf); 2503b3a8eb9SGleb Smirnoff SET_OFFSET(h, i); 2513b3a8eb9SGleb Smirnoff } 2523b3a8eb9SGleb Smirnoff } else { /* must move down */ 2533b3a8eb9SGleb Smirnoff p[i].key = new_key; 2543b3a8eb9SGleb Smirnoff while ( (temp = HEAP_LEFT(i)) <= max ) { 2553b3a8eb9SGleb Smirnoff /* found left child */ 2563b3a8eb9SGleb Smirnoff if (temp != max && 2573b3a8eb9SGleb Smirnoff DN_KEY_LT(p[temp+1].key, p[temp].key)) 2583b3a8eb9SGleb Smirnoff temp++; /* select child with min key */ 2593b3a8eb9SGleb Smirnoff if (DN_KEY_LT(>p[temp].key, new_key)) { 2603b3a8eb9SGleb Smirnoff /* go down */ 2613b3a8eb9SGleb Smirnoff HEAP_SWAP(p[i], p[temp], buf); 2623b3a8eb9SGleb Smirnoff SET_OFFSET(h, i); 2633b3a8eb9SGleb Smirnoff } else 2643b3a8eb9SGleb Smirnoff break; 2653b3a8eb9SGleb Smirnoff i = temp; 2663b3a8eb9SGleb Smirnoff } 2673b3a8eb9SGleb Smirnoff } 2683b3a8eb9SGleb Smirnoff SET_OFFSET(h, i); 2693b3a8eb9SGleb Smirnoff } 2703b3a8eb9SGleb Smirnoff #endif /* heap_move, unused */ 2713b3a8eb9SGleb Smirnoff 2723b3a8eb9SGleb Smirnoff /* 2733b3a8eb9SGleb Smirnoff * heapify() will reorganize data inside an array to maintain the 2743b3a8eb9SGleb Smirnoff * heap property. It is needed when we delete a bunch of entries. 2753b3a8eb9SGleb Smirnoff */ 2763b3a8eb9SGleb Smirnoff static void 2773b3a8eb9SGleb Smirnoff heapify(struct dn_heap *h) 2783b3a8eb9SGleb Smirnoff { 2793b3a8eb9SGleb Smirnoff int i; 2803b3a8eb9SGleb Smirnoff 2813b3a8eb9SGleb Smirnoff for (i = 0; i < h->elements; i++ ) 2823b3a8eb9SGleb Smirnoff heap_insert(h, i , NULL); 2833b3a8eb9SGleb Smirnoff } 2843b3a8eb9SGleb Smirnoff 2853b3a8eb9SGleb Smirnoff int 2863b3a8eb9SGleb Smirnoff heap_scan(struct dn_heap *h, int (*fn)(void *, uintptr_t), 2873b3a8eb9SGleb Smirnoff uintptr_t arg) 2883b3a8eb9SGleb Smirnoff { 2893b3a8eb9SGleb Smirnoff int i, ret, found; 2903b3a8eb9SGleb Smirnoff 2913b3a8eb9SGleb Smirnoff for (i = found = 0 ; i < h->elements ;) { 2923b3a8eb9SGleb Smirnoff ret = fn(h->p[i].object, arg); 2933b3a8eb9SGleb Smirnoff if (ret & HEAP_SCAN_DEL) { 2943b3a8eb9SGleb Smirnoff h->elements-- ; 2953b3a8eb9SGleb Smirnoff h->p[i] = h->p[h->elements] ; 2963b3a8eb9SGleb Smirnoff found++ ; 2973b3a8eb9SGleb Smirnoff } else 2983b3a8eb9SGleb Smirnoff i++ ; 2993b3a8eb9SGleb Smirnoff if (ret & HEAP_SCAN_END) 3003b3a8eb9SGleb Smirnoff break; 3013b3a8eb9SGleb Smirnoff } 3023b3a8eb9SGleb Smirnoff if (found) 3033b3a8eb9SGleb Smirnoff heapify(h); 3043b3a8eb9SGleb Smirnoff return found; 3053b3a8eb9SGleb Smirnoff } 3063b3a8eb9SGleb Smirnoff 3073b3a8eb9SGleb Smirnoff /* 3083b3a8eb9SGleb Smirnoff * cleanup the heap and free data structure 3093b3a8eb9SGleb Smirnoff */ 3103b3a8eb9SGleb Smirnoff void 3113b3a8eb9SGleb Smirnoff heap_free(struct dn_heap *h) 3123b3a8eb9SGleb Smirnoff { 3133b3a8eb9SGleb Smirnoff if (h->size >0 ) 3143b3a8eb9SGleb Smirnoff free(h->p, M_DN_HEAP); 3153b3a8eb9SGleb Smirnoff bzero(h, sizeof(*h) ); 3163b3a8eb9SGleb Smirnoff } 3173b3a8eb9SGleb Smirnoff 3183b3a8eb9SGleb Smirnoff /* 3193b3a8eb9SGleb Smirnoff * hash table support. 3203b3a8eb9SGleb Smirnoff */ 3213b3a8eb9SGleb Smirnoff 3223b3a8eb9SGleb Smirnoff struct dn_ht { 3233b3a8eb9SGleb Smirnoff int buckets; /* how many buckets, really buckets - 1*/ 3243b3a8eb9SGleb Smirnoff int entries; /* how many entries */ 3253b3a8eb9SGleb Smirnoff int ofs; /* offset of link field */ 3263b3a8eb9SGleb Smirnoff uint32_t (*hash)(uintptr_t, int, void *arg); 3273b3a8eb9SGleb Smirnoff int (*match)(void *_el, uintptr_t key, int, void *); 3283b3a8eb9SGleb Smirnoff void *(*newh)(uintptr_t, int, void *); 3293b3a8eb9SGleb Smirnoff void **ht; /* bucket heads */ 3303b3a8eb9SGleb Smirnoff }; 3313b3a8eb9SGleb Smirnoff /* 3323b3a8eb9SGleb Smirnoff * Initialize, allocating bucket pointers inline. 3333b3a8eb9SGleb Smirnoff * Recycle previous record if possible. 3343b3a8eb9SGleb Smirnoff * If the 'newh' function is not supplied, we assume that the 3353b3a8eb9SGleb Smirnoff * key passed to ht_find is the same object to be stored in. 3363b3a8eb9SGleb Smirnoff */ 3373b3a8eb9SGleb Smirnoff struct dn_ht * 3383b3a8eb9SGleb Smirnoff dn_ht_init(struct dn_ht *ht, int buckets, int ofs, 3393b3a8eb9SGleb Smirnoff uint32_t (*h)(uintptr_t, int, void *), 3403b3a8eb9SGleb Smirnoff int (*match)(void *, uintptr_t, int, void *), 3413b3a8eb9SGleb Smirnoff void *(*newh)(uintptr_t, int, void *)) 3423b3a8eb9SGleb Smirnoff { 3433b3a8eb9SGleb Smirnoff int l; 3443b3a8eb9SGleb Smirnoff 3453b3a8eb9SGleb Smirnoff /* 3463b3a8eb9SGleb Smirnoff * Notes about rounding bucket size to a power of two. 3473b3a8eb9SGleb Smirnoff * Given the original bucket size, we compute the nearest lower and 3483b3a8eb9SGleb Smirnoff * higher power of two, minus 1 (respectively b_min and b_max) because 3493b3a8eb9SGleb Smirnoff * this value will be used to do an AND with the index returned 3503b3a8eb9SGleb Smirnoff * by hash function. 3513b3a8eb9SGleb Smirnoff * To choice between these two values, the original bucket size is 3523b3a8eb9SGleb Smirnoff * compared with b_min. If the original size is greater than 4/3 b_min, 3533b3a8eb9SGleb Smirnoff * we round the bucket size to b_max, else to b_min. 3543b3a8eb9SGleb Smirnoff * This ratio try to round to the nearest power of two, advantaging 3553b3a8eb9SGleb Smirnoff * the greater size if the different between two power is relatively 3563b3a8eb9SGleb Smirnoff * big. 3573b3a8eb9SGleb Smirnoff * Rounding the bucket size to a power of two avoid the use of 3583b3a8eb9SGleb Smirnoff * module when calculating the correct bucket. 3593b3a8eb9SGleb Smirnoff * The ht->buckets variable store the bucket size - 1 to simply 3603b3a8eb9SGleb Smirnoff * do an AND between the index returned by hash function and ht->bucket 3613b3a8eb9SGleb Smirnoff * instead of a module. 3623b3a8eb9SGleb Smirnoff */ 3633b3a8eb9SGleb Smirnoff int b_min; /* min buckets */ 3643b3a8eb9SGleb Smirnoff int b_max; /* max buckets */ 3653b3a8eb9SGleb Smirnoff int b_ori; /* original buckets */ 3663b3a8eb9SGleb Smirnoff 3673b3a8eb9SGleb Smirnoff if (h == NULL || match == NULL) { 3683b3a8eb9SGleb Smirnoff printf("--- missing hash or match function"); 3693b3a8eb9SGleb Smirnoff return NULL; 3703b3a8eb9SGleb Smirnoff } 3713b3a8eb9SGleb Smirnoff if (buckets < 1 || buckets > 65536) 3723b3a8eb9SGleb Smirnoff return NULL; 3733b3a8eb9SGleb Smirnoff 3743b3a8eb9SGleb Smirnoff b_ori = buckets; 3753b3a8eb9SGleb Smirnoff /* calculate next power of 2, - 1*/ 3763b3a8eb9SGleb Smirnoff buckets |= buckets >> 1; 3773b3a8eb9SGleb Smirnoff buckets |= buckets >> 2; 3783b3a8eb9SGleb Smirnoff buckets |= buckets >> 4; 3793b3a8eb9SGleb Smirnoff buckets |= buckets >> 8; 3803b3a8eb9SGleb Smirnoff buckets |= buckets >> 16; 3813b3a8eb9SGleb Smirnoff 3823b3a8eb9SGleb Smirnoff b_max = buckets; /* Next power */ 3833b3a8eb9SGleb Smirnoff b_min = buckets >> 1; /* Previous power */ 3843b3a8eb9SGleb Smirnoff 3853b3a8eb9SGleb Smirnoff /* Calculate the 'nearest' bucket size */ 3863b3a8eb9SGleb Smirnoff if (b_min * 4000 / 3000 < b_ori) 3873b3a8eb9SGleb Smirnoff buckets = b_max; 3883b3a8eb9SGleb Smirnoff else 3893b3a8eb9SGleb Smirnoff buckets = b_min; 3903b3a8eb9SGleb Smirnoff 3913b3a8eb9SGleb Smirnoff if (ht) { /* see if we can reuse */ 3923b3a8eb9SGleb Smirnoff if (buckets <= ht->buckets) { 3933b3a8eb9SGleb Smirnoff ht->buckets = buckets; 3943b3a8eb9SGleb Smirnoff } else { 3953b3a8eb9SGleb Smirnoff /* free pointers if not allocated inline */ 3963b3a8eb9SGleb Smirnoff if (ht->ht != (void *)(ht + 1)) 3973b3a8eb9SGleb Smirnoff free(ht->ht, M_DN_HEAP); 3983b3a8eb9SGleb Smirnoff free(ht, M_DN_HEAP); 3993b3a8eb9SGleb Smirnoff ht = NULL; 4003b3a8eb9SGleb Smirnoff } 4013b3a8eb9SGleb Smirnoff } 4023b3a8eb9SGleb Smirnoff if (ht == NULL) { 4033b3a8eb9SGleb Smirnoff /* Allocate buckets + 1 entries because buckets is use to 4043b3a8eb9SGleb Smirnoff * do the AND with the index returned by hash function 4053b3a8eb9SGleb Smirnoff */ 4063b3a8eb9SGleb Smirnoff l = sizeof(*ht) + (buckets + 1) * sizeof(void **); 4073b3a8eb9SGleb Smirnoff ht = malloc(l, M_DN_HEAP, M_NOWAIT | M_ZERO); 4083b3a8eb9SGleb Smirnoff } 4093b3a8eb9SGleb Smirnoff if (ht) { 4103b3a8eb9SGleb Smirnoff ht->ht = (void **)(ht + 1); 4113b3a8eb9SGleb Smirnoff ht->buckets = buckets; 4123b3a8eb9SGleb Smirnoff ht->ofs = ofs; 4133b3a8eb9SGleb Smirnoff ht->hash = h; 4143b3a8eb9SGleb Smirnoff ht->match = match; 4153b3a8eb9SGleb Smirnoff ht->newh = newh; 4163b3a8eb9SGleb Smirnoff } 4173b3a8eb9SGleb Smirnoff return ht; 4183b3a8eb9SGleb Smirnoff } 4193b3a8eb9SGleb Smirnoff 4203b3a8eb9SGleb Smirnoff /* dummy callback for dn_ht_free to unlink all */ 4213b3a8eb9SGleb Smirnoff static int 4223b3a8eb9SGleb Smirnoff do_del(void *obj, void *arg) 4233b3a8eb9SGleb Smirnoff { 4244d85bfebSLuigi Rizzo (void)obj; 4254d85bfebSLuigi Rizzo (void)arg; 4263b3a8eb9SGleb Smirnoff return DNHT_SCAN_DEL; 4273b3a8eb9SGleb Smirnoff } 4283b3a8eb9SGleb Smirnoff 4293b3a8eb9SGleb Smirnoff void 4303b3a8eb9SGleb Smirnoff dn_ht_free(struct dn_ht *ht, int flags) 4313b3a8eb9SGleb Smirnoff { 4323b3a8eb9SGleb Smirnoff if (ht == NULL) 4333b3a8eb9SGleb Smirnoff return; 4343b3a8eb9SGleb Smirnoff if (flags & DNHT_REMOVE) { 4353b3a8eb9SGleb Smirnoff (void)dn_ht_scan(ht, do_del, NULL); 4363b3a8eb9SGleb Smirnoff } else { 4373b3a8eb9SGleb Smirnoff if (ht->ht && ht->ht != (void *)(ht + 1)) 4383b3a8eb9SGleb Smirnoff free(ht->ht, M_DN_HEAP); 4393b3a8eb9SGleb Smirnoff free(ht, M_DN_HEAP); 4403b3a8eb9SGleb Smirnoff } 4413b3a8eb9SGleb Smirnoff } 4423b3a8eb9SGleb Smirnoff 4433b3a8eb9SGleb Smirnoff int 4443b3a8eb9SGleb Smirnoff dn_ht_entries(struct dn_ht *ht) 4453b3a8eb9SGleb Smirnoff { 4463b3a8eb9SGleb Smirnoff return ht ? ht->entries : 0; 4473b3a8eb9SGleb Smirnoff } 4483b3a8eb9SGleb Smirnoff 4493b3a8eb9SGleb Smirnoff /* lookup and optionally create or delete element */ 4503b3a8eb9SGleb Smirnoff void * 4513b3a8eb9SGleb Smirnoff dn_ht_find(struct dn_ht *ht, uintptr_t key, int flags, void *arg) 4523b3a8eb9SGleb Smirnoff { 4533b3a8eb9SGleb Smirnoff int i; 4543b3a8eb9SGleb Smirnoff void **pp, *p; 4553b3a8eb9SGleb Smirnoff 4563b3a8eb9SGleb Smirnoff if (ht == NULL) /* easy on an empty hash */ 4573b3a8eb9SGleb Smirnoff return NULL; 4583b3a8eb9SGleb Smirnoff i = (ht->buckets == 1) ? 0 : 4593b3a8eb9SGleb Smirnoff (ht->hash(key, flags, arg) & ht->buckets); 4603b3a8eb9SGleb Smirnoff 4613b3a8eb9SGleb Smirnoff for (pp = &ht->ht[i]; (p = *pp); pp = (void **)((char *)p + ht->ofs)) { 4623b3a8eb9SGleb Smirnoff if (flags & DNHT_MATCH_PTR) { 4633b3a8eb9SGleb Smirnoff if (key == (uintptr_t)p) 4643b3a8eb9SGleb Smirnoff break; 4653b3a8eb9SGleb Smirnoff } else if (ht->match(p, key, flags, arg)) /* found match */ 4663b3a8eb9SGleb Smirnoff break; 4673b3a8eb9SGleb Smirnoff } 4683b3a8eb9SGleb Smirnoff if (p) { 4693b3a8eb9SGleb Smirnoff if (flags & DNHT_REMOVE) { 4703b3a8eb9SGleb Smirnoff /* link in the next element */ 4713b3a8eb9SGleb Smirnoff *pp = *(void **)((char *)p + ht->ofs); 4723b3a8eb9SGleb Smirnoff *(void **)((char *)p + ht->ofs) = NULL; 4733b3a8eb9SGleb Smirnoff ht->entries--; 4743b3a8eb9SGleb Smirnoff } 4753b3a8eb9SGleb Smirnoff } else if (flags & DNHT_INSERT) { 4763b3a8eb9SGleb Smirnoff // printf("%s before calling new, bucket %d ofs %d\n", 4773b3a8eb9SGleb Smirnoff // __FUNCTION__, i, ht->ofs); 4783b3a8eb9SGleb Smirnoff p = ht->newh ? ht->newh(key, flags, arg) : (void *)key; 4793b3a8eb9SGleb Smirnoff // printf("%s newh returns %p\n", __FUNCTION__, p); 4803b3a8eb9SGleb Smirnoff if (p) { 4813b3a8eb9SGleb Smirnoff ht->entries++; 4823b3a8eb9SGleb Smirnoff *(void **)((char *)p + ht->ofs) = ht->ht[i]; 4833b3a8eb9SGleb Smirnoff ht->ht[i] = p; 4843b3a8eb9SGleb Smirnoff } 4853b3a8eb9SGleb Smirnoff } 4863b3a8eb9SGleb Smirnoff return p; 4873b3a8eb9SGleb Smirnoff } 4883b3a8eb9SGleb Smirnoff 4893b3a8eb9SGleb Smirnoff /* 4903b3a8eb9SGleb Smirnoff * do a scan with the option to delete the object. Extract next before 4913b3a8eb9SGleb Smirnoff * running the callback because the element may be destroyed there. 4923b3a8eb9SGleb Smirnoff */ 4933b3a8eb9SGleb Smirnoff int 4943b3a8eb9SGleb Smirnoff dn_ht_scan(struct dn_ht *ht, int (*fn)(void *, void *), void *arg) 4953b3a8eb9SGleb Smirnoff { 4963b3a8eb9SGleb Smirnoff int i, ret, found = 0; 4973b3a8eb9SGleb Smirnoff void **curp, *cur, *next; 4983b3a8eb9SGleb Smirnoff 4993b3a8eb9SGleb Smirnoff if (ht == NULL || fn == NULL) 5003b3a8eb9SGleb Smirnoff return 0; 5013b3a8eb9SGleb Smirnoff for (i = 0; i <= ht->buckets; i++) { 5023b3a8eb9SGleb Smirnoff curp = &ht->ht[i]; 5033b3a8eb9SGleb Smirnoff while ( (cur = *curp) != NULL) { 5043b3a8eb9SGleb Smirnoff next = *(void **)((char *)cur + ht->ofs); 5053b3a8eb9SGleb Smirnoff ret = fn(cur, arg); 5063b3a8eb9SGleb Smirnoff if (ret & DNHT_SCAN_DEL) { 5073b3a8eb9SGleb Smirnoff found++; 5083b3a8eb9SGleb Smirnoff ht->entries--; 5093b3a8eb9SGleb Smirnoff *curp = next; 5103b3a8eb9SGleb Smirnoff } else { 5113b3a8eb9SGleb Smirnoff curp = (void **)((char *)cur + ht->ofs); 5123b3a8eb9SGleb Smirnoff } 5133b3a8eb9SGleb Smirnoff if (ret & DNHT_SCAN_END) 5143b3a8eb9SGleb Smirnoff return found; 5153b3a8eb9SGleb Smirnoff } 5163b3a8eb9SGleb Smirnoff } 5173b3a8eb9SGleb Smirnoff return found; 5183b3a8eb9SGleb Smirnoff } 5193b3a8eb9SGleb Smirnoff 5203b3a8eb9SGleb Smirnoff /* 5213b3a8eb9SGleb Smirnoff * Similar to dn_ht_scan(), except that the scan is performed only 5223b3a8eb9SGleb Smirnoff * in the bucket 'bucket'. The function returns a correct bucket number if 5233b3a8eb9SGleb Smirnoff * the original is invalid. 5243b3a8eb9SGleb Smirnoff * If the callback returns DNHT_SCAN_END, the function move the ht->ht[i] 5253b3a8eb9SGleb Smirnoff * pointer to the last entry processed. Moreover, the bucket number passed 5263b3a8eb9SGleb Smirnoff * by caller is decremented, because usually the caller increment it. 5273b3a8eb9SGleb Smirnoff */ 5283b3a8eb9SGleb Smirnoff int 5293b3a8eb9SGleb Smirnoff dn_ht_scan_bucket(struct dn_ht *ht, int *bucket, int (*fn)(void *, void *), 5303b3a8eb9SGleb Smirnoff void *arg) 5313b3a8eb9SGleb Smirnoff { 5323b3a8eb9SGleb Smirnoff int i, ret, found = 0; 5333b3a8eb9SGleb Smirnoff void **curp, *cur, *next; 5343b3a8eb9SGleb Smirnoff 5353b3a8eb9SGleb Smirnoff if (ht == NULL || fn == NULL) 5363b3a8eb9SGleb Smirnoff return 0; 5373b3a8eb9SGleb Smirnoff if (*bucket > ht->buckets) 5383b3a8eb9SGleb Smirnoff *bucket = 0; 5393b3a8eb9SGleb Smirnoff i = *bucket; 5403b3a8eb9SGleb Smirnoff 5413b3a8eb9SGleb Smirnoff curp = &ht->ht[i]; 5423b3a8eb9SGleb Smirnoff while ( (cur = *curp) != NULL) { 5433b3a8eb9SGleb Smirnoff next = *(void **)((char *)cur + ht->ofs); 5443b3a8eb9SGleb Smirnoff ret = fn(cur, arg); 5453b3a8eb9SGleb Smirnoff if (ret & DNHT_SCAN_DEL) { 5463b3a8eb9SGleb Smirnoff found++; 5473b3a8eb9SGleb Smirnoff ht->entries--; 5483b3a8eb9SGleb Smirnoff *curp = next; 5493b3a8eb9SGleb Smirnoff } else { 5503b3a8eb9SGleb Smirnoff curp = (void **)((char *)cur + ht->ofs); 5513b3a8eb9SGleb Smirnoff } 5523b3a8eb9SGleb Smirnoff if (ret & DNHT_SCAN_END) 5533b3a8eb9SGleb Smirnoff return found; 5543b3a8eb9SGleb Smirnoff } 5553b3a8eb9SGleb Smirnoff return found; 5563b3a8eb9SGleb Smirnoff } 557