Tag Parser  9.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
oggiterator.cpp
Go to the documentation of this file.
1 #include "./oggiterator.h"
2 
3 #include "../exceptions.h"
4 
5 #include <c++utilities/io/binaryreader.h>
6 
7 #include <iostream>
8 #include <limits>
9 
10 using namespace std;
11 using namespace CppUtilities;
12 
13 namespace TagParser {
14 
32 void OggIterator::clear(istream &stream, std::uint64_t startOffset, std::uint64_t streamSize)
33 {
34  m_stream = &stream;
35  m_startOffset = startOffset;
36  m_streamSize = streamSize;
37  m_pages.clear();
38 }
39 
46 void OggIterator::reset()
47 {
48  for (m_page = m_segment = m_offset = 0; m_page < m_pages.size() || fetchNextPage(); ++m_page) {
49  const OggPage &page = m_pages[m_page];
50  if (!page.segmentSizes().empty() && matchesFilter(page)) {
51  // page is not empty and matches ID filter if set
52  m_offset = page.startOffset() + page.headerSize();
53  break;
54  }
55  }
56  // no matching page found -> iterator is invalid
57 }
58 
63 void OggIterator::nextPage()
64 {
65  while (++m_page < m_pages.size() || fetchNextPage()) {
66  const OggPage &page = m_pages[m_page];
67  if (!page.segmentSizes().empty() && matchesFilter(page)) {
68  // page is not empty and matches ID filter if set
69  m_segment = m_bytesRead = 0;
70  m_offset = page.startOffset() + page.headerSize();
71  return;
72  }
73  }
74  // no next page available -> iterator is in invalid state
75 }
76 
81 void OggIterator::nextSegment()
82 {
83  const OggPage &page = m_pages[m_page];
84  if (matchesFilter(page) && ++m_segment < page.segmentSizes().size()) {
85  // current page has next segment
86  m_bytesRead = 0;
87  m_offset += page.segmentSizes()[m_segment - 1];
88  } else {
89  // next (matching) page has next segment
90  nextPage();
91  }
92 }
93 
98 void OggIterator::previousPage()
99 {
100  while (m_page) {
101  const OggPage &page = m_pages[--m_page];
102  if (matchesFilter(page)) {
103  m_offset = page.dataOffset(m_segment = page.segmentSizes().size() - 1);
104  return;
105  }
106  }
107 }
108 
113 void OggIterator::previousSegment()
114 {
115  const OggPage &page = m_pages[m_page];
116  if (m_segment && matchesFilter(page)) {
117  m_offset -= page.segmentSizes()[m_segment--];
118  } else {
119  previousPage();
120  }
121 }
122 
134 void OggIterator::read(char *buffer, size_t count)
135 {
136  size_t bytesRead = 0;
137  while (*this && count) {
138  const auto available = currentSegmentSize() - m_bytesRead;
139  stream().seekg(static_cast<streamoff>(currentCharacterOffset()));
140  if (count <= available) {
141  stream().read(buffer + bytesRead, static_cast<streamoff>(count));
142  m_bytesRead += count;
143  return;
144  }
145  stream().read(buffer + bytesRead, available);
146  nextSegment();
147  bytesRead += available;
148  count -= available;
149  }
150  if (count) {
151  // still bytes to read but no more available
152  throw TruncatedDataException();
153  }
154 }
155 
168 size_t OggIterator::readAll(char *buffer, size_t max)
169 {
170  size_t bytesRead = 0;
171  while (*this && max) {
172  const std::uint32_t available = currentSegmentSize() - m_bytesRead;
173  stream().seekg(static_cast<streamoff>(currentCharacterOffset()));
174  if (max <= available) {
175  stream().read(buffer + bytesRead, static_cast<streamoff>(max));
176  m_bytesRead += max;
177  return bytesRead + max;
178  } else {
179  stream().read(buffer + bytesRead, available);
180  nextSegment();
181  bytesRead += available;
182  max -= available;
183  }
184  }
185  return bytesRead;
186 }
187 
198 void OggIterator::ignore(size_t count)
199 {
200  std::uint32_t available = currentSegmentSize() - m_bytesRead;
201  while (*this) {
202  available = currentSegmentSize() - m_bytesRead;
203  if (count <= available) {
204  m_bytesRead += count;
205  return;
206  } else {
207  nextSegment();
208  count -= available;
209  }
210  }
211  throw TruncatedDataException();
212 }
213 
237 bool OggIterator::resyncAt(std::uint64_t offset)
238 {
239  // check whether offset is valid
240  if (offset >= streamSize() || offset < (m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize())) {
241  return false;
242  }
243 
244  // find capture pattern 'OggS'
245  stream().seekg(static_cast<streamoff>(offset));
246  std::uint8_t lettersFound = 0;
247  for (std::uint64_t bytesAvailable = max<std::uint64_t>(streamSize() - offset, 65307ul); bytesAvailable >= 27; --bytesAvailable) {
248  switch (static_cast<char>(stream().get())) {
249  case 'O':
250  lettersFound = 1;
251  break;
252  case 'g':
253  lettersFound = lettersFound == 1 || lettersFound == 2 ? lettersFound + 1 : 0;
254  break;
255  case 'S':
256  if (lettersFound == 3) {
257  // capture pattern found
258  const auto currentOffset = stream().tellg();
259  // -> try to parse an OGG page at this position
260  try {
261  m_pages.emplace_back(stream(), static_cast<std::uint64_t>(stream().tellg()) - 4,
262  bytesAvailable > numeric_limits<std::int32_t>::max() ? numeric_limits<std::int32_t>::max()
263  : static_cast<std::int32_t>(bytesAvailable));
264  setPageIndex(m_pages.size() - 1);
265  return true;
266  } catch (const Failure &) {
267  stream().seekg(currentOffset);
268  }
269  }
270  [[fallthrough]];
271  default:
272  lettersFound = 0;
273  }
274  }
275  return false;
276 }
277 
288 bool OggIterator::fetchNextPage()
289 {
290  if (m_page == m_pages.size()) { // can only fetch the next page if the current page is the last page
291  m_offset = m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize();
292  if (m_offset < m_streamSize) {
293  const std::uint64_t bytesAvailable = m_streamSize - m_offset;
294  m_pages.emplace_back(*m_stream, m_offset,
295  bytesAvailable > numeric_limits<std::int32_t>::max() ? numeric_limits<std::int32_t>::max()
296  : static_cast<std::int32_t>(bytesAvailable));
297  return true;
298  }
299  }
300  return false;
301 }
302 
303 } // namespace TagParser
TagParser::OggPage::segmentSizes
const std::vector< std::uint32_t > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
Definition: oggpage.h:209
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::OggPage::startOffset
std::uint64_t startOffset() const
Returns the start offset of the page.
Definition: oggpage.h:83
TagParser::OggPage::headerSize
std::uint32_t headerSize() const
Returns the header size in byte.
Definition: oggpage.h:219
TagParser::OggPage::dataOffset
std::uint64_t dataOffset(std::uint8_t segmentIndex=0) const
Returns the data offset of the segment with the specified segmentIndex.
Definition: oggpage.h:248
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
TagParser::OggPage
The OggPage class is used to parse OGG pages.
Definition: oggpage.h:13
CppUtilities
Definition: abstractcontainer.h:15
oggiterator.h
TagParser::TruncatedDataException
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:39