The R Project SVN R

Rev

Rev 49813 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

///////////////////////////////////////////////////////////////////////////////
//
/// \file       subblock_encoder.c
/// \brief      Encoder of the Subblock filter
//
//  Author:     Lasse Collin
//
//  This file has been put into the public domain.
//  You can do whatever you want with this file.
//
///////////////////////////////////////////////////////////////////////////////

#include "subblock_encoder.h"
#include "filter_encoder.h"


/// Maximum number of repeats that a single Repeating Data can indicate.
/// This is directly from the file format specification.
#define REPEAT_COUNT_MAX (1U << 28)

/// Number of bytes the data chunk (not including the header part) must be
/// before we care about alignment. This is somewhat arbitrary. It just
/// doesn't make sense to waste bytes for alignment when the data chunk
/// is very small.
#define MIN_CHUNK_SIZE_FOR_ALIGN 4

/// Number of bytes of the header part of Subblock Type `Data'. This is
/// used as the `skew' argument for subblock_align().
#define ALIGN_SKEW_DATA 4

/// Like above but for Repeating Data.
#define ALIGN_SKEW_REPEATING_DATA 5

/// Writes one byte to output buffer and updates the alignment counter.
#define write_byte(b) \
do { \
    assert(*out_pos < out_size); \
    out[*out_pos] = b; \
    ++*out_pos; \
    ++coder->alignment.out_pos; \
} while (0)


struct lzma_coder_s {
    lzma_next_coder next;
    bool next_finished;

    enum {
        SEQ_FILL,
        SEQ_FLUSH,
        SEQ_RLE_COUNT_0,
        SEQ_RLE_COUNT_1,
        SEQ_RLE_COUNT_2,
        SEQ_RLE_COUNT_3,
        SEQ_RLE_SIZE,
        SEQ_RLE_DATA,
        SEQ_DATA_SIZE_0,
        SEQ_DATA_SIZE_1,
        SEQ_DATA_SIZE_2,
        SEQ_DATA_SIZE_3,
        SEQ_DATA,
        SEQ_SUBFILTER_INIT,
        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 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;

        /// Number of input bytes that we have already read but
        /// not yet started writing out. This can be different
        /// to `size' when using Subfilter. That's why we track
        /// in_pending separately for RLE (see below).
        uint32_t in_pending;
    } 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.
        uint64_t count;

