peerplays-fc/src/json.cpp

950 lines
26 KiB
C++
Raw Normal View History

2012-09-08 02:50:37 +00:00
#include <fc/json.hpp>
#include <fc/hex.hpp>
#include <fc/exception.hpp>
#include <fc/sstream.hpp>
#include <fc/filesystem.hpp>
#include <fc/interprocess/file_mapping.hpp>
#include <map>
2012-09-08 02:50:37 +00:00
namespace fc { namespace json {
/**
* Placeholder for unparsed json data.
*/
class string {
public:
template<typename T>
string( T&& v ):json_data( fc::forward<T>(v) ){}
string( const string& s ):json_data(s.json_data){}
string( string&& s ):json_data(fc::move(s.json_data)){}
string(){}
string& operator=( const fc::string& s ) {
json_data = fc::vector<char>(s.begin(),s.end());
return *this;
}
template<typename T>
string& operator=( T&& t ) {
json_data = fc::forward<T>(t);
return *this;
}
string& operator=( string&& s ) {
json_data = fc::move(s.json_data);
return *this;
}
string& operator=( const string& s )
{
json_data = s.json_data;
return *this;
}
fc::vector<char> json_data;
};
2012-09-08 02:50:37 +00:00
namespace errors {
enum error_type {
unknown_error = 0x0001, // Other errors not specified below
warning = 0x0002, // Other warnigns not specified below
sytnax_error = 0x0004, // fatal syntax errors unclosed brace
sytnax_warning = 0x0008, // recoverable syntax error (missing, missing ':', unquoted string)
missing_comma = 0x0010, // if the error was related to json syntax and not semantic
string_to_int = 0x0020, // any time lexical cast from string to int is required
double_to_int = 0x0040, // any time a double is received for an int
int_overflow = 0x0080, // any time int value is greater than underlying type
signed_to_unsigned = 0x0100, // any time a negative value is read for an unsigned field
int_to_bool = 0x0200, // any time an int is read for a bool
string_to_bool = 0x0400, // any time a string is read for a bool
bad_array_index = 0x0800, // atempt to read a vector field beyond end of sequence
unexpected_key = 0x1000, // fields in object
missing_key = 0x2000, // check required fields
type_mismatch = 0x4000, // expected a fundamental, got object, expected object, got array, etc.
type_conversion = 0x8000, // also set any time a conversion occurs
all = 0xffff,
none = 0x0000
};
} // namespace errors
2012-09-08 02:50:37 +00:00
/**
* Stores information about errors that occurred durring the parse.
*
* By default extra fields are 'ignored' as warning
* Loss of presision errors are warning.
* String to Int conversion warnings
* Double to Int
* Int to bool
*
*/
struct parse_error {
parse_error( int32_t ec, fc::string msg, char* s = 0, char* e = 0 )
:message(fc::move(msg)),type(ec),start(s),end(e){}
2012-09-08 02:50:37 +00:00
parse_error( parse_error&& m )
:message(fc::move(m.message)),type(m.type),start(m.start),end(m.end){}
2012-09-08 02:50:37 +00:00
fc::string message;
int32_t type;
char* start;
char* end;
};
2012-09-08 02:50:37 +00:00
/**
* Collects errors and manages how they are responded to.
*/
class error_collector {
public:
error_collector( error_collector&& e )
:m_errors(fc::move(e.m_errors)){
memcpy((char*)m_eclass,(char*)e.m_eclass, sizeof(m_eclass) );
}
/*
error_collector( const error_collector&& e )
:m_errors(e.m_errors){
memcpy((char*)m_eclass,(char*)e.m_eclass, sizeof(m_eclass) );
}
*/
~error_collector() throw() {
try {
m_errors.clear();
}catch(...){}
}
2012-09-08 02:50:37 +00:00
enum error_defaults {
default_report = json::errors::all,
default_recover = json::errors::all,
default_throw = json::errors::none,
default_ignore = ~(default_report|default_recover|default_throw)
};
error_collector(){
m_eclass[report_error_t] = default_report;
m_eclass[recover_error_t] = default_recover;
m_eclass[throw_error_t] = default_throw;
m_eclass[ignore_error_t] = default_ignore;
}
2012-09-08 02:50:37 +00:00
inline bool report( int32_t e )const {
return 0 != (m_eclass[report_error_t] & e);
}
inline bool recover( int32_t e )const {
return 0 != (m_eclass[recover_error_t] & e);
}
inline bool ignore( int32_t e )const {
return 0 != (m_eclass[ignore_error_t] & e);
}
2012-09-08 02:50:37 +00:00
void report_error( int32_t e ) {
m_eclass[report_error_t] |= e;
m_eclass[ignore_error_t] &= ~e;
2012-09-08 02:50:37 +00:00
}
void recover_error( int32_t e ) {
m_eclass[recover_error_t] |= e;
m_eclass[ignore_error_t] &= ~e;
2012-09-08 02:50:37 +00:00
}
void throw_error( int32_t e ) {
m_eclass[throw_error_t] |= e;
m_eclass[ignore_error_t] &= ~e;
2012-09-08 02:50:37 +00:00
}
void ignore_error( int32_t e ) {
m_eclass[ignore_error_t] |= e;
m_eclass[report_error_t] &= ~m_eclass[ignore_error_t];
m_eclass[recover_error_t] &= ~m_eclass[ignore_error_t];
m_eclass[throw_error_t] &= ~m_eclass[ignore_error_t];
}
void post_error( int32_t ec, fc::string msg, char* s = 0, char* e = 0 ) {
m_errors.push_back( parse_error( ec, fc::move(msg), s, e ) );
if( ec & m_eclass[throw_error_t] ) {
throw fc::move(*this);
}
2012-09-08 02:50:37 +00:00
}
const fc::vector<parse_error>& get_errors()const {
return m_errors;
2012-09-08 02:50:37 +00:00
}
private:
enum error_class {
ignore_error_t,
report_error_t,
recover_error_t,
throw_error_t,
num_error_classes
};
uint32_t m_eclass[num_error_classes];
fc::vector<parse_error> m_errors;
2012-09-08 02:50:37 +00:00
};
fc::value to_value( char* start, char* end, error_collector& ec );
2012-09-08 02:50:37 +00:00
fc::string escape_string( const fc::string& s ) {
// calculate escape string size.
uint32_t ecount = 0;
for( auto i = s.begin(); i != s.end(); ++i ) {
if( ' '<= *i && *i <= '~' && *i !='\\' && *i != '"' ) {
ecount+=1;
} else {
switch( *i ) {
case '\t' :
case '\n' :
case '\r' :
case '\\' :
case '"' :
ecount += 2; break;
default:
ecount += 4;
}
}
}
// unless the size changed, just return it.
if( ecount == s.size() ) { return s; }
// reserve the bytes
fc::string out; out.reserve(ecount);
// print it out.
for( auto i = s.begin(); i != s.end(); ++i ) {
if( ' '<= *i && *i <= '~' && *i !='\\' && *i != '"' ) {
out += *i;
} else {
out += '\\';
switch( *i ) {
case '\t' : out += 't'; break;
case '\n' : out += 'n'; break;
case '\r' : out += 'r'; break;
case '\\' : out += '\\'; break;
case '"' : out += '"'; break;
default:
out += "x";
const char* const hexdig = "0123456789abcdef";
out += hexdig[*i >> 4];
out += hexdig[*i & 0xF];
}
}
}
return out;
}
fc::string unescape_string( const fc::string& s ) {
fc::string out; out.reserve(s.size());
for( auto i = s.begin(); i != s.end(); ++i ) {
if( *i != '\\' ) {
if( *i != '"' ) out += *i;
}
else {
++i;
if( i == out.end() ) return out;
switch( *i ) {
case 't' : out += '\t'; break;
case 'n' : out += '\n'; break;
case 'r' : out += '\r'; break;
case '\\' : out += '\\'; break;
case '"' : out += '"'; break;
case 'x' : {
++i; if( i == out.end() ) return out;
char c = from_hex(*i);
++i; if( i == out.end() ) { out += c; return out; }
c = c<<4 | from_hex(*i);
out += c;
break;
}
default:
out += '\\';
out += *i;
}
}
}
return out;
}
2012-09-08 02:50:37 +00:00
/**
* Any unescaped quotes are dropped.
* Because unescaped strings are always shorter, we can simply reuse
* the memory of s.
*
* @param s a null terminated string that contains one or more escape chars
*/
char* inplace_unescape_string( char* s ) {
while( *s == '"' ) ++s;
char* out = s;
for( auto i = s; *i != '\0'; ++i ) {
if( *i != '\\' ) {
if( *i != '"' ) {
*out = *i;
++out;
}
}
else {
++i;
if( *i == '\0' ) { *out = '\0'; return s; }
switch( *i ) {
case 't' : *out = '\t'; ++out; break;
case 'n' : *out = '\n'; ++out; break;
case 'r' : *out = '\r'; ++out; break;
case '\\': *out = '\\'; ++out; break;
case '"' : *out = '"'; ++out; break;
case 'x' : {
++i; if( *i == '\0' ){ *out = '\0'; return s; }
char c = from_hex(*i);
++i; if( *i == '\0' ){ *out = c; ++out; *out = '\0'; return s; }
c = c<<4 | from_hex(*i);
*out = c;
++out;
break;
}
default:
*out = '\\';
++out;
*out = *i;
++out;
}
}
}
*out = '\0';
return s;
}
/**
* Ignores leading white space.
* If it starts with [,", or { reads until matching ],", or }
* If it starts with something else it reads until [{",}]: or whitespace only
* allowing a starting - or a single .
*
* @note internal json syntax errors are not caught, only bracket errors
* are caught by this method. This makes it easy for error recovery
* when values are read recursivley.
*
* @param in start of input
* @param end end of input
* @param oend output parameter to the end of the value
*
* @return a pointer to the start of the value
*/
char* read_value( char* in, char* end, char*& oend ) {
char* start = in;
char* oin = in;
// ignore leading whitespace
while( (in < end) && ((*in == ' ') || (*in == '\t') || (*in == '\n') || (*in == '\r')) ) {
++in;
}
start = in;
if( start == end ) {
oend = end;
return start;
2012-09-08 02:50:37 +00:00
}
bool found_dot = false;
// check for literal vs object, array or string
switch( *in ) {
case ':':
case ',':
case '=':
oend = in+1;
return start;
case '[':
case '{':
case '"':
break;
default: { // literal
// read until non-literal character
// allow it to start with -
// allow only one '.'
while( in != end ) {
switch( *in ) {
case '[': case ']':
case '{': case '}':
case ':': case '=':
case ',': case '"':
case ' ': case '\t': case '\n': case '\r': {
oend = in;
return start;
}
case '.':
if( found_dot ) {
oend = in;
return start;
2012-09-08 02:50:37 +00:00
}
found_dot = true;
2012-09-08 02:50:37 +00:00
break;
case '-':
if( in-start ){ oend = in; return start; }
}
++in;
}
oend = in;
return start;
2012-09-08 02:50:37 +00:00
}
} // end literal check
int depth = 0;
bool in_quote = false;
bool in_escape = false;
// read until closing ]} or " ignoring escaped "
while( in != end ) {
if( !in_quote ) {
switch( *in ) {
case '[':
case '{': ++depth; break;
case ']':
case '}': --depth; break;
case '"':
++depth;
in_quote = true;
break;
default: // do nothing;
break;
}
} else { // in quote
switch( *in ) {
case '"': if( !in_escape ) {
--depth;
in_quote = false;
break;
}
case '\\':
in_escape = !in_escape;
break;
default:
in_escape = false;
}
}
++in;
if( !depth ) { oend = in; return start; }
}
if( depth != 0 ) {
// TODO: Throw Parse Error!
elog("Parse Error!! '%s' size: %d", fc::string( oin, end ).c_str(), (oin-start));
}
oend = in; return start;
}
struct temp_set {
temp_set( char* v, char t )
:old(*v),val(v) { *val = t; }
~temp_set() { *val = old; }
char old;
char* val;
};
/**
* A,B,C
* Warn on extra ',' or missing ','
*/
void read_values( value::array& a, char* in, char* end, fc::json::error_collector& ec ) {
char* ve = 0;
char* v = fc::json::read_value( in, end, ve );
while( *v == ',' ) {
wlog( "unexpected ','");
v = fc::json::read_value( ve, end, ve );
}
if( v == ve ) return; // no values
2012-09-08 02:50:37 +00:00
{ temp_set temp(ve,'\0'); a.fields.push_back( to_value( v, ve, ec ) ); }
2012-09-08 02:50:37 +00:00
char* c;
char* ce = 0;
do { // expect comma + value | ''
2012-09-08 02:50:37 +00:00
// expect comma or ''
c = fc::json::read_value( ve, end, ce );
2012-09-08 02:50:37 +00:00
// '' means we are done, no errors
if( c == ce ) return;
if( *c != ',' ) // we got a value when expecting ','
{
wlog( "missing ," );
temp_set temp(ce,'\0'); a.fields.push_back( to_value(c, ce, ec) );
ve = ce;
continue; // start back at start
}
2012-09-08 02:50:37 +00:00
// expect value
v = fc::json::read_value( ce, end, ve );
while ( *v == ',' ) { // but got comma
// expect value
wlog( "extra comma at c->ce" );
c = v; ce = ve;
v = fc::json::read_value( ve, end, ve );
2012-09-08 02:50:37 +00:00
}
if( v == ve ) {
wlog( "trailing comma at c->ce" );
} else { // got value
temp_set temp(ve,'\0');
a.fields.push_back( to_value( v, ve, ec) );
2012-09-08 02:50:37 +00:00
}
} while( ve < end );// expect comma + value | ''
}
/**
* Reads one level deep, does not recruse into sub objects.
*/
char* read_key_val( std::map<fc::string,json::string>& obj, bool sc, char* in, char* end, fc::json::error_collector& ec ) {
char* name_end = 0;
char* name = in;
do {
// read first char
name = fc::json::read_value( name, end, name_end );
if( sc ) { // if we expect a ,
if( *name != ',' ) { // but didn't get one
wlog( "expected ',' but got %1%", name ); // warn and accept name
} else { // we got the exepcted , read the expected name
name = fc::json::read_value( name_end, end, name_end );
}
} else { // don't start with ','
while( *name == ',' ) { // while we don't have a name, keep looking
wlog( "unexpected ',' " );
name = fc::json::read_value( name_end, end, name_end );
}
}
} while( *name == ',' );
2012-09-08 02:50:37 +00:00
// now we should have a name.
if( name_end >= end -1 ) {
temp_set ntemp(name_end,'\0');
elog( "early end after reading name %1%", name );
return name_end;
}
if( *name != '"' ) {
temp_set ntemp(name_end,'\0');
wlog( "unquoted name '%1%'", name );
} else {
temp_set ntemp(name_end,'\0');
name = inplace_unescape_string(name);
}
2012-09-08 02:50:37 +00:00
char* col_end = 0;
char* col = fc::json::read_value( name_end, end, col_end );
2012-09-08 02:50:37 +00:00
char* val_end = 0;
char* val = 0;
bool sep_error = false;
if( col_end-col == 1 ) {
switch( *col ) {
case ':': break;
case ';':
case '=':
wlog( "found %1% instead of ':'", *col );
break;
default:
sep_error = true;
}
} else {
sep_error = true;
}
2012-09-08 02:50:37 +00:00
if( sep_error ) {
temp_set ntemp(name_end,'\0');
temp_set vtemp(col_end,'\0');
wlog( "expected ':' but got %1%", col );
wlog( "assuming this is the value... " );
val = col;
val_end = col_end;
} else {
if( name_end >= end -1 ) {
temp_set ntemp(name_end,'\0');
temp_set vtemp(col_end,'\0');
elog( "early end after reading name '%1%' and col '%2%'", name, col );
return name_end;
}
val = fc::json::read_value( col_end, end, val_end );
if( val == end ) {
wlog( "no value specified" );
}
}
temp_set ntemp(name_end,'\0');
temp_set vtemp(val_end,'\0');
//slog( "name: '%1%'", fc::string(name,name_end) );
obj[name] = fc::vector<char>(val,val_end);
// obj.fields.push_back( key_val( name, to_value( val, val_end, ec ) ) );
return val_end;
}
/**
* Reads an optional ',' followed by key : value, returning the next input position
* @param sc - start with ','
*/
char* read_key_val( value::object& obj, bool sc, char* in, char* end, fc::json::error_collector& ec ) {
char* name_end = 0;
char* name = in;
do {
// read first char
name = fc::json::read_value( name, end, name_end );
if( name == name_end )
return name;
if( sc ) { // if we expect a ,
if( *name != ',' ) { // but didn't get one
if( *name != '}' )
wlog( "expected ',' or '}' but got %1%", name ); // warn and accept name
} else { // we got the exepcted , read the expected name
name = fc::json::read_value( name_end, end, name_end );
}
} else { // don't start with ','
while( *name == ',' ) { // while we don't have a name, keep looking
wlog( "unexpected ',' " );
name = fc::json::read_value( name_end, end, name_end );
}
}
} while( *name == ',' );
2012-09-08 02:50:37 +00:00
// now we should have a name.
if( name_end >= end -1 ) {
temp_set ntemp(name_end,'\0');
elog( "early end after reading name %1%", name );
return name_end;
}
if( *name != '"' ) {
temp_set ntemp(name_end,'\0');
wlog( "unquoted name '%1%'", name );
} else {
temp_set ntemp(name_end,'\0');
name = inplace_unescape_string(name);
}
2012-09-08 02:50:37 +00:00
char* col_end = 0;
char* col = fc::json::read_value( name_end, end, col_end );
2012-09-08 02:50:37 +00:00
char* val_end = 0;
char* val = 0;
bool sep_error = false;
if( col_end-col == 1 ) {
switch( *col ) {
case ':': break;
case ';':
case '=':
wlog( "found %1% instead of ':'", *col );
break;
default:
sep_error = true;
}
} else {
sep_error = true;
}
2012-09-08 02:50:37 +00:00
if( sep_error ) {
temp_set ntemp(name_end,'\0');
temp_set vtemp(col_end,'\0');
wlog( "expected ':' but got %1%", col );
wlog( "assuming this is the value... " );
val = col;
val_end = col_end;
} else {
if( name_end >= end -1 ) {
temp_set ntemp(name_end,'\0');
temp_set vtemp(col_end,'\0');
elog( "early end after reading name '%1%' and col '%2%'", name, col );
return name_end;
}
val = fc::json::read_value( col_end, end, val_end );
if( val == end ) {
wlog( "no value specified" );
}
}
temp_set ntemp(name_end,'\0');
temp_set vtemp(val_end,'\0');
//slog( "name: '%1%'", fc::string(name,name_end) );
obj.fields.push_back( value::key_val( name, to_value( val, val_end, ec ) ) );
return val_end;
}
// first_key =:: '' | "name" : VALUE *list_key
// list_key '' | ',' "name" : VALUE
void read_key_vals( value::object& obj, char* in, char* end, fc::json::error_collector& ec ) {
bool ex_c = false;
char* kv_end = in;
do {
//slog( "%1% bytes to read", (end-kv_end) );
kv_end = read_key_val( obj, ex_c, kv_end, end, ec );
ex_c = true;
} while( kv_end < end );
}
// first_key =:: '' | "name" : VALUE *list_key
// list_key '' | ',' "name" : VALUE
std::map<fc::string,json::string> read_key_vals( char* in, char* end, fc::json::error_collector& ec ) {
std::map<fc::string,json::string> m;
bool ex_c = false;
char* kv_end = in;
do {
//slog( "%1% bytes to read", (end-kv_end) );
kv_end = read_key_val( m, ex_c, kv_end, end, ec );
ex_c = true;
} while( kv_end < end );
return m;
}
/**
* @brief adaptor for to_value( char*, char*, error_collector& )
*/
fc::value to_value( fc::vector<char>&& v, error_collector& ec ) {
if( v.size() == 0 ) return value();
return to_value( &v.front(), &v.front() + v.size(), ec );
}
/**
* Returns a fc::value containing from the json string.
*
* @param ec - determines how to respond to parse errors and logs
* any errors that occur while parsing the string.
*/
fc::value to_value( char* start, char* end, error_collector& ec ) {
if( start == end ) return value();
char* ve = 0;
char* s = read_value( start, end, ve );
//slog( "'%1%'", fc::string(start,ve) );
switch( s[0] ) {
case '[': {
value::array a;
read_values( a, s+1, ve -1, ec );
return a;
}
case '{': {
value::object o;
read_key_vals( o, s+1, ve -1, ec );
value v = fc::move(o);
return v;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
temp_set move_end(ve,'\0');
for( char* n = s+1; n != ve; ++n ) {
if( *n == '.' ) {
return fc::lexical_cast<double>(s);
2012-09-08 02:50:37 +00:00
}
}
return fc::lexical_cast<uint64_t>(s);
2012-09-08 02:50:37 +00:00
}
case '-': {
temp_set move_end(ve,'\0');
for( char* n = s+1; n != ve; ++n ) {
if( *n == '.' ) {
return fc::lexical_cast<double>(s);
2012-09-08 02:50:37 +00:00
}
}
return fc::lexical_cast<int64_t>(s);
2012-09-08 02:50:37 +00:00
}
case '.': {
temp_set move_end(ve,'\0');
return fc::lexical_cast<int64_t>(s);
2012-09-08 02:50:37 +00:00
}
case '\"': {
temp_set move_end(ve,'\0');
return inplace_unescape_string( s );
2012-09-08 02:50:37 +00:00
}
case 'n': {
temp_set move_end(ve,'\0');
if( strcmp(s,"null" ) ) return value();
2012-09-08 02:50:37 +00:00
}
case 't': {
temp_set move_end(ve,'\0');
if( strcmp(s,"true" ) ) return true;
}
case 'f': {
temp_set move_end(ve,'\0');
if( strcmp(s,"false" ) ) return false;
}
default:
wlog( "return unable to parse... return as string '%s'", fc::string(s,ve).c_str() );
return value( fc::string( s, ve) );
}
}
fc::string pretty_print( fc::vector<char>&& v, uint8_t indent ) {
int level = 0;
fc::stringstream ss;
bool first = false;
bool quote = false;
bool escape = false;
for( uint32_t i = 0; i < v.size(); ++i ) {
switch( v[i] ) {
case '\\':
if( !escape ) {
if( quote )
escape = true;
} else { escape = false; }
ss<<v[i];
break;
case ':':
if( !quote ) {
ss<<": ";
} else {
ss<<':';
2012-09-08 02:50:37 +00:00
}
break;
case '"':
if( first ) {
ss<<'\n';
for( int i = 0; i < level*indent; ++i ) ss<<' ';
first = false;
}
if( !escape ) {
quote = !quote;
}
escape = false;
ss<<'"';
break;
case '{':
case '[':
ss<<v[i];
if( !quote ) {
++level;
first = true;
}else {
escape = false;
}
break;
case '}':
case ']':
if( !quote ) {
if( v[i-1] != '[' && v[i-1] != '{' ) {
ss<<'\n';
}
--level;
if( !first ) {
for( int i = 0; i < level*indent; ++i ) ss<<' ';
2012-09-08 02:50:37 +00:00
}
ss<<v[i];
break;
} else {
escape = false;
ss<<v[i];
2012-09-08 02:50:37 +00:00
}
case ',':
if( !quote ) {
ss<<',';
first = true;
} else {
escape = false;
ss<<',';
}
break;
default:
if( first ) {
ss<<'\n';
for( int i = 0; i < level*indent; ++i ) ss<<' ';
first = false;
}
ss << v[i];
}
}
return ss.str();
}
template<typename Stream>
struct value_visitor : value::const_visitor {
value_visitor( Stream& s ):os(s){}
Stream& os;
virtual void operator()( const int8_t& v ){ os << v; }
virtual void operator()( const int16_t& v ){ os << v; }
virtual void operator()( const int32_t& v ){ os << v; }
virtual void operator()( const int64_t& v ){ os << v; }
virtual void operator()( const uint8_t& v ){ os << v; }
virtual void operator()( const uint16_t& v ){ os << v; }
virtual void operator()( const uint32_t& v ){ os << v; }
virtual void operator()( const uint64_t& v ){ os << v; }
virtual void operator()( const float& v ){ os << v; }
virtual void operator()( const double& v ){ os << v; }
virtual void operator()( const bool& v ){ os << (v ? "true" : "false"); }
virtual void operator()( const fc::string& v ){ os << '"' << escape_string(v) <<'"'; }
virtual void operator()( const value::object& o ){
os << '{';
for( uint32_t i = 0; i < o.fields.size(); ++i ) {
if( i ) os <<',';
(*this)( o.fields[i].key );
os<<':';
o.fields[i].val.visit( value_visitor(*this) );
2012-09-08 02:50:37 +00:00
}
os << '}';
}
virtual void operator()( const value::array& o ){
os << '[';
for( uint32_t i = 0; i < o.fields.size(); ++i ) {
if( i ) os <<',';
o.fields[i].visit( value_visitor(*this) );
2012-09-08 02:50:37 +00:00
}
os << ']';
}
virtual void operator()( ){ os << "null"; }
};
template<typename Stream>
void to_json( const fc::value& v, Stream& os ) {
v.visit( value_visitor<Stream>(os) );
}
void write( ostream& out, const value& val ) {
to_json( val, out );
}
fc::string to_string( const fc::value& v ) {
fc::stringstream ss;
to_json( v, ss );
return ss.str();
}
value from_file( const fc::path& local_path ) {
if( !exists(local_path) ) {
FC_THROW_MSG( "Source file '%s' does not exist", local_path.string() );
2012-09-08 02:50:37 +00:00
}
if( is_directory( local_path ) ) {
FC_THROW_MSG( "Source path '%s' is a directory, expected a file.", local_path.string() );
}
// memory map the file
file_mapping fmap( local_path.string().c_str(), read_only );
size_t fsize = file_size(local_path);
mapped_region mr( fmap, fc::read_only, 0, fsize );
const char* pos = reinterpret_cast<const char*>(mr.get_address());
const char* end = pos + fsize;
// TODO: implement a const version of to_value
fc::vector<char> tmp(pos,end);
json::error_collector ec;
return fc::json::to_value(tmp.data(), tmp.data()+fsize,ec);
}
value from_string( const fc::string& s ) {
return from_string( s.c_str(), s.c_str() + s.size() );
}
value from_string( fc::vector<char>&& v ) {
error_collector ec;
return to_value( v.data(), v.data() + v.size(), ec );
}
value from_string( const char* s, const char* e ) {
return from_string( fc::vector<char>(s,e) );
}
2012-09-08 02:50:37 +00:00
}} // fc::json