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