mirror of
https://github.com/ETLCPP/etl.git
synced 2026-04-30 19:09:10 +08:00
introduce bip buffer spsc atomic (#402)
Based on the works of Andrea Lattuada and James Munns: https://blog.systems.ethz.ch/blog/2019/the-design-and-implementation-of-a-lock-free-ring-buffer-with-contiguous-reservations.html Whose design was inspired by Simon Cooke: https://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist Signed-off-by: Benedek Kupper <benedek.kupper@streamunlimited.com>
This commit is contained in:
parent
86d1bd7890
commit
7a067ba816
3
.gitignore
vendored
3
.gitignore
vendored
@ -318,3 +318,6 @@ test/vs2019/DebugNoSTL
|
||||
test/vs2022/Debug LLVM
|
||||
test/vs2019/Debug MSVC - No STL - Built-ins/etl.exe
|
||||
test/vs2019/Debug-LLVM-NoSTL-Builtins/etl.exe
|
||||
test/vs2019/cmake-build
|
||||
test/vs2019/Debug MSVC - No STL - Built-ins
|
||||
test/vs2019/Test1
|
||||
|
||||
400
include/etl/bip_buffer_spsc_atomic.h
Normal file
400
include/etl/bip_buffer_spsc_atomic.h
Normal file
@ -0,0 +1,400 @@
|
||||
///\file
|
||||
|
||||
/**
|
||||
* @note Based on the works of Andrea Lattuada and James Munns:
|
||||
* https://blog.systems.ethz.ch/blog/2019/the-design-and-implementation-of-a-lock-free-ring-buffer-with-contiguous-reservations.html
|
||||
* Whose design was inspired by Simon Cooke:
|
||||
* https://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist
|
||||
*/
|
||||
#ifndef ETL_BIP_BUFFER_SPSC_ATOMIC_INCLUDED
|
||||
#define ETL_BIP_BUFFER_SPSC_ATOMIC_INCLUDED
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "alignment.h"
|
||||
#include "parameter_type.h"
|
||||
#include "atomic.h"
|
||||
#include "memory_model.h"
|
||||
#include "integral_limits.h"
|
||||
#include "utility.h"
|
||||
#include "error_handler.h"
|
||||
#include "span.h"
|
||||
|
||||
#if ETL_HAS_ATOMIC
|
||||
|
||||
namespace etl
|
||||
{
|
||||
class bip_buffer_exception: public exception
|
||||
{
|
||||
public:
|
||||
bip_buffer_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
|
||||
: exception(reason_, file_name_, line_number_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class bip_buffer_reserve_invalid: public bip_buffer_exception
|
||||
{
|
||||
public:
|
||||
bip_buffer_reserve_invalid(string_type file_name_, numeric_type line_number_)
|
||||
: bip_buffer_exception("bip_buffer:reserve", file_name_, line_number_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <const size_t MEMORY_MODEL = etl::memory_model::MEMORY_MODEL_LARGE>
|
||||
class bip_buffer_spsc_atomic_base
|
||||
{
|
||||
public:
|
||||
/// The type used for determining the size of buffer.
|
||||
typedef typename etl::size_type_lookup<MEMORY_MODEL>::type size_type;
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
bool full() const
|
||||
{
|
||||
return available() == 0;
|
||||
}
|
||||
|
||||
// returns the total used size, which may be split in two blocks
|
||||
// so the size will always be smaller after a read commit
|
||||
size_type size() const
|
||||
{
|
||||
size_type write_index = write.load(etl::memory_order_acquire);
|
||||
size_type read_index = read.load(etl::memory_order_acquire);
|
||||
|
||||
// no wraparound
|
||||
if (write_index >= read_index)
|
||||
{
|
||||
// size is distance between read and write
|
||||
return write_index - read_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_type last_index = last.load(etl::memory_order_acquire);
|
||||
|
||||
// size is distance between beginning and write, plus read and last
|
||||
return (write_index - 0) + (last_index - read_index);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the largest contiguous(!) available block size
|
||||
size_type available() const
|
||||
{
|
||||
size_type write_index = write.load(etl::memory_order_acquire);
|
||||
size_type read_index = read.load(etl::memory_order_acquire);
|
||||
|
||||
// no wraparound
|
||||
if (write_index >= read_index)
|
||||
{
|
||||
size_type forward_size = capacity() - write_index;
|
||||
|
||||
// check if there's more space if wrapping around
|
||||
if (read_index > (forward_size + 1))
|
||||
{
|
||||
return read_index - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return forward_size;
|
||||
}
|
||||
}
|
||||
else // read_index > write_index
|
||||
{
|
||||
return read_index - write_index - 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_type capacity() const
|
||||
{
|
||||
return RESERVED;
|
||||
}
|
||||
|
||||
size_type max_size() const
|
||||
{
|
||||
return RESERVED;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
read.store(0, etl::memory_order_release);
|
||||
write.store(0, etl::memory_order_release);
|
||||
last.store(0, etl::memory_order_release);
|
||||
}
|
||||
|
||||
protected:
|
||||
bip_buffer_spsc_atomic_base(size_type reserved_)
|
||||
: read(0),
|
||||
write(0),
|
||||
last(0),
|
||||
RESERVED(reserved_)
|
||||
{
|
||||
}
|
||||
|
||||
size_type get_write_reserve(size_type* psize)
|
||||
{
|
||||
size_type write_index = write.load(etl::memory_order_relaxed);
|
||||
size_type read_index = read.load(etl::memory_order_acquire);
|
||||
|
||||
// no wraparound
|
||||
if (write_index >= read_index)
|
||||
{
|
||||
size_type forward_size = capacity() - write_index;
|
||||
|
||||
// we still fit in linearly
|
||||
if (*psize <= forward_size)
|
||||
{
|
||||
return write_index;
|
||||
}
|
||||
// there isn't more space even when wrapping around
|
||||
else if (read_index <= (forward_size + 1))
|
||||
{
|
||||
*psize = forward_size;
|
||||
return write_index;
|
||||
}
|
||||
// better wrap around now
|
||||
else
|
||||
{
|
||||
// check if size fits
|
||||
// when wrapping, the write index cannot reach read index,
|
||||
// then we'd not be able to distinguish wrapped situation from linear
|
||||
if (*psize >= read_index)
|
||||
{
|
||||
if (read_index > 0)
|
||||
{
|
||||
*psize = read_index - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*psize = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else // read_index > write_index
|
||||
{
|
||||
// doesn't fit
|
||||
if ((write_index + *psize) >= read_index)
|
||||
{
|
||||
*psize = read_index - write_index - 1;
|
||||
}
|
||||
return write_index;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_write_reserve(size_type windex, size_type wsize)
|
||||
{
|
||||
if (wsize > 0)
|
||||
{
|
||||
size_type write_index = write.load(etl::memory_order_relaxed);
|
||||
size_type read_index = read.load(etl::memory_order_acquire);
|
||||
|
||||
// wrapped around already
|
||||
if (write_index < read_index)
|
||||
{
|
||||
ETL_ASSERT_AND_RETURN((windex == write_index) && ((wsize + 1) <= read_index),
|
||||
ETL_ERROR(bip_buffer_reserve_invalid));
|
||||
}
|
||||
// no wraparound so far, also not wrapping around with this block
|
||||
else if (windex == write_index)
|
||||
{
|
||||
ETL_ASSERT_AND_RETURN(wsize <= (capacity() - write_index),
|
||||
ETL_ERROR(bip_buffer_reserve_invalid));
|
||||
|
||||
// move both indexes forward
|
||||
last.store(windex + wsize, etl::memory_order_release);
|
||||
}
|
||||
// wrapping around now
|
||||
else
|
||||
{
|
||||
ETL_ASSERT_AND_RETURN((windex == 0) && ((wsize + 1) <= read_index),
|
||||
ETL_ERROR(bip_buffer_reserve_invalid));
|
||||
}
|
||||
// always update write index
|
||||
write.store(windex + wsize, etl::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
size_type get_read_reserve(size_type* psize)
|
||||
{
|
||||
size_type read_index = read.load(etl::memory_order_relaxed);
|
||||
size_type write_index = write.load(etl::memory_order_acquire);
|
||||
|
||||
if (read_index > write_index)
|
||||
{
|
||||
// writer has wrapped around
|
||||
|
||||
size_type last_index = last.load(etl::memory_order_relaxed);
|
||||
if (read_index == last_index)
|
||||
{
|
||||
// reader reached the end, start read from 0
|
||||
read_index = 0;
|
||||
}
|
||||
else // (read_index < last_index)
|
||||
{
|
||||
// use the remaining buffer at the end
|
||||
write_index = last_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no wraparound, nothing to adjust
|
||||
}
|
||||
|
||||
// limit to max available size
|
||||
if ((write_index - read_index) < *psize)
|
||||
{
|
||||
*psize = write_index - read_index;
|
||||
}
|
||||
return read_index;
|
||||
}
|
||||
|
||||
void apply_read_reserve(size_type rindex, size_type rsize)
|
||||
{
|
||||
if (rsize > 0)
|
||||
{
|
||||
size_type rsize_checker = rsize;
|
||||
ETL_ASSERT_AND_RETURN((rindex == get_read_reserve(&rsize_checker)) && (rsize == rsize_checker),
|
||||
ETL_ERROR(bip_buffer_reserve_invalid));
|
||||
|
||||
read.store(rindex + rsize, etl::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
etl::atomic<size_type> read;
|
||||
etl::atomic<size_type> write;
|
||||
etl::atomic<size_type> last;
|
||||
const size_type RESERVED;
|
||||
|
||||
#if defined(ETL_POLYMORPHIC_SPSC_BIP_BUFFER_ATOMIC) || defined(ETL_POLYMORPHIC_CONTAINERS)
|
||||
public:
|
||||
virtual ~bip_buffer_spsc_atomic_base()
|
||||
{
|
||||
}
|
||||
#else
|
||||
protected:
|
||||
~bip_buffer_spsc_atomic_base()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T, const size_t MEMORY_MODEL = etl::memory_model::MEMORY_MODEL_LARGE>
|
||||
class ibip_buffer_spsc_atomic : public bip_buffer_spsc_atomic_base<MEMORY_MODEL>
|
||||
{
|
||||
private:
|
||||
typedef typename etl::bip_buffer_spsc_atomic_base<MEMORY_MODEL> base_t;
|
||||
using base_t::get_read_reserve;
|
||||
using base_t::apply_read_reserve;
|
||||
using base_t::get_write_reserve;
|
||||
using base_t::apply_write_reserve;
|
||||
|
||||
public:
|
||||
typedef T value_type; ///< The type stored in the buffer.
|
||||
typedef T& reference; ///< A reference to the type used in the buffer.
|
||||
typedef const T& const_reference; ///< A const reference to the type used in the buffer.
|
||||
#if ETL_CPP11_SUPPORTED
|
||||
typedef T&& rvalue_reference;///< An rvalue_reference to the type used in the buffer.
|
||||
#endif
|
||||
typedef typename base_t::size_type size_type; ///< The type used for determining the size of the buffer.
|
||||
|
||||
// reserves a memory area for reading up to the max_reserve_size
|
||||
span<T> read_reserve(size_type max_reserve_size)
|
||||
{
|
||||
size_type reserve_size = max_reserve_size;
|
||||
auto rindex = get_read_reserve(&reserve_size);
|
||||
return span<T>(p_buffer + rindex, reserve_size);
|
||||
}
|
||||
|
||||
// commits the previously reserved read memory area
|
||||
// the reserve can be trimmed at the end before committing
|
||||
// throws bip_buffer_reserve_invalid
|
||||
void read_commit(const span<T> &reserve)
|
||||
{
|
||||
size_type rindex = etl::distance(p_buffer, reserve.data());
|
||||
apply_read_reserve(rindex, reserve.size());
|
||||
}
|
||||
|
||||
// reserves a memory area for writing up to the max_reserve_size
|
||||
span<T> write_reserve(size_type max_reserve_size)
|
||||
{
|
||||
size_type reserve_size = max_reserve_size;
|
||||
auto windex = get_write_reserve(&reserve_size);
|
||||
return span<T>(p_buffer + windex, reserve_size);
|
||||
}
|
||||
|
||||
// commits the previously reserved write memory area
|
||||
// the reserve can be trimmed at the end before committing
|
||||
// throws bip_buffer_reserve_invalid
|
||||
void write_commit(const span<T> &reserve)
|
||||
{
|
||||
size_type windex = etl::distance(p_buffer, reserve.data());
|
||||
apply_write_reserve(windex, reserve.size());
|
||||
}
|
||||
|
||||
protected:
|
||||
ibip_buffer_spsc_atomic(T* p_buffer_, size_type reserved_)
|
||||
: base_t(reserved_),
|
||||
p_buffer(p_buffer_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// Disable copy construction and assignment.
|
||||
ibip_buffer_spsc_atomic(const ibip_buffer_spsc_atomic&) ETL_DELETE;
|
||||
ibip_buffer_spsc_atomic& operator =(const ibip_buffer_spsc_atomic&) ETL_DELETE;
|
||||
|
||||
#if ETL_CPP11_SUPPORTED
|
||||
ibip_buffer_spsc_atomic(ibip_buffer_spsc_atomic&&) = delete;
|
||||
ibip_buffer_spsc_atomic& operator =(ibip_buffer_spsc_atomic&&) = delete;
|
||||
#endif
|
||||
|
||||
T* const p_buffer;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// A fixed capacity bipartite buffer.
|
||||
/// This buffer supports concurrent access by one producer and one consumer.
|
||||
/// \tparam T The type this buffer should support.
|
||||
/// \tparam SIZE The maximum capacity of the buffer.
|
||||
/// \tparam MEMORY_MODEL The memory model for the buffer. Determines the type of the internal counter variables.
|
||||
//***************************************************************************
|
||||
template <typename T, const size_t SIZE, const size_t MEMORY_MODEL = etl::memory_model::MEMORY_MODEL_LARGE>
|
||||
class bip_buffer_spsc_atomic : public ibip_buffer_spsc_atomic<T, MEMORY_MODEL>
|
||||
{
|
||||
private:
|
||||
typedef typename etl::ibip_buffer_spsc_atomic<T, MEMORY_MODEL> base_t;
|
||||
|
||||
public:
|
||||
typedef typename base_t::size_type size_type;
|
||||
|
||||
private:
|
||||
static const size_type RESERVED_SIZE = size_type(SIZE);
|
||||
|
||||
public:
|
||||
ETL_STATIC_ASSERT((SIZE <= (etl::integral_limits<size_type>::max)), "Size too large for memory model");
|
||||
|
||||
static const size_type MAX_SIZE = size_type(SIZE);
|
||||
|
||||
bip_buffer_spsc_atomic()
|
||||
: base_t(reinterpret_cast<T*>(&buffer[0]), RESERVED_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// The uninitialised buffer of T used in the bip_buffer_spsc.
|
||||
typename etl::aligned_storage<sizeof(T), etl::alignment_of<T>::value>::type buffer[RESERVED_SIZE];
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* ETL_HAS_ATOMIC */
|
||||
|
||||
#endif /* ETL_BIP_BUFFER_SPSC_ATOMIC_INCLUDED */
|
||||
@ -32,6 +32,7 @@ set(TEST_SOURCE_FILES
|
||||
test_atomic_gcc_sync.cpp
|
||||
test_atomic_std.cpp
|
||||
test_binary.cpp
|
||||
test_bip_buffer_spsc_atomic.cpp
|
||||
test_bitset.cpp
|
||||
test_bit_stream.cpp
|
||||
test_bloom_filter.cpp
|
||||
|
||||
@ -255,6 +255,7 @@
|
||||
<Unit filename="../../include/etl/basic_string.h" />
|
||||
<Unit filename="../../include/etl/basic_string_stream.h" />
|
||||
<Unit filename="../../include/etl/binary.h" />
|
||||
<Unit filename="../../include/etl/bip_buffer_spsc_atomic.h" />
|
||||
<Unit filename="../../include/etl/bit_stream.h" />
|
||||
<Unit filename="../../include/etl/bitset.h" />
|
||||
<Unit filename="../../include/etl/bloom_filter.h" />
|
||||
@ -514,6 +515,7 @@
|
||||
<Unit filename="../test_atomic_gcc_sync.cpp" />
|
||||
<Unit filename="../test_atomic_std.cpp" />
|
||||
<Unit filename="../test_binary.cpp" />
|
||||
<Unit filename="../test_bip_buffer_spsc_atomic.cpp" />
|
||||
<Unit filename="../test_bit_stream.cpp" />
|
||||
<Unit filename="../test_bitset.cpp" />
|
||||
<Unit filename="../test_bloom_filter.cpp" />
|
||||
|
||||
356
test/test_bip_buffer_spsc_atomic.cpp
Normal file
356
test/test_bip_buffer_spsc_atomic.cpp
Normal file
@ -0,0 +1,356 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2021 jwellbelove
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "unit_test_framework.h"
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
#include "etl/bip_buffer_spsc_atomic.h"
|
||||
|
||||
#include "data.h"
|
||||
|
||||
#if ETL_HAS_ATOMIC
|
||||
|
||||
#if defined(ETL_TARGET_OS_WINDOWS)
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#define REALTIME_TEST 0
|
||||
|
||||
namespace
|
||||
{
|
||||
SUITE(test_bip_buffer_spsc_atomic)
|
||||
{
|
||||
//*************************************************************************
|
||||
TEST(test_constructor)
|
||||
{
|
||||
etl::bip_buffer_spsc_atomic<int, 5> stream;
|
||||
|
||||
CHECK_EQUAL(5U, stream.max_size());
|
||||
CHECK_EQUAL(5U, stream.capacity());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_size_write_read)
|
||||
{
|
||||
etl::bip_buffer_spsc_atomic<int, 5> stream;
|
||||
etl::ibip_buffer_spsc_atomic<int>& istream = stream;
|
||||
|
||||
// Verify empty buffer
|
||||
CHECK_EQUAL(0U, stream.size());
|
||||
CHECK(stream.empty());
|
||||
CHECK((stream.max_size() / 2U) <= stream.available());
|
||||
CHECK(stream.available() <= stream.max_size());
|
||||
|
||||
auto reader = istream.read_reserve(1U);
|
||||
CHECK_EQUAL(0U, reader.size());
|
||||
|
||||
// Write partially
|
||||
auto writer = istream.write_reserve(1U);
|
||||
CHECK_EQUAL(1U, writer.size());
|
||||
writer[0] = 1;
|
||||
|
||||
CHECK(stream.empty());
|
||||
|
||||
istream.write_commit(writer); // 1 * * * *
|
||||
CHECK_EQUAL(1U, stream.size());
|
||||
|
||||
writer = istream.write_reserve(1U);
|
||||
CHECK_EQUAL(1U, writer.size());
|
||||
writer[0] = 2;
|
||||
|
||||
istream.write_commit(writer); // 1 2 * * *
|
||||
CHECK_EQUAL(2U, stream.size());
|
||||
|
||||
// Write to capacity
|
||||
writer = istream.write_reserve(istream.available());
|
||||
CHECK_EQUAL(3U, writer.size());
|
||||
writer[0] = 3;
|
||||
writer[1] = 4;
|
||||
writer[2] = 5;
|
||||
|
||||
istream.write_commit(writer); // 1 2 3 4 5
|
||||
|
||||
// Verify full buffer
|
||||
CHECK_EQUAL(0U, stream.available());
|
||||
CHECK(stream.full());
|
||||
CHECK((stream.max_size() - 1) <= stream.size());
|
||||
CHECK(stream.size() <= stream.max_size());
|
||||
|
||||
writer = istream.write_reserve(1U);
|
||||
CHECK_EQUAL(0U, writer.size());
|
||||
|
||||
// Read partially
|
||||
reader = istream.read_reserve(1U);
|
||||
CHECK_EQUAL(1U, reader.size());
|
||||
CHECK_EQUAL(1, reader[0]);
|
||||
CHECK_EQUAL(5U, stream.size());
|
||||
|
||||
istream.read_commit(reader); // * 2 3 4 5
|
||||
CHECK_EQUAL(4U, stream.size());
|
||||
|
||||
reader = istream.read_reserve(1U);
|
||||
CHECK_EQUAL(1U, reader.size());
|
||||
CHECK_EQUAL(2, reader[0]);
|
||||
CHECK_EQUAL(4U, stream.size());
|
||||
|
||||
istream.read_commit(reader); // * * 3 4 5
|
||||
CHECK_EQUAL(3U, stream.size());
|
||||
|
||||
// Write to wraparound area (one element remains reserved)
|
||||
writer = istream.write_reserve(istream.available());
|
||||
CHECK_EQUAL(1U, writer.size());
|
||||
CHECK_EQUAL(1U, stream.available());
|
||||
writer[0] = 6;
|
||||
|
||||
istream.write_commit(writer); // 6 * 3 4 5
|
||||
|
||||
// Verify full buffer
|
||||
CHECK_EQUAL(0U, stream.available());
|
||||
CHECK(stream.full());
|
||||
CHECK((stream.max_size() - 1) <= stream.size());
|
||||
CHECK(stream.size() <= stream.max_size());
|
||||
|
||||
writer = istream.write_reserve(1U);
|
||||
CHECK_EQUAL(0U, writer.size());
|
||||
|
||||
// Read to capacity
|
||||
reader = istream.read_reserve(istream.size());
|
||||
CHECK_EQUAL(3U, reader.size());
|
||||
CHECK_EQUAL(3, reader[0]);
|
||||
CHECK_EQUAL(4, reader[1]);
|
||||
CHECK_EQUAL(5, reader[2]);
|
||||
CHECK_EQUAL(4U, stream.size());
|
||||
|
||||
istream.read_commit(reader); // * 2 * * *
|
||||
CHECK_EQUAL(1U, stream.size());
|
||||
|
||||
reader = istream.read_reserve(istream.size());
|
||||
CHECK_EQUAL(1U, reader.size());
|
||||
CHECK_EQUAL(6, reader[0]);
|
||||
CHECK_EQUAL(1U, stream.size());
|
||||
|
||||
istream.read_commit(reader); // * * * * *
|
||||
|
||||
// Verify empty buffer
|
||||
CHECK_EQUAL(0U, stream.size());
|
||||
CHECK(stream.empty());
|
||||
CHECK((stream.max_size() / 2U) <= stream.available());
|
||||
CHECK(stream.available() <= stream.max_size());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_clear)
|
||||
{
|
||||
etl::bip_buffer_spsc_atomic<int, 5> stream;
|
||||
etl::ibip_buffer_spsc_atomic<int>& istream = stream;
|
||||
|
||||
CHECK(stream.empty());
|
||||
|
||||
// Write the whole buffer
|
||||
auto writer = istream.write_reserve(istream.capacity());
|
||||
// data is committed without set to valid value (it won't be read anyway)
|
||||
istream.write_commit(writer);
|
||||
CHECK(stream.full());
|
||||
|
||||
istream.clear();
|
||||
CHECK(stream.empty());
|
||||
|
||||
// Repeat to see that clear() resets the internal state completely and correctly
|
||||
writer = istream.write_reserve(istream.capacity());
|
||||
// data is committed without set to valid value (it won't be read anyway)
|
||||
istream.write_commit(writer);
|
||||
CHECK(stream.full());
|
||||
|
||||
istream.clear();
|
||||
CHECK(stream.empty());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_partial_commits)
|
||||
{
|
||||
etl::bip_buffer_spsc_atomic<int, 5> stream;
|
||||
etl::ibip_buffer_spsc_atomic<int>& istream = stream;
|
||||
|
||||
// Write reserve available
|
||||
auto writer_1 = istream.write_reserve(istream.capacity());
|
||||
CHECK_EQUAL(5U, stream.available());
|
||||
CHECK_EQUAL(5U, writer_1.size());
|
||||
|
||||
// Write and commit partially
|
||||
writer_1[0] = 1;
|
||||
writer_1[1] = 2;
|
||||
writer_1[2] = 3;
|
||||
writer_1[3] = 4;
|
||||
|
||||
// Cannot commit subspans with offset
|
||||
CHECK_THROW(istream.write_commit(writer_1.subspan(1U, 3U)), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.write_commit(writer_1.subspan(2U, 2U)), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.write_commit(writer_1.subspan(3U, 1U)), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
CHECK_NO_THROW(istream.write_commit(writer_1.subspan(0U, 4U))); // 1 2 3 4 *
|
||||
CHECK_EQUAL(4U, stream.size());
|
||||
|
||||
// Can only commit once for each reserve (provided they don't cover a valid area)
|
||||
CHECK_THROW(istream.write_commit(writer_1), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.write_commit(writer_1.subspan(0U, 4U)), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
// Read reserve available
|
||||
auto reader_1 = istream.read_reserve(istream.capacity());
|
||||
CHECK_EQUAL(4U, reader_1.size());
|
||||
|
||||
// Read and commit partially
|
||||
CHECK_EQUAL(1, reader_1[0]);
|
||||
CHECK_EQUAL(2, reader_1[1]);
|
||||
CHECK_EQUAL(3, reader_1[2]);
|
||||
|
||||
// Cannot commit subspans with offset
|
||||
CHECK_THROW(istream.read_commit(reader_1.subspan(1U, 2U)), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.read_commit(reader_1.subspan(2U, 1U)), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
CHECK_NO_THROW(istream.read_commit(reader_1.subspan(0U, 3U))); // * * * 4 *
|
||||
CHECK_EQUAL(1U, stream.size());
|
||||
|
||||
// Can only commit once for each reserve (provided they don't cover a valid area)
|
||||
CHECK_THROW(istream.read_commit(reader_1), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.read_commit(reader_1.subspan(0U, 3U)), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
// Write reserve available
|
||||
auto writer_2 = istream.write_reserve(istream.capacity());
|
||||
CHECK_EQUAL(2U, stream.available());
|
||||
CHECK_EQUAL(2U, writer_2.size());
|
||||
|
||||
// Write and commit partially
|
||||
writer_2[0] = 5;
|
||||
CHECK_NO_THROW(istream.write_commit(writer_2.subspan(0U, 1U))); // 5 * * 4 *
|
||||
CHECK_EQUAL(2U, stream.size());
|
||||
// Even though the second committed span could have fit at the end,
|
||||
// the reservation asked for the largest consecutive block,
|
||||
// which resulted in a wraparound span to be allocated
|
||||
|
||||
// Can only commit once for each reserve (provided they don't cover a valid area)
|
||||
CHECK_THROW(istream.write_commit(writer_1), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.write_commit(writer_2), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.write_commit(writer_2.subspan(0U, 1U)), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
// Read reserve available
|
||||
auto reader_2 = istream.read_reserve(istream.capacity());
|
||||
CHECK_EQUAL(1U, reader_2.size());
|
||||
|
||||
// Read and commit
|
||||
CHECK_EQUAL(4, reader_2[0]);
|
||||
CHECK_NO_THROW(istream.read_commit(reader_2)); // 5 * * * *
|
||||
CHECK_EQUAL(1U, stream.size());
|
||||
|
||||
// Can only commit once for each reserve (provided they don't cover a valid area)
|
||||
CHECK_THROW(istream.read_commit(reader_1), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.read_commit(reader_2), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
// Read reserve available
|
||||
auto reader_3 = istream.read_reserve(istream.capacity());
|
||||
CHECK_EQUAL(1U, reader_3.size());
|
||||
|
||||
// Read and commit
|
||||
CHECK_EQUAL(5, reader_3[0]);
|
||||
CHECK_NO_THROW(istream.read_commit(reader_3)); // * * * * *
|
||||
CHECK_EQUAL(0U, stream.size());
|
||||
|
||||
// Can only commit once for each reserve (provided they don't cover a valid area)
|
||||
CHECK_THROW(istream.read_commit(reader_1), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.read_commit(reader_2), etl::bip_buffer_reserve_invalid);
|
||||
CHECK_THROW(istream.read_commit(reader_3), etl::bip_buffer_reserve_invalid);
|
||||
|
||||
CHECK(stream.empty());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
#if REALTIME_TEST && defined(ETL_COMPILER_MICROSOFT)
|
||||
#if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported
|
||||
#define FIX_PROCESSOR_AFFINITY1 SetThreadAffinityMask(GetCurrentThread(), 1);
|
||||
#define FIX_PROCESSOR_AFFINITY2 SetThreadAffinityMask(GetCurrentThread(), 2);
|
||||
#else
|
||||
#error No thread priority modifier defined
|
||||
#endif
|
||||
|
||||
etl::bip_buffer_spsc_atomic<int, 10> stream;
|
||||
|
||||
const size_t LENGTH = 1000000;
|
||||
|
||||
void timer_event()
|
||||
{
|
||||
FIX_PROCESSOR_AFFINITY1;
|
||||
|
||||
const size_t write_chunk_size = 7;
|
||||
size_t tick = 0;
|
||||
|
||||
while (tick < LENGTH)
|
||||
{
|
||||
auto writer = stream.write_reserve(std::min(write_chunk_size, LENGTH - tick));
|
||||
for (auto& item : writer)
|
||||
{
|
||||
item = tick++;
|
||||
}
|
||||
stream.write_commit(writer);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(bip_buffer_threads)
|
||||
{
|
||||
FIX_PROCESSOR_AFFINITY2;
|
||||
|
||||
const size_t read_chunk_size = stream.capacity();
|
||||
|
||||
std::vector<int> tick_list;
|
||||
tick_list.reserve(LENGTH);
|
||||
|
||||
std::thread t1(timer_event);
|
||||
|
||||
while (tick_list.size() < LENGTH)
|
||||
{
|
||||
reader = stream.read_reserve(read_chunk_size);
|
||||
tick_list.insert(tick_list.end(), reader.begin(), reader.end());
|
||||
stream.read_commit(reader);
|
||||
}
|
||||
|
||||
// Join the thread with the main thread
|
||||
t1.join();
|
||||
|
||||
CHECK_EQUAL(LENGTH, tick_list.size());
|
||||
|
||||
for (size_t i = 0; i < LENGTH; i++)
|
||||
{
|
||||
CHECK_EQUAL(i, tick_list[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif // ETL_HAS_ATOMIC
|
||||
@ -1489,6 +1489,7 @@
|
||||
<ClInclude Include="..\..\include\etl\atomic\atomic_std.h" />
|
||||
<ClInclude Include="..\..\include\etl\basic_format_spec.h" />
|
||||
<ClInclude Include="..\..\include\etl\basic_string_stream.h" />
|
||||
<ClInclude Include="..\..\include\etl\bip_buffer_spsc_atomic.h" />
|
||||
<ClInclude Include="..\..\include\etl\bit_stream.h" />
|
||||
<ClInclude Include="..\..\include\etl\bresenham_line.h" />
|
||||
<ClInclude Include="..\..\include\etl\buffer_descriptors.h" />
|
||||
@ -6560,6 +6561,7 @@
|
||||
<ClCompile Include="..\test_array_wrapper.cpp" />
|
||||
<ClCompile Include="..\test_atomic_std.cpp" />
|
||||
<ClCompile Include="..\test_binary.cpp" />
|
||||
<ClCompile Include="..\test_bip_buffer_spsc_atomic.cpp" />
|
||||
<ClCompile Include="..\test_bitset.cpp" />
|
||||
<ClCompile Include="..\test_bloom_filter.cpp" />
|
||||
<ClCompile Include="..\test_bsd_checksum.cpp" />
|
||||
|
||||
@ -1125,6 +1125,9 @@
|
||||
<ClInclude Include="..\..\include\etl\mem_cast.h">
|
||||
<Filter>ETL\Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\etl\bip_buffer_spsc_atomic.h">
|
||||
<Filter>ETL\Containers</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\etl\private\variant_legacy.h">
|
||||
<Filter>ETL\Private</Filter>
|
||||
</ClInclude>
|
||||
@ -2561,6 +2564,300 @@
|
||||
<ClCompile Include="..\sanity-check\utility.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\variant.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\variant_pool.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\vector.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\version.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\visitor.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\wformat_spec.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\wstring.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\wstring_stream.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_ccitt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_cdma2000.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_darc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_dvbs2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_ebu.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_icode.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_itu.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_maxim.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_rohc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc8_wcdma.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_ccitt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_kermit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_modbus.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_usb.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_xmodem.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_aug_ccitt.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_genibus.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_x25.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_buypass.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_profibus.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_c.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_bzip2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_posix.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_mpeg2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc64_ecma.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_cdma2000.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_dds110.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_dectr.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_dectx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_dnp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_en13757.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_mcrf4xx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_riello.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_t10dif.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_maxim.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_teledisk.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_tms37157.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_a.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_d.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_q.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_jamcrc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc32_xfer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_cdma2000.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_darc.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_dvbs2.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_ebu.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_icode.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_itu.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_maxim.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc8_wcdma.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_a.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_arc.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_buypass.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_dds110.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_dectr.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_dectx.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_dnp.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_en13757.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_maxim.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_mcrf4xx.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_profibus.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_riello.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_t10dif.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_teledisk.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_tms37157.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc32_d.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc32_jamcrc.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc32_q.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc32_xfer.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_crc16_arc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_histogram.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_correlation.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_covariance.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_standard_deviation.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_variance.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_threshold.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_invert.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_limiter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_quantize.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\crc16_cdma2000.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_gamma.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_rescale.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_rms.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_mean.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_bip_buffer_spsc_atomic.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\correlation.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\covariance.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\gamma.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\sanity-check\variance.h.t.cpp">
|
||||
<Filter>Source Files\Sanity Checks</Filter>
|
||||
</ClCompile>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user