LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
LazyVector.h
Go to the documentation of this file.
1 
11 #ifndef LARDATAOBJ_UTILITIES_LAZYVECTOR_H
12 #define LARDATAOBJ_UTILITIES_LAZYVECTOR_H
13 
14 // C/C++ standard libraries
15 #include <algorithm>
16 #include <cassert>
17 #include <stdexcept> // std::out_of_range
18 #include <string> // std::to_string()
19 #include <vector>
20 
21 namespace util {
22 
76  template <typename T, typename A = typename std::vector<T>::allocator_type>
77  class LazyVector {
78 
79  using Data_t = std::vector<T, A>;
80 
81  public:
82  // --- BEGIN STL vector types ----------------------------------------------
85 
86  using allocator_type = typename Data_t::allocator_type;
87  using value_type = typename Data_t::value_type;
88  using size_type = typename Data_t::size_type;
89  using difference_type = typename Data_t::difference_type;
90  using reference = typename Data_t::reference;
91  using const_reference = typename Data_t::const_reference;
92  using pointer = typename Data_t::pointer;
93  using const_pointer = typename Data_t::const_pointer;
94 
96  // --- END STL vector types ------------------------------------------------
97 
100  LazyVector() = default;
101 
103  LazyVector(allocator_type const& a);
104 
118 
140  LazyVector(size_type n, value_type const& defValue);
141 
143 
144  // --- BEGIN Container information -----------------------------------------
147 
149  size_type size() const noexcept { return fNominalSize; }
150 
152  bool empty() const noexcept { return fNominalSize == 0U; }
153 
155  size_type data_size() const noexcept { return fData.size(); }
156 
158  bool has_index(size_type pos) const noexcept { return pos < size(); }
159 
161  bool data_empty() const noexcept { return fData.empty(); }
162 
165  value_type const& data_defvalue() const { return fDefValue; }
166 
170 
174 
176  bool data_has_index(size_type pos) const
177  {
178  return (pos >= data_begin_index()) && (pos < data_end_index());
179  }
180 
192 
194  // --- END Container information -------------------------------------------
195 
196  // --- BEGIN Access to data elements ---------------------------------------
217 
220 
236  value_type at(size_type pos) const;
237  value_type const_at(size_type pos) const { return at(pos); }
239 
253  reference at(size_type pos);
254 
256 
269  value_type operator[](size_type pos) const;
270  value_type const_get(size_type pos) const { return this->operator[](pos); }
272 
274 
288  reference get(size_type pos) { return this->operator[](pos); }
290 
292  // --- END Access to data elements -----------------------------------------
293 
294  // --- BEGIN Setting data elements -----------------------------------------
297 
299  // --- END Setting data elements -------------------------------------------
300 
301  // --- BEGIN Container operations -----------------------------------------
304 
317  void resize(size_type newSize);
318 
335  void reserve(size_type n) { storage().reserve(n); }
336 
338  void clear();
339 
341  void shrink_to_fit() { storage().shrink_to_fit(); }
342 
362  void data_prepare(size_type startIndex, size_type endIndex);
363 
377 
392  void data_init(size_type startIndex, size_type endIndex);
393 
409  void data_init(size_type n) { data_init(0U, n); }
410 
412  // --- END Container operations --------------------------------------------
413 
414  private:
416 
418  size_type fFirstIndex = fData.max_size();
420 
423 
425  Data_t& storage() { return fData; }
427  Data_t const& storage() const { return fData; }
429 
431  size_type index_of(size_type pos) const { return pos - fFirstIndex; }
432 
434  void expand(size_type pos);
435 
437  void init(size_type pos, size_type n = 1U);
438 
440  void expand_front(size_type pos);
441 
443  void expand_back(size_type pos);
444 
446  void fix_size();
447 
449  void data_clear();
450 
452  void check_range(size_type pos) const;
453 
456 
457  }; // LazyVector<>
458 
459 } // namespace util
460 
461 //------------------------------------------------------------------------------
462 //--- template implementation
463 //------------------------------------------------------------------------------
464 template <typename T, typename A /* = std::vector<T>::allocator_type */>
466 
467 //------------------------------------------------------------------------------
468 template <typename T, typename A /* = std::vector<T>::allocator_type */>
470 {}
471 
472 //------------------------------------------------------------------------------
473 template <typename T, typename A /* = std::vector<T>::allocator_type */>
475 {}
476 
477 //------------------------------------------------------------------------------
478 template <typename T, typename A /* = std::vector<T>::allocator_type */>
480  : fNominalSize(n), fDefValue(defValue)
481 {}
482 
483 //------------------------------------------------------------------------------
484 template <typename T, typename A /* = std::vector<T>::allocator_type */>
486 {
487  /*
488  * Behaviour summary:
489  * * if `pos` is out of vector range, throw an exception
490  * * if element at `pos` has no storage, create storage for it
491  * * return a reference to the element at `pos`
492  */
493  check_range(pos); // verify that `pos` is valid, throw otherwise
494  expand(pos);
495  return storage()[index_of(pos)]; // we already know it's valid
496 }
497 
498 //------------------------------------------------------------------------------
499 template <typename T, typename A /* = std::vector<T>::allocator_type */>
501 {
502  /*
503  * Behaviour summary:
504  * * if `pos` is out of vector range, throw an exception
505  * * if element at `pos` has no storage, return a copy of the default value
506  * * otherwise, return a copy of the element at `pos`
507  */
508  check_range(pos); // verify that `pos` is valid, throw otherwise
509  return data_has_index(pos) ? storage()[index_of(pos)] : data_defvalue();
510 } // util::LazyVector<T,A>::at() const
511 
512 //------------------------------------------------------------------------------
513 template <typename T, typename A /* = std::vector<T>::allocator_type */>
515 {
516  /*
517  * Behaviour summary:
518  * * if `pos` is out of vector range, behaviour is undefined
519  * * if element at `pos` has no storage, create storage for it
520  * * return a reference to the element at `pos`
521  */
522  // to have the common case where the requested position has storage be handled
523  // the fastest, we "enforce" the order by nesting (optimiser has last word)
524  if (!data_has_index(pos)) {
525  if (has_index(pos)) expand(pos);
526  }
527  return storage()[index_of(pos)];
528 } // util::LazyVector<T,A>::operator[]()
529 
530 //------------------------------------------------------------------------------
531 template <typename T, typename A /* = std::vector<T>::allocator_type */>
533 {
534  /*
535  * Behaviour summary:
536  * * if `pos` is out of vector range, behaviour is undefined
537  * * if element at `pos` has no storage, return a copy of the default value
538  * * otherwise, return a copy of the element at `pos`
539  */
540  // this implementation will return a default value if `pos` is out of range;
541  // this is not a requirement, and may change at any time.
542  if (pos < data_begin_index()) return data_defvalue();
543  auto const index = index_of(pos);
544  return (index < data_size()) ? storage()[index] : data_defvalue();
545 } // util::LazyVector<T,A>::operator[] () const
546 
547 //------------------------------------------------------------------------------
548 template <typename T, typename A /* = std::vector<T>::allocator_type */>
550  size_type pos) const
551 {
552  /*
553  * Behaviour summary:
554  * * if `pos` is out of vector range, behaviour is undefined
555  * * if element at `pos` has no storage, return nullptr
556  * * otherwise, return the pointer to the specified element
557  */
558  // this implementation will return nullptr if `pos` is out of range;
559  // this is not a requirement, and may change at any time.
560  if (pos < data_begin_index()) return nullptr;
561  auto const index = index_of(pos);
562  return (index < data_size()) ? storage().data() + index : nullptr;
563 } // util::LazyVector<T,A>::data_address()
564 
565 //------------------------------------------------------------------------------
566 template <typename T, typename A /* = std::vector<T>::allocator_type */>
568 {
569  /*
570  * Behaviour summary:
571  * * when extending, do not change storage
572  * * when shrinking, cut the excess storage
573  */
574  fNominalSize = newSize;
575  // delete any excess data
576  if (data_end_index() > newSize) {
578  data_clear(); // no data is left
579  else {
580  storage().erase(storage().begin() + index_of(fNominalSize), storage().end());
581  }
582  }
583 } // util::LazyVector<T,A>::resize()
584 
585 //------------------------------------------------------------------------------
586 template <typename T, typename A /* = std::vector<T>::allocator_type */>
588 {
589  data_clear();
590  fNominalSize = 0U;
591 } // util::LazyVector<T,A>::clear()
592 
593 //------------------------------------------------------------------------------
594 template <typename T, typename A /* = std::vector<T>::allocator_type */>
596 {
597  // we do not go beyond the declared size of the vector:
598  size_type const e = std::min(endIndex, size());
599  if (startIndex >= e) return;
600 
601  data_clear(); // remove the old data
602  storage().reserve(e - startIndex);
603  fFirstIndex = startIndex;
604 
605 } // util::LazyVector<T,A>::data_prepare(size_type, size_type)
606 
607 //------------------------------------------------------------------------------
608 template <typename T, typename A /* = std::vector<T>::allocator_type */>
610 {
611  // we do not go beyond the declared size of the vector:
612  size_type const e = std::min(endIndex, size());
613  if (startIndex >= e) return;
614 
615  data_clear(); // remove the old data
616  storage().resize(e - startIndex, data_defvalue());
617  fFirstIndex = startIndex;
618 
619 } // util::LazyVector<T,A>::data_init(size_type, size_type)
620 
621 //------------------------------------------------------------------------------
622 template <typename T, typename A /* = std::vector<T>::allocator_type */>
624 {
625  // this is just a dispatcher
626  if (data_empty())
627  init(pos);
628  else if (pos < data_begin_index())
629  expand_front(pos);
630  else if (pos >= data_end_index())
631  expand_back(pos);
632 }
633 
634 //------------------------------------------------------------------------------
635 template <typename T, typename A /* = std::vector<T>::allocator_type */>
637 {
638  assert(data_empty());
639  storage().assign(n, data_defvalue());
640  fFirstIndex = pos;
641  fix_size();
642 }
643 
644 //------------------------------------------------------------------------------
645 template <typename T, typename A /* = std::vector<T>::allocator_type */>
647 {
648  assert(pos < data_begin_index());
649  storage().insert(storage().begin(), data_begin_index() - pos, data_defvalue());
650  fFirstIndex = pos;
651 }
652 
653 //------------------------------------------------------------------------------
654 template <typename T, typename A /* = std::vector<T>::allocator_type */>
656 {
657  assert(pos >= data_end_index());
658  storage().resize(pos + 1U - data_begin_index(), data_defvalue());
659  fix_size();
660 }
661 
662 //------------------------------------------------------------------------------
663 template <typename T, typename A /* = std::vector<T>::allocator_type */>
665 {
666  auto const min_size = data_end_index();
667  if (fNominalSize < min_size) fNominalSize = min_size;
668 }
669 
670 //------------------------------------------------------------------------------
671 template <typename T, typename A /* = std::vector<T>::allocator_type */>
673 {
674  storage().clear();
675  fFirstIndex = storage().max_size();
676 } // util::LazyVector<T,A>::data_clear()
677 
678 //------------------------------------------------------------------------------
679 template <typename T, typename A /* = std::vector<T>::allocator_type */>
681 {
682  if (has_index(pos)) return;
683  throw std::out_of_range("Index " + std::to_string(pos) +
684  " is out of LazyVector range (size: " + std::to_string(size()) + ")");
685 } // util::LazyVector<T,A>::check_range()
686 
687 //------------------------------------------------------------------------------
688 
689 #endif // LARDATAOBJ_UTILITIES_LAZYVECTOR_H
const_pointer data_address(size_type pos) const
Returns a constant pointer to the specified element.
Definition: LazyVector.h:549
value_type operator[](size_type pos) const
Returns a copy of the specified element.
Definition: LazyVector.h:532
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
bool has_index(size_type pos) const noexcept
Returns whether the specified position is within the vector.
Definition: LazyVector.h:158
static value_type const & defaultValueType()
Returns the class default value (used when user does not specify any).
Definition: LazyVector.h:455
std::vector< TF1, typename std::vector< TF1 >::allocator_type > Data_t
Actual data storage type.
Definition: LazyVector.h:79
void data_prepare(size_type startIndex, size_type endIndex)
Prepares the vector to store elements in the specified range.
Definition: LazyVector.h:595
value_type const_get(size_type pos) const
Returns a copy of the specified element.
Definition: LazyVector.h:270
void data_clear()
Erases all stored data from the container; nominal size is not changed.
Definition: LazyVector.h:672
typename Data_t::reference reference
Definition: LazyVector.h:90
void data_init(size_type startIndex, size_type endIndex)
Allocates the specified range and stores default values for it.
Definition: LazyVector.h:609
void clear()
Removes all stored data and sets the nominal size to 0.
Definition: LazyVector.h:587
void check_range(size_type pos) const
Throws std::out_of_range if pos is not contained in the vector.
Definition: LazyVector.h:680
size_type index_of(size_type pos) const
Returns the internal storage index for the specified position.
Definition: LazyVector.h:431
void reserve(size_type n)
Allocates enough memory in storage to store n elements.
Definition: LazyVector.h:335
void expand_front(size_type pos)
Expands the storage to include the specified position behind it.
Definition: LazyVector.h:646
void shrink_to_fit()
Reduces memory usage to the amount needed by the elements with storage.
Definition: LazyVector.h:341
value_type const & data_defvalue() const
Definition: LazyVector.h:165
void fix_size()
Makes sure the nominal size is large enough to include all stored data.
Definition: LazyVector.h:664
static value_type const fDefaultDefaultValue
Default-initialised value of type value_type used as default fallback.
Definition: LazyVector.h:422
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
size_type size() const noexcept
Returns the size of the vector.
Definition: LazyVector.h:149
bool data_empty() const noexcept
Returns whether no data is actually stored.
Definition: LazyVector.h:161
bool empty() const noexcept
Returns whether the vector is empty.
Definition: LazyVector.h:152
typename Data_t::size_type size_type
Definition: LazyVector.h:88
LazyVector()=default
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
Data_t fData
Actual data storage.
Definition: LazyVector.h:415
size_type data_begin_index() const
Definition: LazyVector.h:169
Data_t & storage()
Returns the data storage.
Definition: LazyVector.h:426
typename Data_t::allocator_type allocator_type
Definition: LazyVector.h:86
void expand_back(size_type pos)
Expands the storage to include the specified position ahead of it.
Definition: LazyVector.h:655
typename Data_t::const_pointer const_pointer
Definition: LazyVector.h:93
void data_prepare(size_type n)
Prepares the vector to store n elements from 0.
Definition: LazyVector.h:376
value_type at(size_type pos) const
Returns a reference to the specified element, throws an exception if not present. ...
Definition: LazyVector.h:500
Data_t const & storage() const
Returns the data storage.
Definition: LazyVector.h:427
typename Data_t::difference_type difference_type
Definition: LazyVector.h:89
size_type fNominalSize
Alleged data size.
Definition: LazyVector.h:417
void data_init(size_type n)
Allocates and initializes n elements starting from index 0.
Definition: LazyVector.h:409
value_type const_at(size_type pos) const
Returns a reference to the specified element, throws an exception if not present. ...
Definition: LazyVector.h:237
void init(size_type pos, size_type n=1U)
Makes the first data allocation.
Definition: LazyVector.h:636
size_type fFirstIndex
First element currently stored.
Definition: LazyVector.h:418
size_type data_end_index() const
Definition: LazyVector.h:173
typename Data_t::value_type value_type
Definition: LazyVector.h:87
typename Data_t::pointer pointer
Definition: LazyVector.h:92
Char_t n[5]
void expand(size_type pos)
Expands the storage to include the specified position.
Definition: LazyVector.h:623
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
A contiguous data container expanded on write.
Definition: LazyVector.h:77
void resize(size_type newSize)
Changes the nominal size of the container.
Definition: LazyVector.h:567
Float_t e
Definition: plot.C:35
bool data_has_index(size_type pos) const
Returns the internal storage index for the specified position.
Definition: LazyVector.h:176
typename Data_t::const_reference const_reference
Definition: LazyVector.h:91
size_type data_size() const noexcept
Returns the size of data actually stored.
Definition: LazyVector.h:155
value_type fDefValue
Default value.
Definition: LazyVector.h:419