FIFOBuffer events and tests

This commit is contained in:
Aleksandar Fabijanic
2012-04-27 04:41:50 +00:00
parent a9cef39021
commit d7306cc7d9
3 changed files with 131 additions and 16 deletions

View File

@@ -43,6 +43,7 @@
#include "Poco/Foundation.h" #include "Poco/Foundation.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include "Poco/Buffer.h" #include "Poco/Buffer.h"
#include "Poco/BasicEvent.h"
#include "Poco/Mutex.h" #include "Poco/Mutex.h"
#include "Poco/Format.h" #include "Poco/Format.h"
@@ -53,17 +54,43 @@ namespace Poco {
template <class T> template <class T>
class FIFOBuffer class FIFOBuffer
/// A simple buffer class with support for re-entrant, /// A simple buffer class with support for re-entrant,
/// FIFO-style read/write operations as well as /// FIFO-style read/write operations. as well as
/// introspection into the size of buffer, amount of unread data /// empty/full transition notifications. Buffer size
/// and available space. /// introspection as well as amount of unread data and
/// available space are supported as well.
/// ///
/// This class is useful everywhere where a FIFO functionality /// This class is useful anywhere where a FIFO functionality
/// is needed. /// is needed.
{ {
public: public:
FIFOBuffer(std::size_t size): mutable Poco::BasicEvent<bool> Writeable;
/// Event indicating "writeability" of the buffer,
/// triggerred as follows:
///
/// * when buffer transitions from non-full to full,
/// Writeable event observers are notified, with
/// false value as the argument
///
/// * when buffer transitions from full to non-full,
/// Writeable event observers are notified, with
/// true value as the argument
mutable Poco::BasicEvent<bool> Readable;
/// Event indicating "readability" of the buffer,
/// triggerred as follows:
///
/// * when buffer transitions from non-empty to empty,
/// Readable event observers are notified, with false
/// value as the argument
///
/// * when FIFOBuffer transitions from empty to non-empty,
/// Readable event observers are notified, with true value
/// as the argument
FIFOBuffer(std::size_t size, bool notify = false):
_buffer(size), _buffer(size),
_used(0) _used(0),
_notify(notify)
/// Creates and allocates the FIFOBuffer. /// Creates and allocates the FIFOBuffer.
{ {
} }
@@ -87,11 +114,13 @@ public:
if (preserveContent && (newSize < _used)) if (preserveContent && (newSize < _used))
throw InvalidAccessException("Can not resize FIFO without data loss."); throw InvalidAccessException("Can not resize FIFO without data loss.");
std::size_t usedBefore = _used;
_buffer.resize(newSize, preserveContent); _buffer.resize(newSize, preserveContent);
if (!preserveContent) _used = 0; if (!preserveContent) _used = 0;
if (_notify) notify(usedBefore);
} }
Buffer<T>& peek(Poco::Buffer<T>& buffer, std::size_t length = 0) const std::size_t peek(Poco::Buffer<T>& buffer, std::size_t length = 0) const
/// Peeks into the data currently in the FIFO /// Peeks into the data currently in the FIFO
/// without actually extracting it. /// without actually extracting it.
/// Resizes the supplied buffer to the size of /// Resizes the supplied buffer to the size of
@@ -108,10 +137,10 @@ public:
buffer.resize(length); buffer.resize(length);
std::memcpy(buffer.begin(), _buffer.begin(), length * sizeof(T)); std::memcpy(buffer.begin(), _buffer.begin(), length * sizeof(T));
return buffer; return length;
} }
Buffer<T>& read(Poco::Buffer<T>& buffer, std::size_t length = 0) std::size_t read(Poco::Buffer<T>& buffer, std::size_t length = 0)
/// Copies the data currently in the FIFO /// Copies the data currently in the FIFO
/// into the supplied buffer. /// into the supplied buffer.
/// Resizes the supplied buffer to the size of /// Resizes the supplied buffer to the size of
@@ -121,13 +150,18 @@ public:
{ {
Mutex::ScopedLock lock(_mutex); Mutex::ScopedLock lock(_mutex);
std::size_t usedBefore = _used; if (0 == _used) return 0;
peek(buffer, length);
_used -= buffer.size();
if (_used)
std::memmove(_buffer.begin(), _buffer.begin() + usedBefore - _used, _used);
return buffer; std::size_t usedBefore = _used;
std::size_t readLen = peek(buffer, length);
poco_assert (_used >= readLen);
_used -= readLen;
if (_used > 0)
std::memmove(_buffer.begin(), _buffer.begin() + usedBefore - _used, _used);
if (_notify) notify(usedBefore);
return readLen;
} }
std::size_t write(const Buffer<T>& buffer) std::size_t write(const Buffer<T>& buffer)
@@ -141,11 +175,13 @@ public:
Mutex::ScopedLock lock(_mutex); Mutex::ScopedLock lock(_mutex);
poco_assert (_used <= _buffer.size()); poco_assert (_used <= _buffer.size());
std::size_t usedBefore = _used;
std::size_t available = _buffer.size() - _used; std::size_t available = _buffer.size() - _used;
std::size_t len = buffer.size() > available ? available : buffer.size(); std::size_t len = buffer.size() > available ? available : buffer.size();
std::memcpy(_buffer.begin() + _used, buffer.begin(), len * sizeof(T)); std::memcpy(_buffer.begin() + _used, buffer.begin(), len * sizeof(T));
_used += len; _used += len;
poco_assert (_used <= _buffer.size()); poco_assert (_used <= _buffer.size());
if (_notify) notify(usedBefore);
return len; return len;
} }
@@ -197,12 +233,26 @@ public:
} }
private: private:
void notify(std::size_t usedBefore)
{
bool t = true, f = false;
if (usedBefore == 0 && _used > 0)
Readable.notify(this, t);
else if (usedBefore > 0 && 0 == _used)
Readable.notify(this, f);
else if (usedBefore == _buffer.size() && _used < _buffer.size())
Writeable.notify(this, t);
else if (usedBefore < _buffer.size() && _used == _buffer.size())
Writeable.notify(this, f);
}
FIFOBuffer(); FIFOBuffer();
FIFOBuffer(const FIFOBuffer&); FIFOBuffer(const FIFOBuffer&);
FIFOBuffer& operator = (const FIFOBuffer&); FIFOBuffer& operator = (const FIFOBuffer&);
Buffer<T> _buffer; Buffer<T> _buffer;
std::size_t _used; std::size_t _used;
bool _notify;
mutable Mutex _mutex; mutable Mutex _mutex;
}; };

