| /* |
| * 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 |
| * |
| * https://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 avro_Buffer_hh__ |
| #define avro_Buffer_hh__ |
| |
| #ifndef _WIN32 |
| #include <sys/uio.h> |
| #endif |
| #include <utility> |
| #include <vector> |
| |
| #include "../Config.hh" |
| #include "detail/BufferDetail.hh" |
| #include "detail/BufferDetailIterator.hh" |
| |
| /** |
| * \file Buffer.hh |
| * |
| * \brief Definitions for InputBuffer and OutputBuffer classes |
| * |
| **/ |
| |
| namespace 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. |
| * |
| **/ |
| |
| explicit 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<typename T> |
| void writeTo(T val) { |
| pimpl_->writeTo(val, std::is_fundamental<T>()); |
| } |
| |
| /** |
| * 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<class BufferType> |
| 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(detail::BufferImpl::SharedPtr pimpl) : pimpl_(std::move(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. |
| **/ |
| // NOLINTNEXTLINE(google-explicit-constructor) |
| 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); |
| } |
| |
| #ifndef _WIN32 |
| /** |
| * 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<class BufferType> |
| inline void toIovec(BufferType &buf, std::vector<struct iovec> &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<typename BufferType::data_type *>(iter->data()); |
| iov[i].iov_len = iter->size(); |
| ++iter; |
| } |
| } |
| #endif |
| |
| } // namespace avro |
| |
| #endif |