Update to TommyDS v4.0

This commit is contained in:
Andrea Mazzoleni 2026-10-07 18:44:12 +02:00
parent a39fb132f8
commit 2dc349fe93
19 changed files with 1669 additions and 627 deletions

View file

@ -1604,8 +1604,8 @@ void fs_allocate(struct snapraid_disk* disk, block_off_t parity_pos, struct snap
extent = extent_alloc(parity_pos, file, file_pos, count);
/* insert the extent in the trees */
parity_extent = tommy_tree_insert(&disk->fs_parity, &extent->parity_node, extent);
file_extent = tommy_tree_insert(&disk->fs_file, &extent->file_node, extent);
parity_extent = tommy_tree_insert_unique(&disk->fs_parity, &extent->parity_node, extent);
file_extent = tommy_tree_insert_unique(&disk->fs_file, &extent->file_node, extent);
if (parity_extent != extent || file_extent != extent) {
/* LCOV_EXCL_START */
@ -1689,8 +1689,8 @@ void fs_deallocate(struct snapraid_disk* disk, block_off_t parity_pos)
second_extent = extent_alloc(extent->parity_pos + first_count + 1, extent->file, extent->file_pos + first_count + 1, second_count);
/* insert the extent in the trees */
parity_extent = tommy_tree_insert(&disk->fs_parity, &second_extent->parity_node, second_extent);
file_extent = tommy_tree_insert(&disk->fs_file, &second_extent->file_node, second_extent);
parity_extent = tommy_tree_insert_unique(&disk->fs_parity, &second_extent->parity_node, second_extent);
file_extent = tommy_tree_insert_unique(&disk->fs_file, &second_extent->file_node, second_extent);
if (parity_extent != second_extent || file_extent != second_extent) {
/* LCOV_EXCL_START */

View file

@ -119,6 +119,23 @@ void* malloc_nofail(size_t size)
return ptr;
}
void* realloc_nofail(void* ptr, size_t size)
{
/* keep zero-size requests allocated, as in malloc_nofail() */
void* new_ptr = realloc(ptr, size ? size : 1);
if (!new_ptr) {
/* LCOV_EXCL_START */
malloc_fail(size);
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
malloc_counter_inc(size);
return new_ptr;
}
void* nalloc_nofail(size_t count, size_t size)
{
if (size != 0 && count > SIZE_MAX / size) {

View file

@ -23,6 +23,12 @@ void memory_single_thread(int single_thread);
*/
void* malloc_nofail(size_t size);
/**
* Safe realloc.
* If no memory is available, it aborts.
*/
void* realloc_nofail(void* ptr, size_t size);
/**
* Safe malloc with calloc calling arguments.
* If no memory is available, it aborts.

View file

@ -51,25 +51,25 @@ struct hash_test_vector {
* Test vectors for tommy_hash32
*/
static struct hash32_test_vector TEST_HASH32[] = {
{ "", 0, 0x8614384c, 0xa766795d },
{ "a", 1, 0x12c16c36, 0xa766795d },
{ "abc", 3, 0xc58e8af5, 0xa766795d },
{ "message digest", 14, 0x006b32f1, 0xa766795d },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x7e6fcfe0, 0xa766795d },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x8604adf8, 0xa766795d },
{ "The quick brown fox jumps over the lazy dog", 43, 0xdeba3d3a, 0xa766795d },
{ "\x00", 1, 0x4a7d1c33, 0xa766795d },
{ "\x16\x27", 2, 0x8b50899b, 0xa766795d },
{ "\xe2\x56\xb4", 3, 0x60406493, 0xa766795d },
{ "\xc9\x4d\x9c\xda", 4, 0xa049144a, 0xa766795d },
{ "\x79\xf1\x29\x69\x5d", 5, 0x4da2c2f1, 0xa766795d },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0x59de30cf, 0xa766795d },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x219e149c, 0xa766795d },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0x25067520, 0xa766795d },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xa1f368d8, 0xa766795d },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0x805fc63d, 0xa766795d },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0x7f75dd0f, 0xa766795d },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0xb9154382, 0xa766795d },
{ "", 0, 0x3ba63d24, 0xa766795d },
{ "a", 1, 0xc27589e2, 0xa766795d },
{ "abc", 3, 0x4f0589dd, 0xa766795d },
{ "message digest", 14, 0x7bd2e191, 0xa766795d },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0xa1eebba8, 0xa766795d },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0xc84d4c51, 0xa766795d },
{ "The quick brown fox jumps over the lazy dog", 43, 0x294e8a7b, 0xa766795d },
{ "\x00", 1, 0x3e1fbed8, 0xa766795d },
{ "\x16\x27", 2, 0x00233fc6, 0xa766795d },
{ "\xe2\x56\xb4", 3, 0xd3f61ec1, 0xa766795d },
{ "\xc9\x4d\x9c\xda", 4, 0xfa10da80, 0xa766795d },
{ "\x79\xf1\x29\x69\x5d", 5, 0xcd5e7468, 0xa766795d },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0x8ba76f9b, 0xa766795d },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0xbf2129d0, 0xa766795d },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0xc3d26370, 0xa766795d },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xa6840538, 0xa766795d },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0x36115feb, 0xa766795d },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0x48a3b1fb, 0xa766795d },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0x23412f54, 0xa766795d },
{ 0, 0, 0, 0 }
};
@ -77,29 +77,29 @@ static struct hash32_test_vector TEST_HASH32[] = {
* Test vectors for tommy_strhash32
*/
struct strhash32_test_vector TEST_STRHASH32[] = {
{ "", 0x0af1416d, 0xa766795d },
{ "a", 0x68fa0f3f, 0xa766795d },
{ "abc", 0xfc68ffc5, 0xa766795d },
{ "message digest", 0x08477b63, 0xa766795d },
{ "abcdefghijklmnopqrstuvwxyz", 0x5b9c25e5, 0xa766795d },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x1e530ce7, 0xa766795d },
{ "The quick brown fox jumps over the lazy dog", 0xaf93eefe, 0xa766795d },
{ "\xff", 0xfc88801b, 0xa766795d },
{ "\x16\x27", 0xcd7216db, 0xa766795d },
{ "\xe2\x56\xb4", 0x05f98d02, 0xa766795d },
{ "\xc9\x4d\x9c\xda", 0xf65206f8, 0xa766795d },
{ "\x79\xf1\x29\x69\x5d", 0x72bd6bda, 0xa766795d },
{ "\xff\x7e\xdf\x1e\x31\x1c", 0x57dfb9b4, 0xa766795d },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 0x499ff634, 0xa766795d },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 0xe896b7ce, 0xa766795d },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 0xfe3939f0, 0xa766795d },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 0x4351d482, 0xa766795d },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\xff\xb7\xae", 0x88e92135, 0xa766795d },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 0x01109c16, 0xa766795d },
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 0xbcb050dc, 0xa766795d },
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 0xbe5e1fd5, 0xa766795d },
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 0x70d8c97f, 0xa766795d },
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 0x957440a9, 0xa766795d },
{ "", 0x3ba63d24, 0xa766795d },
{ "a", 0xc27589e2, 0xa766795d },
{ "abc", 0x4f0589dd, 0xa766795d },
{ "message digest", 0x7bd2e191, 0xa766795d },
{ "abcdefghijklmnopqrstuvwxyz", 0xa1eebba8, 0xa766795d },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0xc84d4c51, 0xa766795d },
{ "The quick brown fox jumps over the lazy dog", 0x294e8a7b, 0xa766795d },
{ "\xff", 0x6ae84401, 0xa766795d },
{ "\x16\x27", 0x00233fc6, 0xa766795d },
{ "\xe2\x56\xb4", 0xd3f61ec1, 0xa766795d },
{ "\xc9\x4d\x9c\xda", 0xfa10da80, 0xa766795d },
{ "\x79\xf1\x29\x69\x5d", 0xcd5e7468, 0xa766795d },
{ "\xff\x7e\xdf\x1e\x31\x1c", 0xe135b2e2, 0xa766795d },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 0xbf2129d0, 0xa766795d },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 0xc3d26370, 0xa766795d },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 0xa6840538, 0xa766795d },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 0x36115feb, 0xa766795d },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\xff\xb7\xae", 0xef961a79, 0xa766795d },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 0x23412f54, 0xa766795d },
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 0x04332eac, 0xa766795d },
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 0xd3025b16, 0xa766795d },
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 0x4ce44257, 0xa766795d },
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 0x47d86f33, 0xa766795d },
{ 0, 0, 0 }
};
@ -107,25 +107,25 @@ struct strhash32_test_vector TEST_STRHASH32[] = {
* Test vectors for tommy_hash64
*/
static struct hash64_test_vector TEST_HASH64[] = {
{ "", 0, 0x8614384cb5165fbfULL, 0x2f022773a766795dULL },
{ "a", 1, 0x1a2e0298a8e94a3dULL, 0x2f022773a766795dULL },
{ "abc", 3, 0x7555796b7a7d21ebULL, 0x2f022773a766795dULL },
{ "message digest", 14, 0x9411a57d04b92fb4ULL, 0x2f022773a766795dULL },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x3ca3f8d2b4e69832ULL, 0x2f022773a766795dULL },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x6dae542ba0015a4dULL, 0x2f022773a766795dULL },
{ "The quick brown fox jumps over the lazy dog", 43, 0xe06d8cbb3d2ea1a6ULL, 0x2f022773a766795dULL },
{ "\x00", 1, 0x201e664fb5f2c021ULL, 0x2f022773a766795dULL },
{ "\x16\x27", 2, 0xef42fa8032c4b775ULL, 0x2f022773a766795dULL },
{ "\xe2\x56\xb4", 3, 0x6e6c498a6688466cULL, 0x2f022773a766795dULL },
{ "\xc9\x4d\x9c\xda", 4, 0x5195005419905423ULL, 0x2f022773a766795dULL },
{ "\x79\xf1\x29\x69\x5d", 5, 0x221235b48afee7c1ULL, 0x2f022773a766795dULL },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0x1b1f18b9266f095bULL, 0x2f022773a766795dULL },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x2cbafa8e741d49caULL, 0x2f022773a766795dULL },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0x4677f04c06e0758dULL, 0x2f022773a766795dULL },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0x5afe09e8214e2163ULL, 0x2f022773a766795dULL },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0x115b6276d209fab6ULL, 0x2f022773a766795dULL },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0xd0636d2f01cf3a3eULL, 0x2f022773a766795dULL },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0x6d259f5fef74f93eULL, 0x2f022773a766795dULL },
{ "", 0, 0xf1999eea212f89e6ULL, 0x2f022773a766795dULL },
{ "a", 1, 0x6af55c57ef3cc6e2ULL, 0x2f022773a766795dULL },
{ "abc", 3, 0x8a09859573f3ba72ULL, 0x2f022773a766795dULL },
{ "message digest", 14, 0x08a48193f341bef5ULL, 0x2f022773a766795dULL },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x8761b9f83f99b36dULL, 0x2f022773a766795dULL },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x6f9a23bb4217ad12ULL, 0x2f022773a766795dULL },
{ "The quick brown fox jumps over the lazy dog", 43, 0x33e3651d073f9942ULL, 0x2f022773a766795dULL },
{ "\x00", 1, 0x9360785dad01a14eULL, 0x2f022773a766795dULL },
{ "\x16\x27", 2, 0x9d411c7d088f8c1eULL, 0x2f022773a766795dULL },
{ "\xe2\x56\xb4", 3, 0xa20865547db581afULL, 0x2f022773a766795dULL },
{ "\xc9\x4d\x9c\xda", 4, 0xb4eb664b81e7058dULL, 0x2f022773a766795dULL },
{ "\x79\xf1\x29\x69\x5d", 5, 0xf0b386ad052571b4ULL, 0x2f022773a766795dULL },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0xdd3b8851d859c7d4ULL, 0x2f022773a766795dULL },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0xa8b94da524aabf49ULL, 0x2f022773a766795dULL },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0xb913a803aea6d51dULL, 0x2f022773a766795dULL },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xb6961500a25f63beULL, 0x2f022773a766795dULL },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0xfa6dccb9650a1d28ULL, 0x2f022773a766795dULL },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0xc95d0a87ec8c2a98ULL, 0x2f022773a766795dULL },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0x15901950afd60cd2ULL, 0x2f022773a766795dULL },
{ 0, 0, 0, 0 }
};
@ -188,6 +188,13 @@ static void test_hash(void)
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
digest = tommy_hash_u32(TEST_STRHASH32[i].seed, buffer_aligned, strlen(TEST_STRHASH32[i].data));
if (digest != TEST_STRHASH32[i].digest) {
/* LCOV_EXCL_START */
log_fatal(EINTERNAL, "Failed strhash32 test\n");
exit(EXIT_FAILURE);
/* LCOV_EXCL_STOP */
}
}
for (i = 0; TEST_HASH64[i].data; ++i) {
@ -456,7 +463,7 @@ static void test_tommy(void)
tommy_arrayblkof_init(&arrayblkof, sizeof(unsigned));
for (i = 0; i < TOMMY_SIZE; ++i) {
tommy_array_insert(&array, &node[i]);
tommy_array_insert_tail(&array, &node[i]);
tommy_arrayblkof_grow(&arrayblkof, i + 1);
*(unsigned*)tommy_arrayblkof_ref(&arrayblkof, i) = i;
}
@ -584,7 +591,11 @@ static void test_tommy(void)
tommy_tree_insert(&tree, &node[i], (void*)(uintptr_t)(i + 1));
/* try to insert a duplicate, count should not change */
tommy_tree_insert(&tree, &node[TOMMY_SIZE], (void*)(uintptr_t)1);
if (tommy_tree_insert_unique(&tree, &node[TOMMY_SIZE], (void*)(uintptr_t)1) != (void*)(uintptr_t)1) {
/* LCOV_EXCL_START */
goto bail;
/* LCOV_EXCL_STOP */
}
if (tommy_tree_count(&tree) != TOMMY_SIZE) {
/* LCOV_EXCL_START */

View file

@ -4,10 +4,11 @@
/* redefine the malloc for tommy use */
#define tommy_malloc malloc_nofail
#define tommy_calloc calloc_nofail
#define tommy_realloc realloc_nofail
#define tommy_free free
#include "os/portable.h"
#include "cmdline/support.h" /* for malloc/calloc_nofail() */
#include "cmdline/support.h" /* for malloc/calloc/realloc_nofail() */
#include "tommyhash.c"
#include "tommyarray.c"

View file

@ -3,18 +3,18 @@
#include "tommyarray.h"
#include <string.h> /* for memset */
/******************************************************************************/
/* array */
TOMMY_API void tommy_array_init(tommy_array* array)
{
tommy_uint_t i;
/* fixed initial size */
array->bucket_bit = TOMMY_ARRAY_BIT;
array->bucket_max = (tommy_size_t)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)
for (tommy_uint_t i = 1; i < TOMMY_ARRAY_BIT; ++i)
array->bucket[i] = array->bucket[0];
array->count = 0;
@ -22,26 +22,18 @@ TOMMY_API void tommy_array_init(tommy_array* array)
TOMMY_API 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) {
for (tommy_uint_t i = TOMMY_ARRAY_BIT; i < array->bucket_bit; ++i) {
void** segment = array->bucket[i];
tommy_free(&segment[(tommy_ptrdiff_t)1 << i]);
}
}
TOMMY_API void tommy_array_grow(tommy_array* array, tommy_size_t count)
TOMMY_API void tommy_array_reserve(tommy_array* array, tommy_size_t size)
{
if (array->count >= count)
return;
array->count = count;
while (count > array->bucket_max) {
void** segment;
while (size > array->bucket_max) {
/* allocate one more segment */
segment = tommy_cast(void**, tommy_calloc(array->bucket_max, sizeof(void*)));
void** 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 */
@ -52,6 +44,77 @@ TOMMY_API void tommy_array_grow(tommy_array* array, tommy_size_t count)
}
}
TOMMY_API void tommy_array_resize(tommy_array* array, tommy_size_t size)
{
if (size >= array->count) {
tommy_array_grow(array, size);
return;
}
/* clear the unused elements to maintain the invariant that unused slots are zero */
tommy_size_t pos = size;
while (pos < array->count) {
tommy_uint_t bsr = tommy_ilog2(pos | 1);
tommy_size_t seg_end = (bsr < TOMMY_ARRAY_BIT) ? ((tommy_size_t)1 << TOMMY_ARRAY_BIT) : ((tommy_size_t)1 << (bsr + 1));
tommy_size_t chunk_end = array->count < seg_end ? array->count : seg_end;
memset(&array->bucket[bsr][pos], 0, (chunk_end - pos) * sizeof(void*));
pos = chunk_end;
}
array->count = size;
}
TOMMY_API void tommy_array_shrink(tommy_array* array)
{
tommy_uint_t target_bucket_bit = TOMMY_ARRAY_BIT;
if (array->count > (tommy_size_t)1 << TOMMY_ARRAY_BIT)
target_bucket_bit = tommy_ilog2(array->count - 1) + 1;
while (array->bucket_bit > target_bucket_bit) {
--array->bucket_bit;
void** segment = array->bucket[array->bucket_bit];
tommy_free(&segment[(tommy_ptrdiff_t)1 << array->bucket_bit]);
}
array->bucket_max = (tommy_size_t)1 << array->bucket_bit;
}
TOMMY_API void tommy_array_foreach(tommy_array* array, tommy_foreach_func* func)
{
tommy_size_t pos = 0;
while (pos < array->count) {
tommy_uint_t bsr = tommy_ilog2(pos | 1);
tommy_size_t seg_end = (bsr < TOMMY_ARRAY_BIT) ? ((tommy_size_t)1 << TOMMY_ARRAY_BIT) : ((tommy_size_t)1 << (bsr + 1));
tommy_size_t chunk_end = array->count < seg_end ? array->count : seg_end;
void** ptr = array->bucket[bsr];
while (pos < chunk_end) {
func(ptr[pos]);
++pos;
}
}
}
TOMMY_API void tommy_array_foreach_arg(tommy_array* array, tommy_foreach_arg_func* func, void* arg)
{
tommy_size_t pos = 0;
while (pos < array->count) {
tommy_uint_t bsr = tommy_ilog2(pos | 1);
tommy_size_t seg_end = (bsr < TOMMY_ARRAY_BIT) ? ((tommy_size_t)1 << TOMMY_ARRAY_BIT) : ((tommy_size_t)1 << (bsr + 1));
tommy_size_t chunk_end = array->count < seg_end ? array->count : seg_end;
void** ptr = array->bucket[bsr];
while (pos < chunk_end) {
func(arg, ptr[pos]);
++pos;
}
}
}
TOMMY_API tommy_size_t tommy_array_memory_usage(tommy_array* array)
{
return array->bucket_max * (tommy_size_t)sizeof(void*);

View file

@ -50,11 +50,49 @@ TOMMY_API void tommy_array_init(tommy_array* array);
*/
TOMMY_API void tommy_array_done(tommy_array* array);
/**
* Allocates space for at least the specified number of elements.
* The initialized size and existing elements are unchanged.
* Existing element references remain valid.
*/
TOMMY_API void tommy_array_reserve(tommy_array* array, tommy_size_t size);
/**
* Grows the size up to the specified value.
* All the new elements in the array are initialized with the 0 value.
*/
TOMMY_API void tommy_array_grow(tommy_array* array, tommy_size_t size);
tommy_inline void tommy_array_grow(tommy_array* array, tommy_size_t size)
{
if (size > array->count) {
array->count = size;
if (size > array->bucket_max)
tommy_array_reserve(array, size);
}
}
/**
* Changes the initialized size, preserving the common prefix.
* New elements are initialized to zero.
* Reducing the size does not release allocated capacity.
*/
TOMMY_API void tommy_array_resize(tommy_array* array, tommy_size_t size);
/**
* Removes all elements, preserving the allocated capacity.
* Pointed-to objects are not freed.
* The array remains initialized and can be reused immediately.
*/
tommy_inline void tommy_array_clear(tommy_array* array)
{
tommy_array_resize(array, 0);
}
/**
* Releases unused allocated memory.
* Preserves the size, values, and addresses of existing elements.
*/
TOMMY_API void tommy_array_shrink(tommy_array* array);
/**
* Gets a reference of the element at the specified position.
@ -63,12 +101,10 @@ TOMMY_API void tommy_array_grow(tommy_array* array, tommy_size_t size);
*/
tommy_inline void** tommy_array_ref(tommy_array* array, tommy_size_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(pos | 1);
tommy_uint_t bsr = tommy_ilog2(pos | 1);
return &array->bucket[bsr][pos];
}
@ -93,10 +129,20 @@ tommy_inline void* tommy_array_get(tommy_array* array, tommy_size_t pos)
return *tommy_array_ref(array, pos);
}
/**
* Gets the last element.
* The array must not be empty.
*/
tommy_inline void* tommy_array_tail(tommy_array* array)
{
assert(array->count != 0);
return tommy_array_get(array, array->count - 1);
}
/**
* Grows and inserts a new element at the end of the array.
*/
tommy_inline void tommy_array_insert(tommy_array* array, void* element)
tommy_inline void tommy_array_insert_tail(tommy_array* array, void* element)
{
tommy_size_t pos = array->count;
@ -105,6 +151,27 @@ tommy_inline void tommy_array_insert(tommy_array* array, void* element)
tommy_array_set(array, pos, element);
}
/**
* Removes and returns the last element.
* The array must not be empty.
* The removed slot is cleared, and allocated capacity is preserved.
*/
tommy_inline void* tommy_array_remove_tail(tommy_array* array)
{
assert(array->count != 0);
void* element = tommy_array_tail(array);
tommy_array_resize(array, array->count - 1);
return element;
}
/**
* Checks whether the array is empty.
*/
tommy_inline tommy_bool_t tommy_array_empty(tommy_array* array)
{
return array->count == 0;
}
/**
* Gets the initialized size of the array.
*/
@ -113,9 +180,50 @@ tommy_inline tommy_size_t tommy_array_size(tommy_array* array)
return array->count;
}
/**
* Gets the number of elements that fit without further allocation.
*/
tommy_inline tommy_size_t tommy_array_capacity(tommy_array* array)
{
return array->bucket_max;
}
/**
* Exchanges two initialized arrays without copying their elements.
* Element references remain valid and belong to the other array.
* Passing the same array twice has no effect.
*/
tommy_inline void tommy_array_swap(tommy_array* first, tommy_array* second)
{
tommy_array tmp = *first;
*first = *second;
*second = tmp;
}
/**
* Calls the specified function for each element in the array.
*
* You cannot add or remove elements, nor change the size of the array,
* from inside the callback.
*
* \param array Array to iterate.
* \param func Function to call with each element.
*/
TOMMY_API void tommy_array_foreach(tommy_array* array, tommy_foreach_func* func);
/**
* Calls the specified function with an argument for each element in the array.
*
* \param array Array to iterate.
* \param func Function to call with each element.
* \param arg Argument to pass to the function.
*/
TOMMY_API void tommy_array_foreach_arg(tommy_array* array, tommy_foreach_arg_func* func, void* arg);
/**
* Gets the size of allocated memory.
*/
TOMMY_API tommy_size_t tommy_array_memory_usage(tommy_array* array);
#endif

View file

@ -3,12 +3,16 @@
#include "tommyarrayblkof.h"
#include <string.h> /* for memset */
/******************************************************************************/
/* array */
TOMMY_API void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size)
{
tommy_array_init(&array->block);
array->block_capacity = (tommy_size_t)1 << TOMMY_ARRAYBLKOF_BIT;
array->block = tommy_cast(unsigned char**, tommy_malloc(array->block_capacity * sizeof(array->block[0])));
array->block_count = 0;
array->element_size = element_size;
array->count = 0;
@ -16,44 +20,123 @@ TOMMY_API void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t eleme
TOMMY_API void tommy_arrayblkof_done(tommy_arrayblkof* array)
{
tommy_size_t i;
for (tommy_size_t i = 0; i < array->block_count; ++i)
tommy_free(array->block[i]);
for (i = 0; i < tommy_array_size(&array->block); ++i)
tommy_free(tommy_array_get(&array->block, i));
tommy_array_done(&array->block);
tommy_free(array->block);
}
TOMMY_API void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_size_t count)
TOMMY_API void tommy_arrayblkof_reserve(tommy_arrayblkof* array, tommy_size_t size)
{
tommy_size_t block_max;
tommy_size_t block_mac;
if (array->count >= count)
if (size <= array->block_count * TOMMY_ARRAYBLKOF_SIZE)
return;
array->count = count;
block_max = (count + TOMMY_ARRAYBLKOF_SIZE - 1) / TOMMY_ARRAYBLKOF_SIZE;
block_mac = tommy_array_size(&array->block);
tommy_size_t block_max = (size - 1) / TOMMY_ARRAYBLKOF_SIZE + 1;
if (block_mac < block_max) {
/* grow the block array */
tommy_array_grow(&array->block, block_max);
if (array->block_capacity < block_max) {
/* only the directory moves; addresses inside existing blocks stay valid */
tommy_size_t capacity = array->block_capacity;
while (capacity < block_max)
capacity *= 2;
array->block = tommy_cast(unsigned char**, tommy_realloc(array->block, capacity * sizeof(array->block[0])));
array->block_capacity = capacity;
}
/* allocate new blocks */
while (block_mac < block_max) {
void** ptr = tommy_cast(void**, tommy_calloc(TOMMY_ARRAYBLKOF_SIZE, array->element_size));
/* allocate new blocks */
while (array->block_count < block_max) {
unsigned char* ptr = tommy_cast(unsigned char*, tommy_calloc(TOMMY_ARRAYBLKOF_SIZE, array->element_size));
/* set the new block */
tommy_array_set(&array->block, block_mac, ptr);
array->block[array->block_count] = ptr;
++array->block_count;
}
}
++block_mac;
TOMMY_API void tommy_arrayblkof_resize(tommy_arrayblkof* array, tommy_size_t size)
{
if (size >= array->count) {
tommy_arrayblkof_grow(array, size);
return;
}
/* clear the unused elements to maintain the invariant that unused slots are zero */
tommy_size_t pos = size;
while (pos < array->count) {
tommy_size_t blk_idx = pos / TOMMY_ARRAYBLKOF_SIZE;
tommy_size_t blk_offset = pos % TOMMY_ARRAYBLKOF_SIZE;
tommy_size_t blk_rem = TOMMY_ARRAYBLKOF_SIZE - blk_offset;
tommy_size_t count_rem = array->count - pos;
tommy_size_t chunk = blk_rem < count_rem ? blk_rem : count_rem;
memset(array->block[blk_idx] + blk_offset * array->element_size, 0, chunk * array->element_size);
pos += chunk;
}
array->count = size;
}
TOMMY_API void tommy_arrayblkof_shrink(tommy_arrayblkof* array)
{
tommy_size_t block_max = (array->count == 0) ? 0 : ((array->count - 1) / TOMMY_ARRAYBLKOF_SIZE + 1);
while (array->block_count > block_max) {
--array->block_count;
tommy_free(array->block[array->block_count]);
}
tommy_size_t min_capacity = (tommy_size_t)1 << TOMMY_ARRAYBLKOF_BIT;
tommy_size_t capacity = min_capacity;
while (capacity < block_max)
capacity *= 2;
if (capacity < array->block_capacity) {
array->block = tommy_cast(unsigned char**, tommy_realloc(array->block, capacity * sizeof(array->block[0])));
array->block_capacity = capacity;
}
}
TOMMY_API void tommy_arrayblkof_foreach(tommy_arrayblkof* array, tommy_foreach_func* func)
{
tommy_size_t pos = 0;
while (pos < array->count) {
tommy_size_t blk_idx = pos / TOMMY_ARRAYBLKOF_SIZE;
tommy_size_t chunk = array->count - pos;
unsigned char* ptr = array->block[blk_idx];
if (chunk > TOMMY_ARRAYBLKOF_SIZE)
chunk = TOMMY_ARRAYBLKOF_SIZE;
for (tommy_size_t i = 0; i < chunk; ++i) {
func(ptr);
ptr += array->element_size;
}
pos += chunk;
}
}
TOMMY_API void tommy_arrayblkof_foreach_arg(tommy_arrayblkof* array, tommy_foreach_arg_func* func, void* arg)
{
tommy_size_t pos = 0;
while (pos < array->count) {
tommy_size_t blk_idx = pos / TOMMY_ARRAYBLKOF_SIZE;
tommy_size_t chunk = array->count - pos;
unsigned char* ptr = array->block[blk_idx];
if (chunk > TOMMY_ARRAYBLKOF_SIZE)
chunk = TOMMY_ARRAYBLKOF_SIZE;
for (tommy_size_t i = 0; i < chunk; ++i) {
func(arg, ptr);
ptr += array->element_size;
}
pos += chunk;
}
}
TOMMY_API 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;
return array->block_capacity * sizeof(array->block[0]) + array->block_count * TOMMY_ARRAYBLKOF_SIZE * array->element_size;
}

View file

@ -4,27 +4,34 @@
/** \file
* Dynamic array based on blocks of fixed size.
*
* This array is able to grow dynamically upon request, without any reallocation.
* This array is able to grow dynamically without reallocating stored elements.
* The directory of block pointers may be reallocated, but element addresses
* never change.
*
* This is very similar to ::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.
* The container allocates space for elements but never copies user data into
* or out of it. tommy_arrayblkof_ref(), tommy_arrayblkof_tail(), and
* tommy_arrayblkof_insert_tail() return an element's address so the caller can
* read or write it directly.
*/
#ifndef __TOMMYARRAYBLKOF_H
#define __TOMMYARRAYBLKOF_H
#include "tommytypes.h"
#include "tommyarray.h"
#include <assert.h> /* for assert */
/******************************************************************************/
/* array */
/**
* Initial and minimal capacity of the block directory expressed as a power of 2.
*/
#define TOMMY_ARRAYBLKOF_BIT 6
/**
* Elements for each block.
*/
@ -35,7 +42,9 @@
* \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. */
unsigned char** block; /**< Directory of blocks. */
tommy_size_t block_count; /**< Number of allocated blocks. */
tommy_size_t block_capacity; /**< Number of directory entries allocated. */
tommy_size_t element_size; /**< Size of the stored element in bytes. */
tommy_size_t count; /**< Number of initialized elements in the array. */
} tommy_arrayblkof;
@ -51,11 +60,48 @@ TOMMY_API void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t eleme
*/
TOMMY_API void tommy_arrayblkof_done(tommy_arrayblkof* array);
/**
* Allocates space for at least the specified number of elements.
* The initialized size and existing elements are unchanged.
* Existing element references remain valid.
*/
TOMMY_API void tommy_arrayblkof_reserve(tommy_arrayblkof* array, tommy_size_t size);
/**
* Grows the size up to the specified value.
* All the new elements in the array are initialized with the 0 value.
*/
TOMMY_API void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_size_t size);
tommy_inline void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_size_t size)
{
if (size > array->count) {
array->count = size;
if (size > array->block_count * TOMMY_ARRAYBLKOF_SIZE)
tommy_arrayblkof_reserve(array, size);
}
}
/**
* Changes the initialized size, preserving the common prefix.
* New elements are initialized to zero.
* Reducing the size does not release allocated capacity.
*/
TOMMY_API void tommy_arrayblkof_resize(tommy_arrayblkof* array, tommy_size_t size);
/**
* Removes all elements, preserving the allocated capacity.
* The array remains initialized and can be reused immediately.
*/
tommy_inline void tommy_arrayblkof_clear(tommy_arrayblkof* array)
{
tommy_arrayblkof_resize(array, 0);
}
/**
* Releases unused allocated memory.
* Preserves the size, values, and addresses of existing elements.
*/
TOMMY_API void tommy_arrayblkof_shrink(tommy_arrayblkof* array);
/**
* Gets a reference of the element at the specified position.
@ -64,13 +110,47 @@ TOMMY_API void tommy_arrayblkof_grow(tommy_arrayblkof* array, tommy_size_t size)
*/
tommy_inline void* tommy_arrayblkof_ref(tommy_arrayblkof* array, tommy_size_t pos)
{
unsigned char* base;
assert(pos < array->count);
base = tommy_cast(unsigned char*, tommy_array_get(&array->block, pos / TOMMY_ARRAYBLKOF_SIZE));
return array->block[pos / TOMMY_ARRAYBLKOF_SIZE] + (pos % TOMMY_ARRAYBLKOF_SIZE) * array->element_size;
}
return base + (pos % TOMMY_ARRAYBLKOF_SIZE) * array->element_size;
/**
* Gets a reference to the last element.
* The array must not be empty.
*/
tommy_inline void* tommy_arrayblkof_tail(tommy_arrayblkof* array)
{
assert(array->count != 0);
return tommy_arrayblkof_ref(array, array->count - 1);
}
/**
* Adds a zero-initialized element and returns its reference.
*/
tommy_inline void* tommy_arrayblkof_insert_tail(tommy_arrayblkof* array)
{
tommy_size_t pos = array->count;
tommy_arrayblkof_grow(array, pos + 1);
return tommy_arrayblkof_ref(array, pos);
}
/**
* Removes the last element without copying it.
* The array must not be empty. The removed slot is cleared.
*/
tommy_inline void tommy_arrayblkof_remove_tail(tommy_arrayblkof* array)
{
assert(array->count != 0);
tommy_arrayblkof_resize(array, array->count - 1);
}
/**
* Checks whether the array is empty.
*/
tommy_inline tommy_bool_t tommy_arrayblkof_empty(tommy_arrayblkof* array)
{
return array->count == 0;
}
/**
@ -81,9 +161,51 @@ tommy_inline tommy_size_t tommy_arrayblkof_size(tommy_arrayblkof* array)
return array->count;
}
/**
* Gets the number of elements that fit without further allocation.
*/
tommy_inline tommy_size_t tommy_arrayblkof_capacity(tommy_arrayblkof* array)
{
return array->block_count * TOMMY_ARRAYBLKOF_SIZE;
}
/**
* Exchanges two initialized arrays without copying their elements.
* Element references remain valid and belong to the other array.
* Passing the same array twice has no effect.
*/
tommy_inline void tommy_arrayblkof_swap(tommy_arrayblkof* first, tommy_arrayblkof* second)
{
/* keep record size with the storage it describes */
tommy_arrayblkof tmp = *first;
*first = *second;
*second = tmp;
}
/**
* Calls the specified function for each element in the array.
*
* You cannot add or remove elements, nor change the size of the array,
* from inside the callback.
*
* \param array Array to iterate.
* \param func Function to call with each element.
*/
TOMMY_API void tommy_arrayblkof_foreach(tommy_arrayblkof* array, tommy_foreach_func* func);
/**
* Calls the specified function with an argument for each element in the array.
*
* \param array Array to iterate.
* \param func Function to call with each element.
* \param arg Argument to pass to the function.
*/
TOMMY_API void tommy_arrayblkof_foreach_arg(tommy_arrayblkof* array, tommy_foreach_arg_func* func, void* arg);
/**
* Gets the size of allocated memory.
*/
TOMMY_API tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array);
#endif

