├── .gitignore ├── LICENSE ├── README.md ├── array.h ├── binary_heap.h ├── binary_heap.md ├── flic.h ├── list.h ├── list.md ├── map.h ├── map.md ├── memory_buffer.h ├── memzone.h ├── memzone.md ├── queue.h ├── queue.md ├── set.h ├── set.md ├── stack.h ├── stack.md ├── tests ├── array_test.c ├── binary_heap_test.c ├── build-gcc-rpi.sh ├── build-gcc-win32.bat ├── build-gcc-win64.bat ├── canvas2d_test.c ├── flic_test.c ├── list_test.c ├── map_test.c ├── memory_buffer_test.c ├── memzone_test.c ├── queue_test.c ├── set_test.c ├── stack_test.c ├── stb_image_resize.h ├── stb_image_write.h ├── wave_writer_test.c └── xmi2mid.c └── wave_writer.h /.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 | 54 | out.wav 55 | /tests/build/ 56 | /shl.sublime-project 57 | /shl.sublime-workspace 58 | .vscode/ 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alejandro Coto Gutiérrez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Single Header Libraries 2 | 3 | These are single header libraries that I use in my code, much in the style of Sean Barret stb libraries. 4 | 5 | * list.h: A generic list implementation (see [list.md](https://github.com/acoto87/shl/blob/master/list.md)). 6 | * stack.h: A generic stack implementation (see [stack.md](https://github.com/acoto87/shl/blob/master/stack.md)). 7 | * queue.h: A generic queue implementation (see [queue.md](https://github.com/acoto87/shl/blob/master/queue.md)). 8 | * binary_heap.h: A generic binary heap implementation (see [binary_heap.md](https://github.com/acoto87/shl/blob/master/binary_heap.md)) 9 | * map.h: A generic hash-table implementation (see [map.md](https://github.com/acoto87/shl/blob/master/map.md)). 10 | * set.h: A generic hash-set implementation (see [set.md](https://github.com/acoto87/shl/blob/master/set.md)) 11 | * array.h: A generic helper to work with multi-dimentional arrays. 12 | * wave_writer.h: Contains functionalities to write .wav files. 13 | * memory_buffer.h: An in-memory buffer implementation with random access. 14 | * flic.h: Contains functionalities to read FLIC files. It's a C port of the C++ implementation by David Capello's Aseprite FLIC Library: https://github.com/aseprite/flic 15 | * memzone.h: A simple memory allocator. (see [memzone.md](https://github.com/acoto87/shl/blob/master/memzone.md)) 16 | 17 | See the tests/*_tests.c files to see how to use them. 18 | 19 | These are work in progress, and I'm using it in my games, so use at your own risk. 20 | 21 | Any tips/suggestions are welcome. 22 | -------------------------------------------------------------------------------- /array.h: -------------------------------------------------------------------------------- 1 | /* 2 | array.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed stack with push, pop and peek operations. 27 | */ 28 | #ifndef SHL_ARRAY_H 29 | #define SHL_ARRAY_H 30 | 31 | #include 32 | #include 33 | 34 | #define shlDeclareCreateArray(prefix, itemType) \ 35 | itemType** prefix ## CreateArray(int32_t n, int32_t m); 36 | 37 | #define shlDefineCreateArray(prefix, itemType) \ 38 | itemType** prefix ## CreateArray(int32_t n, int32_t m) \ 39 | { \ 40 | itemType* values = (itemType*)calloc(m * n, sizeof(itemType)); \ 41 | itemType** rows = (itemType**)malloc(n * sizeof(itemType*)); \ 42 | for (int i = 0; i < n; ++i) \ 43 | { \ 44 | rows[i] = values + i * m; \ 45 | } \ 46 | return rows; \ 47 | } 48 | 49 | #define shlDeclareFreeArray(prefix, itemType) \ 50 | void prefix ## FreeArray(itemType** arr); 51 | 52 | #define shlDefineFreeArray(prefix, itemType) \ 53 | void prefix ## FreeArray(itemType** arr) \ 54 | { \ 55 | free(*arr); \ 56 | free(arr); \ 57 | } 58 | 59 | #endif // SHL_ARRAY_H 60 | -------------------------------------------------------------------------------- /binary_heap.h: -------------------------------------------------------------------------------- 1 | /* 2 | binary_heap.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed heap of objects. 27 | */ 28 | #ifndef SHL_HEAP_H 29 | #define SHL_HEAP_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define shlDeclareBinaryHeap(typeName, itemType) \ 37 | typedef struct \ 38 | { \ 39 | itemType defaultValue; \ 40 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 41 | int32_t (*compareFn)(const itemType item1, const itemType item2); \ 42 | void (*freeFn)(itemType item); \ 43 | } typeName ## Options; \ 44 | \ 45 | typedef struct \ 46 | { \ 47 | uint32_t count; \ 48 | uint32_t capacity; \ 49 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 50 | int32_t (*compareFn)(const itemType item1, const itemType item2); \ 51 | void (*freeFn)(itemType item); \ 52 | itemType defaultValue; \ 53 | itemType* items; \ 54 | } typeName; \ 55 | \ 56 | void typeName ## Init(typeName* heap, typeName ## Options options); \ 57 | void typeName ## Free(typeName* heap); \ 58 | void typeName ## Push(typeName* heap, itemType value); \ 59 | itemType typeName ## Peek(typeName* heap); \ 60 | itemType typeName ## Pop(typeName* heap); \ 61 | int32_t typeName ## IndexOf(typeName* heap, itemType value); \ 62 | bool typeName ## Contains(typeName* heap, itemType value); \ 63 | void typeName ## Update(typeName* heap, int32_t index, itemType newValue); \ 64 | void typeName ## Clear(typeName* heap); 65 | 66 | #define shlDefineBinaryHeap(typeName, itemType) \ 67 | void typeName ## __resize(typeName* heap, int32_t minSize) \ 68 | { \ 69 | heap->capacity = heap->capacity << 1; \ 70 | if (heap->capacity < minSize) \ 71 | heap->capacity = minSize; \ 72 | \ 73 | heap->items = (itemType *)realloc(heap->items, heap->capacity * sizeof(itemType)); \ 74 | } \ 75 | \ 76 | void typeName ## __heapUp(typeName* heap, int32_t index) \ 77 | { \ 78 | int32_t pindex = (index - 1) >> 1; \ 79 | while (index > 0 && heap->compareFn(heap->items[index], heap->items[pindex]) < 0) \ 80 | { \ 81 | itemType tmp = heap->items[index]; \ 82 | heap->items[index] = heap->items[pindex]; \ 83 | heap->items[pindex] = tmp; \ 84 | \ 85 | index = pindex; \ 86 | pindex = (index - 1) >> 1; \ 87 | } \ 88 | } \ 89 | \ 90 | void typeName ## __heapDown(typeName* heap, int32_t index) \ 91 | { \ 92 | while (index < heap->count) \ 93 | { \ 94 | itemType value = heap->items[index]; \ 95 | \ 96 | int32_t leftIndex = 2 * index + 1; \ 97 | if (leftIndex >= heap->count) \ 98 | break; \ 99 | \ 100 | int32_t minIndex = leftIndex; \ 101 | itemType minValue = heap->items[minIndex]; \ 102 | \ 103 | int32_t rightIndex = 2 * index + 2; \ 104 | if (rightIndex < heap->count) \ 105 | { \ 106 | itemType rightValue = heap->items[rightIndex]; \ 107 | if (heap->compareFn(rightValue, minValue) < 0) \ 108 | { \ 109 | minIndex = rightIndex; \ 110 | minValue = rightValue; \ 111 | } \ 112 | } \ 113 | \ 114 | if (heap->compareFn(minValue, value) >= 0) \ 115 | break; \ 116 | \ 117 | itemType tmp = heap->items[index]; \ 118 | heap->items[index] = heap->items[minIndex]; \ 119 | heap->items[minIndex] = tmp; \ 120 | \ 121 | index = minIndex; \ 122 | } \ 123 | } \ 124 | \ 125 | void typeName ## Init(typeName* heap, typeName ## Options options) \ 126 | { \ 127 | heap->capacity = 8; \ 128 | heap->defaultValue = options.defaultValue; \ 129 | heap->equalsFn = options.equalsFn; \ 130 | heap->compareFn = options.compareFn; \ 131 | heap->freeFn = options.freeFn; \ 132 | heap->count = 0; \ 133 | heap->items = (itemType *)malloc(heap->capacity * sizeof(itemType)); \ 134 | } \ 135 | \ 136 | void typeName ## Free(typeName* heap) \ 137 | { \ 138 | if (!heap->items) \ 139 | return; \ 140 | \ 141 | typeName ## Clear(heap); \ 142 | \ 143 | free(heap->items); \ 144 | heap->items = 0; \ 145 | } \ 146 | \ 147 | void typeName ## Push(typeName* heap, itemType value) \ 148 | { \ 149 | if (!heap->items) \ 150 | return; \ 151 | \ 152 | if (heap->count + 1 >= heap->capacity) \ 153 | typeName ## __resize(heap, heap->count + 1); \ 154 | \ 155 | int32_t index = heap->count; \ 156 | heap->items[index] = value; \ 157 | \ 158 | typeName ## __heapUp(heap, index); \ 159 | heap->count++; \ 160 | } \ 161 | \ 162 | itemType typeName ## Peek(typeName* heap) \ 163 | { \ 164 | if (!heap->items) \ 165 | return heap->defaultValue; \ 166 | \ 167 | if (heap->count == 0) \ 168 | return heap->defaultValue; \ 169 | \ 170 | return heap->items[0]; \ 171 | } \ 172 | \ 173 | itemType typeName ## Pop(typeName* heap) \ 174 | { \ 175 | if (!heap->items) \ 176 | return heap->defaultValue; \ 177 | \ 178 | if (heap->count == 0) \ 179 | return heap->defaultValue; \ 180 | \ 181 | itemType returnValue = heap->items[0]; \ 182 | \ 183 | heap->items[0] = heap->items[heap->count - 1]; \ 184 | heap->count--; \ 185 | typeName ## __heapDown(heap, 0); \ 186 | \ 187 | return returnValue; \ 188 | } \ 189 | \ 190 | int32_t typeName ## IndexOf(typeName* heap, itemType value) \ 191 | { \ 192 | if (!heap->items) \ 193 | return -1; \ 194 | \ 195 | if (!heap->equalsFn) \ 196 | return -1; \ 197 | \ 198 | for(int32_t i = 0; i < heap->count; i++) \ 199 | { \ 200 | if (heap->equalsFn(heap->items[i], value)) \ 201 | return i; \ 202 | } \ 203 | \ 204 | return -1; \ 205 | } \ 206 | \ 207 | bool typeName ## Contains(typeName* heap, itemType value) \ 208 | { \ 209 | return typeName ## IndexOf(heap, value) >= 0; \ 210 | } \ 211 | \ 212 | void typeName ## Update(typeName* heap, int32_t index, itemType newValue) \ 213 | { \ 214 | if (!heap->items) \ 215 | return; \ 216 | \ 217 | if (!heap->compareFn) \ 218 | return; \ 219 | \ 220 | if (index < 0 || index >= heap->count) \ 221 | return; \ 222 | \ 223 | itemType oldValue = heap->items[index]; \ 224 | heap->items[index] = newValue; \ 225 | int32_t cmpValue = heap->compareFn(newValue, oldValue); \ 226 | if (cmpValue < 0) \ 227 | typeName ## __heapUp(heap, index); \ 228 | else if (cmpValue > 0) \ 229 | typeName ## __heapDown(heap, index); \ 230 | } \ 231 | \ 232 | void typeName ## Clear(typeName* heap) \ 233 | { \ 234 | if (!heap->items) \ 235 | return; \ 236 | \ 237 | if (heap->freeFn) \ 238 | { \ 239 | for(int32_t i = 0; i < heap->count; i++) \ 240 | heap->freeFn(heap->items[i]); \ 241 | } \ 242 | \ 243 | heap->count = 0; \ 244 | } 245 | 246 | #endif // SHL_HEAP_H -------------------------------------------------------------------------------- /binary_heap.md: -------------------------------------------------------------------------------- 1 | # Binary heap structure 2 | _This project is based on [GenericMap](https://github.com/mystborn/GenericMap) by mystborn, so there similar function names and structures_ 3 | 4 | A binary heap of objects. 5 | 6 | ## Defining a Type 7 | Use the macro `shlDeclareBinaryHeap` to generate the type and function definitions. It has the following arguments: 8 | 9 | | Argument | Description | 10 | | --- | --- | 11 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 12 | | `itemType` | The type of the binary heap elements. | 13 | 14 | Use the macro `shlDefineBinaryHeap` to generate the function implementations. 15 | 16 | | Argument | Description | 17 | | --- | --- | 18 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 19 | | `itemType` | The type of the binary heap elements. | 20 | 21 | ```c 22 | #include "binary_heap.h" 23 | 24 | bool intEquals(const int x, const int y) 25 | { 26 | return x == y; 27 | } 28 | 29 | shlDeclareList(IntHeap, int) 30 | shlDefineList(IntHeap, int) 31 | ``` 32 | 33 | The list structure allows the following operations (all functions all prefixed with _typeName_): 34 | 35 | | Function | Description | Return type | 36 | | --- | --- | --- | 37 | | `Init`(_typeName_* heap, _typeName_ Options options) | Initializes the data needed for the binary heap. | void | 38 | | `Free`(_typeName_* heap) | Frees the data used by the binary heap. It doesn't free the binary heap itself. | void | 39 | | `Push`(_typeName_* heap, _itemType_ value) | Push an element to the heap. | void | 40 | | `Peek`(_typeName_* heap) | Gets the top of the heap without removing it. | _itemType_ | 41 | | `Pop`(_typeName_* heap) | Remove the top of the heap. | _itemType_ | 42 | | `IndexOf`(_typeName_* heap, _itemType_ value) | Returns the index of an element on the heap | int32_t | 43 | | `Contains`(_typeName_* heap, _itemType_ value) | Return `true` if an object is contained in the heap. | bool | 44 | | `Update`(_typeName_* heap, int32_t index, _itemType_ newValue) | Update the value associated to the element at the `index` position. This may cause reordering the heap. | void | 45 | | `Clear`(_typeName_* heap) | Clear the heap, freeing every element if a `freeFn` was provided. Doesn't free the heap itself. | void | 46 | 47 | ## Options 48 | 49 | Each definition of a binary heap declare a struct _typeName_ Options that is used to initialize the binary heap. The struct has the following members: 50 | 51 | | Name | Type | Description | 52 | | --- | --- | --- | 53 | | `compareFn` | int32_t (*)(const _itemType_, const _itemType_) | A pointer to a function that takes two elements, and returns a value `> 0` if the first element is greater than the second, returns a value `< 0` if the first element is less than the second, and returns a value `= 0` if the two elements are equal. | 54 | | `equalsFn` | bool (*)(const _itemType_, const _itemType_) | _(optional)_ A pointer to a function that takes two elements, and returns `true` if the elements are equals, and returns `false` otherwise. If no `equalsFn` is provided then the operations `IndexOf` always returns -1 and `Contains` always return `false`. | 55 | | `freeFn` | void (*)(_itemType_) | _(optional)_ A pointer to a function that takes an element and free it. If no `freeFn` is provided, then the operation `Clear` and `Free` doesn't free the elements and the user of the binary heap is the responsible for free the elements. | 56 | | `defaultValue` | _itemType_ | The value to return when you apply the `Pop` operation and the binary heap is empty. | 57 | 58 | Example: 59 | ```c 60 | #include 61 | 62 | #include "binary_heap.h" 63 | 64 | int32_t intCompare(const int x, const int y) 65 | { 66 | return x - y; 67 | } 68 | 69 | bool intEquals(const int x, const int y) 70 | { 71 | return x == y; 72 | } 73 | 74 | shlDeclareBinaryHeap(IntHeap, int) 75 | shlDefineBinaryHeap(IntHeap, int) 76 | 77 | int main() 78 | { 79 | IntHeapOptions options = (IntHeapOptions){0}; 80 | options.defaultValue = 0; 81 | options.compareFn = intCompare; 82 | options.equalsFn = intEquals; 83 | 84 | IntHeap heap; 85 | IntHeapInit(&heap, options); 86 | 87 | for (int i = 100; i >= 0; i--) 88 | IntHeapPush(&heap, i); 89 | 90 | // print all the numbers in the heap in increasing order 91 | while (heap.count > 0) 92 | { 93 | int value = IntHeapPop(&heap); 94 | printf("%d\n", value); 95 | } 96 | 97 | return 0; 98 | } 99 | ``` -------------------------------------------------------------------------------- /flic.h: -------------------------------------------------------------------------------- 1 | /* 2 | flic.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This implementation is a C port of the following C++ implementation by David Capello 27 | Aseprite FLIC Library: https://github.com/aseprite/flic 28 | */ 29 | #ifndef SHL_FLIC_H 30 | #define SHL_FLIC_H 31 | 32 | #include 33 | #include 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #define FLI_MAGIC_NUMBER 0xAF11 40 | #define FLC_MAGIC_NUMBER 0xAF12 41 | #define FLI_FRAME_MAGIC_NUMBER 0xF1FA 42 | #define FLI_COLOR_256_CHUNK 4 43 | #define FLI_DELTA_CHUNK 7 44 | #define FLI_COLOR_64_CHUNK 11 45 | #define FLI_LC_CHUNK 12 46 | #define FLI_BLACK_CHUNK 13 47 | #define FLI_BRUN_CHUNK 15 48 | #define FLI_COPY_CHUNK 16 49 | #define FLI_COLORS_SIZE 768 // 256 * 3 50 | #define FLI_PXL_CHANGE 0x100 51 | #define FLI_PXL_INDEX 0xFF 52 | 53 | typedef struct _Flic { 54 | uint16_t frames; 55 | uint16_t width; 56 | uint16_t height; 57 | uint16_t speed; 58 | uint32_t oframe1; 59 | uint32_t oframe2; 60 | 61 | FILE* file; 62 | uint32_t currentFrame; 63 | } Flic; 64 | 65 | typedef struct _FlicFrame { 66 | uint16_t* pixels; 67 | uint32_t rowStride; 68 | uint8_t colors[FLI_COLORS_SIZE]; 69 | } FlicFrame; 70 | 71 | bool flicOpen(Flic* flic, const char* filename); 72 | bool flicReadFrame(Flic* flic, FlicFrame* frame); 73 | void flicMakeImage(Flic* flic, FlicFrame* frame, uint8_t* image); 74 | void flicClose(Flic* flic); 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | #ifdef SHL_FLIC_IMPLEMENTATION 81 | 82 | static inline uint8_t flic__read8(FILE* file) 83 | { 84 | return fgetc(file); 85 | } 86 | 87 | static inline void flic__write8(FILE* file, uint8_t value) 88 | { 89 | fputc(value, file); 90 | } 91 | 92 | static inline uint16_t flic__read16(FILE* file) 93 | { 94 | uint8_t b1 = flic__read8(file); 95 | if (b1 == EOF) return 0; 96 | uint8_t b2 = flic__read8(file); 97 | if (b2 == EOF) return 0; 98 | 99 | return (b2 << 8) | b1; 100 | } 101 | 102 | static inline uint32_t flic__read32(FILE* file) 103 | { 104 | uint8_t b1 = flic__read8(file); 105 | if (b1 == EOF) return 0; 106 | uint8_t b2 = flic__read8(file); 107 | if (b2 == EOF) return 0; 108 | uint8_t b3 = flic__read8(file); 109 | if (b3 == EOF) return 0; 110 | uint8_t b4 = flic__read8(file); 111 | if (b4 == EOF) return 0; 112 | 113 | return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; 114 | } 115 | 116 | static inline size_t flic__tell(FILE* file) 117 | { 118 | return ftell(file); 119 | } 120 | 121 | static inline void flic__seek(FILE* file, size_t pos) 122 | { 123 | fseek(file, pos, SEEK_SET); 124 | } 125 | 126 | static void flic__readBlackChunk(Flic* flic, FlicFrame* frame) 127 | { 128 | memset(frame->pixels, FLI_PXL_CHANGE, frame->rowStride * flic->height); 129 | } 130 | 131 | static void flic__readCopyChunk(Flic* flic, FlicFrame* frame) 132 | { 133 | for (int32_t y = 0; y < flic->height; ++y) 134 | { 135 | uint16_t* row = frame->pixels + frame->rowStride * y; 136 | for (int32_t x = 0; x < flic->width; ++x) 137 | row[x] = flic__read8(flic->file) | FLI_PXL_CHANGE; 138 | } 139 | } 140 | 141 | static void flic__readColorChunk(Flic* flic, FlicFrame* frame, bool is64ColorMap) 142 | { 143 | uint16_t npackets = flic__read16(flic->file); 144 | 145 | uint8_t i = 0; 146 | while (npackets--) 147 | { 148 | i += flic__read8(flic->file); // Colors to skip 149 | 150 | uint16_t colors = (uint16_t)flic__read8(flic->file); 151 | if (colors == 0) 152 | colors = FLI_COLORS_SIZE / 3; 153 | 154 | for (int32_t j = 0; j < colors; ++j) 155 | { 156 | uint8_t r = flic__read8(flic->file); 157 | uint8_t g = flic__read8(flic->file); 158 | uint8_t b = flic__read8(flic->file); 159 | 160 | if (is64ColorMap) 161 | { 162 | r = (uint8_t)(255 * ((float)r / 63)); 163 | g = (uint8_t)(255 * ((float)g / 63)); 164 | b = (uint8_t)(255 * ((float)b / 63)); 165 | } 166 | 167 | frame->colors[(i + j) * 3 + 0] = r; 168 | frame->colors[(i + j) * 3 + 1] = g; 169 | frame->colors[(i + j) * 3 + 2] = b; 170 | } 171 | } 172 | } 173 | 174 | static void flic__readBrunChunk(Flic* flic, FlicFrame* frame) 175 | { 176 | for (int32_t y = 0; y < flic->height; ++y) 177 | { 178 | uint16_t* row = frame->pixels + frame->rowStride * y; 179 | 180 | int32_t x = 0; 181 | flic__read8(flic->file); // Ignore number of packets (we read until x == m_width) 182 | 183 | while (x < flic->width) 184 | { 185 | int8_t count = (int8_t)flic__read8(flic->file); 186 | if (count >= 0) 187 | { 188 | uint8_t color = flic__read8(flic->file); 189 | while (count-- && x < flic->width) 190 | row[x++] = color | FLI_PXL_CHANGE; 191 | } 192 | else 193 | { 194 | count = -count; 195 | while (count--) 196 | row[x++] = flic__read8(flic->file) | FLI_PXL_CHANGE; 197 | } 198 | } 199 | } 200 | } 201 | 202 | static void flic__readLcChunk(Flic* flic, FlicFrame* frame) 203 | { 204 | uint16_t skipLines = flic__read16(flic->file); 205 | uint16_t nlines = flic__read16(flic->file); 206 | 207 | for (int32_t y = skipLines; y < skipLines + nlines; ++y) 208 | { 209 | uint16_t* row = frame->pixels + frame->rowStride * y; 210 | 211 | int32_t x = 0; 212 | uint8_t npackets = flic__read8(flic->file); 213 | while (npackets-- && x < flic->width) 214 | { 215 | uint8_t skip = flic__read8(flic->file); 216 | 217 | x += skip; 218 | 219 | int8_t count = (int8_t)flic__read8(flic->file); 220 | if (count >= 0) 221 | { 222 | while (count--) 223 | row[x++] = flic__read8(flic->file) | FLI_PXL_CHANGE; 224 | } 225 | else 226 | { 227 | count = -count; 228 | 229 | uint8_t color = flic__read8(flic->file); 230 | while (count-- && x < flic->width) 231 | row[x++] = color | FLI_PXL_CHANGE; 232 | } 233 | } 234 | } 235 | } 236 | 237 | static void flic__readDeltaChunk(Flic* flic, FlicFrame* frame) 238 | { 239 | uint16_t nlines = flic__read16(flic->file); 240 | int32_t y = 0; 241 | 242 | while (nlines--) 243 | { 244 | int16_t word = (int16_t)flic__read16(flic->file); 245 | while (word < 0) 246 | { 247 | if (word & 0x4000) // Has bit 14 (0x4000) 248 | { 249 | y += -word; // Skip lines 250 | } 251 | else // Only last pixel has changed 252 | { 253 | if (y >= 0 && y < flic->height) 254 | { 255 | uint16_t* row = frame->pixels + frame->rowStride * y; 256 | row[flic->width - 1] = (word & 0xff) | FLI_PXL_CHANGE; 257 | } 258 | 259 | ++y; 260 | 261 | if (nlines-- == 0) 262 | return; 263 | } 264 | 265 | word = (int16_t)flic__read16(flic->file); 266 | } 267 | 268 | uint16_t npackets = (uint16_t)word; 269 | 270 | int32_t x = 0; 271 | 272 | while (npackets--) 273 | { 274 | x += flic__read8(flic->file); // Skip pixels 275 | 276 | int8_t count = (int8_t)flic__read8(flic->file); // Number of words 277 | 278 | uint16_t* row = frame->pixels + frame->rowStride * y; 279 | 280 | if (count >= 0) 281 | { 282 | while (count-- && x < flic->width) 283 | { 284 | uint8_t color1 = flic__read8(flic->file); 285 | uint8_t color2 = flic__read8(flic->file); 286 | 287 | row[x++] = color1 | FLI_PXL_CHANGE; 288 | 289 | if (x < flic->width) 290 | row[x++] = color2 | FLI_PXL_CHANGE; 291 | } 292 | } 293 | else 294 | { 295 | count = -count; 296 | 297 | uint8_t color1 = flic__read8(flic->file); 298 | uint8_t color2 = flic__read8(flic->file); 299 | 300 | while (count-- && x < flic->width) 301 | { 302 | row[x++] = color1 | FLI_PXL_CHANGE; 303 | 304 | if (x < flic->width) 305 | row[x++] = color2 | FLI_PXL_CHANGE; 306 | } 307 | } 308 | } 309 | 310 | ++y; 311 | } 312 | } 313 | 314 | static void flic__readChunk(Flic* flic, FlicFrame* frame) 315 | { 316 | uint32_t chunkStartPos = flic__tell(flic->file); 317 | uint32_t chunkSize = flic__read32(flic->file); 318 | uint16_t type = flic__read16(flic->file); 319 | 320 | switch (type) { 321 | case FLI_COLOR_256_CHUNK: 322 | flic__readColorChunk(flic, frame, false); 323 | break; 324 | case FLI_DELTA_CHUNK: 325 | flic__readDeltaChunk(flic, frame); 326 | break; 327 | case FLI_COLOR_64_CHUNK: 328 | flic__readColorChunk(flic, frame, true); 329 | break; 330 | case FLI_LC_CHUNK: 331 | flic__readLcChunk(flic, frame); 332 | break; 333 | case FLI_BLACK_CHUNK: 334 | flic__readBlackChunk(flic, frame); 335 | break; 336 | case FLI_BRUN_CHUNK: 337 | flic__readBrunChunk(flic, frame); 338 | break; 339 | case FLI_COPY_CHUNK: 340 | flic__readCopyChunk(flic, frame); 341 | break; 342 | default: 343 | // Ignore all other kind of chunks 344 | break; 345 | } 346 | 347 | flic__seek(flic->file, chunkStartPos + chunkSize); 348 | } 349 | 350 | bool flicOpen(Flic* flic, const char* filename) 351 | { 352 | memset(flic, 0, sizeof(Flic)); 353 | 354 | flic->file = fopen(filename, "rb"); 355 | if (!flic->file) 356 | return false; 357 | 358 | flic__read32(flic->file); // file size 359 | 360 | uint16_t magic = flic__read16(flic->file); 361 | if (magic != FLI_MAGIC_NUMBER && magic != FLC_MAGIC_NUMBER) 362 | return false; 363 | 364 | flic->frames = flic__read16(flic->file); 365 | flic->width = flic__read16(flic->file); 366 | flic->height = flic__read16(flic->file); 367 | 368 | flic__read16(flic->file); // Color depth (it is interpreted as 8bpp anyway) 369 | flic__read16(flic->file); // Skip flags 370 | 371 | flic->speed = flic__read32(flic->file); 372 | 373 | if (magic == FLI_MAGIC_NUMBER) 374 | { 375 | if (flic->speed == 0) 376 | flic->speed = 70; 377 | else 378 | flic->speed = 1000 * flic->speed / 70; 379 | } 380 | 381 | if (magic == FLC_MAGIC_NUMBER) 382 | { 383 | // Offset to the first and second frame header values 384 | flic__seek(flic->file, 80); 385 | 386 | flic->oframe1 = flic__read32(flic->file); 387 | flic->oframe2 = flic__read32(flic->file); 388 | } 389 | 390 | if (flic->width == 0) flic->width = 320; 391 | if (flic->height == 0) flic->width = 200; 392 | 393 | // Skip padding 394 | flic__seek(flic->file, 128); 395 | return true; 396 | } 397 | 398 | void flicClose(Flic* flic) 399 | { 400 | fclose(flic->file); 401 | } 402 | 403 | bool flicReadFrame(Flic* flic, FlicFrame* frame) 404 | { 405 | switch (flic->currentFrame) 406 | { 407 | case 0: 408 | { 409 | if (flic->oframe1) 410 | flic__seek(flic->file, flic->oframe1); 411 | 412 | break; 413 | } 414 | 415 | case 1: 416 | { 417 | if (flic->oframe2) 418 | flic__seek(flic->file, flic->oframe2); 419 | 420 | break; 421 | } 422 | } 423 | 424 | uint32_t frameStartPos = flic__tell(flic->file); 425 | uint32_t frameSize = flic__read32(flic->file); 426 | 427 | uint16_t magic = flic__read16(flic->file); 428 | if (magic != FLI_FRAME_MAGIC_NUMBER) 429 | return false; 430 | 431 | uint16_t chunks = flic__read16(flic->file); 432 | for (int32_t i = 0; i < 8; ++i) // Padding 433 | flic__read8(flic->file); 434 | 435 | for (int32_t i = 0; i < chunks; ++i) 436 | flic__readChunk(flic, frame); 437 | 438 | flic__seek(flic->file, frameStartPos + frameSize); 439 | flic->currentFrame++; 440 | return true; 441 | } 442 | 443 | void flicMakeImage(Flic* flic, FlicFrame* frame, uint8_t* image) 444 | { 445 | for (int32_t k = 0; k < flic->width * flic->height; k++) 446 | { 447 | if (frame->pixels[k] & FLI_PXL_CHANGE) 448 | { 449 | uint8_t index = frame->pixels[k] & FLI_PXL_INDEX; 450 | image[k * 3 + 0] = frame->colors[index * 3 + 0]; 451 | image[k * 3 + 1] = frame->colors[index * 3 + 1]; 452 | image[k * 3 + 2] = frame->colors[index * 3 + 2]; 453 | } 454 | } 455 | } 456 | 457 | #endif // SHL_FLIC_IMPLEMENTATION 458 | #endif // SHL_FLIC_H -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /* 2 | list.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed list of objects that can be accessed by index. 27 | Provides methods to search, sort, and manipulate lists. 28 | */ 29 | #ifndef SHL_LIST_H 30 | #define SHL_LIST_H 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #define shlDeclareList(typeName, itemType) \ 38 | typedef struct \ 39 | { \ 40 | itemType defaultValue; \ 41 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 42 | void (*freeFn)(itemType item); \ 43 | } typeName ## Options; \ 44 | \ 45 | typedef struct \ 46 | { \ 47 | uint32_t count; \ 48 | uint32_t capacity; \ 49 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 50 | void (*freeFn)(itemType item); \ 51 | itemType defaultValue; \ 52 | itemType* items; \ 53 | } typeName; \ 54 | \ 55 | void typeName ## Init(typeName* list, typeName ## Options options); \ 56 | void typeName ## Free(typeName* list); \ 57 | void typeName ## Add(typeName* list, itemType value); \ 58 | void typeName ## AddRange(typeName* list, int32_t count, itemType value[]); \ 59 | void typeName ## Insert(typeName* list, int32_t index, itemType value); \ 60 | void typeName ## InsertRange(typeName* list, int32_t index, int32_t count, itemType values[]); \ 61 | int32_t typeName ## IndexOf(typeName* list, itemType value); \ 62 | itemType typeName ## Get(typeName* list, int32_t index); \ 63 | void typeName ## Set(typeName* list, int32_t index, itemType value); \ 64 | bool typeName ## Contains(typeName* list, itemType value); \ 65 | void typeName ## Remove(typeName* list, itemType value); \ 66 | void typeName ## RemoveAt(typeName* list, int32_t index); \ 67 | void typeName ## RemoveAtRange(typeName* list, int32_t index, int32_t count); \ 68 | void typeName ## Clear(typeName* list); \ 69 | void typeName ## Reverse(typeName* list); \ 70 | void typeName ## Sort(typeName* list, int32_t (*compareFn)(const itemType item1, const itemType item2)); \ 71 | void typeName ## CopyTo(typeName* list, itemType array[], int32_t index); \ 72 | itemType* typeName ## ToArray(typeName* list); \ 73 | 74 | #define shlDefineList(typeName, itemType) \ 75 | void typeName ## __resize(typeName* list, int32_t minSize) \ 76 | { \ 77 | uint32_t oldCapacity = list->capacity; \ 78 | \ 79 | list->capacity = oldCapacity << 1; \ 80 | if (list->capacity < minSize) \ 81 | list->capacity = minSize; \ 82 | \ 83 | list->items = (itemType *)realloc(list->items, list->capacity * sizeof(itemType)); \ 84 | } \ 85 | \ 86 | void typeName ## __qsort(typeName* list, int32_t left, int32_t right, int32_t (*compareFn)(const itemType item1, const itemType item2)) \ 87 | { \ 88 | if (left >= right) \ 89 | return; \ 90 | \ 91 | int32_t middle = left + ((right - left) >> 1); \ 92 | itemType p = list->items[middle]; \ 93 | \ 94 | int32_t i = left - 1; \ 95 | int32_t j = right + 1; \ 96 | \ 97 | while (i < j) \ 98 | { \ 99 | do { i++; } while (compareFn(list->items[i], p) < 0); \ 100 | do { j--; } while (compareFn(list->items[j], p) > 0); \ 101 | \ 102 | if (i >= j) \ 103 | break; \ 104 | \ 105 | itemType tmp = list->items[i]; \ 106 | list->items[i] = list->items[j]; \ 107 | list->items[j] = tmp; \ 108 | } \ 109 | \ 110 | typeName ## __qsort(list, left, j, compareFn); \ 111 | typeName ## __qsort(list, j + 1, right, compareFn); \ 112 | } \ 113 | \ 114 | void typeName ## Init(typeName* list, typeName ## Options options) \ 115 | { \ 116 | list->defaultValue = options.defaultValue; \ 117 | list->equalsFn = options.equalsFn; \ 118 | list->freeFn = options.freeFn; \ 119 | list->capacity = 8; \ 120 | list->count = 0; \ 121 | list->items = (itemType *)malloc(list->capacity * sizeof(itemType)); \ 122 | } \ 123 | \ 124 | void typeName ## Free(typeName* list) \ 125 | { \ 126 | if (!list->items) \ 127 | return; \ 128 | \ 129 | typeName ## Clear(list); \ 130 | \ 131 | free(list->items); \ 132 | list->items = 0; \ 133 | } \ 134 | \ 135 | void typeName ## InsertRange(typeName* list, int32_t index, int32_t count, itemType values[]) \ 136 | { \ 137 | if (!list->items) \ 138 | return; \ 139 | \ 140 | if (index < 0 || index > list->count) \ 141 | return; \ 142 | \ 143 | if (list->count + count >= list->capacity) \ 144 | typeName ## __resize(list, list->count + count); \ 145 | \ 146 | memmove(list->items + index + count, list->items + index, (list->count - index) * sizeof(itemType)); \ 147 | memcpy(list->items + index, values, count * sizeof(itemType)); \ 148 | list->count += count; \ 149 | } \ 150 | \ 151 | void typeName ## Insert(typeName* list, int32_t index, itemType value) \ 152 | { \ 153 | typeName ## InsertRange(list, index, 1, &value); \ 154 | } \ 155 | \ 156 | void typeName ## Add(typeName* list, itemType value) \ 157 | { \ 158 | typeName ## Insert(list, list->count, value); \ 159 | } \ 160 | \ 161 | void typeName ## AddRange(typeName* list, int32_t count, itemType values[]) \ 162 | { \ 163 | typeName ## InsertRange(list, list->count, count, values); \ 164 | } \ 165 | \ 166 | int32_t typeName ## IndexOf(typeName* list, itemType value) \ 167 | { \ 168 | if (!list->items) \ 169 | return -1; \ 170 | \ 171 | if (!list->equalsFn) \ 172 | return -1; \ 173 | \ 174 | for(int32_t i = 0; i < list->count; i++) \ 175 | { \ 176 | if (list->equalsFn(list->items[i], value)) \ 177 | return i; \ 178 | } \ 179 | \ 180 | return -1; \ 181 | } \ 182 | \ 183 | bool typeName ## Contains(typeName* list, itemType value) \ 184 | { \ 185 | return typeName ## IndexOf(list, value) >= 0; \ 186 | } \ 187 | \ 188 | itemType typeName ## Get(typeName* list, int32_t index) \ 189 | { \ 190 | if (!list->items) \ 191 | return list->defaultValue; \ 192 | \ 193 | if (index < 0 || index >= list->count) \ 194 | return list->defaultValue; \ 195 | \ 196 | return list->items[index]; \ 197 | } \ 198 | \ 199 | void typeName ## Set(typeName *list, int32_t index, itemType value) \ 200 | { \ 201 | if (!list->items) \ 202 | return; \ 203 | \ 204 | if (index < 0 || index >= list->count) \ 205 | return; \ 206 | \ 207 | itemType currentValue = list->items[index]; \ 208 | if (list->freeFn) \ 209 | list->freeFn(currentValue); \ 210 | \ 211 | list->items[index] = value; \ 212 | } \ 213 | \ 214 | void typeName ## RemoveAtRange(typeName *list, int32_t index, int32_t count) \ 215 | { \ 216 | if (!list->items) \ 217 | return; \ 218 | \ 219 | if (index < 0 || index >= list->count) \ 220 | return; \ 221 | \ 222 | if (index + count > list->count) \ 223 | return; \ 224 | \ 225 | if (list->freeFn) \ 226 | { \ 227 | for(int32_t i = 0; i < count; i++) \ 228 | list->freeFn(list->items[index + i]); \ 229 | } \ 230 | \ 231 | memmove(list->items + index, list->items + index + count, (list->count - index - count) * sizeof(itemType)); \ 232 | list->count -= count; \ 233 | } \ 234 | \ 235 | void typeName ## RemoveAt(typeName *list, int32_t index) \ 236 | { \ 237 | typeName ## RemoveAtRange(list, index, 1); \ 238 | } \ 239 | \ 240 | void typeName ## Remove(typeName *list, itemType value) \ 241 | { \ 242 | int32_t index = typeName ## IndexOf(list, value); \ 243 | typeName ## RemoveAt(list, index); \ 244 | } \ 245 | \ 246 | void typeName ## Clear(typeName* list) \ 247 | { \ 248 | if (!list->items) \ 249 | return; \ 250 | \ 251 | if (list->freeFn) \ 252 | { \ 253 | for(int32_t i = 0; i < list->count; i++) \ 254 | list->freeFn(list->items[i]); \ 255 | } \ 256 | \ 257 | list->count = 0; \ 258 | } \ 259 | \ 260 | void typeName ## Reverse(typeName *list) \ 261 | { \ 262 | if (!list->items) \ 263 | return; \ 264 | \ 265 | int32_t count = list->count; \ 266 | for(int32_t i = 0; i < count / 2; i++) \ 267 | { \ 268 | itemType tmp = list->items[i]; \ 269 | list->items[i] = list->items[count - i - 1]; \ 270 | list->items[count - i - 1] = tmp; \ 271 | } \ 272 | } \ 273 | void typeName ## Sort(typeName* list, int32_t (*compareFn)(const itemType item1, const itemType item2)) \ 274 | { \ 275 | typeName ## __qsort(list, 0, list->count - 1, compareFn); \ 276 | } \ 277 | \ 278 | void typeName ## CopyTo(typeName* list, itemType array[], int32_t index) \ 279 | { \ 280 | if (!list->items) \ 281 | return; \ 282 | \ 283 | if (index >= 0) \ 284 | memcpy(array + index, list->items, list->count * sizeof(itemType)); \ 285 | } \ 286 | \ 287 | itemType* typeName ## ToArray(typeName* list) \ 288 | { \ 289 | if (!list->items) \ 290 | return 0; \ 291 | \ 292 | itemType* array = (itemType*)malloc(list->count * sizeof(itemType)); \ 293 | memcpy(array, list->items, list->count * sizeof(itemType)); \ 294 | return array; \ 295 | } 296 | 297 | #endif // SHL_LIST_H -------------------------------------------------------------------------------- /list.md: -------------------------------------------------------------------------------- 1 | # List structure 2 | _This project is based on [GenericMap](https://github.com/mystborn/GenericMap) by mystborn, so there similar function names and structures_ 3 | 4 | Represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort, and manipulate lists. 5 | 6 | ## Defining a Type 7 | Use the macro `shlDeclareList` to generate the type and function definitions. It has the following arguments: 8 | 9 | | Argument | Description | 10 | | --- | --- | 11 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 12 | | `itemType` | The type of the list elements. | 13 | 14 | Use the macro `shlDefineList` to generate the function implementations. 15 | 16 | | Argument | Description | 17 | | --- | --- | 18 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 19 | | `itemType` | The type of the list elements. | 20 | 21 | ```c 22 | #include "list.h" 23 | 24 | shlDeclareList(IntList, int) 25 | shlDefineList(IntList, int) 26 | ``` 27 | 28 | The list structure allows the following operations (all functions all prefixed with _typeName_): 29 | 30 | | Function | Description | Return type | 31 | | --- | --- | --- | 32 | | `Init`(_typeName_* list, _typeName_ Options options) | Initializes the data needed for the list. | void | 33 | | `Free`(_typeName_* list) | Frees the data used by the list. It doesn't free the list itself. | void | 34 | | `Add`(_typeName_* list, _itemType_ value) | Add an element at the end of the list. | void | 35 | | `AddRange`(_typeName_* list, int32_t count, itemType values[]) | Add a collection of elements at the end of the list. | void | 36 | | `Insert`(_typeName_* list, int32_t index, itemType value) | Insert an element at the `index` position in the list. | void | 37 | | `InsertRange`(_typeName_* list, int32_t index, int32_t count, itemType values[]) | Insert a collection of elements at the `index` position in the list. | void | 38 | | `IndexOf`(_typeName_* list, _itemType_ value) | Gets the index of the first occurrence in the list of an element. | int32_t | 39 | | `Get`(_typeName_* list, int32_t index) | Gets the value in the list at the `index` position. This function does check bounds of the list. If you don't want the list to check bounds when accesing elements you can use directly `list->items[index]`. | _typeName_ | 40 | | `Set`(_typeName_* list, int32_t index, _itemType_ value) | Sets the value in the list at the `index` position. This function does check bounds of the list. If you don't want the list to check bounds when accesing elements you can assign directly `list->items[index] = value`. This function returns the element previously in the `index` position. | _typeName_ | 41 | | `Contains`(_typeName_* list, _itemType_ value) | Return `true` if an object is contained in the list. | bool | 42 | | `Remove`(_typeName_* list, _itemType_ value) | Remove the first occurrence of an element in the list. This function shift all the remaining elements on index to the left. | void | 43 | | `RemoveAt`(_typeName_* list, int32_t index) | Remove the element at the position `index`. This function shift all the remaining elements on index to the left. | void | 44 | | `RemoveAtRange`(_typeName_* list, int32_t index, int32_t count) | Remove `count` elements from the position `index`. This function shift all the remaining elements on index to the left. | void | 45 | | `Clear`(_typeName_* list) | Clear the list, freeing every element if a `freeFn` was provided. Doesn't free the list itself. | void | 46 | | `Reverse`(_typeName_* list) | Reverse the list. | void | 47 | | `Sort`(_typeName_* list, int32_t (*compareFn)(const _itemType_ item1, const _itemType_ item2)) | Sort the list using the comparing function `compareFn`. This function must receive two elements `item1` and `item2` from the list and must return a value `< 0` if `item1 < item2`, a value `> 0` if `item1 > item2` and a value `= 0` if `item1 == item2` | void | 48 | | `CopyTo`(_typeName_* list, _itemType_ array[], int32_t index) | Copy the elements of the list to `array` from the `index` position. The caller should make sure that array is big enough to fit the entire list. | void | 49 | | `ToArray`(_typeName_* list) | Returns an array with all the elements of the list. | _itemType_* | 50 | 51 | ## Options 52 | 53 | Each definition of a list declare a struct _typeName_ Options that is used to initialize the list. The struct has the following members: 54 | 55 | | Name | Type | Description | 56 | | --- | --- | --- | 57 | | `equalsFn` | bool (*)(const _itemType_, const _itemType_) | _(optional)_ A pointer to a function that takes two elements, and returns `true` if the elements are equals, and returns `false` otherwise. If no `equalsFn` is provided then the operations `IndexOf` always return `-1`, `Contains` always return `false` and `Remove` doesn't do anything. | 58 | | `freeFn` | void (*)(_itemType_) | _(optional)_ A pointer to a function that takes an element and free it. If no `freeFn` is provided, then the operations `Remove`, `RemoveAt`, `RemoveAtRange`, `Clear` and `Free` doesn't free the elements and the user of the list is the responsible for free the elements. | 59 | | `defaultValue` | _itemType_ | The value to return when you try to access an element that doesn't exist. | 60 | 61 | Example: 62 | ```c 63 | #include 64 | 65 | #include "list.h" 66 | 67 | bool intEquals(const int x, const int y) 68 | { 69 | return x == y; 70 | } 71 | 72 | shlDeclareList(IntList, int) 73 | shlDefineList(IntList, int) 74 | 75 | int main() 76 | { 77 | IntListOptions options = (IntListOptions){0}; 78 | options.defaultValue = 0; 79 | options.equalsFn = intEquals; 80 | 81 | IntList list; 82 | IntListInit(&list, options); 83 | 84 | for (int i = 0; i < 100; i++) 85 | IntListAdd(&list, i); 86 | 87 | // squares all the values in the list 88 | for (int i = 0; i < list.count; i++) 89 | { 90 | int v = IntListGet(&list, i); 91 | IntListSet(&list, i, v * v); 92 | } 93 | 94 | for (int i = 0; i < list.count; i++) 95 | printf("Element at index %d is %d\n", i, IntListGet(&list, i)); 96 | 97 | return 0; 98 | } 99 | ``` -------------------------------------------------------------------------------- /map.h: -------------------------------------------------------------------------------- 1 | /* 2 | map.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed list of objects that can be accessed by index. 27 | Provides methods to search, sort, and manipulate lists. 28 | */ 29 | 30 | /* 31 | * This implementation of the macro is a variant of: https://github.com/mystborn/GenericMap 32 | * to make a closed implementation of the map data structure, where each collision is resolved 33 | * by keeping the index of the next element in the array of cells, and not by merely iterate 34 | * until we find an empty cell. 35 | * 36 | * A detailed explanation of the hash function can be found here: 37 | * https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/ 38 | * 39 | * The specific constant was found here: 40 | * http://book.huihoo.com/data-structures-and-algorithms-with-object-oriented-design-patterns-in-c++/html/page214.html 41 | */ 42 | 43 | #ifndef SHL_MAP_H 44 | #define SHL_MAP_H 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #define shlDeclareMap(typeName, keyType, valueType) \ 52 | typedef struct \ 53 | { \ 54 | valueType defaultValue; \ 55 | uint32_t (*hashFn)(const keyType key); \ 56 | bool (*equalsFn)(const keyType item1, const keyType item2); \ 57 | void (*freeFn)(valueType item); \ 58 | } typeName ## Options; \ 59 | \ 60 | typedef struct { \ 61 | bool active; \ 62 | uint32_t hash; \ 63 | int32_t next; \ 64 | keyType key; \ 65 | valueType value; \ 66 | } typeName ## __Entry__; \ 67 | \ 68 | typedef struct { \ 69 | uint32_t count; \ 70 | uint32_t capacity; \ 71 | uint32_t loadFactor; \ 72 | uint32_t shift; \ 73 | uint32_t (*hashFn)(const keyType key); \ 74 | bool (*equalsFn)(const keyType item1, const keyType item2); \ 75 | void (*freeFn)(valueType item); \ 76 | valueType defaultValue; \ 77 | typeName ## __Entry__* entries; \ 78 | } typeName; \ 79 | \ 80 | void typeName ## Init(typeName* map, typeName ## Options options); \ 81 | void typeName ## Free(typeName* map); \ 82 | bool typeName ## Contains(typeName* map, keyType key); \ 83 | valueType typeName ## Get(typeName* map, keyType key); \ 84 | void typeName ## Set(typeName* map, keyType key, valueType value); \ 85 | void typeName ## Remove(typeName* map, keyType key); \ 86 | void typeName ## Clear(typeName* map); 87 | 88 | #define shlDefineMap(typeName, keyType, valueType) \ 89 | static uint32_t typeName ## __fibHash(uint32_t hash, uint32_t shift) \ 90 | { \ 91 | const uint32_t hashConstant = 2654435769u; \ 92 | return (hash * hashConstant) >> shift; \ 93 | } \ 94 | \ 95 | static uint32_t typeName ## __findEmptyBucket(typeName* map, uint32_t index) \ 96 | { \ 97 | for(int32_t i = 0; i < map->capacity; i++) \ 98 | { \ 99 | if (!map->entries[(index + i) % map->capacity].active) \ 100 | return (index + i) % map->capacity; \ 101 | } \ 102 | \ 103 | return -1; \ 104 | } \ 105 | \ 106 | static void typeName ## __insert(typeName* map, keyType key, valueType value) \ 107 | { \ 108 | uint32_t hash, index, next; \ 109 | hash = index = typeName ## __fibHash(map->hashFn(key), map->shift); \ 110 | \ 111 | while (map->entries[index].active && map->entries[index].next >= 0) \ 112 | { \ 113 | if(map->entries[index].hash == hash && map->equalsFn(map->entries[index].key, key)) \ 114 | { \ 115 | valueType currentValue = map->entries[index].value; \ 116 | map->entries[index].value = value; \ 117 | \ 118 | if (map->freeFn) \ 119 | map->freeFn(currentValue); \ 120 | \ 121 | return; \ 122 | } \ 123 | \ 124 | index = map->entries[index].next; \ 125 | } \ 126 | \ 127 | if (map->entries[index].active) \ 128 | { \ 129 | if(map->entries[index].hash == hash && map->equalsFn(map->entries[index].key, key)) \ 130 | { \ 131 | valueType currentValue = map->entries[index].value; \ 132 | map->entries[index].value = value; \ 133 | \ 134 | if (map->freeFn) \ 135 | map->freeFn(currentValue); \ 136 | \ 137 | return; \ 138 | } \ 139 | } \ 140 | \ 141 | next = typeName ## __findEmptyBucket(map, index); \ 142 | if (index != next) \ 143 | map->entries[index].next = next; \ 144 | \ 145 | map->entries[next].active = true; \ 146 | map->entries[next].key = key; \ 147 | map->entries[next].value = value; \ 148 | map->entries[next].hash = hash; \ 149 | map->entries[next].next = -1; \ 150 | map->count++; \ 151 | } \ 152 | \ 153 | static void typeName ## __resize(typeName* map) \ 154 | { \ 155 | uint32_t oldCapacity = map->capacity; \ 156 | typeName ## __Entry__* old = map->entries; \ 157 | \ 158 | map->loadFactor = oldCapacity; \ 159 | map->capacity = 1 << (32 - (--map->shift)); \ 160 | map->entries = (typeName ## __Entry__*)calloc(map->capacity, sizeof(typeName ## __Entry__)); \ 161 | map->count = 0; \ 162 | \ 163 | for(int32_t i = 0; i < oldCapacity; i++) \ 164 | { \ 165 | if(old[i].active) \ 166 | typeName ## __insert(map, old[i].key, old[i].value); \ 167 | } \ 168 | free(old); \ 169 | } \ 170 | \ 171 | void typeName ## Init(typeName* map, typeName ## Options options) \ 172 | { \ 173 | map->defaultValue = options.defaultValue; \ 174 | map->hashFn = options.hashFn; \ 175 | map->equalsFn = options.equalsFn; \ 176 | map->freeFn = options.freeFn; \ 177 | map->shift = 29; \ 178 | map->capacity = 8; \ 179 | map->loadFactor = 6; \ 180 | map->count = 0; \ 181 | map->entries = (typeName ## __Entry__ *)calloc(map->capacity, sizeof(typeName ## __Entry__)); \ 182 | } \ 183 | \ 184 | void typeName ## Free(typeName* map) \ 185 | { \ 186 | if (!map->entries) \ 187 | return; \ 188 | \ 189 | typeName ## Clear(map); \ 190 | \ 191 | free(map->entries); \ 192 | map->entries = 0; \ 193 | } \ 194 | \ 195 | bool typeName ## Contains(typeName* map, keyType key) \ 196 | { \ 197 | if (!map->entries) \ 198 | return false; \ 199 | \ 200 | uint32_t index, hash; \ 201 | hash = index = typeName ## __fibHash(map->hashFn(key), map->shift); \ 202 | \ 203 | bool found = false; \ 204 | \ 205 | while (map->entries[index].active) \ 206 | { \ 207 | if(map->entries[index].hash == hash && map->equalsFn(map->entries[index].key, key)) \ 208 | { \ 209 | found = true; \ 210 | break; \ 211 | } \ 212 | \ 213 | if (map->entries[index].next < 0) \ 214 | { \ 215 | break; \ 216 | } \ 217 | \ 218 | index = map->entries[index].next; \ 219 | } \ 220 | \ 221 | return found; \ 222 | } \ 223 | \ 224 | valueType typeName ## Get(typeName* map, keyType key) \ 225 | { \ 226 | if (!map->entries) \ 227 | return map->defaultValue; \ 228 | \ 229 | uint32_t index, hash; \ 230 | hash = index = typeName ## __fibHash(map->hashFn(key), map->shift); \ 231 | \ 232 | valueType value = map->defaultValue; \ 233 | \ 234 | while (map->entries[index].active) \ 235 | { \ 236 | if(map->entries[index].hash == hash && map->equalsFn(map->entries[index].key, key)) \ 237 | { \ 238 | value = map->entries[index].value; \ 239 | break; \ 240 | } \ 241 | \ 242 | if (map->entries[index].next < 0) \ 243 | { \ 244 | break; \ 245 | } \ 246 | \ 247 | index = map->entries[index].next; \ 248 | } \ 249 | \ 250 | return value; \ 251 | } \ 252 | \ 253 | void typeName ## Set(typeName* map, keyType key, valueType value) \ 254 | { \ 255 | if (!map->entries) \ 256 | return; \ 257 | \ 258 | if(map->count == map->loadFactor) \ 259 | typeName ## __resize(map); \ 260 | \ 261 | typeName ## __insert(map, key, value); \ 262 | } \ 263 | \ 264 | void typeName ## Remove(typeName* map, keyType key) \ 265 | { \ 266 | if (!map->entries) \ 267 | return; \ 268 | \ 269 | uint32_t prevIndex, index, hash; \ 270 | hash = prevIndex = index = typeName ## __fibHash(map->hashFn(key), map->shift); \ 271 | \ 272 | while (map->entries[index].active) \ 273 | { \ 274 | if(map->entries[index].hash == hash && map->equalsFn(map->entries[index].key, key)) \ 275 | { \ 276 | valueType value = map->entries[index].value; \ 277 | \ 278 | map->entries[prevIndex].next = map->entries[index].next; \ 279 | map->entries[index].value = map->defaultValue; \ 280 | map->entries[index].active = false; \ 281 | \ 282 | if (map->freeFn) \ 283 | map->freeFn(value); \ 284 | \ 285 | break; \ 286 | } \ 287 | \ 288 | if (map->entries[index].next < 0) \ 289 | { \ 290 | break; \ 291 | } \ 292 | \ 293 | prevIndex = index; \ 294 | index = map->entries[index].next; \ 295 | } \ 296 | map->count--; \ 297 | } \ 298 | \ 299 | void typeName ## Clear(typeName* map) \ 300 | { \ 301 | if (!map->entries) \ 302 | return; \ 303 | \ 304 | for(int32_t i = 0; i < map->count; i++) \ 305 | { \ 306 | if (map->entries[i].active) \ 307 | { \ 308 | if (map->freeFn) \ 309 | map->freeFn(map->entries[i].value); \ 310 | \ 311 | map->entries[i].active = false; \ 312 | } \ 313 | } \ 314 | \ 315 | map->count = 0; \ 316 | } 317 | 318 | #endif //SHL_MAP_H -------------------------------------------------------------------------------- /map.md: -------------------------------------------------------------------------------- 1 | # Map structure 2 | _This project is based on [GenericMap](https://github.com/mystborn/GenericMap) by mystborn, so there similar function names and structures_ 3 | 4 | Represents a strongly typed collection of key-value. 5 | 6 | ## Defining a Type 7 | Use the macro `shlDeclareMap` to generate the type and function definitions. It has the following arguments: 8 | 9 | | Argument | Description | 10 | | --- | --- | 11 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 12 | | `keyType` | The type of the key. | 13 | | `valueType` | The type of the value. | 14 | 15 | Use the macro `shlDefineMap` to generate the function implementations. 16 | 17 | | Argument | Description | 18 | | --- | --- | 19 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 20 | | `keyType` | The type of the key. | 21 | | `valueType` | The type of the value. | 22 | 23 | ```c 24 | #include "map.h" 25 | 26 | shlDeclareMap(SLengthMap, const char*, int) 27 | shlDefineMap(SLengthMap, const char*, int) 28 | ``` 29 | 30 | The map structure allows the following operations (all functions all prefixed with _typeName_): 31 | 32 | | Function | Description | Return type | 33 | | --- | --- | --- | 34 | | `Init`(_typeName_* map, _typeName_ Options options) | Initializes the data needed for the map. | void | 35 | | `Free`(_typeName_* map) | Frees the data used by the map. It doesn't free the map itself. | void | 36 | | `Contains`(_typeName_* map, _keyType_ key) | Return `true` a key is contained in the map. | bool | 37 | | `Get`(_typeName_* map, _keyType_ key) | Gets the value asociated with the key `key`, or _defaultValue_ if there are no value asociated with the key. | _valueType_ | 38 | | `Set`(_typeName_* map, _keyType_ key, _valueType_ value) | Sets the value `value` asociated with the key `key`. If the key doesn't exists, the map create it. If the key already exists, the value is replaced, freeing the previous value if a `freeFn` function was provided. | void | 39 | | `Remove`(_typeName_* map, _keyType_ key) | Remove the key `key` from the map, freeing the value associated with the key if a `freeFn` function was provided. | void | 40 | | `Clear`(_typeName_* map) | Clear the map, freeing every element if a `freeFn` was provided. Doesn't free the map itself. | void | 41 | 42 | ## Options 43 | 44 | Each definition of a map declare a struct _typeName_ Options that is used to initialize the map. The struct has the following members: 45 | 46 | | Name | Type | Description | 47 | | --- | --- | --- | 48 | | `hashFn` | uint32_t (*)(const _keyType_) | A pointer to a function that takes a key and returns a hash value for that key. | 49 | | `equalsFn` | bool (*)(const _keyType_, const _keyType_) | A pointer to a function that takes two keys, and returns `true` if the keys are equals, and returns `false` otherwise. | 50 | | `freeFn` | void (*)(_valueType_) | _(optional)_ A pointer to a function that takes an element and free it. If no `freeFn` is provided, then the operations `Set` (when there is a value to replace), `Remove`, `Clear` and `Free` doesn't free the elements and the user of the map is the responsible for freeing the elements. | 51 | | `defaultValue` | _valueType_ | The value to return when you try to access an element that doesn't exist. | 52 | 53 | Example: 54 | ```c 55 | #include 56 | #include 57 | 58 | #include "map.h" 59 | 60 | uint32_t fnv32(const char* data) 61 | { 62 | uint32_t hash = FNV_OFFSET_32; 63 | while(*data != 0) 64 | hash = (*data++ ^ hash) * FNV_PRIME_32; 65 | 66 | return hash; 67 | } 68 | 69 | bool equalsStr(const char *s1, const char *s2) 70 | { 71 | return strcmp(s1, s2) == 0; 72 | } 73 | 74 | shlDeclareMap(SLengthMap, const char*, int) 75 | shlDefineMap(SLengthMap, const char*, int) 76 | 77 | int main() 78 | { 79 | SLengthMapOptions options = (SLengthMapOptions){0}; 80 | options.hashFn = fnv32; 81 | options.equalsFn = equalsStr; 82 | options.defaultValue = NULL; 83 | 84 | SLengthMap map; 85 | SLengthMapInit(&map, options); 86 | 87 | char *strings[9] = 88 | { 89 | "tvnccxxgqofssyaikgij", "dehnxzqjek", "mmtlycyoosuieqw", 90 | "tvnccxxgqogij", "defssyaikhnxzqjek", "mmtlycyoosuieqw", 91 | "tvncccyoosuixxgqofssyaikgij", "dehn", "mxzqjekmtlyeqwdfsdf" 92 | } 93 | 94 | for(int i = 0; i < 9; i++) 95 | SLengthMapSet(&map, strings[i], strlen(strings[i])); 96 | 97 | printf("The length of %s is %d\n", strings[2], SLengthMapGet(&map, strings[2])); 98 | 99 | return 0; 100 | } 101 | ``` -------------------------------------------------------------------------------- /memory_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | memory_buffer.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #ifndef SHL_MEMORY_BUFFER_H 27 | #define SHL_MEMORY_BUFFER_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | typedef struct _MemoryBuffer 40 | { 41 | uint8_t* data; 42 | size_t length; 43 | uint8_t* _pointer; 44 | } MemoryBuffer; 45 | 46 | #define mbEnd(buffer) ((buffer)->data + (buffer)->length) 47 | #define mbPosition(buffer) ((buffer)->_pointer - (buffer)->data) 48 | 49 | void mbInitEmpty(MemoryBuffer* buffer); 50 | void mbInitFromMemory(MemoryBuffer* buffer, uint8_t* data, size_t length); 51 | void mbFree(MemoryBuffer* buffer); 52 | uint8_t* mbGetData(MemoryBuffer* buffer, size_t* length); 53 | 54 | bool mbSeek(MemoryBuffer* buffer, uint32_t position); 55 | bool mbSkip(MemoryBuffer* buffer, int32_t distance); 56 | 57 | bool mbScanTo(MemoryBuffer* buffer, const void* data, size_t length); 58 | 59 | bool mbRead(MemoryBuffer* buffer, uint8_t* value); 60 | bool mbReadBytes(MemoryBuffer* buffer, uint8_t* value, size_t count); 61 | bool mbReadString(MemoryBuffer* buffer, char* str, size_t count); 62 | 63 | bool mbReadInt16LE(MemoryBuffer* buffer, int16_t* value); 64 | bool mbReadInt16BE(MemoryBuffer* buffer, int16_t* value); 65 | bool mbReadUInt16LE(MemoryBuffer* buffer, uint16_t* value); 66 | bool mbReadUInt16BE(MemoryBuffer* buffer, uint16_t* value); 67 | 68 | bool mbReadInt24LE(MemoryBuffer* buffer, int32_t* value); 69 | bool mbReadInt24BE(MemoryBuffer* buffer, int32_t* value); 70 | bool mbReadUInt24LE(MemoryBuffer* buffer, uint32_t* value); 71 | bool mbReadUInt24BE(MemoryBuffer* buffer, uint32_t* value); 72 | 73 | bool mbReadInt32LE(MemoryBuffer* buffer, int32_t* value); 74 | bool mbReadInt32BE(MemoryBuffer* buffer, int32_t* value); 75 | bool mbReadUInt32LE(MemoryBuffer* buffer, uint32_t* value); 76 | bool mbReadUInt32BE(MemoryBuffer* buffer, uint32_t* value); 77 | 78 | bool mbWrite(MemoryBuffer* buffer, uint8_t value); 79 | bool mbWriteBytes(MemoryBuffer* buffer, uint8_t values[], size_t count); 80 | bool mbWriteString(MemoryBuffer* buffer, const char* str, size_t count); 81 | 82 | bool mbWriteInt16LE(MemoryBuffer* buffer, int16_t value); 83 | bool mbWriteInt16BE(MemoryBuffer* buffer, int16_t value); 84 | bool mbWriteUInt16LE(MemoryBuffer* buffer, uint16_t value); 85 | bool mbWriteUInt16BE(MemoryBuffer* buffer, uint16_t value); 86 | 87 | bool mbWriteInt24LE(MemoryBuffer* buffer, int32_t value); 88 | bool mbWriteInt24BE(MemoryBuffer* buffer, int32_t value); 89 | bool mbWriteUInt24LE(MemoryBuffer* buffer, uint32_t value); 90 | bool mbWriteUInt24BE(MemoryBuffer* buffer, uint32_t value); 91 | 92 | bool mbWriteInt32LE(MemoryBuffer* buffer, int32_t value); 93 | bool mbWriteInt32BE(MemoryBuffer* buffer, int32_t value); 94 | bool mbWriteUInt32LE(MemoryBuffer* buffer, uint32_t value); 95 | bool mbWriteUInt32BE(MemoryBuffer* buffer, uint32_t value); 96 | 97 | bool mbIsEOF(MemoryBuffer* buffer); 98 | 99 | #ifdef __cplusplus 100 | } 101 | #endif 102 | 103 | #ifdef SHL_MEMORY_BUFFER_IMPLEMENTATION 104 | 105 | static bool mb__realloc(MemoryBuffer* buffer, size_t newLength) 106 | { 107 | if(newLength <= buffer->length) 108 | { 109 | buffer->length = newLength; 110 | return true; 111 | } 112 | 113 | uint8_t* oldData = buffer->data; 114 | uint8_t* newData = (uint8_t*)calloc(newLength, sizeof(uint8_t)); 115 | if (!newData) 116 | { 117 | return false; 118 | } 119 | 120 | size_t count = newLength > buffer->length ? buffer->length : newLength; 121 | memcpy(newData, buffer->data, count); 122 | 123 | buffer->_pointer = newData + mbPosition(buffer); 124 | buffer->data = newData; 125 | buffer->length = newLength; 126 | 127 | free(oldData); 128 | return true; 129 | } 130 | 131 | void mbInitEmpty(MemoryBuffer* buffer) 132 | { 133 | buffer->data = (uint8_t*)calloc(0, sizeof(uint8_t)); 134 | buffer->length = 0; 135 | buffer->_pointer = buffer->data; 136 | } 137 | 138 | void mbInitFromMemory(MemoryBuffer* buffer, uint8_t* data, size_t length) 139 | { 140 | buffer->data = data; 141 | buffer->length = length; 142 | buffer->_pointer = buffer->data; 143 | } 144 | 145 | void mbFree(MemoryBuffer* buffer) 146 | { 147 | if (buffer->data) 148 | free((void*)buffer->data); 149 | 150 | buffer->data = NULL; 151 | buffer->length = 0; 152 | buffer->_pointer = NULL; 153 | } 154 | 155 | uint8_t* mbGetData(MemoryBuffer* buffer, size_t* length) 156 | { 157 | uint8_t* data = (uint8_t*)malloc(buffer->length); 158 | memcpy(data, buffer->data, buffer->length); 159 | *length = buffer->length; 160 | return data; 161 | } 162 | 163 | bool mbSeek(MemoryBuffer* buffer, uint32_t position) 164 | { 165 | if (buffer->data + position > mbEnd(buffer)) 166 | { 167 | if (!mb__realloc(buffer, position)) 168 | return false; 169 | } 170 | 171 | buffer->_pointer = buffer->data + position; 172 | return true; 173 | } 174 | 175 | bool mbSkip(MemoryBuffer* buffer, int32_t distance) 176 | { 177 | if (distance < 0) 178 | { 179 | if (buffer->_pointer + distance < buffer->data) 180 | return false; 181 | } 182 | 183 | return mbSeek(buffer, mbPosition(buffer) + distance); 184 | } 185 | 186 | bool mbScanTo(MemoryBuffer* buffer, const void* data, size_t length) 187 | { 188 | while (buffer->_pointer + length <= mbEnd(buffer)) 189 | { 190 | // printf("%s\n", buffer->_pointer); 191 | if(memcmp(buffer->_pointer, data, length) == 0) 192 | return true; 193 | 194 | buffer->_pointer++; 195 | } 196 | 197 | return false; 198 | } 199 | 200 | bool mbRead(MemoryBuffer* buffer, uint8_t* value) 201 | { 202 | return mbReadBytes(buffer, value, 1); 203 | } 204 | 205 | bool mbReadBytes(MemoryBuffer* buffer, uint8_t* values, size_t count) 206 | { 207 | if (buffer->_pointer + count > mbEnd(buffer)) 208 | return false; 209 | 210 | memcpy(values, buffer->_pointer, count); 211 | buffer->_pointer += count; 212 | return true; 213 | } 214 | 215 | bool mbReadString(MemoryBuffer* buffer, char* str, size_t count) 216 | { 217 | return mbReadBytes(buffer, (uint8_t*)str, count); 218 | } 219 | 220 | bool mbReadInt16LE(MemoryBuffer* buffer, int16_t* value) 221 | { 222 | uint8_t byte0, byte1; 223 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1)) 224 | { 225 | *value = (byte1 << 8) | byte0; 226 | return true; 227 | } 228 | 229 | return false; 230 | } 231 | 232 | bool mbReadInt16BE(MemoryBuffer* buffer, int16_t* value) 233 | { 234 | uint8_t byte0, byte1; 235 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1)) 236 | { 237 | *value = (byte0 << 8) | byte1; 238 | return true; 239 | } 240 | 241 | return false; 242 | } 243 | 244 | bool mbReadUInt16LE(MemoryBuffer* buffer, uint16_t* value) 245 | { 246 | uint8_t byte0, byte1; 247 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1)) 248 | { 249 | *value = (byte1 << 8) | byte0; 250 | return true; 251 | } 252 | 253 | return false; 254 | } 255 | 256 | bool mbReadUInt16BE(MemoryBuffer* buffer, uint16_t* value) 257 | { 258 | uint8_t byte0, byte1; 259 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1)) 260 | { 261 | *value = (byte0 << 8) | byte1; 262 | return true; 263 | } 264 | 265 | return false; 266 | } 267 | 268 | bool mbReadInt24LE(MemoryBuffer* buffer, int32_t* value) 269 | { 270 | uint8_t byte0, byte1, byte2; 271 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2)) 272 | { 273 | *value = (byte2 << 16) | (byte1 << 8) | byte0; 274 | return true; 275 | } 276 | 277 | return false; 278 | } 279 | 280 | bool mbReadInt24BE(MemoryBuffer* buffer, int32_t* value) 281 | { 282 | uint8_t byte0, byte1, byte2; 283 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2)) 284 | { 285 | *value = (byte0 << 16) | (byte1 << 8) | byte2; 286 | return true; 287 | } 288 | 289 | return false; 290 | } 291 | 292 | bool mbReadUInt24LE(MemoryBuffer* buffer, uint32_t* value) 293 | { 294 | uint8_t byte0, byte1, byte2; 295 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2)) 296 | { 297 | *value = (byte2 << 16) | (byte1 << 8) | byte0; 298 | return true; 299 | } 300 | 301 | return false; 302 | } 303 | 304 | bool mbReadUInt24BE(MemoryBuffer* buffer, uint32_t* value) 305 | { 306 | uint8_t byte0, byte1, byte2; 307 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2)) 308 | { 309 | *value = (byte0 << 16) | (byte1 << 8) | byte2; 310 | return true; 311 | } 312 | 313 | return false; 314 | } 315 | 316 | bool mbReadInt32LE(MemoryBuffer* buffer, int32_t* value) 317 | { 318 | uint8_t byte0, byte1, byte2, byte3; 319 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2) && mbRead(buffer, &byte3)) 320 | { 321 | *value = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; 322 | return true; 323 | } 324 | 325 | return false; 326 | } 327 | 328 | bool mbReadInt32BE(MemoryBuffer* buffer, int32_t* value) 329 | { 330 | uint8_t byte0, byte1, byte2, byte3; 331 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2) && mbRead(buffer, &byte3)) 332 | { 333 | *value = (byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3; 334 | return true; 335 | } 336 | 337 | return false; 338 | } 339 | 340 | bool mbReadUInt32LE(MemoryBuffer* buffer, uint32_t* value) 341 | { 342 | uint8_t byte0, byte1, byte2, byte3; 343 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2) && mbRead(buffer, &byte3)) 344 | { 345 | *value = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; 346 | return true; 347 | } 348 | 349 | return false; 350 | } 351 | 352 | bool mbReadUInt32BE(MemoryBuffer* buffer, uint32_t* value) 353 | { 354 | uint8_t byte0, byte1, byte2, byte3; 355 | if(mbRead(buffer, &byte0) && mbRead(buffer, &byte1) && mbRead(buffer, &byte2) && mbRead(buffer, &byte3)) 356 | { 357 | *value = (byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3; 358 | return true; 359 | } 360 | 361 | return false; 362 | } 363 | 364 | bool mbWrite(MemoryBuffer* buffer, uint8_t value) 365 | { 366 | return mbWriteBytes(buffer, &value, 1); 367 | } 368 | 369 | bool mbWriteBytes(MemoryBuffer* buffer, uint8_t values[], size_t count) 370 | { 371 | if (buffer->_pointer + count >= mbEnd(buffer)) 372 | { 373 | if (!mb__realloc(buffer, mbPosition(buffer) + count)) 374 | return false; 375 | } 376 | 377 | memcpy(buffer->_pointer, values, count); 378 | buffer->_pointer += count; 379 | 380 | return true; 381 | } 382 | 383 | bool mbWriteString(MemoryBuffer* buffer, const char* str, size_t count) 384 | { 385 | return mbWriteBytes(buffer, (uint8_t*)str, count); 386 | } 387 | 388 | bool mbWriteInt16LE(MemoryBuffer* buffer, int16_t value) 389 | { 390 | return mbWriteBytes( 391 | buffer, 392 | (uint8_t[]) 393 | { 394 | (uint8_t)value, 395 | (uint8_t)(value >> 8) 396 | }, 397 | sizeof(int16_t)); 398 | } 399 | 400 | bool mbWriteInt16BE(MemoryBuffer* buffer, int16_t value) 401 | { 402 | return mbWriteBytes( 403 | buffer, 404 | (uint8_t[]) 405 | { 406 | (uint8_t)(value >> 8), 407 | (uint8_t)value 408 | }, 409 | sizeof(int16_t)); 410 | } 411 | 412 | bool mbWriteUInt16LE(MemoryBuffer* buffer, uint16_t value) 413 | { 414 | return mbWriteBytes( 415 | buffer, 416 | (uint8_t[]) 417 | { 418 | (uint8_t)value, 419 | (uint8_t)(value >> 8) 420 | }, 421 | sizeof(uint16_t)); 422 | } 423 | 424 | bool mbWriteUInt16BE(MemoryBuffer* buffer, uint16_t value) 425 | { 426 | return mbWriteBytes( 427 | buffer, 428 | (uint8_t[]) 429 | { 430 | (uint8_t)(value >> 8), 431 | (uint8_t)value 432 | }, 433 | sizeof(uint16_t)); 434 | } 435 | 436 | bool mbWriteInt24LE(MemoryBuffer* buffer, int32_t value) 437 | { 438 | return mbWriteBytes( 439 | buffer, 440 | (uint8_t[]) 441 | { 442 | (uint8_t)value, 443 | (uint8_t)(value >> 8), 444 | (uint8_t)(value >> 16) 445 | }, 446 | sizeof(int32_t)); 447 | } 448 | 449 | bool mbWriteInt24BE(MemoryBuffer* buffer, int32_t value) 450 | { 451 | return mbWriteBytes( 452 | buffer, 453 | (uint8_t[]) 454 | { 455 | (uint8_t)(value >> 16), 456 | (uint8_t)(value >> 8), 457 | (uint8_t)value 458 | }, 459 | sizeof(int32_t)); 460 | } 461 | 462 | bool mbWriteUInt24LE(MemoryBuffer* buffer, uint32_t value) 463 | { 464 | return mbWriteBytes( 465 | buffer, 466 | (uint8_t[]) 467 | { 468 | (uint8_t)value, 469 | (uint8_t)(value >> 8), 470 | (uint8_t)(value >> 16) 471 | }, 472 | sizeof(uint32_t)); 473 | } 474 | 475 | bool mbWriteUInt24BE(MemoryBuffer* buffer, uint32_t value) 476 | { 477 | return mbWriteBytes( 478 | buffer, 479 | (uint8_t[]) 480 | { 481 | (uint8_t)(value >> 16), 482 | (uint8_t)(value >> 8), 483 | (uint8_t)value 484 | }, 485 | sizeof(uint32_t)); 486 | } 487 | 488 | bool mbWriteInt32LE(MemoryBuffer* buffer, int32_t value) 489 | { 490 | return mbWriteBytes( 491 | buffer, 492 | (uint8_t[]) 493 | { 494 | (uint8_t)value, 495 | (uint8_t)(value >> 8), 496 | (uint8_t)(value >> 16), 497 | (uint8_t)(value >> 24) 498 | }, 499 | sizeof(int32_t)); 500 | } 501 | 502 | bool mbWriteInt32BE(MemoryBuffer* buffer, int32_t value) 503 | { 504 | return mbWriteBytes( 505 | buffer, 506 | (uint8_t[]) 507 | { 508 | (uint8_t)(value >> 24), 509 | (uint8_t)(value >> 16), 510 | (uint8_t)(value >> 8), 511 | (uint8_t)value 512 | }, 513 | sizeof(int32_t)); 514 | } 515 | 516 | bool mbWriteUInt32LE(MemoryBuffer* buffer, uint32_t value) 517 | { 518 | return mbWriteBytes( 519 | buffer, 520 | (uint8_t[]) 521 | { 522 | (uint8_t)value, 523 | (uint8_t)(value >> 8), 524 | (uint8_t)(value >> 16), 525 | (uint8_t)(value >> 24) 526 | }, 527 | sizeof(uint32_t)); 528 | } 529 | 530 | bool mbWriteUInt32BE(MemoryBuffer* buffer, uint32_t value) 531 | { 532 | return mbWriteBytes( 533 | buffer, 534 | (uint8_t[]) 535 | { 536 | (uint8_t)(value >> 24), 537 | (uint8_t)(value >> 16), 538 | (uint8_t)(value >> 8), 539 | (uint8_t)value 540 | }, 541 | sizeof(uint32_t)); 542 | } 543 | 544 | bool mbIsEOF(MemoryBuffer* buffer) 545 | { 546 | return buffer->_pointer == mbEnd(buffer); 547 | } 548 | 549 | #endif // SHL_MEMORY_BUFFER_IMPLEMENTATION 550 | #endif // SHL_MEMORY_BUFFER_H 551 | -------------------------------------------------------------------------------- /memzone.h: -------------------------------------------------------------------------------- 1 | /* 2 | memzone.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | #ifndef SHL_MEMORY_ZONE_H 27 | #define SHL_MEMORY_ZONE_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | typedef enum 42 | { 43 | MEM_STATIC, // default block type, represent a normal block 44 | MEM_PURGE, // block type that can be reused when allocating, even if it's used 45 | // normally this type of block is created when free some STATIC block 46 | MEM_FIXED // block type that is fixed at the start of the app and will last the 47 | // entire app life-cycle and it can't be defragmented 48 | } memtype_t; 49 | 50 | typedef struct memblock_s 51 | { 52 | size_t size; // size of the block 53 | memtype_t type; // the type of the block 54 | void* user; // a pointer to the pointer returned to the user 55 | struct memblock_s *next, *prev; // pointers to next and prev in the block list 56 | } memblock_t; 57 | 58 | typedef struct 59 | { 60 | size_t usedSize; // how much space is used without including blocks data 61 | size_t maxSize; // the max allowed size that can be allocated 62 | memblock_t* rover; // a pointer to a free block that is used when allocating 63 | memblock_t blockList; // list of blocks, here is where the requested memory begins 64 | } memzone_t; 65 | 66 | memzone_t* mzInit(size_t maxSize); 67 | void* mzAlloc(memzone_t* zone, size_t size); 68 | void mzFree(memzone_t* zone, void* p); 69 | int32_t mzGetNumberOfBlocks(memzone_t* zone); 70 | size_t mzGetUsableFreeSize(memzone_t* zone); 71 | float mzGetFragPercentage(memzone_t* zone); 72 | // void mzDefrag(memzone_t* zone); 73 | void mzPrint(memzone_t* zone, bool printBlocks, bool printMap); 74 | 75 | #define mzIsBlockEmpty(block) ((block)->user == NULL) 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | 81 | #ifdef SHL_MEMORY_ZONE_IMPLEMENTATION 82 | 83 | #define __pointerOffset(t, p, o) ((t*)((uint8_t*)(p) + (o))) 84 | 85 | memzone_t* mzInit(size_t maxSize) 86 | { 87 | if (maxSize < sizeof(memzone_t)) 88 | { 89 | fprintf(stderr, "You need to allocate memory for at least %u bytes.\n", sizeof(memzone_t)); 90 | return NULL; 91 | } 92 | 93 | uint8_t* rawZone = (uint8_t*)malloc(maxSize); 94 | memzone_t* zone = (memzone_t*)rawZone; 95 | if (!zone) 96 | { 97 | fprintf(stderr, "The system couldn't allocate memory for %u bytes.\n", maxSize); 98 | return NULL; 99 | } 100 | 101 | zone->usedSize = sizeof(memzone_t); 102 | zone->maxSize = maxSize; 103 | 104 | zone->blockList = (memblock_t){0}; 105 | zone->blockList.size = maxSize - (sizeof(memzone_t) - sizeof(memblock_t)); 106 | zone->blockList.type = MEM_STATIC; 107 | zone->blockList.user = NULL; 108 | zone->blockList.next = &zone->blockList; 109 | zone->blockList.prev = &zone->blockList; 110 | 111 | zone->rover = &zone->blockList; 112 | 113 | return zone; 114 | } 115 | 116 | void* mzAlloc(memzone_t* zone, size_t size) 117 | { 118 | size_t sizeToAlloc = size + sizeof(memblock_t); 119 | if (sizeToAlloc > zone->maxSize - zone->usedSize) 120 | { 121 | fprintf(stderr, "Memory overflow: There is no more memory to alloc size %u bytes.\n", size); 122 | return NULL; 123 | } 124 | 125 | memblock_t* rover = zone->rover; 126 | if (rover->user || rover->size < sizeToAlloc) 127 | { 128 | rover = rover->next; 129 | while (rover->user || rover->size < sizeToAlloc) 130 | { 131 | // if the rover pointer traversed the entire list and didn't 132 | // find any block to alloc the memory return null, maybe a defrag? 133 | if (rover == zone->rover) 134 | { 135 | fprintf(stderr, "There is total free memory to alloc size %u bytes but there isn't a block big enough.\n", size); 136 | return NULL; 137 | } 138 | 139 | rover = rover->next; 140 | } 141 | } 142 | 143 | // if sizeToAlloc is less or equals to the block size, 144 | // allocate there and not split the block otherwise, 145 | // create a new block and split the current 146 | if (rover->size > sizeToAlloc) 147 | { 148 | // create a new empty block with the remaining free space 149 | memblock_t* newBlock = __pointerOffset(memblock_t, rover, sizeToAlloc); 150 | newBlock->size = rover->size - sizeToAlloc; 151 | newBlock->type = MEM_STATIC; 152 | newBlock->user = NULL; 153 | newBlock->prev = rover; 154 | newBlock->next = rover->next; 155 | 156 | // set the next block to point to the new one 157 | rover->next->prev = newBlock; 158 | rover->next = newBlock; 159 | rover->size = sizeToAlloc; 160 | 161 | // update the zone rover with the new free block created 162 | zone->rover = newBlock; 163 | zone->usedSize += sizeof(memblock_t); 164 | } 165 | 166 | zone->usedSize += size; 167 | 168 | rover->user = __pointerOffset(void, rover, sizeof(memblock_t)); 169 | return rover->user; 170 | } 171 | 172 | void mzFree(memzone_t* zone, void* p) 173 | { 174 | // this function doesn't need to iterate over the list 175 | // if it does this: 176 | // memblock_t* block = (memblock_t *) ( (byte *)p - sizeof(memblock_t)); 177 | // because the pointers returned by `mzAlloc` are just the start of the block 178 | // plus the size of the memblock_t, so this function should expect that 179 | // those pointer are the ones to be free 180 | memblock_t* rover = &zone->blockList; 181 | if (!rover->user || rover->user != p) 182 | { 183 | rover = rover->next; 184 | while (!rover->user || rover->user != p) 185 | { 186 | // if the rover pointer traversed the entire list and didn't 187 | // find any block to de-alloc, just return 188 | if (rover == &zone->blockList) 189 | { 190 | return; 191 | } 192 | 193 | rover = rover->next; 194 | } 195 | } 196 | 197 | rover->user = NULL; 198 | 199 | zone->usedSize -= rover->size - sizeof(memblock_t); 200 | 201 | // merge with next block if empty 202 | if (mzIsBlockEmpty(rover->next)) 203 | { 204 | memblock_t* next = rover->next; 205 | rover->size += next->size; 206 | rover->next = next->next; 207 | rover->next->prev = rover; 208 | } 209 | 210 | // merge with previous if empty 211 | if (mzIsBlockEmpty(rover->prev)) 212 | { 213 | memblock_t* prev = rover->prev; 214 | prev->size += rover->size; 215 | prev->next = rover->next; 216 | prev->next->prev = prev; 217 | } 218 | } 219 | 220 | int32_t mzGetNumberOfBlocks(memzone_t* zone) 221 | { 222 | int32_t numberOfBlocks = 0; 223 | 224 | memblock_t* rover = &zone->blockList; 225 | do 226 | { 227 | numberOfBlocks++; 228 | rover = rover->next; 229 | } while (rover != &zone->blockList); 230 | 231 | return numberOfBlocks; 232 | } 233 | 234 | size_t mzGetUsableFreeSize(memzone_t* zone) 235 | { 236 | size_t usableFreeSize = 0; 237 | 238 | memblock_t* rover = &zone->blockList; 239 | do 240 | { 241 | if (!rover->user) 242 | usableFreeSize += rover->size - sizeof(memblock_t); 243 | 244 | rover = rover->next; 245 | } while (rover != &zone->blockList); 246 | 247 | return usableFreeSize; 248 | } 249 | 250 | float mzGetFragPercentage(memzone_t* zone) 251 | { 252 | /** 253 | * (free - freemax) 254 | * ---------------- x 100% (or 100% for free=0) 255 | * free 256 | * where 257 | * free = total number of bytes free 258 | * freemax = size of largest free block 259 | */ 260 | 261 | size_t free = 0; 262 | size_t freeMax = 0; 263 | 264 | memblock_t* rover = &zone->blockList; 265 | do 266 | { 267 | if (!rover->user) 268 | { 269 | size_t freeSizeOnBlock = rover->size - sizeof(memblock_t); 270 | free += freeSizeOnBlock; 271 | 272 | if (freeSizeOnBlock > freeMax) 273 | freeMax = freeSizeOnBlock; 274 | } 275 | 276 | rover = rover->next; 277 | } while (rover != &zone->blockList); 278 | 279 | return free > 0 ? ((float)(free - freeMax) / free) * 100 : 0; 280 | } 281 | 282 | // void mzDefrag(memzone_t* zone) 283 | // { 284 | // memblock_t* start = &zone->blockList; 285 | // do 286 | // { 287 | // if (!start->user) 288 | // { 289 | // size_t s = 0; 290 | 291 | // memblock_t* end = start; 292 | // while (!end->user) 293 | // { 294 | // s += end->size; 295 | // end = end->next; 296 | // } 297 | 298 | // if (end == &zone->blockList) 299 | // { 300 | // start->size = s; 301 | // start->next = end; 302 | 303 | // end->prev = start; 304 | // break; 305 | // } 306 | 307 | // memblock_t prevStartBlock = *start; 308 | // memblock_t prevEndBlock = *end; 309 | 310 | // memmove(start, end, end->size); 311 | 312 | // size_t currentStartSize = start->size; 313 | 314 | // memblock_t* newBlock = __pointerOffset(memblock_t, start, currentStartSize); 315 | // newBlock->size = prevStartBlock.size; 316 | // newBlock->type = MEM_STATIC; 317 | // newBlock->user = NULL; 318 | // newBlock->prev = start; 319 | // newBlock->next = prevEndBlock.next; 320 | 321 | // start->prev = prevStartBlock.prev; 322 | // start->next = newBlock; 323 | 324 | // newBlock->next->prev = newBlock; 325 | // } 326 | 327 | // start = start->next; 328 | // } while (start != &zone->blockList); 329 | // } 330 | 331 | void mzPrint(memzone_t* zone, bool printBlocks, bool printMap) 332 | { 333 | printf("Zone: %p -> %p\n", zone, __pointerOffset(memzone_t, zone, zone->maxSize)); 334 | printf(" rover: %p\n", zone->rover); 335 | printf(" maxSize: %u\n", zone->maxSize); 336 | printf(" usedSize: %u\n", zone->usedSize); 337 | printf(" freeSize: %d\n", mzGetUsableFreeSize(zone)); 338 | printf(" numberOfBlocks: %d\n", mzGetNumberOfBlocks(zone)); 339 | printf(" fragmentation: %.2f%%\n", mzGetFragPercentage(zone)); 340 | 341 | if (printBlocks) 342 | { 343 | printf(" Blocks:\n"); 344 | memblock_t* rover = &zone->blockList; 345 | do 346 | { 347 | printf(" ----------\n"); 348 | printf(" id: %p\n", rover); 349 | printf(" size: %u\n", rover->size); 350 | printf(" type: %u\n", rover->type); 351 | printf(" user: %p\n", rover->user); 352 | printf(" prev: %p\n", rover->prev); 353 | printf(" next: %p\n", rover->next); 354 | printf(" ----------\n"); 355 | 356 | rover = rover->next; 357 | } while (rover != &zone->blockList); 358 | } 359 | 360 | if (printMap) 361 | { 362 | printf(" Map:\n"); 363 | 364 | char buffer[201]; 365 | 366 | memblock_t* rover = &zone->blockList; 367 | 368 | int32_t numberOfBlocks = mzGetNumberOfBlocks(zone); 369 | int32_t lines = (int32_t)ceilf((float)numberOfBlocks / 200); 370 | while (lines--) 371 | { 372 | memset(buffer, 0, sizeof(buffer)); 373 | 374 | for (int32_t i = 0; i < 200; i++) 375 | { 376 | buffer[i] = mzIsBlockEmpty(rover) ? '-' : '+'; 377 | 378 | rover = rover->next; 379 | if (rover == &zone->blockList) 380 | break; 381 | } 382 | 383 | printf("%s\n", buffer); 384 | } 385 | } 386 | } 387 | 388 | #endif // SHL_MEMORY_ZONE_IMPLEMENTATION 389 | #endif // SHL_MEMORY_ZONE_H 390 | -------------------------------------------------------------------------------- /memzone.md: -------------------------------------------------------------------------------- 1 | # Memzone allocator 2 | 3 | A simple memory allocator that uses a linked list of blocks to manage allocated blocks of memory. 4 | 5 | This is a custom implementation following the ideas of the memory allocator used in DOOM and explained it in the [Fabien Sanglard's Game Engine Black Book for Doom](http://fabiensanglard.net/gebb/index.html). 6 | 7 | Include the `#define SHL_MEMORY_ZONE_IMPLEMENTATION` before one of the `#include memzone.h` to get the implementation of the functions. Otherwise, only the declarations will be included. 8 | 9 | A memory _zone_ is defined with the following information: 10 | 11 | * `usedSize`: how much space is used without including blocks data 12 | * `maxSize`: the max allowed size that can be allocated 13 | * `rover`: a pointer to a free block that is used when allocating 14 | * `blockList`: list of blocks, here is where the requested memory begins 15 | 16 | It maintains a circular double linked-list of _blocks_ with minimal information necessary to allocate and deallocate memory. Each block as the following information: 17 | 18 | * `size`: size of the block 19 | * `type`: the type of the block 20 | * `user`: a pointer to the pointer returned to the user 21 | * `next`: pointer to the next block in the list 22 | * `prev`: pointers to previous block in the list 23 | 24 | The type of a block is one of the following values: 25 | 26 | * `MEM_STATIC`: default block type, represent a normal block. _For now this is the only type of blocks used, in further development the other types of blocks will be consider. 27 | * `MEM_PURGE`: block type that can be reused when allocating, even if it's used, normally this type of block is created when free some STATIC block 28 | * `MEM_FIXED`: block type that is fixed at the start of the app and will last the entire app life-cycle and it can't be defragmented 29 | 30 | The information of the headers of the _zone_ and _blocks_ are part of the `maxSize` passed at initialization time. That's it, if the user is considering a 1MB zone, the information of the headers will occupy part of that 1MB. The size of the header of the _zone_ is 20 bytes and the size of the header of each block is 32 bytes. However, when the user try to allocate a pointer of a given size, the _block_ that hold that pointer will have enough space to hold the requested size. For instance, if the user allocates 256 bytes, the _block_ holding the pointer for that size is 32 + 256 bytes long. 31 | 32 | At the beginning, when the _zone_ is initialized there is only one _block_ with all the free memory, except for the information of the header of the _zone_ and the header of the _block_. When a the user try to allocate memory, the allocator finds the first block that is big enough to hold the space needed, split the _block_ if necessary and return the pointer to the user. When the user try to deallocate a pointer, the allocator finds the _block_ for that pointer, and mark it as free, merging it with the sibilings _blocks_ if they are also empty. 33 | 34 | Example: 35 | ```c 36 | #define SHL_MEMORY_ZONE_IMPLEMENTATION 37 | #include "memzone.h" 38 | ``` 39 | 40 | The memory allocator allows the following operations: 41 | 42 | | Function | Description | Return type | 43 | | --- | --- | --- | 44 | | `mzInit`(size_t maxSize) | Creates and initialize a new memory allocator structure with the specified `maxSize`. | memzone_t* | 45 | | `mzAlloc`(memzone_t* zone, size_t size) | Allocates a block of memory on the specified `memzone_t` object and a given `size` | void* | 46 | | `mzFree`(memzone_t* zone, void* p) | Free a previously allocated block of memory. | void | 47 | | `mzIsBlockEmpty`(memblock_t* block) | A macro to determine if the specified block is empty. It will expand to `((block)->user == NULL)` | bool | 48 | | `mzGetNumberOfBlocks`(memzone_t* zone) | Gets the number of blocks in the allocator. | int32_t | 49 | | `mzGetUsableFreeSize`(memzone_t* zone) | Gets the usable free size on the allocator. |size_t | 50 | | `mzGetFragPercentage`(memzone_t* zone) | Gets the fragmentation percentage of the allocator. | float | 51 | | `mzDefrag`(memzone_t* zone) | Defragment the allocator. _This functionality is still in development_. | void | 52 | | `mzPrint`(memzone_t* zone, bool printBlocks, bool printMap) | Prints the allocator data to `stdout`. | void | 53 | 54 | Example: 55 | ```c 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | #define SHL_MEMORY_ZONE_IMPLEMENTATION 62 | #include "memzone.h" 63 | 64 | typedef enum 65 | { 66 | ENTITY_TYPE_1, 67 | ENTITY_TYPE_2, 68 | ENTITY_TYPE_3, 69 | 70 | ENTITY_TYPE_COUNT 71 | } EntityType; 72 | 73 | typedef struct 74 | { 75 | int32_t id; 76 | EntityType type; 77 | } Entity; 78 | 79 | #define ENTITY_COUNT 30000 80 | #define randabi(a, b) (int32_t)((a) + ((float)rand() / RAND_MAX) * ((b) - (a))) 81 | 82 | int main() 83 | { 84 | srand(time(NULL)); 85 | 86 | size_t zoneSize = 1024 * 1024; // 1MB 87 | memzone_t* zone = mzInit(zoneSize); 88 | if (!zone) 89 | { 90 | printf("ERROR: Couldn't allocate %d bytes\n", zoneSize); 91 | return -1; 92 | } 93 | 94 | Entity** entities = (Entity**)mzAlloc(zone, sizeOfArray); 95 | for (int32_t i = 0; i < ENTITY_COUNT; i++) 96 | { 97 | entities[i] = (Entity*)mzAlloc(zone, sizeof(Entity)); 98 | entities[i]->id = i; 99 | } 100 | 101 | mzPrint(zone, false, true); 102 | 103 | for (int32_t i = 0; i < ENTITY_COUNT / 2; i++) 104 | { 105 | int32_t index = randabi(0, ENTITY_COUNT - 1); 106 | if (entities[index]) 107 | { 108 | mzFree(zone, entities[index]); 109 | entities[index] = NULL; 110 | } 111 | } 112 | 113 | mzPrint(zone, false, true); 114 | 115 | free(zone); 116 | return 0; 117 | } 118 | ``` -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | queue.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed queue with push, pop and peek operations. 27 | */ 28 | #ifndef SHL_QUEUE_H 29 | #define SHL_QUEUE_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define shlDeclareQueue(typeName, itemType) \ 37 | typedef struct \ 38 | { \ 39 | itemType defaultValue; \ 40 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 41 | void (*freeFn)(itemType item); \ 42 | } typeName ## Options; \ 43 | \ 44 | typedef struct \ 45 | { \ 46 | uint32_t head; \ 47 | uint32_t tail; \ 48 | uint32_t count; \ 49 | uint32_t capacity; \ 50 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 51 | void (*freeFn)(itemType item); \ 52 | itemType defaultValue; \ 53 | itemType *items; \ 54 | } typeName; \ 55 | \ 56 | void typeName ## Init(typeName* queue, typeName ## Options options); \ 57 | void typeName ## Free(typeName* queue); \ 58 | void typeName ## Push(typeName* queue, itemType value); \ 59 | itemType typeName ## Peek(typeName* queue); \ 60 | itemType typeName ## Pop(typeName* queue); \ 61 | bool typeName ## Contains(typeName* queue, itemType value); \ 62 | void typeName ## Clear(typeName* queue); 63 | 64 | #define shlDefineQueue(typeName, itemType) \ 65 | void typeName ## __resize(typeName *queue) \ 66 | { \ 67 | uint32_t oldCapacity = queue->capacity; \ 68 | itemType* old = queue->items; \ 69 | \ 70 | queue->capacity = oldCapacity << 1; \ 71 | queue->items = (itemType *)calloc(queue->capacity, sizeof(itemType)); \ 72 | \ 73 | if (queue->head > queue->tail) \ 74 | { \ 75 | memcpy(queue->items, old + queue->head, (oldCapacity - queue->head) * sizeof(itemType)); \ 76 | memcpy(queue->items + oldCapacity - queue->head, old, ((queue->head + queue->count) % oldCapacity) * sizeof(itemType)); \ 77 | } \ 78 | else \ 79 | { \ 80 | memcpy(queue->items, old + queue->head, queue->count * sizeof(itemType)); \ 81 | } \ 82 | \ 83 | queue->head = 0; \ 84 | queue->tail = queue->count; \ 85 | } \ 86 | \ 87 | void typeName ## Init(typeName *queue, typeName ## Options options) \ 88 | { \ 89 | queue->defaultValue = options.defaultValue; \ 90 | queue->equalsFn = options.equalsFn; \ 91 | queue->freeFn = options.freeFn; \ 92 | queue->capacity = 8; \ 93 | queue->count = 0; \ 94 | queue->head = 0; \ 95 | queue->tail = 0; \ 96 | queue->items = (itemType *)calloc(queue->capacity, sizeof(itemType)); \ 97 | } \ 98 | \ 99 | void typeName ## Free(typeName *queue) \ 100 | { \ 101 | if (!queue->items) \ 102 | return; \ 103 | \ 104 | typeName ## Clear(queue); \ 105 | \ 106 | free(queue->items); \ 107 | queue->items = 0; \ 108 | } \ 109 | \ 110 | void typeName ## Push(typeName *queue, itemType value) \ 111 | { \ 112 | if (!queue->items) \ 113 | return; \ 114 | \ 115 | if (queue->count == queue->capacity) \ 116 | typeName ## __resize(queue); \ 117 | \ 118 | queue->items[queue->tail] = value; \ 119 | queue->tail = (queue->tail + 1) % queue->capacity; \ 120 | queue->count++; \ 121 | } \ 122 | \ 123 | itemType typeName ## Peek(typeName *queue) \ 124 | { \ 125 | if (!queue->items || queue->count == 0) \ 126 | return queue->defaultValue; \ 127 | \ 128 | return queue->items[queue->head]; \ 129 | } \ 130 | \ 131 | itemType typeName ## Pop(typeName *queue) \ 132 | { \ 133 | if (!queue->items || queue->count == 0) \ 134 | return queue->defaultValue; \ 135 | \ 136 | itemType value = queue->items[queue->head]; \ 137 | queue->items[queue->head] = queue->defaultValue; \ 138 | queue->head = (queue->head + 1) % queue->capacity; \ 139 | queue->count--; \ 140 | return value; \ 141 | } \ 142 | \ 143 | bool typeName ## Contains(typeName *queue, itemType value) \ 144 | { \ 145 | if (!queue->items) \ 146 | return false; \ 147 | \ 148 | for(int32_t i = 0; i < queue->count; i++) \ 149 | { \ 150 | if (queue->equalsFn(queue->items[(queue->head + i) % queue->capacity], value)) \ 151 | return true; \ 152 | } \ 153 | \ 154 | return false; \ 155 | } \ 156 | void typeName ## Clear(typeName* queue) \ 157 | { \ 158 | if (!queue->items) \ 159 | return; \ 160 | \ 161 | if (queue->freeFn) \ 162 | { \ 163 | for(int32_t i = 0; i < queue->count; i++) \ 164 | queue->freeFn(queue->items[i]); \ 165 | } \ 166 | \ 167 | queue->count = 0; \ 168 | } 169 | 170 | #endif // SHL_QUEUE_H -------------------------------------------------------------------------------- /queue.md: -------------------------------------------------------------------------------- 1 | # Queue structure 2 | _This project is based on [GenericMap](https://github.com/mystborn/GenericMap) by mystborn, so there similar function names and structures_ 3 | 4 | A simple queue of objects. Internally it is implemented as a circular buffer, so Push is O(n). Pop is O(1). 5 | 6 | ## Defining a Type 7 | Use the macro `shlDeclareQueue` to generate the type and function definitions. It has the following arguments: 8 | 9 | | Argument | Description | 10 | | --- | --- | 11 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 12 | | `itemType` | The type of the list elements. | 13 | 14 | Use the macro `shlDefineQueue` to generate the function implementations. 15 | 16 | | Argument | Description | 17 | | --- | --- | 18 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 19 | | `itemType` | The type of the list elements. | 20 | 21 | ```c 22 | #include "queue.h" 23 | 24 | shlDeclareQueue(IntList, int) 25 | shlDefineQueue(IntList, int) 26 | ``` 27 | 28 | This list allows the following operations: 29 | 30 | | Function | Description | Return type | 31 | | --- | --- | --- | 32 | | `Init`(_typeName_* queue, _typeName_ Options options) | Initializes the data needed for the queue. | void | 33 | | `Free`(_typeName_* queue) | Frees the data used by the queue. It doesn't free the queue itself. | void | 34 | | `Push`(_typeName_* queue, _itemType_ value) | Push an element in the top of the queue. | void | 35 | | `Peek`(_typeName_* queue) | Gets the top of the queue without removing it. | _itemType_ | 36 | | `Pop`(_typeName_* queue) | Remove the top of the queue. | _itemType _ | 37 | | `Contains`(_typeName_* queue, _itemType_ value) | Return `true` if an object is contained in the queue. | bool | 38 | | `Clear`(_typeName_* queue) | Clear the queue, freeing every element if a `freeFn` was provided. Doesn't free the queue itself. | void | 39 | 40 | ## Options 41 | 42 | Each definition of a queue declare a struct _typeName_ Options that is used to initialize the queue. The struct has the following members: 43 | 44 | | Name | Type | Description | 45 | | --- | --- | --- | 46 | | `equalsFn` | bool (*)(const _itemType_, const _itemType_) | _(optional)_ A pointer to a function that takes two elements, and returns `true` if the elements are equals, and returns `false` otherwise. If no `equalsFn` is provided then the operation `Contains` always return `false`. | 47 | | `freeFn` | void (*)(_itemType_) | _(optional)_ A pointer to a function that takes an element and free it. If no `freeFn` is provided, then the operation `Clear` and `Free` doesn't free the elements and the user of the queue is the responsible for free the elements. | 48 | | `defaultValue` | _itemType_ | The value to return when you apply the `Pop` operation and the queue is empty. | 49 | 50 | Example: 51 | ```c 52 | #include 53 | 54 | #include "queue.h" 55 | 56 | bool intEquals(const int x, const int y) 57 | { 58 | return x == y; 59 | } 60 | 61 | shlDeclareQueue(IntQueue, int) 62 | shlDefineQueue(IntQueue, int) 63 | 64 | int main() 65 | { 66 | IntQueueOptions options = (IntQueueOptions){0}; 67 | options.defaultValue = 0; 68 | options.equalsFn = intEquals; 69 | 70 | IntQueue queue; 71 | IntQueueInit(&queue, options); 72 | 73 | for (int i = 0; i < 100; i++) 74 | IntQueuePush(&queue, i); 75 | 76 | int sum = 0; 77 | 78 | // sum all the numbers in the queue 79 | while (queue.count > 0) 80 | { 81 | int value = IntQueuePop(&queue); 82 | sum += value; 83 | } 84 | 85 | printf("The sum of the elements of the queue is %d\n", sum); 86 | 87 | return 0; 88 | } 89 | ``` -------------------------------------------------------------------------------- /set.h: -------------------------------------------------------------------------------- 1 | /* 2 | set.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed list of objects that can be accessed by index. 27 | Provides methods to search, sort, and manipulate lists. 28 | */ 29 | 30 | #ifndef SHL_SET_H 31 | #define SHL_SET_H 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define shlDeclareSet(typeName, itemType) \ 39 | typedef struct \ 40 | { \ 41 | itemType defaultValue; \ 42 | uint32_t (*hashFn)(const itemType item); \ 43 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 44 | void (*freeFn)(itemType item); \ 45 | } typeName ## Options; \ 46 | \ 47 | typedef struct { \ 48 | bool active; \ 49 | uint32_t hash; \ 50 | int32_t next; \ 51 | itemType item; \ 52 | } typeName ## __Entry__; \ 53 | \ 54 | typedef struct { \ 55 | uint32_t count; \ 56 | uint32_t capacity; \ 57 | uint32_t loadFactor; \ 58 | uint32_t shift; \ 59 | uint32_t (*hashFn)(const itemType item); \ 60 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 61 | void (*freeFn)(itemType item); \ 62 | itemType defaultValue; \ 63 | typeName ## __Entry__* entries; \ 64 | } typeName; \ 65 | \ 66 | void typeName ## Init(typeName* map, typeName ## Options options); \ 67 | void typeName ## Free(typeName* map); \ 68 | bool typeName ## Add(typeName* set, itemType item); \ 69 | bool typeName ## Contains(typeName* set, itemType item); \ 70 | void typeName ## Remove(typeName* set, itemType item); \ 71 | void typeName ## Clear(typeName* set); \ 72 | 73 | #define shlDefineSet(typeName, itemType) \ 74 | static uint32_t typeName ## __fibHash(uint32_t hash, uint32_t shift) \ 75 | { \ 76 | const uint32_t hashConstant = 2654435769u; \ 77 | return (hash * hashConstant) >> shift; \ 78 | } \ 79 | \ 80 | static uint32_t typeName ## __findEmptyBucket(typeName* set, uint32_t index) \ 81 | { \ 82 | for(int32_t i = 0; i < set->capacity; i++) \ 83 | { \ 84 | if (!set->entries[(index + i) % set->capacity].active) \ 85 | return (index + i) % set->capacity; \ 86 | } \ 87 | \ 88 | return -1; \ 89 | } \ 90 | \ 91 | static void typeName ## __resize(typeName* set) \ 92 | { \ 93 | uint32_t oldCapacity = set->capacity; \ 94 | typeName ## __Entry__* old = set->entries; \ 95 | \ 96 | set->loadFactor = oldCapacity; \ 97 | set->capacity = 1 << (32 - (--set->shift)); \ 98 | set->entries = (typeName ## __Entry__*)calloc(set->capacity, sizeof(typeName ## __Entry__)); \ 99 | set->count = 0; \ 100 | \ 101 | for(int32_t i = 0; i < oldCapacity; i++) \ 102 | { \ 103 | if(old[i].active) \ 104 | typeName ## Add(set, old[i].item); \ 105 | } \ 106 | free(old); \ 107 | } \ 108 | \ 109 | void typeName ## Init(typeName* set, typeName ## Options options) \ 110 | { \ 111 | set->defaultValue = options.defaultValue; \ 112 | set->hashFn = options.hashFn; \ 113 | set->equalsFn = options.equalsFn; \ 114 | set->freeFn = options.freeFn; \ 115 | set->shift = 29; \ 116 | set->capacity = 8; \ 117 | set->loadFactor = 6; \ 118 | set->count = 0; \ 119 | set->entries = (typeName ## __Entry__ *)calloc(set->capacity, sizeof(typeName ## __Entry__)); \ 120 | } \ 121 | \ 122 | void typeName ## Free(typeName* set) \ 123 | { \ 124 | if (!set->entries) \ 125 | return; \ 126 | \ 127 | typeName ## Clear(set); \ 128 | \ 129 | free(set->entries); \ 130 | set->entries = 0; \ 131 | } \ 132 | \ 133 | bool typeName ## Add(typeName* set, itemType item) \ 134 | { \ 135 | if (!set->entries) \ 136 | return false; \ 137 | \ 138 | if(set->count == set->loadFactor) \ 139 | typeName ## __resize(set); \ 140 | \ 141 | uint32_t hash, index, next; \ 142 | hash = index = typeName ## __fibHash(set->hashFn(item), set->shift); \ 143 | \ 144 | while (set->entries[index].active) \ 145 | { \ 146 | if(set->entries[index].hash == hash && set->equalsFn(set->entries[index].item, item)) \ 147 | return false; \ 148 | \ 149 | if (set->entries[index].next < 0) \ 150 | break; \ 151 | \ 152 | index = set->entries[index].next; \ 153 | } \ 154 | \ 155 | next = typeName ## __findEmptyBucket(set, index); \ 156 | if (index != next) \ 157 | set->entries[index].next = next; \ 158 | \ 159 | set->entries[next].active = true; \ 160 | set->entries[next].item = item; \ 161 | set->entries[next].hash = hash; \ 162 | set->entries[next].next = -1; \ 163 | set->count++; \ 164 | return true; \ 165 | } \ 166 | \ 167 | bool typeName ## Contains(typeName* set, itemType item) \ 168 | { \ 169 | if (!set->entries) \ 170 | return false; \ 171 | \ 172 | uint32_t index, hash; \ 173 | hash = index = typeName ## __fibHash(set->hashFn(item), set->shift); \ 174 | \ 175 | bool found = false; \ 176 | \ 177 | while (set->entries[index].active) \ 178 | { \ 179 | if(set->entries[index].hash == hash && set->equalsFn(set->entries[index].item, item)) \ 180 | { \ 181 | found = true; \ 182 | break; \ 183 | } \ 184 | \ 185 | if (set->entries[index].next < 0) \ 186 | break; \ 187 | \ 188 | index = set->entries[index].next; \ 189 | } \ 190 | \ 191 | return found; \ 192 | } \ 193 | \ 194 | void typeName ## Remove(typeName* set, itemType item) \ 195 | { \ 196 | if (!set->entries) \ 197 | return; \ 198 | \ 199 | uint32_t prevIndex, index, hash; \ 200 | hash = prevIndex = index = typeName ## __fibHash(set->hashFn(item), set->shift); \ 201 | \ 202 | while (set->entries[index].active) \ 203 | { \ 204 | if(set->entries[index].hash == hash && set->equalsFn(set->entries[index].item, item)) \ 205 | { \ 206 | itemType item = set->entries[index].item; \ 207 | \ 208 | set->entries[prevIndex].next = set->entries[index].next; \ 209 | set->entries[index].item = set->defaultValue; \ 210 | set->entries[index].active = false; \ 211 | \ 212 | if (set->freeFn) \ 213 | set->freeFn(item); \ 214 | \ 215 | break; \ 216 | } \ 217 | \ 218 | if (set->entries[index].next < 0) \ 219 | break; \ 220 | \ 221 | prevIndex = index; \ 222 | index = set->entries[index].next; \ 223 | } \ 224 | set->count--; \ 225 | } \ 226 | \ 227 | void typeName ## Clear(typeName* set) \ 228 | { \ 229 | if (!set->entries) \ 230 | return; \ 231 | \ 232 | for(int32_t i = 0; i < set->count; i++) \ 233 | { \ 234 | if (set->entries[i].active) \ 235 | { \ 236 | if (set->freeFn) \ 237 | set->freeFn(set->entries[i].item); \ 238 | \ 239 | set->entries[i].active = false; \ 240 | } \ 241 | } \ 242 | \ 243 | set->count = 0; \ 244 | } 245 | 246 | #endif //SHL_SET_H -------------------------------------------------------------------------------- /set.md: -------------------------------------------------------------------------------- 1 | # Map structure 2 | _This project is based on [GenericMap](https://github.com/mystborn/GenericMap) by mystborn, so there similar function names and structures_ 3 | 4 | Represents a strongly typed collection of non-repeating values with fast access via hashing. 5 | 6 | ## Defining a Type 7 | Use the macro `shlDeclareSet` to generate the type and function definitions. It has the following arguments: 8 | 9 | | Argument | Description | 10 | | --- | --- | 11 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 12 | | `itemType` | The type of the set elements. | 13 | 14 | Use the macro `shlDefineSet` to generate the function implementations. 15 | 16 | | Argument | Description | 17 | | --- | --- | 18 | | `typeName` | The name of the generated type. This will also prefix all of the function names. | 19 | | `itemType` | The type of the set elements. | 20 | 21 | ```c 22 | #include "set.h" 23 | 24 | shlDeclareSet(SSet, const char*) 25 | shlDefineSet(SSet, const char*) 26 | ``` 27 | 28 | The set structure allows the following operations (all functions all prefixed with _typeName_): 29 | 30 | void typeName ## Init(typeName* map, typeName ## Options options); \ 31 | void typeName ## Free(typeName* map); \ 32 | void typeName ## Add(typeName* set, itemType item); \ 33 | bool typeName ## Contains(typeName* set, itemType item); \ 34 | void typeName ## Remove(typeName* set, itemType item); \ 35 | void typeName ## Clear(typeName* set); \ 36 | 37 | | Function | Description | Return type | 38 | | --- | --- | --- | 39 | | `Init`(_typeName_* set, _typeName_ Options options) | Initializes the data needed for the set. | void | 40 | | `Free`(_typeName_* set) | Frees the data used by the set. It doesn't free the set itself. | void | 41 | | `Add`(_typeName_* set, _itemType_ item) | Add an item to the set and returns `true` if it was inserted, `false` otherwise. | bool | 42 | | `Contains`(_typeName_* set, _itemType_ item) | Return `true` an item is contained in the set. | bool | 43 | | `Remove`(_typeName_* set, _itemType_ item) | Remove the item `item` from the set, freeing the item if a `freeFn` function was provided. | void | 44 | | `Clear`(_typeName_* set) | Clear the set, freeing every element if a `freeFn` was provided. Doesn't free the set itself. | void | 45 | 46 | ## Options 47 | 48 | Each definition of a set declare a struct _typeName_ Options that is used to initialize the map. The struct has the following members: 49 | 50 | | Name | Type | Description | 51 | | --- | --- | --- | 52 | | `hashFn` | uint32_t (*)(const _itemType_) | A pointer to a function that takes an item and returns a hash value for that item. | 53 | | `equalsFn` | bool (*)(const _itemType_, const _itemType_) | A pointer to a function that takes two items, and returns `true` if the items are equals, and returns `false` otherwise. | 54 | | `freeFn` | void (*)(_itemType_) | _(optional)_ A pointer to a function that takes an element and free it. If no `freeFn` is provided, then the operations `Remove`, `Clear` and `Free` doesn't free the elements and the user of the set is the responsible for freeing the elements. | 55 | | `defaultValue` | _itemType_ | For the set this is an internal value used when you remove an element. | 56 | 57 | Example: 58 | ```c 59 | #include 60 | #include 61 | 62 | #include "set.h" 63 | 64 | uint32_t fnv32(const char* data) 65 | { 66 | uint32_t hash = FNV_OFFSET_32; 67 | while(*data != 0) 68 | hash = (*data++ ^ hash) * FNV_PRIME_32; 69 | 70 | return hash; 71 | } 72 | 73 | bool equalsStr(const char *s1, const char *s2) 74 | { 75 | return strcmp(s1, s2) == 0; 76 | } 77 | 78 | shlDeclareSet(SSet, const char*) 79 | shlDefineSet(SSet, const char*) 80 | 81 | int main() 82 | { 83 | SSetOptions options = (SSetOptions){0}; 84 | options.hashFn = fnv32; 85 | options.equalsFn = equalsStr; 86 | options.defaultValue = NULL; 87 | 88 | SSet set; 89 | SSetInit(&set, options); 90 | 91 | char *strings[9] = 92 | { 93 | "tvnccxxgqofssyaikgij", "dehnxzqjek", "mmtlycyoosuieqw", 94 | "tvnccxxgqofssyaikgij", "tvnccxxgqofssyaikgij", "mmtlycyoosuieqw", 95 | "tvncccyoosuixxgqofssyaikgij", "dehn", "tvnccxxgqofssyaikgij" 96 | } 97 | 98 | for(int i = 0; i < 9; i++) 99 | SSetAdd(&map, strings[i]); 100 | 101 | printf("The number of items in the set is %d\n", set.count); 102 | 103 | return 0; 104 | } 105 | ``` -------------------------------------------------------------------------------- /stack.h: -------------------------------------------------------------------------------- 1 | /* 2 | stack.h - acoto87 (acoto87@gmail.com) 3 | 4 | MIT License 5 | 6 | Copyright (c) 2018 Alejandro Coto Gutiérrez 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | This is a single header file with macros to declare and define a strongly typed stack with push, pop and peek operations. 27 | */ 28 | #ifndef SHL_STACK_H 29 | #define SHL_STACK_H 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define shlDeclareStack(typeName, itemType) \ 37 | typedef struct \ 38 | { \ 39 | itemType defaultValue; \ 40 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 41 | void (*freeFn)(itemType item); \ 42 | } typeName ## Options; \ 43 | \ 44 | typedef struct \ 45 | { \ 46 | uint32_t count; \ 47 | uint32_t capacity; \ 48 | bool (*equalsFn)(const itemType item1, const itemType item2); \ 49 | void (*freeFn)(itemType item); \ 50 | itemType defaultValue; \ 51 | itemType *items; \ 52 | } typeName; \ 53 | \ 54 | void typeName ## Init(typeName *stack, typeName ## Options options); \ 55 | void typeName ## Free(typeName *stack); \ 56 | void typeName ## Push(typeName *stack, itemType value); \ 57 | bool typeName ## Contains(typeName *stack, itemType value); \ 58 | itemType typeName ## Peek(typeName *stack); \ 59 | itemType typeName ## Pop(typeName *stack); \ 60 | void typeName ## Clear(typeName *stack); 61 | 62 | #define shlDefineStack(typeName, itemType) \ 63 | void typeName ## __resize(typeName *stack) \ 64 | { \ 65 | uint32_t oldCapacity = stack->capacity; \ 66 | \ 67 | stack->capacity = oldCapacity << 1; \ 68 | stack->items = (itemType *)realloc(stack->items, stack->capacity * sizeof(itemType)); \ 69 | } \ 70 | \ 71 | void typeName ## Init(typeName *stack, typeName ## Options options) \ 72 | { \ 73 | stack->defaultValue = options.defaultValue; \ 74 | stack->equalsFn = options.equalsFn; \ 75 | stack->freeFn = options.freeFn; \ 76 | stack->capacity = 8; \ 77 | stack->count = 0; \ 78 | stack->items = (itemType *)calloc(stack->capacity, sizeof(itemType)); \ 79 | } \ 80 | \ 81 | void typeName ## Free(typeName *stack) \ 82 | { \ 83 | if (!stack->items) \ 84 | return; \ 85 | \ 86 | typeName ## Clear(stack); \ 87 | \ 88 | free(stack->items); \ 89 | stack->items = 0; \ 90 | } \ 91 | \ 92 | void typeName ## Push(typeName *stack, itemType value) \ 93 | { \ 94 | if (!stack->items) \ 95 | return; \ 96 | \ 97 | if (stack->count == stack->capacity) \ 98 | typeName ## __resize(stack); \ 99 | \ 100 | stack->items[stack->count] = value; \ 101 | stack->count++; \ 102 | } \ 103 | \ 104 | itemType typeName ## Peek(typeName *stack) \ 105 | { \ 106 | if (!stack->items || stack->count == 0) \ 107 | return stack->defaultValue; \ 108 | \ 109 | return stack->items[stack->count - 1]; \ 110 | } \ 111 | \ 112 | itemType typeName ## Pop(typeName *stack) \ 113 | { \ 114 | if (!stack->items || stack->count == 0) \ 115 | return stack->defaultValue; \ 116 | \ 117 | itemType item = stack->items[stack->count - 1]; \ 118 | stack->count--; \ 119 | return item; \ 120 | } \ 121 | \ 122 | bool typeName ## Contains(typeName *stack, itemType value) \ 123 | { \ 124 | if (!stack->items) \ 125 | return false; \ 126 | \ 127 | if (!stack->equalsFn) \ 128 | return false; \ 129 | \ 130 | for(int32_t i = 0; i < stack->count; i++) \ 131 | { \ 132 | if (stack->equalsFn(stack->items[i], value)) \ 133 | return true; \ 134 | } \ 135 | \ 136 | return false; \ 137 | } \ 138 | \ 139 | void typeName ## Clear(typeName* stack) \ 140 | { \ 141 | if (!stack->items) \ 142 | return; \ 143 | \ 144 | if (stack->freeFn) \ 145 | { \ 146 | for(int32_t i = 0; i < stack->count; i++) \ 147 | stack->freeFn(stack->items[i]); \ 148 | } \ 149 | \ 150 | stack->count = 0; \ 151 | } 152 | 153 | #endif // SHL_STACK_H -------------------------------------------------------------------------------- /stack.md: -------------------------------------------------------------------------------- 1 | # Stack structure 2 | _This project is based on [GenericMap](https://github.com/mystborn/GenericMap) by mystborn, so there similar function names and structures_ 3 | 4 | A simple stack of objects. Internally it is implemented as an array, so Push is O(n). Pop is O(1). 5 | 6 | ## Defining a Type 7 | Use the macro `shlDeclareStack` to generate the type and function definitions. It has the following arguments: 8 | 9 | | Argument | Description | 10 | | --- | --- | 11 | | typeName | The name of the generated type. This will also prefix all of the function names. | 12 | | itemType | The type of the list elements. | 13 | 14 | Use the macro `shlDefineStack` to generate the function implementations. 15 | 16 | | Argument | Description | 17 | | --- | --- | 18 | | typeName | The name of the generated type. This will also prefix all of the function names. | 19 | | itemType | The type of the list elements. | 20 | 21 | ```c 22 | #include "stack.h" 23 | 24 | shlDeclareStack(IntStack, int) 25 | shlDefineStack(IntStack, int) 26 | ``` 27 | 28 | This list allows the following operations: 29 | 30 | | Function | Description | Return type | 31 | | --- | --- | --- | 32 | | `Init`(_typeName_ *stack, _typeName_ Options options) | Initializes the data needed for the stack. | void | 33 | | `Free`(_typeName_ *stack) | Frees the data used by the stack. It doesn't free the stack itself. | void | 34 | | `Push`(_typeName_ *stack, _itemType_ value) | Push an element in the top of the stack. | void | 35 | | `Peek`(_typeName_ *stack) | Gets the top of the stack without removing it. | _itemType_ | 36 | | `Pop`(_typeName_ *stack) | Remove the top of the stack. | _itemType _ | 37 | | `Contains`(_typeName_ *stack, _itemType_ value) | Return `true` if an object is contained in the stack. | bool | 38 | | `Clear`(_typeName_* queue) | Clear the stack, freeing every element if a `freeFn` was provided. Doesn't free the stack itself. | void | 39 | 40 | ## Options 41 | 42 | Each definition of a queue declare a struct _typeName_ Options that is used to initialize the stack. The struct has the following members: 43 | 44 | | Name | Type | Description | 45 | | --- | --- | --- | 46 | | `equalsFn` | bool (*)(const _itemType_, const _itemType_) | _(optional)_ A pointer to a function that takes two elements, and returns `true` if the elements are equals, and returns `false` otherwise. If no `equalsFn` is provided then the operation `Contains` always return `false`. | 47 | | `freeFn` | void (*)(_itemType_) | _(optional)_ A pointer to a function that takes an element and free it. If no `freeFn` is provided, then the operation `Clear` and `Free` doesn't free the elements and the user of the stack is the responsible for free the elements. | 48 | | `defaultValue` | _itemType_ | The value to return when you apply the `Pop` operation and the stack is empty. | 49 | 50 | Example: 51 | ```c 52 | #include 53 | 54 | #include "stack.h" 55 | 56 | bool intEquals(const int x, const int y) 57 | { 58 | return x == y; 59 | } 60 | 61 | shlDeclareStack(IntStack, int) 62 | shlDefineStack(IntStack, int) 63 | 64 | int main() 65 | { 66 | IntStackOptions options = (IntStackOptions){0}; 67 | options.defaultValue = 0; 68 | options.equalsFn = intEquals; 69 | 70 | IntStack stack; 71 | IntStackInit(&stack, options); 72 | 73 | for (int i = 0; i < 100; i++) 74 | IntStackPush(&stack, i); 75 | 76 | int sum = 0; 77 | 78 | // sum all the numbers in the stack 79 | while (stack.count > 0) 80 | { 81 | int value = IntStackPop(&stack); 82 | sum += value; 83 | } 84 | 85 | printf("The sum of the elements of the stack is %d\n", sum); 86 | 87 | return 0; 88 | } 89 | ``` -------------------------------------------------------------------------------- /tests/array_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../array.h" 8 | 9 | static const int32_t N = 10; 10 | static const int32_t M = 10; 11 | 12 | shlDefineCreateArray(float, float) 13 | shlDefineFreeArray(float, float) 14 | 15 | void floatPrintArray(int n, int m, float** arr) 16 | { 17 | for(int i = 0; i < n; i++) 18 | { 19 | printf("{ "); 20 | 21 | for(int j = 0; j < m; j++) 22 | { 23 | if (j > 0) 24 | printf(", "); 25 | 26 | printf("%.2f", arr[i][j]); 27 | } 28 | 29 | printf(" }\n"); 30 | } 31 | } 32 | 33 | void valueTypeTest() 34 | { 35 | float** arr = floatCreateArray(N, M); 36 | 37 | for(int i = 0; i < N; i++) 38 | { 39 | for(int j = 0; j < M; j++) 40 | { 41 | arr[i][j] = i * j; 42 | } 43 | } 44 | 45 | floatPrintArray(N, M, arr); 46 | 47 | floatFreeArray(arr); 48 | } 49 | 50 | typedef struct 51 | { 52 | float x, y; 53 | } Point; 54 | 55 | shlDefineCreateArray(point, Point*) 56 | shlDefineFreeArray(point, Point*) 57 | 58 | void pointPrintArray(int n, int m, Point*** arr) 59 | { 60 | for(int i = 0; i < n; i++) 61 | { 62 | printf("{ "); 63 | 64 | for(int j = 0; j < m; j++) 65 | { 66 | if (j > 0) 67 | printf(", "); 68 | 69 | printf("(%.2f, %.2f)", arr[i][j]->x, arr[i][j]->y); 70 | } 71 | 72 | printf(" }\n"); 73 | } 74 | } 75 | 76 | void referenceTypeTest() 77 | { 78 | Point*** arr = pointCreateArray(N, M); 79 | 80 | for(int i = 0; i < N; i++) 81 | { 82 | for(int j = 0; j < M; j++) 83 | { 84 | arr[i][j] = (Point*)malloc(sizeof(Point)); 85 | arr[i][j]->x = (float)i; 86 | arr[i][j]->y = (float)j; 87 | } 88 | } 89 | 90 | pointPrintArray(N, M, arr); 91 | 92 | pointFreeArray(arr); 93 | } 94 | 95 | int main(int argc, char **argv) 96 | { 97 | /* initialize random seed: */ 98 | srand(time(NULL)); 99 | 100 | valueTypeTest(); 101 | 102 | printf("\n\n"); 103 | 104 | referenceTypeTest(); 105 | 106 | return 0; 107 | } -------------------------------------------------------------------------------- /tests/binary_heap_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../binary_heap.h" 9 | 10 | static const int32_t count = 100000; 11 | 12 | static float getTime() 13 | { 14 | return (float)clock() / CLOCKS_PER_SEC; 15 | } 16 | 17 | int32_t compareInt(const int a, const int b) 18 | { 19 | return a - b; 20 | } 21 | 22 | shlDeclareBinaryHeap(IntHeap, int) 23 | shlDefineBinaryHeap(IntHeap, int) 24 | 25 | void valueTypeTest() 26 | { 27 | float start, end; 28 | int min = count; 29 | 30 | IntHeapOptions options = {0}; 31 | options.compareFn = compareInt; 32 | options.defaultValue = 0; 33 | 34 | IntHeap heap; 35 | IntHeapInit(&heap, options); 36 | 37 | printf("--- Start test 1: add %d objects ---\n", count); 38 | start = getTime(); 39 | for(int i = 0; i < count; i++) 40 | { 41 | int x = rand() % count; 42 | if (x < min) min = x; 43 | IntHeapPush(&heap, x); 44 | assert(heap.count == i + 1); 45 | } 46 | end = getTime(); 47 | printf("BinaryHeap count and capacity: (%d, %d)\n", heap.count, heap.capacity); 48 | printf("Time: %.2f seconds\n", end - start); 49 | printf("--- End test 1: add %d objects ---\n", count); 50 | 51 | printf("\n"); 52 | 53 | printf("--- Start test 2: peek min object ---\n"); 54 | start = getTime(); 55 | int peek = IntHeapPeek(&heap); 56 | assert(peek == min); 57 | end = getTime(); 58 | printf("Time: %.2f seconds\n", end - start); 59 | printf("--- End test 2: peek min object ---\n"); 60 | 61 | printf("\n"); 62 | 63 | printf("--- Start test 3: pop min object ---\n"); 64 | start = getTime(); 65 | int prev = -1; 66 | while (heap.count > 0) 67 | { 68 | int x = IntHeapPop(&heap); 69 | if (prev >= 0) 70 | assert(prev <= x); 71 | 72 | prev = x; 73 | } 74 | end = getTime(); 75 | printf("Time: %.2f seconds\n", end - start); 76 | printf("--- End test 3: pop min object ---\n"); 77 | } 78 | 79 | int32_t compareStrLength(const char* a, const char* b) 80 | { 81 | return strlen(a) - strlen(b); 82 | } 83 | 84 | shlDeclareBinaryHeap(SHeap, const char*) 85 | shlDefineBinaryHeap(SHeap, const char*) 86 | 87 | char* generateString() 88 | { 89 | const int stringLength = 50; 90 | char *s = (char*)calloc(stringLength + 1, sizeof(char)); 91 | 92 | for(int i = 0; i < 50; i++) 93 | s[i] = rand() % 27 + 97; 94 | 95 | return s; 96 | } 97 | 98 | void freeStr(const char* str) 99 | { 100 | free((void*)str); 101 | } 102 | 103 | void referenceTypeTest() 104 | { 105 | float start, end; 106 | int min = INT32_MAX; 107 | 108 | printf("--- Start generating %d tests strings ---\n", count); 109 | start = getTime(); 110 | char *strings[100000]; 111 | for(int i = 0; i < count; i++) 112 | strings[i] = generateString(); 113 | end = getTime(); 114 | printf("Time: %.2f seconds\n", end - start); 115 | printf("--- End generating %d tests strings ---\n", count); 116 | 117 | printf("\n\n"); 118 | 119 | SHeapOptions options = {0}; 120 | options.compareFn = compareStrLength; 121 | options.defaultValue = NULL; 122 | options.freeFn = freeStr; 123 | 124 | SHeap heap; 125 | SHeapInit(&heap, options); 126 | 127 | printf("--- Start test 1: add %d objects ---\n", count); 128 | start = getTime(); 129 | for(int i = 0; i < count; i++) 130 | { 131 | int index = rand() % count; 132 | int len = strlen(strings[index]); 133 | if (len < min) min = len; 134 | SHeapPush(&heap, strings[index]); 135 | assert(heap.count == i + 1); 136 | } 137 | end = getTime(); 138 | printf("BinaryHeap count and capacity: (%d, %d)\n", heap.count, heap.capacity); 139 | printf("Time: %.2f seconds\n", end - start); 140 | printf("--- End test 1: add %d objects ---\n", count); 141 | 142 | printf("\n"); 143 | 144 | printf("--- Start test 2: peek min object ---\n"); 145 | start = getTime(); 146 | const char* peek = SHeapPeek(&heap); 147 | assert(strlen(peek) == min); 148 | end = getTime(); 149 | printf("Time: %.2f seconds\n", end - start); 150 | printf("--- End test 2: peek min object ---\n"); 151 | 152 | printf("\n"); 153 | 154 | printf("--- Start test 3: pop min object ---\n"); 155 | start = getTime(); 156 | int prev = -1; 157 | while (heap.count > 0) 158 | { 159 | const char* str = SHeapPop(&heap); 160 | int len = strlen(str); 161 | if (prev >= 0) 162 | assert(prev <= len); 163 | 164 | prev = len; 165 | } 166 | end = getTime(); 167 | printf("Time: %.2f seconds\n", end - start); 168 | printf("--- End test 3: pop min object ---\n"); 169 | } 170 | 171 | int main(int argc, char **argv) 172 | { 173 | /* initialize random seed: */ 174 | srand(time(NULL)); 175 | 176 | valueTypeTest(); 177 | 178 | printf("\n\n"); 179 | 180 | referenceTypeTest(); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /tests/build-gcc-rpi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Output path (relative to build sh file) 4 | OutputPath="./build/arm32" 5 | 6 | # Compiler flags 7 | # ProfilerFlags="-pg" 8 | # OptimizeFlags="-O2" 9 | # AssemblyFlags="-g -Wa,-ahl" 10 | # DebugFlags="-g" 11 | CommonCompilerFlags="-std=c99 -Wall -x c $ProfilerFlags $OptimizeFlags $AssemblyFlags $DebugFlags" 12 | CommonLinkerFlags="-l m" 13 | 14 | # Create output path if doesn't exists 15 | mkdir -p $OutputPath 16 | cd $OutputPath 17 | 18 | # Empty the build folder 19 | rm -f * 20 | 21 | # Compile the tests 22 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../wave_writer_test.c -o wave_writer_test 23 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../array_test.c -o array_test 24 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../binary_heap_test.c -o binary_heap_test 25 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../list_test.c -o list_test 26 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../map_test.c -o map_test 27 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../set_test.c -o set_test 28 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../queue_test.c -o queue_test 29 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../stack_test.c -o stack_test 30 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../memory_buffer_test.c -o memory_buffer_test 31 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../xmi2mid.c -o xmi2mid 32 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../flic_test.c -o flic_test 33 | gcc $CommonCompilerFlags $CommonLinkerFlags ../../memzone_test.c -o memzone_test 34 | -------------------------------------------------------------------------------- /tests/build-gcc-win32.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Output path (relative to build bat file) 4 | SET OutputPath=.\build\win32 5 | 6 | REM Compiler flags 7 | REM ProfilerFlags="-pg" 8 | REM OptimizeFlags="-O2" 9 | REM AssemblyFlags="-g -Wa,-ahl" 10 | REM DebugFlags="-g -O1 -D __DEBUG__" 11 | SET CommonCompilerFlags=-std=c99 -g -Wall -x c %ProfilerFlags% %OptimizeFlags% %AssemblyFlags% %DebugFlags% 12 | 13 | REM Create output path if doesn't exists 14 | IF NOT EXIST %OutputPath% MKDIR %OutputPath% 15 | PUSHD %OutputPath% 16 | 17 | REM Empty the build folder 18 | DEL /Q * 19 | 20 | REM Compile the tests 21 | ECHO Compiling wave_writer_test 22 | gcc %CommonCompilerFlags% ..\..\wave_writer_test.c -o wave_writer_test.exe 23 | ECHO Compiling array_test 24 | gcc %CommonCompilerFlags% ..\..\array_test.c -o array_test.exe 25 | ECHO Compiling binary_heap_test 26 | gcc %CommonCompilerFlags% ..\..\binary_heap_test.c -o binary_heap_test.exe 27 | ECHO Compiling list_test 28 | gcc %CommonCompilerFlags% ..\..\list_test.c -o list_test.exe 29 | ECHO Compiling map_test 30 | gcc %CommonCompilerFlags% ..\..\map_test.c -o map_test.exe 31 | ECHO Compiling set_test 32 | gcc %CommonCompilerFlags% ..\..\set_test.c -o set_test.exe 33 | ECHO Compiling queue_test 34 | gcc %CommonCompilerFlags% ..\..\queue_test.c -o queue_test.exe 35 | ECHO Compiling stack_test 36 | gcc %CommonCompilerFlags% ..\..\stack_test.c -o stack_test.exe 37 | ECHO Compiling memory_buffer_test 38 | gcc %CommonCompilerFlags% ..\..\memory_buffer_test.c -o memory_buffer_test.exe 39 | ECHO Compiling xmi2mid 40 | gcc %CommonCompilerFlags% ..\..\xmi2mid.c -o xmi2mid.exe 41 | ECHO Compiling flic_test 42 | gcc %CommonCompilerFlags% ..\..\flic_test.c -o flic_test.exe 43 | ECHO Compiling memzone_test 44 | gcc %CommonCompilerFlags% ..\..\memzone_test.c -o memzone_test.exe 45 | 46 | POPD 47 | -------------------------------------------------------------------------------- /tests/build-gcc-win64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Output path (relative to build bat file) 4 | SET OutputPath=.\build\win64 5 | 6 | REM Compiler flags 7 | REM ProfilerFlags="-pg" 8 | REM OptimizeFlags="-O2" 9 | REM AssemblyFlags="-g -Wa,-ahl" 10 | REM DebugFlags="-g -O1 -D __DEBUG__" 11 | SET CommonCompilerFlags=-std=c99 -Wall -x c %ProfilerFlags% %OptimizeFlags% %AssemblyFlags% %DebugFlags% 12 | 13 | REM Create output path if doesn't exists 14 | IF NOT EXIST %OutputPath% MKDIR %OutputPath% 15 | PUSHD %OutputPath% 16 | 17 | REM Empty the build folder 18 | DEL /Q * 19 | 20 | REM Compile the tests 21 | ECHO Compiling wave_writer_test 22 | gcc %CommonCompilerFlags% ..\..\wave_writer_test.c -o wave_writer_test.exe 23 | ECHO Compiling array_test 24 | gcc %CommonCompilerFlags% ..\..\array_test.c -o array_test.exe 25 | ECHO Compiling binary_heap_test 26 | gcc %CommonCompilerFlags% ..\..\binary_heap_test.c -o binary_heap_test.exe 27 | ECHO Compiling list_test 28 | gcc %CommonCompilerFlags% ..\..\list_test.c -o list_test.exe 29 | ECHO Compiling map_test 30 | gcc %CommonCompilerFlags% ..\..\map_test.c -o map_test.exe 31 | ECHO Compiling set_test 32 | gcc %CommonCompilerFlags% ..\..\set_test.c -o set_test.exe 33 | ECHO Compiling queue_test 34 | gcc %CommonCompilerFlags% ..\..\queue_test.c -o queue_test.exe 35 | ECHO Compiling stack_test 36 | gcc %CommonCompilerFlags% ..\..\stack_test.c -o stack_test.exe 37 | ECHO Compiling memory_buffer_test 38 | gcc %CommonCompilerFlags% ..\..\memory_buffer_test.c -o memory_buffer_test.exe 39 | ECHO Compiling xmi2mid 40 | gcc %CommonCompilerFlags% ..\..\xmi2mid.c -o xmi2mid.exe 41 | ECHO Compiling flic_test 42 | gcc %CommonCompilerFlags% ..\..\flic_test.c -o flic_test.exe 43 | ECHO Compiling memzone_test 44 | gcc %CommonCompilerFlags% ..\..\memzone_test.c -o memzone_test.exe 45 | 46 | POPD 47 | -------------------------------------------------------------------------------- /tests/canvas2d_test.c: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | #define _CRT_SECURE_NO_WARNINGS 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #define GLFW_DLL 16 | #include 17 | 18 | #include "cglm.h" 19 | 20 | #include "nanovg.c" 21 | #define NANOVG_GL3_IMPLEMENTATION 22 | #include "nanovg_gl.h" 23 | 24 | static void glfwErrorCallback(int error, const char* description) 25 | { 26 | fprintf(stderr, "Error: %d, %s\n", error, description); 27 | } 28 | 29 | int main(int argc, char *argv[]) 30 | { 31 | glfwSetErrorCallback(glfwErrorCallback); 32 | 33 | if (!glfwInit()) 34 | { 35 | printf("Error initializing GLFW!"); 36 | return -1; 37 | } 38 | 39 | uint32_t windowWidth = 600; 40 | uint32_t windowHeight = 400; 41 | uint32_t fbWidth, fbHeight; 42 | char *windowTitle = "Canvas 2d Test"; 43 | 44 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 45 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); 46 | glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 47 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 48 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 49 | 50 | GLFWwindow *window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, NULL, NULL); 51 | if (!window) 52 | { 53 | fprintf(stderr, "GLFW window could not be created!"); 54 | glfwTerminate(); 55 | return -1; 56 | } 57 | 58 | glfwGetWindowSize(window, &windowWidth, &windowHeight); 59 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 60 | 61 | glfwMakeContextCurrent(window); 62 | 63 | glewExperimental = GL_TRUE; 64 | GLenum glewError = glewInit(); 65 | if (glewError != GLEW_OK) 66 | { 67 | fprintf(stderr, "Error initializing GLEW! %s\n", glewGetErrorString(glewError)); 68 | glfwDestroyWindow(window); 69 | glfwTerminate(); 70 | return -1; 71 | } 72 | 73 | // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. 74 | glGetError(); 75 | 76 | NVGcontext *vg = nvgCreateGL3(NVG_STENCIL_STROKES | NVG_DEBUG); 77 | if (!vg) { 78 | fprintf(stderr, "Could not init nanovg.\n"); 79 | return -1; 80 | } 81 | 82 | glViewport(0, 0, fbWidth, fbHeight); 83 | glClearColor(0.3f, 0.3f, 0.3f, 1.0f); 84 | 85 | float time = 0; 86 | float deltaTime = 0; 87 | uint32_t fps = 0; 88 | 89 | while (!glfwWindowShouldClose(window)) 90 | { 91 | sprintf(windowTitle, "War 1: %.2f at %d fps", time, fps); 92 | glfwSetWindowTitle(window, windowTitle); 93 | 94 | // input 95 | if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) 96 | { 97 | glfwSetWindowShouldClose(window, GL_TRUE); 98 | continue; 99 | } 100 | 101 | // Update and render 102 | glfwGetWindowSize(window, &windowWidth, &windowHeight); 103 | glfwGetFramebufferSize(window, &fbWidth, &fbHeight); 104 | float pxRatio = (float)fbWidth / (float)windowWidth; 105 | 106 | glViewport(0, 0, fbWidth, fbHeight); 107 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 108 | 109 | nvgBeginFrame(vg, windowWidth, windowHeight, pxRatio); 110 | 111 | nvgBeginPath(vg); 112 | nvgRect(vg, 100, 100, 200, 200); 113 | // nvgFillColor(vg, nvgRGBA(255, 0, 0, 255)); 114 | // nvgFill(vg); 115 | 116 | nvgStrokeWidth(vg, 2.0f); 117 | nvgStrokeColor(vg, nvgRGBA(255, 0, 0, 255)); 118 | nvgStroke(vg); 119 | 120 | nvgEndFrame(vg); 121 | 122 | // swap buffers 123 | glfwSwapBuffers(window); 124 | glfwPollEvents(); 125 | 126 | float currentTime = glfwGetTime(); 127 | deltaTime = (currentTime - time); 128 | 129 | // force to 60 fps 130 | while (deltaTime <= (1.0f/60.0f)) 131 | { 132 | currentTime = (float)glfwGetTime(); 133 | deltaTime = (currentTime - time); 134 | } 135 | 136 | time = currentTime; 137 | fps = (uint32_t)(1.0f / deltaTime); 138 | } 139 | 140 | nvgDeleteGL3(vg); 141 | glfwDestroyWindow(window); 142 | glfwTerminate(); 143 | return 0; 144 | } -------------------------------------------------------------------------------- /tests/flic_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 5 | #include "stb_image_resize.h" 6 | #define STB_IMAGE_WRITE_IMPLEMENTATION 7 | #include "stb_image_write.h" 8 | 9 | #define SHL_FLIC_IMPLEMENTATION 10 | #include "../flic.h" 11 | 12 | int main(int argc, char **argv) 13 | { 14 | if (argc == 1) 15 | { 16 | printf("Need path to FLIC file\n"); 17 | return -1; 18 | } 19 | 20 | const char* fileName = argv[1]; 21 | 22 | Flic flic; 23 | if (!flicOpen(&flic, fileName)) 24 | { 25 | printf("Could not open the FLIC file\n"); 26 | return -1; 27 | } 28 | 29 | // accumulative image data 30 | uint8_t* imageData = (uint8_t*)calloc(flic.width * flic.height * 3, sizeof(uint8_t)); 31 | 32 | for (int i = 0; i < flic.frames; i++) 33 | { 34 | FlicFrame frame; 35 | frame.pixels = (uint16_t*)calloc(flic.width * flic.height, sizeof(uint16_t)); 36 | frame.rowStride = flic.width; 37 | if (flicReadFrame(&flic, &frame)) 38 | { 39 | flicMakeImage(&flic, &frame, imageData); 40 | 41 | char imageFileName[16]; 42 | sprintf(imageFileName, "frame%02d.bmp", i); 43 | stbi_write_bmp(imageFileName, flic.width, flic.height, 3, imageData); 44 | } 45 | } 46 | 47 | flicClose(&flic); 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /tests/list_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../list.h" 8 | 9 | static const int32_t count = 100000; 10 | 11 | static float getTime() 12 | { 13 | return (float)clock() / CLOCKS_PER_SEC; 14 | } 15 | 16 | // value type test 17 | bool intEquals(const int x, const int y) 18 | { 19 | return x == y; 20 | } 21 | 22 | int32_t intCompare(const int x, const int y) 23 | { 24 | return x - y; 25 | } 26 | 27 | shlDeclareList(IntList, int) 28 | shlDefineList(IntList, int) 29 | 30 | void valueTypeTest() 31 | { 32 | float start, end; 33 | 34 | IntListOptions options = {0}; 35 | options.defaultValue = 0; 36 | options.equalsFn = intEquals; 37 | 38 | IntList list; 39 | IntListInit(&list, options); 40 | 41 | printf("--- Start value type tests ---\n"); 42 | 43 | printf("--- Start test 1: add %d objects ---\n", count); 44 | start = getTime(); 45 | for(int i = 0; i < count; i++) 46 | { 47 | IntListAdd(&list, i); 48 | assert(list.items[list.count - 1] == i); 49 | } 50 | end = getTime(); 51 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 52 | printf("Time: %.2f seconds\n", end - start); 53 | printf("--- End test 1: add %d objects ---\n", count); 54 | 55 | printf("\n"); 56 | 57 | printf("--- Start test 2: contains %d objects ---\n", count / 2); 58 | start = getTime(); 59 | for(int i = 0; i < count/2; i++) 60 | { 61 | int value = rand() % count; 62 | assert(IntListContains(&list, value)); 63 | } 64 | end = getTime(); 65 | printf("Time: %.2f seconds\n", end - start); 66 | printf("--- End test 2: contains %d objects ---\n", count / 2); 67 | 68 | printf("\n"); 69 | 70 | printf("--- Start test 3: remove by index %d objects ---\n", count / 2); 71 | start = getTime(); 72 | for(int i = 0; i < count/2; i++) 73 | { 74 | int index = rand() % list.count; 75 | int value = list.items[index]; 76 | IntListRemoveAt(&list, index); 77 | assert(list.items[index] != value); 78 | } 79 | end = getTime(); 80 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 81 | printf("Time: %.2f seconds\n", end - start); 82 | printf("--- End test 3: remove by index %d objects ---\n", count / 2); 83 | 84 | printf("\n"); 85 | 86 | printf("--- Start test 4: remove by value %d objects ---\n", count / 2); 87 | start = getTime(); 88 | for(int i = 0; i < count/2; i++) 89 | { 90 | int value = rand() % count; 91 | IntListRemove(&list, value); 92 | assert(!IntListContains(&list, value)); 93 | } 94 | end = getTime(); 95 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 96 | printf("Time: %.2f seconds\n", end - start); 97 | printf("--- End test 4: remove by value %d objects ---\n", count / 2); 98 | 99 | printf("\n"); 100 | 101 | printf("--- Start test 5: insert %d objects ---\n", count / 2); 102 | start = getTime(); 103 | for(int i = 0; i < count/2; i++) 104 | { 105 | int index = rand() % list.count; 106 | IntListInsert(&list, index, index); 107 | assert(list.items[index] == index); 108 | } 109 | end = getTime(); 110 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 111 | printf("Time: %.2f seconds\n", end - start); 112 | printf("--- End test 5: insert %d objects ---\n", count / 2); 113 | 114 | printf("\n"); 115 | 116 | const int rangeCount = 50000; 117 | const int rangeAt = 100; 118 | 119 | int* rangeValues = (int*)malloc(rangeCount*sizeof(int)); 120 | for(int i = 0; i < rangeCount; i++) 121 | rangeValues[i] = -i; 122 | 123 | printf("--- Start test 6: insert range %d objects ---\n", rangeCount); 124 | start = getTime(); 125 | IntListInsertRange(&list, rangeAt, rangeCount, rangeValues); 126 | end = getTime(); 127 | for(int i = 0; i < rangeCount; i++) 128 | assert(list.items[rangeAt + i] == rangeValues[i]); 129 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 130 | printf("Time: %.2f seconds\n", end - start); 131 | printf("--- End test 6: insert range %d objects ---\n", rangeCount); 132 | 133 | printf("\n"); 134 | 135 | printf("--- Start test 7: remove range %d objects ---\n", rangeCount); 136 | start = getTime(); 137 | IntListRemoveAtRange(&list, rangeAt, rangeCount); 138 | end = getTime(); 139 | for(int i = 0; i < rangeCount; i++) 140 | assert(list.items[rangeAt + i] != rangeValues[i]); 141 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 142 | printf("Time: %.2f seconds\n", end - start); 143 | printf("--- End test 7: remove range %d objects ---\n", rangeCount); 144 | 145 | free(rangeValues); 146 | 147 | printf("\n"); 148 | 149 | printf("--- Start test 8: sorting %d objects ---\n", list.count); 150 | start = getTime(); 151 | IntListSort(&list, intCompare); 152 | end = getTime(); 153 | for(int i = 1; i < list.count; i++) 154 | assert(list.items[i - 1] <= list.items[i]); 155 | printf("Time: %.2f seconds\n", end - start); 156 | printf("--- End test 8: sorting %d objects ---\n", list.count); 157 | 158 | printf("--- End value type tests ---\n"); 159 | } 160 | 161 | // reference type test 162 | typedef struct 163 | { 164 | int index; 165 | char *name; 166 | } Entry; 167 | 168 | bool EntryEquals(const Entry *e1, const Entry *e2) 169 | { 170 | return e1 == e2; 171 | } 172 | 173 | int32_t EntryCompare(const Entry *e1, const Entry *e2) 174 | { 175 | if (e1->index == e2->index) 176 | return strcmp(e1->name, e2->name); 177 | return e1->index - e2->index; 178 | } 179 | 180 | void EntryFree(Entry* e) 181 | { 182 | free(e); 183 | } 184 | 185 | shlDeclareList(EntriesList, Entry*) 186 | shlDefineList(EntriesList, Entry*) 187 | 188 | void referenceTypeTest() 189 | { 190 | float start, end; 191 | Entry** entries; 192 | 193 | EntriesListOptions options = {0}; 194 | options.defaultValue = NULL; 195 | options.equalsFn = EntryEquals; 196 | options.freeFn = EntryFree; 197 | 198 | EntriesList list; 199 | EntriesListInit(&list, options); 200 | 201 | printf("--- Start reference type tests ---\n"); 202 | 203 | printf("--- Start test 1: add %d objects ---\n", count); 204 | entries = malloc(count * sizeof(Entry*)); 205 | start = getTime(); 206 | for(int i = 0; i < count; i++) 207 | { 208 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 209 | entry->index = i; 210 | entry->name = "entry"; 211 | EntriesListAdd(&list, entry); 212 | assert(list.items[list.count - 1]->index == i); 213 | entries[i] = entry; 214 | } 215 | end = getTime(); 216 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 217 | printf("Time: %.2f seconds\n", end - start); 218 | printf("--- End test 1: add %d objects ---\n", count); 219 | 220 | printf("\n"); 221 | 222 | printf("--- Start test 2: contains %d objects ---\n", count / 2); 223 | start = getTime(); 224 | for(int i = 0; i < count / 2; i++) 225 | { 226 | Entry *entry = entries[rand() % count]; 227 | assert(EntriesListContains(&list, entry)); 228 | } 229 | end = getTime(); 230 | printf("Time: %.2f seconds\n", end - start); 231 | printf("--- End test 2: contains %d objects ---\n", count / 2); 232 | 233 | free(entries); 234 | 235 | printf("\n"); 236 | 237 | printf("--- Start test 3: insert %d objects ---\n", count / 2); 238 | start = getTime(); 239 | for(int i = 0; i < count/2; i++) 240 | { 241 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 242 | entry->index = rand() % list.count; 243 | entry->name = "entry"; 244 | EntriesListInsert(&list, entry->index, entry); 245 | assert(list.items[entry->index]->index == entry->index); 246 | } 247 | end = getTime(); 248 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 249 | printf("Time: %.2f seconds\n", end - start); 250 | printf("--- End test 3: insert %d objects ---\n", count / 2); 251 | 252 | printf("\n"); 253 | 254 | printf("--- Start test 4: remove by index %d objects ---\n", count / 2); 255 | start = getTime(); 256 | for(int i = 0; i < count/2; i++) 257 | { 258 | int index = rand() % (list.count - 1); 259 | Entry* entry = list.items[index + 1]; 260 | EntriesListRemoveAt(&list, index); 261 | assert(EntryEquals(list.items[index], entry)); 262 | } 263 | end = getTime(); 264 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 265 | printf("Time: %.2f seconds\n", end - start); 266 | printf("--- End test 4: remove by index %d objects ---\n", count / 2); 267 | 268 | printf("\n"); 269 | 270 | printf("--- Start test 5: remove by value %d objects ---\n", count / 2); 271 | 272 | entries = malloc((count / 2) * sizeof(Entry*)); 273 | for(int i = 0; i < count / 2; i++) 274 | { 275 | Entry* entry = (Entry*)malloc(sizeof(Entry)); 276 | entry->index = rand() % list.count; 277 | entry->name = "negative"; 278 | EntriesListInsert(&list, entry->index, entry); 279 | entries[i] = entry; 280 | } 281 | start = getTime(); 282 | for(int i = 0; i < count / 2; i++) 283 | EntriesListRemove(&list, entries[i]); 284 | end = getTime(); 285 | for(int i = 0; i < list.count; i++) 286 | assert(strcmp(list.items[i]->name, "negative") != 0); 287 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 288 | printf("Time: %.2f seconds\n", end - start); 289 | printf("--- End test 5: remove by value %d objects ---\n", count / 2); 290 | 291 | free(entries); 292 | 293 | printf("\n"); 294 | 295 | const int rangeCount = 50000; 296 | const int rangeAt = 100; 297 | 298 | Entry** rangeValues = (Entry**)malloc(rangeCount * sizeof(Entry*)); 299 | for(int i = 0; i < rangeCount; i++) 300 | { 301 | rangeValues[i] = malloc(sizeof(Entry)); 302 | rangeValues[i]->index = -i; 303 | rangeValues[i]->name = "insert range"; 304 | } 305 | 306 | printf("--- Start test 6: insert range %d objects ---\n", rangeCount); 307 | start = getTime(); 308 | EntriesListInsertRange(&list, rangeAt, rangeCount, rangeValues); 309 | end = getTime(); 310 | for(int i = 0; i < rangeCount; i++) 311 | assert(EntryEquals(list.items[rangeAt + i], rangeValues[i])); 312 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 313 | printf("Time: %.2f seconds\n", end - start); 314 | printf("--- End test 6: insert range %d objects ---\n", rangeCount); 315 | 316 | printf("\n"); 317 | 318 | printf("--- Start test 7: remove range %d objects ---\n", rangeCount); 319 | start = getTime(); 320 | EntriesListRemoveAtRange(&list, rangeAt, rangeCount); 321 | end = getTime(); 322 | for(int i = 0; i < rangeCount; i++) 323 | assert(list.items[rangeAt + i]->index >= 0); 324 | printf("List count and capacity: (%d, %d)\n", list.count, list.capacity); 325 | printf("Time: %.2f seconds\n", end - start); 326 | printf("--- End test 7: remove range %d objects ---\n", rangeCount); 327 | 328 | free(rangeValues); 329 | 330 | printf("\n"); 331 | 332 | printf("--- Start test 8: sorting %d objects ---\n", list.count); 333 | start = getTime(); 334 | EntriesListSort(&list, EntryCompare); 335 | end = getTime(); 336 | for(int i = 1; i < list.count; i++) 337 | assert(EntryCompare(list.items[i - 1], list.items[i]) <= 0); 338 | printf("Time: %.2f seconds\n", end - start); 339 | printf("--- End test 8: sorting %d objects ---\n", list.count); 340 | 341 | printf("--- End reference type tests ---\n"); 342 | } 343 | 344 | int main(int argc, char **argv) 345 | { 346 | /* initialize random seed: */ 347 | srand(time(NULL)); 348 | 349 | valueTypeTest(); 350 | 351 | printf("\n\n"); 352 | 353 | referenceTypeTest(); 354 | 355 | return 0; 356 | } 357 | -------------------------------------------------------------------------------- /tests/map_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../map.h" 9 | 10 | static const int32_t count = 100000; 11 | 12 | static float getTime() 13 | { 14 | return (float)clock() / CLOCKS_PER_SEC; 15 | } 16 | 17 | // value type tests 18 | uint32_t hashInt(const int x) 19 | { 20 | return x; 21 | } 22 | 23 | bool equalsInt(const int a, const int b) 24 | { 25 | return a == b; 26 | } 27 | 28 | shlDeclareMap(IntMap, int, int) 29 | shlDefineMap(IntMap, int, int) 30 | 31 | void valueTypeTest() 32 | { 33 | float start, end; 34 | 35 | IntMapOptions options = (IntMapOptions){0}; 36 | options.hashFn = hashInt; 37 | options.equalsFn = equalsInt; 38 | options.defaultValue = 0; 39 | 40 | IntMap map; 41 | IntMapInit(&map, options); 42 | 43 | printf("--- Start test 1: add %d objects ---\n", count); 44 | start = getTime(); 45 | for(int i = 0; i < count; i++) 46 | { 47 | int key = i; 48 | IntMapSet(&map, key, key*key); 49 | int value = IntMapGet(&map, key); 50 | assert(value == key*key); 51 | } 52 | end = getTime(); 53 | printf("Map count and capacity: (%d, %d)\n", map.count, map.capacity); 54 | printf("Time: %.2f seconds\n", end - start); 55 | printf("--- End test 1: add %d objects ---\n", count); 56 | 57 | printf("\n"); 58 | 59 | printf("--- Start test 2: contains %d objects ---\n", count/2); 60 | start = getTime(); 61 | for(int i = 0; i < count/2; i++) 62 | { 63 | int key = rand() % count; 64 | assert(IntMapContains(&map, key)); 65 | } 66 | end = getTime(); 67 | printf("Time: %.2f seconds\n", end - start); 68 | printf("--- End test 2: contains %d objects ---\n", count/2); 69 | 70 | printf("\n"); 71 | 72 | printf("--- Start test 3: set existing %d objects ---\n", count/2); 73 | start = getTime(); 74 | for(int i = 0; i < count/2; i++) 75 | { 76 | int key = rand() % count; 77 | IntMapSet(&map, key, key); 78 | int value = IntMapGet(&map, key); 79 | assert(key == value); 80 | } 81 | end = getTime(); 82 | printf("Time: %.2f seconds\n", end - start); 83 | printf("--- End test 3: set existing %d objects ---\n", count/2); 84 | 85 | printf("\n"); 86 | 87 | printf("--- Start test 4: remove %d objects ---\n", count/2); 88 | start = getTime(); 89 | for(int i = 0; i < count/2; i++) 90 | { 91 | int key = rand() % map.count; 92 | IntMapRemove(&map, key); 93 | assert(!IntMapContains(&map, key)); 94 | } 95 | end = getTime(); 96 | printf("Time: %.2f seconds\n", end - start); 97 | printf("--- End test 4: remove %d objects ---\n", count/2); 98 | } 99 | 100 | // reference type tests 101 | #define FNV_PRIME_32 0x01000193 102 | #define FNV_OFFSET_32 0x811c9dc5 103 | 104 | uint32_t fnv32(const char* data) 105 | { 106 | uint32_t hash = FNV_OFFSET_32; 107 | while(*data != 0) 108 | hash = (*data++ ^ hash) * FNV_PRIME_32; 109 | 110 | return hash; 111 | } 112 | 113 | bool equalsStr(const char *s1, const char *s2) 114 | { 115 | return strcmp(s1, s2) == 0; 116 | } 117 | 118 | shlDeclareMap(SSMap, char*, char*) 119 | shlDefineMap(SSMap, char*, char*) 120 | 121 | char* generateString() 122 | { 123 | const int stringLength = 50; 124 | char *s = (char*)calloc(stringLength + 1, sizeof(char)); 125 | 126 | for(int i = 0; i < 50; i++) 127 | s[i] = rand() % 27 + 97; 128 | 129 | return s; 130 | } 131 | 132 | char* toUpperString(const char* str) 133 | { 134 | int len = strlen(str); 135 | char *upper = (char*)calloc(len + 1, sizeof(char)); 136 | 137 | for(int i = 0; i < len; i++) 138 | upper[i] = toupper(str[i]); 139 | 140 | return upper; 141 | } 142 | 143 | char* reverseString(const char* str) 144 | { 145 | int len = strlen(str); 146 | char *reverse = (char*)calloc(len + 1, sizeof(char)); 147 | 148 | for(int i = 0; i < len; i++) 149 | reverse[len - i - 1] = str[i]; 150 | 151 | return reverse; 152 | } 153 | 154 | void freeStr(char* str) 155 | { 156 | free((void*)str); 157 | } 158 | 159 | void referenceTypeTest() 160 | { 161 | float start, end; 162 | 163 | printf("--- Start generating %d tests strings ---\n", count); 164 | start = getTime(); 165 | char *strings[100000]; 166 | for(int i = 0; i < count; i++) 167 | strings[i] = generateString(); 168 | end = getTime(); 169 | printf("Time: %.2f seconds\n", end - start); 170 | printf("--- End generating %d tests strings ---\n", count); 171 | 172 | printf("\n\n"); 173 | 174 | SSMapOptions options = (SSMapOptions){0}; 175 | options.hashFn = fnv32; 176 | options.equalsFn = equalsStr; 177 | options.defaultValue = NULL; 178 | options.freeFn = freeStr; 179 | 180 | SSMap map; 181 | SSMapInit(&map, options); 182 | 183 | printf("--- Start test 1: add %d strings ---\n", count); 184 | start = getTime(); 185 | for(int i = 0; i < count; i++) 186 | { 187 | char *key = strings[i]; 188 | char *upper = toUpperString(key); 189 | SSMapSet(&map, key, upper); 190 | char *value = SSMapGet(&map, key); 191 | assert(!strcmp(value, upper)); 192 | } 193 | end = getTime(); 194 | printf("Map count and capacity: (%d, %d)\n", map.count, map.capacity); 195 | printf("Time: %.2f seconds\n", end - start); 196 | printf("--- End test 1: add %d strings ---\n", count); 197 | 198 | printf("\n"); 199 | 200 | printf("--- Start test 2: contains %d objects ---\n", count/2); 201 | start = getTime(); 202 | for(int i = 0; i < count/2; i++) 203 | { 204 | int index = rand() % count; 205 | assert(SSMapContains(&map, strings[index])); 206 | } 207 | end = getTime(); 208 | printf("Time: %.2f seconds\n", end - start); 209 | printf("--- End test 2: contains %d objects ---\n", count/2); 210 | 211 | printf("\n"); 212 | 213 | printf("--- Start test 3: set existing %d objects ---\n", count/2); 214 | start = getTime(); 215 | for(int i = 0; i < count/2; i++) 216 | { 217 | int index = rand() % count; 218 | char* reverse = reverseString(strings[index]); 219 | SSMapSet(&map, strings[index], reverse); 220 | char *value = SSMapGet(&map, strings[index]); 221 | assert(equalsStr(reverse, value)); 222 | } 223 | end = getTime(); 224 | printf("Time: %.2f seconds\n", end - start); 225 | printf("--- End test 3: set existing %d objects ---\n", count/2); 226 | 227 | printf("\n"); 228 | 229 | printf("--- Start test 4: remove %d objects ---\n", count/2); 230 | start = getTime(); 231 | for(int i = 0; i < count/2; i++) 232 | { 233 | int index = rand() % map.count; 234 | SSMapRemove(&map, strings[index]); 235 | assert(!SSMapContains(&map, strings[index])); 236 | } 237 | end = getTime(); 238 | printf("Time: %.2f seconds\n", end - start); 239 | printf("--- End test 4: remove %d objects ---\n", count/2); 240 | } 241 | 242 | int main(int argc, char **argv) 243 | { 244 | /* initialize random seed: */ 245 | srand(time(NULL)); 246 | 247 | valueTypeTest(); 248 | 249 | printf("\n\n"); 250 | 251 | referenceTypeTest(); 252 | 253 | return 0; 254 | } -------------------------------------------------------------------------------- /tests/memory_buffer_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define SHL_MEMORY_BUFFER_IMPLEMENTATION 8 | #include "../memory_buffer.h" 9 | 10 | const char* BufferTestStr = "Buffer test"; 11 | const char* EndBlockStr = "End block"; 12 | 13 | static float getTime() 14 | { 15 | return (float)clock() / CLOCKS_PER_SEC; 16 | } 17 | 18 | uint8_t* testWriting(size_t* length) 19 | { 20 | MemoryBuffer buffer = {0}; 21 | mbInitEmpty(&buffer); 22 | 23 | assert(mbWriteString(&buffer, BufferTestStr, 11)); 24 | assert(buffer.length == 11); 25 | 26 | for (int32_t i = 0; i < 10; i++) 27 | { 28 | assert(mbWriteInt32LE(&buffer, i)); 29 | assert(buffer.length == 11 + (i + 1) * sizeof(int32_t)); 30 | } 31 | 32 | assert(mbPosition(&buffer) == 11 + 10 * sizeof(int32_t)); 33 | 34 | for (int32_t i = 0; i < 10; i++) 35 | { 36 | assert(mbWriteInt32BE(&buffer, i)); 37 | assert(buffer.length == 11 + (10 + i + 1) * sizeof(int32_t)); 38 | } 39 | 40 | assert(mbPosition(&buffer) == 11 + 20 * sizeof(int32_t)); 41 | 42 | assert(mbWriteString(&buffer, EndBlockStr, 9)); 43 | assert(buffer.length == 100); 44 | 45 | uint8_t* data = mbGetData(&buffer, length); 46 | mbFree(&buffer); 47 | assert(!buffer.data); 48 | return data; 49 | } 50 | 51 | void testReading(uint8_t* data, size_t length) 52 | { 53 | MemoryBuffer buffer = {0}; 54 | mbInitFromMemory(&buffer, data, length); 55 | 56 | assert(buffer.length == length); 57 | 58 | char header[12]; 59 | assert(mbReadString(&buffer, header, 11)); 60 | assert(strcmp(header, BufferTestStr) == 0); 61 | assert(mbPosition(&buffer) == 11); 62 | 63 | for (int32_t i = 0; i < 10; i++) 64 | { 65 | int32_t v; 66 | assert(mbReadInt32LE(&buffer, &v)); 67 | assert(v == i); 68 | } 69 | 70 | assert(mbPosition(&buffer) == 11 + 10 * sizeof(int32_t)); 71 | 72 | for (int32_t i = 0; i < 10; i++) 73 | { 74 | int32_t v; 75 | assert(mbReadInt32BE(&buffer, &v)); 76 | assert(v == i); 77 | } 78 | 79 | assert(mbPosition(&buffer) == 11 + 20 * sizeof(int32_t)); 80 | } 81 | 82 | void testScanTo(uint8_t* data, size_t length) 83 | { 84 | MemoryBuffer buffer = {0}; 85 | mbInitFromMemory(&buffer, data, length); 86 | 87 | assert(mbScanTo(&buffer, "End block", 9)); 88 | 89 | char header[10]; 90 | assert(mbReadString(&buffer, header, 9)); 91 | assert(strcmp(header, EndBlockStr) == 0); 92 | assert(mbIsEOF(&buffer)); 93 | } 94 | 95 | void testSeek(uint8_t* data, size_t length) 96 | { 97 | MemoryBuffer buffer = {0}; 98 | mbInitFromMemory(&buffer, data, length); 99 | 100 | assert(mbSeek(&buffer, 91)); 101 | char header[10]; 102 | assert(mbReadString(&buffer, header, 9)); 103 | assert(strcmp(header, EndBlockStr) == 0); 104 | assert(mbIsEOF(&buffer)); 105 | 106 | assert(mbSeek(&buffer, 7)); 107 | assert(mbWriteInt32LE(&buffer, 22)); 108 | assert(mbSeek(&buffer, 7)); 109 | int32_t v; 110 | assert(mbReadInt32BE(&buffer, &v)); 111 | assert(v == 369098752); 112 | } 113 | 114 | int main(int argc, char **argv) 115 | { 116 | float start, end; 117 | 118 | printf("--- Start writing test ---\n"); 119 | start = getTime(); 120 | size_t length; 121 | uint8_t* data = testWriting(&length); 122 | end = getTime(); 123 | printf("Time: %.2f seconds\n", end - start); 124 | printf("--- End writing test ---\n"); 125 | 126 | printf("\n"); 127 | 128 | printf("--- Start reading test ---\n"); 129 | start = getTime(); 130 | testReading(data, length); 131 | end = getTime(); 132 | printf("Time: %.2f seconds\n", end - start); 133 | printf("--- End reading test ---\n"); 134 | 135 | printf("\n"); 136 | 137 | printf("--- Start scanTo test ---\n"); 138 | start = getTime(); 139 | testScanTo(data, length); 140 | end = getTime(); 141 | printf("Time: %.2f seconds\n", end - start); 142 | printf("--- End scanTo test ---\n"); 143 | 144 | printf("\n"); 145 | 146 | printf("--- Start seek test ---\n"); 147 | start = getTime(); 148 | testSeek(data, length); 149 | end = getTime(); 150 | printf("Time: %.2f seconds\n", end - start); 151 | printf("--- End seek test ---\n"); 152 | 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /tests/memzone_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define SHL_MEMORY_ZONE_IMPLEMENTATION 8 | #include "../memzone.h" 9 | 10 | typedef enum 11 | { 12 | ENTITY_TYPE_1, 13 | ENTITY_TYPE_2, 14 | ENTITY_TYPE_3, 15 | 16 | ENTITY_TYPE_COUNT 17 | } EntityType; 18 | 19 | typedef struct 20 | { 21 | int32_t id; 22 | EntityType type; 23 | } Entity; 24 | 25 | #define ENTITY_COUNT 30000 26 | #define randabi(a, b) (int32_t)((a) + ((float)rand() / RAND_MAX) * ((b) - (a))) 27 | 28 | int main() 29 | { 30 | srand(time(NULL)); 31 | 32 | printf("sizeof(Entity) = %u\n", sizeof(Entity)); 33 | printf("sizeof(memzone_t) = %u\n", sizeof(memzone_t)); 34 | printf("sizeof(memblock_t) = %u\n", sizeof(memblock_t)); 35 | printf("\n"); 36 | 37 | size_t zoneSize = 1024 * 1024; // 1MB 38 | memzone_t* zone = mzInit(zoneSize); 39 | if (!zone) 40 | { 41 | printf("ERROR: Couldn't allocate %d bytes\n", zoneSize); 42 | return -1; 43 | } 44 | 45 | assert(zone->maxSize == zoneSize); 46 | assert(zone->usedSize == sizeof(memzone_t)); 47 | 48 | size_t sizeOfArray = ENTITY_COUNT * sizeof(Entity*); 49 | Entity** entities = (Entity**)mzAlloc(zone, sizeOfArray); 50 | assert(entities); 51 | assert(zone->usedSize == sizeof(memzone_t) + sizeOfArray + sizeof(memblock_t)); 52 | 53 | for (int32_t i = 0; i < ENTITY_COUNT; i++) 54 | { 55 | entities[i] = (Entity*)mzAlloc(zone, sizeof(Entity)); 56 | entities[i]->id = i; 57 | assert(entities[i]); 58 | 59 | size_t partialSizeOfArrayElements = (i + 1) * (sizeof(memblock_t) + sizeof(Entity)); 60 | assert(zone->usedSize == sizeof(memzone_t) + sizeOfArray + partialSizeOfArrayElements + sizeof(memblock_t)); 61 | } 62 | 63 | size_t sizeOfArrayElements = ENTITY_COUNT * (sizeof(memblock_t) + sizeof(Entity)); 64 | assert(zone->usedSize == sizeof(memzone_t) + sizeOfArray + sizeOfArrayElements + sizeof(memblock_t)); 65 | assert(mzGetUsableFreeSize(zone) == zone->maxSize - zone->usedSize); 66 | assert(mzGetNumberOfBlocks(zone) == 1 + ENTITY_COUNT + 1); // 1 for the array itself, ENTITY_COUNT for the elements, 1 for the empty block 67 | 68 | mzPrint(zone, false, true); 69 | 70 | int32_t k = 0; 71 | for (int32_t i = 0; i < ENTITY_COUNT / 2; i++) 72 | { 73 | int32_t index = randabi(0, ENTITY_COUNT - 1); 74 | if (entities[index]) 75 | { 76 | mzFree(zone, entities[index]); 77 | entities[index] = NULL; 78 | 79 | size_t partialSizeOfDeletedElements = (k + 1) * sizeof(Entity); 80 | size_t partialSizeOfArrayElements = sizeOfArrayElements - partialSizeOfDeletedElements; 81 | assert(zone->usedSize == sizeof(memzone_t) + sizeOfArray + partialSizeOfArrayElements + sizeof(memblock_t)); 82 | k++; 83 | } 84 | } 85 | 86 | size_t sizeOfDeletedArrayElements = k * sizeof(Entity); 87 | sizeOfArrayElements -= sizeOfDeletedArrayElements; 88 | assert(zone->usedSize == sizeof(memzone_t) + sizeOfArray + sizeOfArrayElements + sizeof(memblock_t)); 89 | assert(mzGetUsableFreeSize(zone) >= zone->maxSize - zone->usedSize); 90 | assert(mzGetNumberOfBlocks(zone) <= 1 + ENTITY_COUNT + 1); // 1 for the array itself, ENTITY_COUNT for the elements, 1 for the empty block 91 | 92 | mzPrint(zone, false, true); 93 | 94 | free(zone); 95 | 96 | printf("Done\n"); 97 | return 0; 98 | } -------------------------------------------------------------------------------- /tests/queue_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../queue.h" 8 | 9 | static const int32_t count = 100000; 10 | 11 | static float getTime() 12 | { 13 | return (float)clock() / CLOCKS_PER_SEC; 14 | } 15 | 16 | // value type tests 17 | bool intEquals(const int x, const int y) 18 | { 19 | return x == y; 20 | } 21 | 22 | shlDeclareQueue(IntQueue, int) 23 | shlDefineQueue(IntQueue, int) 24 | 25 | void valueTypeTest() 26 | { 27 | float start, end; 28 | int peek; 29 | 30 | IntQueueOptions options = {0}; 31 | options.defaultValue = 0; 32 | options.equalsFn = intEquals; 33 | 34 | IntQueue queue; 35 | IntQueueInit(&queue, options); 36 | 37 | printf("--- Start value type tests ---\n"); 38 | 39 | printf("--- Start test 1: enqueue %d objects ---\n", count); 40 | start = getTime(); 41 | for(int i = 0; i < count; i++) 42 | { 43 | IntQueuePush(&queue, i); 44 | assert(IntQueuePeek(&queue) == 0); 45 | } 46 | end = getTime(); 47 | printf("Queue count and capacity: (%d, %d)\n", queue.count, queue.capacity); 48 | printf("Time: %.2f seconds\n", end - start); 49 | printf("--- End test 1: push %d objects ---\n", count); 50 | 51 | printf("\n"); 52 | 53 | printf("--- Start test 2: contains %d objects ---\n", count / 2); 54 | start = getTime(); 55 | for(int i = 0; i < count/2; i++) 56 | { 57 | int value = rand() % count; 58 | assert(IntQueueContains(&queue, value)); 59 | } 60 | end = getTime(); 61 | printf("Time: %.2f seconds\n", end - start); 62 | printf("--- End test 2: contains %d objects ---\n", count / 2); 63 | 64 | printf("\n"); 65 | 66 | printf("--- Start test 3: pop %d objects ---\n", count / 2); 67 | start = getTime(); 68 | for(int i = 0; i < count/2; i++) 69 | { 70 | peek = IntQueuePeek(&queue); 71 | int value = IntQueuePop(&queue); 72 | assert(peek == value); 73 | } 74 | end = getTime(); 75 | printf("Queue count and capacity: (%d, %d)\n", queue.count, queue.capacity); 76 | printf("Time: %.2f seconds\n", end - start); 77 | printf("--- End test 3: pop %d objects ---\n", count / 2); 78 | 79 | printf("\n"); 80 | 81 | printf("--- Start test 4: push/pop %d objects ---\n", count / 2); 82 | start = getTime(); 83 | peek = IntQueuePeek(&queue); 84 | for(int i = 0; i < count/2; i++) 85 | { 86 | if (rand() % 100 > 50) 87 | { 88 | IntQueuePush(&queue, i); 89 | assert(IntQueuePeek(&queue) == peek); 90 | } 91 | else 92 | { 93 | int value = IntQueuePop(&queue); 94 | assert(peek == value); 95 | peek = IntQueuePeek(&queue); 96 | } 97 | } 98 | end = getTime(); 99 | printf("Queue count and capacity: (%d, %d)\n", queue.count, queue.capacity); 100 | printf("Time: %.2f seconds\n", end - start); 101 | printf("--- End test 4: push/pop %d objects ---\n", count / 2); 102 | 103 | printf("--- End value type tests ---\n"); 104 | } 105 | 106 | // reference type test 107 | typedef struct 108 | { 109 | int index; 110 | char *name; 111 | } Entry; 112 | 113 | bool EntryEquals(const Entry *e1, const Entry *e2) 114 | { 115 | return e1->index == e2->index && 116 | strcmp(e1->name, e2->name) == 0; 117 | } 118 | 119 | void EntryFree(Entry* e) 120 | { 121 | free(e); 122 | } 123 | 124 | shlDeclareQueue(EntriesQueue, Entry*) 125 | shlDefineQueue(EntriesQueue, Entry*) 126 | 127 | void referenceTypeTest() 128 | { 129 | float start, end; 130 | Entry *peek; 131 | 132 | EntriesQueueOptions options = {0}; 133 | options.defaultValue = NULL; 134 | options.equalsFn = EntryEquals; 135 | options.freeFn = EntryFree; 136 | 137 | EntriesQueue queue; 138 | EntriesQueueInit(&queue, options); 139 | 140 | printf("--- Start reference type tests ---\n"); 141 | 142 | printf("--- Start test 1: enqueue %d objects ---\n", count); 143 | start = getTime(); 144 | for(int i = 0; i < count; i++) 145 | { 146 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 147 | entry->index = i; 148 | entry->name = "entry"; 149 | EntriesQueuePush(&queue, entry); 150 | assert(EntriesQueuePeek(&queue)->index == 0); 151 | } 152 | end = getTime(); 153 | printf("Queue count and capacity: (%d, %d)\n", queue.count, queue.capacity); 154 | printf("Time: %.2f seconds\n", end - start); 155 | printf("--- End test 1: push %d objects ---\n", count); 156 | 157 | printf("\n"); 158 | 159 | printf("--- Start test 2: contains %d objects ---\n", count / 2); 160 | start = getTime(); 161 | for(int i = 0; i < count/2; i++) 162 | { 163 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 164 | entry->index = rand() % count; 165 | entry->name = "entry"; 166 | assert(EntriesQueueContains(&queue, entry)); 167 | free(entry); 168 | } 169 | end = getTime(); 170 | printf("Time: %.2f seconds\n", end - start); 171 | printf("--- End test 2: contains %d objects ---\n", count / 2); 172 | 173 | printf("\n"); 174 | 175 | printf("--- Start test 3: pop %d objects ---\n", count / 2); 176 | start = getTime(); 177 | for(int i = 0; i < count/2; i++) 178 | { 179 | peek = EntriesQueuePeek(&queue); 180 | Entry *entry = EntriesQueuePop(&queue); 181 | assert(peek->index == entry->index); 182 | EntryFree(entry); 183 | } 184 | end = getTime(); 185 | printf("Queue count and capacity: (%d, %d)\n", queue.count, queue.capacity); 186 | printf("Time: %.2f seconds\n", end - start); 187 | printf("--- End test 3: pop %d objects ---\n", count / 2); 188 | 189 | printf("\n"); 190 | 191 | printf("--- Start test 4: push/pop %d objects ---\n", count / 2); 192 | start = getTime(); 193 | peek = EntriesQueuePeek(&queue); 194 | for(int i = 0; i < count/2; i++) 195 | { 196 | if (rand() % 100 > 50) 197 | { 198 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 199 | entry->index = rand() % count; 200 | entry->name = "entry"; 201 | EntriesQueuePush(&queue, entry); 202 | assert(EntriesQueuePeek(&queue)->index == peek->index); 203 | } 204 | else 205 | { 206 | Entry *entry = EntriesQueuePop(&queue); 207 | assert(peek->index == entry->index); 208 | peek = EntriesQueuePeek(&queue); 209 | EntryFree(entry); 210 | } 211 | } 212 | end = getTime(); 213 | printf("Queue count and capacity: (%d, %d)\n", queue.count, queue.capacity); 214 | printf("Time: %.2f seconds\n", end - start); 215 | printf("--- End test 4: push/pop %d objects ---\n", count / 2); 216 | 217 | printf("--- End reference type tests ---\n"); 218 | } 219 | 220 | int main(int argc, char **argv) 221 | { 222 | /* initialize random seed: */ 223 | srand(time(NULL)); 224 | 225 | valueTypeTest(); 226 | 227 | printf("\n\n"); 228 | 229 | referenceTypeTest(); 230 | 231 | return 0; 232 | } -------------------------------------------------------------------------------- /tests/set_test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../set.h" 10 | 11 | static const int32_t count = 100000; 12 | 13 | static float getTime() 14 | { 15 | return (float)clock() / CLOCKS_PER_SEC; 16 | } 17 | 18 | // value type tests 19 | uint32_t hashInt(const int x) 20 | { 21 | return x; 22 | } 23 | 24 | bool equalsInt(const int a, const int b) 25 | { 26 | return a == b; 27 | } 28 | 29 | shlDeclareSet(IntSet, int) 30 | shlDefineSet(IntSet, int) 31 | 32 | void valueTypeTest() 33 | { 34 | float start, end; 35 | 36 | IntSetOptions options = (IntSetOptions){0}; 37 | options.hashFn = hashInt; 38 | options.equalsFn = equalsInt; 39 | options.defaultValue = 0; 40 | 41 | IntSet set; 42 | IntSetInit(&set, options); 43 | 44 | printf("--- Start test 1: add %d objects ---\n", count); 45 | start = getTime(); 46 | for(int i = 0; i < count; i++) 47 | { 48 | int x = i; 49 | assert(IntSetAdd(&set, x)); 50 | assert(set.count == i + 1); 51 | } 52 | end = getTime(); 53 | printf("Set count and capacity: (%d, %d)\n", set.count, set.capacity); 54 | printf("Time: %.2f seconds\n", end - start); 55 | printf("--- End test 1: add %d objects ---\n", count); 56 | 57 | printf("\n"); 58 | 59 | printf("--- Start test 2: contains %d objects ---\n", count/2); 60 | start = getTime(); 61 | for(int i = 0; i < count/2; i++) 62 | { 63 | int x = rand() % count; 64 | assert(IntSetContains(&set, x)); 65 | } 66 | end = getTime(); 67 | printf("Time: %.2f seconds\n", end - start); 68 | printf("--- End test 2: contains %d objects ---\n", count/2); 69 | 70 | printf("\n"); 71 | 72 | printf("--- Start test 3: add duplicated %d objects ---\n", count/2); 73 | start = getTime(); 74 | uint32_t prevCount = set.count; 75 | for(int i = 0; i < count/2; i++) 76 | { 77 | int x = rand() % set.count; 78 | assert(!IntSetAdd(&set, x)); 79 | assert(set.count == prevCount); 80 | } 81 | end = getTime(); 82 | printf("Time: %.2f seconds\n", end - start); 83 | printf("--- End test 3: add duplicated %d objects ---\n", count/2); 84 | 85 | printf("\n"); 86 | 87 | printf("--- Start test 4: remove %d objects ---\n", count/2); 88 | start = getTime(); 89 | for(int i = 0; i < count/2; i++) 90 | { 91 | int x = rand() % set.count; 92 | IntSetRemove(&set, x); 93 | assert(!IntSetContains(&set, x)); 94 | } 95 | end = getTime(); 96 | printf("Time: %.2f seconds\n", end - start); 97 | printf("--- End test 4: remove %d objects ---\n", count/2); 98 | } 99 | 100 | // reference type tests 101 | #define FNV_PRIME_32 0x01000193 102 | #define FNV_OFFSET_32 0x811c9dc5 103 | 104 | uint32_t fnv32(const char* data) 105 | { 106 | uint32_t hash = FNV_OFFSET_32; 107 | while(*data != 0) 108 | hash = (*data++ ^ hash) * FNV_PRIME_32; 109 | 110 | return hash; 111 | } 112 | 113 | bool equalsStr(const char *s1, const char *s2) 114 | { 115 | return strcmp(s1, s2) == 0; 116 | } 117 | 118 | shlDeclareSet(SSet, char*) 119 | shlDefineSet(SSet, char*) 120 | 121 | char* generateString() 122 | { 123 | const int stringLength = 50; 124 | char *s = (char*)calloc(stringLength + 1, sizeof(char)); 125 | 126 | for(int i = 0; i < 50; i++) 127 | s[i] = rand() % 27 + 97; 128 | 129 | return s; 130 | } 131 | 132 | void freeStr(char* str) 133 | { 134 | free((void*)str); 135 | } 136 | 137 | void referenceTypeTest() 138 | { 139 | float start, end; 140 | 141 | printf("--- Start generating %d tests strings ---\n", count); 142 | start = getTime(); 143 | char *strings[100000]; 144 | for(int i = 0; i < count; i++) 145 | strings[i] = generateString(); 146 | end = getTime(); 147 | printf("Time: %.2f seconds\n", end - start); 148 | printf("--- End generating %d tests strings ---\n", count); 149 | 150 | printf("\n\n"); 151 | 152 | SSetOptions options = (SSetOptions){0}; 153 | options.hashFn = fnv32; 154 | options.equalsFn = equalsStr; 155 | options.defaultValue = NULL; 156 | options.freeFn = freeStr; 157 | 158 | SSet set; 159 | SSetInit(&set, options); 160 | 161 | printf("--- Start test 1: add %d strings ---\n", count); 162 | start = getTime(); 163 | for(int i = 0; i < count; i++) 164 | { 165 | char *str = strings[i]; 166 | assert(SSetAdd(&set, str)); 167 | assert(set.count == i + 1); 168 | } 169 | end = getTime(); 170 | printf("Set count and capacity: (%d, %d)\n", set.count, set.capacity); 171 | printf("Time: %.2f seconds\n", end - start); 172 | printf("--- End test 1: add %d strings ---\n", count); 173 | 174 | printf("\n"); 175 | 176 | printf("--- Start test 2: contains %d objects ---\n", count/2); 177 | start = getTime(); 178 | for(int i = 0; i < count/2; i++) 179 | { 180 | int index = rand() % count; 181 | assert(SSetContains(&set, strings[index])); 182 | } 183 | end = getTime(); 184 | printf("Time: %.2f seconds\n", end - start); 185 | printf("--- End test 2: contains %d objects ---\n", count/2); 186 | 187 | printf("\n"); 188 | 189 | printf("--- Start test 3: add duplicated %d objects ---\n", count/2); 190 | start = getTime(); 191 | uint32_t prevCount = set.count; 192 | for(int i = 0; i < count/2; i++) 193 | { 194 | int index = rand() % set.count; 195 | assert(!SSetAdd(&set, strings[index])); 196 | assert(set.count == prevCount); 197 | } 198 | end = getTime(); 199 | printf("Time: %.2f seconds\n", end - start); 200 | printf("--- End test 3: add duplicated %d objects ---\n", count/2); 201 | 202 | printf("\n"); 203 | 204 | printf("--- Start test 4: remove %d objects ---\n", count/2); 205 | start = getTime(); 206 | for(int i = 0; i < count/2; i++) 207 | { 208 | int index = rand() % set.count; 209 | SSetRemove(&set, strings[index]); 210 | assert(!SSetContains(&set, strings[index])); 211 | } 212 | end = getTime(); 213 | printf("Time: %.2f seconds\n", end - start); 214 | printf("--- End test 4: remove %d objects ---\n", count/2); 215 | } 216 | 217 | int main(int argc, char **argv) 218 | { 219 | /* initialize random seed: */ 220 | srand(time(NULL)); 221 | 222 | valueTypeTest(); 223 | 224 | printf("\n\n"); 225 | 226 | referenceTypeTest(); 227 | 228 | return 0; 229 | } -------------------------------------------------------------------------------- /tests/stack_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../stack.h" 8 | 9 | static const int32_t count = 100000; 10 | 11 | static float getTime() 12 | { 13 | return (float)clock() / CLOCKS_PER_SEC; 14 | } 15 | 16 | // value type tests 17 | bool intEquals(const int x, const int y) 18 | { 19 | return x == y; 20 | } 21 | 22 | shlDeclareStack(IntStack, int) 23 | shlDefineStack(IntStack, int) 24 | 25 | void valueTypeTest() 26 | { 27 | float start, end; 28 | 29 | IntStackOptions options = {0}; 30 | options.defaultValue = 0; 31 | options.equalsFn = intEquals; 32 | 33 | IntStack stack; 34 | IntStackInit(&stack, options); 35 | 36 | printf("--- Start value type tests ---\n"); 37 | 38 | printf("--- Start test 1: push %d objects ---\n", count); 39 | start = getTime(); 40 | for(int i = 0; i < count; i++) 41 | { 42 | IntStackPush(&stack, i); 43 | assert(IntStackPeek(&stack) == i); 44 | } 45 | end = getTime(); 46 | printf("Stack count and capacity: (%d, %d)\n", stack.count, stack.capacity); 47 | printf("Time: %.2f seconds\n", end - start); 48 | printf("--- End test 1: push %d objects ---\n", count); 49 | 50 | printf("\n"); 51 | 52 | printf("--- Start test 2: contains %d objects ---\n", count / 2); 53 | start = getTime(); 54 | for(int i = 0; i < count/2; i++) 55 | { 56 | int value = rand() % count; 57 | assert(IntStackContains(&stack, value)); 58 | } 59 | end = getTime(); 60 | printf("Time: %.2f seconds\n", end - start); 61 | printf("--- End test 2: contains %d objects ---\n", count / 2); 62 | 63 | printf("\n"); 64 | 65 | printf("--- Start test 3: pop %d objects ---\n", count / 2); 66 | start = getTime(); 67 | for(int i = 0; i < count/2; i++) 68 | { 69 | int peek = IntStackPeek(&stack); 70 | int value = IntStackPop(&stack); 71 | assert(peek == value); 72 | } 73 | end = getTime(); 74 | printf("Stack count and capacity: (%d, %d)\n", stack.count, stack.capacity); 75 | printf("Time: %.2f seconds\n", end - start); 76 | printf("--- End test 3: pop %d objects ---\n", count / 2); 77 | 78 | printf("--- End value type tests ---\n"); 79 | } 80 | 81 | // reference type test 82 | typedef struct 83 | { 84 | int index; 85 | char *name; 86 | } Entry; 87 | 88 | bool EntryEquals(const Entry *e1, const Entry *e2) 89 | { 90 | return e1->index == e2->index && 91 | strcmp(e1->name, e2->name) == 0; 92 | } 93 | 94 | void EntryFree(Entry* e) 95 | { 96 | free(e); 97 | } 98 | 99 | shlDeclareStack(EntriesStack, Entry*) 100 | shlDefineStack(EntriesStack, Entry*) 101 | 102 | void referenceTypeTest() 103 | { 104 | float start, end; 105 | 106 | EntriesStackOptions options = {0}; 107 | options.defaultValue = NULL; 108 | options.equalsFn = EntryEquals; 109 | options.freeFn = EntryFree; 110 | 111 | EntriesStack stack; 112 | EntriesStackInit(&stack, options); 113 | 114 | printf("--- Start reference type tests ---\n"); 115 | 116 | printf("--- Start test 1: push %d objects ---\n", count); 117 | start = getTime(); 118 | for(int i = 0; i < count; i++) 119 | { 120 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 121 | entry->index = i; 122 | entry->name = "entry"; 123 | EntriesStackPush(&stack, entry); 124 | assert(EntriesStackPeek(&stack)->index == entry->index); 125 | } 126 | end = getTime(); 127 | printf("Stack count and capacity: (%d, %d)\n", stack.count, stack.capacity); 128 | printf("Time: %.2f seconds\n", end - start); 129 | printf("--- End test 1: push %d objects ---\n", count); 130 | 131 | printf("\n"); 132 | 133 | printf("--- Start test 2: contains %d objects ---\n", count / 2); 134 | start = getTime(); 135 | for(int i = 0; i < count/2; i++) 136 | { 137 | Entry *entry = (Entry*)malloc(sizeof(Entry)); 138 | entry->index = rand() % count; 139 | entry->name = "entry"; 140 | assert(EntriesStackContains(&stack, entry)); 141 | free(entry); 142 | } 143 | end = getTime(); 144 | printf("Time: %.2f seconds\n", end - start); 145 | printf("--- End test 2: contains %d objects ---\n", count / 2); 146 | 147 | printf("\n"); 148 | 149 | printf("--- Start test 3: pop %d objects ---\n", count / 2); 150 | start = getTime(); 151 | for(int i = 0; i < count/2; i++) 152 | { 153 | Entry *peek = EntriesStackPeek(&stack); 154 | Entry *entry = EntriesStackPop(&stack); 155 | assert(peek->index == entry->index); 156 | free(entry); 157 | } 158 | end = getTime(); 159 | printf("Stack count and capacity: (%d, %d)\n", stack.count, stack.capacity); 160 | printf("Time: %.2f seconds\n", end - start); 161 | printf("--- End test 3: pop %d objects ---\n", count / 2); 162 | 163 | printf("--- End reference type tests ---\n"); 164 | } 165 | 166 | int main(int argc, char **argv) 167 | { 168 | /* initialize random seed: */ 169 | srand(time(NULL)); 170 | 171 | valueTypeTest(); 172 | 173 | printf("\n\n"); 174 | 175 | referenceTypeTest(); 176 | 177 | return 0; 178 | } -------------------------------------------------------------------------------- /tests/wave_writer_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define SHL_WAVE_WRITER_IMPLEMENTATION 6 | #include "../wave_writer.h" 7 | 8 | #define PI 3.14159265359f 9 | 10 | int main(int argc, char **argv) 11 | { 12 | shlWaveFile waveFile; 13 | 14 | long sampleRate = 44100; 15 | long toneHz = 256; 16 | long wavePeriod = sampleRate / toneHz; 17 | long bytesPerSample = sizeof (shl_sample_t); 18 | long toneVolume = 3000; 19 | 20 | if (!shlWaveInit(&waveFile, sampleRate, "out.wav")) { 21 | printf("Couldn't init wave file"); 22 | return -1; 23 | } 24 | 25 | long length = sampleRate * 60; 26 | shl_sample_t *buffer = (shl_sample_t*) malloc(length * bytesPerSample); 27 | 28 | for (int i = 0; i < length; i++) 29 | { 30 | float t = 2.0f * PI * (float)i / (float)wavePeriod; 31 | float sineValue = sinf(t); 32 | float sampleValue = sineValue * toneVolume; 33 | buffer[i] = (shl_sample_t)sampleValue; 34 | } 35 | 36 | shlWaveWrite(&waveFile, buffer, length, 1); 37 | shlWaveFlush(&waveFile, true); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /tests/xmi2mid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define SHL_MEMORY_BUFFER_IMPLEMENTATION 8 | #include "../memory_buffer.h" 9 | 10 | #include "../list.h" 11 | 12 | /** 13 | * Extension function of MemoryBuffer to read variable lengths integer values. 14 | */ 15 | bool mbReadUIntVar(MemoryBuffer* buffer, uint32_t* value) 16 | { 17 | uint32_t v = 0; 18 | uint8_t byte; 19 | for(int32_t i = 0; i < 4; ++i) 20 | { 21 | if(!mbRead(buffer, &byte)) 22 | return false; 23 | 24 | v = (v << 7) | (uint32_t)(byte & 0x7F); 25 | if((byte & 0x80) == 0) 26 | break; 27 | } 28 | *value = v; 29 | return true; 30 | } 31 | 32 | /** 33 | * Extension function of MemoryBuffer to write variable lengths integer values. 34 | */ 35 | bool mbWriteUIntVar(MemoryBuffer* buffer, uint32_t value) 36 | { 37 | int32_t byteCount = 1; 38 | uint32_t v = value & 0x7F; 39 | value >>= 7; 40 | while (value) 41 | { 42 | v = (v << 8) | 0x80 | (value & 0x7F); 43 | ++byteCount; 44 | value >>= 7; 45 | } 46 | 47 | for(int32_t i = 0; i < byteCount; ++i) 48 | { 49 | uint8_t byte = v & 0xFF; 50 | if(!mbWrite(buffer, byte)) 51 | return false; 52 | v >>= 8; 53 | } 54 | return true; 55 | } 56 | 57 | typedef struct _MidiToken 58 | { 59 | int32_t time; 60 | uint32_t bufferLength; 61 | uint8_t* buffer; 62 | uint8_t type; 63 | uint8_t data; 64 | } MidiToken; 65 | 66 | static int32_t compareTokens(const MidiToken left, const MidiToken right) 67 | { 68 | return left.time - right.time; 69 | } 70 | 71 | shlDeclareList(MidiTokenList, MidiToken) 72 | shlDefineList(MidiTokenList, MidiToken) 73 | 74 | static MidiToken* MidiTokenListAppend(MidiTokenList* list, int32_t time, uint8_t type) 75 | { 76 | MidiToken token = {0}; 77 | token.time = time; 78 | token.type = type; 79 | MidiTokenListAdd(list, token); 80 | return &list->items[list->count - 1]; 81 | } 82 | 83 | /** 84 | * This code is a port in C of the XMI2MID converter by Peter "Corsix" Cawley 85 | * in the War1gus repository. You can find the original C++ code here: 86 | * https://github.com/Wargus/war1gus/blob/master/xmi2mid.cpp. 87 | * 88 | * To understand more about these formats see: 89 | * http://www.shikadi.net/moddingwiki/XMI_Format 90 | * http://www.shikadi.net/moddingwiki/MID_Format 91 | * https://github.com/colxi/midi-parser-js/wiki/MIDI-File-Format-Specifications 92 | */ 93 | uint8_t* transcodeXmiToMid(uint8_t* xmiData, size_t xmiLength, size_t* midLength) 94 | { 95 | MemoryBuffer bufInput = {0}; 96 | mbInitFromMemory(&bufInput, xmiData, xmiLength); 97 | 98 | MemoryBuffer bufOutput = {0}; 99 | mbInitEmpty(&bufOutput); 100 | 101 | if (!mbScanTo(&bufInput, "EVNT", 4)) 102 | { 103 | mbFree(&bufOutput); 104 | return NULL; 105 | } 106 | 107 | if (!mbSkip(&bufInput, 8)) 108 | { 109 | mbFree(&bufOutput); 110 | return NULL; 111 | } 112 | 113 | MidiTokenListOptions options = {0}; 114 | 115 | MidiTokenList lstTokens; 116 | MidiTokenListInit(&lstTokens, options); 117 | 118 | MidiToken* token; 119 | int32_t tokenTime = 0; 120 | int32_t tempo = 500000; 121 | bool tempoSet = false; 122 | bool end = false; 123 | uint8_t tokenType, extendedType; 124 | uint32_t intVar; 125 | 126 | while (!mbIsEOF(&bufInput) && !end) 127 | { 128 | while (true) 129 | { 130 | if (!mbRead(&bufInput, &tokenType)) 131 | { 132 | mbFree(&bufOutput); 133 | return NULL; 134 | } 135 | 136 | if (tokenType & 0x80) 137 | break; 138 | 139 | tokenTime += (int32_t)tokenType * 3; 140 | } 141 | 142 | token = MidiTokenListAppend(&lstTokens, tokenTime, tokenType); 143 | token->buffer = bufInput._pointer + 1; 144 | 145 | switch (tokenType & 0xF0) 146 | { 147 | case 0xC0: 148 | case 0xD0: 149 | { 150 | if (!mbRead(&bufInput, &token->data)) 151 | { 152 | mbFree(&bufOutput); 153 | return NULL; 154 | } 155 | 156 | token->buffer = NULL; 157 | break; 158 | } 159 | 160 | case 0x80: 161 | case 0xA0: 162 | case 0xB0: 163 | case 0xE0: 164 | { 165 | if (!mbRead(&bufInput, &token->data)) 166 | { 167 | mbFree(&bufOutput); 168 | return NULL; 169 | } 170 | 171 | if (!mbSkip(&bufInput, 1)) 172 | { 173 | mbFree(&bufOutput); 174 | return NULL; 175 | } 176 | 177 | break; 178 | } 179 | 180 | case 0x90: 181 | { 182 | if (!mbRead(&bufInput, &extendedType)) 183 | { 184 | mbFree(&bufOutput); 185 | return NULL; 186 | } 187 | 188 | token->data = extendedType; 189 | 190 | if (!mbSkip(&bufInput, 1)) 191 | { 192 | mbFree(&bufOutput); 193 | return NULL; 194 | } 195 | 196 | assert(mbReadUIntVar(&bufInput, &intVar)); 197 | token = MidiTokenListAppend(&lstTokens, tokenTime + intVar * 3, tokenType); 198 | token->data = extendedType; 199 | token->buffer = (uint8_t*)"\0"; 200 | 201 | break; 202 | } 203 | 204 | case 0xF0: 205 | { 206 | extendedType = 0; 207 | 208 | if (tokenType == 0xFF) 209 | { 210 | if (!mbRead(&bufInput, &extendedType)) 211 | { 212 | mbFree(&bufOutput); 213 | return NULL; 214 | } 215 | 216 | if (extendedType == 0x2F) 217 | end = true; 218 | else if (extendedType == 0x51) 219 | { 220 | if (!tempoSet) 221 | { 222 | assert(mbSkip(&bufInput, 1)); 223 | assert(mbReadInt24BE(&bufInput, &tempo)); 224 | tempo *= 3; 225 | tempoSet = true; 226 | assert(mbSkip(&bufInput, -4)); 227 | } 228 | else 229 | { 230 | MidiTokenListRemoveAt(&lstTokens, lstTokens.count - 1); 231 | assert(mbReadUIntVar(&bufInput, &intVar)); 232 | if (!mbSkip(&bufInput, intVar)) 233 | { 234 | mbFree(&bufOutput); 235 | return NULL; 236 | } 237 | break; 238 | } 239 | } 240 | } 241 | 242 | token->data = extendedType; 243 | assert(mbReadUIntVar(&bufInput, &token->bufferLength)); 244 | token->buffer = bufInput._pointer; 245 | 246 | if (!mbSkip(&bufInput, token->bufferLength)) 247 | { 248 | mbFree(&bufOutput); 249 | return NULL; 250 | } 251 | 252 | break; 253 | } 254 | } 255 | } 256 | 257 | if (lstTokens.count == 0) 258 | { 259 | mbFree(&bufOutput); 260 | return NULL; 261 | } 262 | if (!mbWriteString(&bufOutput, "MThd\0\0\0\x06\0\0\0\x01", 12)) 263 | { 264 | mbFree(&bufOutput); 265 | return NULL; 266 | } 267 | if (!mbWriteUInt16BE(&bufOutput, (tempo * 3) / 25000)) 268 | { 269 | mbFree(&bufOutput); 270 | return NULL; 271 | } 272 | if (!mbWriteString(&bufOutput, "MTrk\xBA\xAD\xF0\x0D", 8)) 273 | { 274 | mbFree(&bufOutput); 275 | return NULL; 276 | } 277 | 278 | MidiTokenListSort(&lstTokens, compareTokens); 279 | 280 | tokenTime = 0; 281 | tokenType = 0; 282 | end = false; 283 | 284 | for (int32_t i = 0; i < lstTokens.count && !end; i++) 285 | { 286 | MidiToken t = lstTokens.items[i]; 287 | 288 | if (!mbWriteUIntVar(&bufOutput, t.time - tokenTime)) 289 | { 290 | mbFree(&bufOutput); 291 | return NULL; 292 | } 293 | 294 | tokenTime = t.time; 295 | 296 | if (t.type >= 0xF0) 297 | { 298 | tokenType = t.type; 299 | if (!mbWrite(&bufOutput, tokenType)) 300 | { 301 | mbFree(&bufOutput); 302 | return NULL; 303 | } 304 | 305 | if (tokenType == 0xFF) 306 | { 307 | if (!mbWrite(&bufOutput, t.data)) 308 | { 309 | mbFree(&bufOutput); 310 | return NULL; 311 | } 312 | 313 | if (t.data == 0x2F) 314 | end = true; 315 | } 316 | 317 | if (!mbWriteUIntVar(&bufOutput, t.bufferLength)) 318 | { 319 | mbFree(&bufOutput); 320 | return NULL; 321 | } 322 | if (!mbWriteBytes(&bufOutput, t.buffer, t.bufferLength)) 323 | { 324 | mbFree(&bufOutput); 325 | return NULL; 326 | } 327 | } 328 | else 329 | { 330 | if (t.type != tokenType) 331 | { 332 | tokenType = t.type; 333 | 334 | if (!mbWrite(&bufOutput, tokenType)) 335 | { 336 | mbFree(&bufOutput); 337 | return NULL; 338 | } 339 | } 340 | 341 | if (!mbWrite(&bufOutput, t.data)) 342 | { 343 | mbFree(&bufOutput); 344 | return NULL; 345 | } 346 | 347 | if (t.buffer) 348 | { 349 | if (!mbWriteBytes(&bufOutput, t.buffer, 1)) 350 | { 351 | mbFree(&bufOutput); 352 | return NULL; 353 | } 354 | } 355 | } 356 | } 357 | 358 | size_t length = mbPosition(&bufOutput) - 22; 359 | assert(mbSeek(&bufOutput, 18)); 360 | assert(mbWriteUInt32BE(&bufOutput, length)); 361 | 362 | uint8_t* midData = mbGetData(&bufOutput, midLength); 363 | 364 | mbFree(&bufOutput); 365 | return midData; 366 | } 367 | 368 | int main(int argc, char **argv) 369 | { 370 | if (argc == 1) 371 | { 372 | printf("Need path to .xmi file\n"); 373 | return -1; 374 | } 375 | 376 | char* inFileName = argv[1]; 377 | 378 | uint8_t* xmiData; 379 | size_t xmiLength; 380 | 381 | uint8_t* midData; 382 | size_t midLength; 383 | 384 | printf("%s\n", inFileName); 385 | 386 | FILE* f = fopen(inFileName, "rb"); 387 | if (!f) 388 | { 389 | printf("Couldn't open .xmi file\n"); 390 | return -1; 391 | } 392 | 393 | fseek(f, 0, SEEK_END); 394 | xmiLength = ftell(f); 395 | fseek(f, 0, SEEK_SET); 396 | xmiData = (uint8_t*)malloc(xmiLength); 397 | fread(xmiData, sizeof(uint8_t), xmiLength, f); 398 | fclose(f); 399 | 400 | printf("%d\n", xmiLength); 401 | printf("%s\n", xmiData); 402 | 403 | midData = transcodeXmiToMid(xmiData, xmiLength, &midLength); 404 | if (midData) 405 | { 406 | f = fopen("output.mid", "wb"); 407 | if (!f) 408 | { 409 | printf("Couldn't open .mid file\n"); 410 | return -1; 411 | } 412 | fwrite(midData, sizeof(uint8_t), midLength, f); 413 | fclose(f); 414 | free(midData); 415 | } 416 | else 417 | { 418 | printf("midData is NULL\n"); 419 | } 420 | 421 | free(xmiData); 422 | return 0; 423 | } 424 | -------------------------------------------------------------------------------- /wave_writer.h: -------------------------------------------------------------------------------- 1 | /* 2 | WAVE sound file writer for recording 16-bit output during program development. 3 | A single header library version in C89 of the Wave_Writer library written by Shay Green. 4 | 5 | Copyright (C) 2003-2005 by Shay Green. Permission is hereby granted, free 6 | of charge, to any person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, copy, modify, 9 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and 10 | to permit persons to whom the Software is furnished to do so, subject to the 11 | following conditions: The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions of the Software. THE 13 | SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 15 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef SHL_WAVE_WRITER_H 22 | #define SHL_WAVE_WRITER_H 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | typedef short shl_sample_t; 34 | 35 | #define SHL_WAVE_BUFFER_SIZE (32768 * 2) 36 | #define SHL_WAVE_HEADER_SIZE 0x2C 37 | 38 | #define SHL_U8_CAST(x) ((unsigned char)(x)) 39 | 40 | typedef struct _shl_wave_file 41 | { 42 | FILE* _file; 43 | unsigned char* _buffer; 44 | long sampleRate; 45 | long _sampleCount; 46 | long _bufferPos; 47 | int channelCount; 48 | } shlWaveFile; 49 | 50 | bool shlWaveInit(shlWaveFile *waveFile, long sampleRate, const char* filename); 51 | void shlWaveStereo(shlWaveFile *waveFile, bool stereo); 52 | long shlWaveSampleCount(shlWaveFile *waveFile); 53 | bool shlWaveWrite(shlWaveFile *waveFile, const shl_sample_t *in, long count, int skip); 54 | bool shlWaveFlush(shlWaveFile *waveFile, bool closeFile); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | 60 | #ifdef SHL_WAVE_WRITER_IMPLEMENTATION 61 | 62 | static bool __shlWaveFlush(shlWaveFile *waveFile) 63 | { 64 | if (waveFile->_bufferPos && !fwrite(waveFile->_buffer, waveFile->_bufferPos, 1, waveFile->_file)) { 65 | return false; 66 | } 67 | 68 | waveFile->_bufferPos = 0; 69 | return true; 70 | } 71 | 72 | bool shlWaveInit(shlWaveFile *waveFile, long sampleRate, const char* filename) 73 | { 74 | waveFile->_buffer = (unsigned char*) malloc(SHL_WAVE_BUFFER_SIZE); 75 | if (!waveFile->_buffer) { 76 | return false; 77 | } 78 | 79 | waveFile->_file = fopen(filename, "wb"); 80 | if (!waveFile->_file) { 81 | return false; 82 | } 83 | 84 | waveFile->sampleRate = sampleRate; 85 | waveFile->channelCount = 1; 86 | 87 | waveFile->_sampleCount = 0; 88 | waveFile->_bufferPos = SHL_WAVE_HEADER_SIZE; 89 | 90 | return true; 91 | } 92 | 93 | void shlWaveStereo(shlWaveFile *waveFile, bool stereo) 94 | { 95 | waveFile->channelCount = stereo ? 2 : 1; 96 | } 97 | 98 | long shlWaveSampleCount(shlWaveFile *waveFile) 99 | { 100 | return waveFile->_sampleCount; 101 | } 102 | 103 | bool shlWaveWrite(shlWaveFile *waveFile, const shl_sample_t *in, long count, int skip) 104 | { 105 | waveFile->_sampleCount += count; 106 | while (count) { 107 | if (waveFile->_bufferPos >= SHL_WAVE_BUFFER_SIZE) { 108 | if (!__shlWaveFlush(waveFile)) { 109 | return false; 110 | } 111 | } 112 | 113 | long n = (unsigned long) (SHL_WAVE_BUFFER_SIZE - waveFile->_bufferPos) / sizeof (shl_sample_t); 114 | if (n > count) n = count; 115 | count -= n; 116 | 117 | // convert to lsb first format 118 | unsigned char* p = &waveFile->_buffer[waveFile->_bufferPos]; 119 | while (n--) { 120 | int s = *in; 121 | in += skip; 122 | *p++ = (unsigned char) s; 123 | *p++ = (unsigned char) (s >> 8); 124 | } 125 | 126 | waveFile->_bufferPos = p - waveFile->_buffer; 127 | } 128 | 129 | return true; 130 | } 131 | 132 | bool shlWaveFlush(shlWaveFile *waveFile, bool closeFile) 133 | { 134 | if (!__shlWaveFlush(waveFile)) { 135 | return false; 136 | } 137 | 138 | if (closeFile) { 139 | // generate header 140 | long sample_rate = waveFile->sampleRate; 141 | int channel_count = waveFile->channelCount; 142 | long ds = waveFile->_sampleCount * sizeof (shl_sample_t); 143 | long rs = SHL_WAVE_HEADER_SIZE - 8 + ds; 144 | int frame_size = channel_count * sizeof (shl_sample_t); 145 | long bps = sample_rate * frame_size; 146 | 147 | unsigned char header[SHL_WAVE_HEADER_SIZE] = { 148 | 'R','I','F','F', 149 | SHL_U8_CAST(rs), SHL_U8_CAST(rs >> 8), // length of rest of file 150 | SHL_U8_CAST(rs >> 16), SHL_U8_CAST(rs >> 24), 151 | 'W','A','V','E', 152 | 'f','m','t',' ', 153 | 0x10, 0, 0, 0, // size of fmt chunk 154 | 1, 0, // uncompressed format 155 | SHL_U8_CAST(channel_count), 0, // channel count 156 | SHL_U8_CAST(sample_rate), SHL_U8_CAST(sample_rate >> 8), // sample rate 157 | SHL_U8_CAST(sample_rate >> 16), SHL_U8_CAST(sample_rate >> 24), 158 | SHL_U8_CAST(bps), SHL_U8_CAST(bps >> 8), // bytes per second 159 | SHL_U8_CAST(bps >> 16), SHL_U8_CAST(bps >> 24), 160 | SHL_U8_CAST(frame_size), 0, // bytes per sample frame 161 | 16, 0, // bits per sample 162 | 'd','a','t','a', 163 | SHL_U8_CAST(ds), SHL_U8_CAST(ds >> 8), // size of sample data 164 | SHL_U8_CAST(ds >> 16), SHL_U8_CAST(ds >> 24) 165 | // ... // sample data 166 | }; 167 | 168 | // write header 169 | if (fseek(waveFile->_file, 0, SEEK_SET)) { 170 | return false; 171 | } 172 | 173 | if (!fwrite(header, sizeof header, 1, waveFile->_file)) { 174 | return false; 175 | } 176 | 177 | if (fclose(waveFile->_file)) { 178 | return false; 179 | } 180 | 181 | free(waveFile->_buffer); 182 | } 183 | 184 | return true; 185 | } 186 | 187 | #endif // SHL_WAVE_WRITER_IMPLEMENTATION 188 | #endif // SHL_WAVE_WRITER_H --------------------------------------------------------------------------------