@@ -582,6 +582,8 @@ namespace view {
582582
583583 } // namespace detail_view
584584
585+ class scan_context ;
586+
585587 // A checked, zero-copy view of one complete, structurally well-formed CBOR
586588 // item: its leading semantic tags, head, and content. Obtained from
587589 // scan_prefix, parse_exact, wire_cursor, or navigator::finish_item; never
@@ -619,6 +621,8 @@ namespace view {
619621 class tag_range ;
620622 class chunk_iterator ;
621623 class chunk_range ;
624+ class child_iterator ;
625+ class child_range ;
622626
623627 // The item's leading semantic tags, outermost first. Empty for
624628 // untagged items. Tags are exposed, never interpreted: deciding what
@@ -630,6 +634,13 @@ namespace view {
630634 // one. Empty unless kind() is byte_string or text_string.
631635 chunk_range chunks () const noexcept ;
632636
637+ // The raw children of a container item, each a checked item: array
638+ // elements, or a map's keys and values alternating. Empty unless
639+ // kind() is array or map. The item's bytes are checked, so iteration
640+ // cannot fail; the context, which must outlive the range, supplies
641+ // skip workspace and grows only past 32 open containers.
642+ child_range children (scan_context& context) const noexcept ;
643+
633644 // The typed accessors return false, leaving `value` untouched, on a
634645 // kind mismatch. Structural well-formedness was established by scanning.
635646 bool uint64_value (uint64_t & value) const noexcept
@@ -711,8 +722,6 @@ namespace view {
711722 }
712723 };
713724
714- class scan_context ;
715-
716725 // Offset-based access to unchecked CBOR wire data. The cursor borrows its
717726 // input and never exposes a mutable pointer/end pair.
718727 class wire_cursor
@@ -1310,6 +1319,166 @@ namespace view {
13101319
13111320 } // namespace detail_view
13121321
1322+ // Iterates the raw children of a checked container item, measuring each
1323+ // child once to yield it as a complete checked item.
1324+ class item ::child_iterator
1325+ {
1326+ public:
1327+ using value_type = item;
1328+ using reference = item;
1329+ using pointer = void ;
1330+ using difference_type = std::ptrdiff_t ;
1331+ using iterator_category = std::input_iterator_tag;
1332+
1333+ child_iterator () noexcept
1334+ : pos_(nullptr ), next_(nullptr ), end_(nullptr ), remaining_(0 ),
1335+ content_ (nullptr ), context_(nullptr )
1336+ {
1337+ }
1338+
1339+ item operator *() const noexcept
1340+ {
1341+ assert (pos_ != nullptr );
1342+ return detail_view::item_access::make (
1343+ span<const uint8_t >(pos_, static_cast <std::size_t >(next_ - pos_)),
1344+ head_, content_);
1345+ }
1346+
1347+ child_iterator& operator ++()
1348+ {
1349+ advance ();
1350+ return *this ;
1351+ }
1352+
1353+ child_iterator operator ++(int )
1354+ {
1355+ child_iterator temp = *this ;
1356+ advance ();
1357+ return temp;
1358+ }
1359+
1360+ friend bool operator ==(const child_iterator& a, const child_iterator& b) noexcept
1361+ {
1362+ return a.pos_ == b.pos_ ;
1363+ }
1364+
1365+ friend bool operator !=(const child_iterator& a, const child_iterator& b) noexcept
1366+ {
1367+ return a.pos_ != b.pos_ ;
1368+ }
1369+
1370+ private:
1371+ friend class child_range ;
1372+
1373+ child_iterator (const uint8_t * first, const uint8_t * end, uint64_t remaining,
1374+ scan_context& context) noexcept
1375+ : pos_(nullptr ), next_(first), end_(end), remaining_(remaining),
1376+ content_ (nullptr ), context_(&context)
1377+ {
1378+ advance ();
1379+ }
1380+
1381+ void advance ()
1382+ {
1383+ pos_ = next_;
1384+ if (remaining_ == detail_view::indefinite_array_marker)
1385+ {
1386+ if (*pos_ == 0xff )
1387+ {
1388+ pos_ = nullptr ;
1389+ return ;
1390+ }
1391+ }
1392+ else if (remaining_ == 0 )
1393+ {
1394+ pos_ = nullptr ;
1395+ return ;
1396+ }
1397+ else
1398+ {
1399+ --remaining_;
1400+ }
1401+
1402+ const uint8_t * p = pos_;
1403+ std::error_code ec;
1404+ bool ok = detail_view::read_value_head (p, end_, head_, ec);
1405+ assert (ok && !ec);
1406+ content_ = p;
1407+ ok = head_.major_type == cbor::detail::cbor_major_type::array ||
1408+ head_.major_type == cbor::detail::cbor_major_type::map
1409+ ? detail_view::skip_container (p, end_, head_,
1410+ (std::numeric_limits<int >::max)(),
1411+ detail_view::scan_access::workspace (*context_), ec)
1412+ : detail_view::skip_scalar_or_string (head_, p, end_, ec);
1413+ assert (ok && !ec);
1414+ (void )ok;
1415+ next_ = p;
1416+ }
1417+
1418+ const uint8_t * pos_; // current child's begin, nullptr when exhausted
1419+ const uint8_t * next_; // current child's end, the next child's begin
1420+ const uint8_t * end_; // the parent's end
1421+ uint64_t remaining_; // raw children left; indefinite ends at a break
1422+ detail_view::item_head head_;
1423+ const uint8_t * content_;
1424+ scan_context* context_;
1425+ };
1426+
1427+ class item ::child_range
1428+ {
1429+ public:
1430+ using iterator = child_iterator;
1431+ using const_iterator = child_iterator;
1432+
1433+ child_iterator begin () const
1434+ {
1435+ if (first_ == nullptr )
1436+ {
1437+ return child_iterator ();
1438+ }
1439+ return child_iterator (first_, end_, remaining_, *context_);
1440+ }
1441+
1442+ child_iterator end () const noexcept
1443+ {
1444+ return child_iterator ();
1445+ }
1446+
1447+ bool empty () const noexcept
1448+ {
1449+ return first_ == nullptr ||
1450+ (remaining_ == detail_view::indefinite_array_marker
1451+ ? *first_ == 0xff : remaining_ == 0 );
1452+ }
1453+
1454+ private:
1455+ friend class item ;
1456+ child_range (const uint8_t * first, const uint8_t * end, uint64_t remaining,
1457+ scan_context* context) noexcept
1458+ : first_(first), end_(end), remaining_(remaining), context_(context)
1459+ {
1460+ }
1461+
1462+ const uint8_t * first_;
1463+ const uint8_t * end_;
1464+ uint64_t remaining_;
1465+ scan_context* context_;
1466+ };
1467+
1468+ inline item::child_range item::children (scan_context& context) const noexcept
1469+ {
1470+ if (head_.major_type != cbor::detail::cbor_major_type::array &&
1471+ head_.major_type != cbor::detail::cbor_major_type::map)
1472+ {
1473+ return child_range (nullptr , nullptr , 0 , &context);
1474+ }
1475+ const uint64_t remaining = head_.indefinite ()
1476+ ? detail_view::indefinite_array_marker
1477+ : (head_.major_type == cbor::detail::cbor_major_type::map
1478+ ? 2 * head_.value : head_.value );
1479+ return child_range (content_, bytes_.data () + bytes_.size (), remaining, &context);
1480+ }
1481+
13131482 struct scan_result
13141483 {
13151484 item first;
@@ -1858,41 +2027,34 @@ namespace view {
18582027 expected<bool , scan_error> map_keys_sorted (span<const uint8_t > input,
18592028 Order order = Order(), int max_nesting_depth = default_max_nesting_depth)
18602029 {
1861- auto result = navigate_prefix (input, max_nesting_depth);
1862- if (!result)
2030+ scan_context context (max_nesting_depth);
2031+ auto scanned = scan_prefix (input, context);
2032+ if (!scanned)
18632033 {
1864- return expected<bool , scan_error>(unexpect, result .error ());
2034+ return expected<bool , scan_error>(unexpect, scanned .error ());
18652035 }
1866-
1867- navigator nav = std::move (result.value ().first );
1868- if (nav.kind () != item_kind::map)
2036+ const item map_item = scanned.value ().first ;
2037+ if (map_item.kind () != item_kind::map)
18692038 {
18702039 return false ;
18712040 }
18722041
18732042 span<const uint8_t > previous;
1874- if (!nav.enter ())
1875- {
1876- return true ;
1877- }
1878- for (;;)
2043+ bool is_key = true ;
2044+ for (item child : map_item.children (context))
18792045 {
1880- assert (nav.role () == position_role::map_key);
1881- const span<const uint8_t > key = nav.finish_item ().encoded_bytes ();
1882- if (!previous.empty () && order (previous, key) >= 0 )
2046+ if (is_key)
18832047 {
1884- return false ;
1885- }
1886- previous = key;
1887-
1888- const bool has_value = nav.next ();
1889- assert (has_value && nav.role () == position_role::map_value);
1890- (void )has_value;
1891- if (!nav.next ())
1892- {
1893- return true ;
2048+ const span<const uint8_t > key = child.encoded_bytes ();
2049+ if (!previous.empty () && order (previous, key) >= 0 )
2050+ {
2051+ return false ;
2052+ }
2053+ previous = key;
18942054 }
2055+ is_key = !is_key;
18952056 }
2057+ return true ;
18962058 }
18972059
18982060 // True if `text_item` is a text string whose content is well-formed
0 commit comments