View File

@@ -43,6 +43,8 @@
#include "Poco/AtomicCounter.h" #include "Poco/AtomicCounter.h"
#include "Poco/Nullable.h" #include "Poco/Nullable.h"
#include "Poco/Ascii.h" #include "Poco/Ascii.h"
#include "Poco/BasicEvent.h"
#include "Poco/Delegate.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <iostream> #include <iostream>
#include <vector> #include <vector>
@@ -59,6 +61,8 @@ using Poco::FIFOBuffer;
using Poco::AtomicCounter; using Poco::AtomicCounter;
using Poco::Nullable; using Poco::Nullable;
using Poco::Ascii; using Poco::Ascii;
using Poco::BasicEvent;
using Poco::delegate;
using Poco::InvalidAccessException; using Poco::InvalidAccessException;
@@ -214,15 +218,22 @@ void CoreTest::testFIFOBufferChar()
{ {
typedef char T; typedef char T;
FIFOBuffer<T> f(20); FIFOBuffer<T> f(20, true);
Buffer<T> b(10); Buffer<T> b(10);
std::vector<T> v; std::vector<T> v;
f.Readable += delegate(this, &CoreTest::onReadable);
f.Writeable += delegate(this, &CoreTest::onReadable);
for (T c = '0'; c < '0' + 10; ++c) for (T c = '0'; c < '0' + 10; ++c)
v.push_back(c); v.push_back(c);
std::memcpy(b.begin(), &v[0], sizeof(T) * v.size()); std::memcpy(b.begin(), &v[0], sizeof(T) * v.size());
assert(0 == _notToReadable);
assert(0 == _readableToNot);
f.write(b); f.write(b);
assert(1 == _notToReadable);
assert(0 == _readableToNot);
assert (20 == f.size()); assert (20 == f.size());
assert (10 == f.used()); assert (10 == f.used());
assert (!f.isEmpty()); assert (!f.isEmpty());
@@ -290,7 +301,12 @@ void CoreTest::testFIFOBufferChar()
try { T i = f[5]; fail ("must fail"); } try { T i = f[5]; fail ("must fail"); }
catch (InvalidAccessException&) { } catch (InvalidAccessException&) { }
assert(1 == _notToReadable);
assert(0 == _readableToNot);
f.read(b, 6); f.read(b, 6);
assert(1 == _notToReadable);
assert(1 == _readableToNot);
assert (5 == b.size()); assert (5 == b.size());
assert (20 == f.size()); assert (20 == f.size());
assert (0 == f.used()); assert (0 == f.used());
@@ -298,7 +314,12 @@ void CoreTest::testFIFOBufferChar()
catch (InvalidAccessException&) { } catch (InvalidAccessException&) { }
assert (f.isEmpty()); assert (f.isEmpty());
assert(1 == _notToReadable);
assert(1 == _readableToNot);
assert (5 == f.write(b)); assert (5 == f.write(b));
assert(2 == _notToReadable);
assert(1 == _readableToNot);
assert (20 == f.size()); assert (20 == f.size());
assert (5 == f.used()); assert (5 == f.used());
assert (!f.isEmpty()); assert (!f.isEmpty());
@@ -318,10 +339,28 @@ void CoreTest::testFIFOBufferChar()
assert ('i' == f[3]); assert ('i' == f[3]);
assert ('j' == f[4]); assert ('j' == f[4]);
assert(2 == _notToReadable);
assert(1 == _readableToNot);
f.resize(3, false); f.resize(3, false);
assert(2 == _notToReadable);
assert(2 == _readableToNot);
assert (3 == f.size()); assert (3 == f.size());
assert (0 == f.used()); assert (0 == f.used());
assert (f.isEmpty()); assert (f.isEmpty());
b.resize(3);
b[0] = 'x';
b[1] = 'y';
b[2] = 'z';
assert(2 == _notToReadable);
assert(2 == _readableToNot);
f.write(b);
assert(3 == _notToReadable);
assert(2 == _readableToNot);
f.Readable -= delegate(this, &CoreTest::onReadable);
f.Writeable -= delegate(this, &CoreTest::onReadable);
} }
@@ -592,8 +631,26 @@ void CoreTest::testAscii()
} }
void CoreTest::onReadable(bool& b)
{
if (b) ++_notToReadable;
else ++_readableToNot;
};
void CoreTest::onWriteable(bool& b)
{
if (b) ++_notToWriteable;
else ++_writeableToNot;
}
void CoreTest::setUp() void CoreTest::setUp()
{ {
_readableToNot = 0;
_notToReadable = 0;
_writeableToNot = 0;
_notToWriteable = 0;
} }

View File

@@ -63,7 +63,15 @@ public:
static CppUnit::Test* suite(); static CppUnit::Test* suite();
protected:
void onReadable(bool& b);
void onWriteable(bool& b);
private: private:
int _readableToNot;
int _notToReadable;
int _writeableToNot;
int _notToWriteable;
}; };