DAW JSON Link
Loading...
Searching...
No Matches
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"
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
25namespace 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:20