Imported Upstream version 11.2

This commit is contained in:
Mario Fetka
2019-01-07 14:06:15 +01:00
commit 8d2a0b593e
130 changed files with 86303 additions and 0 deletions

24
tommyds/LICENSE Normal file
View File

@@ -0,0 +1,24 @@
Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

42
tommyds/tommy.c Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* redefine the malloc for tommy use */
#define tommy_malloc malloc_nofail
#define tommy_calloc calloc_nofail
#define tommy_free free
#include "cmdline/portable.h"
#include "cmdline/support.h" /* for malloc/calloc_nofail() */
#include "tommyhash.c"
#include "tommyarray.c"
#include "tommyarrayblkof.c"
#include "tommylist.c"
#include "tommytree.c"
#include "tommyhashdyn.c"

83
tommyds/tommyarray.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2011, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "tommyarray.h"
/******************************************************************************/
/* array */
void tommy_array_init(tommy_array* array)
{
tommy_uint_t i;
/* fixed initial size */
array->bucket_bit = TOMMY_ARRAY_BIT;
array->bucket_max = 1 << array->bucket_bit;
array->bucket[0] = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*)));
for (i = 1; i < TOMMY_ARRAY_BIT; ++i)
array->bucket[i] = array->bucket[0];
array->count = 0;
}
void tommy_array_done(tommy_array* array)
{
tommy_uint_t i;
tommy_free(array->bucket[0]);
for (i = TOMMY_ARRAY_BIT; i < array->bucket_bit; ++i) {
void** segment = array->bucket[i];
tommy_free(&segment[((tommy_ptrdiff_t)1) << i]);
}
}
void tommy_array_grow(tommy_array* array, tommy_count_t count)
{
if (array->count >= count)
return;
array->count = count;
while (count > array->bucket_max) {
void** segment;
/* allocate one more segment */
segment = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*)));
/* store it adjusting the offset */
/* cast to ptrdiff_t to ensure to get a negative value */
array->bucket[array->bucket_bit] = &segment[-(tommy_ptrdiff_t)array->bucket_max];
++array->bucket_bit;
array->bucket_max = 1 << array->bucket_bit;
}
}
tommy_size_t tommy_array_memory_usage(tommy_array* array)
{
return array->bucket_max * (tommy_size_t)sizeof(void*);
}

151
tommyds/tommyarray.h Normal file
View File

@@ -0,0 +1,151 @@
/*
* Copyright (c) 2011, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Dynamic array based on segments of exponential growing size.
*
* This array is able to grow dynamically upon request, without any reallocation.
*
* The grow operation involves an allocation of a new array segment, without reallocating
* the already used memory, and then not increasing the heap fragmentation.
* This also implies that the address of the stored elements never change.
*
* Allocated segments grow in size exponentially.
*/
#ifndef __TOMMYARRAY_H
#define __TOMMYARRAY_H
#include "tommytypes.h"
#include <assert.h> /* for assert */
/******************************************************************************/
/* array */
/**
* Initial and minimal size of the array expressed as a power of 2.
* The initial size is 2^TOMMY_ARRAY_BIT.
*/
#define TOMMY_ARRAY_BIT 6
/** \internal
* Max number of elements as a power of 2.
*/
#define TOMMY_ARRAY_BIT_MAX 32
/**
* Array container type.
* \note Don't use internal fields directly, but access the container only using functions.
*/
typedef struct tommy_array_struct {
void** bucket[TOMMY_ARRAY_BIT_MAX]; /**< Dynamic array of buckets. */
tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */
tommy_count_t bucket_max; /**< Number of buckets. */
tommy_count_t count; /**< Number of initialized elements in the array. */
} tommy_array;
/**
* Initializes the array.
*/
void tommy_array_init(tommy_array* array);
/**
* Deinitializes the array.
*/
void tommy_array_done(tommy_array* array);
/**
* Grows the size up to the specified value.
* All the new elements in the array are initialized with the 0 value.
*/
void tommy_array_grow(tommy_array* array, tommy_count_t size);
/**
* Gets a reference of the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_array_grow().
*/
tommy_inline void** tommy_array_ref(tommy_array* array, tommy_count_t pos)
{
tommy_uint_t bsr;
assert(pos < array->count);
/* get the highest bit set, in case of all 0, return 0 */
bsr = tommy_ilog2_u32(pos | 1);
return &array->bucket[bsr][pos];
}
/**
* Sets the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_array_grow().
*/
tommy_inline void tommy_array_set(tommy_array* array, tommy_count_t pos, void* element)
{
*tommy_array_ref(array, pos) = element;
}
/**
* Gets the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_array_grow().
*/
tommy_inline void* tommy_array_get(tommy_array* array, tommy_count_t pos)
{
return *tommy_array_ref(array, pos);
}
/**
* Grows and inserts a new element at the end of the array.
*/
tommy_inline void tommy_array_insert(tommy_array* array, void* element)
{
tommy_count_t pos = array->count;
tommy_array_grow(array, pos + 1);
tommy_array_set(array, pos, element);
}
/**
* Gets the initialized size of the array.
*/
tommy_inline tommy_count_t tommy_array_size(tommy_array* array)
{
return array->count;
}
/**
* Gets the size of allocated memory.
*/
tommy_size_t tommy_array_memory_usage(tommy_array* array);
#endif

83
tommyds/tommyarrayblkof.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2013, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "tommyarrayblkof.h"
/******************************************************************************/
/* array */
void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size)
{
tommy_array_init(&array->block);
array->element_size = element_size;
array->count = 0;
}
void tommy_arrayblkof_done(tommy_arrayblkof* array)
{
tommy_count_t i;
for (i = 0; i < tommy_array_size(&array->block); ++i)
tommy_free(tommy_array_get(&array->block, i));
tommy_array_done(&array->block);
}
void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_count_t count)
{
tommy_count_t block_max;
tommy_count_t block_mac;
if (array->count >= count)
return;
array->count = count;
block_max = (count + TOMMY_ARRAYBLKOF_SIZE - 1) / TOMMY_ARRAYBLKOF_SIZE;
block_mac = tommy_array_size(&array->block);
if (block_mac < block_max) {
/* grow the block array */
tommy_array_grow(&array->block, block_max);
/* allocate new blocks */
while (block_mac < block_max) {
void** ptr = tommy_cast(void**, tommy_calloc(TOMMY_ARRAYBLKOF_SIZE, array->element_size));
/* set the new block */
tommy_array_set(&array->block, block_mac, ptr);
++block_mac;
}
}
}
tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array)
{
return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLKOF_SIZE * array->element_size;
}

114
tommyds/tommyarrayblkof.h Normal file
View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2013, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Dynamic array based on blocks of fixed size.
*
* This array is able to grow dynamically upon request, without any reallocation.
*
* This is very similar at ::tommy_arrayblk, but it allows to store elements of any
* size and not just pointers.
*
* Note that in this case tommy_arrayblkof_ref() returns a pointer to the element,
* that should be used for getting and setting elements in the array,
* as generic getter and setter are not available.
*/
#ifndef __TOMMYARRAYBLKOF_H
#define __TOMMYARRAYBLKOF_H
#include "tommytypes.h"
#include "tommyarray.h"
#include <assert.h> /* for assert */
/******************************************************************************/
/* array */
/**
* Elements for each block.
*/
#define TOMMY_ARRAYBLKOF_SIZE (4 * 1024)
/**
* Array container type.
* \note Don't use internal fields directly, but access the container only using functions.
*/
typedef struct tommy_arrayblkof_struct {
tommy_array block; /**< Array of blocks. */
tommy_size_t element_size; /**< Size of the stored element in bytes. */
tommy_count_t count; /**< Number of initialized elements in the array. */
} tommy_arrayblkof;
/**
* Initializes the array.
* \param element_size Size in byte of the element to store in the array.
*/
void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size);
/**
* Deinitializes the array.
*/
void tommy_arrayblkof_done(tommy_arrayblkof* array);
/**
* Grows the size up to the specified value.
* All the new elements in the array are initialized with the 0 value.
*/
void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_count_t size);
/**
* Gets a reference of the element at the specified position.
* You must be sure that space for this position is already
* allocated calling tommy_arrayblkof_grow().
*/
tommy_inline void* tommy_arrayblkof_ref(tommy_arrayblkof* array, tommy_count_t pos)
{
unsigned char* base;
assert(pos < array->count);
base = tommy_cast(unsigned char*, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLKOF_SIZE));
return base + (pos % TOMMY_ARRAYBLKOF_SIZE) * array->element_size;
}
/**
* Gets the initialized size of the array.
*/
tommy_inline tommy_count_t tommy_arrayblkof_size(tommy_arrayblkof* array)
{
return array->count;
}
/**
* Gets the size of allocated memory.
*/
tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array);
#endif

