29 #include <type_traits> 39 #include <glog/logging.h> 42 #error EliasFanoCoding.h requires x86_64 46 namespace compression {
48 static_assert(
kIsLittleEndian,
"EliasFanoCoding.h requires little endianness");
52 template <
class Po
inter>
56 template <
class OtherPo
inter>
65 upper(reinterpret_cast<Pointer>(other.
upper)) {}
67 template <
class T = Po
inter>
94 class SkipValue = size_t,
95 size_t kSkipQuantum = 0,
96 size_t kForwardQuantum = 0>
100 "Value should be unsigned integral");
109 static constexpr
size_t skipQuantum = kSkipQuantum;
110 static constexpr
size_t forwardQuantum = kForwardQuantum;
113 if (
UNLIKELY(size == 0 || upperBound < size)) {
123 return (size > (upperBound >> candidate)) ? candidate - 1 : candidate;
131 template <
class RandomAccessIterator>
133 RandomAccessIterator
begin,
134 RandomAccessIterator
end) {
136 return MutableCompressedList();
146 : lower_(result.
lower),
147 upper_(result.
upper),
148 skipPointers_(reinterpret_cast<SkipValueType*>(result.
skipPointers)),
157 Layout::fromUpperBoundAndSize(upperBound, size).allocList()) {}
161 CHECK_GE(value, lastValue_);
167 const size_t pos = upperBits + size_;
168 upper_[pos / 8] |= 1U << (pos % 8);
170 if (numLowerBits != 0) {
171 const ValueType lowerBits = value & ((ValueType(1) <<
numLowerBits) - 1);
172 writeBits56(lower_, size_ * numLowerBits, numLowerBits, lowerBits);
175 if (skipQuantum != 0) {
176 while ((skipPointersSize_ + 1) * skipQuantum <= upperBits) {
178 skipPointers_[skipPointersSize_++] = SkipValue(size_);
182 if (forwardQuantum != 0) {
183 if ((size_ + 1) % forwardQuantum == 0) {
184 const auto k = size_ / forwardQuantum;
186 forwardPointers_[
k] = upperBits;
194 const MutableCompressedList&
finish()
const {
195 CHECK_EQ(size_, result_.size);
204 DCHECK_EQ(0, value & ~((
uint64_t(1) << len) - 1));
205 unsigned char*
const ptr = data + (pos / 8);
206 uint64_t ptrv = folly::loadUnaligned<uint64_t>(
ptr);
207 ptrv |= value << (pos % 8);
208 folly::storeUnaligned<uint64_t>(
ptr, ptrv);
211 unsigned char* lower_ =
nullptr;
212 unsigned char* upper_ =
nullptr;
213 SkipValueType* skipPointers_ =
nullptr;
214 SkipValueType* forwardPointers_ =
nullptr;
216 ValueType lastValue_ = 0;
218 size_t skipPointersSize_ = 0;
227 size_t kForwardQuantum>
237 const size_t upperSizeBits =
240 const size_t upper = (upperSizeBits + 7) / 8;
244 CHECK_LT(numLowerBits, 8 *
sizeof(
Value));
249 return fromInternalSizes(numLowerBits, upper, size);
258 layout.
lower = (numLowerBits * size + 7) / 8;
264 if (skipQuantum != 0) {
269 size_t numSkipPointers = (8 * upper -
size) / skipQuantum;
276 if (forwardQuantum != 0) {
277 size_t numForwardPointers = size / forwardQuantum;
288 template <
class Range>
318 buf =
static_cast<uint8_t*
>(malloc(bytes() + 7));
321 return openList(bufRange);
336 template <
class Encoder,
class Instructions,
class SizeType>
352 block_ =
start_ !=
nullptr ? folly::loadUnaligned<block_t>(
start_) : 0;
368 getPreviousInfo(block, inner, outer_);
369 block_ = folly::loadUnaligned<block_t>(
start_ + outer_);
372 return setValue(inner);
377 while (block_ == 0) {
379 block_ = folly::loadUnaligned<block_t>(
start_ + outer_);
383 size_t inner = Instructions::ctz(block_);
384 block_ = Instructions::blsr(block_);
386 return setValue(inner);
395 if (Encoder::forwardQuantum > 0 && n > Encoder::forwardQuantum) {
396 const size_t steps = position_ / Encoder::forwardQuantum;
397 const size_t dest = folly::loadUnaligned<SkipValueType>(
398 this->forwardPointers_ + (steps - 1) *
sizeof(SkipValueType));
400 reposition(dest + steps * Encoder::forwardQuantum);
401 n = position_ + 1 - steps * Encoder::forwardQuantum;
409 block_ = folly::loadUnaligned<block_t>(
start_ + outer_);
414 size_t inner = select64<Instructions>(block_, n - 1);
415 block_ &= (
block_t(-1) << inner) << 1;
417 return setValue(inner);
423 DCHECK_GE(v, value_);
426 if (Encoder::skipQuantum > 0 && v >= value_ + Encoder::skipQuantum) {
427 const size_t steps = v / Encoder::skipQuantum;
428 const size_t dest = folly::loadUnaligned<SkipValueType>(
429 this->skipPointers_ + (steps - 1) *
sizeof(SkipValueType));
431 reposition(dest + Encoder::skipQuantum * steps);
432 position_ = dest - 1;
443 size_t skip = v - (8 * outer_ - position_ - 1);
445 constexpr
size_t kBitsPerBlock = 8 *
sizeof(
block_t);
448 position_ += kBitsPerBlock - cnt;
450 block_ = folly::loadUnaligned<block_t>(
start_ + outer_);
454 auto inner = select64<Instructions>(~block_, skip - 1);
455 position_ += inner - skip + 1;
456 block_ &=
block_t(-1) << inner;
470 auto position = position_;
472 if (Encoder::skipQuantum > 0 && v >= value_ + Encoder::skipQuantum) {
474 const size_t steps = v / Encoder::skipQuantum;
475 const size_t dest = folly::loadUnaligned<SkipValueType>(
476 this->skipPointers_ + (steps - 1) *
sizeof(SkipValueType));
479 outer = (dest + Encoder::skipQuantum *
steps) / 8;
488 __builtin_prefetch(
addr);
489 __builtin_prefetch(
addr + kCacheLineSize);
496 if (Encoder::forwardQuantum == 0 || n <= Encoder::forwardQuantum) {
506 if (Encoder::skipQuantum == 0 || v < Encoder::skipQuantum) {
511 return skipToNext(v);
518 getPreviousInfo(block, inner, outer);
519 return static_cast<ValueType
>(8 * outer + inner - (position_ - 1));
528 value_ =
static_cast<ValueType
>(8 * outer_ + inner - position_);
534 block_ = folly::loadUnaligned<block_t>(
start_ + outer_);
535 block_ &= ~((
block_t(1) << (dest % 8)) - 1);
545 DCHECK_GT(position(), 0);
548 block = folly::loadUnaligned<block_t>(
start_ + outer);
549 inner = size_t(value_) - 8 * outer_ + position_;
550 block &= (
block_t(1) << inner) - 1;
553 outer -= std::min<OuterType>(
sizeof(
block_t), outer);
554 block = folly::loadUnaligned<block_t>(
start_ + outer);
556 inner = 8 *
sizeof(
block_t) - 1 - Instructions::clz(block);
574 bool kUnchecked =
false,
575 class SizeType =
size_t>
586 DCHECK(Instructions::supported());
591 if (kUnchecked ||
UNLIKELY(list.size == 0)) {
595 ValueType lastUpperValue = ValueType(8 * list.upperSize() - size_);
596 auto it = list.upper + list.upperSize() - 1;
599 lastValue_ = readLowerPart(size_ - 1) | (lastUpperValue << numLowerBits_);
604 value_ = kInvalidValue;
608 if (!kUnchecked &&
UNLIKELY(position() == 0)) {
614 readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_);
619 if (!kUnchecked &&
UNLIKELY(position() + 1 >= size_)) {
624 readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_);
631 if (kUnchecked ||
LIKELY(position() + n < size_)) {
632 if (
LIKELY(n < kLinearScanThreshold)) {
633 for (SizeType
i = 0;
i < n; ++
i) {
640 readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_);
649 if (value != kInvalidValue) {
650 DCHECK_GE(value + 1, value_ + 1);
653 if (!kUnchecked && value > lastValue_) {
655 }
else if (value == value_) {
659 ValueType upperValue = (value >> numLowerBits_);
660 ValueType upperSkip = upperValue - upper_.value();
663 if (upperSkip < 2 * kLinearScanThreshold) {
666 }
while (
UNLIKELY(upper_.value() < upperValue));
668 upper_.skipToNext(upperValue);
681 if (value != kInvalidValue) {
682 DCHECK_GE(value + 1, value_ + 1);
685 if ((!kUnchecked && value > lastValue_) || (value == value_)) {
691 ValueType upperValue = (value >> numLowerBits_);
692 const auto upperPosition = upper_.prepareSkipTo(upperValue);
693 const auto addr = lower_ + (upperPosition * numLowerBits_ / 8);
694 __builtin_prefetch(
addr);
695 __builtin_prefetch(
addr + kCacheLineSize);
700 value_ = readLowerPart(n) | (upper_.jump(n + 1) << numLowerBits_);
707 if (!kUnchecked && value > lastValue_) {
711 upper_.jumpToNext(value >> numLowerBits_);
722 DCHECK_GT(position(), 0);
723 DCHECK_LT(position(),
size());
724 return readLowerPart(upper_.position() - 1) |
725 (upper_.previousValue() << numLowerBits_);
733 return position() <
size();
737 return upper_.position();
746 constexpr
static ValueType kInvalidValue =
750 value_ = kInvalidValue;
751 upper_.setDone(size_);
757 const size_t pos = i * numLowerBits_;
758 const unsigned char*
ptr = lower_ + (pos / 8);
759 const uint64_t ptrv = folly::loadUnaligned<uint64_t>(
ptr);
762 assume(numLowerBits_ <
sizeof(ValueType) * 8);
763 return Instructions::bzhi(ptrv >> (pos % 8), numLowerBits_);
769 readLowerPart(upper_.position()) | (upper_.value() << numLowerBits_);
770 if (
LIKELY(value_ >= value)) {
777 constexpr
static size_t kLinearScanThreshold = 8;
782 ValueType value_ = kInvalidValue;
constexpr unsigned int popcount(T const v)
EliasFanoReader(const typename Encoder::CompressedList &list)
Encoder::ValueType ValueType
void prepareSkipTo(ValueType value) const
ValueType previousValue() const
ValueType previousValue() const
BitVectorEncoder< uint32_t, uint32_t, 128, 128 > Encoder
static Layout fromUpperBoundAndSize(size_t upperBound, size_t size)
ValueType skipToNext(ValueType v)
ValueType setValue(size_t inner)
static Layout fromInternalSizes(uint8_t numLowerBits, size_t upper, size_t size)
void advance(size_type n)
void setDone(SizeType endPos)
auto begin(TestAdlIterable &instance)
MutableCompressedList result_
constexpr auto kIsLittleEndian
EliasFanoCompressedListBase< const uint8_t * > EliasFanoCompressedList
—— Concurrent Priority Queue Implementation ——
detail::Skip skip(size_t count)
EliasFanoCompressedListBase< typename Range::iterator > openList(Range &buf) const
ValueType readLowerPart(SizeType i) const
MutableCompressedList allocList() const
SizeType position() const
bool jumpTo(ValueType value)
ValueType skip(SizeType n)
MutableEliasFanoCompressedList MutableCompressedList
void reposition(SizeType dest)
EliasFanoEncoderV2(const MutableCompressedList &result)
constexpr size_t kCacheLineSize
EliasFanoCompressedListBase(const EliasFanoCompressedListBase< OtherPointer > &other)
static void writeBits56(unsigned char *data, size_t pos, uint8_t len, uint64_t value)
bool Value(const T &value, M matcher)
void getPreviousInfo(block_t &block, size_t &inner, OuterType &outer) const
auto end(TestAdlIterable &instance)
constexpr Iter data() const
Range subpiece(size_type first, size_type length=npos) const
Encoder::MutableCompressedList list
const unsigned char *const start_
constexpr unsigned int findLastSet(T const v)
ValueType lastValue() const
SizeType prepareSkipTo(ValueType v) const
SizeType position() const
static const char *const value
EliasFanoCompressedList CompressedList
typename std::common_type< ValueType, SizeType >::type OuterType
UpperBitsReader(const typename Encoder::CompressedList &list)
constexpr Iter end() const
constexpr Iter begin() const
folly::Range< Pointer > data
Encoder::ValueType ValueType
detail::UpperBitsReader< Encoder, Instructions, SizeType > upper_
static MutableCompressedList encode(RandomAccessIterator begin, RandomAccessIterator end)
static uint8_t defaultNumLowerBits(size_t upperBound, size_t size)
void iterateTo(ValueType value)
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
bool skipTo(ValueType value)
EliasFanoCompressedListBase()=default
Encoder::SkipValueType SkipValueType
ValueType jumpToNext(ValueType v)
FOLLY_ALWAYS_INLINE void assume(bool cond)
void add(ValueType value)
auto free() -> decltype(::free(T(nullptr)))
EliasFanoCompressedListBase< uint8_t * > MutableEliasFanoCompressedList
ThreadPoolListHook * addr
EliasFanoEncoderV2(size_t size, ValueType upperBound)
const MutableCompressedList & finish() const