31 #include "../support/SpookyV2.hpp"
32 #include "../util/Vector.hpp"
45 using namespace std::chrono;
48 static const int MAX_BUCKET_SIZE = 3000;
50 static const int MAX_LEAF_SIZE = 24;
51 static const int MAX_FANOUT = 32;
53 #if defined(MORESTATS) && !defined(STATS)
59 #define MAX_LEVEL_TIME (20)
61 static constexpr
double log2e = 1.44269504089;
62 static uint64_t num_bij_trials[MAX_LEAF_SIZE], num_split_trials;
63 static uint64_t num_bij_evals[MAX_LEAF_SIZE], num_split_evals;
64 static uint64_t bij_count[MAX_LEAF_SIZE], split_count;
65 static uint64_t expected_split_trials, expected_split_evals;
66 static uint64_t bij_unary, bij_fixed, bij_unary_golomb, bij_fixed_golomb;
67 static uint64_t split_unary, split_fixed, split_unary_golomb, split_fixed_golomb;
68 static uint64_t max_split_code, min_split_code, sum_split_codes;
69 static uint64_t max_bij_code, min_bij_code, sum_bij_codes;
70 static uint64_t sum_depths;
71 static uint64_t time_bij;
72 static uint64_t time_split[MAX_LEVEL_TIME];
76 static const uint64_t start_seed[] = {0x106393c187cae21a, 0x6453cec3f7376937, 0x643e521ddbd2be98, 0x3740c6412f6572cb, 0x717d47562f1ce470, 0x4cd6eb4c63befb7c, 0x9bfd8c5e18c8da73,
77 0x082f20e10092a9a3, 0x2ada2ce68d21defc, 0xe33cb4f3e7c6466b, 0x3980be458c509c59, 0xc466fd9584828e8c, 0x45f0aabe1a61ede6, 0xf6e7b8b33ad9b98d,
78 0x4ef95e25f4b4983d, 0x81175195173b92d3, 0x4e50927d8dd15978, 0x1ea2099d1fafae7f, 0x425c8a06fbaaa815, 0xcd4216006c74052a};
88 uint64_t
inline remix(uint64_t z) {
89 z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
90 z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
107 this->second = second;
119 uint64_t h0 = seed, h1 = seed;
126 static constexpr
inline uint64_t min(int64_t x, int64_t y) {
return y + ((x - y) & ((x - y) >> 63)); }
127 static constexpr
inline uint64_t max(int64_t x, int64_t y) {
return x - ((x - y) & ((x - y) >> 63)); }
130 static constexpr uint8_t bij_memo[] = {0, 0, 0, 1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, 19, 21, 22, 23, 25, 26, 28, 29, 30};
134 static constexpr uint64_t bij_memo_golomb[] = {0, 0, 1, 3, 7, 18, 45, 113, 288, 740,
135 1910, 4954, 12902, 33714, 88350, 232110, 611118, 1612087, 4259803, 11273253,
136 29874507, 79265963, 210551258, 559849470, 1490011429, 3968988882, 10580669970, 28226919646, 75354118356};
146 static constexpr
size_t _leaf = LEAF_SIZE;
147 static_assert(_leaf >= 1);
148 static_assert(_leaf <= MAX_LEAF_SIZE);
149 size_t m, curr_unit, curr_index, last_unit;
153 inline size_t part_size()
const {
return (curr_index < _fanout - 1) ? unit : last_unit; }
157 static constexpr
size_t lower_aggr = _leaf * max(2, ceil(0.35 * _leaf + 1. / 2));
159 static constexpr
size_t upper_aggr = lower_aggr * (_leaf < 7 ? 2 : ceil(0.21 * _leaf + 9. / 10));
161 static inline constexpr
void split_params(
const size_t m,
size_t &fanout,
size_t &unit) {
162 if (m > upper_aggr) {
163 unit = upper_aggr * (uint16_t(m / 2 + upper_aggr - 1) / upper_aggr);
165 }
else if (m > lower_aggr) {
167 fanout = uint16_t(m + lower_aggr - 1) / lower_aggr;
170 fanout = uint16_t(m + _leaf - 1) / _leaf;
190 strat->curr_unit = strat->part_size();
191 strat->last_unit -= strat->curr_unit;
199 split_params(m, _fanout, unit);
200 this->curr_unit = part_size();
201 this->last_unit -= this->curr_unit;
207 inline size_t fanout() {
return this->_fanout; }
215 template <
size_t LEAF_SIZE>
static constexpr
void _fill_golomb_rice(
const int m, array<uint32_t, MAX_BUCKET_SIZE> *memo) {
216 array<int, MAX_FANOUT> k{0};
218 size_t fanout = 0, unit = 0;
222 for (
size_t i = 0; i < fanout - 1; ++i) {
224 k[fanout - 1] -= k[i];
227 double sqrt_prod = 1;
228 for (
size_t i = 0; i < fanout; ++i) sqrt_prod *= sqrt(k[i]);
230 const double p = sqrt(m) / (pow(2 * M_PI, (fanout - 1.) / 2) * sqrt_prod);
231 auto golomb_rice_length = (uint32_t)ceil(log2(-log((sqrt(5) + 1) / 2) / log1p(-p)));
233 assert(golomb_rice_length <= 0x1F);
234 (*memo)[m] = golomb_rice_length << 27;
235 for (
size_t i = 0; i < fanout; ++i) golomb_rice_length += (*memo)[k[i]] & 0xFFFF;
236 assert(golomb_rice_length <= 0xFFFF);
237 (*memo)[m] |= golomb_rice_length;
240 for (
size_t i = 0; i < fanout; ++i) nodes += ((*memo)[k[i]] >> 16) & 0x7FF;
241 assert(LEAF_SIZE < 3 || nodes <= 0x7FF);
242 (*memo)[m] |= nodes << 16;
245 template <
size_t LEAF_SIZE>
static constexpr array<uint32_t, MAX_BUCKET_SIZE> fill_golomb_rice() {
246 array<uint32_t, MAX_BUCKET_SIZE> memo{0};
248 for (; s <= LEAF_SIZE; ++s) memo[s] = bij_memo[s] << 27 | (s > 1) << 16 | bij_memo[s];
249 for (; s < MAX_BUCKET_SIZE; ++s) _fill_golomb_rice<LEAF_SIZE>(s, &memo);
255 template <
size_t LEAF_SIZE>
static constexpr uint64_t split_golomb_b(
const int m) {
256 array<int, MAX_FANOUT> k{0};
258 size_t fanout = 0, part = 0, unit = 0;
262 for (
size_t i = 0; i < fanout - 1; ++i) {
264 k[fanout - 1] -= k[i];
267 double sqrt_prod = 1;
268 for (
size_t i = 0; i < fanout; ++i) sqrt_prod *= sqrt(k[i]);
270 const double p = sqrt(m) / (pow(2 * M_PI, (fanout - 1.) / 2) * sqrt_prod);
271 return ceil(-log(2 - p) / log1p(-p));
277 static constexpr array<uint8_t, MAX_LEAF_SIZE> fill_bij_midstop() {
278 array<uint8_t, MAX_LEAF_SIZE> memo{0};
279 for (
int s = 0; s < MAX_LEAF_SIZE; ++s) memo[s] = s < (
int)ceil(2 * sqrt(s)) ? s : (int)ceil(2 * sqrt(s));
283 #define first_hash(k, len) spooky(k, len, 0)
284 #define golomb_param(m) (memo[m] >> 27)
285 #define skip_bits(m) (memo[m] & 0xFFFF)
286 #define skip_nodes(m) ((memo[m] >> 16) & 0x7FF)
299 template <
size_t LEAF_SIZE, util::AllocType AT = util::AllocType::MALLOC>
class RecSplit {
302 static constexpr
size_t _leaf = LEAF_SIZE;
303 static constexpr
size_t lower_aggr = SplitStrat::lower_aggr;
304 static constexpr
size_t upper_aggr = SplitStrat::upper_aggr;
308 static constexpr array<uint32_t, MAX_BUCKET_SIZE> memo = fill_golomb_rice<LEAF_SIZE>();
309 static constexpr array<uint8_t, MAX_LEAF_SIZE> bij_midstop = fill_bij_midstop();
329 RecSplit(
const vector<string> &keys,
const size_t bucket_size) {
330 this->bucket_size = bucket_size;
331 this->keys_count = keys.size();
333 for (
size_t i = 0; i < this->keys_count; ++i) {
334 h[i] =
first_hash(keys[i].c_str(), keys[i].size());
350 RecSplit(vector<hash128_t> &keys,
const size_t bucket_size) {
351 this->bucket_size = bucket_size;
352 this->keys_count = keys.size();
363 RecSplit(ifstream& input,
const size_t bucket_size) {
364 this->bucket_size = bucket_size;
366 for(
string key; getline(input, key);) h.push_back(
first_hash(key.c_str(), key.size()));
367 this->keys_count = h.size();
378 const size_t bucket = hash128_to_bucket(hash);
379 uint64_t cum_keys, cum_keys_next, bit_pos;
380 ef.
get(bucket, cum_keys, cum_keys_next, bit_pos);
383 size_t m = cum_keys_next - cum_keys;
388 while (m > upper_aggr) {
390 const size_t hmod = remap16(
remix(hash.
second + d + start_seed[level]), m);
392 const uint32_t split = ((uint16_t(m / 2 + upper_aggr - 1) / upper_aggr)) * upper_aggr;
402 if (m > lower_aggr) {
404 const size_t hmod = remap16(
remix(hash.
second + d + start_seed[level]), m);
406 const int part = uint16_t(hmod) / lower_aggr;
407 m = min(lower_aggr, m - part * lower_aggr);
408 cum_keys += lower_aggr * part;
415 const size_t hmod = remap16(
remix(hash.
second + d + start_seed[level]), m);
417 const int part = uint16_t(hmod) / _leaf;
418 m = min(_leaf, m - part * _leaf);
419 cum_keys += _leaf * part;
425 return cum_keys + remap16(
remix(hash.
second + b + start_seed[level]), m);
436 inline size_t size() {
return this->keys_count; }
440 inline uint64_t hash128_to_bucket(
const hash128_t &hash)
const {
return remap128(hash.
first, nbuckets); }
443 void recSplit(vector<uint64_t> &bucket,
typename RiceBitVector<AT>::Builder &builder, vector<uint32_t> &unary) {
444 const auto m = bucket.size();
445 vector<uint64_t> temp(m);
446 recSplit(bucket, temp, 0, bucket.size(), builder, unary, 0);
449 void recSplit(vector<uint64_t> &bucket, vector<uint64_t> &temp,
size_t start,
size_t end,
typename RiceBitVector<AT>::Builder &builder, vector<uint32_t> &unary,
const int level) {
450 const auto m = end - start;
452 uint64_t x = start_seed[level];
456 sum_depths += m * level;
457 auto start_time = high_resolution_clock::now();
460 const uint32_t found = (1 << m) - 1;
461 if constexpr (_leaf <= 8) {
464 for (
size_t i = start; i < end; i++) mask |= uint32_t(1) << remap16(
remix(bucket[i] + x), m);
466 num_bij_evals[m] += m;
468 if (mask == found)
break;
472 const size_t midstop = bij_midstop[m];
476 for (i = start; i < start + midstop; i++) mask |= uint32_t(1) << remap16(
remix(bucket[i] + x), m);
478 num_bij_evals[m] += midstop;
480 if (
nu(mask) == midstop) {
481 for (; i < end; i++) mask |= uint32_t(1) << remap16(
remix(bucket[i] + x), m);
483 num_bij_evals[m] += m - midstop;
485 if (mask == found)
break;
491 time_bij += duration_cast<nanoseconds>(high_resolution_clock::now() - start_time).count();
493 x -= start_seed[level];
495 builder.appendFixed(x, log2golomb);
496 unary.push_back(x >> log2golomb);
499 num_bij_trials[m] += x + 1;
500 bij_unary += 1 + (x >> log2golomb);
501 bij_fixed += log2golomb;
503 min_bij_code = min(min_bij_code, x);
504 max_bij_code = max(max_bij_code, x);
507 auto b = bij_memo_golomb[m];
509 bij_unary_golomb += x / b + 1;
510 bij_fixed_golomb += x % b < ((1 << log2b + 1) - b) ? log2b : log2b + 1;
514 auto start_time = high_resolution_clock::now();
516 if (m > upper_aggr) {
517 const size_t split = ((uint16_t(m / 2 + upper_aggr - 1) / upper_aggr)) * upper_aggr;
522 for (
size_t i = start; i < end; i++) {
523 count[remap16(
remix(bucket[i] + x), m) >= split]++;
528 if (count[0] == split)
break;
534 for (
size_t i = start; i < end; i++) {
535 temp[count[remap16(
remix(bucket[i] + x), m) >= split]++] = bucket[i];
537 copy(&temp[0], &temp[m], &bucket[start]);
538 x -= start_seed[level];
540 builder.appendFixed(x, log2golomb);
541 unary.push_back(x >> log2golomb);
544 time_split[min(MAX_LEVEL_TIME, level)] += duration_cast<nanoseconds>(high_resolution_clock::now() - start_time).count();
546 recSplit(bucket, temp, start, start + split, builder, unary, level + 1);
547 if (m - split > 1) recSplit(bucket, temp, start + split, end, builder, unary, level + 1);
552 }
else if (m > lower_aggr) {
553 const size_t fanout = uint16_t(m + lower_aggr - 1) / lower_aggr;
554 size_t count[fanout];
556 memset(count, 0,
sizeof count -
sizeof *count);
557 for (
size_t i = start; i < end; i++) {
558 count[uint16_t(remap16(
remix(bucket[i] + x), m)) / lower_aggr]++;
564 for (
size_t i = 0; i < fanout - 1; i++) broken |= count[i] - lower_aggr;
569 for (
size_t i = 0, c = 0; i < fanout; i++, c += lower_aggr) count[i] = c;
570 for (
size_t i = start; i < end; i++) {
571 temp[count[uint16_t(remap16(
remix(bucket[i] + x), m)) / lower_aggr]++] = bucket[i];
573 copy(&temp[0], &temp[m], &bucket[start]);
575 x -= start_seed[level];
577 builder.appendFixed(x, log2golomb);
578 unary.push_back(x >> log2golomb);
581 time_split[min(MAX_LEVEL_TIME, level)] += duration_cast<nanoseconds>(high_resolution_clock::now() - start_time).count();
584 for (i = 0; i < m - lower_aggr; i += lower_aggr) {
585 recSplit(bucket, temp, start + i, start + i + lower_aggr, builder, unary, level + 1);
587 if (m - i > 1) recSplit(bucket, temp, start + i, end, builder, unary, level + 1);
593 const size_t fanout = uint16_t(m + _leaf - 1) / _leaf;
594 size_t count[fanout];
596 memset(count, 0,
sizeof count -
sizeof *count);
597 for (
size_t i = start; i < end; i++) {
598 count[uint16_t(remap16(
remix(bucket[i] + x), m)) / _leaf]++;
604 for (
size_t i = 0; i < fanout - 1; i++) broken |= count[i] - _leaf;
608 for (
size_t i = 0, c = 0; i < fanout; i++, c += _leaf) count[i] = c;
609 for (
size_t i = start; i < end; i++) {
610 temp[count[uint16_t(remap16(
remix(bucket[i] + x), m)) / _leaf]++] = bucket[i];
612 copy(&temp[0], &temp[m], &bucket[start]);
614 x -= start_seed[level];
616 builder.appendFixed(x, log2golomb);
617 unary.push_back(x >> log2golomb);
620 time_split[min(MAX_LEVEL_TIME, level)] += duration_cast<nanoseconds>(high_resolution_clock::now() - start_time).count();
623 for (i = 0; i < m - _leaf; i += _leaf) {
624 recSplit(bucket, temp, start + i, start + i + _leaf, builder, unary, level + 1);
626 if (m - i > 1) recSplit(bucket, temp, start + i, end, builder, unary, level + 1);
635 num_split_trials += x + 1;
639 auto v = strat.begin();
640 for (
int i = 0; i < strat.fanout(); ++i, ++v) {
641 e_trials *= pow((
double)m / *v, *v);
642 for (
size_t j = *v; j > 0; --j, --aux) {
643 e_trials *= (double)j / aux;
646 expected_split_trials += (size_t)e_trials;
647 expected_split_evals += (size_t)e_trials * m;
649 split_unary += 1 + (x >> log2golomb);
650 split_fixed += log2golomb;
652 min_split_code = min(min_split_code, x);
653 max_split_code = max(max_split_code, x);
654 sum_split_codes += x;
656 auto b = split_golomb_b<LEAF_SIZE>(m);
658 split_unary_golomb += x / b + 1;
659 split_fixed_golomb += x % b < ((1ULL << log2b + 1) - b) ? log2b : log2b + 1;
667 memset(time_split, 0,
sizeof time_split);
668 split_unary = split_fixed = 0;
669 bij_unary = bij_fixed = 0;
670 min_split_code = 1UL << 63;
671 max_split_code = sum_split_codes = 0;
672 min_bij_code = 1UL << 63;
673 max_bij_code = sum_bij_codes = 0;
675 size_t minsize = keys_count, maxsize = 0;
676 double ub_split_bits = 0, ub_bij_bits = 0;
677 double ub_split_evals = 0, ub_bij_evals = 0;
680 #ifndef __SIZEOF_INT128__
681 if (keys_count > (1ULL << 32)) {
682 fprintf(stderr,
"For more than 2^32 keys, you need 128-bit integer support.\n");
686 nbuckets = max(1, (keys_count + bucket_size - 1) / bucket_size);
687 auto bucket_size_acc = vector<int64_t>(nbuckets + 1);
688 auto bucket_pos_acc = vector<int64_t>(nbuckets + 1);
690 sort(hashes, hashes + keys_count, [
this](
const hash128_t &a,
const hash128_t &b) {
return hash128_to_bucket(a) < hash128_to_bucket(b); });
691 typename RiceBitVector<AT>::Builder builder;
693 bucket_size_acc[0] = bucket_pos_acc[0] = 0;
694 for (
size_t i = 0, last = 0; i < nbuckets; i++) {
695 vector<uint64_t> bucket;
696 for (; last < keys_count && hash128_to_bucket(hashes[last]) == i; last++) bucket.push_back(hashes[last].second);
698 const size_t s = bucket.size();
699 bucket_size_acc[i + 1] = bucket_size_acc[i] + s;
700 if (bucket.size() > 1) {
701 vector<uint32_t> unary;
702 recSplit(bucket, builder, unary);
703 builder.appendUnaryAll(unary);
705 bucket_pos_acc[i + 1] = builder.getBits();
707 auto upper_leaves = (s + _leaf - 1) / _leaf;
708 auto upper_height = ceil(log(upper_leaves) / log(2));
709 auto upper_s = _leaf * pow(2, upper_height);
710 ub_split_bits += (double)upper_s / (_leaf * 2) * log2(2 * M_PI * _leaf) - .5 * log2(2 * M_PI * upper_s);
711 ub_bij_bits += upper_leaves * _leaf * (log2e - .5 / _leaf * log2(2 * M_PI * _leaf));
712 ub_split_evals += 4 * upper_s * sqrt(pow(2 * M_PI * upper_s, 2 - 1) / pow(2, 2));
713 minsize = min(minsize, s);
714 maxsize = max(maxsize, s);
717 builder.appendFixed(1, 1);
718 descriptors = builder.build();
719 ef = DoubleEF<AT>(vector<uint64_t>(bucket_size_acc.begin(), bucket_size_acc.end()), vector<uint64_t>(bucket_pos_acc.begin(), bucket_pos_acc.end()));
725 double rice_desc = (double)builder.getBits() / keys_count;
726 printf(
"Elias-Fano cumul sizes: %f bits/bucket\n", (
double)ef.
bitCountCumKeys() / nbuckets);
727 printf(
"Elias-Fano cumul bits: %f bits/bucket\n", (
double)ef.
bitCountPosition() / nbuckets);
728 printf(
"Elias-Fano cumul sizes: %f bits/key\n", ef_sizes);
729 printf(
"Elias-Fano cumul bits: %f bits/key\n", ef_bits);
730 printf(
"Rice-Golomb descriptors: %f bits/key\n", rice_desc);
731 printf(
"Total bits: %f bits/key\n", ef_sizes + ef_bits + rice_desc);
736 printf(
"Min bucket size: %lu\n", minsize);
737 printf(
"Max bucket size: %lu\n", maxsize);
740 printf(
"Bijections: %13.3f ms\n", time_bij * 1E-6);
741 for (
int i = 0; i < MAX_LEVEL_TIME; i++) {
742 if (time_split[i] > 0) {
743 printf(
"Split level %d: %10.3f ms\n", i, time_split[i] * 1E-6);
747 uint64_t fact = 1, tot_bij_count = 0, tot_bij_evals;
749 printf(
"Bij count trials exp evals exp tot evals\n");
750 for (
int i = 0; i < MAX_LEAF_SIZE; i++) {
751 if (num_bij_trials[i] != 0) {
752 tot_bij_count += bij_count[i];
753 tot_bij_evals += num_bij_evals[i];
754 printf(
"%-3d%20d%20.2f%20.2f%20.2f%20.2f%20lld\n", i, bij_count[i], (
double)num_bij_trials[i] / bij_count[i], pow(i, i) / fact, (
double)num_bij_evals[i] / bij_count[i],
755 (_leaf <= 8 ? i : bij_midstop[i]) * pow(i, i) / fact, num_bij_evals[i]);
761 printf(
"Split count: %16zu\n", split_count);
763 printf(
"Total split evals: %16lld\n", num_split_evals);
764 printf(
"Total bij evals: %16lld\n", tot_bij_evals);
765 printf(
"Total evals: %16lld\n", num_split_evals + tot_bij_evals);
768 printf(
"Average depth: %f\n", (
double)sum_depths / keys_count);
770 printf(
"Trials per split: %16.3f\n", (
double)num_split_trials / split_count);
771 printf(
"Exp trials per split: %16.3f\n", (
double)expected_split_trials / split_count);
772 printf(
"Evals per split: %16.3f\n", (
double)num_split_evals / split_count);
773 printf(
"Exp evals per split: %16.3f\n", (
double)expected_split_evals / split_count);
776 printf(
"Unary bits per bij: %10.5f\n", (
double)bij_unary / tot_bij_count);
777 printf(
"Fixed bits per bij: %10.5f\n", (
double)bij_fixed / tot_bij_count);
778 printf(
"Total bits per bij: %10.5f\n", (
double)(bij_unary + bij_fixed) / tot_bij_count);
781 printf(
"Unary bits per split: %10.5f\n", (
double)split_unary / split_count);
782 printf(
"Fixed bits per split: %10.5f\n", (
double)split_fixed / split_count);
783 printf(
"Total bits per split: %10.5f\n", (
double)(split_unary + split_fixed) / split_count);
784 printf(
"Total bits per key: %10.5f\n", (
double)(bij_unary + bij_fixed + split_unary + split_fixed) / keys_count);
787 printf(
"Unary bits per bij (Golomb): %10.5f\n", (
double)bij_unary_golomb / tot_bij_count);
788 printf(
"Fixed bits per bij (Golomb): %10.5f\n", (
double)bij_fixed_golomb / tot_bij_count);
789 printf(
"Total bits per bij (Golomb): %10.5f\n", (
double)(bij_unary_golomb + bij_fixed_golomb) / tot_bij_count);
792 printf(
"Unary bits per split (Golomb): %10.5f\n", (
double)split_unary_golomb / split_count);
793 printf(
"Fixed bits per split (Golomb): %10.5f\n", (
double)split_fixed_golomb / split_count);
794 printf(
"Total bits per split (Golomb): %10.5f\n", (
double)(split_unary_golomb + split_fixed_golomb) / split_count);
795 printf(
"Total bits per key (Golomb): %10.5f\n", (
double)(bij_unary_golomb + bij_fixed_golomb + split_unary_golomb + split_fixed_golomb) / keys_count);
799 printf(
"Total split bits %16.3f\n", (
double)split_fixed + split_unary);
800 printf(
"Upper bound split bits: %16.3f\n", ub_split_bits);
801 printf(
"Total bij bits: %16.3f\n", (
double)bij_fixed + bij_unary);
802 printf(
"Upper bound bij bits: %16.3f\n\n", ub_bij_bits);
807 const size_t leaf_size = LEAF_SIZE;
808 os.write((
char *)&leaf_size,
sizeof(leaf_size));
809 os.write((
char *)&rs.bucket_size,
sizeof(rs.bucket_size));
810 os.write((
char *)&rs.keys_count,
sizeof(rs.keys_count));
811 os << rs.descriptors;
818 is.read((
char *)&leaf_size,
sizeof(leaf_size));
819 if (leaf_size != LEAF_SIZE) {
820 fprintf(stderr,
"Serialized leaf size %d, code leaf size %d\n",
int(leaf_size),
int(LEAF_SIZE));
823 is.read((
char *)&rs.bucket_size,
sizeof(bucket_size));
824 is.read((
char *)&rs.keys_count,
sizeof(keys_count));
825 rs.nbuckets = max(1, (rs.keys_count + rs.bucket_size - 1) / rs.bucket_size);
827 is >> rs.descriptors;