virtualStack.h 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2024 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #include "SafePointer.h"
  24. #include "../api/stringAPI.h"
  25. #include <cstdio>
  26. #include <cstdlib>
  27. #include <cstring>
  28. #include <mutex>
  29. #include <thread>
  30. namespace dsr {
  31. // TODO: Make overloaded versions for signed and unsigned integer types.
  32. constexpr uint64_t roundUp(uint64_t size, uint64_t alignment) {
  33. return size + (alignment - 1) - ((size - 1) % alignment);
  34. }
  35. template <typename T>
  36. constexpr uint64_t memory_getPaddedElementSize() {
  37. return roundUp((uint64_t)sizeof(T), (uint64_t)alignof(T));
  38. }
  39. // Allocate memory in the virtual stack owned by the current thread.
  40. // paddedSize is the number of bytes to allocate including all elements and internal padding.
  41. // alignment is what the start address should be divisible by in bytes, which must be a power of two.
  42. uint8_t *virtualStack_push(uint64_t paddedSize, uint64_t alignment);
  43. // A simpler way to get the correct alignment is to allocate a number of elements with a specific type.
  44. // TODO: Create another function for manual alignment exceeding the type's alignment using another template argument.
  45. // TODO: Let the address offset be negated and start with the allocation size going down to zero,
  46. // so that rounding up addresses can be done by simply masking the least significant bits.
  47. template <typename T>
  48. SafePointer<T> virtualStack_push(uint64_t elementCount, const char *name) {
  49. // Calculate element size and multiply by element count to get the total size.
  50. uint64_t paddedSize = memory_getPaddedElementSize<T>() * elementCount;
  51. // Allocate the data with the amount of alignment requested by the element type T.
  52. uint8_t *data = virtualStack_push(paddedSize, (uint64_t)alignof(T));
  53. // Return a safe pointer to the allocated data.
  54. return SafePointer<T>(name, (T*)data, (intptr_t)paddedSize);
  55. }
  56. // Free the last allocation from the virtual stack.
  57. // Must be called from the same thread that pushed, because virtual stacks are local to their threads.
  58. void virtualStack_pop();
  59. // Allocate this array on the stack to automatically free the memory when the scope ends.
  60. // Replaces VLA or alloca.
  61. template <typename T>
  62. struct VirtualStackAllocation {
  63. SafePointer<T> data;
  64. VirtualStackAllocation(uint64_t elementCount)
  65. : data(virtualStack_push<T>(elementCount, "virtual stack allocation")) {}
  66. ~VirtualStackAllocation() {
  67. virtualStack_pop();
  68. }
  69. };
  70. }