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