From 64c261113f3ac1036815426225dd19a229a56cb3 Mon Sep 17 00:00:00 2001 From: Zoltan Szabadka Date: Wed, 22 Apr 2015 17:33:21 +0200 Subject: [PATCH] Add partial output support to the decoder. --- dec/decode.c | 240 ++++++++++++++++++++++++++++++++++++++------------ dec/decode.h | 4 +- dec/state.h | 10 +++ dec/streams.c | 5 +- 4 files changed, 202 insertions(+), 57 deletions(-) diff --git a/dec/decode.c b/dec/decode.c index b0374c5..674dbfb 100644 --- a/dec/decode.c +++ b/dec/decode.c @@ -191,7 +191,7 @@ static BrotliResult ReadHuffmanCodeLengths( const HuffmanCode* p = s->table; uint8_t code_len; if (!BrotliReadMoreInput(br)) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } BrotliFillBitWindow(br); p += (br->val_ >> br->bit_pos_) & 31; @@ -259,7 +259,7 @@ static BrotliResult ReadHuffmanCode(int alphabet_size, switch(s->sub_state[1]) { case BROTLI_STATE_SUB_NONE: if (!BrotliReadMoreInput(br)) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } s->code_lengths = (uint8_t*)BrotliSafeMalloc((uint64_t)alphabet_size, @@ -471,7 +471,7 @@ static BrotliResult DecodeContextMap(int context_map_size, switch(s->sub_state[0]) { case BROTLI_STATE_SUB_NONE: if (!BrotliReadMoreInput(br)) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } *num_htrees = DecodeVarLenUint8(br) + 1; @@ -512,7 +512,7 @@ static BrotliResult DecodeContextMap(int context_map_size, while (s->context_index < context_map_size) { int code; if (!BrotliReadMoreInput(br)) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } code = ReadSymbol(s->context_map_table, br); if (code == 0) { @@ -647,6 +647,7 @@ BrotliResult CopyUncompressedBlockToOutput(BrotliOutput output, int br_pos = s->br.pos_ & BROTLI_IBUF_MASK; uint32_t remaining_bits; int num_read; + int num_written; /* State machine */ for (;;) { @@ -691,13 +692,24 @@ BrotliResult CopyUncompressedBlockToOutput(BrotliOutput output, rb_pos += s->nbytes; s->meta_block_remaining_len -= s->nbytes; + s->partially_written = 0; + s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_1; + /* No break, continue to next state */ + case BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_1: /* If we wrote past the logical end of the ringbuffer, copy the tail of the ringbuffer to its beginning and flush the ringbuffer to the output. */ if (rb_pos >= rb_size) { - if (BrotliWrite(output, s->ringbuffer, (size_t)rb_size) < rb_size) { + num_written = BrotliWrite(output, + s->ringbuffer + s->partially_written, + (size_t)(rb_size - s->partially_written)); + if (num_written < 0) { return BROTLI_RESULT_ERROR; } + s->partially_written += num_written; + if (s->partially_written < rb_size) { + return BROTLI_RESULT_NEEDS_MORE_OUTPUT; + } rb_pos -= rb_size; s->meta_block_remaining_len += rb_size; memcpy(s->ringbuffer, ringbuffer_end, (size_t)rb_pos); @@ -707,37 +719,66 @@ BrotliResult CopyUncompressedBlockToOutput(BrotliOutput output, case BROTLI_STATE_SUB_UNCOMPRESSED_SHORT: while (s->meta_block_remaining_len > 0) { if (!BrotliReadMoreInput(&s->br)) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } s->ringbuffer[rb_pos++] = (uint8_t)BrotliReadBits(&s->br, 8); if (rb_pos == rb_size) { - if (BrotliWrite(output, s->ringbuffer, (size_t)rb_size) < rb_size) { - return BROTLI_RESULT_ERROR; - } - rb_pos = 0; + s->partially_written = 0; + s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_2; + break; } s->meta_block_remaining_len--; } - s->sub_state[0] = BROTLI_STATE_SUB_NONE; - return BROTLI_RESULT_SUCCESS; + if (s->sub_state[0] == BROTLI_STATE_SUB_UNCOMPRESSED_SHORT) { + s->sub_state[0] = BROTLI_STATE_SUB_NONE; + return BROTLI_RESULT_SUCCESS; + } + /* No break, if state is updated, continue to next state */ + case BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_2: + num_written = BrotliWrite(output, s->ringbuffer + s->partially_written, + (size_t)(rb_size - s->partially_written)); + if (num_written < 0) { + return BROTLI_RESULT_ERROR; + } + s->partially_written += num_written; + if (s->partially_written < rb_size) { + return BROTLI_RESULT_NEEDS_MORE_OUTPUT; + } + rb_pos = 0; + s->meta_block_remaining_len--; + s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_SHORT; + break; case BROTLI_STATE_SUB_UNCOMPRESSED_FILL: /* If we have more to copy than the remaining size of the ringbuffer, then we first fill the ringbuffer from the input and then flush the ringbuffer to the output */ - while (rb_pos + s->meta_block_remaining_len >= rb_size) { + if (rb_pos + s->meta_block_remaining_len >= rb_size) { s->nbytes = rb_size - rb_pos; if (BrotliRead(s->br.input_, &s->ringbuffer[rb_pos], (size_t)s->nbytes) < s->nbytes) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } - if (BrotliWrite(output, s->ringbuffer, (size_t)rb_size) < s->nbytes) { - return BROTLI_RESULT_ERROR; - } - s->meta_block_remaining_len -= s->nbytes; - rb_pos = 0; + s->partially_written = 0; + s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_3; + } else { + s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_COPY; + break; } - s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_COPY; /* No break, continue to next state */ + case BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_3: + num_written = BrotliWrite(output, s->ringbuffer + s->partially_written, + (size_t)(rb_size - s->partially_written)); + if (num_written < 0) { + return BROTLI_RESULT_ERROR; + } + s->partially_written += num_written; + if (s->partially_written < rb_size) { + return BROTLI_RESULT_NEEDS_MORE_OUTPUT; + } + s->meta_block_remaining_len -= s->nbytes; + rb_pos = 0; + s->sub_state[0] = BROTLI_STATE_SUB_UNCOMPRESSED_FILL; + break; case BROTLI_STATE_SUB_UNCOMPRESSED_COPY: /* Copy straight from the input onto the ringbuffer. The ringbuffer will be flushed to the output at a later time. */ @@ -745,7 +786,7 @@ BrotliResult CopyUncompressedBlockToOutput(BrotliOutput output, (size_t)s->meta_block_remaining_len); s->meta_block_remaining_len -= num_read; if (s->meta_block_remaining_len > 0) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } /* Restore the state of the bit reader. */ @@ -754,7 +795,7 @@ BrotliResult CopyUncompressedBlockToOutput(BrotliOutput output, /* No break, continue to next state */ case BROTLI_STATE_SUB_UNCOMPRESSED_WARMUP: if (!BrotliWarmupBitReader(&s->br)) { - return BROTLI_RESULT_PARTIAL; + return BROTLI_RESULT_NEEDS_MORE_INPUT; } s->sub_state[0] = BROTLI_STATE_SUB_NONE; return BROTLI_RESULT_SUCCESS; @@ -845,7 +886,7 @@ BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output) { BrotliResult result; BrotliStateInit(&s); result = BrotliDecompressStreaming(input, output, 1, &s); - if (result == BROTLI_RESULT_PARTIAL) { + if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) { /* Not ok: it didn't finish even though this is a non-streaming function. */ result = BROTLI_RESULT_ERROR; } @@ -889,6 +930,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, BrotliBitReader* br = &s->br; int initial_remaining_len; int bytes_copied; + int num_written; /* We need the slack region for the following reasons: - always doing two 8-byte copies for fast backward copying @@ -901,7 +943,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* State machine */ for (;;) { if (result != BROTLI_RESULT_SUCCESS) { - if (result == BROTLI_RESULT_PARTIAL && finish) { + if (result == BROTLI_RESULT_NEEDS_MORE_INPUT && finish) { printf("Unexpected end of input. State: %d\n", s->state); result = BROTLI_RESULT_ERROR; } @@ -930,7 +972,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* No break, continue to next state */ case BROTLI_STATE_BITREADER_WARMUP: if (!BrotliWarmupBitReader(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } /* Decode window size. */ @@ -961,10 +1003,11 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* No break, continue to next state */ case BROTLI_STATE_METABLOCK_BEGIN: if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } if (s->input_end) { + s->partially_written = 0; s->state = BROTLI_STATE_DONE; break; } @@ -1002,7 +1045,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* No break, continue to next state */ case BROTLI_STATE_METABLOCK_HEADER_1: if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } BROTLI_LOG_UINT(pos); @@ -1042,6 +1085,9 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, initial_remaining_len = s->meta_block_remaining_len; /* pos is given as argument since s->pos is only updated at the end. */ result = CopyUncompressedBlockToOutput(output, pos, s); + if (result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) { + break; + } bytes_copied = initial_remaining_len - s->meta_block_remaining_len; pos += bytes_copied; if (bytes_copied > 0) { @@ -1055,7 +1101,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, case BROTLI_STATE_METADATA: for (; s->meta_block_remaining_len > 0; --s->meta_block_remaining_len) { if (!BrotliReadMoreInput(&s->br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } /* Read one byte and ignore it. */ @@ -1104,7 +1150,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, break; case BROTLI_STATE_METABLOCK_HEADER_2: if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } s->distance_postfix_bits = (int)BrotliReadBits(br, 2); @@ -1178,7 +1224,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* Block decoding is the inner loop, jumping with goto makes it 3% faster */ BlockBegin: if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } if (s->meta_block_remaining_len <= 0) { @@ -1227,7 +1273,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, if (s->trivial_literal_context) { while (i < s->insert_length) { if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } if (s->block_length[0] == 0) { @@ -1241,19 +1287,19 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, BROTLI_LOG_UINT(s->literal_htree_index); BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos & s->ringbuffer_mask); if ((pos & s->ringbuffer_mask) == s->ringbuffer_mask) { - if (BrotliWrite(output, s->ringbuffer, - (size_t)s->ringbuffer_size) < 0) { - result = BROTLI_RESULT_ERROR; - break; - } + s->partially_written = 0; + s->state = BROTLI_STATE_BLOCK_INNER_WRITE; + break; } + /* Modifications to this code shold be reflected in + BROTLI_STATE_BLOCK_INNER_WRITE case */ ++pos; ++i; } } else { while (i < s->insert_length) { if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } if (s->block_length[0] == 0) { @@ -1273,18 +1319,18 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, BROTLI_LOG_UINT(s->literal_htree_index); BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos & s->ringbuffer_mask); if ((pos & s->ringbuffer_mask) == s->ringbuffer_mask) { - if (BrotliWrite(output, s->ringbuffer, - (size_t)s->ringbuffer_size) < 0) { - result = BROTLI_RESULT_ERROR; - break; - } + s->partially_written = 0; + s->state = BROTLI_STATE_BLOCK_INNER_WRITE; + break; } + /* Modifications to this code shold be reflected in + BROTLI_STATE_BLOCK_INNER_WRITE case */ ++pos; ++i; } } - - if (result != BROTLI_RESULT_SUCCESS) break; + if (result != BROTLI_RESULT_SUCCESS || + s->state == BROTLI_STATE_BLOCK_INNER_WRITE) break; s->meta_block_remaining_len -= s->insert_length; if (s->meta_block_remaining_len <= 0) { @@ -1299,7 +1345,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* No break, go to next state */ case BROTLI_STATE_BLOCK_DISTANCE: if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } assert(s->distance_code < 0); @@ -1337,7 +1383,7 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, /* No break, go to next state */ case BROTLI_STATE_BLOCK_POST: if (!BrotliReadMoreInput(br)) { - result = BROTLI_RESULT_PARTIAL; + result = BROTLI_RESULT_NEEDS_MORE_INPUT; break; } /* Convert the distance code to the actual distance by possibly */ @@ -1377,11 +1423,21 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, pos += len; s->meta_block_remaining_len -= len; if (s->copy_dst >= s->ringbuffer_end) { - if (BrotliWrite(output, s->ringbuffer, - (size_t)s->ringbuffer_size) < 0) { + s->partially_written = 0; + num_written = BrotliWrite(output, s->ringbuffer, + (size_t)s->ringbuffer_size); + if (num_written < 0) { result = BROTLI_RESULT_ERROR; break; } + s->partially_written += num_written; + if (s->partially_written < s->ringbuffer_size) { + result = BROTLI_RESULT_NEEDS_MORE_OUTPUT; + s->state = BROTLI_STATE_BLOCK_POST_WRITE_1; + break; + } + /* Modifications to this code shold be reflected in + BROTLI_STATE_BLOCK_POST_WRITE_1 case */ memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)(s->copy_dst - s->ringbuffer_end)); } @@ -1431,22 +1487,35 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, s->copy_length = 0; } #endif - + /* Modifications to this loop shold be reflected in + BROTLI_STATE_BLOCK_POST_WRITE_2 case */ for (i = 0; i < s->copy_length; ++i) { s->ringbuffer[pos & s->ringbuffer_mask] = s->ringbuffer[(pos - s->distance) & s->ringbuffer_mask]; if ((pos & s->ringbuffer_mask) == s->ringbuffer_mask) { - if (BrotliWrite(output, s->ringbuffer, - (size_t)s->ringbuffer_size) < 0) { + s->partially_written = 0; + num_written = BrotliWrite(output, s->ringbuffer, + (size_t)s->ringbuffer_size); + if (num_written < 0) { result = BROTLI_RESULT_ERROR; break; } + s->partially_written += num_written; + if (s->partially_written < s->ringbuffer_size) { + result = BROTLI_RESULT_NEEDS_MORE_OUTPUT; + s->state = BROTLI_STATE_BLOCK_POST_WRITE_2; + break; + } } ++pos; --s->meta_block_remaining_len; } + if (result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) { + break; + } } - + /* No break, continue to next state */ + case BROTLI_STATE_BLOCK_POST_CONTINUE: /* When we get here, we must have inserted at least one literal and */ /* made a copy of at least length two, therefore accessing the last 2 */ /* bytes is valid. */ @@ -1454,6 +1523,62 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, s->prev_byte2 = s->ringbuffer[(pos - 2) & s->ringbuffer_mask]; s->state = BROTLI_STATE_BLOCK_BEGIN; goto BlockBegin; + case BROTLI_STATE_BLOCK_INNER_WRITE: + case BROTLI_STATE_BLOCK_POST_WRITE_1: + case BROTLI_STATE_BLOCK_POST_WRITE_2: + num_written = BrotliWrite( + output, s->ringbuffer + s->partially_written, + (size_t)(s->ringbuffer_size - s->partially_written)); + if (num_written < 0) { + result = BROTLI_RESULT_ERROR; + break; + } + s->partially_written += num_written; + if (s->partially_written < s->ringbuffer_size) { + result = BROTLI_RESULT_NEEDS_MORE_OUTPUT; + break; + } + if (s->state == BROTLI_STATE_BLOCK_POST_WRITE_1) { + memcpy(s->ringbuffer, s->ringbuffer_end, + (size_t)(s->copy_dst - s->ringbuffer_end)); + s->state = BROTLI_STATE_BLOCK_POST_CONTINUE; + } else if (s->state == BROTLI_STATE_BLOCK_POST_WRITE_2) { + /* The tail of "i < s->copy_length" loop. */ + ++pos; + --s->meta_block_remaining_len; + ++i; + /* Reenter the loop. */ + for (; i < s->copy_length; ++i) { + s->ringbuffer[pos & s->ringbuffer_mask] = + s->ringbuffer[(pos - s->distance) & s->ringbuffer_mask]; + if ((pos & s->ringbuffer_mask) == s->ringbuffer_mask) { + s->partially_written = 0; + num_written = BrotliWrite(output, s->ringbuffer, + (size_t)s->ringbuffer_size); + if (num_written < 0) { + result = BROTLI_RESULT_ERROR; + break; + } + s->partially_written += num_written; + if (s->partially_written < s->ringbuffer_size) { + result = BROTLI_RESULT_NEEDS_MORE_OUTPUT; + break; + } + } + ++pos; + --s->meta_block_remaining_len; + } + if (result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) { + break; + } + s->state = BROTLI_STATE_BLOCK_POST_CONTINUE; + } else { /* BROTLI_STATE_BLOCK_INNER_WRITE */ + /* The tail of "i < s->insert_length" loop. */ + ++pos; + ++i; + s->state = BROTLI_STATE_BLOCK_INNER; + } + break; case BROTLI_STATE_METABLOCK_DONE: if (s->context_modes != 0) { free(s->context_modes); @@ -1476,10 +1601,17 @@ BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, break; case BROTLI_STATE_DONE: if (s->ringbuffer != 0) { - if (BrotliWrite(output, s->ringbuffer, - (size_t)(pos & s->ringbuffer_mask)) < 0) { + num_written = BrotliWrite( + output, s->ringbuffer + s->partially_written, + (size_t)((pos & s->ringbuffer_mask) - s->partially_written)); + if (num_written < 0) { result = BROTLI_RESULT_ERROR; } + s->partially_written += num_written; + if (s->partially_written < (pos & s->ringbuffer_mask)) { + result = BROTLI_RESULT_NEEDS_MORE_OUTPUT; + break; + } } if (!JumpToByteBoundary(&s->br)) { result = BROTLI_RESULT_ERROR; diff --git a/dec/decode.h b/dec/decode.h index 9efd34a..834a7b5 100644 --- a/dec/decode.h +++ b/dec/decode.h @@ -32,7 +32,9 @@ typedef enum { /* Successfully completely done */ BROTLI_RESULT_SUCCESS = 1, /* Partially done, but must be called again with more input */ - BROTLI_RESULT_PARTIAL = 2 + BROTLI_RESULT_NEEDS_MORE_INPUT = 2, + /* Partially done, but must be called again with more output */ + BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3 } BrotliResult; /* Sets *decoded_size to the decompressed size of the given encoded stream. */ diff --git a/dec/state.h b/dec/state.h index aa5b43a..e5d6b5a 100644 --- a/dec/state.h +++ b/dec/state.h @@ -40,7 +40,11 @@ typedef enum { BROTLI_STATE_BLOCK_POST = 16, BROTLI_STATE_UNCOMPRESSED = 17, BROTLI_STATE_METADATA = 18, + BROTLI_STATE_BLOCK_INNER_WRITE = 19, BROTLI_STATE_METABLOCK_DONE = 20, + BROTLI_STATE_BLOCK_POST_WRITE_1 = 21, + BROTLI_STATE_BLOCK_POST_WRITE_2 = 22, + BROTLI_STATE_BLOCK_POST_CONTINUE = 23, BROTLI_STATE_HUFFMAN_CODE_0 = 30, BROTLI_STATE_HUFFMAN_CODE_1 = 31, BROTLI_STATE_HUFFMAN_CODE_2 = 32, @@ -52,6 +56,9 @@ typedef enum { BROTLI_STATE_SUB_UNCOMPRESSED_FILL = 52, BROTLI_STATE_SUB_UNCOMPRESSED_COPY = 53, BROTLI_STATE_SUB_UNCOMPRESSED_WARMUP = 54, + BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_1 = 55, + BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_2 = 56, + BROTLI_STATE_SUB_UNCOMPRESSED_WRITE_3 = 57, BROTLI_STATE_SUB_HUFFMAN_LENGTH_BEGIN = 60, BROTLI_STATE_SUB_HUFFMAN_LENGTH_SYMBOLS = 61, BROTLI_STATE_SUB_HUFFMAN_DONE = 62, @@ -133,6 +140,9 @@ typedef struct { /* For CopyUncompressedBlockToOutput */ int nbytes; + /* For partial write operations */ + int partially_written; + /* For HuffmanTreeGroupDecode */ int htrees_decoded; diff --git a/dec/streams.c b/dec/streams.c index 623d417..b33f7a4 100644 --- a/dec/streams.c +++ b/dec/streams.c @@ -51,8 +51,9 @@ BrotliInput BrotliInitMemInput(const uint8_t* buffer, size_t length, int BrotliMemOutputFunction(void* data, const uint8_t* buf, size_t count) { BrotliMemOutput* output = (BrotliMemOutput*)data; - if (output->pos + count > output->length) { - return -1; + size_t limit = output->length - output->pos; + if (count > limit) { + count = limit; } memcpy(output->buffer + output->pos, buf, count); output->pos += count;