]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/block_encoder.c
Sort of garbage collection commit. :-| Many things are still
[icculus/xz.git] / src / liblzma / common / block_encoder.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       block_encoder.c
4 /// \brief      Encodes .lzma Blocks
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 "block_encoder.h"
21 #include "block_private.h"
22 #include "filter_encoder.h"
23 #include "check.h"
24
25
26 struct lzma_coder_s {
27         /// The filters in the chain; initialized with lzma_raw_decoder_init().
28         lzma_next_coder next;
29
30         /// Encoding options; we also write Total Size, Compressed Size, and
31         /// Uncompressed Size back to this structure when the encoding has
32         /// been finished.
33         lzma_block *options;
34
35         enum {
36                 SEQ_CODE,
37                 SEQ_PADDING,
38                 SEQ_CHECK,
39         } sequence;
40
41         /// Compressed Size calculated while encoding
42         lzma_vli compressed_size;
43
44         /// Uncompressed Size calculated while encoding
45         lzma_vli uncompressed_size;
46
47         /// Position when writing out the Check field
48         size_t check_pos;
49
50         /// Check of the uncompressed data
51         lzma_check_state check;
52 };
53
54
55 static lzma_ret
56 block_encode(lzma_coder *coder, lzma_allocator *allocator,
57                 const uint8_t *restrict in, size_t *restrict in_pos,
58                 size_t in_size, uint8_t *restrict out,
59                 size_t *restrict out_pos, size_t out_size, lzma_action action)
60 {
61         // Check that our amount of input stays in proper limits.
62         if (coder->options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
63                 if (action == LZMA_FINISH) {
64                         if (coder->options->uncompressed_size
65                                         - coder->uncompressed_size
66                                         != (lzma_vli)(in_size - *in_pos))
67                                 return LZMA_PROG_ERROR;
68                 } else {
69                         if (coder->options->uncompressed_size
70                                         - coder->uncompressed_size
71                                         <  (lzma_vli)(in_size - *in_pos))
72                                 return LZMA_PROG_ERROR;
73                 }
74         } else if (LZMA_VLI_VALUE_MAX - coder->uncompressed_size
75                         < (lzma_vli)(in_size - *in_pos)) {
76                 return LZMA_PROG_ERROR;
77         }
78
79         // Main loop
80         while (*out_pos < out_size
81                         && (*in_pos < in_size || action != LZMA_RUN))
82         switch (coder->sequence) {
83         case SEQ_CODE: {
84                 const size_t in_start = *in_pos;
85                 const size_t out_start = *out_pos;
86
87                 const lzma_ret ret = coder->next.code(coder->next.coder,
88                                 allocator, in, in_pos, in_size,
89                                 out, out_pos, out_size, action);
90
91                 const size_t in_used = *in_pos - in_start;
92                 const size_t out_used = *out_pos - out_start;
93
94                 // FIXME We must also check that Total Size doesn't get
95                 // too big.
96                 if (update_size(&coder->compressed_size, out_used,
97                                 coder->options->compressed_size))
98                         return LZMA_DATA_ERROR;
99
100                 // No need to check for overflow because we have already
101                 // checked it at the beginning of this function.
102                 coder->uncompressed_size += in_used;
103
104                 lzma_check_update(&coder->check, coder->options->check,
105                                 in + in_start, in_used);
106
107                 if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
108                         return ret;
109
110                 assert(*in_pos == in_size);
111                 coder->sequence = SEQ_PADDING;
112                 break;
113         }
114
115         case SEQ_PADDING:
116                 // Pad Compressed Data to a multiple of four bytes.
117                 if (coder->compressed_size & 3) {
118                         out[*out_pos] = 0x00;
119                         ++*out_pos;
120
121                         if (update_size(&coder->compressed_size, 1,
122                                         coder->options->compressed_size))
123                                 return LZMA_DATA_ERROR;
124
125                         break;
126                 }
127
128                 // Compressed and Uncompressed Sizes are now at their final
129                 // values. Verify that they match the values given to us.
130                 if (!is_size_valid(coder->compressed_size,
131                                         coder->options->compressed_size)
132                                 || !is_size_valid(coder->uncompressed_size,
133                                         coder->options->uncompressed_size))
134                         return LZMA_DATA_ERROR;
135
136                 // Copy the values into coder->options. The caller
137                 // may use this information to construct Index.
138                 coder->options->compressed_size = coder->compressed_size;
139                 coder->options->uncompressed_size = coder->uncompressed_size;
140
141                 if (coder->options->check == LZMA_CHECK_NONE)
142                         return LZMA_STREAM_END;
143
144                 lzma_check_finish(&coder->check, coder->options->check);
145                 coder->sequence = SEQ_CHECK;
146
147         // Fall through
148
149         case SEQ_CHECK:
150                 out[*out_pos] = coder->check.buffer.u8[coder->check_pos];
151                 ++*out_pos;
152
153                 if (++coder->check_pos
154                                 == lzma_check_size(coder->options->check))
155                         return LZMA_STREAM_END;
156
157                 break;
158
159         default:
160                 return LZMA_PROG_ERROR;
161         }
162
163         return LZMA_OK;
164 }
165
166
167 static void
168 block_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
169 {
170         lzma_next_end(&coder->next, allocator);
171         lzma_free(coder, allocator);
172         return;
173 }
174
175
176 extern lzma_ret
177 lzma_block_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
178                 lzma_block *options)
179 {
180         lzma_next_coder_init(lzma_block_encoder_init, next, allocator);
181
182         // While lzma_block_total_size_get() is meant to calculate the Total
183         // Size, it also validates the options excluding the filters.
184         if (lzma_block_total_size_get(options) == 0)
185                 return LZMA_PROG_ERROR;
186
187         // If the Check ID is not supported, we cannot calculate the check and
188         // thus not create a proper Block.
189         if ((unsigned)(options->check) > LZMA_CHECK_ID_MAX)
190                 return LZMA_PROG_ERROR;
191
192         if (!lzma_check_is_supported(options->check))
193                 return LZMA_UNSUPPORTED_CHECK;
194
195         // Allocate and initialize *next->coder if needed.
196         if (next->coder == NULL) {
197                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
198                 if (next->coder == NULL)
199                         return LZMA_MEM_ERROR;
200
201                 next->code = &block_encode;
202                 next->end = &block_encoder_end;
203                 next->coder->next = LZMA_NEXT_CODER_INIT;
204         }
205
206         // Basic initializations
207         next->coder->sequence = SEQ_CODE;
208         next->coder->options = options;
209         next->coder->compressed_size = 0;
210         next->coder->uncompressed_size = 0;
211
212         // Initialize the check
213         next->coder->check_pos = 0;
214         lzma_check_init(&next->coder->check, options->check);
215
216         // Initialize the requested filters.
217         return lzma_raw_encoder_init(&next->coder->next, allocator,
218                         options->filters);
219 }
220
221
222 extern LZMA_API lzma_ret
223 lzma_block_encoder(lzma_stream *strm, lzma_block *options)
224 {
225         lzma_next_strm_init(lzma_block_encoder_init, strm, options);
226
227         strm->internal->supported_actions[LZMA_RUN] = true;
228         strm->internal->supported_actions[LZMA_FINISH] = true;
229
230         return LZMA_OK;
231 }