
Warnings fixed are: - warning: type qualifiers ignored on function return type [-Wignored-qualifiers] - warning: unused parameter ... [-Wunused-parameter] - warning: comparison is always true due to limited range of data type [-Wtype-limits] git-svn-id: https://matroska.svn.sourceforge.net/svnroot/matroska/trunk/libebml@822 a6f86f6d-0131-4f8e-9e7b-e335508773d5
707 lines
20 KiB
C++
707 lines
20 KiB
C++
/****************************************************************************
|
|
** libebml : parse EBML files, see http://embl.sourceforge.net/
|
|
**
|
|
** <file/class description>
|
|
**
|
|
** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved.
|
|
**
|
|
** This library is free software; you can redistribute it and/or
|
|
** modify it under the terms of the GNU Lesser General Public
|
|
** License as published by the Free Software Foundation; either
|
|
** version 2.1 of the License, or (at your option) any later version.
|
|
**
|
|
** This library is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
** Lesser General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU Lesser General Public
|
|
** License along with this library; if not, write to the Free Software
|
|
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
**
|
|
** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.
|
|
**
|
|
** Contact license@matroska.org if any conditions of this licensing are
|
|
** not clear to you.
|
|
**
|
|
**********************************************************************/
|
|
|
|
/*!
|
|
\file
|
|
\version \$Id$
|
|
\author Steve Lhomme <robux4 @ users.sf.net>
|
|
*/
|
|
|
|
#include <cassert>
|
|
#include <cstring>
|
|
|
|
#include "ebml/EbmlElement.h"
|
|
#include "ebml/EbmlMaster.h"
|
|
#include "ebml/EbmlStream.h"
|
|
#include "ebml/EbmlVoid.h"
|
|
#include "ebml/EbmlDummy.h"
|
|
#include "ebml/EbmlContexts.h"
|
|
|
|
START_LIBEBML_NAMESPACE
|
|
|
|
/*!
|
|
\todo handle more than CodedSize of 5
|
|
*/
|
|
int CodedSizeLength(uint64 Length, unsigned int SizeLength, bool bSizeFinite)
|
|
{
|
|
unsigned int CodedSize;
|
|
if (bSizeFinite) {
|
|
// prepare the head of the size (000...01xxxxxx)
|
|
// optimal size
|
|
if (Length < 127) // 2^7 - 1
|
|
CodedSize = 1;
|
|
else if (Length < 16383) // 2^14 - 1
|
|
CodedSize = 2;
|
|
else if (Length < 2097151L) // 2^21 - 1
|
|
CodedSize = 3;
|
|
else if (Length < 268435455L) // 2^28 - 1
|
|
CodedSize = 4;
|
|
else CodedSize = 5;
|
|
} else {
|
|
if (Length <= 127) // 2^7 - 1
|
|
CodedSize = 1;
|
|
else if (Length <= 16383) // 2^14 - 1
|
|
CodedSize = 2;
|
|
else if (Length <= 2097151L) // 2^21 - 1
|
|
CodedSize = 3;
|
|
else if (Length <= 268435455L) // 2^28 - 1
|
|
CodedSize = 4;
|
|
else CodedSize = 5;
|
|
}
|
|
|
|
if (SizeLength > 0 && CodedSize < SizeLength) {
|
|
// defined size
|
|
CodedSize = SizeLength;
|
|
}
|
|
|
|
return CodedSize;
|
|
}
|
|
|
|
/*!
|
|
\todo handle more than CodedSize of 5
|
|
*/
|
|
int CodedSizeLengthSigned(int64 Length, unsigned int SizeLength)
|
|
{
|
|
unsigned int CodedSize;
|
|
// prepare the head of the size (000...01xxxxxx)
|
|
// optimal size
|
|
if (Length > -64 && Length < 64) // 2^6
|
|
CodedSize = 1;
|
|
else if (Length > -8192 && Length < 8192) // 2^13
|
|
CodedSize = 2;
|
|
else if (Length > -1048576L && Length < 1048576L) // 2^20
|
|
CodedSize = 3;
|
|
else if (Length > -134217728L && Length < 134217728L) // 2^27
|
|
CodedSize = 4;
|
|
else CodedSize = 5;
|
|
|
|
if (SizeLength > 0 && CodedSize < SizeLength) {
|
|
// defined size
|
|
CodedSize = SizeLength;
|
|
}
|
|
|
|
return CodedSize;
|
|
}
|
|
|
|
int CodedValueLength(uint64 Length, int CodedSize, binary * OutBuffer)
|
|
{
|
|
int _SizeMask = 0xFF;
|
|
OutBuffer[0] = 1 << (8 - CodedSize);
|
|
for (int i=1; i<CodedSize; i++) {
|
|
OutBuffer[CodedSize-i] = Length & 0xFF;
|
|
Length >>= 8;
|
|
_SizeMask >>= 1;
|
|
}
|
|
// first one use a OR with the "EBML size head"
|
|
OutBuffer[0] |= Length & 0xFF & _SizeMask;
|
|
return CodedSize;
|
|
}
|
|
|
|
int CodedValueLengthSigned(int64 Length, int CodedSize, binary * OutBuffer)
|
|
{
|
|
if (Length > -64 && Length < 64) // 2^6
|
|
Length += 63;
|
|
else if (Length > -8192 && Length < 8192) // 2^13
|
|
Length += 8191;
|
|
else if (Length > -1048576L && Length < 1048576L) // 2^20
|
|
Length += 1048575L;
|
|
else if (Length > -134217728L && Length < 134217728L) // 2^27
|
|
Length += 134217727L;
|
|
|
|
return CodedValueLength(Length, CodedSize, OutBuffer);
|
|
}
|
|
|
|
uint64 ReadCodedSizeValue(const binary * InBuffer, uint32 & BufferSize, uint64 & SizeUnknown)
|
|
{
|
|
binary SizeBitMask = 1 << 7;
|
|
uint64 Result = 0x7F;
|
|
unsigned int SizeIdx, PossibleSizeLength = 0;
|
|
binary PossibleSize[8];
|
|
|
|
SizeUnknown = 0x7F; // the last bit is discarded when computing the size
|
|
for (SizeIdx = 0; SizeIdx < BufferSize && SizeIdx < 8; SizeIdx++) {
|
|
if (InBuffer[0] & (SizeBitMask >> SizeIdx)) {
|
|
// ID found
|
|
PossibleSizeLength = SizeIdx + 1;
|
|
SizeBitMask >>= SizeIdx;
|
|
for (SizeIdx = 0; SizeIdx < PossibleSizeLength; SizeIdx++) {
|
|
PossibleSize[SizeIdx] = InBuffer[SizeIdx];
|
|
}
|
|
for (SizeIdx = 0; SizeIdx < PossibleSizeLength - 1; SizeIdx++) {
|
|
Result <<= 7;
|
|
Result |= 0xFF;
|
|
}
|
|
|
|
Result = 0;
|
|
Result |= PossibleSize[0] & ~SizeBitMask;
|
|
for (unsigned int i = 1; i<PossibleSizeLength; i++) {
|
|
Result <<= 8;
|
|
Result |= PossibleSize[i];
|
|
}
|
|
|
|
BufferSize = PossibleSizeLength;
|
|
|
|
return Result;
|
|
}
|
|
SizeUnknown <<= 7;
|
|
SizeUnknown |= 0xFF;
|
|
}
|
|
|
|
BufferSize = 0;
|
|
return 0;
|
|
}
|
|
|
|
int64 ReadCodedSizeSignedValue(const binary * InBuffer, uint32 & BufferSize, uint64 & SizeUnknown)
|
|
{
|
|
int64 Result = ReadCodedSizeValue(InBuffer, BufferSize, SizeUnknown);
|
|
|
|
if (BufferSize != 0)
|
|
{
|
|
switch (BufferSize)
|
|
{
|
|
case 1:
|
|
Result -= 63;
|
|
break;
|
|
case 2:
|
|
Result -= 8191;
|
|
break;
|
|
case 3:
|
|
Result -= 1048575L;
|
|
break;
|
|
case 4:
|
|
Result -= 134217727L;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
EbmlCallbacks::EbmlCallbacks(EbmlElement & (*Creator)(), const EbmlId & aGlobalId, const char * aDebugName, const EbmlSemanticContext & aContext)
|
|
:Create(Creator)
|
|
,GlobalId(aGlobalId)
|
|
,DebugName(aDebugName)
|
|
,Context(aContext)
|
|
{
|
|
assert((Create!=NULL) || !strcmp(aDebugName, "DummyElement"));
|
|
}
|
|
|
|
const EbmlSemantic & EbmlSemanticContext::GetSemantic(size_t i) const
|
|
{
|
|
assert(i<Size);
|
|
if (i<Size)
|
|
return MyTable[i];
|
|
else
|
|
return *(EbmlSemantic*)NULL;
|
|
}
|
|
|
|
|
|
EbmlElement::EbmlElement(uint64 aDefaultSize, bool bValueSet)
|
|
:DefaultSize(aDefaultSize)
|
|
,SizeLength(0) ///< write optimal size by default
|
|
,bSizeIsFinite(true)
|
|
,ElementPosition(0)
|
|
,SizePosition(0)
|
|
,bValueIsSet(bValueSet)
|
|
,DefaultIsSet(false)
|
|
,bLocked(false)
|
|
{
|
|
Size = DefaultSize;
|
|
}
|
|
|
|
EbmlElement::EbmlElement(const EbmlElement & ElementToClone)
|
|
:Size(ElementToClone.Size)
|
|
,DefaultSize(ElementToClone.DefaultSize)
|
|
,SizeLength(ElementToClone.SizeLength)
|
|
,bSizeIsFinite(ElementToClone.bSizeIsFinite)
|
|
,ElementPosition(ElementToClone.ElementPosition)
|
|
,SizePosition(ElementToClone.SizePosition)
|
|
,bValueIsSet(ElementToClone.bValueIsSet)
|
|
,DefaultIsSet(ElementToClone.DefaultIsSet)
|
|
,bLocked(ElementToClone.bLocked)
|
|
{
|
|
}
|
|
|
|
EbmlElement::~EbmlElement()
|
|
{
|
|
assert(!bLocked);
|
|
}
|
|
|
|
/*!
|
|
\todo this method is deprecated and should be called FindThisID
|
|
\todo replace the new RawElement with the appropriate class (when known)
|
|
*/
|
|
EbmlElement * EbmlElement::FindNextID(IOCallback & DataStream, const EbmlCallbacks & ClassInfos, uint64 MaxDataSize)
|
|
{
|
|
binary PossibleId[4];
|
|
int PossibleID_Length = 0;
|
|
binary PossibleSize[8]; // we don't support size stored in more than 64 bits
|
|
uint32 PossibleSizeLength = 0;
|
|
uint64 SizeUnknown;
|
|
uint64 SizeFound;
|
|
bool bElementFound = false;
|
|
|
|
binary BitMask;
|
|
uint64 aElementPosition, aSizePosition;
|
|
while (!bElementFound) {
|
|
// read ID
|
|
aElementPosition = DataStream.getFilePointer();
|
|
uint32 ReadSize = 0;
|
|
BitMask = 1 << 7;
|
|
while (1) {
|
|
ReadSize += DataStream.read(&PossibleId[PossibleID_Length], 1);
|
|
if (ReadSize == uint32(PossibleID_Length)) {
|
|
return NULL; // no more data ?
|
|
}
|
|
if (++PossibleID_Length > 4) {
|
|
return NULL; // we don't support element IDs over class D
|
|
}
|
|
if (PossibleId[0] & BitMask) {
|
|
// this is the last octet of the ID
|
|
// check wether that's the one we're looking for
|
|
/* if (PossibleID == EBML_INFO_ID(ClassInfos)) {
|
|
break;
|
|
} else {
|
|
/// \todo This element should be skipped (use a context ?)
|
|
}*/
|
|
bElementFound = true; /// \todo not exactly the one we're looking for
|
|
break;
|
|
}
|
|
BitMask >>= 1;
|
|
}
|
|
|
|
// read the data size
|
|
aSizePosition = DataStream.getFilePointer();
|
|
uint32 _SizeLength;
|
|
do {
|
|
if (PossibleSizeLength >= 8)
|
|
// Size is larger than 8 bytes
|
|
return NULL;
|
|
|
|
ReadSize += DataStream.read(&PossibleSize[PossibleSizeLength++], 1);
|
|
_SizeLength = PossibleSizeLength;
|
|
SizeFound = ReadCodedSizeValue(&PossibleSize[0], _SizeLength, SizeUnknown);
|
|
} while (_SizeLength == 0);
|
|
}
|
|
|
|
EbmlElement *Result = NULL;
|
|
EbmlId PossibleID(PossibleId, PossibleID_Length);
|
|
if (PossibleID == EBML_INFO_ID(ClassInfos)) {
|
|
// the element is the one expected
|
|
Result = &EBML_INFO_CREATE(ClassInfos);
|
|
} else {
|
|
/// \todo find the element in the context
|
|
Result = new EbmlDummy(PossibleID);
|
|
}
|
|
|
|
Result->SetSizeLength(PossibleSizeLength);
|
|
|
|
Result->Size = SizeFound;
|
|
|
|
if (!Result->ValidateSize() || (SizeFound != SizeUnknown && MaxDataSize < Result->Size)) {
|
|
delete Result;
|
|
return NULL;
|
|
}
|
|
|
|
// check if the size is not all 1s
|
|
if (SizeFound == SizeUnknown) {
|
|
// Size of this element is unknown
|
|
// only possible for Master elements
|
|
if (!Result->SetSizeInfinite()) {
|
|
/// \todo the element is not allowed to be infinite
|
|
delete Result;
|
|
return NULL;
|
|
}
|
|
} else Result->SetSizeInfinite(false);
|
|
Result->ElementPosition = aElementPosition;
|
|
Result->SizePosition = aSizePosition;
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
/*!
|
|
\todo replace the new RawElement with the appropriate class (when known)
|
|
\todo skip data for Dummy elements when they are not allowed
|
|
\todo better check of the size checking for upper elements (using a list of size for each level)
|
|
\param LowLevel Will be returned with the level of the element found compared to the context given
|
|
*/
|
|
EbmlElement * EbmlElement::FindNextElement(IOCallback & DataStream, const EbmlSemanticContext & Context, int & UpperLevel,
|
|
uint64 MaxDataSize, bool AllowDummyElt, unsigned int MaxLowerLevel)
|
|
{
|
|
int PossibleID_Length = 0;
|
|
binary PossibleIdNSize[16];
|
|
int PossibleSizeLength;
|
|
uint64 SizeUnknown;
|
|
int ReadIndex = 0; // trick for the algo, start index at 0
|
|
uint32 ReadSize = 0;
|
|
uint64 SizeFound;
|
|
int SizeIdx;
|
|
bool bFound;
|
|
int UpperLevel_original = UpperLevel;
|
|
|
|
do {
|
|
// read a potential ID
|
|
do {
|
|
assert(ReadIndex < 16);
|
|
// build the ID with the current Read Buffer
|
|
bFound = false;
|
|
binary IdBitMask = 1 << 7;
|
|
for (SizeIdx = 0; SizeIdx < ReadIndex && SizeIdx < 4; SizeIdx++) {
|
|
if (PossibleIdNSize[0] & (IdBitMask >> SizeIdx)) {
|
|
// ID found
|
|
PossibleID_Length = SizeIdx + 1;
|
|
IdBitMask >>= SizeIdx;
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (bFound) {
|
|
break;
|
|
}
|
|
|
|
if (ReadIndex >= 4) {
|
|
// ID not found
|
|
// shift left the read octets
|
|
memmove(&PossibleIdNSize[0],&PossibleIdNSize[1], --ReadIndex);
|
|
}
|
|
|
|
if (DataStream.read(&PossibleIdNSize[ReadIndex++], 1) == 0) {
|
|
return NULL; // no more data ?
|
|
}
|
|
ReadSize++;
|
|
|
|
} while (!bFound && MaxDataSize > ReadSize);
|
|
|
|
SizeIdx = ReadIndex;
|
|
ReadIndex -= PossibleID_Length;
|
|
|
|
// read the data size
|
|
uint32 _SizeLength;
|
|
PossibleSizeLength = ReadIndex;
|
|
while (1)
|
|
{
|
|
_SizeLength = PossibleSizeLength;
|
|
SizeFound = ReadCodedSizeValue(&PossibleIdNSize[PossibleID_Length], _SizeLength, SizeUnknown);
|
|
if (_SizeLength != 0) {
|
|
bFound = true;
|
|
break;
|
|
}
|
|
if (PossibleSizeLength >= 8) {
|
|
bFound = false;
|
|
break;
|
|
}
|
|
ReadSize += DataStream.read(&PossibleIdNSize[SizeIdx++], 1);
|
|
PossibleSizeLength++;
|
|
}
|
|
|
|
if (bFound) {
|
|
// find the element in the context and use the correct creator
|
|
EbmlId PossibleID(PossibleIdNSize, PossibleID_Length);
|
|
EbmlElement * Result = CreateElementUsingContext(PossibleID, Context, UpperLevel, false, AllowDummyElt, MaxLowerLevel);
|
|
///< \todo continue is misplaced
|
|
if (Result != NULL) {
|
|
if (AllowDummyElt || !Result->IsDummy()) {
|
|
Result->SetSizeLength(_SizeLength);
|
|
|
|
Result->Size = SizeFound;
|
|
// UpperLevel values
|
|
// -1 : global element
|
|
// 0 : child
|
|
// 1 : same level
|
|
// + : further parent
|
|
if (Result->ValidateSize() && (SizeFound == SizeUnknown || UpperLevel > 0 || MaxDataSize == 0 || MaxDataSize >= (PossibleID_Length + PossibleSizeLength + SizeFound))) {
|
|
if (SizeFound == SizeUnknown) {
|
|
Result->SetSizeInfinite();
|
|
}
|
|
|
|
Result->SizePosition = DataStream.getFilePointer() - SizeIdx + EBML_ID_LENGTH(PossibleID);
|
|
Result->ElementPosition = Result->SizePosition - EBML_ID_LENGTH(PossibleID);
|
|
// place the file at the beggining of the data
|
|
DataStream.setFilePointer(Result->SizePosition + _SizeLength);
|
|
return Result;
|
|
}
|
|
}
|
|
delete Result;
|
|
}
|
|
}
|
|
|
|
// recover all the data in the buffer minus one byte
|
|
ReadIndex = SizeIdx - 1;
|
|
memmove(&PossibleIdNSize[0], &PossibleIdNSize[1], ReadIndex);
|
|
UpperLevel = UpperLevel_original;
|
|
} while ( MaxDataSize > DataStream.getFilePointer() - SizeIdx + PossibleID_Length );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*!
|
|
\todo what happens if we are in a upper element with a known size ?
|
|
*/
|
|
EbmlElement * EbmlElement::SkipData(EbmlStream & DataStream, const EbmlSemanticContext & Context, EbmlElement * TestReadElt, bool AllowDummyElt)
|
|
{
|
|
EbmlElement * Result = NULL;
|
|
if (bSizeIsFinite) {
|
|
assert(TestReadElt == NULL);
|
|
assert(ElementPosition < SizePosition);
|
|
DataStream.I_O().setFilePointer(SizePosition + CodedSizeLength(Size, SizeLength, bSizeIsFinite) + Size, seek_beginning);
|
|
// DataStream.I_O().setFilePointer(Size, seek_current);
|
|
} else {
|
|
/////////////////////////////////////////////////
|
|
// read elements until an upper element is found
|
|
/////////////////////////////////////////////////
|
|
bool bEndFound = false;
|
|
while (!bEndFound && Result == NULL) {
|
|
// read an element
|
|
/// \todo 0xFF... and true should be configurable
|
|
// EbmlElement * NewElt;
|
|
if (TestReadElt == NULL) {
|
|
int bUpperElement = 0; // trick to call FindNextID correctly
|
|
Result = DataStream.FindNextElement(Context, bUpperElement, 0xFFFFFFFFL, AllowDummyElt);
|
|
} else {
|
|
Result = TestReadElt;
|
|
TestReadElt = NULL;
|
|
}
|
|
|
|
if (Result != NULL) {
|
|
unsigned int EltIndex;
|
|
// data known in this Master's context
|
|
for (EltIndex = 0; EltIndex < EBML_CTX_SIZE(Context); EltIndex++) {
|
|
if (EbmlId(*Result) == EBML_CTX_IDX_ID(Context,EltIndex)) {
|
|
// skip the data with its own context
|
|
Result = Result->SkipData(DataStream, EBML_SEM_CONTEXT(EBML_CTX_IDX(Context,EltIndex)), NULL);
|
|
break; // let's go to the next ID
|
|
}
|
|
}
|
|
|
|
if (EltIndex >= EBML_CTX_SIZE(Context)) {
|
|
if (EBML_CTX_PARENT(Context) != NULL) {
|
|
Result = SkipData(DataStream, *EBML_CTX_PARENT(Context), Result);
|
|
} else {
|
|
assert(Context.GetGlobalContext != NULL);
|
|
if (Context != Context.GetGlobalContext()) {
|
|
Result = SkipData(DataStream, Context.GetGlobalContext(), Result);
|
|
} else {
|
|
bEndFound = true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
bEndFound = true;
|
|
}
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
EbmlElement *EbmlElement::CreateElementUsingContext(const EbmlId & aID, const EbmlSemanticContext & Context,
|
|
int & LowLevel, bool IsGlobalContext, bool bAllowDummy, unsigned int MaxLowerLevel)
|
|
{
|
|
unsigned int ContextIndex;
|
|
EbmlElement *Result = NULL;
|
|
|
|
// elements at the current level
|
|
for (ContextIndex = 0; ContextIndex < EBML_CTX_SIZE(Context); ContextIndex++) {
|
|
if (aID == EBML_CTX_IDX_ID(Context,ContextIndex)) {
|
|
return &EBML_SEM_CREATE(EBML_CTX_IDX(Context,ContextIndex));
|
|
}
|
|
}
|
|
|
|
// global elements
|
|
assert(Context.GetGlobalContext != NULL); // global should always exist, at least the EBML ones
|
|
const EbmlSemanticContext & tstContext = Context.GetGlobalContext();
|
|
if (tstContext != Context) {
|
|
LowLevel--;
|
|
MaxLowerLevel--;
|
|
// recursive is good, but be carefull...
|
|
Result = CreateElementUsingContext(aID, tstContext, LowLevel, true, bAllowDummy, MaxLowerLevel);
|
|
if (Result != NULL) {
|
|
return Result;
|
|
}
|
|
LowLevel++;
|
|
MaxLowerLevel++;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
|
|
// parent elements
|
|
if (EBML_CTX_MASTER(Context) != NULL && aID == EBML_INFO_ID(*EBML_CTX_MASTER(Context))) {
|
|
LowLevel++; // already one level up (same as context)
|
|
return &EBML_INFO_CREATE(*EBML_CTX_MASTER(Context));
|
|
}
|
|
|
|
// check wether it's not part of an upper context
|
|
if (EBML_CTX_PARENT(Context) != NULL) {
|
|
LowLevel++;
|
|
MaxLowerLevel++;
|
|
return CreateElementUsingContext(aID, *EBML_CTX_PARENT(Context), LowLevel, IsGlobalContext, bAllowDummy, MaxLowerLevel);
|
|
}
|
|
|
|
if (!IsGlobalContext && bAllowDummy) {
|
|
LowLevel = 0;
|
|
Result = new EbmlDummy(aID);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/*!
|
|
\todo verify that the size written is the same as the data written
|
|
*/
|
|
filepos_t EbmlElement::Render(IOCallback & output, bool bWithDefault, bool bKeepPosition, bool bForceRender)
|
|
{
|
|
assert(bValueIsSet || (bWithDefault && DefaultISset())); // an element is been rendered without a value set !!!
|
|
// it may be a mandatory element without a default value
|
|
try {
|
|
if (!bWithDefault && IsDefaultValue()) {
|
|
return 0;
|
|
}
|
|
#if defined(LIBEBML_DEBUG)
|
|
uint64 SupposedSize = UpdateSize(bWithDefault, bForceRender);
|
|
#endif // LIBEBML_DEBUG
|
|
filepos_t result = RenderHead(output, bForceRender, bWithDefault, bKeepPosition);
|
|
uint64 WrittenSize = RenderData(output, bForceRender, bWithDefault);
|
|
#if defined(LIBEBML_DEBUG)
|
|
if (static_cast<int64>(SupposedSize) != (0-1)) assert(WrittenSize == SupposedSize);
|
|
#endif // LIBEBML_DEBUG
|
|
result += WrittenSize;
|
|
return result;
|
|
} catch (std::exception & ex) {
|
|
// const char * What = ex.what();
|
|
assert(false); // we should never be here !
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\todo store the position of the Size writing for elements with unknown size
|
|
\todo handle exceptions on errors
|
|
\todo handle CodeSize bigger than 5 bytes
|
|
*/
|
|
filepos_t EbmlElement::RenderHead(IOCallback & output, bool bForceRender, bool bWithDefault, bool bKeepPosition)
|
|
{
|
|
if (EBML_ID_LENGTH((const EbmlId&)*this) <= 0 || EBML_ID_LENGTH((const EbmlId&)*this) > 4)
|
|
return 0;
|
|
|
|
UpdateSize(bWithDefault, bForceRender);
|
|
|
|
return MakeRenderHead(output, bKeepPosition);
|
|
}
|
|
|
|
filepos_t EbmlElement::MakeRenderHead(IOCallback & output, bool bKeepPosition)
|
|
{
|
|
binary FinalHead[4+8]; // Class D + 64 bits coded size
|
|
unsigned int FinalHeadSize;
|
|
|
|
FinalHeadSize = EBML_ID_LENGTH((const EbmlId&)*this);
|
|
EbmlId(*this).Fill(FinalHead);
|
|
|
|
int CodedSize = CodedSizeLength(Size, SizeLength, bSizeIsFinite);
|
|
CodedValueLength(Size, CodedSize, &FinalHead[FinalHeadSize]);
|
|
FinalHeadSize += CodedSize;
|
|
|
|
output.writeFully(FinalHead, FinalHeadSize);
|
|
if (!bKeepPosition) {
|
|
ElementPosition = output.getFilePointer() - FinalHeadSize;
|
|
SizePosition = ElementPosition + EBML_ID_LENGTH((const EbmlId&)*this);
|
|
}
|
|
|
|
return FinalHeadSize;
|
|
}
|
|
|
|
uint64 EbmlElement::ElementSize(bool bWithDefault) const
|
|
{
|
|
if (!bWithDefault && IsDefaultValue())
|
|
return 0; // won't be saved
|
|
return Size + EBML_ID_LENGTH((const EbmlId&)*this) + CodedSizeLength(Size, SizeLength, bSizeIsFinite);
|
|
}
|
|
|
|
bool EbmlElement::IsSmallerThan(const EbmlElement *Cmp) const
|
|
{
|
|
return EbmlId(*this) == EbmlId(*Cmp);
|
|
}
|
|
|
|
bool EbmlElement::CompareElements(const EbmlElement *A, const EbmlElement *B)
|
|
{
|
|
if (EbmlId(*A) == EbmlId(*B))
|
|
return A->IsSmallerThan(B);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void EbmlElement::Read(EbmlStream & inDataStream, const EbmlSemanticContext & /* Context */, int & /* UpperEltFound */, EbmlElement * & /* FoundElt */, bool /* AllowDummyElt */, ScopeMode ReadFully)
|
|
{
|
|
ReadData(inDataStream.I_O(), ReadFully);
|
|
}
|
|
|
|
bool EbmlElement::ForceSize(uint64 NewSize)
|
|
{
|
|
if (bSizeIsFinite) {
|
|
return false;
|
|
}
|
|
|
|
int OldSizeLen = CodedSizeLength(Size, SizeLength, bSizeIsFinite);
|
|
uint64 OldSize = Size;
|
|
|
|
Size = NewSize;
|
|
|
|
if (CodedSizeLength(Size, SizeLength, bSizeIsFinite) == OldSizeLen) {
|
|
bSizeIsFinite = true;
|
|
return true;
|
|
}
|
|
Size = OldSize;
|
|
|
|
return false;
|
|
}
|
|
|
|
filepos_t EbmlElement::OverwriteHead(IOCallback & output, bool bKeepPosition)
|
|
{
|
|
if (ElementPosition == 0) {
|
|
return 0; // the element has not been written
|
|
}
|
|
|
|
uint64 CurrentPosition = output.getFilePointer();
|
|
output.setFilePointer(GetElementPosition());
|
|
filepos_t Result = MakeRenderHead(output, bKeepPosition);
|
|
output.setFilePointer(CurrentPosition);
|
|
return Result;
|
|
}
|
|
|
|
uint64 EbmlElement::VoidMe(IOCallback & output, bool bWithDefault)
|
|
{
|
|
if (ElementPosition == 0) {
|
|
return 0; // the element has not been written
|
|
}
|
|
|
|
EbmlVoid Dummy;
|
|
return Dummy.Overwrite(*this, output, bWithDefault);
|
|
}
|
|
|
|
END_LIBEBML_NAMESPACE
|