Skip to content

Instantly share code, notes, and snippets.

@pdimov
Last active October 3, 2021 05:43
Show Gist options
  • Save pdimov/bcdaa4fef606fdcd0f21195a79bf8648 to your computer and use it in GitHub Desktop.
Save pdimov/bcdaa4fef606fdcd0f21195a79bf8648 to your computer and use it in GitHub Desktop.
#include <boost/json.hpp>
#include <boost/json/basic_parser_impl.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/mp11.hpp>
#include <boost/describe.hpp>
// handler_base
#define FAIL(ec) static constexpr auto loc = BOOST_CURRENT_LOCATION; ec.assign( EINVAL, boost::system::generic_category(), &loc ); return false;
class handler;
struct handler_base
{
virtual ~handler_base() = default;
virtual boost::shared_ptr<handler_base> get_parent() const { return {}; }
virtual void set_parent( handler* /*ph*/, boost::shared_ptr<handler_base> /*pp*/ ) {}
virtual bool on_object_begin( boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_object_end( std::size_t, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_array_begin( boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_array_end( std::size_t, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_key_part( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_key( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_string_part( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_string( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_number_part( boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_int64( std::int64_t, boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_uint64( std::uint64_t, boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_double( double, boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_bool( bool, boost::json::error_code& ec ) { FAIL( ec ); }
virtual bool on_null( boost::json::error_code& ec ) { FAIL( ec ); }
virtual void signal_value() {}
virtual void signal_end() {}
};
// handler
class handler
{
private:
boost::shared_ptr<handler_base> ph_{ new handler_base };
public:
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);
public:
void push_handler( boost::shared_ptr<handler_base> p )
{
p->set_parent( this, ph_ );
ph_ = p;
}
void pop_handler()
{
ph_ = ph_->get_parent();
}
bool on_document_begin( boost::json::error_code& )
{
return true;
}
bool on_document_end( boost::json::error_code& )
{
ph_.reset( new handler_base );
return true;
}
bool on_object_begin( boost::json::error_code& ec ) { return ph_->on_object_begin( ec ); }
bool on_object_end( std::size_t n, boost::json::error_code& ec ) { return ph_->on_object_end( n, ec ); }
bool on_array_begin( boost::json::error_code& ec ) { return ph_->on_array_begin( ec ); }
bool on_array_end( std::size_t n, boost::json::error_code& ec ) { return ph_->on_array_end( n, ec ); }
bool on_key_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) { return ph_->on_key_part( sv, n, ec ); }
bool on_key( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) { return ph_->on_key( sv, n, ec ); }
bool on_string_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) { return ph_->on_string_part( sv, n, ec ); }
bool on_string( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) { return ph_->on_string( sv, n, ec ); }
bool on_number_part( boost::json::string_view sv, boost::json::error_code& ec ) { return ph_->on_number_part( sv, ec ); }
bool on_int64( std::int64_t v, boost::json::string_view sv, boost::json::error_code& ec ) { return ph_->on_int64( v, sv, ec ); }
bool on_uint64( std::uint64_t v, boost::json::string_view sv, boost::json::error_code& ec ) { return ph_->on_uint64( v, sv, ec ); }
bool on_double( double v, boost::json::string_view sv, boost::json::error_code& ec ) { return ph_->on_double( v, sv, ec ); }
bool on_bool( bool v, boost::json::error_code& ec ) { return ph_->on_bool( v, ec ); }
bool on_null( boost::json::error_code& ec ) { return ph_->on_null( ec ); }
bool on_comment_part(boost::json::string_view, boost::json::error_code&) { return true; }
bool on_comment(boost::json::string_view, boost::json::error_code&) { return true; }
};
// integral_handler
template<class V> class integral_handler: handler_base
{
private:
boost::shared_ptr<handler_base> parent_;
V * value_;
public:
explicit integral_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* /*ph*/, boost::shared_ptr<handler_base> pp )
{
parent_ = pp;
}
bool on_number_part( boost::json::string_view, boost::json::error_code& )
{
return true;
}
bool on_int64( std::int64_t v, boost::json::string_view, boost::json::error_code& ec )
{
*value_ = static_cast<V>( v );
parent_->signal_value();
return true;
}
bool on_uint64( std::uint64_t v, boost::json::string_view, boost::json::error_code& ec )
{
*value_ = static_cast<V>( v );
parent_->signal_value();
return true;
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
};
// floating_point_handler
template<class V> struct floating_point_handler: handler_base
{
private:
boost::shared_ptr<handler_base> parent_;
V * value_;
public:
explicit floating_point_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* /*ph*/, boost::shared_ptr<handler_base> pp )
{
parent_ = pp;
}
bool on_number_part( boost::json::string_view, boost::json::error_code& )
{
return true;
}
bool on_int64( std::int64_t v, boost::json::string_view, boost::json::error_code& ec )
{
*value_ = static_cast<V>( v );
parent_->signal_value();
return true;
}
bool on_uint64( std::uint64_t v, boost::json::string_view, boost::json::error_code& ec )
{
*value_ = static_cast<V>( v );
parent_->signal_value();
return true;
}
bool on_double( double v, boost::json::string_view, boost::json::error_code& ec )
{
*value_ = static_cast<V>( v );
parent_->signal_value();
return true;
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
};
// string_handler
template<class V> struct string_handler: handler_base
{
private:
boost::shared_ptr<handler_base> parent_;
V * value_;
public:
explicit string_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* /*ph*/, boost::shared_ptr<handler_base> pp )
{
parent_ = pp;
}
bool on_string_part( boost::json::string_view sv, std::size_t, boost::json::error_code& )
{
value_->append( sv.begin(), sv.end() );
return true;
}
bool on_string( boost::json::string_view sv, std::size_t, boost::json::error_code& )
{
value_->append( sv.begin(), sv.end() );
parent_->signal_value();
return true;
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
};
// bool_handler
template<class V> struct bool_handler: handler_base
{
private:
boost::shared_ptr<handler_base> parent_;
V * value_;
public:
explicit bool_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* /*ph*/, boost::shared_ptr<handler_base> pp )
{
parent_ = pp;
}
bool on_bool( bool v, boost::json::error_code& )
{
*value_ = v;
parent_->signal_value();
return true;
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
};
// forward declarations
template<class V> struct sequence_handler;
template<class V> struct map_handler;
template<class V> struct described_struct_handler;
struct unknown_type_handler {};
// is_string
template<class T> struct is_string: std::is_same<T, std::string>
{
};
// is_map
template<class T, class E = void> struct is_map: std::false_type
{
};
template<class T> struct is_map<T, decltype( std::declval<T&>().emplace( std::declval<std::string>(), std::declval<typename T::mapped_type>() ), (void)0 )>: std::true_type
{
};
// is_sequence
template<class T, class E = void> struct is_sequence: std::false_type
{
};
template<class T> struct is_sequence<T, decltype( std::declval<T&>().push_back( std::declval<typename T::value_type>() ), (void)0 )>: std::true_type
{
};
// is_described_struct
template<class T, class E = void> struct is_described_struct: std::false_type
{
};
template<class T> struct is_described_struct<T, decltype((void)boost::describe::describe_members<T, boost::describe::mod_any_access>())>: std::true_type
{
};
// get_handler
template<class V> using get_handler = boost::mp11::mp_cond<
std::is_same<V, bool>, bool_handler<V>,
std::is_integral<V>, integral_handler<V>,
std::is_floating_point<V>, floating_point_handler<V>,
is_string<V>, string_handler<V>,
is_map<V>, map_handler<V>,
is_sequence<V>, sequence_handler<V>,
is_described_struct<V>, described_struct_handler<V>,
boost::mp11::mp_true, unknown_type_handler
>;
// sequence_handler
template<class V> struct sequence_handler: handler_base
{
private:
handler * handler_ = {};
boost::shared_ptr<handler_base> parent_;
V * value_;
using value_type = typename V::value_type;
using next_handler_type = get_handler<value_type>;
value_type next_value_ = {};
public:
explicit sequence_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* ph, boost::shared_ptr<handler_base> pp )
{
handler_ = ph;
parent_ = pp;
}
bool on_array_begin( boost::json::error_code& )
{
boost::shared_ptr<handler_base> p2( new next_handler_type( &next_value_ ) );
handler_->push_handler( p2 );
return true;
}
void signal_value()
{
value_->push_back( std::move( next_value_ ) );
next_value_ = {};
}
void signal_end()
{
handler_->pop_handler();
parent_->signal_value();
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
};
// map_handler
template<class V> struct map_handler: handler_base
{
private:
handler * handler_ = {};
boost::shared_ptr<handler_base> parent_;
V * value_;
using mapped_type = typename V::mapped_type;
using next_handler_type = get_handler<mapped_type>;
std::string key_;
mapped_type next_value_ = {};
public:
explicit map_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* ph, boost::shared_ptr<handler_base> pp )
{
handler_ = ph;
parent_ = pp;
}
bool on_object_begin( boost::json::error_code& )
{
return true;
}
bool on_key_part( boost::json::string_view sv, std::size_t, boost::json::error_code& )
{
key_.append( sv.data(), sv.size() );
return true;
}
bool on_key( boost::json::string_view sv, std::size_t, boost::json::error_code& )
{
key_.append( sv.data(), sv.size() );
boost::shared_ptr<handler_base> p2( new next_handler_type( &next_value_ ) );
handler_->push_handler( p2 );
return true;
}
void signal_value()
{
value_->emplace( std::move( key_ ), std::move( next_value_ ) );
key_ = {};
next_value_ = {};
handler_->pop_handler();
}
void signal_end()
{
handler_->pop_handler();
parent_->signal_value();
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
bool on_object_end( std::size_t, boost::json::error_code& )
{
parent_->signal_value();
return true;
}
};
// described_struct_handler
template<class V> struct described_struct_handler: handler_base
{
private:
handler * handler_ = {};
boost::shared_ptr<handler_base> parent_;
V * value_;
std::string key_;
public:
explicit described_struct_handler( V* v ): value_( v )
{
}
boost::shared_ptr<handler_base> get_parent() const
{
return parent_;
}
void set_parent( handler* ph, boost::shared_ptr<handler_base> pp )
{
handler_ = ph;
parent_ = pp;
}
bool on_object_begin( boost::json::error_code& )
{
return true;
}
bool on_key_part( boost::json::string_view sv, std::size_t, boost::json::error_code& )
{
key_.append( sv.data(), sv.size() );
return true;
}
bool on_key( boost::json::string_view sv, std::size_t, boost::json::error_code& ec )
{
key_.append( sv.data(), sv.size() );
boost::shared_ptr<handler_base> p2;
using L = boost::describe::describe_members<V, boost::describe::mod_public>;
boost::mp11::mp_for_each<L>([&](auto D){
if( key_ == D.name )
{
auto * pm = &(value_->*D.pointer);
using value_type = std::remove_pointer_t< decltype(pm) >;
using handler_type = get_handler<value_type>;
p2.reset( new handler_type( pm ) );
}
});
if( p2 )
{
handler_->push_handler( p2 );
return true;
}
else
{
FAIL(ec)
}
}
void signal_value()
{
key_ = {};
handler_->pop_handler();
}
void signal_end()
{
handler_->pop_handler();
parent_->signal_value();
}
bool on_array_end( std::size_t, boost::json::error_code& )
{
parent_->signal_end();
return true;
}
bool on_object_end( std::size_t, boost::json::error_code& )
{
parent_->signal_value();
return true;
}
};
// parse_into
template<class V> void parse_into( V& v, boost::json::string_view sv, boost::json::error_code& ec )
{
using handler_type = get_handler<V>;
boost::json::basic_parser<handler> p_( {} );
handler& h = p_.handler();
boost::shared_ptr<handler_base> p2( new handler_type( &v ) );
h.push_handler( p2 );
std::size_t n = p_.write_some( false, sv.data(), sv.size(), ec );
if( !ec && n < sv.size() )
{
ec = boost::json::error::extra_data;
}
}
#include <iostream>
#include <fstream>
#include <iterator>
#include <chrono>
struct coordinate
{
double x{}, y{}, z{};
std::string name;
};
BOOST_DESCRIBE_STRUCT(coordinate, (), (x, y, z, name))
struct coordinates
{
std::vector<coordinate> coordinates;
std::string info;
};
BOOST_DESCRIBE_STRUCT(coordinates, (), (coordinates, info))
using namespace std::chrono_literals;
int main()
{
std::ifstream is( "/tmp/1.json" );
std::string json( std::istreambuf_iterator<char>( is ), std::istreambuf_iterator<char>{} );
std::cout << "1.json: " << json.size() << " bytes\n";
{
auto tp1 = std::chrono::steady_clock::now();
boost::json::value jv = boost::json::parse( json );
auto tp2 = std::chrono::steady_clock::now();
std::cout << "boost::json::parse: " << (tp2 - tp1) / 1ms << " ms\n";
auto x = 0.0, y = 0.0, z = 0.0;
auto len = 0;
auto &obj = jv.get_object();
for( auto& v: obj["coordinates"].get_array() )
{
++len;
auto& coord = v.get_object();
x += coord["x"].get_double();
y += coord["y"].get_double();
z += coord["z"].get_double();
}
x /= len;
y /= len;
z /= len;
auto tp3 = std::chrono::steady_clock::now();
std::cout << " x: " << x << ", y: " << y << ", z: " << z << ": " << (tp3 - tp2) / 1ms << " ms\n";
}
{
auto tp1 = std::chrono::steady_clock::now();
coordinates w;
w.coordinates.reserve( 524288 );
boost::json::error_code ec;
parse_into( w, json, ec );
if( ec.failed() )
{
std::cout << "Error: " << ec.what() << std::endl;
}
auto tp2 = std::chrono::steady_clock::now();
std::cout << "parse_into: " << (tp2 - tp1) / 1ms << " ms\n";
auto x = 0.0, y = 0.0, z = 0.0;
auto len = 0;
for( auto const& v: w.coordinates )
{
x += v.x;
y += v.y;
z += v.z;
++len;
}
x /= len;
y /= len;
z /= len;
auto tp3 = std::chrono::steady_clock::now();
std::cout << " x: " << x << ", y: " << y << ", z: " << z << ": " << (tp3 - tp2) / 1ms << " ms\n";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment