/src/vlc/contrib/contrib-build/ebml/src/EbmlElement.cpp
Line | Count | Source |
1 | | /**************************************************************************** |
2 | | ** libebml : parse EBML files, see http://embl.sourceforge.net/ |
3 | | ** |
4 | | ** <file/class description> |
5 | | ** |
6 | | ** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved. |
7 | | ** |
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. |
12 | | ** |
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. |
17 | | ** |
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | ** |
22 | | ** See http://www.gnu.org/licenses/lgpl-2.1.html for LGPL licensing information. |
23 | | ** |
24 | | ** Contact license@matroska.org if any conditions of this licensing are |
25 | | ** not clear to you. |
26 | | ** |
27 | | **********************************************************************/ |
28 | | |
29 | | /*! |
30 | | \file |
31 | | \version \$Id$ |
32 | | \author Steve Lhomme <robux4 @ users.sf.net> |
33 | | */ |
34 | | |
35 | | #include <cassert> |
36 | | #include <cstdlib> |
37 | | #include <cstring> |
38 | | #include <iostream> |
39 | | #include <new> |
40 | | #include <stdexcept> |
41 | | #include <sstream> |
42 | | |
43 | | #include "ebml/EbmlElement.h" |
44 | | #include "ebml/EbmlMaster.h" |
45 | | #include "ebml/EbmlStream.h" |
46 | | #include "ebml/EbmlVoid.h" |
47 | | #include "ebml/EbmlDummy.h" |
48 | | #include "ebml/EbmlContexts.h" |
49 | | |
50 | | namespace libebml { |
51 | | |
52 | | /*! |
53 | | \todo handle more than CodedSize of 5 |
54 | | */ |
55 | | int CodedSizeLength(uint64 Length, unsigned int SizeLength, bool bSizeIsFinite) |
56 | 2.82M | { |
57 | 2.82M | unsigned int CodedSize; |
58 | 2.82M | if (bSizeIsFinite) { |
59 | | // prepare the head of the size (000...01xxxxxx) |
60 | | // optimal size |
61 | 2.82M | if (Length < 127) // 2^7 - 1 |
62 | 1.54M | CodedSize = 1; |
63 | 1.28M | else if (Length < 16383) // 2^14 - 1 |
64 | 897k | CodedSize = 2; |
65 | 385k | else if (Length < 2097151L) // 2^21 - 1 |
66 | 284k | CodedSize = 3; |
67 | 101k | else if (Length < 268435455L) // 2^28 - 1 |
68 | 93.9k | CodedSize = 4; |
69 | 7.56k | else CodedSize = 5; |
70 | 2.82M | } else { |
71 | 603 | if (Length <= 127) // 2^7 - 1 |
72 | 597 | CodedSize = 1; |
73 | 6 | else if (Length <= 16383) // 2^14 - 1 |
74 | 0 | CodedSize = 2; |
75 | 6 | else if (Length <= 2097151L) // 2^21 - 1 |
76 | 0 | CodedSize = 3; |
77 | 6 | else if (Length <= 268435455L) // 2^28 - 1 |
78 | 0 | CodedSize = 4; |
79 | 6 | else CodedSize = 5; |
80 | 603 | } |
81 | | |
82 | 2.82M | if (SizeLength > 0 && CodedSize < SizeLength) { |
83 | | // defined size |
84 | 496k | CodedSize = SizeLength; |
85 | 496k | } |
86 | | |
87 | 2.82M | return CodedSize; |
88 | 2.82M | } |
89 | | |
90 | | /*! |
91 | | \todo handle more than CodedSize of 5 |
92 | | */ |
93 | | int CodedSizeLengthSigned(int64 Length, unsigned int SizeLength) |
94 | 0 | { |
95 | 0 | unsigned int CodedSize; |
96 | | // prepare the head of the size (000...01xxxxxx) |
97 | | // optimal size |
98 | 0 | if (Length > -64 && Length < 64) // 2^6 |
99 | 0 | CodedSize = 1; |
100 | 0 | else if (Length > -8192 && Length < 8192) // 2^13 |
101 | 0 | CodedSize = 2; |
102 | 0 | else if (Length > -1048576L && Length < 1048576L) // 2^20 |
103 | 0 | CodedSize = 3; |
104 | 0 | else if (Length > -134217728L && Length < 134217728L) // 2^27 |
105 | 0 | CodedSize = 4; |
106 | 0 | else CodedSize = 5; |
107 | |
|
108 | 0 | if (SizeLength > 0 && CodedSize < SizeLength) { |
109 | | // defined size |
110 | 0 | CodedSize = SizeLength; |
111 | 0 | } |
112 | |
|
113 | 0 | return CodedSize; |
114 | 0 | } |
115 | | |
116 | | int CodedValueLength(uint64 Length, int CodedSize, binary * OutBuffer) |
117 | 0 | { |
118 | 0 | int _SizeMask = 0xFF; |
119 | 0 | OutBuffer[0] = 1 << (8 - CodedSize); |
120 | 0 | for (int i=1; i<CodedSize; i++) { |
121 | 0 | OutBuffer[CodedSize-i] = Length & 0xFF; |
122 | 0 | Length >>= 8; |
123 | 0 | _SizeMask >>= 1; |
124 | 0 | } |
125 | | // first one use a OR with the "EBML size head" |
126 | 0 | OutBuffer[0] |= Length & 0xFF & _SizeMask; |
127 | 0 | return CodedSize; |
128 | 0 | } |
129 | | |
130 | | int CodedValueLengthSigned(int64 Length, int CodedSize, binary * OutBuffer) |
131 | 0 | { |
132 | 0 | if (Length > -64 && Length < 64) // 2^6 |
133 | 0 | Length += 63; |
134 | 0 | else if (Length > -8192 && Length < 8192) // 2^13 |
135 | 0 | Length += 8191; |
136 | 0 | else if (Length > -1048576L && Length < 1048576L) // 2^20 |
137 | 0 | Length += 1048575L; |
138 | 0 | else if (Length > -134217728L && Length < 134217728L) // 2^27 |
139 | 0 | Length += 134217727L; |
140 | |
|
141 | 0 | return CodedValueLength(Length, CodedSize, OutBuffer); |
142 | 0 | } |
143 | | |
144 | | uint64 ReadCodedSizeValue(const binary * InBuffer, uint32 & BufferSize, uint64 & SizeUnknown) |
145 | 15.5M | { |
146 | 15.5M | binary SizeBitMask = 1 << 7; |
147 | 15.5M | uint64 Result = 0x7F; |
148 | 15.5M | unsigned int SizeIdx, PossibleSizeLength = 0; |
149 | 15.5M | binary PossibleSize[8]; |
150 | 15.5M | memset(PossibleSize, 0, 8); |
151 | | |
152 | 15.5M | SizeUnknown = 0x7F; // the last bit is discarded when computing the size |
153 | 41.4M | for (SizeIdx = 0; SizeIdx < BufferSize && SizeIdx < 8; SizeIdx++) { |
154 | 33.0M | if (InBuffer[0] & (SizeBitMask >> SizeIdx)) { |
155 | | // ID found |
156 | 7.13M | PossibleSizeLength = SizeIdx + 1; |
157 | 7.13M | SizeBitMask >>= SizeIdx; |
158 | | |
159 | | // Guard against invalid memory accesses with incomplete IDs. |
160 | 7.13M | if (PossibleSizeLength > BufferSize) |
161 | 0 | break; |
162 | | |
163 | 21.5M | for (SizeIdx = 0; SizeIdx < PossibleSizeLength; SizeIdx++) { |
164 | 14.4M | PossibleSize[SizeIdx] = InBuffer[SizeIdx]; |
165 | 14.4M | } |
166 | 14.4M | for (SizeIdx = 0; SizeIdx < PossibleSizeLength - 1; SizeIdx++) { |
167 | 7.26M | Result <<= 7; |
168 | 7.26M | Result |= 0xFF; |
169 | 7.26M | } |
170 | | |
171 | 7.13M | Result = 0; |
172 | 7.13M | Result |= PossibleSize[0] & ~SizeBitMask; |
173 | 14.4M | for (unsigned int i = 1; i<PossibleSizeLength; i++) { |
174 | 7.26M | Result <<= 8; |
175 | 7.26M | Result |= PossibleSize[i]; |
176 | 7.26M | } |
177 | | |
178 | 7.13M | BufferSize = PossibleSizeLength; |
179 | | |
180 | 7.13M | return Result; |
181 | 7.13M | } |
182 | 25.8M | SizeUnknown <<= 7; |
183 | 25.8M | SizeUnknown |= 0xFF; |
184 | 25.8M | } |
185 | | |
186 | 8.41M | BufferSize = 0; |
187 | 8.41M | return 0; |
188 | 15.5M | } |
189 | | |
190 | | int64 ReadCodedSizeSignedValue(const binary * InBuffer, uint32 & BufferSize, uint64 & SizeUnknown) |
191 | 29.7k | { |
192 | 29.7k | int64 Result = ReadCodedSizeValue(InBuffer, BufferSize, SizeUnknown); |
193 | | |
194 | 29.7k | if (BufferSize != 0) { |
195 | 29.5k | switch (BufferSize) { |
196 | 27.2k | case 1: |
197 | 27.2k | Result -= 63; |
198 | 27.2k | break; |
199 | 2.25k | case 2: |
200 | 2.25k | Result -= 8191; |
201 | 2.25k | break; |
202 | 17 | case 3: |
203 | 17 | Result -= 1048575L; |
204 | 17 | break; |
205 | 0 | case 4: |
206 | 0 | Result -= 134217727L; |
207 | 0 | break; |
208 | 29.5k | } |
209 | 29.5k | } |
210 | | |
211 | 29.7k | return Result; |
212 | 29.7k | } |
213 | | |
214 | | |
215 | | EbmlCallbacks::EbmlCallbacks(EbmlElement & (*Creator)(), const EbmlId & aGlobalId, const char * aDebugName, const EbmlSemanticContext & aContext) |
216 | 23.2k | :Create(Creator) |
217 | 23.2k | ,GlobalId(aGlobalId) |
218 | 23.2k | ,DebugName(aDebugName) |
219 | 23.2k | ,Context(aContext) |
220 | 23.2k | { |
221 | 23.2k | assert((Create!=nullptr) || !strcmp(aDebugName, "DummyElement")); |
222 | 23.2k | } |
223 | | |
224 | | const EbmlSemantic & EbmlSemanticContext::GetSemantic(size_t i) const |
225 | 0 | { |
226 | 0 | assert(i<Size); |
227 | 0 | if (i<Size) |
228 | 0 | return MyTable[i]; |
229 | | |
230 | 0 | std::stringstream ss; |
231 | 0 | ss << "EbmlSemanticContext::GetSemantic: programming error: index i outside of table size (" << i << " >= " << Size << ")"; |
232 | 0 | throw std::logic_error(ss.str()); |
233 | 0 | } |
234 | | |
235 | | |
236 | | EbmlElement::EbmlElement(uint64 aDefaultSize, bool bValueSet) |
237 | 1.87M | :DefaultSize(aDefaultSize) |
238 | 1.87M | ,SizeLength(0) ///< write optimal size by default |
239 | 1.87M | ,bSizeIsFinite(true) |
240 | 1.87M | ,ElementPosition(0) |
241 | 1.87M | ,SizePosition(0) |
242 | 1.87M | ,bValueIsSet(bValueSet) |
243 | 1.87M | ,DefaultIsSet(false) |
244 | 1.87M | ,bLocked(false) |
245 | 1.87M | { |
246 | 1.87M | Size = DefaultSize; |
247 | 1.87M | } |
248 | | |
249 | | EbmlElement::~EbmlElement() |
250 | 1.87M | { |
251 | 1.87M | assert(!bLocked); |
252 | 1.87M | } |
253 | | |
254 | | /*! |
255 | | \todo this method is deprecated and should be called FindThisID |
256 | | \todo replace the new RawElement with the appropriate class (when known) |
257 | | */ |
258 | | EbmlElement * EbmlElement::FindNextID(IOCallback & DataStream, const EbmlCallbacks & ClassInfos, uint64 MaxDataSize) |
259 | 8.92k | { |
260 | 8.92k | binary PossibleId[4]; |
261 | 8.92k | int PossibleID_Length = 0; |
262 | 8.92k | binary PossibleSize[8]; // we don't support size stored in more than 64 bits |
263 | 8.92k | uint32 PossibleSizeLength = 0; |
264 | 8.92k | uint64 SizeUnknown = 0; |
265 | 8.92k | uint64 SizeFound = 0; |
266 | 8.92k | bool bElementFound = false; |
267 | | |
268 | 8.92k | binary BitMask; |
269 | 8.92k | uint64 aElementPosition = 0, aSizePosition = 0; |
270 | 15.0k | while (!bElementFound) { |
271 | | // read ID |
272 | 8.92k | aElementPosition = DataStream.getFilePointer(); |
273 | 8.92k | uint32 ReadSize = 0; |
274 | 8.92k | BitMask = 1 << 7; |
275 | 27.2k | while (PossibleID_Length < 4) { |
276 | 27.0k | if (!DataStream.read(&PossibleId[PossibleID_Length], 1)) |
277 | 2.61k | return nullptr; // no more data |
278 | | |
279 | 24.4k | ++ReadSize; |
280 | 24.4k | ++PossibleID_Length; |
281 | | |
282 | 24.4k | if (PossibleId[0] & BitMask) { |
283 | | // this is the last octet of the ID |
284 | | // check wether that's the one we're looking for |
285 | | /* if (PossibleID == EBML_INFO_ID(ClassInfos)) { |
286 | | break; |
287 | | } else { |
288 | | /// \todo This element should be skipped (use a context ?) |
289 | | }*/ |
290 | 6.10k | bElementFound = true; /// \todo not exactly the one we're looking for |
291 | 6.10k | break; |
292 | 6.10k | } |
293 | 18.3k | BitMask >>= 1; |
294 | 18.3k | } |
295 | | |
296 | 6.31k | if (!bElementFound) |
297 | 211 | return nullptr; |
298 | | |
299 | | // read the data size |
300 | 6.10k | aSizePosition = DataStream.getFilePointer(); |
301 | 6.10k | uint32 _SizeLength; |
302 | 21.6k | do { |
303 | 21.6k | if (PossibleSizeLength >= 8) |
304 | | // Size is larger than 8 bytes |
305 | 6 | return nullptr; |
306 | | |
307 | 21.6k | ReadSize += DataStream.read(&PossibleSize[PossibleSizeLength++], 1); |
308 | 21.6k | _SizeLength = PossibleSizeLength; |
309 | 21.6k | SizeFound = ReadCodedSizeValue(&PossibleSize[0], _SizeLength, SizeUnknown); |
310 | 21.6k | } while (_SizeLength == 0); |
311 | 6.10k | } |
312 | | |
313 | 6.09k | EbmlElement *Result = nullptr; |
314 | 6.09k | EbmlId PossibleID(PossibleId, PossibleID_Length); |
315 | 6.09k | if (PossibleID == EBML_INFO_ID(ClassInfos)) { |
316 | | // the element is the one expected |
317 | 5.70k | Result = &EBML_INFO_CREATE(ClassInfos); |
318 | 5.70k | } else { |
319 | | /// \todo find the element in the context |
320 | 392 | Result = new (std::nothrow) EbmlDummy(PossibleID); |
321 | 392 | if(Result == nullptr) |
322 | 0 | return nullptr; |
323 | 392 | } |
324 | | |
325 | 6.09k | Result->SetSizeLength(PossibleSizeLength); |
326 | | |
327 | 6.09k | Result->Size = SizeFound; |
328 | | |
329 | 6.09k | if (!Result->ValidateSize() || (SizeFound != SizeUnknown && MaxDataSize < Result->Size)) { |
330 | 45 | delete Result; |
331 | 45 | return nullptr; |
332 | 45 | } |
333 | | |
334 | | // check if the size is not all 1s |
335 | 6.05k | if (SizeFound == SizeUnknown) { |
336 | | // Size of this element is unknown |
337 | | // only possible for Master elements |
338 | 582 | if (!Result->SetSizeInfinite()) { |
339 | | /// \todo the element is not allowed to be infinite |
340 | 3 | delete Result; |
341 | 3 | return nullptr; |
342 | 3 | } |
343 | 5.47k | } else Result->SetSizeInfinite(false); |
344 | 6.05k | Result->ElementPosition = aElementPosition; |
345 | 6.05k | Result->SizePosition = aSizePosition; |
346 | | |
347 | 6.05k | return Result; |
348 | 6.05k | } |
349 | | |
350 | | |
351 | | /*! |
352 | | \todo replace the new RawElement with the appropriate class (when known) |
353 | | \todo skip data for Dummy elements when they are not allowed |
354 | | \todo better check of the size checking for upper elements (using a list of size for each level) |
355 | | \param LowLevel Will be returned with the level of the element found compared to the context given |
356 | | */ |
357 | | EbmlElement * EbmlElement::FindNextElement(IOCallback & DataStream, const EbmlSemanticContext & Context, int & UpperLevel, |
358 | | uint64 MaxDataSize, bool AllowDummyElt, unsigned int MaxLowerLevel) |
359 | 651k | { |
360 | 651k | int PossibleID_Length = 0; |
361 | 651k | binary PossibleIdNSize[16]; |
362 | 651k | int PossibleSizeLength; |
363 | 651k | uint64 SizeUnknown; |
364 | 651k | int ReadIndex = 0; // trick for the algo, start index at 0 |
365 | 651k | uint32 ReadSize = 0, IdStart = 0; |
366 | 651k | uint64 SizeFound; |
367 | 651k | int SizeIdx; |
368 | 651k | bool bFound; |
369 | 651k | int UpperLevel_original = UpperLevel; |
370 | 651k | uint64 ParseStart = DataStream.getFilePointer(); |
371 | | |
372 | 7.53M | do { |
373 | | // read a potential ID |
374 | 15.4M | do { |
375 | 15.4M | assert(ReadIndex < 16); |
376 | | // build the ID with the current Read Buffer |
377 | 15.4M | bFound = false; |
378 | 15.4M | binary IdBitMask = 1 << 7; |
379 | 49.1M | for (SizeIdx = 0; SizeIdx < ReadIndex && SizeIdx < 4; SizeIdx++) { |
380 | 41.2M | if (PossibleIdNSize[0] & (IdBitMask >> SizeIdx)) { |
381 | | // ID found |
382 | 7.51M | PossibleID_Length = SizeIdx + 1; |
383 | 7.51M | IdBitMask >>= SizeIdx; |
384 | 7.51M | bFound = true; |
385 | 7.51M | break; |
386 | 7.51M | } |
387 | 41.2M | } |
388 | 15.4M | if (bFound) { |
389 | 7.51M | break; |
390 | 7.51M | } |
391 | | |
392 | 7.94M | if (ReadIndex >= 4) { |
393 | | // ID not found |
394 | | // shift left the read octets |
395 | 6.69M | memmove(&PossibleIdNSize[0],&PossibleIdNSize[1], --ReadIndex); |
396 | 6.69M | IdStart++; |
397 | 6.69M | } |
398 | | |
399 | 7.94M | if (MaxDataSize <= ReadSize) |
400 | 19.4k | break; |
401 | 7.92M | if (DataStream.read(&PossibleIdNSize[ReadIndex++], 1) == 0) { |
402 | 4.82k | return nullptr; // no more data ? |
403 | 4.82k | } |
404 | 7.92M | ReadSize++; |
405 | | |
406 | 7.92M | } while (!bFound); |
407 | | |
408 | 7.53M | if (!bFound) |
409 | | // we reached the maximum we could read without a proper ID |
410 | 19.4k | return nullptr; |
411 | | |
412 | 7.51M | SizeIdx = ReadIndex; |
413 | 7.51M | ReadIndex -= PossibleID_Length; |
414 | | |
415 | | // read the data size |
416 | 7.51M | uint32 _SizeLength; |
417 | 7.51M | PossibleSizeLength = ReadIndex; |
418 | 15.4M | while (true) { |
419 | 15.4M | _SizeLength = PossibleSizeLength; |
420 | 15.4M | SizeFound = ReadCodedSizeValue(&PossibleIdNSize[PossibleID_Length], _SizeLength, SizeUnknown); |
421 | 15.4M | if (_SizeLength != 0) { |
422 | 7.09M | bFound = true; |
423 | 7.09M | break; |
424 | 7.09M | } |
425 | 8.40M | if (PossibleSizeLength >= 8) { |
426 | 395k | bFound = false; |
427 | 395k | break; |
428 | 395k | } |
429 | 8.00M | if (MaxDataSize <= ReadSize) { |
430 | 21.9k | bFound = false; |
431 | 21.9k | break; |
432 | 21.9k | } |
433 | 7.98M | if( DataStream.read( &PossibleIdNSize[SizeIdx++], 1 ) == 0 ) { |
434 | 77 | return nullptr; // no more data ? |
435 | 77 | } |
436 | 7.98M | ReadSize++; |
437 | 7.98M | PossibleSizeLength++; |
438 | 7.98M | } |
439 | | |
440 | 7.51M | if (bFound) { |
441 | | // find the element in the context and use the correct creator |
442 | 7.09M | EbmlId PossibleID(PossibleIdNSize, PossibleID_Length); |
443 | 7.09M | EbmlElement * Result = CreateElementUsingContext(PossibleID, Context, UpperLevel, false, AllowDummyElt, MaxLowerLevel); |
444 | | ///< \todo continue is misplaced |
445 | 7.09M | if (Result != nullptr) { |
446 | 1.41M | if (AllowDummyElt || !Result->IsDummy()) { |
447 | 1.41M | Result->SetSizeLength(_SizeLength); |
448 | | |
449 | 1.41M | Result->Size = SizeFound; |
450 | | // UpperLevel values |
451 | | // -1 : global element |
452 | | // 0 : child |
453 | | // 1 : same level |
454 | | // + : further parent |
455 | 1.41M | if (Result->ValidateSize() && (SizeFound == SizeUnknown || UpperLevel > 0 || MaxDataSize == 0 || |
456 | 1.19M | MaxDataSize >= (IdStart + PossibleID_Length + _SizeLength + SizeFound))) { |
457 | 743k | if (SizeFound != SizeUnknown || Result->SetSizeInfinite()) { |
458 | 626k | Result->ElementPosition = ParseStart + IdStart; |
459 | 626k | Result->SizePosition = Result->ElementPosition + PossibleID_Length; |
460 | | // place the file at the beggining of the data |
461 | 626k | DataStream.setFilePointer(Result->SizePosition + _SizeLength); |
462 | 626k | return Result; |
463 | 626k | } |
464 | 743k | } |
465 | 1.41M | } |
466 | 787k | delete Result; |
467 | 787k | } |
468 | 7.09M | } |
469 | | |
470 | | // recover all the data in the buffer minus one byte |
471 | 6.88M | ReadIndex = SizeIdx - 1; |
472 | 6.88M | memmove(&PossibleIdNSize[0], &PossibleIdNSize[1], ReadIndex); |
473 | 6.88M | IdStart++; |
474 | 6.88M | UpperLevel = UpperLevel_original; |
475 | 6.88M | } while ( MaxDataSize >= ReadSize ); |
476 | | |
477 | 0 | return nullptr; |
478 | 651k | } |
479 | | |
480 | | /*! |
481 | | \todo what happens if we are in a upper element with a known size ? |
482 | | */ |
483 | | EbmlElement * EbmlElement::SkipData(EbmlStream & DataStream, const EbmlSemanticContext & Context, EbmlElement * TestReadElt, bool AllowDummyElt) |
484 | 534k | { |
485 | 534k | EbmlElement * Result = nullptr; |
486 | 534k | if (bSizeIsFinite) { |
487 | 534k | assert(TestReadElt == nullptr); |
488 | 534k | assert(ElementPosition < SizePosition); |
489 | 534k | DataStream.I_O().setFilePointer(SizePosition + CodedSizeLength(Size, SizeLength, bSizeIsFinite) + Size, seek_beginning); |
490 | | // DataStream.I_O().setFilePointer(Size, seek_current); |
491 | 534k | } else { |
492 | | ///////////////////////////////////////////////// |
493 | | // read elements until an upper element is found |
494 | | ///////////////////////////////////////////////// |
495 | 0 | bool bEndFound = false; |
496 | 0 | while (!bEndFound && Result == nullptr) { |
497 | | // read an element |
498 | | /// \todo 0xFF... and true should be configurable |
499 | | // EbmlElement * NewElt; |
500 | 0 | if (TestReadElt == nullptr) { |
501 | 0 | int bUpperElement = 0; // trick to call FindNextID correctly |
502 | 0 | Result = DataStream.FindNextElement(Context, bUpperElement, 0xFFFFFFFFL, AllowDummyElt); |
503 | 0 | } else { |
504 | 0 | Result = TestReadElt; |
505 | 0 | TestReadElt = nullptr; |
506 | 0 | } |
507 | |
|
508 | 0 | if (Result != nullptr) { |
509 | 0 | unsigned int EltIndex; |
510 | | // data known in this Master's context |
511 | 0 | for (EltIndex = 0; EltIndex < EBML_CTX_SIZE(Context); EltIndex++) { |
512 | 0 | if (EbmlId(*Result) == EBML_CTX_IDX_ID(Context,EltIndex)) { |
513 | | // skip the data with its own context |
514 | 0 | Result = Result->SkipData(DataStream, EBML_SEM_CONTEXT(EBML_CTX_IDX(Context,EltIndex)), nullptr); |
515 | 0 | break; // let's go to the next ID |
516 | 0 | } |
517 | 0 | } |
518 | |
|
519 | 0 | if (EltIndex >= EBML_CTX_SIZE(Context)) { |
520 | 0 | if (EBML_CTX_PARENT(Context) != nullptr) { |
521 | 0 | Result = SkipData(DataStream, *EBML_CTX_PARENT(Context), Result); |
522 | 0 | } else { |
523 | 0 | assert(Context.GetGlobalContext != nullptr); |
524 | 0 | if (Context != Context.GetGlobalContext()) { |
525 | 0 | Result = SkipData(DataStream, Context.GetGlobalContext(), Result); |
526 | 0 | } else { |
527 | 0 | bEndFound = true; |
528 | 0 | } |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } else { |
532 | 0 | bEndFound = true; |
533 | 0 | } |
534 | 0 | } |
535 | 0 | } |
536 | 534k | return Result; |
537 | 534k | } |
538 | | |
539 | | EbmlElement *EbmlElement::CreateElementUsingContext(const EbmlId & aID, const EbmlSemanticContext & Context, |
540 | | int & LowLevel, bool IsGlobalContext, bool bAllowDummy, unsigned int MaxLowerLevel) |
541 | 15.9M | { |
542 | 15.9M | unsigned int ContextIndex; |
543 | 15.9M | EbmlElement *Result = nullptr; |
544 | | |
545 | | // elements at the current level |
546 | 95.4M | for (ContextIndex = 0; ContextIndex < EBML_CTX_SIZE(Context); ContextIndex++) { |
547 | 79.9M | if (aID == EBML_CTX_IDX_ID(Context,ContextIndex)) { |
548 | 397k | return &EBML_SEM_CREATE(EBML_CTX_IDX(Context,ContextIndex)); |
549 | 397k | } |
550 | 79.9M | } |
551 | | |
552 | | // global elements |
553 | 15.9M | assert(Context.GetGlobalContext != nullptr); // global should always exist, at least the EBML ones |
554 | 15.5M | const EbmlSemanticContext & tstContext = Context.GetGlobalContext(); |
555 | 15.5M | if (tstContext != Context) { |
556 | 7.81M | LowLevel--; |
557 | 7.81M | MaxLowerLevel--; |
558 | | // recursive is good, but be carefull... |
559 | 7.81M | Result = CreateElementUsingContext(aID, tstContext, LowLevel, true, bAllowDummy, MaxLowerLevel); |
560 | 7.81M | if (Result != nullptr) { |
561 | 24.7k | return Result; |
562 | 24.7k | } |
563 | 7.78M | LowLevel++; |
564 | 7.78M | MaxLowerLevel++; |
565 | 7.78M | } else { |
566 | 7.78M | return nullptr; |
567 | 7.78M | } |
568 | | |
569 | | // parent elements |
570 | 7.78M | if (EBML_CTX_MASTER(Context) != nullptr && aID == EBML_INFO_ID(*EBML_CTX_MASTER(Context))) { |
571 | 50.6k | LowLevel++; // already one level up (same as context) |
572 | 50.6k | return &EBML_INFO_CREATE(*EBML_CTX_MASTER(Context)); |
573 | 50.6k | } |
574 | | |
575 | | // check wether it's not part of an upper context |
576 | 7.73M | if (EBML_CTX_PARENT(Context) != nullptr) { |
577 | 1.09M | LowLevel++; |
578 | 1.09M | MaxLowerLevel++; |
579 | 1.09M | return CreateElementUsingContext(aID, *EBML_CTX_PARENT(Context), LowLevel, IsGlobalContext, bAllowDummy, MaxLowerLevel); |
580 | 1.09M | } |
581 | | |
582 | 6.64M | if (!IsGlobalContext && bAllowDummy) { |
583 | 965k | LowLevel = 0; |
584 | 965k | Result = new (std::nothrow) EbmlDummy(aID); |
585 | 965k | } |
586 | | |
587 | 6.64M | return Result; |
588 | 7.73M | } |
589 | | |
590 | | /*! |
591 | | \todo verify that the size written is the same as the data written |
592 | | */ |
593 | | filepos_t EbmlElement::Render(IOCallback & output, bool bWithDefault, bool bKeepPosition, bool bForceRender) |
594 | 0 | { |
595 | 0 | assert(bValueIsSet || (bWithDefault && DefaultISset())); // an element is been rendered without a value set !!! |
596 | | // it may be a mandatory element without a default value |
597 | 0 | if (!bWithDefault && IsDefaultValue()) { |
598 | 0 | return 0; |
599 | 0 | } |
600 | | #if defined(LIBEBML_DEBUG) |
601 | | uint64 SupposedSize = UpdateSize(bWithDefault, bForceRender); |
602 | | #endif // LIBEBML_DEBUG |
603 | 0 | filepos_t result = RenderHead(output, bForceRender, bWithDefault, bKeepPosition); |
604 | 0 | uint64 WrittenSize = RenderData(output, bForceRender, bWithDefault); |
605 | | #if defined(LIBEBML_DEBUG) |
606 | | if (static_cast<int64>(SupposedSize) != (0-1)) |
607 | | assert(WrittenSize == SupposedSize); |
608 | | #endif // LIBEBML_DEBUG |
609 | 0 | result += WrittenSize; |
610 | 0 | return result; |
611 | 0 | } |
612 | | |
613 | | /*! |
614 | | \todo store the position of the Size writing for elements with unknown size |
615 | | \todo handle exceptions on errors |
616 | | \todo handle CodeSize bigger than 5 bytes |
617 | | */ |
618 | | filepos_t EbmlElement::RenderHead(IOCallback & output, bool bForceRender, bool bWithDefault, bool bKeepPosition) |
619 | 0 | { |
620 | 0 | if (EBML_ID_LENGTH((const EbmlId&)*this) <= 0 || EBML_ID_LENGTH((const EbmlId&)*this) > 4) |
621 | 0 | return 0; |
622 | | |
623 | 0 | UpdateSize(bWithDefault, bForceRender); |
624 | |
|
625 | 0 | return MakeRenderHead(output, bKeepPosition); |
626 | 0 | } |
627 | | |
628 | | filepos_t EbmlElement::MakeRenderHead(IOCallback & output, bool bKeepPosition) |
629 | 0 | { |
630 | 0 | binary FinalHead[4+8]; // Class D + 64 bits coded size |
631 | 0 | unsigned int FinalHeadSize; |
632 | |
|
633 | 0 | FinalHeadSize = EBML_ID_LENGTH((const EbmlId&)*this); |
634 | 0 | EbmlId(*this).Fill(FinalHead); |
635 | |
|
636 | 0 | int CodedSize = CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
637 | 0 | CodedValueLength(Size, CodedSize, &FinalHead[FinalHeadSize]); |
638 | 0 | FinalHeadSize += CodedSize; |
639 | |
|
640 | 0 | output.writeFully(FinalHead, FinalHeadSize); |
641 | 0 | if (!bKeepPosition) { |
642 | 0 | ElementPosition = output.getFilePointer() - FinalHeadSize; |
643 | 0 | SizePosition = ElementPosition + EBML_ID_LENGTH((const EbmlId&)*this); |
644 | 0 | } |
645 | |
|
646 | 0 | return FinalHeadSize; |
647 | 0 | } |
648 | | |
649 | | uint64 EbmlElement::ElementSize(bool bWithDefault) const |
650 | 0 | { |
651 | 0 | if (!bWithDefault && IsDefaultValue()) |
652 | 0 | return 0; // won't be saved |
653 | 0 | return Size + EBML_ID_LENGTH((const EbmlId&)*this) + CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
654 | 0 | } |
655 | | |
656 | | bool EbmlElement::IsSmallerThan(const EbmlElement *Cmp) const |
657 | 0 | { |
658 | 0 | return EbmlId(*this) == EbmlId(*Cmp); |
659 | 0 | } |
660 | | |
661 | | bool EbmlElement::CompareElements(const EbmlElement *A, const EbmlElement *B) |
662 | 0 | { |
663 | 0 | if (EbmlId(*A) == EbmlId(*B)) |
664 | 0 | return A->IsSmallerThan(B); |
665 | | |
666 | 0 | return false; |
667 | 0 | } |
668 | | |
669 | | void EbmlElement::Read(EbmlStream & inDataStream, const EbmlSemanticContext & /* Context */, int & /* UpperEltFound */, EbmlElement * & /* FoundElt */, bool /* AllowDummyElt */, ScopeMode ReadFully) |
670 | 124k | { |
671 | 124k | ReadData(inDataStream.I_O(), ReadFully); |
672 | 124k | } |
673 | | |
674 | | bool EbmlElement::ForceSize(uint64 NewSize) |
675 | 0 | { |
676 | 0 | if (bSizeIsFinite) { |
677 | 0 | return false; |
678 | 0 | } |
679 | | |
680 | 0 | int OldSizeLen = CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
681 | 0 | uint64 OldSize = Size; |
682 | |
|
683 | 0 | Size = NewSize; |
684 | |
|
685 | 0 | if (CodedSizeLength(Size, SizeLength, bSizeIsFinite) == OldSizeLen) { |
686 | 0 | bSizeIsFinite = true; |
687 | 0 | return true; |
688 | 0 | } |
689 | 0 | Size = OldSize; |
690 | |
|
691 | 0 | return false; |
692 | 0 | } |
693 | | |
694 | | filepos_t EbmlElement::OverwriteHead(IOCallback & output, bool bKeepPosition) |
695 | 0 | { |
696 | 0 | if (ElementPosition == 0) { |
697 | 0 | return 0; // the element has not been written |
698 | 0 | } |
699 | | |
700 | 0 | uint64 CurrentPosition = output.getFilePointer(); |
701 | 0 | output.setFilePointer(GetElementPosition()); |
702 | 0 | filepos_t Result = MakeRenderHead(output, bKeepPosition); |
703 | 0 | output.setFilePointer(CurrentPosition); |
704 | 0 | return Result; |
705 | 0 | } |
706 | | |
707 | | filepos_t EbmlElement::OverwriteData(IOCallback & output, bool bKeepPosition) |
708 | 0 | { |
709 | 0 | if (ElementPosition == 0) { |
710 | 0 | return 0; // the element has not been written |
711 | 0 | } |
712 | | |
713 | 0 | auto HeaderSize = EbmlId(*this).GetLength() + CodedSizeLength(Size, SizeLength, bSizeIsFinite); |
714 | | #if !defined(NDEBUG) |
715 | | auto DataSize = GetSize(); |
716 | | #endif |
717 | |
|
718 | 0 | auto CurrentPosition = output.getFilePointer(); |
719 | 0 | output.setFilePointer(GetElementPosition() + HeaderSize); |
720 | 0 | auto Result = RenderData(output, true, bKeepPosition); |
721 | 0 | output.setFilePointer(CurrentPosition); |
722 | 0 | assert(Result == DataSize); |
723 | 0 | return Result; |
724 | 0 | } |
725 | | |
726 | | |
727 | | uint64 EbmlElement::VoidMe(IOCallback & output, bool bWithDefault) |
728 | 0 | { |
729 | 0 | if (ElementPosition == 0) { |
730 | 0 | return 0; // the element has not been written |
731 | 0 | } |
732 | | |
733 | 0 | EbmlVoid Dummy; |
734 | 0 | return Dummy.Overwrite(*this, output, true, bWithDefault); |
735 | 0 | } |
736 | | |
737 | | } // namespace libebml |