#include <boost/json.hpp>
#include <boost/json/basic_parser_impl.hpp>
#include <iomanip>
#include <iostream>
#include "file.hpp"
using namespace boost::json;
class null_parser
{
struct handler
{
constexpr static std::size_t max_object_size = std::size_t(-1);
constexpr static std::size_t max_array_size = std::size_t(-1);
constexpr static std::size_t max_key_size = std::size_t(-1);
constexpr static std::size_t max_string_size = std::size_t(-1);
bool on_document_begin( boost::system::error_code& ) { return true; }
bool on_document_end( boost::system::error_code& ) { return true; }
bool on_object_begin( boost::system::error_code& ) { return true; }
bool on_object_end( std::size_t, boost::system::error_code& ) { return true; }
bool on_array_begin( boost::system::error_code& ) { return true; }
bool on_array_end( std::size_t, boost::system::error_code& ) { return true; }
bool on_key_part( string_view, std::size_t, boost::system::error_code& ) { return true; }
bool on_key( string_view, std::size_t, boost::system::error_code& ) { return true; }
bool on_string_part( string_view, std::size_t, boost::system::error_code& ) { return true; }
bool on_string( string_view, std::size_t, boost::system::error_code& ) { return true; }
bool on_number_part( string_view, boost::system::error_code& ) { return true; }
bool on_int64( std::int64_t, string_view, boost::system::error_code& ) { return true; }
bool on_uint64( std::uint64_t, string_view, boost::system::error_code& ) { return true; }
bool on_double( double, string_view, boost::system::error_code& ) { return true; }
bool on_bool( bool, boost::system::error_code& ) { return true; }
bool on_null( boost::system::error_code& ) { return true; }
bool on_comment_part(string_view, boost::system::error_code&) { return true; }
bool on_comment(string_view, boost::system::error_code&) { return true; }
};
basic_parser<handler> p_;
public:
null_parser()
: p_(parse_options())
{
}
~null_parser()
{
}
std::size_t
write(
char const* data,
std::size_t size,
boost::system::error_code& ec)
{
auto const n = p_.write_some( false, data, size, ec );
if(! ec && n < size)
ec = error::extra_data;
return n;
}
};
bool
validate( string_view s )
{
null_parser p;
boost::system::error_code ec;
p.write( s.data(), s.size(), ec );
if( ec )
return false;
return true;
}
int
main(int argc, char** argv)
{
if(argc != 2)
{
std::cerr <<
"Usage: validate <filename>"
<< std::endl;
return EXIT_FAILURE;
}
try
{
auto const s = read_file( argv[1] );
auto const valid = validate( s );
if( valid )
std::cout << argv[1] << " contains a valid JSON\n";
else
std::cout << argv[1] << " does not contain a valid JSON\n";
}
catch(std::exception const& e)
{
std::cerr <<
"Caught exception: "
<< e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
#include <boost/json.hpp>
#include <boost/endian.hpp>
#include <stdexcept>
#include <fstream>
#include <iostream>
using namespace boost::json;
void serialize_cbor_number(
unsigned char mt, std::uint64_t n, std::vector<unsigned char> & out )
{
mt <<= 5;
if( n < 24 )
{
out.push_back( static_cast<unsigned char>( mt + n ) );
}
else if( n < 256 )
{
unsigned char data[] = { static_cast<unsigned char>( mt + 24 ), static_cast<unsigned char>( n ) };
out.insert( out.end(), std::begin( data ), std::end( data ) );
}
else if( n < 65536 )
{
unsigned char data[] = { static_cast<unsigned char>( mt + 25 ), static_cast<unsigned char>( n >> 8 ), static_cast<unsigned char>( n ) };
out.insert( out.end(), std::begin( data ), std::end( data ) );
}
else if( n < 0x1000000ull )
{
unsigned char data[ 5 ];
data[ 0 ] = static_cast<unsigned char>( mt + 26 );
boost::endian::endian_store<std::uint32_t, 4, boost::endian::order::big>( data + 1, static_cast<std::uint32_t>( n ) );
out.insert( out.end(), std::begin( data ), std::end( data ) );
}
else
{
unsigned char data[ 9 ];
data[ 0 ] = static_cast<unsigned char>( mt + 27 );
boost::endian::endian_store<std::uint64_t, 8, boost::endian::order::big>( data + 1, n );
out.insert( out.end(), std::begin( data ), std::end( data ) );
}
}
void
serialize_cbor_string( string_view sv, std::vector<unsigned char>& out )
{
std::size_t n = sv.size();
serialize_cbor_number( 3, n, out );
out.insert( out.end(), sv.data(), sv.data() + n );
}
void
serialize_cbor_value( const value& jv, std::vector<unsigned char>& out )
{
switch( jv.kind() )
{
case kind::null:
out.push_back( 224 + 22 );
break;
case kind::bool_:
out.push_back( 224 + 20 + jv.get_bool() );
break;
case kind::int64:
{
std::int64_t n = jv.get_int64();
if( n >= 0 )
serialize_cbor_number( 0, n, out );
else
serialize_cbor_number( 1, ~n, out );
}
break;
case kind::uint64:
serialize_cbor_number( 0, jv.get_uint64(), out );
break;
case kind::double_:
{
unsigned char data[ 9 ];
data[ 0 ] = 224 + 27;
boost::endian::endian_store<double, 8, boost::endian::order::big>( data + 1, jv.get_double() );
out.insert( out.end(), std::begin(data), std::end(data) );
}
break;
case kind::string:
serialize_cbor_string( jv.get_string(), out );
break;
case kind::array:
{
const array& ja = jv.get_array();
std::size_t n = ja.size();
out.reserve( out.size() + n + 1 );
serialize_cbor_number( 4, n, out );
for( std::size_t i = 0; i < n; ++i )
serialize_cbor_value( ja[i], out );
}
break;
case kind::object:
{
const object& jo = jv.get_object();
std::size_t n = jo.size();
out.reserve( out.size() + 3 * n + 1 );
serialize_cbor_number( 5, n, out );
for( const key_value_pair& kv: jo )
{
serialize_cbor_string( kv.key(), out );
serialize_cbor_value( kv.value(), out );
}
}
break;
}
}
BOOST_NORETURN
void
throw_eof_error()
{
throw std::runtime_error( "Unexpected end of input" );
}
BOOST_NORETURN
void
throw_format_error( char const * err )
{
throw std::runtime_error( err );
}
void
ensure( std::size_t n, const unsigned char* first, const unsigned char* last )
{
if( static_cast<std::size_t>(last - first) < n )
throw_eof_error();
}
const unsigned char*
parse_cbor_value(
const unsigned char* first, const unsigned char* last, value& v );
const unsigned char*
parse_cbor_number(
const unsigned char* first, const unsigned char* last, unsigned char ch, std::uint64_t& n )
{
unsigned char cv = ch & 31;
if( cv < 24 )
{
n = cv;
}
else if( cv == 24 )
{
ensure( 1, first, last );
n = *first++;
}
else if( cv == 25 )
{
ensure( 2, first, last );
n = boost::endian::load_big_u16( first );
first += 2;
}
else if( cv == 26 )
{
ensure( 4, first, last );
n = boost::endian::load_big_u32( first );
first += 4;
}
else if( cv == 27 )
{
ensure( 8, first, last );
n = boost::endian::load_big_u64( first );
first += 8;
}
else if( cv == 31 )
{
throw_format_error( "Infinite sequences aren't supported" );
}
else
{
throw_format_error( "Invalid minor type" );
}
return first;
}
const unsigned char*
parse_cbor_string(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
std::uint64_t n;
first = parse_cbor_number( first, last, ch, n );
ensure( n, first, last );
string_view sv( reinterpret_cast<char const*>( first ), n );
first += n;
v = sv;
return first;
}
const unsigned char*
parse_cbor_array(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
std::uint64_t n;
first = parse_cbor_number( first, last, ch, n );
array & a = v.emplace_array();
a.resize( n );
std::size_t i = 0;
for( ; i < n; ++i )
{
ensure( 1, first, last );
unsigned char ch2 = *first;
if( ch2 != 0xFB ) break;
++first;
ensure( 8, first, last );
double w = boost::endian::endian_load<double, 8, boost::endian::order::big>( first );
first += 8;
a[ i ] = w;
}
for( ; i < n; ++i )
{
ensure( 1, first, last );
unsigned char ch2 = *first;
if( ch2 >= 0x40 ) break;
++first;
std::uint64_t m;
first = parse_cbor_number( first, last, ch2, m );
if( ch2 < 0x20 )
{
a[ i ] = m;
}
else
{
a[ i ] = static_cast<std::int64_t>( ~m );
}
}
for( ; i < n; ++i )
first = parse_cbor_value( first, last, a[ i ] );
return first;
}
const unsigned char*
parse_cbor_object(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
std::uint64_t n;
first = parse_cbor_number( first, last, ch, n );
object & o = v.emplace_object();
o.reserve( n );
for( std::size_t i = 0; i < n; ++i )
{
ensure( 1, first, last );
unsigned char ch2 = *first++;
if( ( ch2 >> 5 ) != 3 )
throw_format_error( "Object keys must be strings" );
std::uint64_t m;
first = parse_cbor_number( first, last, ch2, m );
ensure( m, first, last );
string_view sv( reinterpret_cast<char const*>( first ), m );
first += m;
first = parse_cbor_value( first, last, o[ sv ] );
}
return first;
}
const unsigned char*
parse_cbor_unsigned(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
std::uint64_t n;
first = parse_cbor_number( first, last, ch, n );
v = n;
return first;
}
const unsigned char*
parse_cbor_signed(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
std::uint64_t n;
first = parse_cbor_number( first, last, ch, n );
v = static_cast<std::int64_t>( ~n );
return first;
}
const unsigned char*
parse_cbor_semantic_tag(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
std::uint64_t n;
first = parse_cbor_number( first, last, ch, n );
return parse_cbor_value( first, last, v );
}
const unsigned char*
parse_cbor_type7(
const unsigned char* first, const unsigned char* last, unsigned char ch, value& v )
{
switch( ch & 31 )
{
case 20:
v = false;
return first;
case 21:
v = true;
return first;
case 22:
v = nullptr;
return first;
case 26:
{
ensure( 4, first, last );
float w = boost::endian::endian_load<float, 4, boost::endian::order::big>( first );
first += 4;
v = w;
return first;
}
case 27:
{
ensure( 8, first, last );
double w = boost::endian::endian_load<double, 8, boost::endian::order::big>( first );
first += 8;
v = w;
return first;
}
default:
throw_format_error( "Invalid minor type for major type 7" );
}
}
const unsigned char*
parse_cbor_value( const unsigned char* first, const unsigned char* last, value& v )
{
ensure( 1, first, last );
const unsigned char ch = *first++;
switch( ch >> 5 )
{
case 0:
return parse_cbor_unsigned( first, last, ch, v );
case 1:
return parse_cbor_signed( first, last, ch, v );
case 2:
throw_format_error( "Binary strings aren't supported" );
case 3:
return parse_cbor_string( first, last, ch, v );
case 4:
return parse_cbor_array( first, last, ch, v );
case 5:
return parse_cbor_object( first, last, ch, v );
case 6:
return parse_cbor_semantic_tag( first, last, ch, v );
case 7:
return parse_cbor_type7( first, last, ch, v );
default:
BOOST_JSON_UNREACHABLE();
}
}
int
main(int argc, const char** argv)
{
if( argc != 2 )
{
std::cerr << "Usage: cbor FILE_NAME\n";
return EXIT_FAILURE;
}
std::ifstream is(argv[1]);
is.exceptions(std::ios::badbit);
const value jv = parse(is);
std::vector<unsigned char> out;
serialize_cbor_value( jv, out );
value jv2;
parse_cbor_value( out.data(), out.data() + out.size(), jv2 );
if( jv != jv2 )
{
std::cerr << "Roundtrip check failed\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}