TPCircularBuffer.h
Go to the documentation of this file.
1 //
2 // TPCircularBuffer.h
3 // Circular/Ring buffer implementation
4 //
5 // https://github.com/michaeltyson/TPCircularBuffer
6 //
7 // Created by Michael Tyson on 10/12/2011.
8 //
9 //
10 // This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
11 // of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around
12 // logic. Clients can simply use the returned memory address as if it were contiguous space.
13 //
14 // The implementation is thread-safe in the case of a single producer and single consumer.
15 //
16 // Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
17 // adapted to Darwin by Kurt Revis (http://www.snoize.com,
18 // http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
19 //
20 //
21 // Copyright (C) 2012-2013 A Tasty Pixel
22 //
23 // This software is provided 'as-is', without any express or implied
24 // warranty. In no event will the authors be held liable for any damages
25 // arising from the use of this software.
26 //
27 // Permission is granted to anyone to use this software for any purpose,
28 // including commercial applications, and to alter it and redistribute it
29 // freely, subject to the following restrictions:
30 //
31 // 1. The origin of this software must not be misrepresented; you must not
32 // claim that you wrote the original software. If you use this software
33 // in a product, an acknowledgment in the product documentation would be
34 // appreciated but is not required.
35 //
36 // 2. Altered source versions must be plainly marked as such, and must not be
37 // misrepresented as being the original software.
38 //
39 // 3. This notice may not be removed or altered from any source distribution.
40 //
41 
42 #ifndef TPCircularBuffer_h
43 #define TPCircularBuffer_h
44 
45 #include <stdbool.h>
46 #include <string.h>
47 #include <assert.h>
48 
49 #ifdef __cplusplus
50  extern "C++" {
51  #include <atomic>
52  using namespace std;
53  }
54 #else
55  #include <stdatomic.h>
56 #endif
57 
58 #ifdef __cplusplus
59 extern "C" {
60 #endif
61 
62 typedef struct {
63  void *buffer;
64  int32_t length;
65  int32_t tail;
66  int32_t head;
67  volatile atomic_int fillCount;
68  bool atomic;
70 
88 #define TPCircularBufferInit(buffer, length) \
89  _TPCircularBufferInit(buffer, length, sizeof(*buffer))
90 bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize);
91 
98 
108 
123 void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic);
124 
125 // Reading (consuming)
126 
137 static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
138  *availableBytes = buffer->fillCount;
139  if ( *availableBytes == 0 ) return NULL;
140  return (void*)((char*)buffer->buffer + buffer->tail);
141 }
142 
151 static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) {
152  buffer->tail = (buffer->tail + amount) % buffer->length;
153  if ( buffer->atomic ) {
154  atomic_fetch_add(&buffer->fillCount, -amount);
155  } else {
156  buffer->fillCount -= amount;
157  }
158  assert(buffer->fillCount >= 0);
159 }
160 
171 static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
172  *availableBytes = (buffer->length - buffer->fillCount);
173  if ( *availableBytes == 0 ) return NULL;
174  return (void*)((char*)buffer->buffer + buffer->head);
175 }
176 
177 // Writing (producing)
178 
187 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int32_t amount) {
188  buffer->head = (buffer->head + amount) % buffer->length;
189  if ( buffer->atomic ) {
190  atomic_fetch_add(&buffer->fillCount, amount);
191  } else {
192  buffer->fillCount += amount;
193  }
194  assert(buffer->fillCount <= buffer->length);
195 }
196 
207 static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
208  int32_t space;
209  void *ptr = TPCircularBufferHead(buffer, &space);
210  if ( space < len ) return false;
211  memcpy(ptr, src, len);
212  TPCircularBufferProduce(buffer, len);
213  return true;
214 }
215 
219 static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferConsume instead")
220 void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
221  buffer->tail = (buffer->tail + amount) % buffer->length;
222  buffer->fillCount -= amount;
223  assert(buffer->fillCount >= 0);
224 }
225 
229 static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferProduce instead")
230 void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
231  buffer->head = (buffer->head + amount) % buffer->length;
232  buffer->fillCount += amount;
233  assert(buffer->fillCount <= buffer->length);
234 }
235 
236 #ifdef __cplusplus
237 }
238 #endif
239 
240 #endif