Add createContext methods that don't throw on error
[alure.git] / src / ringbuf.cpp
blobe3b47382f7b0f6ea9e40a8e4f274a7f9a345917e
2 #include "ringbuf.h"
4 #include <stdexcept>
5 #include <cstdlib>
6 #include <cstring>
7 #include <atomic>
8 #include <limits>
11 namespace alure
14 RingBuffer::RingBuffer(size_t sz, size_t elem_sz)
15 : mWritePtr(0), mReadPtr(0), mSize(0), mSizeMask(0), mElemSize(elem_sz)
16 , mBuffer(nullptr)
18 size_t power_of_two = 1;
19 if(sz > 1)
21 power_of_two = sz - 1;
22 power_of_two |= power_of_two>>1;
23 power_of_two |= power_of_two>>2;
24 power_of_two |= power_of_two>>4;
25 power_of_two |= power_of_two>>8;
26 power_of_two |= power_of_two>>16;
27 if(sizeof(power_of_two) > 4)
28 power_of_two |= power_of_two>>32;
29 ++power_of_two;
31 if(power_of_two < sz || std::numeric_limits<size_t>::max()/elem_sz <= power_of_two)
32 throw std::bad_alloc();
34 mSize = power_of_two;
35 mSizeMask = mSize - 1;
37 mBuffer = std::unique_ptr<char[]>(new char[mSize*mElemSize]);
38 std::fill(&(mBuffer[0]), &(mBuffer[mSize*mElemSize]), 0);
41 void RingBuffer::reset()
43 mReadPtr = 0;
44 mWritePtr = 0;
45 std::fill(&(mBuffer[0]), &(mBuffer[mSize*mElemSize]), 0);
48 size_t RingBuffer::read_space() const
50 size_t w = mWritePtr.load();
51 size_t r = mReadPtr.load();
52 return (mSize+w-r) & mSizeMask;
55 size_t RingBuffer::write_space() const
57 size_t w = mWritePtr.load();
58 size_t r = mReadPtr.load();
59 return (mSize+r-w-1) & mSizeMask;
62 size_t RingBuffer::read(char* dest, size_t cnt)
64 size_t read_ptr;
65 size_t free_cnt;
66 size_t cnt2;
67 size_t to_read;
68 size_t n1, n2;
70 read_ptr = mReadPtr.load();
71 free_cnt = (mSize+mWritePtr.load()-read_ptr) & mSizeMask;
72 if(free_cnt == 0) return 0;
74 to_read = (cnt > free_cnt) ? free_cnt : cnt;
75 cnt2 = read_ptr + to_read;
76 if(cnt2 > mSize)
78 n1 = mSize - read_ptr;
79 n2 = cnt2 & mSizeMask;
80 memcpy(dest, &(mBuffer[read_ptr*mElemSize]), n1*mElemSize);
81 memcpy(dest + n1*mElemSize, &(mBuffer[0]), n2*mElemSize);
82 mReadPtr.store((read_ptr + n1 + n2) & mSizeMask);
84 else
86 n1 = to_read;
87 memcpy(dest, &(mBuffer[read_ptr*mElemSize]), n1*mElemSize);
88 mReadPtr.store((read_ptr + n1) & mSizeMask);
91 return to_read;
94 size_t RingBuffer::peek(char* dest, size_t cnt)
96 size_t read_ptr;
97 size_t free_cnt;
98 size_t cnt2;
99 size_t to_read;
100 size_t n1, n2;
102 read_ptr = mReadPtr.load();
103 free_cnt = (mSize+mWritePtr.load()-read_ptr) & mSizeMask;
104 if(free_cnt == 0) return 0;
106 to_read = (cnt > free_cnt) ? free_cnt : cnt;
107 cnt2 = read_ptr + to_read;
108 if(cnt2 > mSize)
110 n1 = mSize - read_ptr;
111 n2 = cnt2 & mSizeMask;
112 memcpy(dest, &(mBuffer[read_ptr*mElemSize]), n1*mElemSize);
113 memcpy(dest + n1*mElemSize, &(mBuffer[0]), n2*mElemSize);
115 else
117 n1 = to_read;
118 memcpy(dest, &(mBuffer[read_ptr*mElemSize]), n1*mElemSize);
121 return to_read;
124 size_t RingBuffer::write(const char* src, size_t cnt)
126 size_t write_ptr;
127 size_t free_cnt;
128 size_t cnt2;
129 size_t to_write;
130 size_t n1, n2;
132 write_ptr = mWritePtr.load();
133 free_cnt = (mSize+mReadPtr.load()-write_ptr-1) & mSizeMask;
134 if(free_cnt == 0) return 0;
136 to_write = (cnt > free_cnt) ? free_cnt : cnt;
137 cnt2 = write_ptr + to_write;
138 if(cnt2 > mSize)
140 n1 = mSize - write_ptr;
141 n2 = cnt2 & mSizeMask;
142 memcpy(&(mBuffer[write_ptr*mElemSize]), src, n1*mElemSize);
143 memcpy(&(mBuffer[0]), src + n1*mElemSize, n2*mElemSize);
144 mWritePtr.store((write_ptr + n1 + n2) & mSizeMask);
146 else
148 n1 = to_write;
149 memcpy(&(mBuffer[write_ptr*mElemSize]), src, n1*mElemSize);
150 mWritePtr.store((write_ptr + n1) & mSizeMask);
153 return to_write;
156 void RingBuffer::read_advance(size_t cnt)
158 size_t tmp = (mReadPtr.load() + cnt) & mSizeMask;
159 mReadPtr.store(tmp);
162 void RingBuffer::write_advance(size_t cnt)
164 size_t tmp = (mWritePtr.load() + cnt) & mSizeMask;
165 mWritePtr.store(tmp);
168 Array<RingBuffer::Data,2> RingBuffer::get_read_vector() const
170 Array<Data,2> vec;
171 size_t free_cnt;
172 size_t cnt2;
173 size_t w, r;
175 w = mWritePtr.load();
176 r = mReadPtr.load();
177 free_cnt = (mSize+w-r) & mSizeMask;
179 cnt2 = r + free_cnt;
180 if(cnt2 > mSize)
182 /* Two part vector: the rest of the buffer after the current write ptr,
183 * plus some from the start of the buffer. */
184 vec[0].buf = &(mBuffer[r*mElemSize]);
185 vec[0].len = mSize - r;
186 vec[1].buf = &(mBuffer[0]);
187 vec[1].len = cnt2 & mSizeMask;
189 else
191 /* Single part vector: just the rest of the buffer */
192 vec[0].buf = &(mBuffer[r*mElemSize]);
193 vec[0].len = free_cnt;
194 vec[1].buf = nullptr;
195 vec[1].len = 0;
198 return vec;
201 Array<RingBuffer::Data,2> RingBuffer::get_write_vector() const
203 Array<Data,2> vec;
204 size_t free_cnt;
205 size_t cnt2;
206 size_t w, r;
208 w = mWritePtr.load();
209 r = mReadPtr.load();
210 free_cnt = (mSize+r-w-1) & mSizeMask;
212 cnt2 = w + free_cnt;
213 if(cnt2 > mSize)
215 /* Two part vector: the rest of the buffer after the current write ptr,
216 * plus some from the start of the buffer. */
217 vec[0].buf = &(mBuffer[w*mElemSize]);
218 vec[0].len = mSize - w;
219 vec[1].buf = &(mBuffer[0]);
220 vec[1].len = cnt2 & mSizeMask;
222 else
224 vec[0].buf = &(mBuffer[w*mElemSize]);
225 vec[0].len = free_cnt;
226 vec[1].buf = nullptr;
227 vec[1].len = 0;
230 return vec;
233 } // namespace alure