1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       block_header_encoder.c
4207753Smm/// \brief      Encodes Block Header for .xz files
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "common.h"
14207753Smm#include "check.h"
15207753Smm
16207753Smm
17207753Smmextern LZMA_API(lzma_ret)
18207753Smmlzma_block_header_size(lzma_block *block)
19207753Smm{
20207753Smm	if (block->version != 0)
21207753Smm		return LZMA_OPTIONS_ERROR;
22207753Smm
23207753Smm	// Block Header Size + Block Flags + CRC32.
24207753Smm	uint32_t size = 1 + 1 + 4;
25207753Smm
26207753Smm	// Compressed Size
27207753Smm	if (block->compressed_size != LZMA_VLI_UNKNOWN) {
28207753Smm		const uint32_t add = lzma_vli_size(block->compressed_size);
29207753Smm		if (add == 0 || block->compressed_size == 0)
30207753Smm			return LZMA_PROG_ERROR;
31207753Smm
32207753Smm		size += add;
33207753Smm	}
34207753Smm
35207753Smm	// Uncompressed Size
36207753Smm	if (block->uncompressed_size != LZMA_VLI_UNKNOWN) {
37207753Smm		const uint32_t add = lzma_vli_size(block->uncompressed_size);
38207753Smm		if (add == 0)
39207753Smm			return LZMA_PROG_ERROR;
40207753Smm
41207753Smm		size += add;
42207753Smm	}
43207753Smm
44207753Smm	// List of Filter Flags
45207753Smm	if (block->filters == NULL || block->filters[0].id == LZMA_VLI_UNKNOWN)
46207753Smm		return LZMA_PROG_ERROR;
47207753Smm
48207753Smm	for (size_t i = 0; block->filters[i].id != LZMA_VLI_UNKNOWN; ++i) {
49207753Smm		// Don't allow too many filters.
50207753Smm		if (i == LZMA_FILTERS_MAX)
51207753Smm			return LZMA_PROG_ERROR;
52207753Smm
53207753Smm		uint32_t add;
54207753Smm		return_if_error(lzma_filter_flags_size(&add,
55207753Smm				block->filters + i));
56207753Smm
57207753Smm		size += add;
58207753Smm	}
59207753Smm
60207753Smm	// Pad to a multiple of four bytes.
61207753Smm	block->header_size = (size + 3) & ~UINT32_C(3);
62207753Smm
63207753Smm	// NOTE: We don't verify that the encoded size of the Block stays
64207753Smm	// within limits. This is because it is possible that we are called
65207753Smm	// with exaggerated Compressed Size (e.g. LZMA_VLI_MAX) to reserve
66207753Smm	// space for Block Header, and later called again with lower,
67207753Smm	// real values.
68207753Smm
69207753Smm	return LZMA_OK;
70207753Smm}
71207753Smm
72207753Smm
73207753Smmextern LZMA_API(lzma_ret)
74207753Smmlzma_block_header_encode(const lzma_block *block, uint8_t *out)
75207753Smm{
76207753Smm	// Validate everything but filters.
77207753Smm	if (lzma_block_unpadded_size(block) == 0
78207753Smm			|| !lzma_vli_is_valid(block->uncompressed_size))
79207753Smm		return LZMA_PROG_ERROR;
80207753Smm
81207753Smm	// Indicate the size of the buffer _excluding_ the CRC32 field.
82207753Smm	const size_t out_size = block->header_size - 4;
83207753Smm
84207753Smm	// Store the Block Header Size.
85207753Smm	out[0] = out_size / 4;
86207753Smm
87207753Smm	// We write Block Flags in pieces.
88207753Smm	out[1] = 0x00;
89207753Smm	size_t out_pos = 2;
90207753Smm
91207753Smm	// Compressed Size
92207753Smm	if (block->compressed_size != LZMA_VLI_UNKNOWN) {
93207753Smm		return_if_error(lzma_vli_encode(block->compressed_size, NULL,
94207753Smm				out, &out_pos, out_size));
95207753Smm
96207753Smm		out[1] |= 0x40;
97207753Smm	}
98207753Smm
99207753Smm	// Uncompressed Size
100207753Smm	if (block->uncompressed_size != LZMA_VLI_UNKNOWN) {
101207753Smm		return_if_error(lzma_vli_encode(block->uncompressed_size, NULL,
102207753Smm				out, &out_pos, out_size));
103207753Smm
104207753Smm		out[1] |= 0x80;
105207753Smm	}
106207753Smm
107207753Smm	// Filter Flags
108207753Smm	if (block->filters == NULL || block->filters[0].id == LZMA_VLI_UNKNOWN)
109207753Smm		return LZMA_PROG_ERROR;
110207753Smm
111207753Smm	size_t filter_count = 0;
112207753Smm	do {
113207753Smm		// There can be a maximum of four filters.
114207753Smm		if (filter_count == LZMA_FILTERS_MAX)
115207753Smm			return LZMA_PROG_ERROR;
116207753Smm
117207753Smm		return_if_error(lzma_filter_flags_encode(
118207753Smm				block->filters + filter_count,
119207753Smm				out, &out_pos, out_size));
120207753Smm
121207753Smm	} while (block->filters[++filter_count].id != LZMA_VLI_UNKNOWN);
122207753Smm
123207753Smm	out[1] |= filter_count - 1;
124207753Smm
125207753Smm	// Padding
126207753Smm	memzero(out + out_pos, out_size - out_pos);
127207753Smm
128207753Smm	// CRC32
129207753Smm	unaligned_write32le(out + out_size, lzma_crc32(out, out_size, 0));
130207753Smm
131207753Smm	return LZMA_OK;
132207753Smm}
133