/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef rmf_avro_Buffer_hh__ #define rmf_avro_Buffer_hh__ #ifndef _WIN32 #include #endif #include #include #include "../Config.hh" #include "detail/BufferDetail.hh" #include "detail/BufferDetailIterator.hh" /** * \file Buffer.hh * * \brief Definitions for InputBuffer and OutputBuffer classes * **/ namespace rmf_avro { class OutputBuffer; class InputBuffer; /** * The OutputBuffer (write-only buffer) * * Use cases for OutputBuffer * * - write message to buffer using ostream class or directly * - append messages to headers * - building up streams of messages via append * - converting to read-only buffers for sending * - extracting parts of the messages into read-only buffers * * -# ASIO access: * - write to a buffer(s) by asio using iterator * - convert to read buffer for deserializing * * OutputBuffer is assignable and copy-constructable. On copy or assignment, * only a pointer is copied, so the two resulting copies are identical, so * modifying one will modify both. **/ class AVRO_DECL OutputBuffer { public: typedef detail::size_type size_type; typedef detail::data_type data_type; /** * The asio library expects a const_iterator (the const-ness refers to the * fact that the underlying avro of buffers will not be modified, even * though the data in those buffers is being modified). The iterator * provides the list of addresses an operation can write to. **/ typedef detail::OutputBufferIterator const_iterator; /** * Default constructor. Will pre-allocate at least the requested size, but * can grow larger on demand. * * Destructor uses the default, which resets a shared pointer, deleting the * underlying data if no other copies of exist. * * Copy and assignment operators are not explicitly provided because the * default ones work fine. The default makes only a shallow copy, so the * copies will refer to the same memory. This is required by asio * functions, which will implicitly make copies for asynchronous * operations. Therefore, the user must be careful that if they create * multiple copies of the same OutputBuffer, only one is being modified * otherwise undefined behavior may occur. * **/ OutputBuffer(size_type reserveSize = 0) : pimpl_(new detail::BufferImpl) { if(reserveSize) { reserve(reserveSize); } } /** * Reserve enough space for a wroteTo() operation. When using writeTo(), * the buffer will grow dynamically as needed. But when using the iterator * to write (followed by wroteTo()), data may only be written to the space * available, so this ensures there is enough room in the buffer before * the write operation. **/ void reserve(size_type reserveSize) { pimpl_->reserveFreeSpace(reserveSize); } /** * Write a block of data to the buffer. The buffer size will automatically * grow if the size is larger than what is currently free. **/ size_type writeTo(const data_type *data, size_type size) { return pimpl_->writeTo(data, size); } /** * Write a single value to the buffer. The buffer size will automatically * grow if there is not room for the byte. The value must be a * "fundamental" type, e.g. int, float, etc. (otherwise use the other * writeTo tests). **/ template void writeTo(T val) { pimpl_->writeTo(val, boost::is_fundamental()); } /** * Update the state of the buffer after writing through the iterator * interface. This function exists primarily for the boost:asio which * writes directly to the buffer using its iterator. In this case, the * internal state of the buffer does not reflect that the data was written * This informs the buffer how much data was written. * * The buffer does not automatically resize in this case, the bytes written * cannot exceed the amount of free space. Attempting to write more will * throw a std::length_error exception. **/ size_type wroteTo(size_type size) { int wrote = 0; if(size) { if(size > freeSpace()) { throw std::length_error("Impossible to write more data than free space"); } wrote = pimpl_->wroteTo(size); } return wrote; } /** * Does the buffer have any data? **/ bool empty() const { return (pimpl_->size()==0); } /** * Returns the size of the buffer, in bytes. */ size_type size() const { return pimpl_->size(); } /** * Returns the current free space that is available to write to in the * buffer, in bytes. This is not a strict limit in size, as writeTo() can * automatically increase capacity if necessary. **/ size_type freeSpace() const { return pimpl_->freeSpace(); } /** * Appends the data in the argument to the end of this buffer. The * argument can be either an InputBuffer or OutputBuffer. * **/ template void append(const BufferType &buf) { // don't append an empty buffer if(buf.size()) { pimpl_->append(*(buf.pimpl_.get())); } } /** * Return an iterator pointing to the first data chunk of this buffer * that may be written to. **/ const_iterator begin() const { return const_iterator(pimpl_->beginWrite()); } /** * Return the end iterator for writing. **/ const_iterator end() const { return const_iterator(pimpl_->endWrite()); } /** * Discard any data in this buffer. **/ void discardData() { pimpl_->discardData(); } /** * Discard the specified number of bytes from this data, starting at the beginning. * Throws if the size is greater than the number of bytes. **/ void discardData(size_t bytes) { if(bytes > 0) { if(bytes < pimpl_->size()) { pimpl_->discardData(bytes); } else if(bytes == pimpl_->size()) { pimpl_->discardData(); } else { throw std::out_of_range("trying to discard more data than exists"); } } } /** * Remove bytes from this buffer, starting from the beginning, and place * them into a new buffer. Throws if the number of requested bytes exceeds * the size of the buffer. Data and freeSpace in the buffer after bytes * remains in this buffer. **/ InputBuffer extractData(size_type bytes); /** * Remove all bytes from this buffer, returning them in a new buffer. * After removing data, some freeSpace may remain in this buffer. **/ InputBuffer extractData(); /** * Clone this buffer, creating a copy that contains the same data. **/ OutputBuffer clone() const { detail::BufferImpl::SharedPtr newImpl(new detail::BufferImpl(*pimpl_)); return OutputBuffer(newImpl); } /** * Add unmanaged data to the buffer. The buffer will not automatically * free the data, but it will call the supplied function when the data is * no longer referenced by the buffer (or copies of the buffer). **/ void appendForeignData(const data_type *data, size_type size, const detail::free_func &func) { pimpl_->appendForeignData(data, size, func); } /** * Returns the number of chunks that contain free space. **/ int numChunks() const { return pimpl_->numFreeChunks(); } /** * Returns the number of chunks that contain data **/ int numDataChunks() const { return pimpl_->numDataChunks(); } private: friend class InputBuffer; friend class BufferReader; explicit OutputBuffer(const detail::BufferImpl::SharedPtr &pimpl) : pimpl_(pimpl) { } detail::BufferImpl::SharedPtr pimpl_; ///< Must never be null. }; /** * The InputBuffer (read-only buffer) * * InputBuffer is an immutable buffer which that may be constructed from an * OutputBuffer, or several of OutputBuffer's methods. Once the data is * transfered to an InputBuffer it cannot be modified, only read (via * BufferReader, istream, or its iterator). * * Assignments and copies are shallow copies. * * -# ASIO access: - iterate using const_iterator for sending messages * **/ class AVRO_DECL InputBuffer { public: typedef detail::size_type size_type; typedef detail::data_type data_type; // needed for asio typedef detail::InputBufferIterator const_iterator; /** * Default InputBuffer creates an empty buffer. * * Copy/assignment functions use the default ones. They will do a shallow * copy, and because InputBuffer is immutable, the copies will be * identical. * * Destructor also uses the default, which resets a shared pointer, * deleting the underlying data if no other copies of exist. **/ InputBuffer() : pimpl_(new detail::BufferImpl) { } /** * Construct an InputBuffer that contains the contents of an OutputBuffer. * The two buffers will have the same contents, but this copy will be * immutable, while the the OutputBuffer may still be written to. * * If you wish to move the data from the OutputBuffer to a new InputBuffer * (leaving only free space in the OutputBuffer), * OutputBuffer::extractData() will do this more efficiently. * * Implicit conversion is allowed. **/ InputBuffer(const OutputBuffer &src) : pimpl_(new detail::BufferImpl(*src.pimpl_)) { } /** * Does the buffer have any data? **/ bool empty() const { return (pimpl_->size() == 0); } /** * Returns the size of the buffer, in bytes. **/ size_type size() const { return pimpl_->size(); } /** * Return an iterator pointing to the first data chunk of this buffer * that contains data. **/ const_iterator begin() const { return const_iterator(pimpl_->beginRead()); } /** * Return the end iterator. **/ const_iterator end() const { return const_iterator(pimpl_->endRead()); } /** * Returns the number of chunks containing data. **/ int numChunks() const { return pimpl_->numDataChunks(); } private: friend class OutputBuffer; // for append function friend class istreambuf; friend class BufferReader; explicit InputBuffer(const detail::BufferImpl::SharedPtr &pimpl) : pimpl_(pimpl) { } /** * Class to indicate that a copy of a OutputBuffer to InputBuffer should be * a shallow copy, used to enable reading of the contents of an * OutputBuffer without need to convert it to InputBuffer using a deep * copy. It is private and only used by BufferReader and istreambuf * classes. * * Writing to an OutputBuffer while it is being read may lead to undefined * behavior. **/ class ShallowCopy {}; /** * Make a shallow copy of an OutputBuffer in order to read it without * causing conversion overhead. **/ InputBuffer(const OutputBuffer &src, const ShallowCopy &) : pimpl_(src.pimpl_) { } /** * Make a shallow copy of an InputBuffer. The default copy constructor * already provides shallow copy, this is just provided for generic * algorithms that wish to treat InputBuffer and OutputBuffer in the same * manner. **/ InputBuffer(const InputBuffer &src, const ShallowCopy &) : pimpl_(src.pimpl_) { } detail::BufferImpl::ConstSharedPtr pimpl_; ///< Must never be null. }; /* * Implementations of some OutputBuffer functions are inlined here * because InputBuffer definition was required before. */ inline InputBuffer OutputBuffer::extractData() { detail::BufferImpl::SharedPtr newImpl(new detail::BufferImpl); if(pimpl_->size()) { pimpl_->extractData(*newImpl); } return InputBuffer(newImpl); } inline InputBuffer OutputBuffer::extractData(size_type bytes) { if(bytes > pimpl_->size()) { throw std::out_of_range("trying to extract more data than exists"); } detail::BufferImpl::SharedPtr newImpl(new detail::BufferImpl); if(bytes > 0) { if(bytes < pimpl_->size()) { pimpl_->extractData(*newImpl, bytes); } else { pimpl_->extractData(*newImpl); } } return InputBuffer(newImpl); } /** * Create an array of iovec structures from the buffer. This utility is used * to support writev and readv function calls. The caller should ensure the * buffer object is not deleted while using the iovec vector. * * If the BufferType is an InputBuffer, the iovec will point to the data that * already exists in the buffer, for reading. * * If the BufferType is an OutputBuffer, the iovec will point to the free * space, which may be written to. Before writing, the caller should call * OutputBuffer::reserve() to create enough room for the desired write (which * can be verified by calling OutputBuffer::freeSpace()), and after writing, * they MUST call OutputBuffer::wroteTo(), otherwise the buffer will not know * the space is not free anymore. * **/ template inline void toIovec(BufferType &buf, std::vector &iov) { const int chunks = buf.numChunks(); iov.resize(chunks); typename BufferType::const_iterator iter = buf.begin(); for (int i = 0; i < chunks; ++i) { iov[i].iov_base = const_cast(iter->data()); iov[i].iov_len = iter->size(); ++iter; } } } // namespace #endif