A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2023 Aidan MacDonald
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/*
23 * Malloc backed buflib. This is intended for debugging rather than for
24 * serious use - the buffer passed to the context is wasted, and memory
25 * is acquired from malloc() instead. The main point is to make ASAN more
26 * effective by isolating buflib allocations from each other.
27 *
28 * Currently this is a bare-minimum implementation, it doesn't even run
29 * buflib callbacks since it never moves anything. It could later be
30 * extended with stress-testing options, for example by randomly moving
31 * allocations around.
32 */
33
34#include "buflib.h"
35#include "panic.h"
36#include <stdlib.h>
37
38static struct buflib_malloc_handle *get_free_handle(struct buflib_context *ctx)
39{
40 struct buflib_malloc_handle *h;
41 for (size_t i = 0; i < ctx->num_allocs; ++i)
42 {
43 h = &ctx->allocs[i];
44 if (h->size == 0)
45 return h;
46 }
47
48 ctx->num_allocs++;
49 ctx->allocs = realloc(ctx->allocs, ctx->num_allocs * sizeof(*ctx->allocs));
50 if (!ctx->allocs)
51 panicf("buflib %p handle OOM", ctx);
52
53 h = &ctx->allocs[ctx->num_allocs - 1];
54 h->size = 0;
55 return h;
56}
57
58static int get_handle_num(struct buflib_context *ctx,
59 struct buflib_malloc_handle *handle)
60{
61 return (handle - ctx->allocs) + 1;
62}
63
64static struct buflib_malloc_handle *get_handle(struct buflib_context *ctx,
65 int handle)
66{
67 return &ctx->allocs[handle - 1];
68}
69
70struct buflib_callbacks buflib_ops_locked = {
71 .move_callback = NULL,
72 .shrink_callback = NULL,
73 .sync_callback = NULL,
74};
75
76void buflib_init(struct buflib_context *ctx, void *buf, size_t size)
77{
78 ctx->allocs = NULL;
79 ctx->num_allocs = 0;
80 ctx->buf = buf;
81 ctx->bufsize = size;
82}
83
84size_t buflib_available(struct buflib_context *ctx)
85{
86 return ctx->bufsize;
87}
88
89size_t buflib_allocatable(struct buflib_context *ctx)
90{
91 return ctx->bufsize;
92}
93
94bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
95{
96 ctx->buf = buf;
97 return true;
98}
99
100int buflib_alloc(struct buflib_context *ctx, size_t size)
101{
102 return buflib_alloc_ex(ctx, size, NULL);
103}
104
105int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
106 struct buflib_callbacks *ops)
107{
108 struct buflib_malloc_handle *handle = get_free_handle(ctx);
109
110 handle->data = malloc(size);
111 handle->user = handle->data;
112 handle->size = size;
113 handle->pin_count = 0;
114 handle->ops = ops;
115
116 if (!handle->data)
117 panicf("buflib %p data OOM", ctx);
118
119 return get_handle_num(ctx, handle);
120}
121
122int buflib_alloc_maximum(struct buflib_context* ctx,
123 size_t *size, struct buflib_callbacks *ops)
124{
125 *size = ctx->bufsize;
126
127 return buflib_alloc_ex(ctx, *size, ops);
128}
129
130bool buflib_shrink(struct buflib_context *ctx, int handle,
131 void *newstart, size_t new_size)
132{
133 struct buflib_malloc_handle *h = get_handle(ctx, handle);
134 if (newstart < h->user || new_size > h->size - (newstart - h->user))
135 return false;
136
137 /* XXX: this might be allowed, but what would be the point... */
138 if (new_size == 0)
139 {
140 panicf("weird shrink");
141 return false;
142 }
143
144 /* due to buflib semantics we must not realloc */
145 h->user = newstart;
146 h->size = new_size;
147 return true;
148}
149
150void buflib_pin(struct buflib_context *ctx, int handle)
151{
152 struct buflib_malloc_handle *h = get_handle(ctx, handle);
153
154 h->pin_count++;
155}
156
157void buflib_unpin(struct buflib_context *ctx, int handle)
158{
159 struct buflib_malloc_handle *h = get_handle(ctx, handle);
160
161 h->pin_count--;
162}
163
164unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
165{
166 struct buflib_malloc_handle *h = get_handle(ctx, handle);
167
168 return h->pin_count;
169}
170
171void _buflib_malloc_put_data_pinned(struct buflib_context *ctx, void *data)
172{
173 for (size_t i = 0; i < ctx->num_allocs; ++i)
174 {
175 if (ctx->allocs[i].user == data)
176 {
177 ctx->allocs[i].pin_count--;
178 break;
179 }
180 }
181}
182
183int buflib_free(struct buflib_context *ctx, int handle)
184{
185 if (handle <= 0)
186 return 0;
187
188 struct buflib_malloc_handle *h = get_handle(ctx, handle);
189
190 free(h->data);
191 h->size = 0;
192
193 return 0;
194}
195
196#ifdef BUFLIB_DEBUG_GET_DATA
197void *buflib_get_data(struct buflib_context *ctx, int handle)
198{
199 /* kind of silly since it's better for ASAN to catch this but... */
200 if (handle <= 0 || handle > ctx->num_allocs)
201 panicf("buflib %p: invalid handle %d", ctx, handle);
202
203 struct buflib_malloc_handle *h = get_handle(ctx, handle);
204 if (h->user == NULL)
205 panicf("buflib %p: handle %d use after free", ctx, handle);
206
207 return h->user;
208}
209#endif
210
211void *buflib_buffer_out(struct buflib_context *ctx, size_t *size)
212{
213 if (*size == 0)
214 *size = ctx->bufsize;
215
216 void *ret = ctx->buf;
217
218 ctx->buf += *size;
219 return ret;
220}
221
222void buflib_buffer_in(struct buflib_context *ctx, int size)
223{
224 ctx->buf -= size;
225}
226
227#ifdef BUFLIB_DEBUG_PRINT
228int buflib_get_num_blocks(struct buflib_context *ctx)
229{
230 return ctx->num_allocs;
231}
232
233bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
234 char *buf, size_t bufsize)
235{
236 if (block_num >= ctx->num_allocs)
237 {
238 if (bufsize > 0)
239 *buf = '\0';
240 return false;
241 }
242
243 struct buflib_malloc_handle *handle = &ctx->allocs[block_num];
244 if (handle->data)
245 {
246 snprintf(buf, bufsize, "%03d addr:%8p length:%zu",
247 block_num, handle->data, handle->size);
248 }
249 else
250 {
251 snprintf(buf, bufsize, "%03d (unallocated)", block_num);
252 }
253
254 return true;
255}
256#endif
257
258#ifdef BUFLIB_DEBUG_CHECK_VALID
259void buflib_check_valid(struct buflib_context *ctx)
260{
261 (void)ctx;
262}
263#endif