XMMS2

src/xmms/ringbuf.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 
00018 
00019 
00020 #include "xmmspriv/xmms_ringbuf.h"
00021 #include <string.h>
00022 
00023 /** @defgroup Ringbuffer Ringbuffer
00024   * @ingroup XMMSServer
00025   * @brief Ringbuffer primitive.
00026   * @{
00027   */
00028 
00029 /**
00030  * A ringbuffer
00031  */
00032 struct xmms_ringbuf_St {
00033     /** The actual bufferdata */
00034     guint8 *buffer;
00035     /** Number of bytes in #buffer */
00036     guint buffer_size;
00037     /** Actually usable number of bytes */
00038     guint buffer_size_usable;
00039     /** Read and write index */
00040     guint rd_index, wr_index;
00041     gboolean eos;
00042 
00043     GQueue *hotspots;
00044 
00045     GCond *free_cond, *used_cond, *eos_cond;
00046 };
00047 
00048 typedef struct xmms_ringbuf_hotspot_St {
00049     guint pos;
00050     gboolean (*callback) (void *);
00051     void (*destroy) (void *);
00052     void *arg;
00053 } xmms_ringbuf_hotspot_t;
00054 
00055 
00056 /**
00057  * The usable size of the ringbuffer.
00058  */
00059 guint
00060 xmms_ringbuf_size (xmms_ringbuf_t *ringbuf)
00061 {
00062     g_return_val_if_fail (ringbuf, 0);
00063 
00064     return ringbuf->buffer_size_usable;
00065 }
00066 
00067 /**
00068  * Allocate a new ringbuffer
00069  *
00070  * @param size The total size of the new ringbuffer
00071  * @returns a new #xmms_ringbuf_t
00072  */
00073 xmms_ringbuf_t *
00074 xmms_ringbuf_new (guint size)
00075 {
00076     xmms_ringbuf_t *ringbuf = g_new0 (xmms_ringbuf_t, 1);
00077 
00078     g_return_val_if_fail (size > 0, NULL);
00079     g_return_val_if_fail (size < G_MAXUINT, NULL);
00080 
00081     /* we need to allocate one byte more than requested, cause the
00082      * final byte cannot be used.
00083      * if we used it, it might lead to the situation where
00084      * read_index == write_index, which is used for the "empty"
00085      * condition.
00086      */
00087     ringbuf->buffer_size_usable = size;
00088     ringbuf->buffer_size = size + 1;
00089     ringbuf->buffer = g_malloc (ringbuf->buffer_size);
00090 
00091     ringbuf->free_cond = g_cond_new ();
00092     ringbuf->used_cond = g_cond_new ();
00093     ringbuf->eos_cond = g_cond_new ();
00094 
00095     ringbuf->hotspots = g_queue_new ();
00096 
00097     return ringbuf;
00098 }
00099 
00100 /**
00101  * Free all memory used by the ringbuffer
00102  */
00103 void
00104 xmms_ringbuf_destroy (xmms_ringbuf_t *ringbuf)
00105 {
00106     g_return_if_fail (ringbuf);
00107 
00108     g_cond_free (ringbuf->eos_cond);
00109     g_cond_free (ringbuf->used_cond);
00110     g_cond_free (ringbuf->free_cond);
00111 
00112     g_queue_free (ringbuf->hotspots);
00113     g_free (ringbuf->buffer);
00114     g_free (ringbuf);
00115 }
00116 
00117 /**
00118  * Clear the ringbuffers data
00119  */
00120 void
00121 xmms_ringbuf_clear (xmms_ringbuf_t *ringbuf)
00122 {
00123     g_return_if_fail (ringbuf);
00124 
00125     ringbuf->rd_index = 0;
00126     ringbuf->wr_index = 0;
00127 
00128     while (!g_queue_is_empty (ringbuf->hotspots)) {
00129         xmms_ringbuf_hotspot_t *hs;
00130         hs = g_queue_pop_head (ringbuf->hotspots);
00131         if (hs->destroy)
00132             hs->destroy (hs->arg);
00133         g_free (hs);
00134     }
00135     g_cond_signal (ringbuf->free_cond);
00136 }
00137 
00138 /**
00139  * Number of bytes free in the ringbuffer
00140  */
00141 guint
00142 xmms_ringbuf_bytes_free (const xmms_ringbuf_t *ringbuf)
00143 {
00144     g_return_val_if_fail (ringbuf, 0);
00145 
00146     return ringbuf->buffer_size_usable -
00147            xmms_ringbuf_bytes_used (ringbuf);
00148 }
00149 
00150 /**
00151  * Number of bytes used in the buffer
00152  */
00153 guint
00154 xmms_ringbuf_bytes_used (const xmms_ringbuf_t *ringbuf)
00155 {
00156     g_return_val_if_fail (ringbuf, 0);
00157 
00158     if (ringbuf->wr_index >= ringbuf->rd_index) {
00159         return ringbuf->wr_index - ringbuf->rd_index;
00160     }
00161 
00162     return ringbuf->buffer_size - (ringbuf->rd_index - ringbuf->wr_index);
00163 }
00164 
00165 static guint
00166 read_bytes (xmms_ringbuf_t *ringbuf, guint8 *data, guint len)
00167 {
00168     guint to_read, r = 0, cnt, tmp;
00169     gboolean ok;
00170 
00171     to_read = MIN (len, xmms_ringbuf_bytes_used (ringbuf));
00172 
00173     while (!g_queue_is_empty (ringbuf->hotspots)) {
00174         xmms_ringbuf_hotspot_t *hs = g_queue_peek_head (ringbuf->hotspots);
00175         if (hs->pos != ringbuf->rd_index) {
00176             /* make sure we don't cross a hotspot */
00177             to_read = MIN (to_read,
00178                            (hs->pos - ringbuf->rd_index + ringbuf->buffer_size)
00179                            % ringbuf->buffer_size);
00180             break;
00181         }
00182 
00183         (void) g_queue_pop_head (ringbuf->hotspots);
00184         ok = hs->callback (hs->arg);
00185         if (hs->destroy)
00186             hs->destroy (hs->arg);
00187         g_free (hs);
00188 
00189         if (!ok) {
00190             return 0;
00191         }
00192 
00193         /* we loop here, to see if there are multiple
00194            hotspots in same position */
00195     }
00196 
00197     tmp = ringbuf->rd_index;
00198 
00199     while (to_read > 0) {
00200         cnt = MIN (to_read, ringbuf->buffer_size - tmp);
00201         memcpy (data, ringbuf->buffer + tmp, cnt);
00202         tmp = (tmp + cnt) % ringbuf->buffer_size;
00203         to_read -= cnt;
00204         r += cnt;
00205         data += cnt;
00206     }
00207 
00208     return r;
00209 }
00210 
00211 /**
00212  * Reads data from the ringbuffer. This is a non-blocking call and can
00213  * return less data than you wanted. Use #xmms_ringbuf_wait_used to
00214  * ensure that you get as much data as you want.
00215  *
00216  * @param ringbuf Buffer to read from
00217  * @param data Allocated buffer where the readed data will end up
00218  * @param len number of bytes to read
00219  * @returns number of bytes that acutally was read.
00220  */
00221 guint
00222 xmms_ringbuf_read (xmms_ringbuf_t *ringbuf, gpointer data, guint len)
00223 {
00224     guint r;
00225 
00226     g_return_val_if_fail (ringbuf, 0);
00227     g_return_val_if_fail (data, 0);
00228     g_return_val_if_fail (len > 0, 0);
00229 
00230     r = read_bytes (ringbuf, (guint8 *) data, len);
00231 
00232     ringbuf->rd_index += r;
00233     ringbuf->rd_index %= ringbuf->buffer_size;
00234 
00235     if (r) {
00236         g_cond_broadcast (ringbuf->free_cond);
00237     }
00238 
00239     return r;
00240 }
00241 
00242 /**
00243  * Same as #xmms_ringbuf_read but does not advance in the buffer after
00244  * the data has been read.
00245  *
00246  * @sa xmms_ringbuf_read
00247  */
00248 guint
00249 xmms_ringbuf_peek (xmms_ringbuf_t *ringbuf, gpointer data, guint len)
00250 {
00251     g_return_val_if_fail (ringbuf, 0);
00252     g_return_val_if_fail (data, 0);
00253     g_return_val_if_fail (len > 0, 0);
00254     g_return_val_if_fail (len <= ringbuf->buffer_size_usable, 0);
00255 
00256     return read_bytes (ringbuf, (guint8 *) data, len);
00257 }
00258 
00259 /**
00260  * Same as #xmms_ringbuf_read but blocks until you have all the data you want.
00261  *
00262  * @sa xmms_ringbuf_read
00263  */
00264 guint
00265 xmms_ringbuf_read_wait (xmms_ringbuf_t *ringbuf, gpointer data,
00266                         guint len, GMutex *mtx)
00267 {
00268     guint r = 0, res;
00269     guint8 *dest = data;
00270 
00271     g_return_val_if_fail (ringbuf, 0);
00272     g_return_val_if_fail (data, 0);
00273     g_return_val_if_fail (len > 0, 0);
00274     g_return_val_if_fail (mtx, 0);
00275 
00276     while (r < len) {
00277         res = xmms_ringbuf_read (ringbuf, dest + r, len - r);
00278         r += res;
00279         if (r == len || ringbuf->eos) {
00280             break;
00281         }
00282         if (!res)
00283             g_cond_wait (ringbuf->used_cond, mtx);
00284     }
00285 
00286     return r;
00287 }
00288 
00289 /**
00290  * Same as #xmms_ringbuf_peek but blocks until you have all the data you want.
00291  *
00292  * @sa xmms_ringbuf_peek
00293  */
00294 guint
00295 xmms_ringbuf_peek_wait (xmms_ringbuf_t *ringbuf, gpointer data,
00296                         guint len, GMutex *mtx)
00297 {
00298     g_return_val_if_fail (ringbuf, 0);
00299     g_return_val_if_fail (data, 0);
00300     g_return_val_if_fail (len > 0, 0);
00301     g_return_val_if_fail (len <= ringbuf->buffer_size_usable, 0);
00302     g_return_val_if_fail (mtx, 0);
00303 
00304     xmms_ringbuf_wait_used (ringbuf, len, mtx);
00305 
00306     return xmms_ringbuf_peek (ringbuf, data, len);
00307 }
00308 
00309 /**
00310  * Write data to the ringbuffer. If not all data can be written
00311  * to the buffer the function will not block.
00312  *
00313  * @sa xmms_ringbuf_write_wait
00314  *
00315  * @param ringbuf Ringbuffer to put data in.
00316  * @param data Data to put in ringbuffer
00317  * @param len Length of data
00318  * @returns Number of bytes that was written
00319  */
00320 guint
00321 xmms_ringbuf_write (xmms_ringbuf_t *ringbuf, gconstpointer data,
00322                     guint len)
00323 {
00324     guint to_write, w = 0, cnt;
00325     const guint8 *src = data;
00326 
00327     g_return_val_if_fail (ringbuf, 0);
00328     g_return_val_if_fail (data, 0);
00329     g_return_val_if_fail (len > 0, 0);
00330 
00331     to_write = MIN (len, xmms_ringbuf_bytes_free (ringbuf));
00332 
00333     while (to_write > 0) {
00334         cnt = MIN (to_write, ringbuf->buffer_size - ringbuf->wr_index);
00335         memcpy (ringbuf->buffer + ringbuf->wr_index, src + w, cnt);
00336         ringbuf->wr_index = (ringbuf->wr_index + cnt) % ringbuf->buffer_size;
00337         to_write -= cnt;
00338         w += cnt;
00339     }
00340 
00341     if (w) {
00342         g_cond_broadcast (ringbuf->used_cond);
00343     }
00344 
00345     return w;
00346 }
00347 
00348 /**
00349  * Same as #xmms_ringbuf_write but blocks until there is enough free space.
00350  */
00351 
00352 guint
00353 xmms_ringbuf_write_wait (xmms_ringbuf_t *ringbuf, gconstpointer data,
00354                          guint len, GMutex *mtx)
00355 {
00356     guint w = 0;
00357     const guint8 *src = data;
00358 
00359     g_return_val_if_fail (ringbuf, 0);
00360     g_return_val_if_fail (data, 0);
00361     g_return_val_if_fail (len > 0, 0);
00362     g_return_val_if_fail (mtx, 0);
00363 
00364     while (w < len) {
00365         w += xmms_ringbuf_write (ringbuf, src + w, len - w);
00366         if (w == len || ringbuf->eos) {
00367             break;
00368         }
00369 
00370         g_cond_wait (ringbuf->free_cond, mtx);
00371     }
00372 
00373     return w;
00374 }
00375 
00376 /**
00377  * Block until we have free space in the ringbuffer.
00378  */
00379 void
00380 xmms_ringbuf_wait_free (const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
00381 {
00382     g_return_if_fail (ringbuf);
00383     g_return_if_fail (len > 0);
00384     g_return_if_fail (len <= ringbuf->buffer_size_usable);
00385     g_return_if_fail (mtx);
00386 
00387     while ((xmms_ringbuf_bytes_free (ringbuf) < len) && !ringbuf->eos) {
00388         g_cond_wait (ringbuf->free_cond, mtx);
00389     }
00390 }
00391 
00392 /**
00393  * Block until we have used space in the buffer
00394  */
00395 
00396 void
00397 xmms_ringbuf_wait_used (const xmms_ringbuf_t *ringbuf, guint len, GMutex *mtx)
00398 {
00399     g_return_if_fail (ringbuf);
00400     g_return_if_fail (len > 0);
00401     g_return_if_fail (len <= ringbuf->buffer_size_usable);
00402     g_return_if_fail (mtx);
00403 
00404     while ((xmms_ringbuf_bytes_used (ringbuf) < len) && !ringbuf->eos) {
00405         g_cond_wait (ringbuf->used_cond, mtx);
00406     }
00407 }
00408 
00409 /**
00410  * Tell if the ringbuffer is EOS
00411  *
00412  * @returns TRUE if the ringbuffer is EOSed.
00413  */
00414 
00415 gboolean
00416 xmms_ringbuf_iseos (const xmms_ringbuf_t *ringbuf)
00417 {
00418     g_return_val_if_fail (ringbuf, TRUE);
00419 
00420     return !xmms_ringbuf_bytes_used (ringbuf) && ringbuf->eos;
00421 }
00422 
00423 /**
00424  * Set EOS flag on ringbuffer.
00425  */
00426 void
00427 xmms_ringbuf_set_eos (xmms_ringbuf_t *ringbuf, gboolean eos)
00428 {
00429     g_return_if_fail (ringbuf);
00430 
00431     ringbuf->eos = eos;
00432 
00433     if (eos) {
00434         g_cond_broadcast (ringbuf->eos_cond);
00435         g_cond_broadcast (ringbuf->used_cond);
00436         g_cond_broadcast (ringbuf->free_cond);
00437     }
00438 }
00439 
00440 
00441 /**
00442  * Block until we are EOSed
00443  */
00444 void
00445 xmms_ringbuf_wait_eos (const xmms_ringbuf_t *ringbuf, GMutex *mtx)
00446 {
00447     g_return_if_fail (ringbuf);
00448     g_return_if_fail (mtx);
00449 
00450     while (!xmms_ringbuf_iseos (ringbuf)) {
00451         g_cond_wait (ringbuf->eos_cond, mtx);
00452     }
00453 
00454 }
00455 /** @} */
00456 
00457 /**
00458  * @internal
00459  * Unused
00460  */
00461 void
00462 xmms_ringbuf_hotspot_set (xmms_ringbuf_t *ringbuf, gboolean (*cb) (void *), void (*destroy) (void *), void *arg)
00463 {
00464     xmms_ringbuf_hotspot_t *hs;
00465     g_return_if_fail (ringbuf);
00466 
00467     hs = g_new0 (xmms_ringbuf_hotspot_t, 1);
00468     hs->pos = ringbuf->wr_index;
00469     hs->callback = cb;
00470     hs->destroy = destroy;
00471     hs->arg = arg;
00472 
00473     g_queue_push_tail (ringbuf->hotspots, hs);
00474 }
00475 
00476