DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_policy_cpp_comments.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
12
17
18#include <daw/daw_attributes.h>
19#include <daw/daw_constant.h>
20#include <daw/daw_likely.h>
21#include <daw/daw_not_null.h>
22#include <daw/daw_traits.h>
23
24namespace daw::json {
25 inline namespace DAW_JSON_VER {
26 /***
27 * Allow skipping C++ style comments in JSON document
28 */
30 template<typename ParseState>
31 DAW_ATTRIB_FLATINLINE static constexpr void
32 skip_comments_unchecked( ParseState &parse_state ) {
33 while( parse_state.front( ) == '/' ) {
34 switch( *( parse_state.first + 1 ) ) {
35 case '/':
36 parse_state.template move_to_next_of_unchecked<'\n'>( );
37 parse_state.remove_prefix( );
38 break;
39 case '*':
40 parse_state.remove_prefix( 2 );
41 while( true ) {
42 parse_state.template move_to_next_of_unchecked<'*'>( );
43 parse_state.remove_prefix( );
44 if( parse_state.front( ) == '/' ) {
45 parse_state.remove_prefix( );
46 break;
47 }
48 // front( ) may be the '*' of the closing "*/", e.g. "**/"
49 }
50 break;
51 default:
52 return;
53 }
54 }
55 }
56
57 template<typename ParseState>
58 DAW_ATTRIB_FLATINLINE static constexpr void
59 skip_comments_checked( ParseState &parse_state ) {
60 while( parse_state.has_more( ) and parse_state.front( ) == '/' ) {
61 // A comment needs a second character, a lone '/' is left for the
62 // parser to reject
63 if( not ParseState::is_zero_terminated_string and
64 parse_state.size( ) < 2 ) {
65 return;
66 }
67 switch( *( parse_state.first + 1 ) ) {
68 case '/':
69 parse_state.template move_to_next_of_checked<'\n'>( );
70 if( parse_state.has_more( ) ) {
71 parse_state.remove_prefix( );
72 }
73 break;
74 case '*':
75 parse_state.remove_prefix( 2 );
76 while( parse_state.has_more( ) ) {
77 parse_state.template move_to_next_of_checked<'*'>( );
78 if( parse_state.has_more( ) ) {
79 parse_state.remove_prefix( );
80 }
81 if( not parse_state.has_more( ) ) {
82 break;
83 } else if( parse_state.front( ) == '/' ) {
84 parse_state.remove_prefix( );
85 break;
86 }
87 // front( ) may be the '*' of the closing "*/", e.g. "**/"
88 }
89 break;
90 default:
91 return;
92 }
93 }
94 }
95
96 template<typename ParseState>
97 DAW_ATTRIB_FLATINLINE static constexpr void
98 skip_comments( ParseState &parse_state ) {
99 if constexpr( ParseState::is_unchecked_input ) {
100 skip_comments_unchecked( parse_state );
101 } else {
102 skip_comments_checked( parse_state );
103 }
104 }
105
106 public:
107 template<typename ParseState>
108 DAW_ATTRIB_FLATINLINE static constexpr void
109 trim_left_checked( ParseState &parse_state ) {
110 skip_comments_checked( parse_state );
111 while( parse_state.has_more( ) and parse_state.is_space_unchecked( ) ) {
112 parse_state.remove_prefix( );
113 skip_comments_checked( parse_state );
114 }
115 }
116
117 template<typename ParseState>
118 DAW_ATTRIB_FLATINLINE static constexpr void
119 trim_left_unchecked( ParseState &parse_state ) {
120 skip_comments_unchecked( parse_state );
121 while( parse_state.is_space_unchecked( ) ) {
122 parse_state.remove_prefix( );
123 // Comments can follow whitespace
124 skip_comments_unchecked( parse_state );
125 }
126 }
127
128 template<typename ParseState>
129 DAW_ATTRIB_FLATINLINE static constexpr void
130 move_next_member_unchecked( ParseState &parse_state ) {
131 parse_state.move_next_member_or_end_unchecked( );
132 }
133
134 template<char... keys, typename ParseState>
135 DAW_ATTRIB_FLATINLINE static constexpr void
136 move_to_next_of( ParseState &parse_state ) {
137 skip_comments( parse_state );
138 while( true ) {
139 // Check before reading front( ), skip_comments can reach the end
140 daw_json_assert_weak( parse_state.has_more( ),
141 ErrorReason::UnexpectedEndOfData,
142 parse_state );
143 if( parse_policy_details::in<keys...>( parse_state.front( ) ) ) {
144 break;
145 }
146 parse_state.remove_prefix( );
147 skip_comments( parse_state );
148 }
149 }
150
151 DAW_ATTRIB_FLATINLINE static constexpr bool is_literal_end( char c ) {
152 return c == '\0' or c == ',' or c == ']' or c == '}' or c == '#';
153 }
154
155 template<json_details::SkipBracketedType BracketedType,
156 typename ParseState>
157 DAW_ATTRIB_FLATINLINE static constexpr ParseState
158 skip_bracketed_item_checked( ParseState &parse_state ) {
159 DAW_CPP23_STATIC_LOCAL constexpr char PrimLeft =
160 BracketedType == json_details::SkipBracketedType::Class ? '{' : '[';
161 using PrimRight = daw::constant<PrimLeft == '{' ? '}' : ']'>;
162 using SecLeft = daw::constant<PrimLeft == '{' ? '[' : '{'>;
163 using SecRight = daw::constant<SecLeft::value == '{' ? '}' : ']'>;
164 // Not checking for Left as it is required to be skipped already
165 auto result = parse_state;
166 std::size_t cnt = 0;
167 std::uint32_t prime_bracket_count = 1;
168 std::uint32_t second_bracket_count = 0;
169 auto ptr_first = parse_state.first;
170 auto const ptr_last = parse_state.last;
171 // A null range is a logic error, not bad input
172 daw_json_dbg_ensure( ptr_first != nullptr and ptr_last != nullptr,
173 ErrorReason::InvalidNull );
174 if( DAW_UNLIKELY( ptr_first >= ptr_last ) ) {
175 return result;
176 }
177 if( *ptr_first == PrimLeft ) {
178 ++ptr_first;
179 }
180 while( DAW_LIKELY( ptr_first < ptr_last ) ) {
181 switch( *ptr_first ) {
182 case '\\':
183 ++ptr_first;
184 break;
185 case '"':
186 ++ptr_first;
187 // ptr_first is past an opening quote, so it is not null
188 ptr_first += json_details::mem_skip_until_end_of_string<
189 ParseState::is_unchecked_input,
190 typename ParseState::exec_tag_t>(
191 daw::not_null<char const *>( daw::never_null, ptr_first ),
192 daw::not_null<char const *>( daw::never_null, ptr_last ) )
193 .get( ) -
194 ptr_first;
195 daw_json_ensure( ptr_first < ptr_last,
196 ErrorReason::UnexpectedEndOfData,
197 parse_state );
198 break;
199 case ',':
200 if( prime_bracket_count == 1 and second_bracket_count == 0 ) {
201 ++cnt;
202 }
203 break;
204 case PrimLeft:
205 ++prime_bracket_count;
206 break;
207 case PrimRight::value:
208 --prime_bracket_count;
209 if( prime_bracket_count == 0 ) {
210 daw_json_ensure( second_bracket_count == 0,
211 ErrorReason::InvalidBracketing,
212 parse_state );
213 ++ptr_first;
214 // We include the close primary bracket in the range so that
215 // subsequent parsers have a terminator inside their range
216 result.last = ptr_first;
217 result.counter = cnt;
218 parse_state.first = ptr_first;
219 return result;
220 }
221 break;
222 case SecLeft::value:
223 ++second_bracket_count;
224 break;
225 case SecRight::value:
226 --second_bracket_count;
227 break;
228 case '/':
229 ++ptr_first;
230 daw_json_ensure( ptr_first < ptr_last,
231 ErrorReason::UnexpectedEndOfData,
232 parse_state );
233 switch( *ptr_first ) {
234 case '/':
235 ++ptr_first;
236 while( ( ptr_last - ptr_first ) > 1 and *ptr_first != '\n' ) {
237 ++ptr_first;
238 }
239 break;
240 case '*':
241 ++ptr_first;
242 // Find the closing */
243 while( ( ptr_last - ptr_first ) >= 2 and
244 not( *ptr_first == '*' and
245 *std::next( ptr_first ) == '/' ) ) {
246 ++ptr_first;
247 }
248 daw_json_ensure( ( ptr_last - ptr_first ) >= 2,
249 ErrorReason::UnexpectedEndOfData,
250 parse_state );
251 // Onto the '/', the increment below moves past it
252 ++ptr_first;
253 break;
254 default:
255 // Not a comment, look at this character as usual
256 continue;
257 }
258 break;
259 }
260 ++ptr_first;
261 }
262 daw_json_assert_weak( ( prime_bracket_count == 0 ) &
263 ( second_bracket_count == 0 ),
264 ErrorReason::InvalidBracketing,
265 parse_state );
266 // We include the close primary bracket in the range so that subsequent
267 // parsers have a terminator inside their range
268 result.last = ptr_first;
269 result.counter = cnt;
270 parse_state.first = ptr_first;
271 return result;
272 }
273
274 template<json_details::SkipBracketedType BracketedType,
275 typename ParseState>
276 DAW_ATTRIB_FLATINLINE static constexpr ParseState
277 skip_bracketed_item_unchecked( ParseState &parse_state ) {
278 DAW_CPP23_STATIC_LOCAL constexpr char PrimLeft =
279 BracketedType == json_details::SkipBracketedType::Class ? '{' : '[';
280 using PrimRight = daw::constant<PrimLeft == '{' ? '}' : ']'>;
281 using SecLeft = daw::constant<PrimLeft == '{' ? '[' : '{'>;
282 using SecRight = daw::constant<SecLeft::value == '{' ? '}' : ']'>;
283
284 // Not checking for Left as it is required to be skipped already
285 auto result = parse_state;
286 std::size_t cnt = 0;
287 std::uint32_t prime_bracket_count = 1;
288 std::uint32_t second_bracket_count = 0;
289 auto ptr_first = parse_state.first;
290 auto const ptr_last = parse_state.last;
291 // A null range is a logic error, not bad input
292 daw_json_dbg_ensure( ptr_first != nullptr and ptr_last != nullptr,
293 ErrorReason::InvalidNull );
294 if( *ptr_first == PrimLeft ) {
295 ++ptr_first;
296 }
297 while( true ) {
298 switch( *ptr_first ) {
299 case '\\':
300 ++ptr_first;
301 break;
302 case '"':
303 ++ptr_first;
304 // ptr_first is past an opening quote, so it is not null
305 ptr_first += json_details::mem_skip_until_end_of_string<
306 ParseState::is_unchecked_input,
307 typename ParseState::exec_tag_t>(
308 daw::not_null<char const *>( daw::never_null, ptr_first ),
309 daw::not_null<char const *>( daw::never_null, ptr_last ) )
310 .get( ) -
311 ptr_first;
312 break;
313 case ',':
314 if( prime_bracket_count == 1 and second_bracket_count == 0 ) {
315 ++cnt;
316 }
317 break;
318 case PrimLeft:
319 ++prime_bracket_count;
320 break;
321 case PrimRight::value:
322 --prime_bracket_count;
323 if( prime_bracket_count == 0 ) {
324 ++ptr_first;
325 // We include the close primary bracket in the range so that
326 // subsequent parsers have a terminator inside their range
327 result.last = ptr_first;
328 result.counter = cnt;
329 parse_state.first = ptr_first;
330 return result;
331 }
332 break;
333 case SecLeft::value:
334 ++second_bracket_count;
335 break;
336 case SecRight::value:
337 --second_bracket_count;
338 break;
339 case '/':
340 ++ptr_first;
341 switch( *ptr_first ) {
342 case '/':
343 ++ptr_first;
344 while( *ptr_first != '\n' ) {
345 ++ptr_first;
346 }
347 break;
348 case '*':
349 ++ptr_first;
350 // Find the closing */
351 while( not( *ptr_first == '*' and
352 *std::next( ptr_first ) == '/' ) ) {
353 ++ptr_first;
354 }
355 // Onto the '/', the increment below moves past it
356 ++ptr_first;
357 break;
358 default:
359 // Not a comment, look at this character as usual
360 continue;
361 }
362 break;
363 }
364 ++ptr_first;
365 }
366 DAW_UNREACHABLE( );
367 }
368 };
369 } // namespace DAW_JSON_VER
370} // namespace daw::json
#define daw_json_dbg_ensure(Bool,...)
#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.
constexpr decltype(auto) get(basic_json_pair< PolicyFlags, Allocator > const &parse_state)
string_writer grows its string with resize_and_overwrite, leaving the new bytes uninitialized,...
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20