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.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.h')
-rw-r--r-- | firmware/include/buflib.h | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h index 36d171963a..32a5a6abe0 100644 --- a/firmware/include/buflib.h +++ b/firmware/include/buflib.h | |||
@@ -7,6 +7,8 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2009 Andrew Mahone | ||
11 | * Copyright (C) 2011 Thomas Martitz | ||
10 | * Copyright (C) 2023 Aidan MacDonald | 12 | * Copyright (C) 2023 Aidan MacDonald |
11 | * | 13 | * |
12 | * This program is free software; you can redistribute it and/or | 14 | * This program is free software; you can redistribute it and/or |
@@ -22,9 +24,366 @@ | |||
22 | #define _BUFLIB_H_ | 24 | #define _BUFLIB_H_ |
23 | 25 | ||
24 | #include "config.h" | 26 | #include "config.h" |
27 | #include <stdint.h> | ||
28 | #include <stdbool.h> | ||
29 | #include <string.h> | ||
30 | |||
31 | /* Add extra checks to buflib_get_data to catch bad handles */ | ||
32 | //#define BUFLIB_DEBUG_GET_DATA | ||
33 | |||
34 | /* Support integrity check */ | ||
35 | //#define BUFLIB_DEBUG_CHECK_VALID | ||
36 | |||
37 | /* Support debug printing of memory blocks */ | ||
38 | //#define BUFLIB_DEBUG_PRINT | ||
39 | |||
40 | /* Defined by the backend header. */ | ||
41 | struct buflib_context; | ||
42 | |||
43 | /* Buflib callback return codes. */ | ||
44 | #define BUFLIB_CB_OK 0 | ||
45 | #define BUFLIB_CB_CANNOT_MOVE 1 | ||
46 | #define BUFLIB_CB_CANNOT_SHRINK 1 | ||
47 | |||
48 | /* Buflib shrink hints. */ | ||
49 | #define BUFLIB_SHRINK_SIZE_MASK (~BUFLIB_SHRINK_POS_MASK) | ||
50 | #define BUFLIB_SHRINK_POS_FRONT (1u<<31) | ||
51 | #define BUFLIB_SHRINK_POS_BACK (1u<<30) | ||
52 | #define BUFLIB_SHRINK_POS_MASK (BUFLIB_SHRINK_POS_FRONT|BUFLIB_SHRINK_POS_BACK) | ||
53 | |||
54 | /** | ||
55 | * Callbacks run by buflib to manage an allocation. | ||
56 | */ | ||
57 | struct buflib_callbacks | ||
58 | { | ||
59 | /** | ||
60 | * \brief Called when buflib wants to move the buffer | ||
61 | * \param handle Handle being moved | ||
62 | * \param current Current address of the buffer | ||
63 | * \param new New address the buffer would have after moving | ||
64 | * \return BUFLIB_CB_OK - Allow the buffer to be moved. | ||
65 | * \return BUFLIB_CB_CANNOT_MOVE - Do not allow the buffer to be moved. | ||
66 | * | ||
67 | * This callback allows you to fix up any pointers that might | ||
68 | * be pointing to the buffer before it is moved. The task of | ||
69 | * actually moving the buffer contents is performed by buflib | ||
70 | * after the move callback returns, if movement is allowed. | ||
71 | * | ||
72 | * Care must be taken to ensure that the buffer is not accessed | ||
73 | * from outside the move callback until the move is complete. If | ||
74 | * this is a concern, eg. due to multi-threaded access, then you | ||
75 | * must implement a sync_callback() and guard any access to the | ||
76 | * buffer with a lock. | ||
77 | * | ||
78 | * If the move callback is NULL then buflib will never move | ||
79 | * the allocation, as if you returned BUFLIB_CB_CANNOT_MOVE. | ||
80 | */ | ||
81 | int (*move_callback)(int handle, void* current, void* new); | ||
82 | |||
83 | /** | ||
84 | * \brief Called when buflib wants to shrink the buffer | ||
85 | * \param handle Handle to shrink | ||
86 | * \param hints Hints regarding the shrink request | ||
87 | * \param start Current address of the buffer | ||
88 | * \param size Current size of the buffer as seen by buflib. | ||
89 | * This may be rounded up compared to the nominal | ||
90 | * allocation size due to alignment requirements. | ||
91 | * \return BUFLIB_CB_OK - Was able to shrink the buffer. | ||
92 | * \return BUFLIB_CB_CANNOT_SHRINK - Buffer cannot shrink. | ||
93 | * | ||
94 | * This callback is run by buflib when it runs out of memory | ||
95 | * and starts a compaction run. Buflib will not actually try | ||
96 | * to shrink or move memory, you must do that yourself and | ||
97 | * call buflib_shrink() to report the new start address and | ||
98 | * size of the buffer. | ||
99 | * | ||
100 | * If the shrink callback is NULL then buflib will regard the | ||
101 | * buffer as non-shrinkable. | ||
102 | */ | ||
103 | int (*shrink_callback)(int handle, unsigned hints, | ||
104 | void *start, size_t size); | ||
105 | |||
106 | /** | ||
107 | * \brief Called before and after attempting to move the buffer | ||
108 | * \param handle Handle being moved | ||
109 | * \param lock True to lock, false to unlock | ||
110 | * | ||
111 | * The purpose of this callback is to block access to the buffer | ||
112 | * from other threads while a buffer is being moved, using a lock | ||
113 | * such as a mutex. | ||
114 | * | ||
115 | * It is called with `sync_callback(handle, true)` before running | ||
116 | * the move callback and `sync_callback(handle, false)` after the | ||
117 | * move is complete, regardless of whether the buffer was actually | ||
118 | * moved or not. | ||
119 | */ | ||
120 | void (*sync_callback)(int handle, bool lock); | ||
121 | }; | ||
122 | |||
123 | /** | ||
124 | * A set of all NULL callbacks for use with allocations that need to stay | ||
125 | * locked in RAM and not moved or shrunk. These type of allocations should | ||
126 | * be avoided as much as possible to avoid memory fragmentation but it can | ||
127 | * suitable for short-lived allocations. | ||
128 | * | ||
129 | * \note Use of this is discouraged. Prefer to use normal moveable | ||
130 | * allocations and pin them. | ||
131 | */ | ||
132 | extern struct buflib_callbacks buflib_ops_locked; | ||
133 | |||
134 | /** | ||
135 | * \brief Intialize a buflib context | ||
136 | * \param ctx Context to initialize | ||
137 | * \param buf Buffer which will be used as the context's memory pool | ||
138 | * \param size Size of the buffer | ||
139 | */ | ||
140 | void buflib_init(struct buflib_context *ctx, void *buf, size_t size); | ||
141 | |||
142 | /** | ||
143 | * Returns the amount of unallocated bytes. It does not mean this amount | ||
144 | * can be actually allocated because they might not be contiguous. | ||
145 | */ | ||
146 | size_t buflib_available(struct buflib_context *ctx); | ||
147 | |||
148 | /** | ||
149 | * Returns the size of the largest possible contiguous allocation, given | ||
150 | * the current state of the memory pool. A larger allocation may still | ||
151 | * succeed if compaction is able to create a larger contiguous area. | ||
152 | */ | ||
153 | size_t buflib_allocatable(struct buflib_context *ctx); | ||
154 | |||
155 | /** | ||
156 | * \brief Relocate the buflib memory pool to a new address | ||
157 | * \param ctx Context to relocate | ||
158 | * \param buf New memory pool address | ||
159 | * \return True if relocation should proceed, false if it cannot. | ||
160 | * | ||
161 | * Updates all pointers inside the buflib context to point to a new pool | ||
162 | * address. You must call this function before moving the pool and move | ||
163 | * the data manually afterwards only if this function returns true. | ||
164 | * | ||
165 | * This is intended from a move_callback() in buflib-on-buflib scenarios, | ||
166 | * where the memory pool of the "inner" buflib is allocated from an "outer" | ||
167 | * buflib. | ||
168 | * | ||
169 | * \warning This does not run any move callbacks, so it is not safe to | ||
170 | * use if any allocations require them. | ||
171 | */ | ||
172 | bool buflib_context_relocate(struct buflib_context *ctx, void *buf); | ||
173 | |||
174 | /** | ||
175 | * \brief Allocate memory from buflib | ||
176 | * \param ctx Context to allocate from | ||
177 | * \param size Allocation size | ||
178 | * \return Handle for the allocation (> 0) or a negative value on error | ||
179 | * | ||
180 | * This is the same as calling buflib_alloc_ex() with a NULL callbacks | ||
181 | * struct. The resulting allocation can be moved by buflib; use pinning | ||
182 | * if you need to prevent moves. | ||
183 | * | ||
184 | * Note that zero is not a valid handle, and will never be returned by | ||
185 | * this function. However, this may change, and you should treat a zero | ||
186 | * or negative return value as an allocation failure. | ||
187 | */ | ||
188 | int buflib_alloc(struct buflib_context *ctx, size_t size); | ||
189 | |||
190 | /** | ||
191 | * \brief Allocate memory from buflib with custom buffer ops | ||
192 | * \param ctx Context to allocate from | ||
193 | * \param size Allocation size | ||
194 | * \param ops Pointer to ops struct or NULL if no ops are needed. | ||
195 | * \return Handle for the allocation (> 0) or a negative value on error. | ||
196 | * | ||
197 | * Use this if you need to pass custom callbacks for responding to buflib | ||
198 | * move or shrink operations. Passing a NULL ops pointer means the buffer | ||
199 | * can be moved by buflib at any time. | ||
200 | * | ||
201 | * Note that zero is not a valid handle, and will never be returned by | ||
202 | * this function. However, this may change, and you should treat a zero | ||
203 | * or negative return value as an allocation failure. | ||
204 | */ | ||
205 | int buflib_alloc_ex(struct buflib_context *ctx, size_t size, | ||
206 | struct buflib_callbacks *ops); | ||
207 | |||
208 | /** | ||
209 | * \brief Attempt a maximum size allocation | ||
210 | * \param ctx Context to allocate from | ||
211 | * \param size Size of the allocation will be written here on success. | ||
212 | * \param ops Pointer to ops struct or NULL if no ops are needed. | ||
213 | * \return Handle for the allocation (> 0) or a negative value on error. | ||
214 | * | ||
215 | * Buflib will attempt to compact and shrink other allocations as much as | ||
216 | * possible and then allocate the largest contigous free area. Since this | ||
217 | * will consume effectively *all* available memory, future allocations are | ||
218 | * likely to fail. | ||
219 | * | ||
220 | * \note There is rarely any justification to use this with the core_alloc | ||
221 | * context due to the impact it has on the entire system. You should | ||
222 | * change your code if you think you need this. Of course, if you are | ||
223 | * using a private buflib context then this warning does not apply. | ||
224 | */ | ||
225 | int buflib_alloc_maximum(struct buflib_context *ctx, | ||
226 | size_t *size, struct buflib_callbacks *ops); | ||
227 | |||
228 | /** | ||
229 | * \brief Reduce the size of a buflib allocation | ||
230 | * \param ctx Buflib context of the allocation | ||
231 | * \param handle Handle identifying the allocation | ||
232 | * \param newstart New start address. Must be within the current bounds | ||
233 | * of the allocation, as returned by buflib_get_data(). | ||
234 | * \param new_size New size of the buffer. | ||
235 | * \return True if shrinking was successful; otherwise, returns false and | ||
236 | * does not modify the allocation. | ||
237 | * | ||
238 | * Shrinking always succeeds provided the new allocation is contained | ||
239 | * within the current allocation. A failure is always a programming | ||
240 | * error, so you need not check for it and in the future the failure | ||
241 | * case may be changed to a panic or undefined behavior with no return | ||
242 | * code. | ||
243 | * | ||
244 | * The new start address and size need not have any particular alignment, | ||
245 | * however buflib cannot work with unaligned addresses so there is rarely | ||
246 | * any purpose to creating unaligned allocations. | ||
247 | * | ||
248 | * Shrinking is typically done from a shrink_callback(), but can be done | ||
249 | * at any time if you want to reduce the size of a buflib allocation. | ||
250 | */ | ||
251 | bool buflib_shrink(struct buflib_context *ctx, int handle, | ||
252 | void *newstart, size_t new_size); | ||
253 | |||
254 | /** | ||
255 | * \brief Increment an allocation's pin count | ||
256 | * \param ctx Buflib context of the allocation | ||
257 | * \param handle Handle identifying the allocation | ||
258 | * | ||
259 | * The pin count acts like a reference count. Buflib will not attempt to | ||
260 | * move any buffer with a positive pin count, nor invoke any move or sync | ||
261 | * callbacks. Hence, when pinned, it is safe to hold pointers to a buffer | ||
262 | * across yields or use them for I/O. | ||
263 | * | ||
264 | * Note that shrink callbacks can still be invoked for pinned handles. | ||
265 | */ | ||
266 | void buflib_pin(struct buflib_context *ctx, int handle); | ||
267 | |||
268 | /** | ||
269 | * \brief Decrement an allocation's pin count | ||
270 | * \param ctx Buflib context of the allocation | ||
271 | * \param handle Handle identifying the allocation | ||
272 | */ | ||
273 | void buflib_unpin(struct buflib_context *ctx, int handle); | ||
274 | |||
275 | /** | ||
276 | * \brief Return the pin count of an allocation | ||
277 | * \param ctx Buflib context of the allocation | ||
278 | * \param handle Handle identifying the allocation | ||
279 | * \return Current pin count; zero means the handle is not pinned. | ||
280 | */ | ||
281 | unsigned buflib_pin_count(struct buflib_context *ctx, int handle); | ||
282 | |||
283 | /** | ||
284 | * \brief Free an allocation and return its memory to the pool | ||
285 | * \param ctx Buflib context of the allocation | ||
286 | * \param handle Handle identifying the allocation | ||
287 | * \return Always returns zero (zero is not a valid handle, so this can | ||
288 | * be used to invalidate the variable containing the handle). | ||
289 | */ | ||
290 | int buflib_free(struct buflib_context *context, int handle); | ||
291 | |||
292 | /** | ||
293 | * \brief Get a pointer to the buffer for an allocation | ||
294 | * \param ctx Buflib context of the allocation | ||
295 | * \param handle Handle identifying the allocation | ||
296 | * \return Pointer to the allocation's memory. | ||
297 | * | ||
298 | * Note that buflib can move allocations in order to free up space when | ||
299 | * making new allocations. For this reason, it's unsafe to hold a pointer | ||
300 | * to a buffer across a yield() or any other operation that can cause a | ||
301 | * context switch. This includes any function that may block, and even | ||
302 | * some functions that might not block -- eg. if a low priority thread | ||
303 | * acquires a mutex, calling mutex_unlock() may trigger a context switch | ||
304 | * to a higher-priority thread. | ||
305 | * | ||
306 | * buflib_get_data() is a very cheap operation, however, costing only | ||
307 | * a few pointer lookups. Don't hesitate to use it extensively. | ||
308 | * | ||
309 | * If you need to hold a pointer across a possible context switch, pin | ||
310 | * the handle with buflib_pin() to prevent the buffer from being moved. | ||
311 | * This is required when doing I/O into buflib allocations, for example. | ||
312 | */ | ||
313 | #ifdef BUFLIB_DEBUG_GET_DATA | ||
314 | void *buflib_get_data(struct buflib_context *ctx, int handle); | ||
315 | #else | ||
316 | static inline void *buflib_get_data(struct buflib_context *ctx, int handle); | ||
317 | #endif | ||
318 | |||
319 | /** | ||
320 | * \brief Shift allocations up to free space at the start of the pool | ||
321 | * \param ctx Context to operate on | ||
322 | * \param size Indicates number of bytes to free up, or 0 to free | ||
323 | * up as much as possible. On return, the actual number | ||
324 | * of bytes freed is written here. | ||
325 | * \return Pointer to the start of the free area | ||
326 | * | ||
327 | * If `*size` is non-zero, the actual amount of space freed up might | ||
328 | * be less than `*size`. | ||
329 | * | ||
330 | * \warning This will move data around in the pool without calling any | ||
331 | * move callbacks! | ||
332 | * \warning This function is deprecated and will eventually be removed. | ||
333 | */ | ||
334 | void* buflib_buffer_out(struct buflib_context *ctx, size_t *size); | ||
335 | |||
336 | /** | ||
337 | * \brief Shift allocations down into free space below the pool | ||
338 | * \param ctx Context to operate on | ||
339 | * \param size Number of bytes to add to the pool. | ||
340 | * | ||
341 | * This operation should only be used to return memory that was previously | ||
342 | * taken from the pool with buflib_buffer_out(), by passing the same size | ||
343 | * that you got from that function. | ||
344 | * | ||
345 | * \warning This will move data around in the pool without calling any | ||
346 | * move callbacks! | ||
347 | * \warning This function is deprecated and will eventually be removed. | ||
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 | ||
25 | 380 | ||
26 | #if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL | 381 | #if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL |
27 | #include "buflib_mempool.h" | 382 | #include "buflib_mempool.h" |
28 | #endif | 383 | #endif |
29 | 384 | ||
385 | #ifndef BUFLIB_ALLOC_OVERHEAD | ||
386 | # define BUFLIB_ALLOC_OVERHEAD 0 | ||
387 | #endif | ||
388 | |||
30 | #endif /* _BUFLIB_H_ */ | 389 | #endif /* _BUFLIB_H_ */ |