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 } 141