View file

@ -65,7 +65,8 @@ tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_
/**
* Merges two chains.
* \param first First chain (will contain the result).
* \param second Second chain (will be empty after the merge).
* \param second Second chain (consumed by the merge; its descriptor is not cleared
* and must not be used as an independent chain afterward).
* \param cmp Comparison function.
*/
tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
@ -101,7 +102,8 @@ tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tom
* Merges two chains managing special degenerated cases.
* It's functionally equivalent to tommy_chain_merge() but faster with already ordered chains.
* \param first First chain (will contain the result).
* \param second Second chain (will be empty after the merge).
* \param second Second chain (consumed by the merge; its descriptor is not cleared
* and must not be used as an independent chain afterward).
* \param cmp Comparison function.
*/
tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp)
@ -149,30 +151,24 @@ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func*
*/
tommy_chain bit[TOMMY_SIZE_BIT + 1];
/**
/*
* Value stored inside the bit bucket.
* It's used to know which bucket is empty or full.
*/
tommy_size_t counter;
tommy_size_t counter = 0;
tommy_node* node = chain->head;
tommy_node* tail = chain->tail;
tommy_size_t mask;
tommy_size_t i;
counter = 0;
while (1) {
tommy_node* next;
tommy_chain* last;
/* carry bit to add */
last = &bit[TOMMY_SIZE_BIT];
tommy_chain* last = &bit[TOMMY_SIZE_BIT];
bit[TOMMY_SIZE_BIT].head = node;
bit[TOMMY_SIZE_BIT].tail = node;
next = node->next;
tommy_node* next = node->next;
/* add the bit, propagating the carry */
i = 0;
mask = counter;
tommy_size_t i = 0;
tommy_size_t mask = counter;
while ((mask & 1) != 0) {
tommy_chain_merge_degenerated(&bit[i], last, cmp);
mask >>= 1;
@ -192,8 +188,8 @@ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func*
}
/* merge the buckets */
i = tommy_ctz(counter);
mask = counter >> i;
tommy_size_t i = tommy_ctz(counter);
tommy_size_t mask = counter >> i;
while (mask != 1) {
mask >>= 1;
if (mask & 1)
@ -207,3 +203,4 @@ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func*
}
#endif

View file

@ -6,7 +6,7 @@
/******************************************************************************/
/* hash */
#include <string.h> /* for memcpy */
#include <string.h> /* for memcpy, strlen */
/**
* Swap endianness.
@ -14,13 +14,26 @@
*/
#if defined(__GNUC__)
#define tommy_swap32(x) __builtin_bswap32(x)
#define tommy_swap64(x) __builtin_bswap64(x)
#else
tommy_inline tommy_uint32_t tommy_swap32(tommy_uint32_t v)
{
return ((v & 0xFF000000) >> 24) |
((v & 0x00FF0000) >> 8) |
((v & 0x0000FF00) << 8) |
((v & 0x000000FF) << 24);
((v & 0x00FF0000) >> 8) |
((v & 0x0000FF00) << 8) |
((v & 0x000000FF) << 24);
}
tommy_inline tommy_uint64_t tommy_swap64(tommy_uint64_t v)
{
return ((v & 0xFF00000000000000ULL) >> 56) |
((v & 0x00FF000000000000ULL) >> 40) |
((v & 0x0000FF0000000000ULL) >> 24) |
((v & 0x000000FF00000000ULL) >> 8) |
((v & 0x00000000FF000000ULL) << 8) |
((v & 0x0000000000FF0000ULL) << 24) |
((v & 0x000000000000FF00ULL) << 40) |
((v & 0x00000000000000FFULL) << 56);
}
#endif
@ -35,200 +48,278 @@ tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr)
return v;
}
tommy_inline tommy_uint64_t tommy_le_uint64_read(const void* ptr)
{
tommy_uint64_t v;
memcpy(&v, ptr, sizeof(v));
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
v = tommy_swap64(v);
#endif
return v;
}
#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)
#define tommy_rot64(x, k) \
(((x) << (k)) | ((x) >> (64 - (k))))
TOMMY_API 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;
const tommy_uint32_t c1 = 0xcc9e2d51;
const tommy_uint32_t c2 = 0x1b873593;
tommy_uint32_t h = init_val;
tommy_size_t nblocks = key_len / 4;
const unsigned char* tail = key + nblocks * 4;
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val;
while (nblocks >= 2) {
tommy_uint32_t k0 = tommy_le_uint32_read(key);
tommy_uint32_t k1 = tommy_le_uint32_read(key + 4);
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);
k0 *= c1;
k1 *= c1;
tommy_mix(a, b, c);
k0 = tommy_rot(k0, 15);
k1 = tommy_rot(k1, 15);
key_len -= 12;
key += 12;
k0 *= c2;
k1 *= c2;
h ^= k0;
h = tommy_rot(h, 13);
h = h * 5 + 0xe6546b64;
h ^= k1;
h = tommy_rot(h, 13);
h = h * 5 + 0xe6546b64;
key += 8;
nblocks -= 2;
}
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; /* fallthrough */
case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */
case 9 : c += key[8]; /* fallthrough */
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; /* fallthrough */
case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */
case 5 : b += key[4]; /* fallthrough */
case 4 :
a += tommy_le_uint32_read(key + 0);
break;
case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */
case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */
case 1 : a += key[0]; /* fallthrough */
if (nblocks) {
tommy_uint32_t k = tommy_le_uint32_read(key);
k *= c1;
k = tommy_rot(k, 15);
k *= c2;
h ^= k;
h = tommy_rot(h, 13);
h = h * 5 + 0xe6546b64;
}
tommy_final(a, b, c);
tommy_uint32_t k = 0;
switch (key_len & 3) {
case 3 : k ^= ((tommy_uint32_t)tail[2]) << 16; /* fallthrough */
case 2 : k ^= ((tommy_uint32_t)tail[1]) << 8; /* fallthrough */
case 1 : k ^= (tommy_uint32_t)tail[0];
k *= c1;
k = tommy_rot(k, 15);
k *= c2;
h ^= k;
}
return c;
h ^= (tommy_uint32_t)key_len;
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
TOMMY_API 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;
const tommy_uint64_t c1 = 0x87c37b91114253d5ULL;
const tommy_uint64_t c2 = 0x4cf5ad432745937fULL;
tommy_uint64_t h = init_val;
tommy_size_t nblocks = key_len / 8;
const unsigned char* tail = key + nblocks * 8;
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff);
c += init_val >> 32;
while (nblocks >= 2) {
tommy_uint64_t k0 = tommy_le_uint64_read(key);
tommy_uint64_t k1 = tommy_le_uint64_read(key + 8);
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);
k0 *= c1;
k1 *= c1;
tommy_mix(a, b, c);
k0 = tommy_rot64(k0, 31);
k1 = tommy_rot64(k1, 31);
key_len -= 12;
key += 12;
k0 *= c2;
k1 *= c2;
h ^= k0;
h = tommy_rot64(h, 27);
h = h * 5 + 0x52dce729ULL;
h ^= k1;
h = tommy_rot64(h, 27);
h = h * 5 + 0x52dce729ULL;
key += 16;
nblocks -= 2;
}
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; /* fallthrough */
case 10 : c += ((tommy_uint32_t)key[9]) << 8; /* fallthrough */
case 9 : c += key[8]; /* fallthrough */
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; /* fallthrough */
case 6 : b += ((tommy_uint32_t)key[5]) << 8; /* fallthrough */
case 5 : b += key[4]; /* fallthrough */
case 4 :
a += tommy_le_uint32_read(key + 0);
break;
case 3 : a += ((tommy_uint32_t)key[2]) << 16; /* fallthrough */
case 2 : a += ((tommy_uint32_t)key[1]) << 8; /* fallthrough */
case 1 : a += key[0]; /* fallthrough */
if (nblocks) {
tommy_uint64_t k = tommy_le_uint64_read(key);
k *= c1;
k = tommy_rot64(k, 31);
k *= c2;
h ^= k;
h = tommy_rot64(h, 27);
h = h * 5 + 0x52dce729ULL;
}
tommy_final(a, b, c);
tommy_uint64_t k = 0;
switch (key_len & 7) {
case 7 : k ^= ((tommy_uint64_t)tail[6]) << 48; /* fallthrough */
case 6 : k ^= ((tommy_uint64_t)tail[5]) << 40; /* fallthrough */
case 5 : k ^= ((tommy_uint64_t)tail[4]) << 32; /* fallthrough */
case 4 : k ^= ((tommy_uint64_t)tail[3]) << 24; /* fallthrough */
case 3 : k ^= ((tommy_uint64_t)tail[2]) << 16; /* fallthrough */
case 2 : k ^= ((tommy_uint64_t)tail[1]) << 8; /* fallthrough */
case 1 : k ^= (tommy_uint64_t)tail[0];
k *= c1;
k = tommy_rot64(k, 31);
k *= c2;
h ^= k;
}
return c + ((tommy_uint64_t)b << 32);
h ^= (tommy_uint64_t)key_len;
h ^= h >> 33;
h *= 0xff51afd7ed558ccdULL;
h ^= h >> 33;
h *= 0xc4ceb9fe1a85ec53ULL;
h ^= h >> 33;
return h;
}
TOMMY_API tommy_uint32_t tommy_strhash_u32(tommy_uint32_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 };
const unsigned char* p = tommy_cast(const unsigned char*, void_key);
const tommy_uint32_t c1 = 0xcc9e2d51;
const tommy_uint32_t c2 = 0x1b873593;
tommy_uint32_t h = init_val;
tommy_size_t len = 0;
a = b = c = 0xdeadbeef + init_val;
/* this is different than original lookup3 and the result won't match */
if (((tommy_uintptr_t)p & 3) != 0)
return tommy_hash_u32(init_val, void_key, strlen(tommy_cast(const char*, void_key)));
/*
* Fast path: string pointer is already aligned to 4 bytes.
* This assumes page-granularity protection and page boundaries aligned to
* 4 bytes, so each aligned load stays within a readable page.
* The final load may read up to 3 bytes past the terminator and outside
* the allocated object. Staying within the page avoids a page-boundary
* fault, but an out-of-object read is still undefined behavior in ISO C
* and can be reported by ASan. This path assumes the compiler and runtime
* tolerate these overreads.
*/
while (1) {
tommy_uint32_t v = tommy_le_uint32_read(key);
tommy_uint32_t v = tommy_le_uint32_read(p);
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];
}
}
tommy_uint32_t z = (v - 0x01010101) & ~v & 0x80808080;
tommy_uint_t zero_idx = tommy_ctz_u32(z) / 8;
if (zero_idx != 0) {
tommy_uint32_t mask = (1U << (zero_idx * 8)) - 1;
tommy_uint32_t k = v & mask;
k *= c1;
k = tommy_rot(k, 15);
k *= c2;
h ^= k;
len += zero_idx;
}
break;
}
a += v;
tommy_uint32_t k = v;
k *= c1;
k = tommy_rot(k, 15);
k *= c2;
h ^= k;
h = tommy_rot(h, 13);
h = h * 5 + 0xe6546b64;
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;
p += 4;
len += 4;
}
/* for lengths that are multipliers of 12 we already have called mix */
/* this is different than the original lookup3 and the result won't match */
h ^= len;
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
tommy_final(a, b, c);
return c;
return h;
}
TOMMY_API tommy_uint64_t tommy_strhash_u64(tommy_uint64_t init_val, const void* void_key)
{
const unsigned char* p = tommy_cast(const unsigned char*, void_key);
const tommy_uint64_t c1 = 0x87c37b91114253d5ULL;
const tommy_uint64_t c2 = 0x4cf5ad432745937fULL;
tommy_uint64_t h = init_val;
tommy_size_t len = 0;
if (((tommy_uintptr_t)p & 7) != 0)
return tommy_hash_u64(init_val, void_key, strlen(tommy_cast(const char*, void_key)));
/*
* Fast path: string pointer is already aligned to 8 bytes.
* This assumes page-granularity protection and page boundaries aligned to
* 8 bytes, so each aligned load stays within a readable page.
* The final load may read up to 7 bytes past the terminator and outside
* the allocated object. Staying within the page avoids a page-boundary
* fault, but an out-of-object read is still undefined behavior in ISO C
* and can be reported by ASan. This path assumes the compiler and runtime
* tolerate these overreads.
*/
while (1) {
tommy_uint64_t v = tommy_le_uint64_read(p);
if (tommy_haszero_u64(v)) {
tommy_uint64_t z = (v - 0x0101010101010101ULL) & ~v & 0x8080808080808080ULL;
tommy_uint_t zero_idx = tommy_ctz_u64(z) / 8;
if (zero_idx != 0) {
tommy_uint64_t mask = (1ULL << (zero_idx * 8)) - 1;
tommy_uint64_t k = v & mask;
k *= c1;
k = tommy_rot64(k, 31);
k *= c2;
h ^= k;
len += zero_idx;
}
break;
}
tommy_uint64_t k = v;
k *= c1;
k = tommy_rot64(k, 31);
k *= c2;
h ^= k;
h = tommy_rot64(h, 27);
h = h * 5 + 0x52dce729ULL;
p += 8;
len += 8;
}
h ^= len;
h ^= h >> 33;
h *= 0xff51afd7ed558ccdULL;
h ^= h >> 33;
h *= 0xc4ceb9fe1a85ec53ULL;
h ^= h >> 33;
return h;
}