        /// Like subblock.in_pending above, but for RLE.
        uint32_t in_pending;
    } rle;

    struct {
        enum {
            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;

    /// Temporary buffer used when we are not the last filter in the chain.
    struct {
        size_t pos;
        size_t size;
        uint8_t buffer[LZMA_BUFFER_SIZE];
    } temp;
};


/// \brief      Aligns the output buffer
///
/// Aligns the output buffer so that after skew bytes the output position is
/// a multiple of coder->alignment.multiple.
static bool
subblock_align(lzma_coder *coder, uint8_t *restrict out,
        size_t *restrict out_pos, size_t out_size,
        size_t chunk_size, uint32_t skew)
{
    assert(*out_pos < out_size);

    // Fix the alignment only if it makes sense at least a little.
    if (chunk_size >= MIN_CHUNK_SIZE_FOR_ALIGN) {
        const uint32_t target = coder->alignment.in_pos
                % coder->alignment.multiple;

        while ((coder->alignment.out_pos + skew)
                % coder->alignment.multiple != target) {
            // Zero indicates padding.
            write_byte(0x00);

            // Check if output buffer got full and indicate it to
            // the caller.
            if (*out_pos == out_size)
                return true;
        }
    }

    // Output buffer is not full.
    return false;
}


/// \brief      Checks if buffer contains repeated data
///
/// \param      needle      Buffer containing a single repeat chunk
/// \param      needle_size Size of needle in bytes
/// \param      buf         Buffer to search for repeated needles
/// \param      buf_chunks  Buffer size is buf_chunks * needle_size.
///
/// \return     True if the whole buf is filled with repeated needles.
///
static bool
is_repeating(const uint8_t *restrict needle, size_t needle_size,
        const uint8_t *restrict buf, size_t buf_chunks)
{
    while (buf_chunks-- != 0) {
        if (memcmp(buf, needle, needle_size) != 0)
            return false;

        buf += needle_size;
    }

    return true;
}


/// \brief      Optimizes the repeating style and updates coder->sequence
static void
subblock_rle_flush(lzma_coder *coder)
{
    // The Subblock decoder can use memset() when the size of the data
    // being repeated is one byte, so we check if the RLE buffer is
    // filled with a single repeating byte.
    if (coder->rle.size > 1) {
        const uint8_t b = coder->rle.buffer[0];
        size_t i = 0;
        while (true) {
            if (coder->rle.buffer[i] != b)
                break;

            if (++i == coder->rle.size) {
                // TODO Integer overflow check maybe,
                // although this needs at least 2**63 bytes
                // of input until it gets triggered...
                coder->rle.count *= coder->rle.size;
                coder->rle.size = 1;
                break;
            }
        }
    }

    if (coder->rle.count == 1) {
        // The buffer should be repeated only once. It is
        // waste of space to use Repeating Data. Instead,
        // write a regular Data Subblock. See SEQ_RLE_COUNT_0
        // in subblock_buffer() for more info.
        coder->tmp = coder->rle.size - 1;
    } else if (coder->rle.count > REPEAT_COUNT_MAX) {
        // There's so much to repeat that it doesn't fit into
        // 28-bit integer. We will write two or more Subblocks
        // of type Repeating Data.
        coder->tmp = REPEAT_COUNT_MAX - 1;
    } else {
        coder->tmp = coder->rle.count - 1;
    }

    coder->sequence = SEQ_RLE_COUNT_0;

    return;
}


/// \brief      Resizes coder->subblock.data for a new size limit
static lzma_ret
subblock_data_size(lzma_coder *coder, lzma_allocator *allocator,
        size_t new_limit)
{
    // Verify that the new limit is valid.
    if (new_limit < LZMA_SUBBLOCK_DATA_SIZE_MIN
            || new_limit > LZMA_SUBBLOCK_DATA_SIZE_MAX)
        return LZMA_OPTIONS_ERROR;

    // Ff the new limit is different than the previous one, we need
    // to reallocate the data buffer.
    if (new_limit != coder->subblock.limit) {
        lzma_free(coder->subblock.data, allocator);
        coder->subblock.data = lzma_alloc(new_limit, allocator);
        if (coder->subblock.data == NULL)
            return LZMA_MEM_ERROR;
    }

    coder->subblock.limit = new_limit;

    return LZMA_OK;
}


static lzma_ret
subblock_buffer(lzma_coder *coder, lzma_allocator *allocator,
        const uint8_t *restrict in, size_t *restrict in_pos,
        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->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)
                return LZMA_PROG_ERROR;
            break;

        case LZMA_SUBFILTER_SET:
            if (coder->subfilter.mode_locked
                    || coder->subfilter.mode != SUB_NONE)
                return LZMA_PROG_ERROR;

            coder->subfilter.mode = SUB_SET;
            coder->subfilter.got_input = false;

            if (coder->sequence == SEQ_FILL)
                coder->sequence = SEQ_FLUSH;

            break;

        case LZMA_SUBFILTER_RUN:
            if (coder->subfilter.mode != SUB_RUN)
                return LZMA_PROG_ERROR;

            break;

        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;
                coder->subfilter.in_avail = in_avail;

            } else if (coder->subfilter.mode != SUB_FINISH
                    || coder->subfilter.in_avail
                        != in_avail) {
                return LZMA_PROG_ERROR;
            }

            break;
        }

        default:
            return LZMA_OPTIONS_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:
        // Grab the new Subblock Data Size and reallocate the buffer.
        if (coder->subblock.size == 0 && coder->options != NULL
                && coder->options->subblock_data_size
                    != coder->subblock.limit)
            return_if_error(subblock_data_size(coder,
                    allocator, coder->options
                        ->subblock_data_size));

        if (coder->subfilter.mode == SUB_NONE) {
            assert(coder->subfilter.subcoder.code == NULL);

            // No Subfilter is enabled, just copy the data as is.
            coder->subblock.in_pending += lzma_bufcpy(
                    in, in_pos, in_size,
                    coder->subblock.data,
                    &coder->subblock.size,
                    coder->subblock.limit);

            // 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;

        } 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_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->subblock.in_pending += in_used;
            if (in_used > 0)
                coder->subfilter.got_input = true;

            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
                // left allocated for long time, and would
                // just waste memory.
                lzma_next_end(&coder->subfilter.subcoder,
                        allocator);

                // 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;
                coder->sequence = SEQ_FLUSH;
                break;
            }

            // 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;
        }

        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

    case SEQ_FLUSH:
        if (coder->options != NULL) {
            // Update the alignment variable.
            coder->alignment.multiple = coder->options->alignment;
            if (coder->alignment.multiple
                    < LZMA_SUBBLOCK_ALIGNMENT_MIN
                    || coder->alignment.multiple
                    > LZMA_SUBBLOCK_ALIGNMENT_MAX)
                return LZMA_OPTIONS_ERROR;

            // Run-length encoder
            //
            // First check if there is some data pending and we
            // have an obvious need to flush it immediatelly.
            if (coder->rle.count > 0
                    && (coder->rle.size
                            != coder->options->rle
                        || coder->subblock.size
                            % coder->rle.size)) {
                subblock_rle_flush(coder);
                break;
            }

            // Grab the (possibly new) RLE chunk size and
            // validate it.
            coder->rle.size = coder->options->rle;
            if (coder->rle.size > LZMA_SUBBLOCK_RLE_MAX)
                return LZMA_OPTIONS_ERROR;

            if (coder->subblock.size != 0
                    && coder->rle.size
                        != LZMA_SUBBLOCK_RLE_OFF
                    && coder->subblock.size
                        % coder->rle.size == 0) {

                // Initialize coder->rle.buffer if we don't
                // have RLE already running.
                if (coder->rle.count == 0)
                    memcpy(coder->rle.buffer,
                            coder->subblock.data,
                            coder->rle.size);

                // Test if coder->subblock.data is repeating.
                // If coder->rle.count would overflow, we
                // force flushing. Forced flushing shouldn't
                // really happen in real-world situations.
                const size_t count = coder->subblock.size
                        / coder->rle.size;
                if (UINT64_MAX - count > coder->rle.count
                        && is_repeating(
                            coder->rle.buffer,
                            coder->rle.size,
                            coder->subblock.data,
                            count)) {
                    coder->rle.count += count;
                    coder->rle.in_pending += coder
                            ->subblock.in_pending;
                    coder->subblock.in_pending = 0;
                    coder->subblock.size = 0;

                } else if (coder->rle.count > 0) {
                    // It's not repeating or at least not
                    // with the same byte sequence as the
                    // earlier Subblock Data buffers. We
                    // have some data pending in the RLE
                    // buffer already, so do a flush.
                    // Once flushed, we will check again
                    // if the Subblock Data happens to
                    // contain a different repeating
                    // sequence.
                    subblock_rle_flush(coder);
                    break;
                }
            }
        }

        // If we now have some data left in coder->subblock, the RLE
        // buffer is empty and we must write a regular Subblock Data.
        if (coder->subblock.size > 0) {
            assert(coder->rle.count == 0);
            coder->tmp = coder->subblock.size - 1;
            coder->sequence = SEQ_DATA_SIZE_0;
            break;
        }

        // Check if we should enable Subfilter.
        if (coder->subfilter.mode == SUB_SET) {
            if (coder->rle.count > 0)
                subblock_rle_flush(coder);
            else
                coder->sequence = SEQ_SUBFILTER_INIT;
            break;
        }

        // Check if we have just finished Subfiltering.
        if (coder->subfilter.mode == SUB_END_MARKER) {
            if (coder->rle.count > 0) {
                subblock_rle_flush(coder);
                break;
            }

            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_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 (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 {
                assert(action == LZMA_FINISH);

                // Write EOPM.
                // NOTE: No need to use write_byte() here
                // since we are finishing.
                out[*out_pos] = 0x10;
                ++*out_pos;
            }

            return LZMA_STREAM_END;
        }

        // Otherwise we have more work to do.
        coder->sequence = SEQ_FILL;
        break;

    case SEQ_RLE_COUNT_0:
        assert(coder->rle.count > 0);

        if (coder->rle.count == 1) {
            // The buffer should be repeated only once. Fix
            // the alignment and write the first byte of
            // Subblock Type `Data'.
            if (subblock_align(coder, out, out_pos, out_size,
                    coder->rle.size, ALIGN_SKEW_DATA))
                return LZMA_OK;

            write_byte(0x20 | (coder->tmp & 0x0F));

        } else {
            // We have something to actually repeat, which should
            // mean that it takes less space with run-length
            // encoding.
            if (subblock_align(coder, out, out_pos, out_size,
                        coder->rle.size,
                        ALIGN_SKEW_REPEATING_DATA))
                return LZMA_OK;

            write_byte(0x30 | (coder->tmp & 0x0F));
        }

        // NOTE: If we have to write more than one Repeating Data
        // due to rle.count > REPEAT_COUNT_MAX, the subsequent
        // Repeating Data Subblocks may get wrong alignment, because
        // we add rle.in_pending to alignment.in_pos at once instead
        // of adding only as much as this particular Repeating Data
        // consumed input data. Correct alignment is always restored
        // after all the required Repeating Data Subblocks have been
        // written. This problem occurs in such a weird cases that
        // it's not worth fixing.
        coder->alignment.out_pos += coder->rle.size;
        coder->alignment.in_pos += coder->rle.in_pending;
        coder->rle.in_pending = 0;

        coder->sequence = SEQ_RLE_COUNT_1;
        break;

    case SEQ_RLE_COUNT_1:
        write_byte(coder->tmp >> 4);
        coder->sequence = SEQ_RLE_COUNT_2;
        break;

    case SEQ_RLE_COUNT_2:
        write_byte(coder->tmp >> 12);
        coder->sequence = SEQ_RLE_COUNT_3;
        break;

    case SEQ_RLE_COUNT_3:
        write_byte(coder->tmp >> 20);

        // Again, see if we are writing regular Data or Repeating Data.
        // In the former case, we skip SEQ_RLE_SIZE.
        if (coder->rle.count == 1)
            coder->sequence = SEQ_RLE_DATA;
        else
            coder->sequence = SEQ_RLE_SIZE;

        if (coder->rle.count > REPEAT_COUNT_MAX)
            coder->rle.count -= REPEAT_COUNT_MAX;
        else
            coder->rle.count = 0;

        break;

    case SEQ_RLE_SIZE:
        assert(coder->rle.size >= LZMA_SUBBLOCK_RLE_MIN);
        assert(coder->rle.size <= LZMA_SUBBLOCK_RLE_MAX);
        write_byte(coder->rle.size - 1);
        coder->sequence = SEQ_RLE_DATA;
        break;

    case SEQ_RLE_DATA:
        lzma_bufcpy(coder->rle.buffer, &coder->pos, coder->rle.size,
                out, out_pos, out_size);
        if (coder->pos < coder->rle.size)
            return LZMA_OK;

        coder->pos = 0;
        coder->sequence = SEQ_FLUSH;
        break;

    case SEQ_DATA_SIZE_0:
        // We need four bytes for the Size field.
        if (subblock_align(coder, out, out_pos, out_size,
                coder->subblock.size, ALIGN_SKEW_DATA))
            return LZMA_OK;

        coder->alignment.out_pos += coder->subblock.size;
        coder->alignment.in_pos += coder->subblock.in_pending;
        coder->subblock.in_pending = 0;

        write_byte(0x20 | (coder->tmp & 0x0F));
        coder->sequence = SEQ_DATA_SIZE_1;
        break;

    case SEQ_DATA_SIZE_1:
        write_byte(coder->tmp >> 4);
        coder->sequence = SEQ_DATA_SIZE_2;
        break;

    case SEQ_DATA_SIZE_2:
        write_byte(coder->tmp >> 12);
        coder->sequence = SEQ_DATA_SIZE_3;
        break;

    case SEQ_DATA_SIZE_3:
        write_byte(coder->tmp >> 20);
        coder->sequence = SEQ_DATA;
        break;

    case SEQ_DATA:
        lzma_bufcpy(coder->subblock.data, &coder->pos,
                coder->subblock.size, out, out_pos, out_size);
        if (coder->pos < coder->subblock.size)
            return LZMA_OK;

        coder->subblock.size = 0;
        coder->pos = 0;
        coder->sequence = SEQ_FLUSH;
        break;

    case SEQ_SUBFILTER_INIT: {
        assert(coder->subblock.size == 0);
        assert(coder->subblock.in_pending == 0);
        assert(coder->rle.count == 0);
        assert(coder->rle.in_pending == 0);
        assert(coder->subfilter.mode == SUB_SET);
        assert(coder->options != NULL);

        // There must be a filter specified.
        if (coder->options->subfilter_options.id == LZMA_VLI_UNKNOWN)
            return LZMA_OPTIONS_ERROR;

        // Initialize a raw encoder to work as a Subfilter.
        lzma_filter options[2];
        options[0] = coder->options->subfilter_options;
        options[1].id = LZMA_VLI_UNKNOWN;

        return_if_error(lzma_raw_encoder_init(
                &coder->subfilter.subcoder, allocator,
                options));

        // Encode the Filter Flags field into a buffer. This should
        // never fail since we have already successfully initialized
        // the Subfilter itself. Check it still, and return
        // LZMA_PROG_ERROR instead of whatever the ret would say.
        lzma_ret ret = lzma_filter_flags_size(
                &coder->subfilter.flags_size, options);
        assert(ret == LZMA_OK);
        if (ret != LZMA_OK)
            return LZMA_PROG_ERROR;

        coder->subfilter.flags = lzma_alloc(
                coder->subfilter.flags_size, allocator);
        if (coder->subfilter.flags == NULL)
            return LZMA_MEM_ERROR;

        // Now we have a big-enough buffer. Encode the Filter Flags.
        // Like above, this should never fail.
        size_t dummy = 0;
        ret = lzma_filter_flags_encode(options, coder->subfilter.flags,
                &dummy, coder->subfilter.flags_size);
        assert(ret == LZMA_OK);
        assert(dummy == coder->subfilter.flags_size);
        if (ret != LZMA_OK || dummy != coder->subfilter.flags_size)
            return LZMA_PROG_ERROR;

        // Write a Subblock indicating a new Subfilter.
        write_byte(0x40);

        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 lzma_bufcpy() which doesn't write unless there is
        // output space.
    }

    // Fall through

    case SEQ_SUBFILTER_FLAGS:
        // Copy the Filter Flags to the output stream.
        lzma_bufcpy(coder->subfilter.flags, &coder->pos,
                coder->subfilter.flags_size,
                out, out_pos, out_size);
        if (coder->pos < coder->subfilter.flags_size)
            return LZMA_OK;

        lzma_free(coder->subfilter.flags, allocator);
        coder->subfilter.flags = NULL;

        coder->pos = 0;
        coder->sequence = SEQ_FILL;
        break;

    default:
        return LZMA_PROG_ERROR;
    }

    return LZMA_OK;
}


