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