xref: /freebsd/contrib/xz/src/liblzma/common/microlzma_encoder.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 // SPDX-License-Identifier: 0BSD
2 
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file       microlzma_encoder.c
6 /// \brief      Encode into MicroLZMA format
7 //
8 //  Author:     Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
11 
12 #include "lzma_encoder.h"
13 
14 
15 typedef struct {
16 	/// LZMA1 encoder
17 	lzma_next_coder lzma;
18 
19 	/// LZMA properties byte (lc/lp/pb)
20 	uint8_t props;
21 } lzma_microlzma_coder;
22 
23 
24 static lzma_ret
25 microlzma_encode(void *coder_ptr, const lzma_allocator *allocator,
26 		const uint8_t *restrict in, size_t *restrict in_pos,
27 		size_t in_size, uint8_t *restrict out,
28 		size_t *restrict out_pos, size_t out_size, lzma_action action)
29 {
30 	lzma_microlzma_coder *coder = coder_ptr;
31 
32 	// Remember *out_pos so that we can overwrite the first byte with
33 	// the LZMA properties byte.
34 	const size_t out_start = *out_pos;
35 
36 	// Remember *in_pos so that we can set it based on how many
37 	// uncompressed bytes were actually encoded.
38 	const size_t in_start = *in_pos;
39 
40 	// Set the output size limit based on the available output space.
41 	// We know that the encoder supports set_out_limit() so
42 	// LZMA_OPTIONS_ERROR isn't possible. LZMA_BUF_ERROR is possible
43 	// but lzma_code() has an assertion to not allow it to be returned
44 	// from here and I don't want to change that for now, so
45 	// LZMA_BUF_ERROR becomes LZMA_PROG_ERROR.
46 	uint64_t uncomp_size;
47 	if (coder->lzma.set_out_limit(coder->lzma.coder,
48 			&uncomp_size, out_size - *out_pos) != LZMA_OK)
49 		return LZMA_PROG_ERROR;
50 
51 	// set_out_limit fails if this isn't true.
52 	assert(out_size - *out_pos >= 6);
53 
54 	// Encode as much as possible.
55 	const lzma_ret ret = coder->lzma.code(coder->lzma.coder, allocator,
56 			in, in_pos, in_size, out, out_pos, out_size, action);
57 
58 	if (ret != LZMA_STREAM_END) {
59 		if (ret == LZMA_OK) {
60 			assert(0);
61 			return LZMA_PROG_ERROR;
62 		}
63 
64 		return ret;
65 	}
66 
67 	// The first output byte is bitwise-negation of the properties byte.
68 	// We know that there is space for this byte because set_out_limit
69 	// and the actual encoding succeeded.
70 	out[out_start] = (uint8_t)(~coder->props);
71 
72 	// The LZMA encoder likely read more input than it was able to encode.
73 	// Set *in_pos based on uncomp_size.
74 	assert(uncomp_size <= in_size - in_start);
75 	*in_pos = in_start + (size_t)(uncomp_size);
76 
77 	return ret;
78 }
79 
80 
81 static void
82 microlzma_encoder_end(void *coder_ptr, const lzma_allocator *allocator)
83 {
84 	lzma_microlzma_coder *coder = coder_ptr;
85 	lzma_next_end(&coder->lzma, allocator);
86 	lzma_free(coder, allocator);
87 	return;
88 }
89 
90 
91 static lzma_ret
92 microlzma_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
93 		const lzma_options_lzma *options)
94 {
95 	lzma_next_coder_init(&microlzma_encoder_init, next, allocator);
96 
97 	lzma_microlzma_coder *coder = next->coder;
98 
99 	if (coder == NULL) {
100 		coder = lzma_alloc(sizeof(lzma_microlzma_coder), allocator);
101 		if (coder == NULL)
102 			return LZMA_MEM_ERROR;
103 
104 		next->coder = coder;
105 		next->code = &microlzma_encode;
106 		next->end = &microlzma_encoder_end;
107 
108 		coder->lzma = LZMA_NEXT_CODER_INIT;
109 	}
110 
111 	// Encode the properties byte. Bitwise-negation of it will be the
112 	// first output byte.
113 	if (lzma_lzma_lclppb_encode(options, &coder->props))
114 		return LZMA_OPTIONS_ERROR;
115 
116 	// Initialize the LZMA encoder.
117 	const lzma_filter_info filters[2] = {
118 		{
119 			.id = LZMA_FILTER_LZMA1,
120 			.init = &lzma_lzma_encoder_init,
121 			.options = (void *)(options),
122 		}, {
123 			.init = NULL,
124 		}
125 	};
126 
127 	return lzma_next_filter_init(&coder->lzma, allocator, filters);
128 }
129 
130 
131 extern LZMA_API(lzma_ret)
132 lzma_microlzma_encoder(lzma_stream *strm, const lzma_options_lzma *options)
133 {
134 	lzma_next_strm_init(microlzma_encoder_init, strm, options);
135 
136 	strm->internal->supported_actions[LZMA_FINISH] = true;
137 
138 	return LZMA_OK;
139 
140 }
141