Tag Parser  6.5.1
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 GENERICFILEELEMENT_H
2 #define GENERICFILEELEMENT_H
3 
4 #include "./notification.h"
5 #include "./exceptions.h"
6 #include "./statusprovider.h"
7 
8 #include <c++utilities/conversion/types.h>
9 #include <c++utilities/io/copy.h>
10 
11 #include <list>
12 #include <initializer_list>
13 #include <memory>
14 #include <iostream>
15 #include <string>
16 
17 namespace IoUtilities {
18 
19 class BinaryReader;
20 class BinaryWriter;
21 
22 }
23 
24 namespace Media {
25 
26 template <class ImplementationType>
28 
33 template<typename ImplementationType>
35 {
36 public:
37  FileElementIterator(ImplementationType *element = nullptr);
38 
39  ImplementationType *operator *();
40  const ImplementationType *operator *() const;
41  operator bool() const;
43 
44 private:
45  ImplementationType *m_current;
46 };
47 
51 template<typename ImplementationType>
53  m_current(element)
54 {}
55 
59 template<typename ImplementationType>
61 {
62  return m_current;
63 }
64 
68 template<typename ImplementationType>
69 inline const ImplementationType *FileElementIterator<ImplementationType>::operator *() const
70 {
71  return m_current;
72 }
73 
77 template<typename ImplementationType>
79 {
80  m_current->parse(); // ensure the current element has been parsed
81  m_current = m_current->nextSibling();
82  return *this;
83 }
84 
88 template<typename ImplementationType>
90 {
91  return m_current != nullptr;
92 }
93 
103 template<typename ImplementationType>
105 {};
106 
114 template <class ImplementationType>
116 {
117  friend class FileElementTraits<ImplementationType>;
118 
119 public:
124 
129 
134 
139 
140  GenericFileElement(containerType &container, uint64 startOffset);
141  GenericFileElement(implementationType &parent, uint64 startOffset);
142  GenericFileElement(containerType &container, uint64 startOffset, uint64 maxSize);
143  GenericFileElement(const GenericFileElement& other) = delete;
144  GenericFileElement(GenericFileElement& other) = delete;
145  GenericFileElement& operator =(const GenericFileElement& other) = delete;
146 
147  containerType& container();
148  const containerType& container() const;
149  std::iostream &stream();
150  IoUtilities::BinaryReader &reader();
151  IoUtilities::BinaryWriter &writer();
152  uint64 startOffset() const;
153  uint64 relativeStartOffset() const;
154  const identifierType &id() const;
155  std::string idToString() const;
156  uint32 idLength() const;
157  uint32 headerSize() const;
158  dataSizeType dataSize() const;
159  uint32 sizeLength() const;
160  uint64 dataOffset() const;
161  uint64 totalSize() const;
162  uint64 endOffset() const;
163  uint64 maxTotalSize() const;
164  implementationType* parent();
165  const implementationType* parent() const;
166  implementationType* nextSibling();
167  const implementationType* nextSibling() const;
168  implementationType* firstChild();
169  const implementationType* firstChild() const;
170  implementationType* subelementByPath(const std::initializer_list<identifierType> &path);
171  implementationType* subelementByPath(std::list<identifierType> &path);
172  implementationType* childById(const identifierType &id);
173  implementationType* siblingById(const identifierType &id, bool includeThis = false);
176  const FileElementIterator<implementationType> begin() const;
177  const FileElementIterator<implementationType> end() const;
178  bool isParent() const;
179  bool isPadding() const;
180  uint64 firstChildOffset() const;
181  bool isParsed() const;
182  void clear();
183  void parse();
184  void reparse();
185  void validateSubsequentElementStructure(NotificationList &gatheredNotifications, uint64 *paddingSize = nullptr);
186  static constexpr uint32 maximumIdLengthSupported();
187  static constexpr uint32 maximumSizeLengthSupported();
188  static constexpr byte minimumElementSize();
189  void copyHeader(std::ostream &targetStream);
190  void copyWithoutChilds(std::ostream &targetStream);
191  void copyEntirely(std::ostream &targetStream);
192  void makeBuffer();
193  void discardBuffer();
194  void copyBuffer(std::ostream &targetStream);
195  void copyPreferablyFromBuffer(std::ostream &targetStream);
196  const std::unique_ptr<char[]> &buffer();
197  implementationType *denoteFirstChild(uint32 offset);
198 
199 protected:
202  uint64 m_maxSize;
203  uint32 m_idLength;
205  uint32 m_sizeLength;
207  std::unique_ptr<implementationType> m_nextSibling;
208  std::unique_ptr<implementationType> m_firstChild;
209  std::unique_ptr<char[]> m_buffer;
210 
211 private:
212  void copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy);
213 
214  containerType* m_container;
215  bool m_parsed;
216 };
217 
222 template <class ImplementationType>
224  m_id(identifierType()),
225  m_startOffset(startOffset),
226  m_idLength(0),
227  m_dataSize(0),
228  m_sizeLength(0),
229  m_parent(nullptr),
230  m_container(&container),
231  m_parsed(false)
232 {
233  m_maxSize = container.fileInfo().size();
234  if(m_maxSize > startOffset) {
235  m_maxSize -= startOffset;
236  stream().seekg(startOffset, std::ios_base::beg);
237  } else {
238  m_maxSize = 0;
239  }
240 }
241 
245 template <class ImplementationType>
246 GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::implementationType &parent, uint64 startOffset) :
247  m_id(identifierType()),
248  m_startOffset(startOffset),
249  m_maxSize(parent.startOffset() + parent.totalSize() - startOffset),
250  m_idLength(0),
251  m_dataSize(0),
252  m_sizeLength(0),
253  m_parent(&parent),
254  m_container(&parent.container()),
255  m_parsed(false)
256 {}
257 
261 template <class ImplementationType>
262 GenericFileElement<ImplementationType>::GenericFileElement(GenericFileElement<ImplementationType>::containerType &container, uint64 startOffset, uint64 maxSize) :
263  m_id(identifierType()),
264  m_startOffset(startOffset),
265  m_maxSize(maxSize),
266  m_idLength(0),
267  m_dataSize(0),
268  m_sizeLength(0),
269  m_parent(nullptr),
270  m_container(&container),
271  m_parsed(false)
272 {}
273 
277 template <class ImplementationType>
279 {
280  return *m_container;
281 }
282 
286 template <class ImplementationType>
288 {
289  return *m_container;
290 }
291 
295 template <class ImplementationType>
297 {
298  return m_container->stream();
299 }
300 
304 template <class ImplementationType>
305 inline IoUtilities::BinaryReader &GenericFileElement<ImplementationType>::reader()
306 {
307  return m_container->reader();
308 }
309 
313 template <class ImplementationType>
314 inline IoUtilities::BinaryWriter &GenericFileElement<ImplementationType>::writer()
315 {
316  return m_container->writer();
317 }
318 
322 template <class ImplementationType>
324 {
325  return m_startOffset;
326 }
327 
331 template <class ImplementationType>
333 {
334  return parent() ? startOffset() - parent()->startOffset() : startOffset();
335 }
336 
340 template <class ImplementationType>
342 {
343  return m_id;
344 }
345 
349 template <class ImplementationType>
351 {
352  return static_cast<ImplementationType *>(this)->idToString();
353 }
354 
358 template <class ImplementationType>
360 {
361  return m_idLength;
362 }
363 
369 template <class ImplementationType>
371 {
372  return m_idLength + m_sizeLength;
373 }
374 
380 template <class ImplementationType>
382 {
383  return m_dataSize;
384 }
385 
389 template <class ImplementationType>
391 {
392  return m_sizeLength;
393 }
394 
400 template <class ImplementationType>
402 {
403  return startOffset() + headerSize();
404 }
405 
411 template <class ImplementationType>
413 {
414  return headerSize() + dataSize();
415 }
416 
420 template <class ImplementationType>
422 {
423  return startOffset() + totalSize();
424 }
425 
432 template <class ImplementationType>
434 {
435  return m_maxSize;
436 }
437 
444 template <class ImplementationType>
446 {
447  return m_parent;
448 }
449 
456 template <class ImplementationType>
458 {
459  return m_parent;
460 }
461 
470 template <class ImplementationType>
472 {
473  return m_nextSibling.get();
474 }
475 
484 template <class ImplementationType>
486 {
487  return m_nextSibling.get();
488 }
489 
498 template <class ImplementationType>
500 {
501  return m_firstChild.get();
502 }
503 
512 template <class ImplementationType>
514 {
515  return m_firstChild.get();
516 }
517 
527 template <class ImplementationType>
529 {
530  std::list<GenericFileElement<ImplementationType>::identifierType> list(path);
531  return subelementByPath(list);
532 }
533 
544 template <class ImplementationType>
546 {
547  parse(); // ensure element is parsed
548  if(path.size()) {
549  if(path.front() == id()) {
550  if(path.size() == 1) {
551  return static_cast<implementationType*>(this);
552  } else {
553  if(firstChild()) {
554  path.pop_front();
555  return firstChild()->subelementByPath(path);
556  }
557  }
558  } else {
559  if(nextSibling()) {
560  return nextSibling()->subelementByPath(path);
561  }
562  }
563  }
564  return nullptr;
565 }
566 
576 template <class ImplementationType>
578 {
579  parse(); // ensure element is parsed
580  for(implementationType *child = firstChild(); child; child = child->nextSibling()) {
581  child->parse();
582  if(child->id() == id) {
583  return child;
584  }
585  }
586  return nullptr;
587 }
588 
603 template <class ImplementationType>
605 {
606  parse(); // ensure element is parsed
607  for(implementationType *sibling = includeThis ? static_cast<implementationType*>(this) : nextSibling(); sibling; sibling = sibling->nextSibling()) {
608  sibling->parse();
609  if(sibling->id() == id) {
610  return sibling;
611  }
612  }
613  return nullptr;
614 }
615 
619 template <class ImplementationType>
621 {
622  return FileElementIterator<implementationType>(firstChild());
623 }
624 
628 template <class ImplementationType>
630 {
631  return FileElementIterator<implementationType>(firstChild());
632 }
633 
637 template <class ImplementationType>
639 {
641 }
642 
646 template <class ImplementationType>
648 {
650 }
651 
655 template <class ImplementationType>
657 {
658  return static_cast<const ImplementationType *>(this)->isParent();
659 }
660 
664 template <class ImplementationType>
666 {
667  return static_cast<const ImplementationType *>(this)->isPadding();
668 }
669 
673 template <class ImplementationType>
675 {
676  return static_cast<const ImplementationType *>(this)->firstChildOffset();
677 }
678 
682 template <class ImplementationType>
684 {
685  return m_parsed;
686 }
687 
694 template <class ImplementationType>
696 {
697  m_id = identifierType();
698  //m_startOffset = 0;
699  m_idLength = 0;
700  m_dataSize = 0;
701  m_sizeLength = 0;
702  m_nextSibling = nullptr;
703  m_firstChild = nullptr;
704  m_parsed = false;
705 }
706 
721 template <class ImplementationType>
723 {
724  if(!m_parsed) {
725  static_cast<ImplementationType *>(this)->internalParse();
726  m_parsed = true;
727  }
728 }
729 
746 template <class ImplementationType>
748 {
749  clear();
750  static_cast<ImplementationType *>(this)->parse();
751  m_parsed = true;
752 }
753 
766 template <class ImplementationType>
768 {
769  try {
770  // validate element itself by just parsing it
771  parse();
772  gatheredNotifications.insert(gatheredNotifications.end(), notifications().begin(), notifications().end());
773  // validate children
774  if(firstChild()) {
775  try {
776  firstChild()->validateSubsequentElementStructure(gatheredNotifications, paddingSize);
777  } catch(const Failure &) {
778  // - ignore critical errors in child structure to continue validating siblings
779  // - critical notifications about the errors should have already been added to
780  // gatheredNotifications
781  }
782  } else if(paddingSize && isPadding()) { // element is padding
783  *paddingSize += totalSize();
784  }
785  // validate siblings
786  if(nextSibling()) {
787  nextSibling()->validateSubsequentElementStructure(gatheredNotifications, paddingSize);
788  }
789  } catch(const Failure &) {
790  gatheredNotifications.insert(gatheredNotifications.end(), notifications().begin(), notifications().end());
791  throw;
792  }
793 }
794 
798 template <class ImplementationType>
800 {
801  copyInternal(targetStream, startOffset(), headerSize());
802 }
803 
807 template <class ImplementationType>
809 {
810  if(uint32 firstChildOffset = this->firstChildOffset()) {
811  copyInternal(targetStream, startOffset(), firstChildOffset);
812  } else {
813  copyInternal(targetStream, startOffset(), totalSize());
814  }
815 }
816 
820 template <class ImplementationType>
822 {
823  copyInternal(targetStream, startOffset(), totalSize());
824 }
825 
830 template <class ImplementationType>
832 {
833  m_buffer = std::make_unique<char[]>(totalSize());
834  container().stream().seekg(startOffset());
835  container().stream().read(m_buffer.get(), totalSize());
836 }
837 
841 template <class ImplementationType>
843 {
844  m_buffer.reset();
845 }
846 
851 template <class ImplementationType>
852 inline void GenericFileElement<ImplementationType>::copyBuffer(std::ostream &targetStream)
853 {
854  targetStream.write(m_buffer.get(), totalSize());
855 }
856 
861 template <class ImplementationType>
863 {
864  m_buffer ? copyBuffer(targetStream) : copyEntirely(targetStream);
865 }
866 
871 template <class ImplementationType>
872 inline const std::unique_ptr<char[]> &GenericFileElement<ImplementationType>::buffer()
873 {
874  return m_buffer;
875 }
876 
884 template <class ImplementationType>
885 void GenericFileElement<ImplementationType>::copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy)
886 {
887  invalidateStatus();
888  // ensure the header has been parsed correctly
889  try {
890  parse();
891  } catch(Failure &) {
892  throw InvalidDataException();
893  }
894  auto &stream = container().stream();
895  stream.seekg(startOffset); // seek to start offset
897  copyHelper.callbackCopy(stream, targetStream, bytesToCopy, std::bind(&GenericFileElement<ImplementationType>::isAborted, this), std::bind(&GenericFileElement<ImplementationType>::updatePercentage, this, std::placeholders::_1));
898  if(isAborted()) {
899  throw OperationAbortedException();
900  }
901 }
902 
907 template <class ImplementationType>
909 {
910  if(relativeFirstChildOffset + minimumElementSize() <= totalSize()) {
911  m_firstChild.reset(new implementationType(static_cast<implementationType &>(*this), startOffset() + relativeFirstChildOffset));
912  } else {
913  m_firstChild.reset();
914  }
915  return m_firstChild.get();
916 }
917 
921 template <class ImplementationType>
923 {
924  return sizeof(identifierType);
925 }
926 
930 template <class ImplementationType>
932 {
933  return sizeof(dataSizeType);
934 }
935 
939 template <class ImplementationType>
941 {
943 }
944 
959 }
960 
961 #endif // GENERICFILEELEMENT_H
uint64 startOffset() const
Returns the start offset in the related stream.
std::iostream & stream()
Returns the related stream.
FileElementTraits< ImplementationType >::identifierType identifierType
Specifies the type used to store identifiers.
IoUtilities::BinaryReader & reader()
Returns the related BinaryReader.
GenericFileElement(containerType &container, uint64 startOffset)
std::unique_ptr< implementationType > m_firstChild
FileElementTraits< ImplementationType >::implementationType implementationType
Specifies the type of the actual implementation.
FileElementTraits< ImplementationType >::dataSizeType dataSizeType
Specifies the type used to store data sizes.
std::unique_ptr< implementationType > m_nextSibling
Contains utility classes helping to read and write streams.
The GenericFileElement class helps to parse binary files which consist of an arboreal element strucut...
FileElementIterator< ImplementationType > & operator++()
Moves to the next sibling.
ImplementationType * operator*()
Returns a reference to the current element.
The FileElementIterator class helps iterating through the children of a FileElement.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
std::list< Notification > NotificationList
Definition: notification.h:39
FileElementTraits< ImplementationType >::containerType containerType
Specifies the type of the corresponding container.
Defines traits for the specified ImplementationType.
FileElementIterator(ImplementationType *element=nullptr)
Constructs a new iterator for the specified element.
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
The StatusProvider class acts as a base class for objects providing status information.
std::unique_ptr< char[]> m_buffer
IoUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
#define TAG_PARSER_EXPORT
Marks the symbol to be exported by the tagparser library.
implementationType * m_parent