Decoder API: added API to attach metadata blocks callbacks

PiperOrigin-RevId: 505734532
This commit is contained in:
Evgenii Kliuchnikov 2023-01-30 18:34:24 +00:00 committed by Evgenii Kliuchnikov
parent 04f294b18a
commit b2c86d1871
6 changed files with 184 additions and 13 deletions

View File

@ -341,6 +341,11 @@ static BROTLI_INLINE BROTLI_BOOL BrotliJumpToByteBoundary(BrotliBitReader* br) {
return TO_BROTLI_BOOL(pad_bits == 0);
}
static BROTLI_INLINE void BrotliDropBytes(BrotliBitReader* br, size_t num) {
br->avail_in -= num;
br->next_in += num;
}
/* Copies remaining input bytes stored in the bit reader to the output. Value
|num| may not be larger than BrotliGetRemainingBytes. The bit reader must be
warmed up again after this. */
@ -352,9 +357,10 @@ static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest,
++dest;
--num;
}
memcpy(dest, br->next_in, num);
br->avail_in -= num;
br->next_in += num;
if (num > 0) {
memcpy(dest, br->next_in, num);
BrotliDropBytes(br, num);
}
}
#if defined(__cplusplus) || defined(c_plusplus)

View File

@ -1354,6 +1354,57 @@ static BROTLI_BOOL BROTLI_NOINLINE BrotliEnsureRingBuffer(
return BROTLI_TRUE;
}
static BrotliDecoderErrorCode BROTLI_NOINLINE
SkipMetadataBlock(BrotliDecoderState* s) {
BrotliBitReader* br = &s->br;
if (s->meta_block_remaining_len == 0) {
return BROTLI_DECODER_SUCCESS;
}
BROTLI_DCHECK((BrotliGetAvailableBits(br) & 7) == 0);
/* Drain accumulator. */
if (BrotliGetAvailableBits(br) >= 8) {
uint8_t buffer[8];
int nbytes = (int)(BrotliGetAvailableBits(br)) >> 3;
BROTLI_DCHECK(nbytes <= 8);
if (nbytes > s->meta_block_remaining_len) {
nbytes = s->meta_block_remaining_len;
}
BrotliCopyBytes(buffer, br, (size_t)nbytes);
if (s->metadata_chunk_func) {
s->metadata_chunk_func(s->metadata_callback_opaque, buffer,
(size_t)nbytes);
}
s->meta_block_remaining_len -= nbytes;
if (s->meta_block_remaining_len == 0) {
return BROTLI_DECODER_SUCCESS;
}
}
/* Direct access to metadata is possible. */
int nbytes = (int)BrotliGetRemainingBytes(br);
if (nbytes > s->meta_block_remaining_len) {
nbytes = s->meta_block_remaining_len;
}
if (nbytes > 0) {
if (s->metadata_chunk_func) {
s->metadata_chunk_func(s->metadata_callback_opaque, br->next_in,
(size_t)nbytes);
}
BrotliDropBytes(br, (size_t)nbytes);
s->meta_block_remaining_len -= nbytes;
if (s->meta_block_remaining_len == 0) {
return BROTLI_DECODER_SUCCESS;
}
}
BROTLI_DCHECK(BrotliGetRemainingBytes(br) == 0);
return BROTLI_DECODER_NEEDS_MORE_INPUT;
}
static BrotliDecoderErrorCode BROTLI_NOINLINE CopyUncompressedBlockToOutput(
size_t* available_out, uint8_t** next_out, size_t* total_out,
BrotliDecoderState* s) {
@ -2414,6 +2465,10 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
}
if (s->is_metadata) {
s->state = BROTLI_STATE_METADATA;
if (s->metadata_start_func) {
s->metadata_start_func(s->metadata_callback_opaque,
(size_t)s->meta_block_remaining_len);
}
break;
}
if (s->meta_block_remaining_len == 0) {
@ -2506,17 +2561,11 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
}
case BROTLI_STATE_METADATA:
for (; s->meta_block_remaining_len > 0; --s->meta_block_remaining_len) {
uint32_t bits;
/* Read one byte and ignore it. */
if (!BrotliSafeReadBits(br, 8, &bits)) {
result = BROTLI_DECODER_NEEDS_MORE_INPUT;
break;
}
}
if (result == BROTLI_DECODER_SUCCESS) {
s->state = BROTLI_STATE_METABLOCK_DONE;
result = SkipMetadataBlock(s);
if (result != BROTLI_DECODER_SUCCESS) {
break;
}
s->state = BROTLI_STATE_METABLOCK_DONE;
break;
case BROTLI_STATE_METABLOCK_HEADER_2: {
@ -2785,6 +2834,15 @@ uint32_t BrotliDecoderVersion(void) {
return BROTLI_VERSION;
}
void BrotliDecoderSetMetadataCallbacks(
BrotliDecoderState* state,
brotli_decoder_metadata_start_func start_func,
brotli_decoder_metadata_chunk_func chunk_func, void* opaque) {
state->metadata_start_func = start_func;
state->metadata_chunk_func = chunk_func;
state->metadata_callback_opaque = opaque;
}
/* Escalate internal functions visibility; for testing purposes only. */
#if defined(BROTLI_TEST)
BROTLI_BOOL SafeReadSymbolForTest(

View File

@ -89,6 +89,10 @@ BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
BrotliSharedDictionaryCreateInstance(alloc_func, free_func, opaque);
if (!s->dictionary) return BROTLI_FALSE;
s->metadata_start_func = NULL;
s->metadata_chunk_func = NULL;
s->metadata_callback_opaque = 0;
return BROTLI_TRUE;
}

View File

@ -9,6 +9,7 @@
#ifndef BROTLI_DEC_STATE_H_
#define BROTLI_DEC_STATE_H_
#include <brotli/decode.h>
#include <brotli/shared_dictionary.h>
#include <brotli/types.h>
@ -322,6 +323,10 @@ struct BrotliDecoderStateStruct {
/* Less used attributes are at the end of this struct. */
brotli_decoder_metadata_start_func metadata_start_func;
brotli_decoder_metadata_chunk_func metadata_chunk_func;
void* metadata_callback_opaque;
/* For reporting. */
uint64_t used_input; /* how many bytes of input are consumed */

View File

@ -361,6 +361,47 @@ BROTLI_DEC_API const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c);
*/
BROTLI_DEC_API uint32_t BrotliDecoderVersion(void);
/**
* Callback to fire on metadata block start.
*
* After this callback is fired, if @p size is not @c 0, it is followed by
* ::brotli_decoder_metadata_chunk_func as more metadata block contents become
* accessible.
*
* @param opaque callback handle
* @param size size of metadata block
*/
typedef void (*brotli_decoder_metadata_start_func)(void* opaque, size_t size);
/**
* Callback to fire on metadata block chunk becomes available.
*
* This function can be invoked multiple times per metadata block; block should
* be considered finished when sum of @p size matches the announced metadata
* block size. Chunks contents pointed by @p data are transient and shouln not
* be accessed after leaving the callback.
*
* @param opaque callback handle
* @param data pointer to metadata contents
* @param size size of metadata block chunk, at least @c 1
*/
typedef void (*brotli_decoder_metadata_chunk_func)(void* opaque,
const uint8_t* data,
size_t size);
/**
* Sets callback for receiving metadata blocks.
*
* @param state decoder instance
* @param start_func callback on metadata block start
* @param chunk_func callback on metadata block chunk
* @param opaque callback handle
*/
BROTLI_DEC_API void BrotliDecoderSetMetadataCallbacks(
BrotliDecoderState* state,
brotli_decoder_metadata_start_func start_func,
brotli_decoder_metadata_chunk_func chunk_func, void* opaque);
#if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */
#endif

View File

@ -23,6 +23,14 @@ decode.h \- API for Brotli decompression\&.
.in +1c
.ti -1c
.RI "typedef void(* \fBbrotli_decoder_metadata_chunk_func\fP) (void *opaque, const uint8_t *data, size_t size)"
.br
.RI "\fICallback to fire on metadata block chunk becomes available\&. \fP"
.ti -1c
.RI "typedef void(* \fBbrotli_decoder_metadata_start_func\fP) (void *opaque, size_t size)"
.br
.RI "\fICallback to fire on metadata block start\&. \fP"
.ti -1c
.RI "typedef enum \fBBrotliDecoderParameter\fP \fBBrotliDecoderParameter\fP"
.br
.RI "\fIOptions to be used with \fBBrotliDecoderSetParameter\fP\&. \fP"
@ -76,6 +84,10 @@ decode.h \- API for Brotli decompression\&.
.br
.RI "\fIChecks if instance has already consumed input\&. \fP"
.ti -1c
.RI "void \fBBrotliDecoderSetMetadataCallbacks\fP (\fBBrotliDecoderState\fP *state, \fBbrotli_decoder_metadata_start_func\fP start_func, \fBbrotli_decoder_metadata_chunk_func\fP chunk_func, void *opaque)"
.br
.RI "\fISets callback for receiving metadata blocks\&. \fP"
.ti -1c
.RI "\fBBROTLI_BOOL\fP \fBBrotliDecoderSetParameter\fP (\fBBrotliDecoderState\fP *state, \fBBrotliDecoderParameter\fP param, uint32_t value)"
.br
.RI "\fISets the specified parameter to the given decoder instance\&. \fP"
@ -123,6 +135,34 @@ BROTLI_DECODER_ERROR_CODES_LIST(CASE_, NEWLINE_)
The value of the last error code, negative integer\&. All other error code values are in the range from \fBBROTLI_LAST_ERROR_CODE\fP to \fC-1\fP\&. There are also 4 other possible non-error codes \fC0\fP \&.\&. \fC3\fP in \fBBrotliDecoderErrorCode\fP enumeration\&.
.SH "Typedef Documentation"
.PP
.SS "typedef void(* brotli_decoder_metadata_chunk_func) (void *opaque, const uint8_t *data, size_t size)"
.PP
Callback to fire on metadata block chunk becomes available\&. This function can be invoked multiple times per metadata block; block should be considered finished when sum of \fCsize\fP matches the announced metadata block size\&. Chunks contents pointed by \fCdata\fP are transient and shouln not be accessed after leaving the callback\&.
.PP
\fBParameters:\fP
.RS 4
\fIopaque\fP callback handle
.br
\fIdata\fP pointer to metadata contents
.br
\fIsize\fP size of metadata block chunk, at least \fC1\fP
.RE
.PP
.SS "typedef void(* brotli_decoder_metadata_start_func) (void *opaque, size_t size)"
.PP
Callback to fire on metadata block start\&. After this callback is fired, if \fCsize\fP is not \fC0\fP, it is followed by \fBbrotli_decoder_metadata_chunk_func\fP as more metadata block contents become accessible\&.
.PP
\fBParameters:\fP
.RS 4
\fIopaque\fP callback handle
.br
\fIsize\fP size of metadata block
.RE
.PP
.SS "typedef enum \fBBrotliDecoderParameter\fP \fBBrotliDecoderParameter\fP"
.PP
@ -378,6 +418,23 @@ Checks if instance has already consumed input\&. Instance that returns \fBBROTLI
.RE
.PP
.SS "void BrotliDecoderSetMetadataCallbacks (\fBBrotliDecoderState\fP * state, \fBbrotli_decoder_metadata_start_func\fP start_func, \fBbrotli_decoder_metadata_chunk_func\fP chunk_func, void * opaque)"
.PP
Sets callback for receiving metadata blocks\&.
.PP
\fBParameters:\fP
.RS 4
\fIstate\fP decoder instance
.br
\fIstart_func\fP callback on metadata block start
.br
\fIchunk_func\fP callback on metadata block chunk
.br
\fIopaque\fP callback handle
.RE
.PP
.SS "\fBBROTLI_BOOL\fP BrotliDecoderSetParameter (\fBBrotliDecoderState\fP * state, \fBBrotliDecoderParameter\fP param, uint32_t value)"
.PP