Imported Upstream version 11.2
This commit is contained in:
24
tommyds/LICENSE
Normal file
24
tommyds/LICENSE
Normal 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
42
tommyds/tommy.c
Normal 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
83
tommyds/tommyarray.c
Normal 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
151
tommyds/tommyarray.h
Normal 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
83
tommyds/tommyarrayblkof.c
Normal 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
114
tommyds/tommyarrayblkof.h
Normal 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
224
tommyds/tommychain.h
Normal 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
241
tommyds/tommyhash.c
Normal 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
140
tommyds/tommyhash.h
Normal 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
224
tommyds/tommyhashdyn.c
Normal 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
296
tommyds/tommyhashdyn.h
Normal 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
60
tommyds/tommylist.c
Normal 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
399
tommyds/tommylist.h
Normal 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
292
tommyds/tommytree.c
Normal 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
228
tommyds/tommytree.h
Normal 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
424
tommyds/tommytypes.h
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user