diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 00:22:50 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 10:56:02 -0400 |
commit | 9c2ed42daa8fbbef4a919c21ec564e2db55e8d60 (patch) | |
tree | 3814f79c10d7b490948d8cb7b112ac1dd41ceff1 /src/mongo/base | |
parent | 01965cf52bce6976637ecb8f4a622aeb05ab256a (diff) | |
download | mongo-9c2ed42daa8fbbef4a919c21ec564e2db55e8d60.tar.gz |
SERVER-18579: Clang-Format - reformat code, no comment reflow
Diffstat (limited to 'src/mongo/base')
62 files changed, 4267 insertions, 4288 deletions
diff --git a/src/mongo/base/checked_cast.h b/src/mongo/base/checked_cast.h index c4e9fe2ffc1..94cf5ea9c1f 100644 --- a/src/mongo/base/checked_cast.h +++ b/src/mongo/base/checked_cast.h @@ -35,43 +35,42 @@ namespace mongo { - /** - * Similar to static_cast, but in debug builds uses RTTI to confirm that the cast - * is legal at runtime. - */ - template<bool> - struct checked_cast_impl; - - template<> - struct checked_cast_impl<false> { - template<typename T, typename U> - static T cast(const U& u) { - return static_cast<T>(u); - } - }; +/** + * Similar to static_cast, but in debug builds uses RTTI to confirm that the cast + * is legal at runtime. + */ +template <bool> +struct checked_cast_impl; - template<> - struct checked_cast_impl<true> { - template<typename T, typename U> - static T cast(U* u) { - if (!u) { - return NULL; - } - T t = dynamic_cast<T>(u); - invariant(t); - return t; - } +template <> +struct checked_cast_impl<false> { + template <typename T, typename U> + static T cast(const U& u) { + return static_cast<T>(u); + } +}; - template<typename T, typename U> - static T cast(const U& u) { - return dynamic_cast<T>(u); +template <> +struct checked_cast_impl<true> { + template <typename T, typename U> + static T cast(U* u) { + if (!u) { + return NULL; } + T t = dynamic_cast<T>(u); + invariant(t); + return t; + } - }; + template <typename T, typename U> + static T cast(const U& u) { + return dynamic_cast<T>(u); + } +}; - template<typename T, typename U> - T checked_cast(const U& u) { - return checked_cast_impl<kDebugBuild>::cast<T>(u); - }; +template <typename T, typename U> +T checked_cast(const U& u) { + return checked_cast_impl<kDebugBuild>::cast<T>(u); +}; -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/compare_numbers.h b/src/mongo/base/compare_numbers.h index ca42f5d1782..0049d5ba336 100644 --- a/src/mongo/base/compare_numbers.h +++ b/src/mongo/base/compare_numbers.h @@ -33,62 +33,69 @@ namespace mongo { - /** - * These functions compare numbers using the same rules as BSON. Care is taken to always give - * numerically correct results when comparing different types. Returns are always -1, 0, or 1 to - * ensure it is safe to negate the result to invert the direction of the comparison. - */ +/** + * These functions compare numbers using the same rules as BSON. Care is taken to always give + * numerically correct results when comparing different types. Returns are always -1, 0, or 1 to + * ensure it is safe to negate the result to invert the direction of the comparison. + */ - inline int compareInts(int lhs, int rhs) { - return lhs == rhs ? 0 : lhs < rhs ? -1 : 1; - } +inline int compareInts(int lhs, int rhs) { + return lhs == rhs ? 0 : lhs < rhs ? -1 : 1; +} - inline int compareLongs(long long lhs, long long rhs) { - return lhs == rhs ? 0 : lhs < rhs ? -1 : 1; - } +inline int compareLongs(long long lhs, long long rhs) { + return lhs == rhs ? 0 : lhs < rhs ? -1 : 1; +} - inline int compareDoubles(double lhs, double rhs) { - if (lhs == rhs) return 0; - if (lhs < rhs) return -1; - if (lhs > rhs) return 1; - - // If none of the above cases returned, lhs or rhs must be NaN. - if (std::isnan(lhs)) return std::isnan(rhs) ? 0 : -1; - dassert(std::isnan(rhs)); +inline int compareDoubles(double lhs, double rhs) { + if (lhs == rhs) + return 0; + if (lhs < rhs) + return -1; + if (lhs > rhs) return 1; - } - // This is the tricky one. Needs to support the following cases: - // * Doubles with a fractional component. - // * Longs that can't be precisely represented as a double. - // * Doubles outside of the range of Longs (including +/- Inf). - // * NaN (defined by us as less than all Longs) - // * Return value is always -1, 0, or 1 to ensure it is safe to negate. - inline int compareLongToDouble(long long lhs, double rhs) { - // All Longs are > NaN - if (std::isnan(rhs)) return 1; + // If none of the above cases returned, lhs or rhs must be NaN. + if (std::isnan(lhs)) + return std::isnan(rhs) ? 0 : -1; + dassert(std::isnan(rhs)); + return 1; +} - // Ints with magnitude <= 2**53 can be precisely represented as doubles. - // Additionally, doubles outside of this range can't have a fractional component. - static const long long kEndOfPreciseDoubles = 1ll << 53; - if (lhs <= kEndOfPreciseDoubles && lhs >= -kEndOfPreciseDoubles) { - return compareDoubles(lhs, rhs); - } - - // Large magnitude doubles (including +/- Inf) are strictly > or < all Longs. - static const double kBoundOfLongRange = -static_cast<double>(LLONG_MIN); // positive 2**63 - if (rhs >= kBoundOfLongRange) return -1; // Can't be represented in a Long. - if (rhs < -kBoundOfLongRange) return 1; // Can be represented in a Long. +// This is the tricky one. Needs to support the following cases: +// * Doubles with a fractional component. +// * Longs that can't be precisely represented as a double. +// * Doubles outside of the range of Longs (including +/- Inf). +// * NaN (defined by us as less than all Longs) +// * Return value is always -1, 0, or 1 to ensure it is safe to negate. +inline int compareLongToDouble(long long lhs, double rhs) { + // All Longs are > NaN + if (std::isnan(rhs)) + return 1; - // Remaining Doubles can have their integer component precisely represented as long longs. - // If they have a fractional component, they must be strictly > or < lhs even after - // truncation of the fractional component since low-magnitude lhs were handled above. - return compareLongs(lhs, rhs); + // Ints with magnitude <= 2**53 can be precisely represented as doubles. + // Additionally, doubles outside of this range can't have a fractional component. + static const long long kEndOfPreciseDoubles = 1ll << 53; + if (lhs <= kEndOfPreciseDoubles && lhs >= -kEndOfPreciseDoubles) { + return compareDoubles(lhs, rhs); } - inline int compareDoubleToLong(double lhs, long long rhs) { - // Only implement the real logic once. - return -compareLongToDouble(rhs, lhs); - } + // Large magnitude doubles (including +/- Inf) are strictly > or < all Longs. + static const double kBoundOfLongRange = -static_cast<double>(LLONG_MIN); // positive 2**63 + if (rhs >= kBoundOfLongRange) + return -1; // Can't be represented in a Long. + if (rhs < -kBoundOfLongRange) + return 1; // Can be represented in a Long. + + // Remaining Doubles can have their integer component precisely represented as long longs. + // If they have a fractional component, they must be strictly > or < lhs even after + // truncation of the fractional component since low-magnitude lhs were handled above. + return compareLongs(lhs, rhs); +} + +inline int compareDoubleToLong(double lhs, long long rhs) { + // Only implement the real logic once. + return -compareLongToDouble(rhs, lhs); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/counter.h b/src/mongo/base/counter.h index 07ec888475d..b8089f1c002 100644 --- a/src/mongo/base/counter.h +++ b/src/mongo/base/counter.h @@ -34,27 +34,35 @@ #include "mongo/platform/cstdint.h" namespace mongo { - /** - * A 64bit (atomic) counter. - * - * The constructor allows setting the start value, and increment([int]) is used to change it. - * - * The value can be returned using get() or the (long long) function operator. - */ - class Counter64 { - public: - - /** Atomically increment. */ - void increment( uint64_t n = 1 ) { _counter.addAndFetch(n); } - - /** Atomically decrement. */ - void decrement( uint64_t n = 1 ) { _counter.subtractAndFetch(n); } - - /** Return the current value */ - long long get() const { return _counter.load(); } - - operator long long() const { return get(); } - private: - AtomicInt64 _counter; - }; +/** + * A 64bit (atomic) counter. + * + * The constructor allows setting the start value, and increment([int]) is used to change it. + * + * The value can be returned using get() or the (long long) function operator. + */ +class Counter64 { +public: + /** Atomically increment. */ + void increment(uint64_t n = 1) { + _counter.addAndFetch(n); + } + + /** Atomically decrement. */ + void decrement(uint64_t n = 1) { + _counter.subtractAndFetch(n); + } + + /** Return the current value */ + long long get() const { + return _counter.load(); + } + + operator long long() const { + return get(); + } + +private: + AtomicInt64 _counter; +}; } diff --git a/src/mongo/base/counter_test.cpp b/src/mongo/base/counter_test.cpp index a37e9defe5c..e40cda206d9 100644 --- a/src/mongo/base/counter_test.cpp +++ b/src/mongo/base/counter_test.cpp @@ -36,23 +36,23 @@ #include "mongo/unittest/unittest.h" namespace mongo { - namespace { - TEST( CounterTest, Test1 ) { - Counter64 c; - ASSERT_EQUALS(c.get(), 0); - c.increment(); - ASSERT_EQUALS(c.get(), 1); - c.decrement(); - ASSERT_EQUALS(c.get(), 0); - c.decrement(3); - ASSERT_EQUALS(c.get(), -3); - c.increment(1); - ASSERT_EQUALS(c.get(), -2); - c.decrement(-1); - ASSERT_EQUALS(c.get(), -1); - c.increment(); - ASSERT_EQUALS(static_cast<long long>(c), 0); - } +namespace { +TEST(CounterTest, Test1) { + Counter64 c; + ASSERT_EQUALS(c.get(), 0); + c.increment(); + ASSERT_EQUALS(c.get(), 1); + c.decrement(); + ASSERT_EQUALS(c.get(), 0); + c.decrement(3); + ASSERT_EQUALS(c.get(), -3); + c.increment(1); + ASSERT_EQUALS(c.get(), -2); + c.decrement(-1); + ASSERT_EQUALS(c.get(), -1); + c.increment(); + ASSERT_EQUALS(static_cast<long long>(c), 0); +} - } // namespace +} // namespace } // namespace mongo diff --git a/src/mongo/base/data_cursor.h b/src/mongo/base/data_cursor.h index 7806c811d9b..4f9d8aef652 100644 --- a/src/mongo/base/data_cursor.h +++ b/src/mongo/base/data_cursor.h @@ -36,167 +36,162 @@ namespace mongo { - class ConstDataCursor : public ConstDataView { - public: +class ConstDataCursor : public ConstDataView { +public: + typedef ConstDataView view_type; + + ConstDataCursor(ConstDataView::bytes_type bytes) : ConstDataView(bytes) {} + + ConstDataCursor operator+(std::size_t s) const { + return view() + s; + } + + ConstDataCursor& operator+=(std::size_t s) { + *this = view() + s; + return *this; + } + + ConstDataCursor operator-(std::size_t s) const { + return view() - s; + } + + ConstDataCursor& operator-=(std::size_t s) { + *this = view() - s; + return *this; + } + + ConstDataCursor& operator++() { + return operator+=(1); + } + + ConstDataCursor operator++(int) { + ConstDataCursor tmp = *this; + operator++(); + return tmp; + } + + ConstDataCursor& operator--() { + return operator-=(1); + } + + ConstDataCursor operator--(int) { + ConstDataCursor tmp = *this; + operator--(); + return tmp; + } + + template <typename T> + ConstDataCursor& skip() { + size_t advance = 0; + + DataType::unsafeLoad<T>(nullptr, view(), &advance); + *this += advance; + + return *this; + } + + template <typename T> + ConstDataCursor& readAndAdvance(T* t) { + size_t advance = 0; + + DataType::unsafeLoad(t, view(), &advance); + *this += advance; + + return *this; + } + + template <typename T> + T readAndAdvance() { + T out(DataType::defaultConstruct<T>()); + readAndAdvance(&out); + return out; + } +}; + +class DataCursor : public DataView { +public: + typedef DataView view_type; + + DataCursor(DataView::bytes_type bytes) : DataView(bytes) {} + + operator ConstDataCursor() const { + return view(); + } + + DataCursor operator+(std::size_t s) const { + return view() + s; + } + + DataCursor& operator+=(std::size_t s) { + *this = view() + s; + return *this; + } + + DataCursor operator-(std::size_t s) const { + return view() - s; + } + + DataCursor& operator-=(std::size_t s) { + *this = view() - s; + return *this; + } + + DataCursor& operator++() { + return operator+=(1); + } + + DataCursor operator++(int) { + DataCursor tmp = *this; + operator++(); + return tmp; + } + + DataCursor& operator--() { + return operator-=(1); + } + + DataCursor operator--(int) { + DataCursor tmp = *this; + operator--(); + return tmp; + } + + template <typename T> + DataCursor& skip() { + size_t advance = 0; + + DataType::unsafeLoad<T>(nullptr, view(), &advance); + *this += advance; + + return *this; + } + + template <typename T> + DataCursor& readAndAdvance(T* t) { + size_t advance = 0; - typedef ConstDataView view_type; + DataType::unsafeLoad(t, view(), &advance); + *this += advance; - ConstDataCursor(ConstDataView::bytes_type bytes) - : ConstDataView(bytes) { - } + return *this; + } + + template <typename T> + T readAndAdvance() { + T out(DataType::defaultConstruct<T>()); + readAndAdvance(&out); + return out; + } - ConstDataCursor operator+(std::size_t s) const { - return view() + s; - } - - ConstDataCursor& operator+=(std::size_t s) { - *this = view() + s; - return *this; - } - - ConstDataCursor operator-(std::size_t s) const { - return view() - s; - } - - ConstDataCursor& operator-=(std::size_t s) { - *this = view() - s; - return *this; - } - - ConstDataCursor& operator++() { - return operator+=(1); - } - - ConstDataCursor operator++(int) { - ConstDataCursor tmp = *this; - operator++(); - return tmp; - } - - ConstDataCursor& operator--() { - return operator-=(1); - } - - ConstDataCursor operator--(int) { - ConstDataCursor tmp = *this; - operator--(); - return tmp; - } + template <typename T> + DataCursor& writeAndAdvance(const T& value) { + size_t advance = 0; - template <typename T> - ConstDataCursor& skip() { - size_t advance = 0; - - DataType::unsafeLoad<T>(nullptr, view(), &advance); - *this += advance; - - return *this; - } - - template <typename T> - ConstDataCursor& readAndAdvance(T* t) { - size_t advance = 0; - - DataType::unsafeLoad(t, view(), &advance); - *this += advance; - - return *this; - } - - template <typename T> - T readAndAdvance() { - T out(DataType::defaultConstruct<T>()); - readAndAdvance(&out); - return out; - } - }; - - class DataCursor : public DataView { - public: - - typedef DataView view_type; - - DataCursor(DataView::bytes_type bytes) - : DataView(bytes) {} - - operator ConstDataCursor() const { - return view(); - } - - DataCursor operator+(std::size_t s) const { - return view() + s; - } - - DataCursor& operator+=(std::size_t s) { - *this = view() + s; - return *this; - } - - DataCursor operator-(std::size_t s) const { - return view() - s; - } - - DataCursor& operator-=(std::size_t s) { - *this = view() - s; - return *this; - } - - DataCursor& operator++() { - return operator+=(1); - } - - DataCursor operator++(int) { - DataCursor tmp = *this; - operator++(); - return tmp; - } - - DataCursor& operator--() { - return operator-=(1); - } - - DataCursor operator--(int) { - DataCursor tmp = *this; - operator--(); - return tmp; - } - - template <typename T> - DataCursor& skip() { - size_t advance = 0; - - DataType::unsafeLoad<T>(nullptr, view(), &advance); - *this += advance; - - return *this; - } + DataType::unsafeStore(value, view(), &advance); + *this += advance; - template <typename T> - DataCursor& readAndAdvance(T* t) { - size_t advance = 0; + return *this; + } +}; - DataType::unsafeLoad(t, view(), &advance); - *this += advance; - - return *this; - } - - template <typename T> - T readAndAdvance() { - T out(DataType::defaultConstruct<T>()); - readAndAdvance(&out); - return out; - } - - template <typename T> - DataCursor& writeAndAdvance(const T& value) { - size_t advance = 0; - - DataType::unsafeStore(value, view(), &advance); - *this += advance; - - return *this; - } - }; - -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_cursor_test.cpp b/src/mongo/base/data_cursor_test.cpp index 55f7901a6f4..bd4690b05de 100644 --- a/src/mongo/base/data_cursor_test.cpp +++ b/src/mongo/base/data_cursor_test.cpp @@ -34,75 +34,74 @@ namespace mongo { - TEST(DataCursor, ConstDataCursor) { - char buf[100]; +TEST(DataCursor, ConstDataCursor) { + char buf[100]; - DataView(buf).write<uint16_t>(1); - DataView(buf).write<LittleEndian<uint32_t>>(2, sizeof(uint16_t)); - DataView(buf).write<BigEndian<uint64_t>>(3, sizeof(uint16_t) + sizeof(uint32_t)); + DataView(buf).write<uint16_t>(1); + DataView(buf).write<LittleEndian<uint32_t>>(2, sizeof(uint16_t)); + DataView(buf).write<BigEndian<uint64_t>>(3, sizeof(uint16_t) + sizeof(uint32_t)); - ConstDataCursor cdc(buf); + ConstDataCursor cdc(buf); - ASSERT_EQUALS(static_cast<uint16_t>(1), cdc.readAndAdvance<uint16_t>()); - ASSERT_EQUALS(static_cast<uint32_t>(2), cdc.readAndAdvance<LittleEndian<uint32_t>>()); - ASSERT_EQUALS(static_cast<uint64_t>(3), cdc.readAndAdvance<BigEndian<uint64_t>>()); + ASSERT_EQUALS(static_cast<uint16_t>(1), cdc.readAndAdvance<uint16_t>()); + ASSERT_EQUALS(static_cast<uint32_t>(2), cdc.readAndAdvance<LittleEndian<uint32_t>>()); + ASSERT_EQUALS(static_cast<uint64_t>(3), cdc.readAndAdvance<BigEndian<uint64_t>>()); - // test skip() - cdc = buf; - cdc.skip<uint32_t>(); - ASSERT_EQUALS(buf + sizeof(uint32_t), cdc.view()); + // test skip() + cdc = buf; + cdc.skip<uint32_t>(); + ASSERT_EQUALS(buf + sizeof(uint32_t), cdc.view()); - // test x + - cdc = buf; - ASSERT_EQUALS(buf + sizeof(uint32_t), (cdc + sizeof(uint32_t)).view()); + // test x + + cdc = buf; + ASSERT_EQUALS(buf + sizeof(uint32_t), (cdc + sizeof(uint32_t)).view()); - // test x - - cdc = buf + sizeof(uint32_t); - ASSERT_EQUALS(buf, (cdc - sizeof(uint32_t)).view()); + // test x - + cdc = buf + sizeof(uint32_t); + ASSERT_EQUALS(buf, (cdc - sizeof(uint32_t)).view()); - // test x += and x -= - cdc = buf; - cdc += sizeof(uint32_t); - ASSERT_EQUALS(buf + sizeof(uint32_t), cdc.view()); - cdc -= sizeof(uint16_t); - ASSERT_EQUALS(buf + sizeof(uint16_t), cdc.view()); + // test x += and x -= + cdc = buf; + cdc += sizeof(uint32_t); + ASSERT_EQUALS(buf + sizeof(uint32_t), cdc.view()); + cdc -= sizeof(uint16_t); + ASSERT_EQUALS(buf + sizeof(uint16_t), cdc.view()); - // test ++x - cdc = buf; - ASSERT_EQUALS(buf + sizeof(uint8_t), (++cdc).view()); - ASSERT_EQUALS(buf + sizeof(uint8_t), cdc.view()); + // test ++x + cdc = buf; + ASSERT_EQUALS(buf + sizeof(uint8_t), (++cdc).view()); + ASSERT_EQUALS(buf + sizeof(uint8_t), cdc.view()); - // test x++ - cdc = buf; - ASSERT_EQUALS(buf, (cdc++).view()); - ASSERT_EQUALS(buf + sizeof(uint8_t), cdc.view()); + // test x++ + cdc = buf; + ASSERT_EQUALS(buf, (cdc++).view()); + ASSERT_EQUALS(buf + sizeof(uint8_t), cdc.view()); - // test --x - cdc = buf + sizeof(uint8_t); - ASSERT_EQUALS(buf, (--cdc).view()); - ASSERT_EQUALS(buf, cdc.view()); + // test --x + cdc = buf + sizeof(uint8_t); + ASSERT_EQUALS(buf, (--cdc).view()); + ASSERT_EQUALS(buf, cdc.view()); - // test x-- - cdc = buf + sizeof(uint8_t); - ASSERT_EQUALS(buf + sizeof(uint8_t), (cdc--).view()); - ASSERT_EQUALS(buf, cdc.view()); + // test x-- + cdc = buf + sizeof(uint8_t); + ASSERT_EQUALS(buf + sizeof(uint8_t), (cdc--).view()); + ASSERT_EQUALS(buf, cdc.view()); +} - } +TEST(DataCursor, DataCursor) { + char buf[100]; - TEST(DataCursor, DataCursor) { - char buf[100]; + DataCursor dc(buf); - DataCursor dc(buf); + dc.writeAndAdvance<uint16_t>(1); + dc.writeAndAdvance<LittleEndian<uint32_t>>(2); + dc.writeAndAdvance<BigEndian<uint64_t>>(3); - dc.writeAndAdvance<uint16_t>(1); - dc.writeAndAdvance<LittleEndian<uint32_t>>(2); - dc.writeAndAdvance<BigEndian<uint64_t>>(3); + ConstDataCursor cdc(buf); - ConstDataCursor cdc(buf); + ASSERT_EQUALS(static_cast<uint16_t>(1), cdc.readAndAdvance<uint16_t>()); + ASSERT_EQUALS(static_cast<uint32_t>(2), cdc.readAndAdvance<LittleEndian<uint32_t>>()); + ASSERT_EQUALS(static_cast<uint64_t>(3), cdc.readAndAdvance<BigEndian<uint64_t>>()); +} - ASSERT_EQUALS(static_cast<uint16_t>(1), cdc.readAndAdvance<uint16_t>()); - ASSERT_EQUALS(static_cast<uint32_t>(2), cdc.readAndAdvance<LittleEndian<uint32_t>>()); - ASSERT_EQUALS(static_cast<uint64_t>(3), cdc.readAndAdvance<BigEndian<uint64_t>>()); - } - -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_range.cpp b/src/mongo/base/data_range.cpp index 08e5f6a56fb..d41a249ce25 100644 --- a/src/mongo/base/data_range.cpp +++ b/src/mongo/base/data_range.cpp @@ -31,20 +31,21 @@ namespace mongo { - Status ConstDataRange::makeOffsetStatus(size_t offset) const { - str::stream ss; - ss << "Invalid offset(" << offset << ") past end of buffer[" << length() - << "] at offset: " << _debug_offset; +Status ConstDataRange::makeOffsetStatus(size_t offset) const { + str::stream ss; + ss << "Invalid offset(" << offset << ") past end of buffer[" << length() + << "] at offset: " << _debug_offset; - return Status(ErrorCodes::Overflow, ss); - } + return Status(ErrorCodes::Overflow, ss); +} - Status DataRangeTypeHelper::makeStoreStatus(size_t t_length, size_t length, - std::ptrdiff_t debug_offset) { - str::stream ss; - ss << "buffer size too small to write (" << t_length << ") bytes into buffer[" - << length << "] at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } +Status DataRangeTypeHelper::makeStoreStatus(size_t t_length, + size_t length, + std::ptrdiff_t debug_offset) { + str::stream ss; + ss << "buffer size too small to write (" << t_length << ") bytes into buffer[" << length + << "] at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); +} } // namespace mongo diff --git a/src/mongo/base/data_range.h b/src/mongo/base/data_range.h index 16c7f68ca98..9226e6c133a 100644 --- a/src/mongo/base/data_range.h +++ b/src/mongo/base/data_range.h @@ -39,141 +39,135 @@ namespace mongo { - class ConstDataRange { - - public: - // begin and end should point to the first and one past last bytes in - // the range you wish to view. - // - // debug_offset provides a way to indicate that the ConstDataRange is - // located at an offset into some larger logical buffer. By setting it - // to a non-zero value, you'll change the Status messages that are - // returned on failure to be offset by the amount passed to this - // constructor. - ConstDataRange(const char* begin, const char* end, std::ptrdiff_t debug_offset = 0) - : _begin(begin), _end(end), _debug_offset(debug_offset) { - invariant(end >= begin); - } - - const char* data() const { - return _begin; - } - - size_t length() const { - return _end - _begin; - } +class ConstDataRange { +public: + // begin and end should point to the first and one past last bytes in + // the range you wish to view. + // + // debug_offset provides a way to indicate that the ConstDataRange is + // located at an offset into some larger logical buffer. By setting it + // to a non-zero value, you'll change the Status messages that are + // returned on failure to be offset by the amount passed to this + // constructor. + ConstDataRange(const char* begin, const char* end, std::ptrdiff_t debug_offset = 0) + : _begin(begin), _end(end), _debug_offset(debug_offset) { + invariant(end >= begin); + } + + const char* data() const { + return _begin; + } + + size_t length() const { + return _end - _begin; + } - template<typename T> - Status read(T* t, size_t offset = 0) const { - if (offset > length()) { - return makeOffsetStatus(offset); - } - - return DataType::load(t, _begin + offset, length() - offset, nullptr, - offset + _debug_offset); + template <typename T> + Status read(T* t, size_t offset = 0) const { + if (offset > length()) { + return makeOffsetStatus(offset); } - template<typename T> - StatusWith<T> read(std::size_t offset = 0) const { - T t(DataType::defaultConstruct<T>()); - Status s = read(&t, offset); + return DataType::load( + t, _begin + offset, length() - offset, nullptr, offset + _debug_offset); + } - if (s.isOK()) { - return StatusWith<T>(std::move(t)); - } else { - return StatusWith<T>(std::move(s)); - } - } - - friend bool operator==(const ConstDataRange& lhs, const ConstDataRange& rhs) { - return std::tie(lhs._begin, lhs._end) == std::tie(rhs._begin, rhs._end); + template <typename T> + StatusWith<T> read(std::size_t offset = 0) const { + T t(DataType::defaultConstruct<T>()); + Status s = read(&t, offset); + + if (s.isOK()) { + return StatusWith<T>(std::move(t)); + } else { + return StatusWith<T>(std::move(s)); } + } - friend bool operator!=(const ConstDataRange& lhs, const ConstDataRange& rhs) { - return !(lhs == rhs); - } + friend bool operator==(const ConstDataRange& lhs, const ConstDataRange& rhs) { + return std::tie(lhs._begin, lhs._end) == std::tie(rhs._begin, rhs._end); + } + friend bool operator!=(const ConstDataRange& lhs, const ConstDataRange& rhs) { + return !(lhs == rhs); + } - protected: - const char* _begin; - const char* _end; - std::ptrdiff_t _debug_offset; - Status makeOffsetStatus(size_t offset) const; +protected: + const char* _begin; + const char* _end; + std::ptrdiff_t _debug_offset; - }; + Status makeOffsetStatus(size_t offset) const; +}; - class DataRange : public ConstDataRange { +class DataRange : public ConstDataRange { +public: + typedef char* bytes_type; - public: - typedef char* bytes_type; + DataRange(bytes_type begin, bytes_type end, std::ptrdiff_t debug_offset = 0) + : ConstDataRange(begin, end, debug_offset) {} - DataRange(bytes_type begin, bytes_type end, std::ptrdiff_t debug_offset = 0) - : ConstDataRange(begin, end, debug_offset) { + template <typename T> + Status write(const T& value, std::size_t offset = 0) { + if (offset > length()) { + return makeOffsetStatus(offset); } - template<typename T> - Status write(const T& value, std::size_t offset = 0) { - if (offset > length()) { - return makeOffsetStatus(offset); - } - - return DataType::store(value, const_cast<char *>(_begin + offset), length() - offset, - nullptr, offset + _debug_offset); + return DataType::store(value, + const_cast<char*>(_begin + offset), + length() - offset, + nullptr, + offset + _debug_offset); + } +}; + +struct DataRangeTypeHelper { + static Status makeStoreStatus(size_t t_length, size_t length, std::ptrdiff_t debug_offset); +}; + +// Enable for classes derived from ConstDataRange +template <typename T> +struct DataType::Handler<T, + typename std::enable_if<std::is_base_of<ConstDataRange, T>::value>::type> { + static Status load( + T* t, const char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + if (t) { + // Assuming you know what you're doing at the read above this + // is fine. Either you're reading into a readable buffer, so + // ptr started off non-const, or the const_cast will feed back + // to const char* taking Const variants. So it'll get tossed + // out again. + *t = T(const_cast<char*>(ptr), const_cast<char*>(ptr) + length); } - }; - - struct DataRangeTypeHelper { - static Status makeStoreStatus(size_t t_length, size_t length, std::ptrdiff_t debug_offset); - }; - - // Enable for classes derived from ConstDataRange - template <typename T> - struct DataType::Handler<T, - typename std::enable_if<std::is_base_of<ConstDataRange, T>::value>::type> { - - static Status load(T* t, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) - { - if (t) { - // Assuming you know what you're doing at the read above this - // is fine. Either you're reading into a readable buffer, so - // ptr started off non-const, or the const_cast will feed back - // to const char* taking Const variants. So it'll get tossed - // out again. - *t = T(const_cast<char*>(ptr), const_cast<char*>(ptr) + length); - } - - if (advanced) { - *advanced = length; - } - - return Status::OK(); + if (advanced) { + *advanced = length; } - static Status store(const T& t, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) - { - if (t.length() > length) { - return DataRangeTypeHelper::makeStoreStatus(t.length(), length, debug_offset); - } - - if (ptr) { - std::memcpy(ptr, t.data(), t.length()); - } + return Status::OK(); + } - if (advanced) { - *advanced = t.length(); - } + static Status store( + const T& t, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + if (t.length() > length) { + return DataRangeTypeHelper::makeStoreStatus(t.length(), length, debug_offset); + } - return Status::OK(); + if (ptr) { + std::memcpy(ptr, t.data(), t.length()); } - static T defaultConstruct() - { - return T(nullptr, nullptr); + if (advanced) { + *advanced = t.length(); } - }; -} // namespace mongo + return Status::OK(); + } + + static T defaultConstruct() { + return T(nullptr, nullptr); + } +}; + +} // namespace mongo diff --git a/src/mongo/base/data_range_cursor.cpp b/src/mongo/base/data_range_cursor.cpp index 6779a8eef5b..1bf1bd0d321 100644 --- a/src/mongo/base/data_range_cursor.cpp +++ b/src/mongo/base/data_range_cursor.cpp @@ -31,20 +31,20 @@ namespace mongo { - Status ConstDataRangeCursor::makeAdvanceStatus(size_t advance) const { - mongoutils::str::stream ss; - ss << "Invalid advance (" << advance << ") past end of buffer[" << length() - << "] at offset: " << _debug_offset; +Status ConstDataRangeCursor::makeAdvanceStatus(size_t advance) const { + mongoutils::str::stream ss; + ss << "Invalid advance (" << advance << ") past end of buffer[" << length() + << "] at offset: " << _debug_offset; - return Status(ErrorCodes::Overflow, ss); - } + return Status(ErrorCodes::Overflow, ss); +} - Status DataRangeCursor::makeAdvanceStatus(size_t advance) const { - mongoutils::str::stream ss; - ss << "Invalid advance (" << advance << ") past end of buffer[" << length() - << "] at offset: " << _debug_offset; +Status DataRangeCursor::makeAdvanceStatus(size_t advance) const { + mongoutils::str::stream ss; + ss << "Invalid advance (" << advance << ") past end of buffer[" << length() + << "] at offset: " << _debug_offset; - return Status(ErrorCodes::Overflow, ss); - } + return Status(ErrorCodes::Overflow, ss); +} } // namespace mongo diff --git a/src/mongo/base/data_range_cursor.h b/src/mongo/base/data_range_cursor.h index c6ebffb996a..36eb4681eaf 100644 --- a/src/mongo/base/data_range_cursor.h +++ b/src/mongo/base/data_range_cursor.h @@ -38,157 +38,147 @@ namespace mongo { - class ConstDataRangeCursor : public ConstDataRange { - public: +class ConstDataRangeCursor : public ConstDataRange { +public: + ConstDataRangeCursor(const char* begin, const char* end, std::ptrdiff_t debug_offset = 0) + : ConstDataRange(begin, end, debug_offset) {} - ConstDataRangeCursor(const char* begin, const char* end, std::ptrdiff_t debug_offset = 0) - : ConstDataRange(begin, end, debug_offset) { - } + ConstDataRangeCursor(ConstDataRange cdr) : ConstDataRange(cdr) {} - ConstDataRangeCursor(ConstDataRange cdr) - : ConstDataRange(cdr) { + Status advance(size_t advance) { + if (advance > length()) { + return makeAdvanceStatus(advance); } - Status advance(size_t advance) { - if (advance > length()) { - return makeAdvanceStatus(advance); - } - - _begin += advance; - _debug_offset += advance; - - return Status::OK(); - } + _begin += advance; + _debug_offset += advance; - template <typename T> - Status skip() { - size_t advanced = 0; + return Status::OK(); + } - Status x = DataType::load<T>(nullptr, _begin, _end - _begin, &advanced, _debug_offset); + template <typename T> + Status skip() { + size_t advanced = 0; - if (x.isOK()) { - _begin += advanced; - _debug_offset += advanced; - } + Status x = DataType::load<T>(nullptr, _begin, _end - _begin, &advanced, _debug_offset); - return x; + if (x.isOK()) { + _begin += advanced; + _debug_offset += advanced; } - template <typename T> - Status readAndAdvance(T* t) { - size_t advanced = 0; + return x; + } - Status x = DataType::load(t, _begin, _end - _begin, &advanced, _debug_offset); + template <typename T> + Status readAndAdvance(T* t) { + size_t advanced = 0; - if (x.isOK()) { - _begin += advanced; - _debug_offset += advanced; - } + Status x = DataType::load(t, _begin, _end - _begin, &advanced, _debug_offset); - return x; + if (x.isOK()) { + _begin += advanced; + _debug_offset += advanced; } - template <typename T> - StatusWith<T> readAndAdvance() { - T out(DataType::defaultConstruct<T>()); - Status x = readAndAdvance(&out); + return x; + } - if (x.isOK()) { - return StatusWith<T>(std::move(out)); - } else { - return StatusWith<T>(std::move(x)); - } - } - - private: + template <typename T> + StatusWith<T> readAndAdvance() { + T out(DataType::defaultConstruct<T>()); + Status x = readAndAdvance(&out); - Status makeAdvanceStatus(size_t advance) const; + if (x.isOK()) { + return StatusWith<T>(std::move(out)); + } else { + return StatusWith<T>(std::move(x)); + } + } - }; +private: + Status makeAdvanceStatus(size_t advance) const; +}; - class DataRangeCursor : public DataRange { - public: +class DataRangeCursor : public DataRange { +public: + DataRangeCursor(char* begin, char* end, std::ptrdiff_t debug_offset = 0) + : DataRange(begin, end, debug_offset) {} - DataRangeCursor(char *begin, char *end, std::ptrdiff_t debug_offset = 0) - : DataRange(begin, end, debug_offset) {} + DataRangeCursor(DataRange range) : DataRange(range) {} - DataRangeCursor(DataRange range) - : DataRange(range) {} + operator ConstDataRangeCursor() const { + return ConstDataRangeCursor(ConstDataRange(_begin, _end, _debug_offset)); + } - operator ConstDataRangeCursor() const { - return ConstDataRangeCursor(ConstDataRange(_begin, _end, _debug_offset)); + Status advance(size_t advance) { + if (advance > length()) { + return makeAdvanceStatus(advance); } - Status advance(size_t advance) { - if (advance > length()) { - return makeAdvanceStatus(advance); - } + _begin += advance; + _debug_offset += advance; - _begin += advance; - _debug_offset += advance; + return Status::OK(); + } - return Status::OK(); - } - - template <typename T> - Status skip() { - size_t advanced = 0; - - Status x = DataType::load<T>(nullptr, _begin, _end - _begin, &advanced, _debug_offset); + template <typename T> + Status skip() { + size_t advanced = 0; - if (x.isOK()) { - _begin += advanced; - _debug_offset += advanced; - } + Status x = DataType::load<T>(nullptr, _begin, _end - _begin, &advanced, _debug_offset); - return x; + if (x.isOK()) { + _begin += advanced; + _debug_offset += advanced; } - template <typename T> - Status readAndAdvance(T* t) { - size_t advanced = 0; + return x; + } - Status x = DataType::load(t, _begin, _end - _begin, &advanced, _debug_offset); + template <typename T> + Status readAndAdvance(T* t) { + size_t advanced = 0; - if (x.isOK()) { - _begin += advanced; - _debug_offset += advanced; - } + Status x = DataType::load(t, _begin, _end - _begin, &advanced, _debug_offset); - return x; + if (x.isOK()) { + _begin += advanced; + _debug_offset += advanced; } - template <typename T> - StatusWith<T> readAndAdvance() { - T out(DataType::defaultConstruct<T>()); - Status x = readAndAdvance(&out); + return x; + } - if (x.isOK()) { - return StatusWith<T>(std::move(out)); - } else { - return StatusWith<T>(std::move(x)); - } - } + template <typename T> + StatusWith<T> readAndAdvance() { + T out(DataType::defaultConstruct<T>()); + Status x = readAndAdvance(&out); - template <typename T> - Status writeAndAdvance(const T& value) { - size_t advanced = 0; + if (x.isOK()) { + return StatusWith<T>(std::move(out)); + } else { + return StatusWith<T>(std::move(x)); + } + } - Status x = DataType::store(value, const_cast<char*>(_begin), _end - _begin, &advanced, - _debug_offset); + template <typename T> + Status writeAndAdvance(const T& value) { + size_t advanced = 0; - if (x.isOK()) { - _begin += advanced; - _debug_offset += advanced; - } + Status x = DataType::store( + value, const_cast<char*>(_begin), _end - _begin, &advanced, _debug_offset); - return x; + if (x.isOK()) { + _begin += advanced; + _debug_offset += advanced; } - private: - - Status makeAdvanceStatus(size_t advance) const; + return x; + } - }; +private: + Status makeAdvanceStatus(size_t advance) const; +}; -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_range_cursor_test.cpp b/src/mongo/base/data_range_cursor_test.cpp index 4ae839fb440..5e326aedecd 100644 --- a/src/mongo/base/data_range_cursor_test.cpp +++ b/src/mongo/base/data_range_cursor_test.cpp @@ -34,80 +34,79 @@ namespace mongo { - TEST(DataRangeCursor, ConstDataRangeCursor) { - char buf[14]; +TEST(DataRangeCursor, ConstDataRangeCursor) { + char buf[14]; - DataView(buf).write<uint16_t>(1); - DataView(buf).write<LittleEndian<uint32_t>>(2, sizeof(uint16_t)); - DataView(buf).write<BigEndian<uint64_t>>(3, sizeof(uint16_t) + sizeof(uint32_t)); + DataView(buf).write<uint16_t>(1); + DataView(buf).write<LittleEndian<uint32_t>>(2, sizeof(uint16_t)); + DataView(buf).write<BigEndian<uint64_t>>(3, sizeof(uint16_t) + sizeof(uint32_t)); - ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); - ConstDataRangeCursor backup(cdrc); + ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); + ConstDataRangeCursor backup(cdrc); - ASSERT_EQUALS(static_cast<uint16_t>(1), cdrc.readAndAdvance<uint16_t>().getValue()); - ASSERT_EQUALS(static_cast<uint32_t>(2), - cdrc.readAndAdvance<LittleEndian<uint32_t>>().getValue()); - ASSERT_EQUALS(static_cast<uint64_t>(3), - cdrc.readAndAdvance<BigEndian<uint64_t>>().getValue()); - ASSERT_EQUALS(false, cdrc.readAndAdvance<char>().isOK()); + ASSERT_EQUALS(static_cast<uint16_t>(1), cdrc.readAndAdvance<uint16_t>().getValue()); + ASSERT_EQUALS(static_cast<uint32_t>(2), + cdrc.readAndAdvance<LittleEndian<uint32_t>>().getValue()); + ASSERT_EQUALS(static_cast<uint64_t>(3), cdrc.readAndAdvance<BigEndian<uint64_t>>().getValue()); + ASSERT_EQUALS(false, cdrc.readAndAdvance<char>().isOK()); - // test skip() - cdrc = backup; - ASSERT_EQUALS(true, cdrc.skip<uint32_t>().isOK());; - ASSERT_EQUALS(true, cdrc.advance(10).isOK()); - ASSERT_EQUALS(false, cdrc.readAndAdvance<char>().isOK()); - } + // test skip() + cdrc = backup; + ASSERT_EQUALS(true, cdrc.skip<uint32_t>().isOK()); + ; + ASSERT_EQUALS(true, cdrc.advance(10).isOK()); + ASSERT_EQUALS(false, cdrc.readAndAdvance<char>().isOK()); +} - TEST(DataRangeCursor, ConstDataRangeCursorType) { - char buf[] = "foo"; +TEST(DataRangeCursor, ConstDataRangeCursorType) { + char buf[] = "foo"; - ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); + ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); - ConstDataRangeCursor out(nullptr, nullptr); + ConstDataRangeCursor out(nullptr, nullptr); - auto inner = cdrc.read(&out); + auto inner = cdrc.read(&out); - ASSERT_OK(inner); - ASSERT_EQUALS(buf, out.data()); - } + ASSERT_OK(inner); + ASSERT_EQUALS(buf, out.data()); +} - TEST(DataRangeCursor, DataRangeCursor) { - char buf[100] = { 0 }; +TEST(DataRangeCursor, DataRangeCursor) { + char buf[100] = {0}; - DataRangeCursor dc(buf, buf + 14); + DataRangeCursor dc(buf, buf + 14); - ASSERT_EQUALS(true, dc.writeAndAdvance<uint16_t>(1).isOK()); - ASSERT_EQUALS(true, dc.writeAndAdvance<LittleEndian<uint32_t>>(2).isOK()); - ASSERT_EQUALS(true, dc.writeAndAdvance<BigEndian<uint64_t>>(3).isOK()); - ASSERT_EQUALS(false, dc.writeAndAdvance<char>(1).isOK()); + ASSERT_EQUALS(true, dc.writeAndAdvance<uint16_t>(1).isOK()); + ASSERT_EQUALS(true, dc.writeAndAdvance<LittleEndian<uint32_t>>(2).isOK()); + ASSERT_EQUALS(true, dc.writeAndAdvance<BigEndian<uint64_t>>(3).isOK()); + ASSERT_EQUALS(false, dc.writeAndAdvance<char>(1).isOK()); - ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); + ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); - ASSERT_EQUALS(static_cast<uint16_t>(1), cdrc.readAndAdvance<uint16_t>().getValue()); - ASSERT_EQUALS(static_cast<uint32_t>(2), - cdrc.readAndAdvance<LittleEndian<uint32_t>>().getValue()); - ASSERT_EQUALS(static_cast<uint64_t>(3), - cdrc.readAndAdvance<BigEndian<uint64_t>>().getValue()); - ASSERT_EQUALS(static_cast<char>(0), cdrc.readAndAdvance<char>().getValue()); - } + ASSERT_EQUALS(static_cast<uint16_t>(1), cdrc.readAndAdvance<uint16_t>().getValue()); + ASSERT_EQUALS(static_cast<uint32_t>(2), + cdrc.readAndAdvance<LittleEndian<uint32_t>>().getValue()); + ASSERT_EQUALS(static_cast<uint64_t>(3), cdrc.readAndAdvance<BigEndian<uint64_t>>().getValue()); + ASSERT_EQUALS(static_cast<char>(0), cdrc.readAndAdvance<char>().getValue()); +} - TEST(DataRangeCursor, DataRangeCursorType) { - char buf[] = "foo"; - char buf2[] = "barZ"; +TEST(DataRangeCursor, DataRangeCursorType) { + char buf[] = "foo"; + char buf2[] = "barZ"; - DataRangeCursor drc(buf, buf + sizeof(buf) + -1); + DataRangeCursor drc(buf, buf + sizeof(buf) + -1); - DataRangeCursor out(nullptr, nullptr); + DataRangeCursor out(nullptr, nullptr); - Status status = drc.read(&out); + Status status = drc.read(&out); - ASSERT_OK(status); - ASSERT_EQUALS(buf, out.data()); + ASSERT_OK(status); + ASSERT_EQUALS(buf, out.data()); - drc = DataRangeCursor(buf2, buf2 + sizeof(buf2) + -1); - status = drc.write(out); + drc = DataRangeCursor(buf2, buf2 + sizeof(buf2) + -1); + status = drc.write(out); - ASSERT_OK(status); - ASSERT_EQUALS(std::string("fooZ"), buf2); - } -} // namespace mongo + ASSERT_OK(status); + ASSERT_EQUALS(std::string("fooZ"), buf2); +} +} // namespace mongo diff --git a/src/mongo/base/data_range_test.cpp b/src/mongo/base/data_range_test.cpp index e43ce7e9148..f31211ba899 100644 --- a/src/mongo/base/data_range_test.cpp +++ b/src/mongo/base/data_range_test.cpp @@ -36,79 +36,79 @@ namespace mongo { - TEST(DataRange, ConstDataRange) { - char buf[sizeof(uint32_t) * 3]; - uint32_t native = 1234; - uint32_t le = endian::nativeToLittle(native); - uint32_t be = endian::nativeToBig(native); +TEST(DataRange, ConstDataRange) { + char buf[sizeof(uint32_t) * 3]; + uint32_t native = 1234; + uint32_t le = endian::nativeToLittle(native); + uint32_t be = endian::nativeToBig(native); - std::memcpy(buf, &native, sizeof(uint32_t)); - std::memcpy(buf + sizeof(uint32_t), &le, sizeof(uint32_t)); - std::memcpy(buf + sizeof(uint32_t) * 2, &be, sizeof(uint32_t)); + std::memcpy(buf, &native, sizeof(uint32_t)); + std::memcpy(buf + sizeof(uint32_t), &le, sizeof(uint32_t)); + std::memcpy(buf + sizeof(uint32_t) * 2, &be, sizeof(uint32_t)); - ConstDataRange cdv(buf, buf + sizeof(buf)); + ConstDataRange cdv(buf, buf + sizeof(buf)); - ASSERT_EQUALS(native, cdv.read<uint32_t>().getValue()); - ASSERT_EQUALS(native, cdv.read<LittleEndian<uint32_t>>(sizeof(uint32_t)).getValue()); - ASSERT_EQUALS(native, cdv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2).getValue()); + ASSERT_EQUALS(native, cdv.read<uint32_t>().getValue()); + ASSERT_EQUALS(native, cdv.read<LittleEndian<uint32_t>>(sizeof(uint32_t)).getValue()); + ASSERT_EQUALS(native, cdv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2).getValue()); - auto result = cdv.read<uint32_t>(sizeof(uint32_t) * 3); - ASSERT_EQUALS(false, result.isOK()); - ASSERT_EQUALS(ErrorCodes::Overflow, result.getStatus().code()); - } + auto result = cdv.read<uint32_t>(sizeof(uint32_t) * 3); + ASSERT_EQUALS(false, result.isOK()); + ASSERT_EQUALS(ErrorCodes::Overflow, result.getStatus().code()); +} - TEST(DataRange, ConstDataRangeType) { - char buf[] = "foo"; +TEST(DataRange, ConstDataRangeType) { + char buf[] = "foo"; - ConstDataRange cdr(buf, buf + sizeof(buf)); + ConstDataRange cdr(buf, buf + sizeof(buf)); - ConstDataRange out(nullptr, nullptr); + ConstDataRange out(nullptr, nullptr); - auto inner = cdr.read(&out); + auto inner = cdr.read(&out); - ASSERT_OK(inner); - ASSERT_EQUALS(buf, out.data()); - } + ASSERT_OK(inner); + ASSERT_EQUALS(buf, out.data()); +} - TEST(DataRange, DataRange) { - char buf[sizeof(uint32_t) * 3]; - uint32_t native = 1234; +TEST(DataRange, DataRange) { + char buf[sizeof(uint32_t) * 3]; + uint32_t native = 1234; - DataRange dv(buf, buf + sizeof(buf)); + DataRange dv(buf, buf + sizeof(buf)); - ASSERT_EQUALS(true, dv.write(native).isOK()); - ASSERT_EQUALS(true, dv.write(LittleEndian<uint32_t>(native), sizeof(uint32_t)).isOK()); - ASSERT_EQUALS(true, dv.write(BigEndian<uint32_t>(native), sizeof(uint32_t) * 2).isOK()); + ASSERT_EQUALS(true, dv.write(native).isOK()); + ASSERT_EQUALS(true, dv.write(LittleEndian<uint32_t>(native), sizeof(uint32_t)).isOK()); + ASSERT_EQUALS(true, dv.write(BigEndian<uint32_t>(native), sizeof(uint32_t) * 2).isOK()); - auto result = dv.write(native, sizeof(uint32_t) * 3); - ASSERT_EQUALS(false, result.isOK()); - ASSERT_EQUALS(ErrorCodes::Overflow, result.code()); + auto result = dv.write(native, sizeof(uint32_t) * 3); + ASSERT_EQUALS(false, result.isOK()); + ASSERT_EQUALS(ErrorCodes::Overflow, result.code()); - ASSERT_EQUALS(native, dv.read<uint32_t>().getValue()); - ASSERT_EQUALS(native, dv.read<LittleEndian<uint32_t>>(sizeof(uint32_t)).getValue()); - ASSERT_EQUALS(native, dv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2).getValue()); + ASSERT_EQUALS(native, dv.read<uint32_t>().getValue()); + ASSERT_EQUALS(native, dv.read<LittleEndian<uint32_t>>(sizeof(uint32_t)).getValue()); + ASSERT_EQUALS(native, dv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2).getValue()); - ASSERT_EQUALS(false, dv.read<uint32_t>(sizeof(uint32_t) * 3).isOK()); - } + ASSERT_EQUALS(false, dv.read<uint32_t>(sizeof(uint32_t) * 3).isOK()); +} - TEST(DataRange, DataRangeType) { - char buf[] = "foo"; - char buf2[] = "barZ"; +TEST(DataRange, DataRangeType) { + char buf[] = "foo"; + char buf2[] = "barZ"; - DataRange dr(buf, buf + sizeof(buf) + -1); + DataRange dr(buf, buf + sizeof(buf) + -1); - DataRange out(nullptr, nullptr); + DataRange out(nullptr, nullptr); - Status status = dr.read(&out); + Status status = dr.read(&out); - ASSERT_OK(status); - ASSERT_EQUALS(buf, out.data()); + ASSERT_OK(status); + ASSERT_EQUALS(buf, out.data()); - dr = DataRange(buf2, buf2 + sizeof(buf2) + -1); - status = dr.write(out); + dr = DataRange(buf2, buf2 + sizeof(buf2) + -1); + status = dr.write(out); - ASSERT_OK(status); - ASSERT_EQUALS(std::string("fooZ"), buf2); - } + ASSERT_OK(status); + ASSERT_EQUALS(std::string("fooZ"), buf2); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_type.cpp b/src/mongo/base/data_type.cpp index 1ffcc3b677d..dc799a1c503 100644 --- a/src/mongo/base/data_type.cpp +++ b/src/mongo/base/data_type.cpp @@ -31,20 +31,18 @@ namespace mongo { - Status DataType::makeTrivialLoadStatus(size_t sizeOfT, size_t length, - size_t debug_offset) { - mongoutils::str::stream ss; - ss << "buffer size too small to read (" << sizeOfT << ") bytes out of buffer[" - << length << "] at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } +Status DataType::makeTrivialLoadStatus(size_t sizeOfT, size_t length, size_t debug_offset) { + mongoutils::str::stream ss; + ss << "buffer size too small to read (" << sizeOfT << ") bytes out of buffer[" << length + << "] at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); +} - Status DataType::makeTrivialStoreStatus(size_t sizeOfT, size_t length, - size_t debug_offset) { - mongoutils::str::stream ss; - ss << "buffer size too small to write (" << sizeOfT << ") bytes into buffer[" - << length << "] at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } +Status DataType::makeTrivialStoreStatus(size_t sizeOfT, size_t length, size_t debug_offset) { + mongoutils::str::stream ss; + ss << "buffer size too small to write (" << sizeOfT << ") bytes into buffer[" << length + << "] at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); +} } // namespace mongo diff --git a/src/mongo/base/data_type.h b/src/mongo/base/data_type.h index 78f7e034a71..d25929e3601 100644 --- a/src/mongo/base/data_type.h +++ b/src/mongo/base/data_type.h @@ -38,138 +38,130 @@ namespace mongo { - struct DataType { - - // Second template parameter allows templatized SFINAE specialization. - // - // Something like: - // template <typename T, typename std::enable_if<std::is_CONDITION<T>::value>::type> - // struct Handler { ... }; - // - // That would allow you to constrain your specialization to all T's - // that std::is_CONDITION<T> - // - // Again, note that you probably don't ever want to use this second - // parameter for anything. If you're not interested in template meta - // programming to duck type in a specialization, you can pretend that - // this just says template <typename T>. - - template <typename T, typename = void> - struct Handler { - - static void unsafeLoad(T* t, const char* ptr, size_t* advanced) - { +struct DataType { + // Second template parameter allows templatized SFINAE specialization. + // + // Something like: + // template <typename T, typename std::enable_if<std::is_CONDITION<T>::value>::type> + // struct Handler { ... }; + // + // That would allow you to constrain your specialization to all T's + // that std::is_CONDITION<T> + // + // Again, note that you probably don't ever want to use this second + // parameter for anything. If you're not interested in template meta + // programming to duck type in a specialization, you can pretend that + // this just says template <typename T>. + + template <typename T, typename = void> + struct Handler { + static void unsafeLoad(T* t, const char* ptr, size_t* advanced) { #if MONGO_HAVE_STD_IS_TRIVIALLY_COPYABLE - static_assert(std::is_trivially_copyable<T>::value, - "The generic DataType implementation requires values " - "to be trivially copyable. You may specialize the " - "template to use it with other types."); + static_assert(std::is_trivially_copyable<T>::value, + "The generic DataType implementation requires values " + "to be trivially copyable. You may specialize the " + "template to use it with other types."); #endif - if (t) { - std::memcpy(t, ptr, sizeof(T)); - } + if (t) { + std::memcpy(t, ptr, sizeof(T)); + } - if (advanced) { - *advanced = sizeof(T); - } + if (advanced) { + *advanced = sizeof(T); } + } - static Status load(T* t, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) - { - if (sizeof(T) > length) { - return DataType::makeTrivialLoadStatus(sizeof(T), length, debug_offset); - } + static Status load( + T* t, const char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + if (sizeof(T) > length) { + return DataType::makeTrivialLoadStatus(sizeof(T), length, debug_offset); + } - unsafeLoad(t, ptr, advanced); + unsafeLoad(t, ptr, advanced); - return Status::OK(); - } + return Status::OK(); + } - static void unsafeStore(const T& t, char* ptr, size_t* advanced) - { + static void unsafeStore(const T& t, char* ptr, size_t* advanced) { #if MONGO_HAVE_STD_IS_TRIVIALLY_COPYABLE - static_assert(std::is_trivially_copyable<T>::value, - "The generic DataType implementation requires values " - "to be trivially copyable. You may specialize the " - "template to use it with other types."); + static_assert(std::is_trivially_copyable<T>::value, + "The generic DataType implementation requires values " + "to be trivially copyable. You may specialize the " + "template to use it with other types."); #endif - if (ptr) { - std::memcpy(ptr, &t, sizeof(T)); - } - - if (advanced) { - *advanced = sizeof(T); - } + if (ptr) { + std::memcpy(ptr, &t, sizeof(T)); } - static Status store(const T& t, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) - { - if (sizeof(T) > length) { - return DataType::makeTrivialStoreStatus(sizeof(T), length, debug_offset); - } - - unsafeStore(t, ptr, advanced); - - return Status::OK(); + if (advanced) { + *advanced = sizeof(T); } + } - // It may be useful to specialize this for types that aren't natively - // default constructible. Otherwise there's no way for us to support - // that body of types (other than wrapping them with another tagged - // type). Also, this guarantees value/aggregate initialization, which - // guarantees no uninitialized memory leaks from load's, which gcc - // otherwise can't seem to see. - static T defaultConstruct() - { - return T{}; + static Status store( + const T& t, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + if (sizeof(T) > length) { + return DataType::makeTrivialStoreStatus(sizeof(T), length, debug_offset); } - }; - - // The following dispatch functions don't just save typing, they also work - // around what seems like template type deduction bugs (for template - // specializations) in gcc. I.e. for sufficiently complicated workflows (a - // specialization for tuple), going through dispatch functions compiles on - // gcc 4.9 and using DataType<T> does not. - - // We return a status and take an out pointer so that we can: - // - // 1. Run a load without returning a value (I.e. skip / validate) - // 2. Load directly into a remote structure, rather than forcing moves of - // possibly large objects - template <typename T> - static Status load(T* t, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - return Handler<T>::load(t, ptr, length, advanced, debug_offset); - } - template <typename T> - static Status store(const T& t, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - return Handler<T>::store(t, ptr, length, advanced, debug_offset); - } + unsafeStore(t, ptr, advanced); - template <typename T> - static void unsafeLoad(T* t, const char* ptr, size_t* advanced) { - Handler<T>::unsafeLoad(t, ptr, advanced); + return Status::OK(); } - template <typename T> - static void unsafeStore(const T& t, char* ptr, size_t* advanced) { - Handler<T>::unsafeStore(t, ptr, advanced); - } - - template <typename T> + // It may be useful to specialize this for types that aren't natively + // default constructible. Otherwise there's no way for us to support + // that body of types (other than wrapping them with another tagged + // type). Also, this guarantees value/aggregate initialization, which + // guarantees no uninitialized memory leaks from load's, which gcc + // otherwise can't seem to see. static T defaultConstruct() { - return Handler<T>::defaultConstruct(); + return T{}; } - - static Status makeTrivialStoreStatus(size_t sizeOfT, size_t length, size_t debug_offset); - static Status makeTrivialLoadStatus(size_t sizeOfT, size_t length, size_t debug_offset); - }; -} // namespace mongo + // The following dispatch functions don't just save typing, they also work + // around what seems like template type deduction bugs (for template + // specializations) in gcc. I.e. for sufficiently complicated workflows (a + // specialization for tuple), going through dispatch functions compiles on + // gcc 4.9 and using DataType<T> does not. + + // We return a status and take an out pointer so that we can: + // + // 1. Run a load without returning a value (I.e. skip / validate) + // 2. Load directly into a remote structure, rather than forcing moves of + // possibly large objects + template <typename T> + static Status load( + T* t, const char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + return Handler<T>::load(t, ptr, length, advanced, debug_offset); + } + + template <typename T> + static Status store( + const T& t, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + return Handler<T>::store(t, ptr, length, advanced, debug_offset); + } + + template <typename T> + static void unsafeLoad(T* t, const char* ptr, size_t* advanced) { + Handler<T>::unsafeLoad(t, ptr, advanced); + } + + template <typename T> + static void unsafeStore(const T& t, char* ptr, size_t* advanced) { + Handler<T>::unsafeStore(t, ptr, advanced); + } + + template <typename T> + static T defaultConstruct() { + return Handler<T>::defaultConstruct(); + } + + static Status makeTrivialStoreStatus(size_t sizeOfT, size_t length, size_t debug_offset); + static Status makeTrivialLoadStatus(size_t sizeOfT, size_t length, size_t debug_offset); +}; + +} // namespace mongo diff --git a/src/mongo/base/data_type_endian.h b/src/mongo/base/data_type_endian.h index ee3c12aab09..860abed79cf 100644 --- a/src/mongo/base/data_type_endian.h +++ b/src/mongo/base/data_type_endian.h @@ -36,130 +36,133 @@ namespace mongo { - template <typename T> - struct BigEndian { - BigEndian() {} - BigEndian(T t) : value(t) {} - T value; - - operator T() const { - return value; - } - }; - - template <typename T> - BigEndian<T> tagBigEndian(T t) { - return t; +template <typename T> +struct BigEndian { + BigEndian() {} + BigEndian(T t) : value(t) {} + T value; + + operator T() const { + return value; } +}; - template <typename T> - struct LittleEndian { - LittleEndian() {} - LittleEndian(T t) : value(t) {} - T value; +template <typename T> +BigEndian<T> tagBigEndian(T t) { + return t; +} - operator T() const { - return value; - } - }; +template <typename T> +struct LittleEndian { + LittleEndian() {} + LittleEndian(T t) : value(t) {} + T value; - template <typename T> - LittleEndian<T> tagLittleEndian(T t) { - return t; + operator T() const { + return value; } - - template <typename T> - struct DataType::Handler<BigEndian<T>> { - static void unsafeLoad(BigEndian<T>* t, const char* ptr, size_t* advanced) { - if (t) { - DataType::unsafeLoad(&t->value, ptr, advanced); - - t->value = endian::bigToNative(t->value); - } - else { - DataType::unsafeLoad(decltype(&t->value){nullptr}, ptr, advanced); - } +}; + +template <typename T> +LittleEndian<T> tagLittleEndian(T t) { + return t; +} + +template <typename T> +struct DataType::Handler<BigEndian<T>> { + static void unsafeLoad(BigEndian<T>* t, const char* ptr, size_t* advanced) { + if (t) { + DataType::unsafeLoad(&t->value, ptr, advanced); + + t->value = endian::bigToNative(t->value); + } else { + DataType::unsafeLoad(decltype(&t->value){nullptr}, ptr, advanced); } + } - static Status load(BigEndian<T>* t, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - if (t) { - Status x = DataType::load(&t->value, ptr, length, advanced, debug_offset); - - if (x.isOK()) { - t->value = endian::bigToNative(t->value); - } + static Status load(BigEndian<T>* t, + const char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + if (t) { + Status x = DataType::load(&t->value, ptr, length, advanced, debug_offset); - return x; - } - else { - return DataType::load(decltype(&t->value){nullptr}, ptr, length, advanced, - debug_offset); + if (x.isOK()) { + t->value = endian::bigToNative(t->value); } - } - static void unsafeStore(const BigEndian<T>& t, char* ptr, size_t* advanced) { - DataType::unsafeStore(endian::nativeToBig(t.value), ptr, advanced); + return x; + } else { + return DataType::load( + decltype(&t->value){nullptr}, ptr, length, advanced, debug_offset); } + } - static Status store(const BigEndian<T>& t, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - return DataType::store(endian::nativeToBig(t.value), ptr, length, advanced, - debug_offset); - } + static void unsafeStore(const BigEndian<T>& t, char* ptr, size_t* advanced) { + DataType::unsafeStore(endian::nativeToBig(t.value), ptr, advanced); + } - static BigEndian<T> defaultConstruct() - { - return DataType::defaultConstruct<T>(); - } + static Status store(const BigEndian<T>& t, + char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + return DataType::store(endian::nativeToBig(t.value), ptr, length, advanced, debug_offset); + } - }; + static BigEndian<T> defaultConstruct() { + return DataType::defaultConstruct<T>(); + } +}; - template <typename T> - struct DataType::Handler<LittleEndian<T>> { - static void unsafeLoad(LittleEndian<T>* t, const char* ptr, size_t* advanced) { - if (t) { - DataType::unsafeLoad(&t->value, ptr, advanced); +template <typename T> +struct DataType::Handler<LittleEndian<T>> { + static void unsafeLoad(LittleEndian<T>* t, const char* ptr, size_t* advanced) { + if (t) { + DataType::unsafeLoad(&t->value, ptr, advanced); - t->value = endian::littleToNative(t->value); - } - else { - DataType::unsafeLoad(decltype(&t->value){nullptr}, ptr, advanced); - } + t->value = endian::littleToNative(t->value); + } else { + DataType::unsafeLoad(decltype(&t->value){nullptr}, ptr, advanced); } + } - static Status load(LittleEndian<T>* t, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - if (t) { - Status x = DataType::load(&t->value, ptr, length, advanced, debug_offset); - - if (x.isOK()) { - t->value = endian::littleToNative(t->value); - } + static Status load(LittleEndian<T>* t, + const char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + if (t) { + Status x = DataType::load(&t->value, ptr, length, advanced, debug_offset); - return x; - } - else { - return DataType::load(decltype(&t->value){nullptr}, ptr, length, advanced, - debug_offset); + if (x.isOK()) { + t->value = endian::littleToNative(t->value); } - } - static void unsafeStore(const LittleEndian<T>& t, char* ptr, size_t* advanced) { - DataType::unsafeStore(endian::nativeToLittle(t.value), ptr, advanced); + return x; + } else { + return DataType::load( + decltype(&t->value){nullptr}, ptr, length, advanced, debug_offset); } + } - static Status store(const LittleEndian<T>& t, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - return DataType::store(endian::nativeToLittle(t.value), ptr, length, advanced, - debug_offset); - } + static void unsafeStore(const LittleEndian<T>& t, char* ptr, size_t* advanced) { + DataType::unsafeStore(endian::nativeToLittle(t.value), ptr, advanced); + } - static LittleEndian<T> defaultConstruct() - { - return DataType::defaultConstruct<T>(); - } + static Status store(const LittleEndian<T>& t, + char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + return DataType::store( + endian::nativeToLittle(t.value), ptr, length, advanced, debug_offset); + } - }; + static LittleEndian<T> defaultConstruct() { + return DataType::defaultConstruct<T>(); + } +}; -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_type_string_data.cpp b/src/mongo/base/data_type_string_data.cpp index ab009c2cadc..40b550b62d3 100644 --- a/src/mongo/base/data_type_string_data.cpp +++ b/src/mongo/base/data_type_string_data.cpp @@ -31,13 +31,13 @@ namespace mongo { - Status DataType::Handler<StringData>::makeStoreStatus( - const StringData& sdata, size_t length, std::ptrdiff_t debug_offset) { - - str::stream ss; - ss << "buffer size too small to write StringData(" << sdata.size() << ") bytes into buffer[" - << length << "] at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } +Status DataType::Handler<StringData>::makeStoreStatus(const StringData& sdata, + size_t length, + std::ptrdiff_t debug_offset) { + str::stream ss; + ss << "buffer size too small to write StringData(" << sdata.size() << ") bytes into buffer[" + << length << "] at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); +} } // namespace mongo diff --git a/src/mongo/base/data_type_string_data.h b/src/mongo/base/data_type_string_data.h index ae7fdbbf75a..cebbdfbc988 100644 --- a/src/mongo/base/data_type_string_data.h +++ b/src/mongo/base/data_type_string_data.h @@ -33,46 +33,52 @@ namespace mongo { - template <> - struct DataType::Handler<StringData> { - static Status load(StringData* sdata, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - if (sdata) { - *sdata = StringData(ptr, length); - } - - if (advanced) { - *advanced = length; - } - - return Status::OK(); +template <> +struct DataType::Handler<StringData> { + static Status load(StringData* sdata, + const char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + if (sdata) { + *sdata = StringData(ptr, length); } - static Status store(const StringData& sdata, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - if (sdata.size() > length) { - return makeStoreStatus(sdata, length, debug_offset); - } + if (advanced) { + *advanced = length; + } - if (ptr) { - std::memcpy(ptr, sdata.rawData(), sdata.size()); - } + return Status::OK(); + } - if (advanced) { - *advanced = sdata.size(); - } + static Status store(const StringData& sdata, + char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + if (sdata.size() > length) { + return makeStoreStatus(sdata, length, debug_offset); + } - return Status::OK(); + if (ptr) { + std::memcpy(ptr, sdata.rawData(), sdata.size()); } - static StringData defaultConstruct() { - return StringData(); + if (advanced) { + *advanced = sdata.size(); } - private: - static Status makeStoreStatus(const StringData& sdata, size_t length, - std::ptrdiff_t debug_offset); + return Status::OK(); + } + + static StringData defaultConstruct() { + return StringData(); + } - }; +private: + static Status makeStoreStatus(const StringData& sdata, + size_t length, + std::ptrdiff_t debug_offset); +}; -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_type_string_data_test.cpp b/src/mongo/base/data_type_string_data_test.cpp index ff873f71c43..a452bfe4447 100644 --- a/src/mongo/base/data_type_string_data_test.cpp +++ b/src/mongo/base/data_type_string_data_test.cpp @@ -35,36 +35,36 @@ namespace mongo { - TEST(DataTypeStringData, Basic) { - char buf[100]; - StringData a("a"); - StringData b("bb"); - StringData c("ccc"); +TEST(DataTypeStringData, Basic) { + char buf[100]; + StringData a("a"); + StringData b("bb"); + StringData c("ccc"); - { - DataRangeCursor drc(buf, buf + sizeof(buf)); + { + DataRangeCursor drc(buf, buf + sizeof(buf)); - ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', StringData>(a))); - ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', StringData>(b))); - ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', StringData>(c))); + ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', StringData>(a))); + ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', StringData>(b))); + ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', StringData>(c))); - ASSERT_EQUALS(1 + 2 + 3 + 3, drc.data() - buf); - } + ASSERT_EQUALS(1 + 2 + 3 + 3, drc.data() - buf); + } - { - ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); + { + ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); - Terminated<'\0', StringData> tsd; + Terminated<'\0', StringData> tsd; - ASSERT_OK(cdrc.readAndAdvance(&tsd)); - ASSERT_EQUALS(a, tsd.value); + ASSERT_OK(cdrc.readAndAdvance(&tsd)); + ASSERT_EQUALS(a, tsd.value); - ASSERT_OK(cdrc.readAndAdvance(&tsd)); - ASSERT_EQUALS(b, tsd.value); + ASSERT_OK(cdrc.readAndAdvance(&tsd)); + ASSERT_EQUALS(b, tsd.value); - ASSERT_OK(cdrc.readAndAdvance(&tsd)); - ASSERT_EQUALS(c, tsd.value); - } + ASSERT_OK(cdrc.readAndAdvance(&tsd)); + ASSERT_EQUALS(c, tsd.value); } +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_type_terminated.cpp b/src/mongo/base/data_type_terminated.cpp index 29dff36f2ae..6171fd8b10d 100644 --- a/src/mongo/base/data_type_terminated.cpp +++ b/src/mongo/base/data_type_terminated.cpp @@ -31,28 +31,31 @@ namespace mongo { - Status TerminatedHelper::makeLoadNoTerminalStatus(char c, size_t length, - std::ptrdiff_t debug_offset) { - str::stream ss; - ss << "couldn't locate terminal char (" << c << ") in buffer[" << length - << "] at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } - - Status TerminatedHelper::makeLoadShortReadStatus(char c, size_t read, size_t length, - std::ptrdiff_t debug_offset) { - str::stream ss; - ss << "only read (" << read << ") bytes. (" << length << ") bytes to terminal char (" << c - << ") at offset: " << debug_offset; - - return Status(ErrorCodes::Overflow, ss); - } - - Status TerminatedHelper::makeStoreStatus(char c, size_t length, std::ptrdiff_t debug_offset) { - str::stream ss; - ss << "couldn't write terminal char (" << c << ") in buffer[" << length - << "] at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } +Status TerminatedHelper::makeLoadNoTerminalStatus(char c, + size_t length, + std::ptrdiff_t debug_offset) { + str::stream ss; + ss << "couldn't locate terminal char (" << c << ") in buffer[" << length + << "] at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); +} + +Status TerminatedHelper::makeLoadShortReadStatus(char c, + size_t read, + size_t length, + std::ptrdiff_t debug_offset) { + str::stream ss; + ss << "only read (" << read << ") bytes. (" << length << ") bytes to terminal char (" << c + << ") at offset: " << debug_offset; + + return Status(ErrorCodes::Overflow, ss); +} + +Status TerminatedHelper::makeStoreStatus(char c, size_t length, std::ptrdiff_t debug_offset) { + str::stream ss; + ss << "couldn't write terminal char (" << c << ") in buffer[" << length + << "] at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); +} } // namespace mongo diff --git a/src/mongo/base/data_type_terminated.h b/src/mongo/base/data_type_terminated.h index 0c2e8847bfc..57fe61dbf9a 100644 --- a/src/mongo/base/data_type_terminated.h +++ b/src/mongo/base/data_type_terminated.h @@ -33,86 +33,91 @@ namespace mongo { - template <char C, typename T> - struct Terminated { - Terminated() : value(DataType::defaultConstruct<T>()) {} - Terminated(T value) : value(std::move(value)) {} - T value; - - operator T() const { - return value; +template <char C, typename T> +struct Terminated { + Terminated() : value(DataType::defaultConstruct<T>()) {} + Terminated(T value) : value(std::move(value)) {} + T value; + + operator T() const { + return value; + } +}; + +struct TerminatedHelper { + static Status makeLoadNoTerminalStatus(char c, size_t length, std::ptrdiff_t debug_offset); + static Status makeLoadShortReadStatus(char c, + size_t read, + size_t length, + std::ptrdiff_t debug_offset); + static Status makeStoreStatus(char c, size_t length, std::ptrdiff_t debug_offset); +}; + +template <char C, typename T> +struct DataType::Handler<Terminated<C, T>> { + using TerminatedType = Terminated<C, T>; + + static Status load(TerminatedType* tt, + const char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + size_t local_advanced = 0; + + const char* end = static_cast<const char*>(std::memchr(ptr, C, length)); + + if (!end) { + return TerminatedHelper::makeLoadNoTerminalStatus(C, length, debug_offset); } - }; - struct TerminatedHelper { - static Status makeLoadNoTerminalStatus(char c, size_t length, std::ptrdiff_t debug_offset); - static Status makeLoadShortReadStatus(char c, size_t read, size_t length, - std::ptrdiff_t debug_offset); - static Status makeStoreStatus(char c, size_t length, std::ptrdiff_t debug_offset); - }; + auto status = DataType::load( + tt ? &tt->value : nullptr, ptr, end - ptr, &local_advanced, debug_offset); - template <char C, typename T> - struct DataType::Handler<Terminated<C, T>> { - using TerminatedType = Terminated<C, T>; - - static Status load(TerminatedType* tt, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) - { - size_t local_advanced = 0; - - const char* end = static_cast<const char*>(std::memchr(ptr, C, length)); - - if (!end) { - return TerminatedHelper::makeLoadNoTerminalStatus(C, length, debug_offset); - } - - auto status = DataType::load(tt ? &tt->value : nullptr, ptr, end - ptr, &local_advanced, - debug_offset); - - if (!status.isOK()) { - return status; - } - - if (local_advanced != static_cast<size_t>(end - ptr)) { - return TerminatedHelper::makeLoadShortReadStatus(C, local_advanced, end - ptr, - debug_offset); - } + if (!status.isOK()) { + return status; + } - if (advanced) { - *advanced = local_advanced + 1; - } + if (local_advanced != static_cast<size_t>(end - ptr)) { + return TerminatedHelper::makeLoadShortReadStatus( + C, local_advanced, end - ptr, debug_offset); + } - return Status::OK(); + if (advanced) { + *advanced = local_advanced + 1; } - static Status store(const TerminatedType& tt, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) - { - size_t local_advanced = 0; + return Status::OK(); + } - auto status = DataType::store(tt.value, ptr, length, &local_advanced, debug_offset); + static Status store(const TerminatedType& tt, + char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + size_t local_advanced = 0; - if (!status.isOK()) { - return status; - } + auto status = DataType::store(tt.value, ptr, length, &local_advanced, debug_offset); - if (length - local_advanced < 1) { - return TerminatedHelper::makeStoreStatus(C, length, debug_offset + local_advanced); - } + if (!status.isOK()) { + return status; + } - ptr[local_advanced] = C; + if (length - local_advanced < 1) { + return TerminatedHelper::makeStoreStatus(C, length, debug_offset + local_advanced); + } - if (advanced) { - *advanced = local_advanced + 1; - } + ptr[local_advanced] = C; - return Status::OK(); + if (advanced) { + *advanced = local_advanced + 1; } - static TerminatedType defaultConstruct() { - return TerminatedType(); - } + return Status::OK(); + } - }; + static TerminatedType defaultConstruct() { + return TerminatedType(); + } +}; -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_type_terminated_test.cpp b/src/mongo/base/data_type_terminated_test.cpp index 364cbd2c359..e0192b53dd5 100644 --- a/src/mongo/base/data_type_terminated_test.cpp +++ b/src/mongo/base/data_type_terminated_test.cpp @@ -34,39 +34,39 @@ namespace mongo { - TEST(DataTypeTerminated, Basic) { - char buf[100]; - char a[] = "a"; - char b[] = "bb"; - char c[] = "ccc"; +TEST(DataTypeTerminated, Basic) { + char buf[100]; + char a[] = "a"; + char b[] = "bb"; + char c[] = "ccc"; - { - DataRangeCursor drc(buf, buf + sizeof(buf)); - ConstDataRange cdr_a(a, a + sizeof(a) + -1); - ConstDataRange cdr_b(b, b + sizeof(b) + -1); - ConstDataRange cdr_c(c, c + sizeof(c) + -1); + { + DataRangeCursor drc(buf, buf + sizeof(buf)); + ConstDataRange cdr_a(a, a + sizeof(a) + -1); + ConstDataRange cdr_b(b, b + sizeof(b) + -1); + ConstDataRange cdr_c(c, c + sizeof(c) + -1); - ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', ConstDataRange>(cdr_a))); - ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', ConstDataRange>(cdr_b))); - ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', ConstDataRange>(cdr_c))); + ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', ConstDataRange>(cdr_a))); + ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', ConstDataRange>(cdr_b))); + ASSERT_OK(drc.writeAndAdvance(Terminated<'\0', ConstDataRange>(cdr_c))); - ASSERT_EQUALS(1 + 2 + 3 + 3, drc.data() - buf); - } + ASSERT_EQUALS(1 + 2 + 3 + 3, drc.data() - buf); + } - { - ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); + { + ConstDataRangeCursor cdrc(buf, buf + sizeof(buf)); - Terminated<'\0', ConstDataRange> tcdr; + Terminated<'\0', ConstDataRange> tcdr; - ASSERT_OK(cdrc.readAndAdvance(&tcdr)); - ASSERT_EQUALS(std::string(a), tcdr.value.data()); + ASSERT_OK(cdrc.readAndAdvance(&tcdr)); + ASSERT_EQUALS(std::string(a), tcdr.value.data()); - ASSERT_OK(cdrc.readAndAdvance(&tcdr)); - ASSERT_EQUALS(std::string(b), tcdr.value.data()); + ASSERT_OK(cdrc.readAndAdvance(&tcdr)); + ASSERT_EQUALS(std::string(b), tcdr.value.data()); - ASSERT_OK(cdrc.readAndAdvance(&tcdr)); - ASSERT_EQUALS(std::string(c), tcdr.value.data()); - } + ASSERT_OK(cdrc.readAndAdvance(&tcdr)); + ASSERT_EQUALS(std::string(c), tcdr.value.data()); } +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_type_validated.h b/src/mongo/base/data_type_validated.h index e13bb03ea60..92b56c14f5f 100644 --- a/src/mongo/base/data_type_validated.h +++ b/src/mongo/base/data_type_validated.h @@ -33,118 +33,119 @@ namespace mongo { +/** + * Allows for specializations of load/store that run validation logic. + * + * To add validation for your T: + * 1) ensure that there are DataType::Handler<T> specializations for your type + * 2) implement a specialization of Validator<T> for your type. The two methods + * you must implement are: + * - Status validateLoad(const char* ptr, size_t length); + * - Status validateStore(const T& toStore); + * + * See bson_validate.h for an example. + * + * Then you can use Validated<T> in a DataRange (and associated types) + * + * Example: + * + * DataRangeCursor drc(buf, buf_end); + * Validated<MyObj> vobj; + * auto status = drc.readAndAdvance(&vobj); + * if (status.isOK()) { + * // use vobj.val + * // .... + * } + */ +template <typename T> +struct Validator { + // These methods are intentionally unimplemented so that if the default validator + // is instantiated, the resulting binary will not link. + /** - * Allows for specializations of load/store that run validation logic. - * - * To add validation for your T: - * 1) ensure that there are DataType::Handler<T> specializations for your type - * 2) implement a specialization of Validator<T> for your type. The two methods - * you must implement are: - * - Status validateLoad(const char* ptr, size_t length); - * - Status validateStore(const T& toStore); - * - * See bson_validate.h for an example. - * - * Then you can use Validated<T> in a DataRange (and associated types) - * - * Example: - * - * DataRangeCursor drc(buf, buf_end); - * Validated<MyObj> vobj; - * auto status = drc.readAndAdvance(&vobj); - * if (status.isOK()) { - * // use vobj.val - * // .... - * } + * Checks that the provided buffer contains at least 1 valid object of type T. + * The length parameter is the size of the buffer, not the size of the object. + * Specializations of this function should be hardened to malicious input from untrusted + * sources. */ - template <typename T> - struct Validator { - - // These methods are intentionally unimplemented so that if the default validator - // is instantiated, the resulting binary will not link. - - /** - * Checks that the provided buffer contains at least 1 valid object of type T. - * The length parameter is the size of the buffer, not the size of the object. - * Specializations of this function should be hardened to malicious input from untrusted - * sources. - */ - static Status validateLoad(const char* ptr, size_t length); - - /** - * Checks that the provided object is valid to store in a buffer. - */ - static Status validateStore(const T& toStore); - }; - - template <typename T> - struct Validated { - - Validated() = default; - Validated(T value) : val(std::move(value)) {} - - operator T&() { - return val; - } - - T val = DataType::defaultConstruct<T>(); - }; - - template <typename T> - struct DataType::Handler<Validated<T>> { - - static Status load(Validated<T>* vt, const char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - - size_t local_advanced = 0; + static Status validateLoad(const char* ptr, size_t length); - auto valid = Validator<T>::validateLoad(ptr, length); - - if (!valid.isOK()) { - return valid; - } - - auto loadStatus = DataType::load(vt ? &vt->val : nullptr, ptr, length, &local_advanced, - debug_offset); - - if (!loadStatus.isOK()) { - return loadStatus; - } + /** + * Checks that the provided object is valid to store in a buffer. + */ + static Status validateStore(const T& toStore); +}; + +template <typename T> +struct Validated { + Validated() = default; + Validated(T value) : val(std::move(value)) {} + + operator T&() { + return val; + } + + T val = DataType::defaultConstruct<T>(); +}; + +template <typename T> +struct DataType::Handler<Validated<T>> { + static Status load(Validated<T>* vt, + const char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + size_t local_advanced = 0; + + auto valid = Validator<T>::validateLoad(ptr, length); + + if (!valid.isOK()) { + return valid; + } - if (advanced) { - *advanced = local_advanced; - } + auto loadStatus = + DataType::load(vt ? &vt->val : nullptr, ptr, length, &local_advanced, debug_offset); - return Status::OK(); + if (!loadStatus.isOK()) { + return loadStatus; } - static Status store(const Validated<T>& vt, char* ptr, size_t length, size_t* advanced, - std::ptrdiff_t debug_offset) { - - size_t local_advanced = 0; + if (advanced) { + *advanced = local_advanced; + } - auto valid = Validator<T>::validateStore(vt.val); + return Status::OK(); + } - if (!valid.isOK()) { - return valid; - } + static Status store(const Validated<T>& vt, + char* ptr, + size_t length, + size_t* advanced, + std::ptrdiff_t debug_offset) { + size_t local_advanced = 0; - auto storeStatus = DataType::store(vt.val, ptr, length, &local_advanced, debug_offset); + auto valid = Validator<T>::validateStore(vt.val); - if (!storeStatus.isOK()) { - return storeStatus; - } + if (!valid.isOK()) { + return valid; + } - if (advanced) { - *advanced = local_advanced; - } + auto storeStatus = DataType::store(vt.val, ptr, length, &local_advanced, debug_offset); - return Status::OK(); + if (!storeStatus.isOK()) { + return storeStatus; } - static Validated<T> defaultConstruct() { - return Validated<T>(); + if (advanced) { + *advanced = local_advanced; } - }; + + return Status::OK(); + } + + static Validated<T> defaultConstruct() { + return Validated<T>(); + } +}; } // namespace mongo diff --git a/src/mongo/base/data_type_validated_test.cpp b/src/mongo/base/data_type_validated_test.cpp index 2a42fe40c12..bad4ab218d4 100644 --- a/src/mongo/base/data_type_validated_test.cpp +++ b/src/mongo/base/data_type_validated_test.cpp @@ -39,62 +39,61 @@ #include "mongo/unittest/unittest.h" namespace mongo { - template<> struct Validator<char> { - static Status validateLoad(const char* ptr, size_t length) { - if ((length >= sizeof(char)) && (ptr[0] == 0xFU)) { - return Status::OK(); - } - return Status(ErrorCodes::BadValue, "bad"); +template <> +struct Validator<char> { + static Status validateLoad(const char* ptr, size_t length) { + if ((length >= sizeof(char)) && (ptr[0] == 0xFU)) { + return Status::OK(); } + return Status(ErrorCodes::BadValue, "bad"); + } - static Status validateStore(const char& toStore) { - if (toStore == 0xFU) { - return Status::OK(); - } - return Status(ErrorCodes::BadValue, "bad"); + static Status validateStore(const char& toStore) { + if (toStore == 0xFU) { + return Status::OK(); } - }; + return Status(ErrorCodes::BadValue, "bad"); + } +}; } // namespace mongo namespace { - using namespace mongo; - using std::end; - using std::begin; - - TEST(DataTypeValidated, SuccessfulValidation) { +using namespace mongo; +using std::end; +using std::begin; - char buf[1]; - - { - DataRangeCursor drc(begin(buf), end(buf)); - ASSERT_OK(drc.writeAndAdvance(Validated<char>(0xFU))); - } +TEST(DataTypeValidated, SuccessfulValidation) { + char buf[1]; - { - Validated<char> valid; - ConstDataRangeCursor cdrc(begin(buf), end(buf)); - ASSERT_OK(cdrc.readAndAdvance(&valid)); - ASSERT_EQUALS(valid.val, char{0xFU}); - } + { + DataRangeCursor drc(begin(buf), end(buf)); + ASSERT_OK(drc.writeAndAdvance(Validated<char>(0xFU))); } - TEST(DataTypeValidated, FailedValidation) { + { + Validated<char> valid; + ConstDataRangeCursor cdrc(begin(buf), end(buf)); + ASSERT_OK(cdrc.readAndAdvance(&valid)); + ASSERT_EQUALS(valid.val, char{0xFU}); + } +} - char buf[1]; +TEST(DataTypeValidated, FailedValidation) { + char buf[1]; - { - DataRangeCursor drc(begin(buf), end(buf)); - ASSERT_NOT_OK(drc.writeAndAdvance(Validated<char>(0x01))); - } + { + DataRangeCursor drc(begin(buf), end(buf)); + ASSERT_NOT_OK(drc.writeAndAdvance(Validated<char>(0x01))); + } - buf[0] = char{0x01}; + buf[0] = char{0x01}; - { - Validated<char> valid; - ConstDataRangeCursor cdrc(begin(buf), end(buf)); - ASSERT_NOT_OK(cdrc.readAndAdvance(&valid)); - } + { + Validated<char> valid; + ConstDataRangeCursor cdrc(begin(buf), end(buf)); + ASSERT_NOT_OK(cdrc.readAndAdvance(&valid)); } +} } // namespace diff --git a/src/mongo/base/data_view.h b/src/mongo/base/data_view.h index 3de3237065d..a0b1dc52111 100644 --- a/src/mongo/base/data_view.h +++ b/src/mongo/base/data_view.h @@ -36,60 +36,54 @@ namespace mongo { - class ConstDataView { +class ConstDataView { +public: + typedef const char* bytes_type; - public: - typedef const char* bytes_type; + ConstDataView(bytes_type bytes) : _bytes(bytes) {} - ConstDataView(bytes_type bytes) - : _bytes(bytes) { - } + bytes_type view(std::size_t offset = 0) const { + return _bytes + offset; + } - bytes_type view(std::size_t offset = 0) const { - return _bytes + offset; - } + template <typename T> + const ConstDataView& read(T* t, size_t offset = 0) const { + DataType::unsafeLoad(t, view(offset), nullptr); - template<typename T> - const ConstDataView& read(T* t, size_t offset = 0) const { - DataType::unsafeLoad(t, view(offset), nullptr); + return *this; + } - return *this; - } + template <typename T> + T read(std::size_t offset = 0) const { + T t(DataType::defaultConstruct<T>()); - template<typename T> - T read(std::size_t offset = 0) const { - T t(DataType::defaultConstruct<T>()); + read(&t, offset); - read(&t, offset); + return t; + } - return t; - } +private: + bytes_type _bytes; +}; - private: - bytes_type _bytes; - }; +class DataView : public ConstDataView { +public: + typedef char* bytes_type; - class DataView : public ConstDataView { + DataView(bytes_type bytes) : ConstDataView(bytes) {} - public: - typedef char* bytes_type; + bytes_type view(std::size_t offset = 0) const { + // It is safe to cast away const here since the pointer stored in our base class was + // originally non-const by way of our constructor. + return const_cast<bytes_type>(ConstDataView::view(offset)); + } - DataView(bytes_type bytes) - : ConstDataView(bytes) { - } + template <typename T> + DataView& write(const T& value, std::size_t offset = 0) { + DataType::unsafeStore(value, view(offset), nullptr); - bytes_type view(std::size_t offset = 0) const { - // It is safe to cast away const here since the pointer stored in our base class was - // originally non-const by way of our constructor. - return const_cast<bytes_type>(ConstDataView::view(offset)); - } + return *this; + } +}; - template<typename T> - DataView& write(const T& value, std::size_t offset = 0) { - DataType::unsafeStore(value, view(offset), nullptr); - - return *this; - } - }; - -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/data_view_test.cpp b/src/mongo/base/data_view_test.cpp index f7ecc82039e..aa0dfbb441f 100644 --- a/src/mongo/base/data_view_test.cpp +++ b/src/mongo/base/data_view_test.cpp @@ -36,42 +36,42 @@ namespace mongo { - TEST(DataView, ConstDataView) { - char buf[sizeof(uint32_t) * 3]; - uint32_t native = 1234; - uint32_t le = endian::nativeToLittle(native); - uint32_t be = endian::nativeToBig(native); +TEST(DataView, ConstDataView) { + char buf[sizeof(uint32_t) * 3]; + uint32_t native = 1234; + uint32_t le = endian::nativeToLittle(native); + uint32_t be = endian::nativeToBig(native); - std::memcpy(buf, &native, sizeof(uint32_t)); - std::memcpy(buf + sizeof(uint32_t), &le, sizeof(uint32_t)); - std::memcpy(buf + sizeof(uint32_t) * 2, &be, sizeof(uint32_t)); + std::memcpy(buf, &native, sizeof(uint32_t)); + std::memcpy(buf + sizeof(uint32_t), &le, sizeof(uint32_t)); + std::memcpy(buf + sizeof(uint32_t) * 2, &be, sizeof(uint32_t)); - ConstDataView cdv(buf); + ConstDataView cdv(buf); - ASSERT_EQUALS(buf, cdv.view()); - ASSERT_EQUALS(buf + 5, cdv.view(5)); + ASSERT_EQUALS(buf, cdv.view()); + ASSERT_EQUALS(buf + 5, cdv.view(5)); - ASSERT_EQUALS(native, cdv.read<uint32_t>()); - ASSERT_EQUALS(native, cdv.read<LittleEndian<uint32_t>>(sizeof(uint32_t))); - ASSERT_EQUALS(native, cdv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2)); - } + ASSERT_EQUALS(native, cdv.read<uint32_t>()); + ASSERT_EQUALS(native, cdv.read<LittleEndian<uint32_t>>(sizeof(uint32_t))); + ASSERT_EQUALS(native, cdv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2)); +} - TEST(DataView, DataView) { - char buf[sizeof(uint32_t) * 3]; - uint32_t native = 1234; +TEST(DataView, DataView) { + char buf[sizeof(uint32_t) * 3]; + uint32_t native = 1234; - DataView dv(buf); + DataView dv(buf); - dv.write(native); - dv.write(LittleEndian<uint32_t>(native), sizeof(uint32_t)); - dv.write(BigEndian<uint32_t>(native), sizeof(uint32_t) * 2); + dv.write(native); + dv.write(LittleEndian<uint32_t>(native), sizeof(uint32_t)); + dv.write(BigEndian<uint32_t>(native), sizeof(uint32_t) * 2); - ASSERT_EQUALS(buf, dv.view()); - ASSERT_EQUALS(buf + 5, dv.view(5)); + ASSERT_EQUALS(buf, dv.view()); + ASSERT_EQUALS(buf + 5, dv.view(5)); - ASSERT_EQUALS(native, dv.read<uint32_t>()); - ASSERT_EQUALS(native, dv.read<LittleEndian<uint32_t>>(sizeof(uint32_t))); - ASSERT_EQUALS(native, dv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2)); - } + ASSERT_EQUALS(native, dv.read<uint32_t>()); + ASSERT_EQUALS(native, dv.read<LittleEndian<uint32_t>>(sizeof(uint32_t))); + ASSERT_EQUALS(native, dv.read<BigEndian<uint32_t>>(sizeof(uint32_t) * 2)); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/disallow_copying.h b/src/mongo/base/disallow_copying.h index 9a3b68903ea..015b663c712 100644 --- a/src/mongo/base/disallow_copying.h +++ b/src/mongo/base/disallow_copying.h @@ -42,6 +42,6 @@ * ... * }; */ -#define MONGO_DISALLOW_COPYING(CLASS) \ - CLASS(const CLASS&) = delete; \ +#define MONGO_DISALLOW_COPYING(CLASS) \ + CLASS(const CLASS&) = delete; \ CLASS& operator=(const CLASS&) = delete diff --git a/src/mongo/base/encoded_value_storage.h b/src/mongo/base/encoded_value_storage.h index 9d83aa7617b..c7022de6821 100644 --- a/src/mongo/base/encoded_value_storage.h +++ b/src/mongo/base/encoded_value_storage.h @@ -33,54 +33,51 @@ namespace mongo { - struct ZeroInitTag_t { - ZeroInitTag_t() { - }; - }; +struct ZeroInitTag_t { + ZeroInitTag_t(){}; +}; - const ZeroInitTag_t kZeroInitTag; +const ZeroInitTag_t kZeroInitTag; - template<typename Layout, typename ConstView, typename View> - class EncodedValueStorage { - protected: - EncodedValueStorage() { - } +template <typename Layout, typename ConstView, typename View> +class EncodedValueStorage { +protected: + EncodedValueStorage() {} - // This explicit constructor is provided to allow for easy zeroing - // during creation of a value. You might prefer this over an - // uninitialised value if the zeroed version provides a useful base - // state. Such cases might include a set of counters that begin at - // zero, flags that start off false or a larger structure where some - // significant portion of storage falls into those kind of use cases. - // Use this where you might have used calloc(1, sizeof(type)) in C. - // - // The added value of providing it as a constructor lies in the ability - // of subclasses to easily inherit a zeroed base state during - // initialization. - explicit EncodedValueStorage(ZeroInitTag_t) { - std::memset(_data, 0, sizeof(_data)); - } + // This explicit constructor is provided to allow for easy zeroing + // during creation of a value. You might prefer this over an + // uninitialised value if the zeroed version provides a useful base + // state. Such cases might include a set of counters that begin at + // zero, flags that start off false or a larger structure where some + // significant portion of storage falls into those kind of use cases. + // Use this where you might have used calloc(1, sizeof(type)) in C. + // + // The added value of providing it as a constructor lies in the ability + // of subclasses to easily inherit a zeroed base state during + // initialization. + explicit EncodedValueStorage(ZeroInitTag_t) { + std::memset(_data, 0, sizeof(_data)); + } - public: +public: + View view() { + return _data; + } - View view() { - return _data; - } + ConstView constView() const { + return _data; + } - ConstView constView() const { - return _data; - } + operator View() { + return view(); + } - operator View() { - return view(); - } + operator ConstView() const { + return constView(); + } - operator ConstView() const { - return constView(); - } +private: + char _data[sizeof(Layout)]; +}; - private: - char _data[sizeof(Layout)]; - }; - -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/encoded_value_storage_test.cpp b/src/mongo/base/encoded_value_storage_test.cpp index 07f73c532b9..682e11a5d35 100644 --- a/src/mongo/base/encoded_value_storage_test.cpp +++ b/src/mongo/base/encoded_value_storage_test.cpp @@ -40,107 +40,106 @@ namespace mongo { namespace EncodedValueStorageTest { #pragma pack(1) - struct Layout { - uint32_t native; - uint32_t le; - uint32_t be; - }; +struct Layout { + uint32_t native; + uint32_t le; + uint32_t be; +}; #pragma pack() - class ConstView { - public: - typedef ConstDataView view_type; +class ConstView { +public: + typedef ConstDataView view_type; - ConstView(const char* data) : _data(data) { } + ConstView(const char* data) : _data(data) {} - const char* view2ptr() const { - return data().view(); - } - - uint32_t getNative() { - return data().read<uint32_t>(offsetof(Layout, native)); - } + const char* view2ptr() const { + return data().view(); + } - uint32_t getLE() { - return data().read<LittleEndian<uint32_t>>(offsetof(Layout, le)); - } + uint32_t getNative() { + return data().read<uint32_t>(offsetof(Layout, native)); + } - uint32_t getBE() { - return data().read<BigEndian<uint32_t>>(offsetof(Layout, be)); - } + uint32_t getLE() { + return data().read<LittleEndian<uint32_t>>(offsetof(Layout, le)); + } - protected: - const view_type& data() const { - return _data; - } + uint32_t getBE() { + return data().read<BigEndian<uint32_t>>(offsetof(Layout, be)); + } - private: - view_type _data; - }; +protected: + const view_type& data() const { + return _data; + } - class View : public ConstView { - public: - typedef DataView view_type; +private: + view_type _data; +}; - View(char* data) : ConstView(data) {} +class View : public ConstView { +public: + typedef DataView view_type; - using ConstView::view2ptr; - char* view2ptr() { - return data().view(); - } + View(char* data) : ConstView(data) {} - void setNative(uint32_t value) { - data().write(value, offsetof(Layout, native)); - } + using ConstView::view2ptr; + char* view2ptr() { + return data().view(); + } - void setLE(uint32_t value) { - data().write(tagLittleEndian(value), offsetof(Layout, le)); - } + void setNative(uint32_t value) { + data().write(value, offsetof(Layout, native)); + } - void setBE(uint32_t value) { - data().write(tagBigEndian(value), offsetof(Layout, be)); - } + void setLE(uint32_t value) { + data().write(tagLittleEndian(value), offsetof(Layout, le)); + } - private: - view_type data() const { - return const_cast<char *>(ConstView::view2ptr()); - } - }; + void setBE(uint32_t value) { + data().write(tagBigEndian(value), offsetof(Layout, be)); + } - class Value : public EncodedValueStorage<Layout, ConstView, View> { - public: - Value() { - BOOST_STATIC_ASSERT(sizeof(Value) == sizeof(Layout)); - } +private: + view_type data() const { + return const_cast<char*>(ConstView::view2ptr()); + } +}; - Value(ZeroInitTag_t zit) : EncodedValueStorage<Layout, ConstView, View>(zit) {} - }; +class Value : public EncodedValueStorage<Layout, ConstView, View> { +public: + Value() { + BOOST_STATIC_ASSERT(sizeof(Value) == sizeof(Layout)); + } + Value(ZeroInitTag_t zit) : EncodedValueStorage<Layout, ConstView, View>(zit) {} +}; } - TEST(EncodedValueStorage, EncodedValueStorage) { - EncodedValueStorageTest::Value raw; - EncodedValueStorageTest::Value zerod(kZeroInitTag); - char buf[sizeof(EncodedValueStorageTest::Layout)] = { 0 }; - - ASSERT_EQUALS(raw.view().view2ptr(), raw.constView().view2ptr()); - - // ensure zeroing with the init tag works - ASSERT_EQUALS(std::memcmp(zerod.view().view2ptr(), buf, sizeof(buf)), 0); - - // see if value assignment and view() works - zerod.view().setNative(1234); - EncodedValueStorageTest::View(buf).setNative(1234); - raw = zerod; - ASSERT_EQUALS(std::memcmp(raw.view().view2ptr(), buf, sizeof(buf)), 0); - - // see if view() and constView() work appropriately - raw.view().setNative(1); - raw.view().setLE(2); - raw.view().setBE(3); - ASSERT_EQUALS(static_cast<uint32_t>(1), raw.constView().getNative()); - ASSERT_EQUALS(static_cast<uint32_t>(2), raw.constView().getLE()); - ASSERT_EQUALS(static_cast<uint32_t>(3), raw.constView().getBE()); - } +TEST(EncodedValueStorage, EncodedValueStorage) { + EncodedValueStorageTest::Value raw; + EncodedValueStorageTest::Value zerod(kZeroInitTag); + char buf[sizeof(EncodedValueStorageTest::Layout)] = {0}; + + ASSERT_EQUALS(raw.view().view2ptr(), raw.constView().view2ptr()); + + // ensure zeroing with the init tag works + ASSERT_EQUALS(std::memcmp(zerod.view().view2ptr(), buf, sizeof(buf)), 0); + + // see if value assignment and view() works + zerod.view().setNative(1234); + EncodedValueStorageTest::View(buf).setNative(1234); + raw = zerod; + ASSERT_EQUALS(std::memcmp(raw.view().view2ptr(), buf, sizeof(buf)), 0); + + // see if view() and constView() work appropriately + raw.view().setNative(1); + raw.view().setLE(2); + raw.view().setBE(3); + ASSERT_EQUALS(static_cast<uint32_t>(1), raw.constView().getNative()); + ASSERT_EQUALS(static_cast<uint32_t>(2), raw.constView().getLE()); + ASSERT_EQUALS(static_cast<uint32_t>(3), raw.constView().getBE()); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/global_initializer.cpp b/src/mongo/base/global_initializer.cpp index 73991180e50..93fb850be0b 100644 --- a/src/mongo/base/global_initializer.cpp +++ b/src/mongo/base/global_initializer.cpp @@ -31,17 +31,17 @@ namespace mongo { - Initializer& getGlobalInitializer() { - static Initializer theGlobalInitializer; - return theGlobalInitializer; - } +Initializer& getGlobalInitializer() { + static Initializer theGlobalInitializer; + return theGlobalInitializer; +} namespace { - // Make sure that getGlobalInitializer() is called at least once before main(), and so at least - // once in a single-threaded context. Otherwise, static initialization inside - // getGlobalInitializer() won't be thread-safe. - Initializer* _theGlobalInitializer = &getGlobalInitializer(); +// Make sure that getGlobalInitializer() is called at least once before main(), and so at least +// once in a single-threaded context. Otherwise, static initialization inside +// getGlobalInitializer() won't be thread-safe. +Initializer* _theGlobalInitializer = &getGlobalInitializer(); } // namespace diff --git a/src/mongo/base/global_initializer.h b/src/mongo/base/global_initializer.h index 2c2e42745ab..a1742789408 100644 --- a/src/mongo/base/global_initializer.h +++ b/src/mongo/base/global_initializer.h @@ -28,14 +28,14 @@ #pragma once namespace mongo { - class Initializer; +class Initializer; - /** - * Get the process-global initializer object. - * - * See mongo/base/initializer.h and mongo/base/init.h for information about process - * initialization in mongo applications. - */ - Initializer& getGlobalInitializer(); +/** + * Get the process-global initializer object. + * + * See mongo/base/initializer.h and mongo/base/init.h for information about process + * initialization in mongo applications. + */ +Initializer& getGlobalInitializer(); } // namespace mongo diff --git a/src/mongo/base/global_initializer_registerer.cpp b/src/mongo/base/global_initializer_registerer.cpp index 4e23b0e70ee..f9fb925ef44 100644 --- a/src/mongo/base/global_initializer_registerer.cpp +++ b/src/mongo/base/global_initializer_registerer.cpp @@ -35,21 +35,19 @@ namespace mongo { - GlobalInitializerRegisterer::GlobalInitializerRegisterer( - const std::string& name, - const InitializerFunction& fn, - const std::vector<std::string>& prerequisites, - const std::vector<std::string>& dependents) { - - Status status = getGlobalInitializer().getInitializerDependencyGraph().addInitializer( - name, fn, prerequisites, dependents); - - - if (Status::OK() != status) { - std::cerr << "Attempt to add global initializer failed, status: " - << status << std::endl; - ::abort(); - } +GlobalInitializerRegisterer::GlobalInitializerRegisterer( + const std::string& name, + const InitializerFunction& fn, + const std::vector<std::string>& prerequisites, + const std::vector<std::string>& dependents) { + Status status = getGlobalInitializer().getInitializerDependencyGraph().addInitializer( + name, fn, prerequisites, dependents); + + + if (Status::OK() != status) { + std::cerr << "Attempt to add global initializer failed, status: " << status << std::endl; + ::abort(); } +} } // namespace mongo diff --git a/src/mongo/base/global_initializer_registerer.h b/src/mongo/base/global_initializer_registerer.h index c9d70d9fbb7..78e77b242cd 100644 --- a/src/mongo/base/global_initializer_registerer.h +++ b/src/mongo/base/global_initializer_registerer.h @@ -36,21 +36,21 @@ namespace mongo { - /** - * Type representing the act of registering a process-global intialization function. - * - * Create a module-global instance of this type to register a new initializer, to be run by a - * call to a variant of mongo::runGlobalInitializers(). See mongo/base/initializer.h, - * mongo/base/init.h and mongo/base/initializer_dependency_graph.h for details. - */ - class GlobalInitializerRegisterer { - MONGO_DISALLOW_COPYING(GlobalInitializerRegisterer); +/** + * Type representing the act of registering a process-global intialization function. + * + * Create a module-global instance of this type to register a new initializer, to be run by a + * call to a variant of mongo::runGlobalInitializers(). See mongo/base/initializer.h, + * mongo/base/init.h and mongo/base/initializer_dependency_graph.h for details. + */ +class GlobalInitializerRegisterer { + MONGO_DISALLOW_COPYING(GlobalInitializerRegisterer); - public: - GlobalInitializerRegisterer(const std::string& name, - const InitializerFunction& fn, - const std::vector<std::string>& prerequisites, - const std::vector<std::string>& dependents); - }; +public: + GlobalInitializerRegisterer(const std::string& name, + const InitializerFunction& fn, + const std::vector<std::string>& prerequisites, + const std::vector<std::string>& dependents); +}; } // namespace mongo diff --git a/src/mongo/base/init.h b/src/mongo/base/init.h index 5bcc074ef70..2bae4441319 100644 --- a/src/mongo/base/init.h +++ b/src/mongo/base/init.h @@ -115,15 +115,15 @@ * A form that takes an existing function or that lets the programmer supply the name * of the function to declare would be options. */ -#define MONGO_INITIALIZER_GENERAL(NAME, PREREQUISITES, DEPENDENTS) \ +#define MONGO_INITIALIZER_GENERAL(NAME, PREREQUISITES, DEPENDENTS) \ ::mongo::Status _MONGO_INITIALIZER_FUNCTION_NAME(NAME)(::mongo::InitializerContext*); \ - namespace { \ - ::mongo::GlobalInitializerRegisterer _mongoInitializerRegisterer_##NAME( \ - #NAME, \ - _MONGO_INITIALIZER_FUNCTION_NAME(NAME), \ - MONGO_MAKE_STRING_VECTOR PREREQUISITES, \ - MONGO_MAKE_STRING_VECTOR DEPENDENTS); \ - } \ + namespace { \ + ::mongo::GlobalInitializerRegisterer _mongoInitializerRegisterer_##NAME( \ + #NAME, \ + _MONGO_INITIALIZER_FUNCTION_NAME(NAME), \ + MONGO_MAKE_STRING_VECTOR PREREQUISITES, \ + MONGO_MAKE_STRING_VECTOR DEPENDENTS); \ + } \ ::mongo::Status _MONGO_INITIALIZER_FUNCTION_NAME(NAME) /** @@ -133,9 +133,10 @@ * initialization steps into phases, such as "all global parameter declarations completed", "all * global parameters initialized". */ -#define MONGO_INITIALIZER_GROUP(NAME, PREREQUISITES, DEPENDENTS) \ - MONGO_INITIALIZER_GENERAL(NAME, PREREQUISITES, DEPENDENTS)( \ - ::mongo::InitializerContext*) { return ::mongo::Status::OK(); } +#define MONGO_INITIALIZER_GROUP(NAME, PREREQUISITES, DEPENDENTS) \ + MONGO_INITIALIZER_GENERAL(NAME, PREREQUISITES, DEPENDENTS)(::mongo::InitializerContext*) { \ + return ::mongo::Status::OK(); \ + } /** * Macro to produce a name for a mongo initializer function for an initializer operation diff --git a/src/mongo/base/initializer.cpp b/src/mongo/base/initializer.cpp index 77e6c34caa3..9bd95915d4d 100644 --- a/src/mongo/base/initializer.cpp +++ b/src/mongo/base/initializer.cpp @@ -34,68 +34,67 @@ namespace mongo { - Initializer::Initializer() {} - Initializer::~Initializer() {} - - Status Initializer::execute(const InitializerContext::ArgumentVector& args, - const InitializerContext::EnvironmentMap& env) const { +Initializer::Initializer() {} +Initializer::~Initializer() {} + +Status Initializer::execute(const InitializerContext::ArgumentVector& args, + const InitializerContext::EnvironmentMap& env) const { + std::vector<std::string> sortedNodes; + Status status = _graph.topSort(&sortedNodes); + if (Status::OK() != status) + return status; + + InitializerContext context(args, env); + + for (size_t i = 0; i < sortedNodes.size(); ++i) { + InitializerFunction fn = _graph.getInitializerFunction(sortedNodes[i]); + if (!fn) { + return Status(ErrorCodes::InternalError, + "topSort returned a node that has no associated function: \"" + + sortedNodes[i] + '"'); + } + try { + status = fn(&context); + } catch (const DBException& xcp) { + return xcp.toStatus(); + } - std::vector<std::string> sortedNodes; - Status status = _graph.topSort(&sortedNodes); if (Status::OK() != status) return status; - - InitializerContext context(args, env); - - for (size_t i = 0; i < sortedNodes.size(); ++i) { - InitializerFunction fn = _graph.getInitializerFunction(sortedNodes[i]); - if (!fn) { - return Status(ErrorCodes::InternalError, - "topSort returned a node that has no associated function: \"" + - sortedNodes[i] + '"'); - } - try { - status = fn(&context); - } catch( const DBException& xcp ) { - return xcp.toStatus(); - } - - if (Status::OK() != status) - return status; - } - return Status::OK(); } + return Status::OK(); +} - Status runGlobalInitializers(const InitializerContext::ArgumentVector& args, - const InitializerContext::EnvironmentMap& env) { - return getGlobalInitializer().execute(args, env); - } +Status runGlobalInitializers(const InitializerContext::ArgumentVector& args, + const InitializerContext::EnvironmentMap& env) { + return getGlobalInitializer().execute(args, env); +} - Status runGlobalInitializers(int argc, const char* const* argv, const char* const* envp) { - InitializerContext::ArgumentVector args(argc); - std::copy(argv, argv + argc, args.begin()); +Status runGlobalInitializers(int argc, const char* const* argv, const char* const* envp) { + InitializerContext::ArgumentVector args(argc); + std::copy(argv, argv + argc, args.begin()); - InitializerContext::EnvironmentMap env; + InitializerContext::EnvironmentMap env; - if (envp) { - for(; *envp; ++envp) { - const char* firstEqualSign = strchr(*envp, '='); - if (!firstEqualSign) { - return Status(ErrorCodes::BadValue, "malformed environment block"); - } - env[std::string(*envp, firstEqualSign)] = std::string(firstEqualSign + 1); + if (envp) { + for (; *envp; ++envp) { + const char* firstEqualSign = strchr(*envp, '='); + if (!firstEqualSign) { + return Status(ErrorCodes::BadValue, "malformed environment block"); } + env[std::string(*envp, firstEqualSign)] = std::string(firstEqualSign + 1); } - - return runGlobalInitializers(args, env); } - void runGlobalInitializersOrDie(int argc, const char* const* argv, const char* const* envp) { - Status status = runGlobalInitializers(argc, argv, envp); - if (!status.isOK()) { - std::cerr << "Failed global initialization: " << status << std::endl; - quickExit(1); - } + return runGlobalInitializers(args, env); +} + +void runGlobalInitializersOrDie(int argc, const char* const* argv, const char* const* envp) { + Status status = runGlobalInitializers(argc, argv, envp); + if (!status.isOK()) { + std::cerr << "Failed global initialization: " << status << std::endl; + quickExit(1); } +} } // namespace mongo diff --git a/src/mongo/base/initializer.h b/src/mongo/base/initializer.h index a8abc6603b3..fa4ba508981 100644 --- a/src/mongo/base/initializer.h +++ b/src/mongo/base/initializer.h @@ -37,59 +37,59 @@ namespace mongo { - /** - * Class representing an initialization process. - * - * Such a process is described by a directed acyclic graph of initialization operations, the - * InitializerDependencyGraph. One constructs an initialization process by adding nodes and - * edges to the graph. Then, one executes the process, causing each initialization operation to - * execute in an order that respects the programmer-established prerequistes. - */ - class Initializer { - MONGO_DISALLOW_COPYING(Initializer); - public: - Initializer(); - ~Initializer(); - - /** - * Get the initializer dependency graph, presumably for the purpose of adding more nodes. - */ - InitializerDependencyGraph& getInitializerDependencyGraph() { return _graph; } - - /** - * Execute the initializer process, using the given argv and environment data as input. - * - * Returns Status::OK on success. All other returns constitute initialization failures, - * and the thing being initialized should be considered dead in the water. - */ - Status execute(const InitializerContext::ArgumentVector& args, - const InitializerContext::EnvironmentMap& env) const; +/** + * Class representing an initialization process. + * + * Such a process is described by a directed acyclic graph of initialization operations, the + * InitializerDependencyGraph. One constructs an initialization process by adding nodes and + * edges to the graph. Then, one executes the process, causing each initialization operation to + * execute in an order that respects the programmer-established prerequistes. + */ +class Initializer { + MONGO_DISALLOW_COPYING(Initializer); - private: +public: + Initializer(); + ~Initializer(); - InitializerDependencyGraph _graph; - }; + /** + * Get the initializer dependency graph, presumably for the purpose of adding more nodes. + */ + InitializerDependencyGraph& getInitializerDependencyGraph() { + return _graph; + } /** - * Run the global initializers. + * Execute the initializer process, using the given argv and environment data as input. * - * It's a programming error for this to fail, but if it does it will return a status other - * than Status::OK. - * - * This means that the few initializers that might want to terminate the program by failing - * should probably arrange to terminate the process themselves. + * Returns Status::OK on success. All other returns constitute initialization failures, + * and the thing being initialized should be considered dead in the water. */ - Status runGlobalInitializers(const InitializerContext::ArgumentVector& args, - const InitializerContext::EnvironmentMap& env); + Status execute(const InitializerContext::ArgumentVector& args, + const InitializerContext::EnvironmentMap& env) const; - Status runGlobalInitializers( - int argc, const char* const* argv, const char* const* envp); +private: + InitializerDependencyGraph _graph; +}; - /** - * Same as runGlobalInitializers(), except prints a brief message to std::cerr - * and terminates the process on failure. - */ - void runGlobalInitializersOrDie( - int argc, const char* const* argv, const char* const* envp); +/** + * Run the global initializers. + * + * It's a programming error for this to fail, but if it does it will return a status other + * than Status::OK. + * + * This means that the few initializers that might want to terminate the program by failing + * should probably arrange to terminate the process themselves. + */ +Status runGlobalInitializers(const InitializerContext::ArgumentVector& args, + const InitializerContext::EnvironmentMap& env); + +Status runGlobalInitializers(int argc, const char* const* argv, const char* const* envp); + +/** + * Same as runGlobalInitializers(), except prints a brief message to std::cerr + * and terminates the process on failure. + */ +void runGlobalInitializersOrDie(int argc, const char* const* argv, const char* const* envp); } // namespace mongo diff --git a/src/mongo/base/initializer_context.cpp b/src/mongo/base/initializer_context.cpp index 2020a59a342..40299c134ba 100644 --- a/src/mongo/base/initializer_context.cpp +++ b/src/mongo/base/initializer_context.cpp @@ -29,8 +29,7 @@ namespace mongo { - InitializerContext::InitializerContext(const ArgumentVector& args, - const EnvironmentMap& env) - : _args(args), _env(env) {} +InitializerContext::InitializerContext(const ArgumentVector& args, const EnvironmentMap& env) + : _args(args), _env(env) {} } // namespace mongo diff --git a/src/mongo/base/initializer_context.h b/src/mongo/base/initializer_context.h index 7642972f35a..4414439065d 100644 --- a/src/mongo/base/initializer_context.h +++ b/src/mongo/base/initializer_context.h @@ -35,27 +35,30 @@ namespace mongo { - /** - * Context of an initialization process. Passed as a parameter to initialization functions. - * - * See mongo/base/initializer.h and mongo/base/initializer_dependency_graph.h for more details. - */ - class InitializerContext { - MONGO_DISALLOW_COPYING(InitializerContext); - - public: - typedef std::vector<std::string> ArgumentVector; - typedef std::map<std::string, std::string> EnvironmentMap; - - InitializerContext(const ArgumentVector& args, - const EnvironmentMap& env); - - const ArgumentVector& args() const { return _args; } - const EnvironmentMap& env() const { return _env; } - - private: - ArgumentVector _args; - EnvironmentMap _env; - }; +/** + * Context of an initialization process. Passed as a parameter to initialization functions. + * + * See mongo/base/initializer.h and mongo/base/initializer_dependency_graph.h for more details. + */ +class InitializerContext { + MONGO_DISALLOW_COPYING(InitializerContext); + +public: + typedef std::vector<std::string> ArgumentVector; + typedef std::map<std::string, std::string> EnvironmentMap; + + InitializerContext(const ArgumentVector& args, const EnvironmentMap& env); + + const ArgumentVector& args() const { + return _args; + } + const EnvironmentMap& env() const { + return _env; + } + +private: + ArgumentVector _args; + EnvironmentMap _env; +}; } // namespace mongo diff --git a/src/mongo/base/initializer_dependency_graph.cpp b/src/mongo/base/initializer_dependency_graph.cpp index d95c3c3c8b1..5da76ccf8bd 100644 --- a/src/mongo/base/initializer_dependency_graph.cpp +++ b/src/mongo/base/initializer_dependency_graph.cpp @@ -33,134 +33,122 @@ namespace mongo { - InitializerDependencyGraph::InitializerDependencyGraph() {} - InitializerDependencyGraph::~InitializerDependencyGraph() {} - - Status InitializerDependencyGraph::addInitializer(const std::string& name, - const InitializerFunction& fn, - const std::vector<std::string>& prerequisites, - const std::vector<std::string>& dependents) { - if (!fn) - return Status(ErrorCodes::BadValue, "Illegal to supply a NULL function"); - - NodeData& newNode = _nodes[name]; - if (newNode.fn) { - return Status(ErrorCodes::DuplicateKey, name); - } - - newNode.fn = fn; - - for (size_t i = 0; i < prerequisites.size(); ++i) { - newNode.prerequisites.insert(prerequisites[i]); - } +InitializerDependencyGraph::InitializerDependencyGraph() {} +InitializerDependencyGraph::~InitializerDependencyGraph() {} + +Status InitializerDependencyGraph::addInitializer(const std::string& name, + const InitializerFunction& fn, + const std::vector<std::string>& prerequisites, + const std::vector<std::string>& dependents) { + if (!fn) + return Status(ErrorCodes::BadValue, "Illegal to supply a NULL function"); + + NodeData& newNode = _nodes[name]; + if (newNode.fn) { + return Status(ErrorCodes::DuplicateKey, name); + } - for (size_t i = 0; i < dependents.size(); ++i) { - _nodes[dependents[i]].prerequisites.insert(name); - } + newNode.fn = fn; - return Status::OK(); + for (size_t i = 0; i < prerequisites.size(); ++i) { + newNode.prerequisites.insert(prerequisites[i]); } - InitializerFunction InitializerDependencyGraph::getInitializerFunction( - const std::string& name) const { + for (size_t i = 0; i < dependents.size(); ++i) { + _nodes[dependents[i]].prerequisites.insert(name); + } - NodeMap::const_iterator iter = _nodes.find(name); - if (iter == _nodes.end()) - return InitializerFunction(); - return iter->second.fn; + return Status::OK(); +} + +InitializerFunction InitializerDependencyGraph::getInitializerFunction( + const std::string& name) const { + NodeMap::const_iterator iter = _nodes.find(name); + if (iter == _nodes.end()) + return InitializerFunction(); + return iter->second.fn; +} + +Status InitializerDependencyGraph::topSort(std::vector<std::string>* sortedNames) const { + /* + * This top-sort is implemented by performing a depth-first traversal of the dependency + * graph, once for each node. "visitedNodeNames" tracks the set of node names ever visited, + * and it is used to prune each DFS. A node that has been visited once on any DFS is never + * visited again. Complexity of this implementation is O(n+m) where "n" is the number of + * nodes and "m" is the number of prerequisite edges. Space complexity is O(n), in both + * stack space and size of the "visitedNodeNames" set. + * + * "inProgressNodeNames" is used to detect and report cycles. + */ + + std::vector<std::string> inProgressNodeNames; + unordered_set<std::string> visitedNodeNames; + + sortedNames->clear(); + for (NodeMap::const_iterator iter = _nodes.begin(), end = _nodes.end(); iter != end; ++iter) { + Status status = + recursiveTopSort(_nodes, *iter, &inProgressNodeNames, &visitedNodeNames, sortedNames); + if (Status::OK() != status) + return status; } + return Status::OK(); +} + +Status InitializerDependencyGraph::recursiveTopSort(const NodeMap& nodeMap, + const Node& currentNode, + std::vector<std::string>* inProgressNodeNames, + unordered_set<std::string>* visitedNodeNames, + std::vector<std::string>* sortedNames) { + /* + * The top sort is performed by depth-first traversal starting at each node in the + * dependency graph, short-circuited any time a node is seen that has already been visited + * in any traversal. "visitedNodeNames" is the set of nodes that have been successfully + * visited, while "inProgressNodeNames" are nodes currently in the exploration chain. This + * structure is kept explicitly to facilitate cycle detection. + * + * This function implements a depth-first traversal, and is called once for each node in the + * graph by topSort(), above. + */ + + if ((*visitedNodeNames).count(currentNode.first)) + return Status::OK(); + + if (!currentNode.second.fn) + return Status(ErrorCodes::BadValue, currentNode.first); - Status InitializerDependencyGraph::topSort(std::vector<std::string>* sortedNames) const { - /* - * This top-sort is implemented by performing a depth-first traversal of the dependency - * graph, once for each node. "visitedNodeNames" tracks the set of node names ever visited, - * and it is used to prune each DFS. A node that has been visited once on any DFS is never - * visited again. Complexity of this implementation is O(n+m) where "n" is the number of - * nodes and "m" is the number of prerequisite edges. Space complexity is O(n), in both - * stack space and size of the "visitedNodeNames" set. - * - * "inProgressNodeNames" is used to detect and report cycles. - */ - - std::vector<std::string> inProgressNodeNames; - unordered_set<std::string> visitedNodeNames; + inProgressNodeNames->push_back(currentNode.first); + std::vector<std::string>::iterator firstOccurence = + std::find(inProgressNodeNames->begin(), inProgressNodeNames->end(), currentNode.first); + if (firstOccurence + 1 != inProgressNodeNames->end()) { sortedNames->clear(); - for (NodeMap::const_iterator iter = _nodes.begin(), end = _nodes.end(); - iter != end; ++iter) { - - Status status = recursiveTopSort(_nodes, - *iter, - &inProgressNodeNames, - &visitedNodeNames, - sortedNames); - if (Status::OK() != status) - return status; - } - return Status::OK(); + std::copy(firstOccurence, inProgressNodeNames->end(), std::back_inserter(*sortedNames)); + std::ostringstream os; + os << "Cycle in dependendcy graph: " << sortedNames->at(0); + for (size_t i = 1; i < sortedNames->size(); ++i) + os << " -> " << sortedNames->at(i); + return Status(ErrorCodes::GraphContainsCycle, os.str()); } - Status InitializerDependencyGraph::recursiveTopSort( - const NodeMap& nodeMap, - const Node& currentNode, - std::vector<std::string>* inProgressNodeNames, - unordered_set<std::string>* visitedNodeNames, - std::vector<std::string>* sortedNames) { - - /* - * The top sort is performed by depth-first traversal starting at each node in the - * dependency graph, short-circuited any time a node is seen that has already been visited - * in any traversal. "visitedNodeNames" is the set of nodes that have been successfully - * visited, while "inProgressNodeNames" are nodes currently in the exploration chain. This - * structure is kept explicitly to facilitate cycle detection. - * - * This function implements a depth-first traversal, and is called once for each node in the - * graph by topSort(), above. - */ - - if ((*visitedNodeNames).count(currentNode.first)) - return Status::OK(); - - if (!currentNode.second.fn) - return Status(ErrorCodes::BadValue, currentNode.first); - - inProgressNodeNames->push_back(currentNode.first); - - std::vector<std::string>::iterator firstOccurence = std::find( - inProgressNodeNames->begin(), inProgressNodeNames->end(), currentNode.first); - if (firstOccurence + 1 != inProgressNodeNames->end()) { - sortedNames->clear(); - std::copy(firstOccurence, inProgressNodeNames->end(), std::back_inserter(*sortedNames)); - std::ostringstream os; - os << "Cycle in dependendcy graph: " << sortedNames->at(0); - for (size_t i = 1; i < sortedNames->size(); ++i) - os << " -> " << sortedNames->at(i); - return Status(ErrorCodes::GraphContainsCycle, os.str()); - } - - for (unordered_set<std::string>::const_iterator - iter = currentNode.second.prerequisites.begin(), - end = currentNode.second.prerequisites.end(); - iter != end; ++iter) { - - NodeMap::const_iterator nextNode = nodeMap.find(*iter); - if (nextNode == nodeMap.end()) - return Status(ErrorCodes::BadValue, *iter); - - Status status = recursiveTopSort(nodeMap, - *nextNode, - inProgressNodeNames, - visitedNodeNames, - sortedNames); - if (Status::OK() != status) - return status; - } - sortedNames->push_back(currentNode.first); - if (inProgressNodeNames->back() != currentNode.first) - return Status(ErrorCodes::InternalError, "inProgressNodeNames stack corrupt"); - inProgressNodeNames->pop_back(); - visitedNodeNames->insert(currentNode.first); - return Status::OK(); + for (unordered_set<std::string>::const_iterator iter = currentNode.second.prerequisites.begin(), + end = currentNode.second.prerequisites.end(); + iter != end; + ++iter) { + NodeMap::const_iterator nextNode = nodeMap.find(*iter); + if (nextNode == nodeMap.end()) + return Status(ErrorCodes::BadValue, *iter); + + Status status = recursiveTopSort( + nodeMap, *nextNode, inProgressNodeNames, visitedNodeNames, sortedNames); + if (Status::OK() != status) + return status; } + sortedNames->push_back(currentNode.first); + if (inProgressNodeNames->back() != currentNode.first) + return Status(ErrorCodes::InternalError, "inProgressNodeNames stack corrupt"); + inProgressNodeNames->pop_back(); + visitedNodeNames->insert(currentNode.first); + return Status::OK(); +} } // namespace mongo diff --git a/src/mongo/base/initializer_dependency_graph.h b/src/mongo/base/initializer_dependency_graph.h index 578f89c395c..d125ddcd41d 100644 --- a/src/mongo/base/initializer_dependency_graph.h +++ b/src/mongo/base/initializer_dependency_graph.h @@ -39,92 +39,91 @@ namespace mongo { +/** + * Representation of a dependency graph of "initialization operations." + * + * Each operation has a unique name, a function object implementing the operation's behavior, + * and a set of prerequisite operations, which may be empty. A legal graph contains no cycles. + * + * Instances of this class are used in two phases. In the first phase, the graph is constructed + * by repeated calls to addInitializer(). In the second phase, a user calls the topSort() + * method to produce an initialization order that respects the dependencies among operations, and + * then uses the getInitializerFunction() to get the behavior function for each operation, in + * turn. + * + * Concurrency Notes: The user is responsible for synchronization. Multiple threads may + * simultaneously call the const functions, getInitializerFunction and topSort, on the same + * instance of InitializerDependencyGraph. However, no thread may call addInitializer while any + * thread is executing those functions or addInitializer on the same instance. + */ +class InitializerDependencyGraph { + MONGO_DISALLOW_COPYING(InitializerDependencyGraph); + +public: + InitializerDependencyGraph(); + ~InitializerDependencyGraph(); + /** - * Representation of a dependency graph of "initialization operations." + * Add a new initializer node, named "name", to the dependency graph, with the given + * behavior, "fn", and the given "prerequisites" (input dependencies) and "dependents" + * (output dependencies). * - * Each operation has a unique name, a function object implementing the operation's behavior, - * and a set of prerequisite operations, which may be empty. A legal graph contains no cycles. - * - * Instances of this class are used in two phases. In the first phase, the graph is constructed - * by repeated calls to addInitializer(). In the second phase, a user calls the topSort() - * method to produce an initialization order that respects the dependencies among operations, and - * then uses the getInitializerFunction() to get the behavior function for each operation, in - * turn. - * - * Concurrency Notes: The user is responsible for synchronization. Multiple threads may - * simultaneously call the const functions, getInitializerFunction and topSort, on the same - * instance of InitializerDependencyGraph. However, no thread may call addInitializer while any - * thread is executing those functions or addInitializer on the same instance. + * If "!fn" (fn is NULL in function pointer parlance), returns status with code + * ErrorCodes::badValue. If "name" is a duplicate of a name already present in the graph, + * returns "ErrorCodes::duplicateKey". Otherwise, returns Status::OK() and adds the new node + * to the graph. Note that cycles in the dependency graph are not discovered in this phase. + * Rather, they're discovered by topSort, below. */ - class InitializerDependencyGraph { - MONGO_DISALLOW_COPYING(InitializerDependencyGraph); - - public: - InitializerDependencyGraph(); - ~InitializerDependencyGraph(); + Status addInitializer(const std::string& name, + const InitializerFunction& fn, + const std::vector<std::string>& prerequisites, + const std::vector<std::string>& dependents); - /** - * Add a new initializer node, named "name", to the dependency graph, with the given - * behavior, "fn", and the given "prerequisites" (input dependencies) and "dependents" - * (output dependencies). - * - * If "!fn" (fn is NULL in function pointer parlance), returns status with code - * ErrorCodes::badValue. If "name" is a duplicate of a name already present in the graph, - * returns "ErrorCodes::duplicateKey". Otherwise, returns Status::OK() and adds the new node - * to the graph. Note that cycles in the dependency graph are not discovered in this phase. - * Rather, they're discovered by topSort, below. - */ - Status addInitializer(const std::string& name, - const InitializerFunction& fn, - const std::vector<std::string>& prerequisites, - const std::vector<std::string>& dependents); - - /** - * Given a dependency operation node named "name", return its behavior function. Returns - * a value that evaluates to "false" in boolean context, otherwise. - */ - InitializerFunction getInitializerFunction(const std::string& name) const; + /** + * Given a dependency operation node named "name", return its behavior function. Returns + * a value that evaluates to "false" in boolean context, otherwise. + */ + InitializerFunction getInitializerFunction(const std::string& name) const; - /** - * Construct a topological sort of the dependency graph, and store that order into - * "sortedNames". Returns Status::OK() on success. - * - * If the graph contains a cycle, returns ErrorCodes::graphContainsCycle, and "sortedNames" - * is an ordered sequence of nodes involved in a cycle. In this case, the first and last - * element of "sortedNames" will be equal. - * - * If any node in the graph names a prerequisite that was never added to the graph via - * addInitializer, this function will return ErrorCodes::badValue. - * - * Any other return value indicates an internal error, and should not occur. - */ - Status topSort(std::vector<std::string>* sortedNames) const; + /** + * Construct a topological sort of the dependency graph, and store that order into + * "sortedNames". Returns Status::OK() on success. + * + * If the graph contains a cycle, returns ErrorCodes::graphContainsCycle, and "sortedNames" + * is an ordered sequence of nodes involved in a cycle. In this case, the first and last + * element of "sortedNames" will be equal. + * + * If any node in the graph names a prerequisite that was never added to the graph via + * addInitializer, this function will return ErrorCodes::badValue. + * + * Any other return value indicates an internal error, and should not occur. + */ + Status topSort(std::vector<std::string>* sortedNames) const; - private: - struct NodeData { - InitializerFunction fn; - unordered_set<std::string> prerequisites; - }; +private: + struct NodeData { + InitializerFunction fn; + unordered_set<std::string> prerequisites; + }; - typedef unordered_map<std::string, NodeData> NodeMap; - typedef NodeMap::value_type Node; + typedef unordered_map<std::string, NodeData> NodeMap; + typedef NodeMap::value_type Node; - /** - * Helper function to recursively top-sort a graph. Used by topSort(). - */ - static Status recursiveTopSort( - const NodeMap& nodeMap, - const Node& currentNode, - std::vector<std::string>* inProgressNodeNames, - unordered_set<std::string>* visitedNodeNames, - std::vector<std::string>* sortedNames); + /** + * Helper function to recursively top-sort a graph. Used by topSort(). + */ + static Status recursiveTopSort(const NodeMap& nodeMap, + const Node& currentNode, + std::vector<std::string>* inProgressNodeNames, + unordered_set<std::string>* visitedNodeNames, + std::vector<std::string>* sortedNames); - /** - * Map of all named nodes. Nodes named as prerequisites or dependents but not explicitly - * added via addInitializer will either be absent from this map or be present with - * NodeData::fn set to a false-ish value. - */ - NodeMap _nodes; - }; + /** + * Map of all named nodes. Nodes named as prerequisites or dependents but not explicitly + * added via addInitializer will either be absent from this map or be present with + * NodeData::fn set to a false-ish value. + */ + NodeMap _nodes; +}; } // namespace mongo diff --git a/src/mongo/base/initializer_dependency_graph_test.cpp b/src/mongo/base/initializer_dependency_graph_test.cpp index 4cc753056a8..82bd62cb3c6 100644 --- a/src/mongo/base/initializer_dependency_graph_test.cpp +++ b/src/mongo/base/initializer_dependency_graph_test.cpp @@ -34,257 +34,256 @@ #include "mongo/base/make_string_vector.h" #include "mongo/unittest/unittest.h" -#define ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ - (GRAPH).addInitializer( \ - (NAME), \ - (FN), \ - MONGO_MAKE_STRING_VECTOR PREREQS, \ - MONGO_MAKE_STRING_VECTOR DEPS) +#define ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ + (GRAPH).addInitializer( \ + (NAME), (FN), MONGO_MAKE_STRING_VECTOR PREREQS, MONGO_MAKE_STRING_VECTOR DEPS) -#define ASSERT_ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ +#define ASSERT_ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ ASSERT_EQUALS(Status::OK(), ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS)) -#define ASSERT_EXACTLY_N_IN_CONTAINER(N, CONTAINER, THING) \ +#define ASSERT_EXACTLY_N_IN_CONTAINER(N, CONTAINER, THING) \ ASSERT_EQUALS(N, std::count((CONTAINER).begin(), (CONTAINER).end(), (THING))) -#define ASSERT_AT_LEAST_N_IN_CONTAINER(N, CONTAINER, THING) \ +#define ASSERT_AT_LEAST_N_IN_CONTAINER(N, CONTAINER, THING) \ ASSERT_LESS_THAN_OR_EQUALS(N, std::count((CONTAINER).begin(), (CONTAINER).end(), (THING))) -#define ASSERT_EXACTLY_ONE_IN_CONTAINER(CONTAINER, THING) \ +#define ASSERT_EXACTLY_ONE_IN_CONTAINER(CONTAINER, THING) \ ASSERT_EXACTLY_N_IN_CONTAINER(1, CONTAINER, THING) namespace mongo { namespace { - Status doNothing(InitializerContext*) { return Status::OK(); } +Status doNothing(InitializerContext*) { + return Status::OK(); +} - TEST(InitializerDependencyGraphTest, InsertNullFunctionFails) { - InitializerDependencyGraph graph; - ASSERT_EQUALS(ErrorCodes::BadValue, ADD_INITIALIZER( - graph, "A", InitializerFunction(), - MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS)); - } +TEST(InitializerDependencyGraphTest, InsertNullFunctionFails) { + InitializerDependencyGraph graph; + ASSERT_EQUALS( + ErrorCodes::BadValue, + ADD_INITIALIZER( + graph, "A", InitializerFunction(), MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS)); +} - TEST(InitializerDependencyGraphTest, InsertSameNameTwiceFails) { - InitializerDependencyGraph graph; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_EQUALS(ErrorCodes::DuplicateKey, ADD_INITIALIZER( - graph, "A", doNothing, - MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS)); - } +TEST(InitializerDependencyGraphTest, InsertSameNameTwiceFails) { + InitializerDependencyGraph graph; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_EQUALS( + ErrorCodes::DuplicateKey, + ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS)); +} - TEST(InitializerDependencyGraphTest, TopSortEmptyGraph) { - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(0U, nodeNames.size()); - } +TEST(InitializerDependencyGraphTest, TopSortEmptyGraph) { + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(0U, nodeNames.size()); +} - TEST(InitializerDependencyGraphTest, TopSortGraphNoDeps) { - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(3U, nodeNames.size()); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); - } +TEST(InitializerDependencyGraphTest, TopSortGraphNoDeps) { + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(3U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); +} - TEST(InitializerDependencyGraphTest, TopSortWithDiamondPrerequisites) { - /* - * This tests top-sorting a simple diamond, specified using prerequisites: - * - * B - * / ^ - * v \ - * A D - * ^ / - * \ v - * C - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("B", "C"), MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, ("A"), MONGO_NO_DEPENDENTS); - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(4U, nodeNames.size()); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); - ASSERT_EQUALS("A", nodeNames.front()); - ASSERT_EQUALS("D", nodeNames.back()); - } +TEST(InitializerDependencyGraphTest, TopSortWithDiamondPrerequisites) { + /* + * This tests top-sorting a simple diamond, specified using prerequisites: + * + * B + * / ^ + * v \ + * A D + * ^ / + * \ v + * C + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("B", "C"), MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, ("A"), MONGO_NO_DEPENDENTS); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(4U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); + ASSERT_EQUALS("A", nodeNames.front()); + ASSERT_EQUALS("D", nodeNames.back()); +} - TEST(InitializerDependencyGraphTest, TopSortWithDiamondDependents) { - /* - * This tests top-sorting a simple diamond, specified using dependents: - * - * B - * / ^ - * v \ - * A D - * ^ / - * \ v - * C - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); - ASSERT_ADD_INITIALIZER(graph, "D", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, ("D")); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, ("D")); - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(4U, nodeNames.size()); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); - ASSERT_EQUALS("A", nodeNames.front()); - ASSERT_EQUALS("D", nodeNames.back()); - } +TEST(InitializerDependencyGraphTest, TopSortWithDiamondDependents) { + /* + * This tests top-sorting a simple diamond, specified using dependents: + * + * B + * / ^ + * v \ + * A D + * ^ / + * \ v + * C + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); + ASSERT_ADD_INITIALIZER(graph, "D", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, ("D")); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, ("D")); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(4U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); + ASSERT_EQUALS("A", nodeNames.front()); + ASSERT_EQUALS("D", nodeNames.back()); +} - TEST(InitializerDependencyGraphTest, TopSortWithDiamondGeneral1) { - /* - * This tests top-sorting a simple diamond, where B and C specify all prerequisites and - * dependents. - * - * B - * / ^ - * v \ - * A D - * ^ / - * \ v - * C - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "D", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), ("D")); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, ("A"), ("D")); - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(4U, nodeNames.size()); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); - ASSERT_EQUALS("A", nodeNames.front()); - ASSERT_EQUALS("D", nodeNames.back()); - } +TEST(InitializerDependencyGraphTest, TopSortWithDiamondGeneral1) { + /* + * This tests top-sorting a simple diamond, where B and C specify all prerequisites and + * dependents. + * + * B + * / ^ + * v \ + * A D + * ^ / + * \ v + * C + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "D", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), ("D")); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, ("A"), ("D")); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(4U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); + ASSERT_EQUALS("A", nodeNames.front()); + ASSERT_EQUALS("D", nodeNames.back()); +} - TEST(InitializerDependencyGraphTest, TopSortWithDiamondGeneral2) { - /* - * This tests top-sorting a simple diamond, where A and D specify all prerequisites and - * dependents. - * - * B - * / ^ - * v \ - * A D - * ^ / - * \ v - * C - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); - ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("C", "B"), MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(4U, nodeNames.size()); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); - ASSERT_EQUALS("A", nodeNames.front()); - ASSERT_EQUALS("D", nodeNames.back()); - } +TEST(InitializerDependencyGraphTest, TopSortWithDiamondGeneral2) { + /* + * This tests top-sorting a simple diamond, where A and D specify all prerequisites and + * dependents. + * + * B + * / ^ + * v \ + * A D + * ^ / + * \ v + * C + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); + ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("C", "B"), MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(4U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); + ASSERT_EQUALS("A", nodeNames.front()); + ASSERT_EQUALS("D", nodeNames.back()); +} - TEST(InitializerDependencyGraphTest, TopSortWithDiamondGeneral3) { - /* - * This tests top-sorting a simple diamond, where A and D specify all prerequisites and - * dependents, but so do B and C. - * - * B - * / ^ - * v \ - * A D - * ^ / - * \ v - * C - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); - ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("C", "B"), MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), ("D")); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, ("A"), ("D")); - ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); - ASSERT_EQUALS(4U, nodeNames.size()); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); - ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); - ASSERT_EQUALS("A", nodeNames.front()); - ASSERT_EQUALS("D", nodeNames.back()); - } +TEST(InitializerDependencyGraphTest, TopSortWithDiamondGeneral3) { + /* + * This tests top-sorting a simple diamond, where A and D specify all prerequisites and + * dependents, but so do B and C. + * + * B + * / ^ + * v \ + * A D + * ^ / + * \ v + * C + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); + ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("C", "B"), MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), ("D")); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, ("A"), ("D")); + ASSERT_EQUALS(Status::OK(), graph.topSort(&nodeNames)); + ASSERT_EQUALS(4U, nodeNames.size()); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "A"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "B"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "C"); + ASSERT_EXACTLY_ONE_IN_CONTAINER(nodeNames, "D"); + ASSERT_EQUALS("A", nodeNames.front()); + ASSERT_EQUALS("D", nodeNames.back()); +} - TEST(InitializerDependencyGraphTest, TopSortWithDiamondAndCycle) { - /* - * This tests top-sorting a graph with a cycle, which should fail.. - * - * B <- E - * / ^ ^ - * v \ / - * A D - * ^ / - * \ v - * C - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); - ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("C", "B"), MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); - ASSERT_ADD_INITIALIZER(graph, "E", doNothing, ("D"), ("B")); - ASSERT_EQUALS(ErrorCodes::GraphContainsCycle, graph.topSort(&nodeNames)); - ASSERT_EQUALS(4U, nodeNames.size()); - ASSERT_EQUALS(nodeNames.front(), nodeNames.back()); - ASSERT_AT_LEAST_N_IN_CONTAINER(1, nodeNames, "D"); - ASSERT_AT_LEAST_N_IN_CONTAINER(1, nodeNames, "E"); - ASSERT_AT_LEAST_N_IN_CONTAINER(1, nodeNames, "B"); - ASSERT_EXACTLY_N_IN_CONTAINER(0, nodeNames, "A"); - ASSERT_EXACTLY_N_IN_CONTAINER(0, nodeNames, "C"); - } +TEST(InitializerDependencyGraphTest, TopSortWithDiamondAndCycle) { + /* + * This tests top-sorting a graph with a cycle, which should fail.. + * + * B <- E + * / ^ ^ + * v \ / + * A D + * ^ / + * \ v + * C + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B", "C")); + ASSERT_ADD_INITIALIZER(graph, "D", doNothing, ("C", "B"), MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "C", doNothing, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); + ASSERT_ADD_INITIALIZER(graph, "E", doNothing, ("D"), ("B")); + ASSERT_EQUALS(ErrorCodes::GraphContainsCycle, graph.topSort(&nodeNames)); + ASSERT_EQUALS(4U, nodeNames.size()); + ASSERT_EQUALS(nodeNames.front(), nodeNames.back()); + ASSERT_AT_LEAST_N_IN_CONTAINER(1, nodeNames, "D"); + ASSERT_AT_LEAST_N_IN_CONTAINER(1, nodeNames, "E"); + ASSERT_AT_LEAST_N_IN_CONTAINER(1, nodeNames, "B"); + ASSERT_EXACTLY_N_IN_CONTAINER(0, nodeNames, "A"); + ASSERT_EXACTLY_N_IN_CONTAINER(0, nodeNames, "C"); +} - TEST(InitializerDependencyGraphTest, TopSortFailsWhenMissingPrerequisite) { - /* - * If a node names a never-declared prerequisite, topSort should fail. - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), MONGO_NO_DEPENDENTS); - ASSERT_EQUALS(ErrorCodes::BadValue, graph.topSort(&nodeNames)); - } +TEST(InitializerDependencyGraphTest, TopSortFailsWhenMissingPrerequisite) { + /* + * If a node names a never-declared prerequisite, topSort should fail. + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "B", doNothing, ("A"), MONGO_NO_DEPENDENTS); + ASSERT_EQUALS(ErrorCodes::BadValue, graph.topSort(&nodeNames)); +} - TEST(InitializerDependencyGraphTest, TopSortFailsWhenMissingDependent) { - /* - * If a node names a never-declared dependent, topSort should fail. - */ - InitializerDependencyGraph graph; - std::vector<std::string> nodeNames; - ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B")); - ASSERT_EQUALS(ErrorCodes::BadValue, graph.topSort(&nodeNames)); - } +TEST(InitializerDependencyGraphTest, TopSortFailsWhenMissingDependent) { + /* + * If a node names a never-declared dependent, topSort should fail. + */ + InitializerDependencyGraph graph; + std::vector<std::string> nodeNames; + ASSERT_ADD_INITIALIZER(graph, "A", doNothing, MONGO_NO_PREREQUISITES, ("B")); + ASSERT_EQUALS(ErrorCodes::BadValue, graph.topSort(&nodeNames)); +} } // namespace } // namespace mongo - diff --git a/src/mongo/base/initializer_function.h b/src/mongo/base/initializer_function.h index c0ab4de3577..93575ef01e2 100644 --- a/src/mongo/base/initializer_function.h +++ b/src/mongo/base/initializer_function.h @@ -32,14 +32,14 @@ namespace mongo { - class InitializerContext; +class InitializerContext; - /** - * An InitializerFunction implements the behavior of an initializer operation. - * - * On successful execution, an InitializerFunction returns Status::OK(). It may - * inspect and mutate the supplied InitializerContext. - */ - typedef stdx::function<Status (InitializerContext*)> InitializerFunction; +/** + * An InitializerFunction implements the behavior of an initializer operation. + * + * On successful execution, an InitializerFunction returns Status::OK(). It may + * inspect and mutate the supplied InitializerContext. + */ +typedef stdx::function<Status(InitializerContext*)> InitializerFunction; } // namespace mongo diff --git a/src/mongo/base/initializer_test.cpp b/src/mongo/base/initializer_test.cpp index 3bdbed8e1d0..260d8d01ef0 100644 --- a/src/mongo/base/initializer_test.cpp +++ b/src/mongo/base/initializer_test.cpp @@ -41,7 +41,7 @@ * * 0 <- 3 <- 7 * ^ / ^ ^ - * \ v \ \ + * \ v \ \ * 2 5 <- 8 * / ^ / / * v \ v v @@ -49,128 +49,143 @@ * */ -#define ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ - (GRAPH).addInitializer( \ - (NAME), \ - (FN), \ - MONGO_MAKE_STRING_VECTOR PREREQS, \ - MONGO_MAKE_STRING_VECTOR DEPS) +#define ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ + (GRAPH).addInitializer( \ + (NAME), (FN), MONGO_MAKE_STRING_VECTOR PREREQS, MONGO_MAKE_STRING_VECTOR DEPS) -#define ASSERT_ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ +#define ASSERT_ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS) \ ASSERT_EQUALS(Status::OK(), ADD_INITIALIZER(GRAPH, NAME, FN, PREREQS, DEPS)) -#define CONSTRUCT_DEPENDENCY_GRAPH(GRAPH, FN0, FN1, FN2, FN3, FN4, FN5, FN6, FN7, FN8) \ - do { \ - InitializerDependencyGraph& _graph_ = (GRAPH); \ +#define CONSTRUCT_DEPENDENCY_GRAPH(GRAPH, FN0, FN1, FN2, FN3, FN4, FN5, FN6, FN7, FN8) \ + do { \ + InitializerDependencyGraph& _graph_ = (GRAPH); \ ASSERT_ADD_INITIALIZER(_graph_, "n0", FN0, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); \ ASSERT_ADD_INITIALIZER(_graph_, "n1", FN1, MONGO_NO_PREREQUISITES, MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n2", FN2, ("n0", "n1"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n3", FN3, ("n0", "n2"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n4", FN4, ("n2", "n1"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n5", FN5, ("n3", "n4"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n6", FN6, ("n4"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n7", FN7, ("n3"), MONGO_NO_DEPENDENTS); \ - ASSERT_ADD_INITIALIZER(_graph_, "n8", FN8, ("n5", "n6", "n7"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n2", FN2, ("n0", "n1"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n3", FN3, ("n0", "n2"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n4", FN4, ("n2", "n1"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n5", FN5, ("n3", "n4"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n6", FN6, ("n4"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n7", FN7, ("n3"), MONGO_NO_DEPENDENTS); \ + ASSERT_ADD_INITIALIZER(_graph_, "n8", FN8, ("n5", "n6", "n7"), MONGO_NO_DEPENDENTS); \ } while (false) namespace mongo { namespace { - int globalCounts[9]; - - Status doNothing(InitializerContext*) { return Status::OK(); } - - Status set0(InitializerContext*) { - globalCounts[0] = 1; - return Status::OK(); - } - - Status set1(InitializerContext*) { - globalCounts[1] = 1; - return Status::OK(); - } - - Status set2(InitializerContext*) { - if (!globalCounts[0] || !globalCounts[1]) - return Status(ErrorCodes::UnknownError, "one of 0 or 1 not already set"); - globalCounts[2] = 1; - return Status::OK(); - } - - Status set3(InitializerContext*) { - if (!globalCounts[0] || !globalCounts[2]) - return Status(ErrorCodes::UnknownError, "one of 0 or 2 not already set"); - globalCounts[3] = 1; - return Status::OK(); - } - - Status set4(InitializerContext*) { - if (!globalCounts[1] || !globalCounts[2]) - return Status(ErrorCodes::UnknownError, "one of 1 or 2 not already set"); - globalCounts[4] = 1; - return Status::OK(); - } - - Status set5(InitializerContext*) { - if (!globalCounts[3] || !globalCounts[4]) - return Status(ErrorCodes::UnknownError, "one of 3 or 4 not already set"); - globalCounts[5] = 1; - return Status::OK(); - } - - Status set6(InitializerContext*) { - if (!globalCounts[4]) - return Status(ErrorCodes::UnknownError, "4 not already set"); - globalCounts[6] = 1; - return Status::OK(); - } - - Status set7(InitializerContext*) { - if (!globalCounts[3]) - return Status(ErrorCodes::UnknownError, "3 not already set"); - globalCounts[7] = 1; - return Status::OK(); - } - - Status set8(InitializerContext*) { - if (!globalCounts[5] || !globalCounts[6] || !globalCounts[7]) - return Status(ErrorCodes::UnknownError, "one of 5, 6, 7 not already set"); - globalCounts[8] = 1; - return Status::OK(); - } - - void clearCounts() { - for (size_t i = 0; i < 9; ++i) - globalCounts[i] = 0; - } - - TEST(InitializerTest, SuccessfulInitialization) { - Initializer initializer; - CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), - set0, set1, set2, set3, set4, set5, set6, set7, set8); - clearCounts(); - ASSERT_OK(initializer.execute(InitializerContext::ArgumentVector(), +int globalCounts[9]; + +Status doNothing(InitializerContext*) { + return Status::OK(); +} + +Status set0(InitializerContext*) { + globalCounts[0] = 1; + return Status::OK(); +} + +Status set1(InitializerContext*) { + globalCounts[1] = 1; + return Status::OK(); +} + +Status set2(InitializerContext*) { + if (!globalCounts[0] || !globalCounts[1]) + return Status(ErrorCodes::UnknownError, "one of 0 or 1 not already set"); + globalCounts[2] = 1; + return Status::OK(); +} + +Status set3(InitializerContext*) { + if (!globalCounts[0] || !globalCounts[2]) + return Status(ErrorCodes::UnknownError, "one of 0 or 2 not already set"); + globalCounts[3] = 1; + return Status::OK(); +} + +Status set4(InitializerContext*) { + if (!globalCounts[1] || !globalCounts[2]) + return Status(ErrorCodes::UnknownError, "one of 1 or 2 not already set"); + globalCounts[4] = 1; + return Status::OK(); +} + +Status set5(InitializerContext*) { + if (!globalCounts[3] || !globalCounts[4]) + return Status(ErrorCodes::UnknownError, "one of 3 or 4 not already set"); + globalCounts[5] = 1; + return Status::OK(); +} + +Status set6(InitializerContext*) { + if (!globalCounts[4]) + return Status(ErrorCodes::UnknownError, "4 not already set"); + globalCounts[6] = 1; + return Status::OK(); +} + +Status set7(InitializerContext*) { + if (!globalCounts[3]) + return Status(ErrorCodes::UnknownError, "3 not already set"); + globalCounts[7] = 1; + return Status::OK(); +} + +Status set8(InitializerContext*) { + if (!globalCounts[5] || !globalCounts[6] || !globalCounts[7]) + return Status(ErrorCodes::UnknownError, "one of 5, 6, 7 not already set"); + globalCounts[8] = 1; + return Status::OK(); +} + +void clearCounts() { + for (size_t i = 0; i < 9; ++i) + globalCounts[i] = 0; +} + +TEST(InitializerTest, SuccessfulInitialization) { + Initializer initializer; + CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), + set0, + set1, + set2, + set3, + set4, + set5, + set6, + set7, + set8); + clearCounts(); + ASSERT_OK(initializer.execute(InitializerContext::ArgumentVector(), + InitializerContext::EnvironmentMap())); + for (int i = 0; i < 9; ++i) + ASSERT_EQUALS(1, globalCounts[i]); +} + +TEST(InitializerTest, Step5Misimplemented) { + Initializer initializer; + CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), + set0, + set1, + set2, + set3, + set4, + doNothing, + set6, + set7, + set8); + clearCounts(); + ASSERT_EQUALS(ErrorCodes::UnknownError, + initializer.execute(InitializerContext::ArgumentVector(), InitializerContext::EnvironmentMap())); - for (int i = 0; i < 9; ++i) - ASSERT_EQUALS(1, globalCounts[i]); - } - - TEST(InitializerTest, Step5Misimplemented) { - Initializer initializer; - CONSTRUCT_DEPENDENCY_GRAPH(initializer.getInitializerDependencyGraph(), - set0, set1, set2, set3, set4, doNothing, set6, set7, set8); - clearCounts(); - ASSERT_EQUALS(ErrorCodes::UnknownError, - initializer.execute(InitializerContext::ArgumentVector(), - InitializerContext::EnvironmentMap())); - ASSERT_EQUALS(1, globalCounts[0]); - ASSERT_EQUALS(1, globalCounts[1]); - ASSERT_EQUALS(1, globalCounts[2]); - ASSERT_EQUALS(1, globalCounts[3]); - ASSERT_EQUALS(1, globalCounts[4]); - ASSERT_EQUALS(0, globalCounts[8]); - } + ASSERT_EQUALS(1, globalCounts[0]); + ASSERT_EQUALS(1, globalCounts[1]); + ASSERT_EQUALS(1, globalCounts[2]); + ASSERT_EQUALS(1, globalCounts[3]); + ASSERT_EQUALS(1, globalCounts[4]); + ASSERT_EQUALS(0, globalCounts[8]); +} } // namespace } // namespace mongo diff --git a/src/mongo/base/make_string_vector.cpp b/src/mongo/base/make_string_vector.cpp index 891b6933aa7..b1c3b177c14 100644 --- a/src/mongo/base/make_string_vector.cpp +++ b/src/mongo/base/make_string_vector.cpp @@ -33,15 +33,15 @@ namespace mongo { - std::vector<std::string> _makeStringVector(int ignored, ...) { - va_list ap; - va_start(ap, ignored); - std::vector<std::string> result; - const char* arg = NULL; - while ((arg = va_arg(ap, const char *))) - result.push_back(arg); - va_end(ap); - return result; - } +std::vector<std::string> _makeStringVector(int ignored, ...) { + va_list ap; + va_start(ap, ignored); + std::vector<std::string> result; + const char* arg = NULL; + while ((arg = va_arg(ap, const char*))) + result.push_back(arg); + va_end(ap); + return result; +} } // namespace mongo diff --git a/src/mongo/base/make_string_vector.h b/src/mongo/base/make_string_vector.h index 89a280bf782..e7a280fbf11 100644 --- a/src/mongo/base/make_string_vector.h +++ b/src/mongo/base/make_string_vector.h @@ -41,19 +41,19 @@ namespace mongo { - /** - * Create a vector of strings from varargs of C-style strings. - * - * WARNING: Only intended for use by MONGO_MAKE_STRING_VECTOR macro, defined above. Aborts - * ungracefully if you misuse it, so stick to the macro. - * - * The first parameter is ignored in all circumstances. The subsequent parameters must be - * const char* C-style strings, or NULL. Of these parameters, at least one must be - * NULL. Parameters at and beyond the NULL are not inserted. Typically, the NULL will be - * the last parameter. The MONGO_MAKE_STRING_VECTOR macro enforces this. - * - * Returns a vector of std::strings. - */ - std::vector<std::string> _makeStringVector(int ignored, ...); +/** + * Create a vector of strings from varargs of C-style strings. + * + * WARNING: Only intended for use by MONGO_MAKE_STRING_VECTOR macro, defined above. Aborts + * ungracefully if you misuse it, so stick to the macro. + * + * The first parameter is ignored in all circumstances. The subsequent parameters must be + * const char* C-style strings, or NULL. Of these parameters, at least one must be + * NULL. Parameters at and beyond the NULL are not inserted. Typically, the NULL will be + * the last parameter. The MONGO_MAKE_STRING_VECTOR macro enforces this. + * + * Returns a vector of std::strings. + */ +std::vector<std::string> _makeStringVector(int ignored, ...); } // namespace mongo diff --git a/src/mongo/base/owned_pointer_map.h b/src/mongo/base/owned_pointer_map.h index 63745b9bc1a..ea1a0456fa5 100644 --- a/src/mongo/base/owned_pointer_map.h +++ b/src/mongo/base/owned_pointer_map.h @@ -33,46 +33,49 @@ namespace mongo { - /** - * An std::map wrapper that deletes pointers within a vector on destruction. The objects - * referenced by the vector's pointers are 'owned' by an object of this class. - * NOTE that an OwnedPointerMap<K,T,Compare> wraps an std::map<K,T*,Compare>. - */ - template<class K, class T, class Compare = std::less<K> > - class OwnedPointerMap { - MONGO_DISALLOW_COPYING(OwnedPointerMap); +/** + * An std::map wrapper that deletes pointers within a vector on destruction. The objects + * referenced by the vector's pointers are 'owned' by an object of this class. + * NOTE that an OwnedPointerMap<K,T,Compare> wraps an std::map<K,T*,Compare>. + */ +template <class K, class T, class Compare = std::less<K>> +class OwnedPointerMap { + MONGO_DISALLOW_COPYING(OwnedPointerMap); - public: - typedef typename std::map<K, T*, Compare> MapType; +public: + typedef typename std::map<K, T*, Compare> MapType; - OwnedPointerMap(); - ~OwnedPointerMap(); + OwnedPointerMap(); + ~OwnedPointerMap(); - /** Access the map. */ - const MapType& map() const { return _map; } - MapType& mutableMap() { return _map; } + /** Access the map. */ + const MapType& map() const { + return _map; + } + MapType& mutableMap() { + return _map; + } - void clear(); + void clear(); - private: - MapType _map; - }; +private: + MapType _map; +}; - template<class K, class T, class Compare> - OwnedPointerMap<K, T, Compare>::OwnedPointerMap() { - } +template <class K, class T, class Compare> +OwnedPointerMap<K, T, Compare>::OwnedPointerMap() {} - template<class K, class T, class Compare> - OwnedPointerMap<K, T, Compare>::~OwnedPointerMap() { - clear(); - } +template <class K, class T, class Compare> +OwnedPointerMap<K, T, Compare>::~OwnedPointerMap() { + clear(); +} - template<class K, class T, class Compare> - void OwnedPointerMap<K, T, Compare>::clear() { - for( typename MapType::iterator i = _map.begin(); i != _map.end(); ++i ) { - delete i->second; - } - _map.clear(); +template <class K, class T, class Compare> +void OwnedPointerMap<K, T, Compare>::clear() { + for (typename MapType::iterator i = _map.begin(); i != _map.end(); ++i) { + delete i->second; } + _map.clear(); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/owned_pointer_map_test.cpp b/src/mongo/base/owned_pointer_map_test.cpp index e1a1a1c0326..5ad7478b239 100644 --- a/src/mongo/base/owned_pointer_map_test.cpp +++ b/src/mongo/base/owned_pointer_map_test.cpp @@ -37,89 +37,94 @@ namespace mongo { namespace { - using std::make_pair; - using std::string; - - /** Helper class that logs its constructor argument to a static vector on destruction. */ - class DestructionLogger { - public: - DestructionLogger( const string& name ) : - _name( name ) { - } - ~DestructionLogger() { _destroyedNames.push_back( _name ); } - - static std::vector<string>& destroyedNames() { return _destroyedNames; } - - string getName() { return _name; } - - private: - string _name; - static std::vector<string> _destroyedNames; - }; - - std::vector<string> DestructionLogger::_destroyedNames; - - TEST(OwnedPointerMapTest, OwnedPointerDestroyed) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerMap<int,DestructionLogger> owned; - owned.mutableMap().insert( make_pair( 0, new DestructionLogger( "foo" ) ) ); - // owned destroyed - } - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "foo", DestructionLogger::destroyedNames()[ 0 ] ); +using std::make_pair; +using std::string; + +/** Helper class that logs its constructor argument to a static vector on destruction. */ +class DestructionLogger { +public: + DestructionLogger(const string& name) : _name(name) {} + ~DestructionLogger() { + _destroyedNames.push_back(_name); } - TEST(OwnedPointerMapTest, OwnedConstPointerDestroyed) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerMap<int,const DestructionLogger> owned; - owned.mutableMap().insert( make_pair( 0, new DestructionLogger( "foo" ) ) ); - // owned destroyed - } - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "foo", DestructionLogger::destroyedNames()[ 0 ] ); + static std::vector<string>& destroyedNames() { + return _destroyedNames; } - TEST(OwnedPointerMapTest, OwnedPointersDestroyedInOrder) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerMap<int,DestructionLogger> owned; - owned.mutableMap().insert( make_pair( 0, new DestructionLogger( "first" ) ) ); - owned.mutableMap().insert( make_pair( 1, new DestructionLogger( "second" ) ) ); - // owned destroyed - } - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); + string getName() { + return _name; } - - TEST(OwnedPointerMapTest, OwnedPointersWithCompare) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerMap<int,DestructionLogger,std::greater<int> > owned; - owned.mutableMap().insert( make_pair( 0, new DestructionLogger( "0" ) ) ); - owned.mutableMap().insert( make_pair( 1, new DestructionLogger( "1" ) ) ); - - // use std::greater<int> rather than the default std::less<int> - std::map<int,DestructionLogger*,std::greater<int> >::iterator it = owned.mutableMap().begin(); - - ASSERT( owned.mutableMap().end() != it); - // "1" should be sorted to be the first item. - ASSERT_EQUALS( "1", it->second->getName() ); - - it++; - ASSERT( owned.mutableMap().end() != it); - ASSERT_EQUALS( "0", it->second->getName() ); - - // owned destroyed - } - // destroyed in descending order - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "1", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "0", DestructionLogger::destroyedNames()[ 1 ] ); + +private: + string _name; + static std::vector<string> _destroyedNames; +}; + +std::vector<string> DestructionLogger::_destroyedNames; + +TEST(OwnedPointerMapTest, OwnedPointerDestroyed) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerMap<int, DestructionLogger> owned; + owned.mutableMap().insert(make_pair(0, new DestructionLogger("foo"))); + // owned destroyed + } + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("foo", DestructionLogger::destroyedNames()[0]); +} + +TEST(OwnedPointerMapTest, OwnedConstPointerDestroyed) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerMap<int, const DestructionLogger> owned; + owned.mutableMap().insert(make_pair(0, new DestructionLogger("foo"))); + // owned destroyed + } + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("foo", DestructionLogger::destroyedNames()[0]); +} + +TEST(OwnedPointerMapTest, OwnedPointersDestroyedInOrder) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerMap<int, DestructionLogger> owned; + owned.mutableMap().insert(make_pair(0, new DestructionLogger("first"))); + owned.mutableMap().insert(make_pair(1, new DestructionLogger("second"))); + // owned destroyed + } + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); +} + +TEST(OwnedPointerMapTest, OwnedPointersWithCompare) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerMap<int, DestructionLogger, std::greater<int>> owned; + owned.mutableMap().insert(make_pair(0, new DestructionLogger("0"))); + owned.mutableMap().insert(make_pair(1, new DestructionLogger("1"))); + + // use std::greater<int> rather than the default std::less<int> + std::map<int, DestructionLogger*, std::greater<int>>::iterator it = + owned.mutableMap().begin(); + + ASSERT(owned.mutableMap().end() != it); + // "1" should be sorted to be the first item. + ASSERT_EQUALS("1", it->second->getName()); + + it++; + ASSERT(owned.mutableMap().end() != it); + ASSERT_EQUALS("0", it->second->getName()); + + // owned destroyed } + // destroyed in descending order + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("1", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("0", DestructionLogger::destroyedNames()[1]); +} -} // namespace -} // namespace mongo +} // namespace +} // namespace mongo diff --git a/src/mongo/base/owned_pointer_vector.h b/src/mongo/base/owned_pointer_vector.h index 167401824de..79ff19bd8e0 100644 --- a/src/mongo/base/owned_pointer_vector.h +++ b/src/mongo/base/owned_pointer_vector.h @@ -34,129 +34,157 @@ namespace mongo { +/** + * An std::vector wrapper that deletes pointers within a vector on destruction. The objects + * referenced by the vector's pointers are 'owned' by an object of this class. + * NOTE that an OwnedPointerVector<T> wraps an std::vector<T*>. + */ +template <class T> +class OwnedPointerVector { + MONGO_DISALLOW_COPYING(OwnedPointerVector); + +public: + OwnedPointerVector() {} + ~OwnedPointerVector() { + clear(); + } + /** - * An std::vector wrapper that deletes pointers within a vector on destruction. The objects - * referenced by the vector's pointers are 'owned' by an object of this class. - * NOTE that an OwnedPointerVector<T> wraps an std::vector<T*>. + * Takes ownership of all pointers contained in 'other'. + * NOTE: argument is intentionally taken by value. */ - template<class T> - class OwnedPointerVector { - MONGO_DISALLOW_COPYING(OwnedPointerVector); - - public: - OwnedPointerVector() {} - ~OwnedPointerVector() { clear(); } - - /** - * Takes ownership of all pointers contained in 'other'. - * NOTE: argument is intentionally taken by value. - */ - OwnedPointerVector(std::vector<T*> other) { _vector.swap(other); } - - /** - * Takes ownership of all pointers contained in 'other'. - * NOTE: argument is intentionally taken by value. - */ - OwnedPointerVector& operator=(std::vector<T*> other) { - clear(); - _vector.swap(other); - return *this; - } + OwnedPointerVector(std::vector<T*> other) { + _vector.swap(other); + } - typedef typename std::vector<T*>::const_iterator const_iterator; - typedef typename std::vector<T*>::const_reverse_iterator const_reverse_iterator; + /** + * Takes ownership of all pointers contained in 'other'. + * NOTE: argument is intentionally taken by value. + */ + OwnedPointerVector& operator=(std::vector<T*> other) { + clear(); + _vector.swap(other); + return *this; + } + + typedef typename std::vector<T*>::const_iterator const_iterator; + typedef typename std::vector<T*>::const_reverse_iterator const_reverse_iterator; + + /** Access the vector. */ + const std::vector<T*>& vector() const { + return _vector; + } + std::vector<T*>& mutableVector() { + return _vector; + } - /** Access the vector. */ - const std::vector<T*>& vector() const { return _vector; } - std::vector<T*>& mutableVector() { return _vector; } + std::size_t size() const { + return _vector.size(); + } + bool empty() const { + return _vector.empty(); + } - std::size_t size() const { return _vector.size(); } - bool empty() const { return _vector.empty(); } + const_iterator begin() const { + return _vector.begin(); + } + const_reverse_iterator rbegin() const { + return _vector.rbegin(); + } + const_iterator end() const { + return _vector.end(); + } + const_reverse_iterator rend() const { + return _vector.rend(); + } - const_iterator begin() const { return _vector.begin(); } - const_reverse_iterator rbegin() const { return _vector.rbegin(); } - const_iterator end() const { return _vector.end(); } - const_reverse_iterator rend() const { return _vector.rend(); } + T* operator[](size_t i) const { + return _vector[i]; + } + T* back() const { + return _vector.back(); + } + T* front() const { + return _vector.front(); + } - T* operator[] (size_t i) const { return _vector[i]; } - T* back() const { return _vector.back(); } - T* front() const { return _vector.front(); } + void push_back(T* ptr) { + _vector.push_back(ptr); + } - void push_back(T* ptr) { _vector.push_back(ptr); } + /** + * Deletes all pointers in the vector, then sets its size to 0. + */ + void clear(); - /** - * Deletes all pointers in the vector, then sets its size to 0. - */ - void clear(); + /** + * Deletes the pointer at 'it', then erases it from the vector. + */ + void erase(const_iterator it) { + delete *it; + _vector.erase(toNonConstIter(it)); + } - /** - * Deletes the pointer at 'it', then erases it from the vector. - */ - void erase(const_iterator it) { + void erase(const_iterator begin, const_iterator end) { + for (const_iterator it = begin; it != end; ++it) { delete *it; - _vector.erase(toNonConstIter(it)); } + _vector.erase(toNonConstIter(begin), toNonConstIter(end)); + } - void erase(const_iterator begin, const_iterator end) { - for (const_iterator it = begin; it != end; ++it) { - delete *it; - } - _vector.erase(toNonConstIter(begin), toNonConstIter(end)); - } + // + // extensions + // - // - // extensions - // - - /** - * Releases the entire vector to allow you to transfer ownership. - * - * Leaves the OwnedPointerVector empty. - * Named after the similar method and pattern in std::unique_ptr. - */ - std::vector<T*> release() { - std::vector<T*> out; - out.swap(_vector); - return out; - } + /** + * Releases the entire vector to allow you to transfer ownership. + * + * Leaves the OwnedPointerVector empty. + * Named after the similar method and pattern in std::unique_ptr. + */ + std::vector<T*> release() { + std::vector<T*> out; + out.swap(_vector); + return out; + } - /** - * Releases ownership of a single element. - * - * Sets that element to NULL and does not change size(). - */ - T* releaseAt(size_t i) { - T* out = _vector[i]; - _vector[i] = NULL; - return out; - } + /** + * Releases ownership of a single element. + * + * Sets that element to NULL and does not change size(). + */ + T* releaseAt(size_t i) { + T* out = _vector[i]; + _vector[i] = NULL; + return out; + } - T* popAndReleaseBack() { - T* out = _vector.back(); - _vector.pop_back(); - return out; - } + T* popAndReleaseBack() { + T* out = _vector.back(); + _vector.pop_back(); + return out; + } - void popAndDeleteBack() { - delete popAndReleaseBack(); - } + void popAndDeleteBack() { + delete popAndReleaseBack(); + } - private: - typename std::vector<T*>::iterator toNonConstIter(const_iterator it) { - // This is needed for a few cases where c++03 vectors require non-const iterators that - // were relaxed in c++11 to allow const_iterators. It can go away when we require c++11. - return _vector.begin() + (it - begin()); - } +private: + typename std::vector<T*>::iterator toNonConstIter(const_iterator it) { + // This is needed for a few cases where c++03 vectors require non-const iterators that + // were relaxed in c++11 to allow const_iterators. It can go away when we require c++11. + return _vector.begin() + (it - begin()); + } - std::vector<T*> _vector; - }; + std::vector<T*> _vector; +}; - template<class T> - inline void OwnedPointerVector<T>::clear() { - for( typename std::vector<T*>::iterator i = _vector.begin(); i != _vector.end(); ++i ) { - delete *i; - } - _vector.clear(); +template <class T> +inline void OwnedPointerVector<T>::clear() { + for (typename std::vector<T*>::iterator i = _vector.begin(); i != _vector.end(); ++i) { + delete *i; } + _vector.clear(); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/owned_pointer_vector_test.cpp b/src/mongo/base/owned_pointer_vector_test.cpp index 3684da00c7e..8ba76b70617 100644 --- a/src/mongo/base/owned_pointer_vector_test.cpp +++ b/src/mongo/base/owned_pointer_vector_test.cpp @@ -36,323 +36,325 @@ namespace mongo { - using std::string; +using std::string; namespace { - /** Helper class that logs its constructor argument to a static vector on destruction. */ - class DestructionLogger { - public: - DestructionLogger( const string& name ) : - _name( name ) { - } - ~DestructionLogger() { _destroyedNames.push_back( _name ); } +/** Helper class that logs its constructor argument to a static vector on destruction. */ +class DestructionLogger { +public: + DestructionLogger(const string& name) : _name(name) {} + ~DestructionLogger() { + _destroyedNames.push_back(_name); + } - static std::vector<string>& destroyedNames() { return _destroyedNames; } + static std::vector<string>& destroyedNames() { + return _destroyedNames; + } - private: - string _name; - static std::vector<string> _destroyedNames; - }; +private: + string _name; + static std::vector<string> _destroyedNames; +}; - std::vector<string> DestructionLogger::_destroyedNames; +std::vector<string> DestructionLogger::_destroyedNames; - TEST(OwnedPointerVectorTest, OwnedPointerDestroyed) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerVector<DestructionLogger> owned; - owned.mutableVector().push_back( new DestructionLogger( "foo" ) ); - // owned destroyed - } - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "foo", DestructionLogger::destroyedNames()[ 0 ] ); +TEST(OwnedPointerVectorTest, OwnedPointerDestroyed) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<DestructionLogger> owned; + owned.mutableVector().push_back(new DestructionLogger("foo")); + // owned destroyed } - - TEST(OwnedPointerVectorTest, OwnedConstPointerDestroyed) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerVector<const DestructionLogger> owned; - owned.push_back( new DestructionLogger( "foo" ) ); - // owned destroyed - } - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "foo", DestructionLogger::destroyedNames()[ 0 ] ); + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("foo", DestructionLogger::destroyedNames()[0]); +} + +TEST(OwnedPointerVectorTest, OwnedConstPointerDestroyed) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<const DestructionLogger> owned; + owned.push_back(new DestructionLogger("foo")); + // owned destroyed } - - TEST(OwnedPointerVectorTest, OwnedPointersDestroyedInOrder) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerVector<DestructionLogger> owned; - owned.push_back( new DestructionLogger( "first" ) ); - owned.push_back( new DestructionLogger( "second" ) ); - // owned destroyed - } - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("foo", DestructionLogger::destroyedNames()[0]); +} + +TEST(OwnedPointerVectorTest, OwnedPointersDestroyedInOrder) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<DestructionLogger> owned; + owned.push_back(new DestructionLogger("first")); + owned.push_back(new DestructionLogger("second")); + // owned destroyed } - - TEST(OwnedPointerVectorTest, ClearDestroyedInOrder) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerVector<DestructionLogger> owned; - owned.push_back( new DestructionLogger( "first" ) ); - owned.push_back( new DestructionLogger( "second" ) ); - - owned.clear(); - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); - ASSERT_EQUALS( 0U, owned.size() ); - // owned destroyed - } - // no additional deletion should have occured when owned was destroyed - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); +} + +TEST(OwnedPointerVectorTest, ClearDestroyedInOrder) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<DestructionLogger> owned; + owned.push_back(new DestructionLogger("first")); + owned.push_back(new DestructionLogger("second")); + + owned.clear(); + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); + ASSERT_EQUALS(0U, owned.size()); + // owned destroyed + } + // no additional deletion should have occured when owned was destroyed + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); +} + +TEST(OwnedPointerVectorTest, EraseDestroysAsCalled) { + DestructionLogger::destroyedNames().clear(); + { + // names are order of erasure + OwnedPointerVector<DestructionLogger> owned; + owned.push_back(new DestructionLogger("third")); + owned.push_back(new DestructionLogger("first")); + owned.push_back(new DestructionLogger("second")); + owned.push_back(new DestructionLogger("fourth")); + + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + + // erase "first", sliding "second" down to index 1 + owned.erase(owned.begin() + 1); + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames().back()); + ASSERT_EQUALS(3U, owned.size()); + + // erase "second" + owned.erase(owned.begin() + 1); + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames().back()); + ASSERT_EQUALS(2U, owned.size()); + + // erase "third" + owned.erase(owned.begin() + 0); + ASSERT_EQUALS(3U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("third", DestructionLogger::destroyedNames().back()); + ASSERT_EQUALS(1U, owned.size()); + + // owned destroyed } - TEST(OwnedPointerVectorTest, EraseDestroysAsCalled) { - DestructionLogger::destroyedNames().clear(); - { - // names are order of erasure - OwnedPointerVector<DestructionLogger> owned; - owned.push_back( new DestructionLogger( "third" ) ); - owned.push_back( new DestructionLogger( "first" ) ); - owned.push_back( new DestructionLogger( "second" ) ); - owned.push_back( new DestructionLogger( "fourth" ) ); - - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - - // erase "first", sliding "second" down to index 1 - owned.erase(owned.begin() + 1); - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames().back() ); - ASSERT_EQUALS( 3U, owned.size() ); - - // erase "second" - owned.erase(owned.begin() + 1); - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames().back() ); - ASSERT_EQUALS( 2U, owned.size() ); - - // erase "third" - owned.erase(owned.begin() + 0); - ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "third", DestructionLogger::destroyedNames().back() ); - ASSERT_EQUALS( 1U, owned.size() ); - - // owned destroyed - } + // only "four" should have been deleted when owned was destroyed + ASSERT_EQUALS(4U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("fourth", DestructionLogger::destroyedNames().back()); +} - // only "four" should have been deleted when owned was destroyed - ASSERT_EQUALS( 4U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "fourth", DestructionLogger::destroyedNames().back() ); - } +TEST(OwnedPointerVectorTest, Accessors) { + OwnedPointerVector<int> owned; + ASSERT_TRUE(owned.empty()); + ASSERT_EQUALS(0U, owned.size()); + + owned.push_back(new int(0)); + owned.push_back(new int(1)); + owned.push_back(new int(2)); - TEST(OwnedPointerVectorTest, Accessors) { - OwnedPointerVector<int> owned; - ASSERT_TRUE( owned.empty() ); - ASSERT_EQUALS( 0U, owned.size() ); + ASSERT_FALSE(owned.empty()); + ASSERT_EQUALS(3U, owned.size()); - owned.push_back( new int(0) ); - owned.push_back( new int(1) ); - owned.push_back( new int(2) ); + ASSERT_EQUALS(0, *owned[0]); + ASSERT_EQUALS(1, *owned[1]); + ASSERT_EQUALS(2, *owned[2]); - ASSERT_FALSE( owned.empty() ); - ASSERT_EQUALS( 3U, owned.size() ); + ASSERT_EQUALS(0, *owned.front()); + ASSERT_EQUALS(2, *owned.back()); +} - ASSERT_EQUALS( 0, *owned[0] ); - ASSERT_EQUALS( 1, *owned[1] ); - ASSERT_EQUALS( 2, *owned[2] ); +TEST(OwnedPointerVectorTest, TransferConstructor) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<DestructionLogger> source; + source.push_back(new DestructionLogger("first")); + source.push_back(new DestructionLogger("second")); - ASSERT_EQUALS( 0, *owned.front() ); - ASSERT_EQUALS( 2, *owned.back() ); + { + OwnedPointerVector<DestructionLogger> dest(source.release()); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(0U, source.size()); + ASSERT_EQUALS(2U, dest.size()); + // dest destroyed + } + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); + + // source destroyed } + // no additional deletions + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); +} + +TEST(OwnedPointerVectorTest, TransferConstructorDoesntModifyArgument) { + DestructionLogger::destroyedNames().clear(); + { + std::vector<DestructionLogger*> source; + source.push_back(new DestructionLogger("first")); + source.push_back(new DestructionLogger("second")); + + { + OwnedPointerVector<DestructionLogger> dest(source); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(2U, source.size()); + ASSERT_EQUALS(2U, dest.size()); + ASSERT(source == dest.vector()); // can't use ASSERT_EQUALS + // dest destroyed + } + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); - TEST(OwnedPointerVectorTest, TransferConstructor) { - DestructionLogger::destroyedNames().clear(); + ASSERT_EQUALS(2U, source.size()); + // source destroyed + } + // no additional deletions + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); +} + +TEST(OwnedPointerVectorTest, TransferAssignment) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<DestructionLogger> dest; { OwnedPointerVector<DestructionLogger> source; - source.push_back( new DestructionLogger( "first" ) ); - source.push_back( new DestructionLogger( "second" ) ); - - { - OwnedPointerVector<DestructionLogger> dest(source.release()); - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 0U, source.size() ); - ASSERT_EQUALS( 2U, dest.size() ); - // dest destroyed - } - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); + source.push_back(new DestructionLogger("first")); + source.push_back(new DestructionLogger("second")); + dest = source.release(); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(0U, source.size()); + ASSERT_EQUALS(2U, dest.size()); // source destroyed } - // no additional deletions - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - } - TEST(OwnedPointerVectorTest, TransferConstructorDoesntModifyArgument) { - DestructionLogger::destroyedNames().clear(); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(2U, dest.size()); + // dest destroyed + } + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); +} + +TEST(OwnedPointerVectorTest, TransferAssignmentDoesntModifyArgument) { + DestructionLogger::destroyedNames().clear(); + { + OwnedPointerVector<DestructionLogger> dest; { std::vector<DestructionLogger*> source; - source.push_back( new DestructionLogger( "first" ) ); - source.push_back( new DestructionLogger( "second" ) ); - - { - OwnedPointerVector<DestructionLogger> dest(source); - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 2U, source.size() ); - ASSERT_EQUALS( 2U, dest.size() ); - ASSERT( source == dest.vector() ); // can't use ASSERT_EQUALS - // dest destroyed - } - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); - - ASSERT_EQUALS( 2U, source.size() ); + source.push_back(new DestructionLogger("first")); + source.push_back(new DestructionLogger("second")); + + dest = source; + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(2U, source.size()); + ASSERT_EQUALS(2U, dest.size()); + ASSERT(source == dest.vector()); // can't use ASSERT_EQUALS // source destroyed } - // no additional deletions - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - } - TEST(OwnedPointerVectorTest, TransferAssignment) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerVector<DestructionLogger> dest; - { - OwnedPointerVector<DestructionLogger> source; - source.push_back( new DestructionLogger( "first" ) ); - source.push_back( new DestructionLogger( "second" ) ); - - dest = source.release(); - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 0U, source.size() ); - ASSERT_EQUALS( 2U, dest.size() ); - // source destroyed - } - - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 2U, dest.size() ); - // dest destroyed - } - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(2U, dest.size()); + // dest destroyed } - - TEST(OwnedPointerVectorTest, TransferAssignmentDoesntModifyArgument) { - DestructionLogger::destroyedNames().clear(); - { - OwnedPointerVector<DestructionLogger> dest; - { - std::vector<DestructionLogger*> source; - source.push_back( new DestructionLogger( "first" ) ); - source.push_back( new DestructionLogger( "second" ) ); - - dest = source; - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 2U, source.size() ); - ASSERT_EQUALS( 2U, dest.size() ); - ASSERT( source == dest.vector() ); // can't use ASSERT_EQUALS - // source destroyed - } - - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 2U, dest.size() ); - // dest destroyed - } - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[ 0 ] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[ 1 ] ); + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); +} + +TEST(OwnedPointerVectorTest, ReleaseAt) { + DestructionLogger::destroyedNames().clear(); + + std::unique_ptr<DestructionLogger> holder; + { + // names are order of deletion + OwnedPointerVector<DestructionLogger> owned; + owned.push_back(new DestructionLogger("first")); + owned.push_back(new DestructionLogger("third")); + owned.push_back(new DestructionLogger("second")); + + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + + // transfer ownership of "third" to holder + holder.reset(owned.releaseAt(1)); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(3U, owned.size()); + ASSERT_EQUALS(static_cast<DestructionLogger*>(NULL), owned[1]); + + // owned destroyed } + // owned deleted "first" and "second", but not "third" + ASSERT_EQUALS(2U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames()[0]); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); - TEST(OwnedPointerVectorTest, ReleaseAt) { - DestructionLogger::destroyedNames().clear(); - - std::unique_ptr<DestructionLogger> holder; - { - // names are order of deletion - OwnedPointerVector<DestructionLogger> owned; - owned.push_back( new DestructionLogger( "first" ) ); - owned.push_back( new DestructionLogger( "third" ) ); - owned.push_back( new DestructionLogger( "second" ) ); - - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); + // delete "third" + holder.reset(); + ASSERT_EQUALS(3U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("third", DestructionLogger::destroyedNames().back()); +} - // transfer ownership of "third" to holder - holder.reset(owned.releaseAt(1)); - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 3U, owned.size() ); - ASSERT_EQUALS( static_cast<DestructionLogger*>(NULL), owned[1] ); +TEST(OwnedPointerVectorTest, PopAndReleaseBack) { + DestructionLogger::destroyedNames().clear(); - // owned destroyed - } - // owned deleted "first" and "second", but not "third" - ASSERT_EQUALS( 2U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames()[0] ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[1] ); - - // delete "third" - holder.reset(); - ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "third", DestructionLogger::destroyedNames().back() ); - } + { + // names are order of deletion + OwnedPointerVector<DestructionLogger> owned; + owned.push_back(new DestructionLogger("second")); + owned.push_back(new DestructionLogger("third")); + owned.push_back(new DestructionLogger("first")); - TEST(OwnedPointerVectorTest, PopAndReleaseBack) { - DestructionLogger::destroyedNames().clear(); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); { - // names are order of deletion - OwnedPointerVector<DestructionLogger> owned; - owned.push_back( new DestructionLogger( "second" ) ); - owned.push_back( new DestructionLogger( "third" ) ); - owned.push_back( new DestructionLogger( "first" ) ); - - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - - { - // transfer ownership of "third" to holder - std::unique_ptr<DestructionLogger> holder(owned.popAndReleaseBack()); - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( 2U, owned.size() ); - // holder destroyed - } - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames().back() ); - // owned destroyed + // transfer ownership of "third" to holder + std::unique_ptr<DestructionLogger> holder(owned.popAndReleaseBack()); + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS(2U, owned.size()); + // holder destroyed } - // owned destructor deleted "second" and "third", but not "first" - ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[1] ); - ASSERT_EQUALS( "third", DestructionLogger::destroyedNames()[2] ); + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames().back()); + // owned destroyed } - - TEST(OwnedPointerVectorTest, PopAndDeleteBack) { - DestructionLogger::destroyedNames().clear(); - - { - // names are order of deletion - OwnedPointerVector<DestructionLogger> owned; - owned.push_back( new DestructionLogger( "second" ) ); - owned.push_back( new DestructionLogger( "third" ) ); - owned.push_back( new DestructionLogger( "first" ) ); - - ASSERT_EQUALS( 0U, DestructionLogger::destroyedNames().size() ); - - owned.popAndDeleteBack(); - ASSERT_EQUALS( 2U, owned.size() ); - ASSERT_EQUALS( 1U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "first", DestructionLogger::destroyedNames().back() ); - // owned destroyed - } - // owned destructor deleted "second" and "third", but not "first" - ASSERT_EQUALS( 3U, DestructionLogger::destroyedNames().size() ); - ASSERT_EQUALS( "second", DestructionLogger::destroyedNames()[1] ); - ASSERT_EQUALS( "third", DestructionLogger::destroyedNames()[2] ); + // owned destructor deleted "second" and "third", but not "first" + ASSERT_EQUALS(3U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); + ASSERT_EQUALS("third", DestructionLogger::destroyedNames()[2]); +} + +TEST(OwnedPointerVectorTest, PopAndDeleteBack) { + DestructionLogger::destroyedNames().clear(); + + { + // names are order of deletion + OwnedPointerVector<DestructionLogger> owned; + owned.push_back(new DestructionLogger("second")); + owned.push_back(new DestructionLogger("third")); + owned.push_back(new DestructionLogger("first")); + + ASSERT_EQUALS(0U, DestructionLogger::destroyedNames().size()); + + owned.popAndDeleteBack(); + ASSERT_EQUALS(2U, owned.size()); + ASSERT_EQUALS(1U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("first", DestructionLogger::destroyedNames().back()); + // owned destroyed } - -} // namespace -} // namespace mongo + // owned destructor deleted "second" and "third", but not "first" + ASSERT_EQUALS(3U, DestructionLogger::destroyedNames().size()); + ASSERT_EQUALS("second", DestructionLogger::destroyedNames()[1]); + ASSERT_EQUALS("third", DestructionLogger::destroyedNames()[2]); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/base/parse_number.cpp b/src/mongo/base/parse_number.cpp index ea3dfaccfdf..6e67645a6f0 100644 --- a/src/mongo/base/parse_number.cpp +++ b/src/mongo/base/parse_number.cpp @@ -39,34 +39,34 @@ namespace mongo { - /** - * Returns the value of the digit "c", with the same conversion behavior as strtol. - * - * Assumes "c" is an ASCII character or UTF-8 octet. - */ - static uint8_t _digitValue(char c) { - if (c >= '0' && c <= '9') - return uint8_t(c - '0'); - if (c >= 'a' && c <= 'z') - return uint8_t(c - 'a' + 10); - if (c >= 'A' && c <= 'Z') - return uint8_t(c - 'A' + 10); - return 36; // Illegal digit value for all supported bases. +/** + * Returns the value of the digit "c", with the same conversion behavior as strtol. + * + * Assumes "c" is an ASCII character or UTF-8 octet. + */ +static uint8_t _digitValue(char c) { + if (c >= '0' && c <= '9') + return uint8_t(c - '0'); + if (c >= 'a' && c <= 'z') + return uint8_t(c - 'a' + 10); + if (c >= 'A' && c <= 'Z') + return uint8_t(c - 'A' + 10); + return 36; // Illegal digit value for all supported bases. +} + +/** + * Assuming "stringValue" represents a parseable number, extracts the sign and returns a + * substring with any sign characters stripped away. "*isNegative" is set to true if the + * number is negative, and false otherwise. + */ +static inline StringData _extractSign(StringData stringValue, bool* isNegative) { + if (stringValue.empty()) { + *isNegative = false; + return stringValue; } - /** - * Assuming "stringValue" represents a parseable number, extracts the sign and returns a - * substring with any sign characters stripped away. "*isNegative" is set to true if the - * number is negative, and false otherwise. - */ - static inline StringData _extractSign(StringData stringValue, bool* isNegative) { - if (stringValue.empty()) { - *isNegative = false; - return stringValue; - } - - bool foundSignMarker; - switch (stringValue[0]) { + bool foundSignMarker; + switch (stringValue[0]) { case '-': foundSignMarker = true; *isNegative = true; @@ -79,198 +79,186 @@ namespace mongo { foundSignMarker = false; *isNegative = false; break; - } - - if (foundSignMarker) - return stringValue.substr(1); - return stringValue; } - /** - * Assuming "stringValue" represents a parseable number, determines what base to use given - * "inputBase". Stores the correct base into "*outputBase". Follows strtol rules. If - * "inputBase" is not 0, *outputBase is set to "inputBase". Otherwise, if "stringValue" starts - * with "0x" or "0X", sets outputBase to 16, or if it starts with 0, sets outputBase to 8. - * - * Returns stringValue, unless it sets *outputBase to 16, in which case it will strip off the - * "0x" or "0X" prefix, if present. - */ - static inline StringData _extractBase( - StringData stringValue, int inputBase, int* outputBase) { - - const StringData hexPrefixLower("0x", StringData::LiteralTag()); - const StringData hexPrefixUpper("0X", StringData::LiteralTag()); - if (inputBase == 0) { - if (stringValue.size() > 2 && (stringValue.startsWith(hexPrefixLower) || - stringValue.startsWith(hexPrefixUpper))) { - *outputBase = 16; - return stringValue.substr(2); - } - if (stringValue.size() > 1 && stringValue[0] == '0') { - *outputBase = 8; - return stringValue; - } - *outputBase = 10; - return stringValue; + if (foundSignMarker) + return stringValue.substr(1); + return stringValue; +} + +/** + * Assuming "stringValue" represents a parseable number, determines what base to use given + * "inputBase". Stores the correct base into "*outputBase". Follows strtol rules. If + * "inputBase" is not 0, *outputBase is set to "inputBase". Otherwise, if "stringValue" starts + * with "0x" or "0X", sets outputBase to 16, or if it starts with 0, sets outputBase to 8. + * + * Returns stringValue, unless it sets *outputBase to 16, in which case it will strip off the + * "0x" or "0X" prefix, if present. + */ +static inline StringData _extractBase(StringData stringValue, int inputBase, int* outputBase) { + const StringData hexPrefixLower("0x", StringData::LiteralTag()); + const StringData hexPrefixUpper("0X", StringData::LiteralTag()); + if (inputBase == 0) { + if (stringValue.size() > 2 && + (stringValue.startsWith(hexPrefixLower) || stringValue.startsWith(hexPrefixUpper))) { + *outputBase = 16; + return stringValue.substr(2); } - else { - *outputBase = inputBase; - if (inputBase == 16 && (stringValue.startsWith(hexPrefixLower) || - stringValue.startsWith(hexPrefixUpper))) { - return stringValue.substr(2); - } + if (stringValue.size() > 1 && stringValue[0] == '0') { + *outputBase = 8; return stringValue; } + *outputBase = 10; + return stringValue; + } else { + *outputBase = inputBase; + if (inputBase == 16 && + (stringValue.startsWith(hexPrefixLower) || stringValue.startsWith(hexPrefixUpper))) { + return stringValue.substr(2); + } + return stringValue; } +} - template <typename NumberType> - Status parseNumberFromStringWithBase( - StringData stringValue, int base, NumberType* result) { +template <typename NumberType> +Status parseNumberFromStringWithBase(StringData stringValue, int base, NumberType* result) { + typedef ::std::numeric_limits<NumberType> limits; - typedef ::std::numeric_limits<NumberType> limits; + if (base == 1 || base < 0 || base > 36) + return Status(ErrorCodes::BadValue, "Invalid base", 0); - if (base == 1 || base < 0 || base > 36) - return Status(ErrorCodes::BadValue, "Invalid base", 0); + bool isNegative = false; + StringData str = _extractBase(_extractSign(stringValue, &isNegative), base, &base); - bool isNegative = false; - StringData str = _extractBase(_extractSign(stringValue, &isNegative), base, &base); + if (str.empty()) + return Status(ErrorCodes::FailedToParse, "No digits"); - if (str.empty()) - return Status(ErrorCodes::FailedToParse, "No digits"); - - NumberType n(0); - if (isNegative) { - if (limits::is_signed) { - for (size_t i = 0; i < str.size(); ++i) { - NumberType digitValue = NumberType(_digitValue(str[i])); - if (int(digitValue) >= base) { - return Status(ErrorCodes::FailedToParse, - "Bad digit \"" + str.substr(i, 1).toString() + - "\" while parsing " + stringValue.toString()); - } - -// MSVC: warning C4146: unary minus operator applied to unsigned type, result still unsigned -// This code is statically known to be dead when NumberType is unsigned, so the warning is not real -#pragma warning(push) -#pragma warning(disable:4146) - if ((NumberType(limits::min() / base) > n) || - ((limits::min() - NumberType(n * base)) > -digitValue)) { -#pragma warning(pop) - - return Status(ErrorCodes::FailedToParse, "Underflow"); - } - - n *= NumberType(base); - n -= NumberType(digitValue); - } - } - else { - return Status(ErrorCodes::FailedToParse, "Negative value"); - } - } - else { + NumberType n(0); + if (isNegative) { + if (limits::is_signed) { for (size_t i = 0; i < str.size(); ++i) { NumberType digitValue = NumberType(_digitValue(str[i])); if (int(digitValue) >= base) { return Status(ErrorCodes::FailedToParse, "Bad digit \"" + str.substr(i, 1).toString() + - "\" while parsing " + stringValue.toString()); + "\" while parsing " + stringValue.toString()); } - if ((NumberType(limits::max() / base) < n) || - (NumberType(limits::max() - n * base) < digitValue)) { - return Status(ErrorCodes::FailedToParse, "Overflow"); +// MSVC: warning C4146: unary minus operator applied to unsigned type, result still unsigned +// This code is statically known to be dead when NumberType is unsigned, so the warning is not real +#pragma warning(push) +#pragma warning(disable : 4146) + if ((NumberType(limits::min() / base) > n) || + ((limits::min() - NumberType(n * base)) > -digitValue)) { +#pragma warning(pop) + + return Status(ErrorCodes::FailedToParse, "Underflow"); } n *= NumberType(base); - n += NumberType(digitValue); + n -= NumberType(digitValue); } + } else { + return Status(ErrorCodes::FailedToParse, "Negative value"); + } + } else { + for (size_t i = 0; i < str.size(); ++i) { + NumberType digitValue = NumberType(_digitValue(str[i])); + if (int(digitValue) >= base) { + return Status(ErrorCodes::FailedToParse, + "Bad digit \"" + str.substr(i, 1).toString() + "\" while parsing " + + stringValue.toString()); + } + if ((NumberType(limits::max() / base) < n) || + (NumberType(limits::max() - n * base) < digitValue)) { + return Status(ErrorCodes::FailedToParse, "Overflow"); + } + + n *= NumberType(base); + n += NumberType(digitValue); } - *result = n; - return Status::OK(); } + *result = n; + return Status::OK(); +} - // Definition of the various supported implementations of parseNumberFromStringWithBase. +// Definition of the various supported implementations of parseNumberFromStringWithBase. -#define DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(NUMBER_TYPE) \ +#define DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(NUMBER_TYPE) \ template Status parseNumberFromStringWithBase<NUMBER_TYPE>(StringData, int, NUMBER_TYPE*); - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(long) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(long long) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned long) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned long long) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(short) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned short) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(int) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned int) - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(int8_t); - DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(uint8_t); +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(long) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(long long) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned long) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned long long) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(short) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned short) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(int) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(unsigned int) +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(int8_t); +DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE(uint8_t); #undef DEFINE_PARSE_NUMBER_FROM_STRING_WITH_BASE #ifdef _WIN32 namespace { - /** - * Converts ascii c-locale uppercase characters to lower case, leaves other char values - * unchanged. - */ - char toLowerAscii(char c) { - if (isascii(c) && isupper(c)) - return _tolower(c); - return c; - } +/** + * Converts ascii c-locale uppercase characters to lower case, leaves other char values + * unchanged. + */ +char toLowerAscii(char c) { + if (isascii(c) && isupper(c)) + return _tolower(c); + return c; +} } // namespace #endif // defined(_WIN32) - template <> - Status parseNumberFromStringWithBase<double>(StringData stringValue, - int base, - double* result) { - if (base != 0) { - return Status(ErrorCodes::BadValue, - "Must pass 0 as base to parseNumberFromStringWithBase<double>."); - } - if (stringValue.empty()) - return Status(ErrorCodes::FailedToParse, "Empty string"); - - if (isspace(stringValue[0])) - return Status(ErrorCodes::FailedToParse, "Leading whitespace"); - - std::string str = stringValue.toString(); - const char* cStr = str.c_str(); - char* endp; - errno = 0; - double d = strtod(cStr, &endp); - int actualErrno = errno; - if (endp != stringValue.size() + cStr) { +template <> +Status parseNumberFromStringWithBase<double>(StringData stringValue, int base, double* result) { + if (base != 0) { + return Status(ErrorCodes::BadValue, + "Must pass 0 as base to parseNumberFromStringWithBase<double>."); + } + if (stringValue.empty()) + return Status(ErrorCodes::FailedToParse, "Empty string"); + + if (isspace(stringValue[0])) + return Status(ErrorCodes::FailedToParse, "Leading whitespace"); + + std::string str = stringValue.toString(); + const char* cStr = str.c_str(); + char* endp; + errno = 0; + double d = strtod(cStr, &endp); + int actualErrno = errno; + if (endp != stringValue.size() + cStr) { #ifdef _WIN32 - // The Windows libc implementation of strtod cannot parse +/-infinity or nan, - // so handle that here. - std::transform(str.begin(), str.end(), str.begin(), toLowerAscii); - if (str == StringData("nan", StringData::LiteralTag())) { - *result = std::numeric_limits<double>::quiet_NaN(); - return Status::OK(); - } - else if (str == StringData("+infinity", StringData::LiteralTag()) || - str == StringData("infinity", StringData::LiteralTag())) { - *result = std::numeric_limits<double>::infinity(); - return Status::OK(); - } - else if (str == StringData("-infinity", StringData::LiteralTag())) { - *result = -std::numeric_limits<double>::infinity(); - return Status::OK(); - } + // The Windows libc implementation of strtod cannot parse +/-infinity or nan, + // so handle that here. + std::transform(str.begin(), str.end(), str.begin(), toLowerAscii); + if (str == StringData("nan", StringData::LiteralTag())) { + *result = std::numeric_limits<double>::quiet_NaN(); + return Status::OK(); + } else if (str == StringData("+infinity", StringData::LiteralTag()) || + str == StringData("infinity", StringData::LiteralTag())) { + *result = std::numeric_limits<double>::infinity(); + return Status::OK(); + } else if (str == StringData("-infinity", StringData::LiteralTag())) { + *result = -std::numeric_limits<double>::infinity(); + return Status::OK(); + } #endif // defined(_WIN32) - return Status(ErrorCodes::FailedToParse, "Did not consume whole number."); - } - if (actualErrno == ERANGE) - return Status(ErrorCodes::FailedToParse, "Out of range"); - *result = d; - return Status::OK(); + return Status(ErrorCodes::FailedToParse, "Did not consume whole number."); } + if (actualErrno == ERANGE) + return Status(ErrorCodes::FailedToParse, "Out of range"); + *result = d; + return Status::OK(); +} } // namespace mongo diff --git a/src/mongo/base/parse_number.h b/src/mongo/base/parse_number.h index f9a5e44d1b8..44ee7af87e4 100644 --- a/src/mongo/base/parse_number.h +++ b/src/mongo/base/parse_number.h @@ -36,29 +36,29 @@ namespace mongo { - /** - * Parses a number out of a StringData. - * - * Parses "stringValue", interpreting it as a number of the given "base". On success, stores - * the parsed value into "*result" and returns Status::OK(). - * - * Valid values for "base" are 2-36, with 0 meaning "choose the base by inspecting the prefix - * on the number", as in strtol. Returns Status::BadValue if an illegal value is supplied for - * "base". - * - * The entirety of the std::string must consist of digits in the given base, except optionally the - * first character may be "+" or "-", and hexadecimal numbers may begin "0x". Same as strtol, - * without the property of stripping whitespace at the beginning, and fails to parse if there - * are non-digit characters at the end of the string. - * - * See parse_number.cpp for the available instantiations, and add any new instantiations there. - */ - template <typename NumberType> - Status parseNumberFromStringWithBase(StringData stringValue, int base, NumberType* result); +/** + * Parses a number out of a StringData. + * + * Parses "stringValue", interpreting it as a number of the given "base". On success, stores + * the parsed value into "*result" and returns Status::OK(). + * + * Valid values for "base" are 2-36, with 0 meaning "choose the base by inspecting the prefix + * on the number", as in strtol. Returns Status::BadValue if an illegal value is supplied for + * "base". + * + * The entirety of the std::string must consist of digits in the given base, except optionally the + * first character may be "+" or "-", and hexadecimal numbers may begin "0x". Same as strtol, + * without the property of stripping whitespace at the beginning, and fails to parse if there + * are non-digit characters at the end of the string. + * + * See parse_number.cpp for the available instantiations, and add any new instantiations there. + */ +template <typename NumberType> +Status parseNumberFromStringWithBase(StringData stringValue, int base, NumberType* result); - template <typename NumberType> - static Status parseNumberFromString(StringData stringValue, NumberType* result) { - return parseNumberFromStringWithBase(stringValue, 0, result); - } +template <typename NumberType> +static Status parseNumberFromString(StringData stringValue, NumberType* result) { + return parseNumberFromStringWithBase(stringValue, 0, result); +} } // namespace mongo diff --git a/src/mongo/base/parse_number_test.cpp b/src/mongo/base/parse_number_test.cpp index 952e1cc3866..75f5fd82f15 100644 --- a/src/mongo/base/parse_number_test.cpp +++ b/src/mongo/base/parse_number_test.cpp @@ -37,276 +37,266 @@ #include "mongo/util/mongoutils/str.h" // for str::stream()! #include "mongo/unittest/unittest.h" -#define ASSERT_PARSES(TYPE, INPUT_STRING, EXPECTED_VALUE) do { \ - TYPE v; \ - ASSERT_OK(parseNumberFromString(INPUT_STRING, &v)); \ - ASSERT_EQUALS(static_cast<TYPE>(EXPECTED_VALUE), v); \ +#define ASSERT_PARSES(TYPE, INPUT_STRING, EXPECTED_VALUE) \ + do { \ + TYPE v; \ + ASSERT_OK(parseNumberFromString(INPUT_STRING, &v)); \ + ASSERT_EQUALS(static_cast<TYPE>(EXPECTED_VALUE), v); \ } while (false) -#define ASSERT_PARSES_WITH_BASE(TYPE, INPUT_STRING, BASE, EXPECTED_VALUE) do { \ - TYPE v; \ +#define ASSERT_PARSES_WITH_BASE(TYPE, INPUT_STRING, BASE, EXPECTED_VALUE) \ + do { \ + TYPE v; \ ASSERT_OK(parseNumberFromStringWithBase(INPUT_STRING, BASE, &v)); \ - ASSERT_EQUALS(static_cast<TYPE>(EXPECTED_VALUE), v); \ + ASSERT_EQUALS(static_cast<TYPE>(EXPECTED_VALUE), v); \ } while (false) namespace mongo { namespace { - template <typename _NumberType> - class CommonNumberParsingTests { - public: - typedef _NumberType NumberType; - typedef std::numeric_limits<NumberType> Limits; +template <typename _NumberType> +class CommonNumberParsingTests { +public: + typedef _NumberType NumberType; + typedef std::numeric_limits<NumberType> Limits; - static void TestRejectingBadBases() { - NumberType ignored; - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", -1, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("10", 1, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("-10", 37, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase(" ", -1, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("f", 37, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("^%", -1, &ignored)); - } - - static void TestParsingNonNegatives() { - ASSERT_PARSES(NumberType, "10", 10); - ASSERT_PARSES(NumberType, "0", 0); - ASSERT_PARSES(NumberType, "1", 1); - ASSERT_PARSES(NumberType, "0xff", 0xff); - ASSERT_PARSES(NumberType, "077", 077); - } + static void TestRejectingBadBases() { + NumberType ignored; + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", -1, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("10", 1, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("-10", 37, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase(" ", -1, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("f", 37, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("^%", -1, &ignored)); + } - static void TestParsingNegatives() { - if (Limits::is_signed) { - ASSERT_PARSES(NumberType, "-10", -10); - ASSERT_PARSES(NumberType, "-0xff", -0xff); - ASSERT_PARSES(NumberType, "-077", -077); - } - else { - NumberType ignored; - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-0xff", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-077", &ignored)); - } - } + static void TestParsingNonNegatives() { + ASSERT_PARSES(NumberType, "10", 10); + ASSERT_PARSES(NumberType, "0", 0); + ASSERT_PARSES(NumberType, "1", 1); + ASSERT_PARSES(NumberType, "0xff", 0xff); + ASSERT_PARSES(NumberType, "077", 077); + } - static void TestParsingGarbage() { + static void TestParsingNegatives() { + if (Limits::is_signed) { + ASSERT_PARSES(NumberType, "-10", -10); + ASSERT_PARSES(NumberType, "-0xff", -0xff); + ASSERT_PARSES(NumberType, "-077", -077); + } else { NumberType ignored; - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString(" ", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString(" 10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("15b", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("--10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+-10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("++10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("--10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0x+10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0x-10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0+10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0-10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1+10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1-10", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("48*3", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0x", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+0x", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-0x", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-0xff", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-077", &ignored)); } + } - static void TestParsingWithExplicitBase() { - NumberType x; - ASSERT_PARSES_WITH_BASE(NumberType, "15b", 16, 0x15b); - ASSERT_PARSES_WITH_BASE(NumberType, "77", 8, 077); - ASSERT_PARSES_WITH_BASE(NumberType, "z", 36, 35); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("1b", 10, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("80", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0X", 16, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0x", 16, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0x", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0X", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0x", 10, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0X", 10, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0X", 16, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0x", 16, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0x", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0X", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0x", 10, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0X", 10, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0X", 16, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0x", 16, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0x", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0X", 8, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0x", 10, &x)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0X", 10, &x)); - } + static void TestParsingGarbage() { + NumberType ignored; + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString(" ", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString(" 10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("15b", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("--10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+-10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("++10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("--10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0x+10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0x-10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0+10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0-10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1+10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1-10", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("48*3", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("0x", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+0x", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-0x", &ignored)); + } - static void TestParsingLimits() { - using namespace mongoutils; - NumberType ignored; - ASSERT_PARSES(NumberType, std::string(str::stream() << Limits::max()), Limits::max()); - ASSERT_PARSES(NumberType, std::string(str::stream() << Limits::min()), Limits::min()); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString(std::string(str::stream() << Limits::max() << '0'), &ignored)); + static void TestParsingWithExplicitBase() { + NumberType x; + ASSERT_PARSES_WITH_BASE(NumberType, "15b", 16, 0x15b); + ASSERT_PARSES_WITH_BASE(NumberType, "77", 8, 077); + ASSERT_PARSES_WITH_BASE(NumberType, "z", 36, 35); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("1b", 10, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("80", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0X", 16, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0x", 16, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0x", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0X", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0x", 10, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("0X", 10, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0X", 16, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0x", 16, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0x", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0X", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0x", 10, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("+0X", 10, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0X", 16, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0x", 16, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0x", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0X", 8, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0x", 10, &x)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromStringWithBase("-0X", 10, &x)); + } - if (Limits::is_signed) - ASSERT_EQUALS( - ErrorCodes::FailedToParse, - parseNumberFromString(std::string(str::stream() << Limits::min() << '0'), &ignored)); - } - }; + static void TestParsingLimits() { + using namespace mongoutils; + NumberType ignored; + ASSERT_PARSES(NumberType, std::string(str::stream() << Limits::max()), Limits::max()); + ASSERT_PARSES(NumberType, std::string(str::stream() << Limits::min()), Limits::min()); + ASSERT_EQUALS( + ErrorCodes::FailedToParse, + parseNumberFromString(std::string(str::stream() << Limits::max() << '0'), &ignored)); -#define GENERAL_NUMBER_TESTS(SHORT_NAME, TYPE) \ - class ParseNumberTests##SHORT_NAME : public unittest::Test { \ - public: \ - typedef CommonNumberParsingTests<TYPE> TestFns; \ - }; \ - TEST_F(ParseNumberTests##SHORT_NAME, RejectBadBases) { \ - TestFns::TestRejectingBadBases(); \ - } \ - TEST_F(ParseNumberTests##SHORT_NAME, ParseNonNegatives) { \ - TestFns::TestParsingNonNegatives(); \ - } \ - TEST_F(ParseNumberTests##SHORT_NAME, ParseNegatives) { \ - TestFns::TestParsingNegatives(); \ - } \ - TEST_F(ParseNumberTests##SHORT_NAME, ParseGarbage) { \ - TestFns::TestParsingGarbage(); \ - } \ - TEST_F(ParseNumberTests##SHORT_NAME, ParseWithExplicitBase) { \ - TestFns::TestParsingWithExplicitBase(); \ - } \ - TEST_F(ParseNumberTests##SHORT_NAME, TestParsingLimits) { \ - TestFns::TestParsingLimits(); \ + if (Limits::is_signed) + ASSERT_EQUALS(ErrorCodes::FailedToParse, + parseNumberFromString(std::string(str::stream() << Limits::min() << '0'), + &ignored)); } +}; - GENERAL_NUMBER_TESTS(Short, short) - GENERAL_NUMBER_TESTS(Int, int) - GENERAL_NUMBER_TESTS(Long, long) - GENERAL_NUMBER_TESTS(LongLong, long long) - GENERAL_NUMBER_TESTS(UnsignedShort, unsigned short) - GENERAL_NUMBER_TESTS(UnsignedInt, unsigned int) - GENERAL_NUMBER_TESTS(UnsignedLong, unsigned long) - GENERAL_NUMBER_TESTS(UnsignedLongLong, unsigned long long) - GENERAL_NUMBER_TESTS(Int16, int16_t); - GENERAL_NUMBER_TESTS(Int32, int32_t); - GENERAL_NUMBER_TESTS(Int64, int64_t); - GENERAL_NUMBER_TESTS(UInt16, uint16_t); - GENERAL_NUMBER_TESTS(UInt32, uint32_t); - GENERAL_NUMBER_TESTS(UInt64, uint64_t); - - TEST(ParseNumber, NotNullTerminated) { - ASSERT_PARSES(int, StringData("1234", 3), 123); +#define GENERAL_NUMBER_TESTS(SHORT_NAME, TYPE) \ + class ParseNumberTests##SHORT_NAME : public unittest::Test { \ + public: \ + typedef CommonNumberParsingTests<TYPE> TestFns; \ + }; \ + TEST_F(ParseNumberTests##SHORT_NAME, RejectBadBases) { \ + TestFns::TestRejectingBadBases(); \ + } \ + TEST_F(ParseNumberTests##SHORT_NAME, ParseNonNegatives) { \ + TestFns::TestParsingNonNegatives(); \ + } \ + TEST_F(ParseNumberTests##SHORT_NAME, ParseNegatives) { \ + TestFns::TestParsingNegatives(); \ + } \ + TEST_F(ParseNumberTests##SHORT_NAME, ParseGarbage) { \ + TestFns::TestParsingGarbage(); \ + } \ + TEST_F(ParseNumberTests##SHORT_NAME, ParseWithExplicitBase) { \ + TestFns::TestParsingWithExplicitBase(); \ + } \ + TEST_F(ParseNumberTests##SHORT_NAME, TestParsingLimits) { \ + TestFns::TestParsingLimits(); \ } - TEST(ParseNumber, Int8) { - int8_t ignored; - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("-129", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("-130", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("-900", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("128", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("130", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("900", &ignored)); +GENERAL_NUMBER_TESTS(Short, short) +GENERAL_NUMBER_TESTS(Int, int) +GENERAL_NUMBER_TESTS(Long, long) +GENERAL_NUMBER_TESTS(LongLong, long long) +GENERAL_NUMBER_TESTS(UnsignedShort, unsigned short) +GENERAL_NUMBER_TESTS(UnsignedInt, unsigned int) +GENERAL_NUMBER_TESTS(UnsignedLong, unsigned long) +GENERAL_NUMBER_TESTS(UnsignedLongLong, unsigned long long) +GENERAL_NUMBER_TESTS(Int16, int16_t); +GENERAL_NUMBER_TESTS(Int32, int32_t); +GENERAL_NUMBER_TESTS(Int64, int64_t); +GENERAL_NUMBER_TESTS(UInt16, uint16_t); +GENERAL_NUMBER_TESTS(UInt32, uint32_t); +GENERAL_NUMBER_TESTS(UInt64, uint64_t); - for (int32_t i = -128; i <= 127; ++i) - ASSERT_PARSES(int8_t, std::string(mongoutils::str::stream() << i), i); - } +TEST(ParseNumber, NotNullTerminated) { + ASSERT_PARSES(int, StringData("1234", 3), 123); +} - TEST(ParseNumber, UInt8) { - uint8_t ignored; - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("-129", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("-130", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("-900", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("+256", &ignored)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString("+900", &ignored)); +TEST(ParseNumber, Int8) { + int8_t ignored; + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-129", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-130", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-900", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("128", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("130", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("900", &ignored)); - for (uint32_t i = 0; i <= 255; ++i) - ASSERT_PARSES(uint8_t, std::string(mongoutils::str::stream() << i), i); - } + for (int32_t i = -128; i <= 127; ++i) + ASSERT_PARSES(int8_t, std::string(mongoutils::str::stream() << i), i); +} - TEST(Double, TestRejectingBadBases) { - double ignored; +TEST(ParseNumber, UInt8) { + uint8_t ignored; + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-129", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-130", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-900", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+256", &ignored)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("+900", &ignored)); - // Only supported base for parseNumberFromStringWithBase<double> is 0. - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", -1, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 1, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 8, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 10, &ignored)); - ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 16, &ignored)); - } + for (uint32_t i = 0; i <= 255; ++i) + ASSERT_PARSES(uint8_t, std::string(mongoutils::str::stream() << i), i); +} - TEST(Double, TestParsingGarbage) { - double d; - CommonNumberParsingTests<double>::TestParsingGarbage(); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0.1", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0-1", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>(" 1.0", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0P4", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1e6 ", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>(" 1e6", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1e6 ", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>(" 1e6", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("0xabcab.defPa", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, - parseNumberFromString<double>(StringData("1.0\0garbage", - StringData::LiteralTag()), - &d)); - } +TEST(Double, TestRejectingBadBases) { + double ignored; - TEST(Double, TestParsingOverflow) { - double d; - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1e309", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-1e309", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1e-400", &d)); - ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-1e-400", &d)); - } + // Only supported base for parseNumberFromStringWithBase<double> is 0. + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", -1, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 1, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 8, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 10, &ignored)); + ASSERT_EQUALS(ErrorCodes::BadValue, parseNumberFromStringWithBase("0", 16, &ignored)); +} - TEST(Double, TestParsingNan) { - double d = 0; - ASSERT_OK(parseNumberFromString("NaN", &d)); - ASSERT_TRUE(std::isnan(d)); - } +TEST(Double, TestParsingGarbage) { + double d; + CommonNumberParsingTests<double>::TestParsingGarbage(); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0.1", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0-1", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>(" 1.0", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0P4", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1e6 ", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>(" 1e6", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1e6 ", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>(" 1e6", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("0xabcab.defPa", &d)); + ASSERT_EQUALS( + ErrorCodes::FailedToParse, + parseNumberFromString<double>(StringData("1.0\0garbage", StringData::LiteralTag()), &d)); +} - TEST(Double, TestParsingInfinity) { - double d = 0; - ASSERT_OK(parseNumberFromString("infinity", &d)); - ASSERT_TRUE(std::isinf(d)); - d = 0; - ASSERT_OK(parseNumberFromString("-Infinity", &d)); - ASSERT_TRUE(std::isinf(d)); - } +TEST(Double, TestParsingOverflow) { + double d; + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1e309", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-1e309", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("1e-400", &d)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString("-1e-400", &d)); +} - TEST(Double, TestParsingNormal) { - ASSERT_PARSES(double, "10", 10); - ASSERT_PARSES(double, "0", 0); - ASSERT_PARSES(double, "1", 1); - ASSERT_PARSES(double, "-10", -10); - ASSERT_PARSES(double, "1e8", 1e8); - ASSERT_PARSES(double, "1e-8", 1e-8); - ASSERT_PARSES(double, "12e-8", 12e-8); - ASSERT_PARSES(double, "-485.381e-8", -485.381e-8); +TEST(Double, TestParsingNan) { + double d = 0; + ASSERT_OK(parseNumberFromString("NaN", &d)); + ASSERT_TRUE(std::isnan(d)); +} + +TEST(Double, TestParsingInfinity) { + double d = 0; + ASSERT_OK(parseNumberFromString("infinity", &d)); + ASSERT_TRUE(std::isinf(d)); + d = 0; + ASSERT_OK(parseNumberFromString("-Infinity", &d)); + ASSERT_TRUE(std::isinf(d)); +} + +TEST(Double, TestParsingNormal) { + ASSERT_PARSES(double, "10", 10); + ASSERT_PARSES(double, "0", 0); + ASSERT_PARSES(double, "1", 1); + ASSERT_PARSES(double, "-10", -10); + ASSERT_PARSES(double, "1e8", 1e8); + ASSERT_PARSES(double, "1e-8", 1e-8); + ASSERT_PARSES(double, "12e-8", 12e-8); + ASSERT_PARSES(double, "-485.381e-8", -485.381e-8); #if !(defined(_WIN32) || defined(__sun)) - // Parse hexadecimal representations of a double. Hex literals not supported by MSVC, and - // not parseable by the Windows SDK libc or the Solaris libc in the mode we build. - // See SERVER-14131. + // Parse hexadecimal representations of a double. Hex literals not supported by MSVC, and + // not parseable by the Windows SDK libc or the Solaris libc in the mode we build. + // See SERVER-14131. - ASSERT_PARSES(double, "0xff", 0xff); - ASSERT_PARSES(double, "-0xff", -0xff); - ASSERT_PARSES(double, "0xabcab.defdefP-10", 0xabcab.defdefP-10); + ASSERT_PARSES(double, "0xff", 0xff); + ASSERT_PARSES(double, "-0xff", -0xff); + ASSERT_PARSES(double, "0xabcab.defdefP-10", 0xabcab.defdefP-10); #endif - } +} } // namespace } // namespace mongo diff --git a/src/mongo/base/status-inl.h b/src/mongo/base/status-inl.h index 880f548a87b..13209a48e33 100644 --- a/src/mongo/base/status-inl.h +++ b/src/mongo/base/status-inl.h @@ -29,82 +29,78 @@ namespace mongo { - inline Status Status::OK() { - return Status(); - } - - inline Status::Status(const Status& other) - : _error(other._error) { - ref(_error); - } - - inline Status& Status::operator=(const Status& other) { - ref(other._error); - unref(_error); - _error = other._error; - return *this; - } - - inline Status::Status(Status&& other) BOOST_NOEXCEPT - : _error(other._error) { - other._error = nullptr; - } - - inline Status& Status::operator=(Status&& other) BOOST_NOEXCEPT { - unref(_error); - _error = other._error; - other._error = nullptr; - return *this; - } - - inline Status::~Status() { - unref(_error); - } - - inline bool Status::isOK() const { - return code() == ErrorCodes::OK; - } - - inline ErrorCodes::Error Status::code() const { - return _error ? _error->code : ErrorCodes::OK; - } - - inline std::string Status::codeString() const { - return ErrorCodes::errorString(code()); - } - - inline std::string Status::reason() const { - return _error ? _error->reason : std::string(); - } - - inline int Status::location() const { - return _error ? _error->location : 0; - } - - inline AtomicUInt32::WordType Status::refCount() const { - return _error ? _error->refs.load() : 0; - } - - inline Status::Status() - : _error(NULL) { - } - - inline void Status::ref(ErrorInfo* error) { - if (error) - error->refs.fetchAndAdd(1); - } - - inline void Status::unref(ErrorInfo* error) { - if (error && (error->refs.subtractAndFetch(1) == 0)) - delete error; - } - - inline bool operator==(const ErrorCodes::Error lhs, const Status& rhs) { - return rhs == lhs; - } - - inline bool operator!=(const ErrorCodes::Error lhs, const Status& rhs) { - return rhs != lhs; - } - -} // namespace mongo +inline Status Status::OK() { + return Status(); +} + +inline Status::Status(const Status& other) : _error(other._error) { + ref(_error); +} + +inline Status& Status::operator=(const Status& other) { + ref(other._error); + unref(_error); + _error = other._error; + return *this; +} + +inline Status::Status(Status&& other) BOOST_NOEXCEPT : _error(other._error) { + other._error = nullptr; +} + +inline Status& Status::operator=(Status&& other) BOOST_NOEXCEPT { + unref(_error); + _error = other._error; + other._error = nullptr; + return *this; +} + +inline Status::~Status() { + unref(_error); +} + +inline bool Status::isOK() const { + return code() == ErrorCodes::OK; +} + +inline ErrorCodes::Error Status::code() const { + return _error ? _error->code : ErrorCodes::OK; +} + +inline std::string Status::codeString() const { + return ErrorCodes::errorString(code()); +} + +inline std::string Status::reason() const { + return _error ? _error->reason : std::string(); +} + +inline int Status::location() const { + return _error ? _error->location : 0; +} + +inline AtomicUInt32::WordType Status::refCount() const { + return _error ? _error->refs.load() : 0; +} + +inline Status::Status() : _error(NULL) {} + +inline void Status::ref(ErrorInfo* error) { + if (error) + error->refs.fetchAndAdd(1); +} + +inline void Status::unref(ErrorInfo* error) { + if (error && (error->refs.subtractAndFetch(1) == 0)) + delete error; +} + +inline bool operator==(const ErrorCodes::Error lhs, const Status& rhs) { + return rhs == lhs; +} + +inline bool operator!=(const ErrorCodes::Error lhs, const Status& rhs) { + return rhs != lhs; +} + +} // namespace mongo diff --git a/src/mongo/base/status.cpp b/src/mongo/base/status.cpp index 62466310ec6..c40d52f0d13 100644 --- a/src/mongo/base/status.cpp +++ b/src/mongo/base/status.cpp @@ -32,64 +32,59 @@ namespace mongo { - Status::ErrorInfo::ErrorInfo(ErrorCodes::Error aCode, std::string aReason, int aLocation) - : code(aCode), reason(std::move(aReason)), location(aLocation) { - } - - Status::ErrorInfo* Status::ErrorInfo::create(ErrorCodes::Error c, std::string r, int l) { - const bool needRep = ((c != ErrorCodes::OK) || - !r.empty() || - (l != 0)); - return needRep ? new ErrorInfo(c, std::move(r), l) : NULL; - } - - Status::Status(ErrorCodes::Error code, std::string reason, int location) - : _error(ErrorInfo::create(code, std::move(reason), location)) { - ref(_error); - } - - bool Status::compare(const Status& other) const { - return - code() == other.code() && - location() == other.location(); - } - - bool Status::operator==(const Status& other) const { - return compare(other); - } - - bool Status::operator!=(const Status& other) const { - return ! compare(other); - } - - bool Status::compareCode(const ErrorCodes::Error other) const { - return code() == other; - } - - bool Status::operator==(const ErrorCodes::Error other) const { - return compareCode(other); - } - - bool Status::operator!=(const ErrorCodes::Error other) const { - return ! compareCode(other); - } - - std::ostream& operator<<(std::ostream& os, const Status& status) { - return os << status.codeString() << " " << status.reason(); - } - - std::ostream& operator<<(std::ostream& os, ErrorCodes::Error code) { - return os << ErrorCodes::errorString(code); - } - - std::string Status::toString() const { - std::ostringstream ss; - ss << codeString(); - if ( !isOK() ) - ss << " " << reason(); - if ( location() != 0 ) - ss << " @ " << location(); - return ss.str(); - } - -} // namespace mongo +Status::ErrorInfo::ErrorInfo(ErrorCodes::Error aCode, std::string aReason, int aLocation) + : code(aCode), reason(std::move(aReason)), location(aLocation) {} + +Status::ErrorInfo* Status::ErrorInfo::create(ErrorCodes::Error c, std::string r, int l) { + const bool needRep = ((c != ErrorCodes::OK) || !r.empty() || (l != 0)); + return needRep ? new ErrorInfo(c, std::move(r), l) : NULL; +} + +Status::Status(ErrorCodes::Error code, std::string reason, int location) + : _error(ErrorInfo::create(code, std::move(reason), location)) { + ref(_error); +} + +bool Status::compare(const Status& other) const { + return code() == other.code() && location() == other.location(); +} + +bool Status::operator==(const Status& other) const { + return compare(other); +} + +bool Status::operator!=(const Status& other) const { + return !compare(other); +} + +bool Status::compareCode(const ErrorCodes::Error other) const { + return code() == other; +} + +bool Status::operator==(const ErrorCodes::Error other) const { + return compareCode(other); +} + +bool Status::operator!=(const ErrorCodes::Error other) const { + return !compareCode(other); +} + +std::ostream& operator<<(std::ostream& os, const Status& status) { + return os << status.codeString() << " " << status.reason(); +} + +std::ostream& operator<<(std::ostream& os, ErrorCodes::Error code) { + return os << ErrorCodes::errorString(code); +} + +std::string Status::toString() const { + std::ostringstream ss; + ss << codeString(); + if (!isOK()) + ss << " " << reason(); + if (location() != 0) + ss << " @ " << location(); + return ss.str(); +} + +} // namespace mongo diff --git a/src/mongo/base/status.h b/src/mongo/base/status.h index 3597da98129..51581a7248e 100644 --- a/src/mongo/base/status.h +++ b/src/mongo/base/status.h @@ -36,122 +36,122 @@ namespace mongo { +/** + * Status represents an error state or the absence thereof. + * + * A Status uses the standardized error codes -- from file 'error_codes.h' -- to + * determine an error's cause. It further clarifies the error with a textual + * description. Optionally, a Status may also have an error location number, which + * should be a unique, grep-able point in the code base (including assert numbers). + * + * Example usage: + * + * Status sumAB(int a, int b, int* c) { + * if (overflowIfSum(a,b)) { + * return Status(ErrorCodes::ERROR_OVERFLOW, "overflow in sumAB", 16494); + * } + * + * *c = a+b; + * return Status::OK(); + * } + * + * TODO: expand base/error_codes.h to capture common errors in current code + * TODO: generate base/error_codes.h out of a description file + * TODO: check 'location' duplicates against assert numbers + */ +class Status { +public: + // Short-hand for returning an OK status. + static inline Status OK(); + /** - * Status represents an error state or the absence thereof. - * - * A Status uses the standardized error codes -- from file 'error_codes.h' -- to - * determine an error's cause. It further clarifies the error with a textual - * description. Optionally, a Status may also have an error location number, which - * should be a unique, grep-able point in the code base (including assert numbers). - * - * Example usage: - * - * Status sumAB(int a, int b, int* c) { - * if (overflowIfSum(a,b)) { - * return Status(ErrorCodes::ERROR_OVERFLOW, "overflow in sumAB", 16494); - * } - * - * *c = a+b; - * return Status::OK(); - * } - * - * TODO: expand base/error_codes.h to capture common errors in current code - * TODO: generate base/error_codes.h out of a description file - * TODO: check 'location' duplicates against assert numbers + * Builds an error status given the error code, a textual description of what + * caused the error, and a unique position in the where the error occurred + * (similar to an assert number) */ - class Status { - public: - // Short-hand for returning an OK status. - static inline Status OK(); - - /** - * Builds an error status given the error code, a textual description of what - * caused the error, and a unique position in the where the error occurred - * (similar to an assert number) - */ - Status(ErrorCodes::Error code, std::string reason, int location = 0); + Status(ErrorCodes::Error code, std::string reason, int location = 0); - inline Status(const Status& other); - inline Status& operator=(const Status& other); + inline Status(const Status& other); + inline Status& operator=(const Status& other); - inline Status(Status&& other) BOOST_NOEXCEPT; - inline Status& operator=(Status&& other) BOOST_NOEXCEPT; + inline Status(Status&& other) BOOST_NOEXCEPT; + inline Status& operator=(Status&& other) BOOST_NOEXCEPT; - inline ~Status(); + inline ~Status(); - /** - * Returns true if 'other's error code and location are equal/different to this - * instance's. Otherwise returns false. - */ - bool compare(const Status& other) const; - bool operator==(const Status& other) const; - bool operator!=(const Status& other) const; + /** + * Returns true if 'other's error code and location are equal/different to this + * instance's. Otherwise returns false. + */ + bool compare(const Status& other) const; + bool operator==(const Status& other) const; + bool operator!=(const Status& other) const; - /** - * Returns true if 'other's error code is equal/different to this instance's. - * Otherwise returns false. - */ - bool compareCode(const ErrorCodes::Error other) const; - bool operator==(const ErrorCodes::Error other) const; - bool operator!=(const ErrorCodes::Error other) const; + /** + * Returns true if 'other's error code is equal/different to this instance's. + * Otherwise returns false. + */ + bool compareCode(const ErrorCodes::Error other) const; + bool operator==(const ErrorCodes::Error other) const; + bool operator!=(const ErrorCodes::Error other) const; - // - // accessors - // + // + // accessors + // - inline bool isOK() const; + inline bool isOK() const; - inline ErrorCodes::Error code() const; + inline ErrorCodes::Error code() const; - inline std::string codeString() const; + inline std::string codeString() const; - inline std::string reason() const; + inline std::string reason() const; - inline int location() const; + inline int location() const; - std::string toString() const; + std::string toString() const; - // - // Below interface used for testing code only. - // + // + // Below interface used for testing code only. + // - inline AtomicUInt32::WordType refCount() const; + inline AtomicUInt32::WordType refCount() const; - private: - inline Status(); +private: + inline Status(); - struct ErrorInfo { - AtomicUInt32 refs; // reference counter - const ErrorCodes::Error code; // error code - const std::string reason; // description of error cause - const int location; // unique location of the triggering line in the code + struct ErrorInfo { + AtomicUInt32 refs; // reference counter + const ErrorCodes::Error code; // error code + const std::string reason; // description of error cause + const int location; // unique location of the triggering line in the code - static ErrorInfo* create(ErrorCodes::Error code, std::string reason, int location); + static ErrorInfo* create(ErrorCodes::Error code, std::string reason, int location); - ErrorInfo(ErrorCodes::Error code, std::string reason, int location); - }; + ErrorInfo(ErrorCodes::Error code, std::string reason, int location); + }; - ErrorInfo* _error; + ErrorInfo* _error; - /** - * Increment/Decrement the reference counter inside an ErrorInfo - * - * @param error ErrorInfo to be incremented - */ - static inline void ref(ErrorInfo* error); - static inline void unref(ErrorInfo* error); - }; + /** + * Increment/Decrement the reference counter inside an ErrorInfo + * + * @param error ErrorInfo to be incremented + */ + static inline void ref(ErrorInfo* error); + static inline void unref(ErrorInfo* error); +}; - inline bool operator==(const ErrorCodes::Error lhs, const Status& rhs); +inline bool operator==(const ErrorCodes::Error lhs, const Status& rhs); - inline bool operator!=(const ErrorCodes::Error lhs, const Status& rhs); +inline bool operator!=(const ErrorCodes::Error lhs, const Status& rhs); - // - // Convenience method for unittest code. Please use accessors otherwise. - // +// +// Convenience method for unittest code. Please use accessors otherwise. +// - std::ostream& operator<<(std::ostream& os, const Status& status); - std::ostream& operator<<(std::ostream& os, ErrorCodes::Error); +std::ostream& operator<<(std::ostream& os, const Status& status); +std::ostream& operator<<(std::ostream& os, ErrorCodes::Error); } // namespace mongo diff --git a/src/mongo/base/status_test.cpp b/src/mongo/base/status_test.cpp index 700d14d4fc9..64af29a0ad5 100644 --- a/src/mongo/base/status_test.cpp +++ b/src/mongo/base/status_test.cpp @@ -38,222 +38,219 @@ namespace { - using mongo::ErrorCodes; - using mongo::Status; +using mongo::ErrorCodes; +using mongo::Status; - TEST(Basic, Accessors) { - Status status(ErrorCodes::MaxError, "error", 9999); - ASSERT_EQUALS(status.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(status.reason(), "error"); - ASSERT_EQUALS(status.location(), 9999); - } - - TEST(Basic, OKIsAValidStatus) { - Status status = Status::OK(); - ASSERT_EQUALS(status.code(), ErrorCodes::OK); - } - - TEST(Basic, Compare) { - Status errMax(ErrorCodes::MaxError, "error"); - ASSERT_TRUE(errMax.compare(errMax)); - ASSERT_FALSE(errMax.compare(Status::OK())); - - Status errMaxWithLoc(ErrorCodes::MaxError, "error", 9998); - ASSERT_FALSE(errMaxWithLoc.compare(errMax)); - } - - TEST(Cloning, Copy) { - Status orig(ErrorCodes::MaxError, "error"); - ASSERT_EQUALS(orig.refCount(), 1U); - - Status dest(orig); - ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(dest.reason(), "error"); - - ASSERT_EQUALS(dest.refCount(), 2U); - ASSERT_EQUALS(orig.refCount(), 2U); - } - - TEST(Cloning, MoveCopyOK) { - Status orig = Status::OK(); - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - Status dest(std::move(orig)); - - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - ASSERT_TRUE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 0U); - } - - TEST(Cloning, MoveCopyError) { - Status orig(ErrorCodes::MaxError, "error"); - ASSERT_FALSE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 1U); - - Status dest(std::move(orig)); - - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - ASSERT_FALSE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 1U); - ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(dest.reason(), "error"); - } - - TEST(Cloning, MoveAssignOKToOK) { - Status orig = Status::OK(); - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - Status dest = Status::OK(); - ASSERT_TRUE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 0U); - - dest = std::move(orig); - - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - ASSERT_TRUE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 0U); - } - - TEST(Cloning, MoveAssignErrorToError) { - Status orig = Status(ErrorCodes::MaxError, "error"); - ASSERT_FALSE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 1U); - ASSERT_EQUALS(orig.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(orig.reason(), "error"); - - Status dest = Status(ErrorCodes::InternalError, "error2"); - ASSERT_FALSE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 1U); - ASSERT_EQUALS(dest.code(), ErrorCodes::InternalError); - ASSERT_EQUALS(dest.reason(), "error2"); - - dest = std::move(orig); - - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - ASSERT_FALSE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 1U); - ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(dest.reason(), "error"); - } - - TEST(Cloning, MoveAssignErrorToOK) { - Status orig = Status(ErrorCodes::MaxError, "error"); - ASSERT_FALSE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 1U); - ASSERT_EQUALS(orig.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(orig.reason(), "error"); - - Status dest = Status::OK(); - ASSERT_TRUE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 0U); - - dest = std::move(orig); - - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - ASSERT_FALSE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 1U); - ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(dest.reason(), "error"); - } - - TEST(Cloning, MoveAssignOKToError) { - Status orig = Status::OK(); - ASSERT_TRUE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 0U); - - Status dest = Status(ErrorCodes::MaxError, "error"); - ASSERT_FALSE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 1U); - ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(dest.reason(), "error"); - - orig = std::move(dest); - - ASSERT_FALSE(orig.isOK()); - ASSERT_EQUALS(orig.refCount(), 1U); - ASSERT_EQUALS(orig.code(), ErrorCodes::MaxError); - ASSERT_EQUALS(orig.reason(), "error"); - - ASSERT_TRUE(dest.isOK()); - ASSERT_EQUALS(dest.refCount(), 0U); - } - - TEST(Cloning, OKIsNotRefCounted) { - ASSERT_EQUALS(Status::OK().refCount(), 0U); - - Status myOk = Status::OK(); - ASSERT_EQUALS(myOk.refCount(), 0U); - ASSERT_EQUALS(Status::OK().refCount(), 0U); - } - - TEST(Parsing, CodeToEnum) { - ASSERT_EQUALS(ErrorCodes::TypeMismatch, ErrorCodes::fromInt(ErrorCodes::TypeMismatch)); - ASSERT_EQUALS(ErrorCodes::UnknownError, ErrorCodes::fromInt(ErrorCodes::UnknownError)); - ASSERT_EQUALS(ErrorCodes::MaxError, ErrorCodes::fromInt(ErrorCodes::MaxError)); - ASSERT_EQUALS(ErrorCodes::OK, ErrorCodes::fromInt(0)); - } - - TEST(Transformers, ExceptionToStatus) { - using mongo::DBException; - using mongo::exceptionToStatus; - - auto reason = "oh no"; - - Status fromDBExcept = [=](){ - try { - throw DBException(reason, ErrorCodes::TypeMismatch); - } - catch (...) { - return exceptionToStatus(); - } - }(); - - ASSERT_NOT_OK(fromDBExcept); - ASSERT_EQUALS(fromDBExcept.reason(), reason); - ASSERT_EQUALS(fromDBExcept.code(), ErrorCodes::TypeMismatch); - - Status fromStdExcept = [=]() { - try { - throw std::out_of_range(reason); - } - catch (...) { - return exceptionToStatus(); - } - }(); - - ASSERT_NOT_OK(fromStdExcept); - // we don't check the exact error message because the type name of the exception - // isn't demangled on windows. - ASSERT_TRUE(fromStdExcept.reason().find(reason) != std::string::npos); - ASSERT_EQUALS(fromStdExcept.code(), ErrorCodes::UnknownError); - - class bar : public boost::exception {}; - - Status fromBoostExcept = [=]() { - try { - throw bar(); - } - catch (...) { - return exceptionToStatus(); - } - }(); - - ASSERT_NOT_OK(fromBoostExcept); - ASSERT_EQUALS(fromBoostExcept, ErrorCodes::UnknownError); - // Reason should include that it was a boost::exception - ASSERT_TRUE(fromBoostExcept.reason().find("boost::exception") != std::string::npos); - } - -} // unnamed namespace +TEST(Basic, Accessors) { + Status status(ErrorCodes::MaxError, "error", 9999); + ASSERT_EQUALS(status.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(status.reason(), "error"); + ASSERT_EQUALS(status.location(), 9999); +} + +TEST(Basic, OKIsAValidStatus) { + Status status = Status::OK(); + ASSERT_EQUALS(status.code(), ErrorCodes::OK); +} + +TEST(Basic, Compare) { + Status errMax(ErrorCodes::MaxError, "error"); + ASSERT_TRUE(errMax.compare(errMax)); + ASSERT_FALSE(errMax.compare(Status::OK())); + + Status errMaxWithLoc(ErrorCodes::MaxError, "error", 9998); + ASSERT_FALSE(errMaxWithLoc.compare(errMax)); +} + +TEST(Cloning, Copy) { + Status orig(ErrorCodes::MaxError, "error"); + ASSERT_EQUALS(orig.refCount(), 1U); + + Status dest(orig); + ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(dest.reason(), "error"); + + ASSERT_EQUALS(dest.refCount(), 2U); + ASSERT_EQUALS(orig.refCount(), 2U); +} + +TEST(Cloning, MoveCopyOK) { + Status orig = Status::OK(); + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + Status dest(std::move(orig)); + + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + ASSERT_TRUE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 0U); +} + +TEST(Cloning, MoveCopyError) { + Status orig(ErrorCodes::MaxError, "error"); + ASSERT_FALSE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 1U); + + Status dest(std::move(orig)); + + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + ASSERT_FALSE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 1U); + ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(dest.reason(), "error"); +} + +TEST(Cloning, MoveAssignOKToOK) { + Status orig = Status::OK(); + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + Status dest = Status::OK(); + ASSERT_TRUE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 0U); + + dest = std::move(orig); + + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + ASSERT_TRUE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 0U); +} + +TEST(Cloning, MoveAssignErrorToError) { + Status orig = Status(ErrorCodes::MaxError, "error"); + ASSERT_FALSE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 1U); + ASSERT_EQUALS(orig.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(orig.reason(), "error"); + + Status dest = Status(ErrorCodes::InternalError, "error2"); + ASSERT_FALSE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 1U); + ASSERT_EQUALS(dest.code(), ErrorCodes::InternalError); + ASSERT_EQUALS(dest.reason(), "error2"); + + dest = std::move(orig); + + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + ASSERT_FALSE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 1U); + ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(dest.reason(), "error"); +} + +TEST(Cloning, MoveAssignErrorToOK) { + Status orig = Status(ErrorCodes::MaxError, "error"); + ASSERT_FALSE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 1U); + ASSERT_EQUALS(orig.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(orig.reason(), "error"); + + Status dest = Status::OK(); + ASSERT_TRUE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 0U); + + dest = std::move(orig); + + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + ASSERT_FALSE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 1U); + ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(dest.reason(), "error"); +} + +TEST(Cloning, MoveAssignOKToError) { + Status orig = Status::OK(); + ASSERT_TRUE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 0U); + + Status dest = Status(ErrorCodes::MaxError, "error"); + ASSERT_FALSE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 1U); + ASSERT_EQUALS(dest.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(dest.reason(), "error"); + + orig = std::move(dest); + + ASSERT_FALSE(orig.isOK()); + ASSERT_EQUALS(orig.refCount(), 1U); + ASSERT_EQUALS(orig.code(), ErrorCodes::MaxError); + ASSERT_EQUALS(orig.reason(), "error"); + + ASSERT_TRUE(dest.isOK()); + ASSERT_EQUALS(dest.refCount(), 0U); +} + +TEST(Cloning, OKIsNotRefCounted) { + ASSERT_EQUALS(Status::OK().refCount(), 0U); + + Status myOk = Status::OK(); + ASSERT_EQUALS(myOk.refCount(), 0U); + ASSERT_EQUALS(Status::OK().refCount(), 0U); +} + +TEST(Parsing, CodeToEnum) { + ASSERT_EQUALS(ErrorCodes::TypeMismatch, ErrorCodes::fromInt(ErrorCodes::TypeMismatch)); + ASSERT_EQUALS(ErrorCodes::UnknownError, ErrorCodes::fromInt(ErrorCodes::UnknownError)); + ASSERT_EQUALS(ErrorCodes::MaxError, ErrorCodes::fromInt(ErrorCodes::MaxError)); + ASSERT_EQUALS(ErrorCodes::OK, ErrorCodes::fromInt(0)); +} + +TEST(Transformers, ExceptionToStatus) { + using mongo::DBException; + using mongo::exceptionToStatus; + + auto reason = "oh no"; + + Status fromDBExcept = [=]() { + try { + throw DBException(reason, ErrorCodes::TypeMismatch); + } catch (...) { + return exceptionToStatus(); + } + }(); + + ASSERT_NOT_OK(fromDBExcept); + ASSERT_EQUALS(fromDBExcept.reason(), reason); + ASSERT_EQUALS(fromDBExcept.code(), ErrorCodes::TypeMismatch); + + Status fromStdExcept = [=]() { + try { + throw std::out_of_range(reason); + } catch (...) { + return exceptionToStatus(); + } + }(); + + ASSERT_NOT_OK(fromStdExcept); + // we don't check the exact error message because the type name of the exception + // isn't demangled on windows. + ASSERT_TRUE(fromStdExcept.reason().find(reason) != std::string::npos); + ASSERT_EQUALS(fromStdExcept.code(), ErrorCodes::UnknownError); + + class bar : public boost::exception {}; + + Status fromBoostExcept = [=]() { + try { + throw bar(); + } catch (...) { + return exceptionToStatus(); + } + }(); + + ASSERT_NOT_OK(fromBoostExcept); + ASSERT_EQUALS(fromBoostExcept, ErrorCodes::UnknownError); + // Reason should include that it was a boost::exception + ASSERT_TRUE(fromBoostExcept.reason().find("boost::exception") != std::string::npos); +} + +} // unnamed namespace diff --git a/src/mongo/base/status_with.h b/src/mongo/base/status_with.h index 5f2d2d58344..1f2c9e85a0e 100644 --- a/src/mongo/base/status_with.h +++ b/src/mongo/base/status_with.h @@ -42,177 +42,169 @@ namespace mongo { +/** + * StatusWith is used to return an error or a value. + * This class is designed to make exception-free code cleaner by not needing as many out + * parameters. + * + * Example: + * StatusWith<int> fib( int n ) { + * if ( n < 0 ) + * return StatusWith<int>( ErrorCodes::BadValue, "parameter to fib has to be >= 0" ); + * if ( n <= 1 ) return StatusWith<int>( 1 ); + * StatusWith<int> a = fib( n - 1 ); + * StatusWith<int> b = fib( n - 2 ); + * if ( !a.isOK() ) return a; + * if ( !b.isOK() ) return b; + * return StatusWith<int>( a.getValue() + b.getValue() ); + * } + */ +template <typename T> +class StatusWith { + static_assert(!(std::is_same<T, mongo::Status>::value), "StatusWith<Status> is banned."); + +public: /** - * StatusWith is used to return an error or a value. - * This class is designed to make exception-free code cleaner by not needing as many out - * parameters. - * - * Example: - * StatusWith<int> fib( int n ) { - * if ( n < 0 ) - * return StatusWith<int>( ErrorCodes::BadValue, "parameter to fib has to be >= 0" ); - * if ( n <= 1 ) return StatusWith<int>( 1 ); - * StatusWith<int> a = fib( n - 1 ); - * StatusWith<int> b = fib( n - 2 ); - * if ( !a.isOK() ) return a; - * if ( !b.isOK() ) return b; - * return StatusWith<int>( a.getValue() + b.getValue() ); - * } + * for the error case */ - template<typename T> - class StatusWith { - static_assert(!(std::is_same<T, mongo::Status>::value), "StatusWith<Status> is banned."); - public: - - /** - * for the error case - */ - StatusWith( ErrorCodes::Error code, std::string reason, int location = 0 ) - : _status( code, std::move( reason ), location ) { - } - - /** - * for the error case - */ - StatusWith( Status status ) - : _status( std::move( status ) ) { - dassert(!isOK()); - } - - /** - * for the OK case - */ - StatusWith(T t) - : _status(Status::OK()), _t(std::move(t)) { - } - -#if defined(_MSC_VER) && _MSC_VER < 1900 - StatusWith(const StatusWith& s) - : _status(s._status), _t(s._t) { - } - - StatusWith(StatusWith&& s) - : _status(std::move(s._status)), _t(std::move(s._t)) { - } - - StatusWith& operator=(const StatusWith& other) { - _status = other._status; - _t = other._t; - return *this; - } - - StatusWith& operator=(StatusWith&& other) { - _status = std::move(other._status); - _t = std::move(other._t); - return *this; - } -#endif - - const T& getValue() const { - dassert(isOK()); - return *_t; - } - - T& getValue() { - dassert(isOK()); - return *_t; - } - - const Status& getStatus() const { - return _status; - } - - bool isOK() const { - return _status.isOK(); - } - - private: - Status _status; - boost::optional<T> _t; - }; - - template<typename T, typename... Args> - StatusWith<T> makeStatusWith(Args&&... args) { - return StatusWith<T>{T(std::forward<Args>(args)...)}; - } - - template<typename T> - std::ostream& operator<<(std::ostream& stream, const StatusWith<T>& sw) { - if (sw.isOK()) - return stream << sw.getValue(); - return stream << sw.getStatus(); - } - - // - // EqualityComparable(StatusWith<T>, T). Intentionally not providing an ordering relation. - // + StatusWith(ErrorCodes::Error code, std::string reason, int location = 0) + : _status(code, std::move(reason), location) {} - template<typename T> - bool operator==(const StatusWith<T>& sw, const T& val) { - return sw.isOK() && sw.getValue() == val; - } - - template<typename T> - bool operator==(const T& val, const StatusWith<T>& sw) { - return sw.isOK() && val == sw.getValue(); - } - - template<typename T> - bool operator!=(const StatusWith<T>& sw, const T& val) { - return !(sw == val); - } - - template<typename T> - bool operator!=(const T& val, const StatusWith<T>& sw) { - return !(val == sw); + /** + * for the error case + */ + StatusWith(Status status) : _status(std::move(status)) { + dassert(!isOK()); } - // - // EqualityComparable(StatusWith<T>, Status) - // + /** + * for the OK case + */ + StatusWith(T t) : _status(Status::OK()), _t(std::move(t)) {} - template<typename T> - bool operator==(const StatusWith<T>& sw, const Status& status) { - return sw.getStatus() == status; - } +#if defined(_MSC_VER) && _MSC_VER < 1900 + StatusWith(const StatusWith& s) : _status(s._status), _t(s._t) {} - template<typename T> - bool operator==(const Status& status, const StatusWith<T>& sw) { - return status == sw.getStatus(); - } + StatusWith(StatusWith&& s) : _status(std::move(s._status)), _t(std::move(s._t)) {} - template<typename T> - bool operator!=(const StatusWith<T>& sw, const Status& status) { - return !(sw == status); + StatusWith& operator=(const StatusWith& other) { + _status = other._status; + _t = other._t; + return *this; } - template<typename T> - bool operator!=(const Status& status, const StatusWith<T>& sw) { - return !(status == sw); + StatusWith& operator=(StatusWith&& other) { + _status = std::move(other._status); + _t = std::move(other._t); + return *this; } +#endif - // - // EqualityComparable(StatusWith<T>, ErrorCode) - // - - template<typename T> - bool operator==(const StatusWith<T>& sw, const ErrorCodes::Error code) { - return sw.getStatus() == code; + const T& getValue() const { + dassert(isOK()); + return *_t; } - template<typename T> - bool operator==(const ErrorCodes::Error code, const StatusWith<T>& sw) { - return code == sw.getStatus(); + T& getValue() { + dassert(isOK()); + return *_t; } - template<typename T> - bool operator!=(const StatusWith<T>& sw, const ErrorCodes::Error code) { - return !(sw == code); + const Status& getStatus() const { + return _status; } - template<typename T> - bool operator!=(const ErrorCodes::Error code, const StatusWith<T>& sw) { - return !(code == sw); + bool isOK() const { + return _status.isOK(); } -} // namespace mongo +private: + Status _status; + boost::optional<T> _t; +}; + +template <typename T, typename... Args> +StatusWith<T> makeStatusWith(Args&&... args) { + return StatusWith<T>{T(std::forward<Args>(args)...)}; +} + +template <typename T> +std::ostream& operator<<(std::ostream& stream, const StatusWith<T>& sw) { + if (sw.isOK()) + return stream << sw.getValue(); + return stream << sw.getStatus(); +} + +// +// EqualityComparable(StatusWith<T>, T). Intentionally not providing an ordering relation. +// + +template <typename T> +bool operator==(const StatusWith<T>& sw, const T& val) { + return sw.isOK() && sw.getValue() == val; +} + +template <typename T> +bool operator==(const T& val, const StatusWith<T>& sw) { + return sw.isOK() && val == sw.getValue(); +} + +template <typename T> +bool operator!=(const StatusWith<T>& sw, const T& val) { + return !(sw == val); +} + +template <typename T> +bool operator!=(const T& val, const StatusWith<T>& sw) { + return !(val == sw); +} + +// +// EqualityComparable(StatusWith<T>, Status) +// + +template <typename T> +bool operator==(const StatusWith<T>& sw, const Status& status) { + return sw.getStatus() == status; +} + +template <typename T> +bool operator==(const Status& status, const StatusWith<T>& sw) { + return status == sw.getStatus(); +} + +template <typename T> +bool operator!=(const StatusWith<T>& sw, const Status& status) { + return !(sw == status); +} + +template <typename T> +bool operator!=(const Status& status, const StatusWith<T>& sw) { + return !(status == sw); +} + +// +// EqualityComparable(StatusWith<T>, ErrorCode) +// + +template <typename T> +bool operator==(const StatusWith<T>& sw, const ErrorCodes::Error code) { + return sw.getStatus() == code; +} + +template <typename T> +bool operator==(const ErrorCodes::Error code, const StatusWith<T>& sw) { + return code == sw.getStatus(); +} + +template <typename T> +bool operator!=(const StatusWith<T>& sw, const ErrorCodes::Error code) { + return !(sw == code); +} + +template <typename T> +bool operator!=(const ErrorCodes::Error code, const StatusWith<T>& sw) { + return !(code == sw); +} + +} // namespace mongo diff --git a/src/mongo/base/status_with_test.cpp b/src/mongo/base/status_with_test.cpp index e9a18eb44cd..ad93bb2d406 100644 --- a/src/mongo/base/status_with_test.cpp +++ b/src/mongo/base/status_with_test.cpp @@ -36,60 +36,59 @@ namespace { - using mongo::makeStatusWith; - using mongo::StatusWith; - - TEST(StatusWith, makeStatusWith) { - - using mongo::StringData; - - auto s1 = makeStatusWith<int>(3); - ASSERT_TRUE(s1.isOK()); - ASSERT_EQUALS(uassertStatusOK(s1), 3); - - auto s2 = makeStatusWith<std::vector<int>>(); - ASSERT_TRUE(s2.isOK()); - ASSERT_EQUALS(uassertStatusOK(s2).size(), 0u); - - std::vector<int> i = {1, 2, 3}; - auto s3 = makeStatusWith<std::vector<int>>(i.begin(), i.end()); - ASSERT_TRUE(s3.isOK()); - ASSERT_EQUALS(uassertStatusOK(s3).size(), 3u); - - auto s4 = makeStatusWith<std::string>("foo"); - - ASSERT_TRUE(s4.isOK()); - ASSERT_EQUALS(uassertStatusOK(s4), std::string{"foo"}); - const char* foo = "barbaz"; - auto s5 = makeStatusWith<StringData>(foo, std::size_t{6}); - ASSERT_TRUE(s5.isOK()); - - // make sure CV qualifiers trigger correct overload - const StatusWith<StringData>& s6 = s5; - ASSERT_EQUALS(uassertStatusOK(s6), foo); - StatusWith<StringData>& s7 = s5; - ASSERT_EQUALS(uassertStatusOK(s7), foo); - ASSERT_EQUALS(uassertStatusOK(std::move(s5)), foo); - - // Check that we use T(...) and not T{...} - // ASSERT_EQUALS requires an ostream overload for vector<int> - ASSERT_TRUE(makeStatusWith<std::vector<int>>(1, 2) == std::vector<int>{2}); - } - - TEST(StatusWith, nonDefaultConstructible) { - - class NoDefault { - NoDefault() = delete; - public: - NoDefault(int x) : _x{x} {}; - int _x{0}; - }; - - auto swND = makeStatusWith<NoDefault>(1); - ASSERT_TRUE(swND.getValue()._x = 1); - - auto swNDerror = StatusWith<NoDefault>(mongo::ErrorCodes::BadValue, "foo"); - ASSERT_FALSE(swNDerror.isOK()); - } +using mongo::makeStatusWith; +using mongo::StatusWith; + +TEST(StatusWith, makeStatusWith) { + using mongo::StringData; + + auto s1 = makeStatusWith<int>(3); + ASSERT_TRUE(s1.isOK()); + ASSERT_EQUALS(uassertStatusOK(s1), 3); + + auto s2 = makeStatusWith<std::vector<int>>(); + ASSERT_TRUE(s2.isOK()); + ASSERT_EQUALS(uassertStatusOK(s2).size(), 0u); + + std::vector<int> i = {1, 2, 3}; + auto s3 = makeStatusWith<std::vector<int>>(i.begin(), i.end()); + ASSERT_TRUE(s3.isOK()); + ASSERT_EQUALS(uassertStatusOK(s3).size(), 3u); + + auto s4 = makeStatusWith<std::string>("foo"); + + ASSERT_TRUE(s4.isOK()); + ASSERT_EQUALS(uassertStatusOK(s4), std::string{"foo"}); + const char* foo = "barbaz"; + auto s5 = makeStatusWith<StringData>(foo, std::size_t{6}); + ASSERT_TRUE(s5.isOK()); + + // make sure CV qualifiers trigger correct overload + const StatusWith<StringData>& s6 = s5; + ASSERT_EQUALS(uassertStatusOK(s6), foo); + StatusWith<StringData>& s7 = s5; + ASSERT_EQUALS(uassertStatusOK(s7), foo); + ASSERT_EQUALS(uassertStatusOK(std::move(s5)), foo); + + // Check that we use T(...) and not T{...} + // ASSERT_EQUALS requires an ostream overload for vector<int> + ASSERT_TRUE(makeStatusWith<std::vector<int>>(1, 2) == std::vector<int>{2}); +} + +TEST(StatusWith, nonDefaultConstructible) { + class NoDefault { + NoDefault() = delete; + + public: + NoDefault(int x) : _x{x} {}; + int _x{0}; + }; + + auto swND = makeStatusWith<NoDefault>(1); + ASSERT_TRUE(swND.getValue()._x = 1); + + auto swNDerror = StatusWith<NoDefault>(mongo::ErrorCodes::BadValue, "foo"); + ASSERT_FALSE(swNDerror.isOK()); +} } // namespace diff --git a/src/mongo/base/string_data-inl.h b/src/mongo/base/string_data-inl.h index 4c8a0bdc924..1bf436cb5ea 100644 --- a/src/mongo/base/string_data-inl.h +++ b/src/mongo/base/string_data-inl.h @@ -33,106 +33,103 @@ namespace mongo { - inline int StringData::compare(StringData other) const { - int res = memcmp(_data, other._data, std::min(_size, other._size)); - if (res != 0) { - return res > 0 ? 1 : -1; - } - else if (_size == other._size) { - return 0; - } - else { - return _size > other._size ? 1 : -1; - } +inline int StringData::compare(StringData other) const { + int res = memcmp(_data, other._data, std::min(_size, other._size)); + if (res != 0) { + return res > 0 ? 1 : -1; + } else if (_size == other._size) { + return 0; + } else { + return _size > other._size ? 1 : -1; } - - inline bool StringData::equalCaseInsensitive( StringData other ) const { - if ( other.size() != size() ) - return false; - - for ( size_t x = 0; x < size(); x++ ) { - char a = _data[x]; - char b = other._data[x]; - if ( a == b ) - continue; - if ( tolower(a) == tolower(b) ) - continue; - return false; - } - - return true; - } - - inline void StringData::copyTo( char* dest, bool includeEndingNull ) const { - memcpy( dest, _data, size() ); - if ( includeEndingNull ) - dest[size()] = 0; - } - - inline size_t StringData::find( char c, size_t fromPos ) const { - if ( fromPos >= size() ) - return std::string::npos; - - const void* x = memchr( _data + fromPos, c, _size - fromPos ); - if ( x == 0 ) - return std::string::npos; - return static_cast<size_t>( static_cast<const char*>(x) - _data ); +} + +inline bool StringData::equalCaseInsensitive(StringData other) const { + if (other.size() != size()) + return false; + + for (size_t x = 0; x < size(); x++) { + char a = _data[x]; + char b = other._data[x]; + if (a == b) + continue; + if (tolower(a) == tolower(b)) + continue; + return false; } - inline size_t StringData::find( StringData needle ) const { - size_t mx = size(); - size_t needleSize = needle.size(); + return true; +} - if ( needleSize == 0 ) - return 0; - else if ( needleSize > mx ) - return std::string::npos; +inline void StringData::copyTo(char* dest, bool includeEndingNull) const { + memcpy(dest, _data, size()); + if (includeEndingNull) + dest[size()] = 0; +} - mx -= needleSize; - - for ( size_t i = 0; i <= mx; i++ ) { - if ( memcmp( _data + i, needle._data, needleSize ) == 0 ) - return i; - } +inline size_t StringData::find(char c, size_t fromPos) const { + if (fromPos >= size()) return std::string::npos; - } + const void* x = memchr(_data + fromPos, c, _size - fromPos); + if (x == 0) + return std::string::npos; + return static_cast<size_t>(static_cast<const char*>(x) - _data); +} - inline size_t StringData::rfind( char c, size_t fromPos ) const { - const size_t sz = size(); - if ( fromPos > sz ) - fromPos = sz; +inline size_t StringData::find(StringData needle) const { + size_t mx = size(); + size_t needleSize = needle.size(); - for ( const char* cur = _data + fromPos; cur > _data; --cur ) { - if ( *(cur - 1) == c ) - return (cur - _data) - 1; - } + if (needleSize == 0) + return 0; + else if (needleSize > mx) return std::string::npos; - } - - inline StringData StringData::substr( size_t pos, size_t n ) const { - if ( pos > size() ) - throw std::out_of_range( "out of range" ); - // truncate to end of string - if ( n > size() - pos ) - n = size() - pos; + mx -= needleSize; - return StringData( _data + pos, n ); + for (size_t i = 0; i <= mx; i++) { + if (memcmp(_data + i, needle._data, needleSize) == 0) + return i; } + return std::string::npos; +} - inline bool StringData::startsWith( StringData prefix ) const { - // TODO: Investigate an optimized implementation. - return substr(0, prefix.size()) == prefix; - } +inline size_t StringData::rfind(char c, size_t fromPos) const { + const size_t sz = size(); + if (fromPos > sz) + fromPos = sz; - inline bool StringData::endsWith( StringData suffix ) const { - // TODO: Investigate an optimized implementation. - const size_t thisSize = size(); - const size_t suffixSize = suffix.size(); - if (suffixSize > thisSize) - return false; - return substr(thisSize - suffixSize) == suffix; + for (const char* cur = _data + fromPos; cur > _data; --cur) { + if (*(cur - 1) == c) + return (cur - _data) - 1; } + return std::string::npos; +} + +inline StringData StringData::substr(size_t pos, size_t n) const { + if (pos > size()) + throw std::out_of_range("out of range"); + + // truncate to end of string + if (n > size() - pos) + n = size() - pos; + + return StringData(_data + pos, n); +} + +inline bool StringData::startsWith(StringData prefix) const { + // TODO: Investigate an optimized implementation. + return substr(0, prefix.size()) == prefix; +} + +inline bool StringData::endsWith(StringData suffix) const { + // TODO: Investigate an optimized implementation. + const size_t thisSize = size(); + const size_t suffixSize = suffix.size(); + if (suffixSize > thisSize) + return false; + return substr(thisSize - suffixSize) == suffix; +} } // namespace mongo diff --git a/src/mongo/base/string_data.cpp b/src/mongo/base/string_data.cpp index 2774f4deb4e..f00b185fc29 100644 --- a/src/mongo/base/string_data.cpp +++ b/src/mongo/base/string_data.cpp @@ -32,33 +32,33 @@ namespace mongo { - namespace { +namespace { - template <int SizeofSizeT> - size_t murmur3(StringData str); +template <int SizeofSizeT> +size_t murmur3(StringData str); - template <> - size_t murmur3<4>(StringData str) { - uint32_t hash; - MurmurHash3_x86_32(str.rawData(), str.size(), 0, &hash); - return hash; - } +template <> +size_t murmur3<4>(StringData str) { + uint32_t hash; + MurmurHash3_x86_32(str.rawData(), str.size(), 0, &hash); + return hash; +} - template <> - size_t murmur3<8>(StringData str) { - uint64_t hash[2]; - MurmurHash3_x64_128(str.rawData(), str.size(), 0, hash); - return static_cast<size_t>(hash[0]); - } +template <> +size_t murmur3<8>(StringData str) { + uint64_t hash[2]; + MurmurHash3_x64_128(str.rawData(), str.size(), 0, hash); + return static_cast<size_t>(hash[0]); +} - } // namespace +} // namespace - std::ostream& operator<<(std::ostream& stream, StringData value) { - return stream.write(value.rawData(), value.size()); - } +std::ostream& operator<<(std::ostream& stream, StringData value) { + return stream.write(value.rawData(), value.size()); +} - size_t StringData::Hasher::operator() (StringData str) const { - return murmur3<sizeof(size_t)>(str); - } +size_t StringData::Hasher::operator()(StringData str) const { + return murmur3<sizeof(size_t)>(str); +} -} // namespace mongo +} // namespace mongo diff --git a/src/mongo/base/string_data.h b/src/mongo/base/string_data.h index e70d27fc975..e3c58a747d4 100644 --- a/src/mongo/base/string_data.h +++ b/src/mongo/base/string_data.h @@ -37,154 +37,163 @@ namespace mongo { +/** + * A StringData object wraps a 'const std::string&' or a 'const char*' without copying its + * contents. The most common usage is as a function argument that takes any of the two + * forms of strings above. Fundamentally, this class tries go around the fact that string + * literals in C++ are char[N]'s. + * + * Notes: + * + * + The object StringData wraps around must be alive while the StringData is. + * + * + Because std::string data can be used to pass a substring around, one should never assume a + * rawData() terminates with a null. + */ +class StringData { +public: + /** Constructs an empty std::string data */ + StringData() : _data(NULL), _size(0) {} + /** - * A StringData object wraps a 'const std::string&' or a 'const char*' without copying its - * contents. The most common usage is as a function argument that takes any of the two - * forms of strings above. Fundamentally, this class tries go around the fact that string - * literals in C++ are char[N]'s. - * - * Notes: - * - * + The object StringData wraps around must be alive while the StringData is. - * - * + Because std::string data can be used to pass a substring around, one should never assume a - * rawData() terminates with a null. + * Constructs a StringData, for the case where the length of std::string is not known. 'c' + * must be a pointer to a null-terminated string. */ - class StringData { - public: - - /** Constructs an empty std::string data */ - StringData() - : _data(NULL), _size(0) {} - - /** - * Constructs a StringData, for the case where the length of std::string is not known. 'c' - * must be a pointer to a null-terminated string. - */ - StringData( const char* str ) - : _data(str), _size((str == NULL) ? 0 : std::strlen(str)) {} - - /** - * Constructs a StringData explicitly, for the case where the length of the std::string is - * already known. 'c' must be a pointer to a null-terminated string, and len must - * be the length that strlen(c) would return, a.k.a the index of the terminator in c. - */ - StringData( const char* c, size_t len ) - : _data(c), _size(len) {} - - /** Constructs a StringData, for the case of a string. */ - StringData( const std::string& s ) - : _data(s.c_str()), _size(s.size()) {} - - /** - * Constructs a StringData explicitly, for the case of a literal whose size is known at - * compile time. - */ - struct LiteralTag {}; - template<size_t N> - StringData( const char (&val)[N], LiteralTag ) - : _data(&val[0]), _size(N-1) {} - - /** - * Returns -1, 0, or 1 if 'this' is less, equal, or greater than 'other' in - * lexicographical order. - */ - int compare(StringData other) const; - - /** - * note: this uses tolower, and therefore does not handle - * come languages correctly. - * should be use sparingly - */ - bool equalCaseInsensitive( StringData other ) const; - - void copyTo( char* dest, bool includeEndingNull ) const; - - StringData substr( size_t pos, size_t n = std::numeric_limits<size_t>::max() ) const; - - // - // finders - // - - size_t find( char c , size_t fromPos = 0 ) const; - size_t find( StringData needle ) const; - size_t rfind( char c, size_t fromPos = std::string::npos ) const; - - /** - * Returns true if 'prefix' is a substring of this instance, anchored at position 0. - */ - bool startsWith( StringData prefix ) const; - - /** - * Returns true if 'suffix' is a substring of this instance, anchored at the end. - */ - bool endsWith( StringData suffix ) const; - - // - // accessors - // - - /** - * Get the pointer to the first byte of StringData. This is not guaranteed to be - * null-terminated, so if using this without checking size(), you are likely doing - * something wrong. - */ - const char* rawData() const { return _data; } - - size_t size() const { return _size; } - bool empty() const { return size() == 0; } - std::string toString() const { return std::string(_data, size()); } - char operator[] ( unsigned pos ) const { return _data[pos]; } - - /** - * Functor compatible with std::hash for std::unordered_{map,set} - * Warning: The hash function is subject to change. Do not use in cases where hashes need - * to be consistent across versions. - */ - struct Hasher { - size_t operator() (StringData str) const; - }; - - // - // iterators - // - - typedef const char* const_iterator; - - const_iterator begin() const { return rawData(); } - const_iterator end() const { return rawData() + size(); } - - private: - const char* _data; // is not guaranted to be null terminated (see "notes" above) - size_t _size; // 'size' does not include the null terminator - }; + StringData(const char* str) : _data(str), _size((str == NULL) ? 0 : std::strlen(str)) {} - inline bool operator==(StringData lhs, StringData rhs) { - return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); - } + /** + * Constructs a StringData explicitly, for the case where the length of the std::string is + * already known. 'c' must be a pointer to a null-terminated string, and len must + * be the length that strlen(c) would return, a.k.a the index of the terminator in c. + */ + StringData(const char* c, size_t len) : _data(c), _size(len) {} - inline bool operator!=(StringData lhs, StringData rhs) { - return !(lhs == rhs); - } + /** Constructs a StringData, for the case of a string. */ + StringData(const std::string& s) : _data(s.c_str()), _size(s.size()) {} - inline bool operator<(StringData lhs, StringData rhs) { - return lhs.compare(rhs) < 0 ; - } + /** + * Constructs a StringData explicitly, for the case of a literal whose size is known at + * compile time. + */ + struct LiteralTag {}; + template <size_t N> + StringData(const char(&val)[N], LiteralTag) + : _data(&val[0]), _size(N - 1) {} - inline bool operator<=(StringData lhs, StringData rhs) { - return lhs.compare(rhs) <= 0; + /** + * Returns -1, 0, or 1 if 'this' is less, equal, or greater than 'other' in + * lexicographical order. + */ + int compare(StringData other) const; + + /** + * note: this uses tolower, and therefore does not handle + * come languages correctly. + * should be use sparingly + */ + bool equalCaseInsensitive(StringData other) const; + + void copyTo(char* dest, bool includeEndingNull) const; + + StringData substr(size_t pos, size_t n = std::numeric_limits<size_t>::max()) const; + + // + // finders + // + + size_t find(char c, size_t fromPos = 0) const; + size_t find(StringData needle) const; + size_t rfind(char c, size_t fromPos = std::string::npos) const; + + /** + * Returns true if 'prefix' is a substring of this instance, anchored at position 0. + */ + bool startsWith(StringData prefix) const; + + /** + * Returns true if 'suffix' is a substring of this instance, anchored at the end. + */ + bool endsWith(StringData suffix) const; + + // + // accessors + // + + /** + * Get the pointer to the first byte of StringData. This is not guaranteed to be + * null-terminated, so if using this without checking size(), you are likely doing + * something wrong. + */ + const char* rawData() const { + return _data; } - inline bool operator>(StringData lhs, StringData rhs) { - return lhs.compare(rhs) > 0; + size_t size() const { + return _size; + } + bool empty() const { + return size() == 0; + } + std::string toString() const { + return std::string(_data, size()); } + char operator[](unsigned pos) const { + return _data[pos]; + } + + /** + * Functor compatible with std::hash for std::unordered_{map,set} + * Warning: The hash function is subject to change. Do not use in cases where hashes need + * to be consistent across versions. + */ + struct Hasher { + size_t operator()(StringData str) const; + }; + + // + // iterators + // - inline bool operator>=(StringData lhs, StringData rhs) { - return lhs.compare(rhs) >= 0; + typedef const char* const_iterator; + + const_iterator begin() const { + return rawData(); + } + const_iterator end() const { + return rawData() + size(); } - std::ostream& operator<<(std::ostream& stream, StringData value); +private: + const char* _data; // is not guaranted to be null terminated (see "notes" above) + size_t _size; // 'size' does not include the null terminator +}; + +inline bool operator==(StringData lhs, StringData rhs) { + return (lhs.size() == rhs.size()) && (lhs.compare(rhs) == 0); +} + +inline bool operator!=(StringData lhs, StringData rhs) { + return !(lhs == rhs); +} + +inline bool operator<(StringData lhs, StringData rhs) { + return lhs.compare(rhs) < 0; +} + +inline bool operator<=(StringData lhs, StringData rhs) { + return lhs.compare(rhs) <= 0; +} + +inline bool operator>(StringData lhs, StringData rhs) { + return lhs.compare(rhs) > 0; +} + +inline bool operator>=(StringData lhs, StringData rhs) { + return lhs.compare(rhs) >= 0; +} + +std::ostream& operator<<(std::ostream& stream, StringData value); -} // namespace mongo +} // namespace mongo #include "mongo/base/string_data-inl.h" diff --git a/src/mongo/base/string_data_test.cpp b/src/mongo/base/string_data_test.cpp index dca13e44dba..5dc19df3dd8 100644 --- a/src/mongo/base/string_data_test.cpp +++ b/src/mongo/base/string_data_test.cpp @@ -35,286 +35,276 @@ namespace { - using mongo::StringData; - using std::string; - - TEST(Construction, Empty) { - StringData strData; - ASSERT_EQUALS(strData.size(), 0U); - ASSERT_TRUE(strData.rawData() == NULL); - } - - TEST(Construction, FromStdString) { - std::string base("aaa"); - StringData strData(base); - ASSERT_EQUALS(strData.size(), base.size()); - ASSERT_EQUALS(strData.toString(), base); - } - - TEST(Construction, FromCString) { - std::string base("aaa"); - StringData strData(base.c_str()); - ASSERT_EQUALS(strData.size(), base.size()); - ASSERT_EQUALS(strData.toString(), base); - } - - TEST(Construction, FromNullCString) { - char* c = NULL; - StringData strData(c); - ASSERT_EQUALS(strData.size(), 0U); - ASSERT_TRUE(strData.rawData() == NULL); - } - - TEST(Construction, FromLiteral) { - StringData strData("ccc", StringData::LiteralTag()); - ASSERT_EQUALS(strData.size(), 3U); - ASSERT_EQUALS(strData.toString(), string("ccc")); - } - - TEST(Comparison, BothEmpty) { - StringData empty(""); - ASSERT_TRUE(empty == empty); - ASSERT_FALSE(empty != empty); - ASSERT_FALSE(empty > empty); - ASSERT_TRUE(empty >= empty); - ASSERT_FALSE(empty < empty); - ASSERT_TRUE(empty <= empty); - } - - TEST(Comparison, BothNonEmptyOnSize) { - StringData a("a"); - StringData aa("aa"); - ASSERT_FALSE(a == aa); - ASSERT_TRUE(a != aa); - ASSERT_FALSE(a > aa); - ASSERT_FALSE(a >= aa); - ASSERT_TRUE(a >= a); - ASSERT_TRUE(a < aa); - ASSERT_TRUE(a <= aa); - ASSERT_TRUE(a <= a); - } - - TEST(Comparison, BothNonEmptyOnContent) { - StringData a("a"); - StringData b("b"); - ASSERT_FALSE(a == b); - ASSERT_TRUE(a != b); - ASSERT_FALSE(a > b); - ASSERT_FALSE(a >= b); - ASSERT_TRUE(a < b); - ASSERT_TRUE(a <= b); - } - - TEST(Comparison, MixedEmptyAndNot) { - StringData empty(""); - StringData a("a"); - ASSERT_FALSE(a == empty); - ASSERT_TRUE(a != empty); - ASSERT_TRUE(a > empty); - ASSERT_TRUE(a >= empty); - ASSERT_FALSE(a < empty); - ASSERT_FALSE(a <= empty); - } - - TEST(Find, Char1) { - ASSERT_EQUALS( string::npos, StringData( "foo" ).find( 'a' ) ); - ASSERT_EQUALS( 0U, StringData( "foo" ).find( 'f' ) ); - ASSERT_EQUALS( 1U, StringData( "foo" ).find( 'o' ) ); - } - - TEST(Find, Str1 ) { - ASSERT_EQUALS( string::npos, StringData( "foo" ).find( "asdsadasda" ) ); - ASSERT_EQUALS( string::npos, StringData( "foo" ).find( "a" ) ); - ASSERT_EQUALS( string::npos, StringData( "foo" ).find( "food" ) ); - ASSERT_EQUALS( string::npos, StringData( "foo" ).find( "ooo" ) ); - - ASSERT_EQUALS( 0U, StringData( "foo" ).find( "f" ) ); - ASSERT_EQUALS( 0U, StringData( "foo" ).find( "fo" ) ); - ASSERT_EQUALS( 0U, StringData( "foo" ).find( "foo" ) ); - ASSERT_EQUALS( 1U, StringData( "foo" ).find( "o" ) ); - ASSERT_EQUALS( 1U, StringData( "foo" ).find( "oo" ) ); - - ASSERT_EQUALS( string("foo").find( "" ), StringData("foo").find( "" ) ); - } - - // Helper function for Test(Hasher, Str1) - template <int SizeofSizeT> - void SDHasher_check(void); - - template <> - void SDHasher_check<4>(void) { - ASSERT_EQUALS(StringData::Hasher()(""), - static_cast<size_t>(0)); - ASSERT_EQUALS(StringData::Hasher()("foo"), - static_cast<size_t>(4138058784ULL)); - ASSERT_EQUALS(StringData::Hasher()("pizza"), - static_cast<size_t>(3587803311ULL)); - ASSERT_EQUALS(StringData::Hasher()("mongo"), - static_cast<size_t>(3724335885ULL)); - ASSERT_EQUALS(StringData::Hasher()("murmur"), - static_cast<size_t>(1945310157ULL)); - } - - template <> - void SDHasher_check<8>(void) { - ASSERT_EQUALS(StringData::Hasher()(""), - static_cast<size_t>(0)); - ASSERT_EQUALS(StringData::Hasher()("foo"), - static_cast<size_t>(16316970633193145697ULL)); - ASSERT_EQUALS(StringData::Hasher()("pizza"), - static_cast<size_t>(12165495155477134356ULL)); - ASSERT_EQUALS(StringData::Hasher()("mongo"), - static_cast<size_t>(2861051452199491487ULL)); - ASSERT_EQUALS(StringData::Hasher()("murmur"), - static_cast<size_t>(18237957392784716687ULL)); - } - - TEST(Hasher, Str1) { - SDHasher_check<sizeof(size_t)>(); - } - - TEST(Rfind, Char1) { - ASSERT_EQUALS( string::npos, StringData( "foo" ).rfind( 'a' ) ); - - ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f' ) ); - ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f', 3 ) ); - ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f', 2 ) ); - ASSERT_EQUALS( 0U, StringData( "foo" ).rfind( 'f', 1 ) ); - ASSERT_EQUALS( string::npos, StringData( "foo", 0 ).rfind( 'f' ) ); - - ASSERT_EQUALS( 2U, StringData( "foo" ).rfind( 'o' ) ); - ASSERT_EQUALS( 2U, StringData( "foo", 3 ).rfind( 'o' ) ); - ASSERT_EQUALS( 1U, StringData( "foo", 2 ).rfind( 'o' ) ); - ASSERT_EQUALS( string::npos, StringData( "foo", 1 ).rfind( 'o' ) ); - ASSERT_EQUALS( string::npos, StringData( "foo", 0 ).rfind( 'o' ) ); - } - - // this is to verify we match std::string - void SUBSTR_TEST_HELP(StringData big, StringData small, size_t start, size_t len) { - ASSERT_EQUALS(small.toString(), big.toString().substr(start, len)); - ASSERT_EQUALS(small, StringData(big).substr(start, len)); - } - void SUBSTR_TEST_HELP(StringData big, StringData small, size_t start) { - ASSERT_EQUALS(small.toString(), big.toString().substr(start)); - ASSERT_EQUALS(small, StringData(big).substr(start)); - } +using mongo::StringData; +using std::string; + +TEST(Construction, Empty) { + StringData strData; + ASSERT_EQUALS(strData.size(), 0U); + ASSERT_TRUE(strData.rawData() == NULL); +} + +TEST(Construction, FromStdString) { + std::string base("aaa"); + StringData strData(base); + ASSERT_EQUALS(strData.size(), base.size()); + ASSERT_EQUALS(strData.toString(), base); +} + +TEST(Construction, FromCString) { + std::string base("aaa"); + StringData strData(base.c_str()); + ASSERT_EQUALS(strData.size(), base.size()); + ASSERT_EQUALS(strData.toString(), base); +} + +TEST(Construction, FromNullCString) { + char* c = NULL; + StringData strData(c); + ASSERT_EQUALS(strData.size(), 0U); + ASSERT_TRUE(strData.rawData() == NULL); +} + +TEST(Construction, FromLiteral) { + StringData strData("ccc", StringData::LiteralTag()); + ASSERT_EQUALS(strData.size(), 3U); + ASSERT_EQUALS(strData.toString(), string("ccc")); +} + +TEST(Comparison, BothEmpty) { + StringData empty(""); + ASSERT_TRUE(empty == empty); + ASSERT_FALSE(empty != empty); + ASSERT_FALSE(empty > empty); + ASSERT_TRUE(empty >= empty); + ASSERT_FALSE(empty < empty); + ASSERT_TRUE(empty <= empty); +} + +TEST(Comparison, BothNonEmptyOnSize) { + StringData a("a"); + StringData aa("aa"); + ASSERT_FALSE(a == aa); + ASSERT_TRUE(a != aa); + ASSERT_FALSE(a > aa); + ASSERT_FALSE(a >= aa); + ASSERT_TRUE(a >= a); + ASSERT_TRUE(a < aa); + ASSERT_TRUE(a <= aa); + ASSERT_TRUE(a <= a); +} + +TEST(Comparison, BothNonEmptyOnContent) { + StringData a("a"); + StringData b("b"); + ASSERT_FALSE(a == b); + ASSERT_TRUE(a != b); + ASSERT_FALSE(a > b); + ASSERT_FALSE(a >= b); + ASSERT_TRUE(a < b); + ASSERT_TRUE(a <= b); +} + +TEST(Comparison, MixedEmptyAndNot) { + StringData empty(""); + StringData a("a"); + ASSERT_FALSE(a == empty); + ASSERT_TRUE(a != empty); + ASSERT_TRUE(a > empty); + ASSERT_TRUE(a >= empty); + ASSERT_FALSE(a < empty); + ASSERT_FALSE(a <= empty); +} + +TEST(Find, Char1) { + ASSERT_EQUALS(string::npos, StringData("foo").find('a')); + ASSERT_EQUALS(0U, StringData("foo").find('f')); + ASSERT_EQUALS(1U, StringData("foo").find('o')); +} + +TEST(Find, Str1) { + ASSERT_EQUALS(string::npos, StringData("foo").find("asdsadasda")); + ASSERT_EQUALS(string::npos, StringData("foo").find("a")); + ASSERT_EQUALS(string::npos, StringData("foo").find("food")); + ASSERT_EQUALS(string::npos, StringData("foo").find("ooo")); + + ASSERT_EQUALS(0U, StringData("foo").find("f")); + ASSERT_EQUALS(0U, StringData("foo").find("fo")); + ASSERT_EQUALS(0U, StringData("foo").find("foo")); + ASSERT_EQUALS(1U, StringData("foo").find("o")); + ASSERT_EQUALS(1U, StringData("foo").find("oo")); + + ASSERT_EQUALS(string("foo").find(""), StringData("foo").find("")); +} + +// Helper function for Test(Hasher, Str1) +template <int SizeofSizeT> +void SDHasher_check(void); + +template <> +void SDHasher_check<4>(void) { + ASSERT_EQUALS(StringData::Hasher()(""), static_cast<size_t>(0)); + ASSERT_EQUALS(StringData::Hasher()("foo"), static_cast<size_t>(4138058784ULL)); + ASSERT_EQUALS(StringData::Hasher()("pizza"), static_cast<size_t>(3587803311ULL)); + ASSERT_EQUALS(StringData::Hasher()("mongo"), static_cast<size_t>(3724335885ULL)); + ASSERT_EQUALS(StringData::Hasher()("murmur"), static_cast<size_t>(1945310157ULL)); +} + +template <> +void SDHasher_check<8>(void) { + ASSERT_EQUALS(StringData::Hasher()(""), static_cast<size_t>(0)); + ASSERT_EQUALS(StringData::Hasher()("foo"), static_cast<size_t>(16316970633193145697ULL)); + ASSERT_EQUALS(StringData::Hasher()("pizza"), static_cast<size_t>(12165495155477134356ULL)); + ASSERT_EQUALS(StringData::Hasher()("mongo"), static_cast<size_t>(2861051452199491487ULL)); + ASSERT_EQUALS(StringData::Hasher()("murmur"), static_cast<size_t>(18237957392784716687ULL)); +} + +TEST(Hasher, Str1) { + SDHasher_check<sizeof(size_t)>(); +} + +TEST(Rfind, Char1) { + ASSERT_EQUALS(string::npos, StringData("foo").rfind('a')); + + ASSERT_EQUALS(0U, StringData("foo").rfind('f')); + ASSERT_EQUALS(0U, StringData("foo").rfind('f', 3)); + ASSERT_EQUALS(0U, StringData("foo").rfind('f', 2)); + ASSERT_EQUALS(0U, StringData("foo").rfind('f', 1)); + ASSERT_EQUALS(string::npos, StringData("foo", 0).rfind('f')); + + ASSERT_EQUALS(2U, StringData("foo").rfind('o')); + ASSERT_EQUALS(2U, StringData("foo", 3).rfind('o')); + ASSERT_EQUALS(1U, StringData("foo", 2).rfind('o')); + ASSERT_EQUALS(string::npos, StringData("foo", 1).rfind('o')); + ASSERT_EQUALS(string::npos, StringData("foo", 0).rfind('o')); +} + +// this is to verify we match std::string +void SUBSTR_TEST_HELP(StringData big, StringData small, size_t start, size_t len) { + ASSERT_EQUALS(small.toString(), big.toString().substr(start, len)); + ASSERT_EQUALS(small, StringData(big).substr(start, len)); +} +void SUBSTR_TEST_HELP(StringData big, StringData small, size_t start) { + ASSERT_EQUALS(small.toString(), big.toString().substr(start)); + ASSERT_EQUALS(small, StringData(big).substr(start)); +} // [12] is number of args to substr -#define SUBSTR_1_TEST_HELP(big,small,start) \ - ASSERT_EQUALS( StringData(small).toString(), StringData(big).toString().substr(start) ); \ - ASSERT_EQUALS( StringData(small), StringData(big).substr(start) ); - -#define SUBSTR_2_TEST_HELP(big,small,start,len) \ - ASSERT_EQUALS( StringData(small).toString(), StringData(big).toString().substr(start, len) ); \ - ASSERT_EQUALS( StringData(small), StringData(big).substr(start, len) ); - - TEST(Substr, Simple1 ) { - SUBSTR_1_TEST_HELP( "abcde", "abcde", 0 ); - SUBSTR_2_TEST_HELP( "abcde", "abcde", 0, 10 ); - SUBSTR_2_TEST_HELP( "abcde", "abcde", 0, 5 ); - SUBSTR_2_TEST_HELP( "abcde", "abc", 0, 3 ); - SUBSTR_1_TEST_HELP( "abcde", "cde", 2 ); - SUBSTR_2_TEST_HELP( "abcde", "cde", 2, 5 ); - SUBSTR_2_TEST_HELP( "abcde", "cde", 2, 3 ); - SUBSTR_2_TEST_HELP( "abcde", "cd", 2, 2 ); - SUBSTR_2_TEST_HELP( "abcde", "cd", 2, 2 ); - SUBSTR_1_TEST_HELP( "abcde", "", 5 ); - SUBSTR_2_TEST_HELP( "abcde", "", 5, 0 ); - SUBSTR_2_TEST_HELP( "abcde", "", 5, 10 ); - - // make sure we don't blow past the end of the StringData - SUBSTR_1_TEST_HELP( StringData("abcdeXXX", 5), "abcde", 0); - SUBSTR_2_TEST_HELP( StringData("abcdeXXX", 5), "abcde", 0, 10); - SUBSTR_1_TEST_HELP( StringData("abcdeXXX", 5), "de", 3); - SUBSTR_2_TEST_HELP( StringData("abcdeXXX", 5), "de", 3, 7); - SUBSTR_1_TEST_HELP( StringData("abcdeXXX", 5), "", 5); - SUBSTR_2_TEST_HELP( StringData("abcdeXXX", 5), "", 5, 1); - } - - TEST( equalCaseInsensitiveTest, Simple1 ) { - ASSERT( StringData( "abc" ).equalCaseInsensitive( "abc" ) ); - ASSERT( StringData( "abc" ).equalCaseInsensitive( "ABC" ) ); - ASSERT( StringData( "ABC" ).equalCaseInsensitive( "abc" ) ); - ASSERT( StringData( "ABC" ).equalCaseInsensitive( "ABC" ) ); - ASSERT( StringData( "ABC" ).equalCaseInsensitive( "AbC" ) ); - ASSERT( !StringData( "ABC" ).equalCaseInsensitive( "AbCd" ) ); - ASSERT( !StringData( "ABC" ).equalCaseInsensitive( "AdC" ) ); - } - - TEST(StartsWith, Simple) { - ASSERT(StringData("").startsWith("")); - ASSERT(!StringData("").startsWith("x")); - ASSERT(StringData("abcde").startsWith("")); - ASSERT(StringData("abcde").startsWith("a")); - ASSERT(StringData("abcde").startsWith("ab")); - ASSERT(StringData("abcde").startsWith("abc")); - ASSERT(StringData("abcde").startsWith("abcd")); - ASSERT(StringData("abcde").startsWith("abcde")); - ASSERT(!StringData("abcde").startsWith("abcdef")); - ASSERT(!StringData("abcde").startsWith("abdce")); - ASSERT(StringData("abcde").startsWith(StringData("abcdeXXXX").substr(0, 4))); - ASSERT(!StringData("abcde").startsWith(StringData("abdef").substr(0, 4))); - ASSERT(!StringData("abcde").substr(0, 3).startsWith("abcd")); - } - - TEST(EndsWith, Simple) { - //ASSERT(StringData("").endsWith("")); - ASSERT(!StringData("").endsWith("x")); - //ASSERT(StringData("abcde").endsWith("")); - ASSERT(StringData("abcde").endsWith(StringData("e", 0))); - ASSERT(StringData("abcde").endsWith("e")); - ASSERT(StringData("abcde").endsWith("de")); - ASSERT(StringData("abcde").endsWith("cde")); - ASSERT(StringData("abcde").endsWith("bcde")); - ASSERT(StringData("abcde").endsWith("abcde")); - ASSERT(!StringData("abcde").endsWith("0abcde")); - ASSERT(!StringData("abcde").endsWith("abdce")); - ASSERT(StringData("abcde").endsWith(StringData("bcdef").substr(0, 4))); - ASSERT(!StringData("abcde").endsWith(StringData("bcde", 3))); - ASSERT(!StringData("abcde").substr(0, 3).endsWith("cde")); - } - - TEST(ConstIterator, StdCopy) { - std::vector<char> chars; - const char rawData[] = "This is some raw data."; - StringData data(rawData, StringData::LiteralTag()); - - chars.resize(data.size()); - std::copy(data.begin(), data.end(), chars.begin()); - - for (size_t i = 0; i < data.size(); ++i) { - ASSERT_EQUALS(data[i], chars[i]); - } +#define SUBSTR_1_TEST_HELP(big, small, start) \ + ASSERT_EQUALS(StringData(small).toString(), StringData(big).toString().substr(start)); \ + ASSERT_EQUALS(StringData(small), StringData(big).substr(start)); + +#define SUBSTR_2_TEST_HELP(big, small, start, len) \ + ASSERT_EQUALS(StringData(small).toString(), StringData(big).toString().substr(start, len)); \ + ASSERT_EQUALS(StringData(small), StringData(big).substr(start, len)); + +TEST(Substr, Simple1) { + SUBSTR_1_TEST_HELP("abcde", "abcde", 0); + SUBSTR_2_TEST_HELP("abcde", "abcde", 0, 10); + SUBSTR_2_TEST_HELP("abcde", "abcde", 0, 5); + SUBSTR_2_TEST_HELP("abcde", "abc", 0, 3); + SUBSTR_1_TEST_HELP("abcde", "cde", 2); + SUBSTR_2_TEST_HELP("abcde", "cde", 2, 5); + SUBSTR_2_TEST_HELP("abcde", "cde", 2, 3); + SUBSTR_2_TEST_HELP("abcde", "cd", 2, 2); + SUBSTR_2_TEST_HELP("abcde", "cd", 2, 2); + SUBSTR_1_TEST_HELP("abcde", "", 5); + SUBSTR_2_TEST_HELP("abcde", "", 5, 0); + SUBSTR_2_TEST_HELP("abcde", "", 5, 10); + + // make sure we don't blow past the end of the StringData + SUBSTR_1_TEST_HELP(StringData("abcdeXXX", 5), "abcde", 0); + SUBSTR_2_TEST_HELP(StringData("abcdeXXX", 5), "abcde", 0, 10); + SUBSTR_1_TEST_HELP(StringData("abcdeXXX", 5), "de", 3); + SUBSTR_2_TEST_HELP(StringData("abcdeXXX", 5), "de", 3, 7); + SUBSTR_1_TEST_HELP(StringData("abcdeXXX", 5), "", 5); + SUBSTR_2_TEST_HELP(StringData("abcdeXXX", 5), "", 5, 1); +} + +TEST(equalCaseInsensitiveTest, Simple1) { + ASSERT(StringData("abc").equalCaseInsensitive("abc")); + ASSERT(StringData("abc").equalCaseInsensitive("ABC")); + ASSERT(StringData("ABC").equalCaseInsensitive("abc")); + ASSERT(StringData("ABC").equalCaseInsensitive("ABC")); + ASSERT(StringData("ABC").equalCaseInsensitive("AbC")); + ASSERT(!StringData("ABC").equalCaseInsensitive("AbCd")); + ASSERT(!StringData("ABC").equalCaseInsensitive("AdC")); +} + +TEST(StartsWith, Simple) { + ASSERT(StringData("").startsWith("")); + ASSERT(!StringData("").startsWith("x")); + ASSERT(StringData("abcde").startsWith("")); + ASSERT(StringData("abcde").startsWith("a")); + ASSERT(StringData("abcde").startsWith("ab")); + ASSERT(StringData("abcde").startsWith("abc")); + ASSERT(StringData("abcde").startsWith("abcd")); + ASSERT(StringData("abcde").startsWith("abcde")); + ASSERT(!StringData("abcde").startsWith("abcdef")); + ASSERT(!StringData("abcde").startsWith("abdce")); + ASSERT(StringData("abcde").startsWith(StringData("abcdeXXXX").substr(0, 4))); + ASSERT(!StringData("abcde").startsWith(StringData("abdef").substr(0, 4))); + ASSERT(!StringData("abcde").substr(0, 3).startsWith("abcd")); +} + +TEST(EndsWith, Simple) { + // ASSERT(StringData("").endsWith("")); + ASSERT(!StringData("").endsWith("x")); + // ASSERT(StringData("abcde").endsWith("")); + ASSERT(StringData("abcde").endsWith(StringData("e", 0))); + ASSERT(StringData("abcde").endsWith("e")); + ASSERT(StringData("abcde").endsWith("de")); + ASSERT(StringData("abcde").endsWith("cde")); + ASSERT(StringData("abcde").endsWith("bcde")); + ASSERT(StringData("abcde").endsWith("abcde")); + ASSERT(!StringData("abcde").endsWith("0abcde")); + ASSERT(!StringData("abcde").endsWith("abdce")); + ASSERT(StringData("abcde").endsWith(StringData("bcdef").substr(0, 4))); + ASSERT(!StringData("abcde").endsWith(StringData("bcde", 3))); + ASSERT(!StringData("abcde").substr(0, 3).endsWith("cde")); +} + +TEST(ConstIterator, StdCopy) { + std::vector<char> chars; + const char rawData[] = "This is some raw data."; + StringData data(rawData, StringData::LiteralTag()); + + chars.resize(data.size()); + std::copy(data.begin(), data.end(), chars.begin()); + + for (size_t i = 0; i < data.size(); ++i) { + ASSERT_EQUALS(data[i], chars[i]); } +} - TEST(ConstIterator, StdReverseCopy) { - std::vector<char> chars; - const char rawData[] = "This is some raw data."; - StringData data(rawData, StringData::LiteralTag()); +TEST(ConstIterator, StdReverseCopy) { + std::vector<char> chars; + const char rawData[] = "This is some raw data."; + StringData data(rawData, StringData::LiteralTag()); - chars.resize(data.size()); - std::reverse_copy(data.begin(), data.end(), chars.begin()); + chars.resize(data.size()); + std::reverse_copy(data.begin(), data.end(), chars.begin()); - const char rawDataExpected[] = ".atad war emos si sihT"; + const char rawDataExpected[] = ".atad war emos si sihT"; - for (size_t i = 0; i < data.size(); ++i) { - ASSERT_EQUALS(rawDataExpected[i], chars[i]); - } + for (size_t i = 0; i < data.size(); ++i) { + ASSERT_EQUALS(rawDataExpected[i], chars[i]); } +} - TEST(ConstIterator, StdReplaceCopy) { - std::vector<char> chars; - const char rawData[] = "This is some raw data."; - StringData data(rawData, StringData::LiteralTag()); +TEST(ConstIterator, StdReplaceCopy) { + std::vector<char> chars; + const char rawData[] = "This is some raw data."; + StringData data(rawData, StringData::LiteralTag()); - chars.resize(data.size()); - std::replace_copy(data.begin(), data.end(), chars.begin(), ' ', '_'); + chars.resize(data.size()); + std::replace_copy(data.begin(), data.end(), chars.begin(), ' ', '_'); - const char rawDataExpected[] = "This_is_some_raw_data."; + const char rawDataExpected[] = "This_is_some_raw_data."; - for (size_t i = 0; i < data.size(); ++i) { - ASSERT_EQUALS(rawDataExpected[i], chars[i]); - } + for (size_t i = 0; i < data.size(); ++i) { + ASSERT_EQUALS(rawDataExpected[i], chars[i]); } +} -} // unnamed namespace +} // unnamed namespace diff --git a/src/mongo/base/validate_locale.cpp b/src/mongo/base/validate_locale.cpp index 54d9d1a0c22..5a4320c34c5 100644 --- a/src/mongo/base/validate_locale.cpp +++ b/src/mongo/base/validate_locale.cpp @@ -33,22 +33,20 @@ namespace mongo { -MONGO_INITIALIZER_GENERAL(ValidateLocale, - MONGO_NO_PREREQUISITES, - MONGO_DEFAULT_PREREQUISITES) - (InitializerContext*) { - try { - // Validate that boost can correctly load the user's locale - boost::filesystem::path("/").has_root_directory(); - } - catch (const std::runtime_error&) { - return Status(ErrorCodes::BadValue, "Invalid or no user locale set." +MONGO_INITIALIZER_GENERAL(ValidateLocale, MONGO_NO_PREREQUISITES, MONGO_DEFAULT_PREREQUISITES) +(InitializerContext*) { + try { + // Validate that boost can correctly load the user's locale + boost::filesystem::path("/").has_root_directory(); + } catch (const std::runtime_error&) { + return Status(ErrorCodes::BadValue, + "Invalid or no user locale set." #ifndef _WIN32 - " Please ensure LANG and/or LC_* environment variables are set correctly." + " Please ensure LANG and/or LC_* environment variables are set correctly." #endif - ); - } - return Status::OK(); + ); } + return Status::OK(); +} } // namespace mongo |