@@ -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,20 @@ 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 (" bounds check failed in pytree arr" );
433
+ }
434
+ return data_[idx];
435
+ }
436
+
437
+ const T& at (size_t idx) const {
438
+ if (idx >= size ()) {
439
+ throw std::out_of_range (" bounds check failed in pytree arr" );
440
+ }
441
+ return data_[idx];
442
+ }
443
+
430
444
inline T* data () {
431
445
return data_.get ();
432
446
}
@@ -458,7 +472,7 @@ struct arr {
458
472
459
473
inline size_t read_number (const StrTreeSpec& spec, size_t & read_idx) {
460
474
size_t num = 0 ;
461
- while (isdigit (spec[ read_idx] )) {
475
+ while (isdigit (spec. at ( read_idx) )) {
462
476
num = 10 * num + (spec[read_idx] - ' 0' );
463
477
read_idx++;
464
478
}
@@ -470,19 +484,22 @@ inline arr<size_t> read_node_layout(const StrTreeSpec& spec, size_t& read_idx) {
470
484
arr<size_t > ret (child_num);
471
485
472
486
size_t child_idx = 0 ;
473
- while (spec[ read_idx] == Config::kChildrenDataSep ) {
487
+ while (spec. at ( read_idx) == Config::kChildrenDataSep ) {
474
488
++read_idx;
475
- ret[ child_idx++] = read_number (spec, read_idx);
489
+ ret. at ( child_idx++) = read_number (spec, read_idx);
476
490
}
477
491
return ret;
478
492
}
479
493
494
+ // spec_data comes from pre_parse, which guarantees 1)
495
+ // spec_data.size() == spec.size() and 2) contents of spec_data are
496
+ // in-bounds indices for spec, so we omit bounds checks for spec_data.
480
497
template <typename Aux>
481
498
TreeSpec<Aux> from_str_internal (
482
499
const StrTreeSpec& spec,
483
500
size_t read_idx,
484
501
const arr<size_t >& spec_data) {
485
- const auto kind_char = spec[ read_idx] ;
502
+ const auto kind_char = spec. at ( read_idx) ;
486
503
switch (kind_char) {
487
504
case Config::kTuple :
488
505
case Config::kNamedTuple :
@@ -496,7 +513,7 @@ TreeSpec<Aux> from_str_internal(
496
513
} else if (Config::kCustom == kind_char) {
497
514
kind = Kind::Custom;
498
515
read_idx++;
499
- assert (spec[ read_idx] == ' (' );
516
+ assert (spec. at ( read_idx) == ' (' );
500
517
auto type_str_end = spec_data[read_idx];
501
518
read_idx++;
502
519
custom_type = spec.substr (read_idx, type_str_end - read_idx);
@@ -515,10 +532,14 @@ TreeSpec<Aux> from_str_internal(
515
532
size_t leaves_offset = 0 ;
516
533
517
534
if (size > 0 ) {
518
- while (spec[ read_idx] != Config::kNodeDataEnd ) {
535
+ while (spec. at ( read_idx) != Config::kNodeDataEnd ) {
519
536
// NOLINTNEXTLINE
520
537
auto next_delim_idx = spec_data[read_idx];
521
538
read_idx++;
539
+ if (child_idx >= size) {
540
+ throw std::out_of_range (
541
+ " bounds check failed writing to pytree item" );
542
+ }
522
543
c->items [child_idx] =
523
544
from_str_internal<Aux>(spec, read_idx, spec_data);
524
545
read_idx = next_delim_idx;
@@ -541,11 +562,14 @@ TreeSpec<Aux> from_str_internal(
541
562
size_t leaves_offset = 0 ;
542
563
543
564
if (size > 0 ) {
544
- while (spec[ read_idx] != Config::kNodeDataEnd ) {
565
+ while (spec. at ( read_idx) != Config::kNodeDataEnd ) {
545
566
// NOLINTNEXTLINE
546
567
auto next_delim_idx = spec_data[read_idx];
547
568
read_idx++;
548
- if (spec[read_idx] == Config::kDictStrKeyQuote ) {
569
+ if (child_idx >= size) {
570
+ throw std::out_of_range (" bounds check failed decoding pytree dict" );
571
+ }
572
+ if (spec.at (read_idx) == Config::kDictStrKeyQuote ) {
549
573
auto key_delim_idx = spec_data[read_idx];
550
574
read_idx++;
551
575
const size_t key_len = key_delim_idx - read_idx;
@@ -562,7 +586,7 @@ TreeSpec<Aux> from_str_internal(
562
586
c->items [child_idx] =
563
587
from_str_internal<Aux>(spec, read_idx, spec_data);
564
588
read_idx = next_delim_idx;
565
- leaves_offset += layout[ child_idx++] ;
589
+ leaves_offset += layout. at ( child_idx++) ;
566
590
}
567
591
} else {
568
592
read_idx++;
@@ -605,7 +629,9 @@ struct stack final {
605
629
}
606
630
};
607
631
632
+ // We guarantee indicies in the result are in bounds.
608
633
inline arr<size_t > pre_parse (const StrTreeSpec& spec) {
634
+ // Invariant: indices in stack are in bounds.
609
635
stack<std::pair<size_t , size_t >> stack;
610
636
size_t i = 0 ;
611
637
const size_t size = spec.size ();
@@ -627,11 +653,15 @@ inline arr<size_t> pre_parse(const StrTreeSpec& spec) {
627
653
case Config::kDictStrKeyQuote : {
628
654
size_t idx = i;
629
655
i++;
630
- while (spec[i] != Config::kDictStrKeyQuote ) {
656
+ while (spec. at (i) != Config::kDictStrKeyQuote ) {
631
657
i++;
632
658
}
633
- ret[idx] = i;
634
- ret[i] = idx;
659
+ if (i >= size) {
660
+ throw std::out_of_range (
661
+ " bounds check failed while parsing dictionary key" );
662
+ }
663
+ ret.at (idx) = i;
664
+ ret.at (i) = idx;
635
665
break ;
636
666
}
637
667
case Config::kChildrenSep : {
0 commit comments