XMMS2
|
00001 /* XMMS2 - X Music Multiplexer System 00002 * Copyright (C) 2003-2011 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