mirror of https://github.com/mongodb/mongo
SERVER-104760 break #include cycles and enforce with clang-tidy (#35786)
GitOrigin-RevId: f186c8347205ebbaa49c45c1f06e1310621366e0
This commit is contained in:
parent
3e3610ff1d
commit
c740e2bb42
|
|
@ -49,6 +49,7 @@ Checks: '-*,
|
|||
hicpp-static-assert,
|
||||
hicpp-undelegated-constructor,
|
||||
misc-definitions-in-headers,
|
||||
misc-header-include-cycle,
|
||||
misc-new-delete-overloads,
|
||||
misc-static-assert,
|
||||
misc-unconventional-assign-operator,
|
||||
|
|
@ -137,7 +138,7 @@ Checks: '-*,
|
|||
-readability-simplify-subscript-expr,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-string-compare,
|
||||
-readability-uniqueptr-delete-release
|
||||
-readability-uniqueptr-delete-release,
|
||||
'
|
||||
HeaderFilterRegex: 'mongo/[-A-Za-z0-9_/]*\.(h|hpp|ipp|inl|cstruct.h|defs|def.h)'
|
||||
CheckOptions:
|
||||
|
|
|
|||
|
|
@ -210,7 +210,6 @@ mongo_cc_library(
|
|||
"//src/mongo/base:data_range_cursor.h",
|
||||
"//src/mongo/base:data_type.h",
|
||||
"//src/mongo/base:data_type_endian.h",
|
||||
"//src/mongo/base:data_type_string_data.h",
|
||||
"//src/mongo/base:data_type_terminated.h",
|
||||
"//src/mongo/base:data_view.h",
|
||||
"//src/mongo/base:dependency_graph.h",
|
||||
|
|
|
|||
|
|
@ -166,10 +166,24 @@ struct DataType {
|
|||
static Status makeTrivialLoadStatus(size_t sizeOfT, size_t length, size_t debug_offset);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct DataType::Handler<StringData> {
|
||||
// Consumes all available data, producing
|
||||
// a `StringData(ptr,length)`.
|
||||
static Status load(StringData* sdata,
|
||||
const char* ptr,
|
||||
size_t length,
|
||||
size_t* advanced,
|
||||
std::ptrdiff_t debug_offset);
|
||||
|
||||
// Copies `sdata` fully into the [ptr,ptr+length) range.
|
||||
// Does nothing and returns an Overflow status if
|
||||
// `sdata` doesn't fit.
|
||||
static Status store(
|
||||
StringData sdata, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset);
|
||||
|
||||
static StringData defaultConstruct() {
|
||||
return StringData();
|
||||
}
|
||||
};
|
||||
} // namespace mongo
|
||||
|
||||
// Force the visibility of the DataType::Handler specializations.
|
||||
#define MONGO_BASE_DATA_TYPE_H_INCLUDE_HANDSHAKE_
|
||||
#include "mongo/base/data_type_string_data.h"
|
||||
|
||||
#undef MONGO_BASE_DATA_TYPE_H_INCLUDE_HANDSHAKE_
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2018-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "mongo/base/data_type.h"
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/base/string_data.h"
|
||||
|
||||
#ifndef MONGO_BASE_DATA_TYPE_H_INCLUDE_HANDSHAKE_
|
||||
#error "do not include directly. Use mongo/base/data_type.h"
|
||||
#endif // MONGO_BASE_DATA_TYPE_H_INCLUDE_HANDSHAKE_
|
||||
|
||||
// Provides a DataType::Handler specialization for StringData.
|
||||
|
||||
namespace mongo {
|
||||
|
||||
template <>
|
||||
struct DataType::Handler<StringData> {
|
||||
// Consumes all available data, producing
|
||||
// a `StringData(ptr,length)`.
|
||||
static Status load(StringData* sdata,
|
||||
const char* ptr,
|
||||
size_t length,
|
||||
size_t* advanced,
|
||||
std::ptrdiff_t debug_offset);
|
||||
|
||||
// Copies `sdata` fully into the [ptr,ptr+length) range.
|
||||
// Does nothing and returns an Overflow status if
|
||||
// `sdata` doesn't fit.
|
||||
static Status store(
|
||||
StringData sdata, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset);
|
||||
|
||||
static StringData defaultConstruct() {
|
||||
return StringData();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mongo
|
||||
|
|
@ -27,7 +27,6 @@ mongo_cc_library(
|
|||
"simple8b.h",
|
||||
"simple8b.inl",
|
||||
"simple8b_builder.h",
|
||||
"simple8b_builder.inl",
|
||||
"simple8b_helpers.h",
|
||||
"simple8b_type_util.h",
|
||||
"//src/mongo/util:overloaded_visitor.h",
|
||||
|
|
|
|||
|
|
@ -30,15 +30,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <absl/numeric/int128.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/none.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "mongo/bson/column/simple8b_helpers.h"
|
||||
|
||||
namespace mongo {
|
||||
/**
|
||||
* Concept for writing 64bit simple8b blocks via a callback.
|
||||
|
|
@ -349,6 +356,625 @@ private:
|
|||
std::vector<PendingValue, PendingValueAllocator> _pendingValues;
|
||||
};
|
||||
|
||||
} // namespace mongo
|
||||
// This is called in _encode while iterating through _pendingValues. For the base selector, we just
|
||||
// return val. Contains unsed vars in order to seamlessly integrate with seven and eight selector
|
||||
// extensions.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::BaseSelectorEncodeFunctor {
|
||||
uint64_t operator()(const PendingValue& value) {
|
||||
return static_cast<uint64_t>(value.value());
|
||||
};
|
||||
};
|
||||
|
||||
#include "mongo/bson/column/simple8b_builder.inl"
|
||||
// This is called in _encode while iterating through _pendingValues. It creates part of a simple8b
|
||||
// word according to the specifications of the sevenSelector extension. This value is then appended
|
||||
// to the full simple8b word in _encode.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::SevenSelectorEncodeFunctor {
|
||||
uint64_t operator()(const PendingValue& value) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
uint8_t trailingZeros = value.trailingZerosCount[kSevenSelector];
|
||||
uint64_t currWord = trailingZeros;
|
||||
// We do two shifts here to account for the case where trailingZeros is > kTrailingZero bit
|
||||
// size. If we subtracted this could lead to shift by a negative value which is undefined.
|
||||
currWord |= static_cast<uint64_t>((value.value() >> trailingZeros)
|
||||
<< kTrailingZeroBitSize[kSevenSelector]);
|
||||
return currWord;
|
||||
};
|
||||
};
|
||||
|
||||
// This is a helper functor that is extended by the EightSelectorSmall and EightSelectorLarge encode
|
||||
// functors. It provides the logic for encoding with the eight selector where the extension type is
|
||||
// designated by the inheritance in the EightSelectorSmall and EightSelectorLarge functors.
|
||||
template <typename T, class Allocator>
|
||||
template <uint8_t ExtensionType>
|
||||
struct Simple8bBuilder<T, Allocator>::EightSelectorEncodeFunctor {
|
||||
uint64_t operator()(const PendingValue& value) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// integer division. We have a nibble shift of size 4
|
||||
uint8_t trailingZeros = value.trailingZerosCount[ExtensionType] / kNibbleShiftSize;
|
||||
uint64_t currWord = trailingZeros;
|
||||
// Shift to remove trailing zeros * 4 and then shift over for the 4 bits to hold
|
||||
// the trailingZerosCount
|
||||
currWord |= static_cast<uint64_t>((value.value() >> (trailingZeros * kNibbleShiftSize))
|
||||
<< kTrailingZeroBitSize[ExtensionType]);
|
||||
return currWord;
|
||||
}
|
||||
};
|
||||
|
||||
// This is called in _encode while iterating through _pendingValues. It creates part of a simple8b
|
||||
// word according to the specifications of the eightSelectorSmall extension. This value is then
|
||||
// appended to the full simple8b word in _encode.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::EightSelectorSmallEncodeFunctor
|
||||
: public EightSelectorEncodeFunctor<simple8b_internal::kEightSelectorSmall> {};
|
||||
|
||||
// This is called in _encode while iterating through _pendingValues. It creates part of a simple8b
|
||||
// word according to the specifications of the eightSelectorLarge extension. This value is then
|
||||
// appended to the full simple8b word in _encode.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::EightSelectorLargeEncodeFunctor
|
||||
: public EightSelectorEncodeFunctor<simple8b_internal::kEightSelectorLarge> {};
|
||||
|
||||
// Base Constructor for PendingValue
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::PendingValue::PendingValue(
|
||||
boost::optional<T> val,
|
||||
std::array<uint8_t, kNumOfSelectorTypes> bitCount,
|
||||
std::array<uint8_t, kNumOfSelectorTypes> trailingZerosCount)
|
||||
: val(val), bitCount(bitCount), trailingZerosCount(trailingZerosCount){};
|
||||
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::PendingIterator::PendingIterator(
|
||||
typename std::vector<PendingValue, PendingValueAllocator>::const_iterator beginning,
|
||||
typename std::vector<PendingValue, PendingValueAllocator>::const_iterator it,
|
||||
reference rleValue,
|
||||
uint32_t rleCount)
|
||||
: _begin(beginning), _it(it), _rleValue(rleValue), _rleCount(rleCount) {}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator->() const -> pointer {
|
||||
return &operator*();
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator*() const -> reference {
|
||||
if (_rleCount > 0)
|
||||
return _rleValue;
|
||||
|
||||
return _it->val;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator++() -> PendingIterator& {
|
||||
if (_rleCount > 0) {
|
||||
--_rleCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
++_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator++(int) -> PendingIterator {
|
||||
auto ret = *this;
|
||||
++(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator--() -> PendingIterator& {
|
||||
if (_rleCount > 0 || _it == _begin) {
|
||||
++_rleCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
--_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator--(int) -> PendingIterator {
|
||||
auto ret = *this;
|
||||
--(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::PendingIterator::operator==(
|
||||
const Simple8bBuilder<T, Allocator>::PendingIterator& rhs) const {
|
||||
return _it == rhs._it && _rleCount == rhs._rleCount;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::PendingIterator::operator!=(
|
||||
const Simple8bBuilder<T, Allocator>::PendingIterator& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::Simple8bBuilder(Allocator allocator) : _pendingValues(allocator) {}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::~Simple8bBuilder() = default;
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
void Simple8bBuilder<T, Allocator>::prefillWithSkips(size_t numSkips, F&& writeFn) {
|
||||
constexpr size_t kMaxSkipsInBlock = 60;
|
||||
constexpr uint64_t kAllSkipsBlock =
|
||||
0xFFFFFFFFFFFFFFF1; // A simple8b block that contains 60 skips
|
||||
|
||||
// Do checks once, and then process all skips in bulk
|
||||
uassert(8575002,
|
||||
"prefillWithSkips() called after other appends",
|
||||
_rleCount == 0 && _pendingValues.empty() && _lastValidExtensionType == 0);
|
||||
|
||||
if (numSkips > kMaxSkipsInBlock) {
|
||||
// Handle case with initialized _rleCount (this should be the common case)
|
||||
writeFn(kAllSkipsBlock);
|
||||
_rleCount = numSkips - kMaxSkipsInBlock;
|
||||
_lastValueInPrevWord = boost::none;
|
||||
} else {
|
||||
// Insufficient skips to start an _rleCount, just default to appending them
|
||||
// individually (we can skip rle checks)
|
||||
for (size_t i = 0; i < numSkips; ++i)
|
||||
_appendSkip(true, writeFn);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
bool Simple8bBuilder<T, Allocator>::append(T value, F&& writeFn) {
|
||||
if (rlePossible()) {
|
||||
if (_lastValueInPrevWord == value) {
|
||||
++_rleCount;
|
||||
// RLE does not use selectors, make sure they are reset.
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
return true;
|
||||
}
|
||||
_handleRleTermination(writeFn);
|
||||
}
|
||||
|
||||
return _appendValue(value, true, writeFn);
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
void Simple8bBuilder<T, Allocator>::skip(F&& writeFn) {
|
||||
if (rlePossible()) {
|
||||
if (!_lastValueInPrevWord.has_value()) {
|
||||
++_rleCount;
|
||||
// RLE does not use selectors, make sure they are reset.
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
return;
|
||||
}
|
||||
_handleRleTermination(writeFn);
|
||||
}
|
||||
_appendSkip(true /* tryRle */, writeFn);
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
void Simple8bBuilder<T, Allocator>::flush(F&& writeFn) {
|
||||
// Flush repeating integers that have been kept for RLE.
|
||||
_handleRleTermination(writeFn);
|
||||
// Flush buffered values in _pendingValues.
|
||||
if (!_pendingValues.empty()) {
|
||||
// always flush with the most recent valid selector. This value is the baseSelector if we
|
||||
// have not have a valid selector yet.
|
||||
do {
|
||||
uint64_t simple8bWord = _encodeLargestPossibleWord(_lastValidExtensionType);
|
||||
writeFn(simple8bWord);
|
||||
} while (!_pendingValues.empty());
|
||||
|
||||
// There are no more words in _pendingValues and RLE is possible.
|
||||
// However the _rleCount is 0 because we have not read any of the values in the next word.
|
||||
_rleCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::setLastForRLE(boost::optional<T> val) {
|
||||
_lastValueInPrevWord = val;
|
||||
if (val) {
|
||||
auto pendingValue = _calculatePendingValue(*val);
|
||||
invariant(pendingValue);
|
||||
invariant(_doesIntegerFitInCurrentWord(*pendingValue));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::resetLastForRLEIfNeeded() {
|
||||
if (!rlePossible()) {
|
||||
_lastValueInPrevWord = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::initializeRLEFrom(const Simple8bBuilder<T, Allocator>& other) {
|
||||
if (other.rlePossible()) {
|
||||
_lastValueInPrevWord = other._lastValueInPrevWord;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
boost::optional<typename Simple8bBuilder<T, Allocator>::PendingValue>
|
||||
Simple8bBuilder<T, Allocator>::_calculatePendingValue(T value) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// Early exit if we try to store max value. They are not handled when counting zeros.
|
||||
if (value == std::numeric_limits<T>::max())
|
||||
return boost::none;
|
||||
|
||||
uint8_t trailingZerosCount = countTrailingZerosWithZero(value);
|
||||
// Initially set every selector as invalid.
|
||||
uint8_t bitCountWithoutLeadingZeros = countBitsWithoutLeadingZeros(value);
|
||||
uint8_t trailingZerosStoredInCountSeven =
|
||||
(std::min(trailingZerosCount, kTrailingZerosMaxCount[kSevenSelector]));
|
||||
uint8_t meaningfulValueBitsStoredWithSeven =
|
||||
bitCountWithoutLeadingZeros - trailingZerosStoredInCountSeven;
|
||||
// We use integer division to ensure that a multiple of 4 is stored in
|
||||
// trailingZerosStoredInCount when we have the nibble shift.
|
||||
uint8_t trailingZerosStoredInCountEightSmall =
|
||||
(std::min(trailingZerosCount, kTrailingZerosMaxCount[kEightSelectorSmall]) /
|
||||
kNibbleShiftSize) *
|
||||
kNibbleShiftSize;
|
||||
uint8_t meaningfulValueBitsStoredWithEightSmall =
|
||||
bitCountWithoutLeadingZeros - trailingZerosStoredInCountEightSmall;
|
||||
// We use integer division to ensure that a multiple of 4 is stored in
|
||||
// trailingZerosStoredInCount when we have the nibble shift.
|
||||
uint8_t trailingZerosStoredInCountEightLarge =
|
||||
(std::min(trailingZerosCount, kTrailingZerosMaxCount[kEightSelectorLarge]) /
|
||||
kNibbleShiftSize) *
|
||||
kNibbleShiftSize;
|
||||
uint8_t meaningfulValueBitsStoredWithEightLarge =
|
||||
bitCountWithoutLeadingZeros - trailingZerosStoredInCountEightLarge;
|
||||
|
||||
// Edge cases where we have the number of trailing zeros bits as all ones and we need to add a
|
||||
// padded zero to the meaningful bits to avoid confilicts with skip storage. Otherwise, we can
|
||||
// reuse the bitCountWithoutLeadingZeros already calculated above.
|
||||
if (trailingZerosCount == kTrailingZerosMaxCount[kSevenSelector]) {
|
||||
meaningfulValueBitsStoredWithSeven =
|
||||
countBitsWithoutLeadingZeros(value >> trailingZerosCount);
|
||||
} else if (trailingZerosCount == kTrailingZerosMaxCount[kEightSelectorSmall]) {
|
||||
meaningfulValueBitsStoredWithEightSmall =
|
||||
countBitsWithoutLeadingZeros(value >> trailingZerosCount);
|
||||
}
|
||||
|
||||
// This case is specifically for 128 bit types where we have 124 zeros or max zeros
|
||||
// count. We do not need to even check this for 64 bit types
|
||||
if constexpr (std::is_same<T, uint128_t>::value) {
|
||||
if (trailingZerosCount == kTrailingZerosMaxCount[kEightSelectorLarge]) {
|
||||
meaningfulValueBitsStoredWithEightLarge =
|
||||
countBitsWithoutLeadingZeros(value >> trailingZerosCount);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<uint8_t, 4> zeroCount = {0,
|
||||
trailingZerosStoredInCountSeven,
|
||||
trailingZerosStoredInCountEightSmall,
|
||||
trailingZerosStoredInCountEightLarge};
|
||||
|
||||
// Check if the amount of bits needed is more than we can store using all selector combinations.
|
||||
if ((bitCountWithoutLeadingZeros > kDataBits[kBaseSelector]) &&
|
||||
(meaningfulValueBitsStoredWithSeven + kTrailingZeroBitSize[kSevenSelector] >
|
||||
kDataBits[kSevenSelector]) &&
|
||||
(meaningfulValueBitsStoredWithEightSmall + kTrailingZeroBitSize[kEightSelectorSmall] >
|
||||
kDataBits[kEightSelectorSmall]) &&
|
||||
(meaningfulValueBitsStoredWithEightLarge + kTrailingZeroBitSize[kEightSelectorLarge] >
|
||||
kDataBits[kEightSelectorLarge])) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return PendingValue{value,
|
||||
{bitCountWithoutLeadingZeros,
|
||||
meaningfulValueBitsStoredWithSeven,
|
||||
meaningfulValueBitsStoredWithEightSmall,
|
||||
meaningfulValueBitsStoredWithEightLarge},
|
||||
zeroCount};
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
bool Simple8bBuilder<T, Allocator>::_appendValue(T value, bool tryRle, F&& writeFn) {
|
||||
auto pendingValue = _calculatePendingValue(value);
|
||||
if (!pendingValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have a valid selector for the current word. This method update the global
|
||||
// isSelectorValid to avoid redundant computation.
|
||||
if (_doesIntegerFitInCurrentWord(*pendingValue)) {
|
||||
// If the integer fits in the current word, add it.
|
||||
_pendingValues.push_back(*pendingValue);
|
||||
_updateSimple8bCurrentState(*pendingValue);
|
||||
} else {
|
||||
// If the integer does not fit in the current word, convert the integers into simple8b
|
||||
// word(s) with no unused buckets until the new value can be added to _pendingValues. Then
|
||||
// add the Simple8b word(s) to the buffer. Finally add the new integer and update any global
|
||||
// variables. We add based on the lastSelector that was valid where priority ordering is the
|
||||
// following: base, seven, eightSmall, eightLarge. Store pending last value for RLE.
|
||||
PendingValue lastPendingValue = _pendingValues.back();
|
||||
do {
|
||||
uint64_t simple8bWord = _encodeLargestPossibleWord(_lastValidExtensionType);
|
||||
writeFn(simple8bWord);
|
||||
} while (!(_doesIntegerFitInCurrentWord(*pendingValue)));
|
||||
|
||||
if (tryRle && _pendingValues.empty() && lastPendingValue.val == value) {
|
||||
// There are no more words in _pendingValues and the last element of the last Simple8b
|
||||
// word is the same as the new value. Therefore, start RLE.
|
||||
_rleCount = 1;
|
||||
_lastValueInPrevWord = lastPendingValue.val;
|
||||
// RLE does not use selectors, make sure they are reset.
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
} else {
|
||||
_pendingValues.push_back(*pendingValue);
|
||||
_updateSimple8bCurrentState(*pendingValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
void Simple8bBuilder<T, Allocator>::_appendSkip(bool tryRle, F&& writeFn) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
if (!_pendingValues.empty()) {
|
||||
bool isLastValueSkip = _pendingValues.back().isSkip();
|
||||
|
||||
// There is never a case where we need to write more than one Simple8b wrod
|
||||
// because we only need 1 bit for skip
|
||||
if (!_doesIntegerFitInCurrentWord({boost::none, kMinDataBits, {0, 0, 0, 0}})) {
|
||||
// Form simple8b word if skip can not fit with last selector
|
||||
uint64_t simple8bWord = _encodeLargestPossibleWord(_lastValidExtensionType);
|
||||
writeFn(simple8bWord);
|
||||
_lastValidExtensionType = kBaseSelector;
|
||||
}
|
||||
|
||||
if (_pendingValues.empty() && isLastValueSkip && tryRle) {
|
||||
// It is possible to start rle
|
||||
_rleCount = 1;
|
||||
_lastValueInPrevWord = boost::none;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Push true into skip and the dummy value, 0, into currNum. We use the dummy value, 0 because
|
||||
// it takes 1 bit and it will not affect our global curr bit length calculations.
|
||||
_pendingValues.push_back({boost::none, {0, 0, 0, 0}, {0, 0, 0, 0}});
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
void Simple8bBuilder<T, Allocator>::_handleRleTermination(F&& writeFn) {
|
||||
// Try to create a RLE Simple8b word.
|
||||
_appendRleEncoding(writeFn);
|
||||
// Add any values that could not be encoded in RLE.
|
||||
while (_rleCount > 0) {
|
||||
if (!_lastValueInPrevWord.has_value()) {
|
||||
_appendSkip(false /* tryRle */, writeFn);
|
||||
} else {
|
||||
_appendValue(_lastValueInPrevWord.value(), false, writeFn);
|
||||
}
|
||||
--_rleCount;
|
||||
}
|
||||
|
||||
// Reset last for RLE and which selectors are possible to use for next word
|
||||
_lastValueInPrevWord = 0;
|
||||
// Ensure that selector state is reset if we have nothing in pending. This will happen when RLE
|
||||
// has been setup but nothing compatible was added later.
|
||||
if (_pendingValues.empty()) {
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
void Simple8bBuilder<T, Allocator>::_appendRleEncoding(F&& writeFn) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// This encodes a value using rle. The selector is set as 15 and the count is added in the next
|
||||
// 4 bits. The value is the previous value stored by simple8b or 0 if no previous value was
|
||||
// stored.
|
||||
auto createRleEncoding = [this, &writeFn](uint8_t count) {
|
||||
uint64_t rleEncoding = kRleSelector;
|
||||
// We will store (count - 1) during encoding and execute (count + 1) during decoding.
|
||||
rleEncoding |= (count - 1) << kSelectorBits;
|
||||
_rleCount -= kRleMultiplier * count;
|
||||
writeFn(rleEncoding);
|
||||
};
|
||||
|
||||
uint32_t count = _rleCount / kRleMultiplier;
|
||||
// Check to make sure count is big enough for RLE encoding
|
||||
if (count >= 1) {
|
||||
while (count > kMaxRleCount) {
|
||||
// If one RLE word is insufficient use multiple RLE words.
|
||||
createRleEncoding(kMaxRleCount);
|
||||
count -= kMaxRleCount;
|
||||
}
|
||||
createRleEncoding(count);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::rlePossible() const {
|
||||
return _pendingValues.empty() || _rleCount != 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::_doesIntegerFitInCurrentWord(const PendingValue& value) {
|
||||
bool fitsInCurrentWord = false;
|
||||
for (uint8_t i = 0; i < kNumOfSelectorTypes; ++i) {
|
||||
if (isSelectorPossible[i]) {
|
||||
fitsInCurrentWord =
|
||||
fitsInCurrentWord || _doesIntegerFitInCurrentWordWithGivenSelectorType(value, i);
|
||||
}
|
||||
// Stop loop early if we find a valid selector.
|
||||
if (fitsInCurrentWord)
|
||||
return fitsInCurrentWord;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::_doesIntegerFitInCurrentWordWithGivenSelectorType(
|
||||
const PendingValue& value, uint8_t extensionType) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
uint64_t numBitsWithValue =
|
||||
(std::max(_currMaxBitLen[extensionType], value.bitCount[extensionType]) +
|
||||
kTrailingZeroBitSize[extensionType]) *
|
||||
(_pendingValues.size() + 1);
|
||||
// If the numBitswithValue is greater than max bits or we cannot fit the trailingZeros we update
|
||||
// this selector as false and return false. Special case for baseSelector where we never add
|
||||
// trailingZeros so we always pass the zeros comparison.
|
||||
if (kDataBits[extensionType] < numBitsWithValue) {
|
||||
isSelectorPossible[extensionType] = false;
|
||||
return false;
|
||||
}
|
||||
// Update so we remember the last validExtensionType when its time to encode a word
|
||||
_lastValidExtensionType = extensionType;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
int64_t Simple8bBuilder<T, Allocator>::_encodeLargestPossibleWord(uint8_t extensionType) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// Since this is always called right after _doesIntegerFitInCurrentWord fails for the first
|
||||
// time, we know all values in _pendingValues fits in the slots for the selector that can store
|
||||
// this many values. Find the smallest selector that doesn't leave any unused slots.
|
||||
uint8_t selector = getSelectorIndex(_pendingValues.size(), extensionType);
|
||||
uint8_t integersCoded = kIntsStoreForSelector[extensionType][selector];
|
||||
uint64_t encodedWord;
|
||||
switch (extensionType) {
|
||||
case kEightSelectorSmall:
|
||||
encodedWord = _encode(EightSelectorSmallEncodeFunctor(), selector, extensionType);
|
||||
break;
|
||||
case kEightSelectorLarge:
|
||||
encodedWord = _encode(EightSelectorLargeEncodeFunctor(), selector, extensionType);
|
||||
break;
|
||||
case kSevenSelector:
|
||||
encodedWord = _encode(SevenSelectorEncodeFunctor(), selector, extensionType);
|
||||
break;
|
||||
default:
|
||||
encodedWord = _encode(BaseSelectorEncodeFunctor(), selector, extensionType);
|
||||
}
|
||||
|
||||
// While we erase from the front of a vector the number of remaining elements is normally small.
|
||||
// Vector is a less complex data structure than a deque and normally has better performance when
|
||||
// the number of elements in it is low.
|
||||
_pendingValues.erase(_pendingValues.begin(), _pendingValues.begin() + integersCoded);
|
||||
_currMaxBitLen = kMinDataBits;
|
||||
for (const auto& val : _pendingValues) {
|
||||
_updateSimple8bCurrentState(val);
|
||||
}
|
||||
// Reset which selectors are possible to use for next word
|
||||
isSelectorPossible.fill(true);
|
||||
return encodedWord;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <typename Func>
|
||||
uint64_t Simple8bBuilder<T, Allocator>::_encode(Func func,
|
||||
uint8_t selectorIdx,
|
||||
uint8_t extensionType) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
uint8_t baseSelector = kExtensionToBaseSelector[extensionType][selectorIdx];
|
||||
uint8_t bitShiftExtension = kBaseSelectorToShiftSize[baseSelector];
|
||||
uint64_t encodedWord = baseSelector;
|
||||
uint8_t bitsPerInteger = kBitsPerIntForSelector[extensionType][selectorIdx];
|
||||
uint8_t integersCoded = kIntsStoreForSelector[extensionType][selectorIdx];
|
||||
uint64_t unshiftedMask = kDecodeMask[extensionType][selectorIdx];
|
||||
uint8_t bitsForTrailingZeros = kTrailingZeroBitSize[extensionType];
|
||||
for (uint8_t i = 0; i < integersCoded; ++i) {
|
||||
uint8_t shiftSize =
|
||||
(bitsPerInteger + bitsForTrailingZeros) * i + kSelectorBits + bitShiftExtension;
|
||||
uint64_t currEncodedWord;
|
||||
if (_pendingValues[i].isSkip()) {
|
||||
currEncodedWord = unshiftedMask;
|
||||
} else {
|
||||
currEncodedWord = func(_pendingValues[i]);
|
||||
}
|
||||
encodedWord |= currEncodedWord << shiftSize;
|
||||
}
|
||||
if (extensionType != kBaseSelector) {
|
||||
encodedWord |= (uint64_t(selectorIdx) << kSelectorBits);
|
||||
}
|
||||
return encodedWord;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::_updateSimple8bCurrentState(const PendingValue& val) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
for (uint8_t i = 0; i < kNumOfSelectorTypes; ++i) {
|
||||
_currMaxBitLen[i] = std::max(_currMaxBitLen[i], val.bitCount[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
typename Simple8bBuilder<T, Allocator>::PendingIterator Simple8bBuilder<T, Allocator>::begin()
|
||||
const {
|
||||
return {_pendingValues.begin(), _pendingValues.begin(), _lastValueInPrevWord, _rleCount};
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
typename Simple8bBuilder<T, Allocator>::PendingIterator Simple8bBuilder<T, Allocator>::end() const {
|
||||
return {_pendingValues.begin(), _pendingValues.end(), _lastValueInPrevWord, 0};
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>
|
||||
Simple8bBuilder<T, Allocator>::rbegin() const {
|
||||
return std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>(end());
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>
|
||||
Simple8bBuilder<T, Allocator>::rend() const {
|
||||
return std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>(begin());
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::isInternalStateIdentical(
|
||||
const Simple8bBuilder<T, Allocator>& other) const {
|
||||
// Verifies the pending values
|
||||
if (!std::equal(begin(), end(), other.begin(), other.end())) {
|
||||
return false;
|
||||
}
|
||||
if (_rleCount != other._rleCount) {
|
||||
return false;
|
||||
}
|
||||
if (_lastValueInPrevWord != other._lastValueInPrevWord) {
|
||||
return false;
|
||||
}
|
||||
if (_currMaxBitLen != other._currMaxBitLen) {
|
||||
return false;
|
||||
}
|
||||
if (_currTrailingZerosCount != other._currTrailingZerosCount) {
|
||||
return false;
|
||||
}
|
||||
if (_lastValidExtensionType != other._lastValidExtensionType) {
|
||||
return false;
|
||||
}
|
||||
if (isSelectorPossible != other.isSelectorPossible) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace mongo
|
||||
|
|
|
|||
|
|
@ -1,665 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2021-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/bson/column/simple8b_builder.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/none.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include "mongo/bson/column/simple8b_helpers.h"
|
||||
|
||||
namespace mongo {
|
||||
// This is called in _encode while iterating through _pendingValues. For the base selector, we just
|
||||
// return val. Contains unsed vars in order to seamlessly integrate with seven and eight selector
|
||||
// extensions.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::BaseSelectorEncodeFunctor {
|
||||
uint64_t operator()(const PendingValue& value) {
|
||||
return static_cast<uint64_t>(value.value());
|
||||
};
|
||||
};
|
||||
|
||||
// This is called in _encode while iterating through _pendingValues. It creates part of a simple8b
|
||||
// word according to the specifications of the sevenSelector extension. This value is then appended
|
||||
// to the full simple8b word in _encode.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::SevenSelectorEncodeFunctor {
|
||||
uint64_t operator()(const PendingValue& value) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
uint8_t trailingZeros = value.trailingZerosCount[kSevenSelector];
|
||||
uint64_t currWord = trailingZeros;
|
||||
// We do two shifts here to account for the case where trailingZeros is > kTrailingZero bit
|
||||
// size. If we subtracted this could lead to shift by a negative value which is undefined.
|
||||
currWord |= static_cast<uint64_t>((value.value() >> trailingZeros)
|
||||
<< kTrailingZeroBitSize[kSevenSelector]);
|
||||
return currWord;
|
||||
};
|
||||
};
|
||||
|
||||
// This is a helper functor that is extended by the EightSelectorSmall and EightSelectorLarge encode
|
||||
// functors. It provides the logic for encoding with the eight selector where the extension type is
|
||||
// designated by the inheritance in the EightSelectorSmall and EightSelectorLarge functors.
|
||||
template <typename T, class Allocator>
|
||||
template <uint8_t ExtensionType>
|
||||
struct Simple8bBuilder<T, Allocator>::EightSelectorEncodeFunctor {
|
||||
uint64_t operator()(const PendingValue& value) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// integer division. We have a nibble shift of size 4
|
||||
uint8_t trailingZeros = value.trailingZerosCount[ExtensionType] / kNibbleShiftSize;
|
||||
uint64_t currWord = trailingZeros;
|
||||
// Shift to remove trailing zeros * 4 and then shift over for the 4 bits to hold
|
||||
// the trailingZerosCount
|
||||
currWord |= static_cast<uint64_t>((value.value() >> (trailingZeros * kNibbleShiftSize))
|
||||
<< kTrailingZeroBitSize[ExtensionType]);
|
||||
return currWord;
|
||||
}
|
||||
};
|
||||
|
||||
// This is called in _encode while iterating through _pendingValues. It creates part of a simple8b
|
||||
// word according to the specifications of the eightSelectorSmall extension. This value is then
|
||||
// appended to the full simple8b word in _encode.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::EightSelectorSmallEncodeFunctor
|
||||
: public EightSelectorEncodeFunctor<simple8b_internal::kEightSelectorSmall> {};
|
||||
|
||||
// This is called in _encode while iterating through _pendingValues. It creates part of a simple8b
|
||||
// word according to the specifications of the eightSelectorLarge extension. This value is then
|
||||
// appended to the full simple8b word in _encode.
|
||||
template <typename T, class Allocator>
|
||||
struct Simple8bBuilder<T, Allocator>::EightSelectorLargeEncodeFunctor
|
||||
: public EightSelectorEncodeFunctor<simple8b_internal::kEightSelectorLarge> {};
|
||||
|
||||
// Base Constructor for PendingValue
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::PendingValue::PendingValue(
|
||||
boost::optional<T> val,
|
||||
std::array<uint8_t, kNumOfSelectorTypes> bitCount,
|
||||
std::array<uint8_t, kNumOfSelectorTypes> trailingZerosCount)
|
||||
: val(val), bitCount(bitCount), trailingZerosCount(trailingZerosCount){};
|
||||
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::PendingIterator::PendingIterator(
|
||||
typename std::vector<PendingValue, PendingValueAllocator>::const_iterator beginning,
|
||||
typename std::vector<PendingValue, PendingValueAllocator>::const_iterator it,
|
||||
reference rleValue,
|
||||
uint32_t rleCount)
|
||||
: _begin(beginning), _it(it), _rleValue(rleValue), _rleCount(rleCount) {}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator->() const -> pointer {
|
||||
return &operator*();
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator*() const -> reference {
|
||||
if (_rleCount > 0)
|
||||
return _rleValue;
|
||||
|
||||
return _it->val;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator++() -> PendingIterator& {
|
||||
if (_rleCount > 0) {
|
||||
--_rleCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
++_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator++(int) -> PendingIterator {
|
||||
auto ret = *this;
|
||||
++(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator--() -> PendingIterator& {
|
||||
if (_rleCount > 0 || _it == _begin) {
|
||||
++_rleCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
--_it;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
auto Simple8bBuilder<T, Allocator>::PendingIterator::operator--(int) -> PendingIterator {
|
||||
auto ret = *this;
|
||||
--(*this);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::PendingIterator::operator==(
|
||||
const Simple8bBuilder<T, Allocator>::PendingIterator& rhs) const {
|
||||
return _it == rhs._it && _rleCount == rhs._rleCount;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::PendingIterator::operator!=(
|
||||
const Simple8bBuilder<T, Allocator>::PendingIterator& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::Simple8bBuilder(Allocator allocator) : _pendingValues(allocator) {}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
Simple8bBuilder<T, Allocator>::~Simple8bBuilder() = default;
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
void Simple8bBuilder<T, Allocator>::prefillWithSkips(size_t numSkips, F &&writeFn) {
|
||||
constexpr size_t kMaxSkipsInBlock = 60;
|
||||
constexpr uint64_t kAllSkipsBlock = 0xFFFFFFFFFFFFFFF1; // A simple8b block that contains 60 skips
|
||||
|
||||
// Do checks once, and then process all skips in bulk
|
||||
uassert(8575002, "prefillWithSkips() called after other appends", _rleCount == 0 && _pendingValues.empty() && _lastValidExtensionType == 0);
|
||||
|
||||
if (numSkips > kMaxSkipsInBlock) {
|
||||
// Handle case with initialized _rleCount (this should be the common case)
|
||||
writeFn(kAllSkipsBlock);
|
||||
_rleCount = numSkips - kMaxSkipsInBlock;
|
||||
_lastValueInPrevWord = boost::none;
|
||||
} else {
|
||||
// Insufficient skips to start an _rleCount, just default to appending them
|
||||
// individually (we can skip rle checks)
|
||||
for (size_t i = 0; i < numSkips; ++i)
|
||||
_appendSkip(true, writeFn);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
bool Simple8bBuilder<T, Allocator>::append(T value, F&& writeFn) {
|
||||
if (rlePossible()) {
|
||||
if (_lastValueInPrevWord == value) {
|
||||
++_rleCount;
|
||||
// RLE does not use selectors, make sure they are reset.
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
return true;
|
||||
}
|
||||
_handleRleTermination(writeFn);
|
||||
}
|
||||
|
||||
return _appendValue(value, true, writeFn);
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
void Simple8bBuilder<T, Allocator>::skip(F&& writeFn) {
|
||||
if (rlePossible()) {
|
||||
if (!_lastValueInPrevWord.has_value()) {
|
||||
++_rleCount;
|
||||
// RLE does not use selectors, make sure they are reset.
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
return;
|
||||
}
|
||||
_handleRleTermination(writeFn);
|
||||
}
|
||||
_appendSkip(true /* tryRle */, writeFn);
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
requires Simple8bBlockWriter<F>
|
||||
void Simple8bBuilder<T, Allocator>::flush(F&& writeFn) {
|
||||
// Flush repeating integers that have been kept for RLE.
|
||||
_handleRleTermination(writeFn);
|
||||
// Flush buffered values in _pendingValues.
|
||||
if (!_pendingValues.empty()) {
|
||||
// always flush with the most recent valid selector. This value is the baseSelector if we
|
||||
// have not have a valid selector yet.
|
||||
do {
|
||||
uint64_t simple8bWord = _encodeLargestPossibleWord(_lastValidExtensionType);
|
||||
writeFn(simple8bWord);
|
||||
} while (!_pendingValues.empty());
|
||||
|
||||
// There are no more words in _pendingValues and RLE is possible.
|
||||
// However the _rleCount is 0 because we have not read any of the values in the next word.
|
||||
_rleCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::setLastForRLE(boost::optional<T> val) {
|
||||
_lastValueInPrevWord = val;
|
||||
if (val) {
|
||||
auto pendingValue = _calculatePendingValue(*val);
|
||||
invariant(pendingValue);
|
||||
invariant(_doesIntegerFitInCurrentWord(*pendingValue));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::resetLastForRLEIfNeeded() {
|
||||
if (!rlePossible()) {
|
||||
_lastValueInPrevWord = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::initializeRLEFrom(const Simple8bBuilder<T, Allocator>& other) {
|
||||
if (other.rlePossible()) {
|
||||
_lastValueInPrevWord = other._lastValueInPrevWord;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
boost::optional<typename Simple8bBuilder<T, Allocator>::PendingValue>
|
||||
Simple8bBuilder<T, Allocator>::_calculatePendingValue(T value) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// Early exit if we try to store max value. They are not handled when counting zeros.
|
||||
if (value == std::numeric_limits<T>::max())
|
||||
return boost::none;
|
||||
|
||||
uint8_t trailingZerosCount = countTrailingZerosWithZero(value);
|
||||
// Initially set every selector as invalid.
|
||||
uint8_t bitCountWithoutLeadingZeros = countBitsWithoutLeadingZeros(value);
|
||||
uint8_t trailingZerosStoredInCountSeven =
|
||||
(std::min(trailingZerosCount, kTrailingZerosMaxCount[kSevenSelector]));
|
||||
uint8_t meaningfulValueBitsStoredWithSeven =
|
||||
bitCountWithoutLeadingZeros - trailingZerosStoredInCountSeven;
|
||||
// We use integer division to ensure that a multiple of 4 is stored in
|
||||
// trailingZerosStoredInCount when we have the nibble shift.
|
||||
uint8_t trailingZerosStoredInCountEightSmall =
|
||||
(std::min(trailingZerosCount, kTrailingZerosMaxCount[kEightSelectorSmall]) /
|
||||
kNibbleShiftSize) *
|
||||
kNibbleShiftSize;
|
||||
uint8_t meaningfulValueBitsStoredWithEightSmall =
|
||||
bitCountWithoutLeadingZeros - trailingZerosStoredInCountEightSmall;
|
||||
// We use integer division to ensure that a multiple of 4 is stored in
|
||||
// trailingZerosStoredInCount when we have the nibble shift.
|
||||
uint8_t trailingZerosStoredInCountEightLarge =
|
||||
(std::min(trailingZerosCount, kTrailingZerosMaxCount[kEightSelectorLarge]) /
|
||||
kNibbleShiftSize) *
|
||||
kNibbleShiftSize;
|
||||
uint8_t meaningfulValueBitsStoredWithEightLarge =
|
||||
bitCountWithoutLeadingZeros - trailingZerosStoredInCountEightLarge;
|
||||
|
||||
// Edge cases where we have the number of trailing zeros bits as all ones and we need to add a
|
||||
// padded zero to the meaningful bits to avoid confilicts with skip storage. Otherwise, we can
|
||||
// reuse the bitCountWithoutLeadingZeros already calculated above.
|
||||
if (trailingZerosCount == kTrailingZerosMaxCount[kSevenSelector]) {
|
||||
meaningfulValueBitsStoredWithSeven =
|
||||
countBitsWithoutLeadingZeros(value >> trailingZerosCount);
|
||||
} else if (trailingZerosCount == kTrailingZerosMaxCount[kEightSelectorSmall]) {
|
||||
meaningfulValueBitsStoredWithEightSmall =
|
||||
countBitsWithoutLeadingZeros(value >> trailingZerosCount);
|
||||
}
|
||||
|
||||
// This case is specifically for 128 bit types where we have 124 zeros or max zeros
|
||||
// count. We do not need to even check this for 64 bit types
|
||||
if constexpr (std::is_same<T, uint128_t>::value) {
|
||||
if (trailingZerosCount == kTrailingZerosMaxCount[kEightSelectorLarge]) {
|
||||
meaningfulValueBitsStoredWithEightLarge =
|
||||
countBitsWithoutLeadingZeros(value >> trailingZerosCount);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<uint8_t, 4> zeroCount = {0,
|
||||
trailingZerosStoredInCountSeven,
|
||||
trailingZerosStoredInCountEightSmall,
|
||||
trailingZerosStoredInCountEightLarge};
|
||||
|
||||
// Check if the amount of bits needed is more than we can store using all selector combinations.
|
||||
if ((bitCountWithoutLeadingZeros > kDataBits[kBaseSelector]) &&
|
||||
(meaningfulValueBitsStoredWithSeven + kTrailingZeroBitSize[kSevenSelector] >
|
||||
kDataBits[kSevenSelector]) &&
|
||||
(meaningfulValueBitsStoredWithEightSmall + kTrailingZeroBitSize[kEightSelectorSmall] >
|
||||
kDataBits[kEightSelectorSmall]) &&
|
||||
(meaningfulValueBitsStoredWithEightLarge + kTrailingZeroBitSize[kEightSelectorLarge] >
|
||||
kDataBits[kEightSelectorLarge])) {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return PendingValue{value,
|
||||
{bitCountWithoutLeadingZeros,
|
||||
meaningfulValueBitsStoredWithSeven,
|
||||
meaningfulValueBitsStoredWithEightSmall,
|
||||
meaningfulValueBitsStoredWithEightLarge},
|
||||
zeroCount};
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
bool Simple8bBuilder<T, Allocator>::_appendValue(T value, bool tryRle, F&& writeFn) {
|
||||
auto pendingValue = _calculatePendingValue(value);
|
||||
if (!pendingValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have a valid selector for the current word. This method update the global
|
||||
// isSelectorValid to avoid redundant computation.
|
||||
if (_doesIntegerFitInCurrentWord(*pendingValue)) {
|
||||
// If the integer fits in the current word, add it.
|
||||
_pendingValues.push_back(*pendingValue);
|
||||
_updateSimple8bCurrentState(*pendingValue);
|
||||
} else {
|
||||
// If the integer does not fit in the current word, convert the integers into simple8b
|
||||
// word(s) with no unused buckets until the new value can be added to _pendingValues. Then
|
||||
// add the Simple8b word(s) to the buffer. Finally add the new integer and update any global
|
||||
// variables. We add based on the lastSelector that was valid where priority ordering is the
|
||||
// following: base, seven, eightSmall, eightLarge. Store pending last value for RLE.
|
||||
PendingValue lastPendingValue = _pendingValues.back();
|
||||
do {
|
||||
uint64_t simple8bWord = _encodeLargestPossibleWord(_lastValidExtensionType);
|
||||
writeFn(simple8bWord);
|
||||
} while (!(_doesIntegerFitInCurrentWord(*pendingValue)));
|
||||
|
||||
if (tryRle && _pendingValues.empty() && lastPendingValue.val == value) {
|
||||
// There are no more words in _pendingValues and the last element of the last Simple8b
|
||||
// word is the same as the new value. Therefore, start RLE.
|
||||
_rleCount = 1;
|
||||
_lastValueInPrevWord = lastPendingValue.val;
|
||||
// RLE does not use selectors, make sure they are reset.
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
} else {
|
||||
_pendingValues.push_back(*pendingValue);
|
||||
_updateSimple8bCurrentState(*pendingValue);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
void Simple8bBuilder<T, Allocator>::_appendSkip(bool tryRle, F&& writeFn) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
if (!_pendingValues.empty()) {
|
||||
bool isLastValueSkip = _pendingValues.back().isSkip();
|
||||
|
||||
// There is never a case where we need to write more than one Simple8b wrod
|
||||
// because we only need 1 bit for skip
|
||||
if (!_doesIntegerFitInCurrentWord({boost::none, kMinDataBits, {0, 0, 0, 0}})) {
|
||||
// Form simple8b word if skip can not fit with last selector
|
||||
uint64_t simple8bWord = _encodeLargestPossibleWord(_lastValidExtensionType);
|
||||
writeFn(simple8bWord);
|
||||
_lastValidExtensionType = kBaseSelector;
|
||||
}
|
||||
|
||||
if (_pendingValues.empty() && isLastValueSkip && tryRle) {
|
||||
// It is possible to start rle
|
||||
_rleCount = 1;
|
||||
_lastValueInPrevWord = boost::none;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Push true into skip and the dummy value, 0, into currNum. We use the dummy value, 0 because
|
||||
// it takes 1 bit and it will not affect our global curr bit length calculations.
|
||||
_pendingValues.push_back({boost::none, {0, 0, 0, 0}, {0, 0, 0, 0}});
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
void Simple8bBuilder<T, Allocator>::_handleRleTermination(F&& writeFn) {
|
||||
// Try to create a RLE Simple8b word.
|
||||
_appendRleEncoding(writeFn);
|
||||
// Add any values that could not be encoded in RLE.
|
||||
while (_rleCount > 0) {
|
||||
if (!_lastValueInPrevWord.has_value()) {
|
||||
_appendSkip(false /* tryRle */, writeFn);
|
||||
} else {
|
||||
_appendValue(_lastValueInPrevWord.value(), false, writeFn);
|
||||
}
|
||||
--_rleCount;
|
||||
}
|
||||
|
||||
// Reset last for RLE and which selectors are possible to use for next word
|
||||
_lastValueInPrevWord = 0;
|
||||
// Ensure that selector state is reset if we have nothing in pending. This will happen when RLE
|
||||
// has been setup but nothing compatible was added later.
|
||||
if (_pendingValues.empty()) {
|
||||
_lastValidExtensionType = 0;
|
||||
isSelectorPossible.fill(true);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <class F>
|
||||
void Simple8bBuilder<T, Allocator>::_appendRleEncoding(F&& writeFn) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// This encodes a value using rle. The selector is set as 15 and the count is added in the next
|
||||
// 4 bits. The value is the previous value stored by simple8b or 0 if no previous value was
|
||||
// stored.
|
||||
auto createRleEncoding = [this, &writeFn](uint8_t count) {
|
||||
uint64_t rleEncoding = kRleSelector;
|
||||
// We will store (count - 1) during encoding and execute (count + 1) during decoding.
|
||||
rleEncoding |= (count - 1) << kSelectorBits;
|
||||
_rleCount -= kRleMultiplier * count;
|
||||
writeFn(rleEncoding);
|
||||
};
|
||||
|
||||
uint32_t count = _rleCount / kRleMultiplier;
|
||||
// Check to make sure count is big enough for RLE encoding
|
||||
if (count >= 1) {
|
||||
while (count > kMaxRleCount) {
|
||||
// If one RLE word is insufficient use multiple RLE words.
|
||||
createRleEncoding(kMaxRleCount);
|
||||
count -= kMaxRleCount;
|
||||
}
|
||||
createRleEncoding(count);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::rlePossible() const {
|
||||
return _pendingValues.empty() || _rleCount != 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::_doesIntegerFitInCurrentWord(const PendingValue& value) {
|
||||
bool fitsInCurrentWord = false;
|
||||
for (uint8_t i = 0; i < kNumOfSelectorTypes; ++i) {
|
||||
if (isSelectorPossible[i]) {
|
||||
fitsInCurrentWord =
|
||||
fitsInCurrentWord || _doesIntegerFitInCurrentWordWithGivenSelectorType(value, i);
|
||||
}
|
||||
// Stop loop early if we find a valid selector.
|
||||
if (fitsInCurrentWord)
|
||||
return fitsInCurrentWord;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::_doesIntegerFitInCurrentWordWithGivenSelectorType(
|
||||
const PendingValue& value, uint8_t extensionType) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
uint64_t numBitsWithValue =
|
||||
(std::max(_currMaxBitLen[extensionType], value.bitCount[extensionType]) +
|
||||
kTrailingZeroBitSize[extensionType]) *
|
||||
(_pendingValues.size() + 1);
|
||||
// If the numBitswithValue is greater than max bits or we cannot fit the trailingZeros we update
|
||||
// this selector as false and return false. Special case for baseSelector where we never add
|
||||
// trailingZeros so we always pass the zeros comparison.
|
||||
if (kDataBits[extensionType] < numBitsWithValue) {
|
||||
isSelectorPossible[extensionType] = false;
|
||||
return false;
|
||||
}
|
||||
// Update so we remember the last validExtensionType when its time to encode a word
|
||||
_lastValidExtensionType = extensionType;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
int64_t Simple8bBuilder<T, Allocator>::_encodeLargestPossibleWord(uint8_t extensionType) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
// Since this is always called right after _doesIntegerFitInCurrentWord fails for the first
|
||||
// time, we know all values in _pendingValues fits in the slots for the selector that can store
|
||||
// this many values. Find the smallest selector that doesn't leave any unused slots.
|
||||
uint8_t selector = getSelectorIndex(_pendingValues.size(), extensionType);
|
||||
uint8_t integersCoded = kIntsStoreForSelector[extensionType][selector];
|
||||
uint64_t encodedWord;
|
||||
switch (extensionType) {
|
||||
case kEightSelectorSmall:
|
||||
encodedWord = _encode(EightSelectorSmallEncodeFunctor(), selector, extensionType);
|
||||
break;
|
||||
case kEightSelectorLarge:
|
||||
encodedWord = _encode(EightSelectorLargeEncodeFunctor(), selector, extensionType);
|
||||
break;
|
||||
case kSevenSelector:
|
||||
encodedWord = _encode(SevenSelectorEncodeFunctor(), selector, extensionType);
|
||||
break;
|
||||
default:
|
||||
encodedWord = _encode(BaseSelectorEncodeFunctor(), selector, extensionType);
|
||||
}
|
||||
|
||||
// While we erase from the front of a vector the number of remaining elements is normally small.
|
||||
// Vector is a less complex data structure than a deque and normally has better performance when
|
||||
// the number of elements in it is low.
|
||||
_pendingValues.erase(_pendingValues.begin(), _pendingValues.begin() + integersCoded);
|
||||
_currMaxBitLen = kMinDataBits;
|
||||
for (const auto& val : _pendingValues) {
|
||||
_updateSimple8bCurrentState(val);
|
||||
}
|
||||
// Reset which selectors are possible to use for next word
|
||||
isSelectorPossible.fill(true);
|
||||
return encodedWord;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
template <typename Func>
|
||||
uint64_t Simple8bBuilder<T, Allocator>::_encode(Func func,
|
||||
uint8_t selectorIdx,
|
||||
uint8_t extensionType) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
uint8_t baseSelector = kExtensionToBaseSelector[extensionType][selectorIdx];
|
||||
uint8_t bitShiftExtension = kBaseSelectorToShiftSize[baseSelector];
|
||||
uint64_t encodedWord = baseSelector;
|
||||
uint8_t bitsPerInteger = kBitsPerIntForSelector[extensionType][selectorIdx];
|
||||
uint8_t integersCoded = kIntsStoreForSelector[extensionType][selectorIdx];
|
||||
uint64_t unshiftedMask = kDecodeMask[extensionType][selectorIdx];
|
||||
uint8_t bitsForTrailingZeros = kTrailingZeroBitSize[extensionType];
|
||||
for (uint8_t i = 0; i < integersCoded; ++i) {
|
||||
uint8_t shiftSize =
|
||||
(bitsPerInteger + bitsForTrailingZeros) * i + kSelectorBits + bitShiftExtension;
|
||||
uint64_t currEncodedWord;
|
||||
if (_pendingValues[i].isSkip()) {
|
||||
currEncodedWord = unshiftedMask;
|
||||
} else {
|
||||
currEncodedWord = func(_pendingValues[i]);
|
||||
}
|
||||
encodedWord |= currEncodedWord << shiftSize;
|
||||
}
|
||||
if (extensionType != kBaseSelector) {
|
||||
encodedWord |= (uint64_t(selectorIdx) << kSelectorBits);
|
||||
}
|
||||
return encodedWord;
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
void Simple8bBuilder<T, Allocator>::_updateSimple8bCurrentState(const PendingValue& val) {
|
||||
using namespace simple8b_internal;
|
||||
|
||||
for (uint8_t i = 0; i < kNumOfSelectorTypes; ++i) {
|
||||
_currMaxBitLen[i] = std::max(_currMaxBitLen[i], val.bitCount[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
typename Simple8bBuilder<T, Allocator>::PendingIterator Simple8bBuilder<T, Allocator>::begin()
|
||||
const {
|
||||
return {_pendingValues.begin(), _pendingValues.begin(), _lastValueInPrevWord, _rleCount};
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
typename Simple8bBuilder<T, Allocator>::PendingIterator Simple8bBuilder<T, Allocator>::end() const {
|
||||
return {_pendingValues.begin(), _pendingValues.end(), _lastValueInPrevWord, 0};
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>
|
||||
Simple8bBuilder<T, Allocator>::rbegin() const {
|
||||
return std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>(end());
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>
|
||||
Simple8bBuilder<T, Allocator>::rend() const {
|
||||
return std::reverse_iterator<typename Simple8bBuilder<T, Allocator>::PendingIterator>(begin());
|
||||
}
|
||||
|
||||
template <typename T, class Allocator>
|
||||
bool Simple8bBuilder<T, Allocator>::isInternalStateIdentical(
|
||||
const Simple8bBuilder<T, Allocator>& other) const {
|
||||
// Verifies the pending values
|
||||
if (!std::equal(begin(), end(), other.begin(), other.end())) {
|
||||
return false;
|
||||
}
|
||||
if (_rleCount != other._rleCount) {
|
||||
return false;
|
||||
}
|
||||
if (_lastValueInPrevWord != other._lastValueInPrevWord) {
|
||||
return false;
|
||||
}
|
||||
if (_currMaxBitLen != other._currMaxBitLen) {
|
||||
return false;
|
||||
}
|
||||
if (_currTrailingZerosCount != other._currTrailingZerosCount) {
|
||||
return false;
|
||||
}
|
||||
if (_lastValidExtensionType != other._lastValidExtensionType) {
|
||||
return false;
|
||||
}
|
||||
if (isSelectorPossible != other.isSelectorPossible) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
|
|
@ -28,7 +28,6 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "mongo/client/sdam/sdam.h"
|
||||
#include "mongo/client/sdam/sdam_datatypes.h"
|
||||
#include "mongo/client/sdam/server_description.h"
|
||||
#include "mongo/client/sdam/server_selector.h"
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "mongo/db/index/index_descriptor.h"
|
||||
#include "mongo/db/index/multikey_paths.h"
|
||||
#include "mongo/db/namespace_string.h"
|
||||
#include "mongo/db/pipeline/expression_context.h"
|
||||
#include "mongo/db/query/collation/collator_interface.h"
|
||||
#include "mongo/db/record_id.h"
|
||||
#include "mongo/db/storage/ident.h"
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include "mongo/db/database_name.h"
|
||||
#include "mongo/db/dbdirectclient.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
#include "mongo/db/pipeline/aggregate_command_gen.h"
|
||||
#include "mongo/db/pipeline/aggregation_request_helper.h"
|
||||
#include "mongo/db/query/query_knobs_gen.h"
|
||||
#include "mongo/db/repl/member_state.h"
|
||||
|
|
|
|||
|
|
@ -44,23 +44,7 @@
|
|||
|
||||
namespace mongo {
|
||||
|
||||
class MultiPlanBucket;
|
||||
|
||||
/**
|
||||
* This class defines tokens that each thread must obtain to attempt multiplanning, provided that
|
||||
* the MultiPlan rate limiting is enabled. If not enough tokens are available, the thread must wait
|
||||
* until either the plan is cached or tokens become available.
|
||||
*/
|
||||
class MultiPlanTokens {
|
||||
public:
|
||||
MultiPlanTokens(size_t tokensCount, boost::intrusive_ptr<MultiPlanBucket> bucket)
|
||||
: _tokensCount(tokensCount), _bucket(bucket) {}
|
||||
~MultiPlanTokens();
|
||||
|
||||
private:
|
||||
size_t _tokensCount;
|
||||
boost::intrusive_ptr<MultiPlanBucket> _bucket;
|
||||
};
|
||||
class MultiPlanTokens;
|
||||
|
||||
/**
|
||||
* This class defines a bucket in the Multi Plan rate limiting algorithm. It controls number of
|
||||
|
|
@ -109,4 +93,20 @@ private:
|
|||
stdx::mutex _mutex;
|
||||
stdx::condition_variable_any _cv;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class defines tokens that each thread must obtain to attempt multiplanning, provided that
|
||||
* the MultiPlan rate limiting is enabled. If not enough tokens are available, the thread must wait
|
||||
* until either the plan is cached or tokens become available.
|
||||
*/
|
||||
class MultiPlanTokens {
|
||||
public:
|
||||
MultiPlanTokens(size_t tokensCount, boost::intrusive_ptr<MultiPlanBucket> bucket)
|
||||
: _tokensCount(tokensCount), _bucket(std::move(bucket)) {}
|
||||
~MultiPlanTokens();
|
||||
|
||||
private:
|
||||
size_t _tokensCount;
|
||||
boost::intrusive_ptr<MultiPlanBucket> _bucket;
|
||||
};
|
||||
} // namespace mongo
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "mongo/db/exec/sbe/sbe_block_test_helpers.h"
|
||||
#include "mongo/db/exec/sbe/sbe_unittest.h"
|
||||
#include "mongo/db/exec/sbe/values/block_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/cell_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/slot.h"
|
||||
#include "mongo/db/exec/sbe/values/value.h"
|
||||
#include "mongo/db/query/collation/collator_interface_mock.h"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "mongo/db/exec/sbe/util/debug_print.h"
|
||||
#include "mongo/db/exec/sbe/values/slot.h"
|
||||
#include "mongo/db/exec/sbe/values/value.h"
|
||||
#include "mongo/db/exec/sbe/vm/code_fragment.h"
|
||||
#include "mongo/db/exec/scoped_timer.h"
|
||||
#include "mongo/db/exec/trial_run_tracker.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
#include <memory>
|
||||
|
||||
#include "mongo/base/compare_numbers.h"
|
||||
#include "mongo/db/exec/sbe/values/cell_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/column_op.h"
|
||||
#include "mongo/db/exec/sbe/values/value.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#include <memory>
|
||||
|
||||
#include "mongo/db/exec/sbe/values/block_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/value.h"
|
||||
|
||||
namespace mongo::sbe::value {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include "mongo/db/exec/sbe/makeobj_spec.h"
|
||||
#include "mongo/db/exec/sbe/sort_spec.h"
|
||||
#include "mongo/db/exec/sbe/values/block_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/cell_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/value.h"
|
||||
#include "mongo/db/exec/sbe/values/value_printer.h"
|
||||
#include "mongo/db/fts/fts_matcher.h"
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include "mongo/db/exec/sbe/values/slot.h"
|
||||
#include "mongo/db/exec/sbe/vm/vm.h"
|
||||
#include "mongo/db/exec/sbe/vm/vm_builtin.h"
|
||||
#include "mongo/db/exec/sbe/vm/vm_instruction.h"
|
||||
#include "mongo/db/exec/sbe/vm/vm_types.h"
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "mongo/db/exec/sbe/in_list.h"
|
||||
#include "mongo/db/exec/sbe/values/arith_common.h"
|
||||
#include "mongo/db/exec/sbe/values/block_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/cell_interface.h"
|
||||
#include "mongo/db/exec/sbe/values/generic_compare.h"
|
||||
#include "mongo/db/exec/sbe/values/util.h"
|
||||
#include "mongo/db/exec/sbe/values/value.h"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "mongo/db/exec/sbe/values/arith_common.h"
|
||||
#include "mongo/db/exec/sbe/values/bson.h"
|
||||
#include "mongo/db/exec/sbe/vm/code_fragment.h"
|
||||
#include "mongo/db/exec/sbe/vm/vm.h"
|
||||
#include "mongo/db/query/collation/collation_index_key.h"
|
||||
|
||||
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@
|
|||
#include "mongo/db/exec/document_value/document.h"
|
||||
#include "mongo/db/namespace_string.h"
|
||||
#include "mongo/db/operation_context.h"
|
||||
#include "mongo/db/pipeline/aggregate_command_gen.h"
|
||||
#include "mongo/db/pipeline/exchange_spec_gen.h"
|
||||
#include "mongo/db/pipeline/legacy_runtime_constants_gen.h"
|
||||
#include "mongo/db/pipeline/plan_executor_pipeline.h"
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
#include "mongo/db/pipeline/stage_constraints.h"
|
||||
#include "mongo/db/pipeline/variables.h"
|
||||
#include "mongo/db/query/query_shape/serialization_options.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/util/string_map.h"
|
||||
|
||||
namespace mongo {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/db/pipeline/document_source_rank_fusion_inputs_gen.h"
|
||||
#include "mongo/db/pipeline/document_source_score_fusion_inputs_gen.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@
|
|||
#include "mongo/db/query/allowed_contexts.h"
|
||||
#include "mongo/db/query/datetime/date_time_support.h"
|
||||
#include "mongo/db/query/query_shape/serialization_options.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/db/query/util/named_enum.h"
|
||||
#include "mongo/db/update/pattern_cmp.h"
|
||||
#include "mongo/db/version_context.h"
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#include "mongo/db/exec/document_value/document.h"
|
||||
#include "mongo/db/exec/document_value/value.h"
|
||||
#include "mongo/db/pipeline/expression_context.h"
|
||||
#include "mongo/db/pipeline/expression_from_accumulator_quantile.h"
|
||||
#include "mongo/db/pipeline/expression_visitor.h"
|
||||
#include "mongo/db/pipeline/percentile_algo.h"
|
||||
#include "mongo/db/pipeline/percentile_algo_continuous.h"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/db/pipeline/expression.h"
|
||||
#include "mongo/platform/basic.h"
|
||||
|
||||
#include "mongo/db/pipeline/expression_walker.h"
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include "mongo/db/matcher/expression_algo.h"
|
||||
#include "mongo/db/matcher/expression_parser.h"
|
||||
#include "mongo/db/matcher/match_expression_dependencies.h"
|
||||
#include "mongo/db/pipeline/document_path_support.h"
|
||||
#include "mongo/db/pipeline/expression.h"
|
||||
|
||||
namespace mongo {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
*/
|
||||
|
||||
#include "mongo/db/pipeline/search/document_source_list_search_indexes.h"
|
||||
#include "mongo/db/pipeline/search/document_source_list_search_indexes_gen.h"
|
||||
|
||||
#include "mongo/db/query/search/search_index_common.h"
|
||||
#include "mongo/db/query/search/search_index_process_interface.h"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/db/pipeline/document_source.h"
|
||||
#include "mongo/db/pipeline/search/document_source_list_search_indexes_gen.h"
|
||||
#include "mongo/db/query/search/search_query_view_spec_gen.h"
|
||||
|
||||
namespace mongo {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include "mongo/db/pipeline/window_function/window_bounds.h"
|
||||
#include "mongo/db/pipeline/window_function/window_function_exec.h"
|
||||
#include "mongo/db/query/datetime/date_time_support.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
namespace mongo {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "mongo/db/pipeline/window_function/window_function_expression.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "mongo/db/pipeline/accumulator_multi.h"
|
||||
#include "mongo/db/pipeline/window_function/window_function.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "mongo/db/pipeline/group_from_first_document_transformation.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/util/uuid.h"
|
||||
|
||||
namespace mongo {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "mongo/db/query/client_cursor/cursor_response_gen.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#include "mongo/db/query/plan_enumerator/enumerator_memo.h"
|
||||
#include "mongo/db/query/plan_enumerator/plan_enumerator_explain_info.h"
|
||||
#include "mongo/db/query/query_knobs_gen.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/platform/atomic_word.h"
|
||||
#include "mongo/stdx/unordered_map.h"
|
||||
#include "mongo/util/fail_point.h"
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@
|
|||
#include "mongo/db/query/projection.h"
|
||||
#include "mongo/db/query/query_knobs_gen.h"
|
||||
#include "mongo/db/query/record_id_bound.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/db/query/stage_types.h"
|
||||
#include "mongo/db/query/timeseries/bucket_spec.h"
|
||||
#include "mongo/platform/atomic_word.h"
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#include "mongo/db/pipeline/expression.h"
|
||||
#include "mongo/db/pipeline/variables.h"
|
||||
#include "mongo/db/query/projection_ast.h"
|
||||
#include "mongo/db/query/sort_pattern.h"
|
||||
#include "mongo/db/query/stage_builder/sbe/abt/comparison_op.h"
|
||||
#include "mongo/db/query/stage_builder/sbe/builder_state.h"
|
||||
#include "mongo/db/query/stage_builder/sbe/sbexpr.h"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "mongo/db/repl/primary_only_service.h"
|
||||
#include "mongo/db/s/migration_blocking_operation/multi_update_coordinator.h"
|
||||
#include "mongo/db/s/migration_blocking_operation/multi_update_coordinator_external_state.h"
|
||||
#include "mongo/db/s/migration_blocking_operation/multi_update_coordinator_gen.h"
|
||||
#include "mongo/db/s/primary_only_service_helpers/retry_until_majority_commit.h"
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@
|
|||
#include "mongo/db/record_id.h"
|
||||
#include "mongo/db/repl/oplog_entry.h"
|
||||
#include "mongo/db/repl/optime.h"
|
||||
#include "mongo/db/s/migration_chunk_cloner_source.h"
|
||||
#include "mongo/db/s/migration_session_id.h"
|
||||
#include "mongo/db/s/session_catalog_migration_source.h"
|
||||
#include "mongo/db/session/logical_session_id.h"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "mongo/db/s/resharding/resharding_change_streams_monitor.h"
|
||||
|
||||
#include "mongo/db/pipeline/aggregate_command_gen.h"
|
||||
#include "mongo/db/pipeline/aggregation_request_helper.h"
|
||||
#include "mongo/db/pipeline/document_source_change_stream.h"
|
||||
#include "mongo/db/pipeline/document_source_change_stream_gen.h"
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/db/field_ref.h"
|
||||
#include "mongo/db/update_index_data.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
#include "mongo/platform/compiler.h"
|
||||
#include "mongo/rpc/get_status_from_command_result.h"
|
||||
#include "mongo/s/analyze_shard_key_role.h"
|
||||
#include "mongo/s/analyze_shard_key_server_parameters_gen.h"
|
||||
#include "mongo/s/client/shard.h"
|
||||
#include "mongo/s/client/shard_registry.h"
|
||||
#include "mongo/s/grid.h"
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
#include "mongo/db/service_context.h"
|
||||
#include "mongo/s/analyze_shard_key_common_gen.h"
|
||||
#include "mongo/s/analyze_shard_key_role.h"
|
||||
#include "mongo/s/analyze_shard_key_server_parameters_gen.h"
|
||||
#include "mongo/stdx/mutex.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/periodic_runner.h"
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#include "mongo/idl/server_parameter_test_util.h"
|
||||
#include "mongo/rpc/op_msg.h"
|
||||
#include "mongo/s/analyze_shard_key_common_gen.h"
|
||||
#include "mongo/s/analyze_shard_key_server_parameters_gen.h"
|
||||
#include "mongo/s/refresh_query_analyzer_configuration_cmd_gen.h"
|
||||
#include "mongo/s/sharding_mongos_test_fixture.h"
|
||||
#include "mongo/stdx/future.h"
|
||||
|
|
|
|||
|
|
@ -33,11 +33,9 @@ mongo_cc_library(
|
|||
"assert.h",
|
||||
"assert_that.h",
|
||||
"barrier.h",
|
||||
"bson_test_util.h",
|
||||
"death_test.h",
|
||||
"framework.h",
|
||||
"golden_test.h",
|
||||
"inline_auto_update.h",
|
||||
"join_thread.h",
|
||||
"log_test.h",
|
||||
"matcher.h",
|
||||
|
|
|
|||
|
|
@ -48,10 +48,13 @@
|
|||
|
||||
#include "mongo/base/status_with.h"
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/bson/bsonelement.h"
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/bson/simple_bsonelement_comparator.h"
|
||||
#include "mongo/bson/simple_bsonobj_comparator.h"
|
||||
#include "mongo/db/exec/mutable_bson/mutable_bson_test_utils.h"
|
||||
#include "mongo/logv2/log_debug.h"
|
||||
#include "mongo/logv2/log_detail.h"
|
||||
#include "mongo/unittest/bson_test_util.h"
|
||||
#include "mongo/unittest/framework.h"
|
||||
#include "mongo/unittest/stringify.h"
|
||||
#include "mongo/unittest/test_info.h"
|
||||
|
|
@ -551,4 +554,139 @@ T assertGet(StatusWith<T>&& swt) {
|
|||
return std::move(swt.getValue());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* BSON comparison utility macro. Do not use directly.
|
||||
*/
|
||||
#define ASSERT_BSON_COMPARISON(NAME, a, b, astr, bstr) \
|
||||
::mongo::unittest::assertComparison_##NAME(__FILE__, __LINE__, astr, bstr, a, b)
|
||||
|
||||
/**
|
||||
* Use to compare two instances of type BSONObj under the default comparator in unit tests.
|
||||
*/
|
||||
#define ASSERT_BSONOBJ_EQ(a, b) ASSERT_BSON_COMPARISON(BSONObjEQ, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LT(a, b) ASSERT_BSON_COMPARISON(BSONObjLT, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LTE(a, b) ASSERT_BSON_COMPARISON(BSONObjLTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GT(a, b) ASSERT_BSON_COMPARISON(BSONObjGT, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GTE(a, b) ASSERT_BSON_COMPARISON(BSONObjGTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_NE(a, b) ASSERT_BSON_COMPARISON(BSONObjNE, a, b, #a, #b)
|
||||
|
||||
/**
|
||||
* Use to compare two instances of type BSONObj with unordered fields in unit tests.
|
||||
*/
|
||||
#define ASSERT_BSONOBJ_EQ_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjEQ_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LT_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjLT_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LTE_UNORDERED(a, b) \
|
||||
ASSERT_BSON_COMPARISON(BSONObjLTE_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GT_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjGT_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GTE_UNORDERED(a, b) \
|
||||
ASSERT_BSON_COMPARISON(BSONObjGTE_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_NE_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjNE_UNORDERED, a, b, #a, #b)
|
||||
|
||||
/**
|
||||
* Use to compare two instances of type BSONElement under the default comparator in unit tests.
|
||||
*/
|
||||
#define ASSERT_BSONELT_EQ(a, b) ASSERT_BSON_COMPARISON(BSONElementEQ, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_LT(a, b) ASSERT_BSON_COMPARISON(BSONElementLT, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_LTE(a, b) ASSERT_BSON_COMPARISON(BSONElementLTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_GT(a, b) ASSERT_BSON_COMPARISON(BSONElementGT, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_GTE(a, b) ASSERT_BSON_COMPARISON(BSONElementGTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_NE(a, b) ASSERT_BSON_COMPARISON(BSONElementNE, a, b, #a, #b)
|
||||
|
||||
#define ASSERT_BSONOBJ_BINARY_EQ(a, b) \
|
||||
::mongo::unittest::assertComparison_BSONObjBINARY_EQ(__FILE__, __LINE__, #a, #b, a, b)
|
||||
|
||||
#define DECLARE_BSON_CMP_FUNC(BSONTYPE, NAME) \
|
||||
void assertComparison_##BSONTYPE##NAME(const std::string& theFile, \
|
||||
unsigned theLine, \
|
||||
StringData aExpression, \
|
||||
StringData bExpression, \
|
||||
const BSONTYPE& aValue, \
|
||||
const BSONTYPE& bValue);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, EQ);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, NE);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, EQ_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LT_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LTE_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GT_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GTE_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, NE_UNORDERED);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, BINARY_EQ);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, EQ);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, LT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, LTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, GT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, GTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, NE);
|
||||
#undef DECLARE_BSON_CMP_FUNC
|
||||
|
||||
/**
|
||||
* Given a BSONObj, return a string that wraps the json form of the BSONObj with
|
||||
* `fromjson(R"(<>)")`.
|
||||
*/
|
||||
std::string formatJsonStr(const std::string& obj);
|
||||
|
||||
#define ASSERT_BSONOBJ_EQ_AUTO(expected, actual) \
|
||||
ASSERT(AUTO_UPDATE_HELPER(::mongo::unittest::formatJsonStr(expected), \
|
||||
::mongo::unittest::formatJsonStr(actual.jsonString()), \
|
||||
false))
|
||||
|
||||
/**
|
||||
* Computes a difference between the expected and actual formatted output and outputs it to the
|
||||
* provide stream instance. Used to display difference between expected and actual format for
|
||||
* auto-update macros. It is exposed in the header here for testability.
|
||||
*/
|
||||
void outputDiff(std::ostream& os,
|
||||
const std::vector<std::string>& expFormatted,
|
||||
const std::vector<std::string>& actualFormatted,
|
||||
size_t startLineNumber);
|
||||
|
||||
bool handleAutoUpdate(const std::string& expected,
|
||||
const std::string& actual,
|
||||
const std::string& fileName,
|
||||
size_t lineNumber,
|
||||
bool needsEscaping);
|
||||
|
||||
bool expandNoPlanMacro(const std::string& fileName, size_t lineNumber);
|
||||
|
||||
void updateDelta(const std::string& fileName, size_t lineNumber, int64_t delta);
|
||||
|
||||
void expandActualPlan(const SourceLocation& location, const std::string& actual);
|
||||
|
||||
// Account for maximum line length after linting. We need to indent, add quotes, etc.
|
||||
static constexpr size_t kAutoUpdateMaxLineLength = 88;
|
||||
|
||||
/**
|
||||
* Auto update result back in the source file if the assert fails.
|
||||
* The expected result must be a multi-line string in the following form:
|
||||
*
|
||||
* ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
* "BinaryOp [Add]\n"
|
||||
* "| Const [2]\n"
|
||||
* "Const [1]\n",
|
||||
* tree);
|
||||
*
|
||||
* Limitations:
|
||||
* 1. There should not be any comments or other formatting inside the multi-line string
|
||||
* constant other than 'NOLINT'. If we have a single-line constant, the auto-updating will
|
||||
* generate a 'NOLINT' at the end of the line.
|
||||
* 2. The expression which we are explaining ('tree' in the example above) must fit on a single
|
||||
* line.
|
||||
* 3. The macro should be indented by 4 spaces.
|
||||
*/
|
||||
#define AUTO_UPDATE_HELPER(expected, actual, needsEscaping) \
|
||||
::mongo::unittest::handleAutoUpdate(expected, actual, __FILE__, __LINE__, needsEscaping)
|
||||
|
||||
#define ASSERT_STR_EQ_AUTO(expected, actual) ASSERT(AUTO_UPDATE_HELPER(expected, actual, true))
|
||||
|
||||
#define ASSERT_NUMBER_EQ_AUTO(expected, actual) \
|
||||
ASSERT(AUTO_UPDATE_HELPER(str::stream() << expected, str::stream() << actual, false))
|
||||
} // namespace mongo::unittest
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@
|
|||
#include "mongo/bson/simple_bsonobj_comparator.h"
|
||||
#include "mongo/bson/unordered_fields_bsonobj_comparator.h"
|
||||
#include "mongo/unittest/assert.h"
|
||||
#include "mongo/unittest/bson_test_util.h"
|
||||
#include "mongo/unittest/inline_auto_update.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/str.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2018-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// IWYU pragma: private, include "mongo/unittest/unittest.h"
|
||||
// IWYU pragma: friend "mongo/unittest/.*"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/bson/bsonelement.h"
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/bson/simple_bsonelement_comparator.h"
|
||||
#include "mongo/bson/simple_bsonobj_comparator.h"
|
||||
#include "mongo/unittest/assert.h"
|
||||
#include "mongo/unittest/inline_auto_update.h"
|
||||
|
||||
/**
|
||||
* BSON comparison utility macro. Do not use directly.
|
||||
*/
|
||||
#define ASSERT_BSON_COMPARISON(NAME, a, b, astr, bstr) \
|
||||
::mongo::unittest::assertComparison_##NAME(__FILE__, __LINE__, astr, bstr, a, b)
|
||||
|
||||
/**
|
||||
* Use to compare two instances of type BSONObj under the default comparator in unit tests.
|
||||
*/
|
||||
#define ASSERT_BSONOBJ_EQ(a, b) ASSERT_BSON_COMPARISON(BSONObjEQ, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LT(a, b) ASSERT_BSON_COMPARISON(BSONObjLT, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LTE(a, b) ASSERT_BSON_COMPARISON(BSONObjLTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GT(a, b) ASSERT_BSON_COMPARISON(BSONObjGT, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GTE(a, b) ASSERT_BSON_COMPARISON(BSONObjGTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_NE(a, b) ASSERT_BSON_COMPARISON(BSONObjNE, a, b, #a, #b)
|
||||
|
||||
/**
|
||||
* Use to compare two instances of type BSONObj with unordered fields in unit tests.
|
||||
*/
|
||||
#define ASSERT_BSONOBJ_EQ_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjEQ_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LT_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjLT_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_LTE_UNORDERED(a, b) \
|
||||
ASSERT_BSON_COMPARISON(BSONObjLTE_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GT_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjGT_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_GTE_UNORDERED(a, b) \
|
||||
ASSERT_BSON_COMPARISON(BSONObjGTE_UNORDERED, a, b, #a, #b)
|
||||
#define ASSERT_BSONOBJ_NE_UNORDERED(a, b) ASSERT_BSON_COMPARISON(BSONObjNE_UNORDERED, a, b, #a, #b)
|
||||
|
||||
/**
|
||||
* Use to compare two instances of type BSONElement under the default comparator in unit tests.
|
||||
*/
|
||||
#define ASSERT_BSONELT_EQ(a, b) ASSERT_BSON_COMPARISON(BSONElementEQ, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_LT(a, b) ASSERT_BSON_COMPARISON(BSONElementLT, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_LTE(a, b) ASSERT_BSON_COMPARISON(BSONElementLTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_GT(a, b) ASSERT_BSON_COMPARISON(BSONElementGT, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_GTE(a, b) ASSERT_BSON_COMPARISON(BSONElementGTE, a, b, #a, #b)
|
||||
#define ASSERT_BSONELT_NE(a, b) ASSERT_BSON_COMPARISON(BSONElementNE, a, b, #a, #b)
|
||||
|
||||
#define ASSERT_BSONOBJ_BINARY_EQ(a, b) \
|
||||
::mongo::unittest::assertComparison_BSONObjBINARY_EQ(__FILE__, __LINE__, #a, #b, a, b)
|
||||
|
||||
namespace mongo {
|
||||
namespace unittest {
|
||||
|
||||
#define DECLARE_BSON_CMP_FUNC(BSONTYPE, NAME) \
|
||||
void assertComparison_##BSONTYPE##NAME(const std::string& theFile, \
|
||||
unsigned theLine, \
|
||||
StringData aExpression, \
|
||||
StringData bExpression, \
|
||||
const BSONTYPE& aValue, \
|
||||
const BSONTYPE& bValue);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, EQ);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, NE);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, EQ_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LT_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, LTE_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GT_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, GTE_UNORDERED);
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, NE_UNORDERED);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONObj, BINARY_EQ);
|
||||
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, EQ);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, LT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, LTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, GT);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, GTE);
|
||||
DECLARE_BSON_CMP_FUNC(BSONElement, NE);
|
||||
#undef DECLARE_BSON_CMP_FUNC
|
||||
|
||||
/**
|
||||
* Given a BSONObj, return a string that wraps the json form of the BSONObj with
|
||||
* `fromjson(R"(<>)")`.
|
||||
*/
|
||||
std::string formatJsonStr(const std::string& obj);
|
||||
|
||||
#define ASSERT_BSONOBJ_EQ_AUTO(expected, actual) \
|
||||
ASSERT(AUTO_UPDATE_HELPER(::mongo::unittest::formatJsonStr(expected), \
|
||||
::mongo::unittest::formatJsonStr(actual.jsonString()), \
|
||||
false))
|
||||
} // namespace unittest
|
||||
} // namespace mongo
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/unittest/inline_auto_update.h"
|
||||
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
|
@ -42,6 +41,7 @@
|
|||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "mongo/unittest/assert.h"
|
||||
#include "mongo/unittest/framework.h"
|
||||
#include "mongo/util/str_escape.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// IWYU pragma: private, include "mongo/unittest/unittest.h"
|
||||
// IWYU pragma: friend "mongo/unittest/.*"
|
||||
|
||||
#include <cstddef>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mongo/platform/source_location.h"
|
||||
#include "mongo/unittest/assert.h"
|
||||
|
||||
namespace mongo::unittest {
|
||||
/**
|
||||
* Computes a difference between the expected and actual formatted output and outputs it to the
|
||||
* provide stream instance. Used to display difference between expected and actual format for
|
||||
* auto-update macros. It is exposed in the header here for testability.
|
||||
*/
|
||||
void outputDiff(std::ostream& os,
|
||||
const std::vector<std::string>& expFormatted,
|
||||
const std::vector<std::string>& actualFormatted,
|
||||
size_t startLineNumber);
|
||||
|
||||
bool handleAutoUpdate(const std::string& expected,
|
||||
const std::string& actual,
|
||||
const std::string& fileName,
|
||||
size_t lineNumber,
|
||||
bool needsEscaping);
|
||||
|
||||
bool expandNoPlanMacro(const std::string& fileName, size_t lineNumber);
|
||||
|
||||
void updateDelta(const std::string& fileName, size_t lineNumber, int64_t delta);
|
||||
|
||||
void expandActualPlan(const SourceLocation& location, const std::string& actual);
|
||||
|
||||
// Account for maximum line length after linting. We need to indent, add quotes, etc.
|
||||
static constexpr size_t kAutoUpdateMaxLineLength = 88;
|
||||
|
||||
/**
|
||||
* Auto update result back in the source file if the assert fails.
|
||||
* The expected result must be a multi-line string in the following form:
|
||||
*
|
||||
* ASSERT_EXPLAIN_V2_AUTO( // NOLINT
|
||||
* "BinaryOp [Add]\n"
|
||||
* "| Const [2]\n"
|
||||
* "Const [1]\n",
|
||||
* tree);
|
||||
*
|
||||
* Limitations:
|
||||
* 1. There should not be any comments or other formatting inside the multi-line string
|
||||
* constant other than 'NOLINT'. If we have a single-line constant, the auto-updating will
|
||||
* generate a 'NOLINT' at the end of the line.
|
||||
* 2. The expression which we are explaining ('tree' in the example above) must fit on a single
|
||||
* line.
|
||||
* 3. The macro should be indented by 4 spaces.
|
||||
*/
|
||||
#define AUTO_UPDATE_HELPER(expected, actual, needsEscaping) \
|
||||
::mongo::unittest::handleAutoUpdate(expected, actual, __FILE__, __LINE__, needsEscaping)
|
||||
|
||||
#define ASSERT_STR_EQ_AUTO(expected, actual) ASSERT(AUTO_UPDATE_HELPER(expected, actual, true))
|
||||
|
||||
#define ASSERT_NUMBER_EQ_AUTO(expected, actual) \
|
||||
ASSERT(AUTO_UPDATE_HELPER(str::stream() << expected, str::stream() << actual, false))
|
||||
} // namespace mongo::unittest
|
||||
|
|
@ -27,7 +27,6 @@
|
|||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/unittest/inline_auto_update.h"
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/unittest/assert.h"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/unittest/assert.h" // IWYU pragma: export
|
||||
#include "mongo/unittest/assert_that.h" // IWYU pragma: export
|
||||
#include "mongo/unittest/bson_test_util.h" // IWYU pragma: export
|
||||
#include "mongo/unittest/framework.h" // IWYU pragma: export
|
||||
#include "mongo/unittest/assert.h" // IWYU pragma: export
|
||||
#include "mongo/unittest/assert_that.h" // IWYU pragma: export
|
||||
#include "mongo/unittest/framework.h" // IWYU pragma: export
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@
|
|||
#include "mongo/logv2/log.h"
|
||||
#include "mongo/stdx/type_traits.h"
|
||||
#include "mongo/unittest/assert.h"
|
||||
#include "mongo/unittest/bson_test_util.h"
|
||||
#include "mongo/unittest/death_test.h"
|
||||
#include "mongo/unittest/framework.h"
|
||||
#include "mongo/unittest/stringify.h"
|
||||
|
|
|
|||
Loading…
Reference in New Issue