mirror of
https://github.com/skywind3000/z.lua
synced 2026-03-15 04:09:49 +00:00
2326 lines
56 KiB
C
2326 lines
56 KiB
C
/**********************************************************************
|
|
*
|
|
* imembase.c - basic interface of memory operation
|
|
* skywind3000 (at) gmail.com, 2006-2016
|
|
*
|
|
**********************************************************************/
|
|
|
|
#include "imembase.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
|
|
#if (defined(__BORLANDC__) || defined(__WATCOMC__))
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
#pragma warn -8002
|
|
#pragma warn -8004
|
|
#pragma warn -8008
|
|
#pragma warn -8012
|
|
#pragma warn -8027
|
|
#pragma warn -8057
|
|
#pragma warn -8066
|
|
#endif
|
|
#endif
|
|
|
|
|
|
/*====================================================================*/
|
|
/* IALLOCATOR */
|
|
/*====================================================================*/
|
|
void *(*__ihook_malloc)(size_t size) = NULL;
|
|
void (*__ihook_free)(void *) = NULL;
|
|
void *(*__ihook_realloc)(void *, size_t size) = NULL;
|
|
|
|
|
|
void* internal_malloc(struct IALLOCATOR *allocator, size_t size)
|
|
{
|
|
if (allocator != NULL) {
|
|
return allocator->alloc(allocator, size);
|
|
}
|
|
if (__ihook_malloc != NULL) {
|
|
return __ihook_malloc(size);
|
|
}
|
|
return malloc(size);
|
|
}
|
|
|
|
void internal_free(struct IALLOCATOR *allocator, void *ptr)
|
|
{
|
|
if (allocator != NULL) {
|
|
allocator->free(allocator, ptr);
|
|
return;
|
|
}
|
|
if (__ihook_free != NULL) {
|
|
__ihook_free(ptr);
|
|
return;
|
|
}
|
|
free(ptr);
|
|
}
|
|
|
|
void* internal_realloc(struct IALLOCATOR *allocator, void *ptr, size_t size)
|
|
{
|
|
if (allocator != NULL) {
|
|
return allocator->realloc(allocator, ptr, size);
|
|
}
|
|
if (__ihook_realloc != NULL) {
|
|
return __ihook_realloc(ptr, size);
|
|
}
|
|
return realloc(ptr, size);
|
|
}
|
|
|
|
|
|
/*====================================================================*/
|
|
/* IKMEM INTERFACE */
|
|
/*====================================================================*/
|
|
#ifndef IKMEM_ALLOCATOR
|
|
#define IKMEM_ALLOCATOR NULL
|
|
#endif
|
|
|
|
struct IALLOCATOR *ikmem_allocator = IKMEM_ALLOCATOR;
|
|
|
|
|
|
void* ikmem_malloc(size_t size)
|
|
{
|
|
return internal_malloc(ikmem_allocator, size);
|
|
}
|
|
|
|
void* ikmem_realloc(void *ptr, size_t size)
|
|
{
|
|
return internal_realloc(ikmem_allocator, ptr, size);
|
|
}
|
|
|
|
void ikmem_free(void *ptr)
|
|
{
|
|
internal_free(ikmem_allocator, ptr);
|
|
}
|
|
|
|
|
|
/*====================================================================*/
|
|
/* IVECTOR */
|
|
/*====================================================================*/
|
|
void iv_init(struct IVECTOR *v, struct IALLOCATOR *allocator)
|
|
{
|
|
if (v == 0) return;
|
|
v->data = 0;
|
|
v->size = 0;
|
|
v->capacity = 0;
|
|
v->allocator = allocator;
|
|
}
|
|
|
|
void iv_destroy(struct IVECTOR *v)
|
|
{
|
|
if (v == NULL) return;
|
|
if (v->data) {
|
|
internal_free(v->allocator, v->data);
|
|
}
|
|
v->data = NULL;
|
|
v->size = 0;
|
|
v->capacity = 0;
|
|
}
|
|
|
|
int iv_capacity(struct IVECTOR *v, size_t newcap)
|
|
{
|
|
if (newcap == v->capacity)
|
|
return 0;
|
|
if (newcap == 0) {
|
|
if (v->capacity > 0) {
|
|
internal_free(v->allocator, v->data);
|
|
}
|
|
v->data = NULL;
|
|
v->capacity = 0;
|
|
v->size = 0;
|
|
}
|
|
else {
|
|
unsigned char *ptr = (unsigned char*)
|
|
internal_malloc(v->allocator, newcap);
|
|
if (ptr == NULL) {
|
|
return -1;
|
|
}
|
|
if (v->data) {
|
|
size_t minsize = (v->size <= newcap)? v->size : newcap;
|
|
if (minsize > 0 && v->data) {
|
|
memcpy(ptr, v->data, minsize);
|
|
}
|
|
internal_free(v->allocator, v->data);
|
|
}
|
|
v->data = ptr;
|
|
v->capacity = newcap;
|
|
if (v->size > v->capacity) {
|
|
v->size = v->capacity;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int iv_resize(struct IVECTOR *v, size_t newsize)
|
|
{
|
|
if (newsize > v->capacity) {
|
|
size_t capacity = v->capacity * 2;
|
|
if (capacity < newsize) {
|
|
capacity = sizeof(char*);
|
|
while (capacity < newsize) {
|
|
capacity = capacity * 2;
|
|
}
|
|
}
|
|
if (iv_capacity(v, capacity) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
v->size = newsize;
|
|
return 0;
|
|
}
|
|
|
|
int iv_reserve(struct IVECTOR *v, size_t size)
|
|
{
|
|
return iv_capacity(v, (size >= v->size)? size : v->size);
|
|
}
|
|
|
|
int iv_push(struct IVECTOR *v, const void *data, size_t size)
|
|
{
|
|
size_t current = v->size;
|
|
if (iv_resize(v, current + size) != 0)
|
|
return -1;
|
|
if (data != NULL)
|
|
memcpy(v->data + current, data, size);
|
|
return 0;
|
|
}
|
|
|
|
size_t iv_pop(struct IVECTOR *v, void *data, size_t size)
|
|
{
|
|
size_t current = v->size;
|
|
if (size >= current) size = current;
|
|
if (data != NULL)
|
|
memcpy(data, v->data + current - size, size);
|
|
iv_resize(v, current - size);
|
|
return size;
|
|
}
|
|
|
|
int iv_insert(struct IVECTOR *v, size_t pos, const void *data, size_t size)
|
|
{
|
|
size_t current = v->size;
|
|
if (pos > current)
|
|
return -1;
|
|
if (iv_resize(v, current + size) != 0)
|
|
return -1;
|
|
if (pos < current) {
|
|
memmove(v->data + pos + size, v->data + pos, current - pos);
|
|
}
|
|
if (data != NULL)
|
|
memcpy(v->data + pos, data, size);
|
|
return 0;
|
|
}
|
|
|
|
int iv_erase(struct IVECTOR *v, size_t pos, size_t size)
|
|
{
|
|
size_t current = v->size;
|
|
if (pos >= current) return 0;
|
|
if (pos + size >= current) size = current - pos;
|
|
if (size == 0) return 0;
|
|
memmove(v->data + pos, v->data + pos + size, current - pos - size);
|
|
if (iv_resize(v, current - size) != 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*====================================================================*/
|
|
/* IMEMNODE */
|
|
/*====================================================================*/
|
|
void imnode_init(struct IMEMNODE *mn, ilong nodesize, struct IALLOCATOR *ac)
|
|
{
|
|
struct IMEMNODE *mnode = mn;
|
|
|
|
assert(mnode != NULL);
|
|
mnode->allocator = ac;
|
|
|
|
iv_init(&mnode->vprev, ac);
|
|
iv_init(&mnode->vnext, ac);
|
|
iv_init(&mnode->vnode, ac);
|
|
iv_init(&mnode->vdata, ac);
|
|
iv_init(&mnode->vmem, ac);
|
|
iv_init(&mnode->vmode, ac);
|
|
|
|
nodesize = IROUND_UP(nodesize, 8);
|
|
|
|
mnode->node_size = nodesize;
|
|
mnode->node_free = 0;
|
|
mnode->node_used = 0;
|
|
mnode->node_max = 0;
|
|
mnode->mem_max = 0;
|
|
mnode->mem_count = 0;
|
|
mnode->list_open = -1;
|
|
mnode->list_close = -1;
|
|
mnode->total_mem = 0;
|
|
mnode->grow_limit = 0;
|
|
mnode->extra = NULL;
|
|
}
|
|
|
|
void imnode_destroy(struct IMEMNODE *mnode)
|
|
{
|
|
ilong i;
|
|
|
|
assert(mnode != NULL);
|
|
if (mnode->mem_count > 0) {
|
|
for (i = 0; i < mnode->mem_count && mnode->mmem; i++) {
|
|
if (mnode->mmem[i]) {
|
|
internal_free(mnode->allocator, mnode->mmem[i]);
|
|
}
|
|
mnode->mmem[i] = NULL;
|
|
}
|
|
mnode->mem_count = 0;
|
|
mnode->mem_max = 0;
|
|
iv_destroy(&mnode->vmem);
|
|
mnode->mmem = NULL;
|
|
}
|
|
|
|
iv_destroy(&mnode->vprev);
|
|
iv_destroy(&mnode->vnext);
|
|
iv_destroy(&mnode->vnode);
|
|
iv_destroy(&mnode->vdata);
|
|
iv_destroy(&mnode->vmode);
|
|
|
|
mnode->mprev = NULL;
|
|
mnode->mnext = NULL;
|
|
mnode->mnode = NULL;
|
|
mnode->mdata = NULL;
|
|
mnode->mmode = NULL;
|
|
|
|
mnode->node_free = 0;
|
|
mnode->node_used = 0;
|
|
mnode->node_max = 0;
|
|
mnode->list_open = -1;
|
|
mnode->list_close= -1;
|
|
mnode->total_mem = 0;
|
|
}
|
|
|
|
static int imnode_node_resize(struct IMEMNODE *mnode, ilong size)
|
|
{
|
|
size_t size1, size2;
|
|
|
|
size1 = (size_t)(size * (ilong)sizeof(ilong));
|
|
size2 = (size_t)(size * (ilong)sizeof(void*));
|
|
|
|
if (iv_resize(&mnode->vprev, size1)) return -1;
|
|
if (iv_resize(&mnode->vnext, size1)) return -2;
|
|
if (iv_resize(&mnode->vnode, size1)) return -3;
|
|
if (iv_resize(&mnode->vdata, size2)) return -5;
|
|
if (iv_resize(&mnode->vmode, size1)) return -6;
|
|
|
|
mnode->mprev = (ilong*)((void*)mnode->vprev.data);
|
|
mnode->mnext = (ilong*)((void*)mnode->vnext.data);
|
|
mnode->mnode = (ilong*)((void*)mnode->vnode.data);
|
|
mnode->mdata =(void**)((void*)mnode->vdata.data);
|
|
mnode->mmode = (ilong*)((void*)mnode->vmode.data);
|
|
mnode->node_max = size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int imnode_mem_add(struct IMEMNODE*mnode, ilong node_count, void**mem)
|
|
{
|
|
size_t newsize;
|
|
char *mptr;
|
|
|
|
if (mnode->mem_count >= mnode->mem_max) {
|
|
newsize = (mnode->mem_max <= 0)? 16 : mnode->mem_max * 2;
|
|
if (iv_resize(&mnode->vmem, newsize * sizeof(void*)))
|
|
return -1;
|
|
mnode->mem_max = newsize;
|
|
mnode->mmem = (char**)((void*)mnode->vmem.data);
|
|
}
|
|
newsize = node_count * mnode->node_size + 16;
|
|
mptr = (char*)internal_malloc(mnode->allocator, newsize);
|
|
if (mptr == NULL) return -2;
|
|
|
|
mnode->mmem[mnode->mem_count++] = mptr;
|
|
mnode->total_mem += newsize;
|
|
mptr = (char*)IROUND_UP(((size_t)mptr), 16);
|
|
|
|
if (mem) *mem = mptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static long imnode_grow(struct IMEMNODE *mnode)
|
|
{
|
|
ilong size_start = mnode->node_max;
|
|
ilong size_endup;
|
|
ilong retval, count, i, j;
|
|
void *mptr;
|
|
char *p;
|
|
|
|
count = (mnode->node_max <= 0)? 8 : mnode->node_max;
|
|
if (mnode->grow_limit > 0) {
|
|
if (count > mnode->grow_limit) count = mnode->grow_limit;
|
|
}
|
|
if (count > 4096) count = 4096;
|
|
size_endup = size_start + count;
|
|
|
|
retval = imnode_node_resize(mnode, size_endup);
|
|
if (retval) return -10 + (long)retval;
|
|
|
|
retval = imnode_mem_add(mnode, count, &mptr);
|
|
|
|
if (retval) {
|
|
imnode_node_resize(mnode, size_start);
|
|
mnode->node_max = size_start;
|
|
return -20 + (long)retval;
|
|
}
|
|
|
|
p = (char*)mptr;
|
|
for (i = mnode->node_max - 1, j = 0; j < count; i--, j++) {
|
|
IMNODE_NODE(mnode, i) = 0;
|
|
IMNODE_MODE(mnode, i) = 0;
|
|
IMNODE_DATA(mnode, i) = p;
|
|
IMNODE_PREV(mnode, i) = -1;
|
|
IMNODE_NEXT(mnode, i) = mnode->list_open;
|
|
if (mnode->list_open >= 0) IMNODE_PREV(mnode, mnode->list_open) = i;
|
|
mnode->list_open = i;
|
|
mnode->node_free++;
|
|
p += mnode->node_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
ilong imnode_new(struct IMEMNODE *mnode)
|
|
{
|
|
ilong node, next;
|
|
|
|
assert(mnode);
|
|
if (mnode->list_open < 0) {
|
|
if (imnode_grow(mnode)) return -2;
|
|
}
|
|
|
|
if (mnode->list_open < 0 || mnode->node_free <= 0) return -3;
|
|
|
|
node = mnode->list_open;
|
|
next = IMNODE_NEXT(mnode, node);
|
|
if (next >= 0) IMNODE_PREV(mnode, next) = -1;
|
|
mnode->list_open = next;
|
|
|
|
IMNODE_PREV(mnode, node) = -1;
|
|
IMNODE_NEXT(mnode, node) = mnode->list_close;
|
|
|
|
if (mnode->list_close >= 0) IMNODE_PREV(mnode, mnode->list_close) = node;
|
|
mnode->list_close = node;
|
|
IMNODE_MODE(mnode, node) = 1;
|
|
|
|
mnode->node_free--;
|
|
mnode->node_used++;
|
|
|
|
return node;
|
|
}
|
|
|
|
void imnode_del(struct IMEMNODE *mnode, ilong index)
|
|
{
|
|
ilong prev, next;
|
|
|
|
assert(mnode);
|
|
assert((index >= 0) && (index < mnode->node_max));
|
|
assert(IMNODE_MODE(mnode, index) != 0);
|
|
|
|
next = IMNODE_NEXT(mnode, index);
|
|
prev = IMNODE_PREV(mnode, index);
|
|
|
|
if (next >= 0) IMNODE_PREV(mnode, next) = prev;
|
|
if (prev >= 0) IMNODE_NEXT(mnode, prev) = next;
|
|
else mnode->list_close = next;
|
|
|
|
IMNODE_PREV(mnode, index) = -1;
|
|
IMNODE_NEXT(mnode, index) = mnode->list_open;
|
|
|
|
if (mnode->list_open >= 0) IMNODE_PREV(mnode, mnode->list_open) = index;
|
|
mnode->list_open = index;
|
|
|
|
IMNODE_MODE(mnode, index) = 0;
|
|
mnode->node_free++;
|
|
mnode->node_used--;
|
|
}
|
|
|
|
ilong imnode_head(const struct IMEMNODE *mnode)
|
|
{
|
|
return (mnode)? mnode->list_close : -1;
|
|
}
|
|
|
|
ilong imnode_next(const struct IMEMNODE *mnode, ilong index)
|
|
{
|
|
return (mnode)? IMNODE_NEXT(mnode, index) : -1;
|
|
}
|
|
|
|
ilong imnode_prev(const struct IMEMNODE *mnode, ilong index)
|
|
{
|
|
return (mnode)? IMNODE_PREV(mnode, index) : -1;
|
|
}
|
|
|
|
void *imnode_data(struct IMEMNODE *mnode, ilong index)
|
|
{
|
|
return (char*)IMNODE_DATA(mnode, index);
|
|
}
|
|
|
|
const void* imnode_data_const(const struct IMEMNODE *mnode, ilong index)
|
|
{
|
|
return (const char*)IMNODE_DATA(mnode, index);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*====================================================================*/
|
|
/* IVECTOR / IMEMNODE MANAGEMENT */
|
|
/*====================================================================*/
|
|
|
|
ib_vector *iv_create(void)
|
|
{
|
|
ib_vector *vec;
|
|
vec = (ib_vector*)ikmem_malloc(sizeof(ib_vector));
|
|
if (vec == NULL) return NULL;
|
|
iv_init(vec, ikmem_allocator);
|
|
return vec;
|
|
}
|
|
|
|
void iv_delete(ib_vector *vec)
|
|
{
|
|
assert(vec);
|
|
iv_destroy(vec);
|
|
ikmem_free(vec);
|
|
}
|
|
|
|
ib_memnode *imnode_create(ilong nodesize, int grow_limit)
|
|
{
|
|
ib_memnode *mnode;
|
|
mnode = (ib_memnode*)ikmem_malloc(sizeof(ib_memnode));
|
|
if (mnode == NULL) return NULL;
|
|
imnode_init(mnode, nodesize, ikmem_allocator);
|
|
mnode->grow_limit = grow_limit;
|
|
return mnode;
|
|
}
|
|
|
|
void imnode_delete(ib_memnode *mnode)
|
|
{
|
|
assert(mnode);
|
|
imnode_destroy(mnode);
|
|
ikmem_free(mnode);
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* Collection - Array */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
struct ib_array
|
|
{
|
|
struct IVECTOR vec;
|
|
void (*fn_destroy)(void*);
|
|
size_t size;
|
|
void **items;
|
|
};
|
|
|
|
ib_array *ib_array_new(void (*destroy_func)(void*))
|
|
{
|
|
ib_array *array = (ib_array*)ikmem_malloc(sizeof(ib_array));
|
|
if (array == NULL) return NULL;
|
|
iv_init(&array->vec, ikmem_allocator);
|
|
array->fn_destroy = destroy_func;
|
|
array->size = 0;
|
|
return array;
|
|
};
|
|
|
|
|
|
void ib_array_delete(ib_array *array)
|
|
{
|
|
array->items = (void**)array->vec.data;
|
|
}
|
|
|
|
void ib_array_release(ib_array *array)
|
|
{
|
|
if (array->fn_destroy) {
|
|
size_t n = array->size;
|
|
size_t i;
|
|
for (i = 0; i < n; i++) {
|
|
array->fn_destroy(array->items[i]);
|
|
array->items[i] = NULL;
|
|
}
|
|
}
|
|
iv_destroy(&array->vec);
|
|
array->size = 0;
|
|
array->items = NULL;
|
|
ikmem_free(array);
|
|
}
|
|
|
|
static void ib_array_update(ib_array *array)
|
|
{
|
|
array->items = (void**)array->vec.data;
|
|
}
|
|
|
|
void ib_array_reserve(ib_array *array, size_t new_size)
|
|
{
|
|
int hr = iv_obj_reserve(&array->vec, char*, new_size);
|
|
if (hr != 0) {
|
|
assert(hr == 0);
|
|
}
|
|
ib_array_update(array);
|
|
}
|
|
|
|
size_t ib_array_size(const ib_array *array)
|
|
{
|
|
return array->size;
|
|
}
|
|
|
|
void** ib_array_ptr(ib_array *array)
|
|
{
|
|
return array->items;
|
|
}
|
|
|
|
void* ib_array_index(ib_array *array, size_t index)
|
|
{
|
|
assert(index < array->size);
|
|
return array->items[index];
|
|
}
|
|
|
|
const void* ib_array_const_index(const ib_array *array, size_t index)
|
|
{
|
|
assert(index < array->size);
|
|
return array->items[index];
|
|
}
|
|
|
|
void ib_array_push(ib_array *array, void *item)
|
|
{
|
|
int hr = iv_obj_push(&array->vec, void*, &item);
|
|
if (hr) {
|
|
assert(hr == 0);
|
|
}
|
|
ib_array_update(array);
|
|
array->size++;
|
|
}
|
|
|
|
void ib_array_push_left(ib_array *array, void *item)
|
|
{
|
|
int hr = iv_obj_insert(&array->vec, void*, 0, &item);
|
|
if (hr) {
|
|
assert(hr == 0);
|
|
}
|
|
ib_array_update(array);
|
|
array->size++;
|
|
}
|
|
|
|
void ib_array_replace(ib_array *array, size_t index, void *item)
|
|
{
|
|
assert(index < array->size);
|
|
if (array->fn_destroy) {
|
|
array->fn_destroy(array->items[index]);
|
|
}
|
|
array->items[index] = item;
|
|
}
|
|
|
|
void* ib_array_pop(ib_array *array)
|
|
{
|
|
void *item;
|
|
int hr;
|
|
assert(array->size > 0);
|
|
array->size--;
|
|
item = array->items[array->size];
|
|
hr = iv_obj_resize(&array->vec, void*, array->size);
|
|
ib_array_update(array);
|
|
if (hr) {
|
|
assert(hr == 0);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
void* ib_array_pop_left(ib_array *array)
|
|
{
|
|
void *item;
|
|
int hr;
|
|
assert(array->size > 0);
|
|
array->size--;
|
|
item = array->items[0];
|
|
hr = iv_obj_erase(&array->vec, void*, 0, 1);
|
|
ib_array_update(array);
|
|
if (hr) {
|
|
assert(hr == 0);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
void ib_array_remove(ib_array *array, size_t index)
|
|
{
|
|
assert(index < array->size);
|
|
if (array->fn_destroy) {
|
|
array->fn_destroy(array->items[index]);
|
|
}
|
|
iv_obj_erase(&array->vec, void*, index, 1);
|
|
ib_array_update(array);
|
|
array->size--;
|
|
}
|
|
|
|
void ib_array_insert_before(ib_array *array, size_t index, void *item)
|
|
{
|
|
int hr = iv_obj_insert(&array->vec, void*, index, &item);
|
|
if (hr) {
|
|
assert(hr == 0);
|
|
}
|
|
ib_array_update(array);
|
|
array->size++;
|
|
}
|
|
|
|
void* ib_array_pop_at(ib_array *array, size_t index)
|
|
{
|
|
void *item;
|
|
int hr;
|
|
assert(array->size > 0);
|
|
item = array->items[index];
|
|
hr = iv_obj_erase(&array->vec, void*, index, 1);
|
|
if (hr) {
|
|
assert(hr == 0);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
void ib_array_sort(ib_array *array,
|
|
int (*compare)(const void*, const void*))
|
|
{
|
|
if (array->size) {
|
|
void **items = array->items;
|
|
size_t size = array->size;
|
|
size_t i, j;
|
|
for (i = 0; i < size - 1; i++) {
|
|
for (j = i + 1; j < size; j++) {
|
|
if (compare(items[i], items[j]) > 0) {
|
|
void *tmp = items[i];
|
|
items[i] = items[j];
|
|
items[j] = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ib_array_for_each(ib_array *array, void (*iterator)(void *item))
|
|
{
|
|
if (iterator) {
|
|
void **items = array->items;
|
|
size_t count = array->size;
|
|
for (; count > 0; count--, items++) {
|
|
iterator(items[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
ilong ib_array_search(const ib_array *array,
|
|
int (*compare)(const void*, const void*),
|
|
const void *item,
|
|
ilong start_pos)
|
|
{
|
|
ilong size = (ilong)array->size;
|
|
void **items = array->items;
|
|
if (start_pos < 0) {
|
|
start_pos = 0;
|
|
}
|
|
for (items = items + start_pos; start_pos < size; start_pos++) {
|
|
if (compare(items[0], item) == 0) {
|
|
return start_pos;
|
|
}
|
|
items++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
ilong ib_array_bsearch(const ib_array *array,
|
|
int (*compare)(const void*, const void*),
|
|
const void *item)
|
|
{
|
|
ilong top, bottom, mid;
|
|
void **items = array->items;
|
|
if (array->size == 0) return -1;
|
|
top = 0;
|
|
bottom = (ilong)array->size - 1;
|
|
while (1) {
|
|
int hr;
|
|
mid = (top + bottom) >> 1;
|
|
hr = compare(item, items[mid]);
|
|
if (hr < 0) bottom = mid;
|
|
else if (hr > 0) top = mid;
|
|
else return mid;
|
|
if (top == bottom) break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
/*====================================================================*/
|
|
/* Binary Search Tree */
|
|
/*====================================================================*/
|
|
|
|
struct ib_node *ib_node_first(struct ib_root *root)
|
|
{
|
|
struct ib_node *node = root->node;
|
|
if (node == NULL) return NULL;
|
|
while (node->left)
|
|
node = node->left;
|
|
return node;
|
|
}
|
|
|
|
struct ib_node *ib_node_last(struct ib_root *root)
|
|
{
|
|
struct ib_node *node = root->node;
|
|
if (node == NULL) return NULL;
|
|
while (node->right)
|
|
node = node->right;
|
|
return node;
|
|
}
|
|
|
|
struct ib_node *ib_node_next(struct ib_node *node)
|
|
{
|
|
if (node == NULL) return NULL;
|
|
if (node->right) {
|
|
node = node->right;
|
|
while (node->left)
|
|
node = node->left;
|
|
}
|
|
else {
|
|
while (1) {
|
|
struct ib_node *last = node;
|
|
node = node->parent;
|
|
if (node == NULL) break;
|
|
if (node->left == last) break;
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
struct ib_node *ib_node_prev(struct ib_node *node)
|
|
{
|
|
if (node == NULL) return NULL;
|
|
if (node->left) {
|
|
node = node->left;
|
|
while (node->right)
|
|
node = node->right;
|
|
}
|
|
else {
|
|
while (1) {
|
|
struct ib_node *last = node;
|
|
node = node->parent;
|
|
if (node == NULL) break;
|
|
if (node->right == last) break;
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
static inline void
|
|
_ib_child_replace(struct ib_node *oldnode, struct ib_node *newnode,
|
|
struct ib_node *parent, struct ib_root *root)
|
|
{
|
|
if (parent) {
|
|
if (parent->left == oldnode)
|
|
parent->left = newnode;
|
|
else
|
|
parent->right = newnode;
|
|
} else {
|
|
root->node = newnode;
|
|
}
|
|
}
|
|
|
|
static inline struct ib_node *
|
|
_ib_node_rotate_left(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
struct ib_node *right = node->right;
|
|
struct ib_node *parent = node->parent;
|
|
node->right = right->left;
|
|
ASSERTION(node && right);
|
|
if (right->left)
|
|
right->left->parent = node;
|
|
right->left = node;
|
|
right->parent = parent;
|
|
_ib_child_replace(node, right, parent, root);
|
|
node->parent = right;
|
|
return right;
|
|
}
|
|
|
|
static inline struct ib_node *
|
|
_ib_node_rotate_right(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
struct ib_node *left = node->left;
|
|
struct ib_node *parent = node->parent;
|
|
node->left = left->right;
|
|
ASSERTION(node && left);
|
|
if (left->right)
|
|
left->right->parent = node;
|
|
left->right = node;
|
|
left->parent = parent;
|
|
_ib_child_replace(node, left, parent, root);
|
|
node->parent = left;
|
|
return left;
|
|
}
|
|
|
|
void ib_node_replace(struct ib_node *victim, struct ib_node *newnode,
|
|
struct ib_root *root)
|
|
{
|
|
struct ib_node *parent = victim->parent;
|
|
_ib_child_replace(victim, newnode, parent, root);
|
|
if (victim->left) victim->left->parent = newnode;
|
|
if (victim->right) victim->right->parent = newnode;
|
|
newnode->left = victim->left;
|
|
newnode->right = victim->right;
|
|
newnode->parent = victim->parent;
|
|
newnode->height = victim->height;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* avl - node manipulation */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
static inline int IB_MAX(int x, int y)
|
|
{
|
|
#if 1
|
|
return (x < y)? y : x; /* this is faster with cmov on x86 */
|
|
#else
|
|
return x - ((x - y) & ((x - y) >> (sizeof(int) * 8 - 1)));
|
|
#endif
|
|
}
|
|
|
|
static inline void
|
|
_ib_node_height_update(struct ib_node *node)
|
|
{
|
|
int h0 = IB_LEFT_HEIGHT(node);
|
|
int h1 = IB_RIGHT_HEIGHT(node);
|
|
node->height = IB_MAX(h0, h1) + 1;
|
|
}
|
|
|
|
static inline struct ib_node *
|
|
_ib_node_fix_l(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
struct ib_node *right = node->right;
|
|
int rh0, rh1;
|
|
ASSERTION(right);
|
|
rh0 = IB_LEFT_HEIGHT(right);
|
|
rh1 = IB_RIGHT_HEIGHT(right);
|
|
if (rh0 > rh1) {
|
|
right = _ib_node_rotate_right(right, root);
|
|
_ib_node_height_update(right->right);
|
|
_ib_node_height_update(right);
|
|
/* _ib_node_height_update(node); */
|
|
}
|
|
node = _ib_node_rotate_left(node, root);
|
|
_ib_node_height_update(node->left);
|
|
_ib_node_height_update(node);
|
|
return node;
|
|
}
|
|
|
|
static inline struct ib_node *
|
|
_ib_node_fix_r(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
struct ib_node *left = node->left;
|
|
int rh0, rh1;
|
|
ASSERTION(left);
|
|
rh0 = IB_LEFT_HEIGHT(left);
|
|
rh1 = IB_RIGHT_HEIGHT(left);
|
|
if (rh0 < rh1) {
|
|
left = _ib_node_rotate_left(left, root);
|
|
_ib_node_height_update(left->left);
|
|
_ib_node_height_update(left);
|
|
/* _ib_node_height_update(node); */
|
|
}
|
|
node = _ib_node_rotate_right(node, root);
|
|
_ib_node_height_update(node->right);
|
|
_ib_node_height_update(node);
|
|
return node;
|
|
}
|
|
|
|
static inline void
|
|
_ib_node_rebalance(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
while (node) {
|
|
int h0 = (int)IB_LEFT_HEIGHT(node);
|
|
int h1 = (int)IB_RIGHT_HEIGHT(node);
|
|
int diff = h0 - h1;
|
|
int height = IB_MAX(h0, h1) + 1;
|
|
if (node->height != height) {
|
|
node->height = height;
|
|
}
|
|
else if (diff >= -1 && diff <= 1) {
|
|
break;
|
|
}
|
|
if (diff <= -2) {
|
|
node = _ib_node_fix_l(node, root);
|
|
}
|
|
else if (diff >= 2) {
|
|
node = _ib_node_fix_r(node, root);
|
|
}
|
|
node = node->parent;
|
|
}
|
|
}
|
|
|
|
void ib_node_post_insert(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
node->height = 1;
|
|
|
|
for (node = node->parent; node; node = node->parent) {
|
|
int h0 = (int)IB_LEFT_HEIGHT(node);
|
|
int h1 = (int)IB_RIGHT_HEIGHT(node);
|
|
int height = IB_MAX(h0, h1) + 1;
|
|
int diff = h0 - h1;
|
|
if (node->height == height) break;
|
|
node->height = height;
|
|
if (diff <= -2) {
|
|
node = _ib_node_fix_l(node, root);
|
|
}
|
|
else if (diff >= 2) {
|
|
node = _ib_node_fix_r(node, root);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ib_node_erase(struct ib_node *node, struct ib_root *root)
|
|
{
|
|
struct ib_node *child, *parent;
|
|
ASSERTION(node);
|
|
if (node->left && node->right) {
|
|
struct ib_node *old = node;
|
|
struct ib_node *left;
|
|
node = node->right;
|
|
while ((left = node->left) != NULL)
|
|
node = left;
|
|
child = node->right;
|
|
parent = node->parent;
|
|
if (child) {
|
|
child->parent = parent;
|
|
}
|
|
_ib_child_replace(node, child, parent, root);
|
|
if (node->parent == old)
|
|
parent = node;
|
|
node->left = old->left;
|
|
node->right = old->right;
|
|
node->parent = old->parent;
|
|
node->height = old->height;
|
|
_ib_child_replace(old, node, old->parent, root);
|
|
ASSERTION(old->left);
|
|
old->left->parent = node;
|
|
if (old->right) {
|
|
old->right->parent = node;
|
|
}
|
|
}
|
|
else {
|
|
if (node->left == NULL)
|
|
child = node->right;
|
|
else
|
|
child = node->left;
|
|
parent = node->parent;
|
|
_ib_child_replace(node, child, parent, root);
|
|
if (child) {
|
|
child->parent = parent;
|
|
}
|
|
}
|
|
if (parent) {
|
|
_ib_node_rebalance(parent, root);
|
|
}
|
|
}
|
|
|
|
|
|
/* avl nodes destroy: fast tear down the whole tree */
|
|
struct ib_node* ib_node_tear(struct ib_root *root, struct ib_node **next)
|
|
{
|
|
struct ib_node *node = *next;
|
|
struct ib_node *parent;
|
|
if (node == NULL) {
|
|
if (root->node == NULL)
|
|
return NULL;
|
|
node = root->node;
|
|
}
|
|
/* sink down to the leaf */
|
|
while (1) {
|
|
if (node->left) node = node->left;
|
|
else if (node->right) node = node->right;
|
|
else break;
|
|
}
|
|
/* tear down one leaf */
|
|
parent = node->parent;
|
|
if (parent == NULL) {
|
|
*next = NULL;
|
|
root->node = NULL;
|
|
return node;
|
|
}
|
|
if (parent->left == node) {
|
|
parent->left = NULL;
|
|
} else {
|
|
parent->right = NULL;
|
|
}
|
|
node->height = 0;
|
|
*next = parent;
|
|
return node;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* avltree - friendly interface */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
void ib_tree_init(struct ib_tree *tree,
|
|
int (*compare)(const void*, const void*), size_t size, size_t offset)
|
|
{
|
|
tree->root.node = NULL;
|
|
tree->offset = offset;
|
|
tree->size = size;
|
|
tree->count = 0;
|
|
tree->compare = compare;
|
|
}
|
|
|
|
|
|
void *ib_tree_first(struct ib_tree *tree)
|
|
{
|
|
struct ib_node *node = ib_node_first(&tree->root);
|
|
if (!node) return NULL;
|
|
return IB_NODE2DATA(node, tree->offset);
|
|
}
|
|
|
|
void *ib_tree_last(struct ib_tree *tree)
|
|
{
|
|
struct ib_node *node = ib_node_last(&tree->root);
|
|
if (!node) return NULL;
|
|
return IB_NODE2DATA(node, tree->offset);
|
|
}
|
|
|
|
void *ib_tree_next(struct ib_tree *tree, void *data)
|
|
{
|
|
struct ib_node *nn;
|
|
if (!data) return NULL;
|
|
nn = IB_DATA2NODE(data, tree->offset);
|
|
nn = ib_node_next(nn);
|
|
if (!nn) return NULL;
|
|
return IB_NODE2DATA(nn, tree->offset);
|
|
}
|
|
|
|
void *ib_tree_prev(struct ib_tree *tree, void *data)
|
|
{
|
|
struct ib_node *nn;
|
|
if (!data) return NULL;
|
|
nn = IB_DATA2NODE(data, tree->offset);
|
|
nn = ib_node_prev(nn);
|
|
if (!nn) return NULL;
|
|
return IB_NODE2DATA(nn, tree->offset);
|
|
}
|
|
|
|
|
|
/* require a temporary user structure (data) which contains the key */
|
|
void *ib_tree_find(struct ib_tree *tree, const void *data)
|
|
{
|
|
struct ib_node *n = tree->root.node;
|
|
int (*compare)(const void*, const void*) = tree->compare;
|
|
int offset = (int)(tree->offset);
|
|
while (n) {
|
|
void *nd = IB_NODE2DATA(n, offset);
|
|
int hr = compare(data, nd);
|
|
if (hr == 0) {
|
|
return nd;
|
|
}
|
|
else if (hr < 0) {
|
|
n = n->left;
|
|
}
|
|
else {
|
|
n = n->right;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void *ib_tree_nearest(struct ib_tree *tree, const void *data)
|
|
{
|
|
struct ib_node *n = tree->root.node;
|
|
struct ib_node *p = NULL;
|
|
int (*compare)(const void*, const void*) = tree->compare;
|
|
int offset = (int)(tree->offset);
|
|
while (n) {
|
|
void *nd = IB_NODE2DATA(n, offset);
|
|
int hr = compare(data, nd);
|
|
p = n;
|
|
if (n == 0) return nd;
|
|
else if (hr < 0) {
|
|
n = n->left;
|
|
}
|
|
else {
|
|
n = n->right;
|
|
}
|
|
}
|
|
return (p)? IB_NODE2DATA(p, offset) : NULL;
|
|
}
|
|
|
|
|
|
/* returns NULL for success, otherwise returns conflict node with same key */
|
|
void *ib_tree_add(struct ib_tree *tree, void *data)
|
|
{
|
|
struct ib_node **link = &tree->root.node;
|
|
struct ib_node *parent = NULL;
|
|
struct ib_node *node = IB_DATA2NODE(data, tree->offset);
|
|
int (*compare)(const void*, const void*) = tree->compare;
|
|
int offset = (int)(tree->offset);
|
|
while (link[0]) {
|
|
void *pd;
|
|
int hr;
|
|
parent = link[0];
|
|
pd = IB_NODE2DATA(parent, offset);
|
|
hr = compare(data, pd);
|
|
if (hr == 0) {
|
|
return pd;
|
|
}
|
|
else if (hr < 0) {
|
|
link = &(parent->left);
|
|
}
|
|
else {
|
|
link = &(parent->right);
|
|
}
|
|
}
|
|
ib_node_link(node, parent, link);
|
|
ib_node_post_insert(node, &tree->root);
|
|
tree->count++;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void ib_tree_remove(struct ib_tree *tree, void *data)
|
|
{
|
|
struct ib_node *node = IB_DATA2NODE(data, tree->offset);
|
|
if (!ib_node_empty(node)) {
|
|
ib_node_erase(node, &tree->root);
|
|
node->parent = node;
|
|
tree->count--;
|
|
}
|
|
}
|
|
|
|
|
|
void ib_tree_replace(struct ib_tree *tree, void *victim, void *newdata)
|
|
{
|
|
struct ib_node *vicnode = IB_DATA2NODE(victim, tree->offset);
|
|
struct ib_node *newnode = IB_DATA2NODE(newdata, tree->offset);
|
|
ib_node_replace(vicnode, newnode, &tree->root);
|
|
vicnode->parent = vicnode;
|
|
}
|
|
|
|
|
|
void ib_tree_clear(struct ib_tree *tree, void (*destroy)(void *data))
|
|
{
|
|
while (1) {
|
|
void *data;
|
|
if (tree->root.node == NULL) break;
|
|
data = IB_NODE2DATA(tree->root.node, tree->offset);
|
|
ib_tree_remove(tree, data);
|
|
if (destroy) destroy(data);
|
|
}
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* fastbin - fixed size object allocator */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
void ib_fastbin_init(struct ib_fastbin *fb, size_t obj_size)
|
|
{
|
|
const size_t align = sizeof(void*);
|
|
size_t need;
|
|
fb->start = NULL;
|
|
fb->endup = NULL;
|
|
fb->next = NULL;
|
|
fb->pages = NULL;
|
|
fb->obj_size = (obj_size + align - 1) & (~(align - 1));
|
|
need = fb->obj_size * 32 + sizeof(void*) + 16;
|
|
fb->page_size = (align <= 2)? 8 : 32;
|
|
while (fb->page_size < need) {
|
|
fb->page_size *= 2;
|
|
}
|
|
fb->maximum = (align <= 2)? fb->page_size : 0x10000;
|
|
}
|
|
|
|
void ib_fastbin_destroy(struct ib_fastbin *fb)
|
|
{
|
|
while (fb->pages) {
|
|
void *page = fb->pages;
|
|
void *next = IB_NEXT(page);
|
|
fb->pages = next;
|
|
ikmem_free(page);
|
|
}
|
|
fb->start = NULL;
|
|
fb->endup = NULL;
|
|
fb->next = NULL;
|
|
fb->pages = NULL;
|
|
}
|
|
|
|
void* ib_fastbin_new(struct ib_fastbin *fb)
|
|
{
|
|
size_t obj_size = fb->obj_size;
|
|
void *obj;
|
|
obj = fb->next;
|
|
if (obj) {
|
|
fb->next = IB_NEXT(fb->next);
|
|
return obj;
|
|
}
|
|
if (fb->start + obj_size > fb->endup) {
|
|
char *page = (char*)ikmem_malloc(fb->page_size);
|
|
size_t lineptr = (size_t)page;
|
|
ASSERTION(page);
|
|
IB_NEXT(page) = fb->pages;
|
|
fb->pages = page;
|
|
lineptr = (lineptr + sizeof(void*) + 15) & (~15);
|
|
fb->start = (char*)lineptr;
|
|
fb->endup = (char*)page + fb->page_size;
|
|
if (fb->page_size < fb->maximum) {
|
|
fb->page_size *= 2;
|
|
}
|
|
}
|
|
obj = fb->start;
|
|
fb->start += obj_size;
|
|
ASSERTION(fb->start <= fb->endup);
|
|
return obj;
|
|
}
|
|
|
|
void ib_fastbin_del(struct ib_fastbin *fb, void *ptr)
|
|
{
|
|
IB_NEXT(ptr) = fb->next;
|
|
fb->next = ptr;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* string */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
ib_string* ib_string_new(void)
|
|
{
|
|
struct ib_string* str = (ib_string*)ikmem_malloc(sizeof(ib_string));
|
|
assert(str);
|
|
str->ptr = str->sso;
|
|
str->size = 0;
|
|
str->capacity = IB_STRING_SSO;
|
|
str->ptr[0] = 0;
|
|
return str;
|
|
}
|
|
|
|
|
|
void ib_string_delete(ib_string *str)
|
|
{
|
|
assert(str);
|
|
if (str) {
|
|
if (str->ptr && str->ptr != str->sso)
|
|
ikmem_free(str->ptr);
|
|
str->ptr = NULL;
|
|
str->size = str->capacity = 0;
|
|
}
|
|
ikmem_free(str);
|
|
}
|
|
|
|
ib_string* ib_string_new_size(const char *text, int size)
|
|
{
|
|
struct ib_string *str = ib_string_new();
|
|
size = (size > 0)? size : 0;
|
|
if (size > 0 && text) {
|
|
ib_string_resize(str, size);
|
|
memcpy(str->ptr, text, size);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_new_from(const char *text)
|
|
{
|
|
return ib_string_new_size(text, (text)? ((int)strlen(text)) : 0);
|
|
}
|
|
|
|
static void _ib_string_set_capacity(ib_string *str, int capacity)
|
|
{
|
|
assert(str);
|
|
assert(capacity >= 0);
|
|
if (capacity <= IB_STRING_SSO) {
|
|
capacity = IB_STRING_SSO;
|
|
if (str->ptr != str->sso) {
|
|
if (str->size > 0) {
|
|
int csize = (str->size < capacity) ? str->size : capacity;
|
|
memcpy(str->sso, str->ptr, csize);
|
|
}
|
|
ikmem_free(str->ptr);
|
|
str->ptr = str->sso;
|
|
str->capacity = IB_STRING_SSO;
|
|
}
|
|
}
|
|
else {
|
|
char *ptr = (char*)ikmem_malloc(capacity + 2);
|
|
int csize = (capacity < str->size) ? capacity : str->size;
|
|
assert(ptr);
|
|
if (csize > 0) {
|
|
memcpy(ptr, str->ptr, csize);
|
|
}
|
|
if (str->ptr != str->sso)
|
|
ikmem_free(str->ptr);
|
|
str->ptr = ptr;
|
|
str->capacity = capacity;
|
|
}
|
|
if (str->size > str->capacity)
|
|
str->size = str->capacity;
|
|
str->ptr[str->size] = 0;
|
|
}
|
|
|
|
ib_string* ib_string_resize(ib_string *str, int newsize)
|
|
{
|
|
assert(str && newsize >= 0);
|
|
if (newsize > str->capacity) {
|
|
int capacity = str->capacity * 2;
|
|
if (capacity < newsize) {
|
|
while (capacity < newsize) {
|
|
capacity = capacity * 2;
|
|
}
|
|
}
|
|
_ib_string_set_capacity(str, capacity);
|
|
}
|
|
str->size = newsize;
|
|
str->ptr[str->size] = 0;
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_reserve(ib_string *str, int newsize)
|
|
{
|
|
int size = (newsize >= str->size)? newsize : str->size;
|
|
_ib_string_set_capacity(str, size);
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_insert(ib_string *str, int pos,
|
|
const void *data, int size)
|
|
{
|
|
int current = str->size;
|
|
if (pos < 0 || pos > str->size)
|
|
return NULL;
|
|
ib_string_resize(str, str->size + size);
|
|
if (pos < current) {
|
|
memmove(str->ptr + pos + size, str->ptr + pos, current - pos);
|
|
}
|
|
if (data) {
|
|
memcpy(str->ptr + pos, data, size);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_insert_c(ib_string *str, int pos, char c)
|
|
{
|
|
int current = str->size;
|
|
if (pos < 0 || pos > str->size)
|
|
return NULL;
|
|
ib_string_resize(str, str->size + 1);
|
|
if (pos < current) {
|
|
memmove(str->ptr + pos + 1, str->ptr + pos, current - pos);
|
|
}
|
|
str->ptr[pos] = c;
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_erase(ib_string *str, int pos, int size)
|
|
{
|
|
int current = str->size;
|
|
if (pos >= current) return 0;
|
|
if (pos + size >= current) size = current - pos;
|
|
if (size == 0) return 0;
|
|
memmove(str->ptr + pos, str->ptr + pos + size, current - pos - size);
|
|
return ib_string_resize(str, current - size);
|
|
}
|
|
|
|
int ib_string_compare(const struct ib_string *a, const struct ib_string *b)
|
|
{
|
|
int minsize = (a->size < b->size)? a->size : b->size;
|
|
int hr = memcmp(a->ptr, b->ptr, minsize);
|
|
if (hr < 0) return -1;
|
|
else if (hr > 0) return 1;
|
|
if (a->size < b->size) return -1;
|
|
else if (a->size > b->size) return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
ib_string* ib_string_assign(ib_string *str, const char *src)
|
|
{
|
|
return ib_string_assign_size(str, src, (int)strlen(src));
|
|
}
|
|
|
|
ib_string* ib_string_assign_size(ib_string *str, const char *src, int size)
|
|
{
|
|
assert(size >= 0);
|
|
ib_string_resize(str, size);
|
|
if (src) {
|
|
memcpy(str->ptr, src, size);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
ib_string* ib_string_append(ib_string *str, const char *src)
|
|
{
|
|
return ib_string_append_size(str, src, (int)strlen(src));
|
|
}
|
|
|
|
|
|
ib_string* ib_string_append_size(ib_string *str, const char *src, int size)
|
|
{
|
|
return ib_string_insert(str, str->size, src, size);
|
|
}
|
|
|
|
ib_string* ib_string_append_c(ib_string *str, char c)
|
|
{
|
|
ib_string_resize(str, str->size + 1);
|
|
str->ptr[str->size - 1] = c;
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_prepend(ib_string *str, const char *src)
|
|
{
|
|
return ib_string_prepend_size(str, src, (int)strlen(src));
|
|
}
|
|
|
|
ib_string* ib_string_prepend_size(ib_string *str, const char *src, int size)
|
|
{
|
|
return ib_string_insert(str, 0, src, size);
|
|
}
|
|
|
|
ib_string* ib_string_prepend_c(ib_string *str, char c)
|
|
{
|
|
int current = str->size;
|
|
ib_string_resize(str, current + 1);
|
|
if (current > 0) {
|
|
memmove(str->ptr + 1, str->ptr, current);
|
|
}
|
|
str->ptr[0] = c;
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_rewrite(ib_string *str, int pos, const char *src)
|
|
{
|
|
return ib_string_rewrite_size(str, pos, src, (int)strlen(src));
|
|
}
|
|
|
|
ib_string* ib_string_rewrite_size(ib_string *str, int pos,
|
|
const char *src, int size)
|
|
{
|
|
if (pos < 0) size += pos, pos = 0;
|
|
if (pos + size >= str->size) size = str->size - pos;
|
|
if (size <= 0) return str;
|
|
if (src) {
|
|
memcpy(str->ptr + pos, src, size);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
int ib_string_find(const ib_string *str, const char *src, int len, int start)
|
|
{
|
|
char *text = str->ptr;
|
|
int pos = (start < 0)? 0 : start;
|
|
int length = (len >= 0)? len : ((int)strlen(src));
|
|
int endup = str->size - length;
|
|
char ch;
|
|
if (length <= 0) return pos;
|
|
for (ch = src[0]; pos <= endup; pos++) {
|
|
if (text[pos] == ch) {
|
|
if (memcmp(text + pos, src, length) == 0)
|
|
return pos;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int ib_string_find_c(const ib_string *str, char ch, int start)
|
|
{
|
|
const char *text = str->ptr;
|
|
int pos = (start < 0)? 0 : start;
|
|
int endup = str->size;
|
|
for (; pos < endup; pos++) {
|
|
if (text[pos] == ch) return pos;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
ib_array* ib_string_split(const ib_string *str, const char *sep, int len)
|
|
{
|
|
if (len == 0) {
|
|
return NULL;
|
|
}
|
|
else {
|
|
ib_array *array = ib_array_new((void (*)(void*))ib_string_delete);
|
|
int start = 0;
|
|
len = (len >= 0)? len : ((int)strlen(sep));
|
|
while (1) {
|
|
int pos = ib_string_find(str, sep, len, start);
|
|
if (pos < 0) {
|
|
ib_string *newstr = ib_string_new();
|
|
ib_string_assign_size(newstr, str->ptr + start,
|
|
str->size - start);
|
|
ib_array_push(array, newstr);
|
|
break;
|
|
}
|
|
else {
|
|
ib_string* newstr = ib_string_new();
|
|
ib_string_assign_size(newstr, str->ptr + start, pos - start);
|
|
start = pos + len;
|
|
ib_array_push(array, newstr);
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
}
|
|
|
|
ib_array* ib_string_split_c(const ib_string *str, char sep)
|
|
{
|
|
if (str == NULL) {
|
|
return NULL;
|
|
}
|
|
else {
|
|
ib_array *array = ib_array_new((void (*)(void*))ib_string_delete);
|
|
int start = 0;
|
|
while (1) {
|
|
int pos = ib_string_find_c(str, sep, start);
|
|
if (pos < 0) {
|
|
ib_string *newstr = ib_string_new();
|
|
ib_string_assign_size(newstr, str->ptr + start,
|
|
str->size - start);
|
|
ib_array_push(array, newstr);
|
|
break;
|
|
}
|
|
else {
|
|
ib_string *newstr = ib_string_new();
|
|
ib_string_assign_size(newstr, str->ptr + start, pos - start);
|
|
start = pos + 1;
|
|
ib_array_push(array, newstr);
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
}
|
|
|
|
ib_string* ib_string_strip(ib_string *str, const char *seps)
|
|
{
|
|
const char *ptr = str->ptr;
|
|
const char *endup = str->ptr + str->size;
|
|
int off, pos;
|
|
for (; ptr < endup; ptr++) {
|
|
const char *sep = seps;
|
|
int match = 0;
|
|
for (; sep[0]; sep++) {
|
|
if (ptr[0] == sep[0]) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (match == 0) break;
|
|
}
|
|
off = (int)(ptr - str->ptr);
|
|
if (off > 0) {
|
|
ib_string_erase(str, 0, off);
|
|
}
|
|
ptr = str->ptr;
|
|
pos = str->size;
|
|
for (; pos > 0; pos--) {
|
|
const char *sep = seps;
|
|
int match = 0;
|
|
for (; sep[0]; sep++) {
|
|
if (ptr[pos - 1] == sep[0]) {
|
|
match = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (match == 0) break;
|
|
}
|
|
ib_string_resize(str, pos);
|
|
return str;
|
|
}
|
|
|
|
ib_string* ib_string_replace_size(ib_string *str, int pos, int size,
|
|
const char *src, int len)
|
|
{
|
|
ib_string_erase(str, pos, size);
|
|
ib_string_insert(str, pos, src, len);
|
|
return str;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* static hash table (closed hash table with avlnode) */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
|
|
void ib_hash_init(struct ib_hash_table *ht,
|
|
size_t (*hash)(const void *key),
|
|
int (*compare)(const void *key1, const void *key2))
|
|
{
|
|
size_t i;
|
|
ht->count = 0;
|
|
ht->index_size = IB_HASH_INIT_SIZE;
|
|
ht->index_mask = ht->index_size - 1;
|
|
ht->hash = hash;
|
|
ht->compare = compare;
|
|
ilist_init(&ht->head);
|
|
ht->index = ht->init;
|
|
for (i = 0; i < IB_HASH_INIT_SIZE; i++) {
|
|
ht->index[i].avlroot.node = NULL;
|
|
ilist_init(&(ht->index[i].node));
|
|
}
|
|
}
|
|
|
|
struct ib_hash_node* ib_hash_node_first(struct ib_hash_table *ht)
|
|
{
|
|
struct ILISTHEAD *head = ht->head.next;
|
|
if (head != &ht->head) {
|
|
struct ib_hash_index *index =
|
|
ilist_entry(head, struct ib_hash_index, node);
|
|
struct ib_node *avlnode = ib_node_first(&index->avlroot);
|
|
if (avlnode == NULL) return NULL;
|
|
return IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct ib_hash_node* ib_hash_node_last(struct ib_hash_table *ht)
|
|
{
|
|
struct ILISTHEAD *head = ht->head.prev;
|
|
if (head != &ht->head) {
|
|
struct ib_hash_index *index =
|
|
ilist_entry(head, struct ib_hash_index, node);
|
|
struct ib_node *avlnode = ib_node_last(&index->avlroot);
|
|
if (avlnode == NULL) return NULL;
|
|
return IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct ib_hash_node* ib_hash_node_next(struct ib_hash_table *ht,
|
|
struct ib_hash_node *node)
|
|
{
|
|
struct ib_node *avlnode;
|
|
struct ib_hash_index *index;
|
|
struct ILISTHEAD *listnode;
|
|
if (node == NULL) return NULL;
|
|
avlnode = ib_node_next(&node->avlnode);
|
|
if (avlnode) {
|
|
return IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
}
|
|
index = &(ht->index[node->hash & ht->index_mask]);
|
|
listnode = index->node.next;
|
|
if (listnode == &(ht->head)) {
|
|
return NULL;
|
|
}
|
|
index = ilist_entry(listnode, struct ib_hash_index, node);
|
|
avlnode = ib_node_first(&index->avlroot);
|
|
if (avlnode == NULL) return NULL;
|
|
return IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
}
|
|
|
|
struct ib_hash_node* ib_hash_node_prev(struct ib_hash_table *ht,
|
|
struct ib_hash_node *node)
|
|
{
|
|
struct ib_node *avlnode;
|
|
struct ib_hash_index *index;
|
|
struct ILISTHEAD *listnode;
|
|
if (node == NULL) return NULL;
|
|
avlnode = ib_node_prev(&node->avlnode);
|
|
if (avlnode) {
|
|
return IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
}
|
|
index = &(ht->index[node->hash & ht->index_mask]);
|
|
listnode = index->node.prev;
|
|
if (listnode == &(ht->head)) {
|
|
return NULL;
|
|
}
|
|
index = ilist_entry(listnode, struct ib_hash_index, node);
|
|
avlnode = ib_node_last(&index->avlroot);
|
|
if (avlnode == NULL) return NULL;
|
|
return IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
}
|
|
|
|
struct ib_hash_node* ib_hash_find(struct ib_hash_table *ht,
|
|
const struct ib_hash_node *node)
|
|
{
|
|
size_t hash = node->hash;
|
|
const void *key = node->key;
|
|
struct ib_hash_index *index = &(ht->index[hash & ht->index_mask]);
|
|
struct ib_node *avlnode = index->avlroot.node;
|
|
int (*compare)(const void *, const void *) = ht->compare;
|
|
while (avlnode) {
|
|
struct ib_hash_node *snode =
|
|
IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
size_t shash = snode->hash;
|
|
if (hash == shash) {
|
|
int hc = compare(key, snode->key);
|
|
if (hc == 0) return snode;
|
|
avlnode = (hc < 0)? avlnode->left : avlnode->right;
|
|
}
|
|
else {
|
|
avlnode = (hash < shash)? avlnode->left : avlnode->right;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void ib_hash_erase(struct ib_hash_table *ht, struct ib_hash_node *node)
|
|
{
|
|
struct ib_hash_index *index;
|
|
ASSERTION(node && ht);
|
|
ASSERTION(!ib_node_empty(&node->avlnode));
|
|
index = &ht->index[node->hash & ht->index_mask];
|
|
if (index->avlroot.node == &node->avlnode && node->avlnode.height == 1) {
|
|
index->avlroot.node = NULL;
|
|
ilist_del_init(&index->node);
|
|
}
|
|
else {
|
|
ib_node_erase(&node->avlnode, &index->avlroot);
|
|
}
|
|
ib_node_init(&node->avlnode);
|
|
ht->count--;
|
|
}
|
|
|
|
struct ib_node** ib_hash_track(struct ib_hash_table *ht,
|
|
const struct ib_hash_node *node, struct ib_node **parent)
|
|
{
|
|
size_t hash = node->hash;
|
|
const void *key = node->key;
|
|
struct ib_hash_index *index = &(ht->index[hash & ht->index_mask]);
|
|
struct ib_node **link = &index->avlroot.node;
|
|
struct ib_node *p = NULL;
|
|
int (*compare)(const void *key1, const void *key2) = ht->compare;
|
|
parent[0] = NULL;
|
|
while (link[0]) {
|
|
struct ib_hash_node *snode;
|
|
size_t shash;
|
|
p = link[0];
|
|
snode = IB_ENTRY(p, struct ib_hash_node, avlnode);
|
|
shash = snode->hash;
|
|
if (hash == shash) {
|
|
int hc = compare(key, snode->key);
|
|
if (hc == 0) {
|
|
parent[0] = p;
|
|
return NULL;
|
|
}
|
|
link = (hc < 0)? (&p->left) : (&p->right);
|
|
}
|
|
else {
|
|
link = (hash < shash)? (&p->left) : (&p->right);
|
|
}
|
|
}
|
|
parent[0] = p;
|
|
return link;
|
|
}
|
|
|
|
|
|
struct ib_hash_node* ib_hash_add(struct ib_hash_table *ht,
|
|
struct ib_hash_node *node)
|
|
{
|
|
struct ib_hash_index *index = &(ht->index[node->hash & ht->index_mask]);
|
|
if (index->avlroot.node == NULL) {
|
|
index->avlroot.node = &node->avlnode;
|
|
node->avlnode.parent = NULL;
|
|
node->avlnode.left = NULL;
|
|
node->avlnode.right = NULL;
|
|
node->avlnode.height = 1;
|
|
ilist_add_tail(&index->node, &ht->head);
|
|
}
|
|
else {
|
|
struct ib_node **link, *parent;
|
|
link = ib_hash_track(ht, node, &parent);
|
|
if (link == NULL) {
|
|
ASSERTION(parent);
|
|
return IB_ENTRY(parent, struct ib_hash_node, avlnode);
|
|
}
|
|
ib_node_link(&node->avlnode, parent, link);
|
|
ib_node_post_insert(&node->avlnode, &index->avlroot);
|
|
}
|
|
ht->count++;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void ib_hash_replace(struct ib_hash_table *ht,
|
|
struct ib_hash_node *victim, struct ib_hash_node *newnode)
|
|
{
|
|
struct ib_hash_index *index = &ht->index[victim->hash & ht->index_mask];
|
|
ib_node_replace(&victim->avlnode, &newnode->avlnode, &index->avlroot);
|
|
}
|
|
|
|
void ib_hash_clear(struct ib_hash_table *ht,
|
|
void (*destroy)(struct ib_hash_node *node))
|
|
{
|
|
while (!ilist_is_empty(&ht->head)) {
|
|
struct ib_hash_index *index = ilist_entry(ht->head.next,
|
|
struct ib_hash_index, node);
|
|
struct ib_node *next = NULL;
|
|
while (index->avlroot.node != NULL) {
|
|
struct ib_node *avlnode = ib_node_tear(&index->avlroot, &next);
|
|
ASSERTION(avlnode);
|
|
if (destroy) {
|
|
struct ib_hash_node *node =
|
|
IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
destroy(node);
|
|
}
|
|
}
|
|
ilist_del_init(&index->node);
|
|
}
|
|
ht->count = 0;
|
|
}
|
|
|
|
|
|
void* ib_hash_swap(struct ib_hash_table *ht, void *ptr, size_t nbytes)
|
|
{
|
|
struct ib_hash_index *old_index = ht->index;
|
|
struct ib_hash_index *new_index = (struct ib_hash_index*)ptr;
|
|
size_t index_size = 1;
|
|
struct ILISTHEAD head;
|
|
size_t i;
|
|
ASSERTION(nbytes >= sizeof(struct ib_hash_index));
|
|
if (new_index == NULL) {
|
|
if (ht->index == ht->init) {
|
|
return NULL;
|
|
}
|
|
new_index = ht->init;
|
|
index_size = IB_HASH_INIT_SIZE;
|
|
}
|
|
else if (new_index == old_index) {
|
|
return old_index;
|
|
}
|
|
if (new_index != ht->init) {
|
|
size_t test_size = sizeof(struct ib_hash_index);
|
|
while (test_size < nbytes) {
|
|
size_t next_size = test_size * 2;
|
|
if (next_size > nbytes) break;
|
|
test_size = next_size;
|
|
index_size = index_size * 2;
|
|
}
|
|
}
|
|
ht->index = new_index;
|
|
ht->index_size = index_size;
|
|
ht->index_mask = index_size - 1;
|
|
ht->count = 0;
|
|
for (i = 0; i < index_size; i++) {
|
|
ht->index[i].avlroot.node = NULL;
|
|
ilist_init(&ht->index[i].node);
|
|
}
|
|
ilist_replace(&ht->head, &head);
|
|
ilist_init(&ht->head);
|
|
while (!ilist_is_empty(&head)) {
|
|
struct ib_hash_index *index = ilist_entry(head.next,
|
|
struct ib_hash_index, node);
|
|
#if 1
|
|
struct ib_node *next = NULL;
|
|
while (index->avlroot.node) {
|
|
struct ib_node *avlnode = ib_node_tear(&index->avlroot, &next);
|
|
struct ib_hash_node *snode, *hr;
|
|
ASSERTION(avlnode);
|
|
snode = IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
hr = ib_hash_add(ht, snode);
|
|
if (hr != NULL) {
|
|
ASSERTION(hr == NULL);
|
|
return NULL;
|
|
}
|
|
}
|
|
#else
|
|
while (index->avlroot.node) {
|
|
struct ib_node *avlnode = index->avlroot.node;
|
|
struct ib_hash_node *snode, *hr;
|
|
ib_node_erase(avlnode, &index->avlroot);
|
|
snode = IB_ENTRY(avlnode, struct ib_hash_node, avlnode);
|
|
hr = ib_hash_add(ht, snode);
|
|
ASSERTION(hr == NULL);
|
|
hr = hr;
|
|
}
|
|
#endif
|
|
ilist_del_init(&index->node);
|
|
}
|
|
return (old_index == ht->init)? NULL : old_index;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* hash map, wrapper of ib_hash_table to support direct key/value */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
struct ib_hash_entry* ib_map_first(struct ib_hash_map *hm)
|
|
{
|
|
struct ib_hash_node *node = ib_hash_node_first(&hm->ht);
|
|
if (node == NULL) return NULL;
|
|
return IB_ENTRY(node, struct ib_hash_entry, node);
|
|
}
|
|
|
|
|
|
struct ib_hash_entry* ib_map_last(struct ib_hash_map *hm)
|
|
{
|
|
struct ib_hash_node *node = ib_hash_node_last(&hm->ht);
|
|
if (node == NULL) return NULL;
|
|
return IB_ENTRY(node, struct ib_hash_entry, node);
|
|
}
|
|
|
|
|
|
struct ib_hash_entry* ib_map_next(struct ib_hash_map *hm,
|
|
struct ib_hash_entry *n)
|
|
{
|
|
struct ib_hash_node *node = ib_hash_node_next(&hm->ht, &n->node);
|
|
if (node == NULL) return NULL;
|
|
return IB_ENTRY(node, struct ib_hash_entry, node);
|
|
}
|
|
|
|
|
|
struct ib_hash_entry* ib_map_prev(struct ib_hash_map *hm,
|
|
struct ib_hash_entry *n)
|
|
{
|
|
struct ib_hash_node *node = ib_hash_node_prev(&hm->ht, &n->node);
|
|
if (node == NULL) return NULL;
|
|
return IB_ENTRY(node, struct ib_hash_entry, node);
|
|
}
|
|
|
|
|
|
void ib_map_init(struct ib_hash_map *hm, size_t (*hash)(const void*),
|
|
int (*compare)(const void *, const void *))
|
|
{
|
|
hm->count = 0;
|
|
hm->key_copy = NULL;
|
|
hm->key_destroy = NULL;
|
|
hm->value_copy = NULL;
|
|
hm->value_destroy = NULL;
|
|
hm->insert = 0;
|
|
hm->fixed = 0;
|
|
ib_hash_init(&hm->ht, hash, compare);
|
|
ib_fastbin_init(&hm->fb, sizeof(struct ib_hash_entry));
|
|
}
|
|
|
|
void ib_map_destroy(struct ib_hash_map *hm)
|
|
{
|
|
void *ptr;
|
|
ib_map_clear(hm);
|
|
ptr = ib_hash_swap(&hm->ht, NULL, 0);
|
|
if (ptr) {
|
|
ikmem_free(ptr);
|
|
}
|
|
ib_fastbin_destroy(&hm->fb);
|
|
}
|
|
|
|
struct ib_hash_entry* ib_map_find(struct ib_hash_map *hm, const void *key)
|
|
{
|
|
struct ib_hash_table *ht = &hm->ht;
|
|
struct ib_hash_node dummy;
|
|
struct ib_hash_node *rh;
|
|
void *ptr = (void*)key;
|
|
ib_hash_node_key(ht, &dummy, ptr);
|
|
rh = ib_hash_find(ht, &dummy);
|
|
return (rh == NULL)? NULL : IB_ENTRY(rh, struct ib_hash_entry, node);
|
|
}
|
|
|
|
|
|
void* ib_map_lookup(struct ib_hash_map *hm, const void *key, void *defval)
|
|
{
|
|
struct ib_hash_entry *entry = ib_map_find(hm, key);
|
|
if (entry == NULL) return defval;
|
|
return ib_hash_value(entry);
|
|
}
|
|
|
|
static inline struct ib_hash_entry*
|
|
ib_hash_entry_allocate(struct ib_hash_map *hm, void *key, void *value)
|
|
{
|
|
struct ib_hash_entry *entry;
|
|
entry = (struct ib_hash_entry*)ib_fastbin_new(&hm->fb);
|
|
ASSERTION(entry);
|
|
if (hm->key_copy) entry->node.key = hm->key_copy(key);
|
|
else entry->node.key = key;
|
|
if (hm->value_copy) entry->value = hm->value_copy(value);
|
|
else entry->value = value;
|
|
return entry;
|
|
}
|
|
|
|
static inline struct ib_hash_entry*
|
|
ib_hash_update(struct ib_hash_map *hm, void *key, void *value, int update)
|
|
{
|
|
size_t hash = hm->ht.hash(key);
|
|
struct ib_hash_index *index = &(hm->ht.index[hash & hm->ht.index_mask]);
|
|
struct ib_node **link = &index->avlroot.node;
|
|
struct ib_node *parent = NULL;
|
|
struct ib_hash_entry *entry;
|
|
int (*compare)(const void *key1, const void *key2) = hm->ht.compare;
|
|
if (index->avlroot.node == NULL) {
|
|
entry = ib_hash_entry_allocate(hm, key, value);
|
|
ASSERTION(entry);
|
|
entry->node.avlnode.height = 1;
|
|
entry->node.avlnode.left = NULL;
|
|
entry->node.avlnode.right = NULL;
|
|
entry->node.avlnode.parent = NULL;
|
|
entry->node.hash = hash;
|
|
index->avlroot.node = &(entry->node.avlnode);
|
|
ilist_add_tail(&index->node, &(hm->ht.head));
|
|
hm->ht.count++;
|
|
hm->insert = 1;
|
|
return entry;
|
|
}
|
|
while (link[0]) {
|
|
struct ib_hash_node *snode;
|
|
size_t shash;
|
|
parent = link[0];
|
|
snode = IB_ENTRY(parent, struct ib_hash_node, avlnode);
|
|
shash = snode->hash;
|
|
if (hash != shash) {
|
|
link = (hash < shash)? (&parent->left) : (&parent->right);
|
|
} else {
|
|
int hc = compare(key, snode->key);
|
|
if (hc == 0) {
|
|
entry = IB_ENTRY(snode, struct ib_hash_entry, node);
|
|
if (update) {
|
|
if (hm->value_destroy) {
|
|
hm->value_destroy(entry->value);
|
|
}
|
|
if (hm->value_copy == NULL) entry->value = value;
|
|
else entry->value = hm->value_copy(value);
|
|
}
|
|
hm->insert = 0;
|
|
return entry;
|
|
} else {
|
|
link = (hc < 0)? (&parent->left) : (&parent->right);
|
|
}
|
|
}
|
|
}
|
|
entry = ib_hash_entry_allocate(hm, key, value);
|
|
ASSERTION(entry);
|
|
entry->node.hash = hash;
|
|
ib_node_link(&(entry->node.avlnode), parent, link);
|
|
ib_node_post_insert(&(entry->node.avlnode), &index->avlroot);
|
|
hm->ht.count++;
|
|
hm->insert = 1;
|
|
return entry;
|
|
}
|
|
|
|
static inline void ib_map_rehash(struct ib_hash_map *hm, size_t capacity)
|
|
{
|
|
size_t isize = hm->ht.index_size;
|
|
size_t limit = (capacity * 6) >> 2; /* capacity * 6 / 4 */
|
|
if (isize < limit && hm->fixed == 0) {
|
|
size_t need = isize;
|
|
size_t size;
|
|
void *ptr;
|
|
while (need < limit) need <<= 1;
|
|
size = need * sizeof(struct ib_hash_index);
|
|
ptr = ikmem_malloc(size);
|
|
ASSERTION(ptr);
|
|
ptr = ib_hash_swap(&hm->ht, ptr, size);
|
|
if (ptr) {
|
|
ikmem_free(ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ib_map_reserve(struct ib_hash_map *hm, size_t capacity)
|
|
{
|
|
ib_map_rehash(hm, capacity);
|
|
}
|
|
|
|
struct ib_hash_entry*
|
|
ib_map_add(struct ib_hash_map *hm, void *key, void *value, int *success)
|
|
{
|
|
struct ib_hash_entry *entry = ib_hash_update(hm, key, value, 0);
|
|
if (success) success[0] = hm->insert;
|
|
ib_map_rehash(hm, hm->ht.count);
|
|
return entry;
|
|
}
|
|
|
|
struct ib_hash_entry*
|
|
ib_map_set(struct ib_hash_map *hm, void *key, void *value)
|
|
{
|
|
struct ib_hash_entry *entry = ib_hash_update(hm, key, value, 0);
|
|
ib_map_rehash(hm, hm->ht.count);
|
|
return entry;
|
|
}
|
|
|
|
void *ib_map_get(struct ib_hash_map *hm, const void *key)
|
|
{
|
|
return ib_map_lookup(hm, key, NULL);
|
|
}
|
|
|
|
void ib_map_erase(struct ib_hash_map *hm, struct ib_hash_entry *entry)
|
|
{
|
|
ASSERTION(entry);
|
|
ASSERTION(!ib_node_empty(&(entry->node.avlnode)));
|
|
ib_hash_erase(&hm->ht, &entry->node);
|
|
ib_node_init(&(entry->node.avlnode));
|
|
if (hm->key_destroy) hm->key_destroy(entry->node.key);
|
|
if (hm->value_destroy) hm->value_destroy(entry->value);
|
|
entry->node.key = NULL;
|
|
entry->value = NULL;
|
|
ib_fastbin_del(&hm->fb, entry);
|
|
}
|
|
|
|
int ib_map_remove(struct ib_hash_map *hm, const void *key)
|
|
{
|
|
struct ib_hash_entry *entry;
|
|
entry = ib_map_find(hm, key);
|
|
if (entry == NULL) {
|
|
return -1;
|
|
}
|
|
ib_map_erase(hm, entry);
|
|
return 0;
|
|
}
|
|
|
|
void ib_map_clear(struct ib_hash_map *hm)
|
|
{
|
|
while (1) {
|
|
struct ib_hash_entry *entry = ib_map_first(hm);
|
|
if (entry == NULL) break;
|
|
ib_map_erase(hm, entry);
|
|
}
|
|
ASSERTION(hm->count == 0);
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* common type hash and equal functions */
|
|
/*--------------------------------------------------------------------*/
|
|
size_t ib_hash_seed = 0x11223344;
|
|
|
|
size_t ib_hash_func_uint(const void *key)
|
|
{
|
|
#if 0
|
|
size_t x = (size_t)key;
|
|
return (x * 2654435761u) ^ ib_hash_seed;
|
|
#else
|
|
return (size_t)key;
|
|
#endif
|
|
}
|
|
|
|
size_t ib_hash_func_int(const void *key)
|
|
{
|
|
#if 0
|
|
size_t x = (size_t)key;
|
|
return (x * 2654435761u) ^ ib_hash_seed;
|
|
#else
|
|
return (size_t)key;
|
|
#endif
|
|
}
|
|
|
|
size_t ib_hash_bytes_stl(const void *ptr, size_t size, size_t seed)
|
|
{
|
|
const unsigned char *buf = (const unsigned char*)ptr;
|
|
const size_t m = 0x5bd1e995;
|
|
size_t hash = size ^ seed;
|
|
for (; size >= 4; buf += 4, size -= 4) {
|
|
size_t k = *((IUINT32*)buf);
|
|
k *= m;
|
|
k = (k >> 24) * m;
|
|
hash = (hash * m) ^ k;
|
|
}
|
|
switch (size) {
|
|
case 3: hash ^= ((IUINT32)buf[2]) << 16;
|
|
case 2: hash ^= ((IUINT32)buf[1]) << 8;
|
|
case 1: hash ^= ((IUINT32)buf[0]); hash = hash * m; break;
|
|
}
|
|
hash = (hash ^ (hash >> 13)) * m;
|
|
return hash ^ (hash >> 15);
|
|
}
|
|
|
|
size_t ib_hash_bytes_lua(const void *ptr, size_t size, size_t seed)
|
|
{
|
|
const unsigned char *name = (const unsigned char*)ptr;
|
|
size_t step = (size >> 5) + 1;
|
|
size_t h = size ^ seed, i;
|
|
for(i = size; i >= step; i -= step)
|
|
h = h ^ ((h << 5) + (h >> 2) + (size_t)name[i - 1]);
|
|
return h;
|
|
}
|
|
|
|
size_t ib_hash_func_str(const void *key)
|
|
{
|
|
ib_string *str = (ib_string*)key;
|
|
#ifndef IB_HASH_BYTES_STL
|
|
return ib_hash_bytes_lua(str->ptr, str->size, ib_hash_seed);
|
|
#else
|
|
return ib_hash_bytes_stl(str->ptr, str->size, ib_hash_seed);
|
|
#endif
|
|
}
|
|
|
|
size_t ib_hash_func_cstr(const void *key)
|
|
{
|
|
const char *str = (const char*)key;
|
|
size_t size = strlen(str);
|
|
#ifndef IB_HASH_BYTES_STL
|
|
return ib_hash_bytes_lua(str, size, ib_hash_seed);
|
|
#else
|
|
return ib_hash_bytes_stl(str, size, ib_hash_seed);
|
|
#endif
|
|
}
|
|
|
|
int ib_hash_compare_uint(const void *key1, const void *key2)
|
|
{
|
|
size_t x = (size_t)key1;
|
|
size_t y = (size_t)key2;
|
|
if (x == y) return 0;
|
|
return (x < y)? -1 : 1;
|
|
}
|
|
|
|
int ib_hash_compare_int(const void *key1, const void *key2)
|
|
{
|
|
ilong x = (ilong)key1;
|
|
ilong y = (ilong)key2;
|
|
if (x == y) return 0;
|
|
return (x < y)? -1 : 1;
|
|
}
|
|
|
|
int ib_hash_compare_str(const void *key1, const void *key2)
|
|
{
|
|
return ib_string_compare((const ib_string*)key1, (const ib_string*)key2);
|
|
}
|
|
|
|
int ib_compare_bytes(const void *p1, size_t s1, const void *p2, size_t s2)
|
|
{
|
|
size_t minsize = (s1 < s2)? s1 : s2;
|
|
int hr = memcmp(p1, p2, minsize);
|
|
if (hr == 0) {
|
|
if (s1 == s2) return 0;
|
|
return (s1 < s2)? -1 : 1;
|
|
}
|
|
else {
|
|
return (hr < 0)? -1 : 1;
|
|
}
|
|
}
|
|
|
|
int ib_hash_compare_cstr(const void *key1, const void *key2)
|
|
{
|
|
const char *x = (const char*)key1;
|
|
const char *y = (const char*)key2;
|
|
return ib_compare_bytes(x, strlen(x), y, strlen(y));
|
|
}
|
|
|
|
|
|
|
|
struct ib_hash_entry *ib_map_find_uint(struct ib_hash_map *hm, iulong key)
|
|
{
|
|
struct ib_hash_entry *hr;
|
|
void *kk = (void*)key;
|
|
ib_map_search(hm, kk, ib_hash_func_uint, ib_hash_compare_uint, hr);
|
|
return hr;
|
|
}
|
|
|
|
struct ib_hash_entry *ib_map_find_int(struct ib_hash_map *hm, ilong key)
|
|
{
|
|
struct ib_hash_entry *hr;
|
|
void *kk = (void*)key;
|
|
ib_map_search(hm, kk, ib_hash_func_int, ib_hash_compare_int, hr);
|
|
return hr;
|
|
}
|
|
|
|
struct ib_hash_entry *ib_map_find_str(struct ib_hash_map *hm, const ib_string *key)
|
|
{
|
|
struct ib_hash_entry *hr;
|
|
void *kk = (void*)key;
|
|
ib_map_search(hm, kk, ib_hash_func_str, ib_hash_compare_str, hr);
|
|
return hr;
|
|
}
|
|
|
|
struct ib_hash_entry *ib_map_find_cstr(struct ib_hash_map *hm, const char *key)
|
|
{
|
|
struct ib_hash_entry *hr;
|
|
void *kk = (void*)key;
|
|
ib_map_search(hm, kk, ib_hash_func_cstr, ib_hash_compare_cstr, hr);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|