XMMS2
ringbuf.c
Go to the documentation of this file.
1/* XMMS2 - X Music Multiplexer System
2 * Copyright (C) 2003-2011 XMMS2 Team
3 *
4 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 */
16
17
18
19
21#include <string.h>
22
23/** @defgroup Ringbuffer Ringbuffer
24 * @ingroup XMMSServer
25 * @brief Ringbuffer primitive.
26 * @{
27 */
28
29/**
30 * A ringbuffer
31 */
32struct xmms_ringbuf_St {
33 /** The actual bufferdata */
34 guint8 *buffer;
35 /** Number of bytes in #buffer */
36 guint buffer_size;
37 /** Actually usable number of bytes */
38 guint buffer_size_usable;
39 /** Read and write index */
40 guint rd_index, wr_index;
41 gboolean eos;
42
43 GQueue *hotspots;
44
45 GCond *free_cond, *used_cond, *eos_cond;
46};
47
48typedef struct xmms_ringbuf_hotspot_St {
49 guint pos;
50 gboolean (*callback) (void *);
51 void (*destroy) (void *);
52 void *arg;
54
55
56/**
57 * The usable size of the ringbuffer.
58 */
59guint
61{
62 g_return_val_if_fail (ringbuf, 0);
63
64 return ringbuf->buffer_size_usable;
65}
66
67/**
68 * Allocate a new ringbuffer
69 *
70 * @param size The total size of the new ringbuffer
71 * @returns a new #xmms_ringbuf_t
72 */
74xmms_ringbuf_new (guint size)
75{
76 xmms_ringbuf_t *ringbuf = g_new0 (xmms_ringbuf_t, 1);
77
78 g_return_val_if_fail (size > 0, NULL);
79 g_return_val_if_fail (size < G_MAXUINT, NULL);
80
81 /* we need to allocate one byte more than requested, cause the
82 * final byte cannot be used.
83 * if we used it, it might lead to the situation where
84 * read_index == write_index, which is used for the "empty"
85 * condition.
86 */
87 ringbuf->buffer_size_usable = size;
88 ringbuf->buffer_size = size + 1;
89 ringbuf->buffer = g_malloc (ringbuf->buffer_size);
90
91 ringbuf->free_cond = g_cond_new ();
92 ringbuf->used_cond = g_cond_new ();
93 ringbuf->eos_cond = g_cond_new ();
94
95 ringbuf->hotspots = g_queue_new ();
96
97 return ringbuf;
98}
99
100/**
101 * Free all memory used by the ringbuffer
102 */
103void
105{
106 g_return_if_fail (ringbuf);
107
108 g_cond_free (ringbuf->eos_cond);
109 g_cond_free (ringbuf->used_cond);
110 g_cond_free (ringbuf->free_cond);
111
112 g_queue_free (ringbuf->hotspots);
113 g_free (ringbuf->buffer);
114 g_free (ringbuf);
115}
116
117/**
118 * Clear the ringbuffers data
119 */
120void
122{
123 g_return_if_fail (ringbuf);
124
125 ringbuf->rd_index = 0;
126 ringbuf->wr_index = 0;
127
128 while (!g_queue_is_empty (ringbuf->hotspots)) {
130 hs = g_queue_pop_head (ringbuf->hotspots);
131 if (hs->destroy)
132 hs->destroy (hs->arg);
133 g_free (hs);
134 }
135 g_cond_signal (ringbuf->free_cond);
136}
137
138/**
139 * Number of bytes free in the ringbuffer
140 */
141guint
143{
144 g_return_val_if_fail (ringbuf, 0);
145
146 return ringbuf->buffer_size_usable -
147 xmms_ringbuf_bytes_used (ringbuf);
148}
149
150/**
151 * Number of bytes used in the buffer
152 */
153guint
155{
156 g_return_val_if_fail (ringbuf, 0);
157
158 if (ringbuf->wr_index >= ringbuf->rd_index) {
159 return ringbuf->wr_index - ringbuf->rd_index;
160 }
161
162 return ringbuf->buffer_size - (ringbuf->rd_index - ringbuf->wr_index);
163}
164
165static guint
166read_bytes (xmms_ringbuf_t *ringbuf, guint8 *data, guint len)
167{
168 guint to_read, r = 0, cnt, tmp;
169 gboolean ok;
170
171 to_read = MIN (len, xmms_ringbuf_bytes_used (ringbuf));
172
173 while (!g_queue_is_empty (ringbuf->hotspots)) {
174 xmms_ringbuf_hotspot_t *hs = g_queue_peek_head (ringbuf->hotspots);
175 if (hs->pos != ringbuf->rd_index) {
176 /* make sure we don't cross a hotspot */
177 to_read = MIN (to_read,
178 (hs->pos - ringbuf->rd_index + ringbuf->buffer_size)
179 % ringbuf->buffer_size);
180 break;
181 }
182
183 (void) g_queue_pop_head (ringbuf->hotspots);
184 ok = hs->callback (hs->arg);
185 if (hs->destroy)
186 hs->destroy (hs->arg);
187 g_free (hs);
188
189 if (!ok) {
190 return 0;
191 }
192
193 /* we loop here, to see if there are multiple
194 hotspots in same position */
195 }
196
197 tmp = ringbuf->rd_index;
198
199 while (to_read > 0) {
200 cnt = MIN (to_read, ringbuf->buffer_size - tmp);
201 memcpy (data, ringbuf->buffer + tmp, cnt);
202 tmp = (tmp + cnt) % ringbuf->buffer_size;
203 to_read -= cnt;
204 r += cnt;
205 data += cnt;
206 }
207
208 return r;
209}
210
211/**
212 * Reads data from the ringbuffer. This is a non-blocking call and can
213 * return less data than you wanted. Use #xmms_ringbuf_wait_used to
214 * ensure that you get as much data as you want.
215 *
216 * @param ringbuf Buffer to read from
217 * @param data Allocated buffer where the readed data will end up
218 * @param len number of bytes to read
219 * @returns number of bytes that acutally was read.
220 */
221guint
222xmms_ringbuf_read (xmms_ringbuf_t *ringbuf, gpointer data, guint len)
223{
224 guint r;
225
226 g_return_val_if_fail (ringbuf, 0);
227 g_return_val_if_fail (data, 0);
228 g_return_val_if_fail (len > 0, 0);
229
230 r = read_bytes (ringbuf, (guint8 *) data, len);
231
232 ringbuf->rd_index += r;
233 ringbuf->rd_index %= ringbuf->buffer_size;
234
235 if (r) {
236 g_cond_broadcast (ringbuf->free_cond);
237 }
238
239 return r;
240}
241
242/**
243 * Same as #xmms_ringbuf_read but does not advance in the buffer after
244 * the data has been read.
245 *
246 * @sa xmms_ringbuf_read
247 */
248guint
249xmms_ringbuf_peek (xmms_ringbuf_t *ringbuf, gpointer data, guint len)
250{
251 g_return_val_if_fail (ringbuf, 0);
252 g_return_val_if_fail (data, 0);
253 g_return_val_if_fail (len > 0, 0);
254 g_return_val_if_fail (len <= ringbuf->buffer_size_usable, 0);
255
256 return read_bytes (ringbuf, (guint8 *) data, len);
257}
258
259/**
260 * Same as #xmms_ringbuf_read but blocks until you have all the data you want.
261 *
262 * @sa xmms_ringbuf_read
263 */
264guint
265xmms_ringbuf_read_wait (xmms_ringbuf_t *ringbuf, gpointer data,
266 guint len, GMutex *mtx)
267{
268 guint r = 0, res;
269 guint8 *dest = data;
270
271 g_return_val_if_fail (ringbuf, 0);
272 g_return_val_if_fail (data, 0);
273 g_return_val_if_fail (len > 0, 0);
274 g_return_val_if_fail (mtx, 0);
275
276 while (r < len) {
277 res = xmms_ringbuf_read (ringbuf, dest + r, len - r);
278 r += res;
279 if (r == len || ringbuf->eos) {
280 break;
281 }
282 if (!res)
283 g_cond_wait (ringbuf->used_cond, mtx);
284 }
285
286 return r;
287}
288
289/**
290 * Same as #xmms_ringbuf_peek but blocks until you have all the data you want.
291 *
292 * @sa xmms_ringbuf_peek
293 */
294guint
295xmms_ringbuf_peek_wait (xmms_ringbuf_t *ringbuf, gpointer data,
296 guint len, GMutex *mtx)
297{
298 g_return_val_if_fail (ringbuf, 0);
299 g_return_val_if_fail (data, 0);
300 g_return_val_if_fail (len > 0, 0);
301 g_return_val_if_fail (len <= ringbuf->buffer_size_usable, 0);
302 g_return_val_if_fail (mtx, 0);
303
304 xmms_ringbuf_wait_used (ringbuf, len, mtx);
305
306 return xmms_ringbuf_peek (ringbuf, data, len);
307}
308
309/**
310 * Write data to the ringbuffer. If not all data can be written
311 * to the buffer the function will not block.
312 *
313 * @sa xmms_ringbuf_write_wait
314 *
315 * @param ringbuf Ringbuffer to put data in.
316 * @param data Data to put in ringbuffer
317 * @param len Length of data
318 * @returns Number of bytes that was written
319 */
320guint
321xmms_ringbuf_write (xmms_ringbuf_t *ringbuf, gconstpointer data,
322 guint len)
323{
324 guint to_write, w = 0, cnt;
325 const guint8 *src = data;
326
327 g_return_val_if_fail (ringbuf, 0);
328 g_return_val_if_fail (data, 0);
329 g_return_val_if_fail (len > 0, 0);
330
331 to_write = MIN (len, xmms_ringbuf_bytes_free (ringbuf));
332
333 while (to_write > 0) {
334 cnt = MIN (to_write, ringbuf->buffer_size - ringbuf->wr_index);
335 memcpy (ringbuf->buffer + ringbuf->wr_index, src + w, cnt);
336 ringbuf->wr_index = (ringbuf->wr_index + cnt) % ringbuf->buffer_size;
337 to_write -= cnt;
338 w += cnt;
339 }
340
341 if (w) {
342 g_cond_broadcast (ringbuf->used_cond);
343 }
344
345 return w;
346}
347
348/**
349 * Same as #xmms_ringbuf_write but blocks until there is enough free space.
350 */
351
352guint
353xmms_ringbuf_write_wait (xmms_ringbuf_t *ringbuf, gconstpointer data,
354 guint len, GMutex *mtx)
355{
356 guint w = 0;
357 const guint8 *src = data;
358
359 g_return_val_if_fail (ringbuf, 0);
360 g_return_val_if_fail (data, 0);
361 g_return_val_if_fail (len > 0, 0);
362 g_return_val_if_fail (mtx, 0);
363
364 while (w < len) {
365 w += xmms_ringbuf_write (ringbuf, src + w, len - w);
366 if (w == len || ringbuf->eos) {
367 break;
368 }
369
370 g_cond_wait (ringbuf->free_cond, mtx);
371 }
372
373 return w;
374}
375
376/**
377 * Block until we have free space in the ringbuffer.
378 */
379void
380xmms_ringbuf_wait_free (const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
381{
382 g_return_if_fail (ringbuf);
383 g_return_if_fail (len > 0);
384 g_return_if_fail (len <= ringbuf->buffer_size_usable);
385 g_return_if_fail (mtx);
386
387 while ((xmms_ringbuf_bytes_free (ringbuf) < len) && !ringbuf->eos) {
388 g_cond_wait (ringbuf->free_cond, mtx);
389 }
390}
391
392/**
393 * Block until we have used space in the buffer
394 */
395
396void
397xmms_ringbuf_wait_used (const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
398{
399 g_return_if_fail (ringbuf);
400 g_return_if_fail (len > 0);
401 g_return_if_fail (len <= ringbuf->buffer_size_usable);
402 g_return_if_fail (mtx);
403
404 while ((xmms_ringbuf_bytes_used (ringbuf) < len) && !ringbuf->eos) {
405 g_cond_wait (ringbuf->used_cond, mtx);
406 }
407}
408
409/**
410 * Tell if the ringbuffer is EOS
411 *
412 * @returns TRUE if the ringbuffer is EOSed.
413 */
414
415gboolean
417{
418 g_return_val_if_fail (ringbuf, TRUE);
419
420 return !xmms_ringbuf_bytes_used (ringbuf) && ringbuf->eos;
421}
422
423/**
424 * Set EOS flag on ringbuffer.
425 */
426void
427xmms_ringbuf_set_eos (xmms_ringbuf_t *ringbuf, gboolean eos)
428{
429 g_return_if_fail (ringbuf);
430
431 ringbuf->eos = eos;
432
433 if (eos) {
434 g_cond_broadcast (ringbuf->eos_cond);
435 g_cond_broadcast (ringbuf->used_cond);
436 g_cond_broadcast (ringbuf->free_cond);
437 }
438}
439
440
441/**
442 * Block until we are EOSed
443 */
444void
445xmms_ringbuf_wait_eos (const xmms_ringbuf_t *ringbuf, GMutex *mtx)
446{
447 g_return_if_fail (ringbuf);
448 g_return_if_fail (mtx);
449
450 while (!xmms_ringbuf_iseos (ringbuf)) {
451 g_cond_wait (ringbuf->eos_cond, mtx);
452 }
453
454}
455/** @} */
456
457/**
458 * @internal
459 * Unused
460 */
461void
462xmms_ringbuf_hotspot_set (xmms_ringbuf_t *ringbuf, gboolean (*cb) (void *), void (*destroy) (void *), void *arg)
463{
465 g_return_if_fail (ringbuf);
466
467 hs = g_new0 (xmms_ringbuf_hotspot_t, 1);
468 hs->pos = ringbuf->wr_index;
469 hs->callback = cb;
470 hs->destroy = destroy;
471 hs->arg = arg;
472
473 g_queue_push_tail (ringbuf->hotspots, hs);
474}
475
476
guint xmms_ringbuf_write_wait(xmms_ringbuf_t *ringbuf, gconstpointer data, guint len, GMutex *mtx)
Same as xmms_ringbuf_write but blocks until there is enough free space.
Definition: ringbuf.c:353
guint xmms_ringbuf_read(xmms_ringbuf_t *ringbuf, gpointer data, guint len)
Reads data from the ringbuffer.
Definition: ringbuf.c:222
void xmms_ringbuf_set_eos(xmms_ringbuf_t *ringbuf, gboolean eos)
Set EOS flag on ringbuffer.
Definition: ringbuf.c:427
void xmms_ringbuf_clear(xmms_ringbuf_t *ringbuf)
Clear the ringbuffers data.
Definition: ringbuf.c:121
void xmms_ringbuf_wait_eos(const xmms_ringbuf_t *ringbuf, GMutex *mtx)
Block until we are EOSed.
Definition: ringbuf.c:445
guint xmms_ringbuf_bytes_free(const xmms_ringbuf_t *ringbuf)
Number of bytes free in the ringbuffer.
Definition: ringbuf.c:142
guint xmms_ringbuf_peek_wait(xmms_ringbuf_t *ringbuf, gpointer data, guint len, GMutex *mtx)
Same as xmms_ringbuf_peek but blocks until you have all the data you want.
Definition: ringbuf.c:295
gboolean xmms_ringbuf_iseos(const xmms_ringbuf_t *ringbuf)
Tell if the ringbuffer is EOS.
Definition: ringbuf.c:416
void xmms_ringbuf_destroy(xmms_ringbuf_t *ringbuf)
Free all memory used by the ringbuffer.
Definition: ringbuf.c:104
void xmms_ringbuf_wait_free(const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
Block until we have free space in the ringbuffer.
Definition: ringbuf.c:380
guint xmms_ringbuf_size(xmms_ringbuf_t *ringbuf)
The usable size of the ringbuffer.
Definition: ringbuf.c:60
guint xmms_ringbuf_peek(xmms_ringbuf_t *ringbuf, gpointer data, guint len)
Same as xmms_ringbuf_read but does not advance in the buffer after the data has been read.
Definition: ringbuf.c:249
void xmms_ringbuf_wait_used(const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
Block until we have used space in the buffer.
Definition: ringbuf.c:397
xmms_ringbuf_t * xmms_ringbuf_new(guint size)
Allocate a new ringbuffer.
Definition: ringbuf.c:74
guint xmms_ringbuf_bytes_used(const xmms_ringbuf_t *ringbuf)
Number of bytes used in the buffer.
Definition: ringbuf.c:154
guint xmms_ringbuf_read_wait(xmms_ringbuf_t *ringbuf, gpointer data, guint len, GMutex *mtx)
Same as xmms_ringbuf_read but blocks until you have all the data you want.
Definition: ringbuf.c:265
guint xmms_ringbuf_write(xmms_ringbuf_t *ringbuf, gconstpointer data, guint len)
Write data to the ringbuffer.
Definition: ringbuf.c:321
struct xmms_ringbuf_hotspot_St xmms_ringbuf_hotspot_t
void xmms_ringbuf_hotspot_set(xmms_ringbuf_t *ringbuf, gboolean(*cb)(void *), void(*destroy)(void *), void *arg)
Definition: ringbuf.c:462
struct xmms_ringbuf_St xmms_ringbuf_t
Definition: xmms_ringbuf.h:25
#define MIN(a, b)
Definition: xmmsc_util.h:36