xref: /freebsd/contrib/xz/src/liblzma/common/block_encoder.c (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       block_encoder.c
4 /// \brief      Encodes .xz Blocks
5 //
6 //  Author:     Lasse Collin
7 //
8 //  This file has been put into the public domain.
9 //  You can do whatever you want with this file.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12 
13 #include "block_encoder.h"
14 #include "filter_encoder.h"
15 #include "check.h"
16 
17 
18 typedef struct {
19 	/// The filters in the chain; initialized with lzma_raw_decoder_init().
20 	lzma_next_coder next;
21 
22 	/// Encoding options; we also write Unpadded Size, Compressed Size,
23 	/// and Uncompressed Size back to this structure when the encoding
24 	/// has been finished.
25 	lzma_block *block;
26 
27 	enum {
28 		SEQ_CODE,
29 		SEQ_PADDING,
30 		SEQ_CHECK,
31 	} sequence;
32 
33 	/// Compressed Size calculated while encoding
34 	lzma_vli compressed_size;
35 
36 	/// Uncompressed Size calculated while encoding
37 	lzma_vli uncompressed_size;
38 
39 	/// Position in the Check field
40 	size_t pos;
41 
42 	/// Check of the uncompressed data
43 	lzma_check_state check;
44 } lzma_block_coder;
45 
46 
47 static lzma_ret
48 block_encode(void *coder_ptr, const lzma_allocator *allocator,
49 		const uint8_t *restrict in, size_t *restrict in_pos,
50 		size_t in_size, uint8_t *restrict out,
51 		size_t *restrict out_pos, size_t out_size, lzma_action action)
52 {
53 	lzma_block_coder *coder = coder_ptr;
54 
55 	// Check that our amount of input stays in proper limits.
56 	if (LZMA_VLI_MAX - coder->uncompressed_size < in_size - *in_pos)
57 		return LZMA_DATA_ERROR;
58 
59 	switch (coder->sequence) {
60 	case SEQ_CODE: {
61 		const size_t in_start = *in_pos;
62 		const size_t out_start = *out_pos;
63 
64 		const lzma_ret ret = coder->next.code(coder->next.coder,
65 				allocator, in, in_pos, in_size,
66 				out, out_pos, out_size, action);
67 
68 		const size_t in_used = *in_pos - in_start;
69 		const size_t out_used = *out_pos - out_start;
70 
71 		if (COMPRESSED_SIZE_MAX - coder->compressed_size < out_used)
72 			return LZMA_DATA_ERROR;
73 
74 		coder->compressed_size += out_used;
75 
76 		// No need to check for overflow because we have already
77 		// checked it at the beginning of this function.
78 		coder->uncompressed_size += in_used;
79 
80 		// Call lzma_check_update() only if input was consumed. This
81 		// avoids null pointer + 0 (undefined behavior) when in == 0.
82 		if (in_used > 0)
83 			lzma_check_update(&coder->check, coder->block->check,
84 					in + in_start, in_used);
85 
86 		if (ret != LZMA_STREAM_END || action == LZMA_SYNC_FLUSH)
87 			return ret;
88 
89 		assert(*in_pos == in_size);
90 		assert(action == LZMA_FINISH);
91 
92 		// Copy the values into coder->block. The caller
93 		// may use this information to construct Index.
94 		coder->block->compressed_size = coder->compressed_size;
95 		coder->block->uncompressed_size = coder->uncompressed_size;
96 
97 		coder->sequence = SEQ_PADDING;
98 	}
99 
100 	// Fall through
101 
102 	case SEQ_PADDING:
103 		// Pad Compressed Data to a multiple of four bytes. We can
104 		// use coder->compressed_size for this since we don't need
105 		// it for anything else anymore.
106 		while (coder->compressed_size & 3) {
107 			if (*out_pos >= out_size)
108 				return LZMA_OK;
109 
110 			out[*out_pos] = 0x00;
111 			++*out_pos;
112 			++coder->compressed_size;
113 		}
114 
115 		if (coder->block->check == LZMA_CHECK_NONE)
116 			return LZMA_STREAM_END;
117 
118 		lzma_check_finish(&coder->check, coder->block->check);
119 
120 		coder->sequence = SEQ_CHECK;
121 
122 	// Fall through
123 
124 	case SEQ_CHECK: {
125 		const size_t check_size = lzma_check_size(coder->block->check);
126 		lzma_bufcpy(coder->check.buffer.u8, &coder->pos, check_size,
127 				out, out_pos, out_size);
128 		if (coder->pos < check_size)
129 			return LZMA_OK;
130 
131 		memcpy(coder->block->raw_check, coder->check.buffer.u8,
132 				check_size);
133 		return LZMA_STREAM_END;
134 	}
135 	}
136 
137 	return LZMA_PROG_ERROR;
138 }
139 
140 
141 static void
142 block_encoder_end(void *coder_ptr, const lzma_allocator *allocator)
143 {
144 	lzma_block_coder *coder = coder_ptr;
145 	lzma_next_end(&coder->next, allocator);
146 	lzma_free(coder, allocator);
147 	return;
148 }
149 
150 
151 static lzma_ret
152 block_encoder_update(void *coder_ptr, const lzma_allocator *allocator,
153 		const lzma_filter *filters lzma_attribute((__unused__)),
154 		const lzma_filter *reversed_filters)
155 {
156 	lzma_block_coder *coder = coder_ptr;
157 
158 	if (coder->sequence != SEQ_CODE)
159 		return LZMA_PROG_ERROR;
160 
161 	return lzma_next_filter_update(
162 			&coder->next, allocator, reversed_filters);
163 }
164 
165 
166 extern lzma_ret
167 lzma_block_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
168 		lzma_block *block)
169 {
170 	lzma_next_coder_init(&lzma_block_encoder_init, next, allocator);
171 
172 	if (block == NULL)
173 		return LZMA_PROG_ERROR;
174 
175 	// The contents of the structure may depend on the version so
176 	// check the version first.
177 	if (block->version > 1)
178 		return LZMA_OPTIONS_ERROR;
179 
180 	// If the Check ID is not supported, we cannot calculate the check and
181 	// thus not create a proper Block.
182 	if ((unsigned int)(block->check) > LZMA_CHECK_ID_MAX)
183 		return LZMA_PROG_ERROR;
184 
185 	if (!lzma_check_is_supported(block->check))
186 		return LZMA_UNSUPPORTED_CHECK;
187 
188 	// Allocate and initialize *next->coder if needed.
189 	lzma_block_coder *coder = next->coder;
190 	if (coder == NULL) {
191 		coder = lzma_alloc(sizeof(lzma_block_coder), allocator);
192 		if (coder == NULL)
193 			return LZMA_MEM_ERROR;
194 
195 		next->coder = coder;
196 		next->code = &block_encode;
197 		next->end = &block_encoder_end;
198 		next->update = &block_encoder_update;
199 		coder->next = LZMA_NEXT_CODER_INIT;
200 	}
201 
202 	// Basic initializations
203 	coder->sequence = SEQ_CODE;
204 	coder->block = block;
205 	coder->compressed_size = 0;
206 	coder->uncompressed_size = 0;
207 	coder->pos = 0;
208 
209 	// Initialize the check
210 	lzma_check_init(&coder->check, block->check);
211 
212 	// Initialize the requested filters.
213 	return lzma_raw_encoder_init(&coder->next, allocator, block->filters);
214 }
215 
216 
217 extern LZMA_API(lzma_ret)
218 lzma_block_encoder(lzma_stream *strm, lzma_block *block)
219 {
220 	lzma_next_strm_init(lzma_block_encoder_init, strm, block);
221 
222 	strm->internal->supported_actions[LZMA_RUN] = true;
223 	strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
224 	strm->internal->supported_actions[LZMA_FINISH] = true;
225 
226 	return LZMA_OK;
227 }
228