Skip to content

Commit e4ce070

Browse files
committed
[base] Add lz4 decompression
Add the lz4 decompression algorithm. This algorithm costs around 400 bytes. Signed-off-by: Siemen Dhooghe <sdhooghe@google.com>
1 parent 1a875b3 commit e4ce070

3 files changed

Lines changed: 130 additions & 0 deletions

File tree

sw/device/lib/base/BUILD

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,15 @@ cc_library(
266266
],
267267
)
268268

269+
cc_library(
270+
name = "lz4",
271+
srcs = ["lz4.c"],
272+
hdrs = ["lz4.h"],
273+
deps = [
274+
"//sw/device/lib/base:macros",
275+
],
276+
)
277+
269278
cc_test(
270279
name = "random_order_unittest",
271280
srcs = ["random_order_unittest.cc"],

sw/device/lib/base/lz4.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright lowRISC contributors (OpenTitan project).
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#include "sw/device/lib/base/lz4.h"
6+
7+
#include <stdint.h>
8+
9+
int LZ4_decompress(const char *src_in, char *dst_in, int compressed_size,
10+
int dst_capacity) {
11+
const uint8_t *src = (const uint8_t *)src_in;
12+
const uint8_t *src_end = src + compressed_size;
13+
uint8_t *dst = (uint8_t *)dst_in;
14+
uint8_t *dst_end = dst + dst_capacity;
15+
uint8_t *dst_ptr = dst;
16+
17+
while (src < src_end) {
18+
uint8_t token = *src++;
19+
20+
// Process literals
21+
unsigned int lit_len = token >> 4;
22+
if (lit_len == 15) {
23+
uint8_t s;
24+
do {
25+
if (src >= src_end)
26+
return -1; // Unexpected end of src
27+
s = *src++;
28+
lit_len += s;
29+
} while (s == 255);
30+
}
31+
32+
// Check bounds for literals
33+
if ((size_t)(src_end - src) < lit_len ||
34+
(size_t)(dst_end - dst_ptr) < lit_len) {
35+
return -1; // Literal length exceeds remaining space
36+
}
37+
38+
// Copy literals
39+
for (unsigned int i = 0; i < lit_len; i++) {
40+
*dst_ptr++ = *src++;
41+
}
42+
43+
// The last sequence stops immediately after the literals.
44+
if (src >= src_end) {
45+
break;
46+
}
47+
48+
// Process match offset
49+
if (src + 2 > src_end)
50+
return -1; // Offset missing
51+
// LZ4 offset is strictly little-endian
52+
uint16_t offset = (uint16_t)(src[0] | (src[1] << 8));
53+
src += 2;
54+
55+
if (offset == 0 || offset > (size_t)(dst_ptr - dst)) {
56+
return -1; // Offset out of bounds
57+
}
58+
59+
// Process match length
60+
unsigned int match_len = token & 0x0F;
61+
if (match_len == 15) {
62+
uint8_t s;
63+
do {
64+
if (src >= src_end)
65+
return -1; // Unexpected end of src
66+
s = *src++;
67+
match_len += s;
68+
} while (s == 255);
69+
}
70+
match_len += 4; // LZ4 min-match length is 4
71+
72+
// Check bounds for match destination
73+
if ((size_t)(dst_end - dst_ptr) < match_len) {
74+
return -1; // Match exceeds destination capacity
75+
}
76+
77+
// This must be a byte-by-byte copy, not memcpy,
78+
// because LZ4 heavily relies on overlapping copies where the match length
79+
// is longer than the match offset.
80+
const uint8_t *match_ptr = dst_ptr - offset;
81+
for (unsigned int i = 0; i < match_len; i++) {
82+
*dst_ptr++ = *match_ptr++;
83+
}
84+
}
85+
86+
return (int)(dst_ptr - dst);
87+
}

sw/device/lib/base/lz4.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright lowRISC contributors (OpenTitan project).
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
#ifndef OPENTITAN_SW_DEVICE_LIB_BASE_LZ4_H_
6+
#define OPENTITAN_SW_DEVICE_LIB_BASE_LZ4_H_
7+
8+
#include <stddef.h>
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/**
15+
* Decompresses a raw LZ4 block into a destination buffer.
16+
*
17+
* This implementation is designed specifically for constrained embedded
18+
* environments. It performs no dynamic allocation and requires no
19+
* framing metadata.
20+
*
21+
* @param src Pointer to the compressed data.
22+
* @param dst Pointer to the destination buffer.
23+
* @param compressed_size The exact size of the compressed data.
24+
* @param dst_capacity The maximum capacity of the destination buffer.
25+
* @return The number of bytes decompressed, or a negative value on error.
26+
*/
27+
int LZ4_decompress(const char *src, char *dst, int compressed_size,
28+
int dst_capacity);
29+
30+
#ifdef __cplusplus
31+
}
32+
#endif
33+
34+
#endif // OPENTITAN_SW_DEVICE_LIB_BASE_LZ4_H_

0 commit comments

Comments
 (0)