|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// TPCircularBuffer.h
|
|
Packit |
1fb8d4 |
// Circular/Ring buffer implementation
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// https://github.com/michaeltyson/TPCircularBuffer
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// Created by Michael Tyson on 10/12/2011.
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
|
|
Packit Service |
5a9772 |
// of the buffer memory directly after the buffer's end, negating the need for any buffer
|
|
Packit Service |
5a9772 |
// wrap-around logic. Clients can simply use the returned memory address as if it were contiguous
|
|
Packit Service |
5a9772 |
// space.
|
|
Packit Service |
5a9772 |
//
|
|
Packit |
1fb8d4 |
// The implementation is thread-safe in the case of a single producer and single consumer.
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
|
|
Packit |
1fb8d4 |
// adapted to Darwin by Kurt Revis (http://www.snoize.com,
|
|
Packit |
1fb8d4 |
// http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// Copyright (C) 2012-2013 A Tasty Pixel
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// This software is provided 'as-is', without any express or implied
|
|
Packit |
1fb8d4 |
// warranty. In no event will the authors be held liable for any damages
|
|
Packit |
1fb8d4 |
// arising from the use of this software.
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// Permission is granted to anyone to use this software for any purpose,
|
|
Packit |
1fb8d4 |
// including commercial applications, and to alter it and redistribute it
|
|
Packit |
1fb8d4 |
// freely, subject to the following restrictions:
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// 1. The origin of this software must not be misrepresented; you must not
|
|
Packit |
1fb8d4 |
// claim that you wrote the original software. If you use this software
|
|
Packit |
1fb8d4 |
// in a product, an acknowledgment in the product documentation would be
|
|
Packit |
1fb8d4 |
// appreciated but is not required.
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
Packit |
1fb8d4 |
// misrepresented as being the original software.
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
// 3. This notice may not be removed or altered from any source distribution.
|
|
Packit |
1fb8d4 |
//
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#ifndef TPCircularBuffer_h
|
|
Packit |
1fb8d4 |
#define TPCircularBuffer_h
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#include <libkern/OSAtomic.h>
|
|
Packit |
1fb8d4 |
#include <string.h>
|
|
Packit |
1fb8d4 |
#include <assert.h>
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#ifdef __cplusplus
|
|
Packit Service |
5a9772 |
extern "C"
|
|
Packit Service |
5a9772 |
{
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
|
|
Packit Service |
5a9772 |
typedef struct
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
void* buffer;
|
|
Packit Service |
5a9772 |
int32_t length;
|
|
Packit Service |
5a9772 |
int32_t tail;
|
|
Packit Service |
5a9772 |
int32_t head;
|
|
Packit Service |
5a9772 |
volatile int32_t fillCount;
|
|
Packit Service |
5a9772 |
} TPCircularBuffer;
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Initialise buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* Note that the length is advisory only: Because of the way the
|
|
Packit Service |
5a9772 |
* memory mirroring technique works, the true buffer length will
|
|
Packit Service |
5a9772 |
* be multiples of the device page size (e.g. 4096 bytes)
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* @param buffer Circular buffer
|
|
Packit Service |
5a9772 |
* @param length Length of buffer
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
bool TPCircularBufferInit(TPCircularBuffer* buffer, int32_t length);
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Cleanup buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* Releases buffer resources.
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
void TPCircularBufferCleanup(TPCircularBuffer* buffer);
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Clear buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* Resets buffer to original, empty state.
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* This is safe for use by consumer while producer is accessing
|
|
Packit Service |
5a9772 |
* buffer.
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
void TPCircularBufferClear(TPCircularBuffer* buffer);
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
// Reading (consuming)
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Access end of buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* This gives you a pointer to the end of the buffer, ready
|
|
Packit Service |
5a9772 |
* for reading, and the number of available bytes to read.
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* @param buffer Circular buffer
|
|
Packit Service |
5a9772 |
* @param availableBytes On output, the number of bytes ready for reading
|
|
Packit Service |
5a9772 |
* @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) void*
|
|
Packit Service |
5a9772 |
TPCircularBufferTail(TPCircularBuffer* buffer, int32_t* availableBytes)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
*availableBytes = buffer->fillCount;
|
|
Packit Service |
5a9772 |
if (*availableBytes == 0)
|
|
Packit Service |
5a9772 |
return NULL;
|
|
Packit Service |
5a9772 |
return (void*)((char*)buffer->buffer + buffer->tail);
|
|
Packit Service |
5a9772 |
}
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Consume bytes in buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* This frees up the just-read bytes, ready for writing again.
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* @param buffer Circular buffer
|
|
Packit Service |
5a9772 |
* @param amount Number of bytes to consume
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) void
|
|
Packit Service |
5a9772 |
TPCircularBufferConsume(TPCircularBuffer* buffer, int32_t amount)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
buffer->tail = (buffer->tail + amount) % buffer->length;
|
|
Packit Service |
5a9772 |
OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
|
|
Packit Service |
5a9772 |
assert(buffer->fillCount >= 0);
|
|
Packit Service |
5a9772 |
}
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Version of TPCircularBufferConsume without the memory barrier, for more optimal use in
|
|
Packit Service |
5a9772 |
* single-threaded contexts
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) void
|
|
Packit Service |
5a9772 |
TPCircularBufferConsumeNoBarrier(TPCircularBuffer* buffer, int32_t amount)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
buffer->tail = (buffer->tail + amount) % buffer->length;
|
|
Packit Service |
5a9772 |
buffer->fillCount -= amount;
|
|
Packit Service |
5a9772 |
assert(buffer->fillCount >= 0);
|
|
Packit Service |
5a9772 |
}
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Access front of buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* This gives you a pointer to the front of the buffer, ready
|
|
Packit Service |
5a9772 |
* for writing, and the number of available bytes to write.
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* @param buffer Circular buffer
|
|
Packit Service |
5a9772 |
* @param availableBytes On output, the number of bytes ready for writing
|
|
Packit Service |
5a9772 |
* @return Pointer to the first bytes ready for writing, or NULL if buffer is full
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) void*
|
|
Packit Service |
5a9772 |
TPCircularBufferHead(TPCircularBuffer* buffer, int32_t* availableBytes)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
*availableBytes = (buffer->length - buffer->fillCount);
|
|
Packit Service |
5a9772 |
if (*availableBytes == 0)
|
|
Packit Service |
5a9772 |
return NULL;
|
|
Packit Service |
5a9772 |
return (void*)((char*)buffer->buffer + buffer->head);
|
|
Packit Service |
5a9772 |
}
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
// Writing (producing)
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Produce bytes in buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* This marks the given section of the buffer ready for reading.
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* @param buffer Circular buffer
|
|
Packit Service |
5a9772 |
* @param amount Number of bytes to produce
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) void
|
|
Packit Service |
5a9772 |
TPCircularBufferProduce(TPCircularBuffer* buffer, int amount)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
buffer->head = (buffer->head + amount) % buffer->length;
|
|
Packit Service |
5a9772 |
OSAtomicAdd32Barrier(amount, &buffer->fillCount);
|
|
Packit Service |
5a9772 |
assert(buffer->fillCount <= buffer->length);
|
|
Packit Service |
5a9772 |
}
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Version of TPCircularBufferProduce without the memory barrier, for more optimal use in
|
|
Packit Service |
5a9772 |
* single-threaded contexts
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) void
|
|
Packit Service |
5a9772 |
TPCircularBufferProduceNoBarrier(TPCircularBuffer* buffer, int amount)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
buffer->head = (buffer->head + amount) % buffer->length;
|
|
Packit Service |
5a9772 |
buffer->fillCount += amount;
|
|
Packit Service |
5a9772 |
assert(buffer->fillCount <= buffer->length);
|
|
Packit Service |
5a9772 |
}
|
|
Packit Service |
5a9772 |
|
|
Packit Service |
5a9772 |
/*!
|
|
Packit Service |
5a9772 |
* Helper routine to copy bytes to buffer
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* This copies the given bytes to the buffer, and marks them ready for writing.
|
|
Packit Service |
5a9772 |
*
|
|
Packit Service |
5a9772 |
* @param buffer Circular buffer
|
|
Packit Service |
5a9772 |
* @param src Source buffer
|
|
Packit Service |
5a9772 |
* @param len Number of bytes in source buffer
|
|
Packit Service |
5a9772 |
* @return true if bytes copied, false if there was insufficient space
|
|
Packit Service |
5a9772 |
*/
|
|
Packit Service |
5a9772 |
static __inline__ __attribute__((always_inline)) bool
|
|
Packit Service |
5a9772 |
TPCircularBufferProduceBytes(TPCircularBuffer* buffer, const void* src, int32_t len)
|
|
Packit Service |
5a9772 |
{
|
|
Packit Service |
5a9772 |
int32_t space;
|
|
Packit Service |
5a9772 |
void* ptr = TPCircularBufferHead(buffer, &space);
|
|
Packit Service |
5a9772 |
if (space < len)
|
|
Packit Service |
5a9772 |
return false;
|
|
Packit Service |
5a9772 |
memcpy(ptr, src, len);
|
|
Packit Service |
5a9772 |
TPCircularBufferProduce(buffer, len);
|
|
Packit Service |
5a9772 |
return true;
|
|
Packit Service |
5a9772 |
}
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#ifdef __cplusplus
|
|
Packit |
1fb8d4 |
}
|
|
Packit |
1fb8d4 |
#endif
|
|
Packit |
1fb8d4 |
|
|
Packit |
1fb8d4 |
#endif
|