224
tommyds/tommychain.h Normal file
View File

@@ -0,0 +1,224 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Chain of nodes.
* A chain of nodes is an abstraction used to implements complex list operations
* like sorting.
*
* Do not use this directly. Use lists instead.
*/
#ifndef __TOMMYCHAIN_H
#define __TOMMYCHAIN_H
#include "tommytypes.h"
/******************************************************************************/
/* chain */
/**
* Chain of nodes.
* A chain of nodes is a sequence of nodes with the following properties:
* - It contains at least one node. A chains of zero nodes cannot exist.
* - The next field of the tail is of *undefined* value.
* - The prev field of the head is of *undefined* value.
* - All the other inner prev and next fields are correctly set.
*/
typedef struct tommy_chain_struct {
tommy_node* head; /**< Pointer to the head of the chain. */
tommy_node* tail; /**< Pointer to the tail of the chain. */
} tommy_chain;
/**
* Splices a chain in the middle of another chain.
*/
tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail)
{
/* set the prev list */
first_after->prev = second_tail;
second_head->prev = first_before;
/* set the next list */
first_before->next = second_head;
second_tail->next = first_after;
}
/**
* Concats two chains.
*/
tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head)
{
/* set the prev list */
second_head->prev = first_tail;
/* set the next list */
first_tail->next = second_head;
}
/**
* Merges two chains.
*/
tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
{
tommy_node* first_i = first->head;
tommy_node* second_i = second->head;
/* merge */
while (1) {
if (cmp(first_i->data, second_i->data) > 0) {
tommy_node* next = second_i->next;
if (first_i == first->head) {
tommy_chain_concat(second_i, first_i);
first->head = second_i;
} else {
tommy_chain_splice(first_i->prev, first_i, second_i, second_i);
}
if (second_i == second->tail)
break;
second_i = next;
} else {
if (first_i == first->tail) {
tommy_chain_concat(first_i, second_i);
first->tail = second->tail;
break;
}
first_i = first_i->next;
}
}
}
/**
* Merges two chains managing special degenerated cases.
* It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains.
*/
tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
{
/* identify the condition first <= second */
if (cmp(first->tail->data, second->head->data) <= 0) {
tommy_chain_concat(first->tail, second->head);
first->tail = second->tail;
return;
}
/* identify the condition second < first */
/* here we must be strict on comparison to keep the sort stable */
if (cmp(second->tail->data, first->head->data) < 0) {
tommy_chain_concat(second->tail, first->head);
first->head = second->head;
return;
}
tommy_chain_merge(first, second, cmp);
}
/**
* Max number of elements as a power of 2.
*/
#define TOMMY_CHAIN_BIT_MAX 32
/**
* Sorts a chain.
* It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity,
* similar at the one used in the SGI STL libraries and in the Linux Kernel,
* but faster on degenerated cases like already ordered lists.
*
* SGI STL stl_list.h
* http://www.sgi.com/tech/stl/stl_list.h
*
* Linux Kernel lib/list_sort.c
* http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c
*/
tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp)
{
/*
* Bit buckets of chains.
* Each bucket contains 2^i nodes or it's empty.
* The chain at address TOMMY_CHAIN_BIT_MAX is an independet variable operating as "carry".
* We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck.
*/
tommy_chain bit[TOMMY_CHAIN_BIT_MAX + 1];
/**
* Value stored inside the bit bucket.
* It's used to know which bucket is empty of full.
*/
tommy_count_t counter;
tommy_node* node = chain->head;
tommy_node* tail = chain->tail;
tommy_count_t mask;
tommy_count_t i;
counter = 0;
while (1) {
tommy_node* next;
tommy_chain* last;
/* carry bit to add */
last = &bit[TOMMY_CHAIN_BIT_MAX];
bit[TOMMY_CHAIN_BIT_MAX].head = node;
bit[TOMMY_CHAIN_BIT_MAX].tail = node;
next = node->next;
/* add the bit, propagating the carry */
i = 0;
mask = counter;
while ((mask & 1) != 0) {
tommy_chain_merge_degenerated(&bit[i], last, cmp);
mask >>= 1;
last = &bit[i];
++i;
}
/* copy the carry in the first empty bit */
bit[i] = *last;
/* add the carry in the counter */
++counter;
if (node == tail)
break;
node = next;
}
/* merge the buckets */
i = tommy_ctz_u32(counter);
mask = counter >> i;
while (mask != 1) {
mask >>= 1;
if (mask & 1)
tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp);
else
bit[i + 1] = bit[i];
++i;
}
*chain = bit[i];
}
#endif

241
tommyds/tommyhash.c Normal file
View File

