blob: 8acff032f10f937a90a9a296d4fde42b8c6f83a7 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * LZ4 - Fast LZ compression algorithm
3 * Copyright (C) 2011-2012, Yann Collet.
4 * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * You can contact the author at :
30 * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
31 * - LZ4 source repository : http://code.google.com/p/lz4/
32 *
33 * Changed for kernel use by:
34 * Chanho Min <chanho.min@lge.com>
35 */
36
37#include <debug.h>
38#include <string.h>
39#include <sys/types.h>
40#include <lib/decompress.h>
41
42static inline __u16 __le16_to_cpup(const __le16 *p)
43{
44 return (__force __u16)*p;
45}
46
47static inline u16 get_unaligned_le16(const void *p)
48{
49 return le16_to_cpup((__le16 *)p);
50}
51
52static inline __u32 __le32_to_cpup(const __le32 *p)
53{
54 return (__force __u32)*p;
55}
56
57static inline u32 get_unaligned_le32(const void *p)
58{
59 return le32_to_cpup((__le32 *)p);
60}
61
62static int lz4_uncompress(const char *source, char *dest, int osize)
63{
64 const BYTE *ip = (const BYTE *) source;
65 const BYTE *ref;
66 BYTE *op = (BYTE *) dest;
67 BYTE *const oend = op + osize;
68 BYTE *cpy;
69 unsigned int token;
70 size_t length;
71
72 while (1) {
73
74 /* get runlength */
75 token = *ip++;
76 length = (token >> ML_BITS);
77 if (length == RUN_MASK) {
78 size_t len;
79
80 len = *ip++;
81 for (; len == 255; length += 255)
82 len = *ip++;
83 if (unlikely(length > (size_t)(length + len))) {
84 goto _output_error;
85 }
86 length += len;
87 }
88
89 /* copy literals */
90 cpy = op + length;
91 if (unlikely(cpy > oend - COPYLENGTH)) {
92 /*
93 * Error: not enough place for another match
94 * (min 4) + 5 literals
95 */
96 if (cpy != oend) {
97 goto _output_error;
98 }
99
100 memcpy(op, ip, length);
101 ip += length;
102 break; /* EOF */
103 }
104 LZ4_WILDCOPY(ip, op, cpy);
105 ip -= (op - cpy);
106 op = cpy;
107
108 /* get offset */
109 LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
110 ip += 2;
111
112 /* Error: offset create reference outside destination buffer */
113 if (unlikely(ref < (BYTE *const) dest)) {
114 goto _output_error;
115 }
116
117 /* get matchlength */
118 length = token & ML_MASK;
119 if (length == ML_MASK) {
120 for (; *ip == 255; length += 255)
121 ip++;
122 if (unlikely(length > (size_t)(length + *ip))) {
123 goto _output_error;
124 }
125 length += *ip++;
126 }
127
128 /* copy repeated sequence */
129 if (unlikely((op - ref) < STEPSIZE)) {
130#if LZ4_ARCH64
131 int dec64 = dec64table[op - ref];
132#else
133 const int dec64 = 0;
134#endif
135 op[0] = ref[0];
136 op[1] = ref[1];
137 op[2] = ref[2];
138 op[3] = ref[3];
139 op += 4;
140 ref += 4;
141 ref -= dec32table[op-ref];
142 PUT4(ref, op);
143 op += STEPSIZE - 4;
144 ref -= dec64;
145 } else {
146 LZ4_COPYSTEP(ref, op);
147 }
148 cpy = op + length - (STEPSIZE - 4);
149 if (cpy > (oend - COPYLENGTH)) {
150
151 /* Error: request to write beyond destination buffer */
152 if (cpy > oend) {
153 goto _output_error;
154 }
155#if LZ4_ARCH64
156 if ((ref + COPYLENGTH) > oend) {
157#else
158 if ((ref + COPYLENGTH) > oend ||
159 (op + COPYLENGTH) > oend) {
160#endif
161 goto _output_error;
162 }
163 LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
164 while (op < cpy)
165 *op++ = *ref++;
166 op = cpy;
167 /*
168 * Check EOF (should never happen, since last 5 bytes
169 * are supposed to be literals)
170 */
171 if (op == oend) {
172 goto _output_error;
173 }
174 continue;
175 }
176 LZ4_SECURECOPY(ref, op, cpy);
177 op = cpy; /* correction */
178 }
179
180 /* end of decoding */
181 return (int) (((char *)ip) - source);
182
183 /* write overflow error detected */
184_output_error:
185 return -ELZ4WO;
186}
187
188static int lz4_decompress(const unsigned char *src, size_t *src_len,
189 unsigned char *dest, size_t actual_dest_len)
190{
191 int input_len = 0;
192
193 input_len = lz4_uncompress((const char *)src, (char *)dest, actual_dest_len);
194
195 if (input_len < 0)
196 return -ELZ4IR;
197
198 /* decode successfully */
199 *src_len = input_len;
200
201 return LZ4_OK;
202}
203
204int unlz4(const void *input, long in_len, void *output)
205{
206 int ret = -1;
207 u8 *inp, *outp;
208 size_t chunksize = 0, dest_len;
209 size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE;
210 size_t out_len;
211 long size = in_len;
212
213 if (output) {
214 outp = output;
215 } else {
216 dprintf(ALWAYS, "unlz4 error: NULL output pointer\n");
217 return -ELZ4NP;
218 }
219
220 if (input) {
221 inp = (u8 *) input;
222 } else {
223 dprintf(ALWAYS, "unlz4 error: NULL input pointer\n");
224 return -ELZ4NP;
225 }
226
227 out_len = get_unaligned_le32(input + in_len);
228 chunksize = get_unaligned_le32(inp);
229 if (chunksize == ARCHIVE_MAGICNUMBER) {
230 inp += 4;
231 size -= 4;
232 } else {
233 dprintf(ALWAYS, "unlz4 error: invalid header\n");
234 return -ELZ4IH;
235 }
236
237 for (;;) {
238
239 chunksize = get_unaligned_le32(inp);
240 if (chunksize == ARCHIVE_MAGICNUMBER) {
241 inp += 4;
242 size -= 4;
243 continue;
244 }
245
246 inp += 4;
247 size -= 4;
248
249 if (out_len >= uncomp_chunksize) {
250 dest_len = uncomp_chunksize;
251 out_len -= dest_len;
252 } else {
253 dest_len = out_len;
254 }
255
256 ret = lz4_decompress(inp, &chunksize, outp, dest_len);
257 if (ret < 0) {
258 dprintf(ALWAYS, "unlz4 error: decodes failure\n");
259 return -ELZ4DF;
260 }
261
262 if (output)
263 outp += dest_len;
264
265 size -= chunksize;
266
267 if (size == 0) // Note: here should append size info at the end
268 break;
269 else if (size < 0) {
270 dprintf(ALWAYS, "unlz4 error: data corrupt\n");
271 return -ELZ4DC;
272 }
273
274 inp += chunksize;
275 }
276
277 return LZ4_OK;
278}