286 lines
8.2 KiB
C++
286 lines
8.2 KiB
C++
/**
|
|
* @file SignalRingBuffer.h
|
|
* @brief Thread-safe fixed-capacity circular buffer of (float64 time, float64 value) pairs.
|
|
*
|
|
* Write() is called from the UDPSClient receive thread; Read/ReadRange are called
|
|
* from the StreamHub push thread. A FastPollingMutexSem protects all operations.
|
|
*/
|
|
|
|
#ifndef STREAMHUB_SIGNAL_RING_BUFFER_H_
|
|
#define STREAMHUB_SIGNAL_RING_BUFFER_H_
|
|
|
|
#include "CompilerTypes.h"
|
|
#include "FastPollingMutexSem.h"
|
|
#include <string.h>
|
|
|
|
namespace StreamHub {
|
|
|
|
using MARTe::uint32;
|
|
using MARTe::float64;
|
|
using MARTe::FastPollingMutexSem;
|
|
|
|
/**
|
|
* @brief Circular buffer of (time, value) float64 pairs.
|
|
*
|
|
* When full, Write() silently overwrites the oldest entry.
|
|
*/
|
|
class SignalRingBuffer {
|
|
public:
|
|
|
|
SignalRingBuffer();
|
|
~SignalRingBuffer();
|
|
|
|
/**
|
|
* @brief Allocate buffer for @p maxPts points.
|
|
* Frees any previously allocated buffer.
|
|
* @return true on success.
|
|
*/
|
|
bool Allocate(uint32 maxPts);
|
|
|
|
/**
|
|
* @brief Write one (t, v) pair. Called from the receive thread.
|
|
*/
|
|
void Write(float64 t, float64 v);
|
|
|
|
/**
|
|
* @brief Copy the last (oldest-to-newest) min(n, count) points into tOut/vOut.
|
|
* @return Number of points actually written.
|
|
*/
|
|
uint32 Read(float64 *tOut, float64 *vOut, uint32 n) const;
|
|
|
|
/**
|
|
* @brief Copy all points with time in [t0, t1] into tOut/vOut (up to maxOut).
|
|
* Uses binary search — requires monotonically non-decreasing time values.
|
|
* @return Number of points actually written.
|
|
*/
|
|
uint32 ReadRange(float64 t0, float64 t1,
|
|
float64 *tOut, float64 *vOut, uint32 maxOut) const;
|
|
|
|
/**
|
|
* @brief Copy points written after @p cursor (a previously returned
|
|
* TotalWritten() value) into tOut/vOut, oldest first. On overrun the
|
|
* read is clamped to the oldest available point. @p cursor is advanced
|
|
* by the number of points consumed (capped by @p maxOut).
|
|
* @return Number of points actually written.
|
|
*/
|
|
uint32 ReadSince(MARTe::uint64 &cursor,
|
|
float64 *tOut, float64 *vOut, uint32 maxOut) const;
|
|
|
|
/** @return Total number of points ever written (monotonic). */
|
|
MARTe::uint64 TotalWritten() const;
|
|
|
|
/** @return Current number of stored points (≤ capacity). */
|
|
uint32 Count() const;
|
|
|
|
/** @brief Discard all stored points. */
|
|
void Clear();
|
|
|
|
private:
|
|
float64 *tBuf;
|
|
float64 *vBuf;
|
|
uint32 capacity;
|
|
uint32 head; /* next write position */
|
|
uint32 count; /* number of valid entries */
|
|
MARTe::uint64 totalWritten; /* monotonic write counter */
|
|
mutable FastPollingMutexSem mutex;
|
|
};
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Inline implementation */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
inline SignalRingBuffer::SignalRingBuffer()
|
|
: tBuf(static_cast<float64 *>(0)),
|
|
vBuf(static_cast<float64 *>(0)),
|
|
capacity(0u),
|
|
head(0u),
|
|
count(0u),
|
|
totalWritten(0u) {
|
|
}
|
|
|
|
inline SignalRingBuffer::~SignalRingBuffer() {
|
|
delete[] tBuf;
|
|
delete[] vBuf;
|
|
}
|
|
|
|
inline bool SignalRingBuffer::Allocate(uint32 maxPts) {
|
|
if (maxPts == 0u) { return false; }
|
|
|
|
/* Allocate outside the lock, swap under it (readers may be active). */
|
|
float64 *newT = new float64[maxPts];
|
|
float64 *newV = new float64[maxPts];
|
|
if ((newT == static_cast<float64 *>(0)) ||
|
|
(newV == static_cast<float64 *>(0))) {
|
|
delete[] newT;
|
|
delete[] newV;
|
|
return false;
|
|
}
|
|
|
|
(void) mutex.FastLock();
|
|
float64 *oldT = tBuf;
|
|
float64 *oldV = vBuf;
|
|
tBuf = newT;
|
|
vBuf = newV;
|
|
capacity = maxPts;
|
|
head = 0u;
|
|
count = 0u;
|
|
totalWritten = 0u;
|
|
mutex.FastUnLock();
|
|
|
|
delete[] oldT;
|
|
delete[] oldV;
|
|
return true;
|
|
}
|
|
|
|
inline void SignalRingBuffer::Write(float64 t, float64 v) {
|
|
(void) mutex.FastLock();
|
|
if (capacity > 0u) {
|
|
tBuf[head] = t;
|
|
vBuf[head] = v;
|
|
head = (head + 1u) % capacity;
|
|
if (count < capacity) { count++; }
|
|
totalWritten++;
|
|
}
|
|
mutex.FastUnLock();
|
|
}
|
|
|
|
inline uint32 SignalRingBuffer::Read(float64 *tOut, float64 *vOut, uint32 n) const {
|
|
if ((capacity == 0u) || (count == 0u)) { return 0u; }
|
|
(void) mutex.FastLock();
|
|
|
|
uint32 avail = count;
|
|
if (n > avail) { n = avail; }
|
|
|
|
/* Oldest entry index: (head - count + capacity) % capacity */
|
|
uint32 start = (head + capacity - avail) % capacity;
|
|
/* We want the last n entries: skip (avail - n) entries */
|
|
uint32 skip = avail - n;
|
|
start = (start + skip) % capacity;
|
|
|
|
for (uint32 i = 0u; i < n; i++) {
|
|
uint32 idx = (start + i) % capacity;
|
|
tOut[i] = tBuf[idx];
|
|
vOut[i] = vBuf[idx];
|
|
}
|
|
|
|
mutex.FastUnLock();
|
|
return n;
|
|
}
|
|
|
|
inline uint32 SignalRingBuffer::ReadRange(float64 t0, float64 t1,
|
|
float64 *tOut, float64 *vOut,
|
|
uint32 maxOut) const {
|
|
(void) mutex.FastLock();
|
|
const uint32 avail = count;
|
|
if ((capacity == 0u) || (avail == 0u) || (t1 < t0)) {
|
|
mutex.FastUnLock();
|
|
return 0u;
|
|
}
|
|
|
|
const uint32 oldest = (head + capacity - avail) % capacity;
|
|
|
|
/* Binary search over the logically-ordered ring (time is monotonically
|
|
* non-decreasing per ring). Port of Go ringbuf.go readRange. */
|
|
|
|
/* lo = first logical index with t >= t0 */
|
|
uint32 lo = 0u;
|
|
{
|
|
uint32 a = 0u;
|
|
uint32 b = avail;
|
|
while (a < b) {
|
|
const uint32 mid = a + ((b - a) >> 1);
|
|
const float64 tm = tBuf[(oldest + mid) % capacity];
|
|
if (tm < t0) { a = mid + 1u; } else { b = mid; }
|
|
}
|
|
lo = a;
|
|
}
|
|
/* hi = first logical index with t > t1 */
|
|
uint32 hi = lo;
|
|
{
|
|
uint32 a = lo;
|
|
uint32 b = avail;
|
|
while (a < b) {
|
|
const uint32 mid = a + ((b - a) >> 1);
|
|
const float64 tm = tBuf[(oldest + mid) % capacity];
|
|
if (tm <= t1) { a = mid + 1u; } else { b = mid; }
|
|
}
|
|
hi = a;
|
|
}
|
|
|
|
uint32 nOut = hi - lo;
|
|
if (nOut > maxOut) { nOut = maxOut; }
|
|
for (uint32 i = 0u; i < nOut; i++) {
|
|
const uint32 idx = (oldest + lo + i) % capacity;
|
|
tOut[i] = tBuf[idx];
|
|
vOut[i] = vBuf[idx];
|
|
}
|
|
|
|
mutex.FastUnLock();
|
|
return nOut;
|
|
}
|
|
|
|
inline uint32 SignalRingBuffer::ReadSince(MARTe::uint64 &cursor,
|
|
float64 *tOut, float64 *vOut,
|
|
uint32 maxOut) const {
|
|
(void) mutex.FastLock();
|
|
|
|
if (cursor > totalWritten) {
|
|
/* Ring was reset (re-Allocate) — restart from current position. */
|
|
cursor = totalWritten;
|
|
}
|
|
MARTe::uint64 pending = totalWritten - cursor;
|
|
if ((capacity == 0u) || (pending == 0u) || (maxOut == 0u)) {
|
|
mutex.FastUnLock();
|
|
return 0u;
|
|
}
|
|
/* Overrun: oldest retained point is totalWritten - count. */
|
|
if (pending > static_cast<MARTe::uint64>(count)) {
|
|
pending = static_cast<MARTe::uint64>(count);
|
|
cursor = totalWritten - pending;
|
|
}
|
|
|
|
uint32 n = (pending > static_cast<MARTe::uint64>(maxOut))
|
|
? maxOut : static_cast<uint32>(pending);
|
|
|
|
/* Logical index of the first pending point, relative to the oldest. */
|
|
const uint32 avail = count;
|
|
const uint32 oldest = (head + capacity - avail) % capacity;
|
|
const uint32 firstLogical =
|
|
avail - static_cast<uint32>(pending);
|
|
|
|
for (uint32 i = 0u; i < n; i++) {
|
|
const uint32 idx = (oldest + firstLogical + i) % capacity;
|
|
tOut[i] = tBuf[idx];
|
|
vOut[i] = vBuf[idx];
|
|
}
|
|
cursor += static_cast<MARTe::uint64>(n);
|
|
|
|
mutex.FastUnLock();
|
|
return n;
|
|
}
|
|
|
|
inline MARTe::uint64 SignalRingBuffer::TotalWritten() const {
|
|
(void) mutex.FastLock();
|
|
const MARTe::uint64 tw = totalWritten;
|
|
mutex.FastUnLock();
|
|
return tw;
|
|
}
|
|
|
|
inline uint32 SignalRingBuffer::Count() const {
|
|
(void) mutex.FastLock();
|
|
uint32 c = count;
|
|
mutex.FastUnLock();
|
|
return c;
|
|
}
|
|
|
|
inline void SignalRingBuffer::Clear() {
|
|
(void) mutex.FastLock();
|
|
head = 0u;
|
|
count = 0u;
|
|
mutex.FastUnLock();
|
|
}
|
|
|
|
} /* namespace StreamHub */
|
|
|
|
#endif /* STREAMHUB_SIGNAL_RING_BUFFER_H_ */
|