@@ -0,0 +1,241 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "tommyhash.h"
/******************************************************************************/
/* hash */
tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr)
{
/* allow unaligned read on Intel x86 and x86_64 platforms */
#if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64)
/* defines from http://predef.sourceforge.net/ */
return *(const tommy_uint32_t*)ptr;
#else
const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr);
return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24);
#endif
}
#define tommy_rot(x, k) \
(((x) << (k)) | ((x) >> (32 - (k))))
#define tommy_mix(a, b, c) \
do { \
a -= c; a ^= tommy_rot(c, 4); c += b; \
b -= a; b ^= tommy_rot(a, 6); a += c; \
c -= b; c ^= tommy_rot(b, 8); b += a; \
a -= c; a ^= tommy_rot(c, 16); c += b; \
b -= a; b ^= tommy_rot(a, 19); a += c; \
c -= b; c ^= tommy_rot(b, 4); b += a; \
} while (0)
#define tommy_final(a, b, c) \
do { \
c ^= b; c -= tommy_rot(b, 14); \
a ^= c; a -= tommy_rot(c, 11); \
b ^= a; b -= tommy_rot(a, 25); \
c ^= b; c -= tommy_rot(b, 16); \
a ^= c; a -= tommy_rot(c, 4); \
b ^= a; b -= tommy_rot(a, 14); \
c ^= b; c -= tommy_rot(b, 24); \
} while (0)
tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len)
{
const unsigned char* key = tommy_cast(const unsigned char*, void_key);
tommy_uint32_t a, b, c;
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val;
while (key_len > 12) {
a += tommy_le_uint32_read(key + 0);
b += tommy_le_uint32_read(key + 4);
c += tommy_le_uint32_read(key + 8);
tommy_mix(a, b, c);
key_len -= 12;
key += 12;
}
switch (key_len) {
case 0 :
return c; /* used only when called with a zero length */
case 12 :
c += tommy_le_uint32_read(key + 8);
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 11 : c += ((tommy_uint32_t)key[10]) << 16;
case 10 : c += ((tommy_uint32_t)key[9]) << 8;
case 9 : c += key[8];
case 8 :
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 7 : b += ((tommy_uint32_t)key[6]) << 16;
case 6 : b += ((tommy_uint32_t)key[5]) << 8;
case 5 : b += key[4];
case 4 :
a += tommy_le_uint32_read(key + 0);
break;
case 3 : a += ((tommy_uint32_t)key[2]) << 16;
case 2 : a += ((tommy_uint32_t)key[1]) << 8;
case 1 : a += key[0];
}
tommy_final(a, b, c);
return c;
}
tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len)
{
const unsigned char* key = tommy_cast(const unsigned char*, void_key);
tommy_uint32_t a, b, c;
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff);
c += init_val >> 32;
while (key_len > 12) {
a += tommy_le_uint32_read(key + 0);
b += tommy_le_uint32_read(key + 4);
c += tommy_le_uint32_read(key + 8);
tommy_mix(a, b, c);
key_len -= 12;
key += 12;
}
switch (key_len) {
case 0 :
return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */
case 12 :
c += tommy_le_uint32_read(key + 8);
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 11 : c += ((tommy_uint32_t)key[10]) << 16;
case 10 : c += ((tommy_uint32_t)key[9]) << 8;
case 9 : c += key[8];
case 8 :
b += tommy_le_uint32_read(key + 4);
a += tommy_le_uint32_read(key + 0);
break;
case 7 : b += ((tommy_uint32_t)key[6]) << 16;
case 6 : b += ((tommy_uint32_t)key[5]) << 8;
case 5 : b += key[4];
case 4 :
a += tommy_le_uint32_read(key + 0);
break;
case 3 : a += ((tommy_uint32_t)key[2]) << 16;
case 2 : a += ((tommy_uint32_t)key[1]) << 8;
case 1 : a += key[0];
}
tommy_final(a, b, c);
return c + ((tommy_uint64_t)b << 32);
}
tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key)
{
const unsigned char* key = tommy_cast(const unsigned char*, void_key);
tommy_uint32_t a, b, c;
tommy_uint32_t m[3] = { 0xff, 0xff00, 0xff0000 };
a = b = c = 0xdeadbeef + init_val;
/* this is different than original lookup3 and the result won't match */
while (1) {
tommy_uint32_t v = tommy_le_uint32_read(key);
if (tommy_haszero_u32(v)) {
if (v & m[0]) {
a += v & m[0];
if (v & m[1]) {
a += v & m[1];
if (v & m[2])
a += v & m[2];
}
}
break;
}
a += v;
v = tommy_le_uint32_read(key + 4);
if (tommy_haszero_u32(v)) {
if (v & m[0]) {
b += v & m[0];
if (v & m[1]) {
b += v & m[1];
if (v & m[2])
b += v & m[2];
}
}
break;
}
b += v;
v = tommy_le_uint32_read(key + 8);
if (tommy_haszero_u32(v)) {
if (v & m[0]) {
c += v & m[0];
if (v & m[1]) {
c += v & m[1];
if (v & m[2])
c += v & m[2];
}
}
break;
}
c += v;
tommy_mix(a, b, c);
key += 12;
}
/* for lengths that are multiplers of 12 we already have called mix */
/* this is different than the original lookup3 and the result won't match */
tommy_final(a, b, c);
return c;
}

140
tommyds/tommyhash.h Normal file
View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin.
*/
#ifndef __TOMMYHASH_H
#define __TOMMYHASH_H
#include "tommytypes.h"
/******************************************************************************/
/* hash */
/**
* Hash type used in hashtables.
*/
typedef tommy_key_t tommy_hash_t;
/**
* Hash function with a 32 bits result.
* Implementation of the Robert Jenkins "lookup3" hash 32 bits version,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle().
*
* This hash is designed to provide a good overall performance in all platforms,
* including 32 bits. If you target only 64 bits, you can use faster hashes,
* like SpookyHash or FarmHash.
*
* \param init_val Initialization value.
* Using a different initialization value, you can generate a completely different set of hash values.
* Use 0 if not relevant.
* \param void_key Pointer to the data to hash.
* \param key_len Size of the data to hash.
* \note
* This function is endianess independent.
* \return The hash value of 32 bits.
*/
tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len);
/**
* Hash function with a 64 bits result.
* Implementation of the Robert Jenkins "lookup3" hash 64 bits versions,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2().
*
* This hash is designed to provide a good overall performance in all platforms,
* including 32 bits. If you target only 64 bits, you can use faster hashes,
* like SpookyHash or FarmHash.
*
* \param init_val Initialization value.
* Using a different initialization value, you can generate a completely different set of hash values.
* Use 0 if not relevant.
* \param void_key Pointer to the data to hash.
* \param key_len Size of the data to hash.
* \note
* This function is endianess independent.
* \return The hash value of 64 bits.
*/
tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len);
/**
* String hash function with a 32 bits result.
* Implementation is based on the the Robert Jenkins "lookup3" hash 32 bits version,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle().
*
* This hash is designed to handle strings with an unknown length. If you
* know the string length, the other hash functions are surely faster.
*
* \param init_val Initialization value.
* Using a different initialization value, you can generate a completely different set of hash values.
* Use 0 if not relevant.
* \param void_key Pointer to the string to hash. It has to be 0 terminated.
* \note
* This function is endianess independent.
* \return The hash value of 32 bits.
*/
tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key);
/**
* Integer reversible hash function for 32 bits.
* Implementation of the Robert Jenkins "4-byte Integer Hashing",
* from http://burtleburtle.net/bob/hash/integer.html
*/
tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key)
{
key -= key << 6;
key ^= key >> 17;
key -= key << 9;
key ^= key << 4;
key -= key << 3;
key ^= key << 10;
key ^= key >> 15;
return key;
}
/**
* Integer reversible hash function for 64 bits.
* Implementation of the Thomas Wang "Integer Hash Function",
* from http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
*/
tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key)
{
key = ~key + (key << 21);
key = key ^ (key >> 24);
key = key + (key << 3) + (key << 8);
key = key ^ (key >> 14);
key = key + (key << 2) + (key << 4);
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
#endif

224
tommyds/tommyhashdyn.c Normal file
View File

@@ -0,0 +1,224 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "tommyhashdyn.h"
#include "tommylist.h"
/******************************************************************************/
/* hashdyn */
void tommy_hashdyn_init(tommy_hashdyn* hashdyn)
{
/* fixed initial size */
hashdyn->bucket_bit = TOMMY_HASHDYN_BIT;
hashdyn->bucket_max = 1 << hashdyn->bucket_bit;
hashdyn->bucket_mask = hashdyn->bucket_max - 1;
hashdyn->bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(hashdyn->bucket_max, sizeof(tommy_hashdyn_node*)));
hashdyn->count = 0;
}
void tommy_hashdyn_done(tommy_hashdyn* hashdyn)
{
tommy_free(hashdyn->bucket);
}
/**
* Resize the bucket vector.
*/
static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_count_t new_bucket_bit)
{
tommy_count_t bucket_bit;
tommy_count_t bucket_max;
tommy_count_t new_bucket_max;
tommy_count_t new_bucket_mask;
tommy_hashdyn_node** new_bucket;
bucket_bit = hashdyn->bucket_bit;
bucket_max = hashdyn->bucket_max;
new_bucket_max = 1 << new_bucket_bit;
new_bucket_mask = new_bucket_max - 1;
/* allocate the new vector using malloc() and not calloc() */
/* because data is fully initialized in the update process */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
/* reinsert all the elements */
if (new_bucket_bit > bucket_bit) {
tommy_count_t i;
/* grow */
for (i = 0; i < bucket_max; ++i) {
tommy_hashdyn_node* j;
/* setup the new two buckets */
new_bucket[i] = 0;
new_bucket[i + bucket_max] = 0;
/* reinsert the bucket */
j = hashdyn->bucket[i];
while (j) {
tommy_hashdyn_node* j_next = j->next;
tommy_count_t pos = j->key & new_bucket_mask;
if (new_bucket[pos])
tommy_list_insert_tail_not_empty(new_bucket[pos], j);
else
tommy_list_insert_first(&new_bucket[pos], j);
j = j_next;
}
}
} else {
tommy_count_t i;
/* shrink */
for (i = 0; i < new_bucket_max; ++i) {
/* setup the new bucket with the lower bucket*/
new_bucket[i] = hashdyn->bucket[i];
/* concat the upper bucket */
tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]);
}
}
tommy_free(hashdyn->bucket);
/* setup */
hashdyn->bucket_bit = new_bucket_bit;
hashdyn->bucket_max = new_bucket_max;
hashdyn->bucket_mask = new_bucket_mask;
hashdyn->bucket = new_bucket;
}
/**
* Grow.
*/
tommy_inline void hashdyn_grow_step(tommy_hashdyn* hashdyn)
{
/* grow if more than 50% full */
if (hashdyn->count >= hashdyn->bucket_max / 2)
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1);
}
/**
* Shrink.
*/
tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn)
{
/* shrink if less than 12.5% full */
if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT)
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1);
}
void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash)
{
tommy_count_t pos = hash & hashdyn->bucket_mask;
tommy_list_insert_tail(&hashdyn->bucket[pos], node, data);
node->key = hash;
++hashdyn->count;
hashdyn_grow_step(hashdyn);
}
void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node)
{
tommy_count_t pos = node->key & hashdyn->bucket_mask;
tommy_list_remove_existing(&hashdyn->bucket[pos], node);
--hashdyn->count;
hashdyn_shrink_step(hashdyn);
return node->data;
}
void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash)
{
tommy_count_t pos = hash & hashdyn->bucket_mask;
tommy_hashdyn_node* node = hashdyn->bucket[pos];
while (node) {
/* we first check if the hash matches, as in the same bucket we may have multiples hash values */
if (node->key == hash && cmp(cmp_arg, node->data) == 0) {
tommy_list_remove_existing(&hashdyn->bucket[pos], node);
--hashdyn->count;
hashdyn_shrink_step(hashdyn);
return node->data;
}
node = node->next;
}
return 0;
}
void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func)
{
tommy_count_t bucket_max = hashdyn->bucket_max;
tommy_hashdyn_node** bucket = hashdyn->bucket;
tommy_count_t pos;
for (pos = 0; pos < bucket_max; ++pos) {
tommy_hashdyn_node* node = bucket[pos];
while (node) {
void* data = node->data;
node = node->next;
func(data);
}
}
}
void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg)
{
tommy_count_t bucket_max = hashdyn->bucket_max;
tommy_hashdyn_node** bucket = hashdyn->bucket;
tommy_count_t pos;
for (pos = 0; pos < bucket_max; ++pos) {
tommy_hashdyn_node* node = bucket[pos];
while (node) {
void* data = node->data;
node = node->next;
func(arg, data);
}
}
}
tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn)
{
return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0])
+ tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node);
}

