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