• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SOURCE_UTIL_SMALL_VECTOR_H_
16 #define SOURCE_UTIL_SMALL_VECTOR_H_
17 
18 #include <cassert>
19 #include <iostream>
20 #include <memory>
21 #include <utility>
22 #include <vector>
23 
24 #include "source/util/make_unique.h"
25 
26 namespace spvtools {
27 namespace utils {
28 
29 // The |SmallVector| class is intended to be a drop-in replacement for
30 // |std::vector|.  The difference is in the implementation. A |SmallVector| is
31 // optimized for when the number of elements in the vector are small.  Small is
32 // defined by the template parameter |small_size|.
33 //
34 // Note that |SmallVector| is not always faster than an |std::vector|, so you
35 // should experiment with different values for |small_size| and compare to
36 // using and |std::vector|.
37 //
38 // TODO: I have implemented the public member functions from |std::vector| that
39 // I needed.  If others are needed they should be implemented. Do not implement
40 // public member functions that are not defined by std::vector.
41 template <class T, size_t small_size>
42 class SmallVector {
43  public:
44   using iterator = T*;
45   using const_iterator = const T*;
46 
SmallVector()47   SmallVector()
48       : size_(0),
49         small_data_(reinterpret_cast<T*>(buffer)),
50         large_data_(nullptr) {}
51 
SmallVector(const SmallVector & that)52   SmallVector(const SmallVector& that) : SmallVector() { *this = that; }
53 
SmallVector(SmallVector && that)54   SmallVector(SmallVector&& that) : SmallVector() { *this = std::move(that); }
55 
SmallVector(const std::vector<T> & vec)56   SmallVector(const std::vector<T>& vec) : SmallVector() {
57     if (vec.size() > small_size) {
58       large_data_ = MakeUnique<std::vector<T>>(vec);
59     } else {
60       size_ = vec.size();
61       for (uint32_t i = 0; i < size_; i++) {
62         new (small_data_ + i) T(vec[i]);
63       }
64     }
65   }
66 
67   template <class InputIt>
SmallVector(InputIt first,InputIt last)68   SmallVector(InputIt first, InputIt last) : SmallVector() {
69     insert(end(), first, last);
70   }
71 
SmallVector(std::vector<T> && vec)72   SmallVector(std::vector<T>&& vec) : SmallVector() {
73     if (vec.size() > small_size) {
74       large_data_ = MakeUnique<std::vector<T>>(std::move(vec));
75     } else {
76       size_ = vec.size();
77       for (uint32_t i = 0; i < size_; i++) {
78         new (small_data_ + i) T(std::move(vec[i]));
79       }
80     }
81     vec.clear();
82   }
83 
SmallVector(std::initializer_list<T> init_list)84   SmallVector(std::initializer_list<T> init_list) : SmallVector() {
85     if (init_list.size() < small_size) {
86       for (auto it = init_list.begin(); it != init_list.end(); ++it) {
87         new (small_data_ + (size_++)) T(std::move(*it));
88       }
89     } else {
90       large_data_ = MakeUnique<std::vector<T>>(std::move(init_list));
91     }
92   }
93 
SmallVector(size_t s,const T & v)94   SmallVector(size_t s, const T& v) : SmallVector() { resize(s, v); }
95 
~SmallVector()96   virtual ~SmallVector() {
97     for (T* p = small_data_; p < small_data_ + size_; ++p) {
98       p->~T();
99     }
100   }
101 
102   SmallVector& operator=(const SmallVector& that) {
103     assert(small_data_);
104     if (that.large_data_) {
105       if (large_data_) {
106         *large_data_ = *that.large_data_;
107       } else {
108         large_data_ = MakeUnique<std::vector<T>>(*that.large_data_);
109       }
110     } else {
111       large_data_.reset(nullptr);
112       size_t i = 0;
113       // Do a copy for any element in |this| that is already constructed.
114       for (; i < size_ && i < that.size_; ++i) {
115         small_data_[i] = that.small_data_[i];
116       }
117 
118       if (i >= that.size_) {
119         // If the size of |this| becomes smaller after the assignment, then
120         // destroy any extra elements.
121         for (; i < size_; ++i) {
122           small_data_[i].~T();
123         }
124       } else {
125         // If the size of |this| becomes larger after the assignement, copy
126         // construct the new elements that are needed.
127         for (; i < that.size_; ++i) {
128           new (small_data_ + i) T(that.small_data_[i]);
129         }
130       }
131       size_ = that.size_;
132     }
133     return *this;
134   }
135 
136   SmallVector& operator=(SmallVector&& that) {
137     if (that.large_data_) {
138       large_data_.reset(that.large_data_.release());
139     } else {
140       large_data_.reset(nullptr);
141       size_t i = 0;
142       // Do a move for any element in |this| that is already constructed.
143       for (; i < size_ && i < that.size_; ++i) {
144         small_data_[i] = std::move(that.small_data_[i]);
145       }
146 
147       if (i >= that.size_) {
148         // If the size of |this| becomes smaller after the assignment, then
149         // destroy any extra elements.
150         for (; i < size_; ++i) {
151           small_data_[i].~T();
152         }
153       } else {
154         // If the size of |this| becomes larger after the assignement, move
155         // construct the new elements that are needed.
156         for (; i < that.size_; ++i) {
157           new (small_data_ + i) T(std::move(that.small_data_[i]));
158         }
159       }
160       size_ = that.size_;
161     }
162 
163     // Reset |that| because all of the data has been moved to |this|.
164     that.DestructSmallData();
165     return *this;
166   }
167 
168   template <class OtherVector>
169   friend bool operator==(const SmallVector& lhs, const OtherVector& rhs) {
170     if (lhs.size() != rhs.size()) {
171       return false;
172     }
173 
174     auto rit = rhs.begin();
175     for (auto lit = lhs.begin(); lit != lhs.end(); ++lit, ++rit) {
176       if (*lit != *rit) {
177         return false;
178       }
179     }
180     return true;
181   }
182 
183 // Avoid infinite recursion from rewritten operators in C++20
184 #if __cplusplus <= 201703L
185   friend bool operator==(const std::vector<T>& lhs, const SmallVector& rhs) {
186     return rhs == lhs;
187   }
188 #endif
189 
190   friend bool operator!=(const SmallVector& lhs, const std::vector<T>& rhs) {
191     return !(lhs == rhs);
192   }
193 
194   friend bool operator!=(const std::vector<T>& lhs, const SmallVector& rhs) {
195     return rhs != lhs;
196   }
197 
198   T& operator[](size_t i) {
199     if (!large_data_) {
200       return small_data_[i];
201     } else {
202       return (*large_data_)[i];
203     }
204   }
205 
206   const T& operator[](size_t i) const {
207     if (!large_data_) {
208       return small_data_[i];
209     } else {
210       return (*large_data_)[i];
211     }
212   }
213 
size()214   size_t size() const {
215     if (!large_data_) {
216       return size_;
217     } else {
218       return large_data_->size();
219     }
220   }
221 
begin()222   iterator begin() {
223     if (large_data_) {
224       return large_data_->data();
225     } else {
226       return small_data_;
227     }
228   }
229 
begin()230   const_iterator begin() const {
231     if (large_data_) {
232       return large_data_->data();
233     } else {
234       return small_data_;
235     }
236   }
237 
cbegin()238   const_iterator cbegin() const { return begin(); }
239 
end()240   iterator end() {
241     if (large_data_) {
242       return large_data_->data() + large_data_->size();
243     } else {
244       return small_data_ + size_;
245     }
246   }
247 
end()248   const_iterator end() const {
249     if (large_data_) {
250       return large_data_->data() + large_data_->size();
251     } else {
252       return small_data_ + size_;
253     }
254   }
255 
cend()256   const_iterator cend() const { return end(); }
257 
data()258   T* data() { return begin(); }
259 
data()260   const T* data() const { return cbegin(); }
261 
front()262   T& front() { return (*this)[0]; }
263 
front()264   const T& front() const { return (*this)[0]; }
265 
erase(const_iterator pos)266   iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
267 
erase(const_iterator first,const_iterator last)268   iterator erase(const_iterator first, const_iterator last) {
269     if (large_data_) {
270       size_t start_index = first - large_data_->data();
271       size_t end_index = last - large_data_->data();
272       auto r = large_data_->erase(large_data_->begin() + start_index,
273                                   large_data_->begin() + end_index);
274       return large_data_->data() + (r - large_data_->begin());
275     }
276 
277     // Since C++11, std::vector has |const_iterator| for the parameters, so I
278     // follow that.  However, I need iterators to modify the current container,
279     // which is not const.  This is why I cast away the const.
280     iterator f = const_cast<iterator>(first);
281     iterator l = const_cast<iterator>(last);
282     iterator e = end();
283 
284     size_t num_of_del_elements = last - first;
285     iterator ret = f;
286     if (first == last) {
287       return ret;
288     }
289 
290     // Move |last| and any elements after it their earlier position.
291     while (l != e) {
292       *f = std::move(*l);
293       ++f;
294       ++l;
295     }
296 
297     // Destroy the elements that were supposed to be deleted.
298     while (f != l) {
299       f->~T();
300       ++f;
301     }
302 
303     // Update the size.
304     size_ -= num_of_del_elements;
305     return ret;
306   }
307 
push_back(const T & value)308   void push_back(const T& value) {
309     if (!large_data_ && size_ == small_size) {
310       MoveToLargeData();
311     }
312 
313     if (large_data_) {
314       large_data_->push_back(value);
315       return;
316     }
317 
318     new (small_data_ + size_) T(value);
319     ++size_;
320   }
321 
push_back(T && value)322   void push_back(T&& value) {
323     if (!large_data_ && size_ == small_size) {
324       MoveToLargeData();
325     }
326 
327     if (large_data_) {
328       large_data_->push_back(std::move(value));
329       return;
330     }
331 
332     new (small_data_ + size_) T(std::move(value));
333     ++size_;
334   }
335 
pop_back()336   void pop_back() {
337     if (large_data_) {
338       large_data_->pop_back();
339     } else {
340       --size_;
341       small_data_[size_].~T();
342     }
343   }
344 
345   template <class InputIt>
insert(iterator pos,InputIt first,InputIt last)346   iterator insert(iterator pos, InputIt first, InputIt last) {
347     size_t element_idx = (pos - begin());
348     size_t num_of_new_elements = std::distance(first, last);
349     size_t new_size = size_ + num_of_new_elements;
350     if (!large_data_ && new_size > small_size) {
351       MoveToLargeData();
352     }
353 
354     if (large_data_) {
355       typename std::vector<T>::iterator new_pos =
356           large_data_->begin() + element_idx;
357       large_data_->insert(new_pos, first, last);
358       return begin() + element_idx;
359     }
360 
361     // Move |pos| and all of the elements after it over |num_of_new_elements|
362     // places.  We start at the end and work backwards, to make sure we do not
363     // overwrite data that we have not moved yet.
364     for (iterator i = begin() + new_size - 1, j = end() - 1; j >= pos;
365          --i, --j) {
366       if (i >= begin() + size_) {
367         new (i) T(std::move(*j));
368       } else {
369         *i = std::move(*j);
370       }
371     }
372 
373     // Copy the new elements into position.
374     iterator p = pos;
375     for (; first != last; ++p, ++first) {
376       if (p >= small_data_ + size_) {
377         new (p) T(*first);
378       } else {
379         *p = *first;
380       }
381     }
382 
383     // Update the size.
384     size_ += num_of_new_elements;
385     return pos;
386   }
387 
empty()388   bool empty() const {
389     if (large_data_) {
390       return large_data_->empty();
391     }
392     return size_ == 0;
393   }
394 
clear()395   void clear() {
396     if (large_data_) {
397       large_data_->clear();
398     } else {
399       DestructSmallData();
400     }
401   }
402 
403   template <class... Args>
emplace_back(Args &&...args)404   void emplace_back(Args&&... args) {
405     if (!large_data_ && size_ == small_size) {
406       MoveToLargeData();
407     }
408 
409     if (large_data_) {
410       large_data_->emplace_back(std::forward<Args>(args)...);
411     } else {
412       new (small_data_ + size_) T(std::forward<Args>(args)...);
413       ++size_;
414     }
415   }
416 
resize(size_t new_size,const T & v)417   void resize(size_t new_size, const T& v) {
418     if (!large_data_ && new_size > small_size) {
419       MoveToLargeData();
420     }
421 
422     if (large_data_) {
423       large_data_->resize(new_size, v);
424       return;
425     }
426 
427     // If |new_size| < |size_|, then destroy the extra elements.
428     for (size_t i = new_size; i < size_; ++i) {
429       small_data_[i].~T();
430     }
431 
432     // If |new_size| > |size_|, the copy construct the new elements.
433     for (size_t i = size_; i < new_size; ++i) {
434       new (small_data_ + i) T(v);
435     }
436 
437     // Update the size.
438     size_ = new_size;
439   }
440 
441  private:
442   // Moves all of the element from |small_data_| into a new std::vector that can
443   // be access through |large_data|.
MoveToLargeData()444   void MoveToLargeData() {
445     assert(!large_data_);
446     large_data_ = MakeUnique<std::vector<T>>();
447     for (size_t i = 0; i < size_; ++i) {
448       large_data_->emplace_back(std::move(small_data_[i]));
449     }
450     DestructSmallData();
451   }
452 
453   // Destroys all of the elements in |small_data_| that have been constructed.
DestructSmallData()454   void DestructSmallData() {
455     for (size_t i = 0; i < size_; ++i) {
456       small_data_[i].~T();
457     }
458     size_ = 0;
459   }
460 
461   // The number of elements in |small_data_| that have been constructed.
462   size_t size_;
463 
464   // The pointed used to access the array of elements when the number of
465   // elements is small.
466   T* small_data_;
467 
468   // The actual data used to store the array elements.  It must never be used
469   // directly, but must only be accessed through |small_data_|.
470   typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type
471       buffer[small_size];
472 
473   // A pointer to a vector that is used to store the elements of the vector when
474   // this size exceeds |small_size|.  If |large_data_| is nullptr, then the data
475   // is stored in |small_data_|.  Otherwise, the data is stored in
476   // |large_data_|.
477   std::unique_ptr<std::vector<T>> large_data_;
478 };  // namespace utils
479 
480 }  // namespace utils
481 }  // namespace spvtools
482 
483 #endif  // SOURCE_UTIL_SMALL_VECTOR_H_
484