spirv_cross_containers.hpp 18 KB

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