Tag Parser  6.4.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>
115 class TAG_PARSER_EXPORT GenericFileElement : public StatusProvider
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:
200  identifierType m_id;
202  uint64 m_maxSize;
203  uint32 m_idLength;
204  dataSizeType m_dataSize;
205  uint32 m_sizeLength;
206  implementationType* m_parent;
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>
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>
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()) {
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.
The exception that is thrown when an operation has been stopped and thus not successfully completed b...
Definition: exceptions.h:43
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...
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:27
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.
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