// ========================================================================== // SeqAn - The Library for Sequence Analysis // ========================================================================== // Copyright (c) 2006-2018, Knut Reinert, FU Berlin // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Knut Reinert or the FU Berlin nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH // DAMAGE. // // ========================================================================== // Author: David Weese // Author: Enrico Siragusa // ========================================================================== // Our own implementation of a streambuf_iterator. We could not use the STL's // iterator as we need to have access to the underlying streambuf which is a // private member of the STL iterator. // ========================================================================== // TODO(esiragusa): tests #ifndef SEQAN_INCLUDE_SEQAN_STREAM_ITER_STREAM_H_ #define SEQAN_INCLUDE_SEQAN_STREAM_ITER_STREAM_H_ namespace seqan { // ============================================================================ // Tags // ============================================================================ template struct StreamIterator {}; // ============================================================================ // Classes // ============================================================================ // ---------------------------------------------------------------------------- // Class StreamBuffer // ---------------------------------------------------------------------------- /*! * @class StreamBuffer * @headerfile * @brief Reinterprets the std::basic_streambuf to grant access to protected member functions. * * @signature template * class StreamBuffer : public std::basic_streambuf; * * @tparam TValue The value type of the stream buffer. * @tparam TTraits The traits to use, defaults to std::char_traits<TValue>. */ // TODO(holtgrew): Add documentation for member functions. // Unfortunately some of the most useful members of basic_streambuf are // protected, so we define a subclass to cast and access them template > struct StreamBuffer : public std::basic_streambuf { using TTraits = TTraits_; using TBasicStream = std::basic_streambuf; using TBasicStream::eback; using TBasicStream::gptr; using TBasicStream::egptr; using TBasicStream::gbump; using TBasicStream::underflow; using TBasicStream::pbase; using TBasicStream::pptr; using TBasicStream::epptr; using TBasicStream::pbump; using TBasicStream::overflow; }; // NOTE(rrahn): This is a wrapper for the StreamBuffer class. // Since we usually work with std::basic_iostreams and their derivatives, we cannot simply cast a pointer to // std::basic_filebuf to StreamBuffer to expose it's protected member functions. // To do so, we only use the StreamBuffer to inherit from basic_streambuf (the base class of all buffer implementations.) // and only expose the protected member functions as public functions. We then store the original basic_streambuf // in this wrapper class and whenever access to the protected members is required we use a reinterpret_cast to convert // basic_streambuf* into the public StreamBuffer*. The reinterpret_cast has zero overhead. // This fixes an undetected error reported w/ the sanitizer option of gcc (https://github.com/seqan/seqan/issues/2104). template > class StreamBufferWrapper { public: typedef std::basic_streambuf TBasicStreamBuffer; typedef StreamBuffer TPubStreamBuffer_; typedef typename TPubStreamBuffer_::TTraits TTraits; TBasicStreamBuffer * streamBuf{nullptr}; StreamBufferWrapper() = default; explicit StreamBufferWrapper(TBasicStreamBuffer * _basicStreamBuf) : streamBuf(_basicStreamBuf) {} size_t chunkSize(Input) { return baseBuf()->egptr() - baseBuf()->gptr(); } size_t chunkSize(Output) { return baseBuf()->epptr() - baseBuf()->pptr(); } template void advanceChunk(TOffset ofs, Input) { baseBuf()->gbump(ofs); } template void advanceChunk(TOffset ofs, Output) { baseBuf()->pbump(ofs); } void reserveChunk(Input) { if (baseBuf()->gptr() == baseBuf()->egptr()) baseBuf()->underflow(); } void reserveChunk(Output) { if (baseBuf()->pptr() == baseBuf()->epptr()) baseBuf()->overflow(EOF); } template typename std::streampos seekoff(TOffset ofs, std::ios_base::seekdir way, std::ios_base::openmode which) { return streamBuf->pubseekoff(ofs, way, which); } template void goFurther(TOffset ofs, TDirection dir) { size_t left = chunkSize(dir); if (SEQAN_LIKELY((size_t)ofs <= left)) { advanceChunk(ofs, dir); return; } while (true) { size_t adv = std::min((size_t)ofs, left); advanceChunk(adv, dir); ofs -= adv; if (ofs == 0) return; SEQAN_IF_CONSTEXPR (IsSameType::VALUE) baseBuf()->underflow(); else baseBuf()->overflow(); left = chunkSize(dir); if (SEQAN_UNLIKELY(left == 0)) { // if chunking isn't available try to seek typename TTraits::pos_type res = seekoff(ofs, std::ios_base::cur, (IsSameType::VALUE)? std::ios_base::in: std::ios_base::out); // if seek doesn't work manually skip characters (when reading) if (res == typename TTraits::pos_type(typename TTraits::off_type(-1))) { SEQAN_IF_CONSTEXPR (IsSameType::VALUE) { for (; ofs != 0; --ofs) baseBuf()->sbumpc(); } SEQAN_IF_CONSTEXPR (IsSameType::VALUE) { for (; ofs != 0; --ofs) baseBuf()->sputc('\0'); } } return; } } } TPubStreamBuffer_* baseBuf() const { return reinterpret_cast(streamBuf); } }; // ---------------------------------------------------------------------------- // Class StreamIterator // ---------------------------------------------------------------------------- /*! * @class StreamIterator * @extends Iter * @brief Abstract base class for input and output stream iterators. * * @signature template * class Iter >; * * @tparam TStream The @link StreamConcept @endlink to iterate over. * @tparam TDirection The iterator direction, one of the @link DirectionTags @endlink. */ // ---------------------------------------------------------------------------- // Class Input StreamIterator // ---------------------------------------------------------------------------- /*! * @class InputStreamIterator Input StreamIterator * @extends StreamIterator * @brief @link Iter @endlink specialiazion for reading from @link StreamConcept streams @endlink. * * @signature template * class Iter >; * * @tparam TStream The @link StreamConcept @endlink to iterate over. */ template class Iter > { public: typedef typename Value::Type TValue; typedef std::basic_istream TIStream; typedef std::basic_streambuf TBasicBuffer; typedef StreamBufferWrapper TStreamBufferWrapper; typedef typename TStreamBufferWrapper::TPubStreamBuffer_ TStreamBuffer; TStreamBufferWrapper streamBufWrapper{nullptr}; /*! * @fn InputStreamIterator::Iter * @brief The constructors. * * @signature Iter::Iter(); * @signature Iter::Iter(stream); * @signature Iter::Iter(streamBuffer); * * @param[in] stream The TStream to read from. * @param[in] streamBuf A @link StreamBuffer @endlink to read from. * * Allows default construction, construction from stream, as well as from a @link StreamBuffer @endlink. */ Iter() = default; Iter(TIStream & stream) : streamBufWrapper(stream.rdbuf()) { // printf("streamBuf: %p\n", streamBuf); stream.exceptions(std::ios_base::badbit); } Iter(TBasicBuffer * buf) : streamBufWrapper(buf) {} }; // ---------------------------------------------------------------------------- // Class StreamIterator // ---------------------------------------------------------------------------- /*! * @class OutputStreamIterator Output StreamIterator * @extends StreamIterator * @brief @link Iter @endlink specialiazion for writing to @link StreamConcept streams @endlink. * * @signature template * class Iter >; * * @tparam TStream The @link StreamConcept @endlink to iterate over. */ template class Iter > { public: typedef typename Value::Type TValue; typedef std::basic_ostream TOStream; typedef std::basic_streambuf TBasicBuffer; typedef StreamBufferWrapper TStreamBufferWrapper; typedef typename TStreamBufferWrapper::TPubStreamBuffer_ TStreamBuffer; TStreamBufferWrapper streamBufWrapper{nullptr}; /*! * @fn Iter::Iter * @brief Constructor. * * @signature Iter::Iter() * @signature Iter::Iter(stream) * @signature Iter::Iter(streamBuf) * * @param[in] stream The TStream to write to. * @param[in] streamBuf A @link StreamBuffer @endlink to write to. * * Allows default construction, construction from stream, as well as from a @link StreamBuffer @endlink. */ Iter() = default; Iter(TOStream & stream) : streamBufWrapper(stream.rdbuf()) { stream.exceptions(std::ios_base::badbit); } Iter(TBasicBuffer * buf) : streamBufWrapper(buf) {} template TValue2 & operator=(TValue2 &val) { setValue(*this, val); return val; } template TValue2 const & operator=(TValue2 const &val) { setValue(*this, val); return val; } }; // ============================================================================ // Metafunctions // ============================================================================ // ---------------------------------------------------------------------------- // Metafunction Chunk // ---------------------------------------------------------------------------- /*! * @mfn StreamBuffer#Chunk * @brief Return chunk type for StreamBuffer * * @signature Chunk::Type; * * @tparam TStreamBuffer The StreamBuffer to query for its chunk type. * @return Type The chunk type of the stream buffer. */ template struct Chunk > { typedef Range Type; }; template struct Chunk > > >: Chunk > >::TStreamBuffer> {}; // ---------------------------------------------------------------------------- // Metafunction Reference // ---------------------------------------------------------------------------- /*! * @mfn StreamBuffer#Reference * @brief Return reference for StreamBuffer. * * @signature Reference::Type; * * @tparam TStreamBuffer The StreamBuffer to query for its reference type. * @return Type The reference type of the stream buffer. */ template struct Reference > >: Value > > {}; template struct Reference > const>: Value > > {}; template struct Reference > > { typedef Iter > Type; }; // ---------------------------------------------------------------------------- // Metafunction GetValue // ---------------------------------------------------------------------------- /*! * @mfn StreamBuffer#GetValue * @brief Return get value for StreamBuffer. * * @signature GetValue::Type; * * @tparam TStreamBuffer The StreamBuffer to query for its get value type. * @return Type The get value type of the stream buffer. */ template struct GetValue > >: Reference > const> {}; // ---------------------------------------------------------------------------- // Metafunction Position // ---------------------------------------------------------------------------- /*! * @mfn StreamBuffer#Position * @brief Return position for StreamBuffer. * * @signature Position::Type; * * @tparam TStreamBuffer The StreamBuffer to query for its position type. * @return Type The position type of the stream buffer. */ template struct Position > > : Position {}; // ---------------------------------------------------------------------------- // Metafunction Difference // ---------------------------------------------------------------------------- /*! * @mfn StreamBuffer#Difference * @brief Return difference for StreamBuffer. * * @signature Difference::Type; * * @tparam TStreamBuffer The StreamBuffer to query for its difference type. * @return Type The difference type of the stream buffer. */ template struct Difference > > : Difference {}; // ---------------------------------------------------------------------------- // Metafunction Size // ---------------------------------------------------------------------------- /*! * @mfn StreamBuffer#Size * @brief Return size for StreamBuffer. * * @signature Size::Type; * * @tparam TStreamBuffer The StreamBuffer to query for its size type. * @return Type The size type of the stream buffer. */ template struct Size > > : Size {}; // ============================================================================ // Functions // ============================================================================ // ---------------------------------------------------------------------------- // Function directionIterator() // ---------------------------------------------------------------------------- /*! * @fn StreamConcept#directionIterator * @brief Returns direction iterator for Stream. * * @signature TDirIter directionIterator(stream, dirTag); * * @param[in] stream The @link StreamConcept @endlink object to compute iterator for. * @param[in] dirTag Direction tag, one of the @link DirectionTags @endlink. */ template inline SEQAN_FUNC_ENABLE_IF(Is >, Iter >) directionIterator(TStream &stream, TDirection const &) { return Iter >(stream); } /*! * @fn ContainerConcept#directionIterator * @brief Returns direction iterator for a container. * * @signature TDirIter directionIterator(streamBuf, dirTag); * * @param[in] streamBuf The @link ContainerConcept container @endlink object to compute iterator for. * @param[in] dirTag Direction tag, one of the @link DirectionTags @endlink. * * @return TDirIter The resulting @link ContainerConcept#DirectionIterator @endlink. */ template inline SEQAN_FUNC_DISABLE_IF(Is >, typename Iterator::Type) directionIterator(TContainer &cont, TDirection const &) { return begin(cont, Rooted()); } // ---------------------------------------------------------------------------- // Function reserveChunk() // ---------------------------------------------------------------------------- /*! * @fn StreamIterator#reserveChunk * @brief Reserve a chunk in the host of the StreamIterator * * @signature void reserveChunk(iter, len, dirTag); * * @param[in] iter The @link StreamIterator @endlink object to reserve chunks for. * @param[in] len The length of the chunk to reserve. * @param[in] dirTag Direction tag, one of @link DirectionTags#Input Input @endlink and @link * DirectionTags#Input Output @endlink . */ template inline void reserveChunk(Iter > &iter, TSize, Input dir) { iter.streamBufWrapper.reserveChunk(dir); } template inline void reserveChunk(Iter > &iter, TSize, Output dir) { iter.streamBufWrapper.reserveChunk(dir); } // ---------------------------------------------------------------------------- // Function advanceChunk() // ---------------------------------------------------------------------------- // TODO(holtgrew): Documentation missing below here. template inline void advanceChunk(Iter > &iter, TSize size) { iter.streamBufWrapper.advanceChunk(size, TDirection()); } // ---------------------------------------------------------------------------- // Function getChunk() // ---------------------------------------------------------------------------- // StreamBuffer template inline void getChunk(TChunk &result, StreamBuffer &buf, Input) { return assignRange(result, buf.gptr(), buf.egptr()); } template inline void getChunk(TChunk &result, StreamBuffer &buf, Output) { return assignRange(result, buf.pptr(), buf.epptr()); } // StreamIterator template inline void getChunk(TChunk &result, Iter > > &iter, Tag) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); getChunk(result, *iter.streamBufWrapper.baseBuf(), Tag()); } // ---------------------------------------------------------------------------- // Function value() - Input // ---------------------------------------------------------------------------- template inline typename Reference > >::Type value(Iter > &iter) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); return iter.streamBufWrapper.baseBuf()->sgetc(); } template inline typename Reference > const>::Type value(Iter > const &iter) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); return iter.streamBufWrapper.baseBuf()->sgetc(); } // ---------------------------------------------------------------------------- // Function value() - Ouput // ---------------------------------------------------------------------------- template inline Iter > & value(Iter > & iter) { return iter; } template inline Iter > const & value(Iter > const & iter) { return iter; } // ---------------------------------------------------------------------------- // Function setValue() // ---------------------------------------------------------------------------- template inline void setValue(Iter > & iter, TValue const &val) { return setValue(const_cast > const &>(iter), val); } template inline void setValue(Iter > const & iter, TValue const &val) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); iter.streamBufWrapper.baseBuf()->sputc((typename Value > >::Type)val); } // ---------------------------------------------------------------------------- // Function writeValue() // ---------------------------------------------------------------------------- // streams template inline void writeValue(Iter > &iter, TValue val) { setValue(iter, val); //goNext(iter); // implicitly done by setValue above } // ---------------------------------------------------------------------------- // Function goNext() // ---------------------------------------------------------------------------- template inline void goNext(Iter > & iter) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); iter.streamBufWrapper.baseBuf()->sbumpc(); } template inline void goNext(Iter > &) { // We do nothing here, as the stream is advanced by sputc whenever you assign // a value to the iterator with *iter= or setValue } // we intentionally don't return an iterator here, as the copied iterator wouldn't // point to the position before the increment. template inline void operator++(Iter > & iter, int) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); iter.streamBufWrapper.baseBuf()->sbumpc(); } // ---------------------------------------------------------------------------- // Function goFurther() // ---------------------------------------------------------------------------- template inline void goFurther(Iter > &iter, TOffset ofs) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); iter.streamBufWrapper.goFurther(ofs, TDirection()); } // ---------------------------------------------------------------------------- // Function position() // ---------------------------------------------------------------------------- template inline typename Position > const>::Type position(Iter > const & iter) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); return iter.streamBufWrapper.baseBuf()->pubseekoff(0, std::ios_base::cur, (IsSameType::VALUE)? std::ios_base::in: std::ios_base::out); } // ---------------------------------------------------------------------------- // Function setPosition() // ---------------------------------------------------------------------------- template inline void setPosition(Iter > const & iter, TPosition pos) { SEQAN_ASSERT(iter.streamBufWrapper.baseBuf() != nullptr); iter.streamBufWrapper.baseBuf()->pubseekpos(pos, (IsSameType::VALUE)? std::ios_base::in: std::ios_base::out); } // ---------------------------------------------------------------------------- // Function atEnd() // ---------------------------------------------------------------------------- template inline bool atEnd(Iter > const & iter) { typedef typename Value > >::Type TValue; typedef StreamBuffer TStreamBuffer; if (SEQAN_UNLIKELY(iter.streamBufWrapper.baseBuf() == nullptr)) { return true; } else { TStreamBuffer * const buf = iter.streamBufWrapper.baseBuf(); if (SEQAN_LIKELY(buf->gptr() < buf->egptr())) return false; else return TStreamBuffer::TTraits::eq_int_type(buf->sgetc(), TStreamBuffer::TTraits::eof()); } } template inline bool atEnd(Iter > const & iter) { typedef typename Value > >::Type TValue; typedef StreamBuffer TStreamBuffer; if (SEQAN_UNLIKELY(iter.streamBufWrapper.baseBuf() == nullptr)) { return true; } else { TStreamBuffer * const buf = iter.streamBufWrapper.baseBuf(); if (SEQAN_LIKELY(buf->pptr() < buf->epptr())) return false; else return TStreamBuffer::TTraits::eq_int_type(buf->overflow(), TStreamBuffer::TTraits::eof()); } } } // namespace seqan #endif // #ifndef SEQAN_INCLUDE_SEQAN_STREAM_ITER_STREAM_H_