View file

@ -15,12 +15,8 @@
/**
* 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 on all platforms,
* including 32 bits. If you target only 64 bits, you can use faster hashes,
* like SpookyHash or FarmHash.
* Implementation of MurmurHash3 (x86_32) by Austin Appleby,
* from https://github.com/aappleby/smhasher
*
* \param init_val Initialization value.
* Using a different initialization value, you can generate a completely different set of hash values.
@ -28,19 +24,19 @@
* \param void_key Pointer to the data to hash.
* \param key_len Size of the data to hash.
* \note
* This function is endianness independent.
* This function is endianness independent and alignment independent.
* It produces identical hash results to ::tommy_strhash_u32 for 0-terminated strings.
* Compatibility across different versions of the library is not guaranteed:
* this hash is intended for in-memory runtime use only and not for persistent storage.
* It is not state-of-the-art against malicious collision attacks, but it is simple and fast.
* \return The hash value of 32 bits.
*/
TOMMY_API 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 version,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2().
*
* This hash is designed to provide a good overall performance on all platforms,
* including 32 bits. If you target only 64 bits, you can use faster hashes,
* like SpookyHash or FarmHash.
* Implementation of MurmurHash3 (x64_64) by Austin Appleby,
* from https://github.com/aappleby/smhasher
*
* \param init_val Initialization value.
* Using a different initialization value, you can generate a completely different set of hash values.
@ -48,29 +44,57 @@ TOMMY_API tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* voi
* \param void_key Pointer to the data to hash.
* \param key_len Size of the data to hash.
* \note
* This function is endianness independent.
* This function is endianness independent and alignment independent.
* It produces identical hash results to ::tommy_strhash_u64 for 0-terminated strings.
* Compatibility across different versions of the library is not guaranteed:
* this hash is intended for in-memory runtime use only and not for persistent storage.
* It is not state-of-the-art against malicious collision attacks, but it is simple and fast.
* \return The hash value of 64 bits.
*/
TOMMY_API 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 Robert Jenkins "lookup3" hash 32 bits version,
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle().
* Implementation is based on MurmurHash3 (x86_32) by Austin Appleby,
* from https://github.com/aappleby/smhasher
*
* This hash is designed to handle strings with an unknown length. If you
* know the string length, the other hash functions are surely faster.
* This hash is designed to handle strings with an unknown length.
* If the string is aligned, it operates in a single pass without calling strlen().
*
* \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 endianness independent.
* This function is endianness independent and alignment independent.
* Compatibility across different versions of the library is not guaranteed:
* this hash is intended for in-memory runtime use only and not for persistent storage.
* It is not state-of-the-art against malicious collision attacks, but it is simple and fast.
* \return The hash value of 32 bits.
*/
TOMMY_API tommy_uint32_t tommy_strhash_u32(tommy_uint32_t init_val, const void* void_key);
/**
* String hash function with a 64 bits result.
* Implementation is based on MurmurHash3 (x64_64) by Austin Appleby,
* from https://github.com/aappleby/smhasher
*
* This hash is designed to handle strings with an unknown length.
* If the string is aligned, it operates in a single pass without calling strlen().
*
* \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 endianness independent and alignment independent.
* Compatibility across different versions of the library is not guaranteed:
* this hash is intended for in-memory runtime use only and not for persistent storage.
* It is not state-of-the-art against malicious collision attacks, but it is simple and fast.
* \return The hash value of 64 bits.
*/
TOMMY_API tommy_uint64_t tommy_strhash_u64(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",
@ -108,3 +132,4 @@ tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key)
}
#endif

