SERVER-104760 break #include cycles and enforce with clang-tidy (#35786)

GitOrigin-RevId: f186c8347205ebbaa49c45c1f06e1310621366e0
This commit is contained in:
Mathias Stearn 2025-05-07 18:13:12 +02:00 committed by MongoDB Bot
parent 3e3610ff1d
commit c740e2bb42
53 changed files with 832 additions and 1015 deletions

View File

@ -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:

View File

@ -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",

View File

@ -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_

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 {
/**

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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 {

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -29,7 +29,6 @@
#pragma once
#include "mongo/db/pipeline/expression.h"
#include "mongo/platform/basic.h"
#include "mongo/db/pipeline/expression_walker.h"

View File

@ -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 {

View File

@ -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"

View File

@ -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 {

View File

@ -29,6 +29,8 @@
#pragma once
#include "mongo/db/query/sort_pattern.h"
namespace mongo {
/**

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -28,7 +28,6 @@
*/
#pragma once
#include "mongo/db/query/client_cursor/cursor_response_gen.h"
namespace mongo {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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",

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"