#ifndef TAG_PARSER_OGGITERATOR_H #define TAG_PARSER_OGGITERATOR_H #include "./oggpage.h" #include #include namespace TagParser { class TAG_PARSER_EXPORT OggIterator { public: OggIterator(std::istream &stream, uint64 startOffset, uint64 streamSize); void clear(std::istream &stream, uint64 startOffset, uint64 streamSize); std::istream &stream(); void setStream(std::istream &stream); uint64 startOffset() const; uint64 streamSize() const; void reset(); void nextPage(); void nextSegment(); void previousPage(); void previousSegment(); const std::vector &pages() const; const OggPage ¤tPage() const; uint64 currentPageOffset() const; std::vector::size_type currentPageIndex() const; void setPageIndex(std::vector::size_type index); void setSegmentIndex(std::vector::size_type index); std::vector::size_type currentSegmentIndex() const; uint64 currentSegmentOffset() const; uint64 currentCharacterOffset() const; uint64 tellg() const; uint32 currentSegmentSize() const; void setFilter(uint32 streamSerialId); void removeFilter(); bool isLastPageFetched() const; void read(char *buffer, std::size_t count); size_t readAll(char *buffer, std::size_t max); void ignore(std::size_t count = 1); bool bytesRemaining(std::size_t atLeast) const; bool resyncAt(uint64 offset); operator bool() const; OggIterator &operator++(); OggIterator operator++(int); OggIterator &operator--(); OggIterator operator--(int); private: bool fetchNextPage(); bool matchesFilter(const OggPage &page); std::istream *m_stream; uint64 m_startOffset; uint64 m_streamSize; std::vector m_pages; std::vector::size_type m_page; std::vector::size_type m_segment; uint64 m_offset; uint32 m_bytesRead; bool m_hasIdFilter; uint32 m_idFilter; }; /*! * \brief Constructs a new iterator for the specified \a stream of \a streamSize bytes at the specified \a startOffset. */ inline OggIterator::OggIterator(std::istream &stream, uint64 startOffset, uint64 streamSize) : m_stream(&stream) , m_startOffset(startOffset) , m_streamSize(streamSize) , m_page(0) , m_segment(0) , m_offset(0) , m_bytesRead(0) , m_hasIdFilter(false) , m_idFilter(0) { } /*! * \brief Returns the stream. * * The stream has been specified when constructing the iterator and might be changed using the setStream() methods. */ inline std::istream &OggIterator::stream() { return *m_stream; } /*! * \brief Sets the stream. * \remarks The new stream must have the same data as the old stream to keep the iterator in a sane state. * \sa stream() */ inline void OggIterator::setStream(std::istream &stream) { m_stream = &stream; } /*! * \brief Returns the start offset (which has been specified when constructing the iterator). */ inline uint64 OggIterator::startOffset() const { return m_startOffset; } /*! * \brief Returns the stream size (which has been specified when constructing the iterator). */ inline uint64 OggIterator::streamSize() const { return m_streamSize; } /*! * \brief Returns a vector of containing the OGG pages that have been fetched yet. */ inline const std::vector &OggIterator::pages() const { return m_pages; } /*! * \brief Returns the current OGG page. * \remarks Calling this method when the iterator is invalid causes undefined behaviour. */ inline const OggPage &OggIterator::currentPage() const { return m_pages[m_page]; } /*! * \brief Returns the start offset of the current OGG page. * \remarks Calling this method when the iterator is invalid causes undefined behaviour. */ inline uint64 OggIterator::currentPageOffset() const { return m_pages[m_page].startOffset(); } /*! * \brief Returns an indication whether the iterator is valid. * * The iterator is invalid when it has just been constructed. Incrementing and decrementing * might cause invalidation. * * If the iterator is invalid, it can be reseted using the reset() method. * * Some methods cause undefined behaviour if called on an invalid iterator. */ inline OggIterator::operator bool() const { return m_page < m_pages.size() && m_segment < m_pages[m_page].segmentSizes().size(); } /*! * \brief Returns the index of the current page if the iterator is valid; otherwise an undefined index is returned. */ inline std::vector::size_type OggIterator::currentPageIndex() const { return m_page; } /*! * \brief Sets the current page index. * \remarks This method should never be called with an \a index out of range (which is defined by the number of fetched pages), since this would cause undefined behaviour. */ inline void OggIterator::setPageIndex(std::vector::size_type index) { const OggPage &page = m_pages[m_page = index]; m_segment = 0; m_offset = page.startOffset() + page.headerSize(); } /*! * \brief Sets the current segment index. * * This method should never be called with an \a index out of range (which is defined by the number of segments in the current page), since this causes undefined behaviour. */ inline void OggIterator::setSegmentIndex(std::vector::size_type index) { const OggPage &page = m_pages[m_page]; m_offset = page.dataOffset(m_segment = index); } /*! * \brief Returns the index of the current segment (in the current page) if the iterator is valid; otherwise an undefined index is returned. */ inline std::vector::size_type OggIterator::currentSegmentIndex() const { return m_segment; } /*! * \brief Returns the start offset of the current segment in the input stream if the iterator is valid; otherwise an undefined offset is returned. * \sa currentCharacterOffset() */ inline uint64 OggIterator::currentSegmentOffset() const { return m_offset; } /*! * \brief Returns the offset of the current character in the input stream if the iterator is valid; otherwise an undefined offset is returned. * \sa currentSegmentOffset() */ inline uint64 OggIterator::currentCharacterOffset() const { return m_offset + m_bytesRead; } /*! * \brief Same as currentCharacterOffset(); only provided for compliance with std::istream. */ inline uint64 OggIterator::tellg() const { return currentCharacterOffset(); } /*! * \brief Returns the size of the current segment. * * This method should never be called on an invalid iterator, since this causes undefined behaviour. */ inline uint32 OggIterator::currentSegmentSize() const { return m_pages[m_page].segmentSizes()[m_segment]; } /*! * \brief Allows to filter pages by the specified \a streamSerialId. * * Pages which do not match the specified \a streamSerialId will be skipped when getting the previous or * the next page. * * \sa removeFilter() */ inline void OggIterator::setFilter(uint32 streamSerialId) { m_hasIdFilter = true; m_idFilter = streamSerialId; } /*! * \brief Removes a previously set filter. * \sa setFilter() */ inline void OggIterator::removeFilter() { m_hasIdFilter = false; } /*! * \brief Returns whether the last page has already been fetched. */ inline bool OggIterator::isLastPageFetched() const { return (m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize()) >= m_streamSize; } /*! * \brief Returns whether there are \a atLeast bytes remaining. */ inline bool OggIterator::bytesRemaining(size_t atLeast) const { return *this && currentCharacterOffset() + atLeast <= streamSize(); } /*! * \brief Increments the current position by one segment if the iterator is valid; otherwise nothing happens. */ inline OggIterator &OggIterator::operator++() { nextSegment(); return *this; } /*! * \brief Increments the current position by one segment if the iterator is valid; otherwise nothing happens. */ inline OggIterator OggIterator::operator++(int) { OggIterator tmp = *this; nextSegment(); return tmp; } /*! * \brief Decrements the current position by one segment if the iterator is valid; otherwise nothing happens. */ inline OggIterator &OggIterator::operator--() { previousSegment(); return *this; } /*! * \brief Decrements the current position by one segment if the iterator is valid; otherwise nothing happens. */ inline OggIterator OggIterator::operator--(int) { OggIterator tmp = *this; previousSegment(); return tmp; } /*! * \brief Returns whether the specified \a page matches the current filter. */ inline bool OggIterator::matchesFilter(const OggPage &page) { return !m_hasIdFilter || m_idFilter == page.streamSerialNumber(); } } // namespace TagParser #endif // TAG_PARSER_OGGITERATOR_H