3 // Circular/Ring buffer implementation
5 // https://github.com/michaeltyson/TPCircularBuffer
7 // Created by Michael Tyson on 10/12/2011.
8 // Copyright 2011-2012 A Tasty Pixel. All rights reserved.
11 // This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
12 // of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around
13 // logic. Clients can simply use the returned memory address as if it were contiguous space.
15 // The implementation is thread-safe in the case of a single producer and single consumer.
17 // Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
18 // adapted to Darwin by Kurt Revis (http://www.snoize.com,
19 // http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
22 #ifndef TPCircularBuffer_h
23 #define TPCircularBuffer_h
25 #include <libkern/OSAtomic.h>
37 volatile int32_t fillCount;
43 * Note that the length is advisory only: Because of the way the
44 * memory mirroring technique works, the true buffer length will
45 * be multiples of the device page size (e.g. 4096 bytes)
47 * @param buffer Circular buffer
48 * @param length Length of buffer
50 bool TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length);
55 * Releases buffer resources.
57 void TPCircularBufferCleanup(TPCircularBuffer *buffer);
62 * Resets buffer to original, empty state.
64 * This is safe for use by consumer while producer is accessing
67 void TPCircularBufferClear(TPCircularBuffer *buffer);
69 // Reading (consuming)
72 * Access end of buffer
74 * This gives you a pointer to the end of the buffer, ready
75 * for reading, and the number of available bytes to read.
77 * @param buffer Circular buffer
78 * @param availableBytes On output, the number of bytes ready for reading
79 * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
81 static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
82 *availableBytes = buffer->fillCount;
83 if ( *availableBytes == 0 ) return NULL;
84 return (void*)((char*)buffer->buffer + buffer->tail);
88 * Consume bytes in buffer
90 * This frees up the just-read bytes, ready for writing again.
92 * @param buffer Circular buffer
93 * @param amount Number of bytes to consume
95 static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) {
96 buffer->tail = (buffer->tail + amount) % buffer->length;
97 OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
101 * Version of TPCircularBufferConsume without the memory barrier, for more optimal use in single-threaded contexts
103 static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
104 buffer->tail = (buffer->tail + amount) % buffer->length;
105 buffer->fillCount -= amount;
109 * Access front of buffer
111 * This gives you a pointer to the front of the buffer, ready
112 * for writing, and the number of available bytes to write.
114 * @param buffer Circular buffer
115 * @param availableBytes On output, the number of bytes ready for writing
116 * @return Pointer to the first bytes ready for writing, or NULL if buffer is full
118 static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
119 *availableBytes = (buffer->length - buffer->fillCount);
120 if ( *availableBytes == 0 ) return NULL;
121 return (void*)((char*)buffer->buffer + buffer->head);
124 // Writing (producing)
127 * Produce bytes in buffer
129 * This marks the given section of the buffer ready for reading.
131 * @param buffer Circular buffer
132 * @param amount Number of bytes to produce
134 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int amount) {
135 buffer->head = (buffer->head + amount) % buffer->length;
136 OSAtomicAdd32Barrier(amount, &buffer->fillCount);
140 * Version of TPCircularBufferProduce without the memory barrier, for more optimal use in single-threaded contexts
142 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int amount) {
143 buffer->head = (buffer->head + amount) % buffer->length;
144 buffer->fillCount += amount;
148 * Helper routine to copy bytes to buffer
150 * This copies the given bytes to the buffer, and marks them ready for writing.
152 * @param buffer Circular buffer
153 * @param src Source buffer
154 * @param len Number of bytes in source buffer
155 * @return true if bytes copied, false if there was insufficient space
157 static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
159 void *ptr = TPCircularBufferHead(buffer, &space);
160 if ( space < len ) return false;
161 memcpy(ptr, src, len);
162 TPCircularBufferProduce(buffer, len);