26 #ifndef JAU_RINGBUFFER_HPP_
27 #define JAU_RINGBUFFER_HPP_
29 #include <type_traits>
33 #include <condition_variable>
48 #define _DEBUG_DUMP(...) { dump(stderr, __VA_ARGS__); }
49 #define _DEBUG_DUMP2(a, ...) { a.dump(stderr, __VA_ARGS__); }
50 #define _DEBUG_PRINT(...) { fprintf(stderr, __VA_ARGS__); }
52 #define _DEBUG_DUMP(...)
53 #define _DEBUG_DUMP2(a, ...)
54 #define _DEBUG_PRINT(...)
110 template <
typename Value_type,
typename NullValue_type,
typename Size_type,
111 bool use_memcpy = std::is_trivially_copyable_v<Value_type>,
112 bool use_memset = std::is_integral_v<Value_type> &&
sizeof(Value_type)==1 &&
113 std::is_integral_v<NullValue_type> &&
sizeof(NullValue_type)==1
138 mutable std::mutex syncWrite, syncMultiWrite;
139 std::condition_variable cvWrite;
142 mutable std::mutex syncRead, syncMultiRead;
143 std::condition_variable cvRead;
145 NullValue_type nullelem;
146 Size_type capacityPlusOne;
152 Value_type * newArray(
const Size_type count) noexcept {
154 Value_type *r =
new Value_type[count];
159 return new Value_type[count];
162 void freeArray(Value_type ** a) noexcept {
165 if(
nullptr == *a ) {
166 ABORT(
"ringbuffer::freeArray with nullptr");
172 template<
typename _DataType,
typename _NullType>
173 static void* memset_wrap(_DataType *block,
const _NullType& c,
size_t n,
174 std::enable_if_t< std::is_integral_v<_DataType> &&
sizeof(_DataType)==1 &&
175 std::is_integral_v<_NullType> &&
sizeof(_NullType)==1,
bool > =
true )
177 return ::memset(block, c, n);
179 template<
typename _DataType,
typename _NullType>
180 static void* memset_wrap(_DataType *block,
const _NullType& c,
size_t n,
181 std::enable_if_t< !std::is_integral_v<_DataType> ||
sizeof(_DataType)!=1 ||
182 !std::is_integral_v<_NullType> ||
sizeof(_NullType)!=1,
bool > =
true )
184 ABORT(
"MEMSET shall not be used");
194 void clearImpl() noexcept {
195 const Size_type size =
getSize();
198 memset_wrap(&array[0], nullelem, capacityPlusOne*
sizeof(Value_type));
202 Size_type localReadPos = readPos;
203 for(Size_type i=0; i<size; i++) {
204 localReadPos = (localReadPos + 1) % capacityPlusOne;
205 array[localReadPos] = nullelem;
207 if( writePos != localReadPos ) {
209 ABORT(
"copy segment error: this %s, readPos %d/%d; writePos %d",
toString().c_str(), readPos.
load(), localReadPos, writePos.
load());
211 readPos = localReadPos;
216 void cloneFrom(
const bool allocArrayAndCapacity,
const ringbuffer & source) noexcept {
217 if( allocArrayAndCapacity ) {
218 capacityPlusOne = source.capacityPlusOne;
219 if(
nullptr != array ) {
222 array = newArray(capacityPlusOne);
223 }
else if( capacityPlusOne != source.capacityPlusOne ) {
224 ABORT( (
"capacityPlusOne not equal: this "+
toString()+
", source "+source.toString() ).c_str() );
227 readPos = source.readPos.
load();
228 writePos = source.writePos.
load();
231 memcpy(
reinterpret_cast<void*
>(&array[0]),
232 reinterpret_cast<void*
>(
const_cast<Value_type*
>(&source.array[0])),
233 capacityPlusOne*
sizeof(Value_type));
235 const Size_type size =
getSize();
236 Size_type localWritePos = readPos;
237 for(Size_type i=0; i<size; i++) {
238 localWritePos = (localWritePos + 1) % capacityPlusOne;
239 array[localWritePos] = source.array[localWritePos];
241 if( writePos != localWritePos ) {
242 ABORT( (
"copy segment error: this "+
toString()+
", localWritePos "+
std::to_string(localWritePos)+
"; source "+source.toString()).c_str() );
247 void resetImpl(
const Value_type * copyFrom,
const Size_type copyFromCount) noexcept {
251 if(
nullptr != copyFrom && 0 < copyFromCount ) {
252 if( copyFromCount > capacityPlusOne-1 ) {
254 if(
nullptr != array ) {
257 capacityPlusOne = copyFromCount + 1;
258 array = newArray(capacityPlusOne);
263 memcpy(
reinterpret_cast<void*
>(&array[0]),
264 reinterpret_cast<void*
>(
const_cast<Value_type*
>(copyFrom)),
265 copyFromCount*
sizeof(Value_type));
266 readPos = capacityPlusOne - 1;
267 writePos = copyFromCount - 1;
269 Size_type localWritePos = writePos;
270 for(Size_type i=0; i<copyFromCount; i++) {
271 localWritePos = (localWritePos + 1) % capacityPlusOne;
272 array[localWritePos] = copyFrom[i];
274 writePos = localWritePos;
279 Value_type peekImpl(
const bool blocking,
const int timeoutMS,
bool& success) noexcept {
280 if( !std::is_copy_constructible_v<Value_type> ) {
281 ABORT(
"Value_type is not copy constructible");
284 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead);
286 const Size_type oldReadPos = readPos;
287 Size_type localReadPos = oldReadPos;
288 if( localReadPos == writePos ) {
290 std::unique_lock<std::mutex> lockWrite(syncWrite);
291 while( localReadPos == writePos ) {
292 if( 0 == timeoutMS ) {
293 cvWrite.wait(lockWrite);
295 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
296 std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS));
297 if( std::cv_status::timeout == s && localReadPos == writePos ) {
308 localReadPos = (localReadPos + 1) % capacityPlusOne;
309 Value_type r = array[localReadPos];
310 readPos = oldReadPos;
315 Value_type moveOutImpl(
const bool blocking,
const int timeoutMS,
bool& success) noexcept {
316 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead);
318 const Size_type oldReadPos = readPos;
319 Size_type localReadPos = oldReadPos;
320 if( localReadPos == writePos ) {
322 std::unique_lock<std::mutex> lockWrite(syncWrite);
323 while( localReadPos == writePos ) {
324 if( 0 == timeoutMS ) {
325 cvWrite.wait(lockWrite);
327 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
328 std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS));
329 if( std::cv_status::timeout == s && localReadPos == writePos ) {
340 localReadPos = (localReadPos + 1) % capacityPlusOne;
341 Value_type r = std::move( array[localReadPos] );
342 array[localReadPos] = nullelem;
344 std::unique_lock<std::mutex> lockRead(syncRead);
345 readPos = localReadPos;
352 Size_type moveOutImpl(Value_type *dest,
const Size_type dest_len,
const Size_type min_count_,
const bool blocking,
const int timeoutMS) noexcept {
353 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead);
355 const Size_type min_count = std::min(dest_len, min_count_);
356 Value_type *iter_out = dest;
358 if( min_count >= capacityPlusOne ) {
361 if( 0 == min_count ) {
365 const Size_type oldReadPos = readPos;
366 Size_type localReadPos = oldReadPos;
367 Size_type available =
getSize();
368 if( min_count > available ) {
370 std::unique_lock<std::mutex> lockWrite(syncWrite);
372 while( min_count > available ) {
373 if( 0 == timeoutMS ) {
374 cvWrite.wait(lockWrite);
377 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
378 std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS));
380 if( std::cv_status::timeout == s && min_count > available ) {
389 const Size_type count = std::min(dest_len, available);
398 Size_type togo_count = count;
399 const Size_type localWritePos = writePos;
400 if( localReadPos > localWritePos ) {
402 localReadPos = ( localReadPos + 1 ) % capacityPlusOne;
403 const Size_type tail_count = std::min(togo_count, capacityPlusOne - localReadPos);
405 memcpy(
reinterpret_cast<void*
>(iter_out),
406 reinterpret_cast<void*
>(&array[localReadPos]),
407 tail_count*
sizeof(Value_type));
409 memset_wrap(&array[localReadPos], nullelem, tail_count*
sizeof(Value_type));
411 for(Size_type i=0; i<tail_count; i++) {
412 array[localReadPos+i] = nullelem;
416 for(Size_type i=0; i<tail_count; i++) {
417 iter_out[i] = std::move( array[localReadPos+i] );
418 array[localReadPos+i] = nullelem;
421 localReadPos = ( localReadPos + tail_count - 1 ) % capacityPlusOne;
422 togo_count -= tail_count;
423 iter_out += tail_count;
425 if( togo_count > 0 ) {
427 localReadPos = ( localReadPos + 1 ) % capacityPlusOne;
429 memcpy(
reinterpret_cast<void*
>(iter_out),
430 reinterpret_cast<void*
>(&array[localReadPos]),
431 togo_count*
sizeof(Value_type));
433 memset_wrap(&array[localReadPos], nullelem, togo_count*
sizeof(Value_type));
435 for(Size_type i=0; i<togo_count; i++) {
436 array[localReadPos+i] = nullelem;
440 for(Size_type i=0; i<togo_count; i++) {
441 iter_out[i] = array[localReadPos+i];
442 array[localReadPos+i] = nullelem;
445 localReadPos = ( localReadPos + togo_count - 1 ) % capacityPlusOne;
449 std::unique_lock<std::mutex> locRead(syncRead);
450 readPos = localReadPos;
456 bool dropImpl (
const Size_type count,
const bool blocking,
const int timeoutMS) noexcept {
457 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead);
459 if( count >= capacityPlusOne ) {
466 const Size_type oldReadPos = readPos;
467 Size_type localReadPos = oldReadPos;
468 Size_type available =
getSize();
469 if( count > available ) {
471 std::unique_lock<std::mutex> lockWrite(syncWrite);
473 while( count > available ) {
474 if( 0 == timeoutMS ) {
475 cvWrite.wait(lockWrite);
478 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
479 std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS));
481 if( std::cv_status::timeout == s && count > available ) {
497 Size_type togo_count = count;
498 const Size_type localWritePos = writePos;
499 if( localReadPos > localWritePos ) {
501 localReadPos = ( localReadPos + 1 ) % capacityPlusOne;
502 const Size_type tail_count = std::min(togo_count, capacityPlusOne - localReadPos);
504 memset_wrap(&array[localReadPos], nullelem, tail_count*
sizeof(Value_type));
506 for(Size_type i=0; i<tail_count; i++) {
507 array[localReadPos+i] = nullelem;
510 localReadPos = ( localReadPos + tail_count - 1 ) % capacityPlusOne;
511 togo_count -= tail_count;
513 if( togo_count > 0 ) {
515 localReadPos = ( localReadPos + 1 ) % capacityPlusOne;
517 memset_wrap(&array[localReadPos], nullelem, togo_count*
sizeof(Value_type));
519 for(Size_type i=0; i<togo_count; i++) {
520 array[localReadPos+i] = nullelem;
523 localReadPos = ( localReadPos + togo_count - 1 ) % capacityPlusOne;
527 std::unique_lock<std::mutex> lockRead(syncRead);
528 readPos = localReadPos;
534 bool moveIntoImpl(Value_type &&e,
const bool blocking,
const int timeoutMS) noexcept {
535 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite);
537 Size_type localWritePos = writePos;
538 localWritePos = (localWritePos + 1) % capacityPlusOne;
539 if( localWritePos == readPos ) {
541 std::unique_lock<std::mutex> lockRead(syncRead);
542 while( localWritePos == readPos ) {
543 if( 0 == timeoutMS ) {
544 cvRead.wait(lockRead);
546 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
547 std::cv_status s = cvRead.wait_until(lockRead, t0 + std::chrono::milliseconds(timeoutMS));
548 if( std::cv_status::timeout == s && localWritePos == readPos ) {
557 array[localWritePos] = std::move(e);
559 std::unique_lock<std::mutex> lockWrite(syncWrite);
560 writePos = localWritePos;
561 cvWrite.notify_all();
566 bool copyIntoImpl(
const Value_type &e,
const bool blocking,
const int timeoutMS) noexcept {
567 if( !std::is_copy_constructible_v<Value_type> ) {
568 ABORT(
"Value_type is not copy constructible");
571 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite);
573 Size_type localWritePos = writePos;
574 localWritePos = (localWritePos + 1) % capacityPlusOne;
575 if( localWritePos == readPos ) {
577 std::unique_lock<std::mutex> lockRead(syncRead);
578 while( localWritePos == readPos ) {
579 if( 0 == timeoutMS ) {
580 cvRead.wait(lockRead);
582 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
583 std::cv_status s = cvRead.wait_until(lockRead, t0 + std::chrono::milliseconds(timeoutMS));
584 if( std::cv_status::timeout == s && localWritePos == readPos ) {
593 array[localWritePos] = e;
595 std::unique_lock<std::mutex> lockWrite(syncWrite);
596 writePos = localWritePos;
597 cvWrite.notify_all();
602 bool copyIntoImpl(
const Value_type *first,
const Value_type* last,
const bool blocking,
const int timeoutMS) noexcept {
603 if( !std::is_copy_constructible_v<Value_type> ) {
604 ABORT(
"Value_type is not copy constructible");
607 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite);
609 const Value_type *iter_in = first;
610 const Size_type total_count = last - first;
612 if( total_count >= capacityPlusOne ) {
615 if( 0 == total_count ) {
619 Size_type localWritePos = writePos;
621 if( total_count > available ) {
623 std::unique_lock<std::mutex> lockRead(syncRead);
625 while( total_count > available ) {
626 if( 0 == timeoutMS ) {
627 cvRead.wait(lockRead);
630 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
631 std::cv_status s = cvRead.wait_until(lockRead, t0 + std::chrono::milliseconds(timeoutMS));
633 if( std::cv_status::timeout == s && total_count > available ) {
649 Size_type togo_count = total_count;
650 const Size_type localReadPos = readPos;
651 if( localWritePos >= localReadPos ) {
653 localWritePos = ( localWritePos + 1 ) % capacityPlusOne;
654 const Size_type tail_count = std::min(togo_count, capacityPlusOne - localWritePos);
656 memcpy(
reinterpret_cast<void*
>(&array[localWritePos]),
657 reinterpret_cast<void*
>(
const_cast<Value_type*
>(iter_in)),
658 tail_count*
sizeof(Value_type));
660 for(Size_type i=0; i<tail_count; i++) {
661 array[localWritePos+i] = iter_in[i];
664 localWritePos = ( localWritePos + tail_count - 1 ) % capacityPlusOne;
665 togo_count -= tail_count;
666 iter_in += tail_count;
668 if( togo_count > 0 ) {
670 localWritePos = ( localWritePos + 1 ) % capacityPlusOne;
672 memcpy(
reinterpret_cast<void*
>(&array[localWritePos]),
673 reinterpret_cast<void*
>(
const_cast<Value_type*
>(iter_in)),
674 togo_count*
sizeof(Value_type));
676 for(Size_type i=0; i<togo_count; i++) {
677 array[localWritePos+i] = iter_in[i];
680 localWritePos = ( localWritePos + togo_count - 1 ) % capacityPlusOne;
684 std::unique_lock<std::mutex> lockRead(syncWrite);
685 writePos = localWritePos;
686 cvWrite.notify_all();
702 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead);
704 Size_type available =
getSize();
705 if( min_count > available ) {
706 std::unique_lock<std::mutex> lockWrite(syncWrite);
708 while( min_count > available ) {
709 if( 0 == timeoutMS ) {
710 cvWrite.wait(lockWrite);
713 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
714 std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS));
716 if( std::cv_status::timeout == s && min_count > available ) {
734 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite);
737 if( min_count > available ) {
738 std::unique_lock<std::mutex> lockRead(syncRead);
740 while( min_count > available ) {
741 if( 0 == timeoutMS ) {
742 cvRead.wait(lockRead);
745 std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
746 std::cv_status s = cvRead.wait_until(lockRead, t0 + std::chrono::milliseconds(timeoutMS));
748 if( std::cv_status::timeout == s && min_count > available ) {
759 const std::string es =
isEmpty() ?
", empty" :
"";
760 const std::string fs =
isFull() ?
", full" :
"";
766 void dump(FILE *stream, std::string prefix)
const noexcept {
768 fprintf(stream,
"%s %s {\n", prefix.c_str(),
toString().c_str());
769 for(Size_type i=0; i<capacityPlusOne; i++) {
772 fprintf(stream,
"}\n");
774 fprintf(stream,
"%s %s, array %p\n", prefix.c_str(),
toString().c_str(), array);
799 ringbuffer(
const NullValue_type& nullelem_,
const std::vector<Value_type> & copyFrom) noexcept
800 : nullelem(nullelem_), capacityPlusOne(copyFrom.size() + 1), array(newArray(capacityPlusOne)),
801 readPos(0), writePos(0)
803 resetImpl(copyFrom.data(), copyFrom.size());
812 ringbuffer(
const NullValue_type& nullelem_,
const Value_type * copyFrom,
const Size_type copyFromSize) noexcept
813 : nullelem(nullelem_), capacityPlusOne(copyFromSize + 1), array(newArray(capacityPlusOne)),
814 readPos(0), writePos(0)
816 resetImpl(copyFrom, copyFromSize);
839 : nullelem(nullelem_), capacityPlusOne(
capacity + 1), array(newArray(capacityPlusOne)),
840 readPos(0), writePos(0)
847 if(
nullptr != array ) {
853 : nullelem(_source.nullelem), capacityPlusOne(_source.capacityPlusOne), array(newArray(capacityPlusOne)),
854 readPos(0), writePos(0)
856 std::unique_lock<std::mutex> lockMultiReadS(_source.syncMultiRead, std::defer_lock);
857 std::unique_lock<std::mutex> lockMultiWriteS(_source.syncMultiWrite, std::defer_lock);
858 std::lock(lockMultiReadS, lockMultiWriteS);
859 cloneFrom(
false, _source);
865 std::unique_lock<std::mutex> lockMultiReadS(_source.syncMultiRead, std::defer_lock);
866 std::unique_lock<std::mutex> lockMultiWriteS(_source.syncMultiWrite, std::defer_lock);
867 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock);
868 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock);
869 std::lock(lockMultiReadS, lockMultiWriteS, lockMultiRead, lockMultiWrite);
871 if(
this == &_source ) {
874 nullelem = _source.nullelem;
876 if( capacityPlusOne != _source.capacityPlusOne ) {
877 cloneFrom(
true, _source);
880 cloneFrom(
false, _source);
891 Size_type
capacity() const noexcept {
return capacityPlusOne-1; }
901 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock);
902 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock);
903 std::lock(lockMultiRead, lockMultiWrite);
911 void reset(
const Value_type * copyFrom,
const Size_type copyFromCount) noexcept {
912 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock);
913 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock);
914 std::lock(lockMultiRead, lockMultiWrite);
915 resetImpl(copyFrom, copyFromCount);
918 void reset(
const std::vector<Value_type> & copyFrom) noexcept {
919 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock);
920 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock);
921 std::lock(lockMultiRead, lockMultiWrite);
922 resetImpl(copyFrom.data(), copyFrom.size());
927 const Size_type R = readPos;
928 const Size_type W = writePos;
931 return W >= R ? W - R : capacityPlusOne - R + W;
938 bool isEmpty() const noexcept {
return writePos == readPos; }
941 bool isFull() const noexcept {
return ( writePos + 1 ) % capacityPlusOne == readPos; ; }
949 return peekImpl(
false, 0, success);
957 bool peek(Value_type& result) noexcept {
959 result = peekImpl(
false, 0, success);
974 return peekImpl(
true, timeoutMS, success);
987 bool peekBlocking(Value_type& result,
const int timeoutMS=0) noexcept {
989 result = peekImpl(
true, timeoutMS, success);
1006 return moveOutImpl(
false, 0, success);
1021 bool get(Value_type& result) noexcept {
1023 result = moveOutImpl(
false, 0, success);
1042 return moveOutImpl(
true, timeoutMS, success);
1059 bool getBlocking(Value_type& result,
const int timeoutMS=0) noexcept {
1061 result = moveOutImpl(
true, timeoutMS, success);
1079 Size_type
get(Value_type *dest,
const Size_type dest_len,
const Size_type min_count) noexcept {
1080 return moveOutImpl(dest, dest_len, min_count,
false, 0);
1100 Size_type
getBlocking(Value_type *dest,
const Size_type dest_len,
const Size_type min_count,
const int timeoutMS=0) noexcept {
1101 return moveOutImpl(dest, dest_len, min_count,
true, timeoutMS);
1112 bool drop(
const Size_type count) noexcept {
1113 return dropImpl(count,
false, 0);
1127 return dropImpl(count,
true, timeoutMS);
1140 bool put(Value_type && e) noexcept {
1141 return moveIntoImpl(std::move(e),
false, 0);
1154 return moveIntoImpl(std::move(e),
true, timeoutMS);
1167 bool put(
const Value_type & e) noexcept {
1168 return copyIntoImpl(e,
false, 0);
1180 bool putBlocking(
const Value_type & e,
const int timeoutMS=0) noexcept {
1181 return copyIntoImpl(e,
true, timeoutMS);
1196 bool put(
const Value_type *first,
const Value_type* last) noexcept {
1197 return copyIntoImpl(first, last,
false, 0);
1212 bool putBlocking(
const Value_type *first,
const Value_type* last,
const int timeoutMS=0) noexcept {
1213 return copyIntoImpl(first, last,
true, timeoutMS);
1223 std::unique_lock<std::mutex> lockMultiRead(syncMultiRead, std::defer_lock);
1224 std::unique_lock<std::mutex> lockMultiWrite(syncMultiWrite, std::defer_lock);
1225 std::lock(lockMultiRead, lockMultiWrite);
1226 const Size_type size =
getSize();
1228 if( capacityPlusOne == newCapacity+1 ) {
1231 if( size > newCapacity ) {
1236 Size_type oldCapacityPlusOne = capacityPlusOne;
1237 Value_type * oldArray = array;
1238 Size_type oldReadPos = readPos;
1241 capacityPlusOne = newCapacity + 1;
1242 array = newArray(capacityPlusOne);
1247 if(
nullptr != oldArray && 0 < size ) {
1248 Size_type localWritePos = writePos;
1249 for(Size_type i=0; i<size; i++) {
1250 localWritePos = (localWritePos + 1) % capacityPlusOne;
1251 oldReadPos = (oldReadPos + 1) % oldCapacityPlusOne;
1252 array[localWritePos] = std::move( oldArray[oldReadPos] );
1254 writePos = localWritePos;
1256 freeArray(&oldArray);