]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/info.c
Imported to git.
[icculus/xz.git] / src / liblzma / common / info.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       info.c
4 /// \brief      Collects and verifies integrity of Stream size information
5 //
6 //  Copyright (C) 2007 Lasse Collin
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 ///////////////////////////////////////////////////////////////////////////////
19
20 #include "common.h"
21
22
23 struct lzma_info_s {
24         struct {
25                 /// Known Size of Header Metadata Block; here's some
26                 /// special things:
27                 ///  - LZMA_VLI_VALUE_UNKNOWN indicates that we don't know
28                 ///    if Header Metadata Block is present.
29                 ///  - 0 indicates that Header Metadata Block is not present.
30                 lzma_vli header_metadata_size;
31
32                 /// Known Total Size of the Data Blocks in the Stream
33                 lzma_vli total_size;
34
35                 /// Known Uncompressed Size of the Data Blocks in the Stream
36                 lzma_vli uncompressed_size;
37
38                 /// Known Size of Footer Metadata Block
39                 lzma_vli footer_metadata_size;
40         } known;
41
42         struct {
43                 /// Sum of Total Size fields stored to the Index so far
44                 lzma_vli total_size;
45
46                 /// Sum of Uncompressed Size fields stored to the Index so far
47                 lzma_vli uncompressed_size;
48
49                 /// First Index Record in the list, or NULL if Index is empty.
50                 lzma_index *head;
51
52                 /// Number of Index Records
53                 size_t record_count;
54
55                 /// Number of Index Records
56                 size_t incomplete_count;
57
58                 /// True when we know that no more Records will get added
59                 /// to the Index.
60                 bool is_final;
61         } index;
62
63         /// Start offset of the Stream. This is needed to calculate
64         /// lzma_info_iter.stream_offset.
65         lzma_vli stream_start_offset;
66
67         /// True if Index is present in Header Metadata Block
68         bool has_index_in_header_metadata;
69 };
70
71
72 //////////////////////
73 // Create/Reset/End //
74 //////////////////////
75
76 static void
77 index_init(lzma_info *info)
78 {
79         info->index.total_size = 0;
80         info->index.uncompressed_size = 0;
81         info->index.head = NULL;
82         info->index.record_count = 0;
83         info->index.incomplete_count = 0;
84         info->index.is_final = false;
85         return;
86 }
87
88
89 static void
90 info_init(lzma_info *info)
91 {
92         info->known.header_metadata_size = LZMA_VLI_VALUE_UNKNOWN;
93         info->known.total_size = LZMA_VLI_VALUE_UNKNOWN;
94         info->known.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
95         info->known.footer_metadata_size = LZMA_VLI_VALUE_UNKNOWN;
96         info->stream_start_offset = 0;
97         info->has_index_in_header_metadata = false;
98
99         index_init(info);
100
101         return;
102 }
103
104
105 extern LZMA_API lzma_info *
106 lzma_info_init(lzma_info *info, lzma_allocator *allocator)
107 {
108         if (info == NULL)
109                 info = lzma_alloc(sizeof(lzma_info), allocator);
110         else
111                 lzma_index_free(info->index.head, allocator);
112
113         if (info != NULL)
114                 info_init(info);
115
116         return info;
117 }
118
119
120 extern LZMA_API void
121 lzma_info_free(lzma_info *info, lzma_allocator *allocator)
122 {
123         lzma_index_free(info->index.head, allocator);
124         lzma_free(info, allocator);
125         return;
126 }
127
128
129 /////////
130 // Set //
131 /////////
132
133 static lzma_ret
134 set_size(lzma_vli new_size, lzma_vli *known_size, lzma_vli index_size,
135                 bool forbid_zero)
136 {
137         assert(new_size <= LZMA_VLI_VALUE_MAX);
138
139         lzma_ret ret = LZMA_OK;
140
141         if (forbid_zero && new_size == 0)
142                 ret = LZMA_PROG_ERROR;
143         else if (index_size > new_size)
144                 ret = LZMA_DATA_ERROR;
145         else if (*known_size == LZMA_VLI_VALUE_UNKNOWN)
146                 *known_size = new_size;
147         else if (*known_size != new_size)
148                 ret = LZMA_DATA_ERROR;
149
150         return ret;
151 }
152
153
154 extern LZMA_API lzma_ret
155 lzma_info_size_set(lzma_info *info, lzma_info_size type, lzma_vli size)
156 {
157         if (size > LZMA_VLI_VALUE_MAX)
158                 return LZMA_PROG_ERROR;
159
160         switch (type) {
161         case LZMA_INFO_STREAM_START:
162                 info->stream_start_offset = size;
163                 return LZMA_OK;
164
165         case LZMA_INFO_HEADER_METADATA:
166                 return set_size(size, &info->known.header_metadata_size,
167                                 0, false);
168
169         case LZMA_INFO_TOTAL:
170                 return set_size(size, &info->known.total_size,
171                                 info->index.total_size, true);
172
173         case LZMA_INFO_UNCOMPRESSED:
174                 return set_size(size, &info->known.uncompressed_size,
175                                 info->index.uncompressed_size, false);
176
177         case LZMA_INFO_FOOTER_METADATA:
178                 return set_size(size, &info->known.footer_metadata_size,
179                                 0, true);
180         }
181
182         return LZMA_PROG_ERROR;
183 }
184
185
186 extern LZMA_API lzma_ret
187 lzma_info_index_set(lzma_info *info, lzma_allocator *allocator,
188                 lzma_index *i_new, lzma_bool eat_index)
189 {
190         if (i_new == NULL)
191                 return LZMA_PROG_ERROR;
192
193         lzma_index *i_old = info->index.head;
194
195         if (i_old != NULL) {
196                 while (true) {
197                         // If the new Index has fewer Records than the old one,
198                         // the new Index cannot be valid.
199                         if (i_new == NULL)
200                                 return LZMA_DATA_ERROR;
201
202                         // The new Index must be complete i.e. no unknown
203                         // values.
204                         if (i_new->total_size > LZMA_VLI_VALUE_MAX
205                                         || i_new->uncompressed_size
206                                                 > LZMA_VLI_VALUE_MAX) {
207                                 if (eat_index)
208                                         lzma_index_free(i_new, allocator);
209
210                                 return LZMA_PROG_ERROR;
211                         }
212
213                         // Compare the values from the new Index with the old
214                         // Index. The old Index may be incomplete; in that
215                         // case we
216                         //  - use the value from the new Index as is;
217                         //  - update the appropriate info->index.foo_size; and
218                         //  - decrease the count of incomplete Index Records.
219                         bool was_incomplete = false;
220
221                         if (i_old->total_size == LZMA_VLI_VALUE_UNKNOWN) {
222                                 assert(!info->index.is_final);
223                                 was_incomplete = true;
224
225                                 i_old->total_size = i_new->total_size;
226
227                                 if (lzma_vli_add(info->index.total_size,
228                                                 i_new->total_size)) {
229                                         if (eat_index)
230                                                 lzma_index_free(i_new,
231                                                                 allocator);
232
233                                         return LZMA_PROG_ERROR;
234                                 }
235                         } else if (i_old->total_size != i_new->total_size) {
236                                 if (eat_index)
237                                         lzma_index_free(i_new, allocator);
238
239                                 return LZMA_DATA_ERROR;
240                         }
241
242                         if (i_old->uncompressed_size
243                                         == LZMA_VLI_VALUE_UNKNOWN) {
244                                 assert(!info->index.is_final);
245                                 was_incomplete = true;
246
247                                 i_old->uncompressed_size
248                                                 = i_new->uncompressed_size;
249
250                                 if (lzma_vli_add(info->index.uncompressed_size,
251                                                 i_new->uncompressed_size)) {
252                                         if (eat_index)
253                                                 lzma_index_free(i_new,
254                                                                 allocator);
255
256                                         return LZMA_PROG_ERROR;
257                                 }
258                         } else if (i_old->uncompressed_size
259                                         != i_new->uncompressed_size) {
260                                 if (eat_index)
261                                         lzma_index_free(i_new, allocator);
262
263                                 return LZMA_DATA_ERROR;
264                         }
265
266                         if (was_incomplete) {
267                                 assert(!info->index.is_final);
268                                 assert(info->index.incomplete_count > 0);
269                                 --info->index.incomplete_count;
270                         }
271
272                         // Get rid of *i_new. It's now identical with *i_old.
273                         lzma_index *tmp = i_new->next;
274                         if (eat_index)
275                                 lzma_free(i_new, allocator);
276
277                         i_new = tmp;
278
279                         // We want to leave i_old pointing to the last
280                         // Index Record in the old Index. This way we can
281                         // concatenate the possible new Records from i_new.
282                         if (i_old->next == NULL)
283                                 break;
284
285                         i_old = i_old->next;
286                 }
287         }
288
289         assert(info->index.incomplete_count == 0);
290
291         // If Index was already known to be final, i_new must be NULL now.
292         // The new Index cannot contain more Records that we already have.
293         if (info->index.is_final) {
294                 assert(info->index.head != NULL);
295
296                 if (i_new != NULL) {
297                         if (eat_index)
298                                 lzma_index_free(i_new, allocator);
299
300                         return LZMA_DATA_ERROR;
301                 }
302
303                 return LZMA_OK;
304         }
305
306         // The rest of the new Index is merged to the old Index. Keep the
307         // current i_new pointer in available. We need it when merging the
308         // new Index with the old one, and if an error occurs so we can
309         // get rid of the broken part of the new Index.
310         lzma_index *i_start = i_new;
311         while (i_new != NULL) {
312                 // The new Index must be complete i.e. no unknown values.
313                 if (i_new->total_size > LZMA_VLI_VALUE_MAX
314                                 || i_new->uncompressed_size
315                                         > LZMA_VLI_VALUE_MAX) {
316                         if (eat_index)
317                                 lzma_index_free(i_start, allocator);
318
319                         return LZMA_PROG_ERROR;
320                 }
321
322                 // Update info->index.foo_sizes.
323                 if (lzma_vli_add(info->index.total_size, i_new->total_size)
324                                 || lzma_vli_add(info->index.uncompressed_size,
325                                         i_new->uncompressed_size)) {
326                         if (eat_index)
327                                 lzma_index_free(i_start, allocator);
328
329                         return LZMA_PROG_ERROR;
330                 }
331
332                 ++info->index.record_count;
333                 i_new = i_new->next;
334         }
335
336         // All the Records in the new Index are good, and info->index.foo_sizes
337         // were successfully updated.
338         if (lzma_info_index_finish(info) != LZMA_OK) {
339                 if (eat_index)
340                         lzma_index_free(i_start, allocator);
341
342                 return LZMA_DATA_ERROR;
343         }
344
345         // The Index is ready to be merged. If we aren't supposed to eat
346         // the Index, make a copy of it first.
347         if (!eat_index && i_start != NULL) {
348                 i_start = lzma_index_dup(i_start, allocator);
349                 if (i_start == NULL)
350                         return LZMA_MEM_ERROR;
351         }
352
353         // Concatenate the new Index with the old one. Note that it is
354         // possible that we don't have any old Index.
355         if (info->index.head == NULL)
356                 info->index.head = i_start;
357         else
358                 i_old->next = i_start;
359
360         return LZMA_OK;
361 }
362
363
364 extern LZMA_API lzma_ret
365 lzma_info_metadata_set(lzma_info *info, lzma_allocator *allocator,
366                 lzma_metadata *metadata, lzma_bool is_header_metadata,
367                 lzma_bool eat_index)
368 {
369         // Validate *metadata.
370         if (!lzma_vli_is_valid(metadata->header_metadata_size)
371                         || !lzma_vli_is_valid(metadata->total_size)
372                         || !lzma_vli_is_valid(metadata->uncompressed_size)) {
373                 if (eat_index) {
374                         lzma_index_free(metadata->index, allocator);
375                         metadata->index = NULL;
376                 }
377
378                 return LZMA_PROG_ERROR;
379         }
380
381         // Index
382         if (metadata->index != NULL) {
383                 if (is_header_metadata)
384                         info->has_index_in_header_metadata = true;
385
386                 const lzma_ret ret = lzma_info_index_set(
387                                 info, allocator, metadata->index, eat_index);
388                 if (ret != LZMA_OK)
389                         return ret;
390
391         } else if (!is_header_metadata
392                         && (metadata->total_size == LZMA_VLI_VALUE_UNKNOWN
393                                 || !info->has_index_in_header_metadata)) {
394                 // Either Total Size or Index must be present in Footer
395                 // Metadata Block. If Index is not present, it must have
396                 // already been in the Header Metadata Block. Since we
397                 // got here, these conditions weren't met.
398                 return LZMA_DATA_ERROR;
399         }
400
401         // Size of Header Metadata
402         if (!is_header_metadata) {
403                 // If it is marked unknown in Metadata, it means that
404                 // it's not present.
405                 const lzma_vli size = metadata->header_metadata_size
406                                         != LZMA_VLI_VALUE_UNKNOWN
407                                 ? metadata->header_metadata_size : 0;
408                 const lzma_ret ret = lzma_info_size_set(
409                                 info, LZMA_INFO_HEADER_METADATA, size);
410                 if (ret != LZMA_OK)
411                         return ret;
412         }
413
414         // Total Size
415         if (metadata->total_size != LZMA_VLI_VALUE_UNKNOWN) {
416                 const lzma_ret ret = lzma_info_size_set(info,
417                                 LZMA_INFO_TOTAL, metadata->total_size);
418                 if (ret != LZMA_OK)
419                         return ret;
420         }
421
422         // Uncompressed Size
423         if (metadata->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
424                 const lzma_ret ret = lzma_info_size_set(info,
425                                 LZMA_INFO_UNCOMPRESSED,
426                                 metadata->uncompressed_size);
427                 if (ret != LZMA_OK)
428                         return ret;
429         }
430
431         return LZMA_OK;
432 }
433
434
435 /////////
436 // Get //
437 /////////
438
439 extern LZMA_API lzma_vli
440 lzma_info_size_get(const lzma_info *info, lzma_info_size type)
441 {
442         switch (type) {
443         case LZMA_INFO_STREAM_START:
444                 return info->stream_start_offset;
445
446         case LZMA_INFO_HEADER_METADATA:
447                 return info->known.header_metadata_size;
448
449         case LZMA_INFO_TOTAL:
450                 return info->known.total_size;
451
452         case LZMA_INFO_UNCOMPRESSED:
453                 return info->known.uncompressed_size;
454
455         case LZMA_INFO_FOOTER_METADATA:
456                 return info->known.footer_metadata_size;
457         }
458
459         return LZMA_VLI_VALUE_UNKNOWN;
460 }
461
462
463 extern LZMA_API lzma_index *
464 lzma_info_index_get(lzma_info *info, lzma_bool detach)
465 {
466         lzma_index *i = info->index.head;
467
468         if (detach)
469                 index_init(info);
470
471         return i;
472 }
473
474
475 extern LZMA_API size_t
476 lzma_info_index_count_get(const lzma_info *info)
477 {
478         return info->index.record_count;
479 }
480
481
482 /////////////////
483 // Incremental //
484 /////////////////
485
486 enum {
487         ITER_INFO,
488         ITER_INDEX,
489         ITER_RESERVED_1,
490         ITER_RESERVED_2,
491 };
492
493
494 #define iter_info ((lzma_info *)(iter->internal[ITER_INFO]))
495
496 #define iter_index ((lzma_index *)(iter->internal[ITER_INDEX]))
497
498
499 extern LZMA_API void
500 lzma_info_iter_begin(lzma_info *info, lzma_info_iter *iter)
501 {
502         *iter = (lzma_info_iter){
503                 .total_size = LZMA_VLI_VALUE_UNKNOWN,
504                 .uncompressed_size = LZMA_VLI_VALUE_UNKNOWN,
505                 .stream_offset = LZMA_VLI_VALUE_UNKNOWN,
506                 .uncompressed_offset = LZMA_VLI_VALUE_UNKNOWN,
507                 .internal = { info, NULL, NULL, NULL },
508         };
509
510         return;
511 }
512
513
514 extern LZMA_API lzma_ret
515 lzma_info_iter_next(lzma_info_iter *iter, lzma_allocator *allocator)
516 {
517         // FIXME debug remove
518         lzma_info *info = iter_info;
519         (void)info;
520
521         if (iter_index == NULL) {
522                 // The first call after lzma_info_iter_begin().
523                 if (iter_info->known.header_metadata_size
524                                 == LZMA_VLI_VALUE_UNKNOWN)
525                         iter->stream_offset = LZMA_VLI_VALUE_UNKNOWN;
526                 else if (lzma_vli_sum3(iter->stream_offset,
527                                 iter_info->stream_start_offset,
528                                 LZMA_STREAM_HEADER_SIZE,
529                                 iter_info->known.header_metadata_size))
530                         return LZMA_PROG_ERROR;
531
532                 iter->uncompressed_offset = 0;
533
534                 if (iter_info->index.head != NULL) {
535                         // The first Index Record has already been allocated.
536                         iter->internal[ITER_INDEX] = iter_info->index.head;
537                         iter->total_size = iter_index->total_size;
538                         iter->uncompressed_size
539                                         = iter_index->uncompressed_size;
540                         return LZMA_OK;
541                 }
542         } else {
543                 // Update iter->*_offsets.
544                 if (iter->stream_offset != LZMA_VLI_VALUE_UNKNOWN) {
545                         if (iter_index->total_size == LZMA_VLI_VALUE_UNKNOWN)
546                                 iter->stream_offset = LZMA_VLI_VALUE_UNKNOWN;
547                         else if (lzma_vli_add(iter->stream_offset,
548                                         iter_index->total_size))
549                                 return LZMA_DATA_ERROR;
550                 }
551
552                 if (iter->uncompressed_offset != LZMA_VLI_VALUE_UNKNOWN) {
553                         if (iter_index->uncompressed_size
554                                         == LZMA_VLI_VALUE_UNKNOWN)
555                                 iter->uncompressed_offset
556                                                 = LZMA_VLI_VALUE_UNKNOWN;
557                         else if (lzma_vli_add(iter->uncompressed_offset,
558                                         iter_index->uncompressed_size))
559                                 return LZMA_DATA_ERROR;
560                 }
561
562                 if (iter_index->next != NULL) {
563                         // The next Record has already been allocated.
564                         iter->internal[ITER_INDEX] = iter_index->next;
565                         iter->total_size = iter_index->total_size;
566                         iter->uncompressed_size
567                                         = iter_index->uncompressed_size;
568                         return LZMA_OK;
569                 }
570         }
571
572         // Don't add new Records to a final Index.
573         if (iter_info->index.is_final)
574                 return LZMA_DATA_ERROR;
575
576         // Allocate and initialize a new Index Record.
577         lzma_index *i = lzma_alloc(sizeof(lzma_index), allocator);
578         if (i == NULL)
579                 return LZMA_MEM_ERROR;
580
581         i->total_size = LZMA_VLI_VALUE_UNKNOWN;
582         i->uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
583         i->next = NULL;
584
585         iter->total_size = LZMA_VLI_VALUE_UNKNOWN;
586         iter->uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
587
588         // Decide where to put the new Index Record.
589         if (iter_info->index.head == NULL)
590                 iter_info->index.head = i;
591
592         if (iter_index != NULL)
593                 iter_index->next = i;
594
595         iter->internal[ITER_INDEX] = i;
596
597         ++iter_info->index.record_count;
598         ++iter_info->index.incomplete_count;
599
600         return LZMA_OK;
601 }
602
603
604 extern LZMA_API lzma_ret
605 lzma_info_iter_set(lzma_info_iter *iter,
606                 lzma_vli total_size, lzma_vli uncompressed_size)
607 {
608         // FIXME debug remove
609         lzma_info *info = iter_info;
610         (void)info;
611
612         if (iter_index == NULL || !lzma_vli_is_valid(total_size)
613                         || !lzma_vli_is_valid(uncompressed_size))
614                 return LZMA_PROG_ERROR;
615
616         const bool was_incomplete = iter_index->total_size
617                                 == LZMA_VLI_VALUE_UNKNOWN
618                         || iter_index->uncompressed_size
619                                 == LZMA_VLI_VALUE_UNKNOWN;
620
621         if (total_size != LZMA_VLI_VALUE_UNKNOWN) {
622                 if (iter_index->total_size == LZMA_VLI_VALUE_UNKNOWN) {
623                         iter_index->total_size = total_size;
624
625                         if (lzma_vli_add(iter_info->index.total_size,
626                                                 total_size)
627                                         || iter_info->index.total_size
628                                                 > iter_info->known.total_size)
629                                 return LZMA_DATA_ERROR;
630
631                 } else if (iter_index->total_size != total_size) {
632                         return LZMA_DATA_ERROR;
633                 }
634         }
635
636         if (uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
637                 if (iter_index->uncompressed_size == LZMA_VLI_VALUE_UNKNOWN) {
638                         iter_index->uncompressed_size = uncompressed_size;
639
640                         if (lzma_vli_add(iter_info->index.uncompressed_size,
641                                                 uncompressed_size)
642                                         || iter_info->index.uncompressed_size
643                                         > iter_info->known.uncompressed_size)
644                                 return LZMA_DATA_ERROR;
645
646                 } else if (iter_index->uncompressed_size
647                                 != uncompressed_size) {
648                         return LZMA_DATA_ERROR;
649                 }
650         }
651
652         // Check if the new information we got managed to finish this
653         // Index Record. If so, update the count of incomplete Index Records.
654         if (was_incomplete && iter_index->total_size
655                                 != LZMA_VLI_VALUE_UNKNOWN
656                         && iter_index->uncompressed_size
657                                 != LZMA_VLI_VALUE_UNKNOWN) {
658                 assert(iter_info->index.incomplete_count > 0);
659                 --iter_info->index.incomplete_count;
660         }
661
662         // Make sure that the known sizes are now available in *iter.
663         iter->total_size = iter_index->total_size;
664         iter->uncompressed_size = iter_index->uncompressed_size;
665
666         return LZMA_OK;
667 }
668
669
670 extern LZMA_API lzma_ret
671 lzma_info_index_finish(lzma_info *info)
672 {
673         if (info->index.record_count == 0 || info->index.incomplete_count > 0
674                         || lzma_info_size_set(info, LZMA_INFO_TOTAL,
675                                 info->index.total_size)
676                         || lzma_info_size_set(info, LZMA_INFO_UNCOMPRESSED,
677                                 info->index.uncompressed_size))
678                 return LZMA_DATA_ERROR;
679
680         info->index.is_final = true;
681
682         return LZMA_OK;
683 }
684
685
686 //////////////
687 // Locating //
688 //////////////
689
690 extern LZMA_API lzma_vli
691 lzma_info_metadata_locate(const lzma_info *info, lzma_bool is_header_metadata)
692 {
693         bool error = false;
694         lzma_vli size = 0;
695
696         if (info->known.header_metadata_size == LZMA_VLI_VALUE_UNKNOWN) {
697                 // We don't know if Header Metadata Block is present, thus
698                 // we cannot locate it either.
699                 //
700                 // Well, you could say that just assume that it is present.
701                 // I'm not sure if this is useful. But it can be useful to
702                 // be able to use this function and get LZMA_VLI_VALUE_UNKNOWN
703                 // to detect that Header Metadata Block wasn't present.
704                 error = true;
705         } else if (is_header_metadata) {
706                 error = lzma_vli_sum(size, info->stream_start_offset,
707                                 LZMA_STREAM_HEADER_SIZE);
708         } else if (!info->index.is_final) {
709                 // Since we don't know if we have all the Index Records yet,
710                 // we cannot know where the Footer Metadata Block is.
711                 error = true;
712         } else {
713                 error = lzma_vli_sum4(size, info->stream_start_offset,
714                                 LZMA_STREAM_HEADER_SIZE,
715                                 info->known.header_metadata_size,
716                                 info->known.total_size);
717         }
718
719         return error ? LZMA_VLI_VALUE_UNKNOWN : size;
720 }
721
722
723 extern LZMA_API uint32_t
724 lzma_info_metadata_alignment_get(
725                 const lzma_info *info, lzma_bool is_header_metadata)
726 {
727         uint32_t alignment;
728
729         if (is_header_metadata) {
730                 alignment = info->stream_start_offset
731                                 + LZMA_STREAM_HEADER_SIZE;
732         } else {
733                 alignment = info->stream_start_offset + LZMA_STREAM_HEADER_SIZE
734                                 + info->known.header_metadata_size
735                                 + info->known.total_size;
736         }
737
738         return alignment;
739 }
740
741
742 extern LZMA_API lzma_ret
743 lzma_info_iter_locate(lzma_info_iter *iter, lzma_allocator *allocator,
744                 lzma_vli uncompressed_offset, lzma_bool allow_alloc)
745 {
746         if (iter == NULL || uncompressed_offset > LZMA_VLI_VALUE_MAX)
747                 return LZMA_PROG_ERROR;
748
749         // Quick check in case Index is final.
750         if (iter_info->index.is_final) {
751                 assert(iter_info->known.uncompressed_size
752                                 == iter_info->index.uncompressed_size);
753                 if (uncompressed_offset >= iter_info->index.uncompressed_size)
754                         return LZMA_DATA_ERROR;
755         }
756
757         // TODO: Optimize so that it uses existing info from *iter when
758         // seeking forward.
759
760         // Initialize *iter
761         if (iter_info->known.header_metadata_size != LZMA_VLI_VALUE_UNKNOWN) {
762                 if (lzma_vli_sum3(iter->stream_offset,
763                                 iter_info->stream_start_offset,
764                                 LZMA_STREAM_HEADER_SIZE,
765                                 iter_info->known.header_metadata_size))
766                         return LZMA_PROG_ERROR;
767         } else {
768                 // We don't know the Size of Header Metadata Block, thus
769                 // we cannot calculate the Stream offset either.
770                 iter->stream_offset = LZMA_VLI_VALUE_UNKNOWN;
771         }
772
773         iter->uncompressed_offset = 0;
774
775         // If we have no Index Records, it's obvious that we need to
776         // add a new one.
777         if (iter_info->index.head == NULL) {
778                 assert(!iter_info->index.is_final);
779                 if (!allow_alloc)
780                         return LZMA_DATA_ERROR;
781
782                 return lzma_info_iter_next(iter, allocator);
783         }
784
785         // Locate an appropriate Index Record.
786         lzma_index *i = iter_info->index.head;
787         while (true) {
788                 // - If Uncompressed Size in the Record is unknown,
789                 //   we have no chance to search further.
790                 // - If the next Record would go past the requested offset,
791                 //   we have found our target Data Block.
792                 if (i->uncompressed_size == LZMA_VLI_VALUE_UNKNOWN
793                                 || iter->uncompressed_offset
794                                 + i->uncompressed_size > uncompressed_offset) {
795                         iter->total_size = i->total_size;
796                         iter->uncompressed_size = i->uncompressed_size;
797                         iter->internal[ITER_INDEX] = i;
798                         return LZMA_OK;
799                 }
800
801                 // Update the stream offset. It may be unknown if we didn't
802                 // know the size of Header Metadata Block.
803                 if (iter->stream_offset != LZMA_VLI_VALUE_UNKNOWN)
804                         if (lzma_vli_add(iter->stream_offset, i->total_size))
805                                 return LZMA_PROG_ERROR;
806
807                 // Update the uncompressed offset. This cannot overflow since
808                 // the Index is known to be valid.
809                 iter->uncompressed_offset += i->uncompressed_size;
810
811                 // Move to the next Block.
812                 if (i->next == NULL) {
813                         assert(!iter_info->index.is_final);
814                         if (!allow_alloc)
815                                 return LZMA_DATA_ERROR;
816
817                         iter->internal[ITER_INDEX] = i;
818                         return lzma_info_iter_next(iter, allocator);
819                 }
820
821                 i = i->next;
822         }
823 }