296
tommyds/tommyhashdyn.h Normal file
View File

@@ -0,0 +1,296 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Dynamic chained hashtable.
*
* This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles
* the size then it reaches a load factor greater than 0.5 and it halves the size with a load
* factor lower than 0.125.
*
* All the elements are reallocated in a single resize operation done inside
* tommy_hashdyn_insert() or tommy_hashdyn_remove().
*
* Note that the resize operation takes approximatively 100 [ms] with 1 million of elements,
* and 1 [second] with 10 millions. This could be a problem in real-time applications.
*
* The resize also fragment the heap, as it involves allocating a double-sized table, copy elements,
* and deallocating the older table. Leaving a big hole in the heap.
*
* The ::tommy_hashlin hashtable fixes both problems.
*
* To initialize the hashtable you have to call tommy_hashdyn_init().
*
* \code
* tommy_hashslin hashdyn;
*
* tommy_hashdyn_init(&hashdyn);
* \endcode
*
* To insert elements in the hashtable you have to call tommy_hashdyn_insert() for
* each element.
* In the insertion call you have to specify the address of the node, the
* address of the object, and the hash value of the key to use.
* The address of the object is used to initialize the tommy_node::data field
* of the node, and the hash to initialize the tommy_node::key field.
*
* \code
* struct object {
* int value;
* // other fields
* tommy_node node;
* };
*
* struct object* obj = malloc(sizeof(struct object)); // creates the object
*
* obj->value = ...; // initializes the object
*
* tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object
* \endcode
*
* To find and element in the hashtable you have to call tommy_hashtable_search()
* providing a comparison function, its argument, and the hash of the key to search.
*
* \code
* int compare(const void* arg, const void* obj)
* {
* return *(const int*)arg != ((const struct object*)obj)->value;
* }
*
* int value_to_find = 1;
* struct object* obj = tommy_hashdyn_search(&hashdyn, compare, &value_to_find, tommy_inthash_u32(value_to_find));
* if (!obj) {
* // not found
* } else {
* // found
* }
* \endcode
*
* To iterate over all the elements in the hashtable with the same key, you have to
* use tommy_hashdyn_bucket() and follow the tommy_node::next pointer until NULL.
* You have also to check explicitely for the key, as the bucket may contains
* different keys.
*
* \code
* int value_to_find = 1;
* tommy_node* i = tommy_hashdyn_bucket(&hashdyn, tommy_inthash_u32(value_to_find));
* while (i) {
* struct object* obj = i->data; // gets the object pointer
*
* if (obj->value == value_to_find) {
* printf("%d\n", obj->value); // process the object
* }
*
* i = i->next; // goes to the next element
* }
* \endcode
*
* To remove an element from the hashtable you have to call tommy_hashdyn_remove()
* providing a comparison function, its argument, and the hash of the key to search
* and remove.
*
* \code
* struct object* obj = tommy_hashdyn_remove(&hashdyn, compare, &value_to_remove, tommy_inthash_u32(value_to_remove));
* if (obj) {
* free(obj); // frees the object allocated memory
* }
* \endcode
*
* To destroy the hashtable you have to remove all the elements, and deinitialize
* the hashtable calling tommy_hashdyn_done().
*
* \code
* tommy_hashdyn_done(&hashdyn);
* \endcode
*
* If you need to iterate over all the elements in the hashtable, you can use
* tommy_hashdyn_foreach() or tommy_hashdyn_foreach_arg().
* If you need a more precise control with a real iteration, you have to insert
* all the elements also in a ::tommy_list, and use the list to iterate.
* See the \ref multiindex example for more detail.
*/
#ifndef __TOMMYHASHDYN_H
#define __TOMMYHASHDYN_H
#include "tommyhash.h"
/******************************************************************************/
/* hashdyn */
/** \internal
* Initial and minimal size of the hashtable expressed as a power of 2.
* The initial size is 2^TOMMY_HASHDYN_BIT.
*/
#define TOMMY_HASHDYN_BIT 4
/**
* Hashtable node.
* This is the node that you have to include inside your objects.
*/
typedef tommy_node tommy_hashdyn_node;
/**
* Hashtable container type.
* \note Don't use internal fields directly, but access the container only using functions.
*/
typedef struct tommy_hashdyn_struct {
tommy_hashdyn_node** bucket; /**< Hash buckets. One list for each hash modulus. */
tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */
tommy_count_t bucket_max; /**< Number of buckets. */
tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */
tommy_count_t count; /**< Number of elements. */
} tommy_hashdyn;
/**
* Initializes the hashtable.
*/
void tommy_hashdyn_init(tommy_hashdyn* hashdyn);
/**
* Deinitializes the hashtable.
*
* You can call this function with elements still contained,
* but such elements are not going to be freed by this call.
*/
void tommy_hashdyn_done(tommy_hashdyn* hashdyn);
/**
* Inserts an element in the hashtable.
*/
void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash);
/**
* Searches and removes an element from the hashtable.
* You have to provide a compare function and the hash of the element you want to remove.
* If the element is not found, 0 is returned.
* If more equal elements are present, the first one is removed.
* \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one.
* The function should return 0 for equal elements, anything other for different elements.
* \param cmp_arg Compare argument passed as first argument of the compare function.
* \param hash Hash of the element to find and remove.
* \return The removed element, or 0 if not found.
*/
void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash);
/**
* Gets the bucket of the specified hash.
* The bucket is guaranteed to contain ALL the elements with the specified hash,
* but it can contain also others.
* You can access elements in the bucket following the ::next pointer until 0.
* \param hash Hash of the element to find.
* \return The head of the bucket, or 0 if empty.
*/
tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, tommy_hash_t hash)
{
return hashdyn->bucket[hash & hashdyn->bucket_mask];
}
/**
* Searches an element in the hashtable.
* You have to provide a compare function and the hash of the element you want to find.
* If more equal elements are present, the first one is returned.
* \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one.
* The function should return 0 for equal elements, anything other for different elements.
* \param cmp_arg Compare argument passed as first argument of the compare function.
* \param hash Hash of the element to find.
* \return The first element found, or 0 if none.
*/
tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash)
{
tommy_hashdyn_node* i = tommy_hashdyn_bucket(hashdyn, hash);
while (i) {
/* we first check if the hash matches, as in the same bucket we may have multiples hash values */
if (i->key == hash && cmp(cmp_arg, i->data) == 0)
return i->data;
i = i->next;
}
return 0;
}
/**
* Removes an element from the hashtable.
* You must already have the address of the element to remove.
* \return The tommy_node::data field of the node removed.
*/
void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node);
/**
* Calls the specified function for each element in the hashtable.
*
* You cannot add or remove elements from the inside of the callback,
* but can use it to deallocate them.
*
* \code
* tommy_hashdyn hashdyn;
*
* // initializes the hashtable
* tommy_hashdyn_init(&hashdyn);
*
* ...
*
* // creates an object
* struct object* obj = malloc(sizeof(struct object));
*
* ...
*
* // insert it in the hashtable
* tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value));
*
* ...
*
* // deallocates all the objects iterating the hashtable
* tommy_hashdyn_foreach(&hashdyn, free);
*
* // deallocates the hashtable
* tommy_hashdyn_done(&hashdyn);
* \endcode
*/
void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func);
/**
* Calls the specified function with an argument for each element in the hashtable.
*/
void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg);
/**
* Gets the number of elements.
*/
tommy_inline tommy_count_t tommy_hashdyn_count(tommy_hashdyn* hashdyn)
{
return hashdyn->count;
}
/**
* Gets the size of allocated memory.
* It includes the size of the ::tommy_hashdyn_node of the stored elements.
*/
tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn);
#endif

