General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
5.1 KiB

  1. #pragma once
  2. #include <gp/algorithms/foreach.hpp>
  3. #include <gp/algorithms/tmp_manip.hpp>
  4. #include <gp/containers/array.hpp>
  5. #include <gp/exception.hpp>
  6. #include <atomic>
  7. #include <stdint.h>
  8. namespace gp {
  9. // TODO: Specify the concept of a skipstone
  10. /**
  11. * @brief An abstraction for exploring and picking in a list sequencially
  12. */
  13. struct linear_skipstone {
  14. constexpr static size_t ccbits = 1;
  15. constexpr static size_t cclast = 1;
  16. size_t ret = 0;
  17. void next() {
  18. ++ret;
  19. }
  20. operator size_t() {
  21. return ret;
  22. }
  23. };
  24. /**
  25. * @brief An abstraction for exploring and picking in a list in a non sequencial but still relatively cache friendly way
  26. */
  27. struct stepcache_skipstone {
  28. constexpr static size_t ccbits = 7;
  29. constexpr static size_t cclast = 127;
  30. constexpr static size_t list[128] = {
  31. 0, 1, 2, 3,
  32. 8, 9, 10, 11,
  33. 20, 21, 22, 23,
  34. 36, 37, 38, 39,
  35. 56, 57, 58, 59,
  36. 80, 81, 82, 83,
  37. 108, 109, 110, 111,
  38. 140, 141, 142, 143,
  39. 176, 177, 178, 179,
  40. 216, 217, 218, 219,
  41. 260, 261, 262, 263,
  42. 308, 309, 310, 311,
  43. 360, 361, 362, 363,
  44. 416, 417, 418, 419,
  45. 476, 477, 478, 479,
  46. 540, 541, 542, 543,
  47. 608, 609, 610, 611,
  48. 680, 681, 682, 683,
  49. 756, 757, 758, 759,
  50. 836, 837, 838, 839,
  51. 920, 921, 922, 923,
  52. 1008, 1009, 1010, 1011,
  53. 1100, 1101, 1102, 1103,
  54. 1196, 1197, 1198, 1199,
  55. 1296, 1297, 1298, 1299,
  56. 1400, 1401, 1402, 1403,
  57. 1508, 1509, 1510, 1511,
  58. 1736, 1737, 1738, 1739,
  59. 1856, 1857, 1858, 1859,
  60. 1980, 1981, 1982, 1983,
  61. 2108, 2109, 2110, 2111,
  62. 2240, 2241, 2242, 2243
  63. };
  64. size_t ret = 0;
  65. void next() {
  66. ++ret;
  67. }
  68. operator size_t() {
  69. [[unlikely]] if ( ret >= 128 )
  70. return ret+list[127];
  71. return list[ret];
  72. }
  73. };
  74. /**
  75. * @brief A datastructure to accurately check belonging in a set in a probabilistic way.
  76. * More precise than a bloomfilter, less space efficient, can allow deletion (handle with care)
  77. *
  78. * @tparam hash_type the type of hash storage type
  79. * @tparam magnitude the size of the filter as a power of 2
  80. * @tparam remainder the number of bits of remainder of the quotient \f$\frac{h}{m}\f$ where \f$h\f$ is a hash and \f$m\f$ the magnitude of the set, can throw away bits
  81. * @tparam skipstone_t the type of sequence exploration to perform
  82. */
  83. template<typename hash_type = uint32_t, uint8_t magnitude = 20, uint8_t remainder = (8*sizeof(hash_type))-magnitude, typename skipstone_t = linear_skipstone>
  84. class quotient_filter {
  85. constexpr static size_t phys_size = (1 << magnitude) / 32;
  86. using rem_t = typename gp::either<(remainder<=16), uint16_t, uint32_t>::type;
  87. struct node{
  88. //rem_t ccount : skipstone_t::ccbits;
  89. rem_t is_occupied : 1;
  90. rem_t is_deleted : 1;
  91. rem_t r : remainder;
  92. };
  93. static_assert(!(sizeof(size_t) <= 4 && magnitude > 31), "Incompatible size for quotient_filter: the filter would fill the address space");
  94. gp::array<
  95. node,
  96. 1 << magnitude
  97. > data;
  98. void skipstone_killswitch(size_t q, size_t skip)
  99. {
  100. if(q == (q+skip)%data.size())
  101. {
  102. if constexpr (gp_config::has_exceptions)
  103. {
  104. throw gp::runtime_error("infinite skiploop detected");
  105. }
  106. else
  107. {
  108. exit((int)gp_errorcodes::infinite_skipstone);
  109. }
  110. }
  111. }
  112. public:
  113. quotient_filter() {
  114. gp::fill(data, node{0,0,0});
  115. }
  116. void set_hash(hash_type v)
  117. {
  118. const size_t q = v & ((1 << magnitude)-1);
  119. const size_t r = (v >> magnitude) & ((1 << remainder)-1);
  120. skipstone_t skip;
  121. for(;;)
  122. {
  123. auto& slot = data[(q+skip)%data.size()];
  124. if(slot.is_occupied)
  125. {
  126. if(slot.is_deleted)
  127. {
  128. slot.r = r;
  129. slot.is_deleted = false;
  130. for(;;)
  131. {
  132. skip.next();
  133. slot = data[(q+skip)%data.size()];
  134. if(!slot.is_occupied)
  135. {
  136. return;
  137. }
  138. else if(slot.r == r)
  139. {
  140. slot.is_deleted = true;
  141. }
  142. skipstone_killswitch(q, skip);
  143. }
  144. }
  145. if(slot.r == r)
  146. {
  147. return;
  148. }
  149. skip.next();
  150. }
  151. else
  152. {
  153. slot.r = r;
  154. slot.is_occupied = true;
  155. slot.is_deleted = false;
  156. return;
  157. }
  158. skipstone_killswitch(q, skip);
  159. }
  160. }
  161. bool test_hash(hash_type v)
  162. {
  163. const size_t q = v & ((1 << magnitude)-1);
  164. const size_t r = (v >> magnitude) & ((1 << remainder)-1);
  165. skipstone_t skip;
  166. for(;;)
  167. {
  168. auto& slot = data[(q+skip)%data.size()];
  169. if(slot.is_occupied)
  170. {
  171. if(!slot.is_deleted)
  172. {
  173. if(slot.r == r)
  174. {
  175. return true;
  176. }
  177. }
  178. skip.next();
  179. }
  180. else
  181. {
  182. return false;
  183. }
  184. skipstone_killswitch(q, skip);
  185. }
  186. }
  187. void remove_hash(hash_type v)
  188. {
  189. const size_t q = v & ((1 << magnitude)-1);
  190. const size_t r = (v >> magnitude) & ((1 << remainder)-1);
  191. skipstone_t skip;
  192. for(;;)
  193. {
  194. auto& slot = data[(q+skip)%data.size()];
  195. if(slot.is_occupied)
  196. {
  197. if(!slot.is_deleted)
  198. {
  199. if(slot.r == r)
  200. {
  201. slot.is_deleted = true;
  202. slot.is_occupied = false;
  203. }
  204. }
  205. skip.next();
  206. }
  207. else
  208. {
  209. return;
  210. }
  211. skipstone_killswitch(q, skip);
  212. }
  213. }
  214. };
  215. }