summaryrefslogtreecommitdiffstats
path: root/src/liblzma/delta/delta_encoder.c
blob: ba4a50b1f42d9e8c80d4e20e35a22fa292c05c9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: 0BSD

///////////////////////////////////////////////////////////////////////////////
//
/// \file       delta_encoder.c
/// \brief      Delta filter encoder
//
//  Author:     Lasse Collin
//
///////////////////////////////////////////////////////////////////////////////

#include "delta_encoder.h"
#include "delta_private.h"


/// Copies and encodes the data at the same time. This is used when Delta
/// is the first filter in the chain (and thus the last filter in the
/// encoder's filter stack).
static void
copy_and_encode(lzma_delta_coder *coder,
		const uint8_t *restrict in, uint8_t *restrict out, size_t size)
{
	const size_t distance = coder->distance;

	for (size_t i = 0; i < size; ++i) {
		const uint8_t tmp = coder->history[
				(distance + coder->pos) & 0xFF];
		coder->history[coder->pos-- & 0xFF] = in[i];
		out[i] = in[i] - tmp;
	}
}


/// Encodes the data in place. This is used when we are the last filter
/// in the chain (and thus non-last filter in the encoder's filter stack).
static void
encode_in_place(lzma_delta_coder *coder, uint8_t *buffer, size_t size)
{
	const size_t distance = coder->distance;

	for (size_t i = 0; i < size; ++i) {
		const uint8_t tmp = coder->history[
				(distance + coder->pos) & 0xFF];
		coder->history[coder->pos-- & 0xFF] = buffer[i];
		buffer[i] -= tmp;
	}
}


static lzma_ret
delta_encode(void *coder_ptr, const lzma_allocator *allocator,
		const uint8_t *restrict in, size_t *restrict in_pos,
		size_t in_size, uint8_t *restrict out,
		size_t *restrict out_pos, size_t out_size, lzma_action action)
{
	lzma_delta_coder *coder = coder_ptr;

	lzma_ret ret;

	if (coder->next.code == NULL) {
		const size_t in_avail = in_size - *in_pos;
		const size_t out_avail = out_size - *out_pos;
		const size_t size = my_min(in_avail, out_avail);

		// in and out might be NULL. In such cases size == 0.
		// Null pointer + 0 is undefined behavior so skip
		// the call in that case as it would do nothing anyway.
		if (size > 0)
			copy_and_encode(coder, in + *in_pos, out + *out_pos,
					size);

		*in_pos += size;
		*out_pos += size;

		ret = action != LZMA_RUN && *in_pos == in_size
				? LZMA_STREAM_END : LZMA_OK;

	} else {
		const size_t out_start = *out_pos;

		ret = coder->next.code(coder->next.coder, allocator,
				in, in_pos, in_size, out, out_pos, out_size,
				action);

		// Like above, avoid null pointer + 0.
		const size_t size = *out_pos - out_start;
		if (size > 0)
			encode_in_place(coder, out + out_start, size);
	}

	return ret;
}


static lzma_ret
delta_encoder_update(void *coder_ptr, const lzma_allocator *allocator,
		const lzma_filter *filters_null lzma_attribute((__unused__)),
		const lzma_filter *reversed_filters)
{
	lzma_delta_coder *coder = coder_ptr;

	// Delta doesn't and will never support changing the options in
	// the middle of encoding. If the app tries to change them, we
	// simply ignore them.
	return lzma_next_filter_update(
			&coder->next, allocator, reversed_filters + 1);
}


extern lzma_ret
lzma_delta_encoder_init(lzma_next_coder *next, const lzma_allocator *allocator,
		const lzma_filter_info *filters)
{
	next->code = &delta_encode;
	next->update = &delta_encoder_update;
	return lzma_delta_coder_init(next, allocator, filters);
}


extern lzma_ret
lzma_delta_props_encode(const void *options, uint8_t *out)
{
	// The caller must have already validated the options, so it's
	// LZMA_PROG_ERROR if they are invalid.
	if (lzma_delta_coder_memusage(options) == UINT64_MAX)
		return LZMA_PROG_ERROR;

	const lzma_options_delta *opt = options;
	out[0] = opt->dist - LZMA_DELTA_DIST_MIN;

	return LZMA_OK;
}