Mercurial > hg > js-dsp-test
view fft/native/bqvec/bqvec/RingBuffer.h @ 29:cf59817a5983
Build stuff for native
author | Chris Cannam |
---|---|
date | Sat, 17 Oct 2015 15:00:08 +0100 |
parents | |
children |
line wrap: on
line source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* bqvec A small library for vector arithmetic and allocation in C++ using raw C pointer arrays. Copyright 2007-2015 Particular Programs Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of Chris Cannam and Particular Programs Ltd shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization. */ #ifndef BQVEC_RINGBUFFER_H #define BQVEC_RINGBUFFER_H #include <sys/types.h> //#define DEBUG_RINGBUFFER 1 #include "Barrier.h" #include "Allocators.h" #include <iostream> namespace breakfastquay { /** * RingBuffer implements a lock-free ring buffer for one writer and * one reader, that is to be used to store a sample type T. * * RingBuffer is thread-safe provided only one thread writes and only * one thread reads. */ template <typename T> class RingBuffer { public: /** * Create a ring buffer with room to write n samples. * * Note that the internal storage size will actually be n+1 * samples, as one element is unavailable for administrative * reasons. Since the ring buffer performs best if its size is a * power of two, this means n should ideally be some power of two * minus one. */ RingBuffer(int n); virtual ~RingBuffer(); /** * Return the total capacity of the ring buffer in samples. * (This is the argument n passed to the constructor.) */ int getSize() const; /** * Return a new ring buffer (allocated with "new" -- caller must * delete when no longer needed) of the given size, containing the * same data as this one. If another thread reads from or writes * to this buffer during the call, the results may be incomplete * or inconsistent. If this buffer's data will not fit in the new * size, the contents are undefined. */ RingBuffer<T> *resized(int newSize) const; /** * Reset read and write pointers, thus emptying the buffer. * Should be called from the write thread. */ void reset(); /** * Return the amount of data available for reading, in samples. */ int getReadSpace() const; /** * Return the amount of space available for writing, in samples. */ int getWriteSpace() const; /** * Read n samples from the buffer. If fewer than n are available, * the remainder will be zeroed out. Returns the number of * samples actually read. * * This is a template function, taking an argument S for the target * sample type, which is permitted to differ from T if the two * types are compatible for arithmetic operations. */ template <typename S> int read(S *const BQ_R__ destination, int n); /** * Read n samples from the buffer, adding them to the destination. * If fewer than n are available, the remainder will be left * alone. Returns the number of samples actually read. * * This is a template function, taking an argument S for the target * sample type, which is permitted to differ from T if the two * types are compatible for arithmetic operations. */ template <typename S> int readAdding(S *const BQ_R__ destination, int n); /** * Read one sample from the buffer. If no sample is available, * this will silently return zero. Calling this repeatedly is * obviously slower than calling read once, but it may be good * enough if you don't want to allocate a buffer to read into. */ T readOne(); /** * Read n samples from the buffer, if available, without advancing * the read pointer -- i.e. a subsequent read() or skip() will be * necessary to empty the buffer. If fewer than n are available, * the remainder will be zeroed out. Returns the number of * samples actually read. */ int peek(T *const BQ_R__ destination, int n) const; /** * Read one sample from the buffer, if available, without * advancing the read pointer -- i.e. a subsequent read() or * skip() will be necessary to empty the buffer. Returns zero if * no sample was available. */ T peekOne() const; /** * Pretend to read n samples from the buffer, without actually * returning them (i.e. discard the next n samples). Returns the * number of samples actually available for discarding. */ int skip(int n); /** * Write n samples to the buffer. If insufficient space is * available, not all samples may actually be written. Returns * the number of samples actually written. * * This is a template function, taking an argument S for the source * sample type, which is permitted to differ from T if the two * types are compatible for assignment. */ template <typename S> int write(const S *const BQ_R__ source, int n); /** * Write n zero-value samples to the buffer. If insufficient * space is available, not all zeros may actually be written. * Returns the number of zeroes actually written. */ int zero(int n); protected: T *const BQ_R__ m_buffer; int m_writer; int m_reader; const int m_size; int readSpaceFor(int w, int r) const { int space; if (w > r) space = w - r; else if (w < r) space = (w + m_size) - r; else space = 0; return space; } int writeSpaceFor(int w, int r) const { int space = (r + m_size - w - 1); if (space >= m_size) space -= m_size; return space; } private: RingBuffer(const RingBuffer &); // not provided RingBuffer &operator=(const RingBuffer &); // not provided }; template <typename T> RingBuffer<T>::RingBuffer(int n) : m_buffer(allocate<T>(n + 1)), m_writer(0), m_size(n + 1) { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T>[" << this << "]::RingBuffer(" << n << ")" << std::endl; #endif m_reader = 0; } template <typename T> RingBuffer<T>::~RingBuffer() { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T>[" << this << "]::~RingBuffer" << std::endl; #endif deallocate(m_buffer); } template <typename T> int RingBuffer<T>::getSize() const { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T>[" << this << "]::getSize(): " << m_size-1 << std::endl; #endif return m_size - 1; } template <typename T> RingBuffer<T> * RingBuffer<T>::resized(int newSize) const { RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize); int w = m_writer; int r = m_reader; while (r != w) { T value = m_buffer[r]; newBuffer->write(&value, 1); if (++r == m_size) r = 0; } return newBuffer; } template <typename T> void RingBuffer<T>::reset() { #ifdef DEBUG_RINGBUFFER std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl; #endif m_reader = m_writer; } template <typename T> int RingBuffer<T>::getReadSpace() const { return readSpaceFor(m_writer, m_reader); } template <typename T> int RingBuffer<T>::getWriteSpace() const { return writeSpaceFor(m_writer, m_reader); } template <typename T> template <typename S> int RingBuffer<T>::read(S *const BQ_R__ destination, int n) { int w = m_writer; int r = m_reader; int available = readSpaceFor(w, r); if (n > available) { std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only " << available << " available" << std::endl; n = available; } if (n == 0) return n; int here = m_size - r; T *const BQ_R__ bufbase = m_buffer + r; if (here >= n) { v_convert(destination, bufbase, n); } else { v_convert(destination, bufbase, here); v_convert(destination + here, m_buffer, n - here); } r += n; while (r >= m_size) r -= m_size; BQ_MBARRIER(); m_reader = r; return n; } template <typename T> template <typename S> int RingBuffer<T>::readAdding(S *const BQ_R__ destination, int n) { int w = m_writer; int r = m_reader; int available = readSpaceFor(w, r); if (n > available) { std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only " << available << " available" << std::endl; n = available; } if (n == 0) return n; int here = m_size - r; T *const BQ_R__ bufbase = m_buffer + r; if (here >= n) { v_add(destination, bufbase, n); } else { v_add(destination, bufbase, here); v_add(destination + here, m_buffer, n - here); } r += n; while (r >= m_size) r -= m_size; BQ_MBARRIER(); m_reader = r; return n; } template <typename T> T RingBuffer<T>::readOne() { int w = m_writer; int r = m_reader; if (w == r) { std::cerr << "WARNING: RingBuffer::readOne: no sample available" << std::endl; return T(); } T value = m_buffer[r]; if (++r == m_size) r = 0; BQ_MBARRIER(); m_reader = r; return value; } template <typename T> int RingBuffer<T>::peek(T *const BQ_R__ destination, int n) const { int w = m_writer; int r = m_reader; int available = readSpaceFor(w, r); if (n > available) { std::cerr << "WARNING: RingBuffer::peek: " << n << " requested, only " << available << " available" << std::endl; memset(destination + available, 0, (n - available) * sizeof(T)); n = available; } if (n == 0) return n; int here = m_size - r; const T *const BQ_R__ bufbase = m_buffer + r; if (here >= n) { v_copy(destination, bufbase, n); } else { v_copy(destination, bufbase, here); v_copy(destination + here, m_buffer, n - here); } return n; } template <typename T> T RingBuffer<T>::peekOne() const { int w = m_writer; int r = m_reader; if (w == r) { std::cerr << "WARNING: RingBuffer::peekOne: no sample available" << std::endl; return 0; } T value = m_buffer[r]; return value; } template <typename T> int RingBuffer<T>::skip(int n) { int w = m_writer; int r = m_reader; int available = readSpaceFor(w, r); if (n > available) { std::cerr << "WARNING: RingBuffer::skip: " << n << " requested, only " << available << " available" << std::endl; n = available; } if (n == 0) return n; r += n; while (r >= m_size) r -= m_size; // No memory barrier required, because we didn't read any data m_reader = r; return n; } template <typename T> template <typename S> int RingBuffer<T>::write(const S *const BQ_R__ source, int n) { int w = m_writer; int r = m_reader; int available = writeSpaceFor(w, r); if (n > available) { std::cerr << "WARNING: RingBuffer::write: " << n << " requested, only room for " << available << std::endl; n = available; } if (n == 0) return n; int here = m_size - w; T *const BQ_R__ bufbase = m_buffer + w; if (here >= n) { v_convert<S, T>(bufbase, source, n); } else { v_convert<S, T>(bufbase, source, here); v_convert<S, T>(m_buffer, source + here, n - here); } w += n; while (w >= m_size) w -= m_size; BQ_MBARRIER(); m_writer = w; return n; } template <typename T> int RingBuffer<T>::zero(int n) { int w = m_writer; int r = m_reader; int available = writeSpaceFor(w, r); if (n > available) { std::cerr << "WARNING: RingBuffer::zero: " << n << " requested, only room for " << available << std::endl; n = available; } if (n == 0) return n; int here = m_size - w; T *const BQ_R__ bufbase = m_buffer + w; if (here >= n) { v_zero(bufbase, n); } else { v_zero(bufbase, here); v_zero(m_buffer, n - here); } w += n; while (w >= m_size) w -= m_size; BQ_MBARRIER(); m_writer = w; return n; } } #endif // BQVEC_RINGBUFFER_H