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