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"
34 auto RingBuffer::Create(std::size_t sz
, std::size_t elem_sz
, bool limit_writes
) -> RingBufferPtr
36 std::size_t power_of_two
{0u};
39 power_of_two
= sz
- 1;
40 power_of_two
|= power_of_two
>>1;
41 power_of_two
|= power_of_two
>>2;
42 power_of_two
|= power_of_two
>>4;
43 power_of_two
|= power_of_two
>>8;
44 power_of_two
|= power_of_two
>>16;
45 if constexpr(sizeof(size_t) > sizeof(uint32_t))
46 power_of_two
|= power_of_two
>>32;
49 if(power_of_two
< sz
|| power_of_two
> std::numeric_limits
<std::size_t>::max()>>1
50 || power_of_two
> std::numeric_limits
<std::size_t>::max()/elem_sz
)
51 throw std::overflow_error
{"Ring buffer size overflow"};
53 const std::size_t bufbytes
{power_of_two
* elem_sz
};
54 RingBufferPtr rb
{new(FamCount(bufbytes
)) RingBuffer
{limit_writes
? sz
: power_of_two
,
55 power_of_two
-1, elem_sz
, bufbytes
}};
60 void RingBuffer::reset() noexcept
62 mWriteCount
.store(0, std::memory_order_relaxed
);
63 mReadCount
.store(0, std::memory_order_relaxed
);
64 std::fill_n(mBuffer
.begin(), (mSizeMask
+1)*mElemSize
, std::byte
{});
68 auto RingBuffer::read(void *dest
, std::size_t count
) noexcept
-> std::size_t
70 const std::size_t w
{mWriteCount
.load(std::memory_order_acquire
)};
71 const std::size_t r
{mReadCount
.load(std::memory_order_relaxed
)};
72 const std::size_t readable
{w
- r
};
73 if(readable
== 0) return 0;
75 const std::size_t to_read
{std::min(count
, readable
)};
76 const std::size_t read_idx
{r
& mSizeMask
};
78 const std::size_t rdend
{read_idx
+ to_read
};
79 const auto [n1
, n2
] = (rdend
<= mSizeMask
+1) ? std::make_tuple(to_read
, 0_uz
)
80 : std::make_tuple(mSizeMask
+1 - read_idx
, rdend
&mSizeMask
);
82 auto dstbytes
= al::span
{static_cast<std::byte
*>(dest
), count
*mElemSize
};
83 auto outiter
= std::copy_n(mBuffer
.begin() + ptrdiff_t(read_idx
*mElemSize
), n1
*mElemSize
,
86 std::copy_n(mBuffer
.begin(), n2
*mElemSize
, outiter
);
87 mReadCount
.store(r
+n1
+n2
, std::memory_order_release
);
91 auto RingBuffer::peek(void *dest
, std::size_t count
) const noexcept
-> std::size_t
93 const std::size_t w
{mWriteCount
.load(std::memory_order_acquire
)};
94 const std::size_t r
{mReadCount
.load(std::memory_order_relaxed
)};
95 const std::size_t readable
{w
- r
};
96 if(readable
== 0) return 0;
98 const std::size_t to_read
{std::min(count
, readable
)};
99 const std::size_t read_idx
{r
& mSizeMask
};
101 const std::size_t rdend
{read_idx
+ to_read
};
102 const auto [n1
, n2
] = (rdend
<= mSizeMask
+1) ? std::make_tuple(to_read
, 0_uz
)
103 : std::make_tuple(mSizeMask
+1 - read_idx
, rdend
&mSizeMask
);
105 auto dstbytes
= al::span
{static_cast<std::byte
*>(dest
), count
*mElemSize
};
106 auto outiter
= std::copy_n(mBuffer
.begin() + ptrdiff_t(read_idx
*mElemSize
), n1
*mElemSize
,
109 std::copy_n(mBuffer
.begin(), n2
*mElemSize
, outiter
);
113 auto RingBuffer::write(const void *src
, std::size_t count
) noexcept
-> std::size_t
115 const std::size_t w
{mWriteCount
.load(std::memory_order_relaxed
)};
116 const std::size_t r
{mReadCount
.load(std::memory_order_acquire
)};
117 const std::size_t writable
{mWriteSize
- (w
- r
)};
118 if(writable
== 0) return 0;
120 const std::size_t to_write
{std::min(count
, writable
)};
121 const std::size_t write_idx
{w
& mSizeMask
};
123 const std::size_t wrend
{write_idx
+ to_write
};
124 const auto [n1
, n2
] = (wrend
<= mSizeMask
+1) ? std::make_tuple(to_write
, 0_uz
)
125 : std::make_tuple(mSizeMask
+1 - write_idx
, wrend
&mSizeMask
);
127 auto srcbytes
= al::span
{static_cast<const std::byte
*>(src
), count
*mElemSize
};
128 std::copy_n(srcbytes
.cbegin(), n1
*mElemSize
, mBuffer
.begin() + ptrdiff_t(write_idx
*mElemSize
));
130 std::copy_n(srcbytes
.cbegin() + ptrdiff_t(n1
*mElemSize
), n2
*mElemSize
, mBuffer
.begin());
131 mWriteCount
.store(w
+n1
+n2
, std::memory_order_release
);
136 auto RingBuffer::getReadVector() noexcept
-> DataPair
138 const std::size_t w
{mWriteCount
.load(std::memory_order_acquire
)};
139 const std::size_t r
{mReadCount
.load(std::memory_order_relaxed
)};
140 const std::size_t readable
{w
- r
};
141 const std::size_t read_idx
{r
& mSizeMask
};
143 const std::size_t rdend
{read_idx
+ readable
};
144 if(rdend
> mSizeMask
+1)
146 /* Two part vector: the rest of the buffer after the current read ptr,
147 * plus some from the start of the buffer.
149 return DataPair
{{mBuffer
.data() + read_idx
*mElemSize
, mSizeMask
+1 - read_idx
},
150 {mBuffer
.data(), rdend
&mSizeMask
}};
152 return DataPair
{{mBuffer
.data() + read_idx
*mElemSize
, readable
}, {}};
155 auto RingBuffer::getWriteVector() noexcept
-> DataPair
157 const std::size_t w
{mWriteCount
.load(std::memory_order_relaxed
)};
158 const std::size_t r
{mReadCount
.load(std::memory_order_acquire
)};
159 const std::size_t writable
{mWriteSize
- (w
- r
)};
160 const std::size_t write_idx
{w
& mSizeMask
};
162 const std::size_t wrend
{write_idx
+ writable
};
163 if(wrend
> mSizeMask
+1)
165 /* Two part vector: the rest of the buffer after the current write ptr,
166 * plus some from the start of the buffer.
168 return DataPair
{{mBuffer
.data() + write_idx
*mElemSize
, mSizeMask
+1 - write_idx
},
169 {mBuffer
.data(), wrend
&mSizeMask
}};
171 return DataPair
{{mBuffer
.data() + write_idx
*mElemSize
, writable
}, {}};