diff options
author | Johns <johns98@gmx.net> | 2011-12-07 15:05:38 +0100 |
---|---|---|
committer | Johns <johns98@gmx.net> | 2011-12-07 15:05:38 +0100 |
commit | ce97b938ca2b1767267c253da6c47b3bf07c32eb (patch) | |
tree | 42b5e3d67595afc8a0790bdd7baecb8a0d570105 /ringbuffer.c | |
parent | ab6c3b4de81554dab6beee615c2744af42b15fd4 (diff) | |
download | vdr-plugin-softhddevice-ce97b938ca2b1767267c253da6c47b3bf07c32eb.tar.gz vdr-plugin-softhddevice-ce97b938ca2b1767267c253da6c47b3bf07c32eb.tar.bz2 |
C part of the plugin.
Diffstat (limited to 'ringbuffer.c')
-rw-r--r-- | ringbuffer.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/ringbuffer.c b/ringbuffer.c new file mode 100644 index 0000000..81722e2 --- /dev/null +++ b/ringbuffer.c @@ -0,0 +1,328 @@ +/// +/// @file ringbuffer.c @brief Ringbuffer module +/// +/// Copyright (c) 2009, 2011 by Johns. All Rights Reserved. +/// +/// Contributor(s): +/// +/// License: AGPLv3 +/// +/// This program is free software: you can redistribute it and/or modify +/// it under the terms of the GNU Affero General Public License as +/// published by the Free Software Foundation, either version 3 of the +/// License. +/// +/// This program is distributed in the hope that it will be useful, +/// but WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +/// GNU Affero General Public License for more details. +/// +/// $Id$ +////////////////////////////////////////////////////////////////////////////// + +/// +/// @defgroup Ringbuffer The ring buffer module. +/// +/// Lock free ring buffer with only one writer and one reader. +/// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <alsa/iatomic.h> + +#include "ringbuffer.h" + + /// ring buffer structure +struct _ring_buffer_ +{ + char *Buffer; ///< ring buffer data + const char *BufferEnd; ///< end of buffer + size_t Size; ///< bytes in buffer (for faster calc) + + const char *ReadPointer; ///< only used by reader + char *WritePointer; ///< only used by writer + + /// The only thing modified by both + atomic_t Filled; ///< how many of the buffer is used +}; + +/** +** Allocate a new ring buffer. +** +** @param size Size of the ring buffer. +** +** @returns Allocated ring buffer, must be freed with +** RingBufferDel(), NULL for out of memory. +*/ +RingBuffer *RingBufferNew(size_t size) +{ + RingBuffer *rb; + + if (!(rb = malloc(sizeof(*rb)))) { // allocate structure + return rb; + } + if (!(rb->Buffer = malloc(size))) { // allocate buffer + free(rb); + return NULL; + } + + rb->Size = size; + rb->ReadPointer = rb->Buffer; + rb->WritePointer = rb->Buffer; + rb->BufferEnd = rb->Buffer + size; + atomic_set(&rb->Filled, 0); + + return rb; +} + +/** +** Free an allocated ring buffer. +*/ +void RingBufferDel(RingBuffer * rb) +{ + free(rb->Buffer); + free(rb); +} + +/** +** Advance write pointer in ring buffer. +** +** @param rb Ring buffer to adance write pointer. +** @param cnt Number of bytes to be adavanced. +** +** @returns Number of bytes that could be advanced in ring buffer. +*/ +size_t RingBufferWriteAdvance(RingBuffer * rb, size_t cnt) +{ + size_t n; + + n = rb->Size - atomic_read(&rb->Filled); + if (cnt > n) { // not enough space + cnt = n; + } + // + // Hitting end of buffer? + // + n = rb->BufferEnd - rb->WritePointer; + if (n > cnt) { // don't cross the end + rb->WritePointer += cnt; + } else { // reached or cross the end + rb->WritePointer = rb->Buffer; + if (n < cnt) { + n = cnt - n; + rb->WritePointer += n; + } + } + + // + // Only atomic modification! + // + atomic_add(cnt, &rb->Filled); + return cnt; +} + +/** +** Write to a ring buffer. +** +** @param rb Ring buffer to write to. +** @param buf Buffer of @p cnt bytes. +** @param cnt Number of bytes in buffer. +** +** @returns The number of bytes that could be placed in the ring +** buffer. +*/ +size_t RingBufferWrite(RingBuffer * rb, const void *buf, size_t cnt) +{ + size_t n; + + n = rb->Size - atomic_read(&rb->Filled); + if (cnt > n) { // not enough space + cnt = n; + } + // + // Hitting end of buffer? + // + n = rb->BufferEnd - rb->WritePointer; + if (n > cnt) { // don't cross the end + memcpy(rb->WritePointer, buf, cnt); + rb->WritePointer += cnt; + } else { // reached or cross the end + memcpy(rb->WritePointer, buf, n); + rb->WritePointer = rb->Buffer; + if (n < cnt) { + buf += n; + n = cnt - n; + memcpy(rb->WritePointer, buf, n); + rb->WritePointer += n; + } + } + + // + // Only atomic modification! + // + atomic_add(cnt, &rb->Filled); + return cnt; +} + +/** +** Get write pointer and free bytes at this position of ring buffer. +** +** @param rb Ring buffer to write to. +** @param[out] wp Write pointer is placed here +** +** @returns The number of bytes that could be placed in the ring +** buffer at the write pointer. +*/ +size_t RingBufferGetWritePointer(RingBuffer * rb, void **wp) +{ + size_t n; + size_t cnt; + + // Total free bytes available in ring buffer + cnt = rb->Size - atomic_read(&rb->Filled); + + *wp = rb->WritePointer; + + // + // Hitting end of buffer? + // + n = rb->BufferEnd - rb->WritePointer; + if (n <= cnt) { // reached or cross the end + return n; + } + return cnt; +} + +/** +** Advance read pointer in ring buffer. +** +** @param rb Ring buffer to adance read pointer. +** @param cnt Number of bytes to be advanced. +** +** @returns Number of bytes that could be advanced in ring buffer. +*/ +size_t RingBufferReadAdvance(RingBuffer * rb, size_t cnt) +{ + size_t n; + + n = atomic_read(&rb->Filled); + if (cnt > n) { // not enough filled + cnt = n; + } + // + // Hitting end of buffer? + // + n = rb->BufferEnd - rb->ReadPointer; + if (n > cnt) { // don't cross the end + rb->ReadPointer += cnt; + } else { // reached or cross the end + rb->ReadPointer = rb->Buffer; + if (n < cnt) { + n = cnt - n; + rb->ReadPointer += n; + } + } + + // + // Only atomic modification! + // + atomic_sub(cnt, &rb->Filled); + return cnt; +} + +/** +** Read from a ring buffer. +** +** @param rb Ring buffer to read from. +** @param buf Buffer of @p cnt bytes. +** @param cnt Number of bytes to be read. +** +** @returns Number of bytes that could be read from ring buffer. +*/ +size_t RingBufferRead(RingBuffer * rb, void *buf, size_t cnt) +{ + size_t n; + + n = atomic_read(&rb->Filled); + if (cnt > n) { // not enough filled + cnt = n; + } + // + // Hitting end of buffer? + // + n = rb->BufferEnd - rb->ReadPointer; + if (n > cnt) { // don't cross the end + memcpy(buf, rb->ReadPointer, cnt); + rb->ReadPointer += cnt; + } else { // reached or cross the end + memcpy(buf, rb->ReadPointer, n); + rb->ReadPointer = rb->Buffer; + if (n < cnt) { + buf += n; + n = cnt - n; + memcpy(buf, rb->ReadPointer, n); + rb->ReadPointer += n; + } + } + + // + // Only atomic modification! + // + atomic_sub(cnt, &rb->Filled); + return cnt; +} + +/** +** Get read pointer and used bytes at this position of ring buffer. +** +** @param rb Ring buffer to read from. +** @param[out] rp Read pointer is placed here +** +** @returns The number of bytes that could be read from the ring +** buffer at the read pointer. +*/ +size_t RingBufferGetReadPointer(RingBuffer * rb, const void **rp) +{ + size_t n; + size_t cnt; + + // Total used bytes in ring buffer + cnt = atomic_read(&rb->Filled); + + *rp = rb->ReadPointer; + + // + // Hitting end of buffer? + // + n = rb->BufferEnd - rb->ReadPointer; + if (n <= cnt) { // reached or cross the end + return n; + } + return cnt; +} + +/** +** Get free bytes in ring buffer. +** +** @param rb Ring buffer. +** +** @returns Number of bytes free in buffer. +*/ +size_t RingBufferFreeBytes(RingBuffer * rb) +{ + return rb->Size - atomic_read(&rb->Filled); +} + +/** +** Get used bytes in ring buffer. +** +** @param rb Ring buffer. +** +** @returns Number of bytes used in buffer. +*/ +size_t RingBufferUsedBytes(RingBuffer * rb) +{ + return atomic_read(&rb->Filled); +} |