@@ -60,7 +60,7 @@ struct Key {
60
60
std::variant<std::monostate, KeyInt, KeyStr> repr_;
61
61
62
62
public:
63
- Key () {}
63
+ Key () = default ;
64
64
/* implicit*/ Key(KeyInt key) : repr_(key) {}
65
65
/* implicit*/ Key(KeyStr key) : repr_(std::move(key)) {}
66
66
@@ -131,7 +131,7 @@ struct ContainerHandle {
131
131
using leaf_type = T;
132
132
std::unique_ptr<container_type> handle;
133
133
134
- ContainerHandle () {}
134
+ ContainerHandle () = default ;
135
135
136
136
template <typename ... Args>
137
137
ContainerHandle (Args... args)
@@ -427,6 +427,22 @@ struct arr {
427
427
return data_[idx];
428
428
}
429
429
430
+ T& at (size_t idx) {
431
+ if (idx >= size ()) {
432
+ throw std::out_of_range (
433
+ " bounds check failed in pytree arr at index " + std::to_string (idx));
434
+ }
435
+ return data_[idx];
436
+ }
437
+
438
+ const T& at (size_t idx) const {
439
+ if (idx >= size ()) {
440
+ throw std::out_of_range (
441
+ " bounds check failed in pytree arr at index " + std::to_string (idx));
442
+ }
443
+ return data_[idx];
444
+ }
445
+
430
446
inline T* data () {
431
447
return data_.get ();
432
448
}
@@ -458,7 +474,7 @@ struct arr {
458
474
459
475
inline size_t read_number (const StrTreeSpec& spec, size_t & read_idx) {
460
476
size_t num = 0 ;
461
- while (isdigit (spec[ read_idx] )) {
477
+ while (isdigit (spec. at ( read_idx) )) {
462
478
num = 10 * num + (spec[read_idx] - ' 0' );
463
479
read_idx++;
464
480
}
@@ -470,19 +486,22 @@ inline arr<size_t> read_node_layout(const StrTreeSpec& spec, size_t& read_idx) {
470
486
arr<size_t > ret (child_num);
471
487
472
488
size_t child_idx = 0 ;
473
- while (spec[ read_idx] == Config::kChildrenDataSep ) {
489
+ while (spec. at ( read_idx) == Config::kChildrenDataSep ) {
474
490
++read_idx;
475
- ret[ child_idx++] = read_number (spec, read_idx);
491
+ ret. at ( child_idx++) = read_number (spec, read_idx);
476
492
}
477
493
return ret;
478
494
}
479
495
496
+ // spec_data comes from pre_parse, which guarantees 1)
497
+ // spec_data.size() == spec.size() and 2) contents of spec_data are
498
+ // in-bounds indices for spec, so we omit bounds checks for spec_data.
480
499
template <typename Aux>
481
500
TreeSpec<Aux> from_str_internal (
482
501
const StrTreeSpec& spec,
483
502
size_t read_idx,
484
503
const arr<size_t >& spec_data) {
485
- const auto kind_char = spec[ read_idx] ;
504
+ const auto kind_char = spec. at ( read_idx) ;
486
505
switch (kind_char) {
487
506
case Config::kTuple :
488
507
case Config::kNamedTuple :
@@ -496,7 +515,7 @@ TreeSpec<Aux> from_str_internal(
496
515
} else if (Config::kCustom == kind_char) {
497
516
kind = Kind::Custom;
498
517
read_idx++;
499
- assert (spec[ read_idx] == ' (' );
518
+ assert (spec. at ( read_idx) == ' (' );
500
519
auto type_str_end = spec_data[read_idx];
501
520
read_idx++;
502
521
custom_type = spec.substr (read_idx, type_str_end - read_idx);
@@ -515,10 +534,15 @@ TreeSpec<Aux> from_str_internal(
515
534
size_t leaves_offset = 0 ;
516
535
517
536
if (size > 0 ) {
518
- while (spec[ read_idx] != Config::kNodeDataEnd ) {
537
+ while (spec. at ( read_idx) != Config::kNodeDataEnd ) {
519
538
// NOLINTNEXTLINE
520
539
auto next_delim_idx = spec_data[read_idx];
521
540
read_idx++;
541
+ if (child_idx >= size) {
542
+ throw std::out_of_range (
543
+ " bounds check failed writing to pytree item at index " +
544
+ std::to_string (child_idx));
545
+ }
522
546
c->items [child_idx] =
523
547
from_str_internal<Aux>(spec, read_idx, spec_data);
524
548
read_idx = next_delim_idx;
@@ -541,11 +565,16 @@ TreeSpec<Aux> from_str_internal(
541
565
size_t leaves_offset = 0 ;
542
566
543
567
if (size > 0 ) {
544
- while (spec[ read_idx] != Config::kNodeDataEnd ) {
568
+ while (spec. at ( read_idx) != Config::kNodeDataEnd ) {
545
569
// NOLINTNEXTLINE
546
570
auto next_delim_idx = spec_data[read_idx];
547
571
read_idx++;
548
- if (spec[read_idx] == Config::kDictStrKeyQuote ) {
572
+ if (child_idx >= size) {
573
+ throw std::out_of_range (
574
+ " bounds check failed decoding pytree dict at index " +
575
+ std::to_string (child_idx));
576
+ }
577
+ if (spec.at (read_idx) == Config::kDictStrKeyQuote ) {
549
578
auto key_delim_idx = spec_data[read_idx];
550
579
read_idx++;
551
580
const size_t key_len = key_delim_idx - read_idx;
@@ -562,7 +591,7 @@ TreeSpec<Aux> from_str_internal(
562
591
c->items [child_idx] =
563
592
from_str_internal<Aux>(spec, read_idx, spec_data);
564
593
read_idx = next_delim_idx;
565
- leaves_offset += layout[ child_idx++] ;
594
+ leaves_offset += layout. at ( child_idx++) ;
566
595
}
567
596
} else {
568
597
read_idx++;
@@ -605,7 +634,9 @@ struct stack final {
605
634
}
606
635
};
607
636
637
+ // We guarantee indicies in the result are in bounds.
608
638
inline arr<size_t > pre_parse (const StrTreeSpec& spec) {
639
+ // Invariant: indices in stack are in bounds.
609
640
stack<std::pair<size_t , size_t >> stack;
610
641
size_t i = 0 ;
611
642
const size_t size = spec.size ();
@@ -627,11 +658,16 @@ inline arr<size_t> pre_parse(const StrTreeSpec& spec) {
627
658
case Config::kDictStrKeyQuote : {
628
659
size_t idx = i;
629
660
i++;
630
- while (spec[i] != Config::kDictStrKeyQuote ) {
661
+ while (spec. at (i) != Config::kDictStrKeyQuote ) {
631
662
i++;
632
663
}
633
- ret[idx] = i;
634
- ret[i] = idx;
664
+ if (i >= size) {
665
+ throw std::out_of_range (
666
+ " bounds check failed while parsing dictionary key at index " +
667
+ std::to_string (i));
668
+ }
669
+ ret.at (idx) = i;
670
+ ret.at (i) = idx;
635
671
break ;
636
672
}
637
673
case Config::kChildrenSep : {
0 commit comments