mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
Implemented LZMA_SYNC_FLUSH support to the Subblock encoder.
The API for handing Subfilters was changed to make it consistent with LZMA_SYNC_FLUSH. A few sanity checks were added for Subfilter handling. Some small bugs were fixed. More comments were added.
This commit is contained in:
parent
23c227a864
commit
e141fe1895
2 changed files with 214 additions and 78 deletions
|
@ -95,9 +95,10 @@ typedef struct {
|
|||
* input_offset % alignment == output_offset % alignment
|
||||
*
|
||||
* The Subblock filter assumes that the first output byte will be
|
||||
* written to a position in the output stream that is properly aligned.
|
||||
*
|
||||
* FIXME desc
|
||||
* written to a position in the output stream that is properly
|
||||
* aligned. This requirement is automatically met when the start
|
||||
* offset of the Stream or Block is correctly told to Block or
|
||||
* Stream encoder.
|
||||
*/
|
||||
uint32_t alignment;
|
||||
# define LZMA_SUBBLOCK_ALIGNMENT_MIN 1
|
||||
|
@ -161,16 +162,17 @@ typedef struct {
|
|||
*
|
||||
* When subfilter_mode is LZMA_SUBFILTER_NONE, the application may
|
||||
* put Subfilter options to subfilter_options structure, and then
|
||||
* set subfilter_mode to LZMA_SUBFILTER_SET. This implies setting
|
||||
* flush to true. No new input data will be read until the Subfilter
|
||||
* has been enabled. Once the Subfilter has been enabled, liblzma
|
||||
* will set subfilter_mode to LZMA_SUBFILTER_RUN.
|
||||
* set subfilter_mode to LZMA_SUBFILTER_SET. No new input data will
|
||||
* be read until the Subfilter has been enabled. Once the Subfilter
|
||||
* has been enabled, liblzma will set subfilter_mode to
|
||||
* LZMA_SUBFILTER_RUN.
|
||||
*
|
||||
* When subfilter_mode is LZMA_SUBFILTER_RUN, the application may
|
||||
* set subfilter_mode to LZMA_SUBFILTER_FINISH. No new input data
|
||||
* will be read until the Subfilter has been finished. Once the
|
||||
* Subfilter has been finished, liblzma will set subfilter_mode
|
||||
* to LZMA_SUBFILTER_NONE.
|
||||
* set subfilter_mode to LZMA_SUBFILTER_FINISH. All the input
|
||||
* currently available will be encoded before unsetting the
|
||||
* Subfilter. Application must not change the amount of available
|
||||
* input until the Subfilter has finished. Once the Subfilter has
|
||||
* finished, liblzma will set subfilter_mode to LZMA_SUBFILTER_NONE.
|
||||
*
|
||||
* If the intent is to have Subfilter enabled to the very end of
|
||||
* the data, it is not needed to separately disable Subfilter with
|
||||
|
@ -178,6 +180,11 @@ typedef struct {
|
|||
* of lzma_code() will make the Subblock encoder to disable the
|
||||
* Subfilter once all the data has been ran through the Subfilter.
|
||||
*
|
||||
* After the first call with LZMA_SYNC_FLUSH or LZMA_FINISH, the
|
||||
* application must not change subfilter_mode until LZMA_STREAM_END.
|
||||
* Setting LZMA_SUBFILTER_SET/LZMA_SUBFILTER_FINISH and
|
||||
* LZMA_SYNC_FLUSH/LZMA_FINISH _at the same time_ is fine.
|
||||
*
|
||||
* \note This variable is ignored if allow_subfilters is false.
|
||||
*/
|
||||
lzma_subfilter_mode subfilter_mode;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/// \file subblock_encoder.c
|
||||
/// \brief Encoder of the Subblock filter
|
||||
//
|
||||
// Copyright (C) 2007 Lasse Collin
|
||||
// Copyright (C) 2007, 2008 Lasse Collin
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -61,27 +61,57 @@ struct lzma_coder_s {
|
|||
SEQ_SUBFILTER_FLAGS,
|
||||
} sequence;
|
||||
|
||||
/// Pointer to the options given by the application. This is used
|
||||
/// for two-way communication with the application.
|
||||
lzma_options_subblock *options;
|
||||
|
||||
/// Position in various arrays.
|
||||
size_t pos;
|
||||
|
||||
/// Holds subblock.size - 1 or rle.size - 1 when encoding size
|
||||
/// of Data or Repeat Count.
|
||||
uint32_t tmp;
|
||||
|
||||
struct {
|
||||
/// This is a copy of options->alignment, or
|
||||
/// LZMA_SUBBLOCK_ALIGNMENT_DEFAULT if options is NULL.
|
||||
uint32_t multiple;
|
||||
|
||||
/// Number of input bytes that we have already read but
|
||||
/// not yet started writing out.
|
||||
uint32_t in_pending;
|
||||
|
||||
/// Number of input bytes which we have processed and started
|
||||
/// writing out. 32-bit integer is enough since we care only
|
||||
/// about the lowest bits when fixing alignment.
|
||||
uint32_t in_pos;
|
||||
|
||||
/// Number of bytes written out.
|
||||
uint32_t out_pos;
|
||||
} alignment;
|
||||
|
||||
struct {
|
||||
/// Pointer to allocated buffer holding the Data field
|
||||
/// of Subblock Type "Data".
|
||||
uint8_t *data;
|
||||
|
||||
/// Number of bytes in the buffer.
|
||||
size_t size;
|
||||
|
||||
/// Allocated size of the buffer.
|
||||
size_t limit;
|
||||
} subblock;
|
||||
|
||||
struct {
|
||||
/// Buffer to hold the data that may be coded with
|
||||
/// Subblock Type `Repeating Data'.
|
||||
uint8_t buffer[LZMA_SUBBLOCK_RLE_MAX];
|
||||
|
||||
/// Number of bytes in buffer[].
|
||||
size_t size;
|
||||
|
||||
/// Number of times the first `size' bytes of buffer[]
|
||||
/// will be repeated.
|
||||
lzma_vli count;
|
||||
} rle;
|
||||
|
||||
|
@ -90,15 +120,38 @@ struct lzma_coder_s {
|
|||
SUB_NONE,
|
||||
SUB_SET,
|
||||
SUB_RUN,
|
||||
SUB_FLUSH,
|
||||
SUB_FINISH,
|
||||
SUB_END_MARKER,
|
||||
} mode;
|
||||
|
||||
/// This is a copy of options->allow_subfilters. We use
|
||||
/// this to verify that the application doesn't change
|
||||
/// the value of allow_subfilters.
|
||||
bool allow;
|
||||
|
||||
/// When this is true, application is not allowed to modify
|
||||
/// options->subblock_mode. We may still modify it here.
|
||||
bool mode_locked;
|
||||
|
||||
/// True if we have encoded at least one byte of data with
|
||||
/// the Subfilter.
|
||||
bool got_input;
|
||||
|
||||
/// Track the amount of input available once
|
||||
/// LZMA_SUBFILTER_FINISH has been enabled.
|
||||
/// This is needed for sanity checking (kind
|
||||
/// of duplicating what common/code.c does).
|
||||
size_t in_avail;
|
||||
|
||||
/// Buffer for the Filter Flags field written after
|
||||
/// the `Set Subfilter' indicator.
|
||||
uint8_t *flags;
|
||||
|
||||
/// Size of Filter Flags field.
|
||||
uint32_t flags_size;
|
||||
|
||||
/// Pointers to Subfilter.
|
||||
lzma_next_coder subcoder;
|
||||
|
||||
} subfilter;
|
||||
|
@ -234,8 +287,16 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
size_t in_size, uint8_t *restrict out,
|
||||
size_t *restrict out_pos, size_t out_size, lzma_action action)
|
||||
{
|
||||
// Changing allow_subfilter is not allowed.
|
||||
if (coder->options != NULL && coder->subfilter.allow
|
||||
!= coder->options->allow_subfilters)
|
||||
return LZMA_PROG_ERROR;
|
||||
|
||||
// Check if we need to do something special with the Subfilter.
|
||||
if (coder->options != NULL && coder->options->allow_subfilters) {
|
||||
if (coder->subfilter.allow) {
|
||||
assert(coder->options != NULL);
|
||||
|
||||
// See if subfilter_mode has been changed.
|
||||
switch (coder->options->subfilter_mode) {
|
||||
case LZMA_SUBFILTER_NONE:
|
||||
if (coder->subfilter.mode != SUB_NONE)
|
||||
|
@ -243,8 +304,9 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
break;
|
||||
|
||||
case LZMA_SUBFILTER_SET:
|
||||
if (coder->subfilter.mode != SUB_NONE)
|
||||
return LZMA_HEADER_ERROR;
|
||||
if (coder->subfilter.mode_locked
|
||||
|| coder->subfilter.mode != SUB_NONE)
|
||||
return LZMA_PROG_ERROR;
|
||||
|
||||
coder->subfilter.mode = SUB_SET;
|
||||
coder->subfilter.got_input = false;
|
||||
|
@ -257,28 +319,47 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
case LZMA_SUBFILTER_RUN:
|
||||
if (coder->subfilter.mode != SUB_RUN)
|
||||
return LZMA_PROG_ERROR;
|
||||
|
||||
break;
|
||||
|
||||
case LZMA_SUBFILTER_FINISH:
|
||||
if (coder->subfilter.mode == SUB_RUN)
|
||||
case LZMA_SUBFILTER_FINISH: {
|
||||
const size_t in_avail = in_size - *in_pos;
|
||||
|
||||
if (coder->subfilter.mode == SUB_RUN) {
|
||||
if (coder->subfilter.mode_locked)
|
||||
return LZMA_PROG_ERROR;
|
||||
|
||||
coder->subfilter.mode = SUB_FINISH;
|
||||
else if (coder->subfilter.mode != SUB_FINISH)
|
||||
return LZMA_PROG_ERROR;
|
||||
coder->subfilter.in_avail = in_avail;
|
||||
|
||||
if (!coder->subfilter.got_input)
|
||||
} else if (coder->subfilter.mode != SUB_FINISH
|
||||
|| coder->subfilter.in_avail
|
||||
!= in_avail) {
|
||||
return LZMA_PROG_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return LZMA_HEADER_ERROR;
|
||||
}
|
||||
|
||||
// If we are sync-flushing or finishing, the application may
|
||||
// no longer change subfilter_mode. Note that this check is
|
||||
// done after checking the new subfilter_mode above; this
|
||||
// way the application may e.g. set LZMA_SUBFILTER_SET and
|
||||
// LZMA_SYNC_FLUSH at the same time, but it cannot modify
|
||||
// subfilter_mode on the later lzma_code() calls before
|
||||
// we have returned LZMA_STREAM_END.
|
||||
if (action != LZMA_RUN)
|
||||
coder->subfilter.mode_locked = true;
|
||||
}
|
||||
|
||||
// Main loop
|
||||
while (*out_pos < out_size)
|
||||
switch (coder->sequence) {
|
||||
case SEQ_FILL: {
|
||||
case SEQ_FILL:
|
||||
// Grab the new Subblock Data Size and reallocate the buffer.
|
||||
if (coder->subblock.size == 0 && coder->options != NULL
|
||||
&& coder->options->subblock_data_size
|
||||
|
@ -297,44 +378,77 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
&coder->subblock.size,
|
||||
coder->subblock.limit);
|
||||
|
||||
} else {
|
||||
const size_t in_start = *in_pos;
|
||||
lzma_ret ret;
|
||||
// If we ran out of input before the whole buffer
|
||||
// was filled, return to application.
|
||||
if (coder->subblock.size < coder->subblock.limit
|
||||
&& action == LZMA_RUN)
|
||||
return LZMA_OK;
|
||||
|
||||
if (coder->subfilter.mode == SUB_FINISH) {
|
||||
// Let the Subfilter write out pending data,
|
||||
// but don't give it any new input anymore.
|
||||
size_t dummy = 0;
|
||||
ret = coder->subfilter.subcoder.code(coder
|
||||
->subfilter.subcoder.coder,
|
||||
allocator, NULL, &dummy, 0,
|
||||
coder->subblock.data,
|
||||
&coder->subblock.size,
|
||||
coder->subblock.limit,
|
||||
LZMA_FINISH);
|
||||
} else {
|
||||
// Give our input data to the Subfilter. Note
|
||||
// that action can be LZMA_FINISH. In that
|
||||
// case, we filter everything until the end
|
||||
// of the input. The application isn't required
|
||||
// to separately set LZMA_SUBBLOCK_FINISH.
|
||||
ret = coder->subfilter.subcoder.code(coder
|
||||
->subfilter.subcoder.coder,
|
||||
allocator, in, in_pos, in_size,
|
||||
coder->subblock.data,
|
||||
&coder->subblock.size,
|
||||
coder->subblock.limit,
|
||||
action);
|
||||
} else {
|
||||
assert(coder->options->subfilter_mode
|
||||
!= LZMA_SUBFILTER_SET);
|
||||
|
||||
// Using LZMA_FINISH automatically toggles
|
||||
// LZMA_SUBFILTER_FINISH.
|
||||
//
|
||||
// NOTE: It is possible that application had set
|
||||
// LZMA_SUBFILTER_SET and LZMA_FINISH at the same
|
||||
// time. In that case it is possible that we will
|
||||
// cycle to LZMA_SUBFILTER_RUN, LZMA_SUBFILTER_FINISH,
|
||||
// and back to LZMA_SUBFILTER_NONE in a single
|
||||
// Subblock encoder function call.
|
||||
if (action == LZMA_FINISH) {
|
||||
coder->options->subfilter_mode
|
||||
= LZMA_SUBFILTER_FINISH;
|
||||
coder->subfilter.mode = SUB_FINISH;
|
||||
}
|
||||
|
||||
const size_t in_used = *in_pos - in_start;
|
||||
const size_t in_start = *in_pos;
|
||||
|
||||
const lzma_ret ret = coder->subfilter.subcoder.code(
|
||||
coder->subfilter.subcoder.coder,
|
||||
allocator, in, in_pos, in_size,
|
||||
coder->subblock.data,
|
||||
&coder->subblock.size,
|
||||
coder->subblock.limit,
|
||||
coder->subfilter.mode == SUB_FINISH
|
||||
? LZMA_FINISH : action);
|
||||
|
||||
const size_t in_used = *in_pos - in_start;
|
||||
coder->alignment.in_pending += in_used;
|
||||
if (in_used > 0)
|
||||
coder->subfilter.got_input = true;
|
||||
|
||||
coder->alignment.in_pending += in_used;
|
||||
coder->subfilter.in_avail = in_size - *in_pos;
|
||||
|
||||
if (ret == LZMA_STREAM_END) {
|
||||
// All currently available input must have
|
||||
// been processed.
|
||||
assert(*in_pos == in_size);
|
||||
|
||||
// Flush now. Even if coder->subblock.size
|
||||
// happened to be zero, we still need to go
|
||||
// to SEQ_FLUSH to possibly finish RLE or
|
||||
// write the Subfilter Unset indicator.
|
||||
coder->sequence = SEQ_FLUSH;
|
||||
|
||||
if (coder->subfilter.mode == SUB_RUN) {
|
||||
// Flushing with Subfilter enabled.
|
||||
assert(action == LZMA_SYNC_FLUSH);
|
||||
coder->subfilter.mode = SUB_FLUSH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Subfilter finished its job.
|
||||
assert(coder->subfilter.mode == SUB_FINISH
|
||||
|| action == LZMA_FINISH);
|
||||
|
||||
// At least one byte of input must have been
|
||||
// encoded with the Subfilter. This is
|
||||
// required by the file format specification.
|
||||
if (!coder->subfilter.got_input)
|
||||
return LZMA_PROG_ERROR;
|
||||
|
||||
// We don't strictly need to do this, but
|
||||
// doing it sounds like a good idea, because
|
||||
// otherwise the Subfilter's memory could be
|
||||
|
@ -343,35 +457,30 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
lzma_next_coder_end(&coder->subfilter.subcoder,
|
||||
allocator);
|
||||
|
||||
assert(coder->options != NULL);
|
||||
coder->options->subfilter_mode
|
||||
= LZMA_SUBFILTER_NONE;
|
||||
|
||||
assert(coder->subfilter.mode == SUB_FINISH
|
||||
|| action == LZMA_FINISH);
|
||||
// We need to flush the currently buffered
|
||||
// data and write Unset Subfilter marker.
|
||||
// Note that we cannot set
|
||||
// coder->options->subfilter_mode to
|
||||
// LZMA_SUBFILTER_NONE yet, because we
|
||||
// haven't written the Unset Subfilter
|
||||
// marker yet.
|
||||
coder->subfilter.mode = SUB_END_MARKER;
|
||||
|
||||
// Flush now. Even if coder->subblock.size
|
||||
// happens to be zero, we still need to go
|
||||
// to SEQ_FLUSH to write the Subfilter Unset
|
||||
// indicator.
|
||||
coder->sequence = SEQ_FLUSH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return if an error occurred.
|
||||
if (ret != LZMA_OK)
|
||||
// Return if we couldn't fill the buffer or
|
||||
// if an error occurred.
|
||||
if (coder->subblock.size < coder->subblock.limit
|
||||
|| ret != LZMA_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// If we ran out of input before the whole buffer
|
||||
// was filled, return to application.
|
||||
if (coder->subblock.size < coder->subblock.limit
|
||||
&& action != LZMA_FINISH)
|
||||
return LZMA_OK;
|
||||
|
||||
coder->sequence = SEQ_FLUSH;
|
||||
}
|
||||
|
||||
// SEQ_FILL doesn't produce any output so falling through
|
||||
// to SEQ_FLUSH is safe.
|
||||
assert(*out_pos < out_size);
|
||||
|
||||
// Fall through
|
||||
|
||||
|
@ -471,21 +580,33 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
break;
|
||||
}
|
||||
|
||||
write_byte(0x50);
|
||||
coder->options->subfilter_mode = LZMA_SUBFILTER_NONE;
|
||||
coder->subfilter.mode = SUB_NONE;
|
||||
|
||||
write_byte(0x50);
|
||||
if (*out_pos == out_size)
|
||||
return LZMA_OK;
|
||||
}
|
||||
|
||||
// Check if we have already written everything.
|
||||
if (action == LZMA_FINISH && *in_pos == in_size
|
||||
&& coder->subfilter.mode == SUB_NONE) {
|
||||
if (action != LZMA_RUN && *in_pos == in_size
|
||||
&& (coder->subfilter.mode == SUB_NONE
|
||||
|| coder->subfilter.mode == SUB_FLUSH)) {
|
||||
if (coder->rle.count > 0) {
|
||||
subblock_rle_flush(coder);
|
||||
break;
|
||||
}
|
||||
|
||||
if (coder->use_eopm) {
|
||||
if (action == LZMA_SYNC_FLUSH) {
|
||||
if (coder->subfilter.mode == SUB_FLUSH)
|
||||
coder->subfilter.mode = SUB_RUN;
|
||||
|
||||
coder->subfilter.mode_locked = false;
|
||||
coder->sequence = SEQ_FILL;
|
||||
|
||||
} else if (coder->use_eopm) {
|
||||
assert(action == LZMA_FINISH);
|
||||
|
||||
// NOTE: No need to use write_byte() here
|
||||
// since we are finishing.
|
||||
out[*out_pos] = 0x10;
|
||||
|
@ -586,7 +707,6 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
return LZMA_OK;
|
||||
|
||||
coder->alignment.out_pos += coder->subblock.size;
|
||||
|
||||
coder->subblock.size = 0;
|
||||
coder->pos = 0;
|
||||
coder->sequence = SEQ_FLUSH;
|
||||
|
@ -642,7 +762,12 @@ subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
|
|||
|
||||
coder->options->subfilter_mode = LZMA_SUBFILTER_RUN;
|
||||
coder->subfilter.mode = SUB_RUN;
|
||||
coder->alignment.out_pos += coder->subfilter.flags_size;
|
||||
coder->sequence = SEQ_SUBFILTER_FLAGS;
|
||||
|
||||
// It is safe to fall through because SEQ_SUBFILTER_FLAGS
|
||||
// uses bufcpy() which doesn't write unless there is output
|
||||
// space.
|
||||
}
|
||||
|
||||
// Fall through
|
||||
|
@ -681,7 +806,7 @@ subblock_encode(lzma_coder *coder, lzma_allocator *allocator,
|
|||
out, out_pos, out_size, action);
|
||||
|
||||
while (*out_pos < out_size
|
||||
&& (*in_pos < in_size || action == LZMA_FINISH)) {
|
||||
&& (*in_pos < in_size || action != LZMA_RUN)) {
|
||||
if (!coder->next_finished
|
||||
&& coder->temp.pos == coder->temp.size) {
|
||||
coder->temp.pos = 0;
|
||||
|
@ -692,7 +817,7 @@ subblock_encode(lzma_coder *coder, lzma_allocator *allocator,
|
|||
coder->temp.buffer, &coder->temp.size,
|
||||
LZMA_BUFFER_SIZE, action);
|
||||
if (ret == LZMA_STREAM_END) {
|
||||
assert(action == LZMA_FINISH);
|
||||
assert(action != LZMA_RUN);
|
||||
coder->next_finished = true;
|
||||
} else if (coder->temp.size == 0 || ret != LZMA_OK) {
|
||||
return ret;
|
||||
|
@ -704,7 +829,7 @@ subblock_encode(lzma_coder *coder, lzma_allocator *allocator,
|
|||
coder->temp.size, out, out_pos, out_size,
|
||||
coder->next_finished ? LZMA_FINISH : LZMA_RUN);
|
||||
if (ret == LZMA_STREAM_END) {
|
||||
assert(action == LZMA_FINISH);
|
||||
assert(action != LZMA_RUN);
|
||||
assert(coder->next_finished);
|
||||
return LZMA_STREAM_END;
|
||||
}
|
||||
|
@ -765,6 +890,7 @@ lzma_subblock_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
|
|||
next->coder->subblock.size = 0;
|
||||
next->coder->rle.count = 0;
|
||||
next->coder->subfilter.mode = SUB_NONE;
|
||||
next->coder->subfilter.mode_locked = false;
|
||||
|
||||
next->coder->temp.pos = 0;
|
||||
next->coder->temp.size = 0;
|
||||
|
@ -781,10 +907,13 @@ lzma_subblock_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
|
|||
}
|
||||
next->coder->alignment.multiple
|
||||
= next->coder->options->alignment;
|
||||
next->coder->subfilter.allow
|
||||
= next->coder->options->allow_subfilters;
|
||||
subblock_size_limit = next->coder->options->subblock_data_size;
|
||||
} else {
|
||||
next->coder->alignment.multiple
|
||||
= LZMA_SUBBLOCK_ALIGNMENT_DEFAULT;
|
||||
next->coder->subfilter.allow = false;
|
||||
subblock_size_limit = LZMA_SUBBLOCK_DATA_SIZE_DEFAULT;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue