// // FIFOBuffer.h // // $Id: //poco/1.4/Foundation/include/Poco/FIFOBuffer.h#2 $ // // Library: Foundation // Package: Core // Module: FIFOBuffer // // Definition of the FIFOBuffer class. // // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #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 FIFOBuffer /// A simple buffer class with support for re-entrant, /// FIFO-style read/write operations. as well as /// empty/full transition notifications. Buffer size /// introspection as well as amount of unread data and /// available space are supported as well. /// /// This class is useful anywhere where a FIFO functionality /// is needed. { public: mutable Poco::BasicEvent 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 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), _used(0), _notify(notify) /// Creates and allocates the FIFOBuffer. { } ~FIFOBuffer() /// 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(Poco::Buffer& 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, the current FIFO used length /// is substituted for it. /// /// Returns the reference to the buffer. { Mutex::ScopedLock lock(_mutex); if (0 == length || length > _used) length = _used; poco_assert (length <= _buffer.size()); buffer.resize(length); std::memcpy(buffer.begin(), _buffer.begin(), length * sizeof(T)); return length; } std::size_t read(Poco::Buffer& 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 reference to the buffer. { Mutex::ScopedLock lock(_mutex); if (0 == _used) return 0; 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& buffer) /// Writes data.size() to the FIFO buffer. /// If there is no sufficient space for the whole /// buffer to be written, data up to available /// length is written. /// /// Returns the length of data written. { Mutex::ScopedLock lock(_mutex); poco_assert (_used <= _buffer.size()); std::size_t usedBefore = _used; std::size_t available = _buffer.size() - _used; std::size_t len = buffer.size() > available ? available : buffer.size(); std::memcpy(_buffer.begin() + _used, buffer.begin(), len * sizeof(T)); _used += len; poco_assert (_used <= _buffer.size()); if (_notify) notify(usedBefore); return len; } 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 _buffer.size() - _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. { if (index >= _used) throw InvalidAccessException(format("Index out of bounds: %z (max index allowed: %z)", index, _used - 1)); return _buffer[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. { if (index >= _used) throw InvalidAccessException(format("Index out of bounds: %z (max index allowed: %z)", index, _used - 1)); return _buffer[index]; } bool isEmpty() const /// Returns true is buffer is empty, flase otherwise. { return 0 == _used; } 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(const FIFOBuffer&); FIFOBuffer& operator = (const FIFOBuffer&); Buffer _buffer; std::size_t _used; bool _notify; mutable Mutex _mutex; }; } // namespace Poco #endif // Foundation_FIFOBuffer_INCLUDED