summaryrefslogtreecommitdiff
path: root/src/mongo/platform/decimal128.cpp
diff options
context:
space:
mode:
authorVincent Do <vincent.do@mongodb.com>2016-06-06 13:56:30 -0400
committerVincent Do <vincent.do@mongodb.com>2016-06-08 10:36:12 -0400
commiteb9810a174da8dd8c0799f82b7d5fe93676f8b53 (patch)
tree7ad3684815087775f7bc7a228f4ea0a6a7c2431a /src/mongo/platform/decimal128.cpp
parentf2d4b6f79ce6d14c1a7c2545df6bc054bf8941b2 (diff)
downloadmongo-eb9810a174da8dd8c0799f82b7d5fe93676f8b53.tar.gz
SERVER-24276 Add decimal test suites from driver specs
Diffstat (limited to 'src/mongo/platform/decimal128.cpp')
-rw-r--r--src/mongo/platform/decimal128.cpp107
1 files changed, 103 insertions, 4 deletions
diff --git a/src/mongo/platform/decimal128.cpp b/src/mongo/platform/decimal128.cpp
index b4fb4f44906..8d6dad6f7e4 100644
--- a/src/mongo/platform/decimal128.cpp
+++ b/src/mongo/platform/decimal128.cpp
@@ -25,10 +25,12 @@
* then also delete it in the license file.
*/
+
#include "mongo/platform/decimal128.h"
#include "mongo/platform/basic.h"
#include <algorithm>
+#include <cctype>
#include <cmath>
#include <cstdlib>
#include <iostream>
@@ -44,6 +46,101 @@
#include "mongo/config.h"
#include "mongo/util/assert_util.h"
+#include "mongo/util/stringutils.h"
+
+namespace {
+void validateInputString(mongo::StringData input, std::uint32_t* signalingFlags) {
+ // Input must be of these forms:
+ // * Valid decimal (standard or scientific notation):
+ // /[-+]?\d*(.\d+)?([e][+\-]?\d+)?/
+ // * NaN: /[-+]?[[Nn][Aa][Nn]]/
+ // * Infinity: /[+\-]?(inf|infinity)
+
+ bool isSigned = input[0] == '-' || input[0] == '+';
+
+ // Check for NaN and Infinity
+ size_t start = (isSigned) ? 1 : 0;
+ mongo::StringData noSign = input.substr(start);
+ bool isNanOrInf = noSign == "nan" || noSign == "inf" || noSign == "infinity";
+ if (isNanOrInf)
+ return;
+
+ // Input starting with non digit
+ if (!std::isdigit(noSign[0])) {
+ if (noSign[0] != '.') {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ } else if (noSign.size() == 1) {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ }
+ }
+ bool isZero = true;
+ bool hasCoefficient = false;
+
+ // Check coefficient, i.e. the part before the e
+ int dotCount = 0;
+ size_t i = 0;
+ for (/*i = 0*/; i < noSign.size(); i++) {
+ char c = noSign[i];
+ if (c == '.') {
+ dotCount++;
+ if (dotCount > 1) {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ }
+ } else if (!std::isdigit(c)) {
+ break;
+ } else {
+ hasCoefficient = true;
+ if (c != '0') {
+ isZero = false;
+ }
+ }
+ }
+
+ if (isZero) {
+ // Override inexact/overflow flag set by the intel library
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kNoFlag;
+ }
+
+ // Input is valid if we've parsed the entire string
+ if (i == noSign.size()) {
+ return;
+ }
+
+ // String with empty coefficient and non-empty exponent
+ if (!hasCoefficient) {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ }
+
+ // Check exponent
+ mongo::StringData exponent = noSign.substr(i);
+
+ if (exponent[0] != 'e' || exponent.size() < 2) {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ }
+ if (exponent[1] == '-' || exponent[1] == '+') {
+ exponent = exponent.substr(2);
+ if (exponent.size() == 0) {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ }
+ } else {
+ exponent = exponent.substr(1);
+ }
+
+ for (size_t j = 0; j < exponent.size(); j++) {
+ char c = exponent[j];
+ if (!std::isdigit(c)) {
+ *signalingFlags = mongo::Decimal128::SignalingFlag::kInvalid;
+ return;
+ }
+ }
+}
+} // namespace
namespace mongo {
@@ -254,14 +351,16 @@ Decimal128::Decimal128(std::string stringValue, RoundingMode roundMode) {
*this = Decimal128(stringValue, &throwAwayFlag, roundMode);
}
+
Decimal128::Decimal128(std::string stringValue,
std::uint32_t* signalingFlags,
RoundingMode roundMode) {
- std::unique_ptr<char[]> charInput(new char[stringValue.size() + 1]);
- std::copy(stringValue.begin(), stringValue.end(), charInput.get());
- charInput[stringValue.size()] = '\0';
+ std::string lower = toAsciiLowerCase(stringValue);
BID_UINT128 dec128;
- dec128 = bid128_from_string(charInput.get(), roundMode, signalingFlags);
+ // The intel library function requires a char * while c_str() returns a const char*.
+ // We're using const_cast here since the library function should not modify the input.
+ dec128 = bid128_from_string(const_cast<char*>(lower.c_str()), roundMode, signalingFlags);
+ validateInputString(StringData(lower), signalingFlags);
_value = libraryTypeToValue(dec128);
}