toyspp

toyspp.git
git clone git://git.lenczewski.org/toyspp.git
Log | Files | Refs | Submodules | README | LICENSE

memory.cpp (6159B)


      1 #include "memory.hpp"
      2 
      3 #if defined(_WIN32)
      4 # include <windows.h>
      5 # include <memoryapi.h>
      6 namespace {
      7 	uint8_t* Win32PageAlloc(size_t size, size_t alignment)
      8 	{
      9 		assert(Test::Utility::IsPowerOfTwo(alignment));
     10 		assert(Test::Memory::MEM_ALIGN_4_KIB <= alignment);
     11 
     12 		uintptr_t aligned_size = Test::Utility::AlignNext(size, alignment);
     13 
     14 		DWORD prot = PAGE_READWRITE;
     15 		DWORD flags = MEM_RESERVE | MEM_COMMIT;
     16 		LPVOID ptr = VirtualAlloc(nullptr, aligned_size, flags, prot);
     17 
     18 		return (uint8_t*) ptr;
     19 	}
     20 
     21 	Test::Memory::MirrorAllocResult Win32MirrorAlloc(size_t size, size_t mirrors)
     22 	{
     23 		assert(Test::Utility::IsAlignedTo(size, Test::Memory::MEM_ALIGN_4_KIB));
     24 
     25 		Test::Memory::MirrorAllocResult result{};
     26 
     27 		(void) size;
     28 		(void) mirrors;
     29 
     30 		return result;
     31 	}
     32 }
     33 
     34 #elif defined(__linux__)
     35 # define _GNU_SOURCE 1
     36 # include <sys/mman.h>
     37 # include <unistd.h>
     38 namespace {
     39 	uint8_t* LinuxPageAlloc(size_t size, size_t alignment)
     40 	{
     41 		assert(Test::Utility::IsPowerOfTwo(alignment));
     42 		assert(Test::Memory::MEM_ALIGN_4_KIB <= alignment);
     43 
     44 		int prot = PROT_READ | PROT_WRITE;
     45 		int flags = MAP_PRIVATE | MAP_ANONYMOUS;
     46 
     47 		uintptr_t aligned_size = Test::Utility::AlignNext(size, alignment);
     48 		void *unaligned_ptr = mmap(nullptr, aligned_size, PROT_NONE, flags, -1, 0);
     49 		if (unaligned_ptr == MAP_FAILED)
     50 			return nullptr;
     51 
     52 		uintptr_t aligned_base = Test::Utility::AlignNext((uintptr_t) unaligned_ptr, alignment);
     53 		void *aligned_ptr = mmap((void *) aligned_base, size, prot, flags | MAP_FIXED, -1, 0);
     54 		assert(aligned_ptr != MAP_FAILED);
     55 
     56 		if (Test::Utility::IsAlignedTo(aligned_base, Test::Memory::MEM_ALIGN_2_MIB))
     57 			madvise(aligned_ptr, size, MADV_HUGEPAGE);
     58 
     59 		munmap(unaligned_ptr, aligned_base - (uintptr_t) aligned_ptr);
     60 
     61 		return (uint8_t*) aligned_ptr;
     62 	}
     63 
     64 	Test::Memory::MirrorAllocResult LinuxMirrorAlloc(size_t size, size_t mirrors)
     65 	{
     66 		assert(Test::Utility::IsAlignedTo(size, Test::Memory::MEM_ALIGN_4_KIB));
     67 
     68 		Test::Memory::MirrorAllocResult result{};
     69 
     70 		result.len = size;
     71 		result.cap = size * mirrors;
     72 
     73 		int prot = PROT_READ | PROT_WRITE;
     74 		int buffer_flags = MAP_PRIVATE | MAP_ANONYMOUS;
     75 		int mirror_flags = MAP_SHARED | MAP_FIXED;
     76 
     77 		result.memfd = memfd_create("linux mirror alloc", MFD_CLOEXEC);
     78 		if (result.memfd < 0) {
     79 			goto error;
     80 		}
     81 
     82 		ftruncate(result.memfd, result.cap);
     83 
     84 		result.ptr = (uint8_t*) mmap(nullptr, result.cap, PROT_NONE, buffer_flags, -1, 0);
     85 		if (result.ptr == MAP_FAILED) {
     86 			close(result.memfd);
     87 			goto error;
     88 		}
     89 
     90 		for (size_t i = 0; i < mirrors; i++) {
     91 			uint8_t* base = result.ptr + (result.len * i);
     92 			void *mirror = mmap(base, result.len, prot, mirror_flags, result.memfd, 0);
     93 			if (mirror == MAP_FAILED) {
     94 				munmap(result.ptr, result.cap);
     95 				close(result.memfd);
     96 				goto error;
     97 			}
     98 		}
     99 
    100 		return result;
    101 
    102 error:
    103 		result.memfd = -1;
    104 		result.ptr = nullptr;
    105 		result.len = result.cap = 0;
    106 
    107 		return result;
    108 	}
    109 }
    110 
    111 #else
    112 # error "Unknown platform, must be one of: windows linux"
    113 #endif
    114 
    115 namespace Test::Memory {
    116 	uint8_t *GenAlloc(size_t size, size_t alignment) {
    117 		assert(Utility::IsPowerOfTwo(alignment));
    118 
    119 		size_t upper_bound = size + (alignment - 1);
    120 
    121 		uintptr_t ptr = (uintptr_t) malloc(upper_bound);
    122 		uintptr_t res = Utility::AlignNext(ptr, alignment);
    123 
    124 		return (uint8_t *) res;
    125 	}
    126 
    127 	uint8_t *PageAlloc(size_t size, size_t alignment) {
    128 		if (alignment < MEM_ALIGN_4_KIB)
    129 			alignment = MEM_ALIGN_4_KIB;
    130 
    131 #if defined(_WIN32)
    132 		return Win32PageAlloc(size, alignment);
    133 #elif defined(__linux__)
    134 		return LinuxPageAlloc(size, alignment);
    135 #else
    136 		return nullptr;
    137 #endif
    138 	}
    139 
    140 	MirrorAllocResult MirrorAlloc(size_t size, size_t mirrors) {
    141 #if defined(_WIN32)
    142 		return Win32MirrorAlloc(size, mirrors);
    143 #elif defined(__linux__)
    144 		return LinuxMirrorAlloc(size, mirrors);
    145 #else
    146 		return nullptr;
    147 #endif
    148 	}
    149 
    150 	void LinearAlloc::Init(uint8_t *buf, size_t cap)
    151 	{
    152 		ptr = buf;
    153 		this->cap = cap;
    154 		len = 0;
    155 	}
    156 
    157 	void LinearAlloc::Reset()
    158 	{
    159 		len = 0;
    160 	}
    161 
    162 	uint8_t *LinearAlloc::AllocRaw(size_t size, size_t alignment)
    163 	{
    164 		assert(size);
    165 		assert(alignment);
    166 		assert(Utility::IsPowerOfTwo(alignment));
    167 
    168 		uintptr_t currentPtr = (uintptr_t) ptr + len;
    169 		uintptr_t endPtr = (uintptr_t) ptr + cap;
    170 
    171 		uintptr_t alignedPtr = Utility::AlignNext(currentPtr, alignment);
    172 		if (endPtr < alignedPtr + size)
    173 			return nullptr;
    174 
    175 		len = (alignedPtr + size) - (uintptr_t) ptr;
    176 
    177 		return (uint8_t *) alignedPtr;
    178 	}
    179 
    180 	void BlockAlloc::Init(uint8_t *buf, size_t cap, size_t block_size, size_t block_alignment)
    181 	{
    182 		assert(Utility::IsPowerOfTwo(block_alignment));
    183 		assert(Utility::IsAlignedTo((uintptr_t) buf, block_alignment));
    184 
    185 		assert(sizeof(FreeListNode) <= block_size);
    186 		assert(alignof(FreeListNode) <= block_alignment);
    187 
    188 		ptr = buf;
    189 		this->cap = cap;
    190 		this->block_size = block_size;
    191 		this->block_alignment = block_alignment;
    192 
    193 		Reset();
    194 	}
    195 
    196 	void BlockAlloc::Reset()
    197 	{
    198 		freelist = nullptr;
    199 
    200 		for (size_t i = 0; i < cap / block_size; i++) {
    201 			uint8_t *block = ptr + (block_size * i);
    202 
    203 			FreeListNode *node = (FreeListNode *) block;
    204 			node->next = freelist;
    205 			freelist = node;
    206 		}
    207 	}
    208 
    209 	uint8_t *BlockAlloc::AllocRaw()
    210 	{
    211 		if (freelist == nullptr)
    212 			return nullptr;
    213 
    214 		FreeListNode *node = freelist;
    215 		freelist = node->next;
    216 
    217 		return (uint8_t *) node;
    218 	}
    219 
    220 	void BlockAlloc::Free(uint8_t *ptr)
    221 	{
    222 		FreeListNode *node = (FreeListNode *) ptr;
    223 		node->next = freelist;
    224 		freelist = node;
    225 	}
    226 
    227 	void StackAlloc::Init(uint8_t *buf, size_t cap)
    228 	{
    229 		ptr = buf;
    230 		this->cap = cap;
    231 		len = 0;
    232 	}
    233 
    234 	void StackAlloc::Reset()
    235 	{
    236 		len = 0;
    237 	}
    238 
    239 	StackAlloc::Result StackAlloc::AllocRaw(size_t size, size_t alignment)
    240 	{
    241 		assert(size);
    242 		assert(alignment);
    243 		assert(Utility::IsPowerOfTwo(alignment));
    244 
    245 		uintptr_t currentPtr = (uintptr_t) ptr + len;
    246 		uintptr_t endPtr = (uintptr_t) ptr + cap;
    247 
    248 		uintptr_t alignedPtr = Utility::AlignNext(currentPtr, alignment);
    249 		if (endPtr < alignedPtr + size) {
    250 			Result result{};
    251 			return result;
    252 		}
    253 
    254 		len = (alignedPtr + size) - (uintptr_t) ptr;
    255 
    256 		Result result{};
    257 		result.ptr = (uint8_t *) alignedPtr;
    258 		result.len = size;
    259 		result.saveptr = (uint8_t *) currentPtr;
    260 		return result;
    261 	}
    262 
    263 	void StackAlloc::Free(Result region)
    264 	{
    265 		len = region.saveptr - ptr;
    266 	}
    267 }