Move folly/Bits.h to folly/lang/
[folly.git] / folly / experimental / EliasFanoCoding.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * @author Philip Pronin (philipp@fb.com)
19  *
20  * Based on the paper by Sebastiano Vigna,
21  * "Quasi-succinct indices" (arxiv:1206.4300).
22  */
23
24 #pragma once
25
26 #include <algorithm>
27 #include <cstdlib>
28 #include <limits>
29 #include <type_traits>
30
31 #include <folly/Likely.h>
32 #include <folly/Portability.h>
33 #include <folly/Range.h>
34 #include <folly/experimental/CodingDetail.h>
35 #include <folly/experimental/Instructions.h>
36 #include <folly/experimental/Select64.h>
37 #include <folly/lang/Assume.h>
38 #include <folly/lang/Bits.h>
39 #include <glog/logging.h>
40
41 #if !FOLLY_X64
42 #error EliasFanoCoding.h requires x86_64
43 #endif
44
45 namespace folly { namespace compression {
46
47 static_assert(kIsLittleEndian, "EliasFanoCoding.h requires little endianness");
48
49 constexpr size_t kCacheLineSize = 64;
50
51 template <class Pointer>
52 struct EliasFanoCompressedListBase {
53   EliasFanoCompressedListBase() = default;
54
55   template <class OtherPointer>
56   EliasFanoCompressedListBase(
57       const EliasFanoCompressedListBase<OtherPointer>& other)
58       : size(other.size),
59         numLowerBits(other.numLowerBits),
60         data(other.data),
61         skipPointers(reinterpret_cast<Pointer>(other.skipPointers)),
62         forwardPointers(reinterpret_cast<Pointer>(other.forwardPointers)),
63         lower(reinterpret_cast<Pointer>(other.lower)),
64         upper(reinterpret_cast<Pointer>(other.upper)) { }
65
66   template <class T = Pointer>
67   auto free() -> decltype(::free(T(nullptr))) {
68     return ::free(data.data());
69   }
70
71   size_t upperSize() const {
72     return size_t(data.end() - upper);
73   }
74
75   size_t size = 0;
76   uint8_t numLowerBits = 0;
77
78   // WARNING: EliasFanoCompressedList has no ownership of data. The 7
79   // bytes following the last byte should be readable.
80   folly::Range<Pointer> data;
81
82   Pointer skipPointers = nullptr;
83   Pointer forwardPointers = nullptr;
84   Pointer lower = nullptr;
85   Pointer upper = nullptr;
86 };
87
88 typedef EliasFanoCompressedListBase<const uint8_t*> EliasFanoCompressedList;
89 typedef EliasFanoCompressedListBase<uint8_t*> MutableEliasFanoCompressedList;
90
91 template <
92     class Value,
93     class SkipValue = size_t,
94     size_t kSkipQuantum = 0, // 0 = disabled
95     size_t kForwardQuantum = 0> // 0 = disabled
96 struct EliasFanoEncoderV2 {
97   static_assert(std::is_integral<Value>::value &&
98                     std::is_unsigned<Value>::value,
99                 "Value should be unsigned integral");
100
101   typedef EliasFanoCompressedList CompressedList;
102   typedef MutableEliasFanoCompressedList MutableCompressedList;
103
104   typedef Value ValueType;
105   typedef SkipValue SkipValueType;
106   struct Layout;
107
108   static constexpr size_t skipQuantum = kSkipQuantum;
109   static constexpr size_t forwardQuantum = kForwardQuantum;
110
111   static uint8_t defaultNumLowerBits(size_t upperBound, size_t size) {
112     if (UNLIKELY(size == 0 || upperBound < size)) {
113       return 0;
114     }
115     // Result that should be returned is "floor(log(upperBound / size))".
116     // In order to avoid expensive division, we rely on
117     // "floor(a) - floor(b) - 1 <= floor(a - b) <= floor(a) - floor(b)".
118     // Assuming "candidate = floor(log(upperBound)) - floor(log(upperBound))",
119     // then result is either "candidate - 1" or "candidate".
120     auto candidate = folly::findLastSet(upperBound) - folly::findLastSet(size);
121     // NOTE: As size != 0, "candidate" is always < 64.
122     return (size > (upperBound >> candidate)) ? candidate - 1 : candidate;
123   }
124
125   // Requires: input range (begin, end) is sorted (encoding
126   // crashes if it's not).
127   // WARNING: encode() mallocates EliasFanoCompressedList::data. As
128   // EliasFanoCompressedList has no ownership of it, you need to call
129   // free() explicitly.
130   template <class RandomAccessIterator>
131   static MutableCompressedList encode(RandomAccessIterator begin,
132                                       RandomAccessIterator end) {
133     if (begin == end) {
134       return MutableCompressedList();
135     }
136     EliasFanoEncoderV2 encoder(size_t(end - begin), *(end - 1));
137     for (; begin != end; ++begin) {
138       encoder.add(*begin);
139     }
140     return encoder.finish();
141   }
142
143   explicit EliasFanoEncoderV2(const MutableCompressedList& result)
144       : lower_(result.lower),
145         upper_(result.upper),
146         skipPointers_(reinterpret_cast<SkipValueType*>(
147               result.skipPointers)),
148         forwardPointers_(reinterpret_cast<SkipValueType*>(
149               result.forwardPointers)),
150         result_(result) {
151     std::fill(result.data.begin(), result.data.end(), '\0');
152   }
153
154   EliasFanoEncoderV2(size_t size, ValueType upperBound)
155       : EliasFanoEncoderV2(
156             Layout::fromUpperBoundAndSize(upperBound, size).allocList()) { }
157
158   void add(ValueType value) {
159     CHECK_LT(value, std::numeric_limits<ValueType>::max());
160     CHECK_GE(value, lastValue_);
161
162     const auto numLowerBits = result_.numLowerBits;
163     const ValueType upperBits = value >> numLowerBits;
164
165     // Upper sequence consists of upperBits 0-bits and (size_ + 1) 1-bits.
166     const size_t pos = upperBits + size_;
167     upper_[pos / 8] |= 1U << (pos % 8);
168     // Append numLowerBits bits to lower sequence.
169     if (numLowerBits != 0) {
170       const ValueType lowerBits = value & ((ValueType(1) << numLowerBits) - 1);
171       writeBits56(lower_, size_ * numLowerBits, numLowerBits, lowerBits);
172     }
173
174     /* static */ if (skipQuantum != 0) {
175       while ((skipPointersSize_ + 1) * skipQuantum <= upperBits) {
176         // Store the number of preceding 1-bits.
177         skipPointers_[skipPointersSize_++] = SkipValue(size_);
178       }
179     }
180
181     /* static */ if (forwardQuantum != 0) {
182       if ((size_ + 1) % forwardQuantum == 0) {
183         const auto k = size_ / forwardQuantum;
184         // Store the number of preceding 0-bits.
185         forwardPointers_[k] = upperBits;
186       }
187     }
188
189     lastValue_ = value;
190     ++size_;
191   }
192
193   const MutableCompressedList& finish() const {
194     CHECK_EQ(size_, result_.size);
195     return result_;
196   }
197
198  private:
199   // Writes value (with len up to 56 bits) to data starting at pos-th bit.
200   static void writeBits56(unsigned char* data, size_t pos,
201                           uint8_t len, uint64_t value) {
202     DCHECK_LE(uint32_t(len), 56);
203     DCHECK_EQ(0, value & ~((uint64_t(1) << len) - 1));
204     unsigned char* const ptr = data + (pos / 8);
205     uint64_t ptrv = folly::loadUnaligned<uint64_t>(ptr);
206     ptrv |= value << (pos % 8);
207     folly::storeUnaligned<uint64_t>(ptr, ptrv);
208   }
209
210   unsigned char* lower_ = nullptr;
211   unsigned char* upper_ = nullptr;
212   SkipValueType* skipPointers_ = nullptr;
213   SkipValueType* forwardPointers_ = nullptr;
214
215   ValueType lastValue_ = 0;
216   size_t size_ = 0;
217   size_t skipPointersSize_ = 0;
218
219   MutableCompressedList result_;
220 };
221
222 template <
223     class Value,
224     class SkipValue,
225     size_t kSkipQuantum,
226     size_t kForwardQuantum>
227 struct EliasFanoEncoderV2<Value,
228                           SkipValue,
229                           kSkipQuantum,
230                           kForwardQuantum>::Layout {
231   static Layout fromUpperBoundAndSize(size_t upperBound, size_t size) {
232     // numLowerBits can be at most 56 because of detail::writeBits56.
233     const uint8_t numLowerBits = std::min(defaultNumLowerBits(upperBound,
234                                                               size),
235                                           uint8_t(56));
236     // *** Upper bits.
237     // Upper bits are stored using unary delta encoding.
238     // For example, (3 5 5 9) will be encoded as 1000011001000_2.
239     const size_t upperSizeBits =
240       (upperBound >> numLowerBits) +  // Number of 0-bits to be stored.
241       size;                           // 1-bits.
242     const size_t upper = (upperSizeBits + 7) / 8;
243
244     // *** Validity checks.
245     // Shift by numLowerBits must be valid.
246     CHECK_LT(numLowerBits, 8 * sizeof(Value));
247     CHECK_LT(size, std::numeric_limits<SkipValueType>::max());
248     CHECK_LT(upperBound >> numLowerBits,
249              std::numeric_limits<SkipValueType>::max());
250
251     return fromInternalSizes(numLowerBits, upper, size);
252   }
253
254   static Layout fromInternalSizes(uint8_t numLowerBits,
255                                   size_t upper,
256                                   size_t size) {
257     Layout layout;
258     layout.size = size;
259     layout.numLowerBits = numLowerBits;
260
261     layout.lower = (numLowerBits * size + 7) / 8;
262     layout.upper = upper;
263
264     // *** Skip pointers.
265     // Store (1-indexed) position of every skipQuantum-th
266     // 0-bit in upper bits sequence.
267     /* static */ if (skipQuantum != 0) {
268       // 8 * upper is used here instead of upperSizeBits, as that is
269       // more serialization-friendly way (upperSizeBits doesn't need
270       // to be known by this function, unlike upper).
271
272       size_t numSkipPointers = (8 * upper - size) / skipQuantum;
273       layout.skipPointers = numSkipPointers * sizeof(SkipValueType);
274     }
275
276     // *** Forward pointers.
277     // Store (1-indexed) position of every forwardQuantum-th
278     // 1-bit in upper bits sequence.
279     /* static */ if (forwardQuantum != 0) {
280       size_t numForwardPointers = size / forwardQuantum;
281       layout.forwardPointers = numForwardPointers * sizeof(SkipValueType);
282     }
283
284     return layout;
285   }
286
287   size_t bytes() const {
288     return lower + upper + skipPointers + forwardPointers;
289   }
290
291   template <class Range>
292   EliasFanoCompressedListBase<typename Range::iterator>
293   openList(Range& buf) const {
294     EliasFanoCompressedListBase<typename Range::iterator> result;
295     result.size = size;
296     result.numLowerBits = numLowerBits;
297     result.data = buf.subpiece(0, bytes());
298
299     auto advance = [&] (size_t n) {
300       auto begin = buf.data();
301       buf.advance(n);
302       return begin;
303     };
304
305     result.skipPointers = advance(skipPointers);
306     result.forwardPointers = advance(forwardPointers);
307     result.lower = advance(lower);
308     result.upper = advance(upper);
309
310     return result;
311   }
312
313   MutableCompressedList allocList() const {
314     uint8_t* buf = nullptr;
315     // WARNING: Current read/write logic assumes that the 7 bytes
316     // following the last byte of lower and upper sequences are
317     // readable (stored value doesn't matter and won't be changed), so
318     // we allocate additional 7 bytes, but do not include them in size
319     // of returned value.
320     if (size > 0) {
321       buf = static_cast<uint8_t*>(malloc(bytes() + 7));
322     }
323     folly::MutableByteRange bufRange(buf, bytes());
324     return openList(bufRange);
325   }
326
327   size_t size = 0;
328   uint8_t numLowerBits = 0;
329
330   // Sizes in bytes.
331   size_t lower = 0;
332   size_t upper = 0;
333   size_t skipPointers = 0;
334   size_t forwardPointers = 0;
335 };
336
337 namespace detail {
338
339 template <class Encoder, class Instructions, class SizeType>
340 class UpperBitsReader : ForwardPointers<Encoder::forwardQuantum>,
341                         SkipPointers<Encoder::skipQuantum> {
342   typedef typename Encoder::SkipValueType SkipValueType;
343  public:
344   typedef typename Encoder::ValueType ValueType;
345
346   explicit UpperBitsReader(const typename Encoder::CompressedList& list)
347       : ForwardPointers<Encoder::forwardQuantum>(list.forwardPointers),
348         SkipPointers<Encoder::skipQuantum>(list.skipPointers),
349         start_(list.upper) {
350     reset();
351   }
352
353   void reset() {
354     block_ = start_ != nullptr ? folly::loadUnaligned<block_t>(start_) : 0;
355     position_ = std::numeric_limits<SizeType>::max();
356     outer_ = 0;
357     value_ = 0;
358   }
359
360   SizeType position() const {
361     return position_;
362   }
363   ValueType value() const {
364     return value_;
365   }
366
367   ValueType next() {
368     // Skip to the first non-zero block.
369     while (block_ == 0) {
370       outer_ += sizeof(block_t);
371       block_ = folly::loadUnaligned<block_t>(start_ + outer_);
372     }
373
374     ++position_;
375     size_t inner = Instructions::ctz(block_);
376     block_ = Instructions::blsr(block_);
377
378     return setValue(inner);
379   }
380
381   ValueType skip(SizeType n) {
382     DCHECK_GT(n, 0);
383
384     position_ += n;  // n 1-bits will be read.
385
386     // Use forward pointer.
387     if (Encoder::forwardQuantum > 0 && n > Encoder::forwardQuantum) {
388       const size_t steps = position_ / Encoder::forwardQuantum;
389       const size_t dest = folly::loadUnaligned<SkipValueType>(
390           this->forwardPointers_ + (steps - 1) * sizeof(SkipValueType));
391
392       reposition(dest + steps * Encoder::forwardQuantum);
393       n = position_ + 1 - steps * Encoder::forwardQuantum; // n is > 0.
394     }
395
396     size_t cnt;
397     // Find necessary block.
398     while ((cnt = Instructions::popcount(block_)) < n) {
399       n -= cnt;
400       outer_ += sizeof(block_t);
401       block_ = folly::loadUnaligned<block_t>(start_ + outer_);
402     }
403
404     // Skip to the n-th one in the block.
405     DCHECK_GT(n, 0);
406     size_t inner = select64<Instructions>(block_, n - 1);
407     block_ &= (block_t(-1) << inner) << 1;
408
409     return setValue(inner);
410   }
411
412   // Skip to the first element that is >= v and located *after* the current
413   // one (so even if current value equals v, position will be increased by 1).
414   ValueType skipToNext(ValueType v) {
415     DCHECK_GE(v, value_);
416
417     // Use skip pointer.
418     if (Encoder::skipQuantum > 0 && v >= value_ + Encoder::skipQuantum) {
419       const size_t steps = v / Encoder::skipQuantum;
420       const size_t dest = folly::loadUnaligned<SkipValueType>(
421           this->skipPointers_ + (steps - 1) * sizeof(SkipValueType));
422
423       reposition(dest + Encoder::skipQuantum * steps);
424       position_ = dest - 1;
425
426       // Correct value_ will be set during the next() call at the end.
427
428       // NOTE: Corresponding block of lower bits sequence may be
429       // prefetched here (via __builtin_prefetch), but experiments
430       // didn't show any significant improvements.
431     }
432
433     // Skip by blocks.
434     size_t cnt;
435     size_t skip = v - (8 * outer_ - position_ - 1);
436
437     constexpr size_t kBitsPerBlock = 8 * sizeof(block_t);
438     while ((cnt = Instructions::popcount(~block_)) < skip) {
439       skip -= cnt;
440       position_ += kBitsPerBlock - cnt;
441       outer_ += sizeof(block_t);
442       block_ = folly::loadUnaligned<block_t>(start_ + outer_);
443     }
444
445     if (LIKELY(skip)) {
446       auto inner = select64<Instructions>(~block_, skip - 1);
447       position_ += inner - skip + 1;
448       block_ &= block_t(-1) << inner;
449     }
450
451     next();
452     return value_;
453   }
454
455   /**
456    * Prepare to skip to `value`. This is a constant-time operation that will
457    * prefetch memory required for a `skipTo(value)` call.
458    *
459    * @return position of reader
460    */
461   SizeType prepareSkipTo(ValueType v) const {
462     auto position = position_;
463
464     if (Encoder::skipQuantum > 0 && v >= value_ + Encoder::skipQuantum) {
465       auto outer = outer_;
466       const size_t steps = v / Encoder::skipQuantum;
467       const size_t dest = folly::loadUnaligned<SkipValueType>(
468           this->skipPointers_ + (steps - 1) * sizeof(SkipValueType));
469
470       position = dest - 1;
471       outer = (dest + Encoder::skipQuantum * steps) / 8;
472
473       // Prefetch up to the beginning of where we linear search. After that,
474       // hardware prefetching will outperform our own. In addition, this
475       // simplifies calculating what to prefetch as we don't have to calculate
476       // the entire destination address. Two cache lines are prefetched because
477       // this results in fewer cycles used (based on practical results) than
478       // one. However, three cache lines does not have any additional effect.
479       const auto addr = start_ + outer;
480       __builtin_prefetch(addr);
481       __builtin_prefetch(addr + kCacheLineSize);
482     }
483
484     return position;
485   }
486
487   ValueType jump(size_t n) {
488     if (Encoder::forwardQuantum == 0 || n <= Encoder::forwardQuantum) {
489       reset();
490     } else {
491       // Avoid reading the head, skip() will reposition.
492       position_ = std::numeric_limits<SizeType>::max();
493     }
494     return skip(n);
495   }
496
497   ValueType jumpToNext(ValueType v) {
498     if (Encoder::skipQuantum == 0 || v < Encoder::skipQuantum) {
499       reset();
500     } else {
501       value_ = 0;  // Avoid reading the head, skipToNext() will reposition.
502     }
503     return skipToNext(v);
504   }
505
506   ValueType previousValue() const {
507     DCHECK_NE(position(), std::numeric_limits<SizeType>::max());
508     DCHECK_GT(position(), 0);
509
510     auto outer = outer_;
511     auto inner = size_t(value_) - 8 * outer_ + position_;
512     block_t block = folly::loadUnaligned<block_t>(start_ + outer);
513     block &= (block_t(1) << inner) - 1;
514
515     while (UNLIKELY(block == 0)) {
516       DCHECK_GT(outer, 0);
517       outer -= std::min<OuterType>(sizeof(block_t), outer);
518       block = folly::loadUnaligned<block_t>(start_ + outer);
519     }
520
521     inner = 8 * sizeof(block_t) - 1 - Instructions::clz(block);
522     return static_cast<ValueType>(8 * outer + inner - (position_ - 1));
523   }
524
525   void setDone(SizeType endPos) {
526     position_ = endPos;
527   }
528
529  private:
530   ValueType setValue(size_t inner) {
531     value_ = static_cast<ValueType>(8 * outer_ + inner - position_);
532     return value_;
533   }
534
535   void reposition(SizeType dest) {
536     outer_ = dest / 8;
537     block_ = folly::loadUnaligned<block_t>(start_ + outer_);
538     block_ &= ~((block_t(1) << (dest % 8)) - 1);
539   }
540
541   using block_t = uint64_t;
542   // The size in bytes of the upper bits is limited by n + universe / 8,
543   // so a type that can hold either sizes or values is sufficient.
544   using OuterType = typename std::common_type<ValueType, SizeType>::type;
545
546   const unsigned char* const start_;
547   block_t block_;
548   SizeType position_; // Index of current value (= #reads - 1).
549   OuterType outer_; // Outer offset: number of consumed bytes in upper.
550   ValueType value_;
551 };
552
553 } // namespace detail
554
555 // If kUnchecked = true the caller must guarantee that all the
556 // operations return valid elements, i.e., they would never return
557 // false if checked.
558 template <
559     class Encoder,
560     class Instructions = instructions::Default,
561     bool kUnchecked = false,
562     class SizeType = size_t>
563 class EliasFanoReader {
564  public:
565   typedef Encoder EncoderType;
566   typedef typename Encoder::ValueType ValueType;
567
568   explicit EliasFanoReader(const typename Encoder::CompressedList& list)
569       : upper_(list),
570         lower_(list.lower),
571         size_(list.size),
572         numLowerBits_(list.numLowerBits) {
573     DCHECK(Instructions::supported());
574     // To avoid extra branching during skipTo() while reading
575     // upper sequence we need to know the last element.
576     // If kUnchecked == true, we do not check that skipTo() is called
577     // within the bounds, so we can avoid initializing lastValue_.
578     if (kUnchecked || UNLIKELY(list.size == 0)) {
579       lastValue_ = 0;
580       return;
581     }
582     ValueType lastUpperValue = ValueType(8 * list.upperSize() - size_);
583     auto it = list.upper + list.upperSize() - 1;
584     DCHECK_NE(*it, 0);
585     lastUpperValue -= 8 - folly::findLastSet(*it);
586     lastValue_ = readLowerPart(size_ - 1) | (lastUpperValue << numLowerBits_);
587   }
588
589   void reset() {
590     upper_.reset();
591     value_ = kInvalidValue;
592   }
593
594   bool next() {
595     if (!kUnchecked && UNLIKELY(position() + 1 >= size_)) {
596       return setDone();
597     }
598     upper_.next();
599     value_ = readLowerPart(upper_.position()) |
600              (upper_.value() << numLowerBits_);
601     return true;
602   }
603
604   bool skip(SizeType n) {
605     CHECK_GT(n, 0);
606
607     if (kUnchecked || LIKELY(position() + n < size_)) {
608       if (LIKELY(n < kLinearScanThreshold)) {
609         for (SizeType i = 0; i < n; ++i) {
610           upper_.next();
611         }
612       } else {
613         upper_.skip(n);
614       }
615       value_ = readLowerPart(upper_.position()) |
616         (upper_.value() << numLowerBits_);
617       return true;
618     }
619
620     return setDone();
621   }
622
623   bool skipTo(ValueType value) {
624     // Also works when value_ == kInvalidValue.
625     if (value != kInvalidValue) { DCHECK_GE(value + 1, value_ + 1); }
626
627     if (!kUnchecked && value > lastValue_) {
628       return setDone();
629     } else if (value == value_) {
630       return true;
631     }
632
633     ValueType upperValue = (value >> numLowerBits_);
634     ValueType upperSkip = upperValue - upper_.value();
635     // The average density of ones in upper bits is 1/2.
636     // LIKELY here seems to make things worse, even for small skips.
637     if (upperSkip < 2 * kLinearScanThreshold) {
638       do {
639         upper_.next();
640       } while (UNLIKELY(upper_.value() < upperValue));
641     } else {
642       upper_.skipToNext(upperValue);
643     }
644
645     iterateTo(value);
646     return true;
647   }
648
649   /**
650    * Prepare to skip to `value` by prefetching appropriate memory in both the
651    * upper and lower bits.
652    */
653   void prepareSkipTo(ValueType value) const {
654     // Also works when value_ == kInvalidValue.
655     if (value != kInvalidValue) {
656       DCHECK_GE(value + 1, value_ + 1);
657     }
658
659     if ((!kUnchecked && value > lastValue_) || (value == value_)) {
660       return;
661     }
662
663     // Do minimal computation required to prefetch address used in
664     // `readLowerPart()`.
665     ValueType upperValue = (value >> numLowerBits_);
666     const auto upperPosition = upper_.prepareSkipTo(upperValue);
667     const auto addr = lower_ + (upperPosition * numLowerBits_ / 8);
668     __builtin_prefetch(addr);
669     __builtin_prefetch(addr + kCacheLineSize);
670   }
671
672   bool jump(SizeType n) {
673     if (LIKELY(n < size_)) {  // Also checks that n != -1.
674       value_ = readLowerPart(n) | (upper_.jump(n + 1) << numLowerBits_);
675       return true;
676     }
677     return setDone();
678   }
679
680   bool jumpTo(ValueType value) {
681     if (!kUnchecked && value > lastValue_) {
682       return setDone();
683     }
684
685     upper_.jumpToNext(value >> numLowerBits_);
686     iterateTo(value);
687     return true;
688   }
689
690   ValueType previousValue() const {
691     DCHECK_GT(position(), 0);
692     DCHECK_LT(position(), size());
693     return readLowerPart(upper_.position() - 1) |
694       (upper_.previousValue() << numLowerBits_);
695   }
696
697   SizeType size() const {
698     return size_;
699   }
700
701   bool valid() const {
702     return position() < size(); // Also checks that position() != -1.
703   }
704
705   SizeType position() const {
706     return upper_.position();
707   }
708   ValueType value() const {
709     DCHECK(valid());
710     return value_;
711   }
712
713  private:
714   // Must hold kInvalidValue + 1 == 0.
715   constexpr static ValueType kInvalidValue =
716       std::numeric_limits<ValueType>::max();
717
718   bool setDone() {
719     value_ = kInvalidValue;
720     upper_.setDone(size_);
721     return false;
722   }
723
724   ValueType readLowerPart(SizeType i) const {
725     DCHECK_LT(i, size_);
726     const size_t pos = i * numLowerBits_;
727     const unsigned char* ptr = lower_ + (pos / 8);
728     const uint64_t ptrv = folly::loadUnaligned<uint64_t>(ptr);
729     // This removes the branch in the fallback implementation of
730     // bzhi. The condition is verified at encoding time.
731     assume(numLowerBits_ < sizeof(ValueType) * 8);
732     return Instructions::bzhi(ptrv >> (pos % 8), numLowerBits_);
733   }
734
735   void iterateTo(ValueType value) {
736     while (true) {
737       value_ = readLowerPart(upper_.position()) |
738         (upper_.value() << numLowerBits_);
739       if (LIKELY(value_ >= value)) {
740         break;
741       }
742       upper_.next();
743     }
744   }
745
746   constexpr static size_t kLinearScanThreshold = 8;
747
748   detail::UpperBitsReader<Encoder, Instructions, SizeType> upper_;
749   const uint8_t* lower_;
750   SizeType size_;
751   ValueType value_ = kInvalidValue;
752   ValueType lastValue_;
753   uint8_t numLowerBits_;
754 };
755
756 } // namespace compression
757 } // namespace folly