static lzma_ret
subblock_encode(lzma_coder *coder, lzma_allocator *allocator,
        const uint8_t *restrict in, size_t *restrict in_pos,
        size_t in_size, uint8_t *restrict out,
        size_t *restrict out_pos, size_t out_size, lzma_action action)
{
    if (coder->next.code == NULL)
        return subblock_buffer(coder, allocator, in, in_pos, in_size,
                out, out_pos, out_size, action);

    while (*out_pos < out_size
            && (*in_pos < in_size || action != LZMA_RUN)) {
        if (!coder->next_finished
                && coder->temp.pos == coder->temp.size) {
            coder->temp.pos = 0;
            coder->temp.size = 0;

            const lzma_ret ret = coder->next.code(coder->next.coder,
                    allocator, in, in_pos, in_size,
                    coder->temp.buffer, &coder->temp.size,
                    LZMA_BUFFER_SIZE, action);
            if (ret == LZMA_STREAM_END) {
                assert(action != LZMA_RUN);
                coder->next_finished = true;
            } else if (coder->temp.size == 0 || ret != LZMA_OK) {
                return ret;
            }
        }

        const lzma_ret ret = subblock_buffer(coder, allocator,
                coder->temp.buffer, &coder->temp.pos,
                coder->temp.size, out, out_pos, out_size,
                coder->next_finished ? LZMA_FINISH : LZMA_RUN);
        if (ret == LZMA_STREAM_END) {
            assert(action != LZMA_RUN);
            assert(coder->next_finished);
            return LZMA_STREAM_END;
        }

        if (ret != LZMA_OK)
            return ret;
    }

    return LZMA_OK;
}


