File indexing completed on 2026-08-17 08:39:05
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_BLOOM_DETAIL_CORE_HPP
0012 #define BOOST_BLOOM_DETAIL_CORE_HPP
0013
0014 #include <algorithm>
0015 #include <boost/assert.hpp>
0016 #include <boost/bloom/detail/mulx64.hpp>
0017 #include <boost/bloom/detail/sse2.hpp>
0018 #include <boost/config.hpp>
0019 #include <boost/core/allocator_traits.hpp>
0020 #include <boost/core/bit.hpp>
0021 #include <boost/core/empty_value.hpp>
0022 #include <boost/core/span.hpp>
0023 #include <boost/throw_exception.hpp>
0024 #include <climits>
0025 #include <cmath>
0026 #include <cstdint>
0027 #include <cstring>
0028 #include <limits>
0029 #include <memory>
0030 #include <stdexcept>
0031 #include <tuple>
0032 #include <type_traits>
0033 #include <utility>
0034
0035 #ifdef __has_builtin
0036 #define BOOST_BLOOM_HAS_BUILTIN(x) __has_builtin(x)
0037 #else
0038 #define BOOST_BLOOM_HAS_BUILTIN(x) 0
0039 #endif
0040
0041 #if !defined(NDEBUG)
0042 #define BOOST_BLOOM_ASSUME(cond) BOOST_ASSERT(cond)
0043 #elif BOOST_BLOOM_HAS_BUILTIN(__builtin_assume)
0044 #define BOOST_BLOOM_ASSUME(cond) __builtin_assume(cond)
0045 #elif defined(__GNUC__) || BOOST_BLOOM_HAS_BUILTIN(__builtin_unreachable)
0046 #define BOOST_BLOOM_ASSUME(cond) \
0047 do{ \
0048 if(!(cond))__builtin_unreachable(); \
0049 }while(0)
0050 #elif defined(_MSC_VER)
0051 #define BOOST_BLOOM_ASSUME(cond) __assume(cond)
0052 #else
0053 #define BOOST_BLOOM_ASSUME(cond) \
0054 do{ \
0055 static_cast<void>(false&&(cond)); \
0056 }while(0)
0057 #endif
0058
0059
0060
0061
0062
0063 #if defined(BOOST_GCC)||defined(BOOST_CLANG)
0064 #define BOOST_BLOOM_PREFETCH(p) __builtin_prefetch((const char*)(p))
0065 #define BOOST_BLOOM_PREFETCH_WRITE(p) __builtin_prefetch((const char*)(p),1)
0066 #elif defined(BOOST_BLOOM_SSE2)
0067 #define BOOST_BLOOM_PREFETCH(p) _mm_prefetch((const char*)(p),_MM_HINT_T0)
0068 #if defined(_MM_HINT_ET0)
0069 #define BOOST_BLOOM_PREFETCH_WRITE(p) \
0070 _mm_prefetch((const char*)(p),_MM_HINT_ET0)
0071 #else
0072 #define BOOST_BLOOM_PREFETCH_WRITE(p) \
0073 _mm_prefetch((const char*)(p),_MM_HINT_T0)
0074 #endif
0075 #else
0076 #define BOOST_BLOOM_PREFETCH(p) ((void)(p))
0077 #define BOOST_BLOOM_PREFETCH_WRITE(p) ((void)(p))
0078 #endif
0079
0080 namespace boost{
0081 namespace bloom{
0082 namespace detail{
0083
0084 #if defined(BOOST_MSVC)
0085 #pragma warning(push)
0086 #pragma warning(disable:4714)
0087 #endif
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 struct fastrange_and_mcg
0102 {
0103 constexpr fastrange_and_mcg(std::size_t m)noexcept:rng{m}{}
0104
0105
0106 inline constexpr std::size_t range()const noexcept{return (std::size_t)rng;}
0107
0108
0109 inline void prepare_hash(std::uint64_t& hash)const noexcept
0110 {
0111 hash|=1u;
0112 }
0113
0114
0115 inline std::size_t next_position(std::uint64_t& hash)const noexcept
0116 {
0117 boost::uint64_t hi;
0118 umul128(hash,rng,hi);
0119
0120 #if ((((SIZE_MAX>>16)>>16)>>16)>>15)!=0
0121 hash*=0xf1357aea2e62a9c5ull;
0122 #else
0123 hash*=0xe817fb2d;
0124 #endif
0125 return (std::size_t)hi;
0126 }
0127
0128 std::uint64_t rng;
0129 };
0130
0131
0132
0133
0134
0135
0136
0137 template<typename Subfilter,typename=void>
0138 struct used_value_size
0139 {
0140 static constexpr std::size_t value=sizeof(typename Subfilter::value_type);
0141 };
0142
0143 template<typename Subfilter>
0144 struct used_value_size<
0145 Subfilter,
0146 typename std::enable_if<Subfilter::used_value_size!=0>::type
0147 >
0148 {
0149 static constexpr std::size_t value=Subfilter::used_value_size;
0150 };
0151
0152
0153
0154 constexpr std::size_t gcd_pow2(std::size_t x,std::size_t p)
0155 {
0156
0157 return (x&(0-x))<p?(x&(0-x)):p;
0158 }
0159
0160
0161
0162 constexpr double constexpr_ldexp_1_positive(int exp)
0163 {
0164 return exp==0?1.0:2.0*constexpr_ldexp_1_positive(exp-1);
0165 }
0166
0167 inline unsigned int unchecked_countr_zero(std::uint64_t x)
0168 {
0169 #if defined(BOOST_MSVC)&&(defined(_M_X64)||defined(_M_ARM64))
0170 unsigned long r;
0171 _BitScanForward64(&r,x);
0172 return (unsigned int)r;
0173 #elif defined(BOOST_GCC)||defined(BOOST_CLANG)
0174 return (unsigned int)__builtin_ctzll(x);
0175 #else
0176 BOOST_BLOOM_ASSUME(x!=0);
0177 return (unsigned int)boost::core::countr_zero(x);
0178 #endif
0179 }
0180
0181 struct filter_array
0182 {
0183 unsigned char* data;
0184 unsigned char* array;
0185 };
0186
0187 struct if_constexpr_void_else{void operator()()const{}};
0188
0189 template<bool B,typename F,typename G=if_constexpr_void_else>
0190 void if_constexpr(F f,G g={})
0191 {
0192 std::get<B?0:1>(std::forward_as_tuple(f,g))();
0193 }
0194
0195 template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
0196 void copy_assign_if(T& x,const T& y){x=y;}
0197
0198 template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
0199 void copy_assign_if(T&,const T&){}
0200
0201 template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
0202 void move_assign_if(T& x,T& y){x=std::move(y);}
0203
0204 template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
0205 void move_assign_if(T&,T&){}
0206
0207 template<bool B,typename T,typename std::enable_if<B>::type* =nullptr>
0208 void swap_if(T& x,T& y){using std::swap; swap(x,y);}
0209
0210 template<bool B,typename T,typename std::enable_if<!B>::type* =nullptr>
0211 void swap_if(T&,T&){}
0212
0213 template<
0214 std::size_t K,typename Subfilter,std::size_t Stride,typename Allocator
0215 >
0216 class filter_core:empty_value<Allocator,0>
0217 {
0218 static_assert(K>0,"K must be >= 1");
0219 static_assert(
0220 std::is_same<allocator_value_type_t<Allocator>,unsigned char>::value,
0221 "Allocator value_type must be unsigned char");
0222
0223 public:
0224 static constexpr std::size_t k=K;
0225 using subfilter=Subfilter;
0226
0227 private:
0228 static constexpr std::size_t kp=subfilter::k;
0229 static constexpr std::size_t k_total=k*kp;
0230 using block_type=typename subfilter::value_type;
0231 static constexpr std::size_t block_size=sizeof(block_type);
0232 static constexpr std::size_t used_value_size=
0233 detail::used_value_size<subfilter>::value;
0234
0235 public:
0236 static constexpr std::size_t stride=Stride?Stride:used_value_size;
0237 static_assert(
0238 stride<=used_value_size,"Stride can't exceed the block size");
0239
0240 private:
0241 static constexpr std::size_t tail_size=sizeof(block_type)-stride;
0242 static constexpr bool are_blocks_aligned=
0243 (stride%alignof(block_type)==0);
0244 static constexpr std::size_t cacheline=64;
0245 static constexpr std::size_t initial_alignment=
0246 are_blocks_aligned?
0247 alignof(block_type)>cacheline?alignof(block_type):cacheline:
0248 1;
0249 static constexpr std::size_t prefetched_cachelines=
0250 1+(block_size+cacheline-1-gcd_pow2(stride,cacheline))/cacheline;
0251 using hash_strategy=detail::fastrange_and_mcg;
0252
0253 public:
0254 using allocator_type=Allocator;
0255 using size_type=std::size_t;
0256 using difference_type=std::ptrdiff_t;
0257 using pointer=unsigned char*;
0258 using const_pointer=const unsigned char*;
0259 static constexpr std::size_t bulk_insert_size=
0260 (64+prefetched_cachelines-1)/prefetched_cachelines;
0261 static constexpr std::size_t bulk_may_contain_size=
0262 (64+prefetched_cachelines-1)/prefetched_cachelines;
0263 static_assert(
0264 bulk_may_contain_size<=64,
0265 "internal check, bulk_may_contain_size must be <= 64");
0266
0267 explicit filter_core(std::size_t m=0):filter_core{m,allocator_type{}}{}
0268
0269 filter_core(std::size_t m,const allocator_type& al_):
0270 allocator_base{empty_init,al_},
0271 hs{requested_range(m)},
0272 ar(new_array(al(),m?hs.range():0))
0273 {
0274 clear_bytes();
0275 }
0276
0277 filter_core(std::size_t n,double fpr,const allocator_type& al_):
0278 filter_core(unadjusted_capacity_for(n,fpr),al_){}
0279
0280 filter_core(const filter_core& x):
0281 filter_core{x,allocator_select_on_container_copy_construction(x.al())}{}
0282
0283 filter_core(filter_core&& x)noexcept:
0284 filter_core{std::move(x),allocator_type(std::move(x.al()))}{}
0285
0286 filter_core(const filter_core& x,const allocator_type& al_):
0287 allocator_base{empty_init,al_},
0288 hs{x.hs},
0289 ar(new_array(al(),x.range()))
0290 {
0291 copy_bytes(x);
0292 }
0293
0294 filter_core(filter_core&& x,const allocator_type& al_):
0295 allocator_base{empty_init,al_},
0296 hs{x.hs}
0297 {
0298 auto empty_ar=new_array(x.al(),0);
0299 if(al()==x.al()){
0300 ar=x.ar;
0301 }
0302 else{
0303 ar=new_array(al(),x.range());
0304 copy_bytes(x);
0305 x.delete_array();
0306 }
0307 x.hs=hash_strategy{0};
0308 x.ar=empty_ar;
0309 }
0310
0311 ~filter_core()noexcept
0312 {
0313 delete_array();
0314 }
0315
0316 filter_core& operator=(const filter_core& x)
0317 {
0318 static constexpr auto pocca=
0319 allocator_propagate_on_container_copy_assignment_t<allocator_type>::
0320 value;
0321
0322 if(this!=&x){
0323 if_constexpr<pocca>([&,this]{
0324 if(al()!=x.al()||range()!=x.range()){
0325 auto x_al=x.al();
0326 auto new_ar=new_array(x_al,x.range());
0327 delete_array();
0328 hs=x.hs;
0329 ar=new_ar;
0330 }
0331 copy_assign_if<pocca>(al(),x.al());
0332 },
0333 [&,this]{
0334 if(range()!=x.range()){
0335 auto new_ar=new_array(al(),x.range());
0336 delete_array();
0337 hs=x.hs;
0338 ar=new_ar;
0339 }
0340 });
0341 copy_bytes(x);
0342 }
0343 return *this;
0344 }
0345
0346 #if defined(BOOST_MSVC)
0347 #pragma warning(push)
0348 #pragma warning(disable:4127)
0349 #endif
0350
0351 filter_core& operator=(filter_core&& x)noexcept(
0352 allocator_propagate_on_container_move_assignment_t<allocator_type>::value||
0353 allocator_is_always_equal_t<allocator_type>::value)
0354 {
0355 static constexpr auto pocma=
0356 allocator_propagate_on_container_move_assignment_t<allocator_type>::
0357 value;
0358
0359 if(this!=&x){
0360 auto empty_ar=new_array(x.al(),0);
0361 if(pocma||al()==x.al()){
0362 delete_array();
0363 move_assign_if<pocma>(al(),x.al());
0364 hs=x.hs;
0365 ar=x.ar;
0366 }
0367 else{
0368 if(range()!=x.range()){
0369 auto new_ar=new_array(al(),x.range());
0370 delete_array();
0371 hs=x.hs;
0372 ar=new_ar;
0373 }
0374 copy_bytes(x);
0375 x.delete_array();
0376 }
0377 x.hs=hash_strategy{0};
0378 x.ar=empty_ar;
0379 }
0380 return *this;
0381 }
0382
0383 #if defined(BOOST_MSVC)
0384 #pragma warning(pop)
0385 #endif
0386
0387 allocator_type get_allocator()const noexcept
0388 {
0389 return al();
0390 }
0391
0392 std::size_t capacity()const noexcept
0393 {
0394 return used_array_size()*CHAR_BIT;
0395 }
0396
0397 static std::size_t capacity_for(std::size_t n,double fpr)
0398 {
0399 auto m=unadjusted_capacity_for(n,fpr);
0400 if(m==0)return 0;
0401 auto rng=hash_strategy{requested_range(m)}.range();
0402 return used_array_size(rng)*CHAR_BIT;
0403 }
0404
0405 static double fpr_for(std::size_t n,std::size_t m)
0406 {
0407 return m==0?1.0:n==0?0.0:fpr_for_c((double)m/n);
0408 }
0409
0410 boost::span<unsigned char> array()noexcept
0411 {
0412 return {ar.data?ar.array:nullptr,capacity()/CHAR_BIT};
0413 }
0414
0415 boost::span<const unsigned char> array()const noexcept
0416 {
0417 return {ar.data?ar.array:nullptr,capacity()/CHAR_BIT};
0418 }
0419
0420 BOOST_FORCEINLINE void insert(std::uint64_t hash)
0421 {
0422 hs.prepare_hash(hash);
0423 for(auto n=k;n--;){
0424 auto p=next_element(hash);
0425
0426
0427
0428
0429 if(BOOST_UNLIKELY(n==k-1&&ar.data==nullptr))return;
0430
0431 set(p,hash);
0432 }
0433 }
0434
0435 template<typename HashStream>
0436 void bulk_insert(HashStream h,std::size_t n)
0437 {
0438 std::uint64_t hashes[bulk_insert_size];
0439 unsigned char* positions[bulk_insert_size];
0440
0441 if(n>=2*bulk_insert_size){
0442 for(auto i=bulk_insert_size;i--;){
0443 auto& hash=hashes[i]=h();
0444 auto& p=positions[i];
0445 hs.prepare_hash(hash);
0446 p=next_element(hash);
0447 }
0448 if(BOOST_UNLIKELY(ar.data==nullptr))return;
0449 do{
0450 for(auto j=k-1;j--;){
0451 for(auto i=bulk_insert_size;i--;){
0452 auto& hash=hashes[i];
0453 auto& p=positions[i];
0454 auto hash0=hash;
0455 auto p0=p;
0456 p=next_element(hash);
0457 set(p0,hash0);
0458 }
0459 }
0460 for(auto i=bulk_insert_size;i--;){
0461 auto& hash=hashes[i];
0462 auto& p=positions[i];
0463 auto hash0=hash;
0464 auto p0=p;
0465 hash=h();
0466 hs.prepare_hash(hash);
0467 p=next_element(hash);
0468 set(p0,hash0);
0469 }
0470 n-=bulk_insert_size;
0471 }while(n>=2*bulk_insert_size);
0472 for(auto j=k-1;j--;){
0473 for(auto i=bulk_insert_size;i--;){
0474 auto& hash=hashes[i];
0475 auto& p=positions[i];
0476 auto hash0=hash;
0477 auto p0=p;
0478 p=next_element(hash);
0479 set(p0,hash0);
0480 }
0481 }
0482 for(auto i=bulk_insert_size;i--;){
0483 auto& hash=hashes[i];
0484 auto& p=positions[i];
0485 set(p,hash);
0486 }
0487 n-=bulk_insert_size;
0488 }
0489 while(n--)insert(h());
0490 }
0491
0492 void swap(filter_core& x)noexcept(
0493 allocator_propagate_on_container_swap_t<allocator_type>::value||
0494 allocator_is_always_equal_t<allocator_type>::value)
0495 {
0496 static constexpr auto pocs=
0497 allocator_propagate_on_container_swap_t<allocator_type>::value;
0498
0499 if_constexpr<pocs>([&,this]{
0500 swap_if<pocs>(al(),x.al());
0501 },
0502 [&,this]{
0503 BOOST_ASSERT(al()==x.al());
0504 (void)this;
0505 });
0506 std::swap(hs,x.hs);
0507 std::swap(ar,x.ar);
0508 }
0509
0510 void clear()noexcept
0511 {
0512 clear_bytes();
0513 }
0514
0515 void reset(std::size_t m=0)
0516 {
0517 hash_strategy new_hs{requested_range(m)};
0518 std::size_t rng=m?new_hs.range():0;
0519 if(rng!=range()){
0520 auto new_ar=new_array(al(),rng);
0521 delete_array();
0522 hs=new_hs;
0523 ar=new_ar;
0524 }
0525 clear_bytes();
0526 }
0527
0528 void reset(std::size_t n,double fpr)
0529 {
0530 reset(capacity_for(n,fpr));
0531 }
0532
0533 filter_core& operator&=(const filter_core& x)
0534 {
0535 combine(x,[](unsigned char& a,unsigned char b){a&=b;});
0536 return *this;
0537 }
0538
0539 filter_core& operator|=(const filter_core& x)
0540 {
0541 combine(x,[](unsigned char& a,unsigned char b){a|=b;});
0542 return *this;
0543 }
0544
0545 BOOST_FORCEINLINE bool may_contain(std::uint64_t hash)const
0546 {
0547 hs.prepare_hash(hash);
0548 #if 1
0549 auto p0=next_element(hash);
0550 for(std::size_t n=k-1;n--;){
0551 auto p=p0;
0552 auto hash0=hash;
0553 p0=next_element(hash);
0554 if(!get(p,hash0))return false;
0555 }
0556 if(!get(p0,hash))return false;
0557 return true;
0558 #else
0559 for(auto n=k;n--;){
0560 auto p=next_element(hash);
0561 if(!get(p,hash))return false;
0562 }
0563 return true;
0564 #endif
0565 }
0566
0567 template<typename HashStream,typename F>
0568 void bulk_may_contain(HashStream h,std::size_t n,F f)const
0569 {
0570 if(k==1){
0571 std::uint64_t hashes[bulk_may_contain_size];
0572 const unsigned char* positions[bulk_may_contain_size];
0573
0574 if(n>=2*bulk_may_contain_size){
0575 for(auto i=bulk_may_contain_size;i--;){
0576 auto& hash=hashes[i]=h();
0577 auto& p=positions[i];
0578 hs.prepare_hash(hash);
0579 p=next_element(hash);
0580 }
0581 do{
0582 for(auto i=bulk_may_contain_size;i--;){
0583 auto& hash=hashes[i];
0584 auto& p=positions[i];
0585 auto hash0=hash;
0586 auto p0=p;
0587 hash=h();
0588 hs.prepare_hash(hash);
0589 p=next_element(hash);
0590 f(get(p0,hash0));
0591 }
0592 n-=bulk_may_contain_size;
0593 }while(n>=2*bulk_may_contain_size);
0594
0595 for(auto i=bulk_may_contain_size;i--;){
0596 auto& hash=hashes[i];
0597 auto& p=positions[i];
0598 f(get(p,hash));
0599 }
0600 n-=bulk_may_contain_size;
0601 }
0602
0603 while(n--)f(may_contain(h()));
0604 }
0605 else{
0606 static constexpr std::uint64_t initial_result_mask=
0607 ((std::uint64_t(1)<<(bulk_may_contain_size/2))<<
0608 ((bulk_may_contain_size+1)/2))-1;
0609
0610 std::uint64_t hashes[bulk_may_contain_size];
0611 const unsigned char* positions[bulk_may_contain_size];
0612 std::uint64_t results=initial_result_mask;
0613
0614 if(n>=2*bulk_may_contain_size){
0615 for(std::size_t i=0;i<bulk_may_contain_size;++i){
0616 auto& hash=hashes[i]=h();
0617 auto& p=positions[i];
0618 hs.prepare_hash(hash);
0619 p=next_element(hash);
0620 }
0621 do{
0622 for(auto j=k;j--;){
0623 auto mask=results;
0624 if(!mask)break;
0625 do{
0626 auto i=unchecked_countr_zero(mask);
0627 auto& hash=hashes[i];
0628 auto& p=positions[i];
0629 auto b=get(p,hash);
0630 p=next_element(hash);
0631 results&=~(std::uint64_t(!b)<<i);
0632 mask&=mask-1;
0633 }while(mask);
0634 }
0635 for(std::size_t i=0;i<bulk_may_contain_size;++i){
0636 auto& hash=hashes[i];
0637 auto& p=positions[i];
0638 hash=h();
0639 hs.prepare_hash(hash);
0640 p=next_element(hash);
0641 f(results&1);
0642 results>>=1;
0643 }
0644 results=initial_result_mask;
0645 n-=bulk_may_contain_size;
0646 }while(n>=2*bulk_may_contain_size);
0647
0648 for(auto j=k;j--;){
0649 auto mask=results;
0650 if(!mask)break;
0651 do{
0652 auto i=unchecked_countr_zero(mask);
0653 auto& hash=hashes[i];
0654 auto& p=positions[i];
0655 auto b=get(p,hash);
0656 p=next_element(hash);
0657 results&=~(std::uint64_t(!b)<<i);
0658 mask&=mask-1;
0659 }while(mask);
0660 }
0661 for(std::size_t i=0;i<bulk_may_contain_size;++i){
0662 f(results&1);
0663 results>>=1;
0664 }
0665 n-=bulk_may_contain_size;
0666 }
0667
0668 while(n--)f(may_contain(h()));
0669 }
0670 }
0671
0672 friend bool operator==(const filter_core& x,const filter_core& y)
0673 {
0674 if(x.range()!=y.range())return false;
0675 else if(!x.ar.data)return true;
0676 else return std::memcmp(x.ar.array,y.ar.array,x.used_array_size())==0;
0677 }
0678
0679 private:
0680 using allocator_base=empty_value<Allocator,0>;
0681
0682 const Allocator& al()const{return allocator_base::get();}
0683 Allocator& al(){return allocator_base::get();}
0684
0685 static std::size_t requested_range(std::size_t m)
0686 {
0687 if(m>(used_value_size-stride)*CHAR_BIT){
0688
0689 m-=(used_value_size-stride)*CHAR_BIT;
0690 }
0691 return
0692 (std::numeric_limits<std::size_t>::max)()-m>=stride*CHAR_BIT-1?
0693 (m+stride*CHAR_BIT-1)/(stride*CHAR_BIT):
0694 m/(stride*CHAR_BIT);
0695 }
0696
0697 static filter_array new_array(allocator_type& al,std::size_t rng)
0698 {
0699 if(rng){
0700 auto p=allocator_allocate(al,space_for(rng));
0701 return {p,array_for(p)};
0702 }
0703 else{
0704
0705
0706
0707
0708
0709
0710
0711 static struct {unsigned char x=-1;}
0712 dummy[space_for(hash_strategy{0}.range())];
0713
0714 return {nullptr,array_for(reinterpret_cast<unsigned char*>(&dummy))};
0715 }
0716 }
0717
0718 void delete_array()noexcept
0719 {
0720 if(ar.data)allocator_deallocate(al(),ar.data,space_for(range()));
0721 }
0722
0723 void clear_bytes()noexcept
0724 {
0725 std::memset(ar.array,0,used_array_size());
0726 }
0727
0728 void copy_bytes(const filter_core& x)
0729 {
0730 BOOST_ASSERT(range()==x.range());
0731 std::memcpy(ar.array,x.ar.array,used_array_size());
0732 }
0733
0734 std::size_t range()const noexcept
0735 {
0736 return ar.data?hs.range():0;
0737 }
0738
0739 static constexpr std::size_t space_for(std::size_t rng)noexcept
0740 {
0741 return (initial_alignment-1)+rng*stride+tail_size;
0742 }
0743
0744 static unsigned char* array_for(unsigned char* p)noexcept
0745 {
0746 return p+
0747 (std::uintptr_t(initial_alignment)-
0748 std::uintptr_t(p))%initial_alignment;
0749 }
0750
0751 std::size_t used_array_size()const noexcept
0752 {
0753 return used_array_size(range());
0754 }
0755
0756 static std::size_t used_array_size(std::size_t rng)noexcept
0757 {
0758 return rng?rng*stride+(used_value_size-stride):0;
0759 }
0760
0761 static std::size_t unadjusted_capacity_for(std::size_t n,double fpr)
0762 {
0763 using size_t_limits=std::numeric_limits<std::size_t>;
0764 using double_limits=std::numeric_limits<double>;
0765
0766 BOOST_ASSERT(fpr>=0.0&&fpr<=1.0);
0767 if(n==0)return fpr==1.0?0:1;
0768
0769 constexpr double eps=1.0/(double)(size_t_limits::max)();
0770 constexpr double max_size_t_as_double=
0771 size_t_limits::digits<=double_limits::digits?
0772 (double)(size_t_limits::max)():
0773 (double)(size_t_limits::max)()
0774
0775 -constexpr_ldexp_1_positive(
0776 size_t_limits::digits-double_limits::digits);
0777
0778 const double c_max=max_size_t_as_double/n;
0779
0780
0781
0782
0783
0784 double d=1.0-std::pow(fpr,1.0/k_total);
0785 if(std::fpclassify(d)==FP_ZERO)return 0;
0786 double l=std::log(d);
0787 if(std::fpclassify(l)==FP_ZERO)return (std::size_t)(c_max*n);
0788 double c0=(std::min)(k_total/-l,c_max);
0789
0790
0791
0792 double c1=c0;
0793 if(fpr_for_c(c1)>fpr){
0794 do{
0795 double cn=c1*1.5;
0796 if(cn>c_max)return (std::size_t)(c_max*n);
0797 c0=c1;
0798 c1=cn;
0799 }while(fpr_for_c(c1)>fpr);
0800 }
0801 else{
0802 do{
0803 double cn=c0/1.5;
0804 c1=c0;
0805 c0=cn;
0806 }while(fpr_for_c(c0)<fpr);
0807 }
0808
0809
0810
0811 double cm;
0812 while((cm=c0+(c1-c0)/2)>c0 && cm<c1 && c1-c0>=eps){
0813 if(fpr_for_c(cm)>fpr)c0=cm;
0814 else c1=cm;
0815 }
0816 return (std::size_t)(cm*n);
0817 }
0818
0819 static double fpr_for_c(double c)
0820 {
0821 constexpr std::size_t w=(2*used_value_size-stride)*CHAR_BIT;
0822 const double lambda=w*k/c;
0823 const double loglambda=std::log(lambda);
0824 double res=0.0;
0825 double deltap=0.0;
0826 for(int i=0;i<1000;++i){
0827 double poisson=std::exp(i*loglambda-lambda-std::lgamma(i+1));
0828 double delta=poisson*subfilter::fpr(i,w);
0829 double resn=res+delta;
0830
0831
0832
0833
0834
0835 if(delta<deltap&&resn==res)break;
0836 deltap=delta;
0837 res=resn;
0838 }
0839
0840
0841
0842
0843
0844
0845
0846 return (std::max)(
0847 std::pow((double)res,(double)k),
0848 std::pow(1.0-std::exp(-(double)k_total/c),(double)k_total));
0849 }
0850
0851 BOOST_FORCEINLINE bool get(const unsigned char* p,std::uint64_t hash)const
0852 {
0853 return get(p,hash,std::integral_constant<bool,are_blocks_aligned>{});
0854 }
0855
0856 BOOST_FORCEINLINE bool get(
0857 const unsigned char* p,std::uint64_t hash,
0858 std::true_type )const
0859 {
0860 return subfilter::check(*reinterpret_cast<const block_type*>(p),hash);
0861 }
0862
0863 BOOST_FORCEINLINE bool get(
0864 const unsigned char* p,std::uint64_t hash,
0865 std::false_type )const
0866 {
0867 block_type x;
0868 std::memcpy(&x,p,block_size);
0869 return subfilter::check(x,hash);
0870 }
0871
0872 BOOST_FORCEINLINE void set(unsigned char* p,std::uint64_t hash)
0873 {
0874 return set(p,hash,std::integral_constant<bool,are_blocks_aligned>{});
0875 }
0876
0877 BOOST_FORCEINLINE void set(
0878 unsigned char* p,std::uint64_t hash,
0879 std::true_type )
0880 {
0881 subfilter::mark(*reinterpret_cast<block_type*>(p),hash);
0882 }
0883
0884 BOOST_FORCEINLINE void set(
0885 unsigned char* p,std::uint64_t hash,
0886 std::false_type )
0887 {
0888 block_type x;
0889 std::memcpy(&x,p,block_size);
0890 subfilter::mark(x,hash);
0891 std::memcpy(p,&x,block_size);
0892 }
0893
0894 BOOST_FORCEINLINE
0895 unsigned char* next_element(std::uint64_t& h)noexcept
0896 {
0897 auto p=ar.array+hs.next_position(h)*stride;
0898 for(std::size_t i=0;i<prefetched_cachelines;++i){
0899 BOOST_BLOOM_PREFETCH_WRITE((unsigned char*)p+i*cacheline);
0900 }
0901 return p;
0902 }
0903
0904 BOOST_FORCEINLINE
0905 const unsigned char* next_element(std::uint64_t& h)const noexcept
0906 {
0907 auto p=ar.array+hs.next_position(h)*stride;
0908 for(std::size_t i=0;i<prefetched_cachelines;++i){
0909 BOOST_BLOOM_PREFETCH((unsigned char*)p+i*cacheline);
0910 }
0911 return p;
0912 }
0913
0914 template<typename F>
0915 void combine(const filter_core& x,F f)
0916 {
0917 if(range()!=x.range()){
0918 BOOST_THROW_EXCEPTION(std::invalid_argument("incompatible filters"));
0919 }
0920 auto first0=ar.array,
0921 last0=first0+used_array_size(),
0922 first1=x.ar.array;
0923 while(first0!=last0)f(*first0++,*first1++);
0924 }
0925
0926 hash_strategy hs;
0927 filter_array ar;
0928 };
0929
0930 #if defined(BOOST_MSVC)
0931 #pragma warning(pop)
0932 #endif
0933
0934 }
0935 }
0936 }
0937 #endif