1 |
/////////////////////////////////////////////////////////////////////////////// |
2 |
// |
3 |
/// \file stream_buffer_encoder.c |
4 |
/// \brief Single-call .xz Stream encoder |
5 |
// |
6 |
// Author: Lasse Collin |
7 |
// |
8 |
// This file has been put into the public domain. |
9 |
// You can do whatever you want with this file. |
10 |
// |
11 |
/////////////////////////////////////////////////////////////////////////////// |
12 |
|
13 |
#include "index.h" |
14 |
|
15 |
|
16 |
/// Maximum size of Index that has exactly one Record. |
17 |
/// Index Indicator + Number of Records + Record + CRC32 rounded up to |
18 |
/// the next multiple of four. |
19 |
#define INDEX_BOUND ((1 + 1 + 2 * LZMA_VLI_BYTES_MAX + 4 + 3) & ~3) |
20 |
|
21 |
/// Stream Header, Stream Footer, and Index |
22 |
#define HEADERS_BOUND (2 * LZMA_STREAM_HEADER_SIZE + INDEX_BOUND) |
23 |
|
24 |
|
25 |
extern LZMA_API(size_t) |
26 |
lzma_stream_buffer_bound(size_t uncompressed_size) |
27 |
{ |
28 |
// Get the maximum possible size of a Block. |
29 |
const size_t block_bound = lzma_block_buffer_bound(uncompressed_size); |
30 |
if (block_bound == 0) |
31 |
return 0; |
32 |
|
33 |
// Catch the possible integer overflow and also prevent the size of |
34 |
// the Stream exceeding LZMA_VLI_MAX (theoretically possible on |
35 |
// 64-bit systems). |
36 |
if (my_min(SIZE_MAX, LZMA_VLI_MAX) - block_bound < HEADERS_BOUND) |
37 |
return 0; |
38 |
|
39 |
return block_bound + HEADERS_BOUND; |
40 |
} |
41 |
|
42 |
|
43 |
extern LZMA_API(lzma_ret) |
44 |
lzma_stream_buffer_encode(lzma_filter *filters, lzma_check check, |
45 |
const lzma_allocator *allocator, |
46 |
const uint8_t *in, size_t in_size, |
47 |
uint8_t *out, size_t *out_pos_ptr, size_t out_size) |
48 |
{ |
49 |
// Sanity checks |
50 |
if (filters == NULL || (unsigned int)(check) > LZMA_CHECK_ID_MAX |
51 |
|| (in == NULL && in_size != 0) || out == NULL |
52 |
|| out_pos_ptr == NULL || *out_pos_ptr > out_size) |
53 |
return LZMA_PROG_ERROR; |
54 |
|
55 |
if (!lzma_check_is_supported(check)) |
56 |
return LZMA_UNSUPPORTED_CHECK; |
57 |
|
58 |
// Note for the paranoids: Index encoder prevents the Stream from |
59 |
// getting too big and still being accepted with LZMA_OK, and Block |
60 |
// encoder catches if the input is too big. So we don't need to |
61 |
// separately check if the buffers are too big. |
62 |
|
63 |
// Use a local copy. We update *out_pos_ptr only if everything |
64 |
// succeeds. |
65 |
size_t out_pos = *out_pos_ptr; |
66 |
|
67 |
// Check that there's enough space for both Stream Header and |
68 |
// Stream Footer. |
69 |
if (out_size - out_pos <= 2 * LZMA_STREAM_HEADER_SIZE) |
70 |
return LZMA_BUF_ERROR; |
71 |
|
72 |
// Reserve space for Stream Footer so we don't need to check for |
73 |
// available space again before encoding Stream Footer. |
74 |
out_size -= LZMA_STREAM_HEADER_SIZE; |
75 |
|
76 |
// Encode the Stream Header. |
77 |
lzma_stream_flags stream_flags = { |
78 |
.version = 0, |
79 |
.check = check, |
80 |
}; |
81 |
|
82 |
if (lzma_stream_header_encode(&stream_flags, out + out_pos) |
83 |
!= LZMA_OK) |
84 |
return LZMA_PROG_ERROR; |
85 |
|
86 |
out_pos += LZMA_STREAM_HEADER_SIZE; |
87 |
|
88 |
// Encode a Block but only if there is at least one byte of input. |
89 |
lzma_block block = { |
90 |
.version = 0, |
91 |
.check = check, |
92 |
.filters = filters, |
93 |
}; |
94 |
|
95 |
if (in_size > 0) |
96 |
return_if_error(lzma_block_buffer_encode(&block, allocator, |
97 |
in, in_size, out, &out_pos, out_size)); |
98 |
|
99 |
// Index |
100 |
{ |
101 |
// Create an Index. It will have one Record if there was |
102 |
// at least one byte of input to encode. Otherwise the |
103 |
// Index will be empty. |
104 |
lzma_index *i = lzma_index_init(allocator); |
105 |
if (i == NULL) |
106 |
return LZMA_MEM_ERROR; |
107 |
|
108 |
lzma_ret ret = LZMA_OK; |
109 |
|
110 |
if (in_size > 0) |
111 |
ret = lzma_index_append(i, allocator, |
112 |
lzma_block_unpadded_size(&block), |
113 |
block.uncompressed_size); |
114 |
|
115 |
// If adding the Record was successful, encode the Index |
116 |
// and get its size which will be stored into Stream Footer. |
117 |
if (ret == LZMA_OK) { |
118 |
ret = lzma_index_buffer_encode( |
119 |
i, out, &out_pos, out_size); |
120 |
|
121 |
stream_flags.backward_size = lzma_index_size(i); |
122 |
} |
123 |
|
124 |
lzma_index_end(i, allocator); |
125 |
|
126 |
if (ret != LZMA_OK) |
127 |
return ret; |
128 |
} |
129 |
|
130 |
// Stream Footer. We have already reserved space for this. |
131 |
if (lzma_stream_footer_encode(&stream_flags, out + out_pos) |
132 |
!= LZMA_OK) |
133 |
return LZMA_PROG_ERROR; |
134 |
|
135 |
out_pos += LZMA_STREAM_HEADER_SIZE; |
136 |
|
137 |
// Everything went fine, make the new output position available |
138 |
// to the application. |
139 |
*out_pos_ptr = out_pos; |
140 |
return LZMA_OK; |
141 |
} |