static void
subblock_encoder_end(lzma_coder *coder, lzma_allocator *allocator)
{
    lzma_next_end(&coder->next, allocator);
    lzma_next_end(&coder->subfilter.subcoder, allocator);
    lzma_free(coder->subblock.data, allocator);
    lzma_free(coder->subfilter.flags, allocator);
    lzma_free(coder, allocator);
    return;
}


extern lzma_ret
lzma_subblock_encoder_init(lzma_next_coder *next, lzma_allocator *allocator,
        const lzma_filter_info *filters)
{
    if (next->coder == NULL) {
        next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
        if (next->coder == NULL)
            return LZMA_MEM_ERROR;

        next->code = &subblock_encode;
        next->end = &subblock_encoder_end;

        next->coder->next = LZMA_NEXT_CODER_INIT;
        next->coder->subblock.data = NULL;
        next->coder->subblock.limit = 0;
        next->coder->subfilter.subcoder = LZMA_NEXT_CODER_INIT;
    } else {
        lzma_next_end(&next->coder->subfilter.subcoder,
                allocator);
        lzma_free(next->coder->subfilter.flags, allocator);
    }

    next->coder->subfilter.flags = NULL;

    next->coder->next_finished = false;
    next->coder->sequence = SEQ_FILL;
    next->coder->options = filters[0].options;
    next->coder->pos = 0;

    next->coder->alignment.in_pos = 0;
    next->coder->alignment.out_pos = 0;
    next->coder->subblock.size = 0;
    next->coder->subblock.in_pending = 0;
    next->coder->rle.count = 0;
    next->coder->rle.in_pending = 0;
    next->coder->subfilter.mode = SUB_NONE;
    next->coder->subfilter.mode_locked = false;

    next->coder->temp.pos = 0;
    next->coder->temp.size = 0;

    // Grab some values from the options structure if it is available.
    size_t subblock_size_limit;
    if (next->coder->options != NULL) {
        if (next->coder->options->alignment
                    < LZMA_SUBBLOCK_ALIGNMENT_MIN
                || next->coder->options->alignment
                    > LZMA_SUBBLOCK_ALIGNMENT_MAX) {
            subblock_encoder_end(next->coder, allocator);
            return LZMA_OPTIONS_ERROR;
        }
        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;
    }

    return_if_error(subblock_data_size(next->coder, allocator,
                subblock_size_limit));

    return lzma_next_filter_init(
            &next->coder->next, allocator, filters + 1);
}