2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
23 #include "ringbuffer.h"
31 #include "alnumeric.h"
35 auto RingBuffer::Create(std::size_t sz
, std::size_t elem_sz
, bool limit_writes
) -> RingBufferPtr
37 std::size_t power_of_two
{0u};
40 power_of_two
= sz
- 1;
41 power_of_two
|= power_of_two
>>1;
42 power_of_two
|= power_of_two
>>2;
43 power_of_two
|= power_of_two
>>4;
44 power_of_two
|= power_of_two
>>8;
45 power_of_two
|= power_of_two
>>16;
46 if constexpr(sizeof(size_t) > sizeof(uint32_t))
47 power_of_two
|= power_of_two
>>32;
50 if(power_of_two
< sz
|| power_of_two
> std::numeric_limits
<std::size_t>::max()>>1
51 || power_of_two
> std::numeric_limits
<std::size_t>::max()/elem_sz
)
52 throw std::overflow_error
{"Ring buffer size overflow"};
54 const std::size_t bufbytes
{power_of_two
* elem_sz
};
55 RingBufferPtr rb
{new(FamCount(bufbytes
)) RingBuffer
{limit_writes
? sz
: power_of_two
,
56 power_of_two
-1, elem_sz
, bufbytes
}};
61 void RingBuffer::reset() noexcept
63 mWriteCount
.store(0, std::memory_order_relaxed
);
64 mReadCount
.store(0, std::memory_order_relaxed
);
65 std::fill_n(mBuffer
.begin(), (mSizeMask
+1)*mElemSize
, std::byte
{});
69 auto RingBuffer::read(void *dest
, std::size_t count
) noexcept
-> std::size_t
71 const std::size_t w
{mWriteCount
.load(std::memory_order_acquire
)};
72 const std::size_t r
{mReadCount
.load(std::memory_order_relaxed
)};
73 const std::size_t readable
{w
- r
};
74 if(readable
== 0) return 0;
76 const std::size_t to_read
{std::min(count
, readable
)};
77 const std::size_t read_idx
{r
& mSizeMask
};
79 const std::size_t rdend
{read_idx
+ to_read
};
80 const auto [n1
, n2
] = (rdend
<= mSizeMask
+1) ? std::array
{to_read
, 0_uz
}
81 : std::array
{mSizeMask
+1 - read_idx
, rdend
&mSizeMask
};
83 auto dstbytes
= al::span
{static_cast<std::byte
*>(dest
), count
*mElemSize
};
84 auto outiter
= std::copy_n(mBuffer
.begin() + ptrdiff_t(read_idx
*mElemSize
), n1
*mElemSize
,
87 std::copy_n(mBuffer
.begin(), n2
*mElemSize
, outiter
);
88 mReadCount
.store(r
+n1
+n2
, std::memory_order_release
);
92 auto RingBuffer::peek(void *dest
, std::size_t count
) const noexcept
-> std::size_t
94 const std::size_t w
{mWriteCount
.load(std::memory_order_acquire
)};
95 const std::size_t r
{mReadCount
.load(std::memory_order_relaxed
)};
96 const std::size_t readable
{w
- r
};
97 if(readable
== 0) return 0;
99 const std::size_t to_read
{std::min(count
, readable
)};
100 const std::size_t read_idx
{r
& mSizeMask
};
102 const std::size_t rdend
{read_idx
+ to_read
};
103 const auto [n1
, n2
] = (rdend
<= mSizeMask
+1) ? std::array
{to_read
, 0_uz
}
104 : std::array
{mSizeMask
+1 - read_idx
, rdend
&mSizeMask
};
106 auto dstbytes
= al::span
{static_cast<std::byte
*>(dest
), count
*mElemSize
};
107 auto outiter
= std::copy_n(mBuffer
.begin() + ptrdiff_t(read_idx
*mElemSize
), n1
*mElemSize
,
110 std::copy_n(mBuffer
.begin(), n2
*mElemSize
, outiter
);
114 auto RingBuffer::write(const void *src
, std::size_t count
) noexcept
-> std::size_t
116 const std::size_t w
{mWriteCount
.load(std::memory_order_relaxed
)};
117 const std::size_t r
{mReadCount
.load(std::memory_order_acquire
)};
118 const std::size_t writable
{mWriteSize
- (w
- r
)};
119 if(writable
== 0) return 0;
121 const std::size_t to_write
{std::min(count
, writable
)};
122 const std::size_t write_idx
{w
& mSizeMask
};
124 const std::size_t wrend
{write_idx
+ to_write
};
125 const auto [n1
, n2
] = (wrend
<= mSizeMask
+1) ? std::array
{to_write
, 0_uz
}
126 : std::array
{mSizeMask
+1 - write_idx
, wrend
&mSizeMask
};
128 auto srcbytes
= al::span
{static_cast<const std::byte
*>(src
), count
*mElemSize
};
129 std::copy_n(srcbytes
.cbegin(), n1
*mElemSize
, mBuffer
.begin() + ptrdiff_t(write_idx
*mElemSize
));
131 std::copy_n(srcbytes
.cbegin() + ptrdiff_t(n1
*mElemSize
), n2
*mElemSize
, mBuffer
.begin());
132 mWriteCount
.store(w
+n1
+n2
, std::memory_order_release
);
137 auto RingBuffer::getReadVector() noexcept
-> DataPair
139 const std::size_t w
{mWriteCount
.load(std::memory_order_acquire
)};
140 const std::size_t r
{mReadCount
.load(std::memory_order_relaxed
)};
141 const std::size_t readable
{w
- r
};
142 const std::size_t read_idx
{r
& mSizeMask
};
144 const std::size_t rdend
{read_idx
+ readable
};
145 if(rdend
> mSizeMask
+1)
147 /* Two part vector: the rest of the buffer after the current read ptr,
148 * plus some from the start of the buffer.
150 return DataPair
{{{mBuffer
.data() + read_idx
*mElemSize
, mSizeMask
+1 - read_idx
},
151 {mBuffer
.data(), rdend
&mSizeMask
}}};
153 return DataPair
{{{mBuffer
.data() + read_idx
*mElemSize
, readable
}, {}}};
156 auto RingBuffer::getWriteVector() noexcept
-> DataPair
158 const std::size_t w
{mWriteCount
.load(std::memory_order_relaxed
)};
159 const std::size_t r
{mReadCount
.load(std::memory_order_acquire
)};
160 const std::size_t writable
{mWriteSize
- (w
- r
)};
161 const std::size_t write_idx
{w
& mSizeMask
};
163 const std::size_t wrend
{write_idx
+ writable
};
164 if(wrend
> mSizeMask
+1)
166 /* Two part vector: the rest of the buffer after the current write ptr,
167 * plus some from the start of the buffer.
169 return DataPair
{{{mBuffer
.data() + write_idx
*mElemSize
, mSizeMask
+1 - write_idx
},
170 {mBuffer
.data(), wrend
&mSizeMask
}}};
172 return DataPair
{{{mBuffer
.data() + write_idx
*mElemSize
, writable
}, {}}};