├── .gitignore ├── README.md ├── make.bat ├── make.sh ├── make_clang.sh ├── wb_alloc.h ├── wb_alloc_test.c ├── wb_alloc_test_cpp.cpp └── wb_alloc_test_nocrt.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.ilk 4 | *.pdb 5 | 6 | wb_alloc_test 7 | wb_alloc_test_cpp 8 | 9 | .vs/ 10 | .vs/* 11 | 12 | Debug/ 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wb_alloc.h 2 | 3 | ## Easy-to-use custom allocators in a public-domain C/C++ library. 4 | 5 | - Allocate as much memory as you want\* with these simple custom 6 | allocators. (The amount of memory is limited to the physical memory in 7 | your machine.) 8 | - Easy to use without the CRT or malloc. 9 | - Doesn't include library headers if you don't need it to. 10 | - Doesn't include operating system headers at all. 11 | - Compiles as C89 and C++ on Windows, Mac, and Linux. 12 | - Templatized versions of allocation functions for C++ for easier memory 13 | safety (use `WB_ALLOC_CPLUSPLUS_FEATURES` to enable these). 14 | 15 | ## Current Status 16 | 17 | As of version 0.1, the library compiles with no warnings under gcc, clang 18 | and MSVC, however, it is barely tested, and should be considered **beta 19 | software**. You may encounter issues on untested OSes such as BSD; your 20 | best bet is to `#define WB_ALLOC_CUSTOM_BACKEND` and implement the backend 21 | functions as your OS requires. 22 | 23 | If you run into a problem where the allocators fail, try including your 24 | system's headers for virtual memory and system info. 25 | 26 | ```C 27 | /* Windows */ 28 | #include 29 | 30 | /* Linux */ 31 | #include 32 | #include 33 | 34 | /* macOS */ 35 | #include 36 | #include 37 | ``` 38 | 39 | There are also some convenience features missing, such as being able to 40 | directly free/clear a memory pool and tagged heap. 41 | 42 | ## Demo 43 | 44 | ```C 45 | #include 46 | 47 | #define WB_ALLOC_IMPLEMENTATION 48 | #include "wb_alloc.h" 49 | 50 | int main() 51 | { 52 | wb_MemoryInfo info; 53 | wb_MemoryArena* arena; 54 | int i; 55 | 56 | /* MemoryInfo contains the information about the system's 57 | total memory, page size, and sets some defaults about the 58 | amount of memory committed at once */ 59 | info = wb_getMemoryInfo(); 60 | 61 | /* Bootstrapping the arena means that it allocates the memory 62 | for itself, then stores its own struct at the beginning */ 63 | arena = wb_arenaBootstrap(info, wb_FlagArenaNormal); 64 | 65 | /* Make some room for numbers! */ 66 | int* numbers1 = wb_arenaPush(arena, sizeof(int) * 100); 67 | int* numbers2 = wb_arenaPush(arena, sizeof(int) * 200); 68 | int* numbers3 = wb_arenaPush(arena, sizeof(int) * 400); 69 | int* numbers4 = wb_arenaPush(arena, sizeof(int) * 800); 70 | 71 | for(i = 0; i < 1500; ++i) { 72 | numbers1[i] = 1500 - i; 73 | } 74 | 75 | for(i = 0; i < 1500; ++i) { 76 | printf("%d ", numbers[i]); 77 | } 78 | printf("\n\n"); 79 | 80 | /* Clearing the arena decommits all its committed pages then 81 | recommits them, which is guaranteed on modern operating 82 | systems to zero them */ 83 | wb_arenaClear(arena); 84 | wb_arenaPush(arena, sizeof(int) * 1500); 85 | 86 | for(i = 0; i < 1500; ++i) { 87 | printf("%d\n", numbers[i]); 88 | } 89 | 90 | return 0; 91 | } 92 | ``` 93 | 94 | ## Allocators Included 95 | 96 | #### Memory Arena 97 | 98 | ```C 99 | void* wb_arenaPush( 100 | wb_MemoryArena* arena, 101 | wb_isize size); 102 | 103 | wb_MemoryArena* wb_arenaBootstrap( 104 | wb_MemoryInfo info, 105 | wb_flags flags); 106 | ``` 107 | 108 | Inspired by the Handmade Hero memory management structure, my memory arena 109 | is a variation on a "linear allocator" or a "bump-pointer allocator". It 110 | starts by allocating a large region of memory, returning a pointer to it, 111 | then incrementing the pointer by the amount of space requested. 112 | 113 | This kind of allocation is extremely fast, but does not allow for easy 114 | deallocation of individual objects; however, in practice, this tends not 115 | to be a problem. The entire arena is easily freed at once, which is often 116 | fine for things like state transitions. 117 | 118 | The memory arena has a couple variants that can be enabled via flag 119 | 120 | 1. `wb_FlagArenaStack` turns the arena from a linear allocator into 121 | a stack allocator. With this, you can always pop (or rather, free) the 122 | last allocation on the stack, which will let you pop the previous one, 123 | and so on, until the stack is empty. 124 | 125 | 2. `wb_FlagArenaExtended` stores extra information at the beginning of 126 | each allocation. While this is by default an 8-byte integer, it can be 127 | changed by defining `WB_ALLOC_EXTENDED_INFO` to the type of your 128 | choice. This is designed to aid with serialization. 129 | 130 | These flags may be used together. 131 | 132 | #### Memory Pool 133 | 134 | ```C 135 | void* wb_poolRetrieve(wb_MemoryPool* pool); 136 | void wb_poolRelease(wb_MemoryPool* pool, void* ptr); 137 | 138 | wb_MemoryPool* wb_poolBootstrap( 139 | wb_MemoryInfo info, 140 | wb_flags flags); 141 | ``` 142 | 143 | The memory pool is a simple fixed-size free-list allocator. It allows you 144 | to freely allocate and free fixed-size objects with no external 145 | fragmentation. This kind of allocation is good when you have many of the 146 | same object that need to be created and destroyed frequently, such as 147 | entities in a game world. 148 | 149 | Using a memory arena under the hood, the memory pool can allocate until it 150 | runs out of virtual memory. 151 | 152 | #### Tagged Heap 153 | 154 | ```C 155 | void* wb_taggedAlloc( 156 | wb_TaggedHeap* heap, 157 | wb_isize tag, 158 | wb_usize size); 159 | 160 | void wb_taggedFree(wb_TaggedHeap* heap, wb_isize tag); 161 | 162 | wb_TaggedHeap* wb_taggedBoostrap( 163 | wb_MemoryInfo info, 164 | wb_isize arenaSize, 165 | wb_flags flags); 166 | ``` 167 | 168 | This one is inspired by the Naughty Dog GDC talk about using fibers to 169 | multithread their engine. They mention this as their solution to the 170 | overuse of wasteful memory arenas. The tagged heap behaves like a memory 171 | pool of memory arenas; that is, when you allocate, you specify a tag, and 172 | then you can free all the memory allocated under a single tag at once. It 173 | does this efficiently by allocating fixed-size arenas under the hood and 174 | allocating out of those as needed per-tag. 175 | 176 | The tagged heap is the most flexible allocator in the library, allowing 177 | you to allocate almost as freely as with malloc and free if you find your 178 | deallocations apply to many related objects at once. 179 | 180 | ## The Magic 181 | 182 | To put it bluntly: this library abuses virtual memory. 183 | 184 | Well, maybe not that strongly; however, this library makes contradictory 185 | promises: 186 | 187 | 1. Allocators that are easy to use, ie, don't require you to do extra work 188 | when you need more memory. 189 | 190 | 2. The allocators are also better than what the standard libraries provide 191 | you with along any number of axes, such as performance, fragmentation, 192 | and understandability. 193 | 194 | (I realize these aren't strictly contradictory, but they sound like they 195 | should be) 196 | 197 | To accomplish both of these I ~~sold my soul~~ decided to rely on the 198 | virtual memory capabilities of the operating system. On Windows, 199 | especially, it is easy to use VirtualAlloc and VirtualFree to create 200 | a large contiguous memory space. This space has almost no real memory cost 201 | until you start commiting pages out of it. By using this capability (which 202 | is possible to do on posix systems with some fanangling), a memory arena 203 | will happily reserve as much memory as you want it to. I figure a sane 204 | default is an amount equal to the amount of physical memory in the 205 | machine. Though I realize you could also go far beyond that (the commit 206 | limit on windows is that + 3x the size of the page file I believe), on its 207 | own, I fear this might not be the safest way to do business. However, the 208 | benefits are real: 209 | 210 | 1. You get very large contiguous ranges of memory to work with. 211 | 212 | 2. While the amount of memory you have isn't infinite, it might as well be 213 | for most projects 214 | 215 | 3. If you were ever to run out of memory, you'd be in trouble anyway. 216 | 217 | With that said, we move on to... 218 | 219 | ## Implementation Details, Caveats, and Flags 220 | 221 | These are mostly things I've thought of relating to the implementation, 222 | which might catch you off-guard if you aren't familiar with how allocators 223 | like these tend to work behind the scenes. 224 | 225 | A general note: to match the behavior of VirtualAlloc and similar, all 226 | memory returned by these allocators is zeroed by default. However, this is 227 | not a free operation, and as such, there exists a family of flags 228 | `wb_FlagNoZeroMemory` that disables the use of memset to zero 229 | memory. However, memset is still used to zero the object's memory in the 230 | initialization functions. This can be disabled by specifying 231 | `WB_ALLOC_NO_ZERO_ON_INIT`. 232 | 233 | #### Memory Arena 234 | 235 | The memory arena will happily allocate memory until it runs out of virtual 236 | space. When it about to run out of committed memory, it commits more, 237 | which means you might end up calling an OS function on any allocation in 238 | the library. 239 | 240 | To alleviate this worry, I have added a `wb_FlagFixedSize` flag 241 | to each allocator, which allows you to initialize them with a buffer of 242 | memory that they aren't allowed to overrun. While this will not call 243 | operating system functions at runtime, you have to pay for the memory up 244 | front instead. 245 | 246 | Memory arenas also have the capability to define a temporary head for 247 | a simple, one-off stack-like behavior. When the temporary state is ended, 248 | the arena, in addition to moving the head pointer to its original place, 249 | decommits and recommits the temporary pages by default. This may be 250 | disabled by using the `wb_FlagArenaNoRecommit` flag. However, to match the 251 | behavior of memory from VirtualAlloc, it will instead memset those pages 252 | to zero instead, which may also be disabled. 253 | 254 | #### Memory Pool 255 | 256 | Internally, the memory pool uses a free list of freed objects. To prevent 257 | double free errors, it walks the list every time you attempt to free an 258 | object. This can be disabled via the `wb_FlagPoolNoDoubleFreeCheck` flag. 259 | I feel that this may be safely disabled if you know what you're doing when 260 | you turn on release mode. 261 | 262 | Another possible gotcha of the memory pool is that, while it will not 263 | encounter external fragmentation, which would possibly decrease the amount 264 | of available memory over time, it can run into internal fragmentation, 265 | which may lead to poor cache behavior if you iterate over its contained 266 | objects in order. If, rather than holding onto individual pointers, you 267 | plan on accessing the pool's contents as an array, you can specify 268 | `wb_FlagPoolCompacting`, which will move the last element into the removed 269 | element's slot when freeing. This keeps the array of elements contiguous, 270 | but will invalidate pointers from the previous free. 271 | 272 | #### Tagged Heap 273 | 274 | I mentioned earlier that the tagged heap behaves like a pool of arenas, 275 | and the caveats that apply to both apply to it, to an extent. It too sits 276 | upon a single, expanding memory arena to back itself and its memory pool. 277 | Its internal arenas are fixed size, which means that you cannot allocate 278 | an object larger than an arena. I'm not sure how the actual Naughty Dog 279 | implementation works, but they mentioned their internal buffers were 280 | 2 megabytes each, which is probably enough for one-off allocations in 281 | things like games. 282 | 283 | Another limitation of the tagged heap is that, for simplicity, it simply 284 | stores an array of pointers to its arenas, and uses its numerical tags to 285 | index into that. By default, only space for 64 arenas is created, but this 286 | may be modified by changing `WB_ALLOC_TAGGED_HEAP_MAX_TAG_COUNT` to you 287 | preferred number. It is intended that you simply create your tags in an 288 | enum starting from zero. 289 | 290 | When allocating in a tagged heap, it is possible that the current arena 291 | for the specific tag would run out of room. By default, the tagged heap 292 | will retrieve another arena from its pool and use that. This is wasteful 293 | if you are allocating objects large enough that only one fits in an arena 294 | in addition to a bunch of smaller objects. To mitigate this, you can set 295 | the flag `wb_FlagTaggedHeapSearchForBestFit`, which changes the behavior 296 | to search for the best of the first 8 (by default) arenas to put the 297 | object in. 298 | 299 | ## C++ Support 300 | 301 | C++ adds a significant amount of friction when working with malloc and 302 | similar because it removes automatic `void*` coercion to other pointer 303 | types. The internals use casts to get around this, but this is especially 304 | unpleasant in the calling code. To alleviate this, I have added 305 | templatized overloads of every function that takes a size or returns 306 | a pointer, a few of which are listed listed here: 307 | 308 | ```C++ 309 | template 310 | T* wb_arenaPush(wb_MemoryArena* arena); 311 | 312 | template 313 | T* wb_poolRetrieve(wb_MemoryPool* pool); 314 | 315 | template 316 | wb_MemoryPool* wb_poolBootstrap(wb_MemoryInfo info, 317 | wb_flags flags); 318 | 319 | template 320 | T* wb_taggedAlloc(wb_TaggedHeap* heap, wb_isize tag); 321 | 322 | void example(wb_MemoryArena* arena, wb_MemoryInfo info) 323 | { 324 | auto numbers = wb_arenaPush(arena); 325 | auto thing10 = wb_arenaPush(arena); 326 | // numbers = (int*)wb_arenaPush(arena, sizeof(int) * 1000); 327 | // thing10 = (Thing*)wb_arenaPush(arena, sizeof(Thing)); 328 | 329 | auto thingPool = wb_poolBootstrap(info); 330 | 331 | /* MemoryPools don't actually remember what type they were 332 | initialized with, so you have to specify on allocation too */ 333 | auto thing2 = wb_poolRetrieve(thingPool); 334 | auto thing3 = wb_poolRetrieve(thingPool); 335 | auto thing4 = wb_poolRetrieve(thingPool); 336 | // thing5 = (Thing*)wb_poolRetrieve(thingPool); 337 | } 338 | ``` 339 | 340 | ## Roadmap 341 | 342 | This library is largely complete for its scope, and mostly needs some 343 | amount of robust testing. Potentially, I have a few features planned: 344 | 345 | - A double ended arena, behaving like the heap/stack behavior we all know. 346 | - Versions backed by malloc to use quickly if you don't want to use the 347 | virtual memory versions. 348 | - Fixed-size only versions of the allocators for complete memory-source 349 | agnosticism. 350 | 351 | I'm also considering writing an [Odin](https://github.com/gingerBill/Odin) 352 | version. 353 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set msvcdir="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\" 4 | if not defined DevEnvDir call %msvcdir%vcvars64.bat >nul 5 | 6 | cl /nologo /TC /Zi /W4 wb_alloc_test.c /link /INCREMENTAL:NO 7 | cl /nologo /TP /Zi /W4 wb_alloc_test_cpp.cpp /link /INCREMENTAL:NO 8 | 9 | cl /nologo /TC /Zi /W4 /Gd /EHsc ^ 10 | /Gs16000000 /GS- /Gm- ^ 11 | wb_alloc_test_nocrt.c /link ^ 12 | /STACK:16777216,16777216 ^ 13 | /NODEFAULTLIB kernel32.lib /INCREMENTAL:NO 14 | 15 | rem There might be a few errors lingering w/ the c89 version 16 | rem Every time I make a change, I'm convinced I break something 17 | rem as to C89 not being my "default" C version in my mind and 18 | rem as far as I can tell, modern MSVC makes no attempt to support 19 | rem it for legacy reasons. 20 | 21 | rem echo [clang] wb_alloc_test.c 22 | rem clang -x c --std=c89 -Wall -Wno-unused-variable wb_alloc_test.c -o wb_alloc_test.exe 23 | rem echo [clang] wb_alloc_test_cpp.cpp 24 | rem clang -x c++ --std=c++11 -Wall -Wno-unused-variable wb_alloc_test_cpp.cpp -o wb_alloc_test_cpp.exe 25 | 26 | 27 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | # This file builds the regular and C++ versions of the tests. 2 | # gcc -ansi -pedantic is as close as I can get to "true" C89/C90 standards 3 | 4 | cc=gcc 5 | 6 | echo wb_alloc_test.c 7 | ${cc} -x c -ansi -Wall -pedantic -Wno-format -Wno-unused-variable wb_alloc_test.c -o wb_alloc_test 8 | 9 | echo wb_alloc_test_cpp.cpp 10 | ${cc} -x c++ --std=c++98 -Wall -Wno-unused-variable wb_alloc_test_cpp.cpp -o wb_alloc_test_cpp 11 | 12 | echo "" 13 | 14 | 15 | -------------------------------------------------------------------------------- /make_clang.sh: -------------------------------------------------------------------------------- 1 | # This should just work on macOS, or anywhere with clang installed 2 | # As far as I can tell, --std=c89 does nothing on clang, so I stick to 3 | # --std==c99 for this version. 4 | 5 | cc=clang 6 | 7 | echo wb_alloc_test.c 8 | ${cc} -x c --std=c99 -Wall -Wno-unused-variable wb_alloc_test.c -o wb_alloc_test 9 | 10 | echo wb_alloc_test_cpp.cpp 11 | ${cc} -x c++ --std=c++11 -Wall -Wno-unused-variable wb_alloc_test_cpp.cpp -o wb_alloc_test_cpp 12 | 13 | echo "" 14 | 15 | 16 | -------------------------------------------------------------------------------- /wb_alloc.h: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | /* Check the bottom of this file for the remainder of the unlicense */ 3 | 4 | /* wb_alloc.h 5 | * 6 | * Three custom allocators that can (hopefully) safely allocate a very large 7 | * amount of memory for you. Check the warnings below for an explanation 8 | * 9 | * Version 0.1 Beta 10 | */ 11 | 12 | /* Author: William Bundy 13 | * Written in November of 2017 14 | * 15 | * A big thanks to @alexkelbo for helping me get this working on macOS 16 | * 17 | * williambundy.xyz 18 | * github.com/williambundy 19 | * Twitter: @William_Bundy 20 | */ 21 | 22 | /* =========================================================================== 23 | * WARNING -- BETA SOFTWARE 24 | * =========================================================================== 25 | * At this point, I can guarantee that most things seem to work without error. 26 | * I haven't pushed the limits or tested every configuration possible. 27 | * 28 | * If the application compiles but segfaults, try including the requisite 29 | * OS headers! 30 | * 31 | * #include 32 | * 33 | * or 34 | * 35 | * #include 36 | * #include 37 | * 38 | * I embed sections of these headers in the document, but they may not match 39 | * what your OS expects. (This is likely especially true of BSD) 40 | * 41 | * Please report all bugs to the github. 42 | * =========================================================================== 43 | * 44 | * Also: Warning -- Virtual Memory abuse 45 | * 46 | * The way the core allocator here works, the MemoryArena, is by reserving 47 | * a ton of virtual memory up front and committing out of it as necessary. 48 | * 49 | * By "a ton" I mean an amount equal to the physical amount of ram on your 50 | * machine. I have 16 gb in my tower, so I reserve 16 gb. 51 | * 52 | * This is probably fine, but... 53 | * 54 | * This might be a bad idea! 55 | * 56 | * It's also a windows-centric design, so it may work poorly on POSIX systems 57 | * that only have mmap. 58 | * 59 | * I've used this quite a bit in my own code, and it's worked fine there, 60 | * but you might find you have problems. 61 | */ 62 | 63 | /* To use this library, include it as 64 | * #define WB_ALLOC_IMPLEMENTATION 65 | * #include "wb_alloc.h" 66 | * 67 | * It probably only works from C right now, sorry! 68 | * 69 | * In a single translation unit, otherwise it behaves as a header 70 | * 71 | * Only minimal documentation is available right now, but check line 317 72 | * for some details. 73 | * 74 | * There is also a short demo at the bottom of the file. 75 | * 76 | * Options: 77 | * 78 | * #define WB_ALLOC_API 79 | * #define WB_ALLOC_BACKEND_API 80 | * This will apply to all functions in the library. By default, without 81 | * WB_ALLOC_IMPLEMENTATION, it is extern, and with it, it is blank 82 | * The internal virtual memory wrapping functions are defined with 83 | * WB_ALLOC_BACKEND_API, which defaults to WB_ALLOC_API. 84 | * 85 | * #ifndef WB_ALLOC_CUSTOM_INTEGER_TYPES 86 | * The library uses wb_usize, wb_isize, and wb_iflags. These are typedef'd 87 | * size_t, ptrdiff_t, and int, respectively. Typedef these on your own and 88 | * define WB_ALLOC_CUSTOM_INTEGERTYPES to ignore them. 89 | * 90 | * #define WB_ALLOC_STACK_PTR wb_usize 91 | * #define WB_ALLOC_EXTENDED_INFO wb_isize 92 | * These are options for modes of wb_MemoryArena. By default they are 8 byte 93 | * integers. 94 | * 95 | * #define WB_ALLOC_MEMSET memset 96 | * #define WB_ALLOC_MEMCPY memcpy 97 | * wb_alloc uses memset and memcpy. If neither of these are present, the 98 | * library includes string.h. You may define your own, and it will not. 99 | * 100 | * #define WB_ALLOC_TAGGEDHEAP_MAX_TAG_COUNT 64 101 | * This defines the total number of tags available to a tagged heap. If you 102 | * need more than 64, or far fewer, redefine it as you need. 103 | * 104 | * #define WB_ALLOC_NO_ZERO_ON_INIT 105 | * Whenever you call wb_allocatorInit(wb_allocator*, ...) we zero the pointer 106 | * you give, unless this flag is set. 107 | * 108 | * #define WB_ALLOC_CPLUSPLUS_FEATURES 109 | * If you are using C++, there are some "features" of C that are not available, 110 | * first and foremost, automatic void* coercion to other pointer types. To save 111 | * your fingers, I've added templated versions of the regular allocation 112 | * functions, which use the provided type to determine the size and return 113 | * type. Too, you can use arenaPush(arena) to allocate room for 114 | * 100 objects (this exists on a few, search for WB_ALLOC_CPLUSPLUS_FEATURES 115 | * to find the actual prototypes). 116 | */ 117 | 118 | /* Things I've borrowed that influence this: 119 | * http://blog.nervus.org/managing-virtual-address-spaces-with-mmap/ 120 | * Jason Gregory's trick where you embed the free list into the pool slots! 121 | * That Naughty Dog GDC talk about fibers where he mentions the tagged heap 122 | */ 123 | 124 | /* 125 | * Roadmap: 126 | * - Testing 127 | * - C++ integration features 128 | * - Fixed-size only version that doesn't define anything to do 129 | * with virtual memory 130 | * - A simple malloc-backed memory arena for projects that really don't 131 | * need anything special at all 132 | */ 133 | 134 | #ifndef WB_ALLOC_NO_DISABLE_STUPID_MSVC_WARNINGS 135 | #ifdef _MSC_VER 136 | #pragma warning(push) 137 | #pragma warning(disable:201 204 28 244 706) 138 | #endif 139 | #endif 140 | 141 | 142 | #ifndef WB_ALLOC_ERROR_HANDLER 143 | #define WB_ALLOC_ERROR_HANDLER(message, object, name) fprintf(stderr, \ 144 | "wbAlloc error: [%s] %s\n", name, message) 145 | #endif 146 | 147 | #ifdef WB_ALLOC_CPLUSPLUS_FEATURES 148 | #ifndef __cplusplus 149 | #undef WB_ALLOC_CPLUSPLUS_FEATURES 150 | #endif 151 | #endif 152 | 153 | #if !(defined(WB_ALLOC_POSIX) || defined(WB_ALLOC_WINDOWS)) 154 | #ifdef _MSC_VER 155 | #define WB_ALLOC_WINDOWS 156 | #endif 157 | #ifdef __unix__ 158 | #define WB_ALLOC_POSIX 159 | #endif 160 | #ifdef __APPLE__ 161 | #define WB_ALLOC_POSIX 162 | #endif 163 | #endif 164 | 165 | #ifndef WB_ALLOC_API 166 | #ifdef WB_ALLOC_IMPLEMENTATION 167 | #define WB_ALLOC_API 168 | #else 169 | #define WB_ALLOC_API extern 170 | #endif 171 | #endif 172 | 173 | #ifndef WB_ALLOC_BACKEND_API 174 | #define WB_ALLOC_BACKEND_API WB_ALLOC_API 175 | #endif 176 | 177 | 178 | #ifndef WB_ALLOC_CUSTOM_INTEGER_TYPES 179 | #include 180 | typedef size_t wb_usize; 181 | typedef ptrdiff_t wb_isize; 182 | typedef wb_isize wb_iflags; 183 | #endif 184 | 185 | 186 | #ifndef WB_ALLOC_STACK_PTR 187 | #define WB_ALLOC_STACK_PTR wb_usize 188 | #endif 189 | 190 | #ifndef WB_ALLOC_EXTENDED_INFO 191 | #define WB_ALLOC_EXTENDED_INFO wb_isize 192 | #endif 193 | 194 | #if !defined(WB_ALLOC_MEMSET) && !defined(WB_ALLOC_MEMCPY) 195 | /* NOTE(will): if the user hasn't provided their own functions, we want 196 | * to use the CRT ones automatically, even if they don't have string.h 197 | * included 198 | */ 199 | #include 200 | #endif 201 | 202 | #ifndef WB_ALLOC_MEMSET 203 | #define WB_ALLOC_MEMSET memset 204 | #endif 205 | 206 | #ifndef WB_ALLOC_MEMCPY 207 | #define WB_ALLOC_MEMCPY memcpy 208 | #endif 209 | 210 | #ifndef WB_ALLOC_TAGGEDHEAP_MAX_TAG_COUNT 211 | /* NOTE(will): if you listen to the Naughty Dog talk the tagged heap is based 212 | * on, it seems like they only have ~4 tags? Something like "game", "render", 213 | * "physics", "anim", kinda thing. 64 should be way more than enough if that 214 | * is the average use case 215 | */ 216 | #define WB_ALLOC_TAGGEDHEAP_MAX_TAG_COUNT 64 217 | #endif 218 | 219 | #define wb_CalcKilobytes(x) (((wb_usize)x) * 1024) 220 | #define wb_CalcMegabytes(x) (wb_CalcKilobytes((wb_usize)x) * 1024) 221 | #define wb_CalcGigabytes(x) (wb_CalcMegabytes((wb_usize)x) * 1024) 222 | 223 | /* These are equivalent to the PROT_READ values and friends */ 224 | #define wb_NoneAccess 0 225 | #define wb_ReadAccess 1 226 | #define wb_WriteAccess 2 227 | #define wb_ExecuteAccess 4 228 | 229 | #define wb_Arena_Normal 0 230 | #define wb_Arena_FixedSize 1 231 | #define wb_Arena_Stack 2 232 | #define wb_Arena_Extended 4 233 | #define wb_Arena_NoZeroMemory 8 234 | #define wb_Arena_NoRecommit 16 235 | 236 | #define wb_Pool_Normal 0 237 | #define wb_Pool_FixedSize 1 238 | #define wb_Pool_Compacting 2 239 | #define wb_Pool_NoZeroMemory 4 240 | #define wb_Pool_NoDoubleFreeCheck 8 241 | 242 | #define wb_TaggedHeap_Normal 0 243 | #define wb_TaggedHeap_FixedSize 1 244 | #define wb_TaggedHeap_NoZeroMemory 2 245 | #define wb_TaggedHeap_NoSetCommitSize 4 246 | #define wb_TaggedHeap_SearchForBestFit 8 247 | #define wbi__TaggedHeapSearchSize 8 248 | 249 | /* Struct Definitions */ 250 | 251 | typedef struct wb_MemoryInfo wb_MemoryInfo; 252 | struct wb_MemoryInfo 253 | { 254 | wb_usize totalMemory, commitSize, pageSize; 255 | wb_iflags commitFlags; 256 | }; 257 | 258 | typedef struct wb_MemoryArena wb_MemoryArena; 259 | struct wb_MemoryArena 260 | { 261 | const char* name; 262 | void *start, *head, *end; 263 | void *tempStart, *tempHead; 264 | wb_MemoryInfo info; 265 | wb_isize align; 266 | wb_iflags flags; 267 | }; 268 | 269 | typedef struct wb_MemoryPool wb_MemoryPool; 270 | struct wb_MemoryPool 271 | { 272 | wb_usize elementSize; 273 | wb_isize count, capacity; 274 | void* slots; 275 | const char* name; 276 | void** freeList; 277 | wb_MemoryArena* alloc; 278 | wb_isize lastFilled; 279 | wb_iflags flags; 280 | }; 281 | 282 | typedef struct wbi__TaggedHeapArena wbi__TaggedHeapArena; 283 | struct wbi__TaggedHeapArena 284 | { 285 | wb_isize tag; 286 | wbi__TaggedHeapArena *next; 287 | void *head, *end; 288 | char buffer; 289 | }; 290 | 291 | typedef struct wb_TaggedHeap wb_TaggedHeap; 292 | struct wb_TaggedHeap 293 | { 294 | const char* name; 295 | wb_MemoryPool pool; 296 | wbi__TaggedHeapArena* arenas[WB_ALLOC_TAGGEDHEAP_MAX_TAG_COUNT]; 297 | wb_MemoryInfo info; 298 | wb_usize arenaSize, align; 299 | wb_iflags flags; 300 | }; 301 | 302 | /* Function Prototypes */ 303 | 304 | /* arenaPush and arenaPushEx increment the head pointer of the provided 305 | * arena and return the old one. It is safe to write information within 306 | * the size provided to the function. 307 | * 308 | * The Ex version also takes a WB_ALLOC_EXTENDED_INFO (defaults to a ptrdiff_t- 309 | * sized int; you may wish to redefine it before including the file), which, 310 | * if you're using an arena with the ArenaExtended flag enabled, will store 311 | * that information before your allocation. 312 | */ 313 | 314 | WB_ALLOC_API 315 | void* wb_arenaPushEx(wb_MemoryArena* arena, 316 | wb_isize size, 317 | WB_ALLOC_EXTENDED_INFO extended); 318 | 319 | WB_ALLOC_API 320 | void* wb_arenaPush(wb_MemoryArena* arena, wb_isize size); 321 | 322 | /* poolRetrieve gets the next element out of the pool. If the pool hasn't 323 | * been used yet, it simply pulls the next item out at the correct location. 324 | * Otherwise, it checks a free list of empty slots. 325 | * 326 | * poolRelease attaches the pointer to the free list. By default, it also 327 | * checks whether the pointer is already on the free list, to help prevent 328 | * double-free bugs. Use the PoolNoDoubleFreeCheck flag to disable this. 329 | * 330 | * If your pool is compacting (PoolCompacting flag), poolRelease will instead 331 | * copy the last element of the pool into the slot pointed at by ptr. 332 | * TODO(will): uh, maybe do some safety checks there... 333 | * This means that you can treat the pool's slots field as an array of your 334 | * struct/union and iterate over it without expecting holes; however, any 335 | * retrieve/release operations can invalidate your pointers 336 | */ 337 | WB_ALLOC_API 338 | void* wb_poolRetrieve(wb_MemoryPool* pool); 339 | WB_ALLOC_API 340 | void wb_poolRelease(wb_MemoryPool* pool, void* ptr); 341 | 342 | /* taggedAlloc behaves much like arenaPush, returning a pointer to a segment 343 | * of memory that is safe to write to. However, you cannot allocate more than 344 | * the arenaSize field of the heap at once; eg: if arenaSize is 1 megabyte, 345 | * you cannot allocate anything larger than that in that heap, ever. 346 | * 347 | * Internally, a tagged heap is a memory pool of arenas (simlified ones rather 348 | * than a full MemoryArena). It, by default, selects the first arena in the 349 | * list, but you can set the TaggedHeapSearchForBestFit flag, which will search 350 | * for (the first eight or so) arenas that can fit the object, then put it into 351 | * the one with the smallest space remaining. 352 | * 353 | * taggedFree allows you to free all allocations on a single tag at once. 354 | * If you do not specify TaggedHeapNoZeroMemory, it will also memset everything 355 | * to zero. 356 | * 357 | */ 358 | WB_ALLOC_API 359 | void* wb_taggedAlloc(wb_TaggedHeap* heap, wb_isize tag, wb_usize size); 360 | WB_ALLOC_API 361 | void wb_taggedFree(wb_TaggedHeap* heap, wb_isize tag); 362 | 363 | #ifdef WB_ALLOC_CPLUSPLUS_FEATURES 364 | template 365 | WB_ALLOC_API 366 | T* wb_arenaPushEx(wb_MemoryArena* arena, 367 | WB_ALLOC_EXTENDED_INFO extended, 368 | int n = 1); 369 | 370 | template 371 | WB_ALLOC_API 372 | T* wb_arenaPush(wb_MemoryArena* arena, int n = 1); 373 | 374 | template 375 | WB_ALLOC_API 376 | T* wb_poolRetrieve(wb_MemoryPool* pool); 377 | 378 | template 379 | WB_ALLOC_API 380 | void wb_poolRelease(wb_MemoryPool* pool, T* ptr); 381 | 382 | 383 | template 384 | WB_ALLOC_API 385 | void wb_poolInit(wb_MemoryPool* pool, wb_MemoryArena* alloc, 386 | wb_iflags flags); 387 | 388 | template 389 | WB_ALLOC_API 390 | wb_MemoryPool* wb_poolBootstrap(wb_MemoryInfo info, 391 | wb_iflags flags); 392 | 393 | template 394 | WB_ALLOC_API 395 | wb_MemoryPool* wb_poolFixedSizeBootstrap( 396 | void* buffer, wb_usize size, 397 | wb_iflags flags); 398 | 399 | 400 | template 401 | WB_ALLOC_API 402 | T* wb_taggedAlloc(wb_TaggedHeap* heap, wb_isize tag, int n = 1); 403 | #endif 404 | 405 | 406 | /* TODO(will): Write per-function documentation */ 407 | 408 | WB_ALLOC_BACKEND_API void* wbi__allocateVirtualSpace(wb_usize size); 409 | WB_ALLOC_BACKEND_API void* wbi__commitMemory(void* addr, wb_usize size, 410 | wb_iflags flags); 411 | WB_ALLOC_BACKEND_API void wbi__decommitMemory(void* addr, wb_usize size); 412 | WB_ALLOC_BACKEND_API void wbi__freeAddressSpace(void* addr, wb_usize size); 413 | WB_ALLOC_BACKEND_API wb_MemoryInfo wb_getMemoryInfo(); 414 | 415 | WB_ALLOC_API 416 | wb_isize wb_alignTo(wb_usize x, wb_usize align); 417 | 418 | WB_ALLOC_API 419 | void wb_arenaInit(wb_MemoryArena* arena, wb_MemoryInfo info, wb_iflags flags); 420 | WB_ALLOC_API 421 | wb_MemoryArena* wb_arenaBootstrap(wb_MemoryInfo info, wb_iflags flags); 422 | 423 | WB_ALLOC_API 424 | void wb_arenaFixedSizeInit(wb_MemoryArena* arena, 425 | void* buffer, wb_isize size, 426 | wb_iflags flags); 427 | WB_ALLOC_API 428 | wb_MemoryArena* arenaFixedSizeBootstrap( 429 | void* buffer, wb_usize size, 430 | wb_iflags flags); 431 | 432 | 433 | WB_ALLOC_API 434 | void wb_arenaPop(wb_MemoryArena* arena); 435 | 436 | WB_ALLOC_API 437 | void wb_arenaStartTemp(wb_MemoryArena* arena); 438 | WB_ALLOC_API 439 | void wb_arenaEndTemp(wb_MemoryArena* arena); 440 | 441 | WB_ALLOC_API 442 | void wb_arenaClear(wb_MemoryArena* arena); 443 | WB_ALLOC_API 444 | void wb_arenaDestroy(wb_MemoryArena* arena); 445 | 446 | 447 | WB_ALLOC_API 448 | void wb_poolInit( 449 | wb_MemoryPool* pool, 450 | wb_MemoryArena* alloc, 451 | wb_usize elementSize, 452 | wb_iflags flags); 453 | 454 | WB_ALLOC_API 455 | wb_MemoryPool* wb_poolBootstrap( 456 | wb_MemoryInfo info, 457 | wb_isize elementSize, 458 | wb_iflags flags); 459 | 460 | WB_ALLOC_API 461 | wb_MemoryPool* wb_poolFixedSizeBootstrap( 462 | wb_isize elementSize, 463 | void* buffer, wb_usize size, 464 | wb_iflags flags); 465 | 466 | 467 | WB_ALLOC_API wb_isize wb_calcTaggedHeapSize( 468 | wb_isize arenaSize, wb_isize arenaCount, 469 | wb_iflags bootstrapped); 470 | 471 | WB_ALLOC_API 472 | void wb_taggedInit( 473 | wb_TaggedHeap* heap, 474 | wb_MemoryArena* arena, 475 | wb_isize internalArenaSize, 476 | wb_iflags flags); 477 | 478 | WB_ALLOC_API 479 | wb_TaggedHeap* wb_taggedBootstrap(wb_MemoryInfo info, 480 | wb_isize arenaSize, 481 | wb_iflags flags); 482 | 483 | WB_ALLOC_API 484 | wb_TaggedHeap* wb_taggedFixedSizeBootstrap(wb_isize arenaSize, 485 | void* buffer, wb_isize bufferSize, 486 | wb_iflags flags); 487 | 488 | WB_ALLOC_API 489 | void wbi__taggedArenaInit(wb_TaggedHeap* heap, 490 | wbi__TaggedHeapArena* arena, 491 | wb_isize tag); 492 | 493 | WB_ALLOC_API 494 | void wbi__taggedArenaSortBySize(wbi__TaggedHeapArena** array, wb_isize count); 495 | 496 | 497 | /* Platform-Specific Code */ 498 | 499 | #ifndef WB_ALLOC_CUSTOM_BACKEND 500 | 501 | #ifdef __cplusplus 502 | #define wbi__SystemExtern extern "C" 503 | #else 504 | #define wbi__SystemExtern extern 505 | #endif 506 | 507 | #ifdef WB_ALLOC_WINDOWS 508 | #ifndef _WINDOWS_ 509 | #ifndef WINAPI 510 | #define WINAPI __stdcall 511 | #endif 512 | #ifndef _In_ 513 | #define _In_ 514 | #define _Out_ 515 | #define _In_opt_ 516 | #endif 517 | typedef unsigned int DWORD; 518 | typedef unsigned short WORD; 519 | typedef DWORD* DWORD_PTR; 520 | typedef void* LPVOID; 521 | typedef int BOOL; 522 | typedef unsigned long long* PULONGLONG; 523 | 524 | typedef struct _SYSTEM_INFO { 525 | union { 526 | DWORD dwOemId; 527 | struct { 528 | WORD wProcessorArchitecture; 529 | WORD wReserved; 530 | }; 531 | }; 532 | DWORD dwPageSize; 533 | LPVOID lpMinimumApplicationAddress; 534 | LPVOID lpMaximumApplicationAddress; 535 | DWORD_PTR dwActiveProcessorMask; 536 | DWORD dwNumberOfProcessors; 537 | DWORD dwProcessorType; 538 | DWORD dwAllocationGranularity; 539 | WORD wProcessorLevel; 540 | WORD wProcessorRevision; 541 | } SYSTEM_INFO, *LPSYSTEM_INFO; 542 | 543 | wbi__SystemExtern 544 | DWORD WINAPI GetLastError(void); 545 | 546 | wbi__SystemExtern 547 | void WINAPI GetSystemInfo( 548 | _Out_ LPSYSTEM_INFO lpSystemInfo 549 | ); 550 | 551 | wbi__SystemExtern 552 | BOOL WINAPI GetPhysicallyInstalledSystemMemory( 553 | _Out_ PULONGLONG TotalMemoryInKilobytes 554 | ); 555 | 556 | wbi__SystemExtern 557 | LPVOID WINAPI VirtualAlloc( 558 | _In_opt_ LPVOID lpAddress, 559 | _In_ wb_usize dwSize, 560 | _In_ DWORD flAllocationType, 561 | _In_ DWORD flProtect 562 | ); 563 | 564 | wbi__SystemExtern 565 | BOOL WINAPI VirtualFree( 566 | _In_ LPVOID lpAddress, 567 | _In_ wb_usize dwSize, 568 | _In_ DWORD dwFreeType 569 | ); 570 | 571 | #define MEM_COMMIT 0x1000 572 | #define MEM_RESERVE 0x2000 573 | #define PAGE_EXECUTE 0x10 574 | #define PAGE_EXECUTE_READ 0x20 575 | #define PAGE_EXECUTE_READWRITE 0x40 576 | #define PAGE_EXECUTE_WRITECOPY 0x80 577 | #define PAGE_NOACCESS 0x1 578 | #define PAGE_READONLY 0x2 579 | #define PAGE_READWRITE 0x4 580 | #define PAGE_WRITECOPY 0x8 581 | #define MEM_DECOMMIT 0x4000 582 | #define MEM_RELEASE 0x8000 583 | #endif 584 | 585 | #ifdef WB_ALLOC_IMPLEMENTATION 586 | 587 | WB_ALLOC_BACKEND_API 588 | void* wbi__allocateVirtualSpace(wb_usize size) 589 | { 590 | return VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); 591 | } 592 | 593 | WB_ALLOC_BACKEND_API 594 | void* wbi__commitMemory(void* addr, wb_usize size, wb_iflags flags) 595 | { 596 | DWORD newFlags = 0; 597 | if(flags & wb_ReadAccess) { 598 | if(flags & wb_WriteAccess) { 599 | if(flags & wb_ExecuteAccess) { 600 | newFlags = PAGE_EXECUTE_READWRITE; 601 | } 602 | newFlags = PAGE_READWRITE; 603 | } else if(flags & wb_ExecuteAccess) { 604 | newFlags = PAGE_EXECUTE_READ; 605 | } else { 606 | newFlags = PAGE_READONLY; 607 | } 608 | } else if(flags & wb_WriteAccess) { 609 | if(flags & wb_ExecuteAccess) { 610 | newFlags = PAGE_EXECUTE_READWRITE; 611 | } else { 612 | newFlags = PAGE_READWRITE; 613 | } 614 | } else if(flags & wb_ExecuteAccess) { 615 | newFlags = PAGE_EXECUTE; 616 | } else { 617 | newFlags = PAGE_NOACCESS; 618 | } 619 | 620 | return VirtualAlloc(addr, size, MEM_COMMIT, newFlags); 621 | } 622 | 623 | WB_ALLOC_BACKEND_API 624 | void wbi__decommitMemory(void* addr, wb_usize size) 625 | { 626 | VirtualFree((void*)addr, size, MEM_DECOMMIT); 627 | } 628 | 629 | WB_ALLOC_BACKEND_API 630 | void wbi__freeAddressSpace(void* addr, wb_usize size) 631 | { 632 | /* This is a stupid hack, but blame clang/gcc with -Wall; not me */ 633 | /* In any kind of optimized code, this should just get removed. */ 634 | wb_usize clangWouldWarnYouAboutThis = size; 635 | clangWouldWarnYouAboutThis++; 636 | VirtualFree((void*)addr, 0, MEM_RELEASE); 637 | } 638 | 639 | WB_ALLOC_BACKEND_API 640 | wb_MemoryInfo wb_getMemoryInfo() 641 | { 642 | SYSTEM_INFO systemInfo; 643 | wb_MemoryInfo info; 644 | GetSystemInfo(&systemInfo); 645 | wb_usize pageSize, localMem, totalMem; 646 | int ret; 647 | 648 | pageSize = systemInfo.dwPageSize; 649 | 650 | localMem = 0; 651 | totalMem = 0; 652 | ret = GetPhysicallyInstalledSystemMemory(&localMem); 653 | if(ret) { 654 | totalMem = localMem * 1024; 655 | } 656 | 657 | info.totalMemory = totalMem; 658 | info.commitSize = wb_CalcMegabytes(1); 659 | info.pageSize = pageSize; 660 | info.commitFlags = wb_ReadAccess | wb_WriteAccess; 661 | return info; 662 | 663 | } 664 | #endif 665 | #endif 666 | 667 | #ifdef WB_ALLOC_POSIX 668 | #ifndef __APPLE__ 669 | #ifndef PROT_NONE 670 | #define PROT_NONE 0 671 | #define PROT_READ 0x1 672 | #define PROT_WRITE 0x2 673 | #define PROT_EXEC 0x4 674 | #define MAP_SHARED 0x1 675 | #define MAP_PRIVATE 0x2 676 | #define MAP_FIXED 0x10 677 | #define MAP_ANON 0x20 678 | #define MS_SYNC 1 679 | #define MS_ASYNC 4 680 | #define MS_INVALIDATE 2 681 | #endif 682 | 683 | #ifndef _SC_PAGESIZE 684 | #define _SC_PAGESIZE 30 685 | #endif 686 | 687 | #ifndef __off_t_defined 688 | typedef wb_usize off_t; 689 | #endif 690 | 691 | #else 692 | #ifndef PROT_NONE 693 | #define PROT_READ 1 694 | #define PROT_WRITE 2 695 | #define PROT_EXEC 4 696 | #define PROT_NONE 0 697 | #define MAP_SHARED 1 698 | #define MAP_PRIVATE 2 699 | #define MAP_FIXED 16 700 | #define MAP_ANON 4096 701 | #define MS_SYNC 16 702 | #define MS_ASYNC 1 703 | #define MS_INVALIDATE 2 704 | #endif 705 | 706 | #ifndef _SC_PAGESIZE 707 | #define _SC_PAGESIZE 29 708 | #endif 709 | #endif 710 | 711 | #ifndef _SYS_SYSINFO_H 712 | typedef short unsigned int wbi__u16; 713 | typedef unsigned int wbi__u32; 714 | typedef long int wbi__i64; 715 | typedef unsigned long int wbi__u64; 716 | struct sysinfo 717 | { 718 | wbi__i64 uptime; 719 | wbi__u64 loads[3]; 720 | wbi__u64 totalram; 721 | wbi__u64 freeMemory; 722 | wbi__u64 sharedMemory; 723 | wbi__u64 bufferMemory; 724 | wbi__u64 totalSwap; 725 | wbi__u64 freeSwap; 726 | wbi__u16 procs; 727 | wbi__u16 pad; 728 | wbi__u64 totalHigh; 729 | wbi__u64 freeHigh; 730 | wbi__u32 memUnit; 731 | #ifdef WB_ALLOC_POSIX_SYSINFO_PADDING 732 | char _f[20-2*sizeof(long)-sizeof(int)]; 733 | #endif 734 | }; 735 | wbi__SystemExtern 736 | int sysinfo(struct sysinfo* info); 737 | 738 | #ifdef __APPLE__ 739 | #ifndef CTL_HW 740 | #define CTL_HW 6 741 | #define HW_PAGESIZE 7 742 | #define HW_PHYSMEM 5 743 | #define HW_MEMSIZE 24 744 | 745 | wbi__SystemExtern 746 | int sysctl(int *name, 747 | unsigned int namelen, 748 | void *oldp, size_t *oldlenp, 749 | void *newp, size_t newlen); 750 | 751 | #endif 752 | WB_ALLOC_BACKEND_API 753 | int __cdecl sysinfo(struct sysinfo* info) 754 | { 755 | int mib[2]; 756 | wb_usize size, eightByte; 757 | 758 | size = sizeof(wb_usize); 759 | mib[0] = CTL_HW; 760 | mib[1] = HW_MEMSIZE; 761 | sysctl(mib, 2, &eightByte, &size, NULL, 0); 762 | info->totalram = eightByte; 763 | 764 | #if 0 765 | mib[0] = CTL_HW; 766 | mib[1] = HW_PAGESIZE; 767 | size = sizeof(int); 768 | sysctl(mib, 2, &fourByte, &size, NULL, 0); 769 | info->freeMemory = (wbi__u64)fourByte; 770 | #endif 771 | return 0; 772 | } 773 | 774 | #endif 775 | wbi__SystemExtern 776 | void* mmap(void* addr, wb_usize len, int prot, 777 | int flags, int fd, off_t offset); 778 | wbi__SystemExtern 779 | int munmap(void* addr, wb_usize len); 780 | wbi__SystemExtern 781 | int msync(void* addr, wb_usize len, int flags); 782 | wbi__SystemExtern 783 | long sysconf(int name); 784 | 785 | #ifdef WB_ALLOC_IMPLEMENTATION 786 | WB_ALLOC_BACKEND_API 787 | void* wbi__allocateVirtualSpace(wb_usize size) 788 | { 789 | void * ptr = mmap((void*)0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0); 790 | msync(ptr, size, MS_SYNC|MS_INVALIDATE); 791 | return ptr; 792 | } 793 | 794 | WB_ALLOC_BACKEND_API 795 | void* wbi__commitMemory(void* addr, wb_usize size, wb_iflags flags) 796 | { 797 | void * ptr = mmap(addr, size, flags, MAP_FIXED|MAP_SHARED|MAP_ANON, -1, 0); 798 | msync(addr, size, MS_SYNC|MS_INVALIDATE); 799 | return ptr; 800 | } 801 | 802 | WB_ALLOC_BACKEND_API 803 | void wbi__decommitMemory(void* addr, wb_usize size) 804 | { 805 | mmap(addr, size, PROT_NONE, MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); 806 | msync(addr, size, MS_SYNC|MS_INVALIDATE); 807 | } 808 | 809 | WB_ALLOC_BACKEND_API 810 | void wbi__freeAddressSpace(void* addr, wb_usize size) 811 | { 812 | msync(addr, size, MS_SYNC); 813 | munmap(addr, size); 814 | } 815 | 816 | WB_ALLOC_BACKEND_API 817 | wb_MemoryInfo wb_getMemoryInfo() 818 | { 819 | struct sysinfo si; 820 | wb_usize totalMem, pageSize; 821 | wb_MemoryInfo info; 822 | sysinfo(&si); 823 | totalMem = si.totalram; 824 | pageSize = sysconf(_SC_PAGESIZE); 825 | 826 | info.totalMemory = totalMem; 827 | info.commitSize = wb_CalcMegabytes(1); 828 | info.pageSize = pageSize; 829 | info.commitFlags = wb_ReadAccess | wb_WriteAccess; 830 | return info; 831 | 832 | } 833 | #endif 834 | #endif 835 | #endif 836 | #endif 837 | 838 | /* =========================================================================== 839 | * Main library -- Platform non-specific code 840 | * =========================================================================== 841 | */ 842 | 843 | #ifdef WB_ALLOC_IMPLEMENTATION 844 | WB_ALLOC_API 845 | wb_isize wb_alignTo(wb_usize x, wb_usize align) 846 | { 847 | wb_usize mod = x & (align - 1); 848 | return mod ? x + (align - mod) : x; 849 | } 850 | 851 | /* Memory Arena */ 852 | 853 | WB_ALLOC_API 854 | void wb_arenaFixedSizeInit(wb_MemoryArena* arena, 855 | void* buffer, wb_isize size, 856 | wb_iflags flags) 857 | { 858 | #ifndef WB_ALLOC_NO_ZERO_ON_INIT 859 | WB_ALLOC_MEMSET(arena, 0, sizeof(wb_MemoryArena)); 860 | #endif 861 | 862 | arena->name = "arena"; 863 | 864 | arena->flags = flags | wb_Arena_FixedSize; 865 | arena->align = 8; 866 | 867 | arena->start = buffer; 868 | arena->head = buffer; 869 | arena->end = (void*)((wb_isize)arena->start + size); 870 | arena->tempStart = NULL; 871 | arena->tempHead = NULL; 872 | } 873 | 874 | 875 | WB_ALLOC_API 876 | void wb_arenaInit(wb_MemoryArena* arena, wb_MemoryInfo info, wb_iflags flags) 877 | { 878 | void* ret; 879 | #ifndef WB_ALLOC_NO_ZERO_ON_INIT 880 | WB_ALLOC_MEMSET(arena, 0, sizeof(wb_MemoryArena)); 881 | #endif 882 | 883 | #ifndef WB_ALLOC_NO_FLAG_CORRECTNESS_CHECKS 884 | if(flags & wb_Arena_FixedSize) { 885 | WB_ALLOC_ERROR_HANDLER( 886 | "can't create a fixed-size arena with arenaInit\n" 887 | "use arenaFixedSizeInit instead.", 888 | arena, "arena"); 889 | return; 890 | } 891 | #endif 892 | 893 | arena->flags = flags; 894 | arena->name = "arena"; 895 | arena->info = info; 896 | arena->start = wbi__allocateVirtualSpace(info.totalMemory); 897 | ret = wbi__commitMemory(arena->start, 898 | info.commitSize, 899 | info.commitFlags); 900 | if(!ret) { 901 | WB_ALLOC_ERROR_HANDLER("failed to commit inital memory", 902 | arena, arena->name); 903 | return; 904 | } 905 | arena->head = arena->start; 906 | arena->end = (char*)arena->start + info.commitSize; 907 | arena->tempStart = NULL; 908 | arena->tempHead = NULL; 909 | arena->align = 8; 910 | } 911 | 912 | WB_ALLOC_API 913 | void* wb_arenaPushEx(wb_MemoryArena* arena, wb_isize size, 914 | WB_ALLOC_EXTENDED_INFO extended) 915 | { 916 | void *oldHead, *ret; 917 | wb_usize newHead, toExpand; 918 | 919 | if(arena->flags & wb_Arena_Stack) { 920 | size += sizeof(WB_ALLOC_STACK_PTR); 921 | } 922 | 923 | if(arena->flags & wb_Arena_Extended) { 924 | size += sizeof(WB_ALLOC_EXTENDED_INFO); 925 | } 926 | 927 | oldHead = arena->head; 928 | newHead = wb_alignTo((wb_isize)arena->head + size, arena->align); 929 | 930 | if(newHead > (wb_usize)arena->end) { 931 | if(arena->flags & wb_Arena_FixedSize) { 932 | WB_ALLOC_ERROR_HANDLER( 933 | "ran out of memory", 934 | arena, arena->name); 935 | return NULL; 936 | } 937 | 938 | toExpand = wb_alignTo(size, arena->info.commitSize); 939 | ret = wbi__commitMemory(arena->end, toExpand, arena->info.commitFlags); 940 | if(!ret) { 941 | WB_ALLOC_ERROR_HANDLER("failed to commit memory in arenaPush", 942 | arena, arena->name); 943 | return NULL; 944 | } 945 | arena->end = (char*)arena->end + toExpand; 946 | } 947 | 948 | if(arena->flags & wb_Arena_Stack) { 949 | WB_ALLOC_STACK_PTR* head; 950 | head = (WB_ALLOC_STACK_PTR*)newHead; 951 | 952 | head--; 953 | *head = (WB_ALLOC_STACK_PTR)oldHead; 954 | } 955 | 956 | if(arena->flags & wb_Arena_Extended) { 957 | WB_ALLOC_EXTENDED_INFO* head; 958 | head = (WB_ALLOC_EXTENDED_INFO*)oldHead; 959 | *head = extended; 960 | head++; 961 | oldHead = (void*)head; 962 | } 963 | 964 | arena->head = (void*)newHead; 965 | 966 | return oldHead; 967 | } 968 | 969 | WB_ALLOC_API 970 | void* wb_arenaPush(wb_MemoryArena* arena, wb_isize size) 971 | { 972 | return wb_arenaPushEx(arena, size, 0); 973 | } 974 | 975 | WB_ALLOC_API 976 | void wb_arenaPop(wb_MemoryArena* arena) 977 | { 978 | wb_usize prevHeadPtr; 979 | void* newHead; 980 | #ifndef WB_ALLOC_NO_FLAG_CORRECTNESS_CHECKS 981 | if(!(arena->flags & wb_Arena_Stack)) { 982 | WB_ALLOC_ERROR_HANDLER( 983 | "can't use arenaPop with non-stack arenas", 984 | arena, arena->name); 985 | return; 986 | } 987 | #endif 988 | 989 | 990 | prevHeadPtr = (wb_isize)arena->head - sizeof(WB_ALLOC_STACK_PTR); 991 | newHead = (void*)(*(WB_ALLOC_STACK_PTR*)prevHeadPtr); 992 | if((wb_isize)newHead <= (wb_isize)arena->start) { 993 | arena->head = arena->start; 994 | return; 995 | } 996 | 997 | if(!(arena->flags & wb_Arena_NoZeroMemory)) { 998 | wb_usize size; 999 | size = (wb_isize)arena->head - (wb_isize)newHead; 1000 | if(size > 0) { 1001 | WB_ALLOC_MEMSET(newHead, 0, size); 1002 | } 1003 | } 1004 | 1005 | arena->head = newHead; 1006 | } 1007 | 1008 | WB_ALLOC_API 1009 | wb_MemoryArena* wb_arenaBootstrap(wb_MemoryInfo info, wb_iflags flags) 1010 | { 1011 | wb_MemoryArena arena, *strapped; 1012 | #ifndef WB_ALLOC_NO_FLAG_CORRECTNESS_CHECKS 1013 | if(flags & wb_Arena_FixedSize) { 1014 | WB_ALLOC_ERROR_HANDLER( 1015 | "can't create a fixed-size arena with arenaBootstrap\n" 1016 | "use arenaFixedSizeBootstrap instead.", 1017 | NULL, "arena"); 1018 | return NULL; 1019 | } 1020 | #endif 1021 | 1022 | wb_arenaInit(&arena, info, flags); 1023 | strapped = (wb_MemoryArena*) 1024 | wb_arenaPush(&arena, sizeof(wb_MemoryArena) + 16); 1025 | *strapped = arena; 1026 | if(flags & wb_Arena_Stack) { 1027 | wb_arenaPushEx(strapped, 0, 0); 1028 | *((WB_ALLOC_STACK_PTR*)(strapped->head) - 1) = 1029 | (WB_ALLOC_STACK_PTR)strapped->head; 1030 | } 1031 | 1032 | return strapped; 1033 | } 1034 | 1035 | WB_ALLOC_API 1036 | wb_MemoryArena* arenaFixedSizeBootstrap(void* buffer, wb_usize size, 1037 | wb_iflags flags) 1038 | { 1039 | wb_MemoryArena arena, *strapped; 1040 | wb_arenaFixedSizeInit(&arena, buffer, size, flags | wb_Arena_FixedSize); 1041 | strapped = (wb_MemoryArena*) 1042 | wb_arenaPush(&arena, sizeof(wb_MemoryArena) + 16); 1043 | *strapped = arena; 1044 | if(flags & wb_Arena_Stack) { 1045 | wb_arenaPushEx(strapped, 0, 0); 1046 | *((WB_ALLOC_STACK_PTR*)(strapped->head) - 1) = 1047 | (WB_ALLOC_STACK_PTR)strapped->head; 1048 | } 1049 | return strapped; 1050 | } 1051 | 1052 | WB_ALLOC_API 1053 | void wb_arenaStartTemp(wb_MemoryArena* arena) 1054 | { 1055 | if(arena->tempStart) return; 1056 | arena->tempStart = (void*)wb_alignTo((wb_isize)arena->head, 1057 | arena->info.pageSize); 1058 | arena->tempHead = arena->head; 1059 | arena->head = arena->tempStart; 1060 | } 1061 | 1062 | WB_ALLOC_API 1063 | void wb_arenaEndTemp(wb_MemoryArena* arena) 1064 | { 1065 | wb_isize size; 1066 | if(!arena->tempStart) return; 1067 | arena->head = (void*)wb_alignTo((wb_isize)arena->head, arena->info.pageSize); 1068 | size = (wb_isize)arena->head - (wb_isize)arena->tempStart; 1069 | 1070 | /* NOTE(will): if you have an arena with flags 1071 | * ArenaNoRecommit | ArenaNoZeroMemory 1072 | * This just moves the pointer, which might be something you want to do. 1073 | */ 1074 | if(!(arena->flags & wb_Arena_NoRecommit)) { 1075 | wbi__decommitMemory(arena->tempStart, size); 1076 | wbi__commitMemory(arena->tempStart, size, arena->info.commitFlags); 1077 | } else if(!(arena->flags & wb_Arena_NoZeroMemory)) { 1078 | WB_ALLOC_MEMSET(arena->tempStart, 0, 1079 | (wb_isize)arena->head - (wb_isize)arena->tempStart); 1080 | } 1081 | 1082 | arena->head = arena->tempHead; 1083 | arena->tempHead = NULL; 1084 | arena->tempStart = NULL; 1085 | } 1086 | 1087 | WB_ALLOC_API 1088 | void wb_arenaClear(wb_MemoryArena* arena) 1089 | { 1090 | wb_MemoryArena local = *arena; 1091 | wb_isize size = (wb_isize)arena->end - (wb_isize)arena->start; 1092 | wbi__decommitMemory(local.start, size); 1093 | wbi__commitMemory(local.start, size, local.info.commitFlags); 1094 | *arena = local; 1095 | } 1096 | 1097 | WB_ALLOC_API 1098 | void wb_arenaDestroy(wb_MemoryArena* arena) 1099 | { 1100 | wbi__freeAddressSpace(arena->start, 1101 | (wb_isize)arena->end - (wb_isize)arena->start); 1102 | } 1103 | 1104 | /* Memory Pool */ 1105 | WB_ALLOC_API 1106 | void wb_poolInit(wb_MemoryPool* pool, wb_MemoryArena* alloc, 1107 | wb_usize elementSize, 1108 | wb_iflags flags) 1109 | { 1110 | #ifndef WB_ALLOC_NO_ZERO_ON_INIT 1111 | WB_ALLOC_MEMSET(pool, 0, sizeof(wb_MemoryPool)); 1112 | #endif 1113 | 1114 | pool->alloc = alloc; 1115 | pool->flags = flags; 1116 | pool->name = "pool"; 1117 | pool->elementSize = elementSize < sizeof(void*) ? 1118 | sizeof(void*) : 1119 | elementSize; 1120 | pool->count = 0; 1121 | pool->lastFilled = -1; 1122 | pool->capacity = (wb_isize) 1123 | ((char*)alloc->end - (char*)alloc->head) / elementSize; 1124 | 1125 | pool->slots = alloc->head; 1126 | pool->freeList = NULL; 1127 | } 1128 | 1129 | WB_ALLOC_API 1130 | wb_MemoryPool* wb_poolBootstrap(wb_MemoryInfo info, 1131 | wb_isize elementSize, 1132 | wb_iflags flags) 1133 | { 1134 | wb_MemoryArena* alloc; 1135 | wb_MemoryPool* pool; 1136 | 1137 | wb_iflags arenaFlags = 0; 1138 | if(flags & wb_Pool_FixedSize) { 1139 | arenaFlags = wb_Arena_FixedSize; 1140 | } 1141 | 1142 | alloc = wb_arenaBootstrap(info, arenaFlags); 1143 | pool = (wb_MemoryPool*)wb_arenaPush(alloc, sizeof(wb_MemoryPool)); 1144 | 1145 | wb_poolInit(pool, alloc, elementSize, flags); 1146 | return pool; 1147 | } 1148 | 1149 | WB_ALLOC_API 1150 | wb_MemoryPool* wb_poolFixedSizeBootstrap( 1151 | wb_isize elementSize, 1152 | void* buffer, wb_usize size, 1153 | wb_iflags flags) 1154 | { 1155 | wb_MemoryArena* alloc; 1156 | wb_MemoryPool* pool; 1157 | flags |= wb_Pool_FixedSize; 1158 | 1159 | alloc = arenaFixedSizeBootstrap(buffer, size, wb_Arena_FixedSize); 1160 | pool = (wb_MemoryPool*)wb_arenaPush(alloc, sizeof(wb_MemoryPool)); 1161 | 1162 | wb_poolInit(pool, alloc, elementSize, flags); 1163 | return pool; 1164 | } 1165 | 1166 | /* Utility functions not used 1167 | wb_isize poolIndex(wb_MemoryPool* pool, void* ptr) 1168 | { 1169 | wb_isize diff = (wb_isize)ptr - (wb_isize)pool->slots; 1170 | return diff / pool->elementSize; 1171 | } 1172 | void* poolFromIndex(wb_MemoryPool* pool, wb_isize index) 1173 | { 1174 | return (char*)pool->slots + index * pool->elementSize; 1175 | } 1176 | */ 1177 | 1178 | WB_ALLOC_API 1179 | void* wb_poolRetrieve(wb_MemoryPool* pool) 1180 | { 1181 | void *ptr, *ret; 1182 | ptr = NULL; 1183 | if((!(pool->flags & wb_Pool_Compacting)) && pool->freeList) { 1184 | ptr = pool->freeList; 1185 | pool->freeList = (void**)*pool->freeList; 1186 | pool->count++; 1187 | 1188 | if(!(pool->flags & wb_Pool_NoZeroMemory)) { 1189 | WB_ALLOC_MEMSET(ptr, 0, pool->elementSize); 1190 | } 1191 | 1192 | return ptr; 1193 | } 1194 | 1195 | if(pool->lastFilled >= pool->capacity - 1) { 1196 | if(pool->flags & wb_Pool_FixedSize) { 1197 | WB_ALLOC_ERROR_HANDLER("pool ran out of memory", 1198 | pool, pool->name); 1199 | return NULL; 1200 | } 1201 | 1202 | ret = wb_arenaPush(pool->alloc, pool->alloc->info.commitSize); 1203 | if(!ret) { 1204 | WB_ALLOC_ERROR_HANDLER("arenaPush failed in poolRetrieve", 1205 | pool, pool->name); 1206 | return NULL; 1207 | } 1208 | pool->capacity = (wb_isize) 1209 | ((char*)pool->alloc->end - (char*)pool->slots) / pool->elementSize; 1210 | } 1211 | 1212 | ptr = (char*)pool->slots + ++pool->lastFilled * pool->elementSize; 1213 | pool->count++; 1214 | if(!(pool->flags & wb_Pool_NoZeroMemory)) { 1215 | WB_ALLOC_MEMSET(ptr, 0, pool->elementSize); 1216 | } 1217 | return ptr; 1218 | } 1219 | 1220 | WB_ALLOC_API 1221 | void wb_poolRelease(wb_MemoryPool* pool, void* ptr) 1222 | { 1223 | pool->count--; 1224 | 1225 | if(pool->freeList && !(pool->flags & wb_Pool_NoDoubleFreeCheck)) { 1226 | void** localList = pool->freeList; 1227 | do { 1228 | if(ptr == localList) { 1229 | WB_ALLOC_ERROR_HANDLER("caught attempting to free previously " 1230 | "freed memory in poolRelease", 1231 | pool, pool->name); 1232 | return; 1233 | } 1234 | } while((localList = (void**)*localList)); 1235 | } 1236 | 1237 | if(pool->flags & wb_Pool_Compacting) { 1238 | WB_ALLOC_MEMCPY(ptr, 1239 | (char*)pool->slots + pool->count * pool->elementSize, 1240 | pool->elementSize); 1241 | return; 1242 | } 1243 | 1244 | *(void**)ptr = pool->freeList; 1245 | pool->freeList = (void**)ptr; 1246 | } 1247 | 1248 | /* 1249 | * TODO(will): Maybe, someday, have a tagged heap that uses real memoryArenas 1250 | * behind the scenes, so that you get to benefit from stack and extended 1251 | * mode for ~free; maybe as a preprocessor flag? 1252 | */ 1253 | WB_ALLOC_API 1254 | wb_isize wb_calcTaggedHeapSize(wb_isize arenaSize, wb_isize arenaCount, 1255 | wb_iflags bootstrapped) 1256 | { 1257 | return arenaCount * (arenaSize + sizeof(wbi__TaggedHeapArena)) 1258 | + sizeof(wb_TaggedHeap) * bootstrapped; 1259 | } 1260 | 1261 | WB_ALLOC_API 1262 | void wb_taggedInit(wb_TaggedHeap* heap, wb_MemoryArena* arena, 1263 | wb_isize internalArenaSize, wb_iflags flags) 1264 | { 1265 | #ifndef WB_ALLOC_NO_ZERO_ON_INIT 1266 | WB_ALLOC_MEMSET(heap, 0, sizeof(wb_TaggedHeap)); 1267 | #endif 1268 | 1269 | heap->name = "taggedHeap"; 1270 | heap->flags = flags; 1271 | heap->align = 8; 1272 | heap->arenaSize = internalArenaSize; 1273 | wb_poolInit(&heap->pool, arena, 1274 | internalArenaSize + sizeof(wbi__TaggedHeapArena), 1275 | wb_Pool_Normal | wb_Pool_NoDoubleFreeCheck | 1276 | ((flags & wb_TaggedHeap_NoZeroMemory) ? 1277 | wb_Pool_NoZeroMemory : 1278 | 0)); 1279 | } 1280 | 1281 | WB_ALLOC_API 1282 | wb_TaggedHeap* wb_taggedBootstrap(wb_MemoryInfo info, 1283 | wb_isize arenaSize, 1284 | wb_iflags flags) 1285 | { 1286 | wb_TaggedHeap* strapped; 1287 | wb_TaggedHeap heap; 1288 | wb_MemoryArena* arena; 1289 | info.commitSize = wb_calcTaggedHeapSize(arenaSize, 8, 1); 1290 | arena = wb_arenaBootstrap(info, 1291 | ((flags & wb_TaggedHeap_NoZeroMemory) ? 1292 | wb_Arena_NoZeroMemory : 1293 | wb_Arena_Normal)); 1294 | strapped = (wb_TaggedHeap*)wb_arenaPush(arena, sizeof(wb_TaggedHeap) + 16); 1295 | wb_taggedInit(&heap, arena, arenaSize, flags); 1296 | *strapped = heap; 1297 | return strapped; 1298 | } 1299 | 1300 | WB_ALLOC_API 1301 | wb_TaggedHeap* wb_taggedFixedSizeBootstrap( 1302 | wb_isize arenaSize, 1303 | void* buffer, wb_isize bufferSize, 1304 | wb_iflags flags) 1305 | { 1306 | wb_MemoryArena* alloc; 1307 | wb_TaggedHeap* heap; 1308 | flags |= wb_TaggedHeap_FixedSize; 1309 | 1310 | alloc = arenaFixedSizeBootstrap(buffer, bufferSize, 1311 | wb_Arena_FixedSize | 1312 | ((flags & wb_TaggedHeap_NoZeroMemory) ? 1313 | wb_Arena_NoZeroMemory : 0)); 1314 | heap = (wb_TaggedHeap*)wb_arenaPush(alloc, sizeof(wb_TaggedHeap)); 1315 | 1316 | wb_taggedInit(heap, alloc, arenaSize, flags); 1317 | return heap; 1318 | 1319 | } 1320 | 1321 | WB_ALLOC_API 1322 | void wbi__taggedArenaInit(wb_TaggedHeap* heap, 1323 | wbi__TaggedHeapArena* arena, 1324 | wb_isize tag) 1325 | { 1326 | #ifndef WB_ALLOC_NO_ZERO_ON_INIT 1327 | WB_ALLOC_MEMSET(arena, 0, sizeof(wbi__TaggedHeapArena)); 1328 | #endif 1329 | 1330 | arena->tag = tag; 1331 | arena->head = &arena->buffer; 1332 | arena->end = (void*)((char*)arena->head + heap->arenaSize); 1333 | } 1334 | 1335 | WB_ALLOC_API 1336 | void wbi__taggedArenaSortBySize(wbi__TaggedHeapArena** array, wb_isize count) 1337 | { 1338 | #define wbi__arenaSize(arena) ((wb_isize)arena->head - (wb_isize)arena->head) 1339 | wb_isize i, j, minSize; 1340 | for(i = 1; i < count; ++i) { 1341 | j = i - 1; 1342 | 1343 | minSize = wbi__arenaSize(array[i]); 1344 | if(wbi__arenaSize(array[j]) > minSize) { 1345 | wbi__TaggedHeapArena* temp = array[i]; 1346 | while((j >= 0) && (wbi__arenaSize(array[j]) > minSize)) { 1347 | array[j + 1] = array[j]; 1348 | j--; 1349 | } 1350 | array[j + 1] = temp; 1351 | } 1352 | } 1353 | #undef wbi__arenaSize 1354 | } 1355 | 1356 | 1357 | WB_ALLOC_API 1358 | void* wb_taggedAlloc(wb_TaggedHeap* heap, wb_isize tag, wb_usize size) 1359 | { 1360 | wbi__TaggedHeapArena *arena, *newArena; 1361 | void* oldHead; 1362 | wbi__TaggedHeapArena* canFit[wbi__TaggedHeapSearchSize]; 1363 | wb_isize canFitCount = 0; 1364 | 1365 | if(size > heap->arenaSize) { 1366 | WB_ALLOC_ERROR_HANDLER("cannot allocate an object larger than the " 1367 | "size of a tagged heap arena.", 1368 | heap, heap->name); 1369 | return NULL; 1370 | } 1371 | 1372 | if(!heap->arenas[tag]) { 1373 | heap->arenas[tag] = (wbi__TaggedHeapArena*)wb_poolRetrieve(&heap->pool); 1374 | if(!heap->arenas[tag]) { 1375 | WB_ALLOC_ERROR_HANDLER("tagged heap arena retrieve returned null " 1376 | "when creating a new tag", 1377 | heap, heap->name); 1378 | return NULL; 1379 | } 1380 | wbi__taggedArenaInit(heap, heap->arenas[tag], tag); 1381 | } 1382 | 1383 | arena = heap->arenas[tag]; 1384 | 1385 | if((char*)arena->head + size > (char*)arena->end) { 1386 | /* TODO(will) add a find-better-fit option rather than 1387 | * allocating new arenas whenever */ 1388 | 1389 | if(heap->flags & wb_TaggedHeap_SearchForBestFit) { 1390 | while((arena = arena->next)) { 1391 | if((char*)arena->head + size < (char*)arena->end) { 1392 | canFit[canFitCount++] = arena; 1393 | if(canFitCount > (wbi__TaggedHeapSearchSize - 1)) { 1394 | break; 1395 | } 1396 | } 1397 | } 1398 | 1399 | if(canFitCount > 0) { 1400 | wbi__taggedArenaSortBySize(canFit, canFitCount); 1401 | arena = canFit[0]; 1402 | } 1403 | } 1404 | 1405 | if(canFitCount == 0) { 1406 | newArena = (wbi__TaggedHeapArena*)wb_poolRetrieve(&heap->pool); 1407 | if(!newArena) { 1408 | WB_ALLOC_ERROR_HANDLER( 1409 | "tagged heap arena retrieve returned null", 1410 | heap, heap->name); 1411 | return NULL; 1412 | } 1413 | wbi__taggedArenaInit(heap, newArena, tag); 1414 | newArena->next = arena; 1415 | arena = newArena; 1416 | } 1417 | } 1418 | 1419 | oldHead = arena->head; 1420 | arena->head = (void*)wb_alignTo((wb_isize)arena->head + size, heap->align); 1421 | return oldHead; 1422 | } 1423 | 1424 | WB_ALLOC_API 1425 | void wb_taggedFree(wb_TaggedHeap* heap, wb_isize tag) 1426 | { 1427 | wbi__TaggedHeapArena *head; 1428 | if((head = heap->arenas[tag])) do { 1429 | wb_poolRelease(&heap->pool, head); 1430 | } while((head = head->next)); 1431 | heap->arenas[tag] = NULL; 1432 | } 1433 | 1434 | #ifdef WB_ALLOC_CPLUSPLUS_FEATURES 1435 | template 1436 | WB_ALLOC_API 1437 | T* wb_arenaPushEx(wb_MemoryArena* arena, 1438 | WB_ALLOC_EXTENDED_INFO extended, 1439 | int n) 1440 | { 1441 | return reinterpret_cast(wb_arenaPushEx(arena, sizeof(T) * n, extended)); 1442 | } 1443 | 1444 | template 1445 | WB_ALLOC_API 1446 | T* wb_arenaPush(wb_MemoryArena* arena, int n) 1447 | { 1448 | return reinterpret_cast(wb_arenaPush(arena, sizeof(T) * n)); 1449 | } 1450 | 1451 | template 1452 | WB_ALLOC_API 1453 | T* wb_poolRetrieve(wb_MemoryPool* pool) 1454 | { 1455 | return reinterpret_cast(wb_poolRetrieve(pool)); 1456 | } 1457 | 1458 | template 1459 | WB_ALLOC_API 1460 | void wb_poolRelease(wb_MemoryPool* pool, T* ptr) 1461 | { 1462 | wb_poolRelease(pool, reinterpret_cast(ptr)); 1463 | } 1464 | 1465 | template 1466 | WB_ALLOC_API 1467 | void wb_poolInit( 1468 | wb_MemoryPool* pool, 1469 | wb_MemoryArena* alloc, 1470 | wb_iflags flags) 1471 | { 1472 | wb_poolInit(pool, alloc, sizeof(T), flags); 1473 | } 1474 | 1475 | template 1476 | WB_ALLOC_API 1477 | wb_MemoryPool* wb_poolBootstrap(wb_MemoryInfo info, 1478 | wb_iflags flags) 1479 | { 1480 | return wb_poolBootstrap(info, sizeof(T), flags); 1481 | } 1482 | 1483 | template 1484 | WB_ALLOC_API 1485 | wb_MemoryPool* wb_poolFixedSizeBootstrap( 1486 | void* buffer, wb_usize size, 1487 | wb_iflags flags) 1488 | { 1489 | return wb_poolFixedSizeBootstrap(sizeof(T), buffer, size, flags); 1490 | } 1491 | 1492 | template 1493 | WB_ALLOC_API 1494 | T* wb_taggedAlloc(wb_TaggedHeap* heap, wb_isize tag, int n) 1495 | { 1496 | return reinterpret_cast(wb_taggedAlloc(heap, tag, sizeof(T) * n)); 1497 | } 1498 | #endif 1499 | #endif 1500 | 1501 | #ifndef WB_ALLOC_NO_DISABLE_STUPID_MSVC_WARNINGS 1502 | #ifdef _MSC_VER 1503 | #pragma warning(pop) 1504 | #endif 1505 | #endif 1506 | 1507 | /* 1508 | Anyone is free to copy, modify, publish, use, compile, sell, or 1509 | distribute this software, either in source code form or as a compiled 1510 | binary, for any purpose, commercial or non-commercial, and by any 1511 | means. 1512 | 1513 | In jurisdictions that recognize copyright laws, the author or authors 1514 | of this software dedicate any and all copyright interest in the 1515 | software to the public domain. We make this dedication for the benefit 1516 | of the public at large and to the detriment of our heirs and 1517 | successors. We intend this dedication to be an overt act of 1518 | relinquishment in perpetuity of all present and future rights to this 1519 | software under copyright law. 1520 | 1521 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1522 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1523 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1524 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 1525 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1526 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 1527 | OTHER DEALINGS IN THE SOFTWARE. 1528 | 1529 | For more information, please refer to 1530 | */ 1531 | -------------------------------------------------------------------------------- /wb_alloc_test.c: -------------------------------------------------------------------------------- 1 | /* This is provided as a short demo on how each allocator in 2 | * wb_alloc works and how to do a simple initialization for them 3 | */ 4 | 5 | /* This is free and unencumbered software released into the public domain. */ 6 | #include 7 | 8 | #define WB_ALLOC_IMPLEMENTATION 9 | #include "wb_alloc.h" 10 | 11 | #ifdef _MSC_VER 12 | /* Disable unused variable warnings on MSVC */ 13 | #pragma warning(disable:189) 14 | #endif 15 | int main() 16 | { 17 | int i; 18 | wb_MemoryInfo info; 19 | wb_MemoryArena* arena; 20 | wb_MemoryPool* pool; 21 | wb_TaggedHeap* heap; 22 | int *numbers1, *numbers2, *numbers3, *numbers4; 23 | wb_usize* soManyNumbers[100]; 24 | wb_usize* memoryView; 25 | wb_usize* aBlock, *bBlock, *cBlock; 26 | 27 | printf("wb_alloc: C test\n"); 28 | /* MemoryInfo contains the information about the system's 29 | total memory, page size, and sets some defaults about the 30 | amount of memory committed at once */ 31 | info = wb_getMemoryInfo(); 32 | printf(" Physical Memory: %zukb\n" 33 | " Page Size......: %zukb\n" 34 | " Commit Size____: %zukb\n\n", 35 | info.totalMemory / wb_CalcKilobytes(1), 36 | info.pageSize / wb_CalcKilobytes(1), 37 | info.commitSize / wb_CalcKilobytes(1)); 38 | 39 | printf("Memory Arena test\n"); 40 | /* Bootstrapping the arena means that it allocates the memory 41 | for itself, then stores its own struct at the beginning */ 42 | arena = wb_arenaBootstrap(info, wb_FlagArenaNormal); 43 | 44 | /* Make some room for numbers! */ 45 | numbers1 = wb_arenaPush(arena, sizeof(int) * 10); 46 | numbers2 = wb_arenaPush(arena, sizeof(int) * 20); 47 | numbers3 = wb_arenaPush(arena, sizeof(int) * 40); 48 | numbers4 = wb_arenaPush(arena, sizeof(int) * 80); 49 | 50 | /* Warnings are dumb */ 51 | numbers2[0] = 0; 52 | numbers3[0] = 0; 53 | numbers4[0] = 0; 54 | 55 | for(i = 0; i < 150; ++i) { 56 | numbers1[i] = 150 - i; 57 | } 58 | 59 | for(i = 0; i < 150; ++i) { 60 | printf("%d ", numbers1[i]); 61 | } 62 | printf("\n\n"); 63 | 64 | /* Clearing the arena decommits all its committed pages then 65 | recommits them, which is guaranteed on modern operating 66 | systems to zero them */ 67 | wb_arenaClear(arena); 68 | wb_arenaPush(arena, sizeof(int) * 150); 69 | 70 | for(i = 0; i < 150; ++i) { 71 | printf("%d ", numbers1[i]); 72 | } 73 | printf("\n\n"); 74 | 75 | wb_arenaDestroy(arena); 76 | 77 | printf("Memory Pool test\n"); 78 | printf("(can you see the free list?)\n"); 79 | /* Internally, the pool creates a memory arena and does the same 80 | * trick the arena does. */ 81 | pool = wb_poolBootstrap(info, 8, wb_FlagPoolNormal); 82 | 83 | /* Get pointers to numbers out of the pool, give them some funky values */ 84 | /* wb_usize* soManyNumbers[100]; */ 85 | for(i = 0; i < 100; ++i) { 86 | soManyNumbers[i] = wb_poolRetrieve(pool); 87 | *soManyNumbers[i] = 4096 - (i + 1) * 4; 88 | } 89 | 90 | /* Return half of them to the pool */ 91 | for(i = 0; i < 50; ++i) { 92 | wb_poolRelease(pool, soManyNumbers[i * 2]); 93 | } 94 | 95 | /* Print them all out; can you see where the pool has written its 96 | * free list pointers? */ 97 | for(i = 0; i < 100; ++i) { 98 | printf("%zx ", *soManyNumbers[i]); 99 | } 100 | printf("\n\n"); 101 | 102 | /* Notice that they're retrieved in reverse order */ 103 | for(i = 0; i < 50; ++i) { 104 | int* returned = wb_poolRetrieve(pool); 105 | *returned = i; 106 | } 107 | 108 | for(i = 0; i < 100; ++i) { 109 | printf("%zx ", *soManyNumbers[i]); 110 | } 111 | printf("\n\n"); 112 | 113 | /* Because everything is stored on the internal memory arena, 114 | * destroying that cleans up the entire pool */ 115 | wb_arenaDestroy(pool->alloc); 116 | 117 | printf("Tagged Heap test\n"); 118 | 119 | #define Tag_A 0 120 | #define Tag_B 1 121 | #define Tag_C 2 122 | 123 | /* This behaves very similarly to the other bootstrap functions */ 124 | heap = wb_taggedBootstrap( 125 | info, 126 | sizeof(wb_usize) * 65, 127 | /* normally the arena size would be 128 | something like wb_CalcMegabytes(2) */ 129 | wb_FlagTaggedHeapNormal); 130 | 131 | /* This'll allow us to neatly print out the memory layout */ 132 | memoryView = heap->pool.slots; 133 | 134 | 135 | /* Allocate some tagged blocks... */ 136 | aBlock = wb_taggedAlloc(heap, Tag_A, sizeof(wb_usize) * 64); 137 | bBlock = wb_taggedAlloc(heap, Tag_B, sizeof(wb_usize) * 64); 138 | cBlock = wb_taggedAlloc(heap, Tag_C, sizeof(wb_usize) * 64); 139 | 140 | /* ...and put recognizable patterns in them */ 141 | for(i = 0; i < 64; ++i) { 142 | aBlock[i] = i; 143 | bBlock[i] = 64 - i; 144 | cBlock[i] = 64 + i; 145 | } 146 | 147 | /* You get to see the memory layout of the TaggedHeapArena structs too 148 | * struct TaggedHeapArnea { 149 | * wb_isize tag; 150 | * TaggedHeapArena* next; 151 | * void *head, *end; 152 | * char buffer; 153 | * } 154 | * Something to note: buffer gets padded out to 8 bytes, so this struct 155 | * probably isn't safe to write to disk if you're building on multiple 156 | * compilers or achitectures, but hey, what's a few bytes to a good 157 | * processor-friend? 158 | * */ 159 | for(i = 0; i < 64*5; ++i) { 160 | printf("%zu ", memoryView[i]); 161 | } 162 | printf("\n\n"); 163 | 164 | wb_taggedFree(heap, Tag_B); 165 | 166 | /* This ensures B is cleared */ 167 | bBlock = wb_taggedAlloc(heap, Tag_B, sizeof(wb_usize) * 64); 168 | 169 | /* And now we can see that its contents have been zeroed */ 170 | for(i = 0; i < 64*5; ++i) { 171 | printf("%zu ", memoryView[i]); 172 | } 173 | printf("\n\n"); 174 | 175 | /* The heap stores everything on its pool's internal arena, so, like 176 | * before, destroying that wipes the whole thing. */ 177 | wb_arenaDestroy(heap->pool.alloc); 178 | 179 | return 0; 180 | } 181 | 182 | -------------------------------------------------------------------------------- /wb_alloc_test_cpp.cpp: -------------------------------------------------------------------------------- 1 | /* This is provided as a short demo of how the C++ features look when 2 | * used in the same tests as the C version. Comments have been removed, 3 | * read the C version first. 4 | */ 5 | 6 | /* This is free and unencumbered software released into the public domain. */ 7 | 8 | #include 9 | 10 | #define WB_ALLOC_IMPLEMENTATION 11 | #define WB_ALLOC_CPLUSPLUS_FEATURES 12 | #include "wb_alloc.h" 13 | 14 | #ifdef _MSC_VER 15 | #pragma warning(disable:189) 16 | #endif 17 | int main() 18 | { 19 | int i; 20 | 21 | printf("wb_alloc: C++ test\n"); 22 | wb_MemoryInfo info = wb_getMemoryInfo(); 23 | printf(" Physical Memory: %zukb\n" 24 | " Page Size......: %zukb\n" 25 | " Commit Size____: %zukb\n\n", 26 | info.totalMemory / wb_CalcKilobytes(1), 27 | info.pageSize / wb_CalcKilobytes(1), 28 | info.commitSize / wb_CalcKilobytes(1)); 29 | 30 | printf("Memory Arena test\n"); 31 | wb_MemoryArena* arena = wb_arenaBootstrap(info, wb_FlagArenaNormal); 32 | 33 | int* numbers1 = wb_arenaPush(arena); 34 | int* numbers2 = wb_arenaPush(arena); 35 | int* numbers3 = wb_arenaPush(arena); 36 | int* numbers4 = wb_arenaPush(arena); 37 | 38 | for(i = 0; i < 150; ++i) { 39 | numbers1[i] = 150 - i; 40 | } 41 | 42 | for(i = 0; i < 150; ++i) { 43 | printf("%d ", numbers1[i]); 44 | } 45 | printf("\n\n"); 46 | 47 | wb_arenaClear(arena); 48 | wb_arenaPush(arena, sizeof(int) * 150); 49 | 50 | for(i = 0; i < 150; ++i) { 51 | printf("%d ", numbers1[i]); 52 | } 53 | printf("\n\n"); 54 | 55 | wb_arenaDestroy(arena); 56 | 57 | printf("Memory Pool test\n"); 58 | printf("(can you see the free list?)\n"); 59 | wb_MemoryPool* pool = wb_poolBootstrap(info, wb_FlagPoolNormal); 60 | 61 | wb_usize* soManyNumbers[100]; 62 | for(i = 0; i < 100; ++i) { 63 | soManyNumbers[i] = wb_poolRetrieve(pool); 64 | *soManyNumbers[i] = 4096 - (i + 1) * 4; 65 | } 66 | 67 | for(i = 0; i < 50; ++i) { 68 | wb_poolRelease(pool, soManyNumbers[i * 2]); 69 | } 70 | 71 | for(i = 0; i < 100; ++i) { 72 | printf("%zx ", *soManyNumbers[i]); 73 | } 74 | printf("\n\n"); 75 | 76 | for(i = 0; i < 50; ++i) { 77 | wb_usize* returned = wb_poolRetrieve(pool); 78 | *returned = i; 79 | } 80 | 81 | for(i = 0; i < 100; ++i) { 82 | printf("%zx ", *soManyNumbers[i]); 83 | } 84 | printf("\n\n"); 85 | 86 | wb_arenaDestroy(pool->alloc); 87 | 88 | printf("Tagged Heap test\n"); 89 | 90 | #define Tag_A 0 91 | #define Tag_B 1 92 | #define Tag_C 2 93 | 94 | wb_TaggedHeap* heap = wb_taggedBootstrap( 95 | info, 96 | sizeof(wb_usize) * 65, 97 | /* normally the arena size would be 98 | something like wb_CalcMegabytes(2) */ 99 | wb_FlagTaggedHeapNormal); 100 | wb_usize* memoryView = (wb_usize*)heap->pool.slots; 101 | 102 | wb_usize* aBlock = wb_taggedAlloc(heap, Tag_A); 103 | wb_usize* bBlock = wb_taggedAlloc(heap, Tag_B); 104 | wb_usize* cBlock = wb_taggedAlloc(heap, Tag_C); 105 | 106 | for(i = 0; i < 64; ++i) { 107 | aBlock[i] = i; 108 | bBlock[i] = 64 - i; 109 | cBlock[i] = 64 + i; 110 | } 111 | 112 | for(i = 0; i < 64*5; ++i) { 113 | printf("%zu ", memoryView[i]); 114 | } 115 | printf("\n\n"); 116 | 117 | wb_taggedFree(heap, Tag_B); 118 | 119 | /* This ensures B is cleared */ 120 | bBlock = wb_taggedAlloc(heap, Tag_B); 121 | 122 | for(i = 0; i < 64*5; ++i) { 123 | printf("%zu ", memoryView[i]); 124 | } 125 | printf("\n\n"); 126 | 127 | wb_arenaDestroy(heap->pool.alloc); 128 | 129 | return 0; 130 | } 131 | 132 | 133 | -------------------------------------------------------------------------------- /wb_alloc_test_nocrt.c: -------------------------------------------------------------------------------- 1 | /* This is free and unencumbered software released into the public domain. */ 2 | 3 | /* Who needs that crufty old C runtime anyway? */ 4 | /* Note: I don't know how to kick the CRT habit with gcc/clang, so this 5 | * is really only designed to be run with MSVC and in a debugger so far. */ 6 | 7 | /* hashtag-professional-programming */ 8 | #define NULL ((void*)0) 9 | 10 | /* So that we see some output; some is better than none, but this 11 | * won't print out any of the numbers */ 12 | void OutputDebugStringA(char* lpOutputString); 13 | #define printf(x, ...) OutputDebugStringA(x) 14 | 15 | /* The default error handler uses fprintf to stderr, neither of which 16 | * we have right now */ 17 | #define WB_ALLOC_ERROR_HANDLER 18 | 19 | /* Who came up with size_t anyway? */ 20 | #define WB_ALLOC_CUSTOM_INTEGER_TYPES 21 | typedef unsigned __int64 wb_usize; 22 | typedef __int64 wb_isize; 23 | typedef wb_isize wb_flags; 24 | 25 | /* These things are kinda important */ 26 | void naiveMemset(void* dest_in, char v, wb_usize size) 27 | { 28 | char* dest = dest_in; 29 | while(size--) { 30 | *dest = v; 31 | dest++; 32 | } 33 | } 34 | 35 | #pragma warning(disable:706) 36 | void naiveMemcpy(char* dest_in, char* src_in, wb_usize size) 37 | { 38 | char* dest = dest_in, *src = src_in; 39 | while(size-- && (*dest++ = *src++)); 40 | } 41 | 42 | #define WB_ALLOC_MEMSET(x, y, z) naiveMemset(x, y, z) 43 | #define WB_ALLOC_MEMCPY(x, y, z) naiveMemcpy(x, y, z) 44 | #define WB_ALLOC_IMPLEMENTATION 45 | #include "wb_alloc.h" 46 | 47 | /* Disable unused variable warnings on MSVC */ 48 | #pragma warning(disable:189) 49 | int main() 50 | { 51 | int i; 52 | 53 | printf("wb_alloc: C test\n"); 54 | /* MemoryInfo contains the information about the system's 55 | total memory, page size, and sets some defaults about the 56 | amount of memory committed at once */ 57 | wb_MemoryInfo info = wb_getMemoryInfo(); 58 | printf(" Physical Memory: %zukb\n" 59 | " Page Size......: %zukb\n" 60 | " Commit Size____: %zukb\n\n", 61 | info.totalMemory / wb_CalcKilobytes(1), 62 | info.pageSize / wb_CalcKilobytes(1), 63 | info.commitSize / wb_CalcKilobytes(1)); 64 | 65 | printf("Memory Arena test\n"); 66 | /* Bootstrapping the arena means that it allocates the memory 67 | for itself, then stores its own struct at the beginning */ 68 | wb_MemoryArena* arena = wb_arenaBootstrap(info, wb_FlagArenaNormal); 69 | 70 | /* Make some room for numbers! */ 71 | int* numbers1 = wb_arenaPush(arena, sizeof(int) * 10); 72 | int* numbers2 = wb_arenaPush(arena, sizeof(int) * 20); 73 | int* numbers3 = wb_arenaPush(arena, sizeof(int) * 40); 74 | int* numbers4 = wb_arenaPush(arena, sizeof(int) * 80); 75 | 76 | for(i = 0; i < 150; ++i) { 77 | numbers1[i] = 150 - i; 78 | } 79 | 80 | for(i = 0; i < 150; ++i) { 81 | printf("%d ", numbers1[i]); 82 | } 83 | printf("\n\n"); 84 | 85 | /* Clearing the arena decommits all its committed pages then 86 | recommits them, which is guaranteed on modern operating 87 | systems to zero them */ 88 | wb_arenaClear(arena); 89 | wb_arenaPush(arena, sizeof(int) * 150); 90 | 91 | for(i = 0; i < 150; ++i) { 92 | printf("%d ", numbers1[i]); 93 | } 94 | printf("\n\n"); 95 | 96 | wb_arenaDestroy(arena); 97 | 98 | printf("Memory Pool test\n"); 99 | printf("(can you see the free list?)\n"); 100 | /* Internally, the pool creates a memory arena and does the same 101 | * trick the arena does. */ 102 | wb_MemoryPool* pool = wb_poolBootstrap(info, 8, wb_FlagPoolNormal); 103 | 104 | /* Get pointers to numbers out of the pool, give them some funky values */ 105 | wb_usize* soManyNumbers[100]; 106 | for(i = 0; i < 100; ++i) { 107 | soManyNumbers[i] = wb_poolRetrieve(pool); 108 | *soManyNumbers[i] = 4096 - (i + 1) * 4; 109 | } 110 | 111 | /* Return half of them to the pool */ 112 | for(i = 0; i < 50; ++i) { 113 | wb_poolRelease(pool, soManyNumbers[i * 2]); 114 | } 115 | 116 | /* Print them all out; can you see where the pool has written its 117 | * free list pointers? */ 118 | for(i = 0; i < 100; ++i) { 119 | printf("%zx ", *soManyNumbers[i]); 120 | } 121 | printf("\n\n"); 122 | 123 | /* Notice that they're retrieved in reverse order */ 124 | for(i = 0; i < 50; ++i) { 125 | int* returned = wb_poolRetrieve(pool); 126 | *returned = i; 127 | } 128 | 129 | for(i = 0; i < 100; ++i) { 130 | printf("%zx ", *soManyNumbers[i]); 131 | } 132 | printf("\n\n"); 133 | 134 | /* Because everything is stored on the internal memory arena, 135 | * destroying that cleans up the entire pool */ 136 | wb_arenaDestroy(pool->alloc); 137 | 138 | printf("Tagged Heap test\n"); 139 | 140 | #define Tag_A 0 141 | #define Tag_B 1 142 | #define Tag_C 2 143 | 144 | /* This behaves very similarly to the other bootstrap functions */ 145 | wb_TaggedHeap* heap = wb_taggedBootstrap( 146 | info, 147 | sizeof(wb_usize) * 65, 148 | /* normally the arena size would be 149 | something like wb_CalcMegabytes(2) */ 150 | wb_FlagTaggedHeapNormal); 151 | 152 | /* This'll allow us to neatly print out the memory layout */ 153 | wb_usize* memoryView = heap->pool.slots; 154 | 155 | 156 | /* Allocate some tagged blocks... */ 157 | wb_usize* aBlock = wb_taggedAlloc(heap, Tag_A, sizeof(wb_usize) * 64); 158 | wb_usize* bBlock = wb_taggedAlloc(heap, Tag_B, sizeof(wb_usize) * 64); 159 | wb_usize* cBlock = wb_taggedAlloc(heap, Tag_C, sizeof(wb_usize) * 64); 160 | 161 | /* ...and put recognizable patterns in them */ 162 | for(i = 0; i < 64; ++i) { 163 | aBlock[i] = i; 164 | bBlock[i] = 64 - i; 165 | cBlock[i] = 64 + i; 166 | } 167 | 168 | /* You get to see the memory layout of the TaggedHeapArena structs too 169 | * struct TaggedHeapArnea { 170 | * wb_isize tag; 171 | * TaggedHeapArena* next; 172 | * void *head, *end; 173 | * char buffer; 174 | * } 175 | * Something to note: buffer gets padded out to 8 bytes, so this struct 176 | * probably isn't safe to write to disk if you're building on multiple 177 | * compilers or achitectures, but hey, what's a few bytes to a good 178 | * processor-friend? 179 | * */ 180 | for(i = 0; i < 64*5; ++i) { 181 | printf("%zu ", memoryView[i]); 182 | } 183 | printf("\n\n"); 184 | 185 | wb_taggedFree(heap, Tag_B); 186 | 187 | /* This ensures B is cleared */ 188 | bBlock = wb_taggedAlloc(heap, Tag_B, sizeof(wb_usize) * 64); 189 | 190 | /* And now we can see that its contents have been zeroed */ 191 | for(i = 0; i < 64*5; ++i) { 192 | printf("%zu ", memoryView[i]); 193 | } 194 | printf("\n\n"); 195 | 196 | /* The heap stores everything on its pool's internal arena, so, like 197 | * before, destroying that wipes the whole thing. */ 198 | wb_arenaDestroy(heap->pool.alloc); 199 | 200 | return 0; 201 | } 202 | 203 | void mainCRTStartup() 204 | { 205 | main(); 206 | //Exit(); 207 | } 208 | --------------------------------------------------------------------------------