13b3a8eb9SGleb Smirnoff /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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
333b3a8eb9SGleb Smirnoff #include <sys/param.h>
343b3a8eb9SGleb Smirnoff #ifdef _KERNEL
353b3a8eb9SGleb Smirnoff #include <sys/systm.h>
363b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
373b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
383b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/dn_heap.h>
393b3a8eb9SGleb Smirnoff #ifndef log
403b3a8eb9SGleb Smirnoff #define log(x, arg...)
413b3a8eb9SGleb Smirnoff #endif
423b3a8eb9SGleb Smirnoff
433b3a8eb9SGleb Smirnoff #else /* !_KERNEL */
443b3a8eb9SGleb Smirnoff
453b3a8eb9SGleb Smirnoff #include <stdio.h>
463b3a8eb9SGleb Smirnoff #include <dn_test.h>
473b3a8eb9SGleb Smirnoff #include <strings.h>
483b3a8eb9SGleb Smirnoff #include <stdlib.h>
493b3a8eb9SGleb Smirnoff
503b3a8eb9SGleb Smirnoff #include "dn_heap.h"
513b3a8eb9SGleb Smirnoff #define log(x, arg...) fprintf(stderr, ## arg)
523b3a8eb9SGleb Smirnoff #define panic(x...) fprintf(stderr, ## x), exit(1)
53e38e277fSLuigi Rizzo #define MALLOC_DEFINE(a, b, c) volatile int __dummy__ ## a __attribute__((__unused__))
my_malloc(int s)543b3a8eb9SGleb Smirnoff static void *my_malloc(int s) { return malloc(s); }
my_free(void * p)553b3a8eb9SGleb Smirnoff static void my_free(void *p) { free(p); }
563b3a8eb9SGleb Smirnoff #define malloc(s, t, w) my_malloc(s)
573b3a8eb9SGleb Smirnoff #define free(p, t) my_free(p)
583b3a8eb9SGleb Smirnoff #endif /* !_KERNEL */
593b3a8eb9SGleb Smirnoff
603b3a8eb9SGleb Smirnoff static MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap");
613b3a8eb9SGleb Smirnoff
623b3a8eb9SGleb Smirnoff /*
633b3a8eb9SGleb Smirnoff * Heap management functions.
643b3a8eb9SGleb Smirnoff *
653b3a8eb9SGleb Smirnoff * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2.
663b3a8eb9SGleb Smirnoff * Some macros help finding parent/children so we can optimize them.
673b3a8eb9SGleb Smirnoff *
683b3a8eb9SGleb Smirnoff * heap_init() is called to expand the heap when needed.
693b3a8eb9SGleb Smirnoff * Increment size in blocks of 16 entries.
703b3a8eb9SGleb Smirnoff * Returns 1 on error, 0 on success
713b3a8eb9SGleb Smirnoff */
723b3a8eb9SGleb Smirnoff #define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 )
733b3a8eb9SGleb Smirnoff #define HEAP_LEFT(x) ( (x)+(x) + 1 )
743b3a8eb9SGleb Smirnoff #define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; }
753b3a8eb9SGleb Smirnoff #define HEAP_INCREMENT 15
763b3a8eb9SGleb Smirnoff
773b3a8eb9SGleb Smirnoff static int
heap_resize(struct dn_heap * h,unsigned int new_size)783b3a8eb9SGleb Smirnoff heap_resize(struct dn_heap *h, unsigned int new_size)
793b3a8eb9SGleb Smirnoff {
803b3a8eb9SGleb Smirnoff struct dn_heap_entry *p;
813b3a8eb9SGleb Smirnoff
824d85bfebSLuigi Rizzo if ((unsigned int)h->size >= new_size ) /* have enough room */
833b3a8eb9SGleb Smirnoff return 0;
843b3a8eb9SGleb Smirnoff #if 1 /* round to the next power of 2 */
853b3a8eb9SGleb Smirnoff new_size |= new_size >> 1;
863b3a8eb9SGleb Smirnoff new_size |= new_size >> 2;
873b3a8eb9SGleb Smirnoff new_size |= new_size >> 4;
883b3a8eb9SGleb Smirnoff new_size |= new_size >> 8;
893b3a8eb9SGleb Smirnoff new_size |= new_size >> 16;
903b3a8eb9SGleb Smirnoff #else
913b3a8eb9SGleb Smirnoff new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT;
923b3a8eb9SGleb Smirnoff #endif
93454529cdSPedro F. Giffuni p = mallocarray(new_size, sizeof(*p), M_DN_HEAP, M_NOWAIT);
943b3a8eb9SGleb Smirnoff if (p == NULL) {
953b3a8eb9SGleb Smirnoff printf("--- %s, resize %d failed\n", __func__, new_size );
963b3a8eb9SGleb Smirnoff return 1; /* error */
973b3a8eb9SGleb Smirnoff }
983b3a8eb9SGleb Smirnoff if (h->size > 0) {
993b3a8eb9SGleb Smirnoff bcopy(h->p, p, h->size * sizeof(*p) );
1003b3a8eb9SGleb Smirnoff free(h->p, M_DN_HEAP);
1013b3a8eb9SGleb Smirnoff }
1023b3a8eb9SGleb Smirnoff h->p = p;
1033b3a8eb9SGleb Smirnoff h->size = new_size;
1043b3a8eb9SGleb Smirnoff return 0;
1053b3a8eb9SGleb Smirnoff }
1063b3a8eb9SGleb Smirnoff
1073b3a8eb9SGleb Smirnoff int
heap_init(struct dn_heap * h,int size,int ofs)1083b3a8eb9SGleb Smirnoff heap_init(struct dn_heap *h, int size, int ofs)
1093b3a8eb9SGleb Smirnoff {
1103b3a8eb9SGleb Smirnoff if (heap_resize(h, size))
1113b3a8eb9SGleb Smirnoff return 1;
1123b3a8eb9SGleb Smirnoff h->elements = 0;
1133b3a8eb9SGleb Smirnoff h->ofs = ofs;
1143b3a8eb9SGleb Smirnoff return 0;
1153b3a8eb9SGleb Smirnoff }
1163b3a8eb9SGleb Smirnoff
1173b3a8eb9SGleb Smirnoff /*
1183b3a8eb9SGleb Smirnoff * Insert element in heap. Normally, p != NULL, we insert p in
1193b3a8eb9SGleb Smirnoff * a new position and bubble up. If p == NULL, then the element is
1203b3a8eb9SGleb Smirnoff * already in place, and key is the position where to start the
1213b3a8eb9SGleb Smirnoff * bubble-up.
1223b3a8eb9SGleb Smirnoff * Returns 1 on failure (cannot allocate new heap entry)
1233b3a8eb9SGleb Smirnoff *
1243b3a8eb9SGleb Smirnoff * If ofs > 0 the position (index, int) of the element in the heap is
1253b3a8eb9SGleb Smirnoff * also stored in the element itself at the given offset in bytes.
1263b3a8eb9SGleb Smirnoff */
1273b3a8eb9SGleb Smirnoff #define SET_OFFSET(h, i) do { \
1283b3a8eb9SGleb Smirnoff if (h->ofs > 0) \
1293b3a8eb9SGleb Smirnoff *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i; \
1303b3a8eb9SGleb Smirnoff } while (0)
1313b3a8eb9SGleb Smirnoff /*
1323b3a8eb9SGleb Smirnoff * RESET_OFFSET is used for sanity checks. It sets ofs
1333b3a8eb9SGleb Smirnoff * to an invalid value.
1343b3a8eb9SGleb Smirnoff */
1353b3a8eb9SGleb Smirnoff #define RESET_OFFSET(h, i) do { \
1363b3a8eb9SGleb Smirnoff if (h->ofs > 0) \
1373b3a8eb9SGleb Smirnoff *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16; \
1383b3a8eb9SGleb Smirnoff } while (0)
1393b3a8eb9SGleb Smirnoff
1403b3a8eb9SGleb Smirnoff int
heap_insert(struct dn_heap * h,uint64_t key1,void * p)1413b3a8eb9SGleb Smirnoff heap_insert(struct dn_heap *h, uint64_t key1, void *p)
1423b3a8eb9SGleb Smirnoff {
1433b3a8eb9SGleb Smirnoff int son = h->elements;
1443b3a8eb9SGleb Smirnoff
1453b3a8eb9SGleb Smirnoff //log("%s key %llu p %p\n", __FUNCTION__, key1, p);
1463b3a8eb9SGleb Smirnoff if (p == NULL) { /* data already there, set starting point */
1473b3a8eb9SGleb Smirnoff son = key1;
1483b3a8eb9SGleb Smirnoff } else { /* insert new element at the end, possibly resize */
1493b3a8eb9SGleb Smirnoff son = h->elements;
1503b3a8eb9SGleb Smirnoff if (son == h->size) /* need resize... */
1513b3a8eb9SGleb Smirnoff // XXX expand by 16 or so
1523b3a8eb9SGleb Smirnoff if (heap_resize(h, h->elements+16) )
1533b3a8eb9SGleb Smirnoff return 1; /* failure... */
1543b3a8eb9SGleb Smirnoff h->p[son].object = p;
1553b3a8eb9SGleb Smirnoff h->p[son].key = key1;
1563b3a8eb9SGleb Smirnoff h->elements++;
1573b3a8eb9SGleb Smirnoff }
1583b3a8eb9SGleb Smirnoff /* make sure that son >= father along the path */
1593b3a8eb9SGleb Smirnoff while (son > 0) {
1603b3a8eb9SGleb Smirnoff int father = HEAP_FATHER(son);
1613b3a8eb9SGleb Smirnoff struct dn_heap_entry tmp;
1623b3a8eb9SGleb Smirnoff
1633b3a8eb9SGleb Smirnoff if (DN_KEY_LT( h->p[father].key, h->p[son].key ) )
1643b3a8eb9SGleb Smirnoff break; /* found right position */
1653b3a8eb9SGleb Smirnoff /* son smaller than father, swap and repeat */
1663b3a8eb9SGleb Smirnoff HEAP_SWAP(h->p[son], h->p[father], tmp);
1673b3a8eb9SGleb Smirnoff SET_OFFSET(h, son);
1683b3a8eb9SGleb Smirnoff son = father;
1693b3a8eb9SGleb Smirnoff }
1703b3a8eb9SGleb Smirnoff SET_OFFSET(h, son);
1713b3a8eb9SGleb Smirnoff return 0;
1723b3a8eb9SGleb Smirnoff }
1733b3a8eb9SGleb Smirnoff
1743b3a8eb9SGleb Smirnoff /*
1753b3a8eb9SGleb Smirnoff * remove top element from heap, or obj if obj != NULL
1763b3a8eb9SGleb Smirnoff */
177*0ba9cb5eSKristof Provost bool
heap_extract(struct dn_heap * h,void * obj)1783b3a8eb9SGleb Smirnoff heap_extract(struct dn_heap *h, void *obj)
1793b3a8eb9SGleb Smirnoff {
1803b3a8eb9SGleb Smirnoff int child, father, max = h->elements - 1;
1813b3a8eb9SGleb Smirnoff
1823b3a8eb9SGleb Smirnoff if (max < 0) {
183*0ba9cb5eSKristof Provost return false;
1843b3a8eb9SGleb Smirnoff }
1853b3a8eb9SGleb Smirnoff if (obj == NULL)
1863b3a8eb9SGleb Smirnoff father = 0; /* default: move up smallest child */
1873b3a8eb9SGleb Smirnoff else { /* extract specific element, index is at offset */
1883b3a8eb9SGleb Smirnoff if (h->ofs <= 0)
1893b3a8eb9SGleb Smirnoff panic("%s: extract from middle not set on %p\n",
1903b3a8eb9SGleb Smirnoff __FUNCTION__, h);
1913b3a8eb9SGleb Smirnoff father = *((int *)((char *)obj + h->ofs));
192*0ba9cb5eSKristof Provost if (father < 0 || father >= h->elements)
193*0ba9cb5eSKristof Provost return false;
1943b3a8eb9SGleb Smirnoff }
195*0ba9cb5eSKristof Provost /* We should make sure that the object we're trying to remove is
196*0ba9cb5eSKristof Provost * actually in this heap. */
197*0ba9cb5eSKristof Provost if (obj != NULL && h->p[father].object != obj)
198*0ba9cb5eSKristof Provost return false;
199*0ba9cb5eSKristof Provost
2003b3a8eb9SGleb Smirnoff /*
2013b3a8eb9SGleb Smirnoff * below, father is the index of the empty element, which
2023b3a8eb9SGleb Smirnoff * we replace at each step with the smallest child until we
2033b3a8eb9SGleb Smirnoff * reach the bottom level.
2043b3a8eb9SGleb Smirnoff */
2053b3a8eb9SGleb Smirnoff // XXX why removing RESET_OFFSET increases runtime by 10% ?
2063b3a8eb9SGleb Smirnoff RESET_OFFSET(h, father);
2073b3a8eb9SGleb Smirnoff while ( (child = HEAP_LEFT(father)) <= max ) {
2083b3a8eb9SGleb Smirnoff if (child != max &&
2093b3a8eb9SGleb Smirnoff DN_KEY_LT(h->p[child+1].key, h->p[child].key) )
2103b3a8eb9SGleb Smirnoff child++; /* take right child, otherwise left */
2113b3a8eb9SGleb Smirnoff h->p[father] = h->p[child];
2123b3a8eb9SGleb Smirnoff SET_OFFSET(h, father);
2133b3a8eb9SGleb Smirnoff father = child;
2143b3a8eb9SGleb Smirnoff }
2153b3a8eb9SGleb Smirnoff h->elements--;
2163b3a8eb9SGleb Smirnoff if (father != max) {
2173b3a8eb9SGleb Smirnoff /*
2183b3a8eb9SGleb Smirnoff * Fill hole with last entry and bubble up,
2193b3a8eb9SGleb Smirnoff * reusing the insert code
2203b3a8eb9SGleb Smirnoff */
2213b3a8eb9SGleb Smirnoff h->p[father] = h->p[max];
2223b3a8eb9SGleb Smirnoff heap_insert(h, father, NULL);
2233b3a8eb9SGleb Smirnoff }
224*0ba9cb5eSKristof Provost
225*0ba9cb5eSKristof Provost return true;
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
heapify(struct dn_heap * h)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
heap_scan(struct dn_heap * h,int (* fn)(void *,uintptr_t),uintptr_t arg)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
heap_free(struct dn_heap * h)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 *
dn_ht_init(struct dn_ht * ht,int buckets,int ofs,uint32_t (* h)(uintptr_t,int,void *),int (* match)(void *,uintptr_t,int,void *),void * (* newh)(uintptr_t,int,void *))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
do_del(void * obj,void * arg)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
dn_ht_free(struct dn_ht * ht,int flags)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
dn_ht_entries(struct dn_ht * ht)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 *
dn_ht_find(struct dn_ht * ht,uintptr_t key,int flags,void * arg)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
dn_ht_scan(struct dn_ht * ht,int (* fn)(void *,void *),void * arg)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
dn_ht_scan_bucket(struct dn_ht * ht,int * bucket,int (* fn)(void *,void *),void * arg)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