]> icculus.org git repositories - icculus/xz.git/blob - src/liblzma/common/block_encoder.c
Update the code to mostly match the new simpler file format
[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 "raw_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_options_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 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[coder->check_pos];
151                 ++*out_pos;
152
153                 if (++coder->check_pos
154                                 == lzma_check_sizes[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_coder_end(&coder->next, allocator);
171         lzma_free(coder, allocator);
172         return;
173 }
174
175
176 static lzma_ret
177 block_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
178                 lzma_options_block *options)
179 {
180         // While lzma_block_total_size_get() is meant to calculate the Total
181         // Size, it also validates the options excluding the filters.
182         if (lzma_block_total_size_get(options) == 0)
183                 return LZMA_PROG_ERROR;
184
185         // Allocate and initialize *next->coder if needed.
186         if (next->coder == NULL) {
187                 next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
188                 if (next->coder == NULL)
189                         return LZMA_MEM_ERROR;
190
191                 next->code = &block_encode;
192                 next->end = &block_encoder_end;
193                 next->coder->next = LZMA_NEXT_CODER_INIT;
194         }
195
196         // Basic initializations
197         next->coder->sequence = SEQ_CODE;
198         next->coder->options = options;
199         next->coder->compressed_size = 0;
200         next->coder->uncompressed_size = 0;
201
202         // Initialize the check
203         next->coder->check_pos = 0;
204         return_if_error(lzma_check_init(&next->coder->check, options->check));
205
206         // Initialize the requested filters.
207         return lzma_raw_encoder_init(&next->coder->next, allocator,
208                         options->filters);
209 }
210
211
212 extern lzma_ret
213 lzma_block_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
214                 lzma_options_block *options)
215 {
216         lzma_next_coder_init(block_encoder_init, next, allocator, options);
217 }
218
219
220 extern LZMA_API lzma_ret
221 lzma_block_encoder(lzma_stream *strm, lzma_options_block *options)
222 {
223         lzma_next_strm_init(strm, block_encoder_init, options);
224
225         strm->internal->supported_actions[LZMA_RUN] = true;
226         strm->internal->supported_actions[LZMA_FINISH] = true;
227
228         return LZMA_OK;
229 }