View file

@ -4,6 +4,8 @@
#include "tommyhashdyn.h"
#include "tommylist.h"
#include <string.h> /* for memset */
/******************************************************************************/
/* hashdyn */
@ -23,6 +25,12 @@ TOMMY_API void tommy_hashdyn_done(tommy_hashdyn* hashdyn)
tommy_free(hashdyn->bucket);
}
TOMMY_API void tommy_hashdyn_clear(tommy_hashdyn* hashdyn)
{
memset(hashdyn->bucket, 0, hashdyn->bucket_max * sizeof(tommy_hashdyn_node*));
hashdyn->count = 0;
}
/**
* Resize the bucket vector.
*/
@ -37,57 +45,62 @@ static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_uint_t new_bucket
if (!hashdyn->count) {
/* empty table: allocate zeroed memory with calloc without looping */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(new_bucket_max, sizeof(tommy_hashdyn_node*)));
} else if (new_bucket_bit == bucket_bit + 1) {
tommy_size_t i;
/* grow by 1 bit: zero the two buckets inline in loop to preserve cache locality */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
for (i = 0; i < bucket_max; ++i) {
tommy_hashdyn_node* j;
new_bucket[i] = 0;
new_bucket[i + bucket_max] = 0;
j = hashdyn->bucket[i];
while (j) {
tommy_hashdyn_node* j_next = j->next;
tommy_size_t pos = j->index & 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 if (new_bucket_bit > bucket_bit) {
tommy_size_t i;
if (new_bucket_bit == bucket_bit + 1) {
/* grow by 1 bit: zero the two buckets inline in loop to preserve cache locality */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
/* grow by multiple bits with elements: allocate zeroed memory and reinsert */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(new_bucket_max, sizeof(tommy_hashdyn_node*)));
for (tommy_size_t i = 0; i < bucket_max; ++i) {
new_bucket[i] = 0;
new_bucket[i + bucket_max] = 0;
for (i = 0; i < bucket_max; ++i) {
tommy_hashdyn_node* j = hashdyn->bucket[i];
tommy_hashdyn_node* j = hashdyn->bucket[i];
while (j) {
tommy_hashdyn_node* j_next = j->next;
tommy_size_t pos = j->index & 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 {
/* grow by multiple bits with elements: allocate zeroed memory and reinsert */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(new_bucket_max, sizeof(tommy_hashdyn_node*)));
while (j) {
tommy_hashdyn_node* j_next = j->next;
tommy_size_t pos = j->index & 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;
for (tommy_size_t i = 0; i < bucket_max; ++i) {
tommy_hashdyn_node* j = hashdyn->bucket[i];
while (j) {
tommy_hashdyn_node* j_next = j->next;
tommy_size_t pos = j->index & 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_size_t i;
/* shrink with elements: all buckets are overwritten, no pre-zeroing needed */
/* all buckets are overwritten, no pre-zeroing needed */
new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*)));
for (i = 0; i < new_bucket_max; ++i) {
new_bucket[i] = hashdyn->bucket[i];
tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]);
if (new_bucket_bit + 1 == bucket_bit) {
/* shrink by 1 bit: each new bucket joins exactly two old buckets */
for (tommy_size_t i = 0; i < new_bucket_max; ++i) {
new_bucket[i] = hashdyn->bucket[i];
tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]);
}
} else {
/* shrink by multiple bits */
for (tommy_size_t i = 0; i < new_bucket_max; ++i) {
/* all old buckets with the same new modulus must be concatenated. */
new_bucket[i] = hashdyn->bucket[i];
for (tommy_size_t j = i + new_bucket_max; j < bucket_max; j += new_bucket_max)
tommy_list_concat(&new_bucket[i], &hashdyn->bucket[j]);
}
}
}
@ -133,6 +146,19 @@ TOMMY_API void tommy_hashdyn_reserve(tommy_hashdyn* hashdyn, tommy_size_t count)
tommy_hashdyn_resize(hashdyn, bucket_bit);
}
TOMMY_API void tommy_hashdyn_shrink(tommy_hashdyn* hashdyn)
{
tommy_uint_t bucket_bit = tommy_ilog2(hashdyn->count | 1) + 2;
if (bucket_bit < TOMMY_HASHDYN_BIT)
bucket_bit = TOMMY_HASHDYN_BIT;
if (bucket_bit >= hashdyn->bucket_bit)
return;
tommy_hashdyn_resize(hashdyn, bucket_bit);
}
TOMMY_API void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash)
{
tommy_size_t pos = hash & hashdyn->bucket_mask;
@ -146,6 +172,27 @@ TOMMY_API void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node*
hashdyn_grow_step(hashdyn);
}
TOMMY_API void* tommy_hashdyn_insert_unique(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash)
{
void* existing = tommy_hashdyn_search(hashdyn, cmp, cmp_arg, hash);
if (existing)
return existing;
tommy_hashdyn_insert(hashdyn, node, data, hash);
return data;
}
TOMMY_API void tommy_hashdyn_rehash_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, tommy_hash_t hash)
{
if (node->index == hash)
return;
/* unlink using the stored hash, without invoking the resize policy. */
tommy_list_remove_existing(&hashdyn->bucket[node->index & hashdyn->bucket_mask], node);
node->index = hash;
tommy_list_insert_tail(&hashdyn->bucket[hash & hashdyn->bucket_mask], node, node->data);
}
TOMMY_API void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node)
{
tommy_size_t pos = node->index & hashdyn->bucket_mask;
@ -185,9 +232,8 @@ TOMMY_API void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func*
{
tommy_size_t bucket_max = hashdyn->bucket_max;
tommy_hashdyn_node** bucket = hashdyn->bucket;
tommy_size_t pos;
for (pos = 0; pos < bucket_max; ++pos) {
for (tommy_size_t pos = 0; pos < bucket_max; ++pos) {
tommy_hashdyn_node* node = bucket[pos];
while (node) {
@ -202,9 +248,8 @@ TOMMY_API void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_a
{
tommy_size_t bucket_max = hashdyn->bucket_max;
tommy_hashdyn_node** bucket = hashdyn->bucket;
tommy_size_t pos;
for (pos = 0; pos < bucket_max; ++pos) {
for (tommy_size_t pos = 0; pos < bucket_max; ++pos) {
tommy_hashdyn_node* node = bucket[pos];
while (node) {
@ -221,62 +266,13 @@ TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn)
+ tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node);
}
/**
* \brief Transfers all elements from the dynamic hashtable into a tommy_list.
*
* Removes every element from the \p hashdyn hashtable and inserts them
* into the provided \p list (at the tail), preserving the per-bucket order
* but not guaranteeing any particular global order.
*
* After the call:
* - the hashtable is left completely empty (\c count = 0, all buckets = NULL)
* - the target list contains all the elements that were previously in the hashtable
*
* This function is useful when you need to:
* - extract all elements to process/sort them outside the hash table
* - convert the hashtable into a list for sequential iteration
* - prepare for a full clear + re-insertion with different hash/ordering
* - move ownership of the nodes to a list-based container
*
* \note The operation is O(n) where n is the number of elements.
* \note No memory allocation is performed.
* \note The relative order of elements that were in the same bucket is preserved,
* but the order among different buckets is bucket-order dependent.
*
* Typical usage pattern:
* \code
* tommy_list all_elements;
* tommy_list_init(&all_elements);
*
* // move everything out of the hashtable into the list
* tommy_hashdyn_to_list(&hashdyn, &all_elements);
*
* // now you can sort, filter, process sequentially, etc.
* tommy_list_sort(&all_elements, compare_by_value);
* \endcode
*
* \param hashdyn The dynamic hashtable to drain
* \param list The list that will receive all the elements
*/
TOMMY_API void tommy_hashdyn_to_list(tommy_hashdyn* hashdyn, tommy_list* list)
{
tommy_size_t bucket_max = hashdyn->bucket_max;
tommy_hashdyn_node** bucket = hashdyn->bucket;
tommy_size_t pos;
/* move everything to the list */
for (pos = 0; pos < bucket_max; ++pos) {
tommy_hashdyn_node* node = bucket[pos];
while (node) {
tommy_node* node_next = node->next;
tommy_list_insert_tail(list, node, node->data);
node = node_next;
}
}
for (tommy_size_t pos = 0; pos < hashdyn->bucket_max; ++pos)
tommy_list_concat(list, &hashdyn->bucket[pos]);
/* clear all */
memset(hashdyn->bucket, 0, hashdyn->bucket_max * sizeof(tommy_hashdyn_node*));
hashdyn->count = 0;
tommy_hashdyn_clear(hashdyn);
}

View file

@ -4,9 +4,9 @@
/** \file
* Dynamic chained hashtable.
*
* This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles
* the size when it reaches a load factor greater than 0.5 and it halves the size when the load
* factor is lower than 0.125.
* This hashtable resizes dynamically. It starts with the minimal size of 16 buckets.
* It doubles the size when it reaches a load factor greater than or equal to 0.5
* and it halves the size when the load factor is less than or equal to 0.125.
*
* All the elements are reallocated in a single resize operation done inside
* tommy_hashdyn_insert() or tommy_hashdyn_remove().
@ -32,7 +32,7 @@
* 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.
* of the node, and the hash to initialize the tommy_node::index field.
*
* \code
* struct object {
@ -48,7 +48,7 @@
* tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object
* \endcode
*
* To find an element in the hashtable you have to call tommy_hashtable_search()
* To find an element in the hashtable you have to call tommy_hashdyn_search()
* providing a comparison function, its argument, and the hash of the key to search.
*
* \code
@ -114,6 +114,7 @@
#define __TOMMYHASHDYN_H
#include "tommyhash.h"
#include "tommylist.h"
/******************************************************************************/
/* hashdyn */
@ -155,6 +156,17 @@ TOMMY_API void tommy_hashdyn_init(tommy_hashdyn* hashdyn);
*/
TOMMY_API void tommy_hashdyn_done(tommy_hashdyn* hashdyn);
/**
* Removes all elements, preserving the allocated buckets.
* The hashtable remains initialized and can be reused immediately.
* Objects are not freed and nodes are not accessed or modified.
* Their links must not be used to traverse the previous contents.
* You can call this function after tommy_hashdyn_foreach() has freed the objects.
* Subsequent insertions and removals retain the normal resizing policy.
* \note This operation is O(b), where b is the number of buckets.
*/
TOMMY_API void tommy_hashdyn_clear(tommy_hashdyn* hashdyn);
/**
* Pre-allocates buckets for the specified number of elements.
*
@ -164,11 +176,40 @@ TOMMY_API void tommy_hashdyn_done(tommy_hashdyn* hashdyn);
*/
TOMMY_API void tommy_hashdyn_reserve(tommy_hashdyn* hashdyn, tommy_size_t count);
/**
* Shrinks the allocated buckets to fit the current number of elements.
* The target is the smallest power of two, at least 16, for which
* the number of elements is strictly less than half the number of buckets.
* If the current allocation is already no larger than the target, nothing is done.
* An empty hashtable is reduced to 16 buckets.
* Objects are not freed, and the tommy_node::data and tommy_node::index fields
* are left unchanged. The order of elements with the same hash is preserved.
* \note This operation is O(b), where b is the previous number of buckets.
*/
TOMMY_API void tommy_hashdyn_shrink(tommy_hashdyn* hashdyn);
/**
* Inserts an element in the hashtable.
*/
TOMMY_API void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash);
/**
* Inserts an element only if no equal element is already contained.
* If found, the first equal element's tommy_node::data field is returned,
* and the hashtable and candidate node are left unchanged.
* Otherwise, the candidate is inserted using the normal insertion policy,
* and its data field is returned.
* Objects are not freed by this call.
* \param node The candidate node. It must not belong to any container.
* \param data The object to insert.
* \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 describing the candidate key.
* \param hash Hash of the candidate key, consistent with the comparison function.
* \return The first equal element's data field, or data if the candidate was inserted.
*/
TOMMY_API void* tommy_hashdyn_insert_unique(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_search_func* cmp, const void* cmp_arg, 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.
@ -225,6 +266,22 @@ tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_search_fun
*/
TOMMY_API void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node);
/**
* Updates the hash of an element already contained in the hashtable.
* The node must belong to this hashtable. The caller updates the object key
* and provides its new hash, without modifying tommy_node::index directly.
* If the hash is unchanged, the node and its position are left unchanged.
* Otherwise, the node is moved to the tail of the destination bucket,
* even if the old and new hashes identify the same bucket.
* The tommy_node::data field and the number of elements are left unchanged.
* No memory allocation, deallocation or resize is performed.
* Equal keys are allowed; no uniqueness check is performed.
* \param node The node whose hash is updated.
* \param hash The new hash of the element.
* \note This operation is O(1).
*/
TOMMY_API void tommy_hashdyn_rehash_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, tommy_hash_t hash);
/**
* Calls the specified function for each element in the hashtable.
*
@ -260,6 +317,9 @@ TOMMY_API void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func*
/**
* Calls the specified function with an argument for each element in the hashtable.
* The iteration order and callback rules are the same as tommy_hashdyn_foreach().
* The callback may deallocate the current element.
* Adding or removing elements from inside the callback is not allowed.
*/
TOMMY_API void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg);
@ -271,6 +331,23 @@ tommy_inline tommy_size_t tommy_hashdyn_count(tommy_hashdyn* hashdyn)
return hashdyn->count;
}
/**
* Checks if empty.
* \return If the hashtable is empty.
*/
tommy_inline tommy_bool_t tommy_hashdyn_empty(tommy_hashdyn* hashdyn)
{
return hashdyn->count == 0;
}
/**
* Gets the number of buckets.
*/
tommy_inline tommy_size_t tommy_hashdyn_bucket_count(tommy_hashdyn* hashdyn)
{
return hashdyn->bucket_max;
}
/**
* Gets the size of allocated memory.
* It includes the size of the ::tommy_hashdyn_node of the stored elements.
@ -285,17 +362,19 @@ TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn);
* but not guaranteeing any particular global order.
*
* After the call:
* - the hashtable is left completely empty
* - the hashtable is left empty and initialized, preserving its allocated buckets
* - the target list contains all the elements that were previously in the hashtable
*
* The tommy_node::data and tommy_node::index fields are left unchanged.
*
* This function is useful when you need to:
* - extract all elements to process/sort them outside the hash table
* - convert the hashtable into a list for sequential iteration
* - prepare for a full clear + re-insertion with different hash/ordering
* - move ownership of the nodes to a list-based container
*
* \note The operation is O(n) where n is the number of elements.
* \note No memory allocation is performed.
* \note The operation is O(b) where b is the number of buckets.
* \note No memory allocation or deallocation is performed.
* \note The relative order of elements that were in the same bucket is preserved,
* but the order among different buckets is bucket-order dependent.
*
@ -311,8 +390,9 @@ TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn);
* tommy_list_sort(&all_elements, compare_by_value);
* \endcode
*
* \param hashdyn The dynamic hashtable to drain
* \param list The list that will receive all the elements
* \param hashdyn The hashtable to drain
* \param list The destination list. It must be initialized and must not share
* nodes with the hashtable. Existing elements remain at the head of the list.
*/
TOMMY_API void tommy_hashdyn_to_list(tommy_hashdyn* hashdyn, tommy_list* list);

View file

@ -14,17 +14,96 @@ tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node*
*list = head;
}
TOMMY_API void tommy_list_splice_before(tommy_list* list, tommy_node* reference, tommy_list* second)
{
tommy_node* second_head = tommy_list_head(second);
if (!second_head)
return;
if (reference == tommy_list_head(list)) {
/* prepend */
tommy_list_prepend(list, second);
} else {
/* splice in the middle */
tommy_chain_splice(reference->prev, reference, second_head, second_head->prev);
}
}
TOMMY_API void tommy_list_splice_after(tommy_list* list, tommy_node* reference, tommy_list* second)
{
tommy_node* second_head = tommy_list_head(second);
if (!second_head)
return;
if (!reference->next) {
/* append */
tommy_list_concat(list, second);
} else {
/* splice in the middle */
tommy_chain_splice(reference, reference->next, second_head, second_head->prev);
}
}
TOMMY_API void tommy_list_merge(tommy_list* first, tommy_list* second, tommy_compare_func* cmp)
{
tommy_node* second_head = tommy_list_head(second);
if (!second_head)
return;
tommy_node* first_head = tommy_list_head(first);
if (!first_head) {
*first = *second;
return;
}
/* create chains from the non-empty lists */
tommy_chain first_chain;
tommy_chain second_chain;
first_chain.head = first_head;
first_chain.tail = first_head->prev;
second_chain.head = second_head;
second_chain.tail = second_head->prev;
tommy_chain_merge_degenerated(&first_chain, &second_chain, cmp);
/* restore the destination list */
tommy_list_set(first, first_chain.head, first_chain.tail);
}
TOMMY_API void tommy_list_reverse(tommy_list* list)
{
tommy_node* head = tommy_list_head(list);
tommy_node* node = head;
tommy_node* prev = 0;
if (!head)
return;
while (node) {
tommy_node* next = node->next;
node->next = prev;
node->prev = next;
prev = node;
node = next;
}
/* the old head is the new tail */
tommy_list_set(list, prev, head);
}
TOMMY_API 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);
tommy_node* head = tommy_list_head(list);
/* create a chain from the list */
tommy_chain chain;
chain.head = head;
chain.tail = head->prev;

