Tag Parser  8.2.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
genericfileelement.h
Go to the documentation of this file.
1 #ifndef TAG_PARSER_GENERICFILEELEMENT_H
2 #define TAG_PARSER_GENERICFILEELEMENT_H
3 
4 #include "./exceptions.h"
5 #include "./progressfeedback.h"
6 
7 #include <c++utilities/conversion/types.h>
8 #include <c++utilities/io/copy.h>
9 
10 #include <initializer_list>
11 #include <iostream>
12 #include <list>
13 #include <memory>
14 #include <string>
15 
16 namespace IoUtilities {
17 
18 class BinaryReader;
19 class BinaryWriter;
20 
21 } // namespace IoUtilities
22 
23 namespace TagParser {
24 
25 class Diagnostics;
26 
36 template <typename ImplementationType> class FileElementTraits {
37 };
38 
47 template <class ImplementationType> class TAG_PARSER_EXPORT GenericFileElement {
48  friend class FileElementTraits<ImplementationType>;
49 
50 public:
55 
60 
65 
66  GenericFileElement(ContainerType &container, uint64 startOffset);
67  GenericFileElement(ImplementationType &parent, uint64 startOffset);
68  GenericFileElement(ContainerType &container, uint64 startOffset, uint64 maxSize);
69  GenericFileElement(const GenericFileElement &other) = delete;
70  GenericFileElement(GenericFileElement &other) = delete;
71  GenericFileElement &operator=(const GenericFileElement &other) = delete;
72 
73  ContainerType &container();
74  const ContainerType &container() const;
75  std::iostream &stream();
76  IoUtilities::BinaryReader &reader();
77  IoUtilities::BinaryWriter &writer();
78  uint64 startOffset() const;
79  uint64 relativeStartOffset() const;
80  const IdentifierType &id() const;
81  std::string idToString() const;
82  uint32 idLength() const;
83  uint32 headerSize() const;
84  DataSizeType dataSize() const;
85  uint32 sizeLength() const;
86  uint64 dataOffset() const;
87  uint64 totalSize() const;
88  uint64 endOffset() const;
89  uint64 maxTotalSize() const;
90  byte level() const;
91  ImplementationType *parent();
92  const ImplementationType *parent() const;
93  ImplementationType *parent(byte n);
94  const ImplementationType *parent(byte n) const;
95  ImplementationType *nextSibling();
96  const ImplementationType *nextSibling() const;
97  ImplementationType *firstChild();
98  const ImplementationType *firstChild() const;
99  ImplementationType *lastChild();
100  const ImplementationType *lastChild() const;
101  ImplementationType *subelementByPath(Diagnostics &diag, IdentifierType item);
102  ImplementationType *subelementByPath(Diagnostics &diag, IdentifierType item, IdentifierType remainingPath...);
103  const ImplementationType *subelementByPath(Diagnostics &diag, IdentifierType item) const;
104  const ImplementationType *subelementByPath(Diagnostics &diag, IdentifierType item, IdentifierType remainingPath...) const;
105  ImplementationType *childById(const IdentifierType &id, Diagnostics &diag);
106  const ImplementationType *childById(const IdentifierType &id, Diagnostics &diag) const;
107  ImplementationType *siblingById(const IdentifierType &id, Diagnostics &diag);
108  const ImplementationType *siblingById(const IdentifierType &id, Diagnostics &diag) const;
109  ImplementationType *siblingByIdIncludingThis(const IdentifierType &id, Diagnostics &diag);
110  const ImplementationType *siblingByIdIncludingThis(const IdentifierType &id, Diagnostics &diag) const;
111  bool isParent() const;
112  bool isPadding() const;
113  uint64 firstChildOffset() const;
114  bool isParsed() const;
115  void clear();
116  void parse(Diagnostics &diag);
117  void reparse(Diagnostics &diag);
118  void validateSubsequentElementStructure(Diagnostics &diag, uint64 *paddingSize = nullptr);
119  static constexpr uint32 maximumIdLengthSupported();
120  static constexpr uint32 maximumSizeLengthSupported();
121  static constexpr byte minimumElementSize();
122  void copyHeader(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress);
123  void copyWithoutChilds(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress);
124  void copyEntirely(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress);
125  void makeBuffer();
126  void discardBuffer();
127  void copyBuffer(std::ostream &targetStream);
128  void copyPreferablyFromBuffer(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress);
129  const std::unique_ptr<char[]> &buffer();
130  ImplementationType *denoteFirstChild(uint32 offset);
131 
132 protected:
135  uint64 m_maxSize;
137  uint32 m_idLength;
138  uint32 m_sizeLength;
139  ImplementationType *m_parent;
140  std::unique_ptr<ImplementationType> m_nextSibling;
141  std::unique_ptr<ImplementationType> m_firstChild;
142  std::unique_ptr<char[]> m_buffer;
143 
144 private:
145  void copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy, Diagnostics &diag, AbortableProgressFeedback *progress);
146 
147  ContainerType *m_container;
148  bool m_parsed;
149 
150 protected:
152 };
153 
158 template <class ImplementationType>
160  : m_id(IdentifierType())
161  , m_startOffset(startOffset)
162  , m_dataSize(0)
163  , m_idLength(0)
164  , m_sizeLength(0)
165  , m_parent(nullptr)
166  , m_container(&container)
167  , m_parsed(false)
168  , m_sizeUnknown(false)
169 {
170  m_maxSize = container.fileInfo().size();
171  if (m_maxSize > startOffset) {
172  m_maxSize -= startOffset;
173  stream().seekg(startOffset, std::ios_base::beg);
174  } else {
175  m_maxSize = 0;
176  }
177 }
178 
182 template <class ImplementationType>
183 GenericFileElement<ImplementationType>::GenericFileElement(ImplementationType &parent, uint64 startOffset)
184  : m_id(IdentifierType())
185  , m_startOffset(startOffset)
186  , m_maxSize(parent.startOffset() + parent.totalSize() - startOffset)
187  , m_dataSize(0)
188  , m_idLength(0)
189  , m_sizeLength(0)
190  , m_parent(&parent)
191  , m_container(&parent.container())
192  , m_parsed(false)
193  , m_sizeUnknown(false)
194 {
195 }
196 
200 template <class ImplementationType>
202  GenericFileElement<ImplementationType>::ContainerType &container, uint64 startOffset, uint64 maxSize)
203  : m_id(IdentifierType())
204  , m_startOffset(startOffset)
205  , m_maxSize(maxSize)
206  , m_dataSize(0)
207  , m_idLength(0)
208  , m_sizeLength(0)
209  , m_parent(nullptr)
210  , m_container(&container)
211  , m_parsed(false)
212  , m_sizeUnknown(false)
213 {
214 }
215 
219 template <class ImplementationType>
221 {
222  return *m_container;
223 }
224 
228 template <class ImplementationType>
230 {
231  return *m_container;
232 }
233 
237 template <class ImplementationType> inline std::iostream &GenericFileElement<ImplementationType>::stream()
238 {
239  return m_container->stream();
240 }
241 
245 template <class ImplementationType> inline IoUtilities::BinaryReader &GenericFileElement<ImplementationType>::reader()
246 {
247  return m_container->reader();
248 }
249 
253 template <class ImplementationType> inline IoUtilities::BinaryWriter &GenericFileElement<ImplementationType>::writer()
254 {
255  return m_container->writer();
256 }
257 
261 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::startOffset() const
262 {
263  return m_startOffset;
264 }
265 
269 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::relativeStartOffset() const
270 {
271  return parent() ? startOffset() - parent()->startOffset() : startOffset();
272 }
273 
277 template <class ImplementationType>
279 {
280  return m_id;
281 }
282 
286 template <class ImplementationType> inline std::string GenericFileElement<ImplementationType>::idToString() const
287 {
288  return static_cast<ImplementationType *>(this)->idToString();
289 }
290 
294 template <class ImplementationType> inline uint32 GenericFileElement<ImplementationType>::idLength() const
295 {
296  return m_idLength;
297 }
298 
304 template <class ImplementationType> inline uint32 GenericFileElement<ImplementationType>::headerSize() const
305 {
306  return m_idLength + m_sizeLength;
307 }
308 
314 template <class ImplementationType>
316 {
317  return m_dataSize;
318 }
319 
323 template <class ImplementationType> inline uint32 GenericFileElement<ImplementationType>::sizeLength() const
324 {
325  return m_sizeLength;
326 }
327 
333 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::dataOffset() const
334 {
335  return startOffset() + headerSize();
336 }
337 
343 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::totalSize() const
344 {
345  return headerSize() + dataSize();
346 }
347 
351 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::endOffset() const
352 {
353  return startOffset() + totalSize();
354 }
355 
362 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::maxTotalSize() const
363 {
364  return m_maxSize;
365 }
366 
371 template <class ImplementationType> byte GenericFileElement<ImplementationType>::level() const
372 {
373  byte level = 0;
374  for (const ImplementationType *parent = m_parent; parent; ++level, parent = parent->m_parent)
375  ;
376  return level;
377 }
378 
385 template <class ImplementationType> inline ImplementationType *GenericFileElement<ImplementationType>::parent()
386 {
387  return m_parent;
388 }
389 
396 template <class ImplementationType> inline const ImplementationType *GenericFileElement<ImplementationType>::parent() const
397 {
398  return m_parent;
399 }
400 
407 template <class ImplementationType> ImplementationType *GenericFileElement<ImplementationType>::parent(byte n)
408 {
409  ImplementationType *parent = static_cast<ImplementationType *>(this);
410  for (; n && parent; --n, parent = parent->m_parent)
411  ;
412  return parent;
413 }
414 
421 template <class ImplementationType> inline const ImplementationType *GenericFileElement<ImplementationType>::parent(byte n) const
422 {
423  return const_cast<GenericFileElement<ImplementationType> *>(this)->parent(n);
424 }
425 
434 template <class ImplementationType> inline ImplementationType *GenericFileElement<ImplementationType>::nextSibling()
435 {
436  return m_nextSibling.get();
437 }
438 
447 template <class ImplementationType> inline const ImplementationType *GenericFileElement<ImplementationType>::nextSibling() const
448 {
449  return m_nextSibling.get();
450 }
451 
460 template <class ImplementationType> inline ImplementationType *GenericFileElement<ImplementationType>::firstChild()
461 {
462  return m_firstChild.get();
463 }
464 
473 template <class ImplementationType> inline const ImplementationType *GenericFileElement<ImplementationType>::firstChild() const
474 {
475  return m_firstChild.get();
476 }
477 
486 template <class ImplementationType> inline ImplementationType *GenericFileElement<ImplementationType>::lastChild()
487 {
488  for (ImplementationType *child = firstChild(); child; child = child->nextSibling()) {
489  if (!child->m_nextSibling) {
490  return child;
491  }
492  }
493  return nullptr;
494 }
495 
504 template <class ImplementationType> inline const ImplementationType *GenericFileElement<ImplementationType>::lastChild() const
505 {
506  return const_cast<GenericFileElement<ImplementationType> *>(this)->lastChild();
507 }
508 
518 template <class ImplementationType>
520 {
521  // ensure element is parsed
522  parse(diag);
523  // return the element if it matches the current and last item in the path
524  if (item == id()) {
525  return static_cast<ImplementationType *>(this);
526  }
527  // check whether a sibling matches the item
528  if (nextSibling()) {
529  return nextSibling()->subelementByPath(diag, item);
530  }
531  return nullptr;
532 }
533 
543 template <class ImplementationType>
545 {
546  // ensure element is parsed
547  parse(diag);
548  // continue with next item in path if the element matches the current item
549  if (item == id()) {
550  if (!firstChild()) {
551  return nullptr;
552  }
553  return firstChild()->subelementByPath(diag, remainingPath);
554  }
555  // check whether a sibling matches the current item
556  if (nextSibling()) {
557  return nextSibling()->subelementByPath(diag, item, remainingPath);
558  }
559  return nullptr;
560 }
561 
571 template <class ImplementationType>
573 {
574  return const_cast<GenericFileElement<ImplementationType> *>(this)->subelementByPath(diag, item);
575 }
576 
586 template <class ImplementationType>
588  Diagnostics &diag, IdentifierType item, IdentifierType remainingPath...) const
589 {
590  return const_cast<GenericFileElement<ImplementationType> *>(this)->subelementByPath(diag, item, remainingPath);
591 }
592 
602 template <class ImplementationType> ImplementationType *GenericFileElement<ImplementationType>::childById(const IdentifierType &id, Diagnostics &diag)
603 {
604  parse(diag); // ensure element is parsed
605  for (ImplementationType *child = firstChild(); child; child = child->nextSibling()) {
606  child->parse(diag);
607  if (child->id() == id) {
608  return child;
609  }
610  }
611  return nullptr;
612 }
613 
623 template <class ImplementationType>
624 const ImplementationType *GenericFileElement<ImplementationType>::childById(const IdentifierType &id, Diagnostics &diag) const
625 {
626  return const_cast<GenericFileElement<ImplementationType> *>(this)->childById(id, diag);
627 }
628 
639 template <class ImplementationType>
641 {
642  parse(diag); // ensure element is parsed
643  for (ImplementationType *sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) {
644  sibling->parse(diag);
645  if (sibling->id() == id) {
646  return sibling;
647  }
648  }
649  return nullptr;
650 }
651 
662 template <class ImplementationType>
663 const ImplementationType *GenericFileElement<ImplementationType>::siblingById(const IdentifierType &id, Diagnostics &diag) const
664 {
665  return const_cast<GenericFileElement<ImplementationType> *>(this)->siblingById(id, diag);
666 }
667 
678 template <class ImplementationType>
680 {
681  parse(diag); // ensure element is parsed
682  for (ImplementationType *sibling = static_cast<ImplementationType *>(this); sibling; sibling = sibling->nextSibling()) {
683  sibling->parse(diag);
684  if (sibling->id() == id) {
685  return sibling;
686  }
687  }
688  return nullptr;
689 }
690 
701 template <class ImplementationType>
703 {
704  return const_cast<GenericFileElement<ImplementationType> *>(this)->siblingByIdIncludingThis(id, diag);
705 }
706 
710 template <class ImplementationType> inline bool GenericFileElement<ImplementationType>::isParent() const
711 {
712  return static_cast<const ImplementationType *>(this)->isParent();
713 }
714 
718 template <class ImplementationType> inline bool GenericFileElement<ImplementationType>::isPadding() const
719 {
720  return static_cast<const ImplementationType *>(this)->isPadding();
721 }
722 
726 template <class ImplementationType> inline uint64 GenericFileElement<ImplementationType>::firstChildOffset() const
727 {
728  return static_cast<const ImplementationType *>(this)->firstChildOffset();
729 }
730 
734 template <class ImplementationType> inline bool GenericFileElement<ImplementationType>::isParsed() const
735 {
736  return m_parsed;
737 }
738 
745 template <class ImplementationType> void GenericFileElement<ImplementationType>::clear()
746 {
747  m_id = IdentifierType();
748  //m_startOffset = 0;
749  m_idLength = 0;
750  m_dataSize = 0;
751  m_sizeLength = 0;
752  m_nextSibling = nullptr;
753  m_firstChild = nullptr;
754  m_parsed = false;
755 }
756 
771 template <class ImplementationType> void GenericFileElement<ImplementationType>::parse(Diagnostics &diag)
772 {
773  if (!m_parsed) {
774  static_cast<ImplementationType *>(this)->internalParse(diag);
775  m_parsed = true;
776  }
777 }
778 
795 template <class ImplementationType> void GenericFileElement<ImplementationType>::reparse(Diagnostics &diag)
796 {
797  clear();
798  static_cast<ImplementationType *>(this)->parse(diag);
799  m_parsed = true;
800 }
801 
814 template <class ImplementationType>
816 {
817  // validate element itself by just parsing it
818  parse(diag);
819  // validate children
820  if (firstChild()) {
821  try {
822  firstChild()->validateSubsequentElementStructure(diag, paddingSize);
823  } catch (const Failure &) {
824  // ignore critical errors in child structure to continue validating siblings
825  // (critical notifications about the errors should have already been added to diag, so nothing to do)
826  }
827  } else if (paddingSize && isPadding()) { // element is padding
828  *paddingSize += totalSize();
829  }
830  // validate siblings
831  if (nextSibling()) {
832  nextSibling()->validateSubsequentElementStructure(diag, paddingSize);
833  }
834 }
835 
839 template <class ImplementationType>
841 {
842  copyInternal(targetStream, startOffset(), headerSize(), diag, progress);
843 }
844 
848 template <class ImplementationType>
850 {
851  if (uint32 firstChildOffset = this->firstChildOffset()) {
852  copyInternal(targetStream, startOffset(), firstChildOffset, diag, progress);
853  } else {
854  copyInternal(targetStream, startOffset(), totalSize(), diag, progress);
855  }
856 }
857 
861 template <class ImplementationType>
863 {
864  copyInternal(targetStream, startOffset(), totalSize(), diag, progress);
865 }
866 
871 template <class ImplementationType> void GenericFileElement<ImplementationType>::makeBuffer()
872 {
873  m_buffer = std::make_unique<char[]>(totalSize());
874  container().stream().seekg(startOffset());
875  container().stream().read(m_buffer.get(), totalSize());
876 }
877 
881 template <class ImplementationType> inline void GenericFileElement<ImplementationType>::discardBuffer()
882 {
883  m_buffer.reset();
884 }
885 
890 template <class ImplementationType> inline void GenericFileElement<ImplementationType>::copyBuffer(std::ostream &targetStream)
891 {
892  targetStream.write(m_buffer.get(), totalSize());
893 }
894 
899 template <class ImplementationType>
901  std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress)
902 {
903  m_buffer ? copyBuffer(targetStream) : copyEntirely(targetStream, diag, progress);
904 }
905 
910 template <class ImplementationType> inline const std::unique_ptr<char[]> &GenericFileElement<ImplementationType>::buffer()
911 {
912  return m_buffer;
913 }
914 
922 template <class ImplementationType>
924  std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy, Diagnostics &diag, AbortableProgressFeedback *progress)
925 {
926  // ensure the header has been parsed correctly
927  try {
928  parse(diag);
929  } catch (const Failure &) {
930  throw InvalidDataException();
931  }
932  auto &stream = container().stream();
933  stream.seekg(startOffset);
935  if (progress) {
936  copyHelper.callbackCopy(stream, targetStream, bytesToCopy, std::bind(&AbortableProgressFeedback::isAborted, std::ref(progress)),
937  std::bind(&AbortableProgressFeedback::updateStepPercentageFromFraction, std::ref(progress), std::placeholders::_1));
938  } else {
939  copyHelper.copy(stream, targetStream, bytesToCopy);
940  }
941 }
942 
947 template <class ImplementationType> ImplementationType *GenericFileElement<ImplementationType>::denoteFirstChild(uint32 relativeFirstChildOffset)
948 {
949  if (relativeFirstChildOffset + minimumElementSize() <= totalSize()) {
950  m_firstChild.reset(new ImplementationType(static_cast<ImplementationType &>(*this), startOffset() + relativeFirstChildOffset));
951  } else {
952  m_firstChild.reset();
953  }
954  return m_firstChild.get();
955 }
956 
960 template <class ImplementationType> constexpr uint32 GenericFileElement<ImplementationType>::maximumIdLengthSupported()
961 {
962  return sizeof(IdentifierType);
963 }
964 
968 template <class ImplementationType> constexpr uint32 GenericFileElement<ImplementationType>::maximumSizeLengthSupported()
969 {
970  return sizeof(DataSizeType);
971 }
972 
976 template <class ImplementationType> constexpr byte GenericFileElement<ImplementationType>::minimumElementSize()
977 {
979 }
980 
995 } // namespace TagParser
996 
997 #endif // TAG_PARSER_GENERICFILEELEMENT_H
Defines traits for the specified ImplementationType.
std::unique_ptr< char[]> m_buffer
std::unique_ptr< ImplementationType > m_firstChild
std::unique_ptr< ImplementationType > m_nextSibling
uint64 startOffset() const
Returns the start offset in the related stream.
bool isPadding() const
Returns an indication whether this instance is a padding element.
The GenericFileElement class helps to parse binary files which consist of an arboreal element strucut...
IoUtilities::BinaryReader & reader()
Returns the related BinaryReader.
Contains utility classes helping to read and write streams.
GenericFileElement(ContainerType &container, uint64 startOffset)
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks.
std::string idToString() const
Returns a printable string representation of the element ID.
IoUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
typename FileElementTraits< ImplementationType >::ContainerType ContainerType
Specifies the type of the corresponding container.
std::iostream & stream()
Returns the related stream.
typename FileElementTraits< Mp4Atom >::DataSizeType DataSizeType
Specifies the type used to store data sizes.
typename FileElementTraits< Mp4Atom >::IdentifierType IdentifierType
Specifies the type used to store identifiers.
bool isParent() const
Returns an indication whether this instance is a parent element.
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
#define TAG_PARSER_EXPORT
Marks the symbol to be exported by the tagparser library.
uint64 firstChildOffset() const
Returns the offset of the first child (relative to the start offset of this element).
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156