diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2023-01-02 19:45:59 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2023-01-15 10:04:13 +0000 |
commit | 680261fbb7c3c0a4cde4321c35ce3fe0e6b0cce8 (patch) | |
tree | 7be68e9dfd2986059f9e4decd369fea1bdc91d1b /firmware/include/buflib_mempool.h | |
parent | e23c78522a1b306c9838672a5fec3698938f9586 (diff) | |
download | rockbox-680261fbb7c3c0a4cde4321c35ce3fe0e6b0cce8.tar.gz rockbox-680261fbb7c3c0a4cde4321c35ce3fe0e6b0cce8.zip |
buflib: Prep for multiple backend support, rename to buflib_mempool
Rename the current buflib implementation to buflib_mempool.
Change-Id: Iefdf74be1f7d8fcd19e6ce2289c3d1459b54d013
Diffstat (limited to 'firmware/include/buflib_mempool.h')
-rw-r--r-- | firmware/include/buflib_mempool.h | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/firmware/include/buflib_mempool.h b/firmware/include/buflib_mempool.h new file mode 100644 index 0000000000..61fe2168b0 --- /dev/null +++ b/firmware/include/buflib_mempool.h | |||
@@ -0,0 +1,381 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * This is a memory allocator designed to provide reasonable management of free | ||
11 | * space and fast access to allocated data. More than one allocator can be used | ||
12 | * at a time by initializing multiple contexts. | ||
13 | * | ||
14 | * Copyright (C) 2009 Andrew Mahone | ||
15 | * Copyright (C) 2011 Thomas Martitz | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or | ||
18 | * modify it under the terms of the GNU General Public License | ||
19 | * as published by the Free Software Foundation; either version 2 | ||
20 | * of the License, or (at your option) any later version. | ||
21 | * | ||
22 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
23 | * KIND, either express or implied. | ||
24 | * | ||
25 | ****************************************************************************/ | ||
26 | #ifndef _BUFLIB_MEMPOOL_H_ | ||
27 | #define _BUFLIB_MEMPOOL_H_ | ||
28 | |||
29 | #include <stdint.h> | ||
30 | #include <stdbool.h> | ||
31 | #include <string.h> | ||
32 | |||
33 | /* add extra checks to buflib_get_data to catch bad handles */ | ||
34 | //#define BUFLIB_DEBUG_GET_DATA | ||
35 | |||
36 | /* support integrity check */ | ||
37 | //#define BUFLIB_DEBUG_CHECK_VALID | ||
38 | |||
39 | /* support debug printing of memory blocks */ | ||
40 | //#define BUFLIB_DEBUG_PRINT | ||
41 | |||
42 | union buflib_data | ||
43 | { | ||
44 | intptr_t val; /* length of the block in n*sizeof(union buflib_data). | ||
45 | Includes buflib metadata overhead. A negative value | ||
46 | indicates block is unallocated */ | ||
47 | volatile unsigned pincount; /* number of pins */ | ||
48 | struct buflib_callbacks* ops; /* callback functions for move and shrink. Can be NULL */ | ||
49 | char* alloc; /* start of allocated memory area */ | ||
50 | union buflib_data *handle; /* pointer to entry in the handle table. | ||
51 | Used during compaction for fast lookup */ | ||
52 | }; | ||
53 | |||
54 | struct buflib_context | ||
55 | { | ||
56 | union buflib_data *handle_table; | ||
57 | union buflib_data *first_free_handle; | ||
58 | union buflib_data *last_handle; | ||
59 | union buflib_data *buf_start; | ||
60 | union buflib_data *alloc_end; | ||
61 | bool compact; | ||
62 | }; | ||
63 | |||
64 | /** | ||
65 | * This declares the minimal overhead that is required per alloc. These | ||
66 | * are bytes that are allocated from the context's pool in addition | ||
67 | * to the actually requested number of bytes. | ||
68 | * | ||
69 | * The total number of bytes consumed by an allocation is | ||
70 | * BUFLIB_ALLOC_OVERHEAD + requested bytes + pad to pointer size | ||
71 | */ | ||
72 | #define BUFLIB_ALLOC_OVERHEAD (4*sizeof(union buflib_data)) | ||
73 | |||
74 | /** | ||
75 | * Callbacks used by the buflib to inform allocation that compaction | ||
76 | * is happening (before data is moved) | ||
77 | * | ||
78 | * Note that buflib tries to move to satisfy new allocations before shrinking. | ||
79 | * So if you have something to resize try to do it outside of the callback. | ||
80 | * | ||
81 | * Regardless of the above, if the allocation is SHRINKABLE, but not | ||
82 | * MUST_NOT_MOVE buflib will move the allocation before even attempting to | ||
83 | * shrink. | ||
84 | */ | ||
85 | struct buflib_callbacks { | ||
86 | /** | ||
87 | * This is called before data is moved. Use this to fix up any cached | ||
88 | * pointers pointing to inside the allocation. The size is unchanged. | ||
89 | * | ||
90 | * This is not needed if you don't cache the data pointer (but always | ||
91 | * call buflib_get_data()) and don't pass pointer to the data to yielding | ||
92 | * functions. | ||
93 | * | ||
94 | * handle: The corresponding handle | ||
95 | * current: The current start of the allocation | ||
96 | * new: The new start of the allocation, after data movement | ||
97 | * | ||
98 | * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_MOVE if movement | ||
99 | * is impossible at this moment. | ||
100 | * | ||
101 | * If NULL: this allocation must not be moved around | ||
102 | * by the buflib when compaction occurs. Attention: Don't confuse | ||
103 | * that with passing NULL for the whole callback structure | ||
104 | * to buflib_alloc_ex(). This would enable moving buffers by default. | ||
105 | * You have to pass NULL inside the "struct buflib_callbacks" structure. | ||
106 | */ | ||
107 | int (*move_callback)(int handle, void* current, void* new); | ||
108 | /** | ||
109 | * This is called when the buflib desires to shrink a buffer | ||
110 | * in order to satisfy new allocation. This happens when buflib runs | ||
111 | * out of memory, e.g. because buflib_alloc_maximum() was called. | ||
112 | * Move data around as you need to make space and call core_shrink() as | ||
113 | * appropriate from within the callback to complete the shrink operation. | ||
114 | * buflib will not move data as part of shrinking. | ||
115 | * | ||
116 | * hint: bit mask containing hints on how shrinking is desired (see below) | ||
117 | * handle: The corresponding handle | ||
118 | * start: The old start of the allocation | ||
119 | * | ||
120 | * Return: Return BUFLIB_CB_OK, or BUFLIB_CB_CANNOT_SHRINK if shirinking | ||
121 | * is impossible at this moment. | ||
122 | * | ||
123 | * if NULL: this allocation cannot be resized. | ||
124 | * It is recommended that allocation that must not move are | ||
125 | * at least shrinkable | ||
126 | */ | ||
127 | int (*shrink_callback)(int handle, unsigned hints, void* start, size_t old_size); | ||
128 | /** | ||
129 | * This is called when special steps must be taken for synchronization | ||
130 | * both before the move_callback is called and after the data has been | ||
131 | * moved. | ||
132 | */ | ||
133 | void (*sync_callback)(int handle, bool sync_on); | ||
134 | }; | ||
135 | |||
136 | /** A set of all NULL callbacks for use with allocations that need to stay | ||
137 | * locked in RAM and not moved or shrunk. These type of allocations should | ||
138 | * be avoided as much as possible to avoid memory fragmentation but it can | ||
139 | * suitable for short-lived allocations. */ | ||
140 | extern struct buflib_callbacks buflib_ops_locked; | ||
141 | |||
142 | #define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK) | ||
143 | #define BUFLIB_SHRINK_POS_FRONT (1u<<31) | ||
144 | #define BUFLIB_SHRINK_POS_BACK (1u<<30) | ||
145 | #define BUFLIB_SHRINK_POS_MASK (BUFLIB_SHRINK_POS_FRONT|BUFLIB_SHRINK_POS_BACK) | ||
146 | |||
147 | /** | ||
148 | * Possible return values for the callbacks, some of them can cause | ||
149 | * compaction to fail and therefore new allocations to fail | ||
150 | */ | ||
151 | /* Everything alright */ | ||
152 | #define BUFLIB_CB_OK 0 | ||
153 | /* Tell buflib that moving failed. Buflib may retry to move at any point */ | ||
154 | #define BUFLIB_CB_CANNOT_MOVE 1 | ||
155 | /* Tell buflib that resizing failed, possibly future making allocations fail */ | ||
156 | #define BUFLIB_CB_CANNOT_SHRINK 1 | ||
157 | |||
158 | /** | ||
159 | * Initializes buflib with a caller allocated context instance and memory pool. | ||
160 | * | ||
161 | * The buflib_context instance needs to be passed to every other buflib | ||
162 | * function. It's should be considered opaque, even though it is not yet | ||
163 | * (that's to make inlining core_get_data() possible). The documentation | ||
164 | * of the other functions will not describe the context | ||
165 | * instance parameter further as it's obligatory. | ||
166 | * | ||
167 | * context: The new buflib instance to be initialized, allocated by the caller | ||
168 | * size: The size of the memory pool | ||
169 | */ | ||
170 | void buflib_init(struct buflib_context *context, void *buf, size_t size); | ||
171 | |||
172 | |||
173 | /** | ||
174 | * Returns the amount of unallocated bytes. It does not mean this amount | ||
175 | * can be actually allocated because they might not be contiguous. | ||
176 | * | ||
177 | * Returns: The number of unallocated bytes in the memory pool. | ||
178 | */ | ||
179 | size_t buflib_available(struct buflib_context *ctx); | ||
180 | |||
181 | /** | ||
182 | * Returns the biggest possible allocation that can be determined to succeed. | ||
183 | * | ||
184 | * Returns: The amount of bytes of the biggest unallocated, contiguous region. | ||
185 | */ | ||
186 | size_t buflib_allocatable(struct buflib_context *ctx); | ||
187 | |||
188 | /** | ||
189 | * Relocates the fields in *ctx to the new buffer position pointed to by buf. | ||
190 | * This does _not_ move any data but updates the pointers. The data has | ||
191 | * to be moved afterwards manually and only if this function returned true. | ||
192 | * | ||
193 | * This is intended to be called from within a move_callback(), for | ||
194 | * buflib-on-buflib scenarios (i.e. a new buflib instance backed by a buffer | ||
195 | * that was allocated by another buflib instance). Be aware that if the parent | ||
196 | * move_callback() moves the underlying buffer _no_ move_callback() of the | ||
197 | * underlying buffer are called. | ||
198 | * | ||
199 | * Returns true of the relocation was successful. If it returns false no | ||
200 | * change to *ctx was made. | ||
201 | */ | ||
202 | bool buflib_context_relocate(struct buflib_context *ctx, void *buf); | ||
203 | |||
204 | /** | ||
205 | * Allocates memory from buflib's memory pool | ||
206 | * | ||
207 | * size: How many bytes to allocate | ||
208 | * | ||
209 | * This function passes NULL for the callback structure "ops", so buffers | ||
210 | * are movable. Don't pass them to functions that yield(). | ||
211 | * | ||
212 | * Returns: A positive integer handle identifying this allocation, or | ||
213 | * a negative value on error (0 is also not a valid handle) | ||
214 | */ | ||
215 | int buflib_alloc(struct buflib_context *context, size_t size); | ||
216 | |||
217 | |||
218 | /** | ||
219 | * Allocates memory from the buflib's memory pool with additional callbacks | ||
220 | * and flags | ||
221 | * | ||
222 | * size: How many bytes to allocate | ||
223 | * ops: a struct with pointers to callback functions (see above). | ||
224 | * if "ops" is NULL: Buffer is movable. | ||
225 | * | ||
226 | * Returns: A positive integer handle identifying this allocation, or | ||
227 | * a negative value on error (0 is also not a valid handle) | ||
228 | */ | ||
229 | int buflib_alloc_ex(struct buflib_context *ctx, size_t size, | ||
230 | struct buflib_callbacks *ops); | ||
231 | |||
232 | |||
233 | /** | ||
234 | * Gets all available memory from buflib, for temporary use. | ||
235 | * | ||
236 | * Since this effectively makes all future allocations fail (unless | ||
237 | * another allocation is freed in the meantime), you should definitely provide | ||
238 | * a shrink callback if you plan to hold the buffer for a longer period. This | ||
239 | * will allow buflib to permit allocations by shrinking the buffer returned by | ||
240 | * this function. | ||
241 | * | ||
242 | * Note that this might return many more bytes than buflib_available() or | ||
243 | * buflib_allocatable() return, because it aggressively compacts the pool | ||
244 | * and even shrinks other allocations. However, do not depend on this behavior, | ||
245 | * it may change. | ||
246 | * | ||
247 | * size: The actual size will be returned into size | ||
248 | * ops: a struct with pointers to callback functions | ||
249 | * | ||
250 | * Returns: A positive integer handle identifying this allocation, or | ||
251 | * a negative value on error (0 is also not a valid handle) | ||
252 | */ | ||
253 | int buflib_alloc_maximum(struct buflib_context* ctx, | ||
254 | size_t *size, struct buflib_callbacks *ops); | ||
255 | |||
256 | /** | ||
257 | * Queries the data pointer for the given handle. It's actually a cheap | ||
258 | * operation, don't hesitate using it extensively. | ||
259 | * | ||
260 | * Notice that you need to re-query after every direct or indirect yield(), | ||
261 | * because compaction can happen by other threads which may get your data | ||
262 | * moved around (or you can get notified about changes by callbacks, | ||
263 | * see further above). | ||
264 | * | ||
265 | * handle: The handle corresponding to the allocation | ||
266 | * | ||
267 | * Returns: The start pointer of the allocation | ||
268 | */ | ||
269 | #ifdef BUFLIB_DEBUG_GET_DATA | ||
270 | void *buflib_get_data(struct buflib_context *ctx, int handle); | ||
271 | #else | ||
272 | static inline void *buflib_get_data(struct buflib_context *ctx, int handle) | ||
273 | { | ||
274 | return (void *)ctx->handle_table[-handle].alloc; | ||
275 | } | ||
276 | #endif | ||
277 | |||
278 | /** | ||
279 | * Shrink the memory allocation associated with the given handle | ||
280 | * Mainly intended to be used with the shrink callback, but it can also | ||
281 | * be called outside as well, e.g. to give back buffer space allocated | ||
282 | * with buflib_alloc_maximum(). | ||
283 | * | ||
284 | * Note that you must move/copy data around yourself before calling this, | ||
285 | * buflib will not do this as part of shrinking. | ||
286 | * | ||
287 | * handle: The handle identifying this allocation | ||
288 | * new_start: the new start of the allocation | ||
289 | * new_size: the new size of the allocation | ||
290 | * | ||
291 | * Returns: true if shrinking was successful. Otherwise it returns false, | ||
292 | * without having modified memory. | ||
293 | * | ||
294 | */ | ||
295 | bool buflib_shrink(struct buflib_context *ctx, int handle, void* newstart, size_t new_size); | ||
296 | |||
297 | /** | ||
298 | * Increment the pin count for a handle. When pinned the handle will not | ||
299 | * be moved and move callbacks will not be triggered, allowing a pointer | ||
300 | * to the buffer to be kept across yields or used for I/O. | ||
301 | * | ||
302 | * Note that shrink callbacks can still be invoked for pinned handles. | ||
303 | */ | ||
304 | void buflib_pin(struct buflib_context *ctx, int handle); | ||
305 | |||
306 | /** | ||
307 | * Decrement the pin count for a handle. | ||
308 | */ | ||
309 | void buflib_unpin(struct buflib_context *ctx, int handle); | ||
310 | |||
311 | /** | ||
312 | * Get the current pin count of a handle. Zero means the handle is not pinned. | ||
313 | */ | ||
314 | unsigned buflib_pin_count(struct buflib_context *ctx, int handle); | ||
315 | |||
316 | /** | ||
317 | * Frees memory associated with the given handle | ||
318 | * | ||
319 | * Returns: 0 (to invalidate handles in one line, 0 is not a valid handle) | ||
320 | */ | ||
321 | int buflib_free(struct buflib_context *context, int handle); | ||
322 | |||
323 | /** | ||
324 | * Moves the underlying buflib buffer up by size bytes (as much as | ||
325 | * possible for size == 0) without moving the end. This effectively | ||
326 | * reduces the available space by taking away manageable space from the | ||
327 | * front. This space is not available for new allocations anymore. | ||
328 | * | ||
329 | * To make space available in the front, everything is moved up. | ||
330 | * It does _NOT_ call the move callbacks | ||
331 | * | ||
332 | * | ||
333 | * size: size in bytes to move the buffer up (take away). The actual | ||
334 | * bytes moved is returned in this | ||
335 | * Returns: The new start of the underlying buflib buffer | ||
336 | */ | ||
337 | void* buflib_buffer_out(struct buflib_context *ctx, size_t *size); | ||
338 | |||
339 | /** | ||
340 | * Moves the underlying buflib buffer down by size bytes without | ||
341 | * moving the end. This grows the buflib buffer by adding space to the front. | ||
342 | * The new bytes are available for new allocations. | ||
343 | * | ||
344 | * Everything is moved down, and the new free space will be in the middle. | ||
345 | * It does _NOT_ call the move callbacks. | ||
346 | * | ||
347 | * size: size in bytes to move the buffer down (new free space) | ||
348 | */ | ||
349 | void buflib_buffer_in(struct buflib_context *ctx, int size); | ||
350 | |||
351 | #ifdef BUFLIB_DEBUG_PRINT | ||
352 | /** | ||
353 | * Return the number of blocks in the buffer, allocated or unallocated. | ||
354 | * | ||
355 | * Only available if BUFLIB_DEBUG_PRINT is defined. | ||
356 | */ | ||
357 | int buflib_get_num_blocks(struct buflib_context *ctx); | ||
358 | |||
359 | /** | ||
360 | * Write a string describing the block at index block_num to the | ||
361 | * provided buffer. The buffer will always be null terminated and | ||
362 | * there is no provision to detect truncation. (A 40-byte buffer | ||
363 | * is enough to contain any returned string.) | ||
364 | * | ||
365 | * Returns false if the block index is out of bounds, and writes | ||
366 | * an empty string. | ||
367 | * | ||
368 | * Only available if BUFLIB_DEBUG_PRINT is defined. | ||
369 | */ | ||
370 | bool buflib_print_block_at(struct buflib_context *ctx, int block_num, | ||
371 | char *buf, size_t bufsize); | ||
372 | #endif | ||
373 | |||
374 | #ifdef BUFLIB_DEBUG_CHECK_VALID | ||
375 | /** | ||
376 | * Check integrity of given buflib context | ||
377 | */ | ||
378 | void buflib_check_valid(struct buflib_context *ctx); | ||
379 | #endif | ||
380 | |||
381 | #endif /* _BUFLIB_MEMPOOL_H_ */ | ||