Tag Parser  8.0.1
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 IoUtilities;
12 
13 namespace TagParser {
14 
32 void OggIterator::clear(istream &stream, uint64 startOffset, uint64 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 uint32 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  uint32 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(uint64 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  byte lettersFound = 0;
247  for (uint64 bytesAvailable = max<uint64>(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<uint64>(stream().tellg()) - 4,
262  bytesAvailable > numeric_limits<int32>::max() ? numeric_limits<int32>::max() : static_cast<int32>(bytesAvailable));
263  setPageIndex(m_pages.size() - 1);
264  return true;
265  } catch (const Failure &) {
266  stream().seekg(currentOffset);
267  }
268  }
269  FALLTHROUGH;
270  default:
271  lettersFound = 0;
272  }
273  }
274  return false;
275 }
276 
287 bool OggIterator::fetchNextPage()
288 {
289  if (m_page == m_pages.size()) { // can only fetch the next page if the current page is the last page
290  m_offset = m_pages.empty() ? m_startOffset : m_pages.back().startOffset() + m_pages.back().totalSize();
291  if (m_offset < m_streamSize) {
292  const uint64 bytesAvailable = m_streamSize - m_offset;
293  m_pages.emplace_back(*m_stream, m_offset,
294  bytesAvailable > numeric_limits<int32>::max() ? numeric_limits<int32>::max() : static_cast<int32>(bytesAvailable));
295  return true;
296  }
297  }
298  return false;
299 }
300 
301 } // namespace TagParser
uint64 dataOffset(byte segmentIndex=0) const
Returns the data offset of the segment with the specified segmentIndex.
Definition: oggpage.h:249
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:32
STL namespace.
const std::vector< uint32 > & segmentSizes() const
Returns the sizes of the segments of the page in byte.
Definition: oggpage.h:210
uint64 startOffset() const
Returns the start offset of the page.
Definition: oggpage.h:84
Contains utility classes helping to read and write streams.
uint32 headerSize() const
Returns the header size in byte.
Definition: oggpage.h:220
The OggPage class is used to parse OGG pages.
Definition: oggpage.h:14
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:9