mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Replace concurrentqueue with ringbuffer
This commit is contained in:
@@ -11,9 +11,11 @@
|
||||
#include <condition_variable>
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include "icsneo/third-party/concurrentqueue/blockingconcurrentqueue.h"
|
||||
#include "icsneo/communication/ringbuffer.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
#define ICSNEO_DRIVER_RINGBUFFER_SIZE (512 * 1024)
|
||||
class Driver {
|
||||
public:
|
||||
Driver(const device_eventhandler_t& handler) : report(handler) {}
|
||||
@@ -24,10 +26,11 @@ public:
|
||||
virtual void awaitModeChangeComplete() {}
|
||||
virtual bool isDisconnected() { return disconnected; };
|
||||
virtual bool close() = 0;
|
||||
bool read(std::vector<uint8_t>& bytes, size_t limit = 0);
|
||||
bool readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout = std::chrono::milliseconds(100), size_t limit = 0);
|
||||
bool write(const std::vector<uint8_t>& bytes);
|
||||
virtual bool isEthernet() const { return false; }
|
||||
bool readAvailable() { return readBuffer.size() > 0; }
|
||||
RingBuffer& getReadBuffer() { return readBuffer; }
|
||||
|
||||
device_eventhandler_t report;
|
||||
|
||||
@@ -54,7 +57,8 @@ protected:
|
||||
virtual bool writeQueueAlmostFull() { return writeQueue.size_approx() > (writeQueueSize * 3 / 4); }
|
||||
virtual bool writeInternal(const std::vector<uint8_t>& b) { return writeQueue.enqueue(WriteOperation(b)); }
|
||||
|
||||
moodycamel::BlockingConcurrentQueue<uint8_t> readQueue;
|
||||
|
||||
RingBuffer readBuffer = RingBuffer(ICSNEO_DRIVER_RINGBUFFER_SIZE);
|
||||
moodycamel::BlockingConcurrentQueue<WriteOperation> writeQueue;
|
||||
std::thread readThread, writeThread;
|
||||
std::atomic<bool> closing{false};
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "icsneo/communication/packet.h"
|
||||
#include "icsneo/communication/ringbuffer.h"
|
||||
#include "icsneo/api/eventmanager.h"
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
#define ICSNEO_PACKETIZER_BUFFER_SIZE (512 * 1024)
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class Packetizer {
|
||||
@@ -22,7 +21,7 @@ public:
|
||||
|
||||
std::vector<uint8_t>& packetWrap(std::vector<uint8_t>& data, bool shortFormat) const;
|
||||
|
||||
bool input(const std::vector<uint8_t>& bytes);
|
||||
bool input(RingBuffer& bytes);
|
||||
std::vector<std::shared_ptr<Packet>> output();
|
||||
|
||||
bool disableChecksum = false; // Even for short packets
|
||||
@@ -37,95 +36,6 @@ private:
|
||||
GetData
|
||||
};
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
private:
|
||||
constexpr static size_t mBufferSize = ICSNEO_PACKETIZER_BUFFER_SIZE;
|
||||
|
||||
size_t mStartOffset;
|
||||
size_t mSize;
|
||||
uint8_t mData[mBufferSize];
|
||||
|
||||
public:
|
||||
RingBuffer(void)
|
||||
: mStartOffset(0)
|
||||
, mSize(0)
|
||||
{
|
||||
(void)memset(mData, 0, mBufferSize);
|
||||
}
|
||||
|
||||
const uint8_t& operator [](size_t offset) { return Get(offset); }
|
||||
size_t size(void) { return mSize; }
|
||||
void pop_front(void)
|
||||
{
|
||||
Erase_front(1);
|
||||
}
|
||||
|
||||
void Erase_front(size_t count)
|
||||
{
|
||||
if (mSize < count)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Underflow");
|
||||
}
|
||||
mStartOffset = (mStartOffset + count) % mBufferSize;
|
||||
mSize -= count;
|
||||
}
|
||||
|
||||
const uint8_t& Get(size_t offset)
|
||||
{
|
||||
if (offset >= mSize)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Index out of range");
|
||||
}
|
||||
return *Resolve(offset);
|
||||
}
|
||||
|
||||
void Copy(const std::vector<uint8_t>& source)
|
||||
{
|
||||
const auto inputSize = source.size();
|
||||
const auto octetsAvailable = (mBufferSize - mSize);
|
||||
|
||||
if (inputSize > octetsAvailable)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Out of memory");
|
||||
}
|
||||
|
||||
const auto octetsAvailableTail = (octetsAvailable - mStartOffset);
|
||||
const auto octetsToWrap = (inputSize > octetsAvailableTail) ? (inputSize - octetsAvailableTail) : 0;
|
||||
const auto octetsToAppend = (inputSize - octetsToWrap);
|
||||
|
||||
(void)memcpy(Resolve(mSize), source.data(), octetsToAppend);
|
||||
if (octetsToWrap > 0)
|
||||
{
|
||||
(void)memcpy(mData, &source.data()[octetsToAppend], octetsToWrap);
|
||||
}
|
||||
mSize += inputSize;
|
||||
}
|
||||
|
||||
void CopyTo(uint8_t* dest, size_t startIndex, size_t length)
|
||||
{
|
||||
if ((startIndex + length) > mSize)
|
||||
{
|
||||
throw std::runtime_error("RingBuffer: Index out of range");
|
||||
}
|
||||
|
||||
const auto octetsToReadHead = std::min<size_t>((mBufferSize - mStartOffset - startIndex), length);
|
||||
const auto octetsToReadTail = (length - octetsToReadHead);
|
||||
|
||||
(void)memcpy(dest, Resolve(startIndex), octetsToReadHead);
|
||||
if (octetsToReadTail > 0)
|
||||
{
|
||||
(void)memcpy(&dest[octetsToReadHead], mData, octetsToReadTail);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
inline uint8_t* Resolve(size_t offset)
|
||||
{
|
||||
return &mData[(mStartOffset + offset) % mBufferSize];
|
||||
}
|
||||
};
|
||||
|
||||
ReadState state = ReadState::SearchForHeader;
|
||||
|
||||
int currentIndex = 0;
|
||||
@@ -134,7 +44,6 @@ private:
|
||||
bool checksum = false;
|
||||
bool gotGoodPackets = false; // Tracks whether we've ever gotten a good packet
|
||||
Packet packet;
|
||||
RingBuffer bytes;
|
||||
|
||||
std::vector<std::shared_ptr<Packet>> processedPackets;
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef _RINGBUFFER_H_
|
||||
#define _RINGBUFFER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#if __cplusplus >= 202002L
|
||||
#include <bit>
|
||||
#endif
|
||||
namespace icsneo {
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
private:
|
||||
static constexpr size_t RoundUp(size_t size) {
|
||||
if (size == 0) {
|
||||
// Avoid underflow when decrementing later
|
||||
return 1;
|
||||
} else if (size >= SIZE_MAX) {
|
||||
// overflow case - resolve to max size
|
||||
return MaxSize;
|
||||
}
|
||||
#if __cplusplus >= 202002L
|
||||
// c++20 gives us countl_zero which should be more effecient on most platforms
|
||||
auto lzero = std::countl_zero(size - 1);
|
||||
auto shift = (sizeof(size_t) * 8) - lzero;
|
||||
return 1ull << shift;
|
||||
#else
|
||||
// Bit twiddling magic! See http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
--size;
|
||||
size |= size >> 1;
|
||||
size |= size >> 2;
|
||||
size |= size >> 4;
|
||||
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
|
||||
size |= size >> (i << 3);
|
||||
}
|
||||
++size;
|
||||
return size;
|
||||
#endif
|
||||
}
|
||||
//static_assert(std::atomic<size_t>::is_always_lock_free, "RingBuffer cursor types are not lock-free");
|
||||
std::atomic<size_t> readCursor;
|
||||
std::atomic<size_t> writeCursor;
|
||||
// Use this to mask the cursor values to the buffer size. This is set to capacity - 1 where capacity is always an integral power of 2 (2, 4, 8, 16, etc)
|
||||
size_t mask;
|
||||
uint8_t* buf;
|
||||
|
||||
public:
|
||||
static constexpr auto MaxSize = 1ull << ((8 * sizeof(size_t)) - 1);
|
||||
RingBuffer(size_t bufferSize);
|
||||
~RingBuffer();
|
||||
const uint8_t& operator[](size_t offset) const;
|
||||
size_t size() const;
|
||||
void pop_front();
|
||||
void pop(size_t count);
|
||||
const uint8_t& get(size_t offset) const;
|
||||
bool write(const uint8_t* addr, size_t count);
|
||||
bool write(const std::vector<uint8_t>& source);
|
||||
bool read(uint8_t* dest, size_t startIndex, size_t length) const;
|
||||
void clear();
|
||||
constexpr size_t capacity() const {
|
||||
return mask + 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
inline uint8_t* resolve(size_t cursor, size_t offset) const {
|
||||
return &buf[(cursor + offset) & mask];
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user