1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: expandtab:ts=8:sw=4:softtabstop=4:
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file       block_header_encoder.c
6 /// \brief      Encodes Block Header for .xz files
7 //
8 //  Author:     Lasse Collin
9 //
10 //  This file has been put into the public domain.
11 //  You can do whatever you want with this file.
12 //
13 ///////////////////////////////////////////////////////////////////////////////
14 
15 #include "common.h"
16 #include "check.h"
17 
18 
19 extern LZMA_API(lzma_ret)
lzma_block_header_size(lzma_block * block)20 lzma_block_header_size(lzma_block *block)
21 {
22 	if (block->version != 0)
23 		return LZMA_OPTIONS_ERROR;
24 
25 	// Block Header Size + Block Flags + CRC32.
26 	uint32_t size = 1 + 1 + 4;
27 
28 	// Compressed Size
29 	if (block->compressed_size != LZMA_VLI_UNKNOWN) {
30 		const uint32_t add = lzma_vli_size(block->compressed_size);
31 		if (add == 0 || block->compressed_size == 0)
32 			return LZMA_PROG_ERROR;
33 
34 		size += add;
35 	}
36 
37 	// Uncompressed Size
38 	if (block->uncompressed_size != LZMA_VLI_UNKNOWN) {
39 		const uint32_t add = lzma_vli_size(block->uncompressed_size);
40 		if (add == 0)
41 			return LZMA_PROG_ERROR;
42 
43 		size += add;
44 	}
45 
46 	// List of Filter Flags
47 	if (block->filters == NULL || block->filters[0].id == LZMA_VLI_UNKNOWN)
48 		return LZMA_PROG_ERROR;
49 
50 	for (size_t i = 0; block->filters[i].id != LZMA_VLI_UNKNOWN; ++i) {
51 		// Don't allow too many filters.
52 		if (i == LZMA_FILTERS_MAX)
53 			return LZMA_PROG_ERROR;
54 
55 		uint32_t add;
56 		return_if_error(lzma_filter_flags_size(&add,
57 				block->filters + i));
58 
59 		size += add;
60 	}
61 
62 	// Pad to a multiple of four bytes.
63 	block->header_size = (size + 3) & ~UINT32_C(3);
64 
65 	// NOTE: We don't verify that the encoded size of the Block stays
66 	// within limits. This is because it is possible that we are called
67 	// with exaggerated Compressed Size (e.g. LZMA_VLI_MAX) to reserve
68 	// space for Block Header, and later called again with lower,
69 	// real values.
70 
71 	return LZMA_OK;
72 }
73 
74 
75 extern LZMA_API(lzma_ret)
lzma_block_header_encode(const lzma_block * block,uint8_t * out)76 lzma_block_header_encode(const lzma_block *block, uint8_t *out)
77 {
78 	// Valdidate everything but filters.
79 	if (lzma_block_unpadded_size(block) == 0
80 			|| !lzma_vli_is_valid(block->uncompressed_size))
81 		return LZMA_PROG_ERROR;
82 
83 	// Indicate the size of the buffer _excluding_ the CRC32 field.
84 	const size_t out_size = block->header_size - 4;
85 
86 	// Store the Block Header Size.
87 	out[0] = out_size / 4;
88 
89 	// We write Block Flags in pieces.
90 	out[1] = 0x00;
91 	size_t out_pos = 2;
92 
93 	// Compressed Size
94 	if (block->compressed_size != LZMA_VLI_UNKNOWN) {
95 		return_if_error(lzma_vli_encode(block->compressed_size, NULL,
96 				out, &out_pos, out_size));
97 
98 		out[1] |= 0x40;
99 	}
100 
101 	// Uncompressed Size
102 	if (block->uncompressed_size != LZMA_VLI_UNKNOWN) {
103 		return_if_error(lzma_vli_encode(block->uncompressed_size, NULL,
104 				out, &out_pos, out_size));
105 
106 		out[1] |= 0x80;
107 	}
108 
109 	// Filter Flags
110 	if (block->filters == NULL || block->filters[0].id == LZMA_VLI_UNKNOWN)
111 		return LZMA_PROG_ERROR;
112 
113 	size_t filter_count = 0;
114 	do {
115 		// There can be at maximum of four filters.
116 		if (filter_count == LZMA_FILTERS_MAX)
117 			return LZMA_PROG_ERROR;
118 
119 		return_if_error(lzma_filter_flags_encode(
120 				block->filters + filter_count,
121 				out, &out_pos, out_size));
122 
123 	} while (block->filters[++filter_count].id != LZMA_VLI_UNKNOWN);
124 
125 	out[1] |= filter_count - 1;
126 
127 	// Padding
128 	memzero(out + out_pos, out_size - out_pos);
129 
130 	// CRC32
131 	integer_write_32(out + out_size, lzma_crc32(out, out_size, 0));
132 
133 	return LZMA_OK;
134 }
135