1 /////////////////////////////////////////////////////////////////////////////// 2 // 3 /// \file delta_encoder.c 4 /// \brief Delta filter encoder 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 "delta_encoder.h" 14 #include "delta_private.h" 15 16 17 /// Copies and encodes the data at the same time. This is used when Delta 18 /// is the first filter in the chain (and thus the last filter in the 19 /// encoder's filter stack). 20 static void 21 copy_and_encode(lzma_delta_coder *coder, 22 const uint8_t *restrict in, uint8_t *restrict out, size_t size) 23 { 24 const size_t distance = coder->distance; 25 26 for (size_t i = 0; i < size; ++i) { 27 const uint8_t tmp = coder->history[ 28 (distance + coder->pos) & 0xFF]; 29 coder->history[coder->pos-- & 0xFF] = in[i]; 30 out[i] = in[i] - tmp; 31 } 32 } 33 34 35 /// Encodes the data in place. This is used when we are the last filter 36 /// in the chain (and thus non-last filter in the encoder's filter stack). 37 static void 38 encode_in_place(lzma_delta_coder *coder, uint8_t *buffer, size_t size) 39 { 40 const size_t distance = coder->distance; 41 42 for (size_t i = 0; i < size; ++i) { 43 const uint8_t tmp = coder->history[ 44 (distance + coder->pos) & 0xFF]; 45 coder->history[coder->pos-- & 0xFF] = buffer[i]; 46 buffer[i] -= tmp; 47 } 48 } 49 50 51 static lzma_ret 52 delta_encode(void *coder_ptr, const lzma_allocator *allocator, 53 const uint8_t *restrict in, size_t *restrict in_pos, 54 size_t in_size, uint8_t *restrict out, 55 size_t *restrict out_pos, size_t out_size, lzma_action action) 56 { 57 lzma_delta_coder *coder = coder_ptr; 58 59 lzma_ret ret; 60 61 if (coder->next.code == NULL) { 62 const size_t in_avail = in_size - *in_pos; 63 const size_t out_avail = out_size - *out_pos; 64 const size_t size = my_min(in_avail, out_avail); 65 66 // in and out might be NULL. In such cases size == 0. 67 // Null pointer + 0 is undefined behavior so skip 68 // the call in that case as it would do nothing anyway. 69 if (size > 0) 70 copy_and_encode(coder, in + *in_pos, out + *out_pos, 71 size); 72 73 *in_pos += size; 74 *out_pos += size; 75 76 ret = action != LZMA_RUN && *in_pos == in_size 77 ? LZMA_STREAM_END : LZMA_OK; 78 79 } else { 80 const size_t out_start = *out_pos; 81 82 ret = coder->next.code(coder->next.coder, allocator, 83 in, in_pos, in_size, out, out_pos, out_size, 84 action); 85 86 // Like above, avoid null pointer + 0. 87 const size_t size = *out_pos - out_start; 88 if (size > 0) 89 encode_in_place(coder, out + out_start, size); 90 } 91 92 return ret; 93 } 94 95 96 static lzma_ret 97 delta_encoder_update(void *coder_ptr, const lzma_allocator *allocator, 98 const lzma_filter *filters_null lzma_attribute((__unused__)), 99 const lzma_filter *reversed_filters) 100 { 101 lzma_delta_coder *coder = coder_ptr; 102 103 // Delta doesn't and will never support changing the options in 104 // the middle of encoding. If the app tries to change them, we 105 // simply ignore them. 106 return lzma_next_filter_update( 107 &coder->next, allocator, reversed_filters + 1); 108 } 109 110 111 extern lzma_ret 112 lzma_delta_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator, 113 const lzma_filter_info *filters) 114 { 115 next->code = &delta_encode; 116 next->update = &delta_encoder_update; 117 return lzma_delta_coder_init(next, allocator, filters); 118 } 119 120 121 extern lzma_ret 122 lzma_delta_props_encode(const void *options, uint8_t *out) 123 { 124 // The caller must have already validated the options, so it's 125 // LZMA_PROG_ERROR if they are invalid. 126 if (lzma_delta_coder_memusage(options) == UINT64_MAX) 127 return LZMA_PROG_ERROR; 128 129 const lzma_options_delta *opt = options; 130 out[0] = opt->dist - LZMA_DELTA_DIST_MIN; 131 132 return LZMA_OK; 133 } 134