]> git.sesse.net Git - vlc/blob - modules/audio_output/TPCircularBuffer.h
mux/mpeg/ts.c: support for libdvbpsi >= 1.0.0
[vlc] / modules / audio_output / TPCircularBuffer.h
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 //  Copyright 2011-2012 A Tasty Pixel. All rights reserved.
9 //
10 //
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.
14 //
15 //  The implementation is thread-safe in the case of a single producer and single consumer.
16 //
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)
20 //
21
22 #ifndef TPCircularBuffer_h
23 #define TPCircularBuffer_h
24
25 #include <libkern/OSAtomic.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32
33 typedef struct {
34     void             *buffer;
35     int32_t           length;
36     int32_t           tail;
37     int32_t           head;
38     volatile int32_t  fillCount;
39 } TPCircularBuffer;
40
41 /*!
42  * Initialise buffer
43  *
44  *  Note that the length is advisory only: Because of the way the
45  *  memory mirroring technique works, the true buffer length will
46  *  be multiples of the device page size (e.g. 4096 bytes)
47  *
48  * @param buffer Circular buffer
49  * @param length Length of buffer
50  */
51 bool  TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length);
52
53 /*!
54  * Cleanup buffer
55  *
56  *  Releases buffer resources.
57  */
58 void  TPCircularBufferCleanup(TPCircularBuffer *buffer);
59
60 /*!
61  * Clear buffer
62  *
63  *  Resets buffer to original, empty state.
64  *
65  *  This is safe for use by consumer while producer is accessing
66  *  buffer.
67  */
68 void  TPCircularBufferClear(TPCircularBuffer *buffer);
69
70 // Reading (consuming)
71
72 /*!
73  * Access end of buffer
74  *
75  *  This gives you a pointer to the end of the buffer, ready
76  *  for reading, and the number of available bytes to read.
77  *
78  * @param buffer Circular buffer
79  * @param availableBytes On output, the number of bytes ready for reading
80  * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
81  */
82 static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
83     *availableBytes = buffer->fillCount;
84     if ( *availableBytes == 0 ) return NULL;
85     return (void*)((char*)buffer->buffer + buffer->tail);
86 }
87
88 /*!
89  * Consume bytes in buffer
90  *
91  *  This frees up the just-read bytes, ready for writing again.
92  *
93  * @param buffer Circular buffer
94  * @param amount Number of bytes to consume
95  */
96 static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) {
97     buffer->tail = (buffer->tail + amount) % buffer->length;
98     OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
99     assert(buffer->fillCount >= 0);
100 }
101
102 /*!
103  * Version of TPCircularBufferConsume without the memory barrier, for more optimal use in single-threaded contexts
104  */
105 static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
106     buffer->tail = (buffer->tail + amount) % buffer->length;
107     buffer->fillCount -= amount;
108     assert(buffer->fillCount >= 0);
109 }
110
111 /*!
112  * Access front of buffer
113  *
114  *  This gives you a pointer to the front of the buffer, ready
115  *  for writing, and the number of available bytes to write.
116  *
117  * @param buffer Circular buffer
118  * @param availableBytes On output, the number of bytes ready for writing
119  * @return Pointer to the first bytes ready for writing, or NULL if buffer is full
120  */
121 static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
122     *availableBytes = (buffer->length - buffer->fillCount);
123     if ( *availableBytes == 0 ) return NULL;
124     return (void*)((char*)buffer->buffer + buffer->head);
125 }
126
127 // Writing (producing)
128
129 /*!
130  * Produce bytes in buffer
131  *
132  *  This marks the given section of the buffer ready for reading.
133  *
134  * @param buffer Circular buffer
135  * @param amount Number of bytes to produce
136  */
137 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int amount) {
138     buffer->head = (buffer->head + amount) % buffer->length;
139     OSAtomicAdd32Barrier(amount, &buffer->fillCount);
140     assert(buffer->fillCount <= buffer->length);
141 }
142
143 /*!
144  * Version of TPCircularBufferProduce without the memory barrier, for more optimal use in single-threaded contexts
145  */
146 static __inline__ __attribute__((always_inline)) void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int amount) {
147     buffer->head = (buffer->head + amount) % buffer->length;
148     buffer->fillCount += amount;
149     assert(buffer->fillCount <= buffer->length);
150 }
151
152 /*!
153  * Helper routine to copy bytes to buffer
154  *
155  *  This copies the given bytes to the buffer, and marks them ready for writing.
156  *
157  * @param buffer Circular buffer
158  * @param src Source buffer
159  * @param len Number of bytes in source buffer
160  * @return true if bytes copied, false if there was insufficient space
161  */
162 static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
163     int32_t space;
164     void *ptr = TPCircularBufferHead(buffer, &space);
165     if ( space < len ) return false;
166     memcpy(ptr, src, len);
167     TPCircularBufferProduce(buffer, len);
168     return true;
169 }
170
171 #ifdef __cplusplus
172 }
173 #endif
174
175 #endif