DAW JSON Link
daw_json_parse_name.h
Go to the documentation of this file.
1 // Copyright (c) Darrell Wright
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 // Official repository: https://github.com/beached/daw_json_link
7 //
8 
9 #pragma once
10 
11 #include "version.h"
12 
13 #include "daw_json_assert.h"
14 #include "daw_json_find_result.h"
17 
18 #include <daw/daw_string_view.h>
19 
20 #include <cstddef>
21 #include <daw/stdinc/data_access.h>
22 #include <daw/stdinc/range_access.h>
23 #include <limits>
24 
25 namespace daw::json {
26  inline namespace DAW_JSON_VER {
27  namespace json_details {
28  namespace name::name_parser {
29  /*
30  * end of string " -> name value separating : -> any white space
31  * the string can be escaped too
32  */
33  template<typename ParseState>
34  static constexpr void trim_end_of_name( ParseState &parse_state ) {
35  parse_state.trim_left( );
36  // TODO: should we check for end
37  daw_json_assert_weak( parse_state.front_checked( ) == ':',
38  ErrorReason::InvalidMemberName, parse_state );
39  parse_state.remove_prefix( );
40  parse_state.trim_left( );
41  }
42 
43  template<typename ParseState>
44  [[nodiscard]] DAW_ATTRIB_INLINE static constexpr daw::string_view
45  parse_nq( ParseState &parse_state ) {
46  if constexpr( ParseState::allow_escaped_names ) {
47  auto r = skip_string_nq( parse_state );
48  trim_end_of_name( parse_state );
49  return daw::string_view( std::data( r ), std::size( r ) );
50  } else {
51  char const *const ptr = parse_state.first;
52  if constexpr( ParseState::is_unchecked_input ) {
53  parse_state.template move_to_next_of_unchecked<'"'>( );
54  } else {
55  parse_state.template move_to_next_of_checked<'"'>( );
56  }
57  daw_json_assert_weak( parse_state.is_quotes_checked( ) and
58  *std::prev( parse_state.first ) != '\\',
59  ErrorReason::InvalidString, parse_state );
60  auto result = daw::string_view( ptr, parse_state.first );
61  parse_state.remove_prefix( );
62  trim_end_of_name( parse_state );
63  return result;
64  }
65  }
66  } // namespace name::name_parser
67 
68  struct pop_json_path_result {
69  daw::string_view current{ };
70  char found_char = 0;
71  };
72  // Paths are specified with dot separators, if the name has a dot in it,
73  // it must be escaped
74  // memberA.memberB.member\.C has 3 parts['memberA', 'memberB', 'member.C']
75  [[nodiscard]] static constexpr pop_json_path_result
76  pop_json_path( daw::string_view &path ) {
77  auto result = pop_json_path_result{ };
78  if( path.empty( ) ) {
79  return result;
80  }
81  if( path.front( ) == '.' ) {
82  path.remove_prefix( );
83  }
84  result.current =
85  path.pop_front_until( [&, in_escape = false]( char c ) mutable {
86  if( in_escape ) {
87  in_escape = false;
88  return false;
89  }
90  switch( c ) {
91  case '\\':
92  in_escape = true;
93  return false;
94  case '.':
95  case '[':
96  case ']':
97  result.found_char = c;
98  return true;
99  default:
100  return false;
101  }
102  } );
103  return result;
104  }
105 
106  [[nodiscard]] static constexpr bool
107  json_path_compare( daw::string_view json_path_item,
108  daw::string_view member_name ) {
109  if( json_path_item.front( ) == '\\' ) {
110  json_path_item.remove_prefix( );
111  }
112  while( not json_path_item.empty( ) and not member_name.empty( ) ) {
113  if( json_path_item.front( ) != member_name.front( ) ) {
114  return false;
115  }
116  json_path_item.remove_prefix( );
117  if( not json_path_item.empty( ) and
118  json_path_item.front( ) == '\\' ) {
119  json_path_item.remove_prefix( );
120  }
121  member_name.remove_prefix( );
122  }
123  return std::size( json_path_item ) == std::size( member_name );
124  }
125 
126  template<typename Result, typename ForwardIterator>
127  constexpr Result parse_unsigned_int( ForwardIterator first,
128  ForwardIterator last ) {
129  std::size_t count = std::numeric_limits<Result>::digits;
130 
131  daw_json_ensure( '-' != *first, ErrorReason::InvalidNumber );
132 
133  Result result = 0;
134  for( ; first != last and count > 0; ++first, --count ) {
135  result *= static_cast<Result>( 10 );
136  Result val =
137  static_cast<Result>( *first ) - static_cast<Result>( '0' );
138  result += val;
139  }
140  daw_json_ensure( first == last, ErrorReason::InvalidNumber );
141  return result;
142  }
143 
144  // Get the next member name
145  // Assumes that the current item in stream is a double quote
146  // Ensures that the stream is left at the position of the associated
147  // value(e.g after the colon(:) and trimmed)
148  template<typename ParseState>
149  [[nodiscard]] DAW_ATTRIB_FLATTEN static inline constexpr daw::string_view
150  parse_name( ParseState &parse_state ) {
151  daw_json_assert_weak( parse_state.is_quotes_checked( ),
152  ErrorReason::InvalidMemberName, parse_state );
153  parse_state.remove_prefix( );
154  return name::name_parser::parse_nq( parse_state );
155  }
156 
157  template<typename ParseState>
158  static constexpr bool find_range2( ParseState &parse_state,
159  daw::string_view path ) {
160 
161  auto pop_result = pop_json_path( path );
162  while( not pop_result.current.empty( ) ) {
163  if( pop_result.found_char == ']' ) {
164  // Array Index
165  daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
166  ErrorReason::InvalidJSONPath, parse_state );
167  parse_state.remove_prefix( );
168  parse_state.trim_left_unchecked( );
169  auto idx = parse_unsigned_int<std::size_t>(
170  pop_result.current.data( ), pop_result.current.data_end( ) );
171 
172  while( idx > 0 ) {
173  --idx;
174  (void)skip_value( parse_state );
175  parse_state.trim_left_checked( );
176  if( ( idx > 0 ) & ( parse_state.has_more( ) and
177  ( parse_state.front( ) != ',' ) ) ) {
178  return false;
179  }
180  parse_state.move_next_member_or_end( );
181  }
182  } else {
183  daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
184  ErrorReason::InvalidJSONPath, parse_state );
185  parse_state.remove_prefix( );
186  parse_state.trim_left_unchecked( );
187  auto name = parse_name( parse_state );
188  while( not json_path_compare( pop_result.current, name ) ) {
189  (void)skip_value( parse_state );
190  parse_state.move_next_member_or_end( );
191  if( parse_state.empty( ) or parse_state.front( ) != '"' ) {
192  return false;
193  }
194  name = parse_name( parse_state );
195  }
196  }
197  pop_result = pop_json_path( path );
198  }
199  return true;
200  }
201 
202  template<typename ParsePolicy>
203  [[nodiscard]] static constexpr find_result<ParsePolicy>
204  find_range( daw::string_view str, daw::string_view start_path ) {
205  auto parse_state =
206  ParsePolicy( std::data( str ), daw::data_end( str ) );
207  parse_state.trim_left_checked( );
208  bool found = true;
209  if( parse_state.has_more( ) and not start_path.empty( ) ) {
210  found = find_range2( parse_state, start_path );
211  }
212  return find_result<ParsePolicy>{ parse_state, found };
213  }
214 
215  template<typename ParsePolicy, typename Allocator>
216  [[nodiscard]] static constexpr find_result<ParsePolicy>
217  find_range( daw::string_view str, daw::string_view start_path,
218  Allocator &alloc ) {
219  static_assert(
220  std::is_same_v<char const *, typename ParsePolicy::iterator>,
221  "Only char const * ranges are currently supported" );
222  auto parse_state = ParsePolicy::with_allocator(
223  std::data( str ), daw::data_end( str ), alloc );
224  parse_state.trim_left_checked( );
225  if( parse_state.has_more( ) and not start_path.empty( ) ) {
226  if( not find_range2( parse_state, start_path ) ) {
227  return find_result<ParsePolicy>{ parse_state, false };
228  }
229  }
230  return find_result<ParsePolicy>{ parse_state, true };
231  }
232  } // namespace json_details
233  } // namespace DAW_JSON_VER
234 } // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Assert that Bool is true when in Checked Input mode If false pass rest of args to daw_json_error.
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:25