Gama C Library
Gama C API Documentation
malloc.h
Go to the documentation of this file.
1/**
2 * @file malloc.h
3 * @brief Custom memory allocation functions (`malloc`, `free`, `calloc`, `realloc`)
4 * using a fixed-size static memory pool.
5 *
6 * This file provides an alternative memory management system for Gama,
7 * primarily for environments where dynamic system `malloc` might be
8 * unavailable or undesirable (e.g., embedded systems, WebAssembly with specific
9 * memory requirements). It pre-allocates a large static buffer and manages
10 * memory chunks within it.
11 *
12 * @warning This implementation redefines standard C library functions (`malloc`,
13 * `free`, `calloc`, `realloc`). Care must be taken to ensure this
14 * does not conflict with system-level memory allocation or other
15 * libraries that expect standard `libc` behavior. This file is
16 * typically included conditionally.
17 */
18#pragma once
19
20#include <stdlib.h>
21#ifndef GM_MALLOC
22#define GM_MALLOC
23#endif
24
25#include <stddef.h>
26/**
27 * @def MEMORY
28 * @internal
29 * @brief Defines the size of the main memory pool in megabytes.
30 * Default is 10MB if not otherwise defined.
31 */
32#ifndef MEMORY
33// default memory to 10MB
34#define MEMORY 10
35
36#endif
37/**
38 * @def MEMORY_B
39 * @internal
40 * @brief Defines an additional size for the memory pool in bytes.
41 * Default is 0 bytes if not otherwise defined.
42 */
43#ifndef MEMORY_B
44#define MEMORY_B 0
45#endif
46/**
47 * @def MEMORY_TOTAL
48 * @internal
49 * @brief Total size of the memory pool in bytes.
50 */
51#define MEMORY_TOTAL ((MEMORY << 20) + MEMORY_B)
52/**
53 * @def MEMORY_SPOTS
54 * @internal
55 * @brief Maximum number of memory blocks (spots) that can be tracked.
56 * Calculated as `MEMORY_TOTAL / 100`.
57 */
58#ifndef MEMORY_SPOTS
59#define MEMORY_SPOTS (MEMORY_TOTAL / 100)
60#endif
61
62#define _MALLOC_H 1
63
64/**
65 * @internal
66 * @brief Represents a block of memory within the static pool.
67 */
69 size_t index; /**< Starting index (offset) in the `_memory` pool. */
70 size_t size; /**< Size of this block (0 indicates a free block). */
71};
72/**
73 * @internal
74 * @brief The main static memory pool buffer.
75 */
76static char _memory[MEMORY_TOTAL];
77/**
78 * @internal
79 * @brief Array to keep track of allocated and free memory spots.
80 */
81static struct _memory_spot _memory_spots[MEMORY_SPOTS];
82/**
83 * @internal
84 * @brief Current number of active memory spots being tracked.
85 */
86static size_t _memory_spot_size = 0;
87/**
88 * @internal
89 * @brief Removes a memory spot from the `_memory_spots` array.
90 * @param index The index of the spot to remove.
91 */
92static void _remove_memory_spot(size_t index) {
93 for (size_t i = index; i < _memory_spot_size - 1; i++) {
94 _memory_spots[i] = _memory_spots[i + 1];
95 }
96 if (_memory_spot_size > 0) {
97 _memory_spot_size--;
98 }
99}
100/**
101 * @internal
102 * @brief Adds a new memory spot to the `_memory_spots` array, maintaining sorted order.
103 * @param index The starting index of the new spot.
104 * @param size The size of the new spot.
105 * @return The newly added `_memory_spot` struct.
106 */
107static struct _memory_spot _add_memory_spot(size_t index, size_t size) {
108 if (_memory_spot_size >= MEMORY_SPOTS) {
109 // gapi_log("OOM: sorry kid, memory's finish, no _spots left, try "
110 // "https://gama.rbs.cm/faq#oom");
111 exit(100); // Exits if no more spots are available
112 return (struct _memory_spot){0, 0};
113 }
114 // Find new spot
115 size_t insert_pos = _memory_spot_size;
116 for (size_t i = 0; i < _memory_spot_size; i++) {
117 if (_memory_spots[i].index > index) {
118 // move others right to keep order and ease searching
119 for (size_t j = _memory_spot_size; j > i; j--) {
120 _memory_spots[j] = _memory_spots[j - 1];
121 }
122 insert_pos = i;
123 break;
124 }
125 }
126 // spot found, use it
127 _memory_spots[insert_pos].index = index;
128 _memory_spots[insert_pos].size = size;
129 if (insert_pos == _memory_spot_size) // last spot => new spot
130 _memory_spot_size++;
131
132 return _memory_spots[insert_pos];
133}
134
135/**
136 * @brief Custom implementation of `malloc` using a static memory pool.
137 *
138 * Allocates a block of `size` bytes from the predefined static memory pool.
139 *
140 * @param size The number of bytes to allocate.
141 * @return A pointer to the allocated memory block, or `NULL` if allocation fails
142 * (e.g., out of memory or no suitable spot).
143 */
144void *malloc(size_t size) {
145 if (size == 0)
146 return NULL;
147 if (_memory_spot_size == 0) {
148 // same as add_memory_spot will do
149 _memory_spots[0].index = 0;
150 _memory_spots[0].size = 0; // 0, free for now
151 _memory_spot_size = 1;
152 }
153 // Look for a free spot that's large enough
154 for (size_t i = 0; i < _memory_spot_size; i++) {
155 if (_memory_spots[i].size == 0) { // Free block
156 size_t start = _memory_spots[i].index;
157 size_t end = (i + 1 < _memory_spot_size) ? _memory_spots[i + 1].index
158 : MEMORY_TOTAL;
159 size_t available_size = end - start;
160 if (available_size >= size) {
161 _memory_spots[i].size = size; // Mark as used
162 // If there's leftover space, split it
163 if (available_size > size) {
164 _add_memory_spot(start + size, 0); // New free block
165 }
166 return &_memory[start];
167 }
168 }
169 }
170 // No suitable block found, create one
171 if (_memory_spot_size > 0) {
172 struct _memory_spot *last = &_memory_spots[_memory_spot_size - 1];
173 size_t end_of_last = last->index + last->size;
174 if (end_of_last + size < MEMORY_TOTAL) {
175 struct _memory_spot new_spot = _add_memory_spot(end_of_last, size);
176 return &_memory[new_spot.index];
177 }
178 }
179 return NULL; // Out of memory
180}
181
182/**
183 * @brief Custom implementation of `free` for memory allocated by `malloc` (this custom version).
184 *
185 * Frees a previously allocated memory block, making it available for future allocations.
186 *
187 * @param ptr A pointer to the memory block to free. If `ptr` is `NULL`, no operation is performed.
188 */
189void free(void *ptr) {
190 if (!ptr)
191 return;
192 char *char_ptr = (char *)ptr;
193 size_t index = char_ptr - _memory;
194 if (index >= MEMORY_TOTAL)
195 return; // Invalid pointer
196 // Find the block this pointer belongs to
197 for (size_t i = 0; i < _memory_spot_size; i++) {
198 if (_memory_spots[i].index == index && _memory_spots[i].size > 0) {
199 _memory_spots[i].size = 0; // Mark as free
200 return;
201 }
202 }
203}
204
205/**
206 * @brief Custom implementation of `calloc` using a static memory pool.
207 *
208 * Allocates a block of memory for an array of `count` elements, each of `size` bytes,
209 * and initializes all bytes in the allocated block to zero.
210 *
211 * @param count The number of elements to allocate.
212 * @param size The size of each element in bytes.
213 * @return A pointer to the allocated and zero-initialized memory, or `NULL` if allocation fails.
214 */
215void *calloc(size_t count, size_t size) {
216 size_t total_size = count * size;
217 void *ptr = malloc(total_size);
218 if (ptr) {
219 char *p = (char *)ptr;
220 for (size_t i = 0; i < total_size; i++) {
221 p[i] = 0; // zero all block contents
222 }
223 }
224 return ptr;
225}
226
227/**
228 * @brief Custom implementation of `realloc` for memory allocated by `malloc` (this custom version).
229 *
230 * Resizes a previously allocated memory block.
231 *
232 * @param ptr A pointer to the memory block to reallocate. If `ptr` is `NULL`, behaves like `malloc`.
233 * @param size The new size for the memory block. If `size` is `0`, behaves like `free`.
234 * @return A pointer to the reallocated memory block, or `NULL` if reallocation fails.
235 */
236void *realloc(void *ptr, size_t size) {
237 if (!ptr)
238 return malloc(size);
239 if (size == 0) {
240 free(ptr);
241 return NULL;
242 }
243 // Find current block size
244 char *char_ptr = (char *)ptr;
245 size_t index = char_ptr - _memory;
246 for (size_t i = 0; i < _memory_spot_size; i++) {
247 if (_memory_spots[i].index == index && _memory_spots[i].size > 0) {
248 if (_memory_spots[i].size >= size) {
249 // Current block is large enough
250 return ptr;
251 } else {
252 // Need to allocate new block and copy
253 void *new_ptr = malloc(size);
254 if (new_ptr) {
255 // Copy old data
256 char *src = (char *)ptr;
257 char *dst = (char *)new_ptr;
258 for (size_t j = 0; j < _memory_spots[i].size; j++) {
259 dst[j] = src[j];
260 }
261 free(ptr);
262 }
263 return new_ptr;
264 }
265 }
266 }
267 return NULL; // Invalid pointer
268}
#define MEMORY_SPOTS
Definition malloc.h:59
void * malloc(size_t size)
Custom implementation of malloc using a static memory pool.
Definition malloc.h:144
#define MEMORY_TOTAL
Definition malloc.h:51
void * realloc(void *ptr, size_t size)
Custom implementation of realloc for memory allocated by malloc (this custom version).
Definition malloc.h:236
void * calloc(size_t count, size_t size)
Custom implementation of calloc using a static memory pool.
Definition malloc.h:215
void free(void *ptr)
Custom implementation of free for memory allocated by malloc (this custom version).
Definition malloc.h:189
Definition malloc.h:68
size_t size
Definition malloc.h:70
size_t index
Definition malloc.h:69