| /* |
| * 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_BufferStreambuf_hh__ |
| #define avro_BufferStreambuf_hh__ |
| |
| #include <utility> |
| |
| #include "Buffer.hh" |
| |
| /** \file BufferStreambuf.hh |
| \brief streambuf implementation for istream and ostream. |
| */ |
| |
| #ifdef min |
| #undef min |
| #endif |
| namespace avro { |
| |
| /** |
| * \brief Implementation of streambuf for use by the Buffer's ostream. |
| * |
| * This class derives from std::streambuf and implements the virtual functions |
| * needed to operate on OutputBuffer. The override functions are overflow and |
| * xsputn. Typically custom streambufs will also override sync for output, |
| * but we have no need since all writes are immediately stored in the buffer. |
| **/ |
| |
| class AVRO_DECL ostreambuf : public std::streambuf { |
| |
| public: |
| /// Default constructor creates a new OutputBuffer. |
| ostreambuf() : std::streambuf(), |
| buffer_() {} |
| |
| /// Construct using an existing OutputBuffer. |
| explicit ostreambuf(OutputBuffer &buffer) : std::streambuf(), |
| buffer_(buffer) {} |
| |
| /// Return the buffer. |
| const OutputBuffer &getBuffer() const { |
| return buffer_; |
| } |
| |
| protected: |
| /// Write a single character to the stream. |
| int_type overflow(int_type c) override { |
| buffer_.writeTo(static_cast<OutputBuffer::data_type>(c)); |
| return c; |
| } |
| |
| /// Write a block of characters to the stream. |
| std::streamsize xsputn(const char_type *s, std::streamsize n) override { |
| return buffer_.writeTo(s, static_cast<size_t>(n)); |
| } |
| |
| private: |
| OutputBuffer buffer_; |
| }; |
| |
| /** |
| * \brief Implementation of streambuf for use by the Buffer's istream. |
| * |
| * This class derives from std::streambuf and implements the virtual functions |
| * needed to operate on InputBuffer. The override functions are underflow, |
| * seekpos, showmanyc, and seek. This is considered a buffered streambuf, |
| * because it can access a chunk of the InputBuffer at a time, using the |
| * iterator interface. Because the input is already buffered, uflow is not |
| * required. pbackfail is not yet implemented but can be if necessary (the |
| * inherited behavior is to fail, and has yet to be a problem). |
| * |
| **/ |
| |
| class AVRO_DECL istreambuf : public std::streambuf { |
| |
| public: |
| /// Default constructor requires an InputBuffer to read from. |
| explicit istreambuf(InputBuffer buffer) : std::streambuf(), |
| buffer_(std::move(buffer)), |
| basePos_(0), |
| iter_(buffer_.begin()) { |
| setBuffer(); |
| } |
| |
| /// Default constructor converts an OutputBuffer to an InputBuffer |
| explicit istreambuf(const OutputBuffer &buffer) : std::streambuf(), |
| buffer_(buffer, InputBuffer::ShallowCopy()), |
| basePos_(0), |
| iter_(buffer_.begin()) { |
| setBuffer(); |
| } |
| |
| /// Return the buffer. |
| const InputBuffer &getBuffer() const { |
| return buffer_; |
| } |
| |
| protected: |
| /// The current chunk of data is exhausted, read the next chunk. |
| int_type underflow() override { |
| if (iter_ != buffer_.end()) { |
| basePos_ += (egptr() - eback()); |
| ++iter_; |
| } |
| return setBuffer(); |
| } |
| |
| /// Get a block of data from the stream. Overrides default behavior |
| /// to ignore eof characters that may reside in the stream. |
| std::streamsize xsgetn(char_type *c, std::streamsize len) override { |
| std::streamsize bytesCopied = 0; |
| |
| while (bytesCopied < len) { |
| |
| size_t inBuffer = egptr() - gptr(); |
| |
| if (inBuffer) { |
| auto remaining = static_cast<size_t>(len - bytesCopied); |
| size_t toCopy = std::min(inBuffer, remaining); |
| memcpy(c, gptr(), toCopy); |
| c += toCopy; |
| bytesCopied += toCopy; |
| gbump(toCopy); |
| } |
| |
| if (bytesCopied < len) { |
| underflow(); |
| if (iter_ == buffer_.end()) { |
| break; |
| } |
| } |
| } |
| |
| return bytesCopied; |
| } |
| |
| /// Special seek override to navigate InputBuffer chunks. |
| pos_type seekoff(off_type off, std::ios::seekdir dir, std::ios_base::openmode) override { |
| |
| off_type curpos = basePos_ + (gptr() - eback()); |
| off_type newpos = off; |
| |
| if (dir == std::ios::cur) { |
| newpos += curpos; |
| } else if (dir == std::ios::end) { |
| newpos += buffer_.size(); |
| } |
| // short circuit for tell() |
| if (newpos == curpos) { |
| return curpos; |
| } |
| |
| off_type endpos = basePos_ + (egptr() - eback()); |
| |
| // if the position is after our current buffer make |
| // sure it's not past the end of the buffer |
| if ((newpos > endpos) && (newpos > static_cast<off_type>(buffer_.size()))) { |
| return {-1}; |
| } |
| // if the new position is before our current iterator |
| // reset the iterator to the beginning |
| else if (newpos < basePos_) { |
| iter_ = buffer_.begin(); |
| basePos_ = 0; |
| setBuffer(); |
| endpos = (egptr() - eback()); |
| } |
| |
| // now if the new position is after the end of the buffer |
| // increase the buffer until it is not |
| while (newpos > endpos) { |
| istreambuf::underflow(); |
| endpos = basePos_ + (egptr() - eback()); |
| } |
| |
| setg(eback(), eback() + (newpos - basePos_), egptr()); |
| return newpos; |
| } |
| |
| /// Calls seekoff for implemention. |
| pos_type seekpos(pos_type pos, std::ios_base::openmode) override { |
| return istreambuf::seekoff(pos, std::ios::beg, std::ios_base::openmode(0)); |
| } |
| |
| /// Shows the number of bytes buffered in the current chunk, or next chunk if |
| /// current is exhausted. |
| std::streamsize showmanyc() override { |
| |
| // this function only gets called when the current buffer has been |
| // completely read, verify this is the case, and if so, underflow to |
| // fetch the next buffer |
| |
| if (egptr() - gptr() == 0) { |
| istreambuf::underflow(); |
| } |
| return egptr() - gptr(); |
| } |
| |
| private: |
| /// Setup the streambuf buffer pointers after updating |
| /// the value of the iterator. Returns the first character |
| /// in the new buffer, or eof if there is no buffer. |
| int_type setBuffer() { |
| int_type ret = traits_type::eof(); |
| |
| if (iter_ != buffer_.end()) { |
| char *loc = const_cast<char *>(iter_->data()); |
| setg(loc, loc, loc + iter_->size()); |
| ret = std::char_traits<char>::to_int_type(*gptr()); |
| } else { |
| setg(nullptr, nullptr, nullptr); |
| } |
| return ret; |
| } |
| |
| const InputBuffer buffer_; |
| off_type basePos_; |
| InputBuffer::const_iterator iter_; |
| }; |
| |
| } // namespace avro |
| |
| #endif |