View file

@ -60,6 +60,40 @@
* }
* \endcode
*
* To iterate backward, start at tommy_list_tail() and use tommy_list_prev().
* Do not follow tommy_node::prev directly past the head, as it points to the tail.
*
* \code
* tommy_node* i = tommy_list_tail(&list);
* while (i) {
* struct object* obj = i->data;
*
* printf("%d\n", obj->value);
*
* i = tommy_list_prev(&list, i);
* }
* \endcode
*
* To reorder existing elements, use tommy_list_move_head(), tommy_list_move_tail()
* or tommy_list_reverse(). To split a list or transfer elements between lists,
* use tommy_list_split(), tommy_list_prepend(), tommy_list_splice_before()
* and tommy_list_splice_after().
* These operations preserve the object pointers and do not allocate memory.
*
* \code
* tommy_list second;
* tommy_list_init(&second);
*
* // transfers node and all following nodes to second
* tommy_list_split(&list, node, &second);
*
* // appends the transferred nodes back to list
* tommy_list_concat(&list, &second);
* \endcode
*
* Two lists sorted with the same comparison function can be merged with
* tommy_list_merge(). The merge is stable.
*
* 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.
@ -67,6 +101,7 @@
* \code
* // deallocates all the objects iterating the list
* tommy_list_foreach(&list, free);
* tommy_list_init(&list); // resets the list before reuse
* \endcode
*/
@ -115,6 +150,22 @@ tommy_inline tommy_node* tommy_list_tail(tommy_list* list)
return head->prev;
}
/**
* Gets the previous node in the list.
* Unlike tommy_node::prev, this function returns 0 at the head of the list,
* allowing backward iteration starting from tommy_list_tail().
* \param list The list.
* \param node The current node. The node must be in the list.
* \return The previous node, or 0 if the current node is the head.
*/
tommy_inline tommy_node* tommy_list_prev(tommy_list* list, tommy_node* node)
{
if (node == tommy_list_head(list))
return 0;
return node->prev;
}
/** \internal
* Creates a new list with a single element.
* \param list The list to initialize.
@ -286,6 +337,130 @@ tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node
return node->data;
}
/**
* Removes the element at the head of the list.
* The object is not deallocated.
* \note The removed node content is left unchanged, as in tommy_list_remove_existing().
* \param list The list.
* \return The tommy_node::data field of the node removed, or 0 if the list is empty.
*/
tommy_inline void* tommy_list_remove_head(tommy_list* list)
{
tommy_node* node = tommy_list_head(list);
if (!node)
return 0;
return tommy_list_remove_existing(list, node);
}
/**
* Removes the element at the tail of the list.
* The object is not deallocated.
* \note The removed node content is left unchanged, as in tommy_list_remove_existing().
* \param list The list.
* \return The tommy_node::data field of the node removed, or 0 if the list is empty.
*/
tommy_inline void* tommy_list_remove_tail(tommy_list* list)
{
tommy_node* node = tommy_list_tail(list);
if (!node)
return 0;
return tommy_list_remove_existing(list, node);
}
/**
* Moves an existing node to the head of the list.
* If the node is already at the head, nothing is done.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param list The list.
* \param node The node to move. The node must be in the list.
*/
tommy_inline void tommy_list_move_head(tommy_list* list, tommy_node* node)
{
if (node == tommy_list_head(list))
return;
tommy_list_remove_existing(list, node);
tommy_list_insert_head(list, node, node->data);
}
/**
* Moves an existing node to the tail of the list.
* If the node is already at the tail, nothing is done.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param list The list.
* \param node The node to move. The node must be in the list.
*/
tommy_inline void tommy_list_move_tail(tommy_list* list, tommy_node* node)
{
if (node == tommy_list_tail(list))
return;
tommy_list_remove_existing(list, node);
tommy_list_insert_tail(list, node, node->data);
}
/**
* Splits a list before the specified node.
* The list retains the nodes preceding the specified node. The second list
* receives the specified node and all following nodes. The order of the elements is preserved.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param list The list to split.
* \param node The first node to transfer. The node must be in the list,
* or 0 to leave list unchanged and set second to 0.
* Specifying the head transfers the whole list.
* \param second The list receiving node and all following nodes.
* It must be distinct from list and need not be initialized. Its previous value
* is overwritten without removing or deallocating any nodes previously referenced by second.
* \note This operation is O(1).
*/
tommy_inline void tommy_list_split(tommy_list* list, tommy_node* node, tommy_list* second)
{
tommy_node* head = tommy_list_head(list);
tommy_node* tail;
tommy_node* before;
if (!node) {
*second = 0;
return;
}
if (node == head) {
*second = *list;
*list = 0;
return;
}
tail = head->prev;
before = node->prev;
/* terminate the retained list */
head->prev = before;
before->next = 0;
/* create the list of transferred nodes */
node->prev = tail;
*second = node;
}
/**
* Exchanges the contents of two lists.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param first The first list.
* \param second The second list. The lists must not share nodes,
* unless first and second are the same pointer, in which case nothing is done.
* \note This operation is O(1).
*/
tommy_inline void tommy_list_swap(tommy_list* first, tommy_list* second)
{
tommy_list tmp = *first;
*first = *second;
*second = tmp;
}
/**
* Concats two lists.
* The second list is concatenated at the first list.
@ -295,24 +470,20 @@ tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node
*/
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);
tommy_node* 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);
tommy_node* first_head = tommy_list_head(first);
if (first_head == 0) {
*first = *second;
return;
}
/* tail of the first list */
first_tail = first_head->prev;
tommy_node* first_tail = first_head->prev;
/* set the "circular" prev list */
first_head->prev = second_head->prev;
@ -322,6 +493,72 @@ tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second)
first_tail->next = second_head;
}
/**
* Prepends two lists.
* The second list is prepended 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_prepend(tommy_list* first, tommy_list* second)
{
tommy_list_concat(second, first);
*first = *second;
}
/**
* Transfers all elements of a list before the specified reference node.
* The order of the transferred elements is preserved.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param list The destination list.
* \param reference The node before which to insert. It must be nonzero and in the destination list.
* \param second The source list. After this call the list content is undefined,
* and you should not use it anymore.
* The lists must be distinct and must not share nodes.
* \note This operation is O(1). An empty source list has no effect.
*/
TOMMY_API void tommy_list_splice_before(tommy_list* list, tommy_node* reference, tommy_list* second);
/**
* Transfers all elements of a list after the specified reference node.
* The order of the transferred elements is preserved.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param list The destination list.
* \param reference The node after which to insert. It must be nonzero and in the destination list.
* \param second The source list. After this call the list content is undefined,
* and you should not use it anymore.
* The lists must be distinct and must not share nodes.
* \note This operation is O(1). An empty source list has no effect.
*/
TOMMY_API void tommy_list_splice_after(tommy_list* list, tommy_node* reference, tommy_list* second);
/**
* Merges two sorted lists.
* Both lists must be sorted using the same comparison function.
* The merge is stable: equal elements from the first list precede those from
* the second list, and the relative order within each list is preserved.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param first The destination list, receiving the sorted result.
* \param second The source list. After this call the list content is undefined,
* and you should not use it anymore.
* The lists must be distinct and must not share nodes.
* \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 greater. It must not modify the lists.
* \note This operation is O(n+m), or O(1) when one list is empty or the ranges
* can be concatenated directly. It uses O(1) auxiliary space.
*/
TOMMY_API void tommy_list_merge(tommy_list* first, tommy_list* second, tommy_compare_func* cmp);
/**
* Reverses the order of the elements in a list.
* Empty lists and lists containing one element are left unchanged.
* The tommy_node::data and tommy_node::index fields are left unchanged.
* \param list The list.
* \note This operation is O(n) and uses O(1) auxiliary space.
*/
TOMMY_API void tommy_list_reverse(tommy_list* list);
/**
* Sorts a list.
* It's a stable merge sort with O(N*log(N)) worst complexity.
@ -398,6 +635,9 @@ tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func)
/**
* Calls the specified function with an argument for each element in the list.
* The iteration order and callback rules are the same as tommy_list_foreach().
* The callback may deallocate the current element.
* Adding or removing elements from inside the callback is not allowed.
*/
tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg)
{
@ -411,3 +651,4 @@ tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_fun
}
#endif

