/src/xz/src/liblzma/common/stream_encoder.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: 0BSD |
2 | | |
3 | | /////////////////////////////////////////////////////////////////////////////// |
4 | | // |
5 | | /// \file stream_encoder.c |
6 | | /// \brief Encodes .xz Streams |
7 | | // |
8 | | // Author: Lasse Collin |
9 | | // |
10 | | /////////////////////////////////////////////////////////////////////////////// |
11 | | |
12 | | #include "block_encoder.h" |
13 | | #include "index_encoder.h" |
14 | | |
15 | | |
16 | | typedef struct { |
17 | | enum { |
18 | | SEQ_STREAM_HEADER, |
19 | | SEQ_BLOCK_INIT, |
20 | | SEQ_BLOCK_HEADER, |
21 | | SEQ_BLOCK_ENCODE, |
22 | | SEQ_INDEX_ENCODE, |
23 | | SEQ_STREAM_FOOTER, |
24 | | } sequence; |
25 | | |
26 | | /// True if Block encoder has been initialized by |
27 | | /// stream_encoder_init() or stream_encoder_update() |
28 | | /// and thus doesn't need to be initialized in stream_encode(). |
29 | | bool block_encoder_is_initialized; |
30 | | |
31 | | /// Block |
32 | | lzma_next_coder block_encoder; |
33 | | |
34 | | /// Options for the Block encoder |
35 | | lzma_block block_options; |
36 | | |
37 | | /// The filter chain currently in use |
38 | | lzma_filter filters[LZMA_FILTERS_MAX + 1]; |
39 | | |
40 | | /// Index encoder. This is separate from Block encoder, because this |
41 | | /// doesn't take much memory, and when encoding multiple Streams |
42 | | /// with the same encoding options we avoid reallocating memory. |
43 | | lzma_next_coder index_encoder; |
44 | | |
45 | | /// Index to hold sizes of the Blocks |
46 | | lzma_index *index; |
47 | | |
48 | | /// Read position in buffer[] |
49 | | size_t buffer_pos; |
50 | | |
51 | | /// Total number of bytes in buffer[] |
52 | | size_t buffer_size; |
53 | | |
54 | | /// Buffer to hold Stream Header, Block Header, and Stream Footer. |
55 | | /// Block Header has biggest maximum size. |
56 | | uint8_t buffer[LZMA_BLOCK_HEADER_SIZE_MAX]; |
57 | | } lzma_stream_coder; |
58 | | |
59 | | |
60 | | static lzma_ret |
61 | | block_encoder_init(lzma_stream_coder *coder, const lzma_allocator *allocator) |
62 | 0 | { |
63 | | // Prepare the Block options. Even though Block encoder doesn't need |
64 | | // compressed_size, uncompressed_size, and header_size to be |
65 | | // initialized, it is a good idea to do it here, because this way |
66 | | // we catch if someone gave us Filter ID that cannot be used in |
67 | | // Blocks/Streams. |
68 | 0 | coder->block_options.compressed_size = LZMA_VLI_UNKNOWN; |
69 | 0 | coder->block_options.uncompressed_size = LZMA_VLI_UNKNOWN; |
70 | |
|
71 | 0 | return_if_error(lzma_block_header_size(&coder->block_options)); |
72 | | |
73 | | // Initialize the actual Block encoder. |
74 | 0 | return lzma_block_encoder_init(&coder->block_encoder, allocator, |
75 | 0 | &coder->block_options); |
76 | 0 | } |
77 | | |
78 | | |
79 | | static lzma_ret |
80 | | stream_encode(void *coder_ptr, const lzma_allocator *allocator, |
81 | | const uint8_t *restrict in, size_t *restrict in_pos, |
82 | | size_t in_size, uint8_t *restrict out, |
83 | | size_t *restrict out_pos, size_t out_size, lzma_action action) |
84 | 0 | { |
85 | 0 | lzma_stream_coder *coder = coder_ptr; |
86 | | |
87 | | // Main loop |
88 | 0 | while (*out_pos < out_size) |
89 | 0 | switch (coder->sequence) { |
90 | 0 | case SEQ_STREAM_HEADER: |
91 | 0 | case SEQ_BLOCK_HEADER: |
92 | 0 | case SEQ_STREAM_FOOTER: |
93 | 0 | lzma_bufcpy(coder->buffer, &coder->buffer_pos, |
94 | 0 | coder->buffer_size, out, out_pos, out_size); |
95 | 0 | if (coder->buffer_pos < coder->buffer_size) |
96 | 0 | return LZMA_OK; |
97 | | |
98 | 0 | if (coder->sequence == SEQ_STREAM_FOOTER) |
99 | 0 | return LZMA_STREAM_END; |
100 | | |
101 | 0 | coder->buffer_pos = 0; |
102 | 0 | ++coder->sequence; |
103 | 0 | break; |
104 | | |
105 | 0 | case SEQ_BLOCK_INIT: { |
106 | 0 | if (*in_pos == in_size) { |
107 | | // If we are requested to flush or finish the current |
108 | | // Block, return LZMA_STREAM_END immediately since |
109 | | // there's nothing to do. |
110 | 0 | if (action != LZMA_FINISH) |
111 | 0 | return action == LZMA_RUN |
112 | 0 | ? LZMA_OK : LZMA_STREAM_END; |
113 | | |
114 | | // The application had used LZMA_FULL_FLUSH to finish |
115 | | // the previous Block, but now wants to finish without |
116 | | // encoding new data, or it is simply creating an |
117 | | // empty Stream with no Blocks. |
118 | | // |
119 | | // Initialize the Index encoder, and continue to |
120 | | // actually encoding the Index. |
121 | 0 | return_if_error(lzma_index_encoder_init( |
122 | 0 | &coder->index_encoder, allocator, |
123 | 0 | coder->index)); |
124 | 0 | coder->sequence = SEQ_INDEX_ENCODE; |
125 | 0 | break; |
126 | 0 | } |
127 | | |
128 | | // Initialize the Block encoder unless it was already |
129 | | // initialized by stream_encoder_init() or |
130 | | // stream_encoder_update(). |
131 | 0 | if (!coder->block_encoder_is_initialized) |
132 | 0 | return_if_error(block_encoder_init(coder, allocator)); |
133 | | |
134 | | // Make it false so that we don't skip the initialization |
135 | | // with the next Block. |
136 | 0 | coder->block_encoder_is_initialized = false; |
137 | | |
138 | | // Encode the Block Header. This shouldn't fail since we have |
139 | | // already initialized the Block encoder. |
140 | 0 | if (lzma_block_header_encode(&coder->block_options, |
141 | 0 | coder->buffer) != LZMA_OK) |
142 | 0 | return LZMA_PROG_ERROR; |
143 | | |
144 | 0 | coder->buffer_size = coder->block_options.header_size; |
145 | 0 | coder->sequence = SEQ_BLOCK_HEADER; |
146 | 0 | break; |
147 | 0 | } |
148 | | |
149 | 0 | case SEQ_BLOCK_ENCODE: { |
150 | 0 | static const lzma_action convert[LZMA_ACTION_MAX + 1] = { |
151 | 0 | LZMA_RUN, |
152 | 0 | LZMA_SYNC_FLUSH, |
153 | 0 | LZMA_FINISH, |
154 | 0 | LZMA_FINISH, |
155 | 0 | LZMA_FINISH, |
156 | 0 | }; |
157 | |
|
158 | 0 | const lzma_ret ret = coder->block_encoder.code( |
159 | 0 | coder->block_encoder.coder, allocator, |
160 | 0 | in, in_pos, in_size, |
161 | 0 | out, out_pos, out_size, convert[action]); |
162 | 0 | if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH) |
163 | 0 | return ret; |
164 | | |
165 | | // Add a new Index Record. |
166 | 0 | const lzma_vli unpadded_size = lzma_block_unpadded_size( |
167 | 0 | &coder->block_options); |
168 | 0 | assert(unpadded_size != 0); |
169 | 0 | return_if_error(lzma_index_append(coder->index, allocator, |
170 | 0 | unpadded_size, |
171 | 0 | coder->block_options.uncompressed_size)); |
172 | | |
173 | 0 | coder->sequence = SEQ_BLOCK_INIT; |
174 | 0 | break; |
175 | 0 | } |
176 | | |
177 | 0 | case SEQ_INDEX_ENCODE: { |
178 | | // Call the Index encoder. It doesn't take any input, so |
179 | | // those pointers can be NULL. |
180 | 0 | const lzma_ret ret = coder->index_encoder.code( |
181 | 0 | coder->index_encoder.coder, allocator, |
182 | 0 | NULL, NULL, 0, |
183 | 0 | out, out_pos, out_size, LZMA_RUN); |
184 | 0 | if (ret != LZMA_STREAM_END) |
185 | 0 | return ret; |
186 | | |
187 | | // Encode the Stream Footer into coder->buffer. |
188 | 0 | const lzma_stream_flags stream_flags = { |
189 | 0 | .version = 0, |
190 | 0 | .backward_size = lzma_index_size(coder->index), |
191 | 0 | .check = coder->block_options.check, |
192 | 0 | }; |
193 | |
|
194 | 0 | if (lzma_stream_footer_encode(&stream_flags, coder->buffer) |
195 | 0 | != LZMA_OK) |
196 | 0 | return LZMA_PROG_ERROR; |
197 | | |
198 | 0 | coder->buffer_size = LZMA_STREAM_HEADER_SIZE; |
199 | 0 | coder->sequence = SEQ_STREAM_FOOTER; |
200 | 0 | break; |
201 | 0 | } |
202 | | |
203 | 0 | default: |
204 | 0 | assert(0); |
205 | 0 | return LZMA_PROG_ERROR; |
206 | 0 | } |
207 | | |
208 | 0 | return LZMA_OK; |
209 | 0 | } |
210 | | |
211 | | |
212 | | static void |
213 | | stream_encoder_end(void *coder_ptr, const lzma_allocator *allocator) |
214 | 0 | { |
215 | 0 | lzma_stream_coder *coder = coder_ptr; |
216 | |
|
217 | 0 | lzma_next_end(&coder->block_encoder, allocator); |
218 | 0 | lzma_next_end(&coder->index_encoder, allocator); |
219 | 0 | lzma_index_end(coder->index, allocator); |
220 | |
|
221 | 0 | lzma_filters_free(coder->filters, allocator); |
222 | |
|
223 | 0 | lzma_free(coder, allocator); |
224 | 0 | return; |
225 | 0 | } |
226 | | |
227 | | |
228 | | static lzma_ret |
229 | | stream_encoder_update(void *coder_ptr, const lzma_allocator *allocator, |
230 | | const lzma_filter *filters, |
231 | | const lzma_filter *reversed_filters) |
232 | 0 | { |
233 | 0 | lzma_stream_coder *coder = coder_ptr; |
234 | 0 | lzma_ret ret; |
235 | | |
236 | | // Make a copy to a temporary buffer first. This way it is easier |
237 | | // to keep the encoder state unchanged if an error occurs with |
238 | | // lzma_filters_copy(). |
239 | 0 | lzma_filter temp[LZMA_FILTERS_MAX + 1]; |
240 | 0 | return_if_error(lzma_filters_copy(filters, temp, allocator)); |
241 | | |
242 | 0 | if (coder->sequence <= SEQ_BLOCK_INIT) { |
243 | | // There is no incomplete Block waiting to be finished, |
244 | | // thus we can change the whole filter chain. Start by |
245 | | // trying to initialize the Block encoder with the new |
246 | | // chain. This way we detect if the chain is valid. |
247 | 0 | coder->block_encoder_is_initialized = false; |
248 | 0 | coder->block_options.filters = temp; |
249 | 0 | ret = block_encoder_init(coder, allocator); |
250 | 0 | coder->block_options.filters = coder->filters; |
251 | 0 | if (ret != LZMA_OK) |
252 | 0 | goto error; |
253 | | |
254 | 0 | coder->block_encoder_is_initialized = true; |
255 | |
|
256 | 0 | } else if (coder->sequence <= SEQ_BLOCK_ENCODE) { |
257 | | // We are in the middle of a Block. Try to update only |
258 | | // the filter-specific options. |
259 | 0 | ret = coder->block_encoder.update( |
260 | 0 | coder->block_encoder.coder, allocator, |
261 | 0 | filters, reversed_filters); |
262 | 0 | if (ret != LZMA_OK) |
263 | 0 | goto error; |
264 | 0 | } else { |
265 | | // Trying to update the filter chain when we are already |
266 | | // encoding Index or Stream Footer. |
267 | 0 | ret = LZMA_PROG_ERROR; |
268 | 0 | goto error; |
269 | 0 | } |
270 | | |
271 | | // Free the options of the old chain. |
272 | 0 | lzma_filters_free(coder->filters, allocator); |
273 | | |
274 | | // Copy the new filter chain in place. |
275 | 0 | memcpy(coder->filters, temp, sizeof(temp)); |
276 | |
|
277 | 0 | return LZMA_OK; |
278 | | |
279 | 0 | error: |
280 | 0 | lzma_filters_free(temp, allocator); |
281 | 0 | return ret; |
282 | 0 | } |
283 | | |
284 | | |
285 | | static lzma_ret |
286 | | stream_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator, |
287 | | const lzma_filter *filters, lzma_check check) |
288 | 0 | { |
289 | 0 | lzma_next_coder_init(&stream_encoder_init, next, allocator); |
290 | |
|
291 | 0 | if (filters == NULL) |
292 | 0 | return LZMA_PROG_ERROR; |
293 | | |
294 | 0 | lzma_stream_coder *coder = next->coder; |
295 | |
|
296 | 0 | if (coder == NULL) { |
297 | 0 | coder = lzma_alloc(sizeof(lzma_stream_coder), allocator); |
298 | 0 | if (coder == NULL) |
299 | 0 | return LZMA_MEM_ERROR; |
300 | | |
301 | 0 | next->coder = coder; |
302 | 0 | next->code = &stream_encode; |
303 | 0 | next->end = &stream_encoder_end; |
304 | 0 | next->update = &stream_encoder_update; |
305 | |
|
306 | 0 | coder->filters[0].id = LZMA_VLI_UNKNOWN; |
307 | 0 | coder->block_encoder = LZMA_NEXT_CODER_INIT; |
308 | 0 | coder->index_encoder = LZMA_NEXT_CODER_INIT; |
309 | 0 | coder->index = NULL; |
310 | 0 | } |
311 | | |
312 | | // Basic initializations |
313 | 0 | coder->sequence = SEQ_STREAM_HEADER; |
314 | 0 | coder->block_options.version = 0; |
315 | 0 | coder->block_options.check = check; |
316 | | |
317 | | // Initialize the Index |
318 | 0 | lzma_index_end(coder->index, allocator); |
319 | 0 | coder->index = lzma_index_init(allocator); |
320 | 0 | if (coder->index == NULL) |
321 | 0 | return LZMA_MEM_ERROR; |
322 | | |
323 | | // Encode the Stream Header |
324 | 0 | lzma_stream_flags stream_flags = { |
325 | 0 | .version = 0, |
326 | 0 | .check = check, |
327 | 0 | }; |
328 | 0 | return_if_error(lzma_stream_header_encode( |
329 | 0 | &stream_flags, coder->buffer)); |
330 | | |
331 | 0 | coder->buffer_pos = 0; |
332 | 0 | coder->buffer_size = LZMA_STREAM_HEADER_SIZE; |
333 | | |
334 | | // Initialize the Block encoder. This way we detect unsupported |
335 | | // filter chains when initializing the Stream encoder instead of |
336 | | // giving an error after Stream Header has already been written out. |
337 | 0 | return stream_encoder_update(coder, allocator, filters, NULL); |
338 | 0 | } |
339 | | |
340 | | |
341 | | extern LZMA_API(lzma_ret) |
342 | | lzma_stream_encoder(lzma_stream *strm, |
343 | | const lzma_filter *filters, lzma_check check) |
344 | 0 | { |
345 | 0 | lzma_next_strm_init(stream_encoder_init, strm, filters, check); |
346 | | |
347 | 0 | strm->internal->supported_actions[LZMA_RUN] = true; |
348 | 0 | strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true; |
349 | 0 | strm->internal->supported_actions[LZMA_FULL_FLUSH] = true; |
350 | 0 | strm->internal->supported_actions[LZMA_FULL_BARRIER] = true; |
351 | 0 | strm->internal->supported_actions[LZMA_FINISH] = true; |
352 | |
|
353 | 0 | return LZMA_OK; |
354 | 0 | } |