1 /****************************************************************************
2 ** libebml : parse EBML files, see http://embl.sourceforge.net/
4 ** <file/class description>
6 ** Copyright (C) 2002-2005 Steve Lhomme. All rights reserved.
8 ** This library is free software; you can redistribute it and/or
9 ** modify it under the terms of the GNU Lesser General Public
10 ** License as published by the Free Software Foundation; either
11 ** version 2.1 of the License, or (at your option) any later version.
13 ** This library is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ** Lesser General Public License for more details.
18 ** You should have received a copy of the GNU Lesser General Public
19 ** License along with this library; if not, write to the Free Software
20 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 ** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.
24 ** Contact license@matroska.org if any conditions of this licensing are
27 **********************************************************************/
31 \version \$Id: EbmlElement.cpp 1232 2005-10-15 15:56:52Z robux4 $
32 \author Steve Lhomme <robux4 @ users.sf.net>
37 #include "ebml/EbmlElement.h"
38 #include "ebml/EbmlMaster.h"
39 #include "ebml/EbmlStream.h"
40 #include "ebml/EbmlVoid.h"
41 #include "ebml/EbmlDummy.h"
42 #include "ebml/EbmlContexts.h"
44 START_LIBEBML_NAMESPACE
47 \todo handle more than CodedSize of 5
49 int CodedSizeLength(uint64 Length
, unsigned int SizeLength
, bool bSizeFinite
)
53 // prepare the head of the size (000...01xxxxxx)
55 if (Length
< 127) // 2^7 - 1
57 else if (Length
< 16383) // 2^14 - 1
59 else if (Length
< 2097151L) // 2^21 - 1
61 else if (Length
< 268435455L) // 2^28 - 1
65 if (Length
<= 127) // 2^7 - 1
67 else if (Length
<= 16383) // 2^14 - 1
69 else if (Length
<= 2097151L) // 2^21 - 1
71 else if (Length
<= 268435455L) // 2^28 - 1
76 if (SizeLength
> 0 && CodedSize
< SizeLength
) {
78 CodedSize
= SizeLength
;
85 \todo handle more than CodedSize of 5
87 int CodedSizeLengthSigned(int64 Length
, unsigned int SizeLength
)
89 unsigned int CodedSize
;
90 // prepare the head of the size (000...01xxxxxx)
92 if (Length
> -64 && Length
< 64) // 2^6
94 else if (Length
> -8192 && Length
< 8192) // 2^13
96 else if (Length
> -1048576L && Length
< 1048576L) // 2^20
98 else if (Length
> -134217728L && Length
< 134217728L) // 2^27
102 if (SizeLength
> 0 && CodedSize
< SizeLength
) {
104 CodedSize
= SizeLength
;
110 int CodedValueLength(uint64 Length
, int CodedSize
, binary
* OutBuffer
)
112 int _SizeMask
= 0xFF;
113 OutBuffer
[0] = 1 << (8 - CodedSize
);
114 for (int i
=1; i
<CodedSize
; i
++) {
115 OutBuffer
[CodedSize
-i
] = Length
& 0xFF;
119 // first one use a OR with the "EBML size head"
120 OutBuffer
[0] |= Length
& 0xFF & _SizeMask
;
124 int CodedValueLengthSigned(int64 Length
, int CodedSize
, binary
* OutBuffer
)
126 if (Length
> -64 && Length
< 64) // 2^6
128 else if (Length
> -8192 && Length
< 8192) // 2^13
130 else if (Length
> -1048576L && Length
< 1048576L) // 2^20
132 else if (Length
> -134217728L && Length
< 134217728L) // 2^27
133 Length
+= 134217727L;
135 return CodedValueLength(Length
, CodedSize
, OutBuffer
);
138 uint64
ReadCodedSizeValue(const binary
* InBuffer
, uint32
& BufferSize
, uint64
& SizeUnknown
)
140 binary SizeBitMask
= 1 << 7;
141 uint64 Result
= 0x7F;
142 unsigned int SizeIdx
, PossibleSizeLength
= 0;
143 binary PossibleSize
[8];
145 SizeUnknown
= 0x7F; // the last bit is discarded when computing the size
146 for (SizeIdx
= 0; SizeIdx
< BufferSize
&& SizeIdx
< 8; SizeIdx
++) {
147 if (InBuffer
[0] & (SizeBitMask
>> SizeIdx
)) {
149 PossibleSizeLength
= SizeIdx
+ 1;
150 SizeBitMask
>>= SizeIdx
;
151 for (SizeIdx
= 0; SizeIdx
< PossibleSizeLength
; SizeIdx
++) {
152 PossibleSize
[SizeIdx
] = InBuffer
[SizeIdx
];
154 for (SizeIdx
= 0; SizeIdx
< PossibleSizeLength
- 1; SizeIdx
++) {
160 Result
|= PossibleSize
[0] & ~SizeBitMask
;
161 for (unsigned int i
= 1; i
<PossibleSizeLength
; i
++) {
163 Result
|= PossibleSize
[i
];
166 BufferSize
= PossibleSizeLength
;
178 int64
ReadCodedSizeSignedValue(const binary
* InBuffer
, uint32
& BufferSize
, uint64
& SizeUnknown
)
180 int64 Result
= ReadCodedSizeValue(InBuffer
, BufferSize
, SizeUnknown
);
196 Result
-= 134217727L;
204 EbmlElement::EbmlElement(const uint64 aDefaultSize
, bool bValueSet
)
205 :DefaultSize(aDefaultSize
)
206 ,SizeLength(0) ///< write optimal size by default
210 ,bValueIsSet(bValueSet
)
217 EbmlElement::EbmlElement(const EbmlElement
& ElementToClone
)
218 :Size(ElementToClone
.Size
)
219 ,DefaultSize(ElementToClone
.DefaultSize
)
220 ,SizeLength(ElementToClone
.SizeLength
)
221 ,bSizeIsFinite(ElementToClone
.bSizeIsFinite
)
222 ,ElementPosition(ElementToClone
.ElementPosition
)
223 ,SizePosition(ElementToClone
.SizePosition
)
224 ,bValueIsSet(ElementToClone
.bValueIsSet
)
225 ,DefaultIsSet(ElementToClone
.DefaultIsSet
)
226 ,bLocked(ElementToClone
.bLocked
)
231 \todo this method is deprecated and should be called FindThisID
232 \todo replace the new RawElement with the appropriate class (when known)
234 EbmlElement
* EbmlElement::FindNextID(IOCallback
& DataStream
, const EbmlCallbacks
& ClassInfos
, const uint64 MaxDataSize
)
236 binary PossibleId
[4];
237 int PossibleID_Length
= 0;
238 binary PossibleSize
[8]; // we don't support size stored in more than 64 bits
239 uint32 PossibleSizeLength
= 0;
242 bool bElementFound
= false;
245 uint64 aElementPosition
, aSizePosition
;
246 while (!bElementFound
) {
248 aElementPosition
= DataStream
.getFilePointer();
252 ReadSize
+= DataStream
.read(&PossibleId
[PossibleID_Length
], 1);
253 if (ReadSize
== uint32(PossibleID_Length
)) {
254 return NULL
; // no more data ?
256 if (++PossibleID_Length
> 4) {
257 return NULL
; // we don't support element IDs over class D
259 if (PossibleId
[0] & BitMask
) {
260 // this is the last octet of the ID
261 // check whether that's the one we're looking for
262 /* if (PossibleID == ClassInfos.GlobalId) {
265 /// \todo This element should be skipped (use a context ?)
267 bElementFound
= true; /// \todo not exactly the one we're looking for
273 // read the data size
274 aSizePosition
= DataStream
.getFilePointer();
277 if (PossibleSizeLength
>= 8)
278 // Size is larger than 8 bytes
281 ReadSize
+= DataStream
.read(&PossibleSize
[PossibleSizeLength
++], 1);
282 _SizeLength
= PossibleSizeLength
;
283 SizeFound
= ReadCodedSizeValue(&PossibleSize
[0], _SizeLength
, SizeUnknown
);
284 } while (_SizeLength
== 0);
287 EbmlElement
*Result
= NULL
;
288 EbmlId
PossibleID(PossibleId
, PossibleID_Length
);
289 if (PossibleID
== ClassInfos
.GlobalId
) {
290 // the element is the one expected
291 Result
= &ClassInfos
.Create();
293 /// \todo find the element in the context
294 Result
= new EbmlDummy(PossibleID
);
297 Result
->SetSizeLength(PossibleSizeLength
);
299 Result
->Size
= SizeFound
;
301 if (!Result
->ValidateSize() || (SizeFound
!= SizeUnknown
&& MaxDataSize
< Result
->Size
)) {
306 // check if the size is not all 1s
307 if (SizeFound
== SizeUnknown
) {
308 // Size of this element is unknown
309 // only possible for Master elements
310 if (!Result
->SetSizeInfinite()) {
311 /// \todo the element is not allowed to be infinite
315 } else Result
->SetSizeInfinite(false);
316 Result
->ElementPosition
= aElementPosition
;
317 Result
->SizePosition
= aSizePosition
;
324 \todo replace the new RawElement with the appropriate class (when known)
325 \todo skip data for Dummy elements when they are not allowed
326 \todo better check of the size checking for upper elements (using a list of size for each level)
327 \param LowLevel Will be returned with the level of the element found compared to the context given
329 EbmlElement
* EbmlElement::FindNextElement(IOCallback
& DataStream
, const EbmlSemanticContext
& Context
, int & UpperLevel
,
330 uint64 MaxDataSize
, bool AllowDummyElt
, unsigned int MaxLowerLevel
)
332 int PossibleID_Length
= 0;
333 binary PossibleIdNSize
[16];
334 int PossibleSizeLength
;
336 int ReadIndex
= 0; // trick for the algo, start index at 0
341 int UpperLevel_original
= UpperLevel
;
344 // read a potential ID
346 assert(ReadIndex
< 16);
347 // build the ID with the current Read Buffer
349 binary IdBitMask
= 1 << 7;
350 for (SizeIdx
= 0; SizeIdx
< ReadIndex
&& SizeIdx
< 4; SizeIdx
++) {
351 if (PossibleIdNSize
[0] & (IdBitMask
>> SizeIdx
)) {
353 PossibleID_Length
= SizeIdx
+ 1;
354 IdBitMask
>>= SizeIdx
;
363 if (ReadIndex
>= 4) {
365 // shift left the read octets
366 memmove(&PossibleIdNSize
[0],&PossibleIdNSize
[1], --ReadIndex
);
369 if (DataStream
.read(&PossibleIdNSize
[ReadIndex
++], 1) == 0) {
370 return NULL
; // no more data ?
374 } while (!bFound
&& MaxDataSize
> ReadSize
);
377 ReadIndex
-= PossibleID_Length
;
379 // read the data size
381 PossibleSizeLength
= ReadIndex
;
384 _SizeLength
= PossibleSizeLength
;
385 SizeFound
= ReadCodedSizeValue(&PossibleIdNSize
[PossibleID_Length
], _SizeLength
, SizeUnknown
);
386 if (_SizeLength
!= 0) {
390 if (PossibleSizeLength
>= 8) {
394 ReadSize
+= DataStream
.read(&PossibleIdNSize
[SizeIdx
++], 1);
395 PossibleSizeLength
++;
399 // find the element in the context and use the correct creator
400 EbmlId
PossibleID(PossibleIdNSize
, PossibleID_Length
);
401 EbmlElement
* Result
= CreateElementUsingContext(PossibleID
, Context
, UpperLevel
, false, AllowDummyElt
, MaxLowerLevel
);
402 ///< \todo continue is misplaced
403 if (Result
!= NULL
) {
404 if (AllowDummyElt
|| !Result
->IsDummy()) {
405 Result
->SetSizeLength(_SizeLength
);
407 Result
->Size
= SizeFound
;
409 // -1 : global element
412 // + : further parent
413 if (Result
->ValidateSize() && (UpperLevel
> 0 || MaxDataSize
== 0 || MaxDataSize
>= (PossibleID_Length
+ PossibleSizeLength
+ SizeFound
))) {
414 if (SizeFound
== SizeUnknown
) {
415 Result
->SetSizeInfinite();
418 Result
->SizePosition
= DataStream
.getFilePointer() - SizeIdx
+ PossibleID
.Length
;
419 Result
->ElementPosition
= Result
->SizePosition
- PossibleID
.Length
;
420 // place the file at the beggining of the data
421 DataStream
.setFilePointer(Result
->SizePosition
+ _SizeLength
);
429 // recover all the data in the buffer minus one byte
430 ReadIndex
= SizeIdx
- 1;
431 memmove(&PossibleIdNSize
[0], &PossibleIdNSize
[1], ReadIndex
);
432 UpperLevel
= UpperLevel_original
;
433 } while ( MaxDataSize
> DataStream
.getFilePointer() - SizeIdx
+ PossibleID_Length
);
439 \todo what happens if we are in a upper element with a known size ?
441 EbmlElement
* EbmlElement::SkipData(EbmlStream
& DataStream
, const EbmlSemanticContext
& Context
, EbmlElement
* TestReadElt
, bool AllowDummyElt
)
443 EbmlElement
* Result
= NULL
;
445 assert(TestReadElt
== NULL
);
446 assert(ElementPosition
< SizePosition
);
447 DataStream
.I_O().setFilePointer(SizePosition
+ CodedSizeLength(Size
, SizeLength
, bSizeIsFinite
) + Size
, seek_beginning
);
448 // DataStream.I_O().setFilePointer(Size, seek_current);
450 /////////////////////////////////////////////////
451 // read elements until an upper element is found
452 /////////////////////////////////////////////////
453 bool bEndFound
= false;
454 while (!bEndFound
&& Result
== NULL
) {
456 /// \todo 0xFF... and true should be configurable
457 // EbmlElement * NewElt;
458 if (TestReadElt
== NULL
) {
459 int bUpperElement
= 0; // trick to call FindNextID correctly
460 Result
= DataStream
.FindNextElement(Context
, bUpperElement
, 0xFFFFFFFFL
, AllowDummyElt
);
462 Result
= TestReadElt
;
465 if (Result
!= NULL
) {
466 unsigned int EltIndex
;
467 // data known in this Master's context
468 for (EltIndex
= 0; EltIndex
< Context
.Size
; EltIndex
++) {
469 if (EbmlId(*Result
) == Context
.MyTable
[EltIndex
].GetCallbacks
.GlobalId
) {
470 // skip the data with its own context
471 Result
= Result
->SkipData(DataStream
, Context
.MyTable
[EltIndex
].GetCallbacks
.Context
, NULL
);
472 break; // let's go to the next ID
476 if (EltIndex
>= Context
.Size
) {
477 if (Context
.UpTable
!= NULL
) {
478 Result
= SkipData(DataStream
, *Context
.UpTable
, Result
);
480 assert(Context
.GetGlobalContext
!= NULL
);
481 if (Context
!= Context
.GetGlobalContext()) {
482 Result
= SkipData(DataStream
, Context
.GetGlobalContext(), Result
);
496 EbmlElement
*EbmlElement::CreateElementUsingContext(const EbmlId
& aID
, const EbmlSemanticContext
& Context
,
497 int & LowLevel
, bool IsGlobalContext
, bool bAllowDummy
, unsigned int MaxLowerLevel
)
499 unsigned int ContextIndex
;
500 EbmlElement
*Result
= NULL
;
502 // elements at the current level
503 for (ContextIndex
= 0; ContextIndex
< Context
.Size
; ContextIndex
++) {
504 if (aID
== Context
.MyTable
[ContextIndex
].GetCallbacks
.GlobalId
) {
505 return &Context
.MyTable
[ContextIndex
].GetCallbacks
.Create();
510 assert(Context
.GetGlobalContext
!= NULL
); // global should always exist, at least the EBML ones
511 const EbmlSemanticContext
& tstContext
= Context
.GetGlobalContext();
512 if (tstContext
!= Context
) {
515 // recursive is good, but be carefull...
516 Result
= CreateElementUsingContext(aID
, tstContext
, LowLevel
, true, bAllowDummy
, MaxLowerLevel
);
517 if (Result
!= NULL
) {
527 if (Context
.MasterElt
!= NULL
&& aID
== Context
.MasterElt
->GlobalId
) {
528 LowLevel
++; // already one level up (same as context)
529 return &Context
.MasterElt
->Create();
532 // check whether it's not part of an upper context
533 if (Context
.UpTable
!= NULL
) {
536 return CreateElementUsingContext(aID
, *Context
.UpTable
, LowLevel
, IsGlobalContext
, bAllowDummy
, MaxLowerLevel
);
539 if (!IsGlobalContext
&& bAllowDummy
) {
541 Result
= new EbmlDummy(aID
);
548 \todo verify that the size written is the same as the data written
550 uint32
EbmlElement::Render(IOCallback
& output
, bool bKeepIntact
, bool bKeepPosition
, bool bForceRender
)
552 assert(bValueIsSet
|| (bKeepIntact
&& DefaultISset())); // an element is been rendered without a value set !!!
553 // it may be a mandatory element without a default value
555 if (!bKeepIntact
&& IsDefaultValue()) {
558 #if defined(_DEBUG) || defined(DEBUG)
559 uint64 SupposedSize
= UpdateSize(bKeepIntact
, bForceRender
);
561 uint32 result
= RenderHead(output
, bForceRender
, bKeepIntact
, bKeepPosition
);
562 uint64 WrittenSize
= RenderData(output
, bForceRender
, bKeepIntact
);
563 #if defined(_DEBUG) || defined(DEBUG)
564 if (SupposedSize
!= (0-1)) assert(WrittenSize
== SupposedSize
);
566 result
+= WrittenSize
;
568 } catch (std::exception
& ex
) {
569 // const char * What = ex.what();
570 assert(false); // we should never be here !
576 \todo store the position of the Size writing for elements with unknown size
577 \todo handle exceptions on errors
578 \todo handle CodeSize bigger than 5 bytes
580 uint32
EbmlElement::RenderHead(IOCallback
& output
, bool bForceRender
, bool bKeepIntact
, bool bKeepPosition
)
582 if (EbmlId(*this).Length
<= 0 || EbmlId(*this).Length
> 4)
585 UpdateSize(bKeepIntact
, bForceRender
);
587 return MakeRenderHead(output
, bKeepPosition
);
590 uint32
EbmlElement::MakeRenderHead(IOCallback
& output
, bool bKeepPosition
)
592 binary FinalHead
[4+8]; // Class D + 64 bits coded size
593 unsigned int FinalHeadSize
;
595 FinalHeadSize
= EbmlId(*this).Length
;
596 EbmlId(*this).Fill(FinalHead
);
598 int CodedSize
= CodedSizeLength(Size
, SizeLength
, bSizeIsFinite
);
599 CodedValueLength(Size
, CodedSize
, &FinalHead
[FinalHeadSize
]);
600 FinalHeadSize
+= CodedSize
;
602 output
.writeFully(FinalHead
, FinalHeadSize
);
603 if (!bKeepPosition
) {
604 ElementPosition
= output
.getFilePointer() - FinalHeadSize
;
605 SizePosition
= ElementPosition
+ EbmlId(*this).Length
;
608 return FinalHeadSize
;
611 uint64
EbmlElement::ElementSize(bool bKeepIntact
) const
613 if (!bKeepIntact
&& IsDefaultValue())
614 return 0; // won't be saved
615 return Size
+ EbmlId(*this).Length
+ CodedSizeLength(Size
, SizeLength
, bSizeIsFinite
);
618 bool EbmlElement::CompareElements(const EbmlElement
*A
, const EbmlElement
*B
)
620 if (EbmlId(*A
) == EbmlId(*B
))
626 void EbmlElement::Read(EbmlStream
& inDataStream
, const EbmlSemanticContext
& Context
, int & UpperEltFound
, EbmlElement
* & FoundElt
, bool AllowDummyElt
, ScopeMode ReadFully
)
628 ReadData(inDataStream
.I_O(), ReadFully
);
631 bool EbmlElement::ForceSize(uint64 NewSize
)
637 int OldSizeLen
= CodedSizeLength(Size
, SizeLength
, bSizeIsFinite
);
638 uint64 OldSize
= Size
;
642 if (CodedSizeLength(Size
, SizeLength
, bSizeIsFinite
) == OldSizeLen
) {
643 bSizeIsFinite
= true;
651 uint32
EbmlElement::OverwriteHead(IOCallback
& output
, bool bKeepPosition
)
653 if (ElementPosition
== 0) {
654 return 0; // the element has not been written
657 uint64 CurrentPosition
= output
.getFilePointer();
658 output
.setFilePointer(GetElementPosition());
659 uint32 Result
= MakeRenderHead(output
, bKeepPosition
);
660 output
.setFilePointer(CurrentPosition
);
664 uint32
EbmlElement::VoidMe(IOCallback
& output
, bool bKeepIntact
)
666 if (ElementPosition
== 0) {
667 return 0; // the element has not been written
671 return Dummy
.Overwrite(*this, output
, bKeepIntact
);
674 END_LIBEBML_NAMESPACE