eedb/share/include/tao/json/pointer.hh
2017-02-26 09:32:45 +01:00

301 lines
8.3 KiB
C++

// Copyright (c) 2016 Dr. Colin Hirsch and Daniel Frey
// Please see LICENSE for license or visit https://github.com/taocpp/json/
#ifndef TAOCPP_JSON_INCLUDE_POINTER_HH
#define TAOCPP_JSON_INCLUDE_POINTER_HH
#include <cstdint>
#include <string>
#include <utility>
#include <stdexcept>
#include <vector>
#include "external/operators.hpp"
namespace tao
{
namespace json
{
namespace internal
{
std::size_t token_to_index( const std::string & key );
} // internal
// RFC 6901
class token
{
private:
std::size_t m_index;
std::string m_key;
public:
explicit token( const std::string & key )
: m_index( internal::token_to_index( key ) ),
m_key( key )
{ }
explicit token( std::string && key )
: m_index( internal::token_to_index( key ) ),
m_key( std::move( key ) )
{ }
token( const token & ) = default;
token( token && v ) noexcept
: m_index( v.m_index ),
m_key( std::move( v.m_key ) )
{ }
token & operator= ( const token & ) = default;
token & operator= ( token && v ) noexcept
{
m_index = v.m_index;
m_key = std::move( v.m_key );
return * this;
}
const std::string & key() const
{
return m_key;
}
std::size_t index() const
{
if ( m_index == std::string::npos ) {
throw std::invalid_argument( "unable to resolve JSON Pointer, invalid token for array access '" + m_key + '\'' );
}
return m_index;
}
friend bool operator==( const token & lhs, const token & rhs )
{
return lhs.m_key == rhs.m_key;
}
friend bool operator<( const token & lhs, const token & rhs )
{
return lhs.m_key < rhs.m_key;
}
};
class pointer
: operators::totally_ordered< pointer >
{
private:
std::vector< token > m_tokens;
static std::vector< token > parse( const std::string & v )
{
std::vector< token > result;
if ( ! v.empty() ) {
const char * p = v.data();
const char * const e = p + v.size();
if ( *p++ != '/' ) {
throw std::invalid_argument( "invalid JSON Pointer value, must be empty or begin with '/'" );
}
std::string token;
while ( p != e ) {
const char c = *p++;
switch( c ) {
case '~':
if ( p != e ) {
switch ( *p++ ) {
case '0':
token += '~';
continue;
case '1':
token += '/';
continue;
}
}
throw std::invalid_argument( "invalid JSON Pointer escape sequence, '~' must be followed by '0' or '1'" );
case '/':
result.emplace_back( std::move( token ) );
token.clear();
continue;
default:
token += c;
}
}
result.emplace_back( std::move( token ) );
}
return result;
}
public:
pointer() = default;
pointer( const pointer & ) = default;
pointer( pointer && p ) noexcept
: m_tokens( std::move( p.m_tokens ) )
{ }
explicit pointer( const std::string & v )
: m_tokens( parse( v ) )
{ }
pointer & operator= ( const pointer & ) = default;
pointer & operator= ( pointer && p ) noexcept
{
m_tokens = std::move( p.m_tokens );
return *this;
}
pointer & operator= ( const std::string & v )
{
m_tokens = parse( v );
return *this;
}
explicit operator bool() const noexcept
{
return ! m_tokens.empty();
}
std::vector< token >::const_iterator begin() const noexcept
{
return m_tokens.begin();
}
std::vector< token >::const_iterator end() const noexcept
{
return m_tokens.end();
}
void push_back( const std::string & v )
{
m_tokens.push_back( token( v ) );
}
void push_back( std::string && v )
{
m_tokens.push_back( token( std::move( v ) ) );
}
pointer & operator+= ( const std::string & v )
{
push_back( v );
return * this;
}
pointer & operator+= ( std::string && v )
{
push_back( std::move( v ) );
return * this;
}
friend bool operator== ( const pointer & lhs, const pointer & rhs ) noexcept
{
return lhs.m_tokens == rhs.m_tokens;
}
friend bool operator< ( const pointer & lhs, const pointer & rhs ) noexcept
{
return lhs.m_tokens < rhs.m_tokens;
}
};
inline pointer operator+ ( const pointer & p, const std::string & v )
{
pointer nrv( p );
nrv += v;
return nrv;
}
inline pointer operator+ ( const pointer & p, std::string && v )
{
pointer nrv( p );
nrv += std::move( v );
return nrv;
}
namespace internal
{
inline std::size_t token_to_index( const std::string & key )
{
if ( ! key.empty() && key.size() <= 20 ) {
if ( key == "0" ) {
return 0;
}
else if ( ( key[ 0 ] != '0' ) && ( key.find_first_not_of( "0123456789" ) == std::string::npos ) ) {
if ( key.size() < 20 || key < "18446744073709551616" ) {
return std::stoull( key );
}
}
}
return std::string::npos;
}
inline std::string tokens_to_string( std::vector< token >::const_iterator it, const std::vector< token >::const_iterator & end )
{
std::string result;
while ( it != end ) {
result += '/';
for ( const char c : it->key() ) {
switch ( c ) {
case '~':
result += "~0";
break;
case '/':
result += "~1";
break;
default:
result += c;
}
}
++it;
}
return result;
}
inline std::string to_string( const pointer & p )
{
return tokens_to_string( p.begin(), p.end() );
}
inline std::runtime_error invalid_type( const std::vector< token >::const_iterator & begin, const std::vector< token >::const_iterator & end )
{
return std::runtime_error( "unable to resolve JSON Pointer '" + tokens_to_string( begin, end ) + "' -- value type is neither 'object' nor 'array'" );
}
template< typename T >
T & pointer_at( T * v, const std::vector< token >::const_iterator & begin, const std::vector< token >::const_iterator & end )
{
auto it = begin;
while ( it != end ) {
switch ( v->type() ) {
case type::ARRAY:
v = & v->at( it->index() );
break;
case type::OBJECT:
v = & v->at( it->key() );
break;
default:
throw invalid_type( begin, std::next( it ) );
}
++it;
}
return * v;
}
} // internal
inline namespace literals
{
inline pointer operator"" _json_pointer( const char * data, const std::size_t size )
{
return pointer( { data, size } );
}
} // literals
} // json
} // tao
#endif