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