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 
20 #include "xmmspriv/xmms_ringbuf.h"
21 #include <string.h>
22 
23 /** @defgroup Ringbuffer Ringbuffer
24  * @ingroup XMMSServer
25  * @brief Ringbuffer primitive.
26  * @{
27  */
28 
29 /**
30  * A ringbuffer
31  */
32 struct 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 
48 typedef 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  */
59 guint
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  */
74 xmms_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  */
103 void
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  */
120 void
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  */
141 guint
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  */
153 guint
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 
165 static guint
166 read_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  */
221 guint
222 xmms_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  */
248 guint
249 xmms_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  */
264 guint
265 xmms_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  */
294 guint
295 xmms_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  */
320 guint
321 xmms_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 
352 guint
353 xmms_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  */
379 void
380 xmms_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 
396 void
397 xmms_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 
415 gboolean
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  */
426 void
427 xmms_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  */
444 void
445 xmms_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  */
461 void
462 xmms_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 
struct xmms_ringbuf_St xmms_ringbuf_t
Definition: xmms_ringbuf.h:25
gboolean xmms_ringbuf_iseos(const xmms_ringbuf_t *ringbuf)
Tell if the ringbuffer is EOS.
Definition: ringbuf.c:416
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_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_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_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
guint xmms_ringbuf_write(xmms_ringbuf_t *ringbuf, gconstpointer data, guint len)
Write data to the ringbuffer.
Definition: ringbuf.c:321
guint xmms_ringbuf_bytes_free(const xmms_ringbuf_t *ringbuf)
Number of bytes free in the ringbuffer.
Definition: ringbuf.c:142
guint xmms_ringbuf_bytes_used(const xmms_ringbuf_t *ringbuf)
Number of bytes used in the buffer.
Definition: ringbuf.c:154
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
guint xmms_ringbuf_read(xmms_ringbuf_t *ringbuf, gpointer data, guint len)
Reads data from the ringbuffer.
Definition: ringbuf.c:222
xmms_ringbuf_t * xmms_ringbuf_new(guint size)
Allocate a new ringbuffer.
Definition: ringbuf.c:74
void xmms_ringbuf_wait_eos(const xmms_ringbuf_t *ringbuf, GMutex *mtx)
Block until we are EOSed.
Definition: ringbuf.c:445
struct xmms_ringbuf_hotspot_St xmms_ringbuf_hotspot_t
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
void xmms_ringbuf_clear(xmms_ringbuf_t *ringbuf)
Clear the ringbuffers data.
Definition: ringbuf.c:121
#define MIN(a, b)
Definition: xmmsc_util.h:36
void xmms_ringbuf_set_eos(xmms_ringbuf_t *ringbuf, gboolean eos)
Set EOS flag on ringbuffer.
Definition: ringbuf.c:427
void xmms_ringbuf_destroy(xmms_ringbuf_t *ringbuf)
Free all memory used by the ringbuffer.
Definition: ringbuf.c:104
void xmms_ringbuf_hotspot_set(xmms_ringbuf_t *ringbuf, gboolean(*cb)(void *), void(*destroy)(void *), void *arg)
Definition: ringbuf.c:462