page_allocator.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #pragma once
  2. #include "Internal/page_allocator.inl.h"
  3. #include "../C/Baselib_ErrorState.h"
  4. namespace baselib
  5. {
  6. BASELIB_CPP_INTERFACE
  7. {
  8. // Page allocator implementation providing platform dependent system page allocation.
  9. //
  10. // Allocations are guaranteed to be aligned to at least the value of `default_alignment`.
  11. // All methods with no page state parameter input will default to `default_page_state` where applicable.
  12. //
  13. // Notes on allocation size:
  14. // All sizes are by allocator standards in bytes. The page allocator internally rounds up sizes to the nearest page size value. Consider this when
  15. // allocating. Use `optimal_size` to retreive number of bytes allocated given a specific size (1 to retreive the page size value).
  16. // Large alignments may lead to a significantly higher use of virtual address space than the amount of memory requested.
  17. // This may result in an aligned page allocation to fail where a less/non-aligned allocation would succeed.
  18. // Note that this is especially common in 32bit applications but a platform may impose additional restrictions on the size of its virtual address space.
  19. // Whether a page allocation is pure virtual address space or already commited memory depends on the platform and passed page state flag.
  20. //
  21. // Page state options
  22. typedef enum Memory_PageState
  23. {
  24. // The page are in a reserved state and any access will cause a seg-fault/access violation.
  25. // On some platforms that support this state this may be just a hint to the OS and there is no guarantee pages in this state behave
  26. // differently the `NoAccess` state.
  27. // The `page_allocator` implementation does a best effort and tries to ensure as best as possible that pages in this state are not commited.
  28. Memory_PageState_Reserved = detail::Memory_PageState_Reserved,
  29. // This is a no access page and will cause a seg-fault/access violation when accessed.
  30. Memory_PageState_NoAccess = detail::Memory_PageState_NoAccess,
  31. // The memory can only be read.
  32. Memory_PageState_ReadOnly = detail::Memory_PageState_ReadOnly,
  33. // The memory can be read and written.
  34. Memory_PageState_ReadWrite = detail::Memory_PageState_ReadWrite,
  35. // The memory can be used to execute code and can be read.
  36. Memory_PageState_ReadOnly_Executable = detail::Memory_PageState_ReadOnly_Executable,
  37. // The memory can be used to execute code and can be both read and written.
  38. Memory_PageState_ReadWrite_Executable = detail::Memory_PageState_ReadWrite_Executable,
  39. } Memory_PageState;
  40. // Allocator
  41. template<uint32_t default_alignment = 4096, Memory_PageState default_page_state = Memory_PageState_ReadWrite>
  42. class page_allocator
  43. {
  44. static_assert((default_alignment != 0), "'default_alignment' must not be zero");
  45. static_assert(::baselib::Algorithm::IsPowerOfTwo(default_alignment), "'default_alignment' must be a power of two value");
  46. using impl = detail::page_allocator<default_alignment>;
  47. const impl m_Impl;
  48. public:
  49. // Allocated memory is guaranteed to always be aligned to at least the value of `alignment`.
  50. static constexpr uint32_t alignment = default_alignment;
  51. // Typedefs
  52. typedef Baselib_ErrorState error_state;
  53. // Create a new instance with system default page size.
  54. page_allocator() : m_Impl() {}
  55. // Create a new instance with `page_size` sized pages. Page size is required to be supported by the target system.
  56. page_allocator(size_t page_size) : m_Impl(page_size)
  57. {
  58. BaselibAssert((page_size != 0), "'page_size' must not be a zero value");
  59. BaselibAssert(::baselib::Algorithm::IsPowerOfTwo(page_size), "'page_size' must be a power of two value");
  60. }
  61. // Allocates number of pages required to hold `size` number of bytes, with initial page state set to `state`
  62. //
  63. // \returns Address to memory block of allocated memory or `nullptr` if allocation failed.
  64. void* allocate(size_t size, Memory_PageState state = default_page_state) const
  65. {
  66. error_state result = Baselib_ErrorState_Create();
  67. return allocate(size, state, &result);
  68. }
  69. // Allocates number of pages required to hold `size` number of bytes, with initial page state set to `state`
  70. //
  71. // If operation failed `error_state_ptr` contains one of the following error codes:
  72. // - Baselib_ErrorCode_InvalidPageSize: Page size doesn't match any of the available page sizes.
  73. // - Baselib_ErrorCode_InvalidPageCount: Requested number of pages is zero.
  74. // - Baselib_ErrorCode_UnsupportedAlignment: Requested alignment is invalid.
  75. // - Baselib_ErrorCode_UnsupportedPageState: The underlying system doesn't support the requested page state.
  76. // - Baselib_ErrorCode_OutOfMemory: If there is not enough continuous address space available, or physical memory space when acquiring committed memory.
  77. //
  78. // \returns Address to memory block of allocated memory or `nullptr` if allocation failed.
  79. void* allocate(size_t size, Memory_PageState state, error_state *error_state_ptr) const
  80. {
  81. return m_Impl.allocate(size, state, error_state_ptr);
  82. }
  83. // Reallocate is not supported by the page allocator. The operation is a no-op.
  84. //
  85. // If `error_state_ptr` is passed it contains the following error code:
  86. // - Baselib_ErrorCode_NotSupported: The operation is not supported by the underlying system.
  87. //
  88. // \returns Always returns `nullptr`.
  89. void* reallocate(void* ptr, size_t old_size, size_t new_size, error_state *error_state_ptr = nullptr) const
  90. {
  91. Baselib_ErrorState_RaiseError(error_state_ptr, Baselib_ErrorCode_NotSupported,
  92. Baselib_ErrorState_NativeErrorCodeType_None, 0,
  93. Baselib_ErrorState_ExtraInformationType_None, 0,
  94. BASELIB_SOURCELOCATION);
  95. return nullptr;
  96. }
  97. // Deallocates memory block in previously allocated or reallocated with `size` pointed to by `ptr`.
  98. // A single call of deallocate must encompass the size that were originally allocated with a single call of `allocate`.
  99. //
  100. // \returns True if the operation was successful.
  101. bool deallocate(void* ptr, size_t size) const
  102. {
  103. error_state result = Baselib_ErrorState_Create();
  104. return deallocate(ptr, size, &result);
  105. }
  106. // Deallocates memory block previously allocated or reallocated with `size` pointed to by `ptr`.
  107. // A single call of deallocate must encompass the size that were originally allocated with a single call of `allocate`.
  108. //
  109. // If operation failed `error_state_ptr` contains one of the following error codes:
  110. // - Baselib_ErrorCode_InvalidAddressRange: Address range was detected to not match a valid allocation.
  111. // CAUTION: Not all platforms are able to detect this and may either raise an error or cause undefined behavior.
  112. // Note to implementors: Raising the error is strongly preferred as it helps identifying issues in user code.
  113. // - Baselib_ErrorCode_InvalidPageSize: If page size doesn't match size with previous call to `allocate` with address in `ptr`.
  114. //
  115. // \returns True if the operation was successful.
  116. bool deallocate(void* ptr, size_t size, error_state *error_state_ptr) const
  117. {
  118. return m_Impl.deallocate(ptr, size, error_state_ptr);
  119. }
  120. // Calculate optimal allocation size given `size`.
  121. // The result size is the number of bytes allocated given a specific size.
  122. //
  123. // \returns Optimal size when allocating memory given `size`.
  124. constexpr size_t optimal_size(size_t size) const
  125. {
  126. return m_Impl.optimal_size(size);
  127. }
  128. // Modifies the page state property of an already allocated virtual address in `ptr` of `size` to `state`.
  129. // It is possible to modify only some of the memory allocated by `allocate`.
  130. // Address is the address of the first page to modify and so must be aligned to size of page size.
  131. // Size is rounded up to the next multiple of page size used.
  132. // Passing `nullptr` or a zero page count result in a no-op.
  133. //
  134. // \returns True if the operation was successful.
  135. bool set_page_state(void* ptr, size_t size, Memory_PageState state) const
  136. {
  137. error_state result = Baselib_ErrorState_Create();
  138. return set_page_state(ptr, size, state, &result);
  139. }
  140. // Modifies the page state property of an already allocated virtual address in `ptr` of `size` to `state`.
  141. // It is possible to modify only some of the memory allocated by `allocate`.
  142. // Address is the address of the first page to modify and so must be aligned to size of page size.
  143. // Size is rounded up to the next multiple of page size used.
  144. // Passing `nullptr` or a zero page count result in a no-op.
  145. //
  146. // If operation failed `error_state_ptr` contains one of the following error codes:
  147. // - Baselib_ErrorCode_InvalidAddressRange: Address range is not covered by a valid allocation.
  148. // Platforms that emulate page allocations (e.g. Emscripten) are not able to present this error and
  149. // will pass the function call silently.
  150. // - Baselib_ErrorCode_InvalidPageSize: If page size doesn't match the previous allocation in `ptr`.
  151. // - Baselib_ErrorCode_UnsupportedPageState: The underlying system doesn't support the requested page state.
  152. //
  153. // \returns True if the operation was successful.
  154. bool set_page_state(void* ptr, size_t size, Memory_PageState state, error_state *error_state_ptr) const
  155. {
  156. return m_Impl.set_page_state(ptr, size, state, error_state_ptr);
  157. }
  158. };
  159. }
  160. }