SERVER-110708 refactor parse_number to avoid linter false positives (#42593)

GitOrigin-RevId: 647bf5bf02bc4e90012242c77654e67081bdd20d
This commit is contained in:
Billy Donahue 2025-10-14 12:23:09 -04:00 committed by MongoDB Bot
parent c14bf70b75
commit 001e728d05
1 changed files with 49 additions and 44 deletions

View File

@ -131,11 +131,10 @@ inline StringData _extractBase(StringData stringValue, int inputBase, int* outpu
} }
} }
inline StatusWith<uint64_t> parseMagnitudeFromStringWithBase(uint64_t base, StatusWith<uint64_t> _parseMagnitude(StringData magnitudeStr,
StringData wholeString, uint64_t base,
StringData magnitudeStr, const char** end,
const char** end, bool allowTrailingText) {
bool allowTrailingText) {
uint64_t n = 0; uint64_t n = 0;
size_t charsConsumed = 0; size_t charsConsumed = 0;
for (char digitChar : magnitudeStr) { for (char digitChar : magnitudeStr) {
@ -166,13 +165,13 @@ StringData removeLeadingWhitespace(StringData s) {
s.begin(), std::find_if_not(s.begin(), s.end(), [](char c) { return ctype::isSpace(c); }))); s.begin(), std::find_if_not(s.begin(), s.end(), [](char c) { return ctype::isSpace(c); })));
} }
template <typename NumberType> template <std::integral NumberType>
Status parseNumberFromStringHelper(StringData s, Status _parseNumber(StringData s,
NumberType* result, NumberType* result,
const char** endptr, const char** endptr,
const NumberParser& parser) { const NumberParser& parser) {
MONGO_STATIC_ASSERT(sizeof(NumberType) <= sizeof(uint64_t)); MONGO_STATIC_ASSERT(sizeof(NumberType) <= sizeof(uint64_t));
typedef ::std::numeric_limits<NumberType> limits; using Limits = std::numeric_limits<NumberType>;
if (endptr) if (endptr)
*endptr = s.data(); *endptr = s.data();
@ -180,44 +179,52 @@ Status parseNumberFromStringHelper(StringData s,
if (parser._base == 1 || parser._base < 0 || parser._base > 36) if (parser._base == 1 || parser._base < 0 || parser._base > 36)
return Status(ErrorCodes::BadValue, "Invalid parser._base"); return Status(ErrorCodes::BadValue, "Invalid parser._base");
if (parser._skipLeadingWhitespace) { if (parser._skipLeadingWhitespace)
s = removeLeadingWhitespace(s); s = removeLeadingWhitespace(s);
}
// Separate the magnitude from modifiers such as sign and parser._base prefixes such as "0x"
bool isNegative = false; bool isNegative = false;
int base = 0; s = _extractSign(s, &isNegative);
StringData magnitudeStr = _extractBase(_extractSign(s, &isNegative), parser._base, &base); if constexpr (!Limits::is_signed)
if (isNegative && !limits::is_signed) if (isNegative)
return Status(ErrorCodes::FailedToParse, "Negative value"); return Status(ErrorCodes::FailedToParse, "Negative value");
if (magnitudeStr.empty())
return Status(ErrorCodes::FailedToParse, "No digits");
auto status = int base = 0;
parseMagnitudeFromStringWithBase(base, s, magnitudeStr, endptr, parser._allowTrailingText); s = _extractBase(s, parser._base, &base);
if (s.empty())
return Status(ErrorCodes::FailedToParse, "No digits");
auto status = _parseMagnitude(s, base, endptr, parser._allowTrailingText);
if (!status.isOK()) if (!status.isOK())
return status.getStatus(); return status.getStatus();
uint64_t magnitude = status.getValue(); uint64_t magnitude = status.getValue();
// The range of 2's complement integers is from -(max + 1) to +max. if constexpr (Limits::is_signed || Limits::digits < 64) {
const uint64_t maxMagnitude = uint64_t(limits::max()) + (isNegative ? 1u : 0u); uint64_t max = Limits::max();
if (magnitude > maxMagnitude) if constexpr (Limits::is_signed)
return Status(ErrorCodes::Overflow, "Overflow"); if (isNegative)
max += 1; // accept range [-(max+1), max]
#pragma warning(push) if (magnitude > max)
// C4146: unary minus operator applied to unsigned type, result still unsigned return Status(ErrorCodes::Overflow, "Overflow");
#pragma warning(disable : 4146) }
*result = NumberType(isNegative ? -magnitude : magnitude);
#pragma warning(pop)
if constexpr (Limits::is_signed)
if (isNegative)
#ifdef _MSC_VER
#pragma "warning(push)"
#pragma "warning(disable : 4146)" // unary minus operator applied to unsigned type
#endif
magnitude = -magnitude;
#ifdef _MSC_VER
#pragma "warning(pop)"
#endif
*result = NumberType(magnitude);
return Status::OK(); return Status::OK();
} }
template <> Status _parseNumber(StringData stringValue,
Status parseNumberFromStringHelper<double>(StringData stringValue, double* result,
double* result, const char** endptr,
const char** endptr, const NumberParser& parser) {
const NumberParser& parser) {
if (endptr) if (endptr)
*endptr = stringValue.data(); *endptr = stringValue.data();
if (parser._base != 0) { if (parser._base != 0) {
@ -273,11 +280,10 @@ Status parseNumberFromStringHelper<double>(StringData stringValue,
return Status::OK(); return Status::OK();
} }
template <> Status _parseNumber(StringData stringValue,
Status parseNumberFromStringHelper<Decimal128>(StringData stringValue, Decimal128* result,
Decimal128* result, const char** endptr,
const char** endptr, const NumberParser& parser) {
const NumberParser& parser) {
if (endptr) if (endptr)
*endptr = stringValue.data(); // same behavior as strtod: if unable to parse, set end to *endptr = stringValue.data(); // same behavior as strtod: if unable to parse, set end to
// be the beginning of input str // be the beginning of input str
@ -320,8 +326,7 @@ Status parseNumberFromStringHelper<Decimal128>(StringData stringValue,
#define DEFINE_NUMBER_PARSER_OPERATOR(type) \ #define DEFINE_NUMBER_PARSER_OPERATOR(type) \
Status NumberParser::operator()(StringData stringValue, type* result, char** endPtr) const { \ Status NumberParser::operator()(StringData stringValue, type* result, char** endPtr) const { \
return parseNumberFromStringHelper( \ return _parseNumber(stringValue, result, const_cast<const char**>(endPtr), *this); \
stringValue, result, const_cast<const char**>(endPtr), *this); \
} }
DEFINE_NUMBER_PARSER_OPERATOR(long) DEFINE_NUMBER_PARSER_OPERATOR(long)