60
tommyds/tommylist.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "tommylist.h"
#include "tommychain.h"
/** \internal
* Setup a list.
*/
tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail)
{
head->prev = tail;
tail->next = 0;
*list = head;
}
void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp)
{
tommy_chain chain;
tommy_node* head;
if (tommy_list_empty(list))
return;
head = tommy_list_head(list);
/* create a chain from the list */
chain.head = head;
chain.tail = head->prev;
tommy_chain_mergesort(&chain, cmp);
/* restore the list */
tommy_list_set(list, chain.head, chain.tail);
}

399
tommyds/tommylist.h Normal file
View File

@@ -0,0 +1,399 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Double linked list for collisions into hashtables.
*
* This list is a double linked list mainly targetted for handling collisions
* into an hashtables, but useable also as a generic list.
*
* The main feature of this list is to require only one pointer to represent the
* list, compared to a classic implementation requiring a head an a tail pointers.
* This reduces the memory usage in hashtables.
*
* Another feature is to support the insertion at the end of the list. This allow to store
* collisions in a stable order. Where for stable order we mean that equal elements keep
* their insertion order.
*
* To initialize the list, you have to call tommy_list_init(), or to simply assign
* to it NULL, as an empty list is represented by the NULL value.
*
* \code
* tommy_list list;
*
* tommy_list_init(&list); // initializes the list
* \endcode
*
* To insert elements in the list you have to call tommy_list_insert_tail()
* or tommy_list_insert_head() for each element.
* In the insertion call you have to specify the address of the node and the
* address of the object.
* The address of the object is used to initialize the tommy_node::data field
* of the node.
*
* \code
* struct object {
* int value;
* // other fields
* tommy_node node;
* };
*
* struct object* obj = malloc(sizeof(struct object)); // creates the object
*
* obj->value = ...; // initializes the object
*
* tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object
* \endcode
*
* To iterate over all the elements in the list you have to call
* tommy_list_head() to get the head of the list and follow the
* tommy_node::next pointer until NULL.
*
* \code
* tommy_node* i = tommy_list_head(&list);
* while (i) {
* struct object* obj = i->data; // gets the object pointer
*
* printf("%d\n", obj->value); // process the object
*
* i = i->next; // go to the next element
* }
* \endcode
*
* To destroy the list you have to remove all the elements,
* as the list is completely inplace and it doesn't allocate memory.
* This can be done with the tommy_list_foreach() function.
*
* \code
* // deallocates all the objects iterating the list
* tommy_list_foreach(&list, free);
* \endcode
*/
#ifndef __TOMMYLIST_H
#define __TOMMYLIST_H
#include "tommytypes.h"
/******************************************************************************/
/* list */
/**
* Double linked list type.
*/
typedef tommy_node* tommy_list;
/**
* Initializes the list.
* The list is completely inplace, so it doesn't need to be deinitialized.
*/
tommy_inline void tommy_list_init(tommy_list* list)
{
*list = 0;
}
/**
* Gets the head of the list.
* \return The head node. For empty lists 0 is returned.
*/
tommy_inline tommy_node* tommy_list_head(tommy_list* list)
{
return *list;
}
/**
* Gets the tail of the list.
* \return The tail node. For empty lists 0 is returned.
*/
tommy_inline tommy_node* tommy_list_tail(tommy_list* list)
{
tommy_node* head = tommy_list_head(list);
if (!head)
return 0;
return head->prev;
}
/** \internal
* Creates a new list with a single element.
* \param list The list to initialize.
* \param node The node to insert.
*/
tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node)
{
/* one element "circular" prev list */
node->prev = node;
/* one element "0 terminated" next list */
node->next = 0;
*list = node;
}
/** \internal
* Inserts an element at the head of a not empty list.
* The element is inserted at the head of the list. The list cannot be empty.
* \param list The list. The list cannot be empty.
* \param node The node to insert.
*/
tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node)
{
tommy_node* head = tommy_list_head(list);
/* insert in the "circular" prev list */
node->prev = head->prev;
head->prev = node;
/* insert in the "0 terminated" next list */
node->next = head;
*list = node;
}
/** \internal
* Inserts an element at the tail of a not empty list.
* The element is inserted at the tail of the list. The list cannot be empty.
* \param head The node at the list head. It cannot be 0.
* \param node The node to insert.
*/
tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node)
{
/* insert in the "circular" prev list */
node->prev = head->prev;
head->prev = node;
/* insert in the "0 terminated" next list */
node->next = 0;
node->prev->next = node;
}
/**
* Inserts an element at the head of a list.
* \param node The node to insert.
* \param data The object containing the node. It's used to set the tommy_node::data field of the node.
*/
tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data)
{
tommy_node* head = tommy_list_head(list);
if (head)
tommy_list_insert_head_not_empty(list, node);
else
tommy_list_insert_first(list, node);
node->data = data;
}
/**
* Inserts an element at the tail of a list.
* \param node The node to insert.
* \param data The object containing the node. It's used to set the tommy_node::data field of the node.
*/
tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data)
{
tommy_node* head = tommy_list_head(list);
if (head)
tommy_list_insert_tail_not_empty(head, node);
else
tommy_list_insert_first(list, node);
node->data = data;
}
/** \internal
* Removes an element from the head of a not empty list.
* \param list The list. The list cannot be empty.
* \return The node removed.
*/
tommy_inline tommy_node* tommy_list_remove_head_not_empty(tommy_list* list)
{
tommy_node* head = tommy_list_head(list);
/* remove from the "circular" prev list */
head->next->prev = head->prev;
/* remove from the "0 terminated" next list */
*list = head->next; /* the new head, in case 0 */
return head;
}
/**
* Removes an element from the list.
* You must already have the address of the element to remove.
* \note The node content is left unchanged, including the tommy_node::next
* and tommy_node::prev fields that still contain pointers at the list.
* \param node The node to remove. The node must be in the list.
* \return The tommy_node::data field of the node removed.
*/
tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node)
{
tommy_node* head = tommy_list_head(list);
/* remove from the "circular" prev list */
if (node->next)
node->next->prev = node->prev;
else
head->prev = node->prev; /* the last */
/* remove from the "0 terminated" next list */
if (head == node)
*list = node->next; /* the new head, in case 0 */
else
node->prev->next = node->next;
return node->data;
}
/**
* Concats two lists.
* The second list is concatenated at the first list.
* \param first The first list.
* \param second The second list. After this call the list content is undefined,
* and you should not use it anymore.
*/
tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second)
{
tommy_node* first_head;
tommy_node* first_tail;
tommy_node* second_head;
/* if the second is empty, nothing to do */
second_head = tommy_list_head(second);
if (second_head == 0)
return;
/* if the first is empty, copy the second */
first_head = tommy_list_head(first);
if (first_head == 0) {
*first = *second;
return;
}
/* tail of the first list */
first_tail = first_head->prev;
/* set the "circular" prev list */
first_head->prev = second_head->prev;
second_head->prev = first_tail;
/* set the "0 terminated" next list */
first_tail->next = second_head;
}
/**
* Sorts a list.
* It's a stable merge sort with O(N*log(N)) worst complexity.
* It's faster on degenerated cases like partially ordered lists.
* \param cmp Compare function called with two elements.
* The function should return <0 if the first element is less than the second, ==0 if equal, and >0 if greather.
*/
void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp);
/**
* Checks if empty.
* \return If the list is empty.
*/
tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list)
{
return tommy_list_head(list) == 0;
}
/**
* Gets the number of elements.
* \note This operation is O(n).
*/
tommy_inline tommy_count_t tommy_list_count(tommy_list* list)
{
tommy_count_t count = 0;
tommy_node* i = tommy_list_head(list);
while (i) {
++count;
i = i->next;
}
return count;
}
/**
* Calls the specified function for each element in the list.
*
* You cannot add or remove elements from the inside of the callback,
* but can use it to deallocate them.
*
* \code
* tommy_list list;
*
* // initializes the list
* tommy_list_init(&list);
*
* ...
*
* // creates an object
* struct object* obj = malloc(sizeof(struct object));
*
* ...
*
* // insert it in the list
* tommy_list_insert_tail(&list, &obj->node, obj);
*
* ...
*
* // deallocates all the objects iterating the list
* tommy_list_foreach(&list, free);
* \endcode
*/
tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func)
{
tommy_node* node = tommy_list_head(list);
while (node) {
void* data = node->data;
node = node->next;
func(data);
}
}
/**
* Calls the specified function with an argument for each element in the list.
*/
tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg)
{
tommy_node* node = tommy_list_head(list);
while (node) {
void* data = node->data;
node = node->next;
func(arg, data);
}
}
#endif

