2015-09-06 19:57:33 +02:00
# include "./matroskaseekinfo.h"
# include "./matroskaid.h"
2015-04-22 19:22:01 +02:00
2018-03-05 17:49:29 +01:00
# include "../diagnostics.h"
2018-03-07 01:17:50 +01:00
# include "../exceptions.h"
2015-04-22 19:22:01 +02:00
# include <c++utilities/conversion/binaryconversion.h>
2018-03-07 01:17:50 +01:00
# include <c++utilities/conversion/stringbuilder.h>
2015-04-22 19:22:01 +02:00
# include <string>
using namespace std ;
2019-06-10 22:49:11 +02:00
using namespace CppUtilities ;
2015-04-22 19:22:01 +02:00
2018-03-06 23:09:15 +01:00
namespace TagParser {
2015-04-22 19:22:01 +02:00
/*!
2018-06-03 20:38:32 +02:00
* \ class TagParser : : MatroskaSeekInfo
2015-11-26 14:34:52 +01:00
* \ brief The MatroskaSeekInfo class helps parsing and making " SeekHead " - elements .
2015-04-22 19:22:01 +02:00
*/
/*!
2017-03-01 18:21:00 +01:00
* \ brief Shifts all offsets greather or equal than \ a start by \ a amount bytes .
2015-04-22 19:22:01 +02:00
*/
2019-03-13 19:06:42 +01:00
void MatroskaSeekInfo : : shift ( std : : uint64_t start , std : : int64_t amount )
2015-04-22 19:22:01 +02:00
{
2018-03-07 01:17:50 +01:00
for ( auto & info : m_info ) {
if ( get < 1 > ( info ) > = start ) {
2018-06-02 22:56:08 +02:00
if ( amount > 0 ) {
2019-03-13 19:06:42 +01:00
get < 1 > ( info ) + = static_cast < std : : uint64_t > ( amount ) ;
2018-06-02 22:56:08 +02:00
} else {
2019-03-13 19:06:42 +01:00
get < 1 > ( info ) - = static_cast < std : : uint64_t > ( - amount ) ;
2018-06-02 22:56:08 +02:00
}
2015-04-22 19:22:01 +02:00
}
}
}
/*!
2019-06-17 19:11:00 +02:00
* \ brief Parses the specified \ a seekHeadElement and populates info ( ) with the gathered information .
2015-04-22 19:22:01 +02:00
* \ throws Throws ios_base : : failure when an IO error occurs .
* \ throws Throws Failure or a derived exception when a parsing error occurs .
2019-06-17 19:11:00 +02:00
* \ remarks
* - The object does not take ownership over the specified \ a seekHeadElement .
* - Possibly previously parsed info ( ) is not cleared . So subsequent calls can be used to gather seek
* information from multiple seek head elements . Use clear ( ) manually if that is not wanted .
* - If the specified \ a seekHeadElement references another seek head element the referenced seek head
* element is parsed as well . One can set \ a maxNesting to 0 to prevent that or even increase the value
* to allow following references even more deeply . References to elements which have already been visited
* are never followed , though .
2015-04-22 19:22:01 +02:00
*/
2019-06-17 19:11:00 +02:00
void MatroskaSeekInfo : : parse ( EbmlElement * seekHeadElement , Diagnostics & diag , size_t maxNesting )
2015-04-22 19:22:01 +02:00
{
static const string context ( " parsing \" SeekHead \" -element " ) ;
2019-06-17 19:11:00 +02:00
m_seekHeadElements . emplace_back ( seekHeadElement ) ;
for ( EbmlElement * seekElement = seekHeadElement - > firstChild ( ) , * seekIdElement , * seekPositionElement ; seekElement ; seekElement = seekElement - > nextSibling ( ) ) {
2018-03-05 17:49:29 +01:00
seekElement - > parse ( diag ) ;
2018-03-07 01:17:50 +01:00
switch ( seekElement - > id ( ) ) {
2015-04-22 19:22:01 +02:00
case MatroskaIds : : Seek :
seekIdElement = seekPositionElement = nullptr ;
2019-06-17 19:11:00 +02:00
for ( auto * seekElementChild = seekElement - > firstChild ( ) ; seekElementChild ; seekElementChild = seekElementChild - > nextSibling ( ) ) {
2018-03-05 17:49:29 +01:00
seekElementChild - > parse ( diag ) ;
2018-03-07 01:17:50 +01:00
switch ( seekElementChild - > id ( ) ) {
2015-04-22 19:22:01 +02:00
case MatroskaIds : : SeekID :
2018-03-07 01:17:50 +01:00
if ( seekIdElement ) {
diag . emplace_back ( DiagLevel : : Warning ,
" The \" Seek \" -element contains multiple \" SeekID \" -elements. Surplus elements will be ignored. " , context ) ;
2015-04-22 19:22:01 +02:00
}
seekIdElement = seekElementChild ;
break ;
case MatroskaIds : : SeekPosition :
2018-03-07 01:17:50 +01:00
if ( seekPositionElement ) {
diag . emplace_back ( DiagLevel : : Warning ,
" The \" Seek \" -element contains multiple \" SeekPosition \" -elements. Surplus elements will be ignored. " , context ) ;
2015-04-22 19:22:01 +02:00
}
seekPositionElement = seekElementChild ;
break ;
case EbmlIds : : Crc32 :
case EbmlIds : : Void :
break ;
default :
2018-03-07 01:17:50 +01:00
diag . emplace_back ( DiagLevel : : Warning ,
" The element \" " % seekElementChild - > idToString ( )
+ " \" within the \" Seek \" element is not a \" SeekID \" -element nor a \" SeekPosition \" -element and will be ignored. " ,
context ) ;
2015-04-22 19:22:01 +02:00
}
}
2019-06-17 19:11:00 +02:00
if ( ! seekIdElement | | ! seekPositionElement ) {
2018-03-05 17:49:29 +01:00
diag . emplace_back ( DiagLevel : : Warning , " The \" Seek \" -element does not contain a \" SeekID \" - and a \" SeekPosition \" -element. " , context ) ;
2019-06-17 19:11:00 +02:00
break ;
}
m_info . emplace_back ( seekIdElement - > readUInteger ( ) , seekPositionElement - > readUInteger ( ) ) ;
// follow possibly referenced seek head element
if ( m_info . back ( ) . first = = MatroskaIds : : SeekHead ) {
const auto startOffset = m_info . back ( ) . second ;
if ( ! maxNesting ) {
diag . emplace_back ( DiagLevel : : Warning ,
argsToString ( " Not following reference by \" Seek \" element at " , seekElement - > startOffset ( ) , " contains to another \" SeekHead \" element at " , startOffset , ' . ' ) ,
context ) ;
break ;
}
auto visited = false ;
for ( const auto * const visitedSeekHeadElement : m_seekHeadElements ) {
if ( visitedSeekHeadElement - > startOffset ( ) = = startOffset ) {
diag . emplace_back ( DiagLevel : : Warning ,
argsToString ( " The \" Seek \" element at " , seekElement - > startOffset ( ) , " contains a loop to the \" SeekHead \" element at " , visitedSeekHeadElement - > startOffset ( ) , ' . ' ) ,
context ) ;
visited = true ;
break ;
}
}
if ( visited ) {
break ;
}
m_additionalSeekHeadElements . emplace_back ( make_unique < EbmlElement > ( seekHeadElement - > container ( ) , startOffset ) ) ;
parse ( m_additionalSeekHeadElements . back ( ) . get ( ) , diag , maxNesting - 1 ) ;
2015-04-22 19:22:01 +02:00
}
2019-06-17 19:11:00 +02:00
2015-04-22 19:22:01 +02:00
break ;
case EbmlIds : : Crc32 :
case EbmlIds : : Void :
break ;
default :
2018-03-07 01:17:50 +01:00
diag . emplace_back (
DiagLevel : : Warning , " The element " % seekElement - > idToString ( ) + " is not a seek element and will be ignored. " , context ) ;
2015-04-22 19:22:01 +02:00
}
}
2018-03-07 01:17:50 +01:00
if ( m_info . empty ( ) ) {
2018-03-05 17:49:29 +01:00
diag . emplace_back ( DiagLevel : : Warning , " No seek information found. " , context ) ;
2015-04-22 19:22:01 +02:00
}
}
/*!
* \ brief Writes a " SeekHead " element for the current instance to the specified \ a stream .
* \ param stream Specifies the stream to write the " SeekHead " element to .
* \ throws Throws ios_base : : failure when an IO error occurs .
* \ throws Throws Failure or a derived exception when a making error occurs .
*/
2018-03-05 17:49:29 +01:00
void MatroskaSeekInfo : : make ( ostream & stream , Diagnostics & diag )
2015-04-22 19:22:01 +02:00
{
2019-06-12 20:40:45 +02:00
CPP_UTILITIES_UNUSED ( diag )
2018-06-02 22:56:08 +02:00
2019-03-13 19:06:42 +01:00
std : : uint64_t totalSize = 0 ;
2015-04-22 19:22:01 +02:00
char buff0 [ 8 ] ;
char buff1 [ 8 ] ;
char buff2 [ 2 ] ;
2019-03-13 19:06:42 +01:00
std : : uint8_t sizeLength0 , sizeLength1 ;
2015-04-22 19:22:01 +02:00
// calculate size
2018-03-07 01:17:50 +01:00
for ( const auto & info : m_info ) {
2015-04-22 19:22:01 +02:00
// "Seek" element + "SeekID" element + "SeekPosition" element
totalSize + = 2 + 1 + ( 2 + 1 + EbmlElement : : calculateIdLength ( get < 0 > ( info ) ) ) + ( 2 + 1 + EbmlElement : : calculateUIntegerLength ( get < 1 > ( info ) ) ) ;
}
// write ID and size
2019-03-13 19:06:42 +01:00
BE : : getBytes ( static_cast < std : : uint32_t > ( MatroskaIds : : SeekHead ) , buff0 ) ;
2015-04-22 19:22:01 +02:00
stream . write ( buff0 , 4 ) ;
sizeLength0 = EbmlElement : : makeSizeDenotation ( totalSize , buff0 ) ;
stream . write ( buff0 , sizeLength0 ) ;
// write entries
2018-03-07 01:17:50 +01:00
for ( const auto & info : m_info ) {
2015-04-22 19:22:01 +02:00
// make values
sizeLength0 = EbmlElement : : makeId ( get < 0 > ( info ) , buff0 ) ;
sizeLength1 = EbmlElement : : makeUInteger ( get < 1 > ( info ) , buff1 ) ;
// "Seek" header
2019-03-13 19:06:42 +01:00
BE : : getBytes ( static_cast < std : : uint16_t > ( MatroskaIds : : Seek ) , buff2 ) ;
2015-04-22 19:22:01 +02:00
stream . write ( buff2 , 2 ) ;
2018-06-02 22:56:08 +02:00
stream . put ( static_cast < char > ( 0x80 | ( 2 + 1 + sizeLength0 + 2 + 1 + sizeLength1 ) ) ) ;
2015-04-22 19:22:01 +02:00
// "SeekID" element
2019-03-13 19:06:42 +01:00
BE : : getBytes ( static_cast < std : : uint16_t > ( MatroskaIds : : SeekID ) , buff2 ) ;
2015-04-22 19:22:01 +02:00
stream . write ( buff2 , 2 ) ;
2018-06-02 22:56:08 +02:00
stream . put ( static_cast < char > ( 0x80 | sizeLength0 ) ) ;
2015-04-22 19:22:01 +02:00
stream . write ( buff0 , sizeLength0 ) ;
// "SeekPosition" element
2019-03-13 19:06:42 +01:00
BE : : getBytes ( static_cast < std : : uint16_t > ( MatroskaIds : : SeekPosition ) , buff2 ) ;
2015-04-22 19:22:01 +02:00
stream . write ( buff2 , 2 ) ;
2018-06-02 22:56:08 +02:00
stream . put ( static_cast < char > ( 0x80 | sizeLength1 ) ) ;
2015-04-22 19:22:01 +02:00
stream . write ( buff1 , sizeLength1 ) ;
}
}
/*!
* \ brief Returns the minimal number of bytes written when calling the make ( ) method .
* \ remarks The returned value gets invalidated when the object is mutated .
*/
2019-03-13 19:06:42 +01:00
std : : uint64_t MatroskaSeekInfo : : minSize ( ) const
2015-04-22 19:22:01 +02:00
{
2019-03-13 19:06:42 +01:00
std : : uint64_t maxTotalSize = m_info . size ( ) * ( 2 + 1 + 2 + 1 + 1 + 2 + 1 + 1 ) ;
2015-04-22 19:22:01 +02:00
return 4 + EbmlElement : : calculateSizeDenotationLength ( maxTotalSize ) + maxTotalSize ;
}
/*!
* \ brief Returns the maximal number of bytes written when calling the make ( ) method .
* \ remarks The returned value gets invalidated when the object is mutated .
*/
2019-03-13 19:06:42 +01:00
std : : uint64_t MatroskaSeekInfo : : maxSize ( ) const
2015-04-22 19:22:01 +02:00
{
2019-03-13 19:06:42 +01:00
std : : uint64_t maxTotalSize = m_info . size ( ) * ( 2 + 1 + 2 + 1 + 4 + 2 + 1 + 8 ) ;
2015-04-22 19:22:01 +02:00
return 4 + EbmlElement : : calculateSizeDenotationLength ( maxTotalSize ) + maxTotalSize ;
}
/*!
* \ brief Returns the number of bytes which will be written when calling the make ( ) method .
* \ remarks The returned value gets invalidated when the object is mutated .
*/
2019-03-13 19:06:42 +01:00
std : : uint64_t MatroskaSeekInfo : : actualSize ( ) const
2015-04-22 19:22:01 +02:00
{
2019-03-13 19:06:42 +01:00
std : : uint64_t totalSize = 0 ;
2018-03-07 01:17:50 +01:00
for ( const auto & info : m_info ) {
2015-04-22 19:22:01 +02:00
// "Seek" element + "SeekID" element + "SeekPosition" element
totalSize + = 2 + 1 + ( 2 + 1 + EbmlElement : : calculateIdLength ( get < 0 > ( info ) ) ) + ( 2 + 1 + EbmlElement : : calculateUIntegerLength ( get < 1 > ( info ) ) ) ;
}
return totalSize + = 4 + EbmlElement : : calculateSizeDenotationLength ( totalSize ) ;
}
/*!
* \ brief Pushes the specified \ a offset of an element with the specified \ a id to the info .
*
* If there is an existing entry with the same \ a id and \ a index the existing entry will be
* updated and no new entry created .
*
* \ returns Returns an indication whether the actualSize ( ) has changed .
*/
2019-03-13 19:06:42 +01:00
bool MatroskaSeekInfo : : push ( unsigned int index , EbmlElement : : IdentifierType id , std : : uint64_t offset )
2015-04-22 19:22:01 +02:00
{
unsigned int currentIndex = 0 ;
2018-03-07 01:17:50 +01:00
for ( auto & entry : info ( ) ) {
if ( get < 0 > ( entry ) = = id ) {
if ( index = = currentIndex ) {
2015-04-22 19:22:01 +02:00
bool sizeUpdated = EbmlElement : : calculateUIntegerLength ( get < 1 > ( entry ) ) ! = EbmlElement : : calculateUIntegerLength ( offset ) ;
get < 1 > ( entry ) = offset ;
return sizeUpdated ;
}
+ + currentIndex ;
}
}
info ( ) . emplace_back ( id , offset ) ;
return true ;
}
/*!
* \ brief Resets the object to its initial state .
*/
void MatroskaSeekInfo : : clear ( )
{
m_seekHeadElement = nullptr ;
m_info . clear ( ) ;
}
/*!
* \ brief Returns a pointer to the first pair with the specified \ a offset or nullptr if no such pair could be found .
*/
2019-03-13 19:06:42 +01:00
std : : pair < EbmlElement : : IdentifierType , std : : uint64_t > * MatroskaSeekInfo : : findSeekInfo ( std : : vector < MatroskaSeekInfo > & seekInfos , std : : uint64_t offset )
2015-04-22 19:22:01 +02:00
{
2018-03-07 01:17:50 +01:00
for ( auto & seekInfo : seekInfos ) {
for ( auto & entry : seekInfo . info ( ) ) {
if ( get < 1 > ( entry ) = = offset ) {
2015-04-22 19:22:01 +02:00
return & entry ;
}
}
}
return nullptr ;
}
/*!
* \ brief Sets the offset of all entires in \ a newSeekInfos to \ a newOffset where the corresponding entry in \ a oldSeekInfos has the offset \ a oldOffset .
* \ returns Returns an indication whether the update altered the offset length .
*/
2018-03-07 01:17:50 +01:00
bool MatroskaSeekInfo : : updateSeekInfo (
2019-03-13 19:06:42 +01:00
const std : : vector < MatroskaSeekInfo > & oldSeekInfos , std : : vector < MatroskaSeekInfo > & newSeekInfos , std : : uint64_t oldOffset , std : : uint64_t newOffset )
2015-04-22 19:22:01 +02:00
{
bool updated = false ;
auto oldIterator0 = oldSeekInfos . cbegin ( ) , oldEnd0 = oldSeekInfos . cend ( ) ;
auto newIterator0 = newSeekInfos . begin ( ) , newEnd0 = newSeekInfos . end ( ) ;
2018-03-07 01:17:50 +01:00
for ( ; oldIterator0 ! = oldEnd0 & & newIterator0 ! = newEnd0 ; + + oldIterator0 , + + newIterator0 ) {
2015-04-22 19:22:01 +02:00
auto oldIterator1 = oldIterator0 - > info ( ) . cbegin ( ) , oldEnd1 = oldIterator0 - > info ( ) . cend ( ) ;
auto newIterator1 = newIterator0 - > info ( ) . begin ( ) , newEnd1 = newIterator0 - > info ( ) . end ( ) ;
2018-03-07 01:17:50 +01:00
for ( ; oldIterator1 ! = oldEnd1 & & newIterator1 ! = newEnd1 ; + + oldIterator1 , + + newIterator1 ) {
if ( get < 1 > ( * oldIterator1 ) = = oldOffset ) {
if ( get < 1 > ( * newIterator1 ) ! = newOffset ) {
updated
= updated | | ( EbmlElement : : calculateUIntegerLength ( newOffset ) ! = EbmlElement : : calculateUIntegerLength ( get < 1 > ( * newIterator1 ) ) ) ;
2015-04-22 19:22:01 +02:00
get < 1 > ( * newIterator1 ) = newOffset ;
}
}
}
}
return updated ;
}
/*!
* \ brief Sets the offset of all entires in \ a newSeekInfos to \ a newOffset where the offset is \ a oldOffset .
* \ returns Returns an whether at least one offset has been updated .
*/
2019-03-13 19:06:42 +01:00
bool MatroskaSeekInfo : : updateSeekInfo ( std : : vector < MatroskaSeekInfo > & newSeekInfos , std : : uint64_t oldOffset , std : : uint64_t newOffset )
2015-04-22 19:22:01 +02:00
{
2018-03-07 01:17:50 +01:00
if ( oldOffset = = newOffset ) {
2015-04-22 19:22:01 +02:00
return false ;
}
bool updated = false ;
2018-03-07 01:17:50 +01:00
for ( auto & seekInfo : newSeekInfos ) {
for ( auto & info : seekInfo . info ( ) ) {
if ( get < 1 > ( info ) = = oldOffset ) {
2015-04-22 19:22:01 +02:00
get < 1 > ( info ) = newOffset ;
updated = true ;
}
}
}
return updated ;
}
2018-03-07 01:17:50 +01:00
} // namespace TagParser