From d93eaeb7c399488b16753b1786a30ec6a59761c7 Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Mon, 9 Jul 2018 12:16:40 +0000 Subject: [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 --- llvm/lib/Support/JSON.cpp | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'llvm/lib/Support') 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(); @@ -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::min() && + I <= std::numeric_limits::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() ? "true" : "false"); break; - case T_Number: - OS << format("%g", as()); + case T_Double: + OS << format("%.*g", std::numeric_limits::max_digits10, + as()); + break; + case T_Integer: + OS << as(); break; case T_StringRef: quote(OS, as()); -- cgit v1.2.3