1 // SPDX-License-Identifier: 0BSD
3 ///////////////////////////////////////////////////////////////////////////////
5 /// \file auto_decoder.c
6 /// \brief Autodetect between .xz, .lzma (LZMA_Alone), and .lz (lzip)
8 // Author: Lasse Collin
10 ///////////////////////////////////////////////////////////////////////////////
12 #include "stream_decoder.h"
13 #include "alone_decoder.h"
14 #ifdef HAVE_LZIP_DECODER
15 # include "lzip_decoder.h"
20 /// .xz Stream decoder, LZMA_Alone decoder, or lzip decoder
35 auto_decode(void *coder_ptr
, const lzma_allocator
*allocator
,
36 const uint8_t *restrict in
, size_t *restrict in_pos
,
37 size_t in_size
, uint8_t *restrict out
,
38 size_t *restrict out_pos
, size_t out_size
, lzma_action action
)
40 lzma_auto_coder
*coder
= coder_ptr
;
42 switch (coder
->sequence
) {
44 if (*in_pos
>= in_size
)
47 // Update the sequence now, because we want to continue from
48 // SEQ_CODE even if we return some LZMA_*_CHECK.
49 coder
->sequence
= SEQ_CODE
;
51 // Detect the file format. .xz files start with 0xFD which
52 // cannot be the first byte of .lzma (LZMA_Alone) format.
53 // The .lz format starts with 0x4C which could be the
54 // first byte of a .lzma file but luckily it would mean
55 // lc/lp/pb being 4/3/1 which liblzma doesn't support because
56 // lc + lp > 4. So using just 0x4C to detect .lz is OK here.
57 if (in
[*in_pos
] == 0xFD) {
58 return_if_error(lzma_stream_decoder_init(
59 &coder
->next
, allocator
,
60 coder
->memlimit
, coder
->flags
));
61 #ifdef HAVE_LZIP_DECODER
62 } else if (in
[*in_pos
] == 0x4C) {
63 return_if_error(lzma_lzip_decoder_init(
64 &coder
->next
, allocator
,
65 coder
->memlimit
, coder
->flags
));
68 return_if_error(lzma_alone_decoder_init(&coder
->next
,
69 allocator
, coder
->memlimit
, true));
71 // If the application wants to know about missing
72 // integrity check or about the check in general, we
73 // need to handle it here, because LZMA_Alone decoder
74 // doesn't accept any flags.
75 if (coder
->flags
& LZMA_TELL_NO_CHECK
)
78 if (coder
->flags
& LZMA_TELL_ANY_CHECK
)
79 return LZMA_GET_CHECK
;
85 const lzma_ret ret
= coder
->next
.code(
86 coder
->next
.coder
, allocator
,
88 out
, out_pos
, out_size
, action
);
89 if (ret
!= LZMA_STREAM_END
90 || (coder
->flags
& LZMA_CONCATENATED
) == 0)
93 coder
->sequence
= SEQ_FINISH
;
99 // When LZMA_CONCATENATED was used and we were decoding
100 // a LZMA_Alone file, we need to check that there is no
101 // trailing garbage and wait for LZMA_FINISH.
102 if (*in_pos
< in_size
)
103 return LZMA_DATA_ERROR
;
105 return action
== LZMA_FINISH
? LZMA_STREAM_END
: LZMA_OK
;
109 return LZMA_PROG_ERROR
;
115 auto_decoder_end(void *coder_ptr
, const lzma_allocator
*allocator
)
117 lzma_auto_coder
*coder
= coder_ptr
;
118 lzma_next_end(&coder
->next
, allocator
);
119 lzma_free(coder
, allocator
);
125 auto_decoder_get_check(const void *coder_ptr
)
127 const lzma_auto_coder
*coder
= coder_ptr
;
129 // It is LZMA_Alone if get_check is NULL.
130 return coder
->next
.get_check
== NULL
? LZMA_CHECK_NONE
131 : coder
->next
.get_check(coder
->next
.coder
);
136 auto_decoder_memconfig(void *coder_ptr
, uint64_t *memusage
,
137 uint64_t *old_memlimit
, uint64_t new_memlimit
)
139 lzma_auto_coder
*coder
= coder_ptr
;
143 if (coder
->next
.memconfig
!= NULL
) {
144 ret
= coder
->next
.memconfig(coder
->next
.coder
,
145 memusage
, old_memlimit
, new_memlimit
);
146 assert(*old_memlimit
== coder
->memlimit
);
148 // No coder is configured yet. Use the base value as
149 // the current memory usage.
150 *memusage
= LZMA_MEMUSAGE_BASE
;
151 *old_memlimit
= coder
->memlimit
;
154 if (new_memlimit
!= 0 && new_memlimit
< *memusage
)
155 ret
= LZMA_MEMLIMIT_ERROR
;
158 if (ret
== LZMA_OK
&& new_memlimit
!= 0)
159 coder
->memlimit
= new_memlimit
;
166 auto_decoder_init(lzma_next_coder
*next
, const lzma_allocator
*allocator
,
167 uint64_t memlimit
, uint32_t flags
)
169 lzma_next_coder_init(&auto_decoder_init
, next
, allocator
);
171 if (flags
& ~LZMA_SUPPORTED_FLAGS
)
172 return LZMA_OPTIONS_ERROR
;
174 lzma_auto_coder
*coder
= next
->coder
;
176 coder
= lzma_alloc(sizeof(lzma_auto_coder
), allocator
);
178 return LZMA_MEM_ERROR
;
181 next
->code
= &auto_decode
;
182 next
->end
= &auto_decoder_end
;
183 next
->get_check
= &auto_decoder_get_check
;
184 next
->memconfig
= &auto_decoder_memconfig
;
185 coder
->next
= LZMA_NEXT_CODER_INIT
;
188 coder
->memlimit
= my_max(1, memlimit
);
189 coder
->flags
= flags
;
190 coder
->sequence
= SEQ_INIT
;
196 extern LZMA_API(lzma_ret
)
197 lzma_auto_decoder(lzma_stream
*strm
, uint64_t memlimit
, uint32_t flags
)
199 lzma_next_strm_init(auto_decoder_init
, strm
, memlimit
, flags
);
201 strm
->internal
->supported_actions
[LZMA_RUN
] = true;
202 strm
->internal
->supported_actions
[LZMA_FINISH
] = true;