@ -0,0 +1,208 @@ | |||||
#pragma once | |||||
#include <stdint.h> | |||||
#include <atomic> | |||||
#include <gp/array.hpp> | |||||
#include <gp/algorithm/tmp_manip.hpp> | |||||
#include <gp/exception.hpp> | |||||
namespace gp { | |||||
struct linear_skipstone { | |||||
constexpr static size_t ccbits = 1; | |||||
constexpr static size_t cclast = 1; | |||||
size_t ret = 0; | |||||
void next() { | |||||
++ret; | |||||
} | |||||
operator size_t() { | |||||
return ret; | |||||
} | |||||
}; | |||||
struct stepcache_skipstone { | |||||
constexpr static size_t ccbits = 7; | |||||
constexpr static size_t cclast = 127; | |||||
constexpr static size_t list[128] = { | |||||
0, 1, 2, 3, | |||||
8, 9, 10, 11, | |||||
20, 21, 22, 23, | |||||
36, 37, 38, 39, | |||||
56, 57, 58, 59, | |||||
80, 81, 82, 83, | |||||
108, 109, 110, 111, | |||||
140, 141, 142, 143, | |||||
176, 177, 178, 179, | |||||
216, 217, 218, 219, | |||||
260, 261, 262, 263, | |||||
308, 309, 310, 311, | |||||
360, 361, 362, 363, | |||||
416, 417, 418, 419, | |||||
476, 477, 478, 479, | |||||
540, 541, 542, 543, | |||||
608, 609, 610, 611, | |||||
680, 681, 682, 683, | |||||
756, 757, 758, 759, | |||||
836, 837, 838, 839, | |||||
920, 921, 922, 923, | |||||
1008, 1009, 1010, 1011, | |||||
1100, 1101, 1102, 1103, | |||||
1196, 1197, 1198, 1199, | |||||
1296, 1297, 1298, 1299, | |||||
1400, 1401, 1402, 1403, | |||||
1508, 1509, 1510, 1511, | |||||
1736, 1737, 1738, 1739, | |||||
1856, 1857, 1858, 1859, | |||||
1980, 1981, 1982, 1983, | |||||
2108, 2109, 2110, 2111, | |||||
2240, 2241, 2242, 2243 | |||||
}; | |||||
size_t ret = 0; | |||||
void next() { | |||||
++ret; | |||||
} | |||||
operator size_t() { | |||||
[[unlikely]] if ( ret >= 128 ) | |||||
return ret+list[127]; | |||||
return list[ret]; | |||||
} | |||||
}; | |||||
template<typename hash_type = uint32_t, uint8_t magnitude = 20, uint8_t remainder = (8*sizeof(hash_type))-magnitude, typename skipstone_t = linear_skipstone> | |||||
class quotient_filter { | |||||
constexpr static size_t phys_size = (1 << magnitude) / 32; | |||||
using rem_t = typename gp::either<(remainder<=16), uint16_t, uint32_t>::type; | |||||
struct node{ | |||||
//rem_t ccount : skipstone_t::ccbits; | |||||
rem_t is_occupied : 1; | |||||
rem_t is_deleted : 1; | |||||
rem_t r : remainder; | |||||
}; | |||||
gp::array< | |||||
node, | |||||
1 << magnitude | |||||
> data; | |||||
void skipstone_killswitch(size_t q, size_t skip) | |||||
{ | |||||
if(q == (q+skip)%data.size()) | |||||
{ | |||||
if constexpr (gp_config::has_exceptions) | |||||
{ | |||||
throw gp::runtime_error("infinite skiploop detected"); | |||||
} | |||||
else | |||||
{ | |||||
exit((int)gp_errorcodes::infinite_skipstone); | |||||
} | |||||
} | |||||
} | |||||
public: | |||||
void set_hash(hash_type v) | |||||
{ | |||||
const size_t q = v & ((1 << magnitude)-1); | |||||
const size_t r = (v >> magnitude) & ((1 << remainder)-1); | |||||
skipstone_t skip; | |||||
for(;;) | |||||
{ | |||||
auto& slot = data[(q+skip)%data.size()]; | |||||
if(slot.is_occupied) | |||||
{ | |||||
if(slot.is_deleted) | |||||
{ | |||||
slot.r = r; | |||||
slot.is_deleted = false; | |||||
for(;;) | |||||
{ | |||||
skip.next(); | |||||
slot = data[(q+skip)%data.size()]; | |||||
if(!slot.is_occupied) | |||||
{ | |||||
return; | |||||
} | |||||
else if(slot.r == r) | |||||
{ | |||||
slot.is_deleted = true; | |||||
} | |||||
skipstone_killswitch(q, skip); | |||||
} | |||||
} | |||||
if(slot.r == r) | |||||
{ | |||||
return; | |||||
} | |||||
skip.next(); | |||||
} | |||||
else | |||||
{ | |||||
slot.r = r; | |||||
slot.is_occupied = true; | |||||
slot.is_deleted = false; | |||||
return; | |||||
} | |||||
skipstone_killswitch(q, skip); | |||||
} | |||||
} | |||||
bool test_hash(hash_type v) | |||||
{ | |||||
const size_t q = v & ((1 << magnitude)-1); | |||||
const size_t r = (v >> magnitude) & ((1 << remainder)-1); | |||||
skipstone_t skip; | |||||
for(;;) | |||||
{ | |||||
auto& slot = data[(q+skip)%data.size()]; | |||||
if(slot.is_occupied) | |||||
{ | |||||
if(!slot.is_deleted) | |||||
{ | |||||
if(slot.r == r) | |||||
{ | |||||
return true; | |||||
} | |||||
} | |||||
skip.next(); | |||||
} | |||||
else | |||||
{ | |||||
return false; | |||||
} | |||||
skipstone_killswitch(q, skip); | |||||
} | |||||
} | |||||
void remove_hash(hash_type v) | |||||
{ | |||||
const size_t q = v & ((1 << magnitude)-1); | |||||
const size_t r = (v >> magnitude) & ((1 << remainder)-1); | |||||
skipstone_t skip; | |||||
for(;;) | |||||
{ | |||||
auto& slot = data[(q+skip)%data.size()]; | |||||
if(slot.is_occupied) | |||||
{ | |||||
if(!slot.is_deleted) | |||||
{ | |||||
if(slot.r == r) | |||||
{ | |||||
slot.is_deleted = true; | |||||
} | |||||
} | |||||
skip.next(); | |||||
} | |||||
else | |||||
{ | |||||
return; | |||||
} | |||||
skipstone_killswitch(q, skip); | |||||
} | |||||
} | |||||
}; | |||||
} |
@ -0,0 +1,127 @@ | |||||
#include "test_scaffold.h" | |||||
#include <random> | |||||
#include <string> | |||||
#include "gp/quotient_filter.hpp" | |||||
typedef std::linear_congruential_engine<uint_fast64_t, 128273, 13, 2147483647> cheap_rand; | |||||
typedef std::linear_congruential_engine<uint_fast64_t, 69857, 87541, 2147483647> cheap_rand_bis; | |||||
struct qfilter_test : public test_scaffold { | |||||
uint32_t seed; | |||||
qfilter_test() { | |||||
seed = std::random_device{}(); | |||||
name = __FILE__ ":1_seed"; | |||||
name += std::to_string(seed); | |||||
} | |||||
virtual int run() { | |||||
cheap_rand setter(seed); | |||||
cheap_rand getter(seed); | |||||
gp::quotient_filter test_filter; | |||||
for(int a = 0 ; a < 100; a++) | |||||
{ | |||||
test_filter.set_hash(setter()); | |||||
} | |||||
bool result = true; | |||||
for(int a = 0 ; a < 100; a++) | |||||
{ | |||||
result *= test_filter.test_hash(getter()); | |||||
} | |||||
return !result; | |||||
} | |||||
}; | |||||
append_test dummy_rshyr43(new qfilter_test{}); | |||||
struct qfilter2_test : public test_scaffold { | |||||
uint32_t seedA; | |||||
uint32_t seedB; | |||||
qfilter2_test() { | |||||
seedA = std::random_device{}(); | |||||
seedB = std::random_device{}(); | |||||
name = __FILE__ ":2_seedA"; | |||||
name += std::to_string(seedA); | |||||
name += "&seedB"; | |||||
name += std::to_string(seedB); | |||||
} | |||||
virtual int run() { | |||||
cheap_rand setter(seedA); | |||||
cheap_rand deleter(seedA); | |||||
cheap_rand_bis getter(seedB); | |||||
gp::quotient_filter test_filter; | |||||
for(int a = 0 ; a < 100000; a++) | |||||
{ | |||||
test_filter.set_hash(setter()); | |||||
} | |||||
int interference = 0; | |||||
for(int a = 0 ; a < 100000; a++) | |||||
{ | |||||
interference += test_filter.test_hash(getter()); | |||||
} | |||||
for(int a = 0 ; a < 100000; a++) | |||||
{ | |||||
test_filter.remove_hash(deleter()); | |||||
} | |||||
return interference > 25; | |||||
} | |||||
}; | |||||
append_test dummy_kegdflu3(new qfilter2_test{}); | |||||
struct qfilter3_test : public test_scaffold { | |||||
uint32_t seedA; | |||||
uint32_t seedB; | |||||
qfilter3_test() { | |||||
seedA = std::random_device{}(); | |||||
seedB = std::random_device{}(); | |||||
name = __FILE__ ":3_seedA"; | |||||
name += std::to_string(seedA); | |||||
name += "&seedB"; | |||||
name += std::to_string(seedB); | |||||
} | |||||
virtual int run() { | |||||
cheap_rand setter(seedA); | |||||
cheap_rand deleter(seedA); | |||||
cheap_rand_bis getter(seedB); | |||||
gp::quotient_filter<uint32_t, 20,12, gp::stepcache_skipstone> test_filter; | |||||
for(int a = 0 ; a < 100000; a++) | |||||
{ | |||||
test_filter.set_hash(setter()); | |||||
} | |||||
int interference = 0; | |||||
for(int a = 0 ; a < 100000; a++) | |||||
{ | |||||
interference += test_filter.test_hash(getter()); | |||||
} | |||||
for(int a = 0 ; a < 100000; a++) | |||||
{ | |||||
test_filter.remove_hash(deleter()); | |||||
} | |||||
return interference > 25; | |||||
} | |||||
}; | |||||
append_test dummy_kjdflu3(new qfilter3_test{}); |