292
tommyds/tommytree.c Normal file
View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 2015, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "tommytree.h"
#include <assert.h> /* for assert */
/******************************************************************************/
/* tree */
void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp)
{
tree->root = 0;
tree->count = 0;
tree->cmp = cmp;
}
static int tommy_tree_delta(tommy_tree_node* root)
{
int left_height = root->prev ? root->prev->key : 0;
int right_height = root->next ? root->next->key : 0;
return left_height - right_height;
}
/* AVL tree operations */
static tommy_tree_node* tommy_tree_balance(tommy_tree_node*);
static tommy_tree_node* tommy_tree_rotate_left(tommy_tree_node* root)
{
tommy_tree_node* next = root->next;
root->next = next->prev;
next->prev = tommy_tree_balance(root);
return tommy_tree_balance(next);
}
static tommy_tree_node* tommy_tree_rotate_right(tommy_tree_node* root)
{
tommy_tree_node* prev = root->prev;
root->prev = prev->next;
prev->next = tommy_tree_balance(root);
return tommy_tree_balance(prev);
}
static tommy_tree_node* tommy_tree_move_right(tommy_tree_node* root, tommy_tree_node* node)
{
if (!root)
return node;
root->next = tommy_tree_move_right(root->next, node);
return tommy_tree_balance(root);
}
static tommy_tree_node* tommy_tree_balance(tommy_tree_node* root)
{
int delta = tommy_tree_delta(root);
if (delta < -1) {
if (tommy_tree_delta(root->next) > 0)
root->next = tommy_tree_rotate_right(root->next);
return tommy_tree_rotate_left(root);
}
if (delta > 1) {
if (tommy_tree_delta(root->prev) < 0)
root->prev = tommy_tree_rotate_left(root->prev);
return tommy_tree_rotate_right(root);
}
/* recompute key */
root->key = 0;
if (root->prev && root->prev->key > root->key)
root->key = root->prev->key;
if (root->next && root->next->key > root->key)
root->key = root->next->key;
/* count itself */
root->key += 1;
return root;
}
static tommy_tree_node* tommy_tree_insert_node(tommy_compare_func* cmp, tommy_tree_node* root, tommy_tree_node** let)
{
int c;
if (!root)
return *let;
c = cmp((*let)->data, root->data);
if (c < 0) {
root->prev = tommy_tree_insert_node(cmp, root->prev, let);
return tommy_tree_balance(root);
}
if (c > 0) {
root->next = tommy_tree_insert_node(cmp, root->next, let);
return tommy_tree_balance(root);
}
/* already present, set the return pointer */
*let = root;
return root;
}
void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data)
{
tommy_tree_node* insert = node;
insert->data = data;
insert->prev = 0;
insert->next = 0;
insert->key = 0;
tree->root = tommy_tree_insert_node(tree->cmp, tree->root, &insert);
if (insert == node)
++tree->count;
return insert->data;
}
static tommy_tree_node* tommy_tree_remove_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data, tommy_tree_node** let)
{
int c;
if (!root)
return 0;
c = cmp(data, root->data);
if (c < 0) {
root->prev = tommy_tree_remove_node(cmp, root->prev, data, let);
return tommy_tree_balance(root);
}
if (c > 0) {
root->next = tommy_tree_remove_node(cmp, root->next, data, let);
return tommy_tree_balance(root);
}
/* found */
*let = root;
return tommy_tree_move_right(root->prev, root->next);
}
void* tommy_tree_remove(tommy_tree* tree, void* data)
{
tommy_tree_node* node = 0;
tree->root = tommy_tree_remove_node(tree->cmp, tree->root, data, &node);
if (!node)
return 0;
--tree->count;
return node->data;
}
static tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* root, void* data)
{
int c;
if (!root)
return 0;
c = cmp(data, root->data);
if (c < 0)
return tommy_tree_search_node(cmp, root->prev, data);
if (c > 0)
return tommy_tree_search_node(cmp, root->next, data);
return root;
}
void* tommy_tree_search(tommy_tree* tree, void* data)
{
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
if (!node)
return 0;
return node->data;
}
void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
{
tommy_tree_node* node = tommy_tree_search_node(cmp, tree->root, cmp_arg);
if (!node)
return 0;
return node->data;
}
void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node)
{
void* data = tommy_tree_remove(tree, node->data);
assert(data != 0);
return data;
}
static void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func)
{
tommy_tree_node* next;
if (!root)
return;
tommy_tree_foreach_node(root->prev, func);
/* make a copy in case func is free() */
next = root->next;
func(root->data);
tommy_tree_foreach_node(next, func);
}
void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func)
{
tommy_tree_foreach_node(tree->root, func);
}
static void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg)
{
tommy_tree_node* next;
if (!root)
return;
tommy_tree_foreach_arg_node(root->prev, func, arg);
/* make a copy in case func is free() */
next = root->next;
func(arg, root->data);
tommy_tree_foreach_arg_node(next, func, arg);
}
void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg)
{
tommy_tree_foreach_arg_node(tree->root, func, arg);
}
tommy_size_t tommy_tree_memory_usage(tommy_tree* tree)
{
return tommy_tree_count(tree) * sizeof(tommy_tree_node);
}

