summaryrefslogtreecommitdiff
path: root/lib/lz4_wrapper.c
blob: cdbcd05bd4373e04f8ada97578a1eee4bb4b1638 (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
// SPDX-License-Identifier: GPL 2.0+ OR BSD-3-Clause
/*
 * Copyright 2015 Google Inc.
 */

#include <common.h>
#include <compiler.h>
#include <image.h>
#include <lz4.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/unaligned.h>

static u16 LZ4_readLE16(const void *src)
{
	return get_unaligned_le16(src);
}
static void LZ4_copy4(void *dst, const void *src)
{
	put_unaligned(get_unaligned((const u32 *)src), (u32 *)dst);
}
static void LZ4_copy8(void *dst, const void *src)
{
	put_unaligned(get_unaligned((const u64 *)src), (u64 *)dst);
}

typedef  uint8_t BYTE;
typedef uint16_t U16;
typedef uint32_t U32;
typedef  int32_t S32;
typedef uint64_t U64;

#define FORCE_INLINE static inline __attribute__((always_inline))

/* lz4.c is unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */
#include "lz4.c"	/* #include for inlining, do not link! */

#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U

int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn)
{
	const void *end = dst + *dstn;
	const void *in = src;
	void *out = dst;
	int has_block_checksum;
	int ret;
	*dstn = 0;

	{ /* With in-place decompression the header may become invalid later. */
		u32 magic;
		u8 flags, version, independent_blocks, has_content_size;
		u8 block_desc;

		if (srcn < sizeof(u32) + 3*sizeof(u8))
			return -EINVAL;	/* input overrun */

		magic = get_unaligned_le32(in);
		in += sizeof(u32);
		flags = *(u8 *)in;
		in += sizeof(u8);
		block_desc = *(u8 *)in;
		in += sizeof(u8);

		version = (flags >> 6) & 0x3;
		independent_blocks = (flags >> 5) & 0x1;
		has_block_checksum = (flags >> 4) & 0x1;
		has_content_size = (flags >> 3) & 0x1;

		/* We assume there's always only a single, standard frame. */
		if (magic != LZ4F_MAGIC || version != 1)
			return -EPROTONOSUPPORT;	/* unknown format */
		if ((flags & 0x03) || (block_desc & 0x8f))
			return -EINVAL;	/* reserved bits must be zero */
		if (!independent_blocks)
			return -EPROTONOSUPPORT; /* we can't support this yet */

		if (has_content_size) {
			if (srcn < sizeof(u32) + 3*sizeof(u8) + sizeof(u64))
				return -EINVAL;	/* input overrun */
			in += sizeof(u64);
		}
		/* Header checksum byte */
		in += sizeof(u8);
	}

	while (1) {
		u32 block_header, block_size;

		block_header = get_unaligned_le32(in);
		in += sizeof(u32);
		block_size = block_header & ~LZ4F_BLOCKUNCOMPRESSED_FLAG;

		if (in - src + block_size > srcn) {
			ret = -EINVAL;		/* input overrun */
			break;
		}

		if (!block_size) {
			ret = 0;	/* decompression successful */
			break;
		}

		if (block_header & LZ4F_BLOCKUNCOMPRESSED_FLAG) {
			size_t size = min((ptrdiff_t)block_size, end - out);
			memcpy(out, in, size);
			out += size;
			if (size < block_size) {
				ret = -ENOBUFS;	/* output overrun */
				break;
			}
		} else {
			/* constant folding essential, do not touch params! */
			ret = LZ4_decompress_generic(in, out, block_size,
					end - out, endOnInputSize,
					full, 0, noDict, out, NULL, 0);
			if (ret < 0) {
				ret = -EPROTO;	/* decompression error */
				break;
			}
			out += ret;
		}

		in += block_size;
		if (has_block_checksum)
			in += sizeof(u32);
	}

	*dstn = out - dst;
	return ret;
}