View file

@ -155,28 +155,33 @@ tommy_inline void tommy_tree_insert_balance(tommy_tree* tree, tommy_tree_node* n
}
}
TOMMY_API void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data)
tommy_inline void* tommy_tree_insert_impl(tommy_tree* tree, tommy_tree_node* node, void* data, tommy_bool_t unique)
{
tommy_tree_node* parent = 0;
tommy_tree_node* existing = 0;
tommy_tree_node** link = &tree->root;
node->data = data;
node->prev = 0;
node->next = 0;
node->index = 0;
while (*link) {
int cmp = tree->cmp(data, (*link)->data);
parent = *link;
if (cmp < 0)
link = &parent->prev;
else if (cmp > 0)
else if (cmp == 0 && unique) {
existing = parent;
link = &parent->prev;
} else {
/* placing equal keys after existing ones preserves insertion order through rotations */
link = &parent->next;
else
return parent->data;
}
}
if (existing)
return existing->data;
node->data = data;
node->prev = 0;
node->next = 0;
node->index = (tommy_size_t)(tommy_uintptr_t)parent;
*link = node;
++tree->count;
@ -186,6 +191,16 @@ TOMMY_API void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void*
return node->data;
}
TOMMY_API void tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data)
{
tommy_tree_insert_impl(tree, node, data, 0);
}
TOMMY_API void* tommy_tree_insert_unique(tommy_tree* tree, tommy_tree_node* node, void* data)
{
return tommy_tree_insert_impl(tree, node, data, 1);
}
tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* node, tommy_bool_t left_shrunk)
{
while (node) {
@ -202,10 +217,8 @@ tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* n
}
if (balance == 0) {
tommy_tree_node* parent;
tommy_tree_balance_set(node, balance);
parent = tommy_tree_parent(node);
tommy_tree_node* parent = tommy_tree_parent(node);
if (!parent)
return;
left_shrunk = node == parent->prev;
@ -326,35 +339,65 @@ TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* no
return data;
}
TOMMY_API void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func)
tommy_inline void tommy_tree_to_list_node(tommy_tree_node* node, tommy_list* list)
{
while (root) {
tommy_tree_node* next;
if (node) {
tommy_tree_node* right = node->next;
tommy_tree_foreach_node(root->prev, func);
/* make a copy in case func is free() */
next = root->next;
func(root->data);
root = next;
tommy_tree_to_list_node(node->prev, list);
/* list insertion overwrites child links, so keep the right subtree reachable */
tommy_list_insert_tail(list, node, node->data);
tommy_tree_to_list_node(right, list);
}
}
TOMMY_API void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg)
TOMMY_API void tommy_tree_to_list(tommy_tree* tree, tommy_list* list)
{
while (root) {
tommy_tree_node* next;
tommy_tree_to_list_node(tree->root, list);
tommy_tree_clear(tree);
}
tommy_tree_foreach_arg_node(root->prev, func, arg);
TOMMY_API void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func)
{
/* AVL height is less than twice the bit width of its node count */
tommy_tree_node* stack[2 * TOMMY_SIZE_BIT];
tommy_size_t depth = 0;
tommy_tree_node* node = tree->root;
/* make a copy in case func is free() */
next = root->next;
while (node || depth) {
while (node) {
stack[depth] = node;
++depth;
node = node->prev;
}
func(arg, root->data);
node = stack[--depth];
/* save the next subtree before the callback can free this node */
tommy_tree_node* next = node->next;
func(node->data);
node = next;
}
}
root = next;
TOMMY_API void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg)
{
/* AVL height is less than twice the bit width of its node count */
tommy_tree_node* stack[2 * TOMMY_SIZE_BIT];
tommy_size_t depth = 0;
tommy_tree_node* node = tree->root;
while (node || depth) {
while (node) {
stack[depth] = node;
++depth;
node = node->prev;
}
node = stack[--depth];
/* save the next subtree before the callback can free this node */
tommy_tree_node* next = node->next;
func(arg, node->data);
node = next;
}
}

