summaryrefslogtreecommitdiff
path: root/src/mongo/bson/json.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/bson/json.cpp')
-rw-r--r--src/mongo/bson/json.cpp199
1 files changed, 176 insertions, 23 deletions
diff --git a/src/mongo/bson/json.cpp b/src/mongo/bson/json.cpp
index b71cd7d3d4e..b6fbdf94541 100644
--- a/src/mongo/bson/json.cpp
+++ b/src/mongo/bson/json.cpp
@@ -67,7 +67,7 @@ using namespace fmt::literals;
// Size hints given to char vectors
enum {
- ID_RESERVE_SIZE = 64,
+ ID_RESERVE_SIZE = 24,
PAT_RESERVE_SIZE = 4096,
OPT_RESERVE_SIZE = 64,
FIELD_RESERVE_SIZE = 4096,
@@ -76,7 +76,9 @@ enum {
BINDATATYPE_RESERVE_SIZE = 4096,
NS_RESERVE_SIZE = 64,
DB_RESERVE_SIZE = 64,
- NUMBERLONG_RESERVE_SIZE = 64,
+ NUMBERINT_RESERVE_SIZE = 16,
+ NUMBERLONG_RESERVE_SIZE = 20,
+ NUMBERDOUBLE_RESERVE_SIZE = 64,
NUMBERDECIMAL_RESERVE_SIZE = 64,
DATE_RESERVE_SIZE = 64
};
@@ -253,6 +255,14 @@ Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObj
if (ret != Status::OK()) {
return ret;
}
+ } else if (firstField == "$regularExpression") {
+ if (!subObject) {
+ return parseError("Reserved field name in base object: $regularExpression");
+ }
+ Status ret = regexObjectCanonical(fieldName, builder);
+ if (ret != Status::OK()) {
+ return ret;
+ }
} else if (firstField == "$ref") {
if (!subObject) {
return parseError("Reserved field name in base object: $ref");
@@ -269,6 +279,14 @@ Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObj
if (ret != Status::OK()) {
return ret;
}
+ } else if (firstField == "$numberInt") {
+ if (!subObject) {
+ return parseError("Reserved field name in base object: $numberInt");
+ }
+ Status ret = numberIntObject(fieldName, builder);
+ if (ret != Status::OK()) {
+ return ret;
+ }
} else if (firstField == "$numberLong") {
if (!subObject) {
return parseError("Reserved field name in base object: $numberLong");
@@ -277,6 +295,15 @@ Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObj
if (ret != Status::OK()) {
return ret;
}
+
+ } else if (firstField == "$numberDouble") {
+ if (!subObject) {
+ return parseError("Reserved field name in base object: $numberDouble");
+ }
+ Status ret = numberDoubleObject(fieldName, builder);
+ if (ret != Status::OK()) {
+ return ret;
+ }
} else if (firstField == "$numberDecimal") {
if (!subObject) {
return parseError("Reserved field name in base object: $numberDecimal");
@@ -367,37 +394,73 @@ Status JParse::binaryObject(StringData fieldName, BSONObjBuilder& builder) {
}
std::string binDataString;
binDataString.reserve(BINDATA_RESERVE_SIZE);
- Status dataRet = quotedString(&binDataString);
- if (dataRet != Status::OK()) {
- return dataRet;
+
+ std::string binDataType;
+ binDataType.reserve(BINDATATYPE_RESERVE_SIZE);
+
+ if (peekToken(LBRACE)) {
+ readToken(LBRACE);
+
+ if (!readField("base64")) {
+ return parseError("Expected field name: \"base64\", in \"$binary\" object");
+ }
+ if (!readToken(COLON)) {
+ return parseError("Expecting ':'");
+ }
+
+ Status dataRet = quotedString(&binDataString);
+ if (dataRet != Status::OK()) {
+ return dataRet;
+ }
+ if (!readToken(COMMA)) {
+ return parseError("Expected ','");
+ }
+ if (!readField("subType")) {
+ return parseError("Expected field name: \"subType\", in \"$binary\" object");
+ }
+ if (!readToken(COLON)) {
+ return parseError("Expected ':'");
+ }
+ Status typeRet = quotedString(&binDataType);
+ if (typeRet != Status::OK()) {
+ return typeRet;
+ }
+ if (binDataType.size() == 1)
+ binDataType = "0" + binDataType;
+ readToken(RBRACE);
+ } else {
+ Status dataRet = quotedString(&binDataString);
+ if (dataRet != Status::OK()) {
+ return dataRet;
+ }
+ if (!readToken(COMMA)) {
+ return parseError("Expected ','");
+ }
+ if (!readField("$type")) {
+ return parseError("Expected second field name: \"$type\", in \"$binary\" object");
+ }
+ if (!readToken(COLON)) {
+ return parseError("Expected ':'");
+ }
+
+ Status typeRet = quotedString(&binDataType);
+ if (typeRet != Status::OK()) {
+ return typeRet;
+ }
}
+
if (binDataString.size() % 4 != 0) {
return parseError("Invalid length base64 encoded string");
}
if (!isBase64String(binDataString)) {
return parseError("Invalid character in base64 encoded string");
}
- const std::string& binData = base64::decode(binDataString);
- if (!readToken(COMMA)) {
- return parseError("Expected ','");
- }
+ std::string binData = base64::decode(binDataString);
- if (!readField("$type")) {
- return parseError("Expected second field name: \"$type\", in \"$binary\" object");
- }
- if (!readToken(COLON)) {
- return parseError("Expected ':'");
- }
- std::string binDataType;
- binDataType.reserve(BINDATATYPE_RESERVE_SIZE);
- Status typeRet = quotedString(&binDataType);
- if (typeRet != Status::OK()) {
- return typeRet;
- }
if ((binDataType.size() != 2) || !isHexString(binDataType)) {
return parseError(
- "Argument of $type in $bindata object must be a hex string representation of a single "
- "byte");
+ "Argument of $type in $bindata object must be a hex string representation of a "
+ "single byte");
}
// The fromHex function returns a signed char, but the highest
@@ -459,6 +522,8 @@ Status JParse::dateObject(StringData fieldName, BSONObjBuilder& builder) {
if (!ret.isOK()) {
return ret;
}
+
+ readToken(RBRACE);
date = Date_t::fromMillisSinceEpoch(numberLong);
} else {
StatusWith<Date_t> parsedDate = parseDate();
@@ -564,6 +629,47 @@ Status JParse::regexObject(StringData fieldName, BSONObjBuilder& builder) {
return Status::OK();
}
+Status JParse::regexObjectCanonical(StringData fieldName, BSONObjBuilder& builder) {
+ if (!readToken(COLON)) {
+ return parseError("Expecting ':'");
+ }
+ readToken(LBRACE);
+ if (!readField("pattern")) {
+ return parseError("Expected field name: \"pattern\", in \"$regularExpression\" object");
+ }
+ if (!readToken(COLON)) {
+ return parseError("Expecting ':'");
+ }
+ std::string pat;
+ pat.reserve(PAT_RESERVE_SIZE);
+ Status patRet = quotedString(&pat);
+ if (patRet != Status::OK()) {
+ return patRet;
+ }
+ if (!readToken(COMMA)) {
+ return parseError("Expected ','");
+ }
+ if (!readField("options")) {
+ return parseError("Expected field name: \"pattern\", in \"$regularExpression\" object");
+ }
+ if (!readToken(COLON)) {
+ return parseError("Expecting ':'");
+ }
+ std::string opt;
+ opt.reserve(OPT_RESERVE_SIZE);
+ Status optRet = quotedString(&opt);
+ if (optRet != Status::OK()) {
+ return optRet;
+ }
+ Status optCheckRet = regexOptCheck(opt);
+ if (optCheckRet != Status::OK()) {
+ return optCheckRet;
+ }
+ readToken(RBRACE);
+ builder.appendRegex(fieldName, pat, opt);
+ return Status::OK();
+}
+
Status JParse::dbRefObject(StringData fieldName, BSONObjBuilder& builder) {
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
@@ -648,6 +754,53 @@ Status JParse::numberLongObject(StringData fieldName, BSONObjBuilder& builder) {
return Status::OK();
}
+Status JParse::numberIntObject(StringData fieldName, BSONObjBuilder& builder) {
+ if (!readToken(COLON)) {
+ return parseError("Expecting ':'");
+ }
+
+ // The number must be a quoted string, since large long numbers could overflow a double and
+ // thus may not be valid JSON
+ std::string numberIntString;
+ numberIntString.reserve(NUMBERINT_RESERVE_SIZE);
+ Status ret = quotedString(&numberIntString);
+ if (!ret.isOK()) {
+ return ret;
+ }
+
+ int numberInt;
+ ret = NumberParser{}(numberIntString, &numberInt);
+ if (!ret.isOK()) {
+ return ret;
+ }
+
+ builder.append(fieldName, numberInt);
+ return Status::OK();
+}
+
+Status JParse::numberDoubleObject(StringData fieldName, BSONObjBuilder& builder) {
+ if (!readToken(COLON)) {
+ return parseError("Expecting ':'");
+ }
+ // The number must be a quoted string, since large double numbers could overflow other types
+ // and thus may not be valid JSON
+ std::string numberDoubleString;
+ numberDoubleString.reserve(NUMBERDOUBLE_RESERVE_SIZE);
+ Status ret = quotedString(&numberDoubleString);
+ if (!ret.isOK()) {
+ return ret;
+ }
+
+ double numberDouble;
+ ret = NumberParser{}(numberDoubleString, &numberDouble);
+ if (!ret.isOK()) {
+ return ret;
+ }
+
+ builder.append(fieldName, numberDouble);
+ return Status::OK();
+}
+
Status JParse::numberDecimalObject(StringData fieldName, BSONObjBuilder& builder) {
if (!readToken(COLON)) {
return parseError("Expecting ':'");