1 ///////////////////////////////////////////////////////////////////////////////
3 /// \file stream_buffer_encoder.c
4 /// \brief Single-call .xz Stream encoder
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
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)
22 /// Stream Header, Stream Footer, and Index
23 #define HEADERS_BOUND (2 * LZMA_STREAM_HEADER_SIZE + INDEX_BOUND)
26 extern LZMA_API(size_t)
27 lzma_stream_buffer_bound(size_t uncompressed_size
)
29 // Get the maximum possible size of a Block.
30 const size_t block_bound
= lzma_block_buffer_bound(uncompressed_size
);
34 // Catch the possible integer overflow and also prevent the size of
35 // the Stream exceeding LZMA_VLI_MAX (theoretically possible on
37 if (my_min(SIZE_MAX
, LZMA_VLI_MAX
) - block_bound
< HEADERS_BOUND
)
40 return block_bound
+ HEADERS_BOUND
;
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
)
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
;
56 if (!lzma_check_is_supported(check
))
57 return LZMA_UNSUPPORTED_CHECK
;
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.
64 // Use a local copy. We update *out_pos_ptr only if everything
66 size_t out_pos
= *out_pos_ptr
;
68 // Check that there's enough space for both Stream Header and
70 if (out_size
- out_pos
<= 2 * LZMA_STREAM_HEADER_SIZE
)
71 return LZMA_BUF_ERROR
;
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
;
77 // Encode the Stream Header.
78 lzma_stream_flags stream_flags
= {
83 if (lzma_stream_header_encode(&stream_flags
, out
+ out_pos
)
85 return LZMA_PROG_ERROR
;
87 out_pos
+= LZMA_STREAM_HEADER_SIZE
;
89 // Encode a Block but only if there is at least one byte of input.
97 return_if_error(lzma_block_buffer_encode(&block
, allocator
,
98 in
, in_size
, out
, &out_pos
, out_size
));
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
);
107 return LZMA_MEM_ERROR
;
109 lzma_ret ret
= LZMA_OK
;
112 ret
= lzma_index_append(i
, allocator
,
113 lzma_block_unpadded_size(&block
),
114 block
.uncompressed_size
);
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
);
122 stream_flags
.backward_size
= lzma_index_size(i
);
125 lzma_index_end(i
, allocator
);
131 // Stream Footer. We have already reserved space for this.
132 if (lzma_stream_footer_encode(&stream_flags
, out
+ out_pos
)
134 return LZMA_PROG_ERROR
;
136 out_pos
+= LZMA_STREAM_HEADER_SIZE
;
138 // Everything went fine, make the new output position available
139 // to the application.
140 *out_pos_ptr
= out_pos
;