1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "json_parser.h" 17#include "logger.h" 18#include "utils.h" 19 20// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 21#define LOG_JSON(level) LOG(level, COMMON) << "JsonParser: " << std::string(log_recursion_level_, '\t') 22 23namespace panda { 24 25bool JsonObject::Parser::Parse(const std::string &text) 26{ 27 std::istringstream iss(text); 28 istream_.rdbuf(iss.rdbuf()); 29 return Parse(); 30} 31 32bool JsonObject::Parser::Parse(std::streambuf *stream_buf) 33{ 34 ASSERT(stream_buf != nullptr); 35 istream_.rdbuf(stream_buf); 36 return Parse(); 37} 38 39bool JsonObject::Parser::Parse() 40{ 41 ASSERT(current_obj_ != nullptr); 42 if (GetJsonObject(current_obj_) && TryGetSymbol('\0')) { 43 LOG_JSON(INFO) << "Successfully parsed JSON-object"; 44 return true; 45 } 46 LOG_JSON(ERROR) << "Parsing failed"; 47 return false; 48} 49 50bool JsonObject::Parser::GetJsonObject(JsonObject *empty_obj) 51{ 52 LOG_JSON(DEBUG) << "Parsing object"; 53 log_recursion_level_++; 54 ASSERT(empty_obj != nullptr); 55 ASSERT(empty_obj->values_map_.empty()); 56 if (!TryGetSymbol('{')) { 57 return false; 58 } 59 60 if (TryGetSymbol('}')) { 61 empty_obj->is_valid_ = true; 62 return true; 63 } 64 65 while (true) { 66 if (!InsertKeyValuePairIn(empty_obj)) { 67 return false; 68 } 69 if (TryGetSymbol(',')) { 70 LOG_JSON(DEBUG) << "Got a comma-separator, getting a new \"key-value\" pair"; 71 continue; 72 } 73 break; 74 } 75 76 log_recursion_level_--; 77 return (empty_obj->is_valid_ = TryGetSymbol('}')); 78} 79 80bool JsonObject::Parser::InsertKeyValuePairIn(JsonObject *obj) 81{ 82 ASSERT(obj != nullptr); 83 // Get key: 84 if (!GetJsonString()) { 85 LOG_JSON(ERROR) << "Error while getting a key"; 86 return false; 87 } 88 if (!TryGetSymbol(':')) { 89 LOG_JSON(ERROR) << "Expected ':' between key and value:"; 90 return false; 91 } 92 ASSERT(parsed_temp_.Get<StringT>() != nullptr); 93 Key key(std::move(*parsed_temp_.Get<StringT>())); 94 95 if (!GetValue()) { 96 return false; 97 } 98 99 // Get value: 100 Value value(std::move(parsed_temp_)); 101 ASSERT(obj != nullptr); 102 103 // Insert pair: 104 bool is_inserted = obj->values_map_.try_emplace(key, std::move(value)).second; 105 if (!is_inserted) { 106 LOG_JSON(ERROR) << "Key \"" << key << "\" must be unique"; 107 return false; 108 } 109 // Save string representation as a "source" of scalar values. For non-scalar types, string_temp_ is "": 110 obj->string_map_.try_emplace(key, std::move(string_temp_)); 111 obj->keys_.push_back(key); 112 113 LOG_JSON(DEBUG) << "Added entry with key \"" << key << "\""; 114 LOG_JSON(DEBUG) << "Parsed `key: value` pair:"; 115 LOG_JSON(DEBUG) << "- key: \"" << key << '"'; 116 ASSERT(obj->GetValueSourceString(key) != nullptr); 117 LOG_JSON(DEBUG) << "- value: \"" << *obj->GetValueSourceString(key) << '"'; 118 119 return true; 120} 121 122bool JsonObject::Parser::GetJsonString() 123{ 124 if (!TryGetSymbol('"')) { 125 LOG_JSON(ERROR) << "Expected '\"' at the start of the string"; 126 return false; 127 } 128 return GetString('"'); 129} 130 131static bool UnescapeStringChunk(std::string *result, const std::string &chunk, char delim, bool *finished) 132{ 133 for (size_t start = 0; start < chunk.size();) { 134 size_t end = chunk.find('\\', start); 135 *result += chunk.substr(start, end - start); 136 137 if (end == std::string::npos) { 138 // No more escapes. 139 break; 140 } 141 142 if (end == chunk.size() - 1) { 143 // Chunk ends with an unfinished escape sequence. 144 *result += delim; 145 *finished = false; 146 return true; 147 } 148 149 ++end; 150 start = end + 1; 151 152 constexpr unsigned ULEN = 4; 153 154 switch (chunk[end]) { 155 case '"': 156 case '\\': 157 case '/': 158 *result += chunk[end]; 159 break; 160 case 'b': 161 *result += '\b'; 162 break; 163 case 'f': 164 *result += '\f'; 165 break; 166 case 'n': 167 *result += '\n'; 168 break; 169 case 'r': 170 *result += '\r'; 171 break; 172 case 't': 173 *result += '\t'; 174 break; 175 case 'u': 176 if (end + ULEN < chunk.size()) { 177 // Char strings cannot include multibyte characters, ignore top byte. 178 *result += static_cast<char>((HexValue(chunk[end + ULEN - 1]) << 4U) | HexValue(chunk[end + ULEN])); 179 start += ULEN; 180 break; 181 } 182 [[fallthrough]]; 183 default: 184 // Invalid escape sequence. 185 return false; 186 } 187 } 188 189 *finished = true; 190 return true; 191} 192 193bool JsonObject::Parser::GetString(char delim) 194{ 195 std::string string; 196 197 for (bool finished = false; !finished;) { 198 std::string chunk; 199 if (!std::getline(istream_, chunk, delim) || !UnescapeStringChunk(&string, chunk, delim, &finished)) { 200 LOG_JSON(ERROR) << "Error while reading a string"; 201 return false; 202 } 203 } 204 205 LOG_JSON(DEBUG) << "Got a string: \"" << string << '"'; 206 string_temp_ = string; 207 parsed_temp_.SetValue(std::move(string)); 208 209 return true; 210} 211 212bool JsonObject::Parser::GetNum() 213{ 214 NumT num = 0; 215 istream_ >> num; 216 if (istream_.fail()) { 217 LOG_JSON(ERROR) << "Failed to read a num"; 218 return false; 219 } 220 parsed_temp_.SetValue(num); 221 LOG_JSON(DEBUG) << "Got an number: " << num; 222 return true; 223} 224 225bool JsonObject::Parser::GetBool() 226{ 227 BoolT boolean {false}; 228 istream_ >> std::boolalpha >> boolean; 229 if (istream_.fail()) { 230 LOG_JSON(ERROR) << "Failed to read a boolean"; 231 return false; 232 } 233 parsed_temp_.SetValue(boolean); 234 LOG_JSON(DEBUG) << "Got a boolean: " << std::boolalpha << boolean; 235 return true; 236} 237 238bool JsonObject::Parser::GetValue() 239{ 240 auto symbol = PeekSymbol(); 241 size_t pos_start = static_cast<size_t>(istream_.tellg()); 242 bool res = false; 243 switch (symbol) { 244 case 't': 245 case 'f': 246 res = GetBool(); 247 break; 248 249 case '0': 250 case '1': 251 case '2': 252 case '3': 253 case '4': 254 case '5': 255 case '6': 256 case '7': 257 case '8': 258 case '9': 259 case '-': 260 case '+': 261 case '.': 262 res = GetNum(); 263 break; 264 265 case '"': 266 return GetJsonString(); 267 case '[': 268 string_temp_ = ""; 269 return GetArray(); 270 case '{': { 271 string_temp_ = ""; 272 auto inner_obj_ptr = std::make_unique<JsonObject>(); 273 if (!GetJsonObject(inner_obj_ptr.get())) { 274 return false; 275 } 276 LOG_JSON(DEBUG) << "Got an inner JSON-object"; 277 parsed_temp_.SetValue(std::move(inner_obj_ptr)); 278 return true; 279 } 280 default: 281 LOG_JSON(ERROR) << "Unexpected character when trying to get value: '" << PeekSymbol() << "'"; 282 return false; 283 } 284 285 // Save source string of parsed value: 286 size_t pos_end = static_cast<size_t>(istream_.tellg()); 287 if (pos_end == static_cast<size_t>(-1)) { 288 return false; 289 } 290 size_t size = pos_end - pos_start; 291 string_temp_.resize(size, '\0'); 292 istream_.seekg(pos_start); 293 istream_.read(&string_temp_[0], size); 294 ASSERT(istream_); 295 return res; 296} 297 298bool JsonObject::Parser::GetArray() 299{ 300 if (!TryGetSymbol('[')) { 301 LOG_JSON(ERROR) << "Expected '[' at the start of an array"; 302 return false; 303 } 304 305 ArrayT temp; 306 307 if (TryGetSymbol(']')) { 308 parsed_temp_.SetValue(std::move(temp)); 309 return true; 310 } 311 312 while (true) { 313 if (!GetValue()) { 314 return false; 315 } 316 temp.push_back(std::move(parsed_temp_)); 317 if (TryGetSymbol(',')) { 318 LOG_JSON(DEBUG) << "Got a comma-separator, getting the next array element"; 319 continue; 320 } 321 break; 322 } 323 parsed_temp_.SetValue(std::move(temp)); 324 return TryGetSymbol(']'); 325} 326 327char JsonObject::Parser::PeekSymbol() 328{ 329 istream_ >> std::ws; 330 if (istream_.peek() == std::char_traits<char>::eof()) { 331 return '\0'; 332 } 333 return static_cast<char>(istream_.peek()); 334} 335 336char JsonObject::Parser::GetSymbol() 337{ 338 if (!istream_) { 339 return '\0'; 340 } 341 istream_ >> std::ws; 342 if (istream_.peek() == std::char_traits<char>::eof()) { 343 istream_.get(); 344 return '\0'; 345 } 346 return static_cast<char>(istream_.get()); 347} 348 349bool JsonObject::Parser::TryGetSymbol(int symbol) 350{ 351 ASSERT(!IsWhitespace(symbol)); 352 if (static_cast<char>(symbol) != GetSymbol()) { 353 istream_.unget(); 354 return false; 355 } 356 return true; 357} 358 359bool JsonObject::Parser::IsWhitespace(int symbol) 360{ 361 return bool(std::isspace(static_cast<unsigned char>(symbol))); 362} 363} // namespace panda 364 365#undef LOG_JSON 366