• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 namespace panda {
24 
Parse(const std::string & text)25 bool JsonObject::Parser::Parse(const std::string &text)
26 {
27     std::istringstream iss(text);
28     istream_.rdbuf(iss.rdbuf());
29     return Parse();
30 }
31 
Parse(std::streambuf * stream_buf)32 bool JsonObject::Parser::Parse(std::streambuf *stream_buf)
33 {
34     ASSERT(stream_buf != nullptr);
35     istream_.rdbuf(stream_buf);
36     return Parse();
37 }
38 
Parse()39 bool 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 
GetJsonObject(JsonObject * empty_obj)50 bool 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 
InsertKeyValuePairIn(JsonObject * obj)80 bool 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 
GetJsonString()122 bool 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 
UnescapeStringChunk(std::string * result,const std::string & chunk,char delim,bool * finished)131 static 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 
GetString(char delim)193 bool 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 
GetNum()212 bool 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 
GetBool()225 bool 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 
GetValue()238 bool 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 
GetArray()298 bool 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 
PeekSymbol()327 char 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 
GetSymbol()336 char 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 
TryGetSymbol(int symbol)349 bool 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 
IsWhitespace(int symbol)359 bool 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