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