#include #include #include #include #include #ifdef __has_builtin #define DPA__U__has_builtin(X) __has_builtin(X) #else #define DPA__U__has_builtin(X) 0 #endif #if __STDC_VERSION__ >= 202311 #define dpa_u_reproducible [[reproducible, gnu::pure]] #elif defined(__GNUC__) || defined(__llvm__) #define dpa_u_reproducible __attribute__((pure)) #else #define dpa_u_reproducible #endif #if __STDC_VERSION__ >= 202311 #define dpa_u_unsequenced [[unsequenced, gnu::const]] #elif defined(__GNUC__) || defined(__llvm__) #define dpa_u_unsequenced __attribute__((const)) #else #define dpa_u_unsequenced #endif #define dpa__u_api #define dpa__u_api_var #if DPA__U__has_builtin(__builtin_unreachable) #define dpa_u_unreachable(...) { __builtin_unreachable(); } #elif DPA__U__has_builtin(__builtin_trap) #define dpa_u_unreachable(...) { __builtin_trap(); } #else #define dpa_u_unreachable(...) { abort(); } #endif typedef unsigned dpa_u_bitmap_entry_t; typedef struct dpa_u_optional_pointer { void* value; bool present; } dpa_u_optional_pointer_t; // This variable is to help with benchmarks dpa__u_api_var extern long dpa_u_total_resize_time; #if DPA__U__has_builtin(__builtin_popcountg) #define dpa_u_count_bits __builtin_popcountg #elif DPA__U__has_builtin(__builtin_popcountll) #define dpa_u_count_bits __builtin_popcountll #else dpa_u_unsequenced dpa__u_api inline int dpa_u_count_bits(long long unsigned int x){ int n = 0; for(unsigned i=0; icount; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_pointer_dump_hashmap_key_hashes(dpa_u_set_pointer_t* that); typedef struct dpa_u_set_uc dpa_u_set_uc_t; // For CHAR_BIT == 8, for a set containing one of 256 entries, we only need 256 bits. // That'd be 32 bytes/chars (because 8 bits per char). For larger integer types, we need less entries. // u8: 32 entries, u16: 16 entries, u32: 8 entries, u64: 4 entries, u128: 2 entries, u256: 1 entry // This will not safe any space, but it may make the lookup more efficient. And 32 bytes, is not a lot. // We round up the number of entres, in case of CHAR_BIT != 8. In that case, space will be wasted, but who's ever going to use such a platform anyway? struct dpa_u_set_uc { dpa_u_bitmap_entry_t bitmask[(((size_t)1<<(sizeof(unsigned char)*CHAR_BIT))+(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT-1))/(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT)]; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_uc_add(dpa_u_set_uc_t* that, unsigned char key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_uc_remove(dpa_u_set_uc_t* that, unsigned char key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_uc_has(const dpa_u_set_uc_t* that, unsigned char key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_uc_clear(dpa_u_set_uc_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_uc_count(const dpa_u_set_uc_t* that){ size_t n = 0; const dpa_u_bitmap_entry_t*restrict const bitmask = that->bitmask; for(unsigned i=0; i<(((size_t)1<<(sizeof(unsigned char)*CHAR_BIT))+(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT-1))/(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT); i++) n += dpa_u_count_bits(bitmask[i]); return n; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_uc_dump_hashmap_key_hashes(dpa_u_set_uc_t* that); typedef struct dpa_u_set_us dpa_u_set_us_t; struct dpa_u_set_us { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned short*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_us_add(dpa_u_set_us_t* that, unsigned short key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_us_remove(dpa_u_set_us_t* that, unsigned short key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_us_has(const dpa_u_set_us_t* that, unsigned short key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_us_clear(dpa_u_set_us_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_us_count(const dpa_u_set_us_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_us_dump_hashmap_key_hashes(dpa_u_set_us_t* that); typedef struct dpa_u_set_u dpa_u_set_u_t; struct dpa_u_set_u { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_u_add(dpa_u_set_u_t* that, unsigned key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_u_remove(dpa_u_set_u_t* that, unsigned key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_u_has(const dpa_u_set_u_t* that, unsigned key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_u_clear(dpa_u_set_u_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_u_count(const dpa_u_set_u_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_u_dump_hashmap_key_hashes(dpa_u_set_u_t* that); typedef struct dpa_u_set_lu dpa_u_set_lu_t; struct dpa_u_set_lu { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned long*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_lu_add(dpa_u_set_lu_t* that, unsigned long key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_lu_remove(dpa_u_set_lu_t* that, unsigned long key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_lu_has(const dpa_u_set_lu_t* that, unsigned long key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_lu_clear(dpa_u_set_lu_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_lu_count(const dpa_u_set_lu_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_lu_dump_hashmap_key_hashes(dpa_u_set_lu_t* that); typedef struct dpa_u_set_llu dpa_u_set_llu_t; struct dpa_u_set_llu { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned long long*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_llu_add(dpa_u_set_llu_t* that, unsigned long long key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_llu_remove(dpa_u_set_llu_t* that, unsigned long long key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_llu_has(const dpa_u_set_llu_t* that, unsigned long long key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_llu_clear(dpa_u_set_llu_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_llu_count(const dpa_u_set_llu_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_llu_dump_hashmap_key_hashes(dpa_u_set_llu_t* that); typedef struct dpa_u_set_z dpa_u_set_z_t; struct dpa_u_set_z { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. size_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_z_add(dpa_u_set_z_t* that, size_t key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_z_remove(dpa_u_set_z_t* that, size_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_z_has(const dpa_u_set_z_t* that, size_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_z_clear(dpa_u_set_z_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_z_count(const dpa_u_set_z_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_z_dump_hashmap_key_hashes(dpa_u_set_z_t* that); typedef struct dpa_u_set_u8 dpa_u_set_u8_t; // For CHAR_BIT == 8, for a set containing one of 256 entries, we only need 256 bits. // That'd be 32 bytes/chars (because 8 bits per char). For larger integer types, we need less entries. // u8: 32 entries, u16: 16 entries, u32: 8 entries, u64: 4 entries, u128: 2 entries, u256: 1 entry // This will not safe any space, but it may make the lookup more efficient. And 32 bytes, is not a lot. // We round up the number of entres, in case of CHAR_BIT != 8. In that case, space will be wasted, but who's ever going to use such a platform anyway? struct dpa_u_set_u8 { dpa_u_bitmap_entry_t bitmask[(((size_t)1<<(sizeof(uint8_t)*CHAR_BIT))+(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT-1))/(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT)]; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_u8_add(dpa_u_set_u8_t* that, uint8_t key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_u8_remove(dpa_u_set_u8_t* that, uint8_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_u8_has(const dpa_u_set_u8_t* that, uint8_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_u8_clear(dpa_u_set_u8_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_u8_count(const dpa_u_set_u8_t* that){ size_t n = 0; const dpa_u_bitmap_entry_t*restrict const bitmask = that->bitmask; for(unsigned i=0; i<(((size_t)1<<(sizeof(uint8_t)*CHAR_BIT))+(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT-1))/(sizeof(dpa_u_bitmap_entry_t)*CHAR_BIT); i++) n += dpa_u_count_bits(bitmask[i]); return n; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_u8_dump_hashmap_key_hashes(dpa_u_set_u8_t* that); typedef struct dpa_u_set_u16 dpa_u_set_u16_t; struct dpa_u_set_u16 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint16_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_u16_add(dpa_u_set_u16_t* that, uint16_t key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_u16_remove(dpa_u_set_u16_t* that, uint16_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_u16_has(const dpa_u_set_u16_t* that, uint16_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_u16_clear(dpa_u_set_u16_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_u16_count(const dpa_u_set_u16_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_u16_dump_hashmap_key_hashes(dpa_u_set_u16_t* that); typedef struct dpa_u_set_u32 dpa_u_set_u32_t; struct dpa_u_set_u32 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint32_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_u32_add(dpa_u_set_u32_t* that, uint32_t key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_u32_remove(dpa_u_set_u32_t* that, uint32_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_u32_has(const dpa_u_set_u32_t* that, uint32_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_u32_clear(dpa_u_set_u32_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_u32_count(const dpa_u_set_u32_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_u32_dump_hashmap_key_hashes(dpa_u_set_u32_t* that); typedef struct dpa_u_set_u64 dpa_u_set_u64_t; struct dpa_u_set_u64 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint64_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; }; /** * Adds an entry to the set. * \returns 1 if the entry was already present, 0 if not, -1 on error. */ dpa__u_api int dpa_u_set_u64_add(dpa_u_set_u64_t* that, uint64_t key); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_set_u64_remove(dpa_u_set_u64_t* that, uint64_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_set_u64_has(const dpa_u_set_u64_t* that, uint64_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_set_u64_clear(dpa_u_set_u64_t* that); dpa_u_reproducible dpa__u_api inline size_t dpa_u_set_u64_count(const dpa_u_set_u64_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_set_u64_dump_hashmap_key_hashes(dpa_u_set_u64_t* that); typedef struct dpa_u_map_pointer dpa_u_map_pointer_t; struct dpa_u_map_pointer { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uintptr_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_pointer_exchange(dpa_u_map_pointer_t* that, void* key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_pointer_set(dpa_u_map_pointer_t* that, void* key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_pointer_remove(dpa_u_map_pointer_t* that, void* key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_pointer_has(const dpa_u_map_pointer_t* that, void* key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_pointer_clear(dpa_u_map_pointer_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_pointer_get(const dpa_u_map_pointer_t* that, void* key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_pointer_get_and_remove(dpa_u_map_pointer_t* that, void* key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_pointer_count(const dpa_u_map_pointer_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_pointer_dump_hashmap_key_hashes(dpa_u_map_pointer_t* that); typedef struct dpa_u_map_uc dpa_u_map_uc_t; struct dpa_u_map_uc { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned char*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_uc_exchange(dpa_u_map_uc_t* that, unsigned char key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_uc_set(dpa_u_map_uc_t* that, unsigned char key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_uc_remove(dpa_u_map_uc_t* that, unsigned char key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_uc_has(const dpa_u_map_uc_t* that, unsigned char key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_uc_clear(dpa_u_map_uc_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_uc_get(const dpa_u_map_uc_t* that, unsigned char key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_uc_get_and_remove(dpa_u_map_uc_t* that, unsigned char key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_uc_count(const dpa_u_map_uc_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_uc_dump_hashmap_key_hashes(dpa_u_map_uc_t* that); typedef struct dpa_u_map_us dpa_u_map_us_t; struct dpa_u_map_us { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned short*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_us_exchange(dpa_u_map_us_t* that, unsigned short key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_us_set(dpa_u_map_us_t* that, unsigned short key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_us_remove(dpa_u_map_us_t* that, unsigned short key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_us_has(const dpa_u_map_us_t* that, unsigned short key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_us_clear(dpa_u_map_us_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_us_get(const dpa_u_map_us_t* that, unsigned short key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_us_get_and_remove(dpa_u_map_us_t* that, unsigned short key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_us_count(const dpa_u_map_us_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_us_dump_hashmap_key_hashes(dpa_u_map_us_t* that); typedef struct dpa_u_map_u dpa_u_map_u_t; struct dpa_u_map_u { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u_exchange(dpa_u_map_u_t* that, unsigned key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u_set(dpa_u_map_u_t* that, unsigned key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_u_remove(dpa_u_map_u_t* that, unsigned key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_u_has(const dpa_u_map_u_t* that, unsigned key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_u_clear(dpa_u_map_u_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u_get(const dpa_u_map_u_t* that, unsigned key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u_get_and_remove(dpa_u_map_u_t* that, unsigned key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_u_count(const dpa_u_map_u_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_u_dump_hashmap_key_hashes(dpa_u_map_u_t* that); typedef struct dpa_u_map_lu dpa_u_map_lu_t; struct dpa_u_map_lu { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned long*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_lu_exchange(dpa_u_map_lu_t* that, unsigned long key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_lu_set(dpa_u_map_lu_t* that, unsigned long key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_lu_remove(dpa_u_map_lu_t* that, unsigned long key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_lu_has(const dpa_u_map_lu_t* that, unsigned long key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_lu_clear(dpa_u_map_lu_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_lu_get(const dpa_u_map_lu_t* that, unsigned long key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_lu_get_and_remove(dpa_u_map_lu_t* that, unsigned long key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_lu_count(const dpa_u_map_lu_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_lu_dump_hashmap_key_hashes(dpa_u_map_lu_t* that); typedef struct dpa_u_map_llu dpa_u_map_llu_t; struct dpa_u_map_llu { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. unsigned long long*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_llu_exchange(dpa_u_map_llu_t* that, unsigned long long key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_llu_set(dpa_u_map_llu_t* that, unsigned long long key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_llu_remove(dpa_u_map_llu_t* that, unsigned long long key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_llu_has(const dpa_u_map_llu_t* that, unsigned long long key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_llu_clear(dpa_u_map_llu_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_llu_get(const dpa_u_map_llu_t* that, unsigned long long key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_llu_get_and_remove(dpa_u_map_llu_t* that, unsigned long long key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_llu_count(const dpa_u_map_llu_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_llu_dump_hashmap_key_hashes(dpa_u_map_llu_t* that); typedef struct dpa_u_map_z dpa_u_map_z_t; struct dpa_u_map_z { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. size_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_z_exchange(dpa_u_map_z_t* that, size_t key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_z_set(dpa_u_map_z_t* that, size_t key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_z_remove(dpa_u_map_z_t* that, size_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_z_has(const dpa_u_map_z_t* that, size_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_z_clear(dpa_u_map_z_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_z_get(const dpa_u_map_z_t* that, size_t key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_z_get_and_remove(dpa_u_map_z_t* that, size_t key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_z_count(const dpa_u_map_z_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_z_dump_hashmap_key_hashes(dpa_u_map_z_t* that); typedef struct dpa_u_map_u8 dpa_u_map_u8_t; struct dpa_u_map_u8 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint8_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u8_exchange(dpa_u_map_u8_t* that, uint8_t key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u8_set(dpa_u_map_u8_t* that, uint8_t key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_u8_remove(dpa_u_map_u8_t* that, uint8_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_u8_has(const dpa_u_map_u8_t* that, uint8_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_u8_clear(dpa_u_map_u8_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u8_get(const dpa_u_map_u8_t* that, uint8_t key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u8_get_and_remove(dpa_u_map_u8_t* that, uint8_t key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_u8_count(const dpa_u_map_u8_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_u8_dump_hashmap_key_hashes(dpa_u_map_u8_t* that); typedef struct dpa_u_map_u16 dpa_u_map_u16_t; struct dpa_u_map_u16 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint16_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u16_exchange(dpa_u_map_u16_t* that, uint16_t key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u16_set(dpa_u_map_u16_t* that, uint16_t key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_u16_remove(dpa_u_map_u16_t* that, uint16_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_u16_has(const dpa_u_map_u16_t* that, uint16_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_u16_clear(dpa_u_map_u16_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u16_get(const dpa_u_map_u16_t* that, uint16_t key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u16_get_and_remove(dpa_u_map_u16_t* that, uint16_t key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_u16_count(const dpa_u_map_u16_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_u16_dump_hashmap_key_hashes(dpa_u_map_u16_t* that); typedef struct dpa_u_map_u32 dpa_u_map_u32_t; struct dpa_u_map_u32 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint32_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u32_exchange(dpa_u_map_u32_t* that, uint32_t key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u32_set(dpa_u_map_u32_t* that, uint32_t key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_u32_remove(dpa_u_map_u32_t* that, uint32_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_u32_has(const dpa_u_map_u32_t* that, uint32_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_u32_clear(dpa_u_map_u32_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u32_get(const dpa_u_map_u32_t* that, uint32_t key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u32_get_and_remove(dpa_u_map_u32_t* that, uint32_t key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_u32_count(const dpa_u_map_u32_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_u32_dump_hashmap_key_hashes(dpa_u_map_u32_t* that); typedef struct dpa_u_map_u64 dpa_u_map_u64_t; struct dpa_u_map_u64 { // After a certain number of entries, it's more efficient to just save the bitmask. // The optimal memory usage for a bitmask of a set in bits is `CHAR_BIT**sizeof(DPA__U_SM_KEY_TYPE)`, but see the comment above for real memory usage. // The memory usage of a set which actually stores the keys is at least `sizeof(DPA__U_SM_KEY_TYPE) * count`, but a set with open addressing should // have a load facter of around 0.75 (75% of allocated entries used), and the memory usage will be that much higher. // This implementation will double the memory used by 2 when the load factor reaches 90%. // If this type is a map rather than a set, a value, which has type void*, has to be allocated for each entry too. // `bitmask` will be used if the amount of memory wasted is <= 50%. // Switching if no memory is wasted would also be possible, but since we already always allocate a power of 2 of memory for the key and value lists, there would be no benefits to that. size_t count; // This is the number of entries stored in the set. unsigned char lbsize; // This is the log 2 of the number of buckets available. union { // The key entry is a reversible hash of the original key. This way, the hash does not need to be recalculated, // even without storing the key and it's hash. uint64_t*restrict key_list; dpa_u_bitmap_entry_t*restrict bitmask; }; void**restrict value_list; }; /** * Sets an entry in the map and returns the existing one if one exists. * If there was none, the variable will remain unchanged. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u64_exchange(dpa_u_map_u64_t* that, uint64_t key, void** value); /** * Sets an entry in the map. * \returns 1 if an existing entry was overwritten, 0 if not, -1 on error. */ dpa__u_api int dpa_u_map_u64_set(dpa_u_map_u64_t* that, uint64_t key, void* value); /** * Removes an entry. * \returns true if the entry was present, false otherwise. */ dpa__u_api bool dpa_u_map_u64_remove(dpa_u_map_u64_t* that, uint64_t key); /** * Checks if an entry is present. * \returns true if the entry was present, false otherwise. */ dpa_u_reproducible dpa__u_api bool dpa_u_map_u64_has(const dpa_u_map_u64_t* that, uint64_t key); /** * Removes all entries, frees all allocations */ dpa__u_api void dpa_u_map_u64_clear(dpa_u_map_u64_t* that); /** * Returns the entry. */ dpa_u_reproducible dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u64_get(const dpa_u_map_u64_t* that, uint64_t key); /** * Removes and returns an entry. */ dpa__u_api dpa_u_optional_pointer_t dpa_u_map_u64_get_and_remove(dpa_u_map_u64_t* that, uint64_t key); dpa_u_reproducible dpa__u_api inline size_t dpa_u_map_u64_count(const dpa_u_map_u64_t* that){ return that->count; } /** * For debugging purposes only */ dpa__u_api void dpa_u_map_u64_dump_hashmap_key_hashes(dpa_u_map_u64_t* that);