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