aboutsummaryrefslogtreecommitdiff
path: root/src/liblzma/common/block_header_encoder.c
blob: 3a16e6c374330d58f39581d18294fb460725312a (plain) (blame)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
///////////////////////////////////////////////////////////////////////////////
//
/// \file       block_header_encoder.c
/// \brief      Encodes Block Header for .lzma files
//
//  Copyright (C) 2007 Lasse Collin
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
///////////////////////////////////////////////////////////////////////////////

#include "common.h"
#include "check.h"


extern LZMA_API lzma_ret
lzma_block_header_size(lzma_block *options)
{
	// Block Header Size + Block Flags + CRC32.
	size_t size = 1 + 1 + 4;

	// Compressed Size
	if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN) {
		if (options->compressed_size > LZMA_VLI_VALUE_MAX / 4 - 1
				|| options->compressed_size == 0
				|| (options->compressed_size & 3))
			return LZMA_PROG_ERROR;

		size += lzma_vli_size(options->compressed_size / 4 - 1);
	}

	// Uncompressed Size
	if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
		const size_t add = lzma_vli_size(options->uncompressed_size);
		if (add == 0)
			return LZMA_PROG_ERROR;

		size += add;
	}

	// List of Filter Flags
	if (options->filters == NULL
			|| options->filters[0].id == LZMA_VLI_VALUE_UNKNOWN)
		return LZMA_PROG_ERROR;

	for (size_t i = 0; options->filters[i].id != LZMA_VLI_VALUE_UNKNOWN;
			++i) {
		// Don't allow too many filters.
		if (i == 4)
			return LZMA_PROG_ERROR;

		uint32_t add;
		return_if_error(lzma_filter_flags_size(&add,
				options->filters + i));

		size += add;
	}

	// Pad to a multiple of four bytes.
	options->header_size = (size + 3) & ~(size_t)(3);

	// NOTE: We don't verify that Total Size of the Block stays within
	// limits. This is because it is possible that we are called with
	// exaggerated values to reserve space for Block Header, and later
	// called again with lower, real values.

	return LZMA_OK;
}


extern LZMA_API lzma_ret
lzma_block_header_encode(const lzma_block *options, uint8_t *out)
{
	if ((options->header_size & 3)
			|| options->header_size < LZMA_BLOCK_HEADER_SIZE_MIN
			|| options->header_size > LZMA_BLOCK_HEADER_SIZE_MAX)
		return LZMA_PROG_ERROR;

	// Indicate the size of the buffer _excluding_ the CRC32 field.
	const size_t out_size = options->header_size - 4;

	// Store the Block Header Size.
	out[0] = out_size / 4;

	// We write Block Flags a little later.
	size_t out_pos = 2;

	// Compressed Size
	if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN) {
		// Compressed Size must be non-zero, fit into a 63-bit
		// integer and be a multiple of four. Also the Total Size
		// of the Block must fit into 63-bit integer.
		if (options->compressed_size == 0
				|| (options->compressed_size & 3)
				|| options->compressed_size
					> LZMA_VLI_VALUE_MAX
				|| lzma_block_total_size_get(options) == 0)
			return LZMA_PROG_ERROR;

		return_if_error(lzma_vli_encode(
				options->compressed_size / 4 - 1, NULL,
				out, &out_pos, out_size));
	}

	// Uncompressed Size
	if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN)
		return_if_error(lzma_vli_encode(
				options->uncompressed_size, NULL,
				out, &out_pos, out_size));

	// Filter Flags
	if (options->filters == NULL
			|| options->filters[0].id == LZMA_VLI_VALUE_UNKNOWN)
		return LZMA_PROG_ERROR;

	size_t filter_count = 0;
	do {
		// There can be at maximum of four filters.
		if (filter_count == 4)
			return LZMA_PROG_ERROR;

		return_if_error(lzma_filter_flags_encode(
				options->filters + filter_count,
				out, &out_pos, out_size));

	} while (options->filters[++filter_count].id
			!= LZMA_VLI_VALUE_UNKNOWN);

	// Block Flags
	out[1] = filter_count - 1;

	if (options->compressed_size != LZMA_VLI_VALUE_UNKNOWN)
		out[1] |= 0x40;

	if (options->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN)
		out[1] |= 0x80;

	// Padding
	memzero(out + out_pos, out_size - out_pos);

	// CRC32
	integer_write_32(out + out_size, lzma_crc32(out, out_size, 0));

	return LZMA_OK;
}