#include <sys/param.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <netpfil/ipfw/dn_heap.h>
#ifndef log
#define log(x, arg...)
#endif
#else
#include <stdio.h>
#include <dn_test.h>
#include <strings.h>
#include <stdlib.h>
#include "dn_heap.h"
#define log(x, arg...) fprintf(stderr, ## arg)
#define panic(x...) fprintf(stderr, ## x), exit(1)
#define MALLOC_DEFINE(a, b, c) volatile int __dummy__ ## a __attribute__((__unused__))
static void *my_malloc(int s) { return malloc(s); }
static void my_free(void *p) { free(p); }
#define malloc(s, t, w) my_malloc(s)
#define free(p, t) my_free(p)
#endif
static MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap");
#define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 )
#define HEAP_LEFT(x) ( (x)+(x) + 1 )
#define HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; }
#define HEAP_INCREMENT 15
static int
heap_resize(struct dn_heap *h, unsigned int new_size)
{
struct dn_heap_entry *p;
if ((unsigned int)h->size >= new_size )
return 0;
#if 1
new_size |= new_size >> 1;
new_size |= new_size >> 2;
new_size |= new_size >> 4;
new_size |= new_size >> 8;
new_size |= new_size >> 16;
#else
new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT;
#endif
p = mallocarray(new_size, sizeof(*p), M_DN_HEAP, M_NOWAIT);
if (p == NULL) {
printf("--- %s, resize %d failed\n", __func__, new_size );
return 1;
}
if (h->size > 0) {
bcopy(h->p, p, h->size * sizeof(*p) );
free(h->p, M_DN_HEAP);
}
h->p = p;
h->size = new_size;
return 0;
}
int
heap_init(struct dn_heap *h, int size, int ofs)
{
if (heap_resize(h, size))
return 1;
h->elements = 0;
h->ofs = ofs;
return 0;
}
#define SET_OFFSET(h, i) do { \
if (h->ofs > 0) \
*((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i; \
} while (0)
#define RESET_OFFSET(h, i) do { \
if (h->ofs > 0) \
*((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16; \
} while (0)
int
heap_insert(struct dn_heap *h, uint64_t key1, void *p)
{
int son = h->elements;
if (p == NULL) {
son = key1;
} else {
son = h->elements;
if (son == h->size)
if (heap_resize(h, h->elements+16) )
return 1;
h->p[son].object = p;
h->p[son].key = key1;
h->elements++;
}
while (son > 0) {
int father = HEAP_FATHER(son);
struct dn_heap_entry tmp;
if (DN_KEY_LT( h->p[father].key, h->p[son].key ) )
break;
HEAP_SWAP(h->p[son], h->p[father], tmp);
SET_OFFSET(h, son);
son = father;
}
SET_OFFSET(h, son);
return 0;
}
bool
heap_extract(struct dn_heap *h, void *obj)
{
int child, father, max = h->elements - 1;
if (max < 0) {
return false;
}
if (obj == NULL)
father = 0;
else {
if (h->ofs <= 0)
panic("%s: extract from middle not set on %p\n",
__FUNCTION__, h);
father = *((int *)((char *)obj + h->ofs));
if (father < 0 || father >= h->elements)
return false;
}
if (obj != NULL && h->p[father].object != obj)
return false;
RESET_OFFSET(h, father);
while ( (child = HEAP_LEFT(father)) <= max ) {
if (child != max &&
DN_KEY_LT(h->p[child+1].key, h->p[child].key) )
child++;
h->p[father] = h->p[child];
SET_OFFSET(h, father);
father = child;
}
h->elements--;
if (father != max) {
h->p[father] = h->p[max];
heap_insert(h, father, NULL);
}
return true;
}
#if 0
static void
heap_move(struct dn_heap *h, uint64_t new_key, void *object)
{
int temp, i, max = h->elements-1;
struct dn_heap_entry *p, buf;
if (h->ofs <= 0)
panic("cannot move items on this heap");
p = h->p;
i = *((int *)((char *)object + h->ofs));
if (DN_KEY_LT(new_key, p[i].key) ) {
p[i].key = new_key;
for (; i>0 &&
DN_KEY_LT(new_key, p[(temp = HEAP_FATHER(i))].key);
i = temp ) {
HEAP_SWAP(p[i], p[temp], buf);
SET_OFFSET(h, i);
}
} else {
p[i].key = new_key;
while ( (temp = HEAP_LEFT(i)) <= max ) {
if (temp != max &&
DN_KEY_LT(p[temp+1].key, p[temp].key))
temp++;
if (DN_KEY_LT(>p[temp].key, new_key)) {
HEAP_SWAP(p[i], p[temp], buf);
SET_OFFSET(h, i);
} else
break;
i = temp;
}
}
SET_OFFSET(h, i);
}
#endif
static void
heapify(struct dn_heap *h)
{
int i;
for (i = 0; i < h->elements; i++ )
heap_insert(h, i , NULL);
}
int
heap_scan(struct dn_heap *h, int (*fn)(void *, uintptr_t),
uintptr_t arg)
{
int i, ret, found;
for (i = found = 0 ; i < h->elements ;) {
ret = fn(h->p[i].object, arg);
if (ret & HEAP_SCAN_DEL) {
h->elements-- ;
h->p[i] = h->p[h->elements] ;
found++ ;
} else
i++ ;
if (ret & HEAP_SCAN_END)
break;
}
if (found)
heapify(h);
return found;
}
void
heap_free(struct dn_heap *h)
{
if (h->size >0 )
free(h->p, M_DN_HEAP);
bzero(h, sizeof(*h) );
}
struct dn_ht {
int buckets;
int entries;
int ofs;
uint32_t (*hash)(uintptr_t, int, void *arg);
int (*match)(void *_el, uintptr_t key, int, void *);
void *(*newh)(uintptr_t, int, void *);
void **ht;
};
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 *))
{
int l;
int b_min;
int b_max;
int b_ori;
if (h == NULL || match == NULL) {
printf("--- missing hash or match function");
return NULL;
}
if (buckets < 1 || buckets > 65536)
return NULL;
b_ori = buckets;
buckets |= buckets >> 1;
buckets |= buckets >> 2;
buckets |= buckets >> 4;
buckets |= buckets >> 8;
buckets |= buckets >> 16;
b_max = buckets;
b_min = buckets >> 1;
if (b_min * 4000 / 3000 < b_ori)
buckets = b_max;
else
buckets = b_min;
if (ht) {
if (buckets <= ht->buckets) {
ht->buckets = buckets;
} else {
if (ht->ht != (void *)(ht + 1))
free(ht->ht, M_DN_HEAP);
free(ht, M_DN_HEAP);
ht = NULL;
}
}
if (ht == NULL) {
l = sizeof(*ht) + (buckets + 1) * sizeof(void **);
ht = malloc(l, M_DN_HEAP, M_NOWAIT | M_ZERO);
}
if (ht) {
ht->ht = (void **)(ht + 1);
ht->buckets = buckets;
ht->ofs = ofs;
ht->hash = h;
ht->match = match;
ht->newh = newh;
}
return ht;
}
static int
do_del(void *obj, void *arg)
{
(void)obj;
(void)arg;
return DNHT_SCAN_DEL;
}
void
dn_ht_free(struct dn_ht *ht, int flags)
{
if (ht == NULL)
return;
if (flags & DNHT_REMOVE) {
(void)dn_ht_scan(ht, do_del, NULL);
} else {
if (ht->ht && ht->ht != (void *)(ht + 1))
free(ht->ht, M_DN_HEAP);
free(ht, M_DN_HEAP);
}
}
int
dn_ht_entries(struct dn_ht *ht)
{
return ht ? ht->entries : 0;
}
void *
dn_ht_find(struct dn_ht *ht, uintptr_t key, int flags, void *arg)
{
int i;
void **pp, *p;
if (ht == NULL)
return NULL;
i = (ht->buckets == 1) ? 0 :
(ht->hash(key, flags, arg) & ht->buckets);
for (pp = &ht->ht[i]; (p = *pp); pp = (void **)((char *)p + ht->ofs)) {
if (flags & DNHT_MATCH_PTR) {
if (key == (uintptr_t)p)
break;
} else if (ht->match(p, key, flags, arg))
break;
}
if (p) {
if (flags & DNHT_REMOVE) {
*pp = *(void **)((char *)p + ht->ofs);
*(void **)((char *)p + ht->ofs) = NULL;
ht->entries--;
}
} else if (flags & DNHT_INSERT) {
p = ht->newh ? ht->newh(key, flags, arg) : (void *)key;
if (p) {
ht->entries++;
*(void **)((char *)p + ht->ofs) = ht->ht[i];
ht->ht[i] = p;
}
}
return p;
}
int
dn_ht_scan(struct dn_ht *ht, int (*fn)(void *, void *), void *arg)
{
int i, ret, found = 0;
void **curp, *cur, *next;
if (ht == NULL || fn == NULL)
return 0;
for (i = 0; i <= ht->buckets; i++) {
curp = &ht->ht[i];
while ( (cur = *curp) != NULL) {
next = *(void **)((char *)cur + ht->ofs);
ret = fn(cur, arg);
if (ret & DNHT_SCAN_DEL) {
found++;
ht->entries--;
*curp = next;
} else {
curp = (void **)((char *)cur + ht->ofs);
}
if (ret & DNHT_SCAN_END)
return found;
}
}
return found;
}
int
dn_ht_scan_bucket(struct dn_ht *ht, int *bucket, int (*fn)(void *, void *),
void *arg)
{
int i, ret, found = 0;
void **curp, *cur, *next;
if (ht == NULL || fn == NULL)
return 0;
if (*bucket > ht->buckets)
*bucket = 0;
i = *bucket;
curp = &ht->ht[i];
while ( (cur = *curp) != NULL) {
next = *(void **)((char *)cur + ht->ofs);
ret = fn(cur, arg);
if (ret & DNHT_SCAN_DEL) {
found++;
ht->entries--;
*curp = next;
} else {
curp = (void **)((char *)cur + ht->ofs);
}
if (ret & DNHT_SCAN_END)
return found;
}
return found;
}