Monero
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 
39 namespace 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 
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:
106  CHECK_ISSPACE();
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
Definition: binary_utils.h:36
#define CHECK_ISSPACE()
Definition: portable_storage_from_json.h:46
array_entry * harray
Definition: portable_storage_base.h:175
bool isdigit(char c)
Definition: parserse_base_utils.h:91
::std::string string
Definition: gtest-port.h:1097
#define EPEE_JSON_RECURSION_LIMIT_INTERNAL
Definition: portable_storage_from_json.h:37
section * hsection
Definition: portable_storage_base.h:174
ps load_from_json(std::string((const char *) buf, len))
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
unsigned __int64 uint64_t
Definition: stdint.h:136
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
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
r
Definition: testupnpigd.py:61
TODO: (mj-xmr) This will be reduced in an another PR.
Definition: byte_slice.h:39
const T & move(const T &t)
Definition: gtest-port.h:1317
Definition: blake256.h:36
signed __int64 int64_t
Definition: stdint.h:135
const char * name
Definition: options.c:30
rapidjson::Document json
Definition: transport.cpp:49