228
tommyds/tommytree.h Normal file
View File

@@ -0,0 +1,228 @@
/*
* Copyright (c) 2015, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* AVL tree.
*
* This tree is a standard AVL tree implementation that stores elements in the
* order defined by the comparison function.
*
* As difference than other tommy containers, duplicate elements cannot be inserted.
*
* To initialize a tree you have to call tommy_tree_init() specifing a comparison
* function that will define the order in the tree.
*
* \code
* tommy_tree tree;
*
* tommy_tree_init(&tree, cmp);
* \endcode
*
* To insert elements in the tree you have to call tommy_tree_insert() for
* each element.
* In the insertion call you have to specify the address of the node and the
* address of the object.
* The address of the object is used to initialize the tommy_node::data field
* of the node.
*
* \code
* struct object {
* int value;
* // other fields
* tommy_tree_node node;
* };
*
* struct object* obj = malloc(sizeof(struct object)); // creates the object
*
* obj->value = ...; // initializes the object
*
* tommy_tree_insert(&tree, &obj->node, obj); // inserts the object
* \endcode
*
* To find and element in the tree you have to call tommy_tree_search() providing
* the key to search.
*
* \code
* struct object value_to_find = { 1 };
* struct object* obj = tommy_tree_search(&tree, &value_to_find);
* if (!obj) {
* // not found
* } else {
* // found
* }
* \endcode
*
* To remove an element from the tree you have to call tommy_tree_remove()
* providing the key to search and remove.
*
* \code
* struct object value_to_remove = { 1 };
* struct object* obj = tommy_tree_remove(&tree, &value_to_remove);
* if (obj) {
* free(obj); // frees the object allocated memory
* }
* \endcode
*
* To destroy the tree you have to remove or destroy all the contained elements.
* The tree itself doesn't have or need a deallocation function.
*
* If you need to iterate over all the elements in the tree, you can use
* tommy_tree_foreach() or tommy_tree_foreach_arg().
* If you need a more precise control with a real iteration, you have to insert
* all the elements also in a ::tommy_list, and use the list to iterate.
* See the \ref multiindex example for more detail.
*/
#ifndef __TOMMYTREE_H
#define __TOMMYTREE_H
#include "tommytypes.h"
/******************************************************************************/
/* tree */
/**
* Tree node.
* This is the node that you have to include inside your objects.
*/
typedef tommy_node tommy_tree_node;
/**
* Tree container type.
* \note Don't use internal fields directly, but access the container only using functions.
*/
typedef struct tommy_tree_struct {
tommy_tree_node* root; /**< Root node. */
tommy_count_t count; /**< Number of elements. */
tommy_compare_func* cmp; /**< Comparison function. */
} tommy_tree;
/**
* Initializes the tree.
* \param cmp The comparison function that defines the orderin the tree.
*/
void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp);
/**
* Inserts an element in the tree.
* If the element is already present, it's not inserted again.
* Check the return value to identify if the element was already present or not.
* You have to provide the pointer of the node embedded into the object and
* the pointer to the object.
* \param node Pointer to the node embedded into the object to insert.
* \param data Pointer to the object to insert.
* \return The element in the tree. Either the already existing one, or the one just inserted.
*/
void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data);
/**
* Searches and removes an element.
* If the element is not found, 0 is returned.
* \param data Element used for comparison.
* \return The removed element, or 0 if not found.
*/
void* tommy_tree_remove(tommy_tree* tree, void* data);
/**
* Searches an element in the tree.
* If the element is not found, 0 is returned.
* \param data Element used for comparison.
* \return The first element found, or 0 if none.
*/
void* tommy_tree_search(tommy_tree* tree, void* data);
/**
* Searches an element in the tree with a specific comparison function.
*
* Like tommy_tree_search() but you can specify a different comparison function.
* Note that this function must define a suborder of the original one.
*
* The ::data argument will be the first argument of the comparison function,
* and it can be of a different type of the objects in the tree.
*/
void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg);
/**
* Removes an element from the tree.
* You must already have the address of the element to remove.
* \return The tommy_node::data field of the node removed.
*/
void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node);
/**
* Calls the specified function for each element in the tree.
*
* The elements are processed in order.
*
* You cannot add or remove elements from the inside of the callback,
* but can use it to deallocate them.
*
* \code
* tommy_tree tree;
*
* // initializes the tree
* tommy_tree_init(&tree, cmp);
*
* ...
*
* // creates an object
* struct object* obj = malloc(sizeof(struct object));
*
* ...
*
* // insert it in the tree
* tommy_tree_insert(&tree, &obj->node, obj);
*
* ...
*
* // deallocates all the objects iterating the tree
* tommy_tree_foreach(&tree, free);
* \endcode
*/
void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func);
/**
* Calls the specified function with an argument for each element in the tree.
*/
void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg);
/**
* Gets the number of elements.
*/
tommy_inline tommy_count_t tommy_tree_count(tommy_tree* tree)
{
return tree->count;
}
/**
* Gets the size of allocated memory.
* It includes the size of the ::tommy_tree_node of the stored elements.
*/
tommy_size_t tommy_tree_memory_usage(tommy_tree* tree);
#endif

424
tommyds/tommytypes.h Normal file
View File

