@ -38,7 +38,7 @@
# include <cstring>
# include <ostream>
# include <sstream>
# include "string_view.h"
# include <string_view>
# include "mapbox/variant.hpp"
@ -92,7 +92,7 @@ struct bt_u64 { uint64_t val; explicit bt_u64(uint64_t val) : val{val} {} };
/// Recursive generic type that can fully represent everything valid for a BT serialization.
using bt_value = mapbox : : util : : variant <
std : : string ,
string_view ,
std : : st ring_view ,
int64_t ,
bt_u64 ,
mapbox : : util : : recursive_wrapper < bt_list > ,
@ -121,8 +121,10 @@ template <typename... Ts> using void_t = typename void_t_impl<Ts...>::type;
namespace detail {
/// Reads digits into an unsigned 64-bit int.
uint64_t extract_unsigned ( string_view & s ) ;
inline uint64_t extract_unsigned ( string_view & & s ) { return extract_unsigned ( s ) ; }
uint64_t extract_unsigned ( std : : string_view & s ) ;
// (Provide non-constant lvalue and rvalue ref functions so that we only accept explicit
// string_views but not implicitly converted ones)
inline uint64_t extract_unsigned ( std : : string_view & & s ) { return extract_unsigned ( s ) ; }
// Fallback base case; we only get here if none of the partial specializations below work
template < typename T , typename SFINAE = void >
@ -132,7 +134,7 @@ template <typename T, typename SFINAE = void>
struct bt_deserialize { static_assert ( ! std : : is_same < T , T > : : value , " Cannot deserialize T: unsupported type for bt deserialization " ) ; } ;
/// Checks that we aren't at the end of a string view and throws if we are.
inline void bt_need_more ( const string_view & s ) {
inline void bt_need_more ( const std : : st ring_view & s ) {
if ( s . empty ( ) )
throw bt_deserialize_invalid { " Unexpected end of string while deserializing " } ;
}
@ -143,7 +145,7 @@ union maybe_signed_int64_t { int64_t i64; uint64_t u64; };
/// iff the value is int64_t because a negative value was read. Throws an exception if the read
/// value doesn't fit in a int64_t (if negative) or a uint64_t (if positive). Removes consumed
/// characters from the string_view.
std : : pair < maybe_signed_int64_t , bool > bt_deserialize_integer ( string_view & s ) ;
std : : pair < maybe_signed_int64_t , bool > bt_deserialize_integer ( std : : st ring_view & s ) ;
/// Integer specializations
template < typename T >
@ -158,7 +160,7 @@ struct bt_serialize<T, std::enable_if_t<std::is_integral<T>::value>> {
template < typename T >
struct bt_deserialize < T , std : : enable_if_t < std : : is_integral < T > : : value > > {
void operator ( ) ( string_view & s , T & val ) {
void operator ( ) ( std : : st ring_view & s , T & val ) {
constexpr uint64_t umax = static_cast < uint64_t > ( std : : numeric_limits < T > : : max ( ) ) ;
constexpr int64_t smin = static_cast < int64_t > ( std : : numeric_limits < T > : : min ( ) ) ,
smax = static_cast < int64_t > ( std : : numeric_limits < T > : : max ( ) ) ;
@ -191,35 +193,35 @@ extern template struct bt_deserialize<uint64_t>;
template < >
struct bt_serialize < bt_u64 > { void operator ( ) ( std : : ostream & os , bt_u64 val ) { bt_serialize < uint64_t > { } ( os , val . val ) ; } } ;
template < >
struct bt_deserialize < bt_u64 > { void operator ( ) ( string_view & s , bt_u64 & val ) { bt_deserialize < uint64_t > { } ( s , val . val ) ; } } ;
struct bt_deserialize < bt_u64 > { void operator ( ) ( std : : st ring_view & s , bt_u64 & val ) { bt_deserialize < uint64_t > { } ( s , val . val ) ; } } ;
template < >
struct bt_serialize < string_view > {
void operator ( ) ( std : : ostream & os , const string_view & val ) { os < < val . size ( ) ; os . put ( ' : ' ) ; os . write ( val . data ( ) , val . size ( ) ) ; }
struct bt_serialize < std : : st ring_view > {
void operator ( ) ( std : : ostream & os , const std : : st ring_view & val ) { os < < val . size ( ) ; os . put ( ' : ' ) ; os . write ( val . data ( ) , val . size ( ) ) ; }
} ;
template < >
struct bt_deserialize < string_view > {
void operator ( ) ( string_view & s , string_view & val ) ;
struct bt_deserialize < std : : st ring_view > {
void operator ( ) ( std : : st ring_view & s , std : : string_view & val ) ;
} ;
/// String specialization
template < >
struct bt_serialize < std : : string > {
void operator ( ) ( std : : ostream & os , const std : : string & val ) { bt_serialize < string_view > { } ( os , val ) ; }
void operator ( ) ( std : : ostream & os , const std : : string & val ) { bt_serialize < std : : st ring_view > { } ( os , val ) ; }
} ;
template < >
struct bt_deserialize < std : : string > {
void operator ( ) ( string_view & s , std : : string & val ) { string_view view ; bt_deserialize < string_view > { } ( s , view ) ; val = { view . data ( ) , view . size ( ) } ; }
void operator ( ) ( std : : st ring_view & s , std : : string & val ) { std : : st ring_view view ; bt_deserialize < std : : string_view > { } ( s , view ) ; val = { view . data ( ) , view . size ( ) } ; }
} ;
/// char * and string literals -- we allow serialization for convenience, but not deserialization
template < >
struct bt_serialize < char * > {
void operator ( ) ( std : : ostream & os , const char * str ) { bt_serialize < string_view > { } ( os , { str , std : : strlen ( str ) } ) ; }
void operator ( ) ( std : : ostream & os , const char * str ) { bt_serialize < std : : st ring_view > { } ( os , { str , std : : strlen ( str ) } ) ; }
} ;
template < size_t N >
struct bt_serialize < char [ N ] > {
void operator ( ) ( std : : ostream & os , const char * str ) { bt_serialize < string_view > { } ( os , { str , N - 1 } ) ; }
void operator ( ) ( std : : ostream & os , const char * str ) { bt_serialize < std : : st ring_view > { } ( os , { str , N - 1 } ) ; }
} ;
/// Partial dict validity; we don't check the second type for serializability, that will be handled
@ -275,7 +277,7 @@ struct bt_serialize<T, std::enable_if_t<is_bt_input_dict_container<T>::value>> {
template < typename T >
struct bt_deserialize < T , std : : enable_if_t < is_bt_output_dict_container < T > : : value > > {
using second_type = typename T : : value_type : : second_type ;
void operator ( ) ( string_view & s , T & dict ) {
void operator ( ) ( std : : st ring_view & s , T & dict ) {
// Smallest dict is 2 bytes "de", for an empty dict.
if ( s . size ( ) < 2 ) throw bt_deserialize_invalid ( " Deserialization failed: end of string found where dict expected " ) ;
if ( s [ 0 ] ! = ' d ' ) throw bt_deserialize_invalid_type ( " Deserialization failed: expected 'd', found ' " s + s [ 0 ] + " ' " s ) ;
@ -330,7 +332,7 @@ struct bt_serialize<T, std::enable_if_t<is_bt_input_list_container<T>::value>> {
template < typename T >
struct bt_deserialize < T , std : : enable_if_t < is_bt_output_list_container < T > : : value > > {
using value_type = typename T : : value_type ;
void operator ( ) ( string_view & s , T & list ) {
void operator ( ) ( std : : st ring_view & s , T & list ) {
// Smallest list is 2 bytes "le", for an empty list.
if ( s . size ( ) < 2 ) throw bt_deserialize_invalid ( " Deserialization failed: end of string found where list expected " ) ;
if ( s [ 0 ] ! = ' l ' ) throw bt_deserialize_invalid_type ( " Deserialization failed: expected 'l', found ' " s + s [ 0 ] + " ' " s ) ;
@ -368,20 +370,20 @@ using is_bt_deserializable = std::integral_constant<bool,
// which means we reached the end without finding any variant type capable of holding the value.
template < typename SFINAE , typename Variant , typename . . . Ts >
struct bt_deserialize_try_variant_impl {
void operator ( ) ( string_view & , Variant & ) {
void operator ( ) ( std : : st ring_view & , Variant & ) {
throw bt_deserialize_invalid ( " Deserialization failed: could not deserialize value into any variant type " ) ;
}
} ;
template < typename . . . Ts , typename Variant >
void bt_deserialize_try_variant ( string_view & s , Variant & variant ) {
void bt_deserialize_try_variant ( std : : st ring_view & s , Variant & variant ) {
bt_deserialize_try_variant_impl < void , Variant , Ts . . . > { } ( s , variant ) ;
}
template < typename Variant , typename T , typename . . . Ts >
struct bt_deserialize_try_variant_impl < std : : enable_if_t < is_bt_deserializable < T > : : value > , Variant , T , Ts . . . > {
void operator ( ) ( string_view & s , Variant & variant ) {
void operator ( ) ( std : : st ring_view & s , Variant & variant ) {
if ( is_bt_output_list_container < T > : : value ? s [ 0 ] = = ' l ' :
is_bt_output_dict_container < T > : : value ? s [ 0 ] = = ' d ' :
std : : is_integral < T > : : value ? s [ 0 ] = = ' i ' :
@ -398,7 +400,7 @@ struct bt_deserialize_try_variant_impl<std::enable_if_t<is_bt_deserializable<T>:
template < typename Variant , typename T , typename . . . Ts >
struct bt_deserialize_try_variant_impl < std : : enable_if_t < ! is_bt_deserializable < T > : : value > , Variant , T , Ts . . . > {
void operator ( ) ( string_view & s , Variant & variant ) {
void operator ( ) ( std : : st ring_view & s , Variant & variant ) {
// Unsupported deserialization type, skip it
bt_deserialize_try_variant < Ts . . . > ( s , variant ) ;
}
@ -406,7 +408,7 @@ struct bt_deserialize_try_variant_impl<std::enable_if_t<!is_bt_deserializable<T>
template < >
struct bt_deserialize < bt_value , void > {
void operator ( ) ( string_view & s , bt_value & val ) ;
void operator ( ) ( std : : st ring_view & s , bt_value & val ) ;
} ;
template < typename . . . Ts >
@ -418,7 +420,7 @@ struct bt_serialize<mapbox::util::variant<Ts...>> {
template < typename . . . Ts >
struct bt_deserialize < mapbox : : util : : variant < Ts . . . > > {
void operator ( ) ( string_view & s , mapbox : : util : : variant < Ts . . . > & val ) {
void operator ( ) ( std : : st ring_view & s , mapbox : : util : : variant < Ts . . . > & val ) {
bt_deserialize_try_variant < Ts . . . > ( s , val ) ;
}
} ;
@ -435,7 +437,7 @@ struct bt_serialize<std::variant<Ts...>> {
template < typename . . . Ts >
struct bt_deserialize < std : : variant < Ts . . . > > {
void operator ( ) ( string_view & s , std : : variant < Ts . . . > & val ) {
void operator ( ) ( std : : st ring_view & s , std : : variant < Ts . . . > & val ) {
bt_deserialize_try_variant < Ts . . . > ( s , val ) ;
}
} ;
@ -501,7 +503,7 @@ std::string bt_serialize(const T &val) { return bt_serializer(val); }
/// bt_deserialize(encoded, value); // Sets value to 42
///
template < typename T , std : : enable_if_t < ! std : : is_const < T > : : value , int > = 0 >
void bt_deserialize ( string_view s , T & val ) {
void bt_deserialize ( std : : st ring_view s , T & val ) {
return detail : : bt_deserialize < T > { } ( s , val ) ;
}
@ -512,7 +514,7 @@ void bt_deserialize(string_view s, T& val) {
/// auto mylist = bt_deserialize<std::list<int>>(encoded);
///
template < typename T >
T bt_deserialize ( string_view s ) {
T bt_deserialize ( std : : st ring_view s ) {
T val ;
bt_deserialize ( s , val ) ;
return val ;
@ -528,7 +530,7 @@ T bt_deserialize(string_view s) {
/// int v = get_int<int>(val); // fails unless the encoded value was actually an integer that
/// // fits into an `int`
///
inline bt_value bt_get ( string_view s ) {
inline bt_value bt_get ( std : : st ring_view s ) {
return bt_deserialize < bt_value > ( s ) ;
}
@ -562,10 +564,10 @@ IntType get_int(const bt_value &v) {
/// memory stays valid for the lifetime of the bt_list_consumer object.
class bt_list_consumer {
protected :
string_view data ;
std : : st ring_view data ;
bt_list_consumer ( ) = default ;
public :
bt_list_consumer ( string_view data_ ) ;
bt_list_consumer ( std : : st ring_view data_ ) ;
/// Copy constructor. Making a copy copies the current position so can be used for multipass
/// iteration through a list.
@ -588,14 +590,14 @@ public:
/// Attempt to parse the next value as a string (and advance just past it). Throws if the next
/// value is not a string.
std : : string consume_string ( ) ;
string_view consume_string_view ( ) ;
std : : st ring_view consume_string_view ( ) ;
/// Attempts to parse the next value as an integer (and advance just past it). Throws if the
/// next value is not an integer.
template < typename IntType >
IntType consume_integer ( ) {
if ( ! is_integer ( ) ) throw bt_deserialize_invalid_type { " next value is not an integer " } ;
string_view next { data } ;
std : : st ring_view next { data } ;
IntType ret ;
detail : : bt_deserialize < IntType > { } ( next , ret ) ;
data = next ;
@ -616,7 +618,7 @@ public:
template < typename T >
void consume_list ( T & list ) {
if ( ! is_list ( ) ) throw bt_deserialize_invalid_type { " next bt value is not a list " } ;
string_view n { data } ;
std : : st ring_view n { data } ;
detail : : bt_deserialize < T > { } ( n , list ) ;
data = n ;
}
@ -635,7 +637,7 @@ public:
template < typename T >
void consume_dict ( T & dict ) {
if ( ! is_dict ( ) ) throw bt_deserialize_invalid_type { " next bt value is not a dict " } ;
string_view n { data } ;
std : : st ring_view n { data } ;
detail : : bt_deserialize < T > { } ( n , dict ) ;
data = n ;
}
@ -647,13 +649,13 @@ public:
/// entire thing. This is recursive into both lists and dicts and likely to be quite
/// inefficient for large, nested structures (unless the values only need to be skipped but
/// aren't separately needed). This, however, does not require dynamic memory allocation.
string_view consume_list_data ( ) ;
std : : st ring_view consume_list_data ( ) ;
/// Attempts to parse the next value as a dict and returns the string_view that contains the
/// entire thing. This is recursive into both lists and dicts and likely to be quite
/// inefficient for large, nested structures (unless the values only need to be skipped but
/// aren't separately needed). This, however, does not require dynamic memory allocation.
string_view consume_dict_data ( ) ;
std : : st ring_view consume_dict_data ( ) ;
} ;
@ -661,7 +663,7 @@ public:
/// copying or allocating memory. It accesses existing memory directly and so the caller must
/// ensure that the referenced memory stays valid for the lifetime of the bt_dict_consumer object.
class bt_dict_consumer : private bt_list_consumer {
string_view key_ ;
std : : st ring_view key_ ;
/// Consume the key if not already consumed and there is a key present (rather than 'e').
/// Throws exception if what should be a key isn't a string, or if the key consumes the entire
@ -671,14 +673,14 @@ class bt_dict_consumer : private bt_list_consumer {
/// Clears the cached key and returns it. Must have already called consume_key directly or
/// indirectly via one of the `is_{...}` methods.
string_view flush_key ( ) {
string_view k ;
std : : st ring_view flush_key ( ) {
std : : st ring_view k ;
k . swap ( key_ ) ;
return k ;
}
public :
bt_dict_consumer ( string_view data_ ) ;
bt_dict_consumer ( std : : st ring_view data_ ) ;
/// Copy constructor. Making a copy copies the current position so can be used for multipass
/// iteration through a list.
@ -703,7 +705,7 @@ public:
/// all of the other consume_* methods. The value is cached whether called here or by some
/// other method; accessing it multiple times simple accesses the cache until the next value is
/// consumed.
string_view key ( ) {
std : : st ring_view key ( ) {
if ( ! consume_key ( ) )
throw bt_deserialize_invalid { " Cannot access next key: at the end of the dict " } ;
return key_ ;
@ -711,14 +713,14 @@ public:
/// Attempt to parse the next value as a string->string pair (and advance just past it). Throws
/// if the next value is not a string.
std : : pair < string_view , string_view > next_string ( ) ;
std : : pair < std : : st ring_view , std : : string_view > next_string ( ) ;
/// Attempts to parse the next value as an string->integer pair (and advance just past it).
/// Throws if the next value is not an integer.
template < typename IntType >
std : : pair < string_view , IntType > next_integer ( ) {
std : : pair < std : : st ring_view , IntType > next_integer ( ) {
if ( ! is_integer ( ) ) throw bt_deserialize_invalid_type { " next bt dict value is not an integer " } ;
std : : pair < string_view , IntType > ret ;
std : : pair < std : : st ring_view , IntType > ret ;
ret . second = bt_list_consumer : : consume_integer < IntType > ( ) ;
ret . first = flush_key ( ) ;
return ret ;
@ -729,15 +731,15 @@ public:
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
/// used).
template < typename T = bt_list >
std : : pair < string_view , T > next_list ( ) {
std : : pair < string_view , T > pair ;
std : : pair < std : : st ring_view , T > next_list ( ) {
std : : pair < std : : st ring_view , T > pair ;
pair . first = consume_list ( pair . second ) ;
return pair ;
}
/// Same as above, but takes a pre-existing list-like data type. Returns the key.
template < typename T >
string_view next_list ( T & list ) {
std : : st ring_view next_list ( T & list ) {
if ( ! is_list ( ) ) throw bt_deserialize_invalid_type { " next bt value is not a list " } ;
bt_list_consumer : : consume_list ( list ) ;
return flush_key ( ) ;
@ -748,15 +750,15 @@ public:
/// which allows alloc-free traversal, but requires parsing twice (if the contents are to be
/// used).
template < typename T = bt_dict >
std : : pair < string_view , T > next_dict ( ) {
std : : pair < string_view , T > pair ;
std : : pair < std : : st ring_view , T > next_dict ( ) {
std : : pair < std : : st ring_view , T > pair ;
pair . first = consume_dict ( pair . second ) ;
return pair ;
}
/// Same as above, but takes a pre-existing dict-like data type. Returns the key.
template < typename T >
string_view next_dict ( T & dict ) {
std : : st ring_view next_dict ( T & dict ) {
if ( ! is_dict ( ) ) throw bt_deserialize_invalid_type { " next bt value is not a dict " } ;
bt_list_consumer : : consume_dict ( dict ) ;
return flush_key ( ) ;
@ -766,25 +768,25 @@ public:
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
/// quite inefficient for large, nested structures (unless the values only need to be skipped
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
std : : pair < string_view , string_view > next_list_data ( ) {
std : : pair < std : : st ring_view , std : : string_view > next_list_data ( ) {
if ( data . size ( ) < 2 | | ! is_list ( ) ) throw bt_deserialize_invalid_type { " next bt dict value is not a list " } ;
return { flush_key ( ) , bt_list_consumer : : consume_list_data ( ) } ;
}
/// Same as next_list_data(), but wraps the value in a bt_list_consumer for convenience
std : : pair < string_view , bt_list_consumer > next_list_consumer ( ) { return next_list_data ( ) ; }
std : : pair < std : : st ring_view , bt_list_consumer > next_list_consumer ( ) { return next_list_data ( ) ; }
/// Attempts to parse the next value as a string->dict pair and returns the string_view that
/// contains the entire thing. This is recursive into both lists and dicts and likely to be
/// quite inefficient for large, nested structures (unless the values only need to be skipped
/// but aren't separately needed). This, however, does not require dynamic memory allocation.
std : : pair < string_view , string_view > next_dict_data ( ) {
std : : pair < std : : st ring_view , std : : string_view > next_dict_data ( ) {
if ( data . size ( ) < 2 | | ! is_dict ( ) ) throw bt_deserialize_invalid_type { " next bt dict value is not a dict " } ;
return { flush_key ( ) , bt_list_consumer : : consume_dict_data ( ) } ;
}
/// Same as next_dict_data(), but wraps the value in a bt_dict_consumer for convenience
std : : pair < string_view , bt_dict_consumer > next_dict_consumer ( ) { return next_dict_data ( ) ; }
std : : pair < std : : st ring_view , bt_dict_consumer > next_dict_consumer ( ) { return next_dict_data ( ) ; }
/// Skips ahead until we find the first key >= the given key or reach the end of the dict.
/// Returns true if we found an exact match, false if we reached some greater value or the end.
@ -799,7 +801,7 @@ public:
/// - this is irreversible; you cannot returned to skipped values without reparsing. (You *can*
/// however, make a copy of the bt_dict_consumer before calling and use the copy to return to
/// the pre-skipped position).
bool skip_until ( string_view find ) {
bool skip_until ( std : : st ring_view find ) {
while ( consume_key ( ) & & key_ < find ) {
flush_key ( ) ;
skip_value ( ) ;
@ -834,8 +836,8 @@ public:
template < typename T >
void consume_dict ( T & dict ) { next_dict ( dict ) ; }
string_view consume_list_data ( ) { return next_list_data ( ) . second ; }
string_view consume_dict_data ( ) { return next_dict_data ( ) . second ; }
std : : st ring_view consume_list_data ( ) { return next_list_data ( ) . second ; }
std : : st ring_view consume_dict_data ( ) { return next_dict_data ( ) . second ; }
bt_list_consumer consume_list_consumer ( ) { return consume_list_data ( ) ; }
bt_dict_consumer consume_dict_consumer ( ) { return consume_dict_data ( ) ; }