Tag Parser  6.1.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 #include <c++utilities/misc/memory.h>
11 
12 #include <list>
13 #include <initializer_list>
14 #include <memory>
15 #include <iostream>
16 #include <string>
17 
18 namespace IoUtilities {
19 
20 class BinaryReader;
21 class BinaryWriter;
22 
23 }
24 
25 namespace Media {
26 
27 template <class ImplementationType>
29 
34 template<typename ImplementationType>
36 {
37 public:
38  FileElementIterator(ImplementationType *element = nullptr);
39 
40  ImplementationType *operator *();
41  const ImplementationType *operator *() const;
42  operator bool() const;
44 
45 private:
46  ImplementationType *m_current;
47 };
48 
52 template<typename ImplementationType>
54  m_current(element)
55 {}
56 
60 template<typename ImplementationType>
62 {
63  return m_current;
64 }
65 
69 template<typename ImplementationType>
70 inline const ImplementationType *FileElementIterator<ImplementationType>::operator *() const
71 {
72  return m_current;
73 }
74 
78 template<typename ImplementationType>
80 {
81  m_current->parse(); // ensure the current element has been parsed
82  m_current = m_current->nextSibling();
83  return *this;
84 }
85 
89 template<typename ImplementationType>
91 {
92  return m_current != nullptr;
93 }
94 
104 template<typename ImplementationType>
106 {};
107 
115 template <class ImplementationType>
116 class TAG_PARSER_EXPORT GenericFileElement : public StatusProvider
117 {
118  friend class FileElementTraits<ImplementationType>;
119 
120 public:
125 
130 
135 
140 
141  GenericFileElement(containerType &container, uint64 startOffset);
142  GenericFileElement(implementationType &parent, uint64 startOffset);
143  GenericFileElement(containerType &container, uint64 startOffset, uint64 maxSize);
144  GenericFileElement(const GenericFileElement& other) = delete;
145  GenericFileElement(GenericFileElement& other) = delete;
146  GenericFileElement& operator =(const GenericFileElement& other) = delete;
147 
148  containerType& container();
149  const containerType& container() const;
150  std::iostream &stream();
151  IoUtilities::BinaryReader &reader();
152  IoUtilities::BinaryWriter &writer();
153  uint64 startOffset() const;
154  uint64 relativeStartOffset() const;
155  const identifierType &id() const;
156  std::string idToString() const;
157  uint32 idLength() const;
158  uint32 headerSize() const;
159  dataSizeType dataSize() const;
160  uint32 sizeLength() const;
161  uint64 dataOffset() const;
162  uint64 totalSize() const;
163  uint64 endOffset() const;
164  uint64 maxTotalSize() const;
165  implementationType* parent();
166  const implementationType* parent() const;
167  implementationType* nextSibling();
168  const implementationType* nextSibling() const;
169  implementationType* firstChild();
170  const implementationType* firstChild() const;
171  implementationType* subelementByPath(const std::initializer_list<identifierType> &path);
172  implementationType* subelementByPath(std::list<identifierType> &path);
173  implementationType* childById(const identifierType &id);
174  implementationType* siblingById(const identifierType &id, bool includeThis = false);
177  const FileElementIterator<implementationType> begin() const;
178  const FileElementIterator<implementationType> end() const;
179  bool isParent() const;
180  bool isPadding() const;
181  uint64 firstChildOffset() const;
182  bool isParsed() const;
183  void clear();
184  void parse();
185  void reparse();
186  void validateSubsequentElementStructure(NotificationList &gatheredNotifications, uint64 *paddingSize = nullptr);
187  static constexpr uint32 maximumIdLengthSupported();
188  static constexpr uint32 maximumSizeLengthSupported();
189  static constexpr byte minimumElementSize();
190  void copyHeader(std::ostream &targetStream);
191  void copyWithoutChilds(std::ostream &targetStream);
192  void copyEntirely(std::ostream &targetStream);
193  void makeBuffer();
194  void discardBuffer();
195  void copyBuffer(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  stream().seekg(0, std::ios_base::end);
234  m_maxSize = static_cast<uint64>(stream().tellg());
235  if(m_maxSize > startOffset) {
236  m_maxSize -= startOffset;
237  stream().seekg(startOffset, std::ios_base::beg);
238  } else {
239  m_maxSize = 0;
240  }
241 }
242 
246 template <class ImplementationType>
248  m_id(identifierType()),
249  m_startOffset(startOffset),
250  m_maxSize(parent.startOffset() + parent.totalSize() - startOffset),
251  m_idLength(0),
252  m_dataSize(0),
253  m_sizeLength(0),
254  m_parent(&parent),
255  m_container(&parent.container()),
256  m_parsed(false)
257 {}
258 
262 template <class ImplementationType>
264  m_id(identifierType()),
265  m_startOffset(startOffset),
266  m_maxSize(maxSize),
267  m_idLength(0),
268  m_dataSize(0),
269  m_sizeLength(0),
270  m_parent(nullptr),
271  m_container(&container),
272  m_parsed(false)
273 {}
274 
278 template <class ImplementationType>
280 {
281  return *m_container;
282 }
283 
287 template <class ImplementationType>
289 {
290  return *m_container;
291 }
292 
296 template <class ImplementationType>
298 {
299  return m_container->stream();
300 }
301 
305 template <class ImplementationType>
306 inline IoUtilities::BinaryReader &GenericFileElement<ImplementationType>::reader()
307 {
308  return m_container->reader();
309 }
310 
314 template <class ImplementationType>
315 inline IoUtilities::BinaryWriter &GenericFileElement<ImplementationType>::writer()
316 {
317  return m_container->writer();
318 }
319 
323 template <class ImplementationType>
325 {
326  return m_startOffset;
327 }
328 
332 template <class ImplementationType>
334 {
335  return parent() ? startOffset() - parent()->startOffset() : startOffset();
336 }
337 
341 template <class ImplementationType>
343 {
344  return m_id;
345 }
346 
350 template <class ImplementationType>
352 {
353  return static_cast<ImplementationType *>(this)->idToString();
354 }
355 
359 template <class ImplementationType>
361 {
362  return m_idLength;
363 }
364 
370 template <class ImplementationType>
372 {
373  return m_idLength + m_sizeLength;
374 }
375 
381 template <class ImplementationType>
383 {
384  return m_dataSize;
385 }
386 
390 template <class ImplementationType>
392 {
393  return m_sizeLength;
394 }
395 
401 template <class ImplementationType>
403 {
404  return startOffset() + headerSize();
405 }
406 
412 template <class ImplementationType>
414 {
415  return headerSize() + dataSize();
416 }
417 
421 template <class ImplementationType>
423 {
424  return startOffset() + totalSize();
425 }
426 
433 template <class ImplementationType>
435 {
436  return m_maxSize;
437 }
438 
445 template <class ImplementationType>
447 {
448  return m_parent;
449 }
450 
457 template <class ImplementationType>
459 {
460  return m_parent;
461 }
462 
471 template <class ImplementationType>
473 {
474  return m_nextSibling.get();
475 }
476 
485 template <class ImplementationType>
487 {
488  return m_nextSibling.get();
489 }
490 
499 template <class ImplementationType>
501 {
502  return m_firstChild.get();
503 }
504 
513 template <class ImplementationType>
515 {
516  return m_firstChild.get();
517 }
518 
528 template <class ImplementationType>
530 {
531  std::list<GenericFileElement<ImplementationType>::identifierType> list(path);
532  return subelementByPath(list);
533 }
534 
545 template <class ImplementationType>
547 {
548  parse(); // ensure element is parsed
549  if(path.size()) {
550  if(path.front() == id()) {
551  if(path.size() == 1) {
552  return static_cast<implementationType*>(this);
553  } else {
554  if(firstChild()) {
555  path.pop_front();
556  return firstChild()->subelementByPath(path);
557  }
558  }
559  } else {
560  if(nextSibling()) {
561  return nextSibling()->subelementByPath(path);
562  }
563  }
564  }
565  return nullptr;
566 }
567 
577 template <class ImplementationType>
579 {
580  parse(); // ensure element is parsed
581  for(implementationType *child = firstChild(); child; child = child->nextSibling()) {
582  child->parse();
583  if(child->id() == id) {
584  return child;
585  }
586  }
587  return nullptr;
588 }
589 
604 template <class ImplementationType>
606 {
607  parse(); // ensure element is parsed
608  for(implementationType *sibling = includeThis ? static_cast<implementationType*>(this) : nextSibling(); sibling; sibling = sibling->nextSibling()) {
609  sibling->parse();
610  if(sibling->id() == id) {
611  return sibling;
612  }
613  }
614  return nullptr;
615 }
616 
620 template <class ImplementationType>
622 {
623  return FileElementIterator<implementationType>(firstChild());
624 }
625 
629 template <class ImplementationType>
631 {
632  return FileElementIterator<implementationType>(firstChild());
633 }
634 
638 template <class ImplementationType>
640 {
642 }
643 
647 template <class ImplementationType>
649 {
651 }
652 
656 template <class ImplementationType>
658 {
659  return static_cast<const ImplementationType *>(this)->isParent();
660 }
661 
665 template <class ImplementationType>
667 {
668  return static_cast<const ImplementationType *>(this)->isPadding();
669 }
670 
674 template <class ImplementationType>
676 {
677  return static_cast<const ImplementationType *>(this)->firstChildOffset();
678 }
679 
683 template <class ImplementationType>
685 {
686  return m_parsed;
687 }
688 
695 template <class ImplementationType>
697 {
698  m_id = identifierType();
699  //m_startOffset = 0;
700  m_idLength = 0;
701  m_dataSize = 0;
702  m_sizeLength = 0;
703  m_nextSibling = nullptr;
704  m_firstChild = nullptr;
705  m_parsed = false;
706 }
707 
722 template <class ImplementationType>
724 {
725  if(!m_parsed) {
726  static_cast<ImplementationType *>(this)->internalParse();
727  m_parsed = true;
728  }
729 }
730 
747 template <class ImplementationType>
749 {
750  clear();
751  static_cast<ImplementationType *>(this)->parse();
752  m_parsed = true;
753 }
754 
767 template <class ImplementationType>
769 {
770  try {
771  parse();
772  gatheredNotifications.insert(gatheredNotifications.end(), notifications().begin(), notifications().end());
773  if(firstChild()) { // element is parent
774  firstChild()->validateSubsequentElementStructure(gatheredNotifications, paddingSize);
775  } else if(paddingSize && isPadding()) { // element is padding
776  *paddingSize += totalSize();
777  }
778  if(nextSibling()) {
779  nextSibling()->validateSubsequentElementStructure(gatheredNotifications, paddingSize);
780  }
781  } catch(Failure &) {
782  gatheredNotifications.insert(gatheredNotifications.end(), notifications().begin(), notifications().end());
783  throw;
784  }
785 }
786 
790 template <class ImplementationType>
792 {
793  copyInternal(targetStream, startOffset(), headerSize());
794 }
795 
799 template <class ImplementationType>
801 {
802  if(uint32 firstChildOffset = this->firstChildOffset()) {
803  copyInternal(targetStream, startOffset(), firstChildOffset);
804  } else {
805  copyInternal(targetStream, startOffset(), totalSize());
806  }
807 }
808 
812 template <class ImplementationType>
814 {
815  copyInternal(targetStream, startOffset(), totalSize());
816 }
817 
822 template <class ImplementationType>
824 {
825  m_buffer = std::make_unique<char[]>(totalSize());
826  container().stream().seekg(startOffset());
827  container().stream().read(m_buffer.get(), totalSize());
828 }
829 
833 template <class ImplementationType>
835 {
836  m_buffer.reset();
837 }
838 
843 template <class ImplementationType>
844 inline void GenericFileElement<ImplementationType>::copyBuffer(std::ostream &targetStream)
845 {
846  targetStream.write(m_buffer.get(), totalSize());
847 }
848 
853 template <class ImplementationType>
854 inline const std::unique_ptr<char[]> &GenericFileElement<ImplementationType>::buffer()
855 {
856  return m_buffer;
857 }
858 
866 template <class ImplementationType>
867 void GenericFileElement<ImplementationType>::copyInternal(std::ostream &targetStream, uint64 startOffset, uint64 bytesToCopy)
868 {
869  invalidateStatus();
870  // ensure the header has been parsed correctly
871  try {
872  parse();
873  } catch(Failure &) {
874  throw InvalidDataException();
875  }
876  auto &stream = container().stream();
877  stream.seekg(startOffset); // seek to start offset
879  copyHelper.callbackCopy(stream, targetStream, bytesToCopy, std::bind(&GenericFileElement<ImplementationType>::isAborted, this), std::bind(&GenericFileElement<ImplementationType>::updatePercentage, this, std::placeholders::_1));
880  if(isAborted()) {
882  }
883 }
884 
889 template <class ImplementationType>
891 {
892  if(relativeFirstChildOffset + minimumElementSize() <= totalSize()) {
893  m_firstChild.reset(new implementationType(static_cast<implementationType &>(*this), startOffset() + relativeFirstChildOffset));
894  } else {
895  m_firstChild.reset();
896  }
897  return m_firstChild.get();
898 }
899 
903 template <class ImplementationType>
905 {
906  return sizeof(identifierType);
907 }
908 
912 template <class ImplementationType>
914 {
915  return sizeof(dataSizeType);
916 }
917 
921 template <class ImplementationType>
923 {
925 }
926 
941 }
942 
943 #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