/src/vlc/modules/demux/mkv/lzokay.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018 Jack Andersen |
3 | | * SPDX-License-Identifier: MIT |
4 | | * https://github.com/AxioDL/lzokay |
5 | | */ |
6 | | |
7 | | #ifdef HAVE_CONFIG_H |
8 | | # include "config.h" |
9 | | #endif |
10 | | |
11 | | #include "lzokay.hpp" |
12 | | #include <cstring> |
13 | | #include <limits> |
14 | | |
15 | | /* |
16 | | * Based on documentation from the Linux sources: Documentation/lzo.txt |
17 | | * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/lzo.txt |
18 | | */ |
19 | | |
20 | | namespace lzokay { |
21 | | |
22 | | static inline uint16_t get_le16(const void *p) |
23 | 27.6k | { |
24 | 27.6k | uint16_t val; |
25 | | |
26 | 27.6k | memcpy (&val, p, sizeof (val)); |
27 | | #ifdef WORDS_BIGENDIAN |
28 | | val = (val << 8) | (val >> 8); |
29 | | #endif |
30 | 27.6k | return val; |
31 | 27.6k | } |
32 | | |
33 | | constexpr std::size_t Max255Count = std::numeric_limits<size_t>::max() / 255 - 2; |
34 | | |
35 | | #define NEEDS_IN(count) \ |
36 | 296k | if (inp + (count) > inp_end) { \ |
37 | 312 | dst_size = outp - dst; \ |
38 | 312 | return EResult::InputOverrun; \ |
39 | 312 | } |
40 | | |
41 | | #define NEEDS_OUT(count) \ |
42 | 101k | if (outp + (count) > outp_end) { \ |
43 | 2.24k | dst_size = outp - dst; \ |
44 | 2.24k | return EResult::OutputOverrun; \ |
45 | 2.24k | } |
46 | | |
47 | | #define CONSUME_ZERO_BYTE_LENGTH \ |
48 | 24.8k | std::size_t offset; \ |
49 | 24.8k | { \ |
50 | 24.8k | const uint8_t *old_inp = inp; \ |
51 | 44.0k | while (inp < inp_end && *inp == 0) ++inp; \ |
52 | 24.8k | if (inp >= inp_end) { \ |
53 | 15 | dst_size = outp - dst; \ |
54 | 15 | return EResult::InputOverrun; \ |
55 | 15 | } \ |
56 | 24.8k | offset = inp - old_inp; \ |
57 | 24.8k | if (offset > Max255Count) { \ |
58 | 0 | dst_size = outp - dst; \ |
59 | 0 | return EResult::Error; \ |
60 | 0 | } \ |
61 | 24.8k | } |
62 | | |
63 | | // constexpr uint32_t M1Marker = 0x0; |
64 | | // constexpr uint32_t M2Marker = 0x40; |
65 | | constexpr uint32_t M3Marker = 0x20; |
66 | | constexpr uint32_t M4Marker = 0x10; |
67 | | |
68 | | EResult decompress(const uint8_t* src, std::size_t src_size, |
69 | | uint8_t* dst, std::size_t init_dst_size, |
70 | 4.08k | std::size_t& dst_size) { |
71 | 4.08k | dst_size = init_dst_size; |
72 | | |
73 | 4.08k | if (src_size < 3) { |
74 | 137 | dst_size = 0; |
75 | 137 | return EResult::InputOverrun; |
76 | 137 | } |
77 | | |
78 | 3.94k | const uint8_t* inp = src; |
79 | 3.94k | const uint8_t* inp_end = src + src_size; |
80 | 3.94k | uint8_t* outp = dst; |
81 | 3.94k | uint8_t* outp_end = dst + dst_size; |
82 | 3.94k | uint8_t* lbcur; |
83 | 3.94k | std::size_t lblen; |
84 | 3.94k | std::size_t state = 0; |
85 | 3.94k | std::size_t nstate = 0; |
86 | | |
87 | | /* First byte encoding */ |
88 | 3.94k | if (*inp >= 22) { |
89 | | /* 22..255 : copy literal string |
90 | | * length = (byte - 17) = 4..238 |
91 | | * state = 4 [ don't copy extra literals ] |
92 | | * skip byte |
93 | | */ |
94 | 3.78k | std::size_t len = *inp++ - uint8_t(17); |
95 | 3.78k | NEEDS_IN(len) |
96 | 3.71k | NEEDS_OUT(len) |
97 | 148k | for (std::size_t i = 0; i < len; ++i) |
98 | 144k | *outp++ = *inp++; |
99 | 3.71k | state = 4; |
100 | 3.71k | } else if (*inp >= 18) { |
101 | | /* 18..21 : copy 0..3 literals |
102 | | * state = (byte - 17) = 0..3 [ copy <state> literals ] |
103 | | * skip byte |
104 | | */ |
105 | 12 | nstate = *inp++ - uint8_t(17); |
106 | 12 | state = nstate; |
107 | 12 | NEEDS_IN(nstate) |
108 | 12 | NEEDS_OUT(nstate) |
109 | 36 | for (std::size_t i = 0; i < nstate; ++i) |
110 | 24 | *outp++ = *inp++; |
111 | 12 | } |
112 | | /* 0..17 : follow regular instruction encoding, see below. It is worth |
113 | | * noting that codes 16 and 17 will represent a block copy from |
114 | | * the dictionary which is empty, and that they will always be |
115 | | * invalid at this place. |
116 | | */ |
117 | | |
118 | 99.7k | while (true) { |
119 | 99.7k | NEEDS_IN(1) |
120 | 99.7k | uint8_t inst = *inp++; |
121 | 99.7k | if (inst & 0xC0) { |
122 | | /* [M2] |
123 | | * 1 L L D D D S S (128..255) |
124 | | * Copy 5-8 bytes from block within 2kB distance |
125 | | * state = S (copy S literals after this block) |
126 | | * length = 5 + L |
127 | | * Always followed by exactly one byte : H H H H H H H H |
128 | | * distance = (H << 3) + D + 1 |
129 | | * |
130 | | * 0 1 L D D D S S (64..127) |
131 | | * Copy 3-4 bytes from block within 2kB distance |
132 | | * state = S (copy S literals after this block) |
133 | | * length = 3 + L |
134 | | * Always followed by exactly one byte : H H H H H H H H |
135 | | * distance = (H << 3) + D + 1 |
136 | | */ |
137 | 34.5k | NEEDS_IN(1) |
138 | 34.4k | lbcur = outp - ((*inp++ << 3) + ((inst >> 2) & 0x7) + 1); |
139 | 34.4k | lblen = std::size_t(inst >> 5) + 1; |
140 | 34.4k | nstate = inst & uint8_t(0x3); |
141 | 65.2k | } else if (inst & M3Marker) { |
142 | | /* [M3] |
143 | | * 0 0 1 L L L L L (32..63) |
144 | | * Copy of small block within 16kB distance (preferably less than 34B) |
145 | | * length = 2 + (L ?: 31 + (zero_bytes * 255) + non_zero_byte) |
146 | | * Always followed by exactly one LE16 : D D D D D D D D : D D D D D D S S |
147 | | * distance = D + 1 |
148 | | * state = S (copy S literals after this block) |
149 | | */ |
150 | 26.7k | lblen = std::size_t(inst & uint8_t(0x1f)) + 2; |
151 | 26.7k | if (lblen == 2) { |
152 | 12.1k | CONSUME_ZERO_BYTE_LENGTH |
153 | 12.1k | NEEDS_IN(1) |
154 | 12.1k | lblen += offset * 255 + 31 + *inp++; |
155 | 12.1k | } |
156 | 26.6k | NEEDS_IN(2) |
157 | 26.6k | nstate = get_le16(inp); |
158 | 26.6k | inp += 2; |
159 | 26.6k | lbcur = outp - ((nstate >> 2) + 1); |
160 | 26.6k | nstate &= 0x3; |
161 | 38.5k | } else if (inst & M4Marker) { |
162 | | /* [M4] |
163 | | * 0 0 0 1 H L L L (16..31) |
164 | | * Copy of a block within 16..48kB distance (preferably less than 10B) |
165 | | * length = 2 + (L ?: 7 + (zero_bytes * 255) + non_zero_byte) |
166 | | * Always followed by exactly one LE16 : D D D D D D D D : D D D D D D S S |
167 | | * distance = 16384 + (H << 14) + D |
168 | | * state = S (copy S literals after this block) |
169 | | * End of stream is reached if distance == 16384 |
170 | | */ |
171 | 1.04k | lblen = std::size_t(inst & uint8_t(0x7)) + 2; |
172 | 1.04k | if (lblen == 2) { |
173 | 14 | CONSUME_ZERO_BYTE_LENGTH |
174 | 11 | NEEDS_IN(1) |
175 | 11 | lblen += offset * 255 + 7 + *inp++; |
176 | 11 | } |
177 | 1.03k | NEEDS_IN(2) |
178 | 1.03k | nstate = get_le16(inp); |
179 | 1.03k | inp += 2; |
180 | 1.03k | lbcur = outp - (((inst & 0x8) << 11) + (nstate >> 2)); |
181 | 1.03k | nstate &= 0x3; |
182 | 1.03k | if (lbcur == outp) |
183 | 1.00k | break; /* Stream finished */ |
184 | 32 | lbcur -= 16384; |
185 | 37.4k | } else { |
186 | | /* [M1] Depends on the number of literals copied by the last instruction. */ |
187 | 37.4k | if (state == 0) { |
188 | | /* If last instruction did not copy any literal (state == 0), this |
189 | | * encoding will be a copy of 4 or more literal, and must be interpreted |
190 | | * like this : |
191 | | * |
192 | | * 0 0 0 0 L L L L (0..15) : copy long literal string |
193 | | * length = 3 + (L ?: 15 + (zero_bytes * 255) + non_zero_byte) |
194 | | * state = 4 (no extra literals are copied) |
195 | | */ |
196 | 29.4k | std::size_t len = inst + 3; |
197 | 29.4k | if (len == 3) { |
198 | 12.6k | CONSUME_ZERO_BYTE_LENGTH |
199 | 12.6k | NEEDS_IN(1) |
200 | 12.6k | len += offset * 255 + 15 + *inp++; |
201 | 12.6k | } |
202 | | /* copy_literal_run */ |
203 | 29.4k | NEEDS_IN(len) |
204 | 29.4k | NEEDS_OUT(len) |
205 | 1.63M | for (std::size_t i = 0; i < len; ++i) |
206 | 1.60M | *outp++ = *inp++; |
207 | 27.2k | state = 4; |
208 | 27.2k | continue; |
209 | 29.4k | } else if (state != 4) { |
210 | | /* If last instruction used to copy between 1 to 3 literals (encoded in |
211 | | * the instruction's opcode or distance), the instruction is a copy of a |
212 | | * 2-byte block from the dictionary within a 1kB distance. It is worth |
213 | | * noting that this instruction provides little savings since it uses 2 |
214 | | * bytes to encode a copy of 2 other bytes but it encodes the number of |
215 | | * following literals for free. It must be interpreted like this : |
216 | | * |
217 | | * 0 0 0 0 D D S S (0..15) : copy 2 bytes from <= 1kB distance |
218 | | * length = 2 |
219 | | * state = S (copy S literals after this block) |
220 | | * Always followed by exactly one byte : H H H H H H H H |
221 | | * distance = (H << 2) + D + 1 |
222 | | */ |
223 | 7.92k | NEEDS_IN(1) |
224 | 7.91k | nstate = inst & uint8_t(0x3); |
225 | 7.91k | lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 1); |
226 | 7.91k | lblen = 2; |
227 | 7.91k | } else { |
228 | | /* If last instruction used to copy 4 or more literals (as detected by |
229 | | * state == 4), the instruction becomes a copy of a 3-byte block from the |
230 | | * dictionary from a 2..3kB distance, and must be interpreted like this : |
231 | | * |
232 | | * 0 0 0 0 D D S S (0..15) : copy 3 bytes from 2..3 kB distance |
233 | | * length = 3 |
234 | | * state = S (copy S literals after this block) |
235 | | * Always followed by exactly one byte : H H H H H H H H |
236 | | * distance = (H << 2) + D + 2049 |
237 | | */ |
238 | 97 | NEEDS_IN(1) |
239 | 55 | nstate = inst & uint8_t(0x3); |
240 | 55 | lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 2049); |
241 | 55 | lblen = 3; |
242 | 55 | } |
243 | 37.4k | } |
244 | 69.0k | if (lbcur < dst) { |
245 | 374 | dst_size = outp - dst; |
246 | 374 | return EResult::LookbehindOverrun; |
247 | 374 | } |
248 | 68.6k | NEEDS_IN(nstate) |
249 | 68.6k | NEEDS_OUT(lblen + nstate) |
250 | | /* Copy lookbehind */ |
251 | 3.18M | for (std::size_t i = 0; i < lblen; ++i) |
252 | 3.11M | *outp++ = *lbcur++; |
253 | 68.6k | state = nstate; |
254 | | /* Copy literal */ |
255 | 113k | for (std::size_t i = 0; i < nstate; ++i) |
256 | 45.1k | *outp++ = *inp++; |
257 | 68.6k | } |
258 | | |
259 | 1.00k | dst_size = outp - dst; |
260 | 1.00k | if (lblen != 3) /* Ensure terminating M4 was encountered */ |
261 | 21 | return EResult::Error; |
262 | 983 | if (inp == inp_end) |
263 | 966 | return EResult::Success; |
264 | 17 | else if (inp < inp_end) |
265 | 17 | return EResult::InputNotConsumed; |
266 | 0 | else |
267 | 0 | return EResult::InputOverrun; |
268 | 983 | } |
269 | | |
270 | | } |