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(µlzma_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 = µlzma_encode;
|
---|
106 | next->end = µlzma_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 | }
|
---|