summaryrefslogtreecommitdiff
path: root/src/mongo/bson/json.cpp
diff options
context:
space:
mode:
authorNathan Brown <nathan.brown@10gen.com>2019-06-06 10:39:58 -0400
committerNathan Brown <nathan.brown@10gen.com>2019-06-27 10:04:18 -0400
commit0fefcf84347a2bcc4961baec0295b9d04047d86f (patch)
treece86921e5b14ee83a00400f4f4e8712093505cb8 /src/mongo/bson/json.cpp
parenta700238800aa2bf1e10c255337ec35373ffd2667 (diff)
downloadmongo-0fefcf84347a2bcc4961baec0295b9d04047d86f.tar.gz
SERVER-7143 replace standard library number parsing with custom NumberParser
Diffstat (limited to 'src/mongo/bson/json.cpp')
-rw-r--r--src/mongo/bson/json.cpp175
1 files changed, 77 insertions, 98 deletions
diff --git a/src/mongo/bson/json.cpp b/src/mongo/bson/json.cpp
index 15661b0a456..91a6fcf2cd8 100644
--- a/src/mongo/bson/json.cpp
+++ b/src/mongo/bson/json.cpp
@@ -32,6 +32,7 @@
#include "mongo/bson/json.h"
#include <cstdint>
+#include <fmt/format.h>
#include "mongo/base/parse_number.h"
#include "mongo/db/jsobj.h"
@@ -48,6 +49,7 @@ namespace mongo {
using std::unique_ptr;
using std::ostringstream;
using std::string;
+using namespace fmt::literals;
#if 0
#define MONGO_JSON_DEBUG(message) \
@@ -414,8 +416,6 @@ Status JParse::dateObject(StringData fieldName, BSONObjBuilder& builder) {
if (!readToken(COLON)) {
return parseError("Expected ':'");
}
- errno = 0;
- char* endptr;
Date_t date;
if (peekToken(DOUBLEQUOTE)) {
@@ -454,32 +454,17 @@ Status JParse::dateObject(StringData fieldName, BSONObjBuilder& builder) {
}
long long numberLong;
- ret = parseNumberFromString(numberLongString, &numberLong);
+ ret = NumberParser{}(numberLongString, &numberLong);
if (!ret.isOK()) {
return ret;
}
date = Date_t::fromMillisSinceEpoch(numberLong);
} else {
- // SERVER-11920: We should use parseNumberFromString here, but that function requires
- // that we know ahead of time where the number ends, which is not currently the case.
- date = Date_t::fromMillisSinceEpoch(strtoll(_input, &endptr, 10));
- if (_input == endptr) {
- return parseError("Date expecting integer milliseconds");
- }
- if (errno == ERANGE) {
- /* Need to handle this because jsonString outputs the value of Date_t as unsigned.
- * See SERVER-8330 and SERVER-8573 */
- errno = 0;
- // SERVER-11920: We should use parseNumberFromString here, but that function
- // requires that we know ahead of time where the number ends, which is not currently
- // the case.
- date =
- Date_t::fromMillisSinceEpoch(static_cast<long long>(strtoull(_input, &endptr, 10)));
- if (errno == ERANGE) {
- return parseError("Date milliseconds overflow");
- }
+ StatusWith<Date_t> parsedDate = parseDate();
+ if (!parsedDate.isOK()) {
+ return parsedDate.getStatus();
}
- _input = endptr;
+ date = std::move(parsedDate).getValue();
}
builder.appendDate(fieldName, date);
return Status::OK();
@@ -503,15 +488,14 @@ Status JParse::timestampObject(StringData fieldName, BSONObjBuilder& builder) {
if (readToken("-")) {
return parseError("Negative seconds in \"$timestamp\"");
}
- errno = 0;
char* endptr;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- uint32_t seconds = strtoul(_input, &endptr, 10);
- if (errno == ERANGE) {
+ uint32_t seconds;
+ NumberParser parser = NumberParser::strToAny(10);
+ Status parsedStatus = parser(_input, &seconds, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("Timestamp seconds overflow");
}
- if (_input == endptr) {
+ if (!parsedStatus.isOK()) {
return parseError("Expecting unsigned integer seconds in \"$timestamp\"");
}
_input = endptr;
@@ -528,14 +512,12 @@ Status JParse::timestampObject(StringData fieldName, BSONObjBuilder& builder) {
if (readToken("-")) {
return parseError("Negative increment in \"$timestamp\"");
}
- errno = 0;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- uint32_t count = strtoul(_input, &endptr, 10);
- if (errno == ERANGE) {
+ uint32_t count;
+ parsedStatus = parser(_input, &count, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("Timestamp increment overflow");
}
- if (_input == endptr) {
+ if (!parsedStatus.isOK()) {
return parseError("Expecting unsigned integer increment in \"$timestamp\"");
}
_input = endptr;
@@ -656,7 +638,7 @@ Status JParse::numberLongObject(StringData fieldName, BSONObjBuilder& builder) {
}
long long numberLong;
- ret = parseNumberFromString(numberLongString, &numberLong);
+ ret = NumberParser{}(numberLongString, &numberLong);
if (!ret.isOK()) {
return ret;
}
@@ -753,26 +735,11 @@ Status JParse::date(StringData fieldName, BSONObjBuilder& builder) {
if (!readToken(LPAREN)) {
return parseError("Expecting '('");
}
- errno = 0;
- char* endptr;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- Date_t date = Date_t::fromMillisSinceEpoch(strtoll(_input, &endptr, 10));
- if (_input == endptr) {
- return parseError("Date expecting integer milliseconds");
+ StatusWith<Date_t> parsedDate = parseDate();
+ if (!parsedDate.isOK()) {
+ return parsedDate.getStatus();
}
- if (errno == ERANGE) {
- /* Need to handle this because jsonString outputs the value of Date_t as unsigned.
- * See SERVER-8330 and SERVER-8573 */
- errno = 0;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires
- // that we know ahead of time where the number ends, which is not currently the case.
- date = Date_t::fromMillisSinceEpoch(static_cast<long long>(strtoull(_input, &endptr, 10)));
- if (errno == ERANGE) {
- return parseError("Date milliseconds overflow");
- }
- }
- _input = endptr;
+ Date_t date = parsedDate.getValue();
if (!readToken(RPAREN)) {
return parseError("Expecting ')'");
}
@@ -787,15 +754,14 @@ Status JParse::timestamp(StringData fieldName, BSONObjBuilder& builder) {
if (readToken("-")) {
return parseError("Negative seconds in \"$timestamp\"");
}
- errno = 0;
char* endptr;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- uint32_t seconds = strtoul(_input, &endptr, 10);
- if (errno == ERANGE) {
+ NumberParser parser = NumberParser::strToAny(10);
+ uint32_t seconds;
+ Status parsedStatus = parser(_input, &seconds, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("Timestamp seconds overflow");
}
- if (_input == endptr) {
+ if (!parsedStatus.isOK()) {
return parseError("Expecting unsigned integer seconds in \"$timestamp\"");
}
_input = endptr;
@@ -805,14 +771,12 @@ Status JParse::timestamp(StringData fieldName, BSONObjBuilder& builder) {
if (readToken("-")) {
return parseError("Negative seconds in \"$timestamp\"");
}
- errno = 0;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- uint32_t count = strtoul(_input, &endptr, 10);
- if (errno == ERANGE) {
+ uint32_t count;
+ parsedStatus = parser(_input, &count, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("Timestamp increment overflow");
}
- if (_input == endptr) {
+ if (!parsedStatus.isOK()) {
return parseError("Expecting unsigned integer increment in \"$timestamp\"");
}
_input = endptr;
@@ -850,15 +814,13 @@ Status JParse::numberLong(StringData fieldName, BSONObjBuilder& builder) {
if (!readToken(LPAREN)) {
return parseError("Expecting '('");
}
- errno = 0;
char* endptr;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- int64_t val = strtoll(_input, &endptr, 10);
- if (errno == ERANGE) {
+ int64_t val;
+ Status parsedStatus = NumberParser::strToAny(10)(_input, &val, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("NumberLong out of range");
}
- if (_input == endptr) {
+ if (!parsedStatus.isOK()) {
return parseError("Expecting number in NumberLong");
}
_input = endptr;
@@ -880,7 +842,15 @@ Status JParse::numberDecimal(StringData fieldName, BSONObjBuilder& builder) {
if (ret != Status::OK()) {
return ret;
}
- Decimal128 val(decString);
+ Decimal128 val;
+ Status parsedStatus = NumberParser().setDecimal128RoundingMode(
+ Decimal128::RoundingMode::kRoundTiesToEven)(decString, &val);
+ if (parsedStatus == ErrorCodes::Overflow) {
+ return parseError("numberDecimal out of range");
+ }
+ if (!parsedStatus.isOK()) {
+ return parseError("Expecting decimal in numberDecimal");
+ }
if (!readToken(RPAREN)) {
return parseError("Expecting ')'");
@@ -893,15 +863,13 @@ Status JParse::numberInt(StringData fieldName, BSONObjBuilder& builder) {
if (!readToken(LPAREN)) {
return parseError("Expecting '('");
}
- errno = 0;
char* endptr;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- int32_t val = strtol(_input, &endptr, 10);
- if (errno == ERANGE) {
+ int32_t val;
+ Status parsedStatus = NumberParser::strToAny(10)(_input, &val, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("NumberInt out of range");
}
- if (_input == endptr) {
+ if (!parsedStatus.isOK()) {
return parseError("Expecting unsigned number in NumberInt");
}
_input = endptr;
@@ -1010,24 +978,15 @@ Status JParse::number(StringData fieldName, BSONObjBuilder& builder) {
long long retll;
double retd;
- // reset errno to make sure that we are getting it from strtod
- errno = 0;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- retd = strtod(_input, &endptrd);
- // if pointer does not move, we found no digits
- if (_input == endptrd) {
- return parseError("Bad characters in value");
- }
- if (errno == ERANGE) {
+ Status parsedStatus = NumberParser::strToAny()(_input, &retd, &endptrd);
+ if (parsedStatus == ErrorCodes::Overflow) {
return parseError("Value cannot fit in double");
}
- // reset errno to make sure that we are getting it from strtoll
- errno = 0;
- // SERVER-11920: We should use parseNumberFromString here, but that function requires that
- // we know ahead of time where the number ends, which is not currently the case.
- retll = strtoll(_input, &endptrll, 10);
- if (endptrll < endptrd || errno == ERANGE) {
+ if (!parsedStatus.isOK()) {
+ return parseError("Bad characters in value");
+ }
+ parsedStatus = NumberParser::strToAny(10)(_input, &retll, &endptrll);
+ if (endptrll < endptrd || parsedStatus == ErrorCodes::Overflow) {
// The number either had characters only meaningful for a double or
// could not fit in a 64 bit int
MONGO_JSON_DEBUG("Type: double");
@@ -1293,6 +1252,28 @@ bool JParse::isArray() {
return peekToken(LBRACKET);
}
+StatusWith<Date_t> JParse::parseDate() {
+ long long msSinceEpoch;
+ char* endptr;
+ Status parsedStatus = NumberParser::strToAny(10)(_input, &msSinceEpoch, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
+ /* Need to handle this because jsonString outputs the value of Date_t as unsigned.
+ * See SERVER-8330 and SERVER-8573 */
+ unsigned long long oldDate; // Date_t used to be stored as unsigned long longs
+ parsedStatus = NumberParser::strToAny(10)(_input, &oldDate, &endptr);
+ if (parsedStatus == ErrorCodes::Overflow) {
+ return parseError("Date milliseconds overflow");
+ }
+ msSinceEpoch = static_cast<long long>(oldDate);
+ } else if (!parsedStatus.isOK()) {
+ return parseError("Date expecting integer milliseconds");
+ }
+ invariant(endptr != _input);
+ Date_t date = Date_t::fromMillisSinceEpoch(msSinceEpoch);
+ _input = endptr;
+ return date;
+}
+
BSONObj fromjson(const char* jsonString, int* len) {
MONGO_JSON_DEBUG("jsonString: " << jsonString);
if (jsonString[0] == '\0') {
@@ -1312,9 +1293,7 @@ BSONObj fromjson(const char* jsonString, int* len) {
}
if (ret != Status::OK()) {
- ostringstream message;
- message << "code " << ret.code() << ": " << ret.codeString() << ": " << ret.reason();
- uasserted(16619, message.str());
+ uasserted(16619, "code {}: {}: {}"_format(ret.code(), ret.codeString(), ret.reason()));
}
if (len)
*len = jparse.offset();