tvm
array.h
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
24 #ifndef TVM_RUNTIME_CONTAINER_ARRAY_H_
25 #define TVM_RUNTIME_CONTAINER_ARRAY_H_
26 
27 #include <algorithm>
28 #include <memory>
29 #include <utility>
30 #include <vector>
31 
32 #include "./base.h"
33 
34 namespace tvm {
35 namespace runtime {
36 
38 class ArrayNode : public Object, public InplaceArrayBase<ArrayNode, ObjectRef> {
39  public:
41  size_t size() const { return this->size_; }
42 
48  const ObjectRef at(int64_t i) const { return this->operator[](i); }
49 
51  const ObjectRef* begin() const { return static_cast<ObjectRef*>(InplaceArrayBase::AddressOf(0)); }
52 
54  const ObjectRef* end() const { return begin() + size_; }
55 
57  void clear() { ShrinkBy(size_); }
58 
64  void SetItem(int64_t i, ObjectRef item) { this->operator[](i) = std::move(item); }
65 
72  static ObjectPtr<ArrayNode> CopyFrom(int64_t cap, ArrayNode* from) {
73  int64_t size = from->size_;
74  ICHECK_GE(cap, size) << "ValueError: not enough capacity";
75  ObjectPtr<ArrayNode> p = ArrayNode::Empty(cap);
76  ObjectRef* write = p->MutableBegin();
77  ObjectRef* read = from->MutableBegin();
78  // To ensure exception safety, size is only incremented after the initialization succeeds
79  for (int64_t& i = p->size_ = 0; i < size; ++i) {
80  new (write++) ObjectRef(*read++);
81  }
82  return p;
83  }
84 
91  static ObjectPtr<ArrayNode> MoveFrom(int64_t cap, ArrayNode* from) {
92  int64_t size = from->size_;
93  ICHECK_GE(cap, size) << "ValueError: not enough capacity";
94  ObjectPtr<ArrayNode> p = ArrayNode::Empty(cap);
95  ObjectRef* write = p->MutableBegin();
96  ObjectRef* read = from->MutableBegin();
97  // To ensure exception safety, size is only incremented after the initialization succeeds
98  for (int64_t& i = p->size_ = 0; i < size; ++i) {
99  new (write++) ObjectRef(std::move(*read++));
100  }
101  from->size_ = 0;
102  return p;
103  }
104 
111  static ObjectPtr<ArrayNode> CreateRepeated(int64_t n, const ObjectRef& val) {
112  ObjectPtr<ArrayNode> p = ArrayNode::Empty(n);
113  ObjectRef* itr = p->MutableBegin();
114  for (int64_t& i = p->size_ = 0; i < n; ++i) {
115  new (itr++) ObjectRef(val);
116  }
117  return p;
118  }
119 
120  static constexpr const uint32_t _type_index = TypeIndex::kRuntimeArray;
121  static constexpr const char* _type_key = "Array";
123 
124  private:
126  size_t GetSize() const { return this->size_; }
127 
129  ObjectRef* MutableBegin() const {
130  return static_cast<ObjectRef*>(InplaceArrayBase::AddressOf(0));
131  }
132 
134  ObjectRef* MutableEnd() const { return MutableBegin() + size_; }
135 
141  static ObjectPtr<ArrayNode> Empty(int64_t n = kInitSize) {
142  ICHECK_GE(n, 0);
143  ObjectPtr<ArrayNode> p = make_inplace_array_object<ArrayNode, ObjectRef>(n);
144  p->capacity_ = n;
145  p->size_ = 0;
146  return p;
147  }
148 
157  template <typename IterType>
158  ArrayNode* InitRange(int64_t idx, IterType first, IterType last) {
159  ObjectRef* itr = MutableBegin() + idx;
160  for (; first != last; ++first) {
161  ObjectRef ref = *first;
162  new (itr++) ObjectRef(std::move(ref));
163  }
164  return this;
165  }
166 
174  ArrayNode* MoveElementsLeft(int64_t dst, int64_t src_begin, int64_t src_end) {
175  ObjectRef* from = MutableBegin() + src_begin;
176  ObjectRef* to = MutableBegin() + dst;
177  while (src_begin++ != src_end) {
178  *to++ = std::move(*from++);
179  }
180  return this;
181  }
182 
190  ArrayNode* MoveElementsRight(int64_t dst, int64_t src_begin, int64_t src_end) {
191  ObjectRef* from = MutableBegin() + src_end;
192  ObjectRef* to = MutableBegin() + (src_end - src_begin + dst);
193  while (src_begin++ != src_end) {
194  *--to = std::move(*--from);
195  }
196  return this;
197  }
198 
205  ArrayNode* EnlargeBy(int64_t delta, const ObjectRef& val = ObjectRef(nullptr)) {
206  ObjectRef* itr = MutableEnd();
207  while (delta-- > 0) {
208  new (itr++) ObjectRef(val);
209  ++size_;
210  }
211  return this;
212  }
213 
219  ArrayNode* ShrinkBy(int64_t delta) {
220  ObjectRef* itr = MutableEnd();
221  while (delta-- > 0) {
222  (--itr)->ObjectRef::~ObjectRef();
223  --size_;
224  }
225  return this;
226  }
227 
229  int64_t size_;
230 
232  int64_t capacity_;
233 
235  static constexpr int64_t kInitSize = 4;
236 
238  static constexpr int64_t kIncFactor = 2;
239 
240  // CRTP parent class
242 
243  // Reference class
244  template <typename, typename>
245  friend class Array;
246 
247  // To specialize make_object<ArrayNode>
248  friend ObjectPtr<ArrayNode> make_object<>();
249 };
250 
268 template <typename T,
269  typename = typename std::enable_if<std::is_base_of<ObjectRef, T>::value>::type>
270 class Array : public ObjectRef {
271  public:
272  using value_type = T;
273  // constructors
277  Array() { data_ = ArrayNode::Empty(); }
278 
283  Array(Array<T>&& other) : ObjectRef() { // NOLINT(*)
284  data_ = std::move(other.data_);
285  }
286 
291  Array(const Array<T>& other) : ObjectRef() { // NOLINT(*)
292  data_ = other.data_;
293  }
294 
299  explicit Array(ObjectPtr<Object> n) : ObjectRef(n) {}
300 
307  template <typename IterType>
308  Array(IterType first, IterType last) {
309  Assign(first, last);
310  }
311 
316  Array(std::initializer_list<T> init) { // NOLINT(*)
317  Assign(init.begin(), init.end());
318  }
319 
324  Array(const std::vector<T>& init) { // NOLINT(*)
325  Assign(init.begin(), init.end());
326  }
327 
333  explicit Array(const size_t n, const T& val) { data_ = ArrayNode::CreateRepeated(n, val); }
334 
341  data_ = std::move(other.data_);
342  return *this;
343  }
344 
350  Array<T>& operator=(const Array<T>& other) {
351  data_ = other.data_;
352  return *this;
353  }
354 
355  public:
356  // iterators
357  struct ValueConverter {
358  using ResultType = T;
359  static T convert(const ObjectRef& n) { return DowncastNoCheck<T>(n); }
360  };
361 
364 
366  iterator begin() const { return iterator(GetArrayNode()->begin()); }
367 
369  iterator end() const { return iterator(GetArrayNode()->end()); }
370 
373  // ArrayNode::end() is never nullptr
374  return reverse_iterator(GetArrayNode()->end() - 1);
375  }
376 
379  // ArrayNode::begin() is never nullptr
380  return reverse_iterator(GetArrayNode()->begin() - 1);
381  }
382 
383  public:
384  // const methods in std::vector
390  const T operator[](int64_t i) const {
391  ArrayNode* p = GetArrayNode();
392  ICHECK(p != nullptr) << "ValueError: cannot index a null array";
393  ICHECK(0 <= i && i < p->size_)
394  << "IndexError: indexing " << i << " on an array of size " << p->size_;
395  return DowncastNoCheck<T>(*(p->begin() + i));
396  }
397 
399  size_t size() const {
400  ArrayNode* p = GetArrayNode();
401  return p == nullptr ? 0 : GetArrayNode()->size_;
402  }
403 
405  size_t capacity() const {
406  ArrayNode* p = GetArrayNode();
407  return p == nullptr ? 0 : GetArrayNode()->capacity_;
408  }
409 
411  bool empty() const { return size() == 0; }
412 
414  const T front() const {
415  ArrayNode* p = GetArrayNode();
416  ICHECK(p != nullptr) << "ValueError: cannot index a null array";
417  ICHECK_GT(p->size_, 0) << "IndexError: cannot index an empty array";
418  return DowncastNoCheck<T>(*(p->begin()));
419  }
420 
422  const T back() const {
423  ArrayNode* p = GetArrayNode();
424  ICHECK(p != nullptr) << "ValueError: cannot index a null array";
425  ICHECK_GT(p->size_, 0) << "IndexError: cannot index an empty array";
426  return DowncastNoCheck<T>(*(p->end() - 1));
427  }
428 
429  public:
430  // mutation in std::vector, implements copy-on-write
431 
436  void push_back(const T& item) {
437  ArrayNode* p = CopyOnWrite(1);
438  p->EmplaceInit(p->size_++, item);
439  }
440 
446  void insert(iterator position, const T& val) {
447  ICHECK(data_ != nullptr) << "ValueError: cannot insert a null array";
448  int64_t idx = std::distance(begin(), position);
449  int64_t size = GetArrayNode()->size_;
450  auto addr = CopyOnWrite(1) //
451  ->EnlargeBy(1) //
452  ->MoveElementsRight(idx + 1, idx, size) //
453  ->MutableBegin();
454  new (addr + idx) ObjectRef(val);
455  }
456 
463  template <typename IterType>
464  void insert(iterator position, IterType first, IterType last) {
465  if (first == last) {
466  return;
467  }
468  ICHECK(data_ != nullptr) << "ValueError: cannot insert a null array";
469  int64_t idx = std::distance(begin(), position);
470  int64_t size = GetArrayNode()->size_;
471  int64_t numel = std::distance(first, last);
472  CopyOnWrite(numel)
473  ->EnlargeBy(numel)
474  ->MoveElementsRight(idx + numel, idx, size)
475  ->InitRange(idx, first, last);
476  }
477 
479  void pop_back() {
480  ICHECK(data_ != nullptr) << "ValueError: cannot pop_back because array is null";
481  int64_t size = GetArrayNode()->size_;
482  ICHECK_GT(size, 0) << "ValueError: cannot pop_back because array is empty";
483  CopyOnWrite()->ShrinkBy(1);
484  }
485 
490  void erase(iterator position) {
491  ICHECK(data_ != nullptr) << "ValueError: cannot erase a null array";
492  int64_t st = std::distance(begin(), position);
493  int64_t size = GetArrayNode()->size_;
494  ICHECK(0 <= st && st < size) << "ValueError: cannot erase at index " << st
495  << ", because Array size is " << size;
496  CopyOnWrite() //
497  ->MoveElementsLeft(st, st + 1, size) //
498  ->ShrinkBy(1);
499  }
500 
506  void erase(iterator first, iterator last) {
507  if (first == last) {
508  return;
509  }
510  ICHECK(data_ != nullptr) << "ValueError: cannot erase a null array";
511  int64_t size = GetArrayNode()->size_;
512  int64_t st = std::distance(begin(), first);
513  int64_t ed = std::distance(begin(), last);
514  ICHECK_LT(st, ed) << "ValueError: cannot erase array in range [" << st << ", " << ed << ")";
515  ICHECK(0 <= st && st <= size && 0 <= ed && ed <= size)
516  << "ValueError: cannot erase array in range [" << st << ", " << ed << ")"
517  << ", because array size is " << size;
518  CopyOnWrite() //
519  ->MoveElementsLeft(st, ed, size) //
520  ->ShrinkBy(ed - st);
521  }
522 
527  void resize(int64_t n) {
528  ICHECK_GE(n, 0) << "ValueError: cannot resize an Array to negative size";
529  if (data_ == nullptr) {
530  SwitchContainer(n);
531  return;
532  }
533  int64_t size = GetArrayNode()->size_;
534  if (size < n) {
535  CopyOnWrite(n - size)->EnlargeBy(n - size);
536  } else if (size > n) {
537  CopyOnWrite()->ShrinkBy(size - n);
538  }
539  }
540 
545  void reserve(int64_t n) {
546  if (data_ == nullptr || n > GetArrayNode()->capacity_) {
547  SwitchContainer(n);
548  }
549  }
550 
552  void clear() {
553  if (data_ != nullptr) {
554  ArrayNode* p = CopyOnWrite();
555  p->clear();
556  }
557  }
558 
559  public:
560  // Array's own methods
561 
567  void Set(int64_t i, T value) {
568  ArrayNode* p = this->CopyOnWrite();
569  ICHECK(0 <= i && i < p->size_)
570  << "IndexError: indexing " << i << " on an array of size " << p->size_;
571  *(p->MutableBegin() + i) = std::move(value);
572  }
573 
575  ArrayNode* GetArrayNode() const { return static_cast<ArrayNode*>(data_.get()); }
576 
583  template <typename F>
584  void MutateByApply(F fmutate) {
585  if (data_ == nullptr) {
586  return;
587  }
588  struct StackFrame {
589  ArrayNode* p;
590  ObjectRef* itr;
591  int64_t i;
592  int64_t size;
593  };
594  std::unique_ptr<StackFrame> s = std::make_unique<StackFrame>();
595  s->p = GetArrayNode();
596  s->itr = s->p->MutableBegin();
597  s->i = 0;
598  s->size = s->p->size_;
599  if (!data_.unique()) {
600  // Loop invariant: keeps iterating when
601  // 1) data is not unique
602  // 2) no elements are actually mutated yet
603  for (; s->i < s->size; ++s->i, ++s->itr) {
604  T new_elem = fmutate(DowncastNoCheck<T>(*s->itr));
605  // do nothing when there is no mutation
606  if (new_elem.same_as(*s->itr)) {
607  continue;
608  }
609  // loop invariant breaks when the first real mutation happens
610  // we copy the elements into a new unique array
611  ObjectPtr<ArrayNode> copy = ArrayNode::CopyFrom(s->p->capacity_, s->p);
612  s->itr = copy->MutableBegin() + (s->i++);
613  *s->itr++ = std::move(new_elem);
614  data_ = std::move(copy);
615  // make sure `data_` is unique and break
616  break;
617  }
618  }
619  // when execution comes to this line, it is guaranteed that either
620  // 1) i == size
621  // or 2) data_.unique() is true
622  for (; s->i < s->size; ++s->i, ++s->itr) {
623  *s->itr = std::move(fmutate(std::move(DowncastNoCheck<T>(std::move(*s->itr)))));
624  }
625  }
626 
633  template <typename IterType>
634  void Assign(IterType first, IterType last) {
635  int64_t cap = std::distance(first, last);
636  ICHECK_GE(cap, 0) << "ValueError: cannot construct an Array of negative size";
637  ArrayNode* p = GetArrayNode();
638  if (p != nullptr && data_.unique() && p->capacity_ >= cap) {
639  // do not have to make new space
640  p->clear();
641  } else {
642  // create new space
643  data_ = ArrayNode::Empty(cap);
644  p = GetArrayNode();
645  }
646  // To ensure exception safety, size is only incremented after the initialization succeeds
647  ObjectRef* itr = p->MutableBegin();
648  for (int64_t& i = p->size_ = 0; i < cap; ++i, ++first, ++itr) {
649  new (itr) ObjectRef(*first);
650  }
651  }
652 
662  if (data_ == nullptr) {
663  return SwitchContainer(ArrayNode::kInitSize);
664  }
665  if (!data_.unique()) {
666  return SwitchContainer(capacity());
667  }
668  return static_cast<ArrayNode*>(data_.get());
669  }
670 
673 
674  private:
680  ArrayNode* CopyOnWrite(int64_t reserve_extra) {
681  ArrayNode* p = GetArrayNode();
682  if (p == nullptr) {
683  // necessary to get around the constexpr address issue before c++17
684  const int64_t kInitSize = ArrayNode::kInitSize;
685  return SwitchContainer(std::max(kInitSize, reserve_extra));
686  }
687  if (p->capacity_ >= p->size_ + reserve_extra) {
688  return CopyOnWrite();
689  }
690  int64_t cap = p->capacity_ * ArrayNode::kIncFactor;
691  cap = std::max(cap, p->size_ + reserve_extra);
692  return SwitchContainer(cap);
693  }
694 
699  ArrayNode* SwitchContainer(int64_t capacity) {
700  if (data_ == nullptr) {
701  data_ = ArrayNode::Empty(capacity);
702  } else if (data_.unique()) {
703  data_ = ArrayNode::MoveFrom(capacity, GetArrayNode());
704  } else {
705  data_ = ArrayNode::CopyFrom(capacity, GetArrayNode());
706  }
707  return static_cast<ArrayNode*>(data_.get());
708  }
709 };
710 
717 template <typename T,
718  typename = typename std::enable_if<std::is_base_of<ObjectRef, T>::value>::type>
719 inline Array<T> Concat(Array<T> lhs, const Array<T>& rhs) {
720  for (const auto& x : rhs) {
721  lhs.push_back(x);
722  }
723  return std::move(lhs);
724 }
725 
726 // Specialize make_object<ArrayNode> to make sure it is correct.
727 template <>
729  return ArrayNode::Empty();
730 }
731 
732 } // namespace runtime
733 
734 // expose the functions to the root namespace.
735 using runtime::Array;
736 using runtime::ArrayNode;
737 } // namespace tvm
738 
739 #endif // TVM_RUNTIME_CONTAINER_ARRAY_H_
void reserve(int64_t n)
Make sure the list has the capacity of at least n.
Definition: array.h:545
static constexpr const uint32_t _type_index
Definition: array.h:120
array node content in array
Definition: array.h:38
const T operator[](int64_t i) const
Immutably read i-th element from array.
Definition: array.h:390
Managed reference to IterSplitExprNode.
Definition: iter_affine_map.h:187
A custom smart pointer for Object.
Definition: object.h:358
void insert(iterator position, IterType first, IterType last)
Insert a range of elements into the given position.
Definition: array.h:464
Array(Array< T > &&other)
move constructor
Definition: array.h:283
TVM_DECLARE_FINAL_OBJECT_INFO(ArrayNode, Object)
T ResultType
Definition: array.h:358
void SetItem(int64_t i, ObjectRef item)
Set i-th element of the array in-place.
Definition: array.h:64
const T back() const
Definition: array.h:422
runtime implementation for LibTorch/TorchScript.
Definition: analyzer.h:36
static ObjectPtr< ArrayNode > CopyFrom(int64_t cap, ArrayNode *from)
Constructs a container and copy from another.
Definition: array.h:72
bool empty() const
Definition: array.h:411
iterator adapter that adapts TIter to return another type.
Definition: base.h:241
void resize(int64_t n)
Resize the array.
Definition: array.h:527
size_t size() const
Definition: array.h:41
Base utilities for common POD(plain old data) container types.
const ObjectRef * end() const
Definition: array.h:54
Array(const size_t n, const T &val)
Constructs a container with n elements. Each element is a copy of val.
Definition: array.h:333
friend ObjectPtr< ArrayNode > make_object()
Definition: array.h:728
ArrayNode * GetArrayNode() const
Definition: array.h:575
base class of all object containers.
Definition: object.h:167
Array()
default constructor
Definition: array.h:277
void MutateByApply(F fmutate)
Helper function to apply fmutate to mutate an array.
Definition: array.h:584
void Set(int64_t i, T value)
set i-th element of the array.
Definition: array.h:567
Array< T > & operator=(const Array< T > &other)
copy assign operator
Definition: array.h:350
void push_back(const T &item)
push a new item to the back of the list
Definition: array.h:436
Base template for classes with array like memory layout.
Definition: base.h:100
const ObjectRef * begin() const
Definition: array.h:51
void insert(iterator position, const T &val)
Insert an element into the given position.
Definition: array.h:446
size_t size() const
Definition: array.h:399
const T front() const
Definition: array.h:414
void erase(iterator position)
Erase an element on the given position.
Definition: array.h:490
Array, container representing a contiguous sequence of ObjectRefs.
Definition: array.h:270
bool unique() const
Definition: object.h:862
ObjectPtr< Object > data_
Internal pointer that backs the reference.
Definition: object.h:574
Array(const Array< T > &other)
copy constructor
Definition: array.h:291
void erase(iterator first, iterator last)
Erase a given range of elements.
Definition: array.h:506
PrimExpr max(PrimExpr a, PrimExpr b, Span span=Span())
take maximum of two values
void clear()
Release reference to all the elements.
Definition: array.h:57
reverse_iterator rbegin() const
Definition: array.h:372
void * AddressOf(size_t idx) const
Return the raw pointer to the element at idx.
Definition: base.h:169
Array(IterType first, IterType last)
Constructor from iterator.
Definition: array.h:308
iterator end() const
Definition: array.h:369
Base class of all object reference.
Definition: object.h:511
Array(const std::vector< T > &init)
constructor from vector
Definition: array.h:324
iterator begin() const
Definition: array.h:366
ArrayNode * CopyOnWrite()
Copy on write semantics Do nothing if current handle is the unique copy of the array. Otherwise make a new copy of the array to ensure the current handle hold a unique copy.
Definition: array.h:661
Array< T > & operator=(Array< T > &&other)
move assign operator
Definition: array.h:340
iterator adapter that adapts TIter to return another type.
Definition: base.h:188
static constexpr const char * _type_key
Definition: array.h:121
runtime::Array.
Definition: object.h:68
const ObjectRef at(int64_t i) const
Read i-th element from array.
Definition: array.h:48
size_t capacity() const
Definition: array.h:405
const ObjectRef & operator[](size_t idx) const
Access element at index.
Definition: base.h:107
void pop_back()
Remove the last item of the list.
Definition: array.h:479
reverse_iterator rend() const
Definition: array.h:378
void Assign(IterType first, IterType last)
reset the array to content from iterator.
Definition: array.h:634
static T convert(const ObjectRef &n)
Definition: array.h:359
Array(std::initializer_list< T > init)
constructor from initializer list
Definition: array.h:316
void EmplaceInit(size_t idx, Args &&... args)
Construct a value in place with the arguments.
Definition: base.h:149
static ObjectPtr< ArrayNode > CreateRepeated(int64_t n, const ObjectRef &val)
Constructs a container with n elements. Each element is a copy of val.
Definition: array.h:111
Array(ObjectPtr< Object > n)
constructor from pointer
Definition: array.h:299
Array< T > Concat(Array< T > lhs, const Array< T > &rhs)
Concat two Arrays.
Definition: array.h:719
void clear()
Release reference to all the elements.
Definition: array.h:552
static ObjectPtr< ArrayNode > MoveFrom(int64_t cap, ArrayNode *from)
Constructs a container and move from another.
Definition: array.h:91