diff options
-rw-r--r-- | include/jau/ringbuffer.hpp | 379 | ||||
-rw-r--r-- | test/test_lfringbuffer01.cpp | 54 | ||||
-rw-r--r-- | test/test_lfringbuffer02.cpp | 53 | ||||
-rw-r--r-- | test/test_lfringbuffer03.cpp | 53 | ||||
-rw-r--r-- | test/test_lfringbuffer11.cpp | 10 | ||||
-rw-r--r-- | test/test_lfringbuffer12.cpp | 10 | ||||
-rw-r--r-- | test/test_lfringbuffer13.cpp | 12 |
7 files changed, 225 insertions, 346 deletions
diff --git a/include/jau/ringbuffer.hpp b/include/jau/ringbuffer.hpp index 720a544..1a423d6 100644 --- a/include/jau/ringbuffer.hpp +++ b/include/jau/ringbuffer.hpp @@ -100,39 +100,40 @@ namespace jau { * - Sequentially Consistent (SC) ordering or SC-DRF (data race free) <https://en.cppreference.com/w/cpp/atomic/memory_order#Sequentially-consistent_ordering> * - std::memory_order <https://en.cppreference.com/w/cpp/atomic/memory_order> * - * We would like to pass `NullValue_type nullelem` as a non-type template parameter of type `NullValue_type`, a potential Class. - * However, this is only allowed in C++20 and we use C++17 for now. - * Hence we have to pass `NullValue_type nullelem` in the constructor. - * * @anchor ringbuffer_ntt_params - * ### Non-Type Template Parameter controlling Value_type memory + * ### Non-Type Template Parameter (NTTP) controlling Value_type memory * See @ref darray_ntt_params. + * * #### `use_memmove` * `use_memmove` see @ref darray_memmove. - * #### `use_secmem` - * `use_secmem` see @ref darray_secmem. + * * #### `use_memcpy` * `use_memcpy` has more strict requirements than `use_memmove`, * i.e. strictly relies on Value_type being `std::is_trivially_copyable_v<Value_type>`. - * #### `use_memset` - * `use_memset` has strict requirements - * and strictly relies on Value_type and NullValue_type being an integral of size 1 byte. + * + * It allows to merely use memory operations w/o the need for constructor or destructor. + * + * See [Trivial destructor](https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor) + * being key requirement to [TriviallyCopyable](https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable). + * > A trivial destructor is a destructor that performs no action. + * > Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage. + * > All data types compatible with the C language (POD types) are trivially destructible.` + * + * #### `use_secmem` + * `use_secmem` see @ref darray_secmem. * * @see @ref darray_ntt_params * @see jau::sc_atomic_critical */ -template <typename Value_type, typename NullValue_type, typename Size_type, +template <typename Value_type, typename Size_type, bool use_memmove = std::is_trivially_copyable_v<Value_type> || is_container_memmove_compliant_v<Value_type>, bool use_memcpy = std::is_trivially_copyable_v<Value_type>, - bool use_memset = std::is_integral_v<Value_type> && sizeof(Value_type)==1 && - std::is_integral_v<NullValue_type> && sizeof(NullValue_type)==1, bool use_secmem = is_enforcing_secmem_v<Value_type> > class ringbuffer { public: constexpr static const bool uses_memmove = use_memmove; constexpr static const bool uses_memcpy = use_memcpy; - constexpr static const bool uses_memset = use_memset; constexpr static const bool uses_secmem = use_secmem; // typedefs' for C++ named requirements: Container (ex iterator) @@ -148,10 +149,14 @@ class ringbuffer { typedef jau::callocator<Value_type> allocator_type; private: + constexpr static const bool is_integral = std::is_integral_v<Value_type>; + typedef std::remove_const_t<Value_type> value_type_mutable; /** Required to create and move immutable elements, aka const */ typedef value_type_mutable* pointer_mutable; + static constexpr void* voidptr_cast(const_pointer p) { return reinterpret_cast<void*>( const_cast<pointer_mutable>( p ) ); } + /** SC atomic integral scalar jau::nsize_t. Memory-Model (MM) guaranteed sequential consistency (SC) between acquire (read) and release (write) */ typedef ordered_atomic<Size_type, std::memory_order::memory_order_seq_cst> sc_atomic_Size_type; @@ -168,31 +173,11 @@ class ringbuffer { allocator_type alloc_inst; - /* const */ NullValue_type nullelem; // not final due to assignment operation /* const */ Size_type capacityPlusOne; // not final due to grow /* const */ Value_type * array; // Synchronized due to MM's data-race-free SC (SC-DRF) between [atomic] acquire/release sc_atomic_Size_type readPos; // Memory-Model (MM) guaranteed sequential consistency (SC) between acquire (read) and release (write) sc_atomic_Size_type writePos; // ditto - template<typename _DataType, typename _NullType> - constexpr static void* memset_wrap(_DataType *block, const _NullType& c, size_t n, - std::enable_if_t< std::is_integral_v<_DataType> && sizeof(_DataType)==1 && - std::is_integral_v<_NullType> && sizeof(_NullType)==1, bool > = true ) - { - return ::memset(block, c, n); - } - template<typename _DataType, typename _NullType> - constexpr static void* memset_wrap(_DataType *block, const _NullType& c, size_t n, - std::enable_if_t< !std::is_integral_v<_DataType> || sizeof(_DataType)!=1 || - !std::is_integral_v<_NullType> || sizeof(_NullType)!=1, bool > = true ) - { - ABORT("MEMSET shall not be used"); - (void)block; - (void)c; - (void)n; - return nullptr; - } - constexpr Value_type * newArray(const Size_type count) noexcept { if( 0 < count ) { value_type * m = alloc_inst.allocate(count); @@ -225,13 +210,13 @@ class ringbuffer { constexpr void dtor_one(const Size_type pos) { ( array + pos )->~value_type(); // placement new -> manual destruction! if constexpr ( uses_secmem ) { - explicit_bzero((void*)(array + pos), sizeof(value_type)); + ::explicit_bzero(voidptr_cast(array + pos), sizeof(value_type)); } } constexpr void dtor_one(pointer elem) { ( elem )->~value_type(); // placement new -> manual destruction! if constexpr ( uses_secmem ) { - explicit_bzero((void*)(elem), sizeof(value_type)); + ::explicit_bzero(voidptr_cast(elem), sizeof(value_type)); } } @@ -241,8 +226,10 @@ class ringbuffer { constexpr void clearImpl() noexcept { const Size_type size_ = size(); if( 0 < size_ ) { - if constexpr ( uses_memset ) { - memset_wrap(&array[0], nullelem, capacityPlusOne*sizeof(Value_type)); + if constexpr ( use_memcpy ) { + if constexpr ( uses_secmem ) { + ::explicit_bzero(voidptr_cast(&array[0]), capacityPlusOne*sizeof(Value_type)); + } readPos = 0; writePos = 0; } else { @@ -278,8 +265,8 @@ class ringbuffer { writePos = source.writePos.load(); if constexpr ( uses_memcpy ) { - ::memcpy(reinterpret_cast<void*>(&array[0]), - reinterpret_cast<void*>(const_cast<Value_type*>(&source.array[0])), + ::memcpy(voidptr_cast(&array[0]), + &source.array[0], capacityPlusOne*sizeof(Value_type)); } else { const Size_type size_ = size(); @@ -311,8 +298,8 @@ class ringbuffer { clearImpl(); } if constexpr ( uses_memcpy ) { - ::memcpy(reinterpret_cast<void*>(&array[0]), - reinterpret_cast<void*>(const_cast<Value_type*>(copyFrom)), + ::memcpy(voidptr_cast(&array[0]), + copyFrom, copyFromCount*sizeof(Value_type)); readPos = capacityPlusOne - 1; // last read-pos writePos = copyFromCount - 1; // last write-pos @@ -329,10 +316,10 @@ class ringbuffer { } } - Value_type peekImpl(const bool blocking, const int timeoutMS, bool& success) noexcept { + bool peekImpl(Value_type& dest, const bool blocking, const int timeoutMS) noexcept { if( !std::is_copy_constructible_v<Value_type> ) { ABORT("Value_type is not copy constructible"); - return nullelem; + return false; } std::unique_lock<std::mutex> lockMultiRead(syncMultiRead); // acquire syncMultiRead, _not_ sync'ing w/ putImpl @@ -348,24 +335,28 @@ class ringbuffer { std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS)); if( std::cv_status::timeout == s && localReadPos == writePos ) { - success = false; - return nullelem; + return false; } } } } else { - success = false; - return nullelem; + return false; } } localReadPos = (localReadPos + 1) % capacityPlusOne; - Value_type r = array[localReadPos]; + if constexpr ( !is_integral && uses_memmove ) { + // must not dtor after memcpy; memcpy OK, not overlapping + ::memcpy(voidptr_cast(&dest), + &array[localReadPos], + sizeof(Value_type)); + } else { + dest = array[localReadPos]; + } readPos = oldReadPos; // SC-DRF release atomic readPos (complete acquire-release even @ peek) - success = true; - return r; + return true; } - Value_type moveOutImpl(const bool blocking, const int timeoutMS, bool& success) noexcept { + bool moveOutImpl(Value_type& dest, const bool blocking, const int timeoutMS) noexcept { std::unique_lock<std::mutex> lockMultiRead(syncMultiRead); // acquire syncMultiRead, _not_ sync'ing w/ putImpl const Size_type oldReadPos = readPos; // SC-DRF acquire atomic readPos, sync'ing with putImpl @@ -380,26 +371,38 @@ class ringbuffer { std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); std::cv_status s = cvWrite.wait_until(lockWrite, t0 + std::chrono::milliseconds(timeoutMS)); if( std::cv_status::timeout == s && localReadPos == writePos ) { - success = false; - return nullelem; + return false; } } } } else { - success = false; - return nullelem; + return false; } } localReadPos = (localReadPos + 1) % capacityPlusOne; - Value_type r( std::move( array[localReadPos] ) ); // uses_memmove: Possible, but just 1 object and Value_type storage still init - dtor_one( localReadPos ); + if constexpr ( is_integral ) { + dest = array[localReadPos]; + if constexpr ( uses_secmem ) { + ::explicit_bzero(voidptr_cast(&array[localReadPos]), sizeof(Value_type)); + } + } else if constexpr ( uses_memmove ) { + // must not dtor after memcpy; memcpy OK, not overlapping + ::memcpy(voidptr_cast(&dest), + &array[localReadPos], + sizeof(Value_type)); + if constexpr ( uses_secmem ) { + ::explicit_bzero(voidptr_cast(&array[localReadPos]), sizeof(Value_type)); + } + } else { + dest = std::move( array[localReadPos] ); + dtor_one( localReadPos ); + } { std::unique_lock<std::mutex> lockRead(syncRead); // SC-DRF w/ putImpl via same lock readPos = localReadPos; // SC-DRF release atomic readPos cvRead.notify_all(); // notify waiting putter } - success = true; - return r; + return true; } Size_type moveOutImpl(Value_type *dest, const Size_type dest_len, const Size_type min_count_, const bool blocking, const int timeoutMS) noexcept { @@ -455,12 +458,12 @@ class ringbuffer { localReadPos = ( localReadPos + 1 ) % capacityPlusOne; // next-read-pos const Size_type tail_count = std::min(togo_count, capacityPlusOne - localReadPos); if constexpr ( uses_memmove ) { - // must not dtor after memmove - ::memmove(reinterpret_cast<void*>(iter_out), - reinterpret_cast<void*>(&array[localReadPos]), - tail_count*sizeof(Value_type)); + // must not dtor after memcpy; memcpy OK, not overlapping + ::memcpy(voidptr_cast(iter_out), + &array[localReadPos], + tail_count*sizeof(Value_type)); if constexpr ( uses_secmem ) { - explicit_bzero(&array[localReadPos], tail_count*sizeof(Value_type)); + ::explicit_bzero(voidptr_cast(&array[localReadPos]), tail_count*sizeof(Value_type)); } } else { for(Size_type i=0; i<tail_count; i++) { @@ -476,12 +479,12 @@ class ringbuffer { // we have a head localReadPos = ( localReadPos + 1 ) % capacityPlusOne; // next-read-pos if constexpr ( uses_memmove ) { - // must not dtor after memmove - ::memmove(reinterpret_cast<void*>(iter_out), - reinterpret_cast<void*>(&array[localReadPos]), - togo_count*sizeof(Value_type)); + // must not dtor after memcpy; memcpy OK, not overlapping + ::memcpy(voidptr_cast(iter_out), + &array[localReadPos], + togo_count*sizeof(Value_type)); if constexpr ( uses_secmem ) { - explicit_bzero(&array[localReadPos], togo_count*sizeof(Value_type)); + ::explicit_bzero(voidptr_cast(&array[localReadPos]), togo_count*sizeof(Value_type)); } } else { for(Size_type i=0; i<togo_count; i++) { @@ -546,8 +549,10 @@ class ringbuffer { // we have a tail localReadPos = ( localReadPos + 1 ) % capacityPlusOne; // next-read-pos const Size_type tail_count = std::min(togo_count, capacityPlusOne - localReadPos); - if constexpr ( uses_memset ) { - memset_wrap(&array[localReadPos], nullelem, tail_count*sizeof(Value_type)); + if constexpr ( uses_memcpy ) { + if constexpr ( uses_secmem ) { + ::explicit_bzero(voidptr_cast(&array[localReadPos]), tail_count*sizeof(Value_type)); + } } else { for(Size_type i=0; i<tail_count; i++) { dtor_one( localReadPos+i ); @@ -559,8 +564,10 @@ class ringbuffer { if( togo_count > 0 ) { // we have a head localReadPos = ( localReadPos + 1 ) % capacityPlusOne; // next-read-pos - if constexpr ( uses_memset ) { - memset_wrap(&array[localReadPos], nullelem, togo_count*sizeof(Value_type)); + if constexpr ( uses_memcpy ) { + if constexpr ( uses_secmem ) { + ::explicit_bzero(voidptr_cast(&array[localReadPos]), togo_count*sizeof(Value_type)); + } } else { for(Size_type i=0; i<togo_count; i++) { dtor_one( localReadPos+i ); @@ -635,7 +642,15 @@ class ringbuffer { return false; } } - new (const_cast<pointer_mutable>(array + localWritePos)) value_type( e ); // placement new + if constexpr ( is_integral ) { + array[localWritePos] = e; + } else if constexpr ( uses_memcpy ) { + ::memcpy(voidptr_cast(&array[localWritePos]), + &e, + sizeof(Value_type)); + } else { + new (const_cast<pointer_mutable>(array + localWritePos)) value_type( e ); // placement new + } { std::unique_lock<std::mutex> lockWrite(syncWrite); // SC-DRF w/ getImpl via same lock writePos = localWritePos; // SC-DRF release atomic writePos @@ -698,8 +713,8 @@ class ringbuffer { localWritePos = ( localWritePos + 1 ) % capacityPlusOne; // next-write-pos const Size_type tail_count = std::min(togo_count, capacityPlusOne - localWritePos); if constexpr ( uses_memcpy ) { - ::memcpy(reinterpret_cast<void*>(&array[localWritePos]), - reinterpret_cast<void*>(const_cast<Value_type*>(iter_in)), + ::memcpy(voidptr_cast(&array[localWritePos]), + iter_in, tail_count*sizeof(Value_type)); } else { for(Size_type i=0; i<tail_count; i++) { @@ -714,8 +729,8 @@ class ringbuffer { // we have a head localWritePos = ( localWritePos + 1 ) % capacityPlusOne; // next-write-pos if constexpr ( uses_memcpy ) { - memcpy(reinterpret_cast<void*>(&array[localWritePos]), - reinterpret_cast<void*>(const_cast<Value_type*>(iter_in)), + memcpy(voidptr_cast(&array[localWritePos]), + iter_in, togo_count*sizeof(Value_type)); } else { for(Size_type i=0; i<togo_count; i++) { @@ -816,7 +831,6 @@ class ringbuffer { ", size "+std::to_string(size())+" / "+std::to_string(capacityPlusOne-1)+ ", uses[mmove "+std::to_string(uses_memmove)+ ", mcpy "+std::to_string(uses_memcpy)+ - ", mset "+std::to_string(uses_memset)+ ", smem "+std::to_string(uses_secmem)+ "]]"); return res; @@ -839,12 +853,11 @@ class ringbuffer { * Implementation will allocate an internal array with size of array <code>copyFrom</code> <i>plus one</i>, * and copy all elements from array <code>copyFrom</code> into the internal array. * </p> - * @param nullelem The `null` value used to zero removed elements on get*(..) and clear() * @param copyFrom mandatory source array determining ring buffer's net {@link #capacity()} and initial content. * @throws IllegalArgumentException if <code>copyFrom</code> is <code>nullptr</code> */ - ringbuffer(const NullValue_type& nullelem_, const std::vector<Value_type> & copyFrom) noexcept - : nullelem(nullelem_), capacityPlusOne(copyFrom.size() + 1), array(newArray(capacityPlusOne)), + ringbuffer(const std::vector<Value_type> & copyFrom) noexcept + : capacityPlusOne(copyFrom.size() + 1), array(newArray(capacityPlusOne)), readPos(0), writePos(0) { resetImpl(copyFrom.data(), copyFrom.size()); @@ -852,12 +865,11 @@ class ringbuffer { } /** - * @param nullelem The `null` value used to zero removed elements on get*(..) and clear() * @param copyFrom * @param copyFromSize */ - ringbuffer(const NullValue_type& nullelem_, const Value_type * copyFrom, const Size_type copyFromSize) noexcept - : nullelem(nullelem_), capacityPlusOne(copyFromSize + 1), array(newArray(capacityPlusOne)), + ringbuffer(const Value_type * copyFrom, const Size_type copyFromSize) noexcept + : capacityPlusOne(copyFromSize + 1), array(newArray(capacityPlusOne)), readPos(0), writePos(0) { resetImpl(copyFrom, copyFromSize); @@ -878,12 +890,11 @@ class ringbuffer { * <p> * Implementation will allocate an internal array of size <code>capacity</code> <i>plus one</i>. * </p> - * @param nullelem The `null` value used to zero removed elements on get*(..) and clear() * @param arrayType the array type of the created empty internal array. * @param capacity the initial net capacity of the ring buffer */ - ringbuffer(const NullValue_type& nullelem_, const Size_type capacity) noexcept - : nullelem(nullelem_), capacityPlusOne(capacity + 1), array(newArray(capacityPlusOne)), + ringbuffer(const Size_type capacity) noexcept + : capacityPlusOne(capacity + 1), array(newArray(capacityPlusOne)), readPos(0), writePos(0) { _DEBUG_DUMP("ctor(capacity)"); @@ -898,7 +909,7 @@ class ringbuffer { } ringbuffer(const ringbuffer &_source) noexcept - : nullelem(_source.nullelem), capacityPlusOne(_source.capacityPlusOne), array(newArray(capacityPlusOne)), + : capacityPlusOne(_source.capacityPlusOne), array(newArray(capacityPlusOne)), readPos(0), writePos(0) { std::unique_lock<std::mutex> lockMultiReadS(_source.syncMultiRead, std::defer_lock); // utilize std::lock(r, w), allowing mixed order waiting on read/write ops @@ -919,7 +930,6 @@ class ringbuffer { if( this == &_source ) { return *this; } - nullelem = _source.nullelem; if( capacityPlusOne != _source.capacityPlusOne ) { cloneFrom(true, _source); @@ -989,139 +999,71 @@ class ringbuffer { /** * Peeks the next element at the read position w/o modifying pointer, nor blocking. - * @return <code>nullelem</code> if empty, otherwise the element which would be read next. - */ - Value_type peek() noexcept { - bool success; - return peekImpl(false, 0, success); - } - - /** - * Peeks the next element at the read position w/o modifying pointer, nor blocking. - * @param result storage for the resulting value if successful, otherwise <code>nullelem</code> if empty. + * + * Method is non blocking and returns immediately;. + * + * @param result storage for the resulting value if successful, otherwise unchanged. * @return true if successful, otherwise false. */ bool peek(Value_type& result) noexcept { - bool success; - result = peekImpl(false, 0, success); - return success; - } - - /** - * Peeks the next element at the read position w/o modifying pointer, but with blocking. - * <p> - * <code>timeoutMS</code> defaults to zero, - * i.e. infinitive blocking until an element available via put.<br> - * Otherwise this methods blocks for the given milliseconds. - * </p> - * @return <code>nullelem</code> if empty or timeout occurred, otherwise the element which would be read next. - */ - Value_type peekBlocking(const int timeoutMS=0) noexcept { - bool success; - return peekImpl(true, timeoutMS, success); + return peekImpl(result, false, 0); } /** * Peeks the next element at the read position w/o modifying pointer, but with blocking. - * <p> + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until an element available via put.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> - * @param result storage for the resulting value if successful, otherwise <code>nullelem</code> if empty. + * + * @param result storage for the resulting value if successful, otherwise unchanged. * @return true if successful, otherwise false. */ bool peekBlocking(Value_type& result, const int timeoutMS=0) noexcept { - bool success; - result = peekImpl(true, timeoutMS, success); - return success; + return peekImpl(result, true, timeoutMS); } /** - * Dequeues the oldest enqueued element if available, otherwise null. - * <p> - * The returned ring buffer slot will be set to <code>nullelem</code> to release the reference - * and move ownership to the caller. - * </p> - * <p> - * Method is non blocking and returns immediately;. - * </p> - * @return the oldest put element if available, otherwise <code>nullelem</code>. - */ - Value_type get() noexcept { - bool success; - return moveOutImpl(false, 0, success); - } - - /** - * Dequeues the oldest enqueued element if available, otherwise null. - * <p> - * The returned ring buffer slot will be set to <code>nullelem</code> to release the reference - * and move ownership to the caller. - * </p> - * <p> + * Dequeues the oldest enqueued element, if available. + * + * The ring buffer slot will be released and its value moved to the caller's `result` storage, if successful. + * * Method is non blocking and returns immediately;. - * </p> - * @param result storage for the resulting value if successful, otherwise <code>nullelem</code> if empty. + * + * @param result storage for the resulting value if successful, otherwise unchanged. * @return true if successful, otherwise false. */ bool get(Value_type& result) noexcept { - bool success; - result = moveOutImpl(false, 0, success); - return success; + return moveOutImpl(result, false, 0); } /** * Dequeues the oldest enqueued element. - * <p> - * The returned ring buffer slot will be set to <code>nullelem</code> to release the reference - * and move ownership to the caller. - * </p> - * <p> - * <code>timeoutMS</code> defaults to zero, - * i.e. infinitive blocking until an element available via put.<br> - * Otherwise this methods blocks for the given milliseconds. - * </p> - * @return the oldest put element or <code>nullelem</code> if timeout occurred. - */ - Value_type getBlocking(const int timeoutMS=0) noexcept { - bool success; - return moveOutImpl(true, timeoutMS, success); - } - - /** - * Dequeues the oldest enqueued element. - * <p> - * The returned ring buffer slot will be set to <code>nullelem</code> to release the reference - * and move ownership to the caller. - * </p> - * <p> + * + * The ring buffer slot will be released and its value moved to the caller's `result` storage, if successful. + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until an element available via put.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> - * @param result storage for the resulting value if successful, otherwise <code>nullelem</code> if empty. + * + * @param result storage for the resulting value if successful, otherwise unchanged. * @return true if successful, otherwise false. */ bool getBlocking(Value_type& result, const int timeoutMS=0) noexcept { - bool success; - result = moveOutImpl(true, timeoutMS, success); - return success; + return moveOutImpl(result, true, timeoutMS); } /** * Dequeues the oldest enqueued `min(dest_len, getSize()>=min_count)` elements by copying them into the given consecutive 'dest' storage. - * <p> - * The returned ring buffer slot will be set to <code>nullelem</code> to release the reference - * and move ownership to the caller. - * </p> - * <p> + * + * The ring buffer slots will be released and its value moved to the caller's `dest` storage, if successful. + * * Method is non blocking and returns immediately;. - * </p> - * @param dest pointer to first storage element of `count` consecutive elements. - * @param dest_len number of consecutive elements in dest and maximum number of elements to get - * @param min_count minimum number of consecutive elements to get - * @return actual number of elements received + * + * @param dest pointer to first storage element of `dest_len` consecutive elements to store the values, if successful. + * @param dest_len number of consecutive elements in `dest`, hence maximum number of elements to return. + * @param min_count minimum number of consecutive elements to return. + * @return actual number of elements returned */ Size_type get(Value_type *dest, const Size_type dest_len, const Size_type min_count) noexcept { return moveOutImpl(dest, dest_len, min_count, false, 0); @@ -1129,20 +1071,18 @@ class ringbuffer { /** * Dequeues the oldest enqueued `min(dest_len, getSize()>=min_count)` elements by copying them into the given consecutive 'dest' storage. - * <p> - * The returned ring buffer slot will be set to <code>nullelem</code> to release the reference - * and move ownership to the caller. - * </p> - * <p> + * + * The ring buffer slots will be released and its value moved to the caller's `dest` storage, if successful. + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until an element available via put.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> - * @param dest pointer to first storage element of `count` consecutive elements. - * @param dest_len number of consecutive elements in dest and maximum number of elements to get - * @param min_count minimum number of consecutive elements to get + * + * @param dest pointer to first storage element of `dest_len` consecutive elements to store the values, if successful. + * @param dest_len number of consecutive elements in `dest`, hence maximum number of elements to return. + * @param min_count minimum number of consecutive elements to return * @param timeoutMS - * @return actual number of elements received + * @return actual number of elements returned */ Size_type getBlocking(Value_type *dest, const Size_type dest_len, const Size_type min_count, const int timeoutMS=0) noexcept { return moveOutImpl(dest, dest_len, min_count, true, timeoutMS); @@ -1150,9 +1090,9 @@ class ringbuffer { /** * Drops {@code count} oldest enqueued elements. - * <p> + * * Method is non blocking and returns immediately;. - * </p> + * * @param count number of elements to drop from ringbuffer. * @return true if successful, otherwise false */ @@ -1162,11 +1102,11 @@ class ringbuffer { /** * Drops {@code count} oldest enqueued elements. - * <p> + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until an element available via put.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> + * * @param count number of elements to drop from ringbuffer. * @return true if successful, otherwise false */ @@ -1176,12 +1116,11 @@ class ringbuffer { /** * Enqueues the given element by moving it into this ringbuffer storage. - * <p> + * * Returns true if successful, otherwise false in case buffer is full. - * </p> - * <p> + * * Method is non blocking and returns immediately;. - * </p> + * * @return true if successful, otherwise false */ bool put(Value_type && e) noexcept { @@ -1190,11 +1129,11 @@ class ringbuffer { /** * Enqueues the given element by moving it into this ringbuffer storage. - * <p> + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until a free slot becomes available via get.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> + * * @return true if successful, otherwise false in case timeout occurred or otherwise. */ bool putBlocking(Value_type && e, const int timeoutMS=0) noexcept { @@ -1203,12 +1142,11 @@ class ringbuffer { /** * Enqueues the given element by copying it into this ringbuffer storage. - * <p> + * * Returns true if successful, otherwise false in case buffer is full. - * </p> - * <p> + * * Method is non blocking and returns immediately;. - * </p> + * * @return true if successful, otherwise false */ bool put(const Value_type & e) noexcept { @@ -1217,11 +1155,11 @@ class ringbuffer { /** * Enqueues the given element by copying it into this ringbuffer storage. - * <p> + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until a free slot becomes available via get.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> + * * @return true if successful, otherwise false in case timeout occurred or otherwise. */ bool putBlocking(const Value_type & e, const int timeoutMS=0) noexcept { @@ -1230,12 +1168,11 @@ class ringbuffer { /** * Enqueues the given range of consecutive elements by copying it into this ringbuffer storage. - * <p> + * * Returns true if successful, otherwise false in case buffer is full. - * </p> - * <p> + * * Method is non blocking and returns immediately;. - * </p> + * * @param first pointer to first consecutive element to range of value_type [first, last) * @param last pointer to last consecutive element to range of value_type [first, last) * @return true if successful, otherwise false @@ -1246,11 +1183,11 @@ class ringbuffer { /** * Enqueues the given range of consecutive elementa by copying it into this ringbuffer storage. - * <p> + * * <code>timeoutMS</code> defaults to zero, * i.e. infinitive blocking until a free slot becomes available via get.<br> * Otherwise this methods blocks for the given milliseconds. - * </p> + * * @param first pointer to first consecutive element to range of value_type [first, last) * @param last pointer to last consecutive element to range of value_type [first, last) * @param timeoutMS diff --git a/test/test_lfringbuffer01.cpp b/test/test_lfringbuffer01.cpp index 998251a..68aeaf2 100644 --- a/test/test_lfringbuffer01.cpp +++ b/test/test_lfringbuffer01.cpp @@ -38,19 +38,19 @@ using namespace jau; typedef uint8_t IntegralType; typedef uint8_t TrivialType; constexpr const TrivialType TrivialTypeNullElem(0xff); -typedef ringbuffer<TrivialType, TrivialType, jau::nsize_t> TrivialTypeRingbuffer; +typedef ringbuffer<TrivialType, jau::nsize_t> TrivialTypeRingbuffer; // Test examples. class TestRingbuffer01 { private: TrivialTypeRingbuffer createEmpty(jau::nsize_t initialCapacity) { - TrivialTypeRingbuffer rb(0xff, initialCapacity); + TrivialTypeRingbuffer rb(initialCapacity); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); return rb; } TrivialTypeRingbuffer createFull(const std::vector<TrivialType> & source) { - TrivialTypeRingbuffer rb(0xff, source); + TrivialTypeRingbuffer rb(source); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); return rb; } @@ -73,25 +73,6 @@ class TestRingbuffer01 { REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); for(jau::nsize_t i=0; i<len; i++) { - TrivialType svI = rb.get(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); - REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), startValue+(IntegralType)i == svI); - } - - REQUIRE_MSG("size "+rb.toString(), preSize-len == rb.size()); - REQUIRE_MSG("free slots after reading "+std::to_string(len)+": "+rb.toString(), rb.freeSlots()>= len); - REQUIRE_MSG("not full "+rb.toString(), !rb.isFull()); - } - void readTestImpl2(TrivialTypeRingbuffer &rb, bool clearRef, jau::nsize_t capacity, jau::nsize_t len, IntegralType startValue) { - (void) clearRef; - - jau::nsize_t preSize = rb.size(); - REQUIRE_MSG("capacity "+rb.toString(), capacity == rb.capacity()); - REQUIRE_MSG("capacity at read "+std::to_string(len)+" elems: "+rb.toString(), capacity >= len); - REQUIRE_MSG("size at read "+std::to_string(len)+" elems: "+rb.toString(), preSize >= len); - REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); - - for(jau::nsize_t i=0; i<len; i++) { TrivialType svI; REQUIRE_MSG("ringbuffer get", rb.get(svI)); REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); @@ -163,7 +144,9 @@ class TestRingbuffer01 { void moveGetPutImpl(TrivialTypeRingbuffer &rb, jau::nsize_t pos) { REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); for(jau::nsize_t i=0; i<pos; i++) { - REQUIRE_MSG("moveFull.get "+rb.toString(), (IntegralType)i == rb.get()); + TrivialType svI; + REQUIRE_MSG("moveFull.get "+rb.toString(), rb.get(svI)); + REQUIRE_MSG("moveFull.get "+rb.toString(), (IntegralType)i == svI); REQUIRE_MSG("moveFull.put "+rb.toString(), rb.put( TrivialType( (IntegralType)i ) ) ); } } @@ -172,7 +155,10 @@ class TestRingbuffer01 { REQUIRE_MSG("RB is full "+rb.toString(), !rb.isFull()); for(jau::nsize_t i=0; i<pos; i++) { REQUIRE_MSG("moveEmpty.put "+rb.toString(), rb.put( TrivialType( 600+(IntegralType)i ) ) ); - REQUIRE_MSG("moveEmpty.get "+rb.toString(), 600+(IntegralType)i == rb.get()); + + TrivialType svI; + REQUIRE_MSG("moveEmpty.get "+rb.toString(), rb.get(svI)); + REQUIRE_MSG("moveEmpty.get "+rb.toString(), 600+(IntegralType)i == svI); } } @@ -182,13 +168,11 @@ class TestRingbuffer01 { TrivialTypeRingbuffer rb = createEmpty(11); std::string msg("Ringbuffer: uses_memcpy "+std::to_string(TrivialTypeRingbuffer::uses_memcpy)+ - ", uses_memset "+std::to_string(TrivialTypeRingbuffer::uses_memset)+ ", trivially_copyable "+std::to_string(std::is_trivially_copyable<typename TrivialTypeRingbuffer::value_type>::value)+ ", size "+std::to_string(sizeof(rb))+" bytes"); fprintf(stderr, "%s\n", msg.c_str()); fprintf(stderr, "%s\n", rb.get_info().c_str()); REQUIRE_MSG("Ringbuffer<T> using memcpy", TrivialTypeRingbuffer::uses_memcpy); - REQUIRE_MSG("Ringbuffer<T> uses memset", TrivialTypeRingbuffer::uses_memset); } void test01_FullRead() { @@ -382,7 +366,7 @@ class TestRingbuffer01 { INFO_STR("test04_FullReadReset: Post Reset w/ source / " + rb.toString()); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); - readTestImpl2(rb, false, capacity, capacity, 0); + readTestImpl(rb, false, capacity, capacity, 0); INFO_STR("test04_FullReadReset: Post Read / " + rb.toString()); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); } @@ -407,7 +391,7 @@ class TestRingbuffer01 { writeTestImpl(rb, capacity, capacity, 0); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); - readTestImpl2(rb, false, capacity, capacity, 0); + readTestImpl(rb, false, capacity, capacity, 0); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); } @@ -461,8 +445,8 @@ class TestRingbuffer01 { TrivialTypeRingbuffer rb = createFull(source); for(jau::nsize_t i=0; i<initialCapacity; i++) { - TrivialType svI = rb.get(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); + TrivialType svI;; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), rb.get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType((0+i)%initialCapacity) == svI); } REQUIRE_MSG("zero size "+rb.toString(), 0 == rb.size()); @@ -489,16 +473,14 @@ class TestRingbuffer01 { REQUIRE_MSG("full "+rb.toString(), rb.isFull()); for(jau::nsize_t i=0; i<initialCapacity; i++) { - TrivialType svI = rb.get(); - // PRINTM("X05["+std::to_string(i)+"]: "+rb.toString()+", svI-null: "+std::to_string(svI==TrivialTypeNullElem)); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), rb.get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType((pos+i)%initialCapacity) == svI); } for(jau::nsize_t i=0; i<growAmount; i++) { - TrivialType svI = rb.get(); - // PRINTM("X07["+std::to_string(i)+"]: "+rb.toString()+", svI-null: "+std::to_string(svI==TrivialTypeNullElem)); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), rb.get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType(100+i) == svI); } diff --git a/test/test_lfringbuffer02.cpp b/test/test_lfringbuffer02.cpp index 41b377c..35953e7 100644 --- a/test/test_lfringbuffer02.cpp +++ b/test/test_lfringbuffer02.cpp @@ -59,19 +59,19 @@ class Integer { typedef Integer TrivialType; static const TrivialType TrivialTypeNullElem(-1); -typedef ringbuffer<TrivialType, TrivialType, jau::nsize_t> TrivialTypeRingbuffer; +typedef ringbuffer<TrivialType, jau::nsize_t> TrivialTypeRingbuffer; // Test examples. class TestRingbuffer02 { private: TrivialTypeRingbuffer createEmpty(jau::nsize_t initialCapacity) { - TrivialTypeRingbuffer rb(Integer(-1), initialCapacity); + TrivialTypeRingbuffer rb(initialCapacity); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); return rb; } TrivialTypeRingbuffer createFull(const std::vector<TrivialType> & source) { - TrivialTypeRingbuffer rb(Integer(-1), source); + TrivialTypeRingbuffer rb(source); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); return rb; } @@ -94,25 +94,6 @@ class TestRingbuffer02 { REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); for(jau::nsize_t i=0; i<len; i++) { - TrivialType svI = rb.get(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); - REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), startValue+(IntegralType)i == svI.intValue()); - } - - REQUIRE_MSG("size "+rb.toString(), preSize-len == rb.size()); - REQUIRE_MSG("free slots after reading "+std::to_string(len)+": "+rb.toString(), rb.freeSlots()>= len); - REQUIRE_MSG("not full "+rb.toString(), !rb.isFull()); - } - void readTestImpl2(TrivialTypeRingbuffer &rb, bool clearRef, jau::nsize_t capacity, jau::nsize_t len, IntegralType startValue) { - (void) clearRef; - - jau::nsize_t preSize = rb.size(); - REQUIRE_MSG("capacity "+rb.toString(), capacity == rb.capacity()); - REQUIRE_MSG("capacity at read "+std::to_string(len)+" elems: "+rb.toString(), capacity >= len); - REQUIRE_MSG("size at read "+std::to_string(len)+" elems: "+rb.toString(), preSize >= len); - REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); - - for(jau::nsize_t i=0; i<len; i++) { TrivialType svI; REQUIRE_MSG("ringbuffer get", rb.get(svI)); REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); @@ -184,7 +165,9 @@ class TestRingbuffer02 { void moveGetPutImpl(TrivialTypeRingbuffer &rb, jau::nsize_t pos) { REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); for(jau::nsize_t i=0; i<pos; i++) { - REQUIRE_MSG("moveFull.get "+rb.toString(), (IntegralType)i == rb.get().intValue()); + TrivialType svI; + REQUIRE_MSG("moveFull.get "+rb.toString(), rb.get(svI)); + REQUIRE_MSG("moveFull.get "+rb.toString(), (IntegralType)i == svI.intValue()); REQUIRE_MSG("moveFull.put "+rb.toString(), rb.put( TrivialType( (IntegralType)i ) ) ); } } @@ -193,7 +176,9 @@ class TestRingbuffer02 { REQUIRE_MSG("RB is full "+rb.toString(), !rb.isFull()); for(jau::nsize_t i=0; i<pos; i++) { REQUIRE_MSG("moveEmpty.put "+rb.toString(), rb.put( TrivialType( 600+(IntegralType)i ) ) ); - REQUIRE_MSG("moveEmpty.get "+rb.toString(), 600+(IntegralType)i == rb.get().intValue()); + TrivialType svI; + REQUIRE_MSG("moveEmpty.get "+rb.toString(), rb.get(svI)); + REQUIRE_MSG("moveEmpty.get "+rb.toString(), 600+(IntegralType)i == svI.intValue()); } } @@ -203,13 +188,11 @@ class TestRingbuffer02 { TrivialTypeRingbuffer rb = createEmpty(11); std::string msg("Ringbuffer: uses_memcpy "+std::to_string(TrivialTypeRingbuffer::uses_memcpy)+ - ", uses_memset "+std::to_string(TrivialTypeRingbuffer::uses_memset)+ ", trivially_copyable "+std::to_string(std::is_trivially_copyable<typename TrivialTypeRingbuffer::value_type>::value)+ ", size "+std::to_string(sizeof(rb))+" bytes"); fprintf(stderr, "%s\n", msg.c_str()); fprintf(stderr, "%s\n", rb.get_info().c_str()); REQUIRE_MSG("Ringbuffer<T> using memcpy", TrivialTypeRingbuffer::uses_memcpy); - REQUIRE_MSG("Ringbuffer<T> not using memset", !TrivialTypeRingbuffer::uses_memset); } void test01_FullRead() { @@ -413,7 +396,7 @@ class TestRingbuffer02 { INFO_STR("test04_FullReadReset: Post Reset w/ source / " + rb.toString()); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); - readTestImpl2(rb, false, capacity, capacity, 0); + readTestImpl(rb, false, capacity, capacity, 0); INFO_STR("test04_FullReadReset: Post Read / " + rb.toString()); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); } @@ -438,7 +421,7 @@ class TestRingbuffer02 { writeTestImpl(rb, capacity, capacity, 0); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); - readTestImpl2(rb, false, capacity, capacity, 0); + readTestImpl(rb, false, capacity, capacity, 0); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); } @@ -492,8 +475,8 @@ class TestRingbuffer02 { TrivialTypeRingbuffer rb = createFull(source); for(jau::nsize_t i=0; i<initialCapacity; i++) { - TrivialType svI = rb.get(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), rb.get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType((0+i)%initialCapacity) == svI.intValue()); } REQUIRE_MSG("zero size "+rb.toString(), 0 == rb.size()); @@ -520,16 +503,14 @@ class TestRingbuffer02 { REQUIRE_MSG("full "+rb.toString(), rb.isFull()); for(jau::nsize_t i=0; i<initialCapacity; i++) { - TrivialType svI = rb.get(); - // PRINTM("X05["+std::to_string(i)+"]: "+rb.toString()+", svI-null: "+std::to_string(svI==TrivialTypeNullElem)); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), rb.get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType((pos+i)%initialCapacity) == svI.intValue()); } for(jau::nsize_t i=0; i<growAmount; i++) { - TrivialType svI = rb.get(); - // PRINTM("X07["+std::to_string(i)+"]: "+rb.toString()+", svI-null: "+std::to_string(svI==TrivialTypeNullElem)); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), rb.get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType(100+i) == svI.intValue()); } diff --git a/test/test_lfringbuffer03.cpp b/test/test_lfringbuffer03.cpp index 51819e3..5e6f7bc 100644 --- a/test/test_lfringbuffer03.cpp +++ b/test/test_lfringbuffer03.cpp @@ -58,19 +58,19 @@ class Integer { std::shared_ptr<Integer> NullInteger = nullptr; typedef std::shared_ptr<Integer> SharedType; -typedef ringbuffer<SharedType, std::nullptr_t, jau::nsize_t> SharedTypeRingbuffer; +typedef ringbuffer<SharedType, jau::nsize_t> SharedTypeRingbuffer; // Test examples. class TestRingbuffer03 { private: std::shared_ptr<SharedTypeRingbuffer> createEmpty(jau::nsize_t initialCapacity) { - std::shared_ptr<SharedTypeRingbuffer> rb = std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(nullptr, initialCapacity)); + std::shared_ptr<SharedTypeRingbuffer> rb = std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(initialCapacity)); REQUIRE_MSG("empty "+rb->toString(), rb->isEmpty()); return rb; } std::shared_ptr<SharedTypeRingbuffer> createFull(const std::vector<std::shared_ptr<Integer>> & source) { - std::shared_ptr<SharedTypeRingbuffer> rb = std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(nullptr, source)); + std::shared_ptr<SharedTypeRingbuffer> rb = std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(source)); REQUIRE_MSG("full "+rb->toString(), rb->isFull()); return rb; } @@ -93,25 +93,6 @@ class TestRingbuffer03 { REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); for(jau::nsize_t i=0; i<len; i++) { - SharedType svI = rb.get(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=nullptr); - REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb.toString(), IntegralType(startValue+i) == svI->intValue()); - } - - REQUIRE_MSG("size "+rb.toString(), preSize-len == rb.size()); - REQUIRE_MSG("free slots after reading "+std::to_string(len)+": "+rb.toString(), rb.freeSlots()>= len); - REQUIRE_MSG("not full "+rb.toString(), !rb.isFull()); - } - void readTestImpl2(SharedTypeRingbuffer &rb, bool clearRef, jau::nsize_t capacity, jau::nsize_t len, IntegralType startValue) { - (void) clearRef; - - jau::nsize_t preSize = rb.size(); - REQUIRE_MSG("capacity "+rb.toString(), capacity == rb.capacity()); - REQUIRE_MSG("capacity at read "+std::to_string(len)+" elems: "+rb.toString(), capacity >= len); - REQUIRE_MSG("size at read "+std::to_string(len)+" elems: "+rb.toString(), preSize >= len); - REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); - - for(jau::nsize_t i=0; i<len; i++) { SharedType svI; REQUIRE_MSG("ringbuffer get", rb.get(svI)); REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb.toString(), svI!=nullptr); @@ -183,7 +164,9 @@ class TestRingbuffer03 { void moveGetPutImpl(SharedTypeRingbuffer &rb, jau::nsize_t pos) { REQUIRE_MSG("not empty "+rb.toString(), !rb.isEmpty()); for(jau::nsize_t i=0; i<pos; i++) { - REQUIRE_MSG("moveFull.get "+rb.toString(), IntegralType(i) == rb.get()->intValue()); + SharedType svI; + REQUIRE_MSG("moveFull.get "+rb.toString(), rb.get(svI)); + REQUIRE_MSG("moveFull.get "+rb.toString(), IntegralType(i) == svI->intValue()); REQUIRE_MSG("moveFull.put "+rb.toString(), rb.put( SharedType( new Integer(i) ) ) ); } } @@ -192,7 +175,9 @@ class TestRingbuffer03 { REQUIRE_MSG("RB is full "+rb.toString(), !rb.isFull()); for(jau::nsize_t i=0; i<pos; i++) { REQUIRE_MSG("moveEmpty.put "+rb.toString(), rb.put( SharedType( new Integer(600+i) ) ) ); - REQUIRE_MSG("moveEmpty.get "+rb.toString(), IntegralType(600+i) == rb.get()->intValue()); + SharedType svI; + REQUIRE_MSG("moveEmpty.get "+rb.toString(), rb.get(svI)); + REQUIRE_MSG("moveEmpty.get "+rb.toString(), IntegralType(600+i) == svI->intValue()); } } @@ -202,13 +187,11 @@ class TestRingbuffer03 { std::shared_ptr<SharedTypeRingbuffer> rb = createEmpty(11); std::string msg("Ringbuffer: uses_memcpy "+std::to_string(SharedTypeRingbuffer::uses_memcpy)+ - ", uses_memset "+std::to_string(SharedTypeRingbuffer::uses_memset)+ ", trivially_copyable "+std::to_string(std::is_trivially_copyable<typename SharedTypeRingbuffer::value_type>::value)+ ", size "+std::to_string(sizeof(*rb))+" bytes"); fprintf(stderr, "%s\n", msg.c_str()); fprintf(stderr, "%s\n", rb->get_info().c_str()); REQUIRE_MSG("Ringbuffer<shared_ptr<T>> not using memcpy", !SharedTypeRingbuffer::uses_memcpy); - REQUIRE_MSG("Ringbuffer<shared_ptr<T>> not using memset", !SharedTypeRingbuffer::uses_memset); } void test01_FullRead() { @@ -398,7 +381,7 @@ class TestRingbuffer03 { INFO_STR("test04_FullReadReset: Post Reset w/ source / " + rb->toString()); REQUIRE_MSG("full "+rb->toString(), rb->isFull()); - readTestImpl2(*rb, false, capacity, capacity, 0); + readTestImpl(*rb, false, capacity, capacity, 0); INFO_STR("test04_FullReadReset: Post Read / " + rb->toString()); REQUIRE_MSG("empty "+rb->toString(), rb->isEmpty()); } @@ -423,7 +406,7 @@ class TestRingbuffer03 { writeTestImpl(*rb, capacity, capacity, 0); REQUIRE_MSG("full "+rb->toString(), rb->isFull()); - readTestImpl2(*rb, false, capacity, capacity, 0); + readTestImpl(*rb, false, capacity, capacity, 0); REQUIRE_MSG("empty "+rb->toString(), rb->isEmpty()); } @@ -477,8 +460,8 @@ class TestRingbuffer03 { std::shared_ptr<SharedTypeRingbuffer> rb = createFull(source); for(jau::nsize_t i=0; i<initialCapacity; i++) { - SharedType svI = rb->get(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), svI!=nullptr); + SharedType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), rb->get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb->toString(), IntegralType((0+i)%initialCapacity) == svI->intValue()); } REQUIRE_MSG("zero size "+rb->toString(), 0 == rb->size()); @@ -505,16 +488,14 @@ class TestRingbuffer03 { REQUIRE_MSG("full "+rb->toString(), rb->isFull()); for(jau::nsize_t i=0; i<initialCapacity; i++) { - SharedType svI = rb->get(); - // PRINTM("X05["+std::to_string(i)+"]: "+rb->toString()+", svI-null: "+std::to_string(svI==nullptr)); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), svI!=nullptr); + SharedType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), rb->get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb->toString(), IntegralType((pos+i)%initialCapacity) == svI->intValue()); } for(jau::nsize_t i=0; i<growAmount; i++) { - SharedType svI = rb->get(); - // PRINTM("X07["+std::to_string(i)+"]: "+rb->toString()+", svI-null: "+std::to_string(svI==nullptr)); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), svI!=nullptr); + SharedType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), rb->get(svI)); REQUIRE_MSG("value at read #"+std::to_string(i+1)+": "+rb->toString(), IntegralType(100+i) == svI->intValue()); } diff --git a/test/test_lfringbuffer11.cpp b/test/test_lfringbuffer11.cpp index 2f329ed..c41d602 100644 --- a/test/test_lfringbuffer11.cpp +++ b/test/test_lfringbuffer11.cpp @@ -40,7 +40,7 @@ using namespace jau; typedef uint8_t IntegralType; typedef uint8_t TrivialType; constexpr const TrivialType TrivialTypeNullElem(0xff); -typedef ringbuffer<TrivialType, TrivialType, jau::nsize_t> TrivialTypeRingbuffer; +typedef ringbuffer<TrivialType, jau::nsize_t> TrivialTypeRingbuffer; constexpr static const IntegralType integral_modulus = 254; @@ -49,12 +49,12 @@ class TestRingbuffer11 { private: TrivialTypeRingbuffer createEmpty(jau::nsize_t initialCapacity) { - TrivialTypeRingbuffer rb(0xff, initialCapacity); + TrivialTypeRingbuffer rb(initialCapacity); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); return rb; } TrivialTypeRingbuffer createFull(const std::vector<TrivialType> & source) { - TrivialTypeRingbuffer rb(0xff, source); + TrivialTypeRingbuffer rb(source); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); return rb; } @@ -74,8 +74,8 @@ class TestRingbuffer11 { // INFO_STR, INFO: Not thread safe yet // INFO_STR(msg+": Created / " + rb->toString()); for(jau::nsize_t i=0; i<len; i++) { - TrivialType svI = rb->getBlocking(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString()+", elem "+std::to_string(svI), svI!= TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString()+", elem "+std::to_string(svI), rb->getBlocking(svI)); // INFO_STR("Got "+std::to_string(svI->intValue())+" / " + rb->toString()); } // INFO_STR(msg+": Dies / " + rb->toString()); diff --git a/test/test_lfringbuffer12.cpp b/test/test_lfringbuffer12.cpp index 3ea1637..75a2985 100644 --- a/test/test_lfringbuffer12.cpp +++ b/test/test_lfringbuffer12.cpp @@ -61,19 +61,19 @@ class Integer { typedef Integer TrivialType; static const TrivialType TrivialTypeNullElem(-1); -typedef ringbuffer<TrivialType, TrivialType, jau::nsize_t> TrivialTypeRingbuffer; +typedef ringbuffer<TrivialType, jau::nsize_t> TrivialTypeRingbuffer; // Test examples. class TestRingbuffer12 { private: TrivialTypeRingbuffer createEmpty(jau::nsize_t initialCapacity) { - TrivialTypeRingbuffer rb(Integer(-1), initialCapacity); + TrivialTypeRingbuffer rb(initialCapacity); REQUIRE_MSG("empty "+rb.toString(), rb.isEmpty()); return rb; } TrivialTypeRingbuffer createFull(const std::vector<TrivialType> & source) { - TrivialTypeRingbuffer rb(Integer(-1), source); + TrivialTypeRingbuffer rb(source); REQUIRE_MSG("full "+rb.toString(), rb.isFull()); return rb; } @@ -93,8 +93,8 @@ class TestRingbuffer12 { // INFO_STR, INFO: Not thread safe yet // INFO_STR(msg+": Created / " + rb->toString()); for(jau::nsize_t i=0; i<len; i++) { - TrivialType svI = rb->getBlocking(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), svI!=TrivialTypeNullElem); + TrivialType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), rb->getBlocking(svI)); // INFO_STR("Got "+std::to_string(svI->intValue())+" / " + rb->toString()); } // INFO_STR(msg+": Dies / " + rb->toString()); diff --git a/test/test_lfringbuffer13.cpp b/test/test_lfringbuffer13.cpp index ba2b994..1588bfc 100644 --- a/test/test_lfringbuffer13.cpp +++ b/test/test_lfringbuffer13.cpp @@ -57,21 +57,19 @@ class Integer { static Integer valueOf(const IntegralType i) { return Integer(i); } }; -std::shared_ptr<Integer> NullInteger = nullptr; - typedef std::shared_ptr<Integer> SharedType; constexpr const std::nullptr_t SharedTypeNullElem = nullptr ; -typedef ringbuffer<SharedType, std::nullptr_t, jau::nsize_t> SharedTypeRingbuffer; +typedef ringbuffer<SharedType, jau::nsize_t> SharedTypeRingbuffer; // Test examples. class TestRingbuffer13 { private: std::shared_ptr<SharedTypeRingbuffer> createEmpty(jau::nsize_t initialCapacity) { - return std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(nullptr, initialCapacity)); + return std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(initialCapacity)); } std::shared_ptr<SharedTypeRingbuffer> createFull(const std::vector<std::shared_ptr<Integer>> & source) { - return std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(nullptr, source)); + return std::shared_ptr<SharedTypeRingbuffer>(new SharedTypeRingbuffer(source)); } std::vector<SharedType> createIntArray(const jau::nsize_t capacity, const IntegralType startValue) { @@ -89,8 +87,8 @@ class TestRingbuffer13 { // INFO_STR, INFO: Not thread safe yet // INFO_STR(msg+": Created / " + rb->toString()); for(jau::nsize_t i=0; i<len; i++) { - SharedType svI = rb->getBlocking(); - REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), svI!=SharedTypeNullElem); + SharedType svI; + REQUIRE_MSG("not empty at read #"+std::to_string(i+1)+": "+rb->toString(), rb->getBlocking(svI)); // INFO_STR("Got "+std::to_string(svI->intValue())+" / " + rb->toString()); } // INFO_STR(msg+": Dies / " + rb->toString()); |