DAW JSON Link
Loading...
Searching...
No Matches
daw_json_iterator.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
16
17#include <daw/daw_cxmath.h>
18#include <daw/daw_move.h>
19#include <daw/daw_string_view.h>
20#include <daw/daw_traits.h>
21#include <daw/daw_utility.h>
22
23#include <array>
24#include <cstddef>
25#include <cstdlib>
26#include <iterator>
27#include <limits>
28#include <optional>
29#include <string>
30#include <type_traits>
31
32namespace daw::json {
33 inline namespace DAW_JSON_VER {
34 namespace json_details {
35 template<typename T>
36 struct assign_on_dtor {
37 T &lhs;
38 T const &rhs;
39
40 DAW_ATTRIB_INLINE constexpr assign_on_dtor( T &Lhs,
41 T const &Rhs ) noexcept
42 : lhs( Lhs )
43 , rhs( Rhs ) {}
44
45 DAW_ATTRIB_INLINE DAW_JSON_CPP20_CX_DTOR ~assign_on_dtor( ) noexcept {
46 lhs = rhs;
47 }
48 };
49
50 template<typename T>
51 assign_on_dtor( T const &, T const & ) -> assign_on_dtor<T>;
52
53 } // namespace json_details
54 /***
55 * Iterator for iterating over JSON array's
56 * @tparam JsonElement type under underlying element in array. If
57 * heterogeneous, a basic_json_value_iterator may be more appropriate
58 * @note An exception from dereferencing or incrementing invalidates the
59 * iterator. Before using it again, restore it by assigning a valid copy
60 * saved before the operation that threw. Incrementing an invalidated
61 * iterator is not a supported way to recover from a parsing error.
62 * @tparam ParseState Parsing policy type
63 */
64 template<typename JsonElement, typename ParseState, typename = void>
66
67 static constexpr ParseState get_range( daw::string_view data,
68 daw::string_view member_path ) {
69 auto [result, is_found] = json_details::find_range<ParseState>(
70 data, { std::data( member_path ), std::size( member_path ) } );
71 daw_json_ensure( is_found, ErrorReason::JSONPathNotFound );
73 result.front( ) == '[', ErrorReason::InvalidArrayStart, result );
74 return result;
75 }
76
77 public:
78 using element_type = json_details::json_deduced_type<JsonElement>;
79 static_assert( not std::is_same_v<element_type, void>,
80 "Unknown JsonElement type." );
81 using value_type = json_details::json_result_t<element_type>;
83 using pointer = json_details::arrow_proxy<value_type>;
84 using difference_type = std::ptrdiff_t;
85 // Can do forward iteration and be stored
86 using iterator_category = std::input_iterator_tag;
87
88 private:
89 ParseState m_state = ParseState( );
90 /***
91 * This lets us fastpath and just skip n characters as we have already
92 * parsed them
93 */
94 mutable char const *m_can_skip = nullptr;
95
96 public:
97 explicit json_array_iterator_t( ) = default;
98
99 explicit constexpr json_array_iterator_t( daw::string_view jd )
100 : m_state( ParseState( std::data( jd ), daw::data_end( jd ) ) ) {
101
102 m_state.trim_left( );
103 daw_json_assert_weak( m_state.is_opening_bracket_checked( ),
104 ErrorReason::InvalidArrayStart,
105 m_state );
106
107 m_state.remove_prefix( );
108 m_state.trim_left( );
110 m_state.has_more( ), ErrorReason::UnexpectedEndOfData, m_state );
111 }
112
113 explicit constexpr json_array_iterator_t( daw::string_view jd,
114 daw::string_view start_path )
115 : m_state( get_range( jd, start_path ) ) {
116
117 m_state.trim_left( );
118 daw_json_assert_weak( m_state.is_opening_bracket_checked( ),
119 ErrorReason::InvalidArrayStart,
120 m_state );
121
122 m_state.remove_prefix( );
123 m_state.trim_left( );
125 m_state.has_more( ), ErrorReason::UnexpectedEndOfData, m_state );
126 }
127
129 constexpr json_array_iterator_t begin( ) const {
130 return *this;
131 }
132
134 constexpr json_array_iterator_t end( ) const {
135 return json_array_iterator_t( );
136 }
137
141 [[nodiscard]] constexpr value_type operator*( ) const {
142 daw_json_assert_weak( m_state.has_more( ) and m_state.front( ) != ']',
143 ErrorReason::UnexpectedEndOfData,
144 m_state );
145
146 auto tmp = m_state;
147
148 auto const run_after_parse =
149 json_details::assign_on_dtor{ m_can_skip, tmp.first };
150 (void)run_after_parse;
151 return json_details::
152 parse_value<element_type, false, element_type::expected_type>( tmp );
153 }
154
160 [[nodiscard]] pointer operator->( ) const {
161 return pointer{ operator*( ) };
162 }
163
164 /***
165 * Move the parse state to the next element
166 * @return iterator after moving
167 */
169 daw_json_assert_weak( m_state.has_more( ) and m_state.front( ) != ']',
170 ErrorReason::UnexpectedEndOfData,
171 m_state );
172 if( m_can_skip ) {
173 m_state.first = m_can_skip;
174 m_can_skip = nullptr;
175 } else {
176 (void)json_details::skip_known_value<element_type>( m_state );
177 }
178 m_state.trim_left( );
179 daw_json_assert_weak( m_state.has_more( ) and
180 m_state.is_at_next_array_element( ),
181 ErrorReason::UnexpectedEndOfData,
182 m_state );
183
184 m_state.move_next_member_or_end( );
185 m_state.trim_left( );
187 m_state.has_more( ), ErrorReason::UnexpectedEndOfData, m_state );
188
189 return *this;
190 }
191
195 constexpr void operator++( int ) & {
196 (void)operator++( );
197 }
198
199 /***
200 * Is it ok to dereference iterator
201 * @return true when there is parse data available
202 */
203 [[nodiscard]] constexpr bool good( ) const {
204 return not m_state.is_null( ) and m_state.has_more( ) and
205 m_state.front( ) != ']';
206 }
207
210 [[nodiscard]] explicit constexpr operator bool( ) const {
211 return good( );
212 }
213
217 [[nodiscard]] constexpr bool
218 operator==( json_array_iterator_t const &rhs ) const {
219 auto const not_lhs = not( *this );
220 auto const not_rhs = not( rhs );
221 if( not_lhs ) {
222 return not_rhs;
223 }
224 if( not_rhs ) {
225 return false;
226 }
227 return ( m_state.first == rhs.m_state.first );
228 }
229
233 [[nodiscard]] constexpr bool
234 operator!=( json_array_iterator_t const &rhs ) const {
235 if( not( *this ) ) {
236 return static_cast<bool>( rhs );
237 }
238 if( not rhs ) {
239 return true;
240 }
241 return m_state.first != rhs.m_state.first;
242 }
243 };
244
245 template<typename JsonElement, auto... PolicyFlags>
247 JsonElement,
249 options::details::make_parse_flags<PolicyFlags...>( ).value>>>;
258 template<typename JsonElement, auto... PolicyFlags>
261 options::details::make_parse_flags<PolicyFlags...>( ).value>>;
262
263 static constexpr ParseState get_range( daw::string_view data,
264 daw::string_view member_path ) {
265 auto [result, is_found] = json_details::find_range<ParseState>(
266 data, { std::data( member_path ), std::size( member_path ) } );
267 daw_json_ensure( is_found, ErrorReason::JSONPathNotFound );
269 result.front( ) == '[', ErrorReason::InvalidArrayStart, result );
270 return result;
271 }
272
273 public:
274 using element_type = json_details::json_deduced_type<JsonElement>;
275 static_assert( not std::is_same_v<element_type, void>,
276 "Unknown JsonElement type." );
277 using value_type = json_details::json_result_t<element_type>;
279 using pointer = json_details::arrow_proxy<value_type>;
280 using difference_type = std::ptrdiff_t;
281
282 private:
283 mutable ParseState m_state = ParseState( );
284
285 public:
286 explicit json_array_iterator_once( ) = default;
287
288 explicit constexpr json_array_iterator_once( daw::string_view jd )
289 : m_state( ParseState( std::data( jd ), daw::data_end( jd ) ) ) {
290
291 m_state.trim_left( );
292 daw_json_assert_weak( m_state.is_opening_bracket_checked( ),
293 ErrorReason::InvalidArrayStart,
294 m_state );
295
296 m_state.remove_prefix( );
297 m_state.trim_left( );
299 m_state.has_more( ), ErrorReason::UnexpectedEndOfData, m_state );
300 }
301
302 explicit constexpr json_array_iterator_once( daw::string_view jd,
303 daw::string_view start_path )
304 : m_state( get_range( jd, start_path ) ) {
305
306 m_state.trim_left( );
307 daw_json_assert_weak( m_state.is_opening_bracket_checked( ),
308 ErrorReason::InvalidArrayStart,
309 m_state );
310
311 m_state.remove_prefix( );
312 m_state.trim_left( );
314 m_state.has_more( ), ErrorReason::UnexpectedEndOfData, m_state );
315 }
316
320 [[nodiscard]] constexpr value_type operator*( ) const {
321 daw_json_assert_weak( m_state.has_more( ) and m_state.front( ) != ']',
322 ErrorReason::UnexpectedEndOfData,
323 m_state );
324
325 return json_details::
326 parse_value<element_type, false, element_type::expected_type>(
327 m_state );
328 }
329
330 /***
331 * Move the parse state to the next element
332 * @return iterator after moving
333 */
335 daw_json_assert_weak( m_state.has_more( ) and
336 m_state.is_at_next_array_element( ),
337 ErrorReason::UnexpectedEndOfData,
338 m_state );
339 m_state.move_next_member_or_end( );
341 m_state.has_more( ), ErrorReason::UnexpectedEndOfData, m_state );
342 return *this;
343 }
344
348 constexpr void operator++( int ) & {
349 (void)operator++( );
350 }
351
352 /***
353 * Is it ok to dereference iterator
354 * @return true when there is parse data available
355 */
356 [[nodiscard]] constexpr bool good( ) const {
357 return not m_state.is_null( ) and m_state.has_more( ) and
358 m_state.front( ) != ']';
359 }
360
363 [[nodiscard]] explicit constexpr operator bool( ) const {
364 return good( );
365 }
366
370 [[nodiscard]] constexpr bool
372 auto const not_lhs = not( *this );
373 auto const not_rhs = not( rhs );
374 if( not_lhs ) {
375 return not_rhs;
376 }
377 if( not_rhs ) {
378 return false;
379 }
380 return ( m_state.first == rhs.m_state.first );
381 }
382
386 [[nodiscard]] constexpr bool
388 auto const not_lhs = not( *this );
389 auto const not_rhs = not( rhs );
390 if( not_lhs ) {
391 return not not_rhs;
392 }
393 if( not_rhs ) {
394 return true;
395 }
396 return m_state.first != rhs.m_state.first;
397 }
398 };
399
402 template<typename JsonElement, auto... PolicyFlags>
405 options::details::make_parse_flags<PolicyFlags...>( ).value>>;
406 using iterator = json_array_iterator<JsonElement, PolicyFlags...>;
407
408 private:
409 iterator m_first{ };
410 iterator m_last{ };
411
412 public:
413 explicit json_array_range( ) = default;
414
415 explicit constexpr json_array_range( daw::string_view jd )
416 : m_first( jd ) {}
417
418 explicit constexpr json_array_range( daw::string_view jd,
419 daw::string_view start_path )
420 : m_first( jd, start_path ) {}
421
423 [[nodiscard]] constexpr iterator begin( ) const {
424 return m_first;
425 }
426
428 [[nodiscard]] constexpr iterator end( ) const {
429 return m_last;
430 }
431
434 [[nodiscard]] constexpr bool empty( ) const {
435 return m_first == m_last;
436 }
437 };
438
442 template<typename JsonElement, auto... PolicyFlags>
445 options::details::make_parse_flags<PolicyFlags...>( ).value>>;
446 using iterator = json_array_iterator_once<JsonElement, PolicyFlags...>;
447
448 private:
449 iterator m_first{ };
450 iterator m_last{ };
451
452 public:
454
455 explicit constexpr json_array_range_once( daw::string_view jd )
456 : m_first( jd ) {}
457
458 explicit constexpr json_array_range_once( daw::string_view jd,
459 daw::string_view start_path )
460 : m_first( jd, start_path ) {}
461
463 [[nodiscard]] constexpr iterator begin( ) const {
464 return m_first;
465 }
466
468 [[nodiscard]] constexpr iterator end( ) const {
469 return m_last;
470 }
471
474 [[nodiscard]] constexpr bool empty( ) const {
475 return m_first == m_last;
476 }
477 };
478 } // namespace DAW_JSON_VER
479} // namespace daw::json
Iterator for iterating over JSON arrays. Requires that operator* and operator++ be called in that seq...
constexpr bool operator!=(json_array_iterator_once const &rhs) const
Check if the other iterator is not equivalent.
constexpr void operator++(int) &
Move the parse state to the next element.
static constexpr ParseState get_range(daw::string_view data, daw::string_view member_path)
TryDefaultParsePolicy< BasicParsePolicy< options::details::make_parse_flags< PolicyFlags... >().value > > ParseState
constexpr value_type operator*() const
Parse the current element.
constexpr bool operator==(json_array_iterator_once const &rhs) const
Compare rhs for equivalence.
constexpr json_array_iterator_once(daw::string_view jd, daw::string_view start_path)
constexpr void operator++(int) &
Move the parse state to the next element.
constexpr value_type operator*() const
Parse the current element.
constexpr bool operator==(json_array_iterator_t const &rhs) const
Compare rhs for equivalence.
static constexpr ParseState get_range(daw::string_view data, daw::string_view member_path)
constexpr bool operator!=(json_array_iterator_t const &rhs) const
Check if the other iterator is not equivalent.
pointer operator->() const
A dereferencable value proxy holding the result of operator* This is for compatibility with the Itera...
constexpr json_array_iterator_t(daw::string_view jd, daw::string_view start_path)
#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.
#define DAW_JSON_CPP20_CX_DTOR
daw::conditional_t< ParsePolicy::is_default_parse_policy, DefaultParsePolicy, ParsePolicy > TryDefaultParsePolicy
Customization point traits.
A range of json_array_iterator_onces. Requires that op*‍/op++ be called in that sequence one time unt...
constexpr json_array_range_once(daw::string_view jd, daw::string_view start_path)
TryDefaultParsePolicy< BasicParsePolicy< options::details::make_parse_flags< PolicyFlags... >().value > > ParsePolicy
constexpr bool empty() const
Are there any elements in range.
constexpr bool empty() const
Are there any elements in range.
constexpr json_array_range(daw::string_view jd, daw::string_view start_path)
TryDefaultParsePolicy< BasicParsePolicy< options::details::make_parse_flags< PolicyFlags... >().value > > ParsePolicy
Handles the bounds and policy items for parsing execution and comments.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20