]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/block_header_encoder.c
Imported to git.
[icculus/xz.git] / src / liblzma / common / block_header_encoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       block_header_encoder.c
4 /// \brief      Encodes Block Header for .lzma files
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 #include "check.h"
22
23
24 extern LZMA_API lzma_ret
25 lzma_block_header_size(lzma_options_block *options)
26 {
27         // Block Flags take two bytes.
28         size_t size = 2;
29
30         // Compressed Size
31         if (!lzma_vli_is_valid(options->compressed_size)) {
32                 return LZMA_PROG_ERROR;
33
34         } else if (options->compressed_reserve != 0) {
35                 // Make sure that the known Compressed Size fits into the
36                 // reserved space. Note that lzma_vli_size() will return zero
37                 // if options->compressed_size is LZMA_VLI_VALUE_UNKNOWN, so
38                 // we don't need to handle that special case separately.
39                 if (options->compressed_reserve > LZMA_VLI_BYTES_MAX
40                                 || lzma_vli_size(options->compressed_size)
41                                 > (size_t)(options->compressed_reserve))
42                         return LZMA_PROG_ERROR;
43
44                 size += options->compressed_reserve;
45
46         } else if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN) {
47                 // Compressed Size is known. We have already checked
48                 // that is is a valid VLI, and since it isn't
49                 // LZMA_VLI_VALUE_UNKNOWN, we can be sure that
50                 // lzma_vli_size() will succeed.
51                 size += lzma_vli_size(options->compressed_size);
52         }
53
54         // Uncompressed Size
55         if (!lzma_vli_is_valid(options->uncompressed_size)) {
56                 return LZMA_PROG_ERROR;
57
58         } else if (options->uncompressed_reserve != 0) {
59                 if (options->uncompressed_reserve > LZMA_VLI_BYTES_MAX
60                                 || lzma_vli_size(options->uncompressed_size)
61                                 > (size_t)(options->uncompressed_reserve))
62                         return LZMA_PROG_ERROR;
63
64                 size += options->uncompressed_reserve;
65
66         } else if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
67                 size += lzma_vli_size(options->uncompressed_size);
68         }
69
70         // List of Filter Flags
71         for (size_t i = 0; options->filters[i].id != LZMA_VLI_VALUE_UNKNOWN;
72                         ++i) {
73                 // Don't allow too many filters.
74                 if (i == 7)
75                         return LZMA_PROG_ERROR;
76
77                 uint32_t tmp;
78                 const lzma_ret ret = lzma_filter_flags_size(&tmp,
79                                 options->filters + i);
80                 if (ret != LZMA_OK)
81                         return ret;
82
83                 size += tmp;
84         }
85
86         // CRC32
87         if (options->has_crc32)
88                 size += 4;
89
90         // Padding
91         int32_t padding;
92         if (options->padding == LZMA_BLOCK_HEADER_PADDING_AUTO) {
93                 const uint32_t preferred = lzma_alignment_output(
94                                 options->filters, 1);
95                 const uint32_t unaligned = size + options->alignment;
96                 padding = (int32_t)(unaligned % preferred);
97                 if (padding != 0)
98                         padding = preferred - padding;
99         } else if (options->padding >= LZMA_BLOCK_HEADER_PADDING_MIN
100                         && options->padding <= LZMA_BLOCK_HEADER_PADDING_MAX) {
101                 padding = options->padding;
102         } else {
103                 return LZMA_PROG_ERROR;
104         }
105
106         // All success. Copy the calculated values to the options structure.
107         options->padding = padding;
108         options->header_size = size + (size_t)(padding);
109
110         return LZMA_OK;
111 }
112
113
114 extern LZMA_API lzma_ret
115 lzma_block_header_encode(uint8_t *out, const lzma_options_block *options)
116 {
117         // We write the Block Flags later.
118         if (options->header_size < 2)
119                 return LZMA_PROG_ERROR;
120
121         const size_t out_size = options->header_size;
122         size_t out_pos = 2;
123
124         // Compressed Size
125         if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN
126                         || options->compressed_reserve != 0) {
127                 const lzma_vli size = options->compressed_size
128                                         != LZMA_VLI_VALUE_UNKNOWN
129                                 ? options->compressed_size : 0;
130                 size_t vli_pos = 0;
131                 if (lzma_vli_encode(
132                                 size, &vli_pos, options->compressed_reserve,
133                                 out, &out_pos, out_size) != LZMA_STREAM_END)
134                         return LZMA_PROG_ERROR;
135
136         }
137
138         // Uncompressed Size
139         if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN
140                         || options->uncompressed_reserve != 0) {
141                 const lzma_vli size = options->uncompressed_size
142                                         != LZMA_VLI_VALUE_UNKNOWN
143                                 ? options->uncompressed_size : 0;
144                 size_t vli_pos = 0;
145                 if (lzma_vli_encode(
146                                 size, &vli_pos, options->uncompressed_reserve,
147                                 out, &out_pos, out_size) != LZMA_STREAM_END)
148                         return LZMA_PROG_ERROR;
149
150         }
151
152         // Filter Flags
153         size_t filter_count;
154         for (filter_count = 0; options->filters[filter_count].id
155                         != LZMA_VLI_VALUE_UNKNOWN; ++filter_count) {
156                 // There can be at maximum of seven filters.
157                 if (filter_count == 7)
158                         return LZMA_PROG_ERROR;
159
160                 const lzma_ret ret = lzma_filter_flags_encode(out, &out_pos,
161                                 out_size, options->filters + filter_count);
162                 // FIXME: Don't return LZMA_BUF_ERROR.
163                 if (ret != LZMA_OK)
164                         return ret;
165         }
166
167         // Block Flags 1
168         out[0] = filter_count;
169
170         if (options->has_eopm)
171                 out[0] |= 0x08;
172         else if (options->uncompressed_size == LZMA_VLI_VALUE_UNKNOWN)
173                 return LZMA_PROG_ERROR;
174
175         if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN
176                         || options->compressed_reserve != 0)
177                 out[0] |= 0x10;
178
179         if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN
180                         || options->uncompressed_reserve != 0)
181                 out[0] |= 0x20;
182
183         if (options->is_metadata)
184                 out[0] |= 0x80;
185
186         // Block Flags 2
187         if (options->padding < LZMA_BLOCK_HEADER_PADDING_MIN
188                         || options->padding > LZMA_BLOCK_HEADER_PADDING_MAX)
189                 return LZMA_PROG_ERROR;
190
191         out[1] = (uint8_t)(options->padding);
192
193         // CRC32
194         if (options->has_crc32) {
195                 if (out_size - out_pos < 4)
196                         return LZMA_PROG_ERROR;
197
198                 const uint32_t crc = lzma_crc32(out, out_pos, 0);
199                 for (size_t i = 0; i < 4; ++i)
200                         out[out_pos++] = crc >> (i * 8);
201         }
202
203         // Padding - the amount of available space must now match with
204         // the size of the Padding field.
205         if (out_size - out_pos != (size_t)(options->padding))
206                 return LZMA_PROG_ERROR;
207
208         memzero(out + out_pos, (size_t)(options->padding));
209
210         return LZMA_OK;
211 }