diff options
author | Jason Carey <jcarey@argv.me> | 2016-05-10 14:17:00 -0400 |
---|---|---|
committer | Jason Carey <jcarey@argv.me> | 2016-06-17 17:56:11 -0400 |
commit | 907182f4672ab2dcea1e16da5366518a4e44fa8d (patch) | |
tree | b74d4d9f97a3797b409db0f428ac05462501fa73 /src/mongo/base | |
parent | 737d557b1729d8d6c2892832b75630f045787ec1 (diff) | |
download | mongo-907182f4672ab2dcea1e16da5366518a4e44fa8d.tar.gz |
SERVER-24651 Add and use string data literals
Replace StringData("foo", StringData::LiteralTag()) with "foo"_sd
Diffstat (limited to 'src/mongo/base')
-rw-r--r-- | src/mongo/base/parse_number.cpp | 11 | ||||
-rw-r--r-- | src/mongo/base/parse_number_test.cpp | 4 | ||||
-rw-r--r-- | src/mongo/base/string_data-inl.h | 145 | ||||
-rw-r--r-- | src/mongo/base/string_data.h | 136 | ||||
-rw-r--r-- | src/mongo/base/string_data_test.cpp | 34 |
5 files changed, 149 insertions, 181 deletions
diff --git a/src/mongo/base/parse_number.cpp b/src/mongo/base/parse_number.cpp index 7aa112f08be..b9c81807b19 100644 --- a/src/mongo/base/parse_number.cpp +++ b/src/mongo/base/parse_number.cpp @@ -95,8 +95,8 @@ static inline StringData _extractSign(StringData stringValue, bool* isNegative) * "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()); + const auto hexPrefixLower = "0x"_sd; + const auto hexPrefixUpper = "0X"_sd; if (inputBase == 0) { if (stringValue.size() > 2 && (stringValue.startsWith(hexPrefixLower) || stringValue.startsWith(hexPrefixUpper))) { @@ -239,14 +239,13 @@ Status parseNumberFromStringWithBase<double>(StringData stringValue, int base, d // 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())) { + if (str == "nan"_sd) { *result = std::numeric_limits<double>::quiet_NaN(); return Status::OK(); - } else if (str == StringData("+infinity", StringData::LiteralTag()) || - str == StringData("infinity", StringData::LiteralTag())) { + } else if (str == "+infinity"_sd || str == "infinity"_sd) { *result = std::numeric_limits<double>::infinity(); return Status::OK(); - } else if (str == StringData("-infinity", StringData::LiteralTag())) { + } else if (str == "-infinity"_sd) { *result = -std::numeric_limits<double>::infinity(); return Status::OK(); } diff --git a/src/mongo/base/parse_number_test.cpp b/src/mongo/base/parse_number_test.cpp index df2902800e0..0ed768a7dd3 100644 --- a/src/mongo/base/parse_number_test.cpp +++ b/src/mongo/base/parse_number_test.cpp @@ -249,9 +249,7 @@ TEST(Double, TestParsingGarbage) { 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)); + ASSERT_EQUALS(ErrorCodes::FailedToParse, parseNumberFromString<double>("1.0\0garbage"_sd, &d)); } TEST(Double, TestParsingOverflow) { diff --git a/src/mongo/base/string_data-inl.h b/src/mongo/base/string_data-inl.h deleted file mode 100644 index a8342aa88bb..00000000000 --- a/src/mongo/base/string_data-inl.h +++ /dev/null @@ -1,145 +0,0 @@ -// string_data_inline.h - -/* Copyright 2010 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects - * for all of the code used other than as permitted herein. If you modify - * file(s) with this exception, you may extend this exception to your - * version of the file(s), but you are not obligated to do so. If you do not - * wish to do so, delete this exception statement from your version. If you - * delete this exception statement from all source files in the program, - * then also delete it in the license file. - */ - -// this should never be included directly - -#include <stdexcept> - -namespace mongo { - -inline int StringData::compare(StringData other) const { - // It is illegal to pass nullptr to memcmp. It is an invariant of - // StringData that if _data is nullptr, _size is zero. If asked to - // compare zero bytes, memcmp returns zero (how could they - // differ?). So, if either StringData object has a nullptr _data - // object, then memcmp would return zero. Achieve this by assuming - // zero, and only calling memcmp if both pointers are valid. - int res = 0; - if (_data && other._data) - res = memcmp(_data, other._data, std::min(_size, other._size)); - - if (res != 0) - return res > 0 ? 1 : -1; - - if (_size == other._size) - return 0; - - 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 { - if (_data) - 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 size_t StringData::find(StringData needle) const { - size_t mx = size(); - size_t needleSize = needle.size(); - - if (needleSize == 0) - return 0; - else if (needleSize > mx) - return std::string::npos; - - mx -= needleSize; - - for (size_t i = 0; i <= mx; i++) { - if (memcmp(_data + i, needle._data, needleSize) == 0) - return i; - } - return std::string::npos; -} - -inline size_t StringData::rfind(char c, size_t fromPos) const { - const size_t sz = size(); - if (fromPos > sz) - fromPos = sz; - - 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.h b/src/mongo/base/string_data.h index 86c809fc7a7..b1491b919e6 100644 --- a/src/mongo/base/string_data.h +++ b/src/mongo/base/string_data.h @@ -33,6 +33,7 @@ #include <cstring> #include <iosfwd> #include <limits> +#include <stdexcept> #include <string> #define MONGO_INCLUDE_INVARIANT_H_WHITELISTED @@ -56,14 +57,14 @@ namespace mongo { */ class StringData { struct TrustedInitTag {}; - StringData(const char* c, size_t len, TrustedInitTag) : _data(c), _size(len) {} + constexpr StringData(const char* c, size_t len, TrustedInitTag) : _data(c), _size(len) {} public: // Declared in string_data_comparator_interface.h. class ComparatorInterface; /** Constructs an empty StringData. */ - StringData() = default; + constexpr StringData() = default; /** * Constructs a StringData, for the case where the length of the @@ -73,16 +74,6 @@ public: StringData(const char* str) : StringData(str, str ? std::strlen(str) : 0) {} /** - * Constructs a StringData explicitly, for the case of a literal - * whose size is known at compile time. Note that you probably - * don't need this on a modern compiler that can see that the call - * to std::strlen on StringData("foo") can be constexpr'ed out. - */ - struct LiteralTag {}; - template <size_t N> - StringData(const char (&val)[N], LiteralTag) : StringData(&val[0], N - 1) {} - - /** * Constructs a StringData, for the case of a std::string. We can * use the trusted init path with no follow on checks because * string::data is assured to never return nullptr. @@ -101,6 +92,12 @@ public: } /** + * Constructs a StringData from a user defined literal. This allows + * for constexpr creation of StringData's that are known at compile time. + */ + constexpr friend StringData operator"" _sd(const char* c, std::size_t len); + + /** * Returns -1, 0, or 1 if 'this' is less, equal, or greater than 'other' in * lexicographical order. */ @@ -214,6 +211,117 @@ inline bool operator>=(StringData lhs, StringData rhs) { std::ostream& operator<<(std::ostream& stream, StringData value); -} // namespace mongo +constexpr StringData operator"" _sd(const char* c, std::size_t len) { + return StringData(c, len, StringData::TrustedInitTag{}); +} + +inline int StringData::compare(StringData other) const { + // It is illegal to pass nullptr to memcmp. It is an invariant of + // StringData that if _data is nullptr, _size is zero. If asked to + // compare zero bytes, memcmp returns zero (how could they + // differ?). So, if either StringData object has a nullptr _data + // object, then memcmp would return zero. Achieve this by assuming + // zero, and only calling memcmp if both pointers are valid. + int res = 0; + if (_data && other._data) + res = memcmp(_data, other._data, std::min(_size, other._size)); + + if (res != 0) + return res > 0 ? 1 : -1; + + if (_size == other._size) + return 0; -#include "mongo/base/string_data-inl.h" + 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 { + if (_data) + 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 size_t StringData::find(StringData needle) const { + size_t mx = size(); + size_t needleSize = needle.size(); + + if (needleSize == 0) + return 0; + else if (needleSize > mx) + return std::string::npos; + + mx -= needleSize; + + for (size_t i = 0; i <= mx; i++) { + if (memcmp(_data + i, needle._data, needleSize) == 0) + return i; + } + return std::string::npos; +} + +inline size_t StringData::rfind(char c, size_t fromPos) const { + const size_t sz = size(); + if (fromPos > sz) + fromPos = sz; + + 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_test.cpp b/src/mongo/base/string_data_test.cpp index 5dc19df3dd8..2c5e6c4019f 100644 --- a/src/mongo/base/string_data_test.cpp +++ b/src/mongo/base/string_data_test.cpp @@ -33,9 +33,8 @@ #include "mongo/base/string_data.h" #include "mongo/unittest/unittest.h" -namespace { +namespace mongo { -using mongo::StringData; using std::string; TEST(Construction, Empty) { @@ -65,10 +64,22 @@ TEST(Construction, FromNullCString) { 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(Construction, FromUserDefinedLiteral) { + const auto strData = "cc\0c"_sd; + ASSERT_EQUALS(strData.size(), 4U); + ASSERT_EQUALS(strData.toString(), string("cc\0c", 4)); +} + +TEST(Construction, FromUserDefinedRawLiteral) { + const auto strData = R"("")"_sd; + ASSERT_EQUALS(strData.size(), 2U); + ASSERT_EQUALS(strData.toString(), string("\"\"", 2)); +} + +TEST(Construction, FromEmptyUserDefinedLiteral) { + const auto strData = ""_sd; + ASSERT_EQUALS(strData.size(), 0U); + ASSERT_EQUALS(strData.toString(), string("")); } TEST(Comparison, BothEmpty) { @@ -266,8 +277,7 @@ TEST(EndsWith, Simple) { TEST(ConstIterator, StdCopy) { std::vector<char> chars; - const char rawData[] = "This is some raw data."; - StringData data(rawData, StringData::LiteralTag()); + auto data = "This is some raw data."_sd; chars.resize(data.size()); std::copy(data.begin(), data.end(), chars.begin()); @@ -279,8 +289,7 @@ TEST(ConstIterator, StdCopy) { TEST(ConstIterator, StdReverseCopy) { std::vector<char> chars; - const char rawData[] = "This is some raw data."; - StringData data(rawData, StringData::LiteralTag()); + auto data = "This is some raw data."_sd; chars.resize(data.size()); std::reverse_copy(data.begin(), data.end(), chars.begin()); @@ -294,8 +303,7 @@ TEST(ConstIterator, StdReverseCopy) { TEST(ConstIterator, StdReplaceCopy) { std::vector<char> chars; - const char rawData[] = "This is some raw data."; - StringData data(rawData, StringData::LiteralTag()); + auto data = "This is some raw data."_sd; chars.resize(data.size()); std::replace_copy(data.begin(), data.end(), chars.begin(), ' ', '_'); @@ -307,4 +315,4 @@ TEST(ConstIterator, StdReplaceCopy) { } } -} // unnamed namespace +} // namespace mongo |