1 /* 2 * Copyright 2019-2021 Hans-Kristian Arntzen 3 * SPDX-License-Identifier: Apache-2.0 OR MIT 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * At your option, you may choose to accept this material under either: 20 * 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or 21 * 2. The MIT License, found at <http://opensource.org/licenses/MIT>. 22 */ 23 24 #ifndef SPIRV_CROSS_CONTAINERS_HPP 25 #define SPIRV_CROSS_CONTAINERS_HPP 26 27 #include "spirv_cross_error_handling.hpp" 28 #include <algorithm> 29 #include <functional> 30 #include <iterator> 31 #include <limits> 32 #include <memory> 33 #include <stack> 34 #include <stddef.h> 35 #include <stdint.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <type_traits> 39 #include <unordered_map> 40 #include <unordered_set> 41 #include <utility> 42 #include <vector> 43 44 #ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE 45 #define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE 46 #else 47 #define SPIRV_CROSS_NAMESPACE spirv_cross 48 #endif 49 50 namespace SPIRV_CROSS_NAMESPACE 51 { 52 #ifndef SPIRV_CROSS_FORCE_STL_TYPES 53 // std::aligned_storage does not support size == 0, so roll our own. 54 template <typename T, size_t N> 55 class AlignedBuffer 56 { 57 public: data()58 T *data() 59 { 60 #if defined(_MSC_VER) && _MSC_VER < 1900 61 // MSVC 2013 workarounds, sigh ... 62 // Only use this workaround on MSVC 2013 due to some confusion around default initialized unions. 63 // Spec seems to suggest the memory will be zero-initialized, which is *not* what we want. 64 return reinterpret_cast<T *>(u.aligned_char); 65 #else 66 return reinterpret_cast<T *>(aligned_char); 67 #endif 68 } 69 70 private: 71 #if defined(_MSC_VER) && _MSC_VER < 1900 72 // MSVC 2013 workarounds, sigh ... 73 union 74 { 75 char aligned_char[sizeof(T) * N]; 76 double dummy_aligner; 77 } u; 78 #else 79 alignas(T) char aligned_char[sizeof(T) * N]; 80 #endif 81 }; 82 83 template <typename T> 84 class AlignedBuffer<T, 0> 85 { 86 public: data()87 T *data() 88 { 89 return nullptr; 90 } 91 }; 92 93 // An immutable version of SmallVector which erases type information about storage. 94 template <typename T> 95 class VectorView 96 { 97 public: operator [](size_t i)98 T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT 99 { 100 return ptr[i]; 101 } 102 operator [](size_t i) const103 const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT 104 { 105 return ptr[i]; 106 } 107 empty() const108 bool empty() const SPIRV_CROSS_NOEXCEPT 109 { 110 return buffer_size == 0; 111 } 112 size() const113 size_t size() const SPIRV_CROSS_NOEXCEPT 114 { 115 return buffer_size; 116 } 117 data()118 T *data() SPIRV_CROSS_NOEXCEPT 119 { 120 return ptr; 121 } 122 data() const123 const T *data() const SPIRV_CROSS_NOEXCEPT 124 { 125 return ptr; 126 } 127 begin()128 T *begin() SPIRV_CROSS_NOEXCEPT 129 { 130 return ptr; 131 } 132 end()133 T *end() SPIRV_CROSS_NOEXCEPT 134 { 135 return ptr + buffer_size; 136 } 137 begin() const138 const T *begin() const SPIRV_CROSS_NOEXCEPT 139 { 140 return ptr; 141 } 142 end() const143 const T *end() const SPIRV_CROSS_NOEXCEPT 144 { 145 return ptr + buffer_size; 146 } 147 front()148 T &front() SPIRV_CROSS_NOEXCEPT 149 { 150 return ptr[0]; 151 } 152 front() const153 const T &front() const SPIRV_CROSS_NOEXCEPT 154 { 155 return ptr[0]; 156 } 157 back()158 T &back() SPIRV_CROSS_NOEXCEPT 159 { 160 return ptr[buffer_size - 1]; 161 } 162 back() const163 const T &back() const SPIRV_CROSS_NOEXCEPT 164 { 165 return ptr[buffer_size - 1]; 166 } 167 168 // Makes it easier to consume SmallVector. 169 #if defined(_MSC_VER) && _MSC_VER < 1900 operator std::vector<T>() const170 explicit operator std::vector<T>() const 171 { 172 // Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations. 173 return std::vector<T>(ptr, ptr + buffer_size); 174 } 175 #else 176 // Makes it easier to consume SmallVector. operator std::vector<T>() const177 explicit operator std::vector<T>() const & 178 { 179 return std::vector<T>(ptr, ptr + buffer_size); 180 } 181 182 // If we are converting as an r-value, we can pilfer our elements. operator std::vector<T>()183 explicit operator std::vector<T>() && 184 { 185 return std::vector<T>(std::make_move_iterator(ptr), std::make_move_iterator(ptr + buffer_size)); 186 } 187 #endif 188 189 // Avoid sliced copies. Base class should only be read as a reference. 190 VectorView(const VectorView &) = delete; 191 void operator=(const VectorView &) = delete; 192 193 protected: 194 VectorView() = default; 195 T *ptr = nullptr; 196 size_t buffer_size = 0; 197 }; 198 199 // Simple vector which supports up to N elements inline, without malloc/free. 200 // We use a lot of throwaway vectors all over the place which triggers allocations. 201 // This class only implements the subset of std::vector we need in SPIRV-Cross. 202 // It is *NOT* a drop-in replacement in general projects. 203 template <typename T, size_t N = 8> 204 class SmallVector : public VectorView<T> 205 { 206 public: SmallVector()207 SmallVector() SPIRV_CROSS_NOEXCEPT 208 { 209 this->ptr = stack_storage.data(); 210 buffer_capacity = N; 211 } 212 SmallVector(const T * arg_list_begin,const T * arg_list_end)213 SmallVector(const T *arg_list_begin, const T *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector() 214 { 215 auto count = size_t(arg_list_end - arg_list_begin); 216 reserve(count); 217 for (size_t i = 0; i < count; i++, arg_list_begin++) 218 new (&this->ptr[i]) T(*arg_list_begin); 219 this->buffer_size = count; 220 } 221 SmallVector(std::initializer_list<T> init)222 SmallVector(std::initializer_list<T> init) SPIRV_CROSS_NOEXCEPT : SmallVector(init.begin(), init.end()) 223 { 224 } 225 SmallVector(SmallVector && other)226 SmallVector(SmallVector &&other) SPIRV_CROSS_NOEXCEPT : SmallVector() 227 { 228 *this = std::move(other); 229 } 230 operator =(SmallVector && other)231 SmallVector &operator=(SmallVector &&other) SPIRV_CROSS_NOEXCEPT 232 { 233 clear(); 234 if (other.ptr != other.stack_storage.data()) 235 { 236 // Pilfer allocated pointer. 237 if (this->ptr != stack_storage.data()) 238 free(this->ptr); 239 this->ptr = other.ptr; 240 this->buffer_size = other.buffer_size; 241 buffer_capacity = other.buffer_capacity; 242 other.ptr = nullptr; 243 other.buffer_size = 0; 244 other.buffer_capacity = 0; 245 } 246 else 247 { 248 // Need to move the stack contents individually. 249 reserve(other.buffer_size); 250 for (size_t i = 0; i < other.buffer_size; i++) 251 { 252 new (&this->ptr[i]) T(std::move(other.ptr[i])); 253 other.ptr[i].~T(); 254 } 255 this->buffer_size = other.buffer_size; 256 other.buffer_size = 0; 257 } 258 return *this; 259 } 260 SmallVector(const SmallVector & other)261 SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector() 262 { 263 *this = other; 264 } 265 operator =(const SmallVector & other)266 SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT 267 { 268 if (this == &other) 269 return *this; 270 271 clear(); 272 reserve(other.buffer_size); 273 for (size_t i = 0; i < other.buffer_size; i++) 274 new (&this->ptr[i]) T(other.ptr[i]); 275 this->buffer_size = other.buffer_size; 276 return *this; 277 } 278 SmallVector(size_t count)279 explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector() 280 { 281 resize(count); 282 } 283 ~SmallVector()284 ~SmallVector() 285 { 286 clear(); 287 if (this->ptr != stack_storage.data()) 288 free(this->ptr); 289 } 290 clear()291 void clear() SPIRV_CROSS_NOEXCEPT 292 { 293 for (size_t i = 0; i < this->buffer_size; i++) 294 this->ptr[i].~T(); 295 this->buffer_size = 0; 296 } 297 push_back(const T & t)298 void push_back(const T &t) SPIRV_CROSS_NOEXCEPT 299 { 300 reserve(this->buffer_size + 1); 301 new (&this->ptr[this->buffer_size]) T(t); 302 this->buffer_size++; 303 } 304 push_back(T && t)305 void push_back(T &&t) SPIRV_CROSS_NOEXCEPT 306 { 307 reserve(this->buffer_size + 1); 308 new (&this->ptr[this->buffer_size]) T(std::move(t)); 309 this->buffer_size++; 310 } 311 pop_back()312 void pop_back() SPIRV_CROSS_NOEXCEPT 313 { 314 // Work around false positive warning on GCC 8.3. 315 // Calling pop_back on empty vector is undefined. 316 if (!this->empty()) 317 resize(this->buffer_size - 1); 318 } 319 320 template <typename... Ts> emplace_back(Ts &&...ts)321 void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT 322 { 323 reserve(this->buffer_size + 1); 324 new (&this->ptr[this->buffer_size]) T(std::forward<Ts>(ts)...); 325 this->buffer_size++; 326 } 327 reserve(size_t count)328 void reserve(size_t count) SPIRV_CROSS_NOEXCEPT 329 { 330 if ((count > std::numeric_limits<size_t>::max() / sizeof(T)) || 331 (count > std::numeric_limits<size_t>::max() / 2)) 332 { 333 // Only way this should ever happen is with garbage input, terminate. 334 std::terminate(); 335 } 336 337 if (count > buffer_capacity) 338 { 339 size_t target_capacity = buffer_capacity; 340 if (target_capacity == 0) 341 target_capacity = 1; 342 343 // Weird parens works around macro issues on Windows if NOMINMAX is not used. 344 target_capacity = (std::max)(target_capacity, N); 345 346 // Need to ensure there is a POT value of target capacity which is larger than count, 347 // otherwise this will overflow. 348 while (target_capacity < count) 349 target_capacity <<= 1u; 350 351 T *new_buffer = 352 target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data(); 353 354 // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. 355 if (!new_buffer) 356 std::terminate(); 357 358 // In case for some reason two allocations both come from same stack. 359 if (new_buffer != this->ptr) 360 { 361 // We don't deal with types which can throw in move constructor. 362 for (size_t i = 0; i < this->buffer_size; i++) 363 { 364 new (&new_buffer[i]) T(std::move(this->ptr[i])); 365 this->ptr[i].~T(); 366 } 367 } 368 369 if (this->ptr != stack_storage.data()) 370 free(this->ptr); 371 this->ptr = new_buffer; 372 buffer_capacity = target_capacity; 373 } 374 } 375 insert(T * itr,const T * insert_begin,const T * insert_end)376 void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT 377 { 378 auto count = size_t(insert_end - insert_begin); 379 if (itr == this->end()) 380 { 381 reserve(this->buffer_size + count); 382 for (size_t i = 0; i < count; i++, insert_begin++) 383 new (&this->ptr[this->buffer_size + i]) T(*insert_begin); 384 this->buffer_size += count; 385 } 386 else 387 { 388 if (this->buffer_size + count > buffer_capacity) 389 { 390 auto target_capacity = this->buffer_size + count; 391 if (target_capacity == 0) 392 target_capacity = 1; 393 if (target_capacity < N) 394 target_capacity = N; 395 396 while (target_capacity < count) 397 target_capacity <<= 1u; 398 399 // Need to allocate new buffer. Move everything to a new buffer. 400 T *new_buffer = 401 target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data(); 402 403 // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. 404 if (!new_buffer) 405 std::terminate(); 406 407 // First, move elements from source buffer to new buffer. 408 // We don't deal with types which can throw in move constructor. 409 auto *target_itr = new_buffer; 410 auto *original_source_itr = this->begin(); 411 412 if (new_buffer != this->ptr) 413 { 414 while (original_source_itr != itr) 415 { 416 new (target_itr) T(std::move(*original_source_itr)); 417 original_source_itr->~T(); 418 ++original_source_itr; 419 ++target_itr; 420 } 421 } 422 423 // Copy-construct new elements. 424 for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr) 425 new (target_itr) T(*source_itr); 426 427 // Move over the other half. 428 if (new_buffer != this->ptr || insert_begin != insert_end) 429 { 430 while (original_source_itr != this->end()) 431 { 432 new (target_itr) T(std::move(*original_source_itr)); 433 original_source_itr->~T(); 434 ++original_source_itr; 435 ++target_itr; 436 } 437 } 438 439 if (this->ptr != stack_storage.data()) 440 free(this->ptr); 441 this->ptr = new_buffer; 442 buffer_capacity = target_capacity; 443 } 444 else 445 { 446 // Move in place, need to be a bit careful about which elements are constructed and which are not. 447 // Move the end and construct the new elements. 448 auto *target_itr = this->end() + count; 449 auto *source_itr = this->end(); 450 while (target_itr != this->end() && source_itr != itr) 451 { 452 --target_itr; 453 --source_itr; 454 new (target_itr) T(std::move(*source_itr)); 455 } 456 457 // For already constructed elements we can move-assign. 458 std::move_backward(itr, source_itr, target_itr); 459 460 // For the inserts which go to already constructed elements, we can do a plain copy. 461 while (itr != this->end() && insert_begin != insert_end) 462 *itr++ = *insert_begin++; 463 464 // For inserts into newly allocated memory, we must copy-construct instead. 465 while (insert_begin != insert_end) 466 { 467 new (itr) T(*insert_begin); 468 ++itr; 469 ++insert_begin; 470 } 471 } 472 473 this->buffer_size += count; 474 } 475 } 476 insert(T * itr,const T & value)477 void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT 478 { 479 insert(itr, &value, &value + 1); 480 } 481 erase(T * itr)482 T *erase(T *itr) SPIRV_CROSS_NOEXCEPT 483 { 484 std::move(itr + 1, this->end(), itr); 485 this->ptr[--this->buffer_size].~T(); 486 return itr; 487 } 488 erase(T * start_erase,T * end_erase)489 void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT 490 { 491 if (end_erase == this->end()) 492 { 493 resize(size_t(start_erase - this->begin())); 494 } 495 else 496 { 497 auto new_size = this->buffer_size - (end_erase - start_erase); 498 std::move(end_erase, this->end(), start_erase); 499 resize(new_size); 500 } 501 } 502 resize(size_t new_size)503 void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT 504 { 505 if (new_size < this->buffer_size) 506 { 507 for (size_t i = new_size; i < this->buffer_size; i++) 508 this->ptr[i].~T(); 509 } 510 else if (new_size > this->buffer_size) 511 { 512 reserve(new_size); 513 for (size_t i = this->buffer_size; i < new_size; i++) 514 new (&this->ptr[i]) T(); 515 } 516 517 this->buffer_size = new_size; 518 } 519 520 private: 521 size_t buffer_capacity = 0; 522 AlignedBuffer<T, N> stack_storage; 523 }; 524 525 // A vector without stack storage. 526 // Could also be a typedef-ed to std::vector, 527 // but might as well use the one we have. 528 template <typename T> 529 using Vector = SmallVector<T, 0>; 530 531 #else // SPIRV_CROSS_FORCE_STL_TYPES 532 533 template <typename T, size_t N = 8> 534 using SmallVector = std::vector<T>; 535 template <typename T> 536 using Vector = std::vector<T>; 537 template <typename T> 538 using VectorView = std::vector<T>; 539 540 #endif // SPIRV_CROSS_FORCE_STL_TYPES 541 542 // An object pool which we use for allocating IVariant-derived objects. 543 // We know we are going to allocate a bunch of objects of each type, 544 // so amortize the mallocs. 545 class ObjectPoolBase 546 { 547 public: 548 virtual ~ObjectPoolBase() = default; 549 virtual void free_opaque(void *ptr) = 0; 550 }; 551 552 template <typename T> 553 class ObjectPool : public ObjectPoolBase 554 { 555 public: ObjectPool(unsigned start_object_count_=16)556 explicit ObjectPool(unsigned start_object_count_ = 16) 557 : start_object_count(start_object_count_) 558 { 559 } 560 561 template <typename... P> allocate(P &&...p)562 T *allocate(P &&... p) 563 { 564 if (vacants.empty()) 565 { 566 unsigned num_objects = start_object_count << memory.size(); 567 T *ptr = static_cast<T *>(malloc(num_objects * sizeof(T))); 568 if (!ptr) 569 return nullptr; 570 571 for (unsigned i = 0; i < num_objects; i++) 572 vacants.push_back(&ptr[i]); 573 574 memory.emplace_back(ptr); 575 } 576 577 T *ptr = vacants.back(); 578 vacants.pop_back(); 579 new (ptr) T(std::forward<P>(p)...); 580 return ptr; 581 } 582 free(T * ptr)583 void free(T *ptr) 584 { 585 ptr->~T(); 586 vacants.push_back(ptr); 587 } 588 free_opaque(void * ptr)589 void free_opaque(void *ptr) override 590 { 591 free(static_cast<T *>(ptr)); 592 } 593 clear()594 void clear() 595 { 596 vacants.clear(); 597 memory.clear(); 598 } 599 600 protected: 601 Vector<T *> vacants; 602 603 struct MallocDeleter 604 { operator ()SPIRV_CROSS_NAMESPACE::ObjectPool::MallocDeleter605 void operator()(T *ptr) 606 { 607 ::free(ptr); 608 } 609 }; 610 611 SmallVector<std::unique_ptr<T, MallocDeleter>> memory; 612 unsigned start_object_count; 613 }; 614 615 template <size_t StackSize = 4096, size_t BlockSize = 4096> 616 class StringStream 617 { 618 public: StringStream()619 StringStream() 620 { 621 reset(); 622 } 623 ~StringStream()624 ~StringStream() 625 { 626 reset(); 627 } 628 629 // Disable copies and moves. Makes it easier to implement, and we don't need it. 630 StringStream(const StringStream &) = delete; 631 void operator=(const StringStream &) = delete; 632 633 template <typename T, typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0> operator <<(const T & t)634 StringStream &operator<<(const T &t) 635 { 636 auto s = std::to_string(t); 637 append(s.data(), s.size()); 638 return *this; 639 } 640 641 // Only overload this to make float/double conversions ambiguous. operator <<(uint32_t v)642 StringStream &operator<<(uint32_t v) 643 { 644 auto s = std::to_string(v); 645 append(s.data(), s.size()); 646 return *this; 647 } 648 operator <<(char c)649 StringStream &operator<<(char c) 650 { 651 append(&c, 1); 652 return *this; 653 } 654 operator <<(const std::string & s)655 StringStream &operator<<(const std::string &s) 656 { 657 append(s.data(), s.size()); 658 return *this; 659 } 660 operator <<(const char * s)661 StringStream &operator<<(const char *s) 662 { 663 append(s, strlen(s)); 664 return *this; 665 } 666 667 template <size_t N> operator <<(const char (& s)[N])668 StringStream &operator<<(const char (&s)[N]) 669 { 670 append(s, strlen(s)); 671 return *this; 672 } 673 str() const674 std::string str() const 675 { 676 std::string ret; 677 size_t target_size = 0; 678 for (auto &saved : saved_buffers) 679 target_size += saved.offset; 680 target_size += current_buffer.offset; 681 ret.reserve(target_size); 682 683 for (auto &saved : saved_buffers) 684 ret.insert(ret.end(), saved.buffer, saved.buffer + saved.offset); 685 ret.insert(ret.end(), current_buffer.buffer, current_buffer.buffer + current_buffer.offset); 686 return ret; 687 } 688 reset()689 void reset() 690 { 691 for (auto &saved : saved_buffers) 692 if (saved.buffer != stack_buffer) 693 free(saved.buffer); 694 if (current_buffer.buffer != stack_buffer) 695 free(current_buffer.buffer); 696 697 saved_buffers.clear(); 698 current_buffer.buffer = stack_buffer; 699 current_buffer.offset = 0; 700 current_buffer.size = sizeof(stack_buffer); 701 } 702 703 private: 704 struct Buffer 705 { 706 char *buffer = nullptr; 707 size_t offset = 0; 708 size_t size = 0; 709 }; 710 Buffer current_buffer; 711 char stack_buffer[StackSize]; 712 SmallVector<Buffer> saved_buffers; 713 append(const char * s,size_t len)714 void append(const char *s, size_t len) 715 { 716 size_t avail = current_buffer.size - current_buffer.offset; 717 if (avail < len) 718 { 719 if (avail > 0) 720 { 721 memcpy(current_buffer.buffer + current_buffer.offset, s, avail); 722 s += avail; 723 len -= avail; 724 current_buffer.offset += avail; 725 } 726 727 saved_buffers.push_back(current_buffer); 728 size_t target_size = len > BlockSize ? len : BlockSize; 729 current_buffer.buffer = static_cast<char *>(malloc(target_size)); 730 if (!current_buffer.buffer) 731 SPIRV_CROSS_THROW("Out of memory."); 732 733 memcpy(current_buffer.buffer, s, len); 734 current_buffer.offset = len; 735 current_buffer.size = target_size; 736 } 737 else 738 { 739 memcpy(current_buffer.buffer + current_buffer.offset, s, len); 740 current_buffer.offset += len; 741 } 742 } 743 }; 744 745 } // namespace SPIRV_CROSS_NAMESPACE 746 747 #endif 748