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