-
-
Save pdimov/bcdaa4fef606fdcd0f21195a79bf8648 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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