View file

@ -7,7 +7,8 @@
* This tree is a standard AVL tree implementation that stores elements in the
* order defined by the comparison function.
*
* As a difference from other tommy containers, duplicate elements cannot be inserted.
* Duplicate elements are allowed. They are visited in insertion order.
* Use tommy_tree_insert_unique() to reject duplicate keys.
*
* To initialize a tree you have to call tommy_tree_init() specifying a comparison
* function that will define the order in the tree.
@ -39,8 +40,8 @@
* tommy_tree_insert(&tree, &obj->node, obj); // inserts the object
* \endcode
*
* To find an element in the tree you have to call tommy_tree_search() providing
* the key to search.
* To find the first element with a given key in tree order, call
* tommy_tree_search() providing the key to search.
*
* \code
* struct object value_to_find = { 1 };
@ -67,8 +68,8 @@
* }
* \endcode
*
* To remove an element from the tree you have to call tommy_tree_remove()
* providing the key to search and remove.
* To remove the first element with a given key in tree order, call
* tommy_tree_remove() providing the key to search and remove.
*
* \code
* struct object value_to_remove = { 1 };
@ -111,6 +112,7 @@
#define __TOMMYTREE_H
#include "tommytypes.h"
#include "tommylist.h"
/******************************************************************************/
/* tree */
@ -145,16 +147,68 @@ tommy_inline 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.
* Exchanges the contents of two initialized trees, including their comparison functions.
* Each comparison function moves with its root to preserve the tree order.
* The trees must not share nodes. Nodes are not accessed or modified.
* Passing the same tree twice has no effect.
* \param first The first tree.
* \param second The second tree.
* \note This operation is O(1).
*/
tommy_inline void tommy_tree_swap(tommy_tree* first, tommy_tree* second)
{
tommy_tree tmp = *first;
*first = *second;
*second = tmp;
}
/**
* Removes all elements, preserving the comparison function.
* The tree remains initialized and can be reused immediately.
* Objects are not freed and nodes are not accessed or modified.
* Their links must not be used to traverse the previous contents.
* You can call this function after tommy_tree_foreach() has freed the objects.
* \note This operation is O(1).
*/
tommy_inline void tommy_tree_clear(tommy_tree* tree)
{
tree->root = 0;
tree->count = 0;
}
/**
* Inserts an element in the tree, allowing duplicate keys.
* Equal elements are visited in insertion order.
* 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.
*/
TOMMY_API void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data);
TOMMY_API void tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data);
/**
* Inserts an element only if no equal element is already contained.
* If found, the first equal element in tree order is returned and the
* candidate node is left unchanged. Otherwise the candidate is inserted
* and its data field is returned.
* \param node Pointer to the node embedded into the object to insert.
* \param data Pointer to the object to insert.
* \return The first equal element, or data if the candidate was inserted.
*/
TOMMY_API void* tommy_tree_insert_unique(tommy_tree* tree, tommy_tree_node* node, void* data);
/**
* Transfers all elements from the tree to the tail of a list in tree order.
* Existing list elements remain before the transferred elements. The tree is
* left empty and initialized with its original comparison function.
* The list must be initialized and must not share nodes with the tree.
* Objects are not freed, and the node data and index fields are left unchanged.
* The index field no longer represents a valid tree parent after the transfer.
* \param tree The tree to drain.
* \param list The destination list.
* \note This operation is O(n), using O(log n) stack space.
*/
TOMMY_API void tommy_tree_to_list(tommy_tree* tree, tommy_list* list);
/**
* Removes an element from the tree.
@ -163,22 +217,6 @@ TOMMY_API void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void*
*/
TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node);
/** \internal
* Calls the specified function for each element in the tree.
* \param root Root node of the tree.
* \param func Function to call.
*/
TOMMY_API void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func);
/** \internal
* Calls the specified function with an argument for each element in the tree.
* \param root Root node of the tree.
* \param func Function to call.
* \param arg Custom argument for the function.
*/
TOMMY_API void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg);
/**
* Gets the parent of the specified node.
* \param node Node contained in the tree.
@ -196,8 +234,6 @@ tommy_inline tommy_tree_node* tommy_tree_parent(tommy_tree_node* node)
*/
tommy_inline tommy_tree_node* tommy_tree_next(tommy_tree_node* node)
{
tommy_tree_node* parent;
if (node->next) {
node = node->next;
while (node->prev)
@ -205,7 +241,7 @@ tommy_inline tommy_tree_node* tommy_tree_next(tommy_tree_node* node)
return node;
}
parent = tommy_tree_parent(node);
tommy_tree_node* parent = tommy_tree_parent(node);
while (parent && node == parent->next) {
node = parent;
parent = tommy_tree_parent(parent);
@ -221,8 +257,6 @@ tommy_inline tommy_tree_node* tommy_tree_next(tommy_tree_node* node)
*/
tommy_inline tommy_tree_node* tommy_tree_prev(tommy_tree_node* node)
{
tommy_tree_node* parent;
if (node->prev) {
node = node->prev;
while (node->next)
@ -230,7 +264,7 @@ tommy_inline tommy_tree_node* tommy_tree_prev(tommy_tree_node* node)
return node;
}
parent = tommy_tree_parent(node);
tommy_tree_node* parent = tommy_tree_parent(node);
while (parent && node == parent->prev) {
node = parent;
parent = tommy_tree_parent(parent);
@ -303,87 +337,71 @@ tommy_inline void* tommy_tree_remove_tail(tommy_tree* tree)
return tommy_tree_remove_existing(tree, node);
}
/** \internal
* Searches a node in the tree.
* If the element is not found, 0 is returned.
* \param cmp Comparison function.
* \param node Node to start the search from.
* \param data Element used for comparison.
* \return The first node found, or 0 if none.
/**
* 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 first equal element in tree order is returned.
*
* The cmp_arg argument will be the first argument of the comparison function,
* and it can be of a different type than the objects in the tree.
*/
tommy_inline tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
tommy_inline void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
{
tommy_tree_node* node = tree->root;
void* candidate = 0;
while (node) {
int c = cmp(data, node->data);
int c = cmp(cmp_arg, node->data);
if (c < 0)
node = node->prev;
else if (c > 0)
node = node->next;
else
return node;
else {
candidate = node->data;
node = node->prev;
}
}
return 0;
return candidate;
}
/**
* 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.
* \return The first equal element in tree order, or 0 if none.
*/
tommy_inline void* tommy_tree_search(tommy_tree* tree, void* data)
tommy_inline void* tommy_tree_search(tommy_tree* tree, const void* data)
{
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
if (!node)
return 0;
return node->data;
return tommy_tree_search_compare(tree, tree->cmp, data);
}
/**
* Searches an element in the tree with a specific comparison function.
* Searches an element in the tree with key greater or equal than the specified one with a specific comparison function.
*
* Like tommy_tree_search() but you can specify a different comparison function.
* Like tommy_tree_search_greater_equal() but you can specify a different comparison function.
* Note that this function must define a suborder of the original one.
* The first qualifying element in tree order is returned.
*
* The ::data argument will be the first argument of the comparison function,
* The cmp_arg argument will be the first argument of the comparison function,
* and it can be of a different type than the objects in the tree.
*/
tommy_inline void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
tommy_inline void* tommy_tree_search_greater_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
{
tommy_tree_node* node = tommy_tree_search_node(cmp, tree->root, cmp_arg);
if (!node)
return 0;
return node->data;
}
/** \internal
* Searches a node in the tree with key greater or equal than the specified one.
* If no such element exists, 0 is returned.
* \param cmp Comparison function.
* \param node Node to start the search from.
* \param data Element used for comparison.
* \return The first node found, or 0 if none.
*/
tommy_inline tommy_tree_node* tommy_tree_search_greater_equal_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
{
tommy_tree_node* candidate = 0;
tommy_tree_node* node = tree->root;
void* candidate = 0;
while (node) {
int c = cmp(data, node->data);
int c = cmp(cmp_arg, node->data);
if (c < 0) {
candidate = node;
if (c <= 0) {
candidate = node->data;
node = node->prev;
} else if (c > 0) {
node = node->next;
} else {
return node;
node = node->next;
}
}
@ -394,59 +412,74 @@ tommy_inline tommy_tree_node* tommy_tree_search_greater_equal_node(tommy_compare
* Searches an element in the tree with key greater or equal than the specified one.
* If no such element exists, 0 is returned.
* \param data Element used for comparison.
* \return The first element found, or 0 if none.
* \return The first element in tree order with key greater or equal, or 0 if none.
*/
tommy_inline void* tommy_tree_search_greater_equal(tommy_tree* tree, void* data)
tommy_inline void* tommy_tree_search_greater_equal(tommy_tree* tree, const void* data)
{
tommy_tree_node* node = tommy_tree_search_greater_equal_node(tree->cmp, tree->root, data);
if (!node)
return 0;
return node->data;
return tommy_tree_search_greater_equal_compare(tree, tree->cmp, data);
}
/**
* Searches an element in the tree with key greater or equal than the specified one with a specific comparison function.
*
* Like tommy_tree_search_greater_equal() 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 than the objects in the tree.
* Searches the first element in tree order with key strictly greater than the specified one using a specific comparison function.
* The function must define a suborder of the tree comparison function.
* Equivalent elements are excluded from the result.
* \param cmp Comparison function called with cmp_arg and the object in the tree.
* \param cmp_arg Search key, which may have a different type from tree objects.
* \return The first strictly greater element, or 0 if none.
*/
tommy_inline void* tommy_tree_search_greater_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
tommy_inline void* tommy_tree_search_greater_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
{
tommy_tree_node* node = tommy_tree_search_greater_equal_node(cmp, tree->root, cmp_arg);
if (!node)
return 0;
return node->data;
}
/** \internal
* Searches a node in the tree with key less or equal than the specified one.
* If no such element exists, 0 is returned.
* \param cmp Comparison function.
* \param node Node to start the search from.
* \param data Element used for comparison.
* \return The first node found, or 0 if none.
*/
tommy_inline tommy_tree_node* tommy_tree_search_less_equal_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
{
tommy_tree_node* candidate = 0;
tommy_tree_node* node = tree->root;
void* candidate = 0;
while (node) {
int c = cmp(data, node->data);
int c = cmp(cmp_arg, node->data);
if (c < 0) {
candidate = node->data;
node = node->prev;
} else {
node = node->next;
}
}
return candidate;
}
/**
* Searches the first element in tree order with key strictly greater than the specified one.
* If no such element exists, 0 is returned.
* \param data Element used for comparison.
* \return The first strictly greater element, or 0 if none.
*/
tommy_inline void* tommy_tree_search_greater(tommy_tree* tree, const void* data)
{
return tommy_tree_search_greater_compare(tree, tree->cmp, data);
}
/**
* Searches an element in the tree with key less or equal than the specified one with a specific comparison function.
*
* Like tommy_tree_search_less_equal() but you can specify a different comparison function.
* Note that this function must define a suborder of the original one.
* The last qualifying element in tree order is returned.
*
* The cmp_arg argument will be the first argument of the comparison function,
* and it can be of a different type than the objects in the tree.
*/
tommy_inline void* tommy_tree_search_less_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
{
tommy_tree_node* node = tree->root;
void* candidate = 0;
while (node) {
int c = cmp(cmp_arg, node->data);
if (c < 0) {
node = node->prev;
} else if (c > 0) {
candidate = node;
node = node->next;
} else {
return node;
candidate = node->data;
node = node->next;
}
}
@ -457,51 +490,93 @@ tommy_inline tommy_tree_node* tommy_tree_search_less_equal_node(tommy_compare_fu
* Searches an element in the tree with key less or equal than the specified one.
* If no such element exists, 0 is returned.
* \param data Element used for comparison.
* \return The first element found, or 0 if none.
* \return The last element in tree order with key less or equal, or 0 if none.
*/
tommy_inline void* tommy_tree_search_less_equal(tommy_tree* tree, void* data)
tommy_inline void* tommy_tree_search_less_equal(tommy_tree* tree, const void* data)
{
tommy_tree_node* node = tommy_tree_search_less_equal_node(tree->cmp, tree->root, data);
if (!node)
return 0;
return node->data;
return tommy_tree_search_less_equal_compare(tree, tree->cmp, data);
}
/**
* Searches an element in the tree with key less or equal than the specified one with a specific comparison function.
*
* Like tommy_tree_search_less_equal() 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 than the objects in the tree.
* Searches the last element in tree order with key strictly less than the specified one using a specific comparison function.
* The function must define a suborder of the tree comparison function.
* Equivalent elements are excluded from the result.
* \param cmp Comparison function called with cmp_arg and the object in the tree.
* \param cmp_arg Search key, which may have a different type from tree objects.
* \return The last strictly lesser element, or 0 if none.
*/
tommy_inline void* tommy_tree_search_less_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
tommy_inline void* tommy_tree_search_less_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
{
tommy_tree_node* node = tommy_tree_search_less_equal_node(cmp, tree->root, cmp_arg);
tommy_tree_node* node = tree->root;
void* candidate = 0;
if (!node)
while (node) {
int c = cmp(cmp_arg, node->data);
if (c > 0) {
candidate = node->data;
node = node->next;
} else {
node = node->prev;
}
}
return candidate;
}
/**
* Searches the last element in tree order with key strictly less than the specified one.
* If no such element exists, 0 is returned.
* \param data Element used for comparison.
* \return The last strictly lesser element, or 0 if none.
*/
tommy_inline void* tommy_tree_search_less(tommy_tree* tree, const void* data)
{
return tommy_tree_search_less_compare(tree, tree->cmp, data);
}
/**
* Searches and removes an element with a specific comparison function.
* The comparison function must define a suborder of the original one.
* The cmp_arg argument is passed as the first argument of the comparison
* function and may have a different type than the objects in the tree.
* \param cmp Comparison function used to find the element.
* \param cmp_arg Argument passed to the comparison function.
* \return The first equal element in tree order, or 0 if not found.
*/
tommy_inline void* tommy_tree_remove_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
{
tommy_tree_node* node = tree->root;
tommy_tree_node* candidate = 0;
while (node) {
int c = cmp(cmp_arg, node->data);
if (c < 0)
node = node->prev;
else if (c > 0)
node = node->next;
else {
candidate = node;
node = node->prev;
}
}
if (!candidate)
return 0;
return node->data;
return tommy_tree_remove_existing(tree, candidate);
}
/**
* 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.
* \return The first equal element in tree order, or 0 if not found.
*/
tommy_inline void* tommy_tree_remove(tommy_tree* tree, void* data)
tommy_inline void* tommy_tree_remove(tommy_tree* tree, const void* data)
{
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
if (!node)
return 0;
return tommy_tree_remove_existing(tree, node);
return tommy_tree_remove_compare(tree, tree->cmp, data);
}
/**
@ -534,18 +609,15 @@ tommy_inline void* tommy_tree_remove(tommy_tree* tree, void* data)
* tommy_tree_foreach(&tree, free);
* \endcode
*/
tommy_inline void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func)
{
tommy_tree_foreach_node(tree->root, func);
}
TOMMY_API void tommy_tree_foreach(tommy_tree* tree, tommy_foreach_func* func);
/**
* Calls the specified function with an argument for each element in the tree.
* The iteration order and callback rules are the same as tommy_tree_foreach().
* The callback may deallocate the current element.
* Adding or removing elements from inside the callback is not allowed.
*/
tommy_inline 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_API void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg);
/**
* Checks if empty.

View file

@ -200,6 +200,7 @@ typedef struct tommy_node_struct {
/**
* Pointer to the object containing the node.
* This field is initialized when inserting nodes into a data structure.
* The object pointer passed to insertion functions must not be 0.
*/
void* data;
@ -355,7 +356,7 @@ tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value)
*/
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 */
/* 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,
@ -372,13 +373,12 @@ tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value)
#endif
}
#if TOMMY_SIZE_BIT == 64
/**
* Bit scan reverse or integer log2 for 64 bits.
*/
tommy_inline tommy_uint_t tommy_ilog2_u64(tommy_uint64_t value)
{
#if defined(_MSC_VER)
#if defined(_MSC_VER) && defined(_WIN64)
unsigned long count;
_BitScanReverse64(&count, value);
return count;
@ -393,7 +393,6 @@ tommy_inline tommy_uint_t tommy_ilog2_u64(tommy_uint64_t value)
return tommy_ilog2_u32(l);
#endif
}
#endif
/**
* Bit scan forward or trailing zero count.
@ -412,24 +411,23 @@ tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value)
#elif defined(__GNUC__)
return __builtin_ctz(value);
#else
/* Count the consecutive zero bits (trailing) on the right with multiply and lookup */
/* 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];
return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & -value) * 0x077CB531U)) >> 27];
#endif
}
#if TOMMY_SIZE_BIT == 64
/**
* Bit scan forward or trailing zero count for 64 bits.
*/
tommy_inline tommy_uint_t tommy_ctz_u64(tommy_uint64_t value)
{
#if defined(_MSC_VER)
#if defined(_MSC_VER) && defined(_WIN64)
unsigned long count;
_BitScanForward64(&count, value);
return count;
@ -444,7 +442,6 @@ tommy_inline tommy_uint_t tommy_ctz_u64(tommy_uint64_t value)
return tommy_ctz_u32(h) + 32;
#endif
}
#endif
/**
* Rounds up to the next power of 2.
@ -453,7 +450,7 @@ tommy_inline tommy_uint_t tommy_ctz_u64(tommy_uint64_t value)
*/
tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
{
/* Round up to the next highest power of 2 */
/* round up to the next highest power of 2 */
/* from http://graphics.stanford.edu/~seander/bithacks.html */
--value;
@ -493,7 +490,16 @@ tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
return ((value - 0x01010101) & ~value & 0x80808080) != 0;
}
/*
/**
* Check if the specified 64-bit word has a byte at 0.
* \return 0 or 1.
*/
tommy_inline int tommy_haszero_u64(tommy_uint64_t value)
{
return ((value - 0x0101010101010101ULL) & ~value & 0x8080808080808080ULL) != 0;
}
/**
* Bit depth mapping.
*/
#if TOMMY_SIZE_BIT == 64
@ -507,3 +513,4 @@ tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
#endif
#endif