1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file stream_encoder.c
4 /// \brief Encodes .xz Streams
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
13 #include "block_encoder.h"
14 #include "index_encoder.h"
27 /// True if Block encoder has been initialized by
28 /// stream_encoder_init() or stream_encoder_update()
29 /// and thus doesn't need to be initialized in stream_encode().
30 bool block_encoder_is_initialized
;
33 lzma_next_coder block_encoder
;
35 /// Options for the Block encoder
36 lzma_block block_options
;
38 /// The filter chain currently in use
39 lzma_filter filters
[LZMA_FILTERS_MAX
+ 1];
41 /// Index encoder. This is separate from Block encoder, because this
42 /// doesn't take much memory, and when encoding multiple Streams
43 /// with the same encoding options we avoid reallocating memory.
44 lzma_next_coder index_encoder
;
46 /// Index to hold sizes of the Blocks
49 /// Read position in buffer[]
52 /// Total number of bytes in buffer[]
55 /// Buffer to hold Stream Header, Block Header, and Stream Footer.
56 /// Block Header has biggest maximum size.
57 uint8_t buffer
[LZMA_BLOCK_HEADER_SIZE_MAX
];
62 block_encoder_init(lzma_coder
*coder
, const lzma_allocator
*allocator
)
64 // Prepare the Block options. Even though Block encoder doesn't need
65 // compressed_size, uncompressed_size, and header_size to be
66 // initialized, it is a good idea to do it here, because this way
67 // we catch if someone gave us Filter ID that cannot be used in
69 coder
->block_options
.compressed_size
= LZMA_VLI_UNKNOWN
;
70 coder
->block_options
.uncompressed_size
= LZMA_VLI_UNKNOWN
;
72 return_if_error(lzma_block_header_size(&coder
->block_options
));
74 // Initialize the actual Block encoder.
75 return lzma_block_encoder_init(&coder
->block_encoder
, allocator
,
76 &coder
->block_options
);
81 stream_encode(lzma_coder
*coder
, const lzma_allocator
*allocator
,
82 const uint8_t *restrict in
, size_t *restrict in_pos
,
83 size_t in_size
, uint8_t *restrict out
,
84 size_t *restrict out_pos
, size_t out_size
, lzma_action action
)
87 while (*out_pos
< out_size
)
88 switch (coder
->sequence
) {
89 case SEQ_STREAM_HEADER
:
90 case SEQ_BLOCK_HEADER
:
91 case SEQ_STREAM_FOOTER
:
92 lzma_bufcpy(coder
->buffer
, &coder
->buffer_pos
,
93 coder
->buffer_size
, out
, out_pos
, out_size
);
94 if (coder
->buffer_pos
< coder
->buffer_size
)
97 if (coder
->sequence
== SEQ_STREAM_FOOTER
)
98 return LZMA_STREAM_END
;
100 coder
->buffer_pos
= 0;
104 case SEQ_BLOCK_INIT
: {
105 if (*in_pos
== in_size
) {
106 // If we are requested to flush or finish the current
107 // Block, return LZMA_STREAM_END immediately since
108 // there's nothing to do.
109 if (action
!= LZMA_FINISH
)
110 return action
== LZMA_RUN
111 ? LZMA_OK
: LZMA_STREAM_END
;
113 // The application had used LZMA_FULL_FLUSH to finish
114 // the previous Block, but now wants to finish without
115 // encoding new data, or it is simply creating an
116 // empty Stream with no Blocks.
118 // Initialize the Index encoder, and continue to
119 // actually encoding the Index.
120 return_if_error(lzma_index_encoder_init(
121 &coder
->index_encoder
, allocator
,
123 coder
->sequence
= SEQ_INDEX_ENCODE
;
127 // Initialize the Block encoder unless it was already
128 // initialized by stream_encoder_init() or
129 // stream_encoder_update().
130 if (!coder
->block_encoder_is_initialized
)
131 return_if_error(block_encoder_init(coder
, allocator
));
133 // Make it false so that we don't skip the initialization
134 // with the next Block.
135 coder
->block_encoder_is_initialized
= false;
137 // Encode the Block Header. This shouldn't fail since we have
138 // already initialized the Block encoder.
139 if (lzma_block_header_encode(&coder
->block_options
,
140 coder
->buffer
) != LZMA_OK
)
141 return LZMA_PROG_ERROR
;
143 coder
->buffer_size
= coder
->block_options
.header_size
;
144 coder
->sequence
= SEQ_BLOCK_HEADER
;
148 case SEQ_BLOCK_ENCODE
: {
149 static const lzma_action convert
[LZMA_ACTION_MAX
+ 1] = {
157 const lzma_ret ret
= coder
->block_encoder
.code(
158 coder
->block_encoder
.coder
, allocator
,
160 out
, out_pos
, out_size
, convert
[action
]);
161 if (ret
!= LZMA_STREAM_END
|| action
== LZMA_SYNC_FLUSH
)
164 // Add a new Index Record.
165 const lzma_vli unpadded_size
= lzma_block_unpadded_size(
166 &coder
->block_options
);
167 assert(unpadded_size
!= 0);
168 return_if_error(lzma_index_append(coder
->index
, allocator
,
170 coder
->block_options
.uncompressed_size
));
172 coder
->sequence
= SEQ_BLOCK_INIT
;
176 case SEQ_INDEX_ENCODE
: {
177 // Call the Index encoder. It doesn't take any input, so
178 // those pointers can be NULL.
179 const lzma_ret ret
= coder
->index_encoder
.code(
180 coder
->index_encoder
.coder
, allocator
,
182 out
, out_pos
, out_size
, LZMA_RUN
);
183 if (ret
!= LZMA_STREAM_END
)
186 // Encode the Stream Footer into coder->buffer.
187 const lzma_stream_flags stream_flags
= {
189 .backward_size
= lzma_index_size(coder
->index
),
190 .check
= coder
->block_options
.check
,
193 if (lzma_stream_footer_encode(&stream_flags
, coder
->buffer
)
195 return LZMA_PROG_ERROR
;
197 coder
->buffer_size
= LZMA_STREAM_HEADER_SIZE
;
198 coder
->sequence
= SEQ_STREAM_FOOTER
;
204 return LZMA_PROG_ERROR
;
212 stream_encoder_end(lzma_coder
*coder
, const lzma_allocator
*allocator
)
214 lzma_next_end(&coder
->block_encoder
, allocator
);
215 lzma_next_end(&coder
->index_encoder
, allocator
);
216 lzma_index_end(coder
->index
, allocator
);
218 for (size_t i
= 0; coder
->filters
[i
].id
!= LZMA_VLI_UNKNOWN
; ++i
)
219 lzma_free(coder
->filters
[i
].options
, allocator
);
221 lzma_free(coder
, allocator
);
227 stream_encoder_update(lzma_coder
*coder
, const lzma_allocator
*allocator
,
228 const lzma_filter
*filters
,
229 const lzma_filter
*reversed_filters
)
231 if (coder
->sequence
<= SEQ_BLOCK_INIT
) {
232 // There is no incomplete Block waiting to be finished,
233 // thus we can change the whole filter chain. Start by
234 // trying to initialize the Block encoder with the new
235 // chain. This way we detect if the chain is valid.
236 coder
->block_encoder_is_initialized
= false;
237 coder
->block_options
.filters
= (lzma_filter
*)(filters
);
238 const lzma_ret ret
= block_encoder_init(coder
, allocator
);
239 coder
->block_options
.filters
= coder
->filters
;
243 coder
->block_encoder_is_initialized
= true;
245 } else if (coder
->sequence
<= SEQ_BLOCK_ENCODE
) {
246 // We are in the middle of a Block. Try to update only
247 // the filter-specific options.
248 return_if_error(coder
->block_encoder
.update(
249 coder
->block_encoder
.coder
, allocator
,
250 filters
, reversed_filters
));
252 // Trying to update the filter chain when we are already
253 // encoding Index or Stream Footer.
254 return LZMA_PROG_ERROR
;
257 // Free the copy of the old chain and make a copy of the new chain.
258 for (size_t i
= 0; coder
->filters
[i
].id
!= LZMA_VLI_UNKNOWN
; ++i
)
259 lzma_free(coder
->filters
[i
].options
, allocator
);
261 return lzma_filters_copy(filters
, coder
->filters
, allocator
);
266 stream_encoder_init(lzma_next_coder
*next
, const lzma_allocator
*allocator
,
267 const lzma_filter
*filters
, lzma_check check
)
269 lzma_next_coder_init(&stream_encoder_init
, next
, allocator
);
272 return LZMA_PROG_ERROR
;
274 if (next
->coder
== NULL
) {
275 next
->coder
= lzma_alloc(sizeof(lzma_coder
), allocator
);
276 if (next
->coder
== NULL
)
277 return LZMA_MEM_ERROR
;
279 next
->code
= &stream_encode
;
280 next
->end
= &stream_encoder_end
;
281 next
->update
= &stream_encoder_update
;
283 next
->coder
->filters
[0].id
= LZMA_VLI_UNKNOWN
;
284 next
->coder
->block_encoder
= LZMA_NEXT_CODER_INIT
;
285 next
->coder
->index_encoder
= LZMA_NEXT_CODER_INIT
;
286 next
->coder
->index
= NULL
;
289 // Basic initializations
290 next
->coder
->sequence
= SEQ_STREAM_HEADER
;
291 next
->coder
->block_options
.version
= 0;
292 next
->coder
->block_options
.check
= check
;
294 // Initialize the Index
295 lzma_index_end(next
->coder
->index
, allocator
);
296 next
->coder
->index
= lzma_index_init(allocator
);
297 if (next
->coder
->index
== NULL
)
298 return LZMA_MEM_ERROR
;
300 // Encode the Stream Header
301 lzma_stream_flags stream_flags
= {
305 return_if_error(lzma_stream_header_encode(
306 &stream_flags
, next
->coder
->buffer
));
308 next
->coder
->buffer_pos
= 0;
309 next
->coder
->buffer_size
= LZMA_STREAM_HEADER_SIZE
;
311 // Initialize the Block encoder. This way we detect unsupported
312 // filter chains when initializing the Stream encoder instead of
313 // giving an error after Stream Header has already written out.
314 return stream_encoder_update(
315 next
->coder
, allocator
, filters
, NULL
);
319 extern LZMA_API(lzma_ret
)
320 lzma_stream_encoder(lzma_stream
*strm
,
321 const lzma_filter
*filters
, lzma_check check
)
323 lzma_next_strm_init(stream_encoder_init
, strm
, filters
, check
);
325 strm
->internal
->supported_actions
[LZMA_RUN
] = true;
326 strm
->internal
->supported_actions
[LZMA_SYNC_FLUSH
] = true;
327 strm
->internal
->supported_actions
[LZMA_FULL_FLUSH
] = true;
328 strm
->internal
->supported_actions
[LZMA_FULL_BARRIER
] = true;
329 strm
->internal
->supported_actions
[LZMA_FINISH
] = true;