590 lines · cpp
1//===--------------------- JSON.cpp -----------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "JSON.h"10 11// C includes12#include <cassert>13#include <climits>14 15// C++ includes16#include "StringConvert.h"17#include <iomanip>18#include <sstream>19 20std::string JSONString::json_string_quote_metachars(const std::string &s) {21 if (s.find('"') == std::string::npos)22 return s;23 24 std::string output;25 const size_t s_size = s.size();26 const char *s_chars = s.c_str();27 for (size_t i = 0; i < s_size; i++) {28 unsigned char ch = *(s_chars + i);29 if (ch == '"') {30 output.push_back('\\');31 }32 output.push_back(ch);33 }34 return output;35}36 37JSONString::JSONString() : JSONValue(JSONValue::Kind::String), m_data() {}38 39JSONString::JSONString(const char *s)40 : JSONValue(JSONValue::Kind::String), m_data(s ? s : "") {}41 42JSONString::JSONString(const std::string &s)43 : JSONValue(JSONValue::Kind::String), m_data(s) {}44 45void JSONString::Write(std::ostream &s) {46 s << "\"" << json_string_quote_metachars(m_data) << "\"";47}48 49uint64_t JSONNumber::GetAsUnsigned() const {50 switch (m_data_type) {51 case DataType::Unsigned:52 return m_data.m_unsigned;53 case DataType::Signed:54 return (uint64_t)m_data.m_signed;55 case DataType::Double:56 return (uint64_t)m_data.m_double;57 }58}59 60int64_t JSONNumber::GetAsSigned() const {61 switch (m_data_type) {62 case DataType::Unsigned:63 return (int64_t)m_data.m_unsigned;64 case DataType::Signed:65 return m_data.m_signed;66 case DataType::Double:67 return (int64_t)m_data.m_double;68 }69}70 71double JSONNumber::GetAsDouble() const {72 switch (m_data_type) {73 case DataType::Unsigned:74 return (double)m_data.m_unsigned;75 case DataType::Signed:76 return (double)m_data.m_signed;77 case DataType::Double:78 return m_data.m_double;79 }80}81 82void JSONNumber::Write(std::ostream &s) {83 switch (m_data_type) {84 case DataType::Unsigned:85 s << m_data.m_unsigned;86 break;87 case DataType::Signed:88 s << m_data.m_signed;89 break;90 case DataType::Double:91 // Set max precision to emulate %g.92 s << std::setprecision(std::numeric_limits<double>::digits10 + 1);93 s << m_data.m_double;94 break;95 }96}97 98JSONTrue::JSONTrue() : JSONValue(JSONValue::Kind::True) {}99 100void JSONTrue::Write(std::ostream &s) { s << "true"; }101 102JSONFalse::JSONFalse() : JSONValue(JSONValue::Kind::False) {}103 104void JSONFalse::Write(std::ostream &s) { s << "false"; }105 106JSONNull::JSONNull() : JSONValue(JSONValue::Kind::Null) {}107 108void JSONNull::Write(std::ostream &s) { s << "null"; }109 110JSONObject::JSONObject() : JSONValue(JSONValue::Kind::Object) {}111 112void JSONObject::Write(std::ostream &s) {113 bool first = true;114 s << '{';115 auto iter = m_elements.begin(), end = m_elements.end();116 for (; iter != end; iter++) {117 if (first)118 first = false;119 else120 s << ',';121 JSONString key(iter->first);122 JSONValue::SP value(iter->second);123 key.Write(s);124 s << ':';125 value->Write(s);126 }127 s << '}';128}129 130bool JSONObject::SetObject(const std::string &key, JSONValue::SP value) {131 if (key.empty() || nullptr == value.get())132 return false;133 m_elements[key] = value;134 return true;135}136 137JSONValue::SP JSONObject::GetObject(const std::string &key) const {138 auto iter = m_elements.find(key), end = m_elements.end();139 if (iter == end)140 return JSONValue::SP();141 return iter->second;142}143 144bool JSONObject::GetObjectAsBool(const std::string &key, bool &value) const {145 auto value_sp = GetObject(key);146 if (!value_sp) {147 // The given key doesn't exist, so we have no value.148 return false;149 }150 151 if (JSONTrue::classof(value_sp.get())) {152 // We have the value, and it is true.153 value = true;154 return true;155 } else if (JSONFalse::classof(value_sp.get())) {156 // We have the value, and it is false.157 value = false;158 return true;159 } else {160 // We don't have a valid bool value for the given key.161 return false;162 }163}164 165bool JSONObject::GetObjectAsString(const std::string &key,166 std::string &value) const {167 auto value_sp = GetObject(key);168 if (!value_sp) {169 // The given key doesn't exist, so we have no value.170 return false;171 }172 173 if (!JSONString::classof(value_sp.get()))174 return false;175 176 value = static_cast<JSONString *>(value_sp.get())->GetData();177 return true;178}179 180JSONArray::JSONArray() : JSONValue(JSONValue::Kind::Array) {}181 182void JSONArray::Write(std::ostream &s) {183 bool first = true;184 s << '[';185 auto iter = m_elements.begin(), end = m_elements.end();186 for (; iter != end; iter++) {187 if (first)188 first = false;189 else190 s << ',';191 (*iter)->Write(s);192 }193 s << ']';194}195 196bool JSONArray::SetObject(Index i, JSONValue::SP value) {197 if (value.get() == nullptr)198 return false;199 if (i < m_elements.size()) {200 m_elements[i] = value;201 return true;202 }203 if (i == m_elements.size()) {204 m_elements.push_back(value);205 return true;206 }207 return false;208}209 210bool JSONArray::AppendObject(JSONValue::SP value) {211 if (value.get() == nullptr)212 return false;213 m_elements.push_back(value);214 return true;215}216 217JSONValue::SP JSONArray::GetObject(Index i) {218 if (i < m_elements.size())219 return m_elements[i];220 return JSONValue::SP();221}222 223JSONArray::Size JSONArray::GetNumElements() { return m_elements.size(); }224 225JSONParser::JSONParser(const char *cstr) : StdStringExtractor(cstr) {}226 227JSONParser::Token JSONParser::GetToken(std::string &value) {228 std::ostringstream error;229 230 value.clear();231 SkipSpaces();232 const uint64_t start_index = m_index;233 const char ch = GetChar();234 switch (ch) {235 case '{':236 return Token::ObjectStart;237 case '}':238 return Token::ObjectEnd;239 case '[':240 return Token::ArrayStart;241 case ']':242 return Token::ArrayEnd;243 case ',':244 return Token::Comma;245 case ':':246 return Token::Colon;247 case '\0':248 return Token::EndOfFile;249 case 't':250 if (GetChar() == 'r')251 if (GetChar() == 'u')252 if (GetChar() == 'e')253 return Token::True;254 break;255 256 case 'f':257 if (GetChar() == 'a')258 if (GetChar() == 'l')259 if (GetChar() == 's')260 if (GetChar() == 'e')261 return Token::False;262 break;263 264 case 'n':265 if (GetChar() == 'u')266 if (GetChar() == 'l')267 if (GetChar() == 'l')268 return Token::Null;269 break;270 271 case '"': {272 while (true) {273 bool was_escaped = false;274 int escaped_ch = GetEscapedChar(was_escaped);275 if (escaped_ch == -1) {276 error << "error: an error occurred getting a character from offset "277 << start_index;278 value = error.str();279 return Token::Status;280 281 } else {282 const bool is_end_quote = escaped_ch == '"';283 const bool is_null = escaped_ch == 0;284 if (was_escaped || (!is_end_quote && !is_null)) {285 if (CHAR_MIN <= escaped_ch && escaped_ch <= CHAR_MAX) {286 value.append(1, (char)escaped_ch);287 } else {288 error << "error: wide character support is needed for unicode "289 "character 0x"290 << std::setprecision(4) << std::hex << escaped_ch;291 error << " at offset " << start_index;292 value = error.str();293 return Token::Status;294 }295 } else if (is_end_quote) {296 return Token::String;297 } else if (is_null) {298 value = "error: missing end quote for string";299 return Token::Status;300 }301 }302 }303 } break;304 305 case '-':306 case '0':307 case '1':308 case '2':309 case '3':310 case '4':311 case '5':312 case '6':313 case '7':314 case '8':315 case '9': {316 bool done = false;317 bool got_decimal_point = false;318 uint64_t exp_index = 0;319 bool got_int_digits = (ch >= '0') && (ch <= '9');320 bool got_frac_digits = false;321 bool got_exp_digits = false;322 while (!done) {323 const char next_ch = PeekChar();324 switch (next_ch) {325 case '0':326 case '1':327 case '2':328 case '3':329 case '4':330 case '5':331 case '6':332 case '7':333 case '8':334 case '9':335 if (exp_index != 0) {336 got_exp_digits = true;337 } else if (got_decimal_point) {338 got_frac_digits = true;339 } else {340 got_int_digits = true;341 }342 ++m_index; // Skip this character343 break;344 345 case '.':346 if (got_decimal_point) {347 error << "error: extra decimal point found at offset " << start_index;348 value = error.str();349 return Token::Status;350 } else {351 got_decimal_point = true;352 ++m_index; // Skip this character353 }354 break;355 356 case 'e':357 case 'E':358 if (exp_index != 0) {359 error << "error: extra exponent character found at offset "360 << start_index;361 value = error.str();362 return Token::Status;363 } else {364 exp_index = m_index;365 ++m_index; // Skip this character366 }367 break;368 369 case '+':370 case '-':371 // The '+' and '-' can only come after an exponent character...372 if (exp_index == m_index - 1) {373 ++m_index; // Skip the exponent sign character374 } else {375 error << "error: unexpected " << next_ch << " character at offset "376 << start_index;377 value = error.str();378 return Token::Status;379 }380 break;381 382 default:383 done = true;384 break;385 }386 }387 388 if (m_index > start_index) {389 value = m_packet.substr(start_index, m_index - start_index);390 if (got_decimal_point) {391 if (exp_index != 0) {392 // We have an exponent, make sure we got exponent digits393 if (got_exp_digits) {394 return Token::Float;395 } else {396 error << "error: got exponent character but no exponent digits at "397 "offset in float value \""398 << value << "\"";399 value = error.str();400 return Token::Status;401 }402 } else {403 // No exponent, but we need at least one decimal after the decimal404 // point405 if (got_frac_digits) {406 return Token::Float;407 } else {408 error << "error: no digits after decimal point \"" << value << "\"";409 value = error.str();410 return Token::Status;411 }412 }413 } else {414 // No decimal point415 if (got_int_digits) {416 // We need at least some integer digits to make an integer417 return Token::Integer;418 } else {419 error << "error: no digits negate sign \"" << value << "\"";420 value = error.str();421 return Token::Status;422 }423 }424 } else {425 error << "error: invalid number found at offset " << start_index;426 value = error.str();427 return Token::Status;428 }429 } break;430 default:431 break;432 }433 error << "error: failed to parse token at offset " << start_index434 << " (around character '" << ch << "')";435 value = error.str();436 return Token::Status;437}438 439int JSONParser::GetEscapedChar(bool &was_escaped) {440 was_escaped = false;441 const char ch = GetChar();442 if (ch == '\\') {443 was_escaped = true;444 const char ch2 = GetChar();445 switch (ch2) {446 case '"':447 case '\\':448 case '/':449 default:450 break;451 452 case 'b':453 return '\b';454 case 'f':455 return '\f';456 case 'n':457 return '\n';458 case 'r':459 return '\r';460 case 't':461 return '\t';462 case 'u': {463 const int hi_byte = DecodeHexU8();464 const int lo_byte = DecodeHexU8();465 if (hi_byte >= 0 && lo_byte >= 0)466 return hi_byte << 8 | lo_byte;467 return -1;468 } break;469 }470 return ch2;471 }472 return ch;473}474 475JSONValue::SP JSONParser::ParseJSONObject() {476 // The "JSONParser::Token::ObjectStart" token should have already been477 // consumed478 // by the time this function is called479 std::unique_ptr<JSONObject> dict_up(new JSONObject());480 481 std::string value;482 std::string key;483 while (true) {484 JSONParser::Token token = GetToken(value);485 486 if (token == JSONParser::Token::String) {487 key.swap(value);488 token = GetToken(value);489 if (token == JSONParser::Token::Colon) {490 JSONValue::SP value_sp = ParseJSONValue();491 if (value_sp)492 dict_up->SetObject(key, value_sp);493 else494 break;495 }496 } else if (token == JSONParser::Token::ObjectEnd) {497 return JSONValue::SP(dict_up.release());498 } else if (token == JSONParser::Token::Comma) {499 continue;500 } else {501 break;502 }503 }504 return JSONValue::SP();505}506 507JSONValue::SP JSONParser::ParseJSONArray() {508 // The "JSONParser::Token::ObjectStart" token should have already been509 // consumed510 // by the time this function is called511 std::unique_ptr<JSONArray> array_up(new JSONArray());512 513 std::string value;514 std::string key;515 while (true) {516 JSONParser::Token token = GetToken(value);517 if (token == JSONParser::Token::ArrayEnd)518 return JSONValue::SP(array_up.release());519 JSONValue::SP value_sp = ParseJSONValue(value, token);520 if (value_sp)521 array_up->AppendObject(value_sp);522 else523 break;524 525 token = GetToken(value);526 if (token == JSONParser::Token::Comma) {527 continue;528 } else if (token == JSONParser::Token::ArrayEnd) {529 return JSONValue::SP(array_up.release());530 } else {531 break;532 }533 }534 return JSONValue::SP();535}536 537JSONValue::SP JSONParser::ParseJSONValue() {538 std::string value;539 const JSONParser::Token token = GetToken(value);540 return ParseJSONValue(value, token);541}542 543JSONValue::SP JSONParser::ParseJSONValue(const std::string &value,544 const Token &token) {545 switch (token) {546 case JSONParser::Token::ObjectStart:547 return ParseJSONObject();548 549 case JSONParser::Token::ArrayStart:550 return ParseJSONArray();551 552 case JSONParser::Token::Integer: {553 if (value.front() == '-') {554 bool success = false;555 int64_t sval = StringConvert::ToSInt64(value.c_str(), 0, 0, &success);556 if (success)557 return JSONValue::SP(new JSONNumber(sval));558 } else {559 bool success = false;560 uint64_t uval = StringConvert::ToUInt64(value.c_str(), 0, 0, &success);561 if (success)562 return JSONValue::SP(new JSONNumber(uval));563 }564 } break;565 566 case JSONParser::Token::Float: {567 bool success = false;568 double val = StringConvert::ToDouble(value.c_str(), 0.0, &success);569 if (success)570 return JSONValue::SP(new JSONNumber(val));571 } break;572 573 case JSONParser::Token::String:574 return JSONValue::SP(new JSONString(value));575 576 case JSONParser::Token::True:577 return JSONValue::SP(new JSONTrue());578 579 case JSONParser::Token::False:580 return JSONValue::SP(new JSONFalse());581 582 case JSONParser::Token::Null:583 return JSONValue::SP(new JSONNull());584 585 default:586 break;587 }588 return JSONValue::SP();589}590