mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Update to TommyDS v4.0
This commit is contained in:
parent
a39fb132f8
commit
9505499e15
21 changed files with 2220 additions and 959 deletions
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ static const uint64_t MUSEAIR_CONSTANT[13] = {
|
|||
do { \
|
||||
l = a; \
|
||||
h = b; \
|
||||
util_mum(&l, &h); \
|
||||
tommy_mum(&l, &h); \
|
||||
} while (0)
|
||||
|
||||
#define likely(a) tommy_likely(a)
|
||||
|
|
|
|||
|
|
@ -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, 0x1138f522, 0xa766795d },
|
||||
{ "a", 1, 0x3366e86d, 0xa766795d },
|
||||
{ "abc", 3, 0x7a017599, 0xa766795d },
|
||||
{ "message digest", 14, 0xd11d9402, 0xa766795d },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x9e379e7b, 0xa766795d },
|
||||
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x429490b3, 0xa766795d },
|
||||
{ "The quick brown fox jumps over the lazy dog", 43, 0xa720287c, 0xa766795d },
|
||||
{ "\x00", 1, 0xf0afd007, 0xa766795d },
|
||||
{ "\x16\x27", 2, 0x7d78b59a, 0xa766795d },
|
||||
{ "\xe2\x56\xb4", 3, 0x1690ec86, 0xa766795d },
|
||||
{ "\xc9\x4d\x9c\xda", 4, 0xa9efc35d, 0xa766795d },
|
||||
{ "\x79\xf1\x29\x69\x5d", 5, 0xf138ecc1, 0xa766795d },
|
||||
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0xb3f64b75, 0xa766795d },
|
||||
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x27f94e67, 0xa766795d },
|
||||
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0x3770a9f5, 0xa766795d },
|
||||
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xd98ebad1, 0xa766795d },
|
||||
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0xe7f0bdc0, 0xa766795d },
|
||||
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0xaf9a9089, 0xa766795d },
|
||||
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0x605ceb65, 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 },
|
||||
{ "", 0x1138f522, 0xa766795d },
|
||||
{ "a", 0x3366e86d, 0xa766795d },
|
||||
{ "abc", 0x7a017599, 0xa766795d },
|
||||
{ "message digest", 0xd11d9402, 0xa766795d },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 0x9e379e7b, 0xa766795d },
|
||||
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x429490b3, 0xa766795d },
|
||||
{ "The quick brown fox jumps over the lazy dog", 0xa720287c, 0xa766795d },
|
||||
{ "\xff", 0xa9d0ff62, 0xa766795d },
|
||||
{ "\x16\x27", 0x7d78b59a, 0xa766795d },
|
||||
{ "\xe2\x56\xb4", 0x1690ec86, 0xa766795d },
|
||||
{ "\xc9\x4d\x9c\xda", 0xa9efc35d, 0xa766795d },
|
||||
{ "\x79\xf1\x29\x69\x5d", 0xf138ecc1, 0xa766795d },
|
||||
{ "\xff\x7e\xdf\x1e\x31\x1c", 0xc9633d79, 0xa766795d },
|
||||
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 0x27f94e67, 0xa766795d },
|
||||
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 0x3770a9f5, 0xa766795d },
|
||||
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 0xd98ebad1, 0xa766795d },
|
||||
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 0xe7f0bdc0, 0xa766795d },
|
||||
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\xff\xb7\xae", 0x1c63af47, 0xa766795d },
|
||||
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 0x605ceb65, 0xa766795d },
|
||||
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 0x1a41ce84, 0xa766795d },
|
||||
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 0xae824889, 0xa766795d },
|
||||
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 0x879c5032, 0xa766795d },
|
||||
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 0x5234d367, 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, 0xf64d2d2164ca4e35ULL, 0x2f022773a766795dULL },
|
||||
{ "a", 1, 0xcb0b7863fd912608ULL, 0x2f022773a766795dULL },
|
||||
{ "abc", 3, 0x023adbd4412dfbc2ULL, 0x2f022773a766795dULL },
|
||||
{ "message digest", 14, 0x8d16f620b58a030eULL, 0x2f022773a766795dULL },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 26, 0xa615ea6b87f2b63fULL, 0x2f022773a766795dULL },
|
||||
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0xb9f145676ebdb4fcULL, 0x2f022773a766795dULL },
|
||||
{ "The quick brown fox jumps over the lazy dog", 43, 0x99b05c9548b523dcULL, 0x2f022773a766795dULL },
|
||||
{ "\x00", 1, 0x432204e0ea8617fbULL, 0x2f022773a766795dULL },
|
||||
{ "\x16\x27", 2, 0x5118c58166c6ab3eULL, 0x2f022773a766795dULL },
|
||||
{ "\xe2\x56\xb4", 3, 0x29484604a9976acbULL, 0x2f022773a766795dULL },
|
||||
{ "\xc9\x4d\x9c\xda", 4, 0xf000ac44a9f98f4aULL, 0x2f022773a766795dULL },
|
||||
{ "\x79\xf1\x29\x69\x5d", 5, 0x9603fc74ec52e5aaULL, 0x2f022773a766795dULL },
|
||||
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0xd39f549b363c8e2bULL, 0x2f022773a766795dULL },
|
||||
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x1be47d9f53a2a31dULL, 0x2f022773a766795dULL },
|
||||
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0xe3fa24e31af8c4ebULL, 0x2f022773a766795dULL },
|
||||
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xef8c3dcec426a25dULL, 0x2f022773a766795dULL },
|
||||
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0xed0be01a88bde7cbULL, 0x2f022773a766795dULL },
|
||||
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0xf9b50d9db5dfa543ULL, 0x2f022773a766795dULL },
|
||||
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0x695329dad2094e8aULL, 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 */
|
||||
|
|
|
|||
119
cmdline/util.c
119
cmdline/util.c
|
|
@ -7,6 +7,7 @@
|
|||
#include "util.h"
|
||||
#include "raid/cpu.h"
|
||||
#include "raid/memory.h"
|
||||
#include "tommyds/tommyhash.h"
|
||||
|
||||
/****************************************************************************/
|
||||
/* memory */
|
||||
|
|
@ -610,112 +611,6 @@ static inline void util_write64(void* ptr, uint64_t v)
|
|||
memcpy(ptr, &v, sizeof(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* 64*64 -> 128bit multiply function.
|
||||
*
|
||||
* @param A Address of 64-bit number.
|
||||
* @param B Address of 64-bit number.
|
||||
*
|
||||
* Calculates 128-bit C = *A * *B.
|
||||
*
|
||||
* Overwrites A contents with C's low 64 bits.
|
||||
* Overwrites B contents with C's high 64 bits.
|
||||
*/
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
/**
|
||||
* This is intentionally placed before the __SIZEOF_INT128__ implementation.
|
||||
*
|
||||
* This implementation happens to generate more efficient code.
|
||||
* While this likely depends only on the compiler, as long as it remains faster,
|
||||
* we will keep it in this order.
|
||||
*/
|
||||
static inline void util_mum(uint64_t* A, uint64_t* B)
|
||||
{
|
||||
uint64_t a = *A;
|
||||
uint64_t b = *B;
|
||||
uint64_t low, high;
|
||||
|
||||
__asm__ (
|
||||
"mulq %3"
|
||||
: "=a" (low), "=d" (high)
|
||||
: "a" (a), "rm" (b)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#elif defined(__SIZEOF_INT128__)
|
||||
static inline void util_mum(uint64_t* A, uint64_t* B)
|
||||
{
|
||||
__uint128_t product = ((__uint128_t)*A) * ((__uint128_t)*B);
|
||||
*A = (uint64_t)product;
|
||||
*B = (uint64_t)(product >> 64);
|
||||
}
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
/**
|
||||
* This is intentionally placed after the __SIZEOF_INT128__ implementation.
|
||||
*
|
||||
* Although the code looks correct, there is a report of this
|
||||
* implementation failing the self-test on macOS (Darwin 25.4.0, arm64)
|
||||
* with Apple clang 21.0.0 (clang-2100.0.123.102).
|
||||
*
|
||||
* See: https://github.com/amadvance/snapraid/issues/43
|
||||
*
|
||||
* Note that the implementation is the same of OpenSSL:
|
||||
*
|
||||
* #elif defined(__aarch64__) && defined(SIXTY_FOUR_BIT_LONG)
|
||||
* #if defined(__GNUC__) && __GNUC__ >= 2
|
||||
* #define BN_UMULT_HIGH(a, b) ({ \
|
||||
* register BN_ULONG ret; \
|
||||
* asm ("umulh %0,%1,%2" \
|
||||
* : "=r"(ret) \
|
||||
* : "r"(a), "r"(b)); \
|
||||
* ret; })
|
||||
* #endif
|
||||
* #endif
|
||||
*
|
||||
* See: https://github.com/openssl/openssl/blob/0463cbf185680e66a3a8960f10cd33001377d28e/crypto/bn/bn_local.h#L439
|
||||
*/
|
||||
static inline void util_mum(uint64_t* A, uint64_t* B)
|
||||
{
|
||||
uint64_t a = *A;
|
||||
uint64_t b = *B;
|
||||
uint64_t low = a * b;
|
||||
uint64_t high;
|
||||
__asm__ (
|
||||
"umulh %0, %1, %2"
|
||||
: "=r" (high)
|
||||
: "r" (a), "r" (b)
|
||||
);
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#else
|
||||
static inline void util_mum(uint64_t* A, uint64_t* B)
|
||||
{
|
||||
uint64_t a = *A;
|
||||
uint64_t b = *B;
|
||||
|
||||
uint64_t a_lo = (uint32_t)a;
|
||||
uint64_t a_hi = a >> 32;
|
||||
uint64_t b_lo = (uint32_t)b;
|
||||
uint64_t b_hi = b >> 32;
|
||||
|
||||
uint64_t p0 = a_lo * b_lo;
|
||||
uint64_t p1 = a_lo * b_hi;
|
||||
uint64_t p2 = a_hi * b_lo;
|
||||
uint64_t p3 = a_hi * b_hi;
|
||||
|
||||
uint64_t middle = p1 + (p0 >> 32) + (uint32_t)p2;
|
||||
uint64_t low = (middle << 32) | (uint32_t)p0;
|
||||
uint64_t high = p3 + (middle >> 32) + (p2 >> 32);
|
||||
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#endif
|
||||
|
||||
int util_selftest(void)
|
||||
{
|
||||
/* rotate left 32-bit */
|
||||
|
|
@ -748,33 +643,33 @@ int util_selftest(void)
|
|||
if (util_rotr64(0x123456789ABCDEF0ULL, 0) != 0x123456789ABCDEF0ULL) return -1;
|
||||
if (util_rotr64(0x123456789ABCDEF0ULL, 64) != 0x123456789ABCDEF0ULL) return -1;
|
||||
|
||||
/* util_mum tests */
|
||||
/* tommy_mum tests */
|
||||
uint64_t a, b;
|
||||
|
||||
/* small values */
|
||||
a = 3; b = 4;
|
||||
util_mum(&a, &b);
|
||||
tommy_mum(&a, &b);
|
||||
if (a != 12 || b != 0) return -1;
|
||||
|
||||
/* 1 * anything */
|
||||
a = 0xFFFFFFFFFFFFFFFFULL; b = 1;
|
||||
util_mum(&a, &b);
|
||||
tommy_mum(&a, &b);
|
||||
if (a != 0xFFFFFFFFFFFFFFFFULL || b != 0) return -1;
|
||||
|
||||
/* (2^32) * (2^32) = 2^64 */
|
||||
a = 1ULL << 32; b = 1ULL << 32;
|
||||
util_mum(&a, &b);
|
||||
tommy_mum(&a, &b);
|
||||
if (a != 0 || b != 1) return -1;
|
||||
|
||||
/* max uint64 * max uint64 */
|
||||
a = 0xFFFFFFFFFFFFFFFFULL; b = 0xFFFFFFFFFFFFFFFFULL;
|
||||
util_mum(&a, &b);
|
||||
tommy_mum(&a, &b);
|
||||
if (a != 1 || b != 0xFFFFFFFFFFFFFFFEULL) return -1;
|
||||
|
||||
/* larger random-ish test */
|
||||
a = 0x0123456789ABCDEFULL;
|
||||
b = 0xFEDCBA9876543210ULL;
|
||||
util_mum(&a, &b);
|
||||
tommy_mum(&a, &b);
|
||||
if (a != 0x2236D88FE5618CF0ULL || b != 0x121FA00AD77D742ULL) return -1;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,7 +44,78 @@ TOMMY_API void tommy_array_grow(tommy_array* array, tommy_size_t count)
|
|||
}
|
||||
}
|
||||
|
||||
TOMMY_API tommy_size_t tommy_array_memory_usage(tommy_array* array)
|
||||
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(const tommy_array* array)
|
||||
{
|
||||
return array->bucket_max * (tommy_size_t)sizeof(void*);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
*
|
||||
* The grow operation involves an allocation of a new array segment, without reallocating
|
||||
* the already used memory, and thus **not increasing** the heap fragmentation.
|
||||
* This also implies that the address of the stored elements never change.
|
||||
* This also implies that the address of the stored elements never changes.
|
||||
*
|
||||
* Allocated segments grow in size exponentially.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYARRAY_H
|
||||
#define __TOMMYARRAY_H
|
||||
#ifndef TOMMY_ARRAY_H
|
||||
#define TOMMY_ARRAY_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
|
||||
|
|
@ -50,25 +50,62 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference of the element at the specified position.
|
||||
* 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.
|
||||
* Call tommy_array_shrink() after clearing to restore the initial 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 to the element at the specified position.
|
||||
* You must be sure that space for this position is already
|
||||
* allocated calling tommy_array_grow().
|
||||
* allocated by calling tommy_array_grow().
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
|
@ -76,7 +113,7 @@ tommy_inline void** tommy_array_ref(tommy_array* array, tommy_size_t pos)
|
|||
/**
|
||||
* Sets the element at the specified position.
|
||||
* You must be sure that space for this position is already
|
||||
* allocated calling tommy_array_grow().
|
||||
* allocated by calling tommy_array_grow().
|
||||
*/
|
||||
tommy_inline void tommy_array_set(tommy_array* array, tommy_size_t pos, void* element)
|
||||
{
|
||||
|
|
@ -86,17 +123,27 @@ tommy_inline void tommy_array_set(tommy_array* array, tommy_size_t pos, void* el
|
|||
/**
|
||||
* Gets the element at the specified position.
|
||||
* You must be sure that space for this position is already
|
||||
* allocated calling tommy_array_grow().
|
||||
* allocated by calling tommy_array_grow().
|
||||
*/
|
||||
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,17 +152,90 @@ 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);
|
||||
|
||||
tommy_size_t pos = array->count - 1;
|
||||
void** ptr = tommy_array_ref(array, pos);
|
||||
void* element = *ptr;
|
||||
|
||||
/* keep unused slots zero so growing within capacity needs no initialization */
|
||||
*ptr = 0;
|
||||
array->count = pos;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the array is empty.
|
||||
*/
|
||||
tommy_inline tommy_bool_t tommy_array_empty(const tommy_array* array)
|
||||
{
|
||||
return array->count == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the initialized size of the array.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_array_size(tommy_array* array)
|
||||
tommy_inline tommy_size_t tommy_array_size(const tommy_array* array)
|
||||
{
|
||||
return array->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of elements that fit without further allocation.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_array_capacity(const 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.
|
||||
*
|
||||
* If the callback frees the objects, the array retains dangling
|
||||
* pointers. Call tommy_array_clear() before reusing it, or
|
||||
* tommy_array_done() to release its storage.
|
||||
*/
|
||||
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);
|
||||
TOMMY_API tommy_size_t tommy_array_memory_usage(const tommy_array* array);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -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,127 @@ 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);
|
||||
|
||||
++block_mac;
|
||||
}
|
||||
array->block[array->block_count] = ptr;
|
||||
++array->block_count;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API tommy_size_t tommy_arrayblkof_memory_usage(tommy_arrayblkof* array)
|
||||
TOMMY_API void tommy_arrayblkof_resize(tommy_arrayblkof* array, tommy_size_t size)
|
||||
{
|
||||
return tommy_array_memory_usage(&array->block) + tommy_array_size(&array->block) * TOMMY_ARRAYBLKOF_SIZE * array->element_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)
|
||||
{
|
||||
/* cache the element size to avoid reloading it after each callback */
|
||||
tommy_size_t element_size = array->element_size;
|
||||
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 += element_size;
|
||||
}
|
||||
|
||||
pos += chunk;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_arrayblkof_foreach_arg(tommy_arrayblkof* array, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
/* cache the element size to avoid reloading it after each callback */
|
||||
tommy_size_t element_size = array->element_size;
|
||||
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 += element_size;
|
||||
}
|
||||
|
||||
pos += chunk;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API tommy_size_t tommy_arrayblkof_memory_usage(const tommy_arrayblkof* array)
|
||||
{
|
||||
return array->block_capacity * sizeof(array->block[0]) + array->block_count * TOMMY_ARRAYBLKOF_SIZE * array->element_size;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,27 +4,41 @@
|
|||
/** \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
|
||||
* This is very similar to ::tommy_arrayblk, but it allows storing 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.
|
||||
*
|
||||
* \note Only types with fundamental alignment requirements are supported.
|
||||
* Storage is allocated using calloc(), which does not guarantee the alignment
|
||||
* required by over-aligned types (e.g. _Alignas(32) or _Alignas(64)).
|
||||
* When storing typed elements, use sizeof(type) as the element size to
|
||||
* preserve alignment for every element.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYARRAYBLKOF_H
|
||||
#define __TOMMYARRAYBLKOF_H
|
||||
#ifndef TOMMY_ARRAYBLKOF_H
|
||||
#define TOMMY_ARRAYBLKOF_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
#include "tommyarray.h"
|
||||
|
||||
#include <assert.h> /* for assert */
|
||||
#include <string.h> /* for memset */
|
||||
|
||||
/******************************************************************************/
|
||||
/* 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,14 +49,17 @@
|
|||
* \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;
|
||||
|
||||
/**
|
||||
* Initializes the array.
|
||||
* \param element_size Size in byte of the element to store in the array.
|
||||
* \param array Array to initialize.
|
||||
* \param element_size Size in bytes of the element to store in the array.
|
||||
*/
|
||||
TOMMY_API void tommy_arrayblkof_init(tommy_arrayblkof* array, tommy_size_t element_size);
|
||||
|
||||
|
|
@ -51,39 +68,164 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference of the element at the specified position.
|
||||
* 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.
|
||||
* Call tommy_arrayblkof_shrink() after clearing to restore the initial 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 to the element at the specified position.
|
||||
* You must be sure that space for this position is already
|
||||
* allocated calling tommy_arrayblkof_grow().
|
||||
* allocated by calling tommy_arrayblkof_grow().
|
||||
*/
|
||||
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_size_t pos = array->count - 1;
|
||||
void* ptr = tommy_arrayblkof_ref(array, pos);
|
||||
|
||||
/* keep unused slots zero so growing within capacity needs no initialization */
|
||||
memset(ptr, 0, array->element_size);
|
||||
array->count = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the array is empty.
|
||||
*/
|
||||
tommy_inline tommy_bool_t tommy_arrayblkof_empty(const tommy_arrayblkof* array)
|
||||
{
|
||||
return array->count == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the initialized size of the array.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_arrayblkof_size(tommy_arrayblkof* array)
|
||||
tommy_inline tommy_size_t tommy_arrayblkof_size(const tommy_arrayblkof* array)
|
||||
{
|
||||
return array->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of elements that fit without further allocation.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_arrayblkof_capacity(const 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.
|
||||
*
|
||||
* Elements are stored inside the array: passing free() as the callback
|
||||
* is invalid. A callback may release resources owned by each element,
|
||||
* but must not free the element itself. Call tommy_arrayblkof_clear() before
|
||||
* reusing it, or tommy_arrayblkof_done() to release its storage.
|
||||
*/
|
||||
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);
|
||||
TOMMY_API tommy_size_t tommy_arrayblkof_memory_usage(const tommy_arrayblkof* array);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
* Do not use this directly. Use lists instead.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYCHAIN_H
|
||||
#define __TOMMYCHAIN_H
|
||||
#ifndef TOMMY_CHAIN_H
|
||||
#define TOMMY_CHAIN_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
|
||||
|
|
@ -65,31 +65,39 @@ 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)
|
||||
tommy_inline void tommy_chain_merge(tommy_chain* tommy_restrict first, tommy_chain* tommy_restrict second, tommy_compare_func* cmp)
|
||||
{
|
||||
tommy_node* first_i = first->head;
|
||||
/*
|
||||
* comparators must not change the chains, so cache these endpoints across calls.
|
||||
* also caching first_tail caused register spills with GCC and Clang on x86-64.
|
||||
*/
|
||||
tommy_node* first_head = first->head;
|
||||
tommy_node* second_tail = second->tail;
|
||||
tommy_node* first_i = first_head;
|
||||
tommy_node* second_i = second->head;
|
||||
|
||||
/* merge */
|
||||
while (1) {
|
||||
if (cmp(first_i->data, second_i->data) > 0) {
|
||||
tommy_node* next = second_i->next;
|
||||
if (first_i == first->head) {
|
||||
if (first_i == first_head) {
|
||||
tommy_chain_concat(second_i, first_i);
|
||||
first->head = second_i;
|
||||
first_head = second_i;
|
||||
first->head = first_head;
|
||||
} else {
|
||||
tommy_chain_splice(first_i->prev, first_i, second_i, second_i);
|
||||
}
|
||||
if (second_i == second->tail)
|
||||
if (second_i == second_tail)
|
||||
break;
|
||||
second_i = next;
|
||||
} else {
|
||||
if (first_i == first->tail) {
|
||||
tommy_chain_concat(first_i, second_i);
|
||||
first->tail = second->tail;
|
||||
first->tail = second_tail;
|
||||
break;
|
||||
}
|
||||
first_i = first_i->next;
|
||||
|
|
@ -98,13 +106,36 @@ 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).
|
||||
* Merges two chains containing one node each.
|
||||
* \param first First singleton chain (will contain the result).
|
||||
* \param second Second singleton 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)
|
||||
tommy_inline void tommy_chain_merge_singleton(tommy_chain* tommy_restrict first, tommy_chain* tommy_restrict second, tommy_compare_func* cmp)
|
||||
{
|
||||
tommy_node* first_node = first->head;
|
||||
tommy_node* second_node = second->head;
|
||||
|
||||
/* keep equal elements in their original order for a stable sort */
|
||||
if (cmp(first_node->data, second_node->data) <= 0) {
|
||||
tommy_chain_concat(first_node, second_node);
|
||||
first->tail = second_node;
|
||||
} else {
|
||||
tommy_chain_concat(second_node, first_node);
|
||||
first->head = second_node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two chains managing special degenerate 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 (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* tommy_restrict first, tommy_chain* tommy_restrict second, tommy_compare_func* cmp)
|
||||
{
|
||||
/* identify the condition first <= second */
|
||||
if (cmp(first->tail->data, second->head->data) <= 0) {
|
||||
|
|
@ -128,7 +159,7 @@ tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain*
|
|||
* Sorts a chain.
|
||||
* It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity,
|
||||
* similar to the one used in the SGI STL libraries and in the Linux Kernel,
|
||||
* but faster on degenerated cases like already ordered lists.
|
||||
* but faster on degenerate cases like already ordered lists.
|
||||
*
|
||||
* SGI STL stl_list.h
|
||||
* http://www.sgi.com/tech/stl/stl_list.h
|
||||
|
|
@ -143,36 +174,41 @@ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func*
|
|||
{
|
||||
/*
|
||||
* Bit buckets of chains.
|
||||
* Each bucket contains 2^i nodes or it's empty.
|
||||
* Each bucket contains 2^(i+1) nodes or it's empty.
|
||||
* The bucket containing an unpaired final node has one node less.
|
||||
* The chain at address TOMMY_BIT_MAX is an independent variable operating as "carry".
|
||||
* We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck.
|
||||
*/
|
||||
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;
|
||||
|
||||
/* start with a sorted pair to halve the number of bucket insertions */
|
||||
if (node != tail) {
|
||||
node = next;
|
||||
/* save the next input node before merging can change the links */
|
||||
next = node->next;
|
||||
tommy_chain second = { node, node };
|
||||
tommy_chain_merge_singleton(last, &second, cmp);
|
||||
}
|
||||
|
||||
/* 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 +228,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 +243,4 @@ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func*
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// SPDX-License-Identifier: BSD-2-Clause AND MIT
|
||||
// Copyright (C) 2010 Andrea Mazzoleni
|
||||
|
||||
#include "tommyhash.h"
|
||||
|
|
@ -13,222 +13,157 @@
|
|||
* They are needed only if BigEndian.
|
||||
*/
|
||||
#if defined(__GNUC__)
|
||||
#define tommy_swap32(x) __builtin_bswap32(x)
|
||||
#define tommy_imp_hash_swap32(x) __builtin_bswap32(x)
|
||||
#else
|
||||
tommy_inline tommy_uint32_t tommy_swap32(tommy_uint32_t v)
|
||||
tommy_inline tommy_uint32_t tommy_imp_hash_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);
|
||||
}
|
||||
#endif
|
||||
|
||||
tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr)
|
||||
tommy_inline tommy_uint32_t tommy_imp_hash_le_uint32_read(const void* ptr)
|
||||
{
|
||||
tommy_uint32_t v;
|
||||
memcpy(&v, ptr, sizeof(v));
|
||||
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
v = tommy_swap32(v);
|
||||
v = tommy_imp_hash_swap32(v);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
#define tommy_rot(x, k) \
|
||||
(((x) << (k)) | ((x) >> (32 - (k))))
|
||||
/******************************************************************************/
|
||||
/* a5hash */
|
||||
|
||||
#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)
|
||||
/**
|
||||
* a5hash32 and a5hash (64-bit) by Aleksey Vaneev, from https://github.com/avaneev/a5hash.
|
||||
* Adapted from version 5.25 with little-endian word reads.
|
||||
* Copyright (c) 2025 Aleksey Vaneev. Released under the MIT license, see LICENSES/MIT.
|
||||
*/
|
||||
|
||||
#define tommy_final(a, b, c) \
|
||||
do { \
|
||||
c ^= b; c -= tommy_rot(b, 14); \
|
||||
a ^= c; a -= tommy_rot(c, 11); \
|
||||
b ^= a; b -= tommy_rot(a, 25); \
|
||||
c ^= b; c -= tommy_rot(b, 16); \
|
||||
a ^= c; a -= tommy_rot(c, 4); \
|
||||
b ^= a; b -= tommy_rot(a, 14); \
|
||||
c ^= b; c -= tommy_rot(b, 24); \
|
||||
} while (0)
|
||||
tommy_inline void tommy_imp_a5hash_mul(tommy_uint32_t a, tommy_uint32_t b, tommy_uint32_t* lo, tommy_uint32_t* hi)
|
||||
{
|
||||
/* both operands are 32-bit; the full product supplies two state words */
|
||||
tommy_uint64_t v = (tommy_uint64_t)a * b;
|
||||
*lo = (tommy_uint32_t)v;
|
||||
*hi = v >> 32;
|
||||
}
|
||||
|
||||
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;
|
||||
tommy_uint32_t val01 = 0x55555555;
|
||||
tommy_uint32_t val10 = 0xaaaaaaaa;
|
||||
tommy_uint32_t h[4] = { 0x243f6a88, 0x85a308d3, 0xfb0bd3ea, 0x0f58fd47 };
|
||||
tommy_uint32_t a, b;
|
||||
tommy_size_t len = key_len;
|
||||
|
||||
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val;
|
||||
h[0] ^= key_len;
|
||||
h[1] ^= key_len;
|
||||
#if TOMMY_SIZE_BIT == 64
|
||||
tommy_imp_a5hash_mul((key_len >> 32) ^ 0x452821e6, (key_len >> 32) ^ 0x38d01377, &h[2], &h[3]);
|
||||
#endif
|
||||
tommy_imp_a5hash_mul(h[1] ^ (init_val & val10), h[0] ^ (init_val & val01), &h[0], &h[1]);
|
||||
|
||||
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);
|
||||
if (len <= 16) {
|
||||
if (len >= 4) {
|
||||
const unsigned char* tail = key + len - 4;
|
||||
a = tommy_imp_hash_le_uint32_read(key);
|
||||
b = tommy_imp_hash_le_uint32_read(tail);
|
||||
if (len >= 9) {
|
||||
tommy_size_t offset = (len / 8) * 4;
|
||||
tommy_uint32_t c = tommy_imp_hash_le_uint32_read(key + offset);
|
||||
tommy_uint32_t d = tommy_imp_hash_le_uint32_read(tail - offset);
|
||||
tommy_imp_a5hash_mul(c + h[2], d + h[3], &h[2], &h[3]);
|
||||
}
|
||||
} else {
|
||||
a = 0;
|
||||
b = 0;
|
||||
if (len != 0) {
|
||||
a = key[0];
|
||||
if (len >= 2)
|
||||
a |= key[1] << 8;
|
||||
if (len == 3)
|
||||
a |= key[2] << 16;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val01 ^= h[0];
|
||||
val10 ^= h[1];
|
||||
do {
|
||||
tommy_uint32_t s1 = h[0];
|
||||
tommy_uint32_t s4 = h[3];
|
||||
tommy_imp_a5hash_mul(tommy_imp_hash_le_uint32_read(key) + h[0], tommy_imp_hash_le_uint32_read(key + 4) + h[1], &h[0], &h[1]);
|
||||
tommy_imp_a5hash_mul(tommy_imp_hash_le_uint32_read(key + 8) + h[2], tommy_imp_hash_le_uint32_read(key + 12) + h[3], &h[2], &h[3]);
|
||||
h[0] += val01;
|
||||
h[1] += s4;
|
||||
h[2] += s1;
|
||||
h[3] += val10;
|
||||
key += 16;
|
||||
len -= 16;
|
||||
} while (len > 16);
|
||||
|
||||
tommy_mix(a, b, c);
|
||||
|
||||
key_len -= 12;
|
||||
key += 12;
|
||||
/* the last words may overlap the previous block, but stay within the input */
|
||||
const unsigned char* tail = key + len;
|
||||
a = tommy_imp_hash_le_uint32_read(tail - 8);
|
||||
b = tommy_imp_hash_le_uint32_read(tail - 4);
|
||||
if (len >= 9) {
|
||||
tommy_uint32_t c = tommy_imp_hash_le_uint32_read(tail - 16);
|
||||
tommy_uint32_t d = tommy_imp_hash_le_uint32_read(tail - 12);
|
||||
tommy_imp_a5hash_mul(c + h[2], d + h[3], &h[2], &h[3]);
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
}
|
||||
|
||||
tommy_final(a, b, c);
|
||||
|
||||
return c;
|
||||
h[0] ^= h[2];
|
||||
h[1] ^= h[3];
|
||||
tommy_imp_a5hash_mul(a + h[0], b + h[1], &h[0], &h[1]);
|
||||
tommy_imp_a5hash_mul(val01 ^ h[0], h[1], &a, &b);
|
||||
return a ^ b;
|
||||
}
|
||||
|
||||
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;
|
||||
tommy_uint64_t val01 = 0x5555555555555555ULL;
|
||||
tommy_uint64_t val10 = 0xaaaaaaaaaaaaaaaaULL;
|
||||
tommy_uint64_t h[2] = { (0x452821e638d01377ULL ^ key_len) ^ (init_val & val10), (0x243f6a8885a308d3ULL ^ key_len) ^ (init_val & val01) };
|
||||
tommy_size_t len = key_len;
|
||||
|
||||
a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff);
|
||||
c += init_val >> 32;
|
||||
|
||||
while (key_len > 12) {
|
||||
a += tommy_le_uint32_read(key + 0);
|
||||
b += tommy_le_uint32_read(key + 4);
|
||||
c += tommy_le_uint32_read(key + 8);
|
||||
|
||||
tommy_mix(a, b, c);
|
||||
|
||||
key_len -= 12;
|
||||
key += 12;
|
||||
tommy_mum(&h[0], &h[1]);
|
||||
if (len > 16) {
|
||||
val01 ^= h[0];
|
||||
val10 ^= h[1];
|
||||
do {
|
||||
/* each 64-bit operand puts the first little-endian word in its high half */
|
||||
h[0] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key) << 32 ^ tommy_imp_hash_le_uint32_read(key + 4);
|
||||
h[1] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key + 8) << 32 ^ tommy_imp_hash_le_uint32_read(key + 12);
|
||||
tommy_mum(&h[0], &h[1]);
|
||||
h[0] += val01;
|
||||
h[1] += val10;
|
||||
key += 16;
|
||||
len -= 16;
|
||||
} while (len > 16);
|
||||
}
|
||||
|
||||
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 (len >= 4) {
|
||||
const unsigned char* tail = key + len - 4;
|
||||
tommy_size_t offset = (len / 8) * 4;
|
||||
h[0] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key) << 32 | tommy_imp_hash_le_uint32_read(tail);
|
||||
h[1] ^= (tommy_uint64_t)tommy_imp_hash_le_uint32_read(key + offset) << 32 | tommy_imp_hash_le_uint32_read(tail - offset);
|
||||
} else if (len != 0) {
|
||||
h[0] ^= key[0];
|
||||
if (len >= 2)
|
||||
h[0] ^= key[1] << 8;
|
||||
if (len == 3)
|
||||
h[0] ^= key[2] << 16;
|
||||
}
|
||||
|
||||
tommy_final(a, b, c);
|
||||
|
||||
return c + ((tommy_uint64_t)b << 32);
|
||||
}
|
||||
|
||||
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 };
|
||||
|
||||
a = b = c = 0xdeadbeef + init_val;
|
||||
/* this is different than original lookup3 and the result won't match */
|
||||
|
||||
while (1) {
|
||||
tommy_uint32_t v = tommy_le_uint32_read(key);
|
||||
|
||||
if (tommy_haszero_u32(v)) {
|
||||
if (v & m[0]) {
|
||||
a += v & m[0];
|
||||
if (v & m[1]) {
|
||||
a += v & m[1];
|
||||
if (v & m[2])
|
||||
a += v & m[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
a += v;
|
||||
|
||||
v = tommy_le_uint32_read(key + 4);
|
||||
|
||||
if (tommy_haszero_u32(v)) {
|
||||
if (v & m[0]) {
|
||||
b += v & m[0];
|
||||
if (v & m[1]) {
|
||||
b += v & m[1];
|
||||
if (v & m[2])
|
||||
b += v & m[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
b += v;
|
||||
|
||||
v = tommy_le_uint32_read(key + 8);
|
||||
|
||||
if (tommy_haszero_u32(v)) {
|
||||
if (v & m[0]) {
|
||||
c += v & m[0];
|
||||
if (v & m[1]) {
|
||||
c += v & m[1];
|
||||
if (v & m[2])
|
||||
c += v & m[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
c += v;
|
||||
|
||||
tommy_mix(a, b, c);
|
||||
|
||||
key += 12;
|
||||
}
|
||||
|
||||
/* for lengths that are multipliers of 12 we already have called mix */
|
||||
/* this is different than the original lookup3 and the result won't match */
|
||||
|
||||
tommy_final(a, b, c);
|
||||
|
||||
return c;
|
||||
tommy_mum(&h[0], &h[1]);
|
||||
h[0] ^= val01;
|
||||
tommy_mum(&h[0], &h[1]);
|
||||
return h[0] ^ h[1];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,76 +3,130 @@
|
|||
|
||||
/** \file
|
||||
* Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin.
|
||||
*
|
||||
* This module provides hash functions designed for in-memory data structures.
|
||||
* ::tommy_hash_u32 and ::tommy_strhash_u32 use a5hash32, while
|
||||
* ::tommy_hash_u64 and ::tommy_strhash_u64 use a5hash (64-bit).
|
||||
* Reversible integer hash functions are also provided for 32-bit and 64-bit keys.
|
||||
*
|
||||
* a5hash32 is intended for 32-bit platforms and uses 32-bit operands with
|
||||
* full 64-bit multiplication results. a5hash uses 64-bit operands with
|
||||
* full 128-bit multiplication results. Both algorithms pass SMHasher3 tests.
|
||||
*
|
||||
* Prefer the 64-bit version whenever possible, as it provides better resistance
|
||||
* to collisions, particularly with specially crafted inputs. However, neither
|
||||
* version is designed to resist deliberate collision attacks. If resistance to
|
||||
* such attacks is required, use a dedicated hash function designed for that purpose.
|
||||
*
|
||||
* All hash functions are intended for runtime in-memory use only and not
|
||||
* for persistent storage, as hash compatibility across different versions of the
|
||||
* library is not guaranteed. String and sized buffer hash functions produce
|
||||
* identical results for 0-terminated strings.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYHASH_H
|
||||
#define __TOMMYHASH_H
|
||||
#ifndef TOMMY_HASH_H
|
||||
#define TOMMY_HASH_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
#include <string.h> /* for strlen */
|
||||
|
||||
/******************************************************************************/
|
||||
/* hash */
|
||||
|
||||
/**
|
||||
* 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().
|
||||
* Hash function with a 32-bit result.
|
||||
* Implementation of a5hash32 by Aleksey Vaneev,
|
||||
* from https://github.com/avaneev/a5hash
|
||||
*
|
||||
* 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.
|
||||
* This algorithm is compact, fast on short inputs, and passes SMHasher3 tests.
|
||||
*
|
||||
* \param init_val Initialization value.
|
||||
* Using a different initialization value, you can generate a completely different set of hash values.
|
||||
* Use 0 if not relevant.
|
||||
* \param void_key Pointer to the data to hash.
|
||||
* \param void_key Pointer to the data to hash. Must not be NULL, even if key_len is 0.
|
||||
* \param key_len Size of the data to hash.
|
||||
* \note
|
||||
* This function is endianness independent.
|
||||
* \return The hash value of 32 bits.
|
||||
* 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 non-cryptographic and is not designed to protect against malicious collision attacks.
|
||||
* \return The 32-bit hash value.
|
||||
*/
|
||||
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().
|
||||
* Hash function with a 64-bit result.
|
||||
* Implementation of a5hash (64-bit) by Aleksey Vaneev,
|
||||
* from https://github.com/avaneev/a5hash
|
||||
*
|
||||
* 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.
|
||||
* This algorithm is compact, fast on short inputs, and passes SMHasher3 tests.
|
||||
*
|
||||
* \param init_val Initialization value.
|
||||
* Using a different initialization value, you can generate a completely different set of hash values.
|
||||
* Use 0 if not relevant.
|
||||
* \param void_key Pointer to the data to hash.
|
||||
* \param void_key Pointer to the data to hash. Must not be NULL, even if key_len is 0.
|
||||
* \param key_len Size of the data to hash.
|
||||
* \note
|
||||
* This function is endianness independent.
|
||||
* \return The hash value of 64 bits.
|
||||
* This function is endianness independent and alignment independent.
|
||||
* It produces identical hash results to ::tommy_strhash_u64 for 0-terminated strings.
|
||||
* It is non-cryptographic and is not designed to protect against malicious collision attacks.
|
||||
* \return The 64-bit hash value.
|
||||
*/
|
||||
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().
|
||||
* String hash function with a 32-bit result.
|
||||
* Implementation of a5hash32 by Aleksey Vaneev,
|
||||
* from https://github.com/avaneev/a5hash
|
||||
*
|
||||
* 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.
|
||||
* It is implemented inline using strlen() followed by ::tommy_hash_u32.
|
||||
* This gives better performance than scanning for the terminator while hashing.
|
||||
*
|
||||
* \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.
|
||||
* \param void_key Pointer to the string to hash. It has to be 0-terminated.
|
||||
* \note
|
||||
* This function is endianness independent.
|
||||
* \return The hash value of 32 bits.
|
||||
* This function is endianness independent and alignment independent.
|
||||
* It produces identical hash results to ::tommy_hash_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 non-cryptographic and is not designed to protect against malicious collision attacks.
|
||||
* \return The 32-bit hash value.
|
||||
*/
|
||||
TOMMY_API tommy_uint32_t tommy_strhash_u32(tommy_uint32_t init_val, const void* void_key);
|
||||
tommy_inline tommy_uint32_t tommy_strhash_u32(tommy_uint32_t init_val, const void* void_key)
|
||||
{
|
||||
return tommy_hash_u32(init_val, void_key, strlen(tommy_cast(const char*, void_key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Integer reversible hash function for 32 bits.
|
||||
* String hash function with a 64-bit result.
|
||||
* Implementation of a5hash (64-bit) by Aleksey Vaneev,
|
||||
* from https://github.com/avaneev/a5hash
|
||||
*
|
||||
* This hash is designed to handle strings with an unknown length.
|
||||
* It is implemented inline using strlen() followed by ::tommy_hash_u64.
|
||||
* This gives better performance than scanning for the terminator while hashing.
|
||||
*
|
||||
* \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.
|
||||
* It produces identical hash results to ::tommy_hash_u64 for 0-terminated strings.
|
||||
* It is non-cryptographic and is not designed to protect against malicious collision attacks.
|
||||
* \return The 64-bit hash value.
|
||||
*/
|
||||
tommy_inline tommy_uint64_t tommy_strhash_u64(tommy_uint64_t init_val, const void* void_key)
|
||||
{
|
||||
return tommy_hash_u64(init_val, void_key, strlen(tommy_cast(const char*, void_key)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reversible 32-bit integer hash function.
|
||||
* Implementation of the Robert Jenkins "4-byte Integer Hashing",
|
||||
* from http://burtleburtle.net/bob/hash/integer.html
|
||||
*/
|
||||
|
|
@ -90,7 +144,7 @@ tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key)
|
|||
}
|
||||
|
||||
/**
|
||||
* Integer reversible hash function for 64 bits.
|
||||
* Reversible 64-bit integer hash function.
|
||||
* Implementation of the Thomas Wang "Integer Hash Function",
|
||||
* from http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
*/
|
||||
|
|
@ -107,4 +161,105 @@ tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key)
|
|||
return key;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* multiplication */
|
||||
|
||||
/**
|
||||
* Multiply two 64-bit unsigned numbers to obtain the full 128-bit product.
|
||||
*
|
||||
* \param A Address of the first operand, overwritten with the low 64 bits of the product.
|
||||
* \param B Address of the second operand, overwritten with the high 64 bits of the product.
|
||||
*/
|
||||
#if defined(_MSC_VER) && defined(_M_X64)
|
||||
/**
|
||||
* MSVC x64 does not support inline assembly, so use its full-product intrinsic.
|
||||
*/
|
||||
#pragma intrinsic(_umul128)
|
||||
tommy_inline void tommy_mum(tommy_uint64_t* A, tommy_uint64_t* B)
|
||||
{
|
||||
tommy_uint64_t high;
|
||||
tommy_uint64_t low = _umul128(*A, *B, &high);
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#elif defined(_MSC_VER) && defined(_M_ARM64)
|
||||
#pragma intrinsic(__umulh)
|
||||
tommy_inline void tommy_mum(tommy_uint64_t* A, tommy_uint64_t* B)
|
||||
{
|
||||
tommy_uint64_t a = *A;
|
||||
tommy_uint64_t b = *B;
|
||||
*A = a * b;
|
||||
*B = __umulh(a, b);
|
||||
}
|
||||
#elif defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__))
|
||||
/**
|
||||
* Keep this before __SIZEOF_INT128__ because the explicit mulq generates more efficient code.
|
||||
*/
|
||||
tommy_inline void tommy_mum(tommy_uint64_t* A, tommy_uint64_t* B)
|
||||
{
|
||||
tommy_uint64_t a = *A;
|
||||
tommy_uint64_t b = *B;
|
||||
tommy_uint64_t low, high;
|
||||
|
||||
/* require a register to avoid Clang spilling the operand before mulq */
|
||||
__asm__ (
|
||||
"mulq %3"
|
||||
: "=a" (low), "=d" (high)
|
||||
: "a" (a), "r" (b)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#elif defined(__SIZEOF_INT128__)
|
||||
tommy_inline void tommy_mum(tommy_uint64_t* A, tommy_uint64_t* B)
|
||||
{
|
||||
__uint128_t product = (__uint128_t)*A * *B;
|
||||
*A = product;
|
||||
*B = product >> 64;
|
||||
}
|
||||
#elif defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__))
|
||||
/**
|
||||
* Keep this after __SIZEOF_INT128__ because the assembly path failed optimized
|
||||
* hash tests on Apple clang 21.0.0. See https://github.com/amadvance/snapraid/issues/43.
|
||||
*/
|
||||
tommy_inline void tommy_mum(tommy_uint64_t* A, tommy_uint64_t* B)
|
||||
{
|
||||
tommy_uint64_t a = *A;
|
||||
tommy_uint64_t b = *B;
|
||||
tommy_uint64_t low = a * b;
|
||||
tommy_uint64_t high;
|
||||
__asm__ (
|
||||
"umulh %0, %1, %2"
|
||||
: "=r" (high)
|
||||
: "r" (a), "r" (b)
|
||||
);
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#else
|
||||
tommy_inline void tommy_mum(tommy_uint64_t* A, tommy_uint64_t* B)
|
||||
{
|
||||
tommy_uint64_t a = *A;
|
||||
tommy_uint64_t b = *B;
|
||||
tommy_uint64_t a_lo = (tommy_uint32_t)a;
|
||||
tommy_uint64_t a_hi = a >> 32;
|
||||
tommy_uint64_t b_lo = (tommy_uint32_t)b;
|
||||
tommy_uint64_t b_hi = b >> 32;
|
||||
tommy_uint64_t p0 = a_lo * b_lo;
|
||||
tommy_uint64_t p1 = a_lo * b_hi;
|
||||
tommy_uint64_t p2 = a_hi * b_lo;
|
||||
tommy_uint64_t p3 = a_hi * b_hi;
|
||||
|
||||
/* retain the carry from the middle words in the high half */
|
||||
tommy_uint64_t middle = p1 + (p0 >> 32) + (tommy_uint32_t)p2;
|
||||
tommy_uint64_t low = (middle << 32) | (tommy_uint32_t)p0;
|
||||
tommy_uint64_t high = p3 + (middle >> 32) + (p2 >> 32);
|
||||
*A = low;
|
||||
*B = high;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
#include "tommyhashdyn.h"
|
||||
#include "tommylist.h"
|
||||
|
||||
#include <string.h> /* for memset */
|
||||
|
||||
/******************************************************************************/
|
||||
/* hashdyn */
|
||||
|
||||
|
|
@ -23,10 +25,16 @@ 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.
|
||||
*/
|
||||
static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_uint_t new_bucket_bit)
|
||||
static void tommy_imp_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_uint_t new_bucket_bit)
|
||||
{
|
||||
tommy_size_t bucket_bit = hashdyn->bucket_bit;
|
||||
tommy_size_t bucket_max = hashdyn->bucket_max;
|
||||
|
|
@ -37,57 +45,72 @@ 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];
|
||||
if (j && !j->next) {
|
||||
/* growth keeps old buckets separate, and singleton links are already valid */
|
||||
new_bucket[j->index & new_bucket_mask] = j;
|
||||
continue;
|
||||
}
|
||||
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];
|
||||
|
||||
if (j && !j->next) {
|
||||
/* growth keeps old buckets separate, and singleton links are already valid */
|
||||
new_bucket[j->index & new_bucket_mask] = j;
|
||||
continue;
|
||||
}
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,21 +126,21 @@ static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_uint_t new_bucket
|
|||
/**
|
||||
* Grow.
|
||||
*/
|
||||
tommy_inline void hashdyn_grow_step(tommy_hashdyn* hashdyn)
|
||||
tommy_inline void tommy_imp_hashdyn_grow_step(tommy_hashdyn* hashdyn)
|
||||
{
|
||||
/* grow if more than 50% full */
|
||||
/* grow if at least 50% full */
|
||||
if (hashdyn->count >= hashdyn->bucket_max / 2)
|
||||
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1);
|
||||
tommy_imp_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrink.
|
||||
*/
|
||||
tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn)
|
||||
tommy_inline void tommy_imp_hashdyn_shrink_step(tommy_hashdyn* hashdyn)
|
||||
{
|
||||
/* shrink if less than 12.5% full */
|
||||
/* shrink if at most 12.5% full */
|
||||
if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT)
|
||||
tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1);
|
||||
tommy_imp_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1);
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_hashdyn_reserve(tommy_hashdyn* hashdyn, tommy_size_t count)
|
||||
|
|
@ -130,7 +153,20 @@ TOMMY_API void tommy_hashdyn_reserve(tommy_hashdyn* hashdyn, tommy_size_t count)
|
|||
if (bucket_bit <= hashdyn->bucket_bit)
|
||||
return;
|
||||
|
||||
tommy_hashdyn_resize(hashdyn, bucket_bit);
|
||||
tommy_imp_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_imp_hashdyn_resize(hashdyn, bucket_bit);
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash)
|
||||
|
|
@ -143,7 +179,42 @@ TOMMY_API void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node*
|
|||
|
||||
++hashdyn->count;
|
||||
|
||||
hashdyn_grow_step(hashdyn);
|
||||
tommy_imp_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)
|
||||
{
|
||||
tommy_hashdyn_node** bucket = &hashdyn->bucket[hash & hashdyn->bucket_mask];
|
||||
tommy_hashdyn_node* i = *bucket;
|
||||
|
||||
while (i) {
|
||||
/* compare keys only for matching full hashes, as the bucket may contain different hashes */
|
||||
if (i->index == hash && cmp(cmp_arg, i->data) == 0)
|
||||
return i->data;
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
/* insert before resizing, while the bucket selected for the search is still valid */
|
||||
tommy_list_insert_tail(bucket, node, data);
|
||||
|
||||
node->index = hash;
|
||||
|
||||
++hashdyn->count;
|
||||
|
||||
tommy_imp_hashdyn_grow_step(hashdyn);
|
||||
|
||||
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)
|
||||
|
|
@ -154,7 +225,7 @@ TOMMY_API void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hash
|
|||
|
||||
--hashdyn->count;
|
||||
|
||||
hashdyn_shrink_step(hashdyn);
|
||||
tommy_imp_hashdyn_shrink_step(hashdyn);
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
|
@ -171,7 +242,7 @@ TOMMY_API void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func*
|
|||
|
||||
--hashdyn->count;
|
||||
|
||||
hashdyn_shrink_step(hashdyn);
|
||||
tommy_imp_hashdyn_shrink_step(hashdyn);
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
|
@ -185,9 +256,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 +272,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) {
|
||||
|
|
@ -215,68 +284,28 @@ TOMMY_API void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_a
|
|||
}
|
||||
}
|
||||
|
||||
TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn)
|
||||
TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(const tommy_hashdyn* hashdyn)
|
||||
{
|
||||
return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0])
|
||||
+ tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* \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;
|
||||
tommy_builder builder;
|
||||
tommy_node* builder_tail = tommy_builder_init(&builder);
|
||||
|
||||
/* 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;
|
||||
/* clear buckets as they are transferred to avoid a second scan */
|
||||
for (tommy_size_t pos = 0; pos < hashdyn->bucket_max; ++pos) {
|
||||
tommy_node* head = hashdyn->bucket[pos];
|
||||
if (head) {
|
||||
tommy_node* tail = head->prev;
|
||||
builder_tail = tommy_builder_concat(builder_tail, head, tail);
|
||||
}
|
||||
hashdyn->bucket[pos] = 0;
|
||||
}
|
||||
|
||||
/* clear all */
|
||||
memset(hashdyn->bucket, 0, hashdyn->bucket_max * sizeof(tommy_hashdyn_node*));
|
||||
tommy_list_concat_builder(list, &builder, builder_tail);
|
||||
hashdyn->count = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -110,10 +110,11 @@
|
|||
* See the \ref multiindex example for more detail.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYHASHDYN_H
|
||||
#define __TOMMYHASHDYN_H
|
||||
#ifndef TOMMY_HASHDYN_H
|
||||
#define TOMMY_HASHDYN_H
|
||||
|
||||
#include "tommyhash.h"
|
||||
#include "tommylist.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* hashdyn */
|
||||
|
|
@ -155,25 +156,87 @@ TOMMY_API void tommy_hashdyn_init(tommy_hashdyn* hashdyn);
|
|||
*/
|
||||
TOMMY_API void tommy_hashdyn_done(tommy_hashdyn* hashdyn);
|
||||
|
||||
/**
|
||||
* Exchanges the complete state of two initialized hashtables, including bucket allocations.
|
||||
* The hashtables must not share nodes. Nodes and objects are not accessed or modified.
|
||||
* Existing node pointers remain valid and belong to the other hashtable.
|
||||
* Passing the same hashtable twice has no effect. Both hashtables remain usable and deinitializable.
|
||||
* \param first The first hashtable.
|
||||
* \param second The second hashtable.
|
||||
* \note This operation is O(1) with respect to the number of elements.
|
||||
*/
|
||||
tommy_inline void tommy_hashdyn_swap(tommy_hashdyn* first, tommy_hashdyn* second)
|
||||
{
|
||||
tommy_hashdyn tmp = *first;
|
||||
*first = *second;
|
||||
*second = tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements, preserving the allocated buckets.
|
||||
* Call tommy_hashdyn_shrink() after clearing to restore the initial capacity.
|
||||
* 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.
|
||||
*
|
||||
* It ensures that the hashtable has enough buckets allocated to contain
|
||||
* the specified number of elements without triggering a dynamic resize.
|
||||
* the specified number of elements without triggering a dynamic resize on insertion.
|
||||
* Subsequent removals retain the normal shrinking policy and may reduce
|
||||
* the allocated capacity.
|
||||
* \param hashdyn Hashtable to reserve space in.
|
||||
* \param count Number of elements to reserve space for.
|
||||
*/
|
||||
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 hashdyn Hashtable to insert into.
|
||||
* \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.
|
||||
* If the element is not found, 0 is returned.
|
||||
* If more equal elements are present, the first one is removed.
|
||||
* \param hashdyn Hashtable to remove from.
|
||||
* \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one.
|
||||
* The function should return 0 for equal elements, anything other for different elements.
|
||||
* \param cmp_arg Compare argument passed as first argument of the compare function.
|
||||
|
|
@ -186,7 +249,8 @@ TOMMY_API void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func*
|
|||
* Gets the bucket of the specified hash.
|
||||
* The bucket is guaranteed to contain ALL the elements with the specified hash,
|
||||
* but it can contain also others.
|
||||
* You can access elements in the bucket following the ::next pointer until 0.
|
||||
* You can access elements in the bucket following the tommy_node::next pointer until 0.
|
||||
* \param hashdyn Hashtable to query.
|
||||
* \param hash Hash of the element to find.
|
||||
* \return The head of the bucket, or 0 if empty.
|
||||
*/
|
||||
|
|
@ -199,6 +263,7 @@ tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, to
|
|||
* Searches an element in the hashtable.
|
||||
* You have to provide a compare function and the hash of the element you want to find.
|
||||
* If more equal elements are present, the first one is returned.
|
||||
* \param hashdyn Hashtable to search.
|
||||
* \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one.
|
||||
* The function should return 0 for equal elements, anything other for different elements.
|
||||
* \param cmp_arg Compare argument passed as first argument of the compare function.
|
||||
|
|
@ -225,6 +290,23 @@ 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 hashdyn Hashtable containing the node.
|
||||
* \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.
|
||||
*
|
||||
|
|
@ -255,47 +337,73 @@ TOMMY_API void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hash
|
|||
* // deallocates the hashtable
|
||||
* tommy_hashdyn_done(&hashdyn);
|
||||
* \endcode
|
||||
*
|
||||
* If the callback frees the objects, the hashtable retains dangling
|
||||
* node pointers. Call tommy_hashdyn_clear() before reusing it, or
|
||||
* tommy_hashdyn_done() to release its storage.
|
||||
*/
|
||||
TOMMY_API void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* 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);
|
||||
|
||||
/**
|
||||
* Gets the number of elements.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_hashdyn_count(tommy_hashdyn* hashdyn)
|
||||
tommy_inline tommy_size_t tommy_hashdyn_count(const tommy_hashdyn* hashdyn)
|
||||
{
|
||||
return hashdyn->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if empty.
|
||||
* \return If the hashtable is empty.
|
||||
*/
|
||||
tommy_inline tommy_bool_t tommy_hashdyn_empty(const tommy_hashdyn* hashdyn)
|
||||
{
|
||||
return hashdyn->count == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of buckets.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_hashdyn_bucket_count(const 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.
|
||||
*/
|
||||
TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn);
|
||||
TOMMY_API tommy_size_t tommy_hashdyn_memory_usage(const tommy_hashdyn* hashdyn);
|
||||
|
||||
/**
|
||||
* \brief Transfers all elements from the hashtable into a tommy_list.
|
||||
* Transfers all elements from the 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
|
||||
* - 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 +419,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);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,30 +7,109 @@
|
|||
/** \internal
|
||||
* Setup a list.
|
||||
*/
|
||||
tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail)
|
||||
tommy_inline void tommy_imp_list_set(tommy_list* list, tommy_node* head, tommy_node* tail)
|
||||
{
|
||||
head->prev = tail;
|
||||
tail->next = 0;
|
||||
*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_imp_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_imp_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;
|
||||
|
||||
tommy_chain_mergesort(&chain, cmp);
|
||||
|
||||
/* restore the list */
|
||||
tommy_list_set(list, chain.head, chain.tail);
|
||||
tommy_imp_list_set(list, chain.head, chain.tail);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Double linked list for collisions into hashtables.
|
||||
*
|
||||
* This list is a **doubly** linked list mainly targeted for handling collisions
|
||||
* into an hashtable, but **usable** also as a generic list.
|
||||
* into a hashtable, but **usable** also as a generic list.
|
||||
*
|
||||
* The main feature of this list is to require only one pointer to represent the
|
||||
* list, compared to a classic implementation requiring a head **and** a tail pointers.
|
||||
|
|
@ -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,11 +101,12 @@
|
|||
* \code
|
||||
* // deallocates all the objects iterating the list
|
||||
* tommy_list_foreach(&list, free);
|
||||
* tommy_list_clear(&list); // resets the list before reuse
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYLIST_H
|
||||
#define __TOMMYLIST_H
|
||||
#ifndef TOMMY_LIST_H
|
||||
#define TOMMY_LIST_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
|
||||
|
|
@ -83,6 +118,43 @@
|
|||
*/
|
||||
typedef tommy_node* tommy_list;
|
||||
|
||||
/**
|
||||
* Temporary list builder with an embedded sentinel.
|
||||
* Initialize with tommy_builder_init() and finish with tommy_list_concat_builder().
|
||||
* Keep the builder at the same address until finalization, as node links
|
||||
* refer to its sentinel.
|
||||
*/
|
||||
typedef struct tommy_builder_struct {
|
||||
tommy_node dummy; /**< Sentinel excluded from the resulting list. */
|
||||
} tommy_builder;
|
||||
|
||||
/**
|
||||
* Initializes an empty builder in O(1).
|
||||
* \param builder Builder to initialize in its final location.
|
||||
* \return Initial tail, pointing to the builder's sentinel.
|
||||
*/
|
||||
tommy_inline tommy_node* tommy_builder_init(tommy_builder* builder)
|
||||
{
|
||||
builder->dummy.next = 0;
|
||||
return &builder->dummy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a non-empty chain to a builder in O(1), preserving element order.
|
||||
* \param tail Current tail, initially returned by tommy_builder_init().
|
||||
* \param head Head of the non-empty source chain.
|
||||
* \param last Tail of the source chain. For a single node, pass head again.
|
||||
* The chains must not share nodes. Inner links must be valid;
|
||||
* head's prev and last's next may be undefined.
|
||||
* \return Updated tail to pass to the next append or tommy_list_concat_builder().
|
||||
*/
|
||||
tommy_inline tommy_node* tommy_builder_concat(tommy_node* tail, tommy_node* head, tommy_node* last)
|
||||
{
|
||||
head->prev = tail;
|
||||
tail->next = head;
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the list.
|
||||
* The list is completely inplace, so it doesn't need to be deinitialized.
|
||||
|
|
@ -92,6 +164,20 @@ tommy_inline void tommy_list_init(tommy_list* list)
|
|||
*list = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements.
|
||||
* The list 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_list_foreach() has freed the objects.
|
||||
* \param list The list to clear.
|
||||
* \note This operation is O(1).
|
||||
*/
|
||||
tommy_inline void tommy_list_clear(tommy_list* list)
|
||||
{
|
||||
*list = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the head of the list.
|
||||
* \return The head node. For empty lists 0 is returned.
|
||||
|
|
@ -115,6 +201,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.
|
||||
|
|
@ -287,32 +389,168 @@ tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node
|
|||
}
|
||||
|
||||
/**
|
||||
* Concats two lists.
|
||||
* The second list is concatenated at the first list.
|
||||
* 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;
|
||||
|
||||
/* the new head must retain the tail pointer in the "circular" prev list */
|
||||
if (node->next)
|
||||
node->next->prev = node->prev;
|
||||
|
||||
*list = node->next;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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* head = tommy_list_head(list);
|
||||
|
||||
if (!head)
|
||||
return 0;
|
||||
|
||||
tommy_node* node = head->prev;
|
||||
|
||||
/* a single node is also the head, and its links must remain unchanged */
|
||||
if (node == head) {
|
||||
*list = 0;
|
||||
} else {
|
||||
head->prev = node->prev;
|
||||
node->prev->next = 0;
|
||||
}
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_not_empty(list, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->next == 0)
|
||||
return;
|
||||
|
||||
tommy_list_remove_existing(list, node);
|
||||
tommy_list_insert_tail_not_empty(tommy_list_head(list), node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates two lists.
|
||||
* The second list is concatenated to the first list.
|
||||
* \param first The first list.
|
||||
* \param second The second list. After this call the list content is undefined,
|
||||
* and you should not use it anymore.
|
||||
*/
|
||||
tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second)
|
||||
{
|
||||
tommy_node* first_head;
|
||||
tommy_node* first_tail;
|
||||
tommy_node* second_head;
|
||||
|
||||
/* if the second is empty, nothing to do */
|
||||
second_head = tommy_list_head(second);
|
||||
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,10 +560,108 @@ tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second)
|
|||
first_tail->next = second_head;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a builder's chain to a list, preserving element order and restoring list links.
|
||||
* \param list Destination list, possibly empty. It must not share nodes with the builder.
|
||||
* \param builder Initialized builder, possibly empty. Its contents are consumed;
|
||||
* reinitialize it before reuse.
|
||||
* \param tail Current tail returned by the builder helpers, ignored when empty.
|
||||
* \note This operation is O(1). An empty builder has no effect.
|
||||
*/
|
||||
tommy_inline void tommy_list_concat_builder(tommy_list* list, const tommy_builder* builder, tommy_node* tail)
|
||||
{
|
||||
tommy_node* head = builder->dummy.next;
|
||||
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
tommy_node* first = *list;
|
||||
|
||||
/* replace the sentinel link so no list node refers to the temporary builder */
|
||||
if (first) {
|
||||
tommy_node* first_tail = first->prev;
|
||||
first_tail->next = head;
|
||||
head->prev = first_tail;
|
||||
first->prev = tail;
|
||||
} else {
|
||||
*list = head;
|
||||
head->prev = tail;
|
||||
}
|
||||
|
||||
tail->next = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a list to another list.
|
||||
* The second list is prepended to 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.
|
||||
* It's faster on degenerated cases like partially ordered lists.
|
||||
* It's faster on degenerate cases like partially ordered lists.
|
||||
* \param list The list to sort.
|
||||
* \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.
|
||||
*/
|
||||
|
|
@ -335,19 +671,19 @@ TOMMY_API void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp);
|
|||
* Checks if empty.
|
||||
* \return If the list is empty.
|
||||
*/
|
||||
tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list)
|
||||
tommy_inline tommy_bool_t tommy_list_empty(const tommy_list* list)
|
||||
{
|
||||
return tommy_list_head(list) == 0;
|
||||
return *list == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of elements.
|
||||
* \note This operation is O(n).
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_list_count(tommy_list* list)
|
||||
tommy_inline tommy_size_t tommy_list_count(const tommy_list* list)
|
||||
{
|
||||
tommy_size_t count = 0;
|
||||
tommy_node* i = tommy_list_head(list);
|
||||
const tommy_node* i = *list;
|
||||
|
||||
while (i) {
|
||||
++count;
|
||||
|
|
@ -383,7 +719,12 @@ tommy_inline tommy_size_t tommy_list_count(tommy_list* list)
|
|||
*
|
||||
* // deallocates all the objects iterating the list
|
||||
* tommy_list_foreach(&list, free);
|
||||
* tommy_list_clear(&list); // resets the list before reuse
|
||||
* \endcode
|
||||
*
|
||||
* If the callback frees the objects, the list retains dangling node
|
||||
* pointers. Call tommy_list_clear() or tommy_list_init() before reusing it.
|
||||
* The list has no storage to release.
|
||||
*/
|
||||
tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func)
|
||||
{
|
||||
|
|
@ -398,6 +739,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 +755,4 @@ tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_fun
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#define TOMMY_TREE_BALANCE_LEFT ((tommy_size_t)1)
|
||||
#define TOMMY_TREE_BALANCE_RIGHT ((tommy_size_t)2)
|
||||
|
||||
tommy_inline int tommy_tree_balance_get(tommy_tree_node* node)
|
||||
tommy_inline int tommy_imp_tree_balance_get(tommy_tree_node* node)
|
||||
{
|
||||
tommy_size_t balance = node->index & TOMMY_TREE_BALANCE_MASK;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ tommy_inline int tommy_tree_balance_get(tommy_tree_node* node)
|
|||
return 0;
|
||||
}
|
||||
|
||||
tommy_inline void tommy_tree_balance_set(tommy_tree_node* node, int balance)
|
||||
tommy_inline void tommy_imp_tree_balance_set(tommy_tree_node* node, int balance)
|
||||
{
|
||||
tommy_size_t value = 0;
|
||||
|
||||
|
|
@ -37,12 +37,12 @@ tommy_inline void tommy_tree_balance_set(tommy_tree_node* node, int balance)
|
|||
node->index = (node->index & ~TOMMY_TREE_BALANCE_MASK) | value;
|
||||
}
|
||||
|
||||
tommy_inline void tommy_tree_parent_set(tommy_tree_node* node, tommy_tree_node* parent)
|
||||
tommy_inline void tommy_imp_tree_parent_set(tommy_tree_node* node, tommy_tree_node* parent)
|
||||
{
|
||||
node->index = (tommy_size_t)(tommy_uintptr_t)parent | (node->index & TOMMY_TREE_BALANCE_MASK);
|
||||
}
|
||||
|
||||
tommy_inline void tommy_tree_replace(tommy_tree* tree, tommy_tree_node* root, tommy_tree_node* node)
|
||||
tommy_inline void tommy_imp_tree_replace(tommy_tree* tree, tommy_tree_node* root, tommy_tree_node* node)
|
||||
{
|
||||
tommy_tree_node* parent = tommy_tree_parent(root);
|
||||
|
||||
|
|
@ -55,45 +55,45 @@ tommy_inline void tommy_tree_replace(tommy_tree* tree, tommy_tree_node* root, to
|
|||
}
|
||||
|
||||
if (node)
|
||||
tommy_tree_parent_set(node, parent);
|
||||
tommy_imp_tree_parent_set(node, parent);
|
||||
}
|
||||
|
||||
tommy_inline tommy_tree_node* tommy_tree_rotate_left(tommy_tree* tree, tommy_tree_node* root)
|
||||
tommy_inline tommy_tree_node* tommy_imp_tree_rotate_left(tommy_tree* tree, tommy_tree_node* root)
|
||||
{
|
||||
tommy_tree_node* next = root->next;
|
||||
|
||||
tommy_tree_replace(tree, root, next);
|
||||
tommy_imp_tree_replace(tree, root, next);
|
||||
root->next = next->prev;
|
||||
if (root->next)
|
||||
tommy_tree_parent_set(root->next, root);
|
||||
tommy_imp_tree_parent_set(root->next, root);
|
||||
|
||||
next->prev = root;
|
||||
tommy_tree_parent_set(root, next);
|
||||
tommy_imp_tree_parent_set(root, next);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
tommy_inline tommy_tree_node* tommy_tree_rotate_right(tommy_tree* tree, tommy_tree_node* root)
|
||||
tommy_inline tommy_tree_node* tommy_imp_tree_rotate_right(tommy_tree* tree, tommy_tree_node* root)
|
||||
{
|
||||
tommy_tree_node* prev = root->prev;
|
||||
|
||||
tommy_tree_replace(tree, root, prev);
|
||||
tommy_imp_tree_replace(tree, root, prev);
|
||||
root->prev = prev->next;
|
||||
if (root->prev)
|
||||
tommy_tree_parent_set(root->prev, root);
|
||||
tommy_imp_tree_parent_set(root->prev, root);
|
||||
|
||||
prev->next = root;
|
||||
tommy_tree_parent_set(root, prev);
|
||||
tommy_imp_tree_parent_set(root, prev);
|
||||
|
||||
return prev;
|
||||
}
|
||||
|
||||
tommy_inline void tommy_tree_insert_balance(tommy_tree* tree, tommy_tree_node* node)
|
||||
tommy_inline void tommy_imp_tree_insert_balance(tommy_tree* tree, tommy_tree_node* node)
|
||||
{
|
||||
tommy_tree_node* parent = tommy_tree_parent(node);
|
||||
|
||||
while (parent) {
|
||||
int balance = tommy_tree_balance_get(parent);
|
||||
int balance = tommy_imp_tree_balance_get(parent);
|
||||
|
||||
if (node == parent->prev)
|
||||
--balance;
|
||||
|
|
@ -102,12 +102,12 @@ tommy_inline void tommy_tree_insert_balance(tommy_tree* tree, tommy_tree_node* n
|
|||
}
|
||||
|
||||
if (balance == 0) {
|
||||
tommy_tree_balance_set(parent, balance);
|
||||
tommy_imp_tree_balance_set(parent, balance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (balance >= -1 && balance <= 1) {
|
||||
tommy_tree_balance_set(parent, balance);
|
||||
tommy_imp_tree_balance_set(parent, balance);
|
||||
node = parent;
|
||||
parent = tommy_tree_parent(parent);
|
||||
continue;
|
||||
|
|
@ -115,39 +115,39 @@ tommy_inline void tommy_tree_insert_balance(tommy_tree* tree, tommy_tree_node* n
|
|||
|
||||
if (balance == -2) {
|
||||
tommy_tree_node* left = parent->prev;
|
||||
int left_balance = tommy_tree_balance_get(left);
|
||||
int left_balance = tommy_imp_tree_balance_get(left);
|
||||
|
||||
if (left_balance < 0) {
|
||||
tommy_tree_rotate_right(tree, parent);
|
||||
tommy_tree_balance_set(parent, 0);
|
||||
tommy_tree_balance_set(left, 0);
|
||||
tommy_imp_tree_rotate_right(tree, parent);
|
||||
tommy_imp_tree_balance_set(parent, 0);
|
||||
tommy_imp_tree_balance_set(left, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = left->next;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
int middle_balance = tommy_imp_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_left(tree, left);
|
||||
tommy_tree_rotate_right(tree, parent);
|
||||
tommy_tree_balance_set(parent, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(left, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
tommy_imp_tree_rotate_left(tree, left);
|
||||
tommy_imp_tree_rotate_right(tree, parent);
|
||||
tommy_imp_tree_balance_set(parent, middle_balance < 0 ? 1 : 0);
|
||||
tommy_imp_tree_balance_set(left, middle_balance > 0 ? -1 : 0);
|
||||
tommy_imp_tree_balance_set(middle, 0);
|
||||
}
|
||||
} else {
|
||||
tommy_tree_node* right = parent->next;
|
||||
int right_balance = tommy_tree_balance_get(right);
|
||||
int right_balance = tommy_imp_tree_balance_get(right);
|
||||
|
||||
if (right_balance > 0) {
|
||||
tommy_tree_rotate_left(tree, parent);
|
||||
tommy_tree_balance_set(parent, 0);
|
||||
tommy_tree_balance_set(right, 0);
|
||||
tommy_imp_tree_rotate_left(tree, parent);
|
||||
tommy_imp_tree_balance_set(parent, 0);
|
||||
tommy_imp_tree_balance_set(right, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = right->prev;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
int middle_balance = tommy_imp_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_right(tree, right);
|
||||
tommy_tree_rotate_left(tree, parent);
|
||||
tommy_tree_balance_set(parent, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(right, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
tommy_imp_tree_rotate_right(tree, right);
|
||||
tommy_imp_tree_rotate_left(tree, parent);
|
||||
tommy_imp_tree_balance_set(parent, middle_balance > 0 ? -1 : 0);
|
||||
tommy_imp_tree_balance_set(right, middle_balance < 0 ? 1 : 0);
|
||||
tommy_imp_tree_balance_set(middle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,41 +155,59 @@ 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_imp_tree_insert(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;
|
||||
/* cache the comparator to avoid reloading it after each comparison */
|
||||
tommy_compare_func* cmp_func = tree->cmp;
|
||||
|
||||
while (*link) {
|
||||
/* comparators must not change the tree, so keep the node across the call */
|
||||
parent = *link;
|
||||
int cmp = cmp_func(data, parent->data);
|
||||
|
||||
if (cmp < 0)
|
||||
link = &parent->prev;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (existing)
|
||||
return existing->data;
|
||||
|
||||
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)
|
||||
link = &parent->next;
|
||||
else
|
||||
return parent->data;
|
||||
}
|
||||
|
||||
node->index = (tommy_size_t)(tommy_uintptr_t)parent;
|
||||
*link = node;
|
||||
++tree->count;
|
||||
|
||||
tommy_tree_insert_balance(tree, node);
|
||||
tommy_imp_tree_insert_balance(tree, node);
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* node, tommy_bool_t left_shrunk)
|
||||
TOMMY_API void tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data)
|
||||
{
|
||||
tommy_imp_tree_insert(tree, node, data, 0);
|
||||
}
|
||||
|
||||
TOMMY_API void* tommy_tree_insert_unique(tommy_tree* tree, tommy_tree_node* node, void* data)
|
||||
{
|
||||
return tommy_imp_tree_insert(tree, node, data, 1);
|
||||
}
|
||||
|
||||
tommy_inline void tommy_imp_tree_remove_balance(tommy_tree* tree, tommy_tree_node* node, tommy_bool_t left_shrunk)
|
||||
{
|
||||
while (node) {
|
||||
int balance = tommy_tree_balance_get(node);
|
||||
int balance = tommy_imp_tree_balance_get(node);
|
||||
|
||||
if (left_shrunk)
|
||||
++balance;
|
||||
|
|
@ -197,15 +215,13 @@ tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* n
|
|||
--balance;
|
||||
|
||||
if (balance == -1 || balance == 1) {
|
||||
tommy_tree_balance_set(node, balance);
|
||||
tommy_imp_tree_balance_set(node, balance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (balance == 0) {
|
||||
tommy_tree_node* parent;
|
||||
|
||||
tommy_tree_balance_set(node, balance);
|
||||
parent = tommy_tree_parent(node);
|
||||
tommy_imp_tree_balance_set(node, balance);
|
||||
tommy_tree_node* parent = tommy_tree_parent(node);
|
||||
if (!parent)
|
||||
return;
|
||||
left_shrunk = node == parent->prev;
|
||||
|
|
@ -215,27 +231,27 @@ tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* n
|
|||
|
||||
if (balance == -2) {
|
||||
tommy_tree_node* left = node->prev;
|
||||
int left_balance = tommy_tree_balance_get(left);
|
||||
int left_balance = tommy_imp_tree_balance_get(left);
|
||||
tommy_tree_node* root;
|
||||
|
||||
if (left_balance <= 0) {
|
||||
root = tommy_tree_rotate_right(tree, node);
|
||||
root = tommy_imp_tree_rotate_right(tree, node);
|
||||
if (left_balance == 0) {
|
||||
tommy_tree_balance_set(node, -1);
|
||||
tommy_tree_balance_set(left, 1);
|
||||
tommy_imp_tree_balance_set(node, -1);
|
||||
tommy_imp_tree_balance_set(left, 1);
|
||||
return;
|
||||
}
|
||||
tommy_tree_balance_set(node, 0);
|
||||
tommy_tree_balance_set(left, 0);
|
||||
tommy_imp_tree_balance_set(node, 0);
|
||||
tommy_imp_tree_balance_set(left, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = left->next;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
int middle_balance = tommy_imp_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_left(tree, left);
|
||||
root = tommy_tree_rotate_right(tree, node);
|
||||
tommy_tree_balance_set(node, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(left, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
tommy_imp_tree_rotate_left(tree, left);
|
||||
root = tommy_imp_tree_rotate_right(tree, node);
|
||||
tommy_imp_tree_balance_set(node, middle_balance < 0 ? 1 : 0);
|
||||
tommy_imp_tree_balance_set(left, middle_balance > 0 ? -1 : 0);
|
||||
tommy_imp_tree_balance_set(middle, 0);
|
||||
}
|
||||
|
||||
node = tommy_tree_parent(root);
|
||||
|
|
@ -247,27 +263,27 @@ tommy_inline void tommy_tree_remove_balance(tommy_tree* tree, tommy_tree_node* n
|
|||
|
||||
{
|
||||
tommy_tree_node* right = node->next;
|
||||
int right_balance = tommy_tree_balance_get(right);
|
||||
int right_balance = tommy_imp_tree_balance_get(right);
|
||||
tommy_tree_node* root;
|
||||
|
||||
if (right_balance >= 0) {
|
||||
root = tommy_tree_rotate_left(tree, node);
|
||||
root = tommy_imp_tree_rotate_left(tree, node);
|
||||
if (right_balance == 0) {
|
||||
tommy_tree_balance_set(node, 1);
|
||||
tommy_tree_balance_set(right, -1);
|
||||
tommy_imp_tree_balance_set(node, 1);
|
||||
tommy_imp_tree_balance_set(right, -1);
|
||||
return;
|
||||
}
|
||||
tommy_tree_balance_set(node, 0);
|
||||
tommy_tree_balance_set(right, 0);
|
||||
tommy_imp_tree_balance_set(node, 0);
|
||||
tommy_imp_tree_balance_set(right, 0);
|
||||
} else {
|
||||
tommy_tree_node* middle = right->prev;
|
||||
int middle_balance = tommy_tree_balance_get(middle);
|
||||
int middle_balance = tommy_imp_tree_balance_get(middle);
|
||||
|
||||
tommy_tree_rotate_right(tree, right);
|
||||
root = tommy_tree_rotate_left(tree, node);
|
||||
tommy_tree_balance_set(node, middle_balance > 0 ? -1 : 0);
|
||||
tommy_tree_balance_set(right, middle_balance < 0 ? 1 : 0);
|
||||
tommy_tree_balance_set(middle, 0);
|
||||
tommy_imp_tree_rotate_right(tree, right);
|
||||
root = tommy_imp_tree_rotate_left(tree, node);
|
||||
tommy_imp_tree_balance_set(node, middle_balance > 0 ? -1 : 0);
|
||||
tommy_imp_tree_balance_set(right, middle_balance < 0 ? 1 : 0);
|
||||
tommy_imp_tree_balance_set(middle, 0);
|
||||
}
|
||||
|
||||
node = tommy_tree_parent(root);
|
||||
|
|
@ -289,7 +305,7 @@ TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* no
|
|||
|
||||
parent = tommy_tree_parent(node);
|
||||
left_shrunk = parent && node == parent->prev;
|
||||
tommy_tree_replace(tree, node, child);
|
||||
tommy_imp_tree_replace(tree, node, child);
|
||||
} else {
|
||||
tommy_tree_node* next = node->next;
|
||||
|
||||
|
|
@ -298,67 +314,106 @@ TOMMY_API void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* no
|
|||
|
||||
parent = tommy_tree_parent(next);
|
||||
if (parent == node) {
|
||||
tommy_tree_replace(tree, node, next);
|
||||
tommy_imp_tree_replace(tree, node, next);
|
||||
next->prev = node->prev;
|
||||
tommy_tree_parent_set(next->prev, next);
|
||||
tommy_tree_balance_set(next, tommy_tree_balance_get(node));
|
||||
tommy_imp_tree_parent_set(next->prev, next);
|
||||
tommy_imp_tree_balance_set(next, tommy_imp_tree_balance_get(node));
|
||||
parent = next;
|
||||
left_shrunk = 0;
|
||||
} else {
|
||||
parent->prev = next->next;
|
||||
if (parent->prev)
|
||||
tommy_tree_parent_set(parent->prev, parent);
|
||||
tommy_imp_tree_parent_set(parent->prev, parent);
|
||||
|
||||
tommy_tree_replace(tree, node, next);
|
||||
tommy_imp_tree_replace(tree, node, next);
|
||||
next->prev = node->prev;
|
||||
next->next = node->next;
|
||||
tommy_tree_parent_set(next->prev, next);
|
||||
tommy_tree_parent_set(next->next, next);
|
||||
tommy_tree_balance_set(next, tommy_tree_balance_get(node));
|
||||
tommy_imp_tree_parent_set(next->prev, next);
|
||||
tommy_imp_tree_parent_set(next->next, next);
|
||||
tommy_imp_tree_balance_set(next, tommy_imp_tree_balance_get(node));
|
||||
left_shrunk = 1;
|
||||
}
|
||||
}
|
||||
|
||||
--tree->count;
|
||||
if (parent)
|
||||
tommy_tree_remove_balance(tree, parent, left_shrunk);
|
||||
tommy_imp_tree_remove_balance(tree, parent, left_shrunk);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_tree_foreach_node(tommy_tree_node* root, tommy_foreach_func* func)
|
||||
TOMMY_API void tommy_tree_to_list(tommy_tree* tree, tommy_list* list)
|
||||
{
|
||||
while (root) {
|
||||
tommy_tree_node* next;
|
||||
/* 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;
|
||||
tommy_builder builder;
|
||||
tommy_node* builder_tail = tommy_builder_init(&builder);
|
||||
|
||||
tommy_tree_foreach_node(root->prev, func);
|
||||
while (node || depth) {
|
||||
while (node) {
|
||||
stack[depth] = node;
|
||||
++depth;
|
||||
node = node->prev;
|
||||
}
|
||||
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
node = stack[--depth];
|
||||
/* chain appending overwrites child links, so keep the right subtree reachable */
|
||||
tommy_tree_node* next = node->next;
|
||||
builder_tail = tommy_builder_concat(builder_tail, node, node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
func(root->data);
|
||||
tommy_list_concat_builder(list, &builder, builder_tail);
|
||||
tommy_tree_clear(tree);
|
||||
}
|
||||
|
||||
root = next;
|
||||
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;
|
||||
|
||||
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(node->data);
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API void tommy_tree_foreach_arg_node(tommy_tree_node* root, tommy_foreach_arg_func* func, void* arg)
|
||||
TOMMY_API void tommy_tree_foreach_arg(tommy_tree* tree, tommy_foreach_arg_func* func, void* arg)
|
||||
{
|
||||
while (root) {
|
||||
tommy_tree_node* next;
|
||||
/* 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;
|
||||
|
||||
tommy_tree_foreach_arg_node(root->prev, func, arg);
|
||||
while (node || depth) {
|
||||
while (node) {
|
||||
stack[depth] = node;
|
||||
++depth;
|
||||
node = node->prev;
|
||||
}
|
||||
|
||||
/* make a copy in case func is free() */
|
||||
next = root->next;
|
||||
|
||||
func(arg, root->data);
|
||||
|
||||
root = next;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
TOMMY_API tommy_size_t tommy_tree_memory_usage(tommy_tree* tree)
|
||||
TOMMY_API tommy_size_t tommy_tree_memory_usage(const tommy_tree* tree)
|
||||
{
|
||||
return tommy_tree_count(tree) * sizeof(tommy_tree_node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,22 @@
|
|||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To remove an element from the tree you have to call tommy_tree_remove()
|
||||
* providing the key to search and remove.
|
||||
* \anchor tommy_tree_iterator_validity
|
||||
* Iterators are pointers to nodes contained in the tree.
|
||||
* Inserting elements or removing other nodes between iterator calls does not
|
||||
* invalidate pointers to nodes that remain in the tree.
|
||||
* Subsequent next/prev calls use the current contents and ordering;
|
||||
* inserted elements may become visible and removed elements are skipped.
|
||||
*
|
||||
* Removing the pointed node invalidates its use as an iterator, even if
|
||||
* its storage has not been freed. To continue traversal after removing
|
||||
* the current node, obtain the next or previous node before removal.
|
||||
*
|
||||
* Keys and fields used for ordering must not be modified while contained.
|
||||
* Concurrent traversal and modification require external synchronization.
|
||||
*
|
||||
* 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 };
|
||||
|
|
@ -107,10 +122,11 @@
|
|||
* \endcode
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYTREE_H
|
||||
#define __TOMMYTREE_H
|
||||
#ifndef TOMMY_TREE_H
|
||||
#define TOMMY_TREE_H
|
||||
|
||||
#include "tommytypes.h"
|
||||
#include "tommylist.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* tree */
|
||||
|
|
@ -135,6 +151,7 @@ typedef struct tommy_tree_struct {
|
|||
|
||||
/**
|
||||
* Initializes the tree.
|
||||
* \param tree The tree to initialize.
|
||||
* \param cmp The comparison function that defines the order in the tree.
|
||||
*/
|
||||
tommy_inline void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp)
|
||||
|
|
@ -145,40 +162,80 @@ 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 tree The tree.
|
||||
* \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 tree The tree.
|
||||
* \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.
|
||||
* You must already have the address of the element to remove.
|
||||
* \param tree The tree.
|
||||
* \param node The node to remove.
|
||||
* \return The tommy_node::data field of the node removed.
|
||||
*/
|
||||
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.
|
||||
|
|
@ -191,13 +248,12 @@ tommy_inline tommy_tree_node* tommy_tree_parent(tommy_tree_node* node)
|
|||
|
||||
/**
|
||||
* Gets the node following the specified one in tree order.
|
||||
* See \ref tommy_tree_iterator_validity for iterator validity across insertions and removals.
|
||||
* \param node Node contained in the tree.
|
||||
* \return The following node. For the tail node 0 is returned.
|
||||
*/
|
||||
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 +261,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);
|
||||
|
|
@ -216,13 +272,12 @@ tommy_inline tommy_tree_node* tommy_tree_next(tommy_tree_node* node)
|
|||
|
||||
/**
|
||||
* Gets the node preceding the specified one in tree order.
|
||||
* See \ref tommy_tree_iterator_validity for iterator validity across insertions and removals.
|
||||
* \param node Node contained in the tree.
|
||||
* \return The preceding node. For the head node 0 is returned.
|
||||
*/
|
||||
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 +285,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 +358,84 @@ 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.
|
||||
*/
|
||||
tommy_inline tommy_tree_node* tommy_tree_search_node(tommy_compare_func* cmp, tommy_tree_node* node, void* data)
|
||||
{
|
||||
while (node) {
|
||||
int c = cmp(data, node->data);
|
||||
|
||||
if (c < 0)
|
||||
node = node->prev;
|
||||
else if (c > 0)
|
||||
node = node->next;
|
||||
else
|
||||
return node;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search(tommy_tree* tree, void* data)
|
||||
{
|
||||
tommy_tree_node* node = tommy_tree_search_node(tree->cmp, tree->root, data);
|
||||
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
return node->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ::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.
|
||||
* \param tree The tree.
|
||||
* \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 equal element in tree order, or 0 if not found.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search_compare(tommy_tree* tree, tommy_compare_func* cmp, void* cmp_arg)
|
||||
tommy_inline void* tommy_tree_search_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);
|
||||
/* comparators must not change the tree, so retain the data across the call */
|
||||
void* data = node->data;
|
||||
int c = cmp(cmp_arg, data);
|
||||
|
||||
if (c < 0) {
|
||||
candidate = node;
|
||||
if (c < 0)
|
||||
node = node->prev;
|
||||
} else if (c > 0) {
|
||||
else if (c > 0)
|
||||
node = node->next;
|
||||
else {
|
||||
candidate = data;
|
||||
node = node->prev;
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an element in the tree.
|
||||
* If the element is not found, 0 is returned.
|
||||
* \param tree The tree.
|
||||
* \param data Element used for comparison.
|
||||
* \return The first equal element in tree order, or 0 if none.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search(tommy_tree* tree, const void* data)
|
||||
{
|
||||
return tommy_tree_search_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 first 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.
|
||||
* \param tree The tree.
|
||||
* \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 element in tree order with key greater or equal, or 0 if none.
|
||||
*/
|
||||
tommy_inline void* tommy_tree_search_greater_equal_compare(tommy_tree* tree, tommy_compare_func* cmp, const void* cmp_arg)
|
||||
{
|
||||
tommy_tree_node* node = tree->root;
|
||||
void* candidate = 0;
|
||||
|
||||
while (node) {
|
||||
/* comparators must not change the tree, so retain the data across the call */
|
||||
void* data = node->data;
|
||||
int c = cmp(cmp_arg, data);
|
||||
|
||||
if (c <= 0) {
|
||||
candidate = data;
|
||||
node = node->prev;
|
||||
} else {
|
||||
return node;
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,60 +445,86 @@ 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 tree The tree.
|
||||
* \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 tree The tree.
|
||||
* \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);
|
||||
/* comparators must not change the tree, so retain the data across the call */
|
||||
void* data = node->data;
|
||||
int c = cmp(cmp_arg, data);
|
||||
|
||||
if (c < 0) {
|
||||
candidate = 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 tree The tree.
|
||||
* \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.
|
||||
* \param tree The tree.
|
||||
* \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 element in tree order with key less or equal, or 0 if none.
|
||||
*/
|
||||
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) {
|
||||
/* comparators must not change the tree, so retain the data across the call */
|
||||
void* data = node->data;
|
||||
int c = cmp(cmp_arg, data);
|
||||
|
||||
if (c < 0) {
|
||||
node = node->prev;
|
||||
} else if (c > 0) {
|
||||
candidate = node;
|
||||
node = node->next;
|
||||
} else {
|
||||
return node;
|
||||
candidate = data;
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -456,52 +534,101 @@ 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 tree The tree.
|
||||
* \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 tree The tree.
|
||||
* \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 less 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) {
|
||||
/* comparators must not change the tree, so retain the data across the call */
|
||||
void* data = node->data;
|
||||
int c = cmp(cmp_arg, data);
|
||||
|
||||
if (c > 0) {
|
||||
candidate = 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 tree The tree.
|
||||
* \param data Element used for comparison.
|
||||
* \return The last strictly less 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 tree 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 tree The tree.
|
||||
* \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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -532,26 +659,28 @@ tommy_inline void* tommy_tree_remove(tommy_tree* tree, void* data)
|
|||
*
|
||||
* // deallocates all the objects iterating the tree
|
||||
* tommy_tree_foreach(&tree, free);
|
||||
* tommy_tree_clear(&tree); // resets the tree before reuse
|
||||
* \endcode
|
||||
*
|
||||
* If the callback frees the objects, the tree retains dangling node
|
||||
* pointers. Call tommy_tree_clear() or tommy_tree_init() before reusing it.
|
||||
* The tree has no storage to release.
|
||||
*/
|
||||
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.
|
||||
* \return If the tree is empty.
|
||||
*/
|
||||
tommy_inline tommy_bool_t tommy_tree_empty(tommy_tree* tree)
|
||||
tommy_inline tommy_bool_t tommy_tree_empty(const tommy_tree* tree)
|
||||
{
|
||||
return tree->root == 0;
|
||||
}
|
||||
|
|
@ -559,7 +688,7 @@ tommy_inline tommy_bool_t tommy_tree_empty(tommy_tree* tree)
|
|||
/**
|
||||
* Gets the number of elements.
|
||||
*/
|
||||
tommy_inline tommy_size_t tommy_tree_count(tommy_tree* tree)
|
||||
tommy_inline tommy_size_t tommy_tree_count(const tommy_tree* tree)
|
||||
{
|
||||
return tree->count;
|
||||
}
|
||||
|
|
@ -568,7 +697,7 @@ tommy_inline tommy_size_t tommy_tree_count(tommy_tree* tree)
|
|||
* Gets the size of allocated memory.
|
||||
* It includes the size of the ::tommy_tree_node of the stored elements.
|
||||
*/
|
||||
TOMMY_API tommy_size_t tommy_tree_memory_usage(tommy_tree* tree);
|
||||
TOMMY_API tommy_size_t tommy_tree_memory_usage(const tommy_tree* tree);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,25 @@
|
|||
* Generic types.
|
||||
*/
|
||||
|
||||
#ifndef __TOMMYTYPES_H
|
||||
#define __TOMMYTYPES_H
|
||||
#ifndef TOMMY_TYPES_H
|
||||
#define TOMMY_TYPES_H
|
||||
|
||||
/******************************************************************************/
|
||||
/* memory bounds checking */
|
||||
|
||||
/**
|
||||
* Enable bounds-safe implementations when compiling with AddressSanitizer
|
||||
* or Fil-C. These environments detect accesses beyond allocated objects,
|
||||
* including intentional overreads that remain within a readable memory page.
|
||||
* Normal builds retain the faster implementations.
|
||||
*/
|
||||
#if defined(__SANITIZE_ADDRESS__) || defined(__FILC__)
|
||||
#define TOMMY_MEMORY_BOUNDS_CHECK 1
|
||||
#elif defined(__has_feature)
|
||||
#define TOMMY_MEMORY_BOUNDS_CHECK __has_feature(address_sanitizer)
|
||||
#else
|
||||
#define TOMMY_MEMORY_BOUNDS_CHECK 0
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/* types */
|
||||
|
|
@ -14,15 +31,18 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <limits.h>
|
||||
typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */
|
||||
typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */
|
||||
typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */
|
||||
#ifdef _WIN64
|
||||
#define TOMMY_SIZE_BIT 64
|
||||
#define TOMMY_SIZE_MAX _UI64_MAX
|
||||
typedef unsigned _int64 tommy_size_t; /**< Generic size_t type. */
|
||||
typedef _int64 tommy_ssize_t; /**< Generic ssize_t type. */
|
||||
#else
|
||||
#define TOMMY_SIZE_BIT 32
|
||||
#define TOMMY_SIZE_MAX UINT_MAX
|
||||
typedef unsigned tommy_size_t; /**< Generic size_t type. */
|
||||
typedef int tommy_ssize_t; /**< Generic ssize_t type. */
|
||||
#endif
|
||||
|
|
@ -33,10 +53,12 @@ typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */
|
|||
typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */
|
||||
#if SIZE_MAX == UINT64_MAX
|
||||
#define TOMMY_SIZE_BIT 64
|
||||
#define TOMMY_SIZE_MAX SIZE_MAX
|
||||
typedef uint64_t tommy_size_t; /**< Generic size_t type. */
|
||||
typedef int64_t tommy_ssize_t; /**< Generic ssize_t type. */
|
||||
#elif SIZE_MAX == UINT32_MAX
|
||||
#define TOMMY_SIZE_BIT 32
|
||||
#define TOMMY_SIZE_MAX SIZE_MAX
|
||||
typedef uint32_t tommy_size_t; /**< Generic size_t type. */
|
||||
typedef int32_t tommy_ssize_t; /**< Generic ssize_t type. */
|
||||
#else
|
||||
|
|
@ -99,7 +121,7 @@ typedef tommy_uint32_t tommy_uint_t;
|
|||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
/* modificators */
|
||||
/* modifiers */
|
||||
|
||||
/** \internal
|
||||
* Definition of TOMMY_API.
|
||||
|
|
@ -124,7 +146,7 @@ typedef tommy_uint32_t tommy_uint_t;
|
|||
* Definition of the restrict keyword if available.
|
||||
*/
|
||||
#if !defined(tommy_restrict)
|
||||
#if __STDC_VERSION__ >= 199901L
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
#define tommy_restrict restrict
|
||||
#elif defined(_MSC_VER) || defined(__GNUC__)
|
||||
#define tommy_restrict __restrict
|
||||
|
|
@ -200,6 +222,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;
|
||||
|
||||
|
|
@ -207,6 +230,7 @@ typedef struct tommy_node_struct {
|
|||
* Index of the node.
|
||||
* With tries this field is used to store the key.
|
||||
* With hashtables this field is used to store the hash value.
|
||||
* With trees this field is used to store the parent pointer and balance factor.
|
||||
* With lists this field is not used.
|
||||
*/
|
||||
tommy_size_t index;
|
||||
|
|
@ -246,12 +270,12 @@ typedef int tommy_compare_func(const void* obj_a, const void* obj_b);
|
|||
|
||||
/**
|
||||
* Search function for elements.
|
||||
* \param arg Pointer to the value to search as passed at the search function.
|
||||
* \param arg Pointer to the value to search as passed to the search function.
|
||||
* \param obj Pointer to the object to compare to.
|
||||
* \return ==0 if the value matches the element. !=0 if different.
|
||||
*
|
||||
* The first argument is a pointer to the value to search exactly
|
||||
* as it's passed at the search function called.
|
||||
* as it's passed to the search function called.
|
||||
* The second argument is a pointer to the object inside the hashtable to compare.
|
||||
*
|
||||
* The return value has to be 0 if the values are equal. != 0 if they are different.
|
||||
|
|
@ -315,7 +339,7 @@ typedef void tommy_foreach_arg_func(void* arg, void* obj);
|
|||
|
||||
/** \internal
|
||||
* Integer log2 for constants.
|
||||
* You can use it only for exact power of 2 up to 256.
|
||||
* You can use it only for exact powers of 2 up to 256.
|
||||
*/
|
||||
#define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 : (value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0)
|
||||
|
||||
|
|
@ -355,9 +379,9 @@ 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] = {
|
||||
static const unsigned char TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = {
|
||||
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
|
||||
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
|
||||
};
|
||||
|
|
@ -372,13 +396,17 @@ 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.
|
||||
* Return the bit index of the most significant 1 bit.
|
||||
*
|
||||
* If no bit is set, the result is undefined.
|
||||
* \param value Value to scan. 0 is not allowed.
|
||||
* \return The index of the most significant bit set.
|
||||
*/
|
||||
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 +421,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 +439,28 @@ 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.
|
||||
* Return the bit index of the least significant 1 bit.
|
||||
*
|
||||
* If no bit is set, the result is undefined.
|
||||
* \param value Value to scan. 0 is not allowed.
|
||||
* \return The index of the least significant bit set.
|
||||
*/
|
||||
tommy_inline tommy_uint_t tommy_ctz_u64(tommy_uint64_t value)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_MSC_VER) && defined(_WIN64)
|
||||
unsigned long count;
|
||||
_BitScanForward64(&count, value);
|
||||
return count;
|
||||
|
|
@ -444,16 +475,16 @@ 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.
|
||||
* For the value 0, the result is undefined.
|
||||
* \param value Value to round up.
|
||||
* \return The smallest power of 2 not less than the specified value.
|
||||
*/
|
||||
tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
|
||||
{
|
||||
/* Round up to the next highest power of 2 */
|
||||
/* round up to the next highest power of 2 */
|
||||
/* from http://graphics.stanford.edu/~seander/bithacks.html */
|
||||
|
||||
--value;
|
||||
|
|
@ -469,6 +500,9 @@ tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
|
|||
|
||||
/**
|
||||
* Rounds up to the next power of 2 for 64 bits.
|
||||
* For the value 0, the result is undefined.
|
||||
* \param value Value to round up.
|
||||
* \return The smallest power of 2 not less than the specified value.
|
||||
*/
|
||||
tommy_inline tommy_uint64_t tommy_roundup_pow2_u64(tommy_uint64_t value)
|
||||
{
|
||||
|
|
@ -485,7 +519,8 @@ tommy_inline tommy_uint64_t tommy_roundup_pow2_u64(tommy_uint64_t value)
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the specified word has a byte at 0.
|
||||
* Checks if the specified word has a byte at 0.
|
||||
* \param value Value to check.
|
||||
* \return 0 or 1.
|
||||
*/
|
||||
tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
|
||||
|
|
@ -493,7 +528,17 @@ tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
|
|||
return ((value - 0x01010101) & ~value & 0x80808080) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Checks if the specified 64-bit word has a byte at 0.
|
||||
* \param value Value to check.
|
||||
* \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 +552,4 @@ tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue