1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
5 /// \file stream_encoder.c
6 /// \brief Encodes .xz Streams
8 // Author: Lasse Collin
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "block_encoder.h"
13 #include "index_encoder.h"
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
;
32 lzma_next_coder block_encoder
;
34 /// Options for the Block encoder
35 lzma_block block_options
;
37 /// The filter chain currently in use
38 lzma_filter filters
[LZMA_FILTERS_MAX
+ 1];
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
;
45 /// Index to hold sizes of the Blocks
48 /// Read position in buffer[]
51 /// Total number of bytes in buffer[]
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
];
61 block_encoder_init(lzma_stream_coder
*coder
, const lzma_allocator
*allocator
)
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
68 coder
->block_options
.compressed_size
= LZMA_VLI_UNKNOWN
;
69 coder
->block_options
.uncompressed_size
= LZMA_VLI_UNKNOWN
;
71 return_if_error(lzma_block_header_size(&coder
->block_options
));
73 // Initialize the actual Block encoder.
74 return lzma_block_encoder_init(&coder
->block_encoder
, allocator
,
75 &coder
->block_options
);
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
)
85 lzma_stream_coder
*coder
= coder_ptr
;
88 while (*out_pos
< out_size
)
89 switch (coder
->sequence
) {
90 case SEQ_STREAM_HEADER
:
91 case SEQ_BLOCK_HEADER
:
92 case SEQ_STREAM_FOOTER
:
93 lzma_bufcpy(coder
->buffer
, &coder
->buffer_pos
,
94 coder
->buffer_size
, out
, out_pos
, out_size
);
95 if (coder
->buffer_pos
< coder
->buffer_size
)
98 if (coder
->sequence
== SEQ_STREAM_FOOTER
)
99 return LZMA_STREAM_END
;
101 coder
->buffer_pos
= 0;
105 case SEQ_BLOCK_INIT
: {
106 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 if (action
!= LZMA_FINISH
)
111 return action
== LZMA_RUN
112 ? LZMA_OK
: LZMA_STREAM_END
;
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.
119 // Initialize the Index encoder, and continue to
120 // actually encoding the Index.
121 return_if_error(lzma_index_encoder_init(
122 &coder
->index_encoder
, allocator
,
124 coder
->sequence
= SEQ_INDEX_ENCODE
;
128 // Initialize the Block encoder unless it was already
129 // initialized by stream_encoder_init() or
130 // stream_encoder_update().
131 if (!coder
->block_encoder_is_initialized
)
132 return_if_error(block_encoder_init(coder
, allocator
));
134 // Make it false so that we don't skip the initialization
135 // with the next Block.
136 coder
->block_encoder_is_initialized
= false;
138 // Encode the Block Header. This shouldn't fail since we have
139 // already initialized the Block encoder.
140 if (lzma_block_header_encode(&coder
->block_options
,
141 coder
->buffer
) != LZMA_OK
)
142 return LZMA_PROG_ERROR
;
144 coder
->buffer_size
= coder
->block_options
.header_size
;
145 coder
->sequence
= SEQ_BLOCK_HEADER
;
149 case SEQ_BLOCK_ENCODE
: {
150 static const lzma_action convert
[LZMA_ACTION_MAX
+ 1] = {
158 const lzma_ret ret
= coder
->block_encoder
.code(
159 coder
->block_encoder
.coder
, allocator
,
161 out
, out_pos
, out_size
, convert
[action
]);
162 if (ret
!= LZMA_STREAM_END
|| action
== LZMA_SYNC_FLUSH
)
165 // Add a new Index Record.
166 const lzma_vli unpadded_size
= lzma_block_unpadded_size(
167 &coder
->block_options
);
168 assert(unpadded_size
!= 0);
169 return_if_error(lzma_index_append(coder
->index
, allocator
,
171 coder
->block_options
.uncompressed_size
));
173 coder
->sequence
= SEQ_BLOCK_INIT
;
177 case SEQ_INDEX_ENCODE
: {
178 // Call the Index encoder. It doesn't take any input, so
179 // those pointers can be NULL.
180 const lzma_ret ret
= coder
->index_encoder
.code(
181 coder
->index_encoder
.coder
, allocator
,
183 out
, out_pos
, out_size
, LZMA_RUN
);
184 if (ret
!= LZMA_STREAM_END
)
187 // Encode the Stream Footer into coder->buffer.
188 const lzma_stream_flags stream_flags
= {
190 .backward_size
= lzma_index_size(coder
->index
),
191 .check
= coder
->block_options
.check
,
194 if (lzma_stream_footer_encode(&stream_flags
, coder
->buffer
)
196 return LZMA_PROG_ERROR
;
198 coder
->buffer_size
= LZMA_STREAM_HEADER_SIZE
;
199 coder
->sequence
= SEQ_STREAM_FOOTER
;
205 return LZMA_PROG_ERROR
;
213 stream_encoder_end(void *coder_ptr
, const lzma_allocator
*allocator
)
215 lzma_stream_coder
*coder
= coder_ptr
;
217 lzma_next_end(&coder
->block_encoder
, allocator
);
218 lzma_next_end(&coder
->index_encoder
, allocator
);
219 lzma_index_end(coder
->index
, allocator
);
221 lzma_filters_free(coder
->filters
, allocator
);
223 lzma_free(coder
, allocator
);
229 stream_encoder_update(void *coder_ptr
, const lzma_allocator
*allocator
,
230 const lzma_filter
*filters
,
231 const lzma_filter
*reversed_filters
)
233 lzma_stream_coder
*coder
= coder_ptr
;
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 lzma_filter temp
[LZMA_FILTERS_MAX
+ 1];
240 return_if_error(lzma_filters_copy(filters
, temp
, allocator
));
242 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 coder
->block_encoder_is_initialized
= false;
248 coder
->block_options
.filters
= temp
;
249 ret
= block_encoder_init(coder
, allocator
);
250 coder
->block_options
.filters
= coder
->filters
;
254 coder
->block_encoder_is_initialized
= true;
256 } 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 ret
= coder
->block_encoder
.update(
260 coder
->block_encoder
.coder
, allocator
,
261 filters
, reversed_filters
);
265 // Trying to update the filter chain when we are already
266 // encoding Index or Stream Footer.
267 ret
= LZMA_PROG_ERROR
;
271 // Free the options of the old chain.
272 lzma_filters_free(coder
->filters
, allocator
);
274 // Copy the new filter chain in place.
275 memcpy(coder
->filters
, temp
, sizeof(temp
));
280 lzma_filters_free(temp
, allocator
);
286 stream_encoder_init(lzma_next_coder
*next
, const lzma_allocator
*allocator
,
287 const lzma_filter
*filters
, lzma_check check
)
289 lzma_next_coder_init(&stream_encoder_init
, next
, allocator
);
292 return LZMA_PROG_ERROR
;
294 lzma_stream_coder
*coder
= next
->coder
;
297 coder
= lzma_alloc(sizeof(lzma_stream_coder
), allocator
);
299 return LZMA_MEM_ERROR
;
302 next
->code
= &stream_encode
;
303 next
->end
= &stream_encoder_end
;
304 next
->update
= &stream_encoder_update
;
306 coder
->filters
[0].id
= LZMA_VLI_UNKNOWN
;
307 coder
->block_encoder
= LZMA_NEXT_CODER_INIT
;
308 coder
->index_encoder
= LZMA_NEXT_CODER_INIT
;
312 // Basic initializations
313 coder
->sequence
= SEQ_STREAM_HEADER
;
314 coder
->block_options
.version
= 0;
315 coder
->block_options
.check
= check
;
317 // Initialize the Index
318 lzma_index_end(coder
->index
, allocator
);
319 coder
->index
= lzma_index_init(allocator
);
320 if (coder
->index
== NULL
)
321 return LZMA_MEM_ERROR
;
323 // Encode the Stream Header
324 lzma_stream_flags stream_flags
= {
328 return_if_error(lzma_stream_header_encode(
329 &stream_flags
, coder
->buffer
));
331 coder
->buffer_pos
= 0;
332 coder
->buffer_size
= LZMA_STREAM_HEADER_SIZE
;
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 return stream_encoder_update(coder
, allocator
, filters
, NULL
);
341 extern LZMA_API(lzma_ret
)
342 lzma_stream_encoder(lzma_stream
*strm
,
343 const lzma_filter
*filters
, lzma_check check
)
345 lzma_next_strm_init(stream_encoder_init
, strm
, filters
, check
);
347 strm
->internal
->supported_actions
[LZMA_RUN
] = true;
348 strm
->internal
->supported_actions
[LZMA_SYNC_FLUSH
] = true;
349 strm
->internal
->supported_actions
[LZMA_FULL_FLUSH
] = true;
350 strm
->internal
->supported_actions
[LZMA_FULL_BARRIER
] = true;
351 strm
->internal
->supported_actions
[LZMA_FINISH
] = true;