brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.8 KiB · 5453d85 Raw
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