@@ -0,0 +1,424 @@
/*
* Copyright (c) 2010, Andrea Mazzoleni. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
* Generic types.
*/
#ifndef __TOMMYTYPES_H
#define __TOMMYTYPES_H
/******************************************************************************/
/* types */
#include <stddef.h>
#if defined(_MSC_VER)
typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */
typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */
typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */
#else
#include <stdint.h>
typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */
typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */
typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */
#endif
typedef size_t tommy_size_t; /**< Generic size_t type. */
typedef ptrdiff_t tommy_ptrdiff_t; /**< Generic ptrdiff_t type. */
typedef int tommy_bool_t; /**< Generic boolean type. */
/**
* Generic unsigned integer type.
*
* It has no specific size, as is used to store only small values.
* To make the code more efficient, a full 32 bit integer is used.
*/
typedef tommy_uint32_t tommy_uint_t;
/**
* Generic unsigned integer for counting objects.
*
* TommyDS doesn't support more than 2^32-1 objects.
*/
typedef tommy_uint32_t tommy_count_t;
/** \internal
* Type cast required for the C++ compilation.
* When compiling in C++ we cannot convert a void* pointer to another pointer.
* In such case we need an explicit cast.
*/
#ifdef __cplusplus
#define tommy_cast(type, value) static_cast<type>(value)
#else
#define tommy_cast(type, value) (value)
#endif
/******************************************************************************/
/* heap */
/* by default uses malloc/calloc/realloc/free */
/**
* Generic malloc(), calloc(), realloc() and free() functions.
* Redefine them to what you need. By default they map to the C malloc(), calloc(), realloc() and free().
*/
#if !defined(tommy_malloc) || !defined(tommy_calloc) || !defined(tommy_realloc) || !defined(tommy_free)
#include <stdlib.h>
#endif
#if !defined(tommy_malloc)
#define tommy_malloc malloc
#endif
#if !defined(tommy_calloc)
#define tommy_calloc calloc
#endif
#if !defined(tommy_realloc)
#define tommy_realloc realloc
#endif
#if !defined(tommy_free)
#define tommy_free free
#endif
/******************************************************************************/
/* modificators */
/** \internal
* Definition of the inline keyword if available.
*/
#if !defined(tommy_inline)
#if defined(_MSC_VER) || defined(__GNUC__)
#define tommy_inline static __inline
#else
#define tommy_inline static
#endif
#endif
/** \internal
* Definition of the restrict keyword if available.
*/
#if !defined(tommy_restrict)
#if __STDC_VERSION__ >= 199901L
#define tommy_restrict restrict
#elif defined(_MSC_VER) || defined(__GNUC__)
#define tommy_restrict __restrict
#else
#define tommy_restrict
#endif
#endif
/** \internal
* Hints the compiler that a condition is likely true.
*/
#if !defined(tommy_likely)
#if defined(__GNUC__)
#define tommy_likely(x) __builtin_expect(!!(x), 1)
#else
#define tommy_likely(x) (x)
#endif
#endif
/** \internal
* Hints the compiler that a condition is likely false.
*/
#if !defined(tommy_unlikely)
#if defined(__GNUC__)
#define tommy_unlikely(x) __builtin_expect(!!(x), 0)
#else
#define tommy_unlikely(x) (x)
#endif
#endif
/******************************************************************************/
/* key */
/**
* Key type used in indexed data structures to store the key or the hash value.
*/
typedef tommy_uint32_t tommy_key_t;
/**
* Bits into the ::tommy_key_t type.
*/
#define TOMMY_KEY_BIT (sizeof(tommy_key_t) * 8)
/******************************************************************************/
/* node */
/**
* Data structure node.
* This node type is shared between all the data structures and used to store some
* info directly into the objects you want to store.
*
* A typical declaration is:
* \code
* struct object {
* tommy_node node;
* // other fields
* };
* \endcode
*/
typedef struct tommy_node_struct {
/**
* Next node.
* The tail node has it at 0, like a 0 terminated list.
*/
struct tommy_node_struct* next;
/**
* Previous node.
* The head node points to the tail node, like a circular list.
*/
struct tommy_node_struct* prev;
/**
* Pointer to the object containing the node.
* This field is initialized when inserting nodes into a data structure.
*/
void* data;
/**
* Key used to store the node.
* With hashtables this field is used to store the hash value.
* With lists this field is not used.
*/
tommy_key_t key;
} tommy_node;
/******************************************************************************/
/* compare */
/**
* Compare function for elements.
* \param obj_a Pointer to the first object to compare.
* \param obj_b Pointer to the second object to compare.
* \return <0 if the first element is less than the second, ==0 equal, >0 if greather.
*
* This function is like the C strcmp().
*
* \code
* struct object {
* tommy_node node;
* int value;
* };
*
* int compare(const void* obj_a, const void* obj_b)
* {
* if (((const struct object*)obj_a)->value < ((const struct object*)obj_b)->value)
* return -1;
* if (((const struct object*)obj_a)->value > ((const struct object*)obj_b)->value)
* return 1;
* return 0;
* }
*
* tommy_list_sort(&list, compare);
* \endcode
*
*/
typedef int tommy_compare_func(const void* obj_a, const void* obj_b);
/**
* Search function for elements.
* \param arg Pointer to the value to search as passed at the search function.
* \param obj Pointer to the object to compare to.
* \return ==0 if the value matches the element. !=0 if different.
*
* The first argument is a pointer to the value to search exactly
* as it's passed at the search function called.
* The second argument is a pointer to the object inside the hashtable to compare.
*
* The return value has to be 0 if the values are equal. != 0 if they are different.
*
* \code
* struct object {
* tommy_node node;
* int value;
* };
*
* int compare(const void* arg, const void* obj)
* {
* const int* value_to_find = arg;
* const struct object* object_to_compare = obj;
*
* return *value_to_find != object_to_compare->value;
* }
*
* int value_to_find = 1;
* struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find));
* if (!obj) {
* // not found
* } else {
* // found
* }
* \endcode
*
*/
typedef int tommy_search_func(const void* arg, const void* obj);
/**
* Foreach function.
* \param obj Pointer to the object to iterate.
*
* A typical example is to use free() to deallocate all the objects in a list.
* \code
* tommy_list_foreach(&list, (tommy_foreach_func*)free);
* \endcode
*/
typedef void tommy_foreach_func(void* obj);
/**
* Foreach function with an argument.
* \param arg Pointer to a generic argument.
* \param obj Pointer to the object to iterate.
*/
typedef void tommy_foreach_arg_func(void* arg, void* obj);
/******************************************************************************/
/* bit hacks */
#if defined(_MSC_VER) && !defined(__cplusplus)
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanForward)
#endif
/** \internal
* Integer log2 for constants.
* You can use it only for exact power of 2 up to 256.
*/
#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 : (value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0)
/**
* Bit scan reverse or integer log2.
* Return the bit index of the most significant 1 bit.
*
* If no bit is set, the result is undefined.
* To force a return 0 in this case, you can use tommy_ilog2_u32(value | 1).
*
* Other interesting ways for bitscan are at:
*
* Bit Twiddling Hacks
* http://graphics.stanford.edu/~seander/bithacks.html
*
* Chess Programming BitScan
* http://chessprogramming.wikispaces.com/BitScan
*
* \param value Value to scan. 0 is not allowed.
* \return The index of the most significant bit set.
*/
tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value)
{
#if defined(_MSC_VER)
unsigned long count;
_BitScanReverse(&count, value);
return count;
#elif defined(__GNUC__)
/*
* GCC implements __builtin_clz(x) as "__builtin_clz(x) = bsr(x) ^ 31"
*
* Where "x ^ 31 = 31 - x", but gcc does not optimize "31 - __builtin_clz(x)" to bsr(x),
* but generates 31 - (bsr(x) xor 31).
*
* So we write "__builtin_clz(x) ^ 31" instead of "31 - __builtin_clz(x)",
* to allow the double xor to be optimized out.
*/
return __builtin_clz(value) ^ 31;
#else
/* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */
/* from http://graphics.stanford.edu/~seander/bithacks.html */
static unsigned char TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = {
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27];
#endif
}
/**
* Bit scan forward or trailing zero count.
* Return the bit index of the least significant 1 bit.
*
* If no bit is set, the result is undefined.
* \param value Value to scan. 0 is not allowed.
* \return The index of the least significant bit set.
*/
tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value)
{
#if defined(_MSC_VER)
unsigned long count;
_BitScanForward(&count, value);
return count;
#elif defined(__GNUC__)
return __builtin_ctz(value);
#else
/* Count the consecutive zero bits (trailing) on the right with multiply and lookup */
/* from http://graphics.stanford.edu/~seander/bithacks.html */
static const unsigned char TOMMY_DE_BRUIJN_INDEX_CTZ[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & - value) * 0x077CB531U)) >> 27];
#endif
}
/**
* Rounds up to the next power of 2.
* For the value 0, the result is undefined.
* \return The smallest power of 2 not less than the specified value.
*/
tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
{
/* Round up to the next highest power of 2 */
/* from http://graphics.stanford.edu/~seander/bithacks.html */
--value;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
++value;
return value;
}
/**
* Check if the specified word has a byte at 0.
* \return 0 or 1.
*/
tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
{
return ((value - 0x01010101) & ~value & 0x80808080) != 0;
}
#endif