]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/stream_encoder.c
Sort of garbage collection commit. :-| Many things are still
[icculus/xz.git] / src / liblzma / common / stream_encoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       stream_encoder.c
4 /// \brief      Encodes .lzma Streams
5 //
6 //  Copyright (C) 2007-2008 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 "stream_encoder.h"
21 #include "stream_flags_common.h"
22 #include "block_encoder.h"
23 #include "index_encoder.h"
24
25
26 struct lzma_coder_s {
27         enum {
28                 SEQ_STREAM_HEADER,
29                 SEQ_BLOCK_INIT,
30                 SEQ_BLOCK_HEADER,
31                 SEQ_BLOCK_ENCODE,
32                 SEQ_INDEX_ENCODE,
33                 SEQ_STREAM_FOOTER,
34         } sequence;
35
36         /// Block
37         lzma_next_coder block_encoder;
38
39         /// Options for the Block encoder
40         lzma_block block_options;
41
42         /// Index encoder. This is separate from Block encoder, because this
43         /// doesn't take much memory, and when encoding multiple Streams
44         /// with the same encoding options we avoid reallocating memory.
45         lzma_next_coder index_encoder;
46
47         /// Index to hold sizes of the Blocks
48         lzma_index *index;
49
50         /// Read position in buffer[]
51         size_t buffer_pos;
52
53         /// Total number of bytes in buffer[]
54         size_t buffer_size;
55
56         /// Buffer to hold Stream Header, Block Header, and Stream Footer.
57         /// Block Header has biggest maximum size.
58         uint8_t buffer[LZMA_BLOCK_HEADER_SIZE_MAX];
59 };
60
61
62 static lzma_ret
63 block_encoder_init(lzma_coder *coder, lzma_allocator *allocator)
64 {
65         // Prepare the Block options.
66         coder->block_options.compressed_size = LZMA_VLI_VALUE_UNKNOWN;
67         coder->block_options.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
68
69         return_if_error(lzma_block_header_size(&coder->block_options));
70
71         // Initialize the actual Block encoder.
72         return lzma_block_encoder_init(&coder->block_encoder, allocator,
73                         &coder->block_options);
74 }
75
76
77 static lzma_ret
78 stream_encode(lzma_coder *coder, lzma_allocator *allocator,
79                 const uint8_t *restrict in, size_t *restrict in_pos,
80                 size_t in_size, uint8_t *restrict out,
81                 size_t *restrict out_pos, size_t out_size, lzma_action action)
82 {
83         // Main loop
84         while (*out_pos < out_size)
85         switch (coder->sequence) {
86         case SEQ_STREAM_HEADER:
87         case SEQ_BLOCK_HEADER:
88         case SEQ_STREAM_FOOTER:
89                 lzma_bufcpy(coder->buffer, &coder->buffer_pos,
90                                 coder->buffer_size, out, out_pos, out_size);
91                 if (coder->buffer_pos < coder->buffer_size)
92                         return LZMA_OK;
93
94                 if (coder->sequence == SEQ_STREAM_FOOTER)
95                         return LZMA_STREAM_END;
96
97                 coder->buffer_pos = 0;
98                 ++coder->sequence;
99                 break;
100
101         case SEQ_BLOCK_INIT: {
102                 if (*in_pos == in_size) {
103                         // If we are requested to flush or finish the current
104                         // Block, return LZMA_STREAM_END immediatelly since
105                         // there's nothing to do.
106                         if (action != LZMA_FINISH)
107                                 return action == LZMA_RUN
108                                                 ? LZMA_OK : LZMA_STREAM_END;
109
110                         // The application had used LZMA_FULL_FLUSH to finish
111                         // the previous Block, but now wants to finish without
112                         // encoding new data, or it is simply creating an
113                         // empty Stream with no Blocks.
114                         //
115                         // Initialize the Index encoder, and continue to
116                         // actually encoding the Index.
117                         return_if_error(lzma_index_encoder_init(
118                                         &coder->index_encoder, allocator,
119                                         coder->index));
120                         coder->sequence = SEQ_INDEX_ENCODE;
121                         break;
122                 }
123
124                 // Initialize the Block encoder except if this is the first
125                 // Block, because stream_encoder_init() has already
126                 // initialized it.
127                 if (lzma_index_count(coder->index) != 0)
128                         return_if_error(block_encoder_init(coder, allocator));
129
130                 // Encode the Block Header. This shouldn't fail since we have
131                 // already initialized the Block encoder.
132                 if (lzma_block_header_encode(&coder->block_options,
133                                 coder->buffer) != LZMA_OK)
134                         return LZMA_PROG_ERROR;
135
136                 coder->buffer_size = coder->block_options.header_size;
137                 coder->sequence = SEQ_BLOCK_HEADER;
138                 break;
139         }
140
141         case SEQ_BLOCK_ENCODE: {
142                 static const lzma_action convert[4] = {
143                         LZMA_RUN,
144                         LZMA_SYNC_FLUSH,
145                         LZMA_FINISH,
146                         LZMA_FINISH,
147                 };
148
149                 const lzma_ret ret = coder->block_encoder.code(
150                                 coder->block_encoder.coder, allocator,
151                                 in, in_pos, in_size,
152                                 out, out_pos, out_size, convert[action]);
153                 if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
154                         return ret;
155
156                 // Add a new Index Record.
157                 const lzma_vli total_size = lzma_block_total_size_get(
158                                 &coder->block_options);
159                 assert(total_size != 0);
160                 return_if_error(lzma_index_append(coder->index, allocator,
161                                 total_size,
162                                 coder->block_options.uncompressed_size));
163
164                 coder->sequence = SEQ_BLOCK_INIT;
165                 break;
166         }
167
168         case SEQ_INDEX_ENCODE: {
169                 // Call the Index encoder. It doesn't take any input, so
170                 // those pointers can be NULL.
171                 const lzma_ret ret = coder->index_encoder.code(
172                                 coder->index_encoder.coder, allocator,
173                                 NULL, NULL, 0,
174                                 out, out_pos, out_size, LZMA_RUN);
175                 if (ret != LZMA_STREAM_END)
176                         return ret;
177
178                 // Encode the Stream Footer into coder->buffer.
179                 const lzma_stream_flags stream_flags = {
180                         .backward_size = lzma_index_size(coder->index),
181                         .check = coder->block_options.check,
182                 };
183
184                 if (lzma_stream_footer_encode(&stream_flags, coder->buffer)
185                                 != LZMA_OK)
186                         return LZMA_PROG_ERROR;
187
188                 coder->buffer_size = LZMA_STREAM_HEADER_SIZE;
189                 coder->sequence = SEQ_STREAM_FOOTER;
190                 break;
191         }
192
193         default:
194                 assert(0);
195                 return LZMA_PROG_ERROR;
196         }
197
198         return LZMA_OK;
199 }
200
201
202 static void
203 stream_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
204 {
205         lzma_next_end(&coder->block_encoder, allocator);
206         lzma_next_end(&coder->index_encoder, allocator);
207         lzma_index_end(coder->index, allocator);
208         lzma_free(coder, allocator);
209         return;
210 }
211
212
213 extern lzma_ret
214 lzma_stream_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
215                 const lzma_filter *filters, lzma_check check)
216 {
217         lzma_next_coder_init(lzma_stream_encoder_init, next, allocator);
218
219         if (filters == NULL)
220                 return LZMA_PROG_ERROR;
221
222         if (next->coder == NULL) {
223                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
224                 if (next->coder == NULL)
225                         return LZMA_MEM_ERROR;
226
227                 next->code = &stream_encode;
228                 next->end = &stream_encoder_end;
229
230                 next->coder->block_encoder = LZMA_NEXT_CODER_INIT;
231                 next->coder->index_encoder = LZMA_NEXT_CODER_INIT;
232                 next->coder->index = NULL;
233         }
234
235         // Basic initializations
236         next->coder->sequence = SEQ_STREAM_HEADER;
237         next->coder->block_options.check = check;
238         next->coder->block_options.filters = (lzma_filter *)(filters);
239
240         // Initialize the Index
241         next->coder->index = lzma_index_init(next->coder->index, allocator);
242         if (next->coder->index == NULL)
243                 return LZMA_MEM_ERROR;
244
245         // Encode the Stream Header
246         lzma_stream_flags stream_flags = {
247                 .check = check,
248         };
249         return_if_error(lzma_stream_header_encode(
250                         &stream_flags, next->coder->buffer));
251
252         next->coder->buffer_pos = 0;
253         next->coder->buffer_size = LZMA_STREAM_HEADER_SIZE;
254
255         // Initialize the Block encoder. This way we detect if the given
256         // filters are supported by the current liblzma build, and the
257         // application doesn't need to keep the filters structure available
258         // unless it is going to use LZMA_FULL_FLUSH.
259         return block_encoder_init(next->coder, allocator);
260 }
261
262
263 extern LZMA_API lzma_ret
264 lzma_stream_encoder(lzma_stream *strm,
265                 const lzma_filter *filters, lzma_check check)
266 {
267         lzma_next_strm_init(lzma_stream_encoder_init, strm, filters, check);
268
269         strm->internal->supported_actions[LZMA_RUN] = true;
270         strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
271         strm->internal->supported_actions[LZMA_FULL_FLUSH] = true;
272         strm->internal->supported_actions[LZMA_FINISH] = true;
273
274         return LZMA_OK;
275 }