diff options
author | Sam McCall <sam.mccall@gmail.com> | 2018-07-09 12:16:40 +0000 |
---|---|---|
committer | Sam McCall <sam.mccall@gmail.com> | 2018-07-09 12:16:40 +0000 |
commit | d93eaeb7c399488b16753b1786a30ec6a59761c7 (patch) | |
tree | 67793aef0e29ef86851aee92b0f0c1cdc018240e /llvm/lib/Support/JSON.cpp | |
parent | 6f33b330ae1808ab2a5d7184abafefc5d5821059 (diff) | |
download | bcm5719-llvm-d93eaeb7c399488b16753b1786a30ec6a59761c7.tar.gz bcm5719-llvm-d93eaeb7c399488b16753b1786a30ec6a59761c7.zip |
[Support] Make JSON handle doubles and int64s losslessly
Summary:
This patch adds a new "integer" ValueType, and renames Number -> Double.
This allows us to preserve the full precision of int64_t when parsing integers
from the wire, or constructing from an integer.
The API is unchanged, other than giving asInteger() a clearer contract.
In addition, always output doubles with enough precision that parsing will
reconstruct the same double.
Reviewers: simon_tatham
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D46209
llvm-svn: 336541
Diffstat (limited to 'llvm/lib/Support/JSON.cpp')
-rw-r--r-- | llvm/lib/Support/JSON.cpp | 42 |
1 files changed, 26 insertions, 16 deletions
diff --git a/llvm/lib/Support/JSON.cpp b/llvm/lib/Support/JSON.cpp index 0371d1ab251..c2025bb2299 100644 --- a/llvm/lib/Support/JSON.cpp +++ b/llvm/lib/Support/JSON.cpp @@ -104,7 +104,8 @@ void Value::copyFrom(const Value &M) { switch (Type) { case T_Null: case T_Boolean: - case T_Number: + case T_Double: + case T_Integer: memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer)); break; case T_StringRef: @@ -127,7 +128,8 @@ void Value::moveFrom(const Value &&M) { switch (Type) { case T_Null: case T_Boolean: - case T_Number: + case T_Double: + case T_Integer: memcpy(Union.buffer, M.Union.buffer, sizeof(Union.buffer)); break; case T_StringRef: @@ -152,7 +154,8 @@ void Value::destroy() { switch (Type) { case T_Null: case T_Boolean: - case T_Number: + case T_Double: + case T_Integer: break; case T_StringRef: as<StringRef>().~StringRef(); @@ -217,7 +220,7 @@ private: } // On invalid syntax, parseX() functions return false and set Err. - bool parseNumber(char First, double &Out); + bool parseNumber(char First, Value &Out); bool parseString(std::string &Out); bool parseUnicode(std::string &Out); bool parseError(const char *Msg); // always returns false @@ -317,25 +320,28 @@ bool Parser::parseValue(Value &Out) { } } default: - if (isNumber(C)) { - double Num; - if (parseNumber(C, Num)) { - Out = Num; - return true; - } else { - return false; - } - } + if (isNumber(C)) + return parseNumber(C, Out); return parseError("Invalid JSON value"); } } -bool Parser::parseNumber(char First, double &Out) { +bool Parser::parseNumber(char First, Value &Out) { + // Read the number into a string. (Must be null-terminated for strto*). SmallString<24> S; S.push_back(First); while (isNumber(peek())) S.push_back(next()); char *End; + // Try first to parse as integer, and if so preserve full 64 bits. + // strtoll returns long long >= 64 bits, so check it's in range too. + auto I = std::strtoll(S.c_str(), &End, 10); + if (End == S.end() && I >= std::numeric_limits<int64_t>::min() && + I <= std::numeric_limits<int64_t>::max()) { + Out = int64_t(I); + return true; + } + // If it's not an integer Out = std::strtod(S.c_str(), &End); return End == S.end() || parseError("Invalid JSON value (number?)"); } @@ -558,8 +564,12 @@ void llvm::json::Value::print(raw_ostream &OS, const Indenter &I) const { case T_Boolean: OS << (as<bool>() ? "true" : "false"); break; - case T_Number: - OS << format("%g", as<double>()); + case T_Double: + OS << format("%.*g", std::numeric_limits<double>::max_digits10, + as<double>()); + break; + case T_Integer: + OS << as<int64_t>(); break; case T_StringRef: quote(OS, as<StringRef>()); |