You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wownero/contrib/epee/include/storages/portable_storage_from_json.h

418 lines
17 KiB

// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Andrey N. Sabelnikov nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#pragma once
#include <boost/lexical_cast.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "parserse_base_utils.h"
#include "file_io_utils.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "serialization"
#define EPEE_JSON_RECURSION_LIMIT_INTERNAL 100
namespace epee
{
using namespace misc_utils::parse;
namespace serialization
{
namespace json
{
#define CHECK_ISSPACE() if(!epee::misc_utils::parse::isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));}
/*inline void parse_error()
{
ASSERT_MES_AND_THROW("json parse error");
}*/
template<class t_storage>
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)
{
CHECK_AND_ASSERT_THROW_MES(recursion < EPEE_JSON_RECURSION_LIMIT_INTERNAL, "Wrong JSON data: recursion limitation (" << EPEE_JSON_RECURSION_LIMIT_INTERNAL << ") exceeded");
std::string name;
typename t_storage::harray h_array = nullptr;
enum match_state
{
match_state_lookup_for_section_start,
match_state_lookup_for_name,
match_state_waiting_separator,
match_state_wonder_after_separator,
match_state_wonder_after_value,
match_state_wonder_array,
match_state_array_after_value,
match_state_array_waiting_value,
match_state_error
};
enum array_mode
{
array_mode_undifined = 0,
array_mode_sections,
array_mode_string,
array_mode_numbers,
array_mode_booleans
};
match_state state = match_state_lookup_for_section_start;
array_mode array_md = array_mode_undifined;
std::string::const_iterator it = sec_buf_begin;
for(;it != buf_end;it++)
{
switch (state)
{
case match_state_lookup_for_section_start:
if(*it == '{')
state = match_state_lookup_for_name;
else CHECK_ISSPACE();
break;
case match_state_lookup_for_name:
switch(*it)
{
case '"':
match_string2(it, buf_end, name);
state = match_state_waiting_separator;
break;
case '}':
//this is it! section ends here.
//seems that it is empty section
sec_buf_begin = it;
return;
default:
CHECK_ISSPACE();
}
break;
case match_state_waiting_separator:
if(*it == ':')
state = match_state_wonder_after_separator;
else CHECK_ISSPACE();
break;
case match_state_wonder_after_separator:
if(*it == '"')
{//just a named string value started
std::string val;
match_string2(it, buf_end, val);
//insert text value
stg.set_value(name, std::move(val), current_section);
state = match_state_wonder_after_value;
}else if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
{//just a named number value started
boost::string_ref val;
bool is_v_float = false;bool is_signed = false;
match_number2(it, buf_end, val, is_v_float, is_signed);
if(!is_v_float)
{
if(is_signed)
{
errno = 0;
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
stg.set_value(name, int64_t(nval), current_section);
}else
{
errno = 0;
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
stg.set_value(name, uint64_t(nval), current_section);
}
}else
{
errno = 0;
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
stg.set_value(name, double(nval), current_section);
}
state = match_state_wonder_after_value;
}else if(isalpha(*it) )
{// could be null, true or false
boost::string_ref word;
match_word2(it, buf_end, word);
if(boost::iequals(word, "null"))
{
state = match_state_wonder_after_value;
//just skip this,
}else if(boost::iequals(word, "true"))
{
stg.set_value(name, true, current_section);
state = match_state_wonder_after_value;
}else if(boost::iequals(word, "false"))
{
stg.set_value(name, false, current_section);
state = match_state_wonder_after_value;
}else ASSERT_MES_AND_THROW("Unknown value keyword " << word);
}else if(*it == '{')
{
//sub section here
typename t_storage::hsection new_sec = stg.open_section(name, current_section, true);
CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end));
run_handler(new_sec, it, buf_end, stg, recursion + 1);
state = match_state_wonder_after_value;
}else if(*it == '[')
{//array of something
state = match_state_wonder_array;
}else CHECK_ISSPACE();
break;
case match_state_wonder_after_value:
if(*it == ',')
state = match_state_lookup_for_name;
else if(*it == '}')
{
//this is it! section ends here.
sec_buf_begin = it;
return;
}else CHECK_ISSPACE();
break;
case match_state_wonder_array:
if(*it == '[')
{
ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry");
//mean array of array
}
if(*it == '{')
{
//mean array of sections
typename t_storage::hsection new_sec = nullptr;
h_array = stg.insert_first_section(name, new_sec, current_section);
CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section");
run_handler(new_sec, it, buf_end, stg, recursion + 1);
state = match_state_array_after_value;
array_md = array_mode_sections;
}else if(*it == '"')
{
//mean array of strings
std::string val;
match_string2(it, buf_end, val);
h_array = stg.insert_first_value(name, std::move(val), current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry");
state = match_state_array_after_value;
array_md = array_mode_string;
}else if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
{//array of numbers value started
boost::string_ref val;
bool is_v_float = false;bool is_signed_val = false;
match_number2(it, buf_end, val, is_v_float, is_signed_val);
if(!is_v_float)
{
if (is_signed_val)
{
errno = 0;
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
h_array = stg.insert_first_value(name, int64_t(nval), current_section);
}else
{
errno = 0;
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
h_array = stg.insert_first_value(name, uint64_t(nval), current_section);
}
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
}else
{
errno = 0;
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
h_array = stg.insert_first_value(name, double(nval), current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
}
state = match_state_array_after_value;
array_md = array_mode_numbers;
}else if(*it == ']')//empty array
{
array_md = array_mode_undifined;
state = match_state_wonder_after_value;
}else if(isalpha(*it) )
{// array of booleans
boost::string_ref word;
match_word2(it, buf_end, word);
if(boost::iequals(word, "true"))
{
h_array = stg.insert_first_value(name, true, current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
state = match_state_array_after_value;
array_md = array_mode_booleans;
}else if(boost::iequals(word, "false"))
{
h_array = stg.insert_first_value(name, false, current_section);
CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry");
state = match_state_array_after_value;
array_md = array_mode_booleans;
}else ASSERT_MES_AND_THROW("Unknown value keyword " << word)
}else CHECK_ISSPACE();
break;
case match_state_array_after_value:
if(*it == ',')
state = match_state_array_waiting_value;
else if(*it == ']')
{
h_array = nullptr;
array_md = array_mode_undifined;
state = match_state_wonder_after_value;
}else CHECK_ISSPACE();
break;
case match_state_array_waiting_value:
switch(array_md)
{
case array_mode_sections:
if(*it == '{')
{
typename t_storage::hsection new_sec = NULL;
bool res = stg.insert_next_section(h_array, new_sec);
CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section");
run_handler(new_sec, it, buf_end, stg, recursion + 1);
state = match_state_array_after_value;
}else CHECK_ISSPACE();
break;
case array_mode_string:
if(*it == '"')
{
std::string val;
match_string2(it, buf_end, val);
bool res = stg.insert_next_value(h_array, std::move(val));
CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values");
state = match_state_array_after_value;
}else CHECK_ISSPACE();
break;
case array_mode_numbers:
if (epee::misc_utils::parse::isdigit(*it) || *it == '-')
{//array of numbers value started
boost::string_ref val;
bool is_v_float = false;bool is_signed_val = false;
match_number2(it, buf_end, val, is_v_float, is_signed_val);
bool insert_res = false;
if(!is_v_float)
{
if (is_signed_val)
{
errno = 0;
int64_t nval = strtoll(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
insert_res = stg.insert_next_value(h_array, int64_t(nval));
}else
{
errno = 0;
uint64_t nval = strtoull(val.data(), NULL, 10);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
insert_res = stg.insert_next_value(h_array, uint64_t(nval));
}
}else
{
errno = 0;
double nval = strtod(val.data(), NULL);
if (errno) throw std::runtime_error("Invalid number: " + std::string(val));
insert_res = stg.insert_next_value(h_array, double(nval));
}
CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value");
state = match_state_array_after_value;
array_md = array_mode_numbers;
}else CHECK_ISSPACE();
break;
case array_mode_booleans:
if(isalpha(*it) )
{// array of booleans
boost::string_ref word;
match_word2(it, buf_end, word);
if(boost::iequals(word, "true"))
{
bool r = stg.insert_next_value(h_array, true);
CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry");
state = match_state_array_after_value;
}else if(boost::iequals(word, "false"))
{
bool r = stg.insert_next_value(h_array, false);
CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry");
state = match_state_array_after_value;
}
else ASSERT_MES_AND_THROW("Unknown value keyword " << word);
}else CHECK_ISSPACE();
break;
case array_mode_undifined:
default:
ASSERT_MES_AND_THROW("Bad array state");
}
break;
case match_state_error:
default:
ASSERT_MES_AND_THROW("WRONG JSON STATE");
}
}
}
/*
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": -10021,
"have_boobs": true,
"have_balls": false
},
"phoneNumber": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
],
"phoneNumbers": [
"812 123-1234",
"916 123-4567"
]
}
*/
template<class t_storage>
inline bool load_from_json(const std::string& buff_json, t_storage& stg)
{
std::string::const_iterator sec_buf_begin = buff_json.begin();
try
{
run_handler(nullptr, sec_buf_begin, buff_json.end(), stg, 0);
return true;
}
catch(const std::exception& ex)
{
MERROR("Failed to parse json, what: " << ex.what());
return false;
}
catch(...)
{
MERROR("Failed to parse json");
return false;
}
}
}
}
}