@@ -60,7 +60,7 @@ struct Key {
6060  std::variant<std::monostate, KeyInt, KeyStr> repr_;
6161
6262 public: 
63-   Key () {} 
63+   Key () =  default ; 
6464  /* implicit*/   Key(KeyInt key) : repr_(key) {}
6565  /* implicit*/   Key(KeyStr key) : repr_(std::move(key)) {}
6666
@@ -131,7 +131,7 @@ struct ContainerHandle {
131131  using  leaf_type = T;
132132  std::unique_ptr<container_type> handle;
133133
134-   ContainerHandle () {} 
134+   ContainerHandle () =  default ; 
135135
136136  template  <typename ... Args>
137137  ContainerHandle (Args... args)
@@ -427,6 +427,20 @@ struct arr {
427427    return  data_[idx];
428428  }
429429
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+ 
430444  inline  T* data () {
431445    return  data_.get ();
432446  }
@@ -458,7 +472,7 @@ struct arr {
458472
459473inline  size_t  read_number (const  StrTreeSpec& spec, size_t & read_idx) {
460474  size_t  num = 0 ;
461-   while  (isdigit (spec[ read_idx] )) {
475+   while  (isdigit (spec. at ( read_idx) )) {
462476    num = 10  * num + (spec[read_idx] - ' 0'  );
463477    read_idx++;
464478  }
@@ -470,19 +484,22 @@ inline arr<size_t> read_node_layout(const StrTreeSpec& spec, size_t& read_idx) {
470484  arr<size_t > ret (child_num);
471485
472486  size_t  child_idx = 0 ;
473-   while  (spec[ read_idx]  == Config::kChildrenDataSep ) {
487+   while  (spec. at ( read_idx)  == Config::kChildrenDataSep ) {
474488    ++read_idx;
475-     ret[ child_idx++]  = read_number (spec, read_idx);
489+     ret. at ( child_idx++)  = read_number (spec, read_idx);
476490  }
477491  return  ret;
478492}
479493
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.
480497template  <typename  Aux>
481498TreeSpec<Aux> from_str_internal (
482499    const  StrTreeSpec& spec,
483500    size_t  read_idx,
484501    const  arr<size_t >& spec_data) {
485-   const  auto  kind_char = spec[ read_idx] ;
502+   const  auto  kind_char = spec. at ( read_idx) ;
486503  switch  (kind_char) {
487504    case  Config::kTuple :
488505    case  Config::kNamedTuple :
@@ -496,7 +513,7 @@ TreeSpec<Aux> from_str_internal(
496513      } else  if  (Config::kCustom  == kind_char) {
497514        kind = Kind::Custom;
498515        read_idx++;
499-         assert (spec[ read_idx]  == ' ('  );
516+         assert (spec. at ( read_idx)  == ' ('  );
500517        auto  type_str_end = spec_data[read_idx];
501518        read_idx++;
502519        custom_type = spec.substr (read_idx, type_str_end - read_idx);
@@ -515,10 +532,14 @@ TreeSpec<Aux> from_str_internal(
515532      size_t  leaves_offset = 0 ;
516533
517534      if  (size > 0 ) {
518-         while  (spec[ read_idx]  != Config::kNodeDataEnd ) {
535+         while  (spec. at ( read_idx)  != Config::kNodeDataEnd ) {
519536          //  NOLINTNEXTLINE
520537          auto  next_delim_idx = spec_data[read_idx];
521538          read_idx++;
539+           if  (child_idx >= size) {
540+             throw  std::out_of_range (
541+                 " bounds check failed writing to pytree item"  );
542+           }
522543          c->items [child_idx] =
523544              from_str_internal<Aux>(spec, read_idx, spec_data);
524545          read_idx = next_delim_idx;
@@ -541,11 +562,14 @@ TreeSpec<Aux> from_str_internal(
541562      size_t  leaves_offset = 0 ;
542563
543564      if  (size > 0 ) {
544-         while  (spec[ read_idx]  != Config::kNodeDataEnd ) {
565+         while  (spec. at ( read_idx)  != Config::kNodeDataEnd ) {
545566          //  NOLINTNEXTLINE
546567          auto  next_delim_idx = spec_data[read_idx];
547568          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 ) {
549573            auto  key_delim_idx = spec_data[read_idx];
550574            read_idx++;
551575            const  size_t  key_len = key_delim_idx - read_idx;
@@ -562,7 +586,7 @@ TreeSpec<Aux> from_str_internal(
562586          c->items [child_idx] =
563587              from_str_internal<Aux>(spec, read_idx, spec_data);
564588          read_idx = next_delim_idx;
565-           leaves_offset += layout[ child_idx++] ;
589+           leaves_offset += layout. at ( child_idx++) ;
566590        }
567591      } else  {
568592        read_idx++;
@@ -605,7 +629,9 @@ struct stack final {
605629  }
606630};
607631
632+ //  We guarantee indicies in the result are in bounds.
608633inline  arr<size_t > pre_parse (const  StrTreeSpec& spec) {
634+   //  Invariant: indices in stack are in bounds.
609635  stack<std::pair<size_t , size_t >> stack;
610636  size_t  i = 0 ;
611637  const  size_t  size = spec.size ();
@@ -627,11 +653,15 @@ inline arr<size_t> pre_parse(const StrTreeSpec& spec) {
627653      case  Config::kDictStrKeyQuote : {
628654        size_t  idx = i;
629655        i++;
630-         while  (spec[i]  != Config::kDictStrKeyQuote ) {
656+         while  (spec. at (i)  != Config::kDictStrKeyQuote ) {
631657          i++;
632658        }
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;
635665        break ;
636666      }
637667      case  Config::kChildrenSep : {
0 commit comments