1 // SPDX-License-Identifier: GPL-2.0-only
3 * LZO1X Compressor from LZO
5 * Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <markus@oberhumer.com>
7 * The full LZO package can be found at:
8 * http://www.oberhumer.com/opensource/lzo/
10 * Changed for Linux kernel use by:
11 * Nitin Gupta <nitingupta910@gmail.com>
12 * Richard Purdie <rpurdie@openedhand.com>
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/unaligned.h>
18 #include <linux/lzo.h>
21 static noinline
size_t
22 lzo1x_1_do_compress(const unsigned char *in
, size_t in_len
,
23 unsigned char *out
, size_t *out_len
,
24 size_t ti
, void *wrkmem
, signed char *state_offset
,
25 const unsigned char bitstream_version
)
27 const unsigned char *ip
;
29 const unsigned char * const in_end
= in
+ in_len
;
30 const unsigned char * const ip_end
= in
+ in_len
- 20;
31 const unsigned char *ii
;
32 lzo_dict_t
* const dict
= (lzo_dict_t
*) wrkmem
;
37 ip
+= ti
< 4 ? 4 - ti
: 0;
40 const unsigned char *m_pos
= NULL
;
41 size_t t
, m_len
, m_off
;
45 ip
+= 1 + ((ip
- ii
) >> 5);
47 if (unlikely(ip
>= ip_end
))
49 dv
= get_unaligned_le32(ip
);
51 if (dv
== 0 && bitstream_version
) {
52 const unsigned char *ir
= ip
+ 4;
53 const unsigned char *limit
= min(ip_end
, ip
+ MAX_ZERO_RUN_LENGTH
+ 1);
54 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && \
55 defined(LZO_FAST_64BIT_MEMORY_ACCESS)
58 for (; (ir
+ 32) <= limit
; ir
+= 32) {
59 dv64
= get_unaligned((u64
*)ir
);
60 dv64
|= get_unaligned((u64
*)ir
+ 1);
61 dv64
|= get_unaligned((u64
*)ir
+ 2);
62 dv64
|= get_unaligned((u64
*)ir
+ 3);
66 for (; (ir
+ 8) <= limit
; ir
+= 8) {
67 dv64
= get_unaligned((u64
*)ir
);
69 # if defined(__LITTLE_ENDIAN)
70 ir
+= __builtin_ctzll(dv64
) >> 3;
71 # elif defined(__BIG_ENDIAN)
72 ir
+= __builtin_clzll(dv64
) >> 3;
74 # error "missing endian definition"
80 while ((ir
< (const unsigned char *)
81 ALIGN((uintptr_t)ir
, 4)) &&
82 (ir
< limit
) && (*ir
== 0))
84 if (IS_ALIGNED((uintptr_t)ir
, 4)) {
85 for (; (ir
+ 4) <= limit
; ir
+= 4) {
88 # if defined(__LITTLE_ENDIAN)
89 ir
+= __builtin_ctz(dv
) >> 3;
90 # elif defined(__BIG_ENDIAN)
91 ir
+= __builtin_clz(dv
) >> 3;
93 # error "missing endian definition"
100 while (likely(ir
< limit
) && unlikely(*ir
== 0))
102 run_length
= ir
- ip
;
103 if (run_length
> MAX_ZERO_RUN_LENGTH
)
104 run_length
= MAX_ZERO_RUN_LENGTH
;
106 t
= ((dv
* 0x1824429d) >> (32 - D_BITS
)) & D_MASK
;
107 m_pos
= in
+ dict
[t
];
108 dict
[t
] = (lzo_dict_t
) (ip
- in
);
109 if (unlikely(dv
!= get_unaligned_le32(m_pos
)))
118 op
[*state_offset
] |= t
;
121 } else if (t
<= 16) {
124 COPY8(op
+ 8, ii
+ 8);
132 while (unlikely(tt
> 255)) {
140 COPY8(op
+ 8, ii
+ 8);
151 if (unlikely(run_length
)) {
153 run_length
-= MIN_ZERO_RUN_LENGTH
;
154 put_unaligned_le32((run_length
<< 21) | 0xfffc18
155 | (run_length
& 0x7), op
);
159 goto finished_writing_instruction
;
164 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ64)
166 v
= get_unaligned((const u64
*) (ip
+ m_len
)) ^
167 get_unaligned((const u64
*) (m_pos
+ m_len
));
168 if (unlikely(v
== 0)) {
171 v
= get_unaligned((const u64
*) (ip
+ m_len
)) ^
172 get_unaligned((const u64
*) (m_pos
+ m_len
));
173 if (unlikely(ip
+ m_len
>= ip_end
))
177 # if defined(__LITTLE_ENDIAN)
178 m_len
+= (unsigned) __builtin_ctzll(v
) / 8;
179 # elif defined(__BIG_ENDIAN)
180 m_len
+= (unsigned) __builtin_clzll(v
) / 8;
182 # error "missing endian definition"
184 #elif defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ32)
186 v
= get_unaligned((const u32
*) (ip
+ m_len
)) ^
187 get_unaligned((const u32
*) (m_pos
+ m_len
));
188 if (unlikely(v
== 0)) {
191 v
= get_unaligned((const u32
*) (ip
+ m_len
)) ^
192 get_unaligned((const u32
*) (m_pos
+ m_len
));
196 v
= get_unaligned((const u32
*) (ip
+ m_len
)) ^
197 get_unaligned((const u32
*) (m_pos
+ m_len
));
198 if (unlikely(ip
+ m_len
>= ip_end
))
202 # if defined(__LITTLE_ENDIAN)
203 m_len
+= (unsigned) __builtin_ctz(v
) / 8;
204 # elif defined(__BIG_ENDIAN)
205 m_len
+= (unsigned) __builtin_clz(v
) / 8;
207 # error "missing endian definition"
210 if (unlikely(ip
[m_len
] == m_pos
[m_len
])) {
213 if (ip
[m_len
] != m_pos
[m_len
])
216 if (ip
[m_len
] != m_pos
[m_len
])
219 if (ip
[m_len
] != m_pos
[m_len
])
222 if (ip
[m_len
] != m_pos
[m_len
])
225 if (ip
[m_len
] != m_pos
[m_len
])
228 if (ip
[m_len
] != m_pos
[m_len
])
231 if (ip
[m_len
] != m_pos
[m_len
])
234 if (unlikely(ip
+ m_len
>= ip_end
))
236 } while (ip
[m_len
] == m_pos
[m_len
]);
244 if (m_len
<= M2_MAX_LEN
&& m_off
<= M2_MAX_OFFSET
) {
246 *op
++ = (((m_len
- 1) << 5) | ((m_off
& 7) << 2));
247 *op
++ = (m_off
>> 3);
248 } else if (m_off
<= M3_MAX_OFFSET
) {
250 if (m_len
<= M3_MAX_LEN
)
251 *op
++ = (M3_MARKER
| (m_len
- 2));
254 *op
++ = M3_MARKER
| 0;
255 while (unlikely(m_len
> 255)) {
261 *op
++ = (m_off
<< 2);
262 *op
++ = (m_off
>> 6);
265 if (m_len
<= M4_MAX_LEN
)
266 *op
++ = (M4_MARKER
| ((m_off
>> 11) & 8)
269 if (unlikely(((m_off
& 0x403f) == 0x403f)
272 && likely(bitstream_version
)) {
273 // Under lzo-rle, block copies
274 // for 261 <= length <= 264 and
275 // (distance & 0x80f3) == 0x80f3
276 // can result in ambiguous
277 // output. Adjust length
278 // to 260 to prevent ambiguity.
283 *op
++ = (M4_MARKER
| ((m_off
>> 11) & 8));
284 while (unlikely(m_len
> 255)) {
290 *op
++ = (m_off
<< 2);
291 *op
++ = (m_off
>> 6);
294 finished_writing_instruction
:
299 return in_end
- (ii
- ti
);
302 static int lzogeneric1x_1_compress(const unsigned char *in
, size_t in_len
,
303 unsigned char *out
, size_t *out_len
,
304 void *wrkmem
, const unsigned char bitstream_version
)
306 const unsigned char *ip
= in
;
307 unsigned char *op
= out
;
308 unsigned char *data_start
;
311 signed char state_offset
= -2;
312 unsigned int m4_max_offset
;
314 // LZO v0 will never write 17 as first byte (except for zero-length
315 // input), so this is used to version the bitstream
316 if (bitstream_version
> 0) {
318 *op
++ = bitstream_version
;
319 m4_max_offset
= M4_MAX_OFFSET_V1
;
321 m4_max_offset
= M4_MAX_OFFSET_V0
;
327 size_t ll
= min_t(size_t, l
, m4_max_offset
+ 1);
328 uintptr_t ll_end
= (uintptr_t) ip
+ ll
;
329 if ((ll_end
+ ((t
+ ll
) >> 5)) <= ll_end
)
331 BUILD_BUG_ON(D_SIZE
* sizeof(lzo_dict_t
) > LZO1X_1_MEM_COMPRESS
);
332 memset(wrkmem
, 0, D_SIZE
* sizeof(lzo_dict_t
));
333 t
= lzo1x_1_do_compress(ip
, ll
, op
, out_len
, t
, wrkmem
,
334 &state_offset
, bitstream_version
);
342 const unsigned char *ii
= in
+ in_len
- t
;
344 if (op
== data_start
&& t
<= 238) {
347 op
[state_offset
] |= t
;
348 } else if (t
<= 18) {
361 COPY8(op
+ 8, ii
+ 8);
371 *op
++ = M4_MARKER
| 1;
379 int lzo1x_1_compress(const unsigned char *in
, size_t in_len
,
380 unsigned char *out
, size_t *out_len
,
383 return lzogeneric1x_1_compress(in
, in_len
, out
, out_len
, wrkmem
, 0);
386 int lzorle1x_1_compress(const unsigned char *in
, size_t in_len
,
387 unsigned char *out
, size_t *out_len
,
390 return lzogeneric1x_1_compress(in
, in_len
, out
, out_len
,
391 wrkmem
, LZO_VERSION
);
394 EXPORT_SYMBOL_GPL(lzo1x_1_compress
);
395 EXPORT_SYMBOL_GPL(lzorle1x_1_compress
);
397 MODULE_LICENSE("GPL");
398 MODULE_DESCRIPTION("LZO1X-1 Compressor");