Monero
Loading...
Searching...
No Matches
portable_storage_from_json.h
Go to the documentation of this file.
1// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above copyright
9// notice, this list of conditions and the following disclaimer in the
10// documentation and/or other materials provided with the distribution.
11// * Neither the name of the Andrey N. Sabelnikov nor the
12// names of its contributors may be used to endorse or promote products
13// derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
19// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25//
26
27#pragma once
28#include <boost/lexical_cast.hpp>
29#include <boost/utility/string_ref.hpp>
30#include <boost/algorithm/string/predicate.hpp>
31#include "parserse_base_utils.h"
32#include "file_io_utils.h"
33
34#undef MONERO_DEFAULT_LOG_CATEGORY
35#define MONERO_DEFAULT_LOG_CATEGORY "serialization"
36
37#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100
38
39namespace epee
40{
41 using namespace misc_utils::parse;
42 namespace serialization
43 {
44 namespace json
45 {
46#define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));}
47
48 /*inline void parse_error()
49 {
50 ASSERT_MES_AND_THROW("json parse error");
51 }*/
52 template<class t_storage>
53 inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg, unsigned int recursion)
54 {
55 CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded");
56
57 std::string name;
58 typename t_storage::harray h_array = nullptr;
59 enum match_state
60 {
61 match_state_lookup_for_section_start,
62 match_state_lookup_for_name,
63 match_state_waiting_separator,
64 match_state_wonder_after_separator,
65 match_state_wonder_after_value,
66 match_state_wonder_array,
67 match_state_array_after_value,
68 match_state_array_waiting_value,
69 match_state_error
70 };
71
72 enum array_mode
73 {
74 array_mode_undifined = 0,
75 array_mode_sections,
76 array_mode_string,
77 array_mode_numbers,
78 array_mode_booleans
79 };
80
81 match_state state = match_state_lookup_for_section_start;
82 array_mode array_md = array_mode_undifined;
83 std::string::const_iterator it = sec_buf_begin;
84 for(;it != buf_end;it++)
85 {
86 switch (state)
87 {
88 case match_state_lookup_for_section_start:
89 if(*it == '{')
90 state = match_state_lookup_for_name;
91 else CHECK_ISSPACE();
92 break;
93 case match_state_lookup_for_name:
94 switch(*it)
95 {
96 case '"':
97 match_string2(it, buf_end, name);
98 state = match_state_waiting_separator;
99 break;
100 case '}':
101 //this is it! section ends here.
102 //seems that it is empty section
103 sec_buf_begin = it;
104 return;
105 default:
107 }
108 break;
109 case match_state_waiting_separator:
110 if(*it == ':')
111 state = match_state_wonder_after_separator;
112 else CHECK_ISSPACE();
113 break;
114 case match_state_wonder_after_separator:
115 if(*it == '"')
116 {//just a named string value started
117 std::string val;
118 match_string2(it, buf_end, val);
119 //insert text value
120 stg.set_value(name, std::move(val), current_section);
121 state = match_state_wonder_after_value;
122 }else if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
123 {//just a named number value started
124 boost::string_ref val;
125 bool is_v_float = false;bool is_signed = false;
126 match_number2(it, buf_end, val, is_v_float, is_signed);
127 if(!is_v_float)
128 {
129 if(is_signed)
130 {
131 errno = 0;
132 int64_t nval = strtoll(val.data(), NULL, 10);
133 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
134 stg.set_value(name, int64_t(nval), current_section);
135 }else
136 {
137 errno = 0;
138 uint64_t nval = strtoull(val.data(), NULL, 10);
139 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
140 stg.set_value(name, uint64_t(nval), current_section);
141 }
142 }else
143 {
144 errno = 0;
145 double nval = strtod(val.data(), NULL);
146 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
147 stg.set_value(name, double(nval), current_section);
148 }
149 state = match_state_wonder_after_value;
150 }else if(isalpha(*it) )
151 {// could be null, true or false
152 boost::string_ref word;
153 match_word2(it, buf_end, word);
154 if(boost::iequals(word, "null"))
155 {
156 state = match_state_wonder_after_value;
157 //just skip this,
158 }else if(boost::iequals(word, "true"))
159 {
160 stg.set_value(name, true, current_section);
161 state = match_state_wonder_after_value;
162 }else if(boost::iequals(word, "false"))
163 {
164 stg.set_value(name, false, current_section);
165 state = match_state_wonder_after_value;
166 }else ASSERT_MES_AND_THROW("Unknown value keyword " << word);
167 }else if(*it == '{')
168 {
169 //sub section here
170 typename t_storage::hsection new_sec = stg.open_section(name, current_section, true);
171 CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end));
172 run_handler(new_sec, it, buf_end, stg, recursion + 1);
173 state = match_state_wonder_after_value;
174 }else if(*it == '[')
175 {//array of something
176 state = match_state_wonder_array;
177 }else CHECK_ISSPACE();
178 break;
179 case match_state_wonder_after_value:
180 if(*it == ',')
181 state = match_state_lookup_for_name;
182 else if(*it == '}')
183 {
184 //this is it! section ends here.
185 sec_buf_begin = it;
186 return;
187 }else CHECK_ISSPACE();
188 break;
189 case match_state_wonder_array:
190 if(*it == '[')
191 {
192 ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry");
193 //mean array of array
194 }
195 if(*it == '{')
196 {
197 //mean array of sections
198 typename t_storage::hsection new_sec = nullptr;
199 h_array = stg.insert_first_section(name, new_sec, current_section);
200 CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section");
201 run_handler(new_sec, it, buf_end, stg, recursion + 1);
202 state = match_state_array_after_value;
203 array_md = array_mode_sections;
204 }else if(*it == '"')
205 {
206 //mean array of strings
207 std::string val;
208 match_string2(it, buf_end, val);
209 h_array = stg.insert_first_value(name, std::move(val), current_section);
210 CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry");
211 state = match_state_array_after_value;
212 array_md = array_mode_string;
213 }else if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
214 {//array of numbers value started
215 boost::string_ref val;
216 bool is_v_float = false;bool is_signed_val = false;
217 match_number2(it, buf_end, val, is_v_float, is_signed_val);
218 if(!is_v_float)
219 {
220 if (is_signed_val)
221 {
222 errno = 0;
223 int64_t nval = strtoll(val.data(), NULL, 10);
224 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
225 h_array = stg.insert_first_value(name, int64_t(nval), current_section);
226 }else
227 {
228 errno = 0;
229 uint64_t nval = strtoull(val.data(), NULL, 10);
230 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
231 h_array = stg.insert_first_value(name, uint64_t(nval), current_section);
232 }
233 CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
234 }else
235 {
236 errno = 0;
237 double nval = strtod(val.data(), NULL);
238 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
239 h_array = stg.insert_first_value(name, double(nval), current_section);
240 CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
241 }
242
243 state = match_state_array_after_value;
244 array_md = array_mode_numbers;
245 }else if(*it == ']')//empty array
246 {
247 array_md = array_mode_undifined;
248 state = match_state_wonder_after_value;
249 }else if(isalpha(*it) )
250 {// array of booleans
251 boost::string_ref word;
252 match_word2(it, buf_end, word);
253 if(boost::iequals(word, "true"))
254 {
255 h_array = stg.insert_first_value(name, true, current_section);
256 CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
257 state = match_state_array_after_value;
258 array_md = array_mode_booleans;
259 }else if(boost::iequals(word, "false"))
260 {
261 h_array = stg.insert_first_value(name, false, current_section);
262 CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
263 state = match_state_array_after_value;
264 array_md = array_mode_booleans;
265
266 }else ASSERT_MES_AND_THROW("Unknown value keyword " << word)
267 }else CHECK_ISSPACE();
268 break;
269 case match_state_array_after_value:
270 if(*it == ',')
271 state = match_state_array_waiting_value;
272 else if(*it == ']')
273 {
274 h_array = nullptr;
275 array_md = array_mode_undifined;
276 state = match_state_wonder_after_value;
277 }else CHECK_ISSPACE();
278 break;
279 case match_state_array_waiting_value:
280 switch(array_md)
281 {
282 case array_mode_sections:
283 if(*it == '{')
284 {
285 typename t_storage::hsection new_sec = NULL;
286 bool res = stg.insert_next_section(h_array, new_sec);
287 CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section");
288 run_handler(new_sec, it, buf_end, stg, recursion + 1);
289 state = match_state_array_after_value;
290 }else CHECK_ISSPACE();
291 break;
292 case array_mode_string:
293 if(*it == '"')
294 {
295 std::string val;
296 match_string2(it, buf_end, val);
297 bool res = stg.insert_next_value(h_array, std::move(val));
298 CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values");
299 state = match_state_array_after_value;
300 }else CHECK_ISSPACE();
301 break;
302 case array_mode_numbers:
303 if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
304 {//array of numbers value started
305 boost::string_ref val;
306 bool is_v_float = false;bool is_signed_val = false;
307 match_number2(it, buf_end, val, is_v_float, is_signed_val);
308 bool insert_res = false;
309 if(!is_v_float)
310 {
311 if (is_signed_val)
312 {
313 errno = 0;
314 int64_t nval = strtoll(val.data(), NULL, 10);
315 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
316 insert_res = stg.insert_next_value(h_array, int64_t(nval));
317 }else
318 {
319 errno = 0;
320 uint64_t nval = strtoull(val.data(), NULL, 10);
321 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
322 insert_res = stg.insert_next_value(h_array, uint64_t(nval));
323 }
324 }else
325 {
326 errno = 0;
327 double nval = strtod(val.data(), NULL);
328 if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
329 insert_res = stg.insert_next_value(h_array, double(nval));
330 }
331 CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value");
332 state = match_state_array_after_value;
333 array_md = array_mode_numbers;
334 }else CHECK_ISSPACE();
335 break;
336 case array_mode_booleans:
337 if(isalpha(*it) )
338 {// array of booleans
339 boost::string_ref word;
340 match_word2(it, buf_end, word);
341 if(boost::iequals(word, "true"))
342 {
343 bool r = stg.insert_next_value(h_array, true);
344 CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry");
345 state = match_state_array_after_value;
346 }else if(boost::iequals(word, "false"))
347 {
348 bool r = stg.insert_next_value(h_array, false);
349 CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry");
350 state = match_state_array_after_value;
351 }
352 else ASSERT_MES_AND_THROW("Unknown value keyword " << word);
353 }else CHECK_ISSPACE();
354 break;
355 case array_mode_undifined:
356 default:
357 ASSERT_MES_AND_THROW("Bad array state");
358 }
359 break;
360 case match_state_error:
361 default:
362 ASSERT_MES_AND_THROW("WRONG JSON STATE");
363 }
364 }
365 }
366/*
367{
368 "firstName": "John",
369 "lastName": "Smith",
370 "age": 25,
371 "address": {
372 "streetAddress": "21 2nd Street",
373 "city": "New York",
374 "state": "NY",
375 "postalCode": -10021,
376 "have_boobs": true,
377 "have_balls": false
378 },
379 "phoneNumber": [
380 {
381 "type": "home",
382 "number": "212 555-1234"
383 },
384 {
385 "type": "fax",
386 "number": "646 555-4567"
387 }
388 ],
389 "phoneNumbers": [
390 "812 123-1234",
391 "916 123-4567"
392 ]
393}
394*/
395 template<class t_storage>
396 inline bool load_from_json(const std::string& buff_json, t_storage& stg)
397 {
398 std::string::const_iterator sec_buf_begin = buff_json.begin();
399 try
400 {
401 run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0);
402 return true;
403 }
404 catch(const std::exception& ex)
405 {
406 MERROR("Failed to parse json, what: " << ex.what());
407 return false;
408 }
409 catch(...)
410 {
411 MERROR("Failed to parse json");
412 return false;
413 }
414 }
415 }
416 }
417}
const char * res
Definition hmac_keccak.cpp:42
void match_string2(std::string::const_iterator &star_end_string, std::string::const_iterator buf_end, std::string &val)
Definition parserse_base_utils.cpp:95
void match_number2(std::string::const_iterator &star_end_string, std::string::const_iterator buf_end, boost::string_ref &val, bool &is_float_val, bool &is_signed_val)
Definition parserse_base_utils.cpp:187
bool isdigit(char c)
Definition parserse_base_utils.h:91
void match_word2(std::string::const_iterator &star_end_string, std::string::const_iterator buf_end, boost::string_ref &val)
Definition parserse_base_utils.cpp:223
Definition portable_storage_from_json.h:45
bool load_from_json(const std::string &buff_json, t_storage &stg)
Definition portable_storage_from_json.h:396
void run_handler(typename t_storage::hsection current_section, std::string::const_iterator &sec_buf_begin, std::string::const_iterator buf_end, t_storage &stg, unsigned int recursion)
Definition portable_storage_from_json.h:53
TODO: (mj-xmr) This will be reduced in an another PR.
Definition byte_slice.h:40
Definition binary_utils.h:36
const char * name
Definition options.c:30
#define EPEE_JSON_RECURSION_LIMIT_INTERNAL
Definition portable_storage_from_json.h:37
#define CHECK_ISSPACE()
Definition portable_storage_from_json.h:46
signed __int64 int64_t
Definition stdint.h:135
unsigned __int64 uint64_t
Definition stdint.h:136
Definition blake256.h:36