mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Update to latest TommyDS
This commit is contained in:
parent
9b2c5514c0
commit
272853e40a
9 changed files with 234 additions and 66 deletions
|
|
@ -39,6 +39,11 @@ struct hash32_test_vector {
|
|||
uint32_t digest;
|
||||
};
|
||||
|
||||
struct strhash32_test_vector {
|
||||
char* data;
|
||||
uint32_t digest;
|
||||
};
|
||||
|
||||
struct hash64_test_vector {
|
||||
const char* data;
|
||||
int len;
|
||||
|
|
@ -77,6 +82,36 @@ static struct hash32_test_vector TEST_HASH32[] = {
|
|||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Test vectors for tommy_strhash32
|
||||
*/
|
||||
struct strhash32_test_vector TEST_STRHASH32[] = {
|
||||
{ "", 0x0af1416d },
|
||||
{ "a", 0x68fa0f3f },
|
||||
{ "abc", 0xfc68ffc5 },
|
||||
{ "message digest", 0x08477b63 },
|
||||
{ "abcdefghijklmnopqrstuvwxyz", 0x5b9c25e5 },
|
||||
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 0x1e530ce7 },
|
||||
{ "The quick brown fox jumps over the lazy dog", 0xaf93eefe },
|
||||
{ "\xff", 0xfc88801b },
|
||||
{ "\x16\x27", 0xcd7216db },
|
||||
{ "\xe2\x56\xb4", 0x05f98d02 },
|
||||
{ "\xc9\x4d\x9c\xda", 0xf65206f8 },
|
||||
{ "\x79\xf1\x29\x69\x5d", 0x72bd6bda },
|
||||
{ "\xff\x7e\xdf\x1e\x31\x1c", 0x57dfb9b4 },
|
||||
{ "\x2a\x4c\xe1\xff\x9e\x6f\x53", 0x499ff634 },
|
||||
{ "\xba\x02\xab\x18\x30\xc5\x0e\x8a", 0xe896b7ce },
|
||||
{ "\xec\x4e\x7a\x72\x1e\x71\x2a\xc9\x33", 0xfe3939f0 },
|
||||
{ "\xfd\xe2\x9c\x0f\x72\xb7\x08\xea\xd0\x78", 0x4351d482 },
|
||||
{ "\x65\xc4\x8a\xb8\x80\x86\x9a\x79\xff\xb7\xae", 0x88e92135 },
|
||||
{ "\x77\xe9\xd7\x80\x0e\x3f\x5c\x43\xc8\xc2\x46\x39", 0x01109c16 },
|
||||
{ "\x87\xd8\x61\x61\x4c\x89\x17\x4e\xa1\xa4\xef\x13\xa9", 0xbcb050dc },
|
||||
{ "\xfe\xa6\x5b\xc2\xda\xe8\x95\xd4\x64\xab\x4c\x39\x58\x29", 0xbe5e1fd5 },
|
||||
{ "\x94\x49\xc0\x78\xa0\x80\xda\xc7\x71\x4e\x17\x37\xa9\x7c\x40", 0x70d8c97f },
|
||||
{ "\x53\x7e\x36\xb4\x2e\xc9\xb9\xcc\x18\x3e\x9a\x5f\xfc\xb7\xb0\x61", 0x957440a9 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Test vectors for tommy_hash64
|
||||
*/
|
||||
|
|
@ -157,7 +192,7 @@ static void test_hash(void)
|
|||
for (i = 0; TEST_HASH32[i].data; ++i) {
|
||||
uint32_t digest;
|
||||
memcpy(buffer_aligned, TEST_HASH32[i].data, TEST_HASH32[i].len);
|
||||
digest = tommy_hash_u32(seed32, buffer_aligned, TEST_MURMUR3[i].len);
|
||||
digest = tommy_hash_u32(seed32, buffer_aligned, TEST_HASH32[i].len);
|
||||
if (digest != TEST_HASH32[i].digest) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Failed hash32 test\n");
|
||||
|
|
@ -166,10 +201,22 @@ static void test_hash(void)
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; TEST_STRHASH32[i].data; ++i) {
|
||||
uint32_t digest;
|
||||
memcpy(buffer_aligned, TEST_STRHASH32[i].data, strlen(TEST_STRHASH32[i].data) + 1);
|
||||
digest = tommy_strhash_u32(seed32, buffer_aligned);
|
||||
if (digest != TEST_STRHASH32[i].digest) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Failed strhash32 test\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; TEST_HASH64[i].data; ++i) {
|
||||
uint64_t digest;
|
||||
memcpy(buffer_aligned, TEST_HASH64[i].data, TEST_HASH64[i].len);
|
||||
digest = tommy_hash_u64(seed64, buffer_aligned, TEST_MURMUR3[i].len);
|
||||
digest = tommy_hash_u64(seed64, buffer_aligned, TEST_HASH64[i].len);
|
||||
if (digest != TEST_HASH64[i].digest) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Failed hash64 test\n");
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@
|
|||
* - All the other inner prev and next fields are correctly set.
|
||||
*/
|
||||
typedef struct tommy_chain_struct {
|
||||
tommy_node* head; /**< Pointer at the head of the chain. */
|
||||
tommy_node* tail; /**< Pointer at the tail of the chain. */
|
||||
tommy_node* head; /**< Pointer to the head of the chain. */
|
||||
tommy_node* tail; /**< Pointer to the tail of the chain. */
|
||||
} tommy_chain;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -165,3 +165,77 @@ tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tom
|
|||
return c + ((tommy_uint64_t)b << 32);
|
||||
}
|
||||
|
||||
tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key)
|
||||
{
|
||||
const unsigned char* key = tommy_cast(const unsigned char*, void_key);
|
||||
tommy_uint32_t a, b, c;
|
||||
tommy_uint32_t m[3] = { 0xff, 0xff00, 0xff0000 };
|
||||
|
||||
a = b = c = 0xdeadbeef + init_val;
|
||||
/* this is different than original lookup3 and the result won't match */
|
||||
|
||||
while (1) {
|
||||
tommy_uint32_t v = tommy_le_uint32_read(key);
|
||||
|
||||
if (tommy_haszero_u32(v)) {
|
||||
if (v & m[0]) {
|
||||
a += v & m[0];
|
||||
if (v & m[1]) {
|
||||
a += v & m[1];
|
||||
if (v & m[2])
|
||||
a += v & m[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
a += v;
|
||||
|
||||
v = tommy_le_uint32_read(key + 4);
|
||||
|
||||
if (tommy_haszero_u32(v)) {
|
||||
if (v & m[0]) {
|
||||
b += v & m[0];
|
||||
if (v & m[1]) {
|
||||
b += v & m[1];
|
||||
if (v & m[2])
|
||||
b += v & m[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
b += v;
|
||||
|
||||
v = tommy_le_uint32_read(key + 8);
|
||||
|
||||
if (tommy_haszero_u32(v)) {
|
||||
if (v & m[0]) {
|
||||
c += v & m[0];
|
||||
if (v & m[1]) {
|
||||
c += v & m[1];
|
||||
if (v & m[2])
|
||||
c += v & m[2];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
c += v;
|
||||
|
||||
tommy_mix(a, b, c);
|
||||
|
||||
key += 12;
|
||||
}
|
||||
|
||||
/* for lengths that are multiplers of 12 we already have called mix */
|
||||
/* this is different than the original lookup3 and the result won't match */
|
||||
|
||||
tommy_final(a, b, c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,10 +46,15 @@ typedef tommy_key_t tommy_hash_t;
|
|||
* Hash function with a 32 bits result.
|
||||
* Implementation of the Robert Jenkins "lookup3" hash 32 bits version,
|
||||
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle().
|
||||
*
|
||||
* This hash is designed to provide a good overall performance in all platforms,
|
||||
* including 32 bits. If you target only 64 bits, you can use faster hashes,
|
||||
* like SpookyHash or FarmHash.
|
||||
*
|
||||
* \param init_val Initialization value.
|
||||
* Using a different initialization value, you can generate a completely different set of hash values.
|
||||
* Use 0 if not relevant.
|
||||
* \param void_key Pointer at the data to hash.
|
||||
* \param void_key Pointer to the data to hash.
|
||||
* \param key_len Size of the data to hash.
|
||||
* \note
|
||||
* This function is endianess independent.
|
||||
|
|
@ -61,10 +66,15 @@ tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tom
|
|||
* Hash function with a 64 bits result.
|
||||
* Implementation of the Robert Jenkins "lookup3" hash 64 bits versions,
|
||||
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2().
|
||||
*
|
||||
* This hash is designed to provide a good overall performance in all platforms,
|
||||
* including 32 bits. If you target only 64 bits, you can use faster hashes,
|
||||
* like SpookyHash or FarmHash.
|
||||
*
|
||||
* \param init_val Initialization value.
|
||||
* Using a different initialization value, you can generate a completely different set of hash values.
|
||||
* Use 0 if not relevant.
|
||||
* \param void_key Pointer at the data to hash.
|
||||
* \param void_key Pointer to the data to hash.
|
||||
* \param key_len Size of the data to hash.
|
||||
* \note
|
||||
* This function is endianess independent.
|
||||
|
|
@ -73,7 +83,25 @@ tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tom
|
|||
tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len);
|
||||
|
||||
/**
|
||||
* Integer hash of 32 bits.
|
||||
* String hash function with a 32 bits result.
|
||||
* Implementation is based on the the Robert Jenkins "lookup3" hash 32 bits version,
|
||||
* from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle().
|
||||
*
|
||||
* This hash is designed to handle strings with an unknown length. If you
|
||||
* know the string length, the other hash functions are surely faster.
|
||||
*
|
||||
* \param init_val Initialization value.
|
||||
* Using a different initialization value, you can generate a completely different set of hash values.
|
||||
* Use 0 if not relevant.
|
||||
* \param void_key Pointer to the string to hash. It has to be 0 terminated.
|
||||
* \note
|
||||
* This function is endianess independent.
|
||||
* \return The hash value of 32 bits.
|
||||
*/
|
||||
tommy_uint32_t tommy_strhash_u32(tommy_uint64_t init_val, const void* void_key);
|
||||
|
||||
/**
|
||||
* Integer reversible hash function for 32 bits.
|
||||
* Implementation of the Robert Jenkins "4-byte Integer Hashing",
|
||||
* from http://burtleburtle.net/bob/hash/integer.html
|
||||
*/
|
||||
|
|
@ -91,9 +119,9 @@ tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key)
|
|||
}
|
||||
|
||||
/**
|
||||
* Integer hash of 64 bits.
|
||||
* Integer reversible hash function for 64 bits.
|
||||
* Implementation of the Thomas Wang "Integer Hash Function",
|
||||
* from http://www.cris.com/~Ttwang/tech/inthash.htm
|
||||
* from http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
*/
|
||||
tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@
|
|||
*
|
||||
* \code
|
||||
* struct object {
|
||||
* tommy_node node;
|
||||
* // other fields
|
||||
* int value;
|
||||
* // other fields
|
||||
* tommy_node node;
|
||||
* };
|
||||
*
|
||||
* struct object* obj = malloc(sizeof(struct object)); // creates the object
|
||||
|
|
@ -243,7 +243,8 @@ void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node*
|
|||
/**
|
||||
* Calls the specified function for each element in the hashtable.
|
||||
*
|
||||
* You can use this function to deallocate all the elements inserted.
|
||||
* You cannot add or remove elements from the inside of the callback,
|
||||
* but can use it to deallocate them.
|
||||
*
|
||||
* \code
|
||||
* tommy_hashdyn hashdyn;
|
||||
|
|
|
|||
|
|
@ -28,32 +28,6 @@
|
|||
#include "tommylist.h"
|
||||
#include "tommychain.h"
|
||||
|
||||
void tommy_list_concat(tommy_list* first, tommy_list* second)
|
||||
{
|
||||
tommy_node* first_head;
|
||||
tommy_node* first_tail;
|
||||
tommy_node* second_head;
|
||||
|
||||
if (tommy_list_empty(second))
|
||||
return;
|
||||
|
||||
if (tommy_list_empty(first)) {
|
||||
*first = *second;
|
||||
return;
|
||||
}
|
||||
|
||||
first_head = tommy_list_head(first);
|
||||
second_head = tommy_list_head(second);
|
||||
first_tail = tommy_list_tail(first);
|
||||
|
||||
/* set the "circular" prev list */
|
||||
first_head->prev = second_head->prev;
|
||||
second_head->prev = first_tail;
|
||||
|
||||
/* set the "0 terminated" next list */
|
||||
first_tail->next = second_head;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* Setup a list.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@
|
|||
*
|
||||
* \code
|
||||
* struct object {
|
||||
* tommy_node node;
|
||||
* // other fields
|
||||
* int value;
|
||||
* // other fields
|
||||
* tommy_node node;
|
||||
* };
|
||||
*
|
||||
* struct object* obj = malloc(sizeof(struct object)); // creates the object
|
||||
|
|
@ -278,7 +278,34 @@ tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node
|
|||
* \param second The second list. After this call the list content is undefined,
|
||||
* and you should not use it anymore.
|
||||
*/
|
||||
void tommy_list_concat(tommy_list* first, tommy_list* second);
|
||||
tommy_inline void tommy_list_concat(tommy_list* first, tommy_list* second)
|
||||
{
|
||||
tommy_node* first_head;
|
||||
tommy_node* first_tail;
|
||||
tommy_node* second_head;
|
||||
|
||||
/* if the second is empty, nothing to do */
|
||||
second_head = tommy_list_head(second);
|
||||
if (second_head == 0)
|
||||
return;
|
||||
|
||||
/* if the first is empty, copy the second */
|
||||
first_head = tommy_list_head(first);
|
||||
if (first_head == 0) {
|
||||
*first = *second;
|
||||
return;
|
||||
}
|
||||
|
||||
/* tail of the first list */
|
||||
first_tail = first_head->prev;
|
||||
|
||||
/* set the "circular" prev list */
|
||||
first_head->prev = second_head->prev;
|
||||
second_head->prev = first_tail;
|
||||
|
||||
/* set the "0 terminated" next list */
|
||||
first_tail->next = second_head;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts a list.
|
||||
|
|
@ -318,8 +345,8 @@ tommy_inline tommy_count_t tommy_list_count(tommy_list* list)
|
|||
/**
|
||||
* Calls the specified function for each element in the list.
|
||||
*
|
||||
* You can use this function to deallocate all the elements
|
||||
* inserted in a list.
|
||||
* You cannot add or remove elements from the inside of the callback,
|
||||
* but can use it to deallocate them.
|
||||
*
|
||||
* \code
|
||||
* tommy_list list;
|
||||
|
|
|
|||
|
|
@ -29,16 +29,16 @@
|
|||
* AVL tree.
|
||||
*
|
||||
* This tree is a standard AVL tree implementation that stores elements in the
|
||||
* order defined by the comparsion function.
|
||||
* order defined by the comparison function.
|
||||
*
|
||||
* As difference from other tommy containers, duplicate elements cannot be inserted.
|
||||
*
|
||||
* \code
|
||||
* tommy_tree tree;
|
||||
* As difference than other tommy containers, duplicate elements cannot be inserted.
|
||||
*
|
||||
* To initialize a tree you have to call tommy_tree_init() specifing a comparison
|
||||
* function that will define the order in the tree.
|
||||
*
|
||||
* \code
|
||||
* tommy_tree tree;
|
||||
*
|
||||
* tommy_tree_init(&tree, cmp);
|
||||
* \endcode
|
||||
*
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
* To remove an element from the tree you have to call tommy_tree_remove()
|
||||
* providing the key to search and remove.
|
||||
*
|
||||
* \code
|
||||
* \code
|
||||
* struct object value_to_remove = { 1 };
|
||||
* struct object* obj = tommy_tree_remove(&tree, &value_to_remove);
|
||||
* if (obj) {
|
||||
|
|
@ -87,7 +87,8 @@
|
|||
* }
|
||||
* \endcode
|
||||
*
|
||||
* To destroy the tree you have to remove all the elements.
|
||||
* To destroy the tree you have to remove or destroy all the contained elements.
|
||||
* The tree itself doesn't have or need a deallocation function.
|
||||
*
|
||||
* If you need to iterate over all the elements in the tree, you can use
|
||||
* tommy_tree_foreach() or tommy_tree_foreach_arg().
|
||||
|
|
@ -129,10 +130,11 @@ void tommy_tree_init(tommy_tree* tree, tommy_compare_func* cmp);
|
|||
/**
|
||||
* Inserts an element in the tree.
|
||||
* If the element is already present, it's not inserted again.
|
||||
* Check the return value to identify if the element was already present or not.
|
||||
* You have to provide the pointer of the node embedded into the object and
|
||||
* the pointer at the object.
|
||||
* \param node Pointer at the node embedded into the object to insert.
|
||||
* \param data Pointer at the object to insert.
|
||||
* \param node Pointer to the node embedded into the object to insert.
|
||||
* \param data Pointer to the object to insert.
|
||||
* \return The element in the tree. Either the already existing one, or the one just inserted.
|
||||
*/
|
||||
void* tommy_tree_insert(tommy_tree* tree, tommy_tree_node* node, void* data);
|
||||
|
|
@ -176,7 +178,8 @@ void* tommy_tree_remove_existing(tommy_tree* tree, tommy_tree_node* node);
|
|||
*
|
||||
* The elements are processed in order.
|
||||
*
|
||||
* You can use this function to deallocate all the elements inserted.
|
||||
* You cannot add or remove elements from the inside of the callback,
|
||||
* but can use it to deallocate them.
|
||||
*
|
||||
* \code
|
||||
* tommy_tree tree;
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ typedef struct tommy_node_struct {
|
|||
struct tommy_node_struct* prev;
|
||||
|
||||
/**
|
||||
* Pointer at the object containing the node.
|
||||
* Pointer to the object containing the node.
|
||||
* This field is initialized when inserting nodes into a data structure.
|
||||
*/
|
||||
void* data;
|
||||
|
|
@ -212,8 +212,8 @@ typedef struct tommy_node_struct {
|
|||
|
||||
/**
|
||||
* Compare function for elements.
|
||||
* \param obj_a Pointer at the first object to compare.
|
||||
* \param obj_b Pointer at the second object to compare.
|
||||
* \param obj_a Pointer to the first object to compare.
|
||||
* \param obj_b Pointer to the second object to compare.
|
||||
* \return <0 if the first element is less than the second, ==0 equal, >0 if greather.
|
||||
*
|
||||
* This function is like the C strcmp().
|
||||
|
|
@ -241,13 +241,15 @@ typedef int tommy_compare_func(const void* obj_a, const void* obj_b);
|
|||
|
||||
/**
|
||||
* Search function for elements.
|
||||
* \param arg Pointer at the value to search.
|
||||
* \param obj Pointer at the object to compare to.
|
||||
* \param arg Pointer to the value to search as passed at the search function.
|
||||
* \param obj Pointer to the object to compare to.
|
||||
* \return ==0 if the value matches the element. !=0 if different.
|
||||
*
|
||||
* Note that the first argument is a pointer to the value to search and
|
||||
* the second one is a pointer to the object to compare.
|
||||
* They are pointers of two different types.
|
||||
* The first argument is a pointer to the value to search exactly
|
||||
* as it's passed at the search function called.
|
||||
* The second argument is a pointer to the object inside the hashtable to compare.
|
||||
*
|
||||
* The return value has to be 0 if the values are equal. != 0 if they are different.
|
||||
*
|
||||
* \code
|
||||
* struct object {
|
||||
|
|
@ -257,7 +259,10 @@ typedef int tommy_compare_func(const void* obj_a, const void* obj_b);
|
|||
*
|
||||
* int compare(const void* arg, const void* obj)
|
||||
* {
|
||||
* return *(const int*)arg != ((const struct object*)obj)->value;
|
||||
* const int* value_to_find = arg;
|
||||
* const struct object* object_to_compare = obj;
|
||||
*
|
||||
* return *value_to_find != object_to_compare->value;
|
||||
* }
|
||||
*
|
||||
* int value_to_find = 1;
|
||||
|
|
@ -274,7 +279,7 @@ typedef int tommy_search_func(const void* arg, const void* obj);
|
|||
|
||||
/**
|
||||
* Foreach function.
|
||||
* \param obj Pointer at the object to iterate.
|
||||
* \param obj Pointer to the object to iterate.
|
||||
*
|
||||
* A typical example is to use free() to deallocate all the objects in a list.
|
||||
* \code
|
||||
|
|
@ -285,8 +290,8 @@ typedef void tommy_foreach_func(void* obj);
|
|||
|
||||
/**
|
||||
* Foreach function with an argument.
|
||||
* \param arg Pointer at a generic argument.
|
||||
* \param obj Pointer at the object to iterate.
|
||||
* \param arg Pointer to a generic argument.
|
||||
* \param obj Pointer to the object to iterate.
|
||||
*/
|
||||
typedef void tommy_foreach_arg_func(void* arg, void* obj);
|
||||
|
||||
|
|
@ -394,7 +399,7 @@ tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value)
|
|||
tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
|
||||
{
|
||||
/* Round up to the next highest power of 2 */
|
||||
/* from http://www-graphics.stanford.edu/~seander/bithacks.html */
|
||||
/* from http://graphics.stanford.edu/~seander/bithacks.html */
|
||||
|
||||
--value;
|
||||
value |= value >> 1;
|
||||
|
|
@ -406,5 +411,14 @@ tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value)
|
|||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the specified word has a byte at 0.
|
||||
* \return 0 or 1.
|
||||
*/
|
||||
tommy_inline int tommy_haszero_u32(tommy_uint32_t value)
|
||||
{
|
||||
return ((value - 0x01010101) & ~value & 0x80808080) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue