summaryrefslogtreecommitdiffstats
path: root/depends/lzma/wrapper/lzma_header.c
blob: ab32549f67ef5e08b1b2e00a85251e72753200f5 (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
131
132
133
134
/*
 * Written in 2009 by Lloyd Hilaiel
 *
 * License
 *
 * All the cruft you find here is public domain.  You don't have to credit
 * anyone to use this code, but my personal request is that you mention
 * Igor Pavlov for his hard, high quality work.
 */

/* XXX: clean this up, it's mostly lifted from pavel */

#include "lzma_header.h"

#include <string.h>
#include <assert.h>

#define ELZMA_LZMA_HEADER_SIZE 13
#define ELZMA_LZMA_PROPSBUF_SIZE 5

/****************
  Header parsing
 ****************/

#ifndef UINT64_MAX
#define UINT64_MAX ((unsigned long long)-1)
#endif

/* Parse the properties byte */
static char lzmadec_header_properties(unsigned char *pb, unsigned char *lp, unsigned char *lc,
									  const unsigned char c)
{
	/*	 pb, lp and lc are encoded into a single byte.  */
	if (c > (9 * 5 * 5))
		return -1;
	*pb = c / (9 * 5);	   /* 0 <= pb <= 4 */
	*lp = (c % (9 * 5)) / 9; /* 0 <= lp <= 4 */
	*lc = c % 9;			 /* 0 <= lc <= 8 */

	assert(*pb < 5 && *lp < 5 && *lc < 9);
	return 0;
}

/* Parse the dictionary size (4 bytes, little endian) */
static char lzmadec_header_dictionary(unsigned int *size, const unsigned char *buffer)
{
	unsigned int i;
	*size = 0;
	for (i = 0; i < 4; i++)
		*size += (unsigned int)(*buffer++) << (i * 8);
	/* The dictionary size is limited to 256 MiB (checked from
	 * LZMA SDK 4.30) */
	if (*size > (1 << 28))
		return -1;
	return 0;
}

/* Parse the uncompressed size field (8 bytes, little endian) */
static void lzmadec_header_uncompressed(unsigned long long *size, unsigned char *is_streamed,
										const unsigned char *buffer)
{
	unsigned int i;

	/* Streamed files have all 64 bits set in the size field.
	 * We don't know the uncompressed size beforehand. */
	*is_streamed = 1; /* Assume streamed. */
	*size = 0;
	for (i = 0; i < 8; i++)
	{
		*size += (unsigned long long)buffer[i] << (i * 8);
		if (buffer[i] != 255)
			*is_streamed = 0;
	}
	assert((*is_streamed == 1 && *size == UINT64_MAX) ||
		   (*is_streamed == 0 && *size < UINT64_MAX));
}

static void initLzmaHeader(struct elzma_file_header *hdr)
{
	memset((void *)hdr, 0, sizeof(struct elzma_file_header));
}

static int parseLzmaHeader(const unsigned char *hdrBuf, struct elzma_file_header *hdr)
{
	if (lzmadec_header_properties(&(hdr->pb), &(hdr->lp), &(hdr->lc), *hdrBuf) ||
		lzmadec_header_dictionary(&(hdr->dictSize), hdrBuf + 1))
	{
		return 1;
	}
	lzmadec_header_uncompressed(&(hdr->uncompressedSize), &(hdr->isStreamed), hdrBuf + 5);

	return 0;
}

static int serializeLzmaHeader(unsigned char *hdrBuf, const struct elzma_file_header *hdr)
{
	unsigned int i;

	memset((void *)hdrBuf, 0, ELZMA_LZMA_HEADER_SIZE);

	/* encode lc, pb, and lp */
	*hdrBuf++ = hdr->lc + (hdr->pb * 45) + (hdr->lp * 45 * 9);

	/* encode dictionary size */
	for (i = 0; i < 4; i++)
	{
		*(hdrBuf++) = (unsigned char)(hdr->dictSize >> (i * 8));
	}

	/* encode uncompressed size */
	for (i = 0; i < 8; i++)
	{
		if (hdr->isStreamed)
		{
			*(hdrBuf++) = 0xff;
		}
		else
		{
			*(hdrBuf++) = (unsigned char)(hdr->uncompressedSize >> (i * 8));
		}
	}

	return 0;
}

void initializeLZMAFormatHandler(struct elzma_format_handler *hand)
{
	hand->header_size = ELZMA_LZMA_HEADER_SIZE;
	hand->init_header = initLzmaHeader;
	hand->parse_header = parseLzmaHeader;
	hand->serialize_header = serializeLzmaHeader;
	hand->footer_size = 0;
	hand->serialize_footer = NULL;
}