123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- //
- // FIFOBuffer.h
- //
- // Library: Foundation
- // Package: Core
- // Module: FIFOBuffer
- //
- // Definition of the FIFOBuffer class.
- //
- // Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
- // and Contributors.
- //
- // SPDX-License-Identifier: BSL-1.0
- //
- #ifndef Foundation_FIFOBuffer_INCLUDED
- #define Foundation_FIFOBuffer_INCLUDED
- #include "Poco/Foundation.h"
- #include "Poco/Exception.h"
- #include "Poco/Buffer.h"
- #include "Poco/BasicEvent.h"
- #include "Poco/Mutex.h"
- #include "Poco/Format.h"
- namespace Poco {
- template <class T>
- class BasicFIFOBuffer
- /// A simple buffer class with support for re-entrant,
- /// FIFO-style read/write operations, as well as (optional)
- /// empty/non-empty/full (i.e. writable/readable) transition
- /// notifications. Buffer can be flagged with end-of-file and
- /// error flags, which renders it un-readable/writable.
- ///
- /// Critical portions of code are protected by a recursive mutex.
- /// However, to achieve thread-safety in cases where multiple
- /// member function calls are involved and have to be atomic,
- /// the mutex must be locked externally.
- ///
- /// Buffer size, as well as amount of unread data and
- /// available space introspections are supported as well.
- ///
- /// This class is useful anywhere where a FIFO functionality
- /// is needed.
- {
- public:
- typedef T Type;
- mutable Poco::BasicEvent<bool> writable;
- /// Event indicating "writability" of the buffer,
- /// triggered as follows:
- ///
- /// * when buffer transitions from non-full to full,
- /// Writable event observers are notified, with
- /// false value as the argument
- ///
- /// * when buffer transitions from full to non-full,
- /// Writable event observers are notified, with
- /// true value as the argument
- mutable Poco::BasicEvent<bool> readable;
- /// Event indicating "readability" of the buffer,
- /// triggered 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
- BasicFIFOBuffer(std::size_t size, bool notify = false):
- _buffer(size),
- _begin(0),
- _used(0),
- _notify(notify),
- _eof(false),
- _error(false)
- /// Creates the FIFOBuffer.
- {
- }
- BasicFIFOBuffer(T* pBuffer, std::size_t size, bool notify = false):
- _buffer(pBuffer, size),
- _begin(0),
- _used(0),
- _notify(notify),
- _eof(false),
- _error(false)
- /// Creates the FIFOBuffer.
- {
- }
- BasicFIFOBuffer(const T* pBuffer, std::size_t size, bool notify = false):
- _buffer(pBuffer, size),
- _begin(0),
- _used(size),
- _notify(notify),
- _eof(false),
- _error(false)
- /// Creates the FIFOBuffer.
- {
- }
- ~BasicFIFOBuffer()
- /// Destroys the FIFOBuffer.
- {
- }
-
- void resize(std::size_t newSize, bool preserveContent = true)
- /// Resizes the buffer. If preserveContent is true,
- /// the content of the old buffer is preserved.
- /// New size can be larger or smaller than
- /// the current size, but it must not be 0.
- /// Additionally, if the new length is smaller
- /// than currently used length and preserveContent
- /// is true, InvalidAccessException is thrown.
- {
- Mutex::ScopedLock lock(_mutex);
- if (preserveContent && (newSize < _used))
- throw InvalidAccessException("Can not resize FIFO without data loss.");
-
- std::size_t usedBefore = _used;
- _buffer.resize(newSize, preserveContent);
- if (!preserveContent) _used = 0;
- if (_notify) notify(usedBefore);
- }
-
- std::size_t peek(T* pBuffer, std::size_t length) const
- /// Peeks into the data currently in the FIFO
- /// without actually extracting it.
- /// If length is zero, the return is immediate.
- /// If length is greater than used length,
- /// it is substituted with the the current FIFO
- /// used length.
- ///
- /// Returns the number of elements copied in the
- /// supplied buffer.
- {
- if (0 == length) return 0;
- Mutex::ScopedLock lock(_mutex);
- if (!isReadable()) return 0;
- if (length > _used) length = _used;
- std::memcpy(pBuffer, _buffer.begin() + _begin, length * sizeof(T));
- return length;
- }
-
- std::size_t peek(Poco::Buffer<T>& buffer, std::size_t length = 0) const
- /// Peeks into the data currently in the FIFO
- /// without actually extracting it.
- /// Resizes the supplied buffer to the size of
- /// data written to it. If length is not
- /// supplied by the caller or is greater than length
- /// of currently used data, the current FIFO used
- /// data length is substituted for it.
- ///
- /// Returns the number of elements copied in the
- /// supplied buffer.
- {
- Mutex::ScopedLock lock(_mutex);
- if (!isReadable()) return 0;
- if (0 == length || length > _used) length = _used;
- buffer.resize(length);
- return peek(buffer.begin(), length);
- }
-
- std::size_t read(T* pBuffer, std::size_t length)
- /// Copies the data currently in the FIFO
- /// into the supplied buffer, which must be
- /// preallocated to at least the length size
- /// before calling this function.
- ///
- /// Returns the size of the copied data.
- {
- if (0 == length) return 0;
- Mutex::ScopedLock lock(_mutex);
- if (!isReadable()) return 0;
- std::size_t usedBefore = _used;
- std::size_t readLen = peek(pBuffer, length);
- poco_assert (_used >= readLen);
- _used -= readLen;
- if (0 == _used) _begin = 0;
- else _begin += length;
- if (_notify) notify(usedBefore);
- return readLen;
- }
-
- std::size_t read(Poco::Buffer<T>& buffer, std::size_t length = 0)
- /// Copies the data currently in the FIFO
- /// into the supplied buffer.
- /// Resizes the supplied buffer to the size of
- /// data written to it.
- ///
- /// Returns the size of the copied data.
- {
- Mutex::ScopedLock lock(_mutex);
- if (!isReadable()) return 0;
- std::size_t usedBefore = _used;
- std::size_t readLen = peek(buffer, length);
- poco_assert (_used >= readLen);
- _used -= readLen;
- if (0 == _used) _begin = 0;
- else _begin += length;
- if (_notify) notify(usedBefore);
- return readLen;
- }
- std::size_t write(const T* pBuffer, std::size_t length)
- /// Writes data from supplied buffer to the FIFO buffer.
- /// If there is no sufficient space for the whole
- /// buffer to be written, data up to available
- /// length is written.
- /// The length of data to be written is determined from the
- /// length argument. Function does nothing and returns zero
- /// if length argument is equal to zero.
- ///
- /// Returns the length of data written.
- {
- if (0 == length) return 0;
- Mutex::ScopedLock lock(_mutex);
-
- if (!isWritable()) return 0;
-
- if (_buffer.size() - (_begin + _used) < length)
- {
- std::memmove(_buffer.begin(), begin(), _used * sizeof(T));
- _begin = 0;
- }
- std::size_t usedBefore = _used;
- std::size_t available = _buffer.size() - _used - _begin;
- std::size_t len = length > available ? available : length;
- std::memcpy(begin() + _used, pBuffer, len * sizeof(T));
- _used += len;
- poco_assert (_used <= _buffer.size());
- if (_notify) notify(usedBefore);
- return len;
- }
- std::size_t write(const Buffer<T>& buffer, std::size_t length = 0)
- /// Writes data from supplied buffer to the FIFO buffer.
- /// If there is no sufficient space for the whole
- /// buffer to be written, data up to available
- /// length is written.
- /// The length of data to be written is determined from the
- /// length argument or buffer size (when length argument is
- /// default zero or greater than buffer size).
- ///
- /// Returns the length of data written.
- {
- if (length == 0 || length > buffer.size())
- length = buffer.size();
- return write(buffer.begin(), length);
- }
- std::size_t size() const
- /// Returns the size of the buffer.
- {
- return _buffer.size();
- }
-
- std::size_t used() const
- /// Returns the size of the used portion of the buffer.
- {
- return _used;
- }
-
- std::size_t available() const
- /// Returns the size of the available portion of the buffer.
- {
- return size() - _used;
- }
- void drain(std::size_t length = 0)
- /// Drains length number of elements from the buffer.
- /// If length is zero or greater than buffer current
- /// content length, buffer is emptied.
- {
- Mutex::ScopedLock lock(_mutex);
- std::size_t usedBefore = _used;
- if (0 == length || length >= _used)
- {
- _begin = 0;
- _used = 0;
- }
- else
- {
- _begin += length;
- _used -= length;
- }
- if (_notify) notify(usedBefore);
- }
- void copy(const T* ptr, std::size_t length)
- /// Copies the supplied data to the buffer and adjusts
- /// the used buffer size.
- {
- poco_check_ptr(ptr);
- if (0 == length) return;
- Mutex::ScopedLock lock(_mutex);
-
- if (length > available())
- throw Poco::InvalidAccessException("Cannot extend buffer.");
-
- if (!isWritable())
- throw Poco::InvalidAccessException("Buffer not writable.");
- std::memcpy(begin() + _used, ptr, length * sizeof(T));
- std::size_t usedBefore = _used;
- _used += length;
- if (_notify) notify(usedBefore);
- }
- void advance(std::size_t length)
- /// Advances buffer by length elements.
- /// Should be called AFTER the data
- /// was copied into the buffer.
- {
- Mutex::ScopedLock lock(_mutex);
- if (length > available())
- throw Poco::InvalidAccessException("Cannot extend buffer.");
-
- if (!isWritable())
- throw Poco::InvalidAccessException("Buffer not writable.");
- if (_buffer.size() - (_begin + _used) < length)
- {
- std::memmove(_buffer.begin(), begin(), _used * sizeof(T));
- _begin = 0;
- }
- std::size_t usedBefore = _used;
- _used += length;
- if (_notify) notify(usedBefore);
- }
- T* begin()
- /// Returns the pointer to the beginning of the buffer.
- {
- Mutex::ScopedLock lock(_mutex);
- if (_begin != 0)
- {
- // Move the data to the start of the buffer so begin() and next()
- // always return consistent pointers with each other and allow writing
- // to the end of the buffer.
- std::memmove(_buffer.begin(), _buffer.begin() + _begin, _used * sizeof(T));
- _begin = 0;
- }
- return _buffer.begin();
- }
- T* next()
- /// Returns the pointer to the next available position in the buffer.
- {
- Mutex::ScopedLock lock(_mutex);
- return begin() + _used;
- }
- T& operator [] (std::size_t index)
- /// Returns value at index position.
- /// Throws InvalidAccessException if index is larger than
- /// the last valid (used) buffer position.
- {
- Mutex::ScopedLock lock(_mutex);
- if (index >= _used)
- throw InvalidAccessException(format("Index out of bounds: %z (max index allowed: %z)", index, _used - 1));
- return _buffer[_begin + index];
- }
- const T& operator [] (std::size_t index) const
- /// Returns value at index position.
- /// Throws InvalidAccessException if index is larger than
- /// the last valid (used) buffer position.
- {
- Mutex::ScopedLock lock(_mutex);
- if (index >= _used)
- throw InvalidAccessException(format("Index out of bounds: %z (max index allowed: %z)", index, _used - 1));
- return _buffer[_begin + index];
- }
- const Buffer<T>& buffer() const
- /// Returns const reference to the underlying buffer.
- {
- return _buffer;
- }
-
- void setError(bool error = true)
- /// Sets the error flag on the buffer and empties it.
- /// If notifications are enabled, they will be triggered
- /// if appropriate.
- ///
- /// Setting error flag to true prevents reading and writing
- /// to the buffer; to re-enable FIFOBuffer for reading/writing,
- /// the error flag must be set to false.
- {
- if (error)
- {
- bool f = false;
- Mutex::ScopedLock lock(_mutex);
- if (error && isReadable() && _notify) readable.notify(this, f);
- if (error && isWritable() && _notify) writable.notify(this, f);
- _error = error;
- _used = 0;
- }
- else
- {
- bool t = true;
- Mutex::ScopedLock lock(_mutex);
- _error = false;
- if (_notify && !_eof) writable.notify(this, t);
- }
- }
- bool isValid() const
- /// Returns true if error flag is not set on the buffer,
- /// otherwise returns false.
- {
- return !_error;
- }
- void setEOF(bool eof = true)
- /// Sets end-of-file flag on the buffer.
- ///
- /// Setting EOF flag to true prevents writing to the
- /// buffer; reading from the buffer will still be
- /// allowed until all data present in the buffer at the
- /// EOF set time is drained. After that, to re-enable
- /// FIFOBuffer for reading/writing, EOF must be
- /// set to false.
- ///
- /// Setting EOF flag to false clears EOF state if it
- /// was previously set. If EOF was not set, it has no
- /// effect.
- {
- Mutex::ScopedLock lock(_mutex);
- bool flag = !eof;
- if (_notify) writable.notify(this, flag);
- _eof = eof;
- }
- bool hasEOF() const
- /// Returns true if EOF flag has been set.
- {
- return _eof;
- }
- bool isEOF() const
- /// Returns true if EOF flag has been set and buffer is empty.
- {
- return isEmpty() && _eof;
- }
- bool isEmpty() const
- /// Returns true is buffer is empty, false otherwise.
- {
- return 0 == _used;
- }
- bool isFull() const
- /// Returns true is buffer is full, false otherwise.
- {
- return size() == _used;
- }
- bool isReadable() const
- /// Returns true if buffer contains data and is not
- /// in error state.
- {
- return !isEmpty() && isValid();
- }
- bool isWritable() const
- /// Returns true if buffer is not full and is not
- /// in error state.
- {
- return !isFull() && isValid() && !_eof;
- }
- void setNotify(bool notify = true)
- /// Enables/disables notifications.
- {
- _notify = notify;
- }
- bool getNotify() const
- /// Returns true if notifications are enabled, false otherwise.
- {
- return _notify;
- }
- Mutex& mutex()
- /// Returns reference to mutex.
- {
- return _mutex;
- }
- 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);
-
- if (usedBefore == _buffer.size() && _used < _buffer.size())
- writable.notify(this, t);
- else if (usedBefore < _buffer.size() && _used == _buffer.size())
- writable.notify(this, f);
- }
- BasicFIFOBuffer();
- BasicFIFOBuffer(const BasicFIFOBuffer&);
- BasicFIFOBuffer& operator = (const BasicFIFOBuffer&);
- Buffer<T> _buffer;
- std::size_t _begin;
- std::size_t _used;
- bool _notify;
- mutable Mutex _mutex;
- bool _eof;
- bool _error;
- };
- //
- // We provide an instantiation for char
- //
- typedef BasicFIFOBuffer<char> FIFOBuffer;
- } // namespace Poco
- #endif // Foundation_FIFOBuffer_INCLUDED
|