Tag Parser  6.3.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 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  const std::unique_ptr<char[]> &buffer();
196  implementationType *denoteFirstChild(uint32 offset);
197 
198 protected:
199  identifierType m_id;
201  uint64 m_maxSize;
202  uint32 m_idLength;
203  dataSizeType m_dataSize;
204  uint32 m_sizeLength;
205  implementationType* m_parent;
206  std::unique_ptr<implementationType> m_nextSibling;
207  std::unique_ptr<implementationType> m_firstChild;
208  std::unique_ptr<char[]> m_buffer;
209 
210 private:
211  void copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy);
212 
213  containerType* m_container;
214  bool m_parsed;
215 };
216 
221 template <class ImplementationType>
223  m_id(identifierType()),
224  m_startOffset(startOffset),
225  m_idLength(0),
226  m_dataSize(0),
227  m_sizeLength(0),
228  m_parent(nullptr),
229  m_container(&container),
230  m_parsed(false)
231 {
232  stream().seekg(0, std::ios_base::end);
233  m_maxSize = static_cast<uint64>(stream().tellg());
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  parse();
771  gatheredNotifications.insert(gatheredNotifications.end(), notifications().begin(), notifications().end());
772  if(firstChild()) { // element is parent
773  firstChild()->validateSubsequentElementStructure(gatheredNotifications, paddingSize);
774  } else if(paddingSize && isPadding()) { // element is padding
775  *paddingSize += totalSize();
776  }
777  if(nextSibling()) {
778  nextSibling()->validateSubsequentElementStructure(gatheredNotifications, paddingSize);
779  }
780  } catch(Failure &) {
781  gatheredNotifications.insert(gatheredNotifications.end(), notifications().begin(), notifications().end());
782  throw;
783  }
784 }
785 
789 template <class ImplementationType>
791 {
792  copyInternal(targetStream, startOffset(), headerSize());
793 }
794 
798 template <class ImplementationType>
800 {
801  if(uint32 firstChildOffset = this->firstChildOffset()) {
802  copyInternal(targetStream, startOffset(), firstChildOffset);
803  } else {
804  copyInternal(targetStream, startOffset(), totalSize());
805  }
806 }
807 
811 template <class ImplementationType>
813 {
814  copyInternal(targetStream, startOffset(), totalSize());
815 }
816 
821 template <class ImplementationType>
823 {
824  m_buffer = std::make_unique<char[]>(totalSize());
825  container().stream().seekg(startOffset());
826  container().stream().read(m_buffer.get(), totalSize());
827 }
828 
832 template <class ImplementationType>
834 {
835  m_buffer.reset();
836 }
837 
842 template <class ImplementationType>
843 inline void GenericFileElement<ImplementationType>::copyBuffer(std::ostream &targetStream)
844 {
845  targetStream.write(m_buffer.get(), totalSize());
846 }
847 
852 template <class ImplementationType>
853 inline const std::unique_ptr<char[]> &GenericFileElement<ImplementationType>::buffer()
854 {
855  return m_buffer;
856 }
857 
865 template <class ImplementationType>
866 void GenericFileElement<ImplementationType>::copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy)
867 {
868  invalidateStatus();
869  // ensure the header has been parsed correctly
870  try {
871  parse();
872  } catch(Failure &) {
873  throw InvalidDataException();
874  }
875  auto &stream = container().stream();
876  stream.seekg(startOffset); // seek to start offset
878  copyHelper.callbackCopy(stream, targetStream, bytesToCopy, std::bind(&GenericFileElement<ImplementationType>::isAborted, this), std::bind(&GenericFileElement<ImplementationType>::updatePercentage, this, std::placeholders::_1));
879  if(isAborted()) {
881  }
882 }
883 
888 template <class ImplementationType>
890 {
891  if(relativeFirstChildOffset + minimumElementSize() <= totalSize()) {
892  m_firstChild.reset(new implementationType(static_cast<implementationType &>(*this), startOffset() + relativeFirstChildOffset));
893  } else {
894  m_firstChild.reset();
895  }
896  return m_firstChild.get();
897 }
898 
902 template <class ImplementationType>
904 {
905  return sizeof(identifierType);
906 }
907 
911 template <class ImplementationType>
913 {
914  return sizeof(dataSizeType);
915 }
916 
920 template <class ImplementationType>
922 {
924 }
925 
940 }
941 
942 #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