├── .clang-format ├── .gitignore ├── .travis.yml ├── .vscode └── c_cpp_properties.json ├── README.md ├── appveyor.yml ├── examples ├── Makefile ├── build_cygwin.sh ├── external │ ├── ig_debugheap │ │ ├── DebugHeap.c │ │ ├── DebugHeap.h │ │ ├── README.md │ │ ├── demo.c │ │ └── tundra.lua │ └── minctest │ │ ├── LICENSE │ │ ├── README.md │ │ ├── example.c │ │ └── minctest.h ├── sralloc.sln └── unittest │ ├── unittest.c │ ├── unittest.vcxproj │ ├── unittest.vcxproj.filters │ └── unittest.vcxproj.user └── sralloc.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: '-2' 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: 'true' 6 | AlignConsecutiveDeclarations: 'true' 7 | AlignEscapedNewlinesLeft: 'true' 8 | AlignOperands: 'true' 9 | AlignTrailingComments: 'true' 10 | AllowAllParametersOfDeclarationOnNextLine: 'false' 11 | AllowShortBlocksOnASingleLine: 'false' 12 | AllowShortCaseLabelsOnASingleLine: 'false' 13 | AllowShortFunctionsOnASingleLine: Inline 14 | AllowShortIfStatementsOnASingleLine: 'false' 15 | AllowShortLoopsOnASingleLine: 'false' 16 | AlwaysBreakAfterReturnType: TopLevelDefinitions 17 | AlwaysBreakBeforeMultilineStrings: 'true' 18 | AlwaysBreakTemplateDeclarations: 'true' 19 | BinPackArguments: 'false' 20 | BinPackParameters: 'false' 21 | BraceWrapping: { 22 | AfterClass: 'false' 23 | AfterControlStatement: 'false' 24 | AfterEnum : 'false' 25 | AfterFunction : 'false' 26 | AfterNamespace : 'false' 27 | AfterStruct : 'false' 28 | AfterUnion : 'false' 29 | BeforeCatch : 'false' 30 | BeforeElse : 'true' 31 | IndentBraces : 'false' 32 | } 33 | BreakBeforeBraces: 'Custom' 34 | BreakBeforeTernaryOperators: 'true' 35 | BreakConstructorInitializersBeforeComma: 'true' 36 | BreakStringLiterals: 'false' 37 | ColumnLimit: '100' 38 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 39 | ConstructorInitializerIndentWidth: '0' 40 | ContinuationIndentWidth: '2' 41 | Cpp11BracedListStyle: 'false' 42 | DerivePointerAlignment: 'false' 43 | 44 | # Disabled for now, seems it's not supported in LLVM 4.0 on windows? 45 | # FixNamespaceComments: 'true' 46 | 47 | IndentCaseLabels: 'false' 48 | IndentWidth: '4' 49 | IndentWrappedFunctionNames: 'false' 50 | KeepEmptyLinesAtTheStartOfBlocks: 'true' 51 | MaxEmptyLinesToKeep: '1' 52 | NamespaceIndentation: None 53 | ObjCBlockIndentWidth: '2' 54 | ObjCSpaceAfterProperty: 'true' 55 | ObjCSpaceBeforeProtocolList: 'true' 56 | PointerAlignment: Left 57 | ReflowComments: 'true' 58 | SortIncludes: 'true' 59 | SpaceAfterCStyleCast: 'false' 60 | SpaceBeforeAssignmentOperators: 'true' 61 | SpaceBeforeParens: ControlStatements 62 | SpaceInEmptyParentheses: 'false' 63 | SpacesBeforeTrailingComments: '1' 64 | SpacesInAngles: 'false' 65 | SpacesInCStyleCastParentheses: 'false' 66 | SpacesInContainerLiterals: 'false' 67 | SpacesInParentheses: 'true' 68 | SpacesInSquareBrackets: 'false' 69 | Standard: Cpp11 70 | TabWidth: '2' 71 | UseTab: Never 72 | 73 | ... 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | examples/.vs/ 54 | examples/Build/ 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: C 2 | 3 | install: true 4 | 5 | compiler: 6 | - clang 7 | - gcc 8 | 9 | os: 10 | - linux 11 | - osx 12 | 13 | script: 14 | - cd examples 15 | - make all 16 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "/usr/include", 7 | "/usr/local/include", 8 | "${workspaceRoot}" 9 | ], 10 | "defines": [], 11 | "intelliSenseMode": "clang-x64", 12 | "browse": { 13 | "path": [ 14 | "/usr/include", 15 | "/usr/local/include", 16 | "${workspaceRoot}" 17 | ], 18 | "limitSymbolsToIncludedHeaders": true, 19 | "databaseFilename": "" 20 | }, 21 | "macFrameworkPath": [ 22 | "/System/Library/Frameworks", 23 | "/Library/Frameworks" 24 | ] 25 | }, 26 | { 27 | "name": "Linux", 28 | "includePath": [ 29 | "/usr/include", 30 | "/usr/local/include", 31 | "${workspaceRoot}" 32 | ], 33 | "defines": [], 34 | "intelliSenseMode": "clang-x64", 35 | "browse": { 36 | "path": [ 37 | "/usr/include", 38 | "/usr/local/include", 39 | "${workspaceRoot}" 40 | ], 41 | "limitSymbolsToIncludedHeaders": true, 42 | "databaseFilename": "" 43 | } 44 | }, 45 | { 46 | "name": "Win32", 47 | "includePath": [ 48 | "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.12.25827/include/*", 49 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/um", 50 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/ucrt", 51 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/shared", 52 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/winrt", 53 | "${workspaceRoot}" 54 | ], 55 | "defines": [ 56 | "_DEBUG", 57 | "UNICODE", 58 | "SRALLOC_IMPLEMENTATION", 59 | "SRALLOC_USE_NAME" 60 | ], 61 | "intelliSenseMode": "msvc-x64", 62 | "browse": { 63 | "path": [ 64 | "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.12.25827/include/*", 65 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/um", 66 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/ucrt", 67 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/shared", 68 | "C:/Program Files (x86)/Windows Kits/10/Include/10.0.16299.0/winrt", 69 | "${workspaceRoot}" 70 | ], 71 | "limitSymbolsToIncludedHeaders": true, 72 | "databaseFilename": "" 73 | } 74 | } 75 | ], 76 | "version": 3 77 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | | OSX/Linux | Windows | 3 | | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | 4 | | [![Build Status](https://travis-ci.org/Srekel/sralloc.svg?branch=master)](https://travis-ci.org/Srekel/sralloc) | [![Build Status](https://ci.appveyor.com/api/projects/status/9vxpg5jccc3pas2j?svg=true)](https://ci.appveyor.com/project/Srekel/sralloc) | 5 | **** 6 | 7 | # sralloc 8 | 9 | **sralloc** is a set of memory allocators written in **politely coded C99** (possibly with C++ wrappers in the future). They are intended to be more or less **interchangeable**, **stackable**, and **swappable**, in addition to being **performant** and **visualizable**. 10 | 11 | sralloc is mainly intended for game development but there isn't really anything game-specific in it. 12 | 13 | **sralloc is still WIP - that includes the documentation.** Also, this is very much a learning project for me, so there may very well be ways to do things smarterly! Feedback appreciated. :) 14 | 15 | ### Memory allocators? What would I use that? 16 | 17 | Using allocators generally comes with a set of pros and cons and sralloc is no different. 18 | 19 | The main things sralloc will **give** you are: 20 | 21 | - Heap allocation performance that is very near something like rpmalloc (don't be alarmed, I'm not reinventing the wheel - sralloc *wraps* rpmalloc, dlmalloc, or malloc, or any other generic heap allocator you prefer, with little overhead). 22 | - Special case performant allocators, like Frame allocator and Slot allocator. 23 | - Memory tracking in a hierarcical manner, so you can find memory leaks and easily see which parts of your application are using memory. 24 | - Debuggability, in case you're having use-after-free or buffer overrun bugs. 25 | - You can transparently switch one allocator out for another - for example for debugging or performance. 26 | 27 | As for the negatives: 28 | 29 | - As mentioned - performance overhead. Generally, an extra function call per allocation in "release" builds, but with some luck I can get rid of that. In debug builds there's more memory usage and extra calculations per allocation. 30 | - More init/teardown code. 31 | - Needs a bit of memory. In a slim (release) build, an allocator is a struct with two function pointers (so 16 bytes). In dev, ten times that is not unlikely (for keeping track of stats among other things). 32 | - May influence how you design your containers: 33 | - You can store an allocator **in** the container. 34 | - You can pass it into the container's API: `my_container.push(my_object, my_allocator);` 35 | - You can keep your containers' memory usage out of the loop, so to speak. In this case you lose the memory tracking, OR you can have an statscollector-allocator in your system that catches allocations that would otherwise "disappear". 36 | 37 | A word to the wise: allocators do well in "system" based code such as ECS architectures, and less well when each instance or object needs to manage memory, simply because of the memory overhead. A friend once calculated that `sizeof(TheGameObject)` in an engine we used was about 1kb (which is eyebrow-raising on its own) and that *10% of that was pointers to the same allocator*. This happened because an object had multiple sub-objects (graphics, physics, etc) and each of those needed a pointer to the allocator, too. 38 | 39 | ## Introduction to allocators 40 | 41 | This section serves as both a usage guide and a beginner's tutorial for people who don't know anything about memory allocators, how they can be used, and why they are common in the games industry. 42 | 43 | (By the way, much of this library is loosely inspired by the Stingray engine's take on allocators, so in addition to reading this, I also recommend checking out the Bitsquid (same thing, different name) blog and Niklas Gray's videos on Stingray. References to these and other allocator-related resources can be found at the bottom of this document.) 44 | 45 | ### The bare minimum 46 | 47 | Let's say you're just starting out and you want the simplest thing possible. For now you just want to use the **malloc allocator** as your main allocator. This is how you would do that: 48 | 49 | ```C 50 | #define SRALLOC_IMPLEMENTATION // Standard single-header-library detail 51 | #include 52 | 53 | void main() { 54 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 55 | void* ptr = sralloc_alloc( mallocalloc, 1024 ); 56 | 57 | // ...do things with ptr... 58 | 59 | sralloc_dealloc( allocator, ptr ); 60 | sralloc_destroy_malloc_allocator( mallocalloc ); 61 | } 62 | ``` 63 | 64 | The **malloc allocator** is stupid simple - it simply calls `malloc` and `free` to manage its allocations. It also collects some **statistics** and handles memory **alignment** - more on that later. 65 | 66 | Lots of ado about nothing, right? You could just call malloc and free directly and lose all the cruft. Well yes, but... 67 | 68 | ### The basics 69 | 70 | Let's say that you're writing a game, and you've just set up a basic structure: simulation + rendering. You'd like to use separate allocators for them so that you can know how much memory the different parts use, and what their allocation patterns looks like. 71 | 72 | Let's see what that might look like. 73 | 74 | ```C 75 | void main() { 76 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 77 | srallocator_t* simalloc = sralloc_create_proxy_allocator( mallocalloc, "sim" ); 78 | srallocator_t* graphicsalloc = sralloc_create_proxy_allocator( mallocalloc, "graphics" ); 79 | 80 | Simulation* sim = SRALLOC_OBJECT( simalloc, Simulation ); 81 | Graphics* graphics = SRALLOC_OBJECT( graphicsalloc, Graphics ); 82 | 83 | setup_sim(sim, simalloc); 84 | setup_graphics(graphics, graphicsalloc); 85 | 86 | bool quit = false; 87 | while (!quit) { 88 | quit = update_simulation(sim, simalloc); 89 | render_game(sim, graphics, graphicsalloc); 90 | } 91 | 92 | destroy_sim(sim, simalloc); 93 | destroy_graphics(graphics, graphicsalloc); 94 | 95 | sralloc_dealloc( allocator, sim ); 96 | sralloc_dealloc( allocator, graphics ); 97 | sralloc_destroy_proxy_allocator( simalloc ); 98 | sralloc_destroy_proxy_allocator( graphicsalloc ); 99 | sralloc_destroy_malloc_allocator( mallocalloc ); 100 | } 101 | 102 | void setup_sim(Simulation* sim, srallocator_t* allocator) { 103 | srallocator_t* sys1alloc = sralloc_create_proxy_allocator( allocator, "sys1" ); 104 | sim->system1 = create_sys1(sysalloc1); 105 | } 106 | 107 | void destroy_sim(Simulation* sim, srallocator_t* allocator) { 108 | srallocator_t* sys1alloc = sys1->allocator; 109 | destroy_sys1(sys1alloc); 110 | sralloc_destroy_proxy_allocator( sys1alloc ); 111 | } 112 | 113 | ``` 114 | 115 | It's still not very exciting, but one perk you get immediately is that if you haven't cleaned up properly - if one of the allocators hasn't freed **exactly** the same amount of memory that it has allocated, it will assert, letting you know which allocator failed, and how many allocations and the amount of memory it still had allocated. Memory leaks begone. (Well, not gone, but they will certainly be easier to find!) 116 | 117 | Of course, allocation tracking and statistics is something you can disable, since it has some performance and memory overhead. Simply `#define SRALLOC_DISABLE_STATS` before including `sralloc.h`. 118 | 119 | So what does the **proxy allocator** do? Simple - it forwards any allocations to its **backing allocator** - in this case, the malloc allocator (we pass it in to `sralloc_create_proxy_allocator`, see?). And like every other allocator, it collects stats and aligns memory if you so wish. 120 | 121 | #### A note on the macros and API 122 | 123 | We used the utility macro `SRALLOC_OBJECT`. It takes a type as the second parameter, allocates something of its size, and casts the return value. It does NOT initialize the value, and obviously, does not do inplace new (that's C++ after all) but I intend to add utility macros for that too, since it can be quite nice. 124 | 125 | The macro `SRALLOC_ARRAY` takes a type and a count and allocates an array of that size. 126 | 127 | For consistency, there's also a `SRALLOC_BYTES` that simply wraps `sralloc_alloc`, and `SRALLOC_DEALLOC` that wraps `sralloc_dealloc`. 128 | 129 | For those macros, there are also matching macros that returns aligned pointers, `SRALLOC_ALIGNED_BYTES` and so on. 130 | 131 | As you may have guessed, `sralloc_alloc` and `sralloc_alloc_aligned` are the "core" functions that you will call to allocate memory. 132 | 133 | There's one more detail that's worth mentioning here. There's also `sralloc_alloc_with_size` (and aligned) that gives you a struct result back: 134 | 135 | ```C 136 | typedef struct { 137 | void* ptr; 138 | srint_t size; 139 | } sr_result_t; 140 | ``` 141 | 142 | Sometimes, an allocator can return **more** memory than you requested. In some cases you may be able to take advantage of this, and that's where this comes in. 143 | 144 | ### Adding a Frame allocator 145 | 146 | In Sweden we say that "a beloved child has many names". I'm not sure where it comes from but it's certainly true for the Frame allocator. I've heard "arena allocator", "scratch allocator", "stack allocator", "frame allocator", "stack frame allocator", and "temp allocator", and as far as I know, they all mean the same thing. 147 | 148 | Let's see how one might be useful. 149 | 150 | In the example above, there's an update loop. A common pattern in games is that memory needs to be allocated during a frame and then **only** gets used during that frame. This fact can be abused. 151 | 152 | Here are some issues you might run into with non-allocator based solutions: 153 | 154 | - Sometimes, the lifetime of a variable on the stack is not enough. 155 | - The stack may not be big enough to allocate as much memory as you need. 156 | - Perhaps you need to allocate a pointer in one system, then pass it off to another system that will process it later in the update. 157 | - If it's too much data, it's not possible or desireable to pass it by value. 158 | - Sometimes the receiving system can't immediately copy the incoming data to an internal buffer. 159 | 160 | One solution you might try is to malloc some memory, pass the pointer and rely on the receiving system to free it. I would generally consider this a very bad solution - unless you like memory leaks. In my experience, it's almost always the case that you want the place in the code that deallocates memory to be very close to the code that allocated it. 161 | 162 | Another solution is to go full C++ and use something like `shared_ptr`, but as you well know, **you never go full C++**. Joking aside, the games industry seem to be moving away from shared_ptrs and the like. We used it on Just Cause 2 and didn't realize the performance, compile-time, and executable-size implications until too late (don't ask me for numbers, it was many years ago, I can only say that for a period of time, rebuilding the JC2 code base took 40+ minutes. I don't know how much of that was due to shared_ptr, but hopefully you can appreciate the operational cost of 50 programmers rolling their thumbs for hours each day, so compile-time costs are nothing to scoff at). 163 | 164 | Even if you **can** malloc and free the pointer inside your system, like this.... 165 | 166 | ```C 167 | void sys1_update(System1* sys) { 168 | for (i = 0; i < 1000) { 169 | MyObject* obj = malloc(sizeof(MyObject)); 170 | init_obj(sys, i, obj); 171 | sys2_api(obj); 172 | free(obj); 173 | } 174 | } 175 | ``` 176 | 177 | ... it might not be a good solution because heap allocations can be relatively expensive. 178 | 179 | Here's how using a **frame allocator** would fix the problem: 180 | 181 | ```C 182 | void main() { 183 | // ... 184 | srallocator_t* frame_allocator = sralloc_create_frame_allocator("my_frame", mallocalloc, 10000); 185 | 186 | bool quit = false; 187 | while (!quit) { 188 | sralloc_frame_allocator_clear(frame_allocator); 189 | quit = update_simulation(sim, frame_allocator); 190 | render_game(sim, graphics, frame_allocator); 191 | } 192 | 193 | sralloc_destroy_frame_allocator(frame_allocator); 194 | // ... 195 | } 196 | 197 | void sys1_update(System1* sys, srallocator_t* frame_allocator) { 198 | for (i = 0; i < 1000) { 199 | MyObject* obj = SRALLOC_OBJECT(frame_allocator, System2Object); 200 | init_obj(sys, i, obj); 201 | syst2_api(obj); 202 | } 203 | } 204 | 205 | ``` 206 | 207 | We create a frame allocator with a predetermined size. Systems are free to allocate memory from it and don't have to care about freeing it. (In fact, deallocing a stack allocated pointer does nothing!) 208 | 209 | So a frame allocator is faster for two reasons: 210 | - You don't have deallocate memory. 211 | - Allocations themselves are very fast. 212 | 213 | You could alternatively have a frame allocator for each system that needs it. However, a "global" (or shared) frame allocator will, in all likelyhood, **save you memory**. You're sharing the allocator between multiple systems - one system can use more memory one frame while another uses less. 214 | 215 | Of course it's important to allocate enough for the worst-case-scenario, but depending on your game this might be less than the sum of the worst-case-scenario of each individual system. For example, maybe you know that there can be a maximum of 100 space aliens and 50 tentacle monsters, but each spawned tentacle monster eats two space aliens, so there'll never be a total of 150 enemies. 216 | 217 | ## License 218 | 219 | MIT/PD 220 | 221 | ## TODO 222 | - Optional assert on allocation fail (instead of return 0) 223 | - Rename stack allocator 224 | - Mutex allocator 225 | - rpmalloc wrapper 226 | - Ensure as much overhead as possible can be disabled in release builds 227 | - C++ API 228 | 229 | ## References 230 | - Stingray Engine Code Video Walkthrough: [#2 Memory](https://www.youtube.com/watch?v=pGXEsVasv_o) 231 | - Bitsquid blog: [Custom Memory Allocation in C++](http://bitsquid.blogspot.se/2010/09/custom-memory-allocation-in-c.html) 232 | - Nicholas Frechette blog: [Stack Frame Allocators]( https://nfrechette.github.io/2016/05/08/stack_frame_allocators/) 233 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | #---------------------------------# 2 | # general configuration # 3 | #---------------------------------# 4 | 5 | # version format 6 | version: 1.0.{build} 7 | 8 | #---------------------------------# 9 | # environment configuration # 10 | #---------------------------------# 11 | 12 | image: Visual Studio 2017 13 | 14 | platform: 15 | - x64 16 | # - Win32 17 | 18 | configuration: 19 | - Debug 20 | - Release 21 | 22 | build: 23 | parallel: true 24 | project: examples/sralloc.sln 25 | 26 | #---------------------------------# 27 | # tests configuration # 28 | #---------------------------------# 29 | 30 | test_script: 31 | - '%APPVEYOR_BUILD_FOLDER%\examples\build\%PLATFORM%_%CONFIGURATION%\unittest.exe' 32 | 33 | # to disable automatic tests 34 | #test: off 35 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | # INCLUDES = -I.. 2 | 3 | CFLAGS = -Werror -Wall -Wextra -Wpedantic -std=c99 $(EXTRA_DEFINES) 4 | CPPFLAGS = -Werror -Wall -Wextra -Wpedantic -std=c++0x $(EXTRA_DEFINES) 5 | 6 | build_c: 7 | $(CC) $(CFLAGS) unittest/unittest.c -DNO_IGDEBUG 8 | build_cpp: 9 | $(CXX) $(CPPFLAGS) unittest/unittest.c external/ig_debugheap/DebugHeap.c 10 | 11 | all: build_c 12 | -------------------------------------------------------------------------------- /examples/build_cygwin.sh: -------------------------------------------------------------------------------- 1 | # make all EXTRA_DEFINES="-D_WIN32 -DNO_IGDEBUG" 2 | make all EXTRA_DEFINES="-D_WIN32" 3 | -------------------------------------------------------------------------------- /examples/external/ig_debugheap/DebugHeap.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Insomniac Games 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "DebugHeap.h" 28 | #include 29 | #include 30 | #include 31 | 32 | #if defined(_WIN32) 33 | #include 34 | #elif defined(__APPLE__) || defined(linux) 35 | #include 36 | #else 37 | # error What are you?! 38 | #endif 39 | 40 | //----------------------------------------------------------------------------- 41 | // Preliminaries 42 | 43 | // An assert macro that kills the program. 44 | // Substitute your own assert macro that takes formatted text. 45 | #define ASSERT_FATAL(expr, message, ...) \ 46 | assert(expr && message) 47 | 48 | // Routines that wrap platform-specific virtual memory functionality. 49 | 50 | static void* VmAllocate(size_t size); 51 | static void VmFree(void* ptr, size_t size); 52 | static void VmCommit(void* ptr, size_t size); 53 | static void VmDecommit(void* ptr, size_t size); 54 | 55 | // Windows virtual memory support. 56 | #if defined(_WIN32) 57 | typedef volatile LONG DebugHeapAtomicType; 58 | 59 | static void* VmAllocate(size_t size) 60 | { 61 | void* result = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE); 62 | ASSERT_FATAL(result, "Couldn't allocate address space"); 63 | return result; 64 | } 65 | 66 | static void VmFree(void* ptr, size_t size) 67 | { 68 | BOOL result = VirtualFree(ptr, 0, MEM_RELEASE); 69 | ASSERT_FATAL(result, "Failed to free memory"); 70 | (void) size; 71 | } 72 | 73 | static void VmCommit(void* ptr, size_t size) 74 | { 75 | LPVOID result = VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE); 76 | ASSERT_FATAL(result, "Failed to commit memory"); 77 | } 78 | 79 | static void VmDecommit(void* ptr, size_t size) 80 | { 81 | BOOL result = VirtualFree(ptr, size, MEM_DECOMMIT); 82 | ASSERT_FATAL(result, "Failed to decommit memory"); 83 | } 84 | 85 | static DebugHeapAtomicType AtomicInc32(DebugHeapAtomicType *var) 86 | { 87 | return InterlockedIncrement(var); 88 | } 89 | 90 | static DebugHeapAtomicType AtomicDec32(DebugHeapAtomicType *var) 91 | { 92 | return InterlockedDecrement(var); 93 | } 94 | #endif 95 | 96 | #if defined(__APPLE__) || defined(linux) 97 | typedef volatile uint32_t DebugHeapAtomicType; 98 | 99 | static void* VmAllocate(size_t size) 100 | { 101 | void* result = mmap(NULL, size, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); 102 | ASSERT_FATAL(result, "Couldn't allocate address space"); 103 | return result; 104 | } 105 | 106 | static void VmFree(void* ptr, size_t size) 107 | { 108 | int result = munmap(ptr, size); 109 | ASSERT_FATAL(0 == result, "Failed to free memory"); 110 | } 111 | 112 | static void VmCommit(void* ptr, size_t size) 113 | { 114 | int result = mprotect(ptr, size, PROT_READ|PROT_WRITE); 115 | ASSERT_FATAL(0 == result, "Failed to commit memory"); 116 | } 117 | 118 | static void VmDecommit(void* ptr, size_t size) 119 | { 120 | int result = madvise(ptr, size, MADV_DONTNEED); 121 | ASSERT_FATAL(0 == result, "madvise() failed"); 122 | result = mprotect(ptr, size, PROT_NONE); 123 | ASSERT_FATAL(0 == result, "Failed to decommit memory"); 124 | } 125 | 126 | static DebugHeapAtomicType AtomicInc32(DebugHeapAtomicType *var) 127 | { 128 | return __sync_add_and_fetch(var, 1); 129 | } 130 | 131 | static DebugHeapAtomicType AtomicDec32(DebugHeapAtomicType *var) 132 | { 133 | return __sync_sub_and_fetch(var, 1); 134 | } 135 | #endif 136 | 137 | 138 | // We want to use the smallest page size possible, and that happens to be 4k on x86/x64. 139 | // Using larger pages sizes would waste enormous amounts of memory. 140 | enum 141 | { 142 | kPageSize = 4096, 143 | }; 144 | 145 | typedef struct DebugBlockInfo 146 | { 147 | uint32_t m_Allocated : 1; 148 | uint32_t m_PageCount : 31; 149 | uint32_t m_PendingFree : 1; 150 | uint32_t m_PageIndex : 31; 151 | struct DebugBlockInfo *m_Prev; 152 | struct DebugBlockInfo *m_Next; 153 | } DebugBlockInfo; 154 | 155 | struct DebugHeap 156 | { 157 | uint32_t m_MaxAllocs; 158 | uint32_t m_PageCount; 159 | 160 | char* m_BaseAddress; 161 | 162 | uint32_t m_FreeListSize; 163 | DebugBlockInfo** m_FreeList; 164 | 165 | uint32_t m_PendingListSize; 166 | DebugBlockInfo** m_PendingList; 167 | 168 | DebugBlockInfo** m_BlockLookup; 169 | DebugBlockInfo* m_FirstUnusedBlockInfo; 170 | 171 | DebugBlockInfo* m_Blocks; 172 | 173 | DebugHeapAtomicType m_ReentrancyGuard; 174 | }; 175 | 176 | #define DEBUG_THREAD_GUARD_ENTER(heap) \ 177 | ASSERT_FATAL(1 == AtomicInc32(&heap->m_ReentrancyGuard), "Unsynchronized MT usage detected") 178 | 179 | #define DEBUG_THREAD_GUARD_LEAVE(heap) \ 180 | ASSERT_FATAL(0 == AtomicDec32(&heap->m_ReentrancyGuard), "Unsynchronized MT usage detected") 181 | 182 | static void* AdvancePtr(void* src, size_t amount) 183 | { 184 | return (char*)src + amount; 185 | } 186 | 187 | DebugBlockInfo* AllocBlockInfo(DebugHeap* heap) 188 | { 189 | DebugBlockInfo* result = heap->m_FirstUnusedBlockInfo; 190 | ASSERT_FATAL((uint32_t)result->m_Allocated, "Block info corrupted"); 191 | ASSERT_FATAL((uint32_t)result->m_PendingFree, "Block info corrupted"); 192 | heap->m_FirstUnusedBlockInfo = result->m_Next; 193 | 194 | memset(result, 0, sizeof *result); 195 | 196 | return result; 197 | } 198 | 199 | void FreeBlockInfo(DebugHeap* heap, DebugBlockInfo* block_info) 200 | { 201 | block_info->m_Allocated = 1; 202 | block_info->m_PendingFree = 1; 203 | block_info->m_Prev = NULL; 204 | block_info->m_Next = heap->m_FirstUnusedBlockInfo; 205 | heap->m_FirstUnusedBlockInfo = block_info; 206 | } 207 | 208 | DebugHeap* DebugHeapInit(size_t mem_size_bytes) 209 | { 210 | DebugHeap* self; 211 | 212 | const size_t mem_page_count = mem_size_bytes / kPageSize; 213 | const size_t max_allocs = mem_page_count / 2; 214 | 215 | const size_t bookkeeping_bytes = 216 | sizeof(DebugHeap) + 217 | (3 * mem_page_count * sizeof(DebugBlockInfo*)) + 218 | sizeof(DebugBlockInfo) * mem_page_count; 219 | 220 | const size_t bookkeeping_pages = (bookkeeping_bytes + kPageSize - 1) / kPageSize; 221 | const size_t total_pages = bookkeeping_pages + mem_page_count; 222 | const size_t total_bytes = total_pages * kPageSize; 223 | 224 | char* range = (char *)VmAllocate(total_bytes); 225 | if (!range) 226 | { 227 | return NULL; 228 | } 229 | 230 | VmCommit(range, bookkeeping_pages * kPageSize); 231 | 232 | self = (DebugHeap*) range; 233 | 234 | self->m_MaxAllocs = (uint32_t) max_allocs; 235 | self->m_BaseAddress = range + kPageSize * bookkeeping_pages; 236 | self->m_PageCount = (uint32_t) mem_page_count; 237 | self->m_FreeList = (DebugBlockInfo**) AdvancePtr(range, sizeof(DebugHeap)); 238 | self->m_PendingList = (DebugBlockInfo**) AdvancePtr(self->m_FreeList, sizeof(DebugBlockInfo*) * mem_page_count); 239 | self->m_BlockLookup = (DebugBlockInfo**) AdvancePtr(self->m_PendingList, sizeof(DebugBlockInfo*) * mem_page_count); 240 | self->m_Blocks = (DebugBlockInfo*) AdvancePtr(self->m_BlockLookup, sizeof(DebugBlockInfo*) * mem_page_count); 241 | self->m_FreeListSize = 1; 242 | self->m_PendingListSize = 0; 243 | self->m_ReentrancyGuard = 0; 244 | 245 | // Initialize block allocation linked list 246 | { 247 | uint32_t i, count; 248 | for (i = 0, count = self->m_MaxAllocs; i < count; ++i) 249 | { 250 | self->m_Blocks[i].m_Allocated = 1; // flag invalid 251 | self->m_Blocks[i].m_PendingFree = 1; // flag invalid 252 | self->m_Blocks[i].m_Prev = NULL; 253 | self->m_Blocks[i].m_Next = (i + 1) < count ? &self->m_Blocks[i+1] : NULL; 254 | } 255 | } 256 | 257 | self->m_FirstUnusedBlockInfo = &self->m_Blocks[0]; 258 | 259 | { 260 | DebugBlockInfo* root_block = AllocBlockInfo(self); 261 | 262 | root_block->m_PageIndex = 0; 263 | root_block->m_Allocated = 0; 264 | root_block->m_PendingFree = 0; 265 | root_block->m_PageCount = (uint32_t) mem_page_count; 266 | root_block->m_Prev = NULL; 267 | root_block->m_Next = NULL; 268 | 269 | self->m_FreeList[0] = root_block; 270 | } 271 | 272 | return self; 273 | } 274 | 275 | void DebugHeapDestroy(DebugHeap* heap) 276 | { 277 | VmFree(heap, heap->m_PageCount * kPageSize); 278 | } 279 | 280 | static void* AllocFromFreeList(DebugHeap* heap, size_t page_req) 281 | { 282 | // Cache in register to avoid repeated memory derefs 283 | DebugBlockInfo** const free_list = heap->m_FreeList; 284 | 285 | // Keep track of the best fitting block so far. 286 | DebugBlockInfo* best_block = NULL; 287 | uint32_t best_block_size = ~0u; 288 | uint32_t best_freelist_index = 0; 289 | uint32_t i, count; 290 | 291 | // First try the free list. This is slow. That's OK. It's a debug heap. 292 | for (i = 0, count = heap->m_FreeListSize; i < count; ++i) 293 | { 294 | DebugBlockInfo* block = free_list[i]; 295 | uint32_t block_count = block->m_PageCount; 296 | ASSERT_FATAL(!block->m_Allocated, "block info corrupted"); 297 | ASSERT_FATAL(!block->m_PendingFree, "block info corrupted"); 298 | 299 | if (block_count >= page_req && block_count < best_block_size) 300 | { 301 | best_block = block; 302 | best_block_size = block_count; 303 | best_freelist_index = i; 304 | } 305 | } 306 | 307 | if (!best_block) 308 | return NULL; 309 | 310 | // Take this block off the free list. 311 | if (heap->m_FreeListSize > 1) 312 | { 313 | heap->m_FreeList[best_freelist_index] = heap->m_FreeList[heap->m_FreeListSize - 1]; 314 | } 315 | heap->m_FreeListSize--; 316 | 317 | // Carve out the number of pages we need from our best block. 318 | { 319 | uint32_t unused_page_count = (uint32_t) (best_block_size - page_req); 320 | 321 | if (unused_page_count > 0) 322 | { 323 | // Allocate a new block to keep track of the tail end. 324 | DebugBlockInfo* tail_block = AllocBlockInfo(heap); 325 | tail_block->m_Allocated = 0; 326 | tail_block->m_PendingFree = 0; 327 | tail_block->m_PageIndex = best_block->m_PageIndex + best_block->m_PageCount - unused_page_count; 328 | tail_block->m_PageCount = unused_page_count; 329 | 330 | // Link it in to the chain. 331 | tail_block->m_Next = best_block->m_Next; 332 | tail_block->m_Prev = best_block; 333 | best_block->m_Next = tail_block; 334 | 335 | // Add it to the free list 336 | heap->m_FreeList[heap->m_FreeListSize++] = tail_block; 337 | 338 | // Patch up this block 339 | best_block->m_PageCount = (uint32_t) page_req; 340 | } 341 | } 342 | 343 | best_block->m_Allocated = 1; 344 | 345 | ASSERT_FATAL(heap->m_BlockLookup[best_block->m_PageIndex] == NULL, "block lookup corrupted"); 346 | heap->m_BlockLookup[best_block->m_PageIndex] = best_block; 347 | 348 | { 349 | uint32_t i, max; 350 | for (i = 1, max = best_block->m_PageCount; i < max; ++i) 351 | { 352 | ASSERT_FATAL(heap->m_BlockLookup[best_block->m_PageIndex + i] == NULL, "block lookup corrupted"); 353 | } 354 | } 355 | 356 | return heap->m_BaseAddress + ((uint64_t)(best_block->m_PageIndex)) * kPageSize; 357 | } 358 | 359 | static void* FinalizeAlloc(void* ptr_in, size_t user_size, size_t pages_allocated, size_t user_alignment) 360 | { 361 | char* ptr = (char*) ptr_in; 362 | uint32_t ideal_offset, aligned_offset; 363 | 364 | // Commit pages in user-accessible section. 365 | VmCommit(ptr, (pages_allocated - 1) * kPageSize); 366 | 367 | // Decommit guard page to force crashes for stepping over bounds 368 | VmDecommit(ptr + (pages_allocated - 1) * kPageSize, kPageSize); 369 | 370 | // Align user allocation towards end of page, respecting user alignment. 371 | 372 | // Ideally the offset would be kPageSize - user_size % kPageSize. 373 | ideal_offset = ((uint32_t)(kPageSize - user_size)) % kPageSize; 374 | 375 | // Align down to meet user minimum alignment. 376 | aligned_offset = ideal_offset & ~((uint32_t)(user_alignment-1)); 377 | 378 | // Garbage fill start of page. 379 | memset(ptr, 0xfc, aligned_offset); 380 | 381 | return ptr + aligned_offset; 382 | } 383 | 384 | static void FlushPendingFrees(DebugHeap* heap) 385 | { 386 | uint32_t i, count; 387 | for (i = 0, count = heap->m_PendingListSize; i < count; ++i) 388 | { 389 | int block_removed = 0; 390 | 391 | DebugBlockInfo* block = heap->m_PendingList[i]; 392 | DebugBlockInfo* prev; 393 | DebugBlockInfo* next; 394 | 395 | // Attempt to merge into an adjacent block to the left. 396 | // We can only merge with blocks that are free and not on this same pending list. 397 | if (NULL != (prev = block->m_Prev)) 398 | { 399 | if (!prev->m_Allocated && !prev->m_PendingFree && prev->m_PageIndex + prev->m_PageCount == block->m_PageIndex) 400 | { 401 | // Linked list setup. 402 | prev->m_Next = block->m_Next; 403 | 404 | if (block->m_Next) 405 | block->m_Next->m_Prev = prev; 406 | 407 | // Increase size of left neighbor. 408 | prev->m_PageCount += block->m_PageCount; 409 | 410 | // Kill this pending block. 411 | FreeBlockInfo(heap, block); 412 | 413 | // Attempt to do right side coalescing with this other block instead. 414 | block = prev; 415 | 416 | // Don't try to delete this block later - we've already done that. 417 | block_removed = 1; 418 | } 419 | } 420 | 421 | // Attempt to merge into an adjacent block to the right. 422 | if (NULL != (next = block->m_Next)) 423 | { 424 | if (!next->m_Allocated && !next->m_PendingFree && next->m_PageIndex == block->m_PageIndex + block->m_PageCount) 425 | { 426 | uint32_t fi, fcount; 427 | // Linked list setup. 428 | block->m_Next = next->m_Next; 429 | if (block->m_Next) 430 | block->m_Next->m_Prev = block; 431 | block->m_PageCount += next->m_PageCount; 432 | 433 | // Find this thing on the free list and remove it. This is slow. 434 | for (fi = 0, fcount = heap->m_FreeListSize; fi < fcount; ++fi) 435 | { 436 | if (heap->m_FreeList[fi] == next) 437 | { 438 | heap->m_FreeList[fi] = heap->m_FreeList[heap->m_FreeListSize-1]; 439 | --heap->m_FreeListSize; 440 | break; 441 | } 442 | } 443 | 444 | // Free the R neighbor block now that we're done with it. 445 | FreeBlockInfo(heap, next); 446 | } 447 | } 448 | 449 | if (!block_removed) 450 | { 451 | // This block goes on the free list. 452 | block->m_PendingFree = 0; 453 | heap->m_FreeList[heap->m_FreeListSize++] = block; 454 | } 455 | } 456 | 457 | heap->m_PendingListSize = 0; 458 | } 459 | 460 | void* DebugHeapAllocate(DebugHeap* heap, size_t size, size_t alignment) 461 | { 462 | void* ptr; 463 | uint32_t page_req; 464 | 465 | DEBUG_THREAD_GUARD_ENTER(heap); 466 | 467 | // Figure out how many pages we're going to need. 468 | // Always increment by one so we have room for a guard page at the end. 469 | page_req = 1 + (uint32_t) ((size + kPageSize - 1) / kPageSize); 470 | 471 | if (NULL != (ptr = AllocFromFreeList(heap, page_req))) 472 | { 473 | void* result = FinalizeAlloc(ptr, size, page_req, alignment); 474 | DEBUG_THREAD_GUARD_LEAVE(heap); 475 | return result; 476 | } 477 | 478 | // We couldn't find a block off the free list. Consolidate pending frees. 479 | FlushPendingFrees(heap); 480 | 481 | // Try again. 482 | if (NULL != (ptr = AllocFromFreeList(heap, page_req))) 483 | { 484 | void* result = FinalizeAlloc(ptr, size, page_req, alignment); 485 | DEBUG_THREAD_GUARD_LEAVE(heap); 486 | return result; 487 | } 488 | 489 | // Out of memory. 490 | DEBUG_THREAD_GUARD_LEAVE(heap); 491 | return NULL; 492 | } 493 | 494 | void DebugHeapFree(DebugHeap* heap, void* ptr_in) 495 | { 496 | uintptr_t ptr; 497 | uintptr_t relative_offset; 498 | uint32_t page_index; 499 | DebugBlockInfo *block; 500 | char *block_base; 501 | 502 | DEBUG_THREAD_GUARD_ENTER(heap); 503 | 504 | // Figure out what page this belongs to. 505 | ptr = (uintptr_t) ptr_in; 506 | 507 | relative_offset = ptr - (uintptr_t) heap->m_BaseAddress; 508 | page_index = (uint32_t) (relative_offset / kPageSize); 509 | 510 | ASSERT_FATAL(page_index < heap->m_PageCount, "Invalid pointer %p freed", ptr_in); 511 | 512 | block = heap->m_BlockLookup[page_index]; 513 | 514 | ASSERT_FATAL(block, "Double free of %p", ptr_in); 515 | 516 | ASSERT_FATAL((uint32_t)block->m_Allocated, "Block state corrupted"); 517 | ASSERT_FATAL(!block->m_PendingFree, "Block state corrupted"); 518 | 519 | // TODO: Check the fill pattern before the user pointer. 520 | 521 | block->m_Allocated = 0; 522 | block->m_PendingFree = 1; 523 | 524 | // Zero out this block in the lookup to catch double frees. 525 | heap->m_BlockLookup[page_index] = NULL; 526 | 527 | { 528 | uint32_t i, max; 529 | for (i = 1, max = block->m_PageCount; i < max; ++i) 530 | { 531 | ASSERT_FATAL(heap->m_BlockLookup[page_index + i] == NULL, "block lookup corrupted"); 532 | } 533 | } 534 | 535 | // Add the block to the pending free list 536 | heap->m_PendingList[heap->m_PendingListSize++] = block; 537 | 538 | // Protect these blocks from reading or writing completely by decommiting the pages. 539 | // The last page is already inaccessible. 540 | block_base = heap->m_BaseAddress + ((uint64_t)block->m_PageIndex) * kPageSize; 541 | VmDecommit(block_base, ((uint64_t)(block->m_PageCount - 1)) * kPageSize); 542 | 543 | DEBUG_THREAD_GUARD_LEAVE(heap); 544 | } 545 | 546 | size_t DebugHeapGetAllocSize(DebugHeap* heap, void* ptr_in) 547 | { 548 | uintptr_t ptr; 549 | uintptr_t relative_offset; 550 | uint32_t page_index; 551 | size_t result; 552 | DebugBlockInfo* block; 553 | 554 | DEBUG_THREAD_GUARD_ENTER(heap); 555 | 556 | // Figure out what page this belongs to. 557 | ptr = (uintptr_t) ptr_in; 558 | 559 | relative_offset = ptr - (uintptr_t) heap->m_BaseAddress; 560 | page_index = (uint32_t) (relative_offset / kPageSize); 561 | 562 | ASSERT_FATAL(page_index < heap->m_PageCount, "Invalid pointer %p", ptr_in); 563 | 564 | block = heap->m_BlockLookup[page_index]; 565 | 566 | result = (block->m_PageCount - 1) * kPageSize - ptr % kPageSize; 567 | 568 | DEBUG_THREAD_GUARD_LEAVE(heap); 569 | 570 | return result; 571 | } 572 | 573 | int DebugHeapOwns(DebugHeap* heap, void* buffer) 574 | { 575 | uintptr_t ptr; 576 | uintptr_t base; 577 | uintptr_t end; 578 | int status; 579 | 580 | DEBUG_THREAD_GUARD_ENTER(heap); 581 | 582 | ptr = (uintptr_t) buffer; 583 | base = (uintptr_t) heap->m_BaseAddress; 584 | end = base + ((uint64_t)heap->m_PageCount) * kPageSize; 585 | status = ptr >= base && ptr <= end; 586 | 587 | DEBUG_THREAD_GUARD_LEAVE(heap); 588 | return status; 589 | } 590 | -------------------------------------------------------------------------------- /examples/external/ig_debugheap/DebugHeap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(__cplusplus) 6 | extern "C" { 7 | #endif 8 | 9 | /* 10 | Copyright (c) 2014, Insomniac Games 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | Redistributions of source code must retain the above copyright notice, this 17 | list of conditions and the following disclaimer. 18 | 19 | Redistributions in binary form must reproduce the above copyright notice, this 20 | list of conditions and the following disclaimer in the documentation and/or 21 | other materials provided with the distribution. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 24 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | //----------------------------------------------------------------------------- 36 | // Debug heap functionality 37 | // 38 | // These set of functions implement a debug heap. It provides the following 39 | // features: 40 | // 41 | // - Array indexing errors (positive) trigger crashes, because allocations are 42 | // aligned as closely as possible up to an inaccessible virtual memory page. 43 | // 44 | // - Using memory after freeing it trigger crashes most of the time. 45 | // 46 | // - Double frees are detected most of the time. 47 | // 48 | // - Unsynchronized multi-threaded access is detected. 49 | // 50 | // To improve the chances of crashing on use-after-free or double frees, 51 | // increase the size of the heap. Freed blocks are kept on an "observation 52 | // list" for as long as possible to flush out these error classes, but it will 53 | // eventually be reused. 54 | // 55 | // This heap is terribly slow, and wastes tons of memory. You only want to use 56 | // it to track down memory errors. One neat way of doing that is to provide a 57 | // heap interface that can dynamically switch to this heap, maybe with a 58 | // configuration option. You can then hunt for memory errors without recompiling. 59 | 60 | typedef struct DebugHeap DebugHeap; 61 | 62 | // Create and initialize a debug heap. 63 | // The size must be a multiple of the page size (4k), and should be generously padded. 64 | // At the very least you need 2 pages per sub-4k allocation, but the more the better. 65 | // The implementation is 64-bit clean and you can throw more than 4 GB at it just fine. 66 | DebugHeap* DebugHeapInit(size_t size); 67 | 68 | // Nuke a debug heap. All memory is returned to the OS. 69 | void DebugHeapDestroy(DebugHeap* heap); 70 | 71 | // Allocate memory from a debug heap. 72 | // Size can be any value, except zero. 73 | // Alignment must be a power of two. 74 | // Returns NULL if the heap is full. 75 | void* DebugHeapAllocate(DebugHeap* heap, size_t size, size_t alignment); 76 | 77 | // Free memory in a debug heap. 78 | void DebugHeapFree(DebugHeap* heap, void* ptr); 79 | 80 | // Return the allocation size for a previously allocated block. 81 | size_t DebugHeapGetAllocSize(DebugHeap* heap, void* ptr); 82 | 83 | // A quick and dirty range check to see if a buffer could have come from a debug heap. 84 | // Doesn't validate that the buffer is actually allocated. 85 | int DebugHeapOwns(DebugHeap* heap, void* buffer); 86 | 87 | #if defined(__cplusplus) 88 | } 89 | #endif 90 | -------------------------------------------------------------------------------- /examples/external/ig_debugheap/README.md: -------------------------------------------------------------------------------- 1 | ig-debugheap - A debugging heap 2 | ============================================================================= 3 | 4 | This is a debug heap useful when trying to track down memory errors (especially 5 | on Windows, where there's no Valgrind.) It is written in C, and works on Mac, 6 | Linux and Windows. 7 | 8 | This package provides the following features: 9 | 10 | - Array indexing errors (positive) trigger crashes, because allocations are 11 | aligned as closely as possible up to an inaccessible virtual memory page. 12 | 13 | - Using memory after freeing it triggers a crash most of the time. 14 | 15 | - Double frees are detected most of the time. 16 | 17 | - Unsynchronized multi-threaded access is detected. 18 | 19 | To improve the chances of crashing on use-after-free or double frees, 20 | increase the size of the heap. Freed blocks are kept on an "observation 21 | list" for as long as possible to flush out these error classes, but it will 22 | eventually be reused. 23 | 24 | This heap is terribly slow, and wastes tons of memory. You only want to use 25 | it to track down memory errors. One neat way of doing that is to provide a 26 | heap interface that can dynamically switch to this heap, maybe with a 27 | configuration option. You can then hunt for memory errors without recompiling. 28 | 29 | License 30 | ----------------------------------------------------------------------------- 31 | Copyright (c) 2014, Insomniac Games 32 | All rights reserved. 33 | 34 | Redistribution and use in source and binary forms, with or without 35 | modification, are permitted provided that the following conditions are met: 36 | 37 | Redistributions of source code must retain the above copyright notice, this 38 | list of conditions and the following disclaimer. 39 | 40 | Redistributions in binary form must reproduce the above copyright notice, this 41 | list of conditions and the following disclaimer in the documentation and/or 42 | other materials provided with the distribution. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 45 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 46 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 47 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 48 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 50 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 51 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 52 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 53 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 | -------------------------------------------------------------------------------- /examples/external/ig_debugheap/demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014, Insomniac Games 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | // demo.c - quick demo of debug heap functionality 28 | 29 | #include 30 | #include 31 | 32 | #include "DebugHeap.h" 33 | 34 | int main(int argc, char* argv[]) 35 | { 36 | DebugHeap *heap; 37 | 38 | if (argc < 2) { 39 | fprintf(stderr, "Usage: demo \n"); 40 | fprintf(stderr, "\nTest cases:\n"); 41 | fprintf(stderr, "0: setup+teardown\n"); 42 | fprintf(stderr, "1: array overrun (should crash)\n"); 43 | fprintf(stderr, "2: double free (should assert)\n"); 44 | fprintf(stderr, "3: use after free (should crash)\n"); 45 | exit(1); 46 | } 47 | 48 | heap = DebugHeapInit(2 * 1024 * 1024); 49 | 50 | switch (atoi(argv[1])) { 51 | case 0: 52 | { 53 | char* ptr; 54 | ptr = DebugHeapAllocate(heap, 128, 4); 55 | ptr[127] = 'a'; 56 | DebugHeapFree(heap, ptr); 57 | } 58 | break; 59 | 60 | case 1: 61 | { 62 | char* ptr; 63 | ptr = DebugHeapAllocate(heap, 128, 4); 64 | ptr[128] = 'a'; // should crash here 65 | } 66 | break; 67 | 68 | case 2: 69 | { 70 | char* ptr; 71 | ptr = DebugHeapAllocate(heap, 128, 4); 72 | DebugHeapFree(heap, ptr); 73 | DebugHeapFree(heap, ptr); // should assert here 74 | ptr[127] = 'a'; 75 | } 76 | break; 77 | 78 | case 3: 79 | { 80 | char* ptr; 81 | ptr = DebugHeapAllocate(heap, 128, 4); 82 | DebugHeapFree(heap, ptr); 83 | ptr[0] = 'a'; // should crash here 84 | } 85 | break; 86 | 87 | default: 88 | fprintf(stderr, "Unsupported test case\n"); 89 | break; 90 | } 91 | 92 | DebugHeapDestroy(heap); 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /examples/external/ig_debugheap/tundra.lua: -------------------------------------------------------------------------------- 1 | local common = { 2 | Env = { 3 | CCOPTS = { 4 | -- clang and GCC 5 | { "-g"; Config = { "*-gcc-debug", "*-clang-debug" } }, 6 | { "-g -O2"; Config = { "*-gcc-production", "*-clang-production" } }, 7 | { "-O3"; Config = { "*-gcc-release", "*-clang-release" } }, 8 | { "-Wall", "-Werror", "-Wextra", "-Wno-unused-parameter", "-Wno-unused-function" 9 | ; Config = { "*-gcc-*", "*-clang-*" } 10 | }, 11 | { "/W4"; Config = { "*-msvc-*" } }, 12 | }, 13 | 14 | CPPDEFS = { 15 | { "NDEBUG"; Config = "*-*-release" }, 16 | }, 17 | }, 18 | } 19 | 20 | Build { 21 | Units = function () 22 | 23 | local demo = Program { 24 | Name = "demo", 25 | Sources = { 26 | "DebugHeap.c", 27 | "demo.c", 28 | }, 29 | } 30 | 31 | Default(demo) 32 | end, 33 | 34 | Configs = { 35 | Config { 36 | Name = "macosx-clang", 37 | Inherit = common, 38 | DefaultOnHost = "macosx", 39 | Tools = { "clang-osx", }, 40 | }, 41 | Config { 42 | Name = "linux-gcc", 43 | Inherit = common, 44 | DefaultOnHost = "linux", 45 | Tools = { "gcc", }, 46 | }, 47 | Config { 48 | Name = "win64-msvc", 49 | Inherit = common, 50 | DefaultOnHost = "windows", 51 | Tools = { { "msvc-vs2012"; TargetArch = "x64" }, }, 52 | }, 53 | }, 54 | 55 | Variants = { 56 | { Name = "debug", Options = { GeneratePdb = true } }, 57 | { Name = "release" }, 58 | }, 59 | DefaultVariant = "debug", 60 | } 61 | -------------------------------------------------------------------------------- /examples/external/minctest/LICENSE: -------------------------------------------------------------------------------- 1 | zlib License 2 | 3 | Copyright (C) 2014, 2015 Lewis Van Winkle 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgement in the product documentation would be 16 | appreciated but is not required. 17 | 2. Altered source versions must be plainly marked as such, and must not be 18 | misrepresented as being the original software. 19 | 3. This notice may not be removed or altered from any source distribution. 20 | 21 | -------------------------------------------------------------------------------- /examples/external/minctest/README.md: -------------------------------------------------------------------------------- 1 | # Minctest 2 | 3 | 4 | Minctest is a very minimal unit-testing "framework" written in ANSI C and 5 | implemented in a single header file. It's handy when you want some real simple 6 | unit tests for a small project. 7 | 8 | Basically, it implements assertion and equal functions. It'll track and time 9 | how many tests pass and fail. Failed tests will also display which line the 10 | failing test code was on. 11 | 12 | There is a [Node.js port here](https://github.com/codeplea/minctest-node) and a [Lua port here.](https://github.com/codeplea/minctest-lua) 13 | 14 | ## Features 15 | 16 | - **ANSI C with no dependencies**. 17 | - Single header file. 18 | - Reports file and line number for failed assertions. 19 | - Reports run time for each test. 20 | - Tests continue even after an assertion fails. 21 | - Has assertion for checking float equality. 22 | - Released under the zlib license - free for nearly any use. 23 | 24 | ## Example 25 | 26 | #include "minctest.h" 27 | 28 | void test1() { 29 | lok('a' == 'a'); 30 | } 31 | 32 | void test2() { 33 | lequal(5, 5); 34 | lfequal(5.5, 5.5); 35 | lsequal("abc", "abc"); 36 | } 37 | 38 | int main(int argc, char *argv[]) 39 | { 40 | lrun("test1", test1); 41 | lrun("test2", test2); 42 | lresults(); 43 | return lfails != 0; 44 | } 45 | 46 | 47 | That produces the following output: 48 | 49 | test1 pass: 1 fail: 0 0ms 50 | test2 pass: 3 fail: 0 1ms 51 | ALL TESTS PASSED (4/4) 52 | 53 | 54 | 55 | ## Hints 56 | All functions/variables start with the letter 'l'. 57 | 58 | ## Users 59 | 60 | Minctest is used in almost all of my C projects, including: 61 | 62 | * [Tulip Indicators - Financial Technical Analysis Indicators](https://tulipindicators.org) 63 | * [TinyExpr - Math Expression Evaluation Library](https://codeplea.com/tinyexpr) 64 | * [Genann - Neural Network Library](https://codeplea.com/genann) 65 | 66 | You can check those out to see how Minctest is used in practice. 67 | 68 | If you're using Minctest in your project, let me know. I could add a link back. 69 | -------------------------------------------------------------------------------- /examples/external/minctest/example.c: -------------------------------------------------------------------------------- 1 | /* Simple example of using MINCTEST to test a couple obvious cases. */ 2 | 3 | #include "minctest.h" 4 | 5 | void test1() { 6 | lok('a' == 'a'); 7 | } 8 | 9 | void test2() { 10 | lequal(5, 5); 11 | lfequal(5.5, 5.5); 12 | lsequal("abc", "abc"); 13 | } 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | lrun("test1", test1); 18 | lrun("test2", test2); 19 | lresults(); 20 | return lfails != 0; 21 | } 22 | -------------------------------------------------------------------------------- /examples/external/minctest/minctest.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * MINCTEST - Minimal C Test Library - 0.2.0 4 | * 5 | * Copyright (c) 2014-2017 Lewis Van Winkle 6 | * 7 | * http://CodePlea.com 8 | * 9 | * This software is provided 'as-is', without any express or implied 10 | * warranty. In no event will the authors be held liable for any damages 11 | * arising from the use of this software. 12 | * 13 | * Permission is granted to anyone to use this software for any purpose, 14 | * including commercial applications, and to alter it and redistribute it 15 | * freely, subject to the following restrictions: 16 | * 17 | * 1. The origin of this software must not be misrepresented; you must not 18 | * claim that you wrote the original software. If you use this software 19 | * in a product, an acknowledgement in the product documentation would be 20 | * appreciated but is not required. 21 | * 2. Altered source versions must be plainly marked as such, and must not be 22 | * misrepresented as being the original software. 23 | * 3. This notice may not be removed or altered from any source distribution. 24 | * 25 | */ 26 | 27 | /* 28 | * SRALLOC NOTE: I have made minor changes to this file to better suit the unit 29 | * tests for sralloc. 30 | * /Anders Elfgren 31 | */ 32 | 33 | 34 | 35 | /* 36 | * MINCTEST - Minimal testing library for C 37 | * 38 | * 39 | * Example: 40 | * 41 | * void test1() { 42 | * lok('a' == 'a'); 43 | * } 44 | * 45 | * void test2() { 46 | * lequal(5, 6); 47 | * lfequal(5.5, 5.6); 48 | * } 49 | * 50 | * int main() { 51 | * lrun("test1", test1); 52 | * lrun("test2", test2); 53 | * lresults(); 54 | * return lfails != 0; 55 | * } 56 | * 57 | * 58 | * 59 | * Hints: 60 | * All functions/variables start with the letter 'l'. 61 | * 62 | */ 63 | 64 | 65 | #ifndef __MINCTEST_H__ 66 | #define __MINCTEST_H__ 67 | 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | 74 | /* How far apart can floats be before we consider them unequal. */ 75 | #ifndef LTEST_FLOAT_TOLERANCE 76 | #define LTEST_FLOAT_TOLERANCE 0.001 77 | #endif 78 | 79 | 80 | /* Track the number of passes, fails. */ 81 | /* NB this is made for all tests to be in one file. */ 82 | static int ltests = 0; 83 | static int lfails = 0; 84 | 85 | 86 | /* Display the test results. */ 87 | #define lresults() do {\ 88 | if (lfails == 0) {\ 89 | printf("ALL TESTS PASSED (%d/%d)\n", ltests, ltests);\ 90 | } else {\ 91 | printf("SOME TESTS FAILED (%d/%d)\n", ltests-lfails, ltests);\ 92 | }\ 93 | } while (0) 94 | 95 | 96 | /* Run a test. Name can be any string to print out, test is the function name to call. */ 97 | #define lrun(name, test) do {\ 98 | const int ts = ltests;\ 99 | const int fs = lfails;\ 100 | const clock_t start = clock();\ 101 | printf("\t%-30s", name);\ 102 | test();\ 103 | printf("pass:%2d fail:%2d %4dms\n",\ 104 | (ltests-ts)-(lfails-fs), lfails-fs,\ 105 | (int)((clock() - start) * 1000 / CLOCKS_PER_SEC));\ 106 | } while (0) 107 | 108 | 109 | /* Assert a true statement. */ 110 | #define lok(test) do {\ 111 | ++ltests;\ 112 | if (!(test)) {\ 113 | ++lfails;\ 114 | printf("%s:%d error \n", __FILE__, __LINE__);\ 115 | }} while (0) 116 | 117 | 118 | /* Prototype to assert equal. */ 119 | #define lequal_base(equality, a, b, format) do {\ 120 | ++ltests;\ 121 | if (!(equality)) {\ 122 | ++lfails;\ 123 | printf("%s:%d ("format " != " format")\n", __FILE__, __LINE__, (a), (b));\ 124 | }} while (0) 125 | 126 | 127 | /* Assert two integers are equal. */ 128 | #define lequal(a, b)\ 129 | lequal_base((a) == (b), a, b, "%d") 130 | 131 | 132 | /* Assert two floats are equal (Within LTEST_FLOAT_TOLERANCE). */ 133 | #define lfequal(a, b)\ 134 | lequal_base(fabs((double)(a)-(double)(b)) <= LTEST_FLOAT_TOLERANCE\ 135 | && fabs((double)(a)-(double)(b)) == fabs((double)(a)-(double)(b)), (double)(a), (double)(b), "%f") 136 | 137 | 138 | /* Assert two strings are equal. */ 139 | #define lsequal(a, b)\ 140 | lequal_base(strcmp(a, b) == 0, a, b, "%s") 141 | 142 | 143 | #endif /*__MINCTEST_H__*/ 144 | -------------------------------------------------------------------------------- /examples/sralloc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittest", "unittest/unittest.vcxproj", "{97FE62A5-44C3-4741-882F-FC515FDC8A86}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x64.ActiveCfg = Debug|x64 17 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x64.Build.0 = Debug|x64 18 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x86.ActiveCfg = Debug|Win32 19 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Debug|x86.Build.0 = Debug|Win32 20 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x64.ActiveCfg = Release|x64 21 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x64.Build.0 = Release|x64 22 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x86.ActiveCfg = Release|Win32 23 | {97FE62A5-44C3-4741-882F-FC515FDC8A86}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /examples/unittest/unittest.c: -------------------------------------------------------------------------------- 1 | 2 | #ifdef _WIN32 3 | #ifdef _MSC_VER 4 | #pragma warning( push, 0 ) 5 | #endif 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #ifdef _MSC_VER 9 | #pragma warning( pop ) 10 | #endif 11 | #endif 12 | 13 | #ifndef NO_IGDEBUG 14 | #ifdef _MSC_VER 15 | #pragma warning( push ) 16 | #pragma warning( disable : 4464 4820 ) 17 | #endif 18 | #include "../external/ig_debugheap/DebugHeap.h" 19 | #ifdef _MSC_VER 20 | #pragma warning( pop ) 21 | #endif 22 | #define SRALLOC_ENABLE_IG_DEBUGHEAP 23 | #endif // NO_IGDEBUG 24 | 25 | #define SRALLOC_IMPLEMENTATION 26 | // #define SRALLOC_DISABLE_NAMES 27 | // #define SRALLOC_DISABLE_STATS 28 | #ifdef _MSC_VER 29 | #pragma warning( disable : 4464 4710 ) 30 | #endif 31 | #include "../../sralloc.h" 32 | 33 | #ifdef _MSC_VER 34 | #pragma warning( push ) 35 | #pragma warning( disable : 4777 4710 4005 ) 36 | 37 | #pragma warning( push ) 38 | #pragma warning( disable : 4464 4820 ) 39 | #endif 40 | 41 | #include "../external/minctest/minctest.h" 42 | #ifdef _MSC_VER 43 | #pragma warning( pop ) 44 | #endif 45 | 46 | #ifdef SRALLOC_DISABLE_STATS 47 | #define lequal( ... ) 48 | #endif 49 | 50 | sr_result_t 51 | unittest_alloc( srallocator_t* allocator, int size ) { 52 | sr_result_t res = sralloc_alloc_with_size( allocator, size ); 53 | memset( res.ptr, ( ( (sruintptr_t)res.ptr ) & 0xFF0 ) >> 8, res.size ); 54 | return res; 55 | } 56 | 57 | void 58 | unittest_dealloc( srallocator_t* allocator, sr_result_t res ) { 59 | for ( int i = 0; i < res.size; ++i ) { 60 | lequal( *( (char*)res.ptr + i ), (char)( ( ( (sruintptr_t)res.ptr ) & 0xFF0 ) >> 8 ) ); 61 | } 62 | 63 | sralloc_dealloc( allocator, res.ptr ); 64 | } 65 | 66 | void 67 | generic_allocator_tests( srallocator_t* allocator ) { 68 | lequal( allocator->stats.num_allocations, 0 ); 69 | lequal( allocator->stats.amount_allocated, 0 ); 70 | 71 | // Single 72 | sr_result_t pA1 = unittest_alloc( allocator, 73 ); 73 | lequal( allocator->stats.num_allocations, 1 ); 74 | unittest_dealloc( allocator, pA1 ); 75 | lequal( allocator->stats.num_allocations, 0 ); 76 | lequal( allocator->stats.amount_allocated, 0 ); 77 | 78 | // Multiple 79 | sr_result_t pB1 = unittest_alloc( allocator, 27 ); 80 | sr_result_t pB2 = unittest_alloc( allocator, 57 ); 81 | lequal( allocator->stats.num_allocations, 2 ); 82 | unittest_dealloc( allocator, pB2 ); 83 | lequal( allocator->stats.num_allocations, 1 ); 84 | unittest_dealloc( allocator, pB1 ); 85 | lequal( allocator->stats.num_allocations, 0 ); 86 | lequal( allocator->stats.amount_allocated, 0 ); 87 | 88 | // Aligned 89 | void* psC[10]; 90 | for ( int i = 0; i < 10; i++ ) { 91 | psC[i] = sralloc_alloc_aligned( allocator, i * 7 + 100, 16 ); 92 | } 93 | lequal( allocator->stats.num_allocations, 10 ); 94 | for ( int i = 0; i < 10; i++ ) { 95 | sralloc_dealloc( allocator, psC[i] ); 96 | } 97 | lequal( allocator->stats.num_allocations, 0 ); 98 | lequal( allocator->stats.amount_allocated, 0 ); 99 | 100 | // Test returned size 101 | sr_result_t psD[10]; 102 | for ( int i = 0; i < 10; i++ ) { 103 | psD[i] = sralloc_alloc_aligned_with_size( allocator, i * 7 + 100, 16 ); 104 | } 105 | for ( int i = 0; i < 10; i++ ) { 106 | memset( psD[i].ptr, i + 150, psD[i].size ); 107 | } 108 | lequal( allocator->stats.num_allocations, 10 ); 109 | for ( int i = 0; i < 10; i++ ) { 110 | sralloc_dealloc( allocator, psD[i].ptr ); 111 | } 112 | lequal( allocator->stats.num_allocations, 0 ); 113 | lequal( allocator->stats.amount_allocated, 0 ); 114 | 115 | // Test returned size 2 116 | sr_result_t psE[10]; 117 | for ( int i = 0; i < 10; i++ ) { 118 | psE[i] = unittest_alloc( allocator, i * 7 + 100 ); 119 | } 120 | lequal( allocator->stats.num_allocations, 10 ); 121 | for ( int i = 0; i < 10; i++ ) { 122 | unittest_dealloc( allocator, psE[i] ); 123 | } 124 | lequal( allocator->stats.num_allocations, 0 ); 125 | lequal( allocator->stats.amount_allocated, 0 ); 126 | } 127 | 128 | void 129 | malloc_test( void ) { 130 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 131 | generic_allocator_tests( mallocalloc ); 132 | sralloc_destroy_malloc_allocator( mallocalloc ); 133 | } 134 | 135 | void 136 | stack_test( void ) { 137 | { 138 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 139 | srallocator_t* stackalloc = sralloc_create_stack_allocator( "stack", mallocalloc, 20000 ); 140 | generic_allocator_tests( stackalloc ); 141 | sralloc_destroy_stack_allocator( stackalloc ); 142 | lequal( mallocalloc->stats.num_allocations, 0 ); 143 | lequal( mallocalloc->stats.amount_allocated, 0 ); 144 | sralloc_destroy_malloc_allocator( mallocalloc ); 145 | } 146 | { 147 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 148 | srallocator_t* stackalloc = sralloc_create_stack_allocator( "stack", mallocalloc, 20000 ); 149 | int* pA1 = SRALLOC_OBJECT( stackalloc, int ); 150 | *pA1 = 111; 151 | sralloc_stack_allocator_push_state( stackalloc ); 152 | void* pA2 = SRALLOC_BYTES( stackalloc, 100 ); 153 | (void)pA2; 154 | sralloc_stack_allocator_pop_state( stackalloc ); 155 | lequal( stackalloc->stats.num_allocations, 1 ); 156 | int* pA3 = SRALLOC_OBJECT( stackalloc, int ); 157 | *pA3 = 333; 158 | lequal( *pA1, 111 ); 159 | *pA1 = 111; 160 | lequal( *pA3, 333 ); 161 | SRALLOC_DEALLOC( stackalloc, pA3 ); 162 | SRALLOC_DEALLOC( stackalloc, pA1 ); 163 | lequal( stackalloc->stats.num_allocations, 0 ); 164 | sralloc_destroy_stack_allocator( stackalloc ); 165 | lequal( mallocalloc->stats.num_allocations, 0 ); 166 | lequal( mallocalloc->stats.amount_allocated, 0 ); 167 | sralloc_destroy_malloc_allocator( mallocalloc ); 168 | } 169 | } 170 | 171 | void 172 | proxy_test( void ) { 173 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 174 | srallocator_t* proxyalloc1 = sralloc_create_proxy_allocator( "proxy1", mallocalloc ); 175 | srallocator_t* proxyalloc2 = sralloc_create_proxy_allocator( "proxy2", mallocalloc ); 176 | generic_allocator_tests( proxyalloc1 ); 177 | generic_allocator_tests( proxyalloc2 ); 178 | srallocator_t* proxyalloc3 = sralloc_create_proxy_allocator( "proxy3", proxyalloc2 ); 179 | lequal( proxyalloc2->stats.num_allocations, 1 ); 180 | generic_allocator_tests( proxyalloc3 ); 181 | sralloc_destroy_proxy_allocator( proxyalloc3 ); 182 | sralloc_destroy_proxy_allocator( proxyalloc1 ); 183 | sralloc_destroy_proxy_allocator( proxyalloc2 ); 184 | lequal( mallocalloc->stats.num_allocations, 0 ); 185 | lequal( mallocalloc->stats.amount_allocated, 0 ); 186 | sralloc_destroy_malloc_allocator( mallocalloc ); 187 | } 188 | 189 | void 190 | end_of_page_test( void ) { 191 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 192 | srallocator_t* eopalloc = sralloc_create_end_of_page_allocator( "eop1", mallocalloc ); 193 | generic_allocator_tests( eopalloc ); 194 | sr_result_t pA1 = unittest_alloc( eopalloc, 100 ); 195 | // for ( int i = 0; i < 1000; ++i ) { 196 | // char c = pA1[i]; 197 | // pA1[i] = c + 1; 198 | // } 199 | unittest_dealloc( eopalloc, pA1 ); 200 | sralloc_destroy_end_of_page_allocator( eopalloc ); 201 | lequal( mallocalloc->stats.num_allocations, 0 ); 202 | lequal( mallocalloc->stats.amount_allocated, 0 ); 203 | sralloc_destroy_malloc_allocator( mallocalloc ); 204 | } 205 | 206 | #ifndef NO_IGDEBUG 207 | void 208 | ig_debugheap_test( void ) { 209 | srallocator_t* mallocalloc = sralloc_create_malloc_allocator( "root" ); 210 | srallocator_t* igdbgalloc = 211 | sralloc_create_ig_debugheap_allocator( "igdebugheap", mallocalloc, 2 * 1024 * 1024 ); 212 | // generic_allocator_tests( igdbgalloc ); 213 | sr_result_t pA1 = unittest_alloc( igdbgalloc, 100 ); 214 | // sr_result_t pA2 = sralloc_alloc_aligned_with_size( igdbgalloc, 100, 64 ); 215 | // *( ( (char*)pA1.ptr ) + 101 ) = 1; 216 | // *( ( (char*)pA2.ptr ) + 101 ) = 1; 217 | // for ( int i = 0; i < 1000; ++i ) { 218 | // char c = pA1[i]; 219 | // pA1[i] = c + 1; 220 | // } 221 | unittest_dealloc( igdbgalloc, pA1 ); 222 | // unittest_dealloc( igdbgalloc, pA2 ); 223 | sralloc_destroy_ig_debugheap_allocator( igdbgalloc ); 224 | lequal( mallocalloc->stats.num_allocations, 0 ); 225 | lequal( mallocalloc->stats.amount_allocated, 0 ); 226 | sralloc_destroy_malloc_allocator( mallocalloc ); 227 | } 228 | #endif 229 | 230 | int 231 | main( void ) { 232 | 233 | lrun( "malloc_allocator", malloc_test ); 234 | lrun( "stack_allocator", stack_test ); 235 | lrun( "proxy_allocator", proxy_test ); 236 | lrun( "end_of_page_allocator", end_of_page_test ); 237 | 238 | #ifndef NO_IGDEBUG 239 | lrun( "ig_debugheap_allocator", ig_debugheap_test ); 240 | #endif 241 | 242 | lresults(); 243 | 244 | #ifdef _MSC_VER 245 | if ( IsDebuggerPresent() ) { 246 | system( "pause" ); 247 | } 248 | #endif 249 | 250 | return lfails != 0; 251 | } 252 | 253 | #ifdef _MSC_VER 254 | #pragma warning( pop ) 255 | #endif 256 | -------------------------------------------------------------------------------- /examples/unittest/unittest.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {97FE62A5-44C3-4741-882F-FC515FDC8A86} 23 | Win32Proj 24 | sralloc 25 | 10.0.14393.0 26 | 27 | 28 | 29 | Application 30 | true 31 | v141 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v141 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(SolutionDir)Build\$(Platform)_$(Configuration)\ 75 | $(SolutionDir)Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 76 | 77 | 78 | true 79 | $(SolutionDir)Build\$(Platform)_$(Configuration)\ 80 | $(SolutionDir)Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 81 | 82 | 83 | false 84 | $(SolutionDir)Build\$(Platform)_$(Configuration)\ 85 | $(SolutionDir)Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 86 | 87 | 88 | false 89 | $(SolutionDir)Build\$(Platform)_$(Configuration)\ 90 | $(SolutionDir)Build\_$(ProjectName)\$(Platform)_$(Configuration)\ 91 | 92 | 93 | 94 | 95 | 96 | EnableAllWarnings 97 | Disabled 98 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 99 | true 100 | true 101 | 102 | 103 | Console 104 | true 105 | 106 | 107 | 108 | 109 | 110 | 111 | EnableAllWarnings 112 | Disabled 113 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 114 | true 115 | true 116 | 117 | 118 | Console 119 | true 120 | 121 | 122 | 123 | 124 | EnableAllWarnings 125 | 126 | 127 | MaxSpeed 128 | true 129 | true 130 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 131 | true 132 | true 133 | 134 | 135 | Console 136 | true 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | EnableAllWarnings 144 | 145 | 146 | MaxSpeed 147 | true 148 | true 149 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 150 | true 151 | true 152 | 153 | 154 | Console 155 | true 156 | true 157 | true 158 | 159 | 160 | 161 | 162 | Level3 163 | Level3 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /examples/unittest/unittest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/unittest/unittest.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sralloc.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INCLUDE_SRALLOC_H 3 | #define INCLUDE_SRALLOC_H 4 | 5 | #ifndef SRALLOC_ENABLE_WARNINGS 6 | #ifdef _MSC_VER 7 | #pragma warning( push ) 8 | #pragma warning( disable : 4820 4548 4711 ) 9 | #endif 10 | 11 | #ifdef __clang__ 12 | #pragma clang diagnostic push 13 | #endif 14 | 15 | #ifdef __GNUC__ 16 | #pragma GCC diagnostic push 17 | #pragma GCC diagnostic ignored "-Wunused-value" 18 | #endif 19 | #endif // SRALLOC_ENABLE_WARNINGS 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | typedef struct srallocator srallocator_t; 26 | 27 | #ifndef SRALLOC_TYPES 28 | #include 29 | typedef char srchar_t; 30 | typedef int srint_t; 31 | typedef unsigned int sruint_t; 32 | typedef short srshort_t; 33 | typedef uintptr_t sruintptr_t; 34 | #endif 35 | 36 | typedef struct { 37 | void* ptr; 38 | srint_t size; 39 | // srint_t offset; 40 | } sr_result_t; 41 | 42 | #if defined( SRALLOC_STATIC ) 43 | #define SRALLOC_API static 44 | #else 45 | #define SRALLOC_API extern 46 | #endif 47 | 48 | // General API 49 | SRALLOC_API void* sralloc_alloc( srallocator_t* allocator, srint_t size ); 50 | SRALLOC_API sr_result_t sralloc_alloc_with_size( srallocator_t* allocator, srint_t size ); 51 | SRALLOC_API void* sralloc_alloc_aligned( srallocator_t* allocator, srint_t size, srint_t align ); 52 | SRALLOC_API sr_result_t sralloc_alloc_aligned_with_size( srallocator_t* allocator, 53 | srint_t size, 54 | srint_t align ); 55 | SRALLOC_API void sralloc_dealloc( srallocator_t* allocator, void* ptr ); 56 | SRALLOC_API void* sralloc_allocate( srallocator_t* allocator, srint_t size, srint_t align ); 57 | 58 | // Malloc allocator (global allocator) 59 | SRALLOC_API srallocator_t* sralloc_create_malloc_allocator( const char* name ); 60 | SRALLOC_API void sralloc_destroy_malloc_allocator( srallocator_t* allocator ); 61 | 62 | // Stack allocator (or stack frame allocator) 63 | SRALLOC_API srallocator_t* 64 | sralloc_create_stack_allocator( const char* name, srallocator_t* parent, srint_t capacity ); 65 | SRALLOC_API void sralloc_destroy_stack_allocator( srallocator_t* allocator ); 66 | 67 | // Proxy allocator (for categorizing/structuring) 68 | SRALLOC_API srallocator_t* sralloc_create_proxy_allocator( const char* name, 69 | srallocator_t* parent ); 70 | SRALLOC_API void sralloc_destroy_proxy_allocator( srallocator_t* allocator ); 71 | 72 | // End-of-page allocator (for debugging write-past-eob) 73 | SRALLOC_API srallocator_t* sralloc_create_end_of_page_allocator( const char* name, 74 | srallocator_t* parent ); 75 | SRALLOC_API void sralloc_destroy_end_of_page_allocator( srallocator_t* allocator ); 76 | 77 | #ifdef SRALLOC_ENABLE_IG_DEBUGHEAP 78 | // Insomniac Games's debug heap allocator (for lots of things) 79 | // See its github page. sralloc assumes that it's .h-file has been included 80 | // prior to including sralloc.h for the implementation 81 | // https://github.com/deplinenoise/ig-debugheap 82 | SRALLOC_API srallocator_t* 83 | sralloc_create_ig_debugheap_allocator( const char* name, srallocator_t* parent, sruint_t capacity ); 84 | SRALLOC_API void sralloc_destroy_ig_debugheap_allocator( srallocator_t* allocator ); 85 | #endif 86 | 87 | // Slot allocator (for things of same size) 88 | SRALLOC_API srallocator_t* sralloc_create_slot_allocator( srallocator_t* parent, 89 | const char* name, 90 | srint_t slot_size, 91 | srint_t capacity ); 92 | SRALLOC_API void sralloc_destroy_slot_allocator( srallocator_t* allocator ); 93 | 94 | // Util API. BYTES and DEALLOC only here for consistency. 95 | #ifndef SRALLOC_ALIGNOF 96 | #define SRALLOC_ALIGNOF alignof 97 | #endif 98 | 99 | #define SRALLOC_BYTES( allocator, size ) sralloc_alloc( allocator, size ) 100 | #define SRALLOC_OBJECT( allocator, type ) ( (type*)sralloc_alloc( allocator, sizeof( type ) ) ) 101 | #define SRALLOC_ARRAY( allocator, type, length ) \ 102 | ( (type*)sralloc_alloc( allocator, sizeof( size ) * length ) ); 103 | 104 | #define SRALLOC_ALIGNED_BYTES( allocator, size, align ) \ 105 | sralloc_alloc_aligned( allocator, size, align ) 106 | #define SRALLOC_ALIGNED_OBJECT( allocator, type ) \ 107 | ( (type*)sralloc_alloc_aligned( allocator, sizeof( type ), SRALLOC_ALIGNOF( type ) ) ) 108 | #define SRALLOC_ALIGNED_ARRAY( allocator, type, length ) \ 109 | ( (type*)sralloc_alloc_aligned( allocator, sizeof( type ) * length ), SRALLOC_ALIGNOF( type ) ); 110 | 111 | #define SRALLOC_DEALLOC( allocator, ptr ) sralloc_dealloc( allocator, ptr ); 112 | 113 | #ifdef __cplusplus 114 | } 115 | #endif 116 | 117 | // Implementation 118 | #ifdef SRALLOC_IMPLEMENTATION 119 | 120 | #ifndef SRALLOC_malloc 121 | #include 122 | #define SRALLOC_malloc malloc 123 | #define SRALLOC_free free 124 | #endif 125 | 126 | #ifndef SRALLOC_assert 127 | #include 128 | #define SRALLOC_assert assert 129 | #endif 130 | 131 | #ifndef SRALLOC_memset 132 | #include 133 | #define SRALLOC_memset memset 134 | #endif 135 | 136 | #ifndef SRALLOC_memcpy 137 | #include 138 | #define SRALLOC_memcpy memcpy 139 | #endif 140 | 141 | #ifndef SRALLOC_NULL 142 | #define SRALLOC_NULL 0 143 | #endif 144 | 145 | #ifndef SRALLOC_ZERO_SIZE_PTR 146 | #define SRALLOC_ZERO_SIZE_PTR ( (void*)16 ) // kmalloc style 147 | #endif 148 | 149 | #ifndef SRALLOC_UNUSED 150 | #define SRALLOC_UNUSED( ... ) (void)( __VA_ARGS__ ) 151 | #endif 152 | 153 | #ifdef SRALLOC_DO_WRITE_DEALLOCATION_PATTERN 154 | #ifndef SRALLOC_DEALLOCATION_PATTERN 155 | #define SRALLOC_DEALLOCATION_PATTERN 0xcd 156 | #endif 157 | #define SRALLOC_WRITE_DEALLOCATION_PATTERN( ptr, size ) \ 158 | SRALLOC_memset( ptr, SRALLOC_DEALLOCATION_PATTERN, size ) 159 | #else 160 | #define SRALLOC_WRITE_DEALLOCATION_PATTERN( ptr, size ) 161 | #endif 162 | 163 | #ifndef SRALLOC_DISABLE_STATS 164 | #define SRALLOC_USE_STATS 165 | #endif 166 | 167 | #ifndef SRALLOC_DISABLE_NAMES 168 | #define SRALLOC_USE_NAMES 169 | #endif 170 | 171 | // #ifndef SRALLOC_DISABLE_TYPES 172 | // #define SRALLOC_USE_TYPES 173 | // #endif 174 | 175 | // End of page allocator config 176 | #ifndef SRALLOC_PAGE_SIZE 177 | #define SRALLOC_PAGE_SIZE 0x1000 178 | #endif 179 | 180 | #ifndef SRALLOC_PROTECT_MEMORY 181 | #if defined( _WIN32 ) 182 | #define WIN32_LEAN_AND_MEAN 183 | #include 184 | typedef DWORD srmemflag_t; 185 | #define SRALLOC_MEMPROTECT_FLAG PAGE_NOACCESS 186 | #define SRALLOC_PROTECT_MEMORY( ptr, size, protection, old_protection ) \ 187 | VirtualProtect( ptr, size, protection, old_protection ); 188 | #elif defined( __APPLE__ ) || defined( __linux__ ) 189 | #include 190 | typedef int srmemflag_t; 191 | #define SRALLOC_MEMPROTECT_FLAG PROT_NONE 192 | #define SRALLOC_PROTECT_MEMORY( ptr, size, protection, old_protection ) \ 193 | SRALLOC_UNUSED( old_protection ); \ 194 | mprotect( ptr, size, protection ); 195 | #else 196 | #define SRALLOC_PROTECT_MEMORY( ptr, size, protection, old_protection ) \ 197 | SRALLOC_UNUSED( ptr, size, protection, old_protection ); 198 | #endif // _WIN32 199 | #endif // SRALLOC_PROTECT_MEMORY 200 | 201 | typedef struct { 202 | int num_allocations; 203 | int amount_allocated; 204 | } sralloc_stats_t; 205 | 206 | typedef sr_result_t ( *sralloc_allocate_func )( srallocator_t* allocator, 207 | srint_t size, 208 | srint_t align ); 209 | typedef void ( *sralloc_deallocate_func )( srallocator_t* allocator, void* ptr ); 210 | 211 | struct srallocator { 212 | #ifdef SRALLOC_USE_NAMES 213 | const char* name; // TODO arrayify 214 | #endif 215 | // #ifdef SRALLOC_USE_TYPES 216 | // char type[16]; 217 | // #endif 218 | sralloc_allocate_func allocate_func; 219 | sralloc_deallocate_func deallocate_func; 220 | #ifdef SRALLOC_USE_STATS 221 | srallocator_t* parent; 222 | srallocator_t** children; 223 | srint_t num_children; 224 | srint_t children_capacity; 225 | sralloc_stats_t stats; 226 | #endif 227 | }; 228 | 229 | // ██╗███╗ ██╗████████╗███████╗██████╗ ███╗ ██╗ █████╗ ██╗ 230 | // ██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗████╗ ██║██╔══██╗██║ 231 | // ██║██╔██╗ ██║ ██║ █████╗ ██████╔╝██╔██╗ ██║███████║██║ 232 | // ██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══██║██║ 233 | // ██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ╚████║██║ ██║███████╗ 234 | // ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ 235 | 236 | static void 237 | sr__add_child_allocator( srallocator_t* parent, srallocator_t* child ) { 238 | SRALLOC_UNUSED( parent, child ); 239 | #ifdef SRALLOC_USE_STATS 240 | child->parent = parent; 241 | if ( parent->children_capacity > parent->num_children ) { 242 | parent->children[parent->num_children++] = child; 243 | return; 244 | } 245 | 246 | parent->children_capacity = parent->children_capacity ? parent->children_capacity * 2 : 2; 247 | 248 | void* new_children = SRALLOC_NULL; 249 | if ( parent->parent != SRALLOC_NULL ) { 250 | new_children = (srallocator_t*)sralloc_alloc( 251 | parent->parent, sizeof( parent->children ) * parent->children_capacity ); 252 | SRALLOC_memcpy( 253 | new_children, parent->children, parent->num_children * sizeof( parent->children ) ); 254 | sralloc_dealloc( parent->parent, parent->children ); 255 | parent->children = (srallocator_t**)new_children; 256 | parent->children[parent->num_children++] = child; 257 | } 258 | else { 259 | new_children = (srallocator_t*)sralloc_alloc( 260 | parent, sizeof( parent->children ) * parent->children_capacity ); 261 | SRALLOC_memcpy( 262 | new_children, parent->children, parent->num_children * sizeof( parent->children ) ); 263 | sralloc_dealloc( parent, parent->children ); 264 | parent->children = (srallocator_t**)new_children; 265 | parent->children[parent->num_children++] = child; 266 | } 267 | #endif // SRALLOC_USE_STATS 268 | } 269 | 270 | static void 271 | sr__remove_child_allocator( srallocator_t* parent, srallocator_t* child ) { 272 | SRALLOC_UNUSED( parent, child ); 273 | #ifdef SRALLOC_USE_STATS 274 | for ( srint_t i_child = 0; i_child < parent->num_children; ++i_child ) { 275 | if ( parent->children[i_child] == child ) { 276 | parent->children[i_child] = parent->children[--parent->num_children]; 277 | 278 | if ( parent->num_children == 0 ) { 279 | if ( parent->parent != SRALLOC_NULL ) { 280 | sralloc_dealloc( parent->parent, parent->children ); 281 | } 282 | else { 283 | sralloc_dealloc( parent, parent->children ); 284 | } 285 | 286 | parent->children = SRALLOC_NULL; 287 | parent->children_capacity = 0; 288 | } 289 | 290 | return; 291 | } 292 | } 293 | 294 | SRALLOC_assert( 0 ); 295 | #endif // SRALLOC_USE_STATS 296 | } 297 | 298 | static void 299 | sr__set_name( srallocator_t* allocator, const srchar_t* name ) { 300 | SRALLOC_UNUSED( allocator, name ); 301 | #ifdef SRALLOC_USE_NAMES 302 | allocator->name = name; 303 | #endif 304 | } 305 | 306 | // static void 307 | // sr__set_type( srallocator_t* allocator, const srchar_t* name ) { 308 | // SRALLOC_UNUSED( allocator, name ); 309 | // #ifdef SRALLOC_USE_NAMES 310 | // SRALLOC_memcpy( allocator->name, SRALLOC_strlen( name ), name ); 311 | // #endif 312 | // } 313 | 314 | static void* 315 | sr__ptr_to_aligned_ptr( void* ptr, srint_t align ) { 316 | if ( align == 0 ) { 317 | return ptr; 318 | } 319 | 320 | sruintptr_t offset = ( ~(sruintptr_t)ptr + 1 ) & ( align - 1 ); 321 | void* aligned_ptr = (srchar_t*)ptr + offset; 322 | return aligned_ptr; 323 | } 324 | 325 | static srchar_t* 326 | sr__aligned_ptr_after_preamble( void* ptr, srint_t preamble_size, srint_t align ) { 327 | srchar_t* after_preamble = (srchar_t*)ptr + preamble_size; 328 | return sr__ptr_to_aligned_ptr( after_preamble, align ); 329 | } 330 | 331 | static srint_t 332 | sr__ptr_diff( void* ptr1, void* ptr2 ) { 333 | return ( srint_t )( (srchar_t*)ptr1 - (srchar_t*)ptr2 ); 334 | } 335 | 336 | // █████╗ ██████╗ ██╗ 337 | // ██╔══██╗██╔══██╗██║ 338 | // ███████║██████╔╝██║ 339 | // ██╔══██║██╔═══╝ ██║ 340 | // ██║ ██║██║ ██║ 341 | // ╚═╝ ╚═╝╚═╝ ╚═╝ 342 | 343 | SRALLOC_API void* 344 | sralloc_alloc( srallocator_t* allocator, srint_t size ) { 345 | if ( size == 0 ) { 346 | return SRALLOC_ZERO_SIZE_PTR; 347 | } 348 | 349 | return allocator->allocate_func( allocator, size, 0 ).ptr; 350 | } 351 | 352 | SRALLOC_API sr_result_t 353 | sralloc_alloc_with_size( srallocator_t* allocator, srint_t size ) { 354 | if ( size == 0 ) { 355 | sr_result_t res = { SRALLOC_ZERO_SIZE_PTR, 0 }; 356 | return res; 357 | } 358 | 359 | return allocator->allocate_func( allocator, size, 0 ); 360 | } 361 | 362 | SRALLOC_API void* 363 | sralloc_alloc_aligned( srallocator_t* allocator, srint_t size, srint_t align ) { 364 | if ( size == 0 ) { 365 | return SRALLOC_ZERO_SIZE_PTR; 366 | } 367 | 368 | return allocator->allocate_func( allocator, size, align ).ptr; 369 | } 370 | 371 | SRALLOC_API sr_result_t 372 | sralloc_alloc_aligned_with_size( srallocator_t* allocator, srint_t size, srint_t align ) { 373 | if ( size == 0 ) { 374 | sr_result_t res = { SRALLOC_ZERO_SIZE_PTR, 0 }; 375 | return res; 376 | } 377 | 378 | return allocator->allocate_func( allocator, size, align ); 379 | } 380 | 381 | SRALLOC_API void 382 | sralloc_dealloc( srallocator_t* allocator, void* ptr ) { 383 | if ( ptr == SRALLOC_ZERO_SIZE_PTR ) { 384 | return; 385 | } 386 | 387 | if ( ptr == SRALLOC_NULL ) { 388 | return; 389 | } 390 | 391 | allocator->deallocate_func( allocator, ptr ); 392 | } 393 | 394 | // ███╗ ███╗ █████╗ ██╗ ██╗ ██████╗ ██████╗ 395 | // ████╗ ████║██╔══██╗██║ ██║ ██╔═══██╗██╔════╝ 396 | // ██╔████╔██║███████║██║ ██║ ██║ ██║██║ 397 | // ██║╚██╔╝██║██╔══██║██║ ██║ ██║ ██║██║ 398 | // ██║ ╚═╝ ██║██║ ██║███████╗███████╗╚██████╔╝╚██████╗ 399 | // ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝ ╚═════╝ 400 | 401 | typedef struct { 402 | srint_t offset; 403 | srint_t size; 404 | } sralloc_malloc_preamble_t; 405 | 406 | static sr_result_t 407 | sralloc_malloc_allocate( srallocator_t* allocator, srint_t wanted_size, srint_t align ) { 408 | SRALLOC_UNUSED( allocator ); 409 | srint_t preamble_size = sizeof( sralloc_malloc_preamble_t ); 410 | srint_t size = wanted_size; 411 | size += align; 412 | size += preamble_size; 413 | 414 | #ifdef SRALLOC_USE_STATS 415 | allocator->stats.amount_allocated += size; 416 | allocator->stats.num_allocations++; 417 | #endif 418 | 419 | srchar_t* unaligned_ptr = SRALLOC_malloc( size ); 420 | if ( unaligned_ptr == SRALLOC_NULL ) { 421 | sr_result_t res = { SRALLOC_NULL, 0 }; 422 | return res; 423 | } 424 | 425 | srchar_t* ptr = sr__aligned_ptr_after_preamble( unaligned_ptr, preamble_size, align ); 426 | sralloc_malloc_preamble_t* preamble = (sralloc_malloc_preamble_t*)ptr - 1; 427 | preamble->size = size; 428 | preamble->offset = sr__ptr_diff( preamble, unaligned_ptr ); 429 | 430 | sr_result_t res; 431 | res.ptr = (void*)ptr; 432 | res.size = wanted_size; 433 | return res; 434 | } 435 | 436 | static void 437 | sralloc_malloc_deallocate( srallocator_t* allocator, void* ptr ) { 438 | SRALLOC_UNUSED( allocator ); 439 | sralloc_malloc_preamble_t* preamble = (sralloc_malloc_preamble_t*)ptr - 1; 440 | srchar_t* unaligned_ptr = (srchar_t*)preamble - preamble->offset; 441 | #ifdef SRALLOC_USE_STATS 442 | allocator->stats.amount_allocated -= preamble->size; 443 | allocator->stats.num_allocations--; 444 | #endif 445 | SRALLOC_free( unaligned_ptr ); 446 | } 447 | 448 | SRALLOC_API srallocator_t* 449 | sralloc_create_malloc_allocator( const char* name ) { 450 | srallocator_t* allocator = (srallocator_t*)SRALLOC_malloc( sizeof( srallocator_t ) ); 451 | SRALLOC_memset( allocator, 0, sizeof( srallocator_t ) ); 452 | sr__set_name( allocator, name ); 453 | // sr__set_type( allocator, "malloc" ); 454 | allocator->allocate_func = sralloc_malloc_allocate; 455 | allocator->deallocate_func = sralloc_malloc_deallocate; 456 | return allocator; 457 | } 458 | 459 | SRALLOC_API void 460 | sralloc_destroy_malloc_allocator( srallocator_t* allocator ) { 461 | #ifdef SRALLOC_USE_STATS 462 | SRALLOC_assert( allocator->num_children == 0 ); 463 | SRALLOC_assert( allocator->stats.num_allocations == 0 ); 464 | SRALLOC_assert( allocator->stats.amount_allocated == 0 ); 465 | #endif 466 | SRALLOC_free( allocator ); 467 | } 468 | 469 | // ███████╗████████╗ █████╗ ██████╗██╗ ██╗ 470 | // ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ 471 | // ███████╗ ██║ ███████║██║ █████╔╝ 472 | // ╚════██║ ██║ ██╔══██║██║ ██╔═██╗ 473 | // ███████║ ██║ ██║ ██║╚██████╗██║ ██╗ 474 | // ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ 475 | 476 | typedef struct { 477 | srint_t offset; 478 | srint_t size; 479 | } sralloc_stack_preamble_t; 480 | 481 | typedef struct { 482 | void* top; 483 | #ifdef SRALLOC_USE_STATS 484 | sralloc_stats_t stats; 485 | #endif 486 | } srallocator_stack_state_t; 487 | 488 | typedef struct { 489 | void* top; 490 | void* end; 491 | srallocator_t* backing_allocator; 492 | srallocator_stack_state_t states[4]; 493 | srint_t num_states; 494 | } srallocator_stack_t; 495 | 496 | static sr_result_t 497 | sralloc_stack_allocate( srallocator_t* allocator, srint_t wanted_size, srint_t align ) { 498 | srint_t preamble_size = sizeof( sralloc_stack_preamble_t ); 499 | srint_t size = wanted_size; 500 | size += align; 501 | size += preamble_size; 502 | 503 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 504 | if ( size > sr__ptr_diff( stack_allocator->end, stack_allocator->top ) ) { 505 | sr_result_t res = { SRALLOC_NULL, 0 }; 506 | return res; 507 | } 508 | 509 | #ifdef SRALLOC_USE_STATS 510 | allocator->stats.amount_allocated += size; 511 | allocator->stats.num_allocations++; 512 | #endif 513 | 514 | void* unaligned_ptr = stack_allocator->top; 515 | srchar_t* ptr = sr__aligned_ptr_after_preamble( unaligned_ptr, preamble_size, align ); 516 | sralloc_malloc_preamble_t* preamble = (sralloc_malloc_preamble_t*)ptr - 1; 517 | preamble->size = size; 518 | preamble->offset = sr__ptr_diff( preamble, unaligned_ptr ); 519 | stack_allocator->top = ptr + size; 520 | sr_result_t res; 521 | res.ptr = (void*)( ptr ); 522 | res.size = wanted_size; 523 | return res; 524 | } 525 | 526 | static void 527 | sralloc_stack_deallocate( srallocator_t* allocator, void* ptr ) { 528 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 529 | sralloc_malloc_preamble_t* preamble = (sralloc_malloc_preamble_t*)ptr - 1; 530 | srchar_t* unaligned_ptr = (srchar_t*)preamble - preamble->offset; 531 | stack_allocator->top = unaligned_ptr; 532 | #ifdef SRALLOC_USE_STATS 533 | allocator->stats.amount_allocated -= preamble->size; 534 | allocator->stats.num_allocations--; 535 | #endif 536 | } 537 | 538 | SRALLOC_API void 539 | sralloc_stack_allocator_clear( srallocator_t* allocator ) { 540 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 541 | stack_allocator->top = stack_allocator + 1; 542 | #ifdef SRALLOC_USE_STATS 543 | allocator->stats.amount_allocated = 0; 544 | allocator->stats.num_allocations = 0; 545 | #endif 546 | } 547 | 548 | SRALLOC_API void 549 | sralloc_stack_allocator_push_state( srallocator_t* allocator ) { 550 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 551 | SRALLOC_assert( 552 | stack_allocator->num_states < 553 | ( srint_t )( sizeof( stack_allocator->states ) / sizeof( srallocator_stack_state_t ) ) ); 554 | srallocator_stack_state_t* state = &stack_allocator->states[stack_allocator->num_states++]; 555 | state->top = stack_allocator->top; 556 | #ifdef SRALLOC_USE_STATS 557 | state->stats = allocator->stats; 558 | #endif 559 | } 560 | 561 | SRALLOC_API void 562 | sralloc_stack_allocator_pop_state( srallocator_t* allocator ) { 563 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 564 | SRALLOC_assert( stack_allocator->num_states > 0 ); 565 | srallocator_stack_state_t* state = &stack_allocator->states[--stack_allocator->num_states]; 566 | stack_allocator->top = state->top; 567 | #ifdef SRALLOC_USE_STATS 568 | allocator->stats = state->stats; 569 | #endif 570 | } 571 | 572 | SRALLOC_API srallocator_t* 573 | sralloc_create_stack_allocator( const char* name, srallocator_t* parent, srint_t capacity ) { 574 | srint_t allocator_size = sizeof( srallocator_t ) + sizeof( srallocator_stack_t ) + capacity; 575 | void* memory = sralloc_alloc( parent, allocator_size ); 576 | srallocator_t* allocator = (srallocator_t*)memory; 577 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 578 | 579 | SRALLOC_memset( allocator, 0, allocator_size ); 580 | sr__add_child_allocator( parent, allocator ); 581 | sr__set_name( allocator, name ); 582 | allocator->allocate_func = sralloc_stack_allocate; 583 | allocator->deallocate_func = sralloc_stack_deallocate; 584 | stack_allocator->top = stack_allocator + 1; 585 | stack_allocator->end = ( (char*)stack_allocator->top ) + capacity; 586 | stack_allocator->num_states = 0; 587 | stack_allocator->backing_allocator = parent; 588 | return allocator; 589 | } 590 | 591 | SRALLOC_API void 592 | sralloc_destroy_stack_allocator( srallocator_t* allocator ) { 593 | #ifdef SRALLOC_USE_STATS 594 | SRALLOC_assert( allocator->stats.num_allocations == 0 ); 595 | SRALLOC_assert( allocator->stats.amount_allocated == 0 ); 596 | sr__remove_child_allocator( allocator->parent, allocator ); 597 | SRALLOC_assert( allocator->num_children == 0 ); 598 | #endif 599 | 600 | srallocator_stack_t* stack_allocator = (srallocator_stack_t*)( allocator + 1 ); 601 | SRALLOC_UNUSED( stack_allocator ); 602 | SRALLOC_assert( stack_allocator->num_states == 0 ); 603 | sralloc_dealloc( stack_allocator->backing_allocator, allocator ); 604 | } 605 | 606 | // ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗ 607 | // ██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝ 608 | // ██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝ 609 | // ██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝ 610 | // ██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║ 611 | // ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ 612 | 613 | typedef struct { 614 | srallocator_t* backing_allocator; 615 | } srallocator_proxy_t; 616 | 617 | typedef struct { 618 | srint_t size; 619 | srint_t offset; 620 | } sralloc_proxy_preamble_t; 621 | 622 | static sr_result_t 623 | sralloc_proxy_allocate( srallocator_t* allocator, srint_t wanted_size, srint_t align ) { 624 | SRALLOC_UNUSED( allocator ); 625 | srint_t preamble_size = sizeof( sralloc_proxy_preamble_t ); 626 | srint_t size = wanted_size; 627 | size += align; 628 | size += preamble_size; 629 | 630 | #ifdef SRALLOC_USE_STATS 631 | allocator->stats.amount_allocated += size; 632 | allocator->stats.num_allocations++; 633 | #endif 634 | 635 | srallocator_proxy_t* proxy_allocator = (srallocator_proxy_t*)( allocator + 1 ); 636 | srchar_t* unaligned_ptr = SRALLOC_BYTES( proxy_allocator->backing_allocator, size ); 637 | if ( unaligned_ptr == SRALLOC_NULL ) { 638 | sr_result_t res = { SRALLOC_NULL, 0 }; 639 | return res; 640 | } 641 | 642 | srchar_t* ptr = sr__aligned_ptr_after_preamble( unaligned_ptr, preamble_size, align ); 643 | sralloc_proxy_preamble_t* preamble = (sralloc_proxy_preamble_t*)ptr - 1; 644 | preamble->size = size; 645 | preamble->offset = sr__ptr_diff( preamble, unaligned_ptr ); 646 | 647 | sr_result_t res; 648 | res.ptr = (void*)ptr; 649 | res.size = wanted_size; 650 | return res; 651 | } 652 | 653 | static void 654 | sralloc_proxy_deallocate( srallocator_t* allocator, void* ptr ) { 655 | SRALLOC_UNUSED( allocator ); 656 | sralloc_proxy_preamble_t* preamble = (sralloc_proxy_preamble_t*)ptr - 1; 657 | srchar_t* unaligned_ptr = (srchar_t*)preamble - preamble->offset; 658 | #ifdef SRALLOC_USE_STATS 659 | allocator->stats.amount_allocated -= preamble->size; 660 | allocator->stats.num_allocations--; 661 | #endif 662 | srallocator_proxy_t* proxy_allocator = (srallocator_proxy_t*)( allocator + 1 ); 663 | SRALLOC_DEALLOC( proxy_allocator->backing_allocator, unaligned_ptr ); 664 | } 665 | 666 | SRALLOC_API srallocator_t* 667 | sralloc_create_proxy_allocator( const char* name, srallocator_t* parent ) { 668 | #ifdef SRALLOC_DISABLE_PROXY 669 | return parent; 670 | #endif 671 | 672 | srint_t allocator_size = sizeof( srallocator_t ) + sizeof( srallocator_proxy_t ); 673 | void* memory = sralloc_alloc( parent, allocator_size ); 674 | srallocator_t* allocator = (srallocator_t*)memory; 675 | srallocator_proxy_t* proxy_allocator = (srallocator_proxy_t*)( allocator + 1 ); 676 | 677 | SRALLOC_memset( allocator, 0, allocator_size ); 678 | sr__add_child_allocator( parent, allocator ); 679 | sr__set_name( allocator, name ); 680 | allocator->allocate_func = sralloc_proxy_allocate; 681 | allocator->deallocate_func = sralloc_proxy_deallocate; 682 | proxy_allocator->backing_allocator = parent; 683 | 684 | return allocator; 685 | } 686 | 687 | SRALLOC_API void 688 | sralloc_destroy_proxy_allocator( srallocator_t* allocator ) { 689 | #ifdef SRALLOC_DISABLE_PROXY 690 | return; 691 | #endif 692 | #ifdef SRALLOC_USE_STATS 693 | sr__remove_child_allocator( allocator->parent, allocator ); 694 | SRALLOC_assert( allocator->num_children == 0 ); 695 | SRALLOC_assert( allocator->stats.num_allocations == 0 ); 696 | SRALLOC_assert( allocator->stats.amount_allocated == 0 ); 697 | #endif 698 | srallocator_proxy_t* proxy_allocator = (srallocator_proxy_t*)( allocator + 1 ); 699 | SRALLOC_DEALLOC( proxy_allocator->backing_allocator, allocator ); 700 | } 701 | 702 | // ███████╗███╗ ██╗██████╗ ██████╗ ███████╗ ██████╗ █████╗ ██████╗ ███████╗ 703 | // ██╔════╝████╗ ██║██╔══██╗ ██╔═══██╗██╔════╝ ██╔══██╗██╔══██╗██╔════╝ ██╔════╝ 704 | // █████╗ ██╔██╗ ██║██║ ██║ ██║ ██║█████╗ ██████╔╝███████║██║ ███╗█████╗ 705 | // ██╔══╝ ██║╚██╗██║██║ ██║ ██║ ██║██╔══╝ ██╔═══╝ ██╔══██║██║ ██║██╔══╝ 706 | // ███████╗██║ ╚████║██████╔╝███████╗╚██████╔╝██║███████╗██║ ██║ ██║╚██████╔╝███████╗ 707 | // ╚══════╝╚═╝ ╚═══╝╚═════╝ ╚══════╝ ╚═════╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ 708 | 709 | typedef struct { 710 | srallocator_t* backing_allocator; 711 | } srallocator_end_of_page_t; 712 | 713 | typedef struct { 714 | srint_t size; 715 | srint_t offset; 716 | srmemflag_t initial_protection; 717 | srchar_t* page_ptr; 718 | } sralloc_end_of_page_preamble_t; 719 | 720 | static sr_result_t 721 | sralloc_end_of_page_allocate( srallocator_t* allocator, srint_t wanted_size, srint_t align ) { 722 | SRALLOC_UNUSED( allocator ); 723 | srint_t preamble_size = sizeof( sralloc_end_of_page_preamble_t ); 724 | srint_t size_to_allocate = wanted_size; 725 | size_to_allocate += align; 726 | size_to_allocate += preamble_size; 727 | size_to_allocate += SRALLOC_PAGE_SIZE * 2; 728 | 729 | #ifdef SRALLOC_USE_STATS 730 | allocator->stats.amount_allocated += size_to_allocate; 731 | allocator->stats.num_allocations++; 732 | #endif 733 | 734 | srallocator_end_of_page_t* end_of_page_allocator = 735 | (srallocator_end_of_page_t*)( allocator + 1 ); 736 | srchar_t* unaligned_ptr = 737 | SRALLOC_BYTES( end_of_page_allocator->backing_allocator, size_to_allocate ); 738 | if ( unaligned_ptr == SRALLOC_NULL ) { 739 | sr_result_t res = { SRALLOC_NULL, 0 }; 740 | return res; 741 | } 742 | 743 | srchar_t* page_ptr = sr__ptr_to_aligned_ptr( 744 | unaligned_ptr + wanted_size + align + preamble_size, SRALLOC_PAGE_SIZE ); 745 | 746 | srchar_t* ptr = page_ptr - wanted_size - align; 747 | sralloc_end_of_page_preamble_t* preamble = (sralloc_end_of_page_preamble_t*)ptr - 1; 748 | preamble->size = size_to_allocate; 749 | preamble->offset = sr__ptr_diff( preamble, unaligned_ptr ); 750 | preamble->page_ptr = page_ptr; 751 | 752 | // SRALLOC_assert( sr__ptr_to_aligned_ptr( ptr, align ) == ptr ); 753 | SRALLOC_PROTECT_MEMORY( 754 | page_ptr, SRALLOC_PAGE_SIZE, SRALLOC_MEMPROTECT_FLAG, &preamble->initial_protection ); 755 | 756 | sr_result_t res; 757 | res.ptr = (void*)ptr; 758 | res.size = wanted_size; 759 | return res; 760 | } 761 | 762 | static void 763 | sralloc_end_of_page_deallocate( srallocator_t* allocator, void* ptr ) { 764 | SRALLOC_UNUSED( allocator ); 765 | sralloc_end_of_page_preamble_t* preamble = (sralloc_end_of_page_preamble_t*)ptr - 1; 766 | srchar_t* unaligned_ptr = (srchar_t*)preamble - preamble->offset; 767 | #ifdef SRALLOC_USE_STATS 768 | allocator->stats.amount_allocated -= preamble->size; 769 | allocator->stats.num_allocations--; 770 | #endif 771 | srchar_t* page_ptr = preamble->page_ptr; 772 | srmemflag_t current_protection; 773 | SRALLOC_PROTECT_MEMORY( 774 | page_ptr, SRALLOC_PAGE_SIZE, preamble->initial_protection, ¤t_protection ); 775 | 776 | srallocator_end_of_page_t* end_of_page_allocator = 777 | (srallocator_end_of_page_t*)( allocator + 1 ); 778 | SRALLOC_DEALLOC( end_of_page_allocator->backing_allocator, unaligned_ptr ); 779 | } 780 | 781 | SRALLOC_API srallocator_t* 782 | sralloc_create_end_of_page_allocator( const char* name, srallocator_t* parent ) { 783 | #ifdef SRALLOC_DISABLE_end_of_page 784 | return parent; 785 | #endif 786 | 787 | srint_t allocator_size = sizeof( srallocator_t ) + sizeof( srallocator_end_of_page_t ); 788 | void* memory = sralloc_alloc( parent, allocator_size ); 789 | srallocator_t* allocator = (srallocator_t*)memory; 790 | srallocator_end_of_page_t* end_of_page_allocator = 791 | (srallocator_end_of_page_t*)( allocator + 1 ); 792 | 793 | SRALLOC_memset( allocator, 0, allocator_size ); 794 | sr__add_child_allocator( parent, allocator ); 795 | sr__set_name( allocator, name ); 796 | allocator->allocate_func = sralloc_end_of_page_allocate; 797 | allocator->deallocate_func = sralloc_end_of_page_deallocate; 798 | end_of_page_allocator->backing_allocator = parent; 799 | 800 | return allocator; 801 | } 802 | 803 | SRALLOC_API void 804 | sralloc_destroy_end_of_page_allocator( srallocator_t* allocator ) { 805 | #ifdef SRALLOC_USE_STATS 806 | sr__remove_child_allocator( allocator->parent, allocator ); 807 | SRALLOC_assert( allocator->num_children == 0 ); 808 | SRALLOC_assert( allocator->stats.num_allocations == 0 ); 809 | SRALLOC_assert( allocator->stats.amount_allocated == 0 ); 810 | #endif 811 | srallocator_end_of_page_t* end_of_page_allocator = 812 | (srallocator_end_of_page_t*)( allocator + 1 ); 813 | SRALLOC_DEALLOC( end_of_page_allocator->backing_allocator, allocator ); 814 | } 815 | 816 | // ██╗ ██████╗ ██████╗ ███████╗██████╗ ██╗ ██╗ ██████╗ ██╗ ██╗███████╗ █████╗ ██████╗ 817 | // ██║██╔════╝ ██╔══██╗██╔════╝██╔══██╗██║ ██║██╔════╝ ██║ ██║██╔════╝██╔══██╗██╔══██╗ 818 | // ██║██║ ███╗ ██║ ██║█████╗ ██████╔╝██║ ██║██║ ███╗███████║█████╗ ███████║██████╔╝ 819 | // ██║██║ ██║ ██║ ██║██╔══╝ ██╔══██╗██║ ██║██║ ██║██╔══██║██╔══╝ ██╔══██║██╔═══╝ 820 | // ██║╚██████╔╝███████╗██████╔╝███████╗██████╔╝╚██████╔╝╚██████╔╝██║ ██║███████╗██║ ██║██║ 821 | // ╚═╝ ╚═════╝ ╚══════╝╚═════╝ ╚══════╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ 822 | 823 | #ifdef SRALLOC_ENABLE_IG_DEBUGHEAP 824 | 825 | typedef struct { 826 | DebugHeap* debugheap; 827 | } srallocator_ig_debugheap_t; 828 | 829 | typedef struct { 830 | srint_t size; 831 | srint_t offset; 832 | srmemflag_t initial_protection; 833 | srchar_t* page_ptr; 834 | } sralloc_ig_debugheap_preamble_t; 835 | 836 | static sr_result_t 837 | sralloc_ig_debugheap_allocate( srallocator_t* allocator, srint_t wanted_size, srint_t align ) { 838 | srallocator_ig_debugheap_t* ig_debugheap_allocator = 839 | (srallocator_ig_debugheap_t*)( allocator + 1 ); 840 | 841 | void* ptr = 842 | DebugHeapAllocate( ig_debugheap_allocator->debugheap, (size_t)wanted_size, (size_t)align ); 843 | 844 | srint_t actual_size = (srint_t)DebugHeapGetAllocSize( ig_debugheap_allocator->debugheap, ptr ); 845 | 846 | #ifdef SRALLOC_USE_STATS 847 | allocator->stats.amount_allocated += actual_size; 848 | allocator->stats.num_allocations += 1; 849 | #endif 850 | 851 | sr_result_t res; 852 | res.ptr = (void*)ptr; 853 | res.size = actual_size; 854 | return res; 855 | } 856 | 857 | static void 858 | sralloc_ig_debugheap_deallocate( srallocator_t* allocator, void* ptr ) { 859 | srallocator_ig_debugheap_t* ig_debugheap_allocator = 860 | (srallocator_ig_debugheap_t*)( allocator + 1 ); 861 | 862 | #ifdef SRALLOC_USE_STATS 863 | srint_t actual_size = (srint_t)DebugHeapGetAllocSize( ig_debugheap_allocator->debugheap, ptr ); 864 | allocator->stats.amount_allocated -= actual_size; 865 | allocator->stats.num_allocations -= 1; 866 | #endif 867 | 868 | DebugHeapFree( ig_debugheap_allocator->debugheap, ptr ); 869 | } 870 | 871 | SRALLOC_API srallocator_t* 872 | sralloc_create_ig_debugheap_allocator( const char* name, 873 | srallocator_t* parent, 874 | sruint_t capacity ) { 875 | DebugHeap* debugheap = DebugHeapInit( (size_t)capacity ); 876 | 877 | srint_t allocator_size = sizeof( srallocator_t ) + sizeof( srallocator_ig_debugheap_t ); 878 | void* memory = DebugHeapAllocate( debugheap, allocator_size, 64u ); 879 | srallocator_t* allocator = (srallocator_t*)memory; 880 | srallocator_ig_debugheap_t* ig_debugheap_allocator = 881 | (srallocator_ig_debugheap_t*)( allocator + 1 ); 882 | 883 | SRALLOC_memset( allocator, 0, allocator_size ); 884 | sr__add_child_allocator( parent, allocator ); 885 | sr__set_name( allocator, name ); 886 | allocator->allocate_func = sralloc_ig_debugheap_allocate; 887 | allocator->deallocate_func = sralloc_ig_debugheap_deallocate; 888 | ig_debugheap_allocator->debugheap = debugheap; 889 | 890 | return allocator; 891 | } 892 | 893 | SRALLOC_API void 894 | sralloc_destroy_ig_debugheap_allocator( srallocator_t* allocator ) { 895 | #ifdef SRALLOC_USE_STATS 896 | sr__remove_child_allocator( allocator->parent, allocator ); 897 | SRALLOC_assert( allocator->num_children == 0 ); 898 | SRALLOC_assert( allocator->stats.num_allocations == 0 ); 899 | SRALLOC_assert( allocator->stats.amount_allocated == 0 ); 900 | #endif 901 | srallocator_ig_debugheap_t* ig_debugheap_allocator = 902 | (srallocator_ig_debugheap_t*)( allocator + 1 ); 903 | 904 | DebugHeap* debugheap = ig_debugheap_allocator->debugheap; 905 | DebugHeapFree( ig_debugheap_allocator->debugheap, allocator ); 906 | DebugHeapDestroy( debugheap ); 907 | } 908 | 909 | #endif // SRALLOC_ENABLE_IG_DEBUGHEAP 910 | 911 | /* 912 | 913 | // ███████╗██╗ ██████╗ ████████╗ 914 | // ██╔════╝██║ ██╔═══██╗╚══██╔══╝ 915 | // ███████╗██║ ██║ ██║ ██║ 916 | // ╚════██║██║ ██║ ██║ ██║ 917 | // ███████║███████╗╚██████╔╝ ██║ 918 | // ╚══════╝╚══════╝ ╚═════╝ ╚═╝ 919 | 920 | typedef struct { 921 | 922 | } srallocator_slot_t; 923 | 924 | void* 925 | sralloc_slot_allocate( srallocator_t* allocator, srint_t size ) { 926 | srallocator_slot_t* slot_allocator = (srallocator_slot_t*)( allocator + 1 ); 927 | srshort_t slot = slot_allocator->free_slot; 928 | if ( slot_allocator->free_slot != 0 ) { 929 | srshort_t next_slot_offset = slot * slot_allocator->slot_size; 930 | char* next_free_slot = (char*)slot_allocator->slots + next_slot_offset; 931 | slot_allocator->free_slot = *(srshort_t*)next_free_slot; 932 | } 933 | else { 934 | SRALLOC_assert( slot_allocator->num_slots < slot_allocator->capacity ); 935 | slot = slot_allocator->num_slots++; 936 | } 937 | 938 | allocator->stats.amount_allocated += size; 939 | allocator->stats.num_allocations++; 940 | 941 | srshort_t slot_offset = slot * slot_allocator->slot_size; 942 | char* slot = (char*)slot_allocator->slots + next_slot_offset; 943 | void* ptr = (void*)slot; 944 | return ptr; 945 | } 946 | 947 | void 948 | sralloc_slot_deallocate( srallocator_t* allocator, void* ptr ) { 949 | srallocator_slot_t* slot_allocator = (srallocator_slot_t*)( allocator + 1 ); 950 | srint_t* slot_ptr = (srint_t*)ptr; 951 | if ( slot_allocator->free_slot == 0 ) { 952 | slot_allocator->free_slot = (slot_ptr - slot_allocator->slots; 953 | } 954 | 955 | slot_ptr--; 956 | allocator->stats.amount_allocated -= *slot_ptr; 957 | allocator->stats.num_allocations--; 958 | return free( slot_ptr ); 959 | } 960 | 961 | SRALLOC_API srallocator_t* 962 | sralloc_create_slot_allocator( srallocator_t* parent, 963 | const char* name, 964 | srint_t slot_size, 965 | srint_t capacity ) { 966 | 967 | srint_t allocator_size = 968 | sizeof( srallocator_t ) + sizeof( srallocator_slot_t ) + slot_size * capacity; 969 | srallocator_t* allocator = parent->allocate( parent, allocator_size ); 970 | srallocator_slot_t* slot_allocator = (srallocator_slot_t*)( allocator + 1 ); 971 | 972 | SRALLOC_memset( allocator, 0, sizeof( srallocator_t ) ); 973 | allocator->name = name; 974 | allocator->parent = parent; 975 | allocator->allocate = sralloc_slot_allocate; 976 | allocator->deallocate = sralloc_slot_deallocate; 977 | slot_allocator->num_slots = 0; 978 | slot_allocator->capacity = capacity; 979 | slot_allocator->slot_size = slot_size; 980 | slot_allocator->slots = (void*)slot_allocator + 1; 981 | return allocator; 982 | }; 983 | 984 | SRALLOC_API void 985 | sralloc_destroy_slot_allocator( srallocator_t* allocator ) { 986 | SRALLOC_assert( allocator->num_children == 0 ); 987 | SRALLOC_assert( allocator->stats.num_allocations == 0 ); 988 | SRALLOC_assert( allocator->stats.amount_allocated == 0 ); 989 | allocator->parent->deallocate( allocator->parent, allocator ); 990 | } 991 | END OF SLOT 992 | */ 993 | #endif // SRALLOC_IMPLEMENTATION 994 | 995 | #if defined( __cplusplus ) && !defined( SRALLOC_NO_CLASSES ) 996 | 997 | namespace sralloc { 998 | class Allocator { 999 | public: 1000 | // clang-format off 1001 | static Allocator create_malloc_allocator( const char* name ) 1002 | { return Allocator( sralloc_create_malloc_allocator( name ) ); } 1003 | static Allocator create_stack_allocator( const char* name, srallocator_t* parent, srint_t capacity ) 1004 | { return Allocator( sralloc_create_stack_allocator( name, parent, capacity ) ); } 1005 | 1006 | void* allocate( srint_t size ) 1007 | { return sralloc_alloc( _allocator, size ); } 1008 | 1009 | sr_result_t allocate_with_size( srint_t size ) 1010 | { sralloc_alloc_with_size( _allocator, size ); } 1011 | 1012 | void* allocate_aligned( srint_t size, srint_t align ) 1013 | { sralloc_alloc_aligned( _allocator, size ); } 1014 | 1015 | sr_result_t allocate_aligned_with_size( srint_t size, srint_t align ) 1016 | { sralloc_alloc_aligned_with_size( _allocator, size ); } 1017 | 1018 | template 1019 | T* allocate() 1020 | { return static_cast( allocate( sizeof( T ) ) ); } 1021 | 1022 | template 1023 | T* allocate_aligned( srint_t align ) 1024 | { return static_cast( allocate_aligned( sizeof( T ), align ) ); } 1025 | 1026 | void deallocate( void* ptr ) 1027 | { sralloc_dealloc( _allocator, ptr ); } 1028 | // clang-format on 1029 | 1030 | ~Allocator() { 1031 | switch ( _type ) { 1032 | case AllocatorMalloc: 1033 | sralloc_destroy_malloc_allocator( _allocator ); 1034 | break; 1035 | case AllocatorStack: 1036 | sralloc_destroy_stack_allocator( _allocator ); 1037 | break; 1038 | } 1039 | } 1040 | 1041 | private: 1042 | Allocator( srallocator_t* allocator ) 1043 | : _allocator( allocator ) {} 1044 | 1045 | Allocator( const Allocator& ) = delete; 1046 | void operator=( const Allocator& ) = delete; 1047 | 1048 | enum AllocatorType { 1049 | AllocatorInvalid = 0, 1050 | AllocatorMalloc, 1051 | AllocatorStack, 1052 | }; 1053 | 1054 | srallocator_t* _allocator; 1055 | AllocatorType _type; 1056 | }; 1057 | 1058 | } // namespace sralloc 1059 | #endif //__cplusplus && SRALLOC_NO_CLASSES 1060 | 1061 | #ifndef SRALLOC_ENABLE_WARNINGS 1062 | #ifdef _MSC_VER 1063 | #pragma warning( pop ) 1064 | #endif 1065 | 1066 | #ifdef __clang__ 1067 | #pragma clang diagnostic pop 1068 | #endif 1069 | 1070 | #ifdef __GNUC__ 1071 | #pragma GCC diagnostic pop 1072 | #endif 1073 | #endif // DEBUGINATOR_ENABLE_WARNINGS 1074 | 1075 | #endif // INCLUDE_SRALLOC_H 1076 | --------------------------------------------------------------------------------