Update to TommyDS v4.0

This commit is contained in:
Andrea Mazzoleni 2026-10-07 18:44:12 +02:00
parent a39fb132f8
commit 9505499e15
21 changed files with 2220 additions and 959 deletions

View file

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

View file

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

View file

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

View file

@ -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)

View file

@ -51,25 +51,25 @@ struct hash_test_vector {
* Test vectors for tommy_hash32
*/
static struct hash32_test_vector TEST_HASH32[] = {
{ "", 0, 0x8614384c, 0xa766795d },
{ "a", 1, 0x12c16c36, 0xa766795d },
{ "abc", 3, 0xc58e8af5, 0xa766795d },
{ "message digest", 14, 0x006b32f1, 0xa766795d },
{ "abcdefghijklmnopqrstuvwxyz", 26, 0x7e6fcfe0, 0xa766795d },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62, 0x8604adf8, 0xa766795d },
{ "The quick brown fox jumps over the lazy dog", 43, 0xdeba3d3a, 0xa766795d },
{ "\x00", 1, 0x4a7d1c33, 0xa766795d },
{ "\x16\x27", 2, 0x8b50899b, 0xa766795d },
{ "\xe2\x56\xb4", 3, 0x60406493, 0xa766795d },
{ "\xc9\x4d\x9c\xda", 4, 0xa049144a, 0xa766795d },
{ "\x79\xf1\x29\x69\x5d", 5, 0x4da2c2f1, 0xa766795d },
{ "\x00\x7e\xdf\x1e\x31\x1c", 6, 0x59de30cf, 0xa766795d },
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 7, 0x219e149c, 0xa766795d },
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 8, 0x25067520, 0xa766795d },
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 9, 0xa1f368d8, 0xa766795d },
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 10, 0x805fc63d, 0xa766795d },
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\x00\xb7\xae", 11, 0x7f75dd0f, 0xa766795d },
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 12, 0xb9154382, 0xa766795d },
{ "", 0, 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 */

View file

@ -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;