301 lines
8.3 KiB
C++
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
|