Layout::fromUpperBoundAndSize(upperBound, size).allocList()) {}
void add(ValueType value) {
- CHECK_GE(value, lastValue_);
+ CHECK_LT(value, std::numeric_limits<ValueType>::max());
+ // Also works when lastValue_ == -1.
+ CHECK_GT(value + 1, lastValue_ + 1)
+ << "BitVectorCoding only supports stricly monotone lists";
+
auto block = bits_ + (value / 64) * sizeof(uint64_t);
size_t inner = value % 64;
folly::Bits<folly::Unaligned<uint64_t>>::set(
uint8_t* const skipPointers_ = nullptr;
uint8_t* const forwardPointers_ = nullptr;
- ValueType lastValue_ = 0;
+ ValueType lastValue_ = -1;
size_t size_ = 0;
size_t skipPointersSize_ = 0;
outer_ = 0;
inner_ = -1;
position_ = -1;
- value_ = 0;
+ value_ = kInvalidValue;
}
bool next() {
}
bool skipTo(ValueType v) {
- DCHECK_GE(v, value_);
- if (v <= value_) {
- return true;
- } else if (!kUnchecked && v > upperBound_) {
+ // Also works when value_ == kInvalidValue.
+ if (v != kInvalidValue) { DCHECK_GE(v + 1, value_ + 1); }
+
+ if (!kUnchecked && v > upperBound_) {
return setDone();
+ } else if (v == value_) {
+ return true;
}
// Small skip optimization.
size_t size() const { return size_; }
+ bool valid() const {
+ return position() < size(); // Also checks that position() != -1.
+ }
+
size_t position() const { return position_; }
- ValueType value() const { return value_; }
+ ValueType value() const {
+ DCHECK(valid());
+ return value_;
+ }
bool jump(size_t n) {
reset();
- if (n > 0) {
- return skip(n);
- } else {
- return true;
- }
+ return skip(n + 1);
}
bool jumpTo(ValueType v) {
}
bool setDone() {
- value_ = std::numeric_limits<ValueType>::max();
+ value_ = kInvalidValue;
position_ = size_;
return false;
}
private:
+ constexpr static ValueType kInvalidValue =
+ std::numeric_limits<ValueType>::max(); // Must hold kInvalidValue + 1 == 0.
+
bool setValue() {
value_ = static_cast<ValueType>(8 * outer_ + inner_);
return true;
size_t inner_;
size_t position_;
uint64_t block_;
- ValueType value_ = 0;
+ ValueType value_;
size_t size_;
ValueType upperBound_;
Layout::fromUpperBoundAndSize(upperBound, size).allocList()) { }
void add(ValueType value) {
+ CHECK_LT(value, std::numeric_limits<ValueType>::max());
CHECK_GE(value, lastValue_);
const auto numLowerBits = result_.numLowerBits;
void reset() {
upper_.reset();
- value_ = 0;
+ value_ = kInvalidValue;
}
bool next() {
}
bool skipTo(ValueType value) {
- DCHECK_GE(value, value_);
- if (value <= value_) {
- return true;
- } else if (!kUnchecked && value > lastValue_) {
+ // Also works when value_ == kInvalidValue.
+ if (value != kInvalidValue) { DCHECK_GE(value + 1, value_ + 1); }
+
+ if (!kUnchecked && value > lastValue_) {
return setDone();
+ } else if (value == value_) {
+ return true;
}
size_t upperValue = (value >> numLowerBits_);
}
bool jump(size_t n) {
- if (LIKELY(n - 1 < size_)) { // n > 0 && n <= size_
- value_ = readLowerPart(n - 1) | (upper_.jump(n) << numLowerBits_);
- return true;
- } else if (n == 0) {
- reset();
+ if (LIKELY(n < size_)) { // Also checks that n != -1.
+ value_ = readLowerPart(n) | (upper_.jump(n + 1) << numLowerBits_);
return true;
}
return setDone();
}
bool jumpTo(ValueType value) {
- if (value <= 0) {
- reset();
- return true;
- } else if (!kUnchecked && value > lastValue_) {
+ if (!kUnchecked && value > lastValue_) {
return setDone();
}
size_t size() const { return size_; }
+ bool valid() const {
+ return position() < size(); // Also checks that position() != -1.
+ }
+
size_t position() const { return upper_.position(); }
- ValueType value() const { return value_; }
+ ValueType value() const {
+ DCHECK(valid());
+ return value_;
+ }
private:
+ constexpr static ValueType kInvalidValue =
+ std::numeric_limits<ValueType>::max(); // Must hold kInvalidValue + 1 == 0.
+
bool setDone() {
- value_ = std::numeric_limits<ValueType>::max();
+ value_ = kInvalidValue;
upper_.setDone(size_);
return false;
}
const uint8_t* lower_;
detail::UpperBitsReader<Encoder, Instructions> upper_;
const ValueType lowerMask_;
- ValueType value_ = 0;
+ ValueType value_ = kInvalidValue;
ValueType lastValue_;
uint8_t numLowerBits_;
};
template <class Reader, class List>
void testNext(const std::vector<uint32_t>& data, const List& list) {
Reader reader(list);
- EXPECT_EQ(reader.value(), 0);
+ EXPECT_FALSE(reader.valid());
+
for (size_t i = 0; i < data.size(); ++i) {
EXPECT_TRUE(reader.next());
+ EXPECT_TRUE(reader.valid());
EXPECT_EQ(reader.value(), data[i]);
EXPECT_EQ(reader.position(), i);
maybeTestPreviousValue(data, reader, i);
}
EXPECT_FALSE(reader.next());
- EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(reader.valid());
EXPECT_EQ(reader.position(), reader.size());
}
size_t skipStep) {
CHECK_GT(skipStep, 0);
Reader reader(list);
- EXPECT_EQ(reader.value(), 0);
+
for (size_t i = skipStep - 1; i < data.size(); i += skipStep) {
EXPECT_TRUE(reader.skip(skipStep));
+ EXPECT_TRUE(reader.valid());
EXPECT_EQ(reader.value(), data[i]);
EXPECT_EQ(reader.position(), i);
maybeTestPreviousValue(data, reader, i);
}
EXPECT_FALSE(reader.skip(skipStep));
- EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(reader.valid());
EXPECT_EQ(reader.position(), reader.size());
EXPECT_FALSE(reader.next());
}
void testSkipTo(const std::vector<uint32_t>& data, const List& list,
size_t skipToStep) {
CHECK_GT(skipToStep, 0);
-
Reader reader(list);
- EXPECT_EQ(reader.value(), 0);
const uint32_t delta = std::max<uint32_t>(1, data.back() / skipToStep);
uint32_t value = delta;
break;
}
EXPECT_TRUE(reader.skipTo(value));
+ EXPECT_TRUE(reader.valid());
EXPECT_EQ(reader.value(), *it);
+ EXPECT_EQ(reader.position(), std::distance(data.begin(), it));
value = reader.value() + delta;
maybeTestPreviousValue(data, reader, std::distance(data.begin(), it));
}
- EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(reader.valid());
EXPECT_EQ(reader.position(), reader.size());
EXPECT_FALSE(reader.next());
}
}
testSkipTo<Reader, List>(data, list, std::numeric_limits<size_t>::max());
{
+ // Skip to the first element.
+ Reader reader(list);
+ EXPECT_TRUE(reader.skipTo(data[0]));
+ EXPECT_EQ(reader.value(), data[0]);
+ EXPECT_EQ(reader.position(), 0);
+ }
+ {
+ // Skip past the last element.
Reader reader(list);
EXPECT_FALSE(reader.skipTo(data.back() + 1));
- EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(reader.valid());
+ EXPECT_EQ(reader.position(), reader.size());
+ EXPECT_FALSE(reader.next());
+ }
+ {
+ // Skip to maximum integer.
+ Reader reader(list);
+ using ValueType = typename Reader::ValueType;
+ EXPECT_FALSE(reader.skipTo(std::numeric_limits<ValueType>::max()));
+ EXPECT_FALSE(reader.valid());
EXPECT_EQ(reader.position(), reader.size());
EXPECT_FALSE(reader.next());
}
}
Reader reader(list);
- EXPECT_TRUE(reader.jump(0));
- EXPECT_EQ(reader.value(), 0);
for (auto i : is) {
- EXPECT_TRUE(reader.jump(i + 1));
+ EXPECT_TRUE(reader.jump(i));
EXPECT_EQ(reader.value(), data[i]);
EXPECT_EQ(reader.position(), i);
maybeTestPreviousValue(data, reader, i);
}
- EXPECT_FALSE(reader.jump(data.size() + 1));
- EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(reader.jump(data.size()));
+ EXPECT_FALSE(reader.valid());
EXPECT_EQ(reader.position(), reader.size());
}
}
EXPECT_TRUE(reader.jumpTo(0));
- EXPECT_EQ(reader.value(), 0);
- EXPECT_EQ(reader.position(), -1);
-
- if (data.front() > 0) {
- EXPECT_TRUE(reader.jumpTo(1));
- EXPECT_EQ(reader.value(), data.front());
- EXPECT_EQ(reader.position(), 0);
- }
+ EXPECT_EQ(reader.value(), data[0]);
+ EXPECT_EQ(reader.position(), 0);
EXPECT_TRUE(reader.jumpTo(data.back()));
EXPECT_EQ(reader.value(), data.back());
EXPECT_EQ(reader.position(), reader.size() - 1);
EXPECT_FALSE(reader.jumpTo(data.back() + 1));
- EXPECT_EQ(reader.value(), std::numeric_limits<uint32_t>::max());
+ EXPECT_FALSE(reader.valid());
EXPECT_EQ(reader.position(), reader.size());
}
Reader reader(list);
EXPECT_FALSE(reader.skip(1));
EXPECT_FALSE(reader.skip(10));
- EXPECT_FALSE(reader.jump(1));
+ EXPECT_FALSE(reader.jump(0));
EXPECT_FALSE(reader.jump(10));
}
{