Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

chunk_alloc

chunk_alloc allows arrays (or any data) to be allocated in smaller chunks

you have to save the indices..
variable data will have variable indices you need to
store these as [chunk_alloc] doesn't keep track
if you have a fixed size for each
alloc you can do indice / sizeof(data)
or index * sizeof(data) to convert

Lots of debug stuff still in and it needs optimization

User provides chunk_size and max_chunks
max_chunks * struct chunk will be allocated at start
with (1) chunk_size allocation initially

alloc_chunk() with size = 0 shrinks the last allocation to the size of the data used

add OOM checks on buflib_alloc -- oops

move bytes available to the header -- less memory per chunk & better alignment
keep track of the current in use chunk index -- should speed things up a bit

Now allows:
realloc chunk header
larger allocations than chunk size

reallocs smaller than existing will shrink the current array
rather than alloc a new and copy data

Comments welcome :)

Change-Id: I8ed170eef73da95da19430a80b32e5debf0c8276

authored by

William Wilgus and committed by
William Wilgus
7faf6be3 a513cee8

+377
+1
firmware/SOURCES
··· 207 207 208 208 /* Common */ 209 209 #ifndef BOOTLOADER 210 + chunk_alloc.c 210 211 common/strptokspn.c 211 212 common/ap_int.c 212 213 #endif
+304
firmware/chunk_alloc.c
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2023 by William Wilgus 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + /* [chunk_alloc] allows arrays (or any data) to be allocated in smaller chunks */ 22 + 23 + #include "chunk_alloc.h" 24 + #include "panic.h" 25 + 26 + //#define LOGF_ENABLE 27 + #include "logf.h" 28 + 29 + #ifndef MAX 30 + #define MAX(a,b) ((a) > (b) ? (a) : (b)) 31 + #endif 32 + #ifndef MIN 33 + #define MIN(a,b) ((a) < (b) ? (a) : (b)) 34 + #endif 35 + 36 + /*Note on offsets returned 37 + * variable data will have variable offsets you need to 38 + * store these as [chunk_alloc] doesn't keep track 39 + * if you have a fixed size for each alloc you can do 40 + * offset / sizeof(data) or index * sizeof(data) to convert 41 + */ 42 + 43 + struct chunk 44 + { 45 + int handle; /* data handle of buflib allocated bytes */ 46 + size_t max_start_offset; /* start of last allocation */ 47 + }; 48 + 49 + #define CHUNK_ARRSZ(n) (sizeof(struct chunk) * n) 50 + 51 + static struct chunk* get_chunk_array(struct buflib_context *ctx, int handle) 52 + { 53 + return (struct chunk*)buflib_get_data(ctx, handle); 54 + } 55 + 56 + /* shrink or grow chunk allocation 57 + * chunks greater than max_chunks will be freed 58 + * new allocs will default to chunk_size 59 + * previous or current chunk < max_chunks will NOT be changed 60 + * Returns true on success false on failure 61 + */ 62 + bool chunk_realloc(struct chunk_alloc_header *hdr, 63 + size_t chunk_size, size_t max_chunks) 64 + { 65 + struct buflib_context *ctx = hdr->context; 66 + struct chunk *new_chunk = NULL; 67 + struct chunk *old_chunk; 68 + size_t min_chunk = 1; 69 + int new_handle = 0; 70 + 71 + if (max_chunks > hdr->count) /* need room for more chunks */ 72 + { 73 + new_handle = buflib_alloc(ctx, CHUNK_ARRSZ(max_chunks)); 74 + 75 + if (new_handle <= 0) 76 + { 77 + logf("%s Error OOM %ld chunks", __func__, max_chunks); 78 + return false; 79 + } 80 + new_chunk = get_chunk_array(ctx, new_handle); 81 + /* ensure all chunks data is zeroed, we depend on it */ 82 + memset(new_chunk, 0, CHUNK_ARRSZ(max_chunks)); 83 + } 84 + if (hdr->chunk_handle > 0) /* handle existing chunk */ 85 + { 86 + logf("%s %ld chunks (%ld bytes) => %ld chunks (%ld bytes)", __func__, 87 + hdr->count, CHUNK_ARRSZ(hdr->count), max_chunks, CHUNK_ARRSZ(max_chunks)); 88 + 89 + buflib_pin(ctx, hdr->chunk_handle); 90 + old_chunk = get_chunk_array(ctx, hdr->chunk_handle); 91 + 92 + if (new_chunk != NULL) /* copy any valid old chunks to new */ 93 + { 94 + min_chunk = MIN(max_chunks, hdr->current + 1); 95 + logf("%s copying %ld chunks", __func__, min_chunk); 96 + memcpy(new_chunk, old_chunk, CHUNK_ARRSZ(min_chunk)); 97 + } 98 + /* free any chunks that no longer fit */ 99 + for (size_t i = max_chunks; i <= hdr->current; i++) 100 + { 101 + logf("%s discarding chunk[%ld]", __func__, i); 102 + buflib_free(ctx, old_chunk[i].handle); 103 + } 104 + buflib_unpin(ctx, hdr->chunk_handle); 105 + 106 + if (max_chunks < hdr->count && max_chunks > 0) 107 + { 108 + logf("%s shrink existing chunk array", __func__); 109 + min_chunk = max_chunks; 110 + buflib_shrink(ctx, hdr->chunk_handle, 111 + old_chunk, CHUNK_ARRSZ(max_chunks)); 112 + 113 + new_handle = hdr->chunk_handle; 114 + } 115 + else 116 + { 117 + logf("%s free existing chunk array", __func__); 118 + buflib_free(ctx, hdr->chunk_handle); /* free old chunk array */ 119 + } 120 + 121 + hdr->current = min_chunk - 1; 122 + } 123 + else 124 + { 125 + logf("chunk_alloc_init %ld chunks (%ld bytes)", 126 + hdr->count, (hdr->count)); 127 + } 128 + 129 + hdr->chunk_handle = new_handle; 130 + hdr->chunk_size = chunk_size; 131 + hdr->count = max_chunks; 132 + 133 + return true; 134 + } 135 + 136 + /* frees all allocations */ 137 + void chunk_alloc_free(struct chunk_alloc_header *hdr) 138 + { 139 + logf("%s freeing %ld chunks", __func__, hdr->count); 140 + chunk_realloc(hdr, 0, 0); 141 + } 142 + 143 + /* initialize chunk allocator 144 + * chunk_size specifies initial size of each chunk 145 + * a single allocation CAN be larger than this 146 + * max_chunks * chunk_size is the total expected size of the buffer 147 + * more data will not be allocated once all chunks used 148 + * Returns true on success or false on failure 149 + */ 150 + bool chunk_alloc_init(struct chunk_alloc_header *hdr, 151 + struct buflib_context *ctx, 152 + size_t chunk_size, size_t max_chunks) 153 + { 154 + /* initialize header */ 155 + hdr->chunk_handle = 0; 156 + hdr->chunk_bytes_total = 0; 157 + hdr->chunk_bytes_free = 0; 158 + hdr->current = 0; 159 + hdr->context = ctx; 160 + hdr->count = 0; 161 + 162 + return chunk_realloc(hdr, chunk_size, max_chunks); 163 + } 164 + 165 + /* shrink current chunk to size used */ 166 + static void finalize(struct chunk_alloc_header *hdr, struct chunk *chunk_array) 167 + { 168 + /*Note calling functions check if chunk_bytes_free > 0*/ 169 + size_t idx = hdr->current; 170 + if (idx >= hdr->count) 171 + return; 172 + int handle = chunk_array[idx].handle; 173 + struct buflib_context *ctx = hdr->context; 174 + 175 + void* chunk_start = buflib_get_data(ctx, handle); 176 + 177 + hdr->chunk_bytes_total -= hdr->chunk_bytes_free; 178 + hdr->chunk_bytes_free = 0; 179 + 180 + buflib_shrink(ctx, handle, chunk_start, hdr->chunk_bytes_total); 181 + 182 + logf("%s shrink hdr idx[%ld] offset[%ld]: new size: %ld", 183 + __func__, idx, chunk_array[idx].max_start_offset, hdr->chunk_bytes_total); 184 + } 185 + 186 + /* shrink current chunk to size used */ 187 + void chunk_alloc_finalize(struct chunk_alloc_header *hdr) 188 + { 189 + if (hdr->chunk_bytes_free > 0) 190 + { 191 + struct chunk *chunk = get_chunk_array(hdr->context, hdr->chunk_handle); 192 + finalize(hdr, chunk); 193 + } 194 + } 195 + 196 + /* allocates from current chunk if size > bytes remaining 197 + * current chunk shrinks to size used and a new chunk is allocated 198 + * returns virtual offset on success or CHUNK_ALLOC_INVALID on error 199 + */ 200 + size_t chunk_alloc(struct chunk_alloc_header *hdr, size_t size) 201 + { 202 + size_t idx = hdr->current; 203 + int handle = hdr->chunk_handle; 204 + struct buflib_context *ctx = hdr->context; 205 + 206 + buflib_pin(ctx, handle); 207 + struct chunk *chunk = get_chunk_array(ctx, handle); 208 + 209 + while (size > 0) 210 + { 211 + if (idx >= hdr->count) 212 + { 213 + logf("%s Error OOM -- out of chunks", __func__); 214 + break; 215 + } 216 + hdr->current = idx; 217 + 218 + if(chunk[idx].handle <= 0) /* need to make an new allocation */ 219 + { 220 + size_t new_alloc_size = MAX(size, hdr->chunk_size); 221 + 222 + chunk[idx].handle = buflib_alloc(ctx, new_alloc_size); 223 + 224 + if (chunk[idx].handle <= 0) 225 + { 226 + logf("%s Error OOM", __func__); 227 + goto fail; /* OOM */ 228 + } 229 + 230 + hdr->chunk_bytes_total = new_alloc_size; 231 + hdr->chunk_bytes_free = new_alloc_size; 232 + 233 + chunk[idx].max_start_offset = 234 + (idx > 0 ? (chunk[idx - 1].max_start_offset) : 0); 235 + 236 + logf("%s New alloc hdr idx[%ld] offset[%ld]: available: %ld", 237 + __func__, idx, chunk[idx].max_start_offset, new_alloc_size); 238 + } 239 + 240 + if(size <= hdr->chunk_bytes_free) /* request will fit */ 241 + { 242 + size_t offset = chunk[idx].max_start_offset; 243 + chunk[idx].max_start_offset += size; 244 + hdr->chunk_bytes_free -= size; 245 + /*logf("%s hdr idx[%ld] offset[%ld] size: %ld", 246 + __func__, idx, offset, size);*/ 247 + 248 + buflib_unpin(ctx, handle); 249 + return offset; 250 + } 251 + else if (hdr->chunk_bytes_free > 0) /* shrink the current chunk */ 252 + { 253 + finalize(hdr, chunk); 254 + } 255 + idx++; 256 + } 257 + fail: 258 + buflib_unpin(ctx, handle); 259 + return CHUNK_ALLOC_INVALID; 260 + } 261 + 262 + /* returns chunk idx given virtual offset */ 263 + static size_t chunk_find_data_idx(struct chunk_alloc_header *hdr, 264 + size_t offset, struct chunk **chunk) 265 + { 266 + size_t idx; 267 + *chunk = get_chunk_array(hdr->context, hdr->chunk_handle); 268 + /*logf("%s search for offset[%ld]", __func__, offset);*/ 269 + for (idx = hdr->current; idx < hdr->count; idx--) 270 + { 271 + if (offset < (*chunk)[idx].max_start_offset 272 + && (idx == 0 || offset >= (*chunk)[idx - 1].max_start_offset)) 273 + { 274 + /*logf("%s found hdr idx[%ld] max offset[%ld]", 275 + __func__, idx, (*chunk)[idx].max_start_offset);*/ 276 + return idx; 277 + } 278 + } 279 + panicf("%s Error offset %d does not exist", __func__, (unsigned int)offset); 280 + return CHUNK_ALLOC_INVALID; 281 + } 282 + 283 + /* get data - buffer chunk can't be moved while pinned 284 + * multiple calls will up pin count so put should be called for each 285 + * Returns data at offset 286 + */ 287 + void* chunk_get_data(struct chunk_alloc_header *hdr, size_t offset) 288 + { 289 + struct chunk *chunk; 290 + size_t idx = chunk_find_data_idx(hdr, offset, &chunk); 291 + if (idx > 0) 292 + offset -= chunk[idx - 1].max_start_offset; 293 + logf("%s adjusted offset: %ld", __func__, offset); 294 + buflib_pin(hdr->context, chunk[idx].handle); 295 + return buflib_get_data(hdr->context, chunk[idx].handle) + offset; 296 + } 297 + 298 + /* release a pinned buffer, chunk can't be moved till pin count == 0 */ 299 + void chunk_put_data(struct chunk_alloc_header *hdr, size_t offset) 300 + { 301 + struct chunk *chunk; 302 + size_t idx = chunk_find_data_idx(hdr, offset, &chunk); 303 + buflib_unpin(hdr->context, chunk[idx].handle); 304 + }
+64
firmware/include/chunk_alloc.h
··· 1 + /*************************************************************************** 2 + * __________ __ ___. 3 + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 + * \/ \/ \/ \/ \/ 8 + * $Id$ 9 + * 10 + * Copyright (C) 2023 William Wilgus 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 2 15 + * of the License, or (at your option) any later version. 16 + * 17 + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 + * KIND, either express or implied. 19 + * 20 + ****************************************************************************/ 21 + 22 + #ifndef _CHUNKALLOC_H_ 23 + #define _CHUNKALLOC_H_ 24 + #include <stdbool.h> 25 + #include <string.h> 26 + #include "config.h" 27 + #include "buflib.h" 28 + 29 + #define CHUNK_ALLOC_INVALID ((size_t)-1) 30 + 31 + struct chunk_alloc_header 32 + { 33 + size_t chunk_bytes_total; /* total bytes in current chunk */ 34 + size_t chunk_bytes_free; /* free bytes in current chunk */ 35 + size_t chunk_size; /* default chunk size */ 36 + size_t count; /* total chunks possible */ 37 + size_t current; /* current chunk in use */ 38 + 39 + struct buflib_context *context; /* buflib context for all allocations */ 40 + int chunk_handle; /* data handle of buflib allocated array of struct chunk */ 41 + }; 42 + 43 + void chunk_alloc_free(struct chunk_alloc_header *hdr); 44 + 45 + bool chunk_alloc_init(struct chunk_alloc_header *hdr, 46 + struct buflib_context *ctx, 47 + size_t chunk_size, size_t max_chunks); 48 + 49 + bool chunk_realloc(struct chunk_alloc_header *hdr, 50 + size_t chunk_size, size_t max_chunks); 51 + 52 + void chunk_alloc_finalize(struct chunk_alloc_header *hdr); 53 + 54 + size_t chunk_alloc(struct chunk_alloc_header *hdr, size_t size); /* Returns offset */ 55 + 56 + void* chunk_get_data(struct chunk_alloc_header *hdr, size_t offset); /* Returns data */ 57 + 58 + void chunk_put_data(struct chunk_alloc_header *hdr, size_t offset); 59 + 60 + static inline bool chunk_alloc_is_initialized(struct chunk_alloc_header *hdr) 61 + { 62 + return (hdr->chunk_handle > 0); 63 + } 64 + #endif
+8
firmware/include/core_alloc.h
··· 5 5 #include <stdbool.h> 6 6 #include "config.h" 7 7 #include "buflib.h" 8 + #include "chunk_alloc.h" 8 9 9 10 /* All functions below are wrappers for functions in buflib.h, except 10 11 * they have a predefined context ··· 43 44 return buflib_get_data(&core_ctx, handle); 44 45 } 45 46 47 + /* core context chunk_alloc */ 48 + static inline bool core_chunk_alloc_init(struct chunk_alloc_header *hdr, 49 + size_t chunk_size, size_t max_chunks) 50 + { 51 + extern struct buflib_context core_ctx; 52 + return chunk_alloc_init(hdr, &core_ctx, chunk_size, max_chunks); 53 + } 46 54 #endif /* __CORE_ALLOC_H__ */