mirror of https://github.com/acidanthera/audk.git
BaseTools: Update Brotli Compress to the latest one 1.0.6
https://bugzilla.tianocore.org/show_bug.cgi?id=1201 Update Brotli to the latest version 1.0.6 https://github.com/google/brotli Verify VS2017, GCC5 build. Verify Decompression boot functionality. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Liming Gao <liming.gao@intel.com> Reviewed-by: Star Zeng <star.zeng@intel.com>
This commit is contained in:
parent
78af0984b4
commit
dd4f667e70
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# This script will exec Brotli tool with -e/-d options.
|
# This script will exec Brotli tool with -e/-d options.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
# Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
|
||||||
# This program and the accompanying materials
|
# This program and the accompanying materials
|
||||||
# are licensed and made available under the terms and conditions of the BSD License
|
# are licensed and made available under the terms and conditions of the BSD License
|
||||||
# which accompanies this distribution. The full text of the license may be found at
|
# which accompanies this distribution. The full text of the license may be found at
|
||||||
|
@ -11,18 +11,15 @@
|
||||||
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||||||
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||||
#
|
#
|
||||||
QLT="-q 9"
|
QLT="-q 9 -w 22"
|
||||||
INPUTFLAG=0
|
|
||||||
ARGS=
|
ARGS=
|
||||||
|
|
||||||
while test $# -gt 0
|
while test $# -gt 0
|
||||||
do
|
do
|
||||||
case $1 in
|
case $1 in
|
||||||
-e)
|
-e)
|
||||||
INPUTFLAG=1
|
|
||||||
;;
|
;;
|
||||||
-d)
|
-d)
|
||||||
INPUTFLAG=1
|
|
||||||
ARGS+="$1 "
|
ARGS+="$1 "
|
||||||
;;
|
;;
|
||||||
-o|-g)
|
-o|-g)
|
||||||
|
@ -34,16 +31,10 @@ do
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
if [ $INPUTFLAG -eq 1 ]
|
ARGS+="$1 "
|
||||||
then
|
|
||||||
ARGS+="-i $1 "
|
|
||||||
INPUTFLAG=0
|
|
||||||
else
|
|
||||||
ARGS+="$1 "
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
exec Brotli $ARGS $QLT
|
exec Brotli $QLT $ARGS
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@REM @file
|
@REM @file
|
||||||
@REM This script will exec Brotli tool with -e/-d options.
|
@REM This script will exec Brotli tool with -e/-d options.
|
||||||
@REM
|
@REM
|
||||||
@REM Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
@REM Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
|
||||||
@REM This program and the accompanying materials
|
@REM This program and the accompanying materials
|
||||||
@REM are licensed and made available under the terms and conditions of the BSD License
|
@REM are licensed and made available under the terms and conditions of the BSD License
|
||||||
@REM which accompanies this distribution. The full text of the license may be found at
|
@REM which accompanies this distribution. The full text of the license may be found at
|
||||||
|
@ -14,22 +14,19 @@
|
||||||
@echo off
|
@echo off
|
||||||
@setlocal
|
@setlocal
|
||||||
|
|
||||||
set QLT=-q 9
|
set QLT=-q 9 -w 22
|
||||||
set INPUTFLAG=0
|
|
||||||
set ARGS=
|
set ARGS=
|
||||||
|
|
||||||
:Begin
|
:Begin
|
||||||
if "%1"=="" goto End
|
if "%1"=="" goto End
|
||||||
|
|
||||||
if "%1"=="-d" (
|
if "%1"=="-d" (
|
||||||
set INPUTFLAG=1
|
|
||||||
set ARGS=%ARGS% %1
|
set ARGS=%ARGS% %1
|
||||||
shift
|
shift
|
||||||
goto Begin
|
goto Begin
|
||||||
)
|
)
|
||||||
|
|
||||||
if "%1"=="-e" (
|
if "%1"=="-e" (
|
||||||
set INPUTFLAG=1
|
|
||||||
shift
|
shift
|
||||||
goto Begin
|
goto Begin
|
||||||
)
|
)
|
||||||
|
@ -55,15 +52,10 @@ if "%1"=="-q" (
|
||||||
goto Begin
|
goto Begin
|
||||||
)
|
)
|
||||||
|
|
||||||
if %INPUTFLAG% == 1 (
|
set ARGS=%ARGS% %1
|
||||||
set ARGS=%ARGS% -i %1
|
|
||||||
set INPUTFLAG=0
|
|
||||||
) else (
|
|
||||||
set ARGS=%ARGS% %1
|
|
||||||
)
|
|
||||||
shift
|
shift
|
||||||
goto Begin
|
goto Begin
|
||||||
|
|
||||||
:End
|
:End
|
||||||
Brotli %ARGS% %QLT%
|
Brotli %QLT% %ARGS%
|
||||||
@echo on
|
@echo on
|
|
@ -15,20 +15,24 @@ MAKEROOT ?= ..
|
||||||
APPNAME = Brotli
|
APPNAME = Brotli
|
||||||
|
|
||||||
OBJECTS = \
|
OBJECTS = \
|
||||||
tools/bro.o \
|
tools/brotli.o \
|
||||||
common/dictionary.o \
|
common/dictionary.o \
|
||||||
|
common/transform.o \
|
||||||
dec/bit_reader.o \
|
dec/bit_reader.o \
|
||||||
dec/decode.o \
|
dec/decode.o \
|
||||||
dec/huffman.o \
|
dec/huffman.o \
|
||||||
dec/state.o \
|
dec/state.o \
|
||||||
enc/backward_references.o \
|
enc/backward_references.o \
|
||||||
|
enc/backward_references_hq.o \
|
||||||
enc/bit_cost.o \
|
enc/bit_cost.o \
|
||||||
enc/block_splitter.o \
|
enc/block_splitter.o \
|
||||||
enc/brotli_bit_stream.o \
|
enc/brotli_bit_stream.o \
|
||||||
enc/cluster.o \
|
enc/cluster.o \
|
||||||
enc/compress_fragment.o \
|
enc/compress_fragment.o \
|
||||||
enc/compress_fragment_two_pass.o \
|
enc/compress_fragment_two_pass.o \
|
||||||
|
enc/dictionary_hash.o \
|
||||||
enc/encode.o \
|
enc/encode.o \
|
||||||
|
enc/encoder_dict.o \
|
||||||
enc/entropy_encode.o \
|
enc/entropy_encode.o \
|
||||||
enc/histogram.o \
|
enc/histogram.o \
|
||||||
enc/literal_cost.o \
|
enc/literal_cost.o \
|
||||||
|
@ -39,4 +43,5 @@ OBJECTS = \
|
||||||
|
|
||||||
include $(MAKEROOT)/Makefiles/app.makefile
|
include $(MAKEROOT)/Makefiles/app.makefile
|
||||||
|
|
||||||
|
TOOL_INCLUDE = -I ./include
|
||||||
LIBS += -lm
|
LIBS += -lm
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
## @file
|
## @file
|
||||||
# Windows makefile for 'Brotli' module build.
|
# Windows makefile for 'Brotli' module build.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
# Copyright (c) 2017 - 2018, Intel Corporation. All rights reserved.<BR>
|
||||||
# This program and the accompanying materials
|
# This program and the accompanying materials
|
||||||
# are licensed and made available under the terms and conditions of the BSD License
|
# are licensed and made available under the terms and conditions of the BSD License
|
||||||
# which accompanies this distribution. The full text of the license may be found at
|
# which accompanies this distribution. The full text of the license may be found at
|
||||||
|
@ -12,13 +12,14 @@
|
||||||
#
|
#
|
||||||
!INCLUDE ..\Makefiles\ms.common
|
!INCLUDE ..\Makefiles\ms.common
|
||||||
|
|
||||||
|
INC = -I .\include $(INC)
|
||||||
CFLAGS = $(CFLAGS) /W2
|
CFLAGS = $(CFLAGS) /W2
|
||||||
|
|
||||||
APPNAME = Brotli
|
APPNAME = Brotli
|
||||||
|
|
||||||
#LIBS = $(LIB_PATH)\Common.lib
|
#LIBS = $(LIB_PATH)\Common.lib
|
||||||
|
|
||||||
COMMON_OBJ = common\dictionary.obj
|
COMMON_OBJ = common\dictionary.obj common\transform.obj
|
||||||
DEC_OBJ = \
|
DEC_OBJ = \
|
||||||
dec\bit_reader.obj \
|
dec\bit_reader.obj \
|
||||||
dec\decode.obj \
|
dec\decode.obj \
|
||||||
|
@ -26,13 +27,16 @@ DEC_OBJ = \
|
||||||
dec\state.obj
|
dec\state.obj
|
||||||
ENC_OBJ = \
|
ENC_OBJ = \
|
||||||
enc\backward_references.obj \
|
enc\backward_references.obj \
|
||||||
|
enc\backward_references_hq.obj \
|
||||||
enc\bit_cost.obj \
|
enc\bit_cost.obj \
|
||||||
enc\block_splitter.obj \
|
enc\block_splitter.obj \
|
||||||
enc\brotli_bit_stream.obj \
|
enc\brotli_bit_stream.obj \
|
||||||
enc\cluster.obj \
|
enc\cluster.obj \
|
||||||
enc\compress_fragment.obj \
|
enc\compress_fragment.obj \
|
||||||
enc\compress_fragment_two_pass.obj \
|
enc\compress_fragment_two_pass.obj \
|
||||||
|
enc\dictionary_hash.obj \
|
||||||
enc\encode.obj \
|
enc\encode.obj \
|
||||||
|
enc\encoder_dict.obj \
|
||||||
enc\entropy_encode.obj \
|
enc\entropy_encode.obj \
|
||||||
enc\histogram.obj \
|
enc\histogram.obj \
|
||||||
enc\literal_cost.obj \
|
enc\literal_cost.obj \
|
||||||
|
@ -42,19 +46,9 @@ ENC_OBJ = \
|
||||||
enc\utf8_util.obj
|
enc\utf8_util.obj
|
||||||
|
|
||||||
OBJECTS = \
|
OBJECTS = \
|
||||||
tools\bro.obj \
|
tools\brotli.obj \
|
||||||
$(COMMON_OBJ) \
|
$(COMMON_OBJ) \
|
||||||
$(DEC_OBJ) \
|
$(DEC_OBJ) \
|
||||||
$(ENC_OBJ)
|
$(ENC_OBJ)
|
||||||
|
|
||||||
!INCLUDE ..\Makefiles\ms.app
|
!INCLUDE ..\Makefiles\ms.app
|
||||||
|
|
||||||
all: $(BIN_PATH)\BrotliCompress.bat
|
|
||||||
|
|
||||||
$(BIN_PATH)\BrotliCompress.bat: BrotliCompress.bat
|
|
||||||
copy BrotliCompress.bat $(BIN_PATH)\BrotliCompress.bat /Y
|
|
||||||
|
|
||||||
cleanall: localCleanall
|
|
||||||
|
|
||||||
localCleanall:
|
|
||||||
del /f /q $(BIN_PATH)\BrotliCompress.bat > nul
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
It is based on the Brotli v0.5.2.
|
It is based on the Brotli v1.0.6.
|
||||||
Brotli was released on the website https://github.com/google/brotli.
|
Brotli was released on the website https://github.com/google/brotli.
|
||||||
|
|
|
@ -28,14 +28,25 @@
|
||||||
/* "code length of 8 is repeated" */
|
/* "code length of 8 is repeated" */
|
||||||
#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8
|
#define BROTLI_INITIAL_REPEATED_CODE_LENGTH 8
|
||||||
|
|
||||||
|
/* "Large Window Brotli" */
|
||||||
|
#define BROTLI_LARGE_MAX_DISTANCE_BITS 62U
|
||||||
|
#define BROTLI_LARGE_MIN_WBITS 10
|
||||||
|
#define BROTLI_LARGE_MAX_WBITS 30
|
||||||
|
|
||||||
/* Specification: 4. Encoding of distances */
|
/* Specification: 4. Encoding of distances */
|
||||||
#define BROTLI_NUM_DISTANCE_SHORT_CODES 16
|
#define BROTLI_NUM_DISTANCE_SHORT_CODES 16
|
||||||
#define BROTLI_MAX_NPOSTFIX 3
|
#define BROTLI_MAX_NPOSTFIX 3
|
||||||
#define BROTLI_MAX_NDIRECT 120
|
#define BROTLI_MAX_NDIRECT 120
|
||||||
/* BROTLI_NUM_DISTANCE_SYMBOLS == 520 */
|
#define BROTLI_MAX_DISTANCE_BITS 24U
|
||||||
#define BROTLI_NUM_DISTANCE_SYMBOLS (BROTLI_NUM_DISTANCE_SHORT_CODES + \
|
#define BROTLI_DISTANCE_ALPHABET_SIZE(NPOSTFIX, NDIRECT, MAXNBITS) ( \
|
||||||
BROTLI_MAX_NDIRECT + \
|
BROTLI_NUM_DISTANCE_SHORT_CODES + (NDIRECT) + \
|
||||||
(24 << (BROTLI_MAX_NPOSTFIX + 1)))
|
((MAXNBITS) << ((NPOSTFIX) + 1)))
|
||||||
|
/* BROTLI_NUM_DISTANCE_SYMBOLS == 1128 */
|
||||||
|
#define BROTLI_NUM_DISTANCE_SYMBOLS \
|
||||||
|
BROTLI_DISTANCE_ALPHABET_SIZE( \
|
||||||
|
BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX, BROTLI_LARGE_MAX_DISTANCE_BITS)
|
||||||
|
#define BROTLI_MAX_DISTANCE 0x3FFFFFC
|
||||||
|
#define BROTLI_MAX_ALLOWED_DISTANCE 0x7FFFFFFC
|
||||||
|
|
||||||
/* 7.1. Context modes and context ID lookup for literals */
|
/* 7.1. Context modes and context ID lookup for literals */
|
||||||
/* "context IDs for literals are in the range of 0..63" */
|
/* "context IDs for literals are in the range of 0..63" */
|
||||||
|
@ -44,4 +55,10 @@
|
||||||
/* 7.2. Context ID for distances */
|
/* 7.2. Context ID for distances */
|
||||||
#define BROTLI_DISTANCE_CONTEXT_BITS 2
|
#define BROTLI_DISTANCE_CONTEXT_BITS 2
|
||||||
|
|
||||||
|
/* 9.1. Format of the Stream Header */
|
||||||
|
/* Number of slack bytes for window size. Don't confuse
|
||||||
|
with BROTLI_NUM_DISTANCE_SHORT_CODES. */
|
||||||
|
#define BROTLI_WINDOW_GAP 16
|
||||||
|
#define BROTLI_MAX_BACKWARD_LIMIT(W) (((size_t)1 << (W)) - BROTLI_WINDOW_GAP)
|
||||||
|
|
||||||
#endif /* BROTLI_COMMON_CONSTANTS_H_ */
|
#endif /* BROTLI_COMMON_CONSTANTS_H_ */
|
||||||
|
|
|
@ -6,110 +6,171 @@
|
||||||
|
|
||||||
/* Lookup table to map the previous two bytes to a context id.
|
/* Lookup table to map the previous two bytes to a context id.
|
||||||
|
|
||||||
There are four different context modeling modes defined here:
|
There are four different context modeling modes defined here:
|
||||||
CONTEXT_LSB6: context id is the least significant 6 bits of the last byte,
|
CONTEXT_LSB6: context id is the least significant 6 bits of the last byte,
|
||||||
CONTEXT_MSB6: context id is the most significant 6 bits of the last byte,
|
CONTEXT_MSB6: context id is the most significant 6 bits of the last byte,
|
||||||
CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text,
|
CONTEXT_UTF8: second-order context model tuned for UTF8-encoded text,
|
||||||
CONTEXT_SIGNED: second-order context model tuned for signed integers.
|
CONTEXT_SIGNED: second-order context model tuned for signed integers.
|
||||||
|
|
||||||
The context id for the UTF8 context model is calculated as follows. If p1
|
If |p1| and |p2| are the previous two bytes, and |mode| is current context
|
||||||
and p2 are the previous two bytes, we calculate the context as
|
mode, we calculate the context as:
|
||||||
|
|
||||||
context = kContextLookup[p1] | kContextLookup[p2 + 256].
|
context = ContextLut(mode)[p1] | ContextLut(mode)[p2 + 256].
|
||||||
|
|
||||||
If the previous two bytes are ASCII characters (i.e. < 128), this will be
|
For CONTEXT_UTF8 mode, if the previous two bytes are ASCII characters
|
||||||
equivalent to
|
(i.e. < 128), this will be equivalent to
|
||||||
|
|
||||||
context = 4 * context1(p1) + context2(p2),
|
context = 4 * context1(p1) + context2(p2),
|
||||||
|
|
||||||
where context1 is based on the previous byte in the following way:
|
where context1 is based on the previous byte in the following way:
|
||||||
|
|
||||||
0 : non-ASCII control
|
0 : non-ASCII control
|
||||||
1 : \t, \n, \r
|
1 : \t, \n, \r
|
||||||
2 : space
|
2 : space
|
||||||
3 : other punctuation
|
3 : other punctuation
|
||||||
4 : " '
|
4 : " '
|
||||||
5 : %
|
5 : %
|
||||||
6 : ( < [ {
|
6 : ( < [ {
|
||||||
7 : ) > ] }
|
7 : ) > ] }
|
||||||
8 : , ; :
|
8 : , ; :
|
||||||
9 : .
|
9 : .
|
||||||
10 : =
|
10 : =
|
||||||
11 : number
|
11 : number
|
||||||
12 : upper-case vowel
|
12 : upper-case vowel
|
||||||
13 : upper-case consonant
|
13 : upper-case consonant
|
||||||
14 : lower-case vowel
|
14 : lower-case vowel
|
||||||
15 : lower-case consonant
|
15 : lower-case consonant
|
||||||
|
|
||||||
and context2 is based on the second last byte:
|
and context2 is based on the second last byte:
|
||||||
|
|
||||||
0 : control, space
|
0 : control, space
|
||||||
1 : punctuation
|
1 : punctuation
|
||||||
2 : upper-case letter, number
|
2 : upper-case letter, number
|
||||||
3 : lower-case letter
|
3 : lower-case letter
|
||||||
|
|
||||||
If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
||||||
stream it will be a continuation byte, value between 128 and 191), the
|
stream it will be a continuation byte, value between 128 and 191), the
|
||||||
context is the same as if the second last byte was an ASCII control or space.
|
context is the same as if the second last byte was an ASCII control or space.
|
||||||
|
|
||||||
If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
||||||
be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
||||||
the last byte and to a lesser extent on the second last byte if it is ASCII.
|
the last byte and to a lesser extent on the second last byte if it is ASCII.
|
||||||
|
|
||||||
If the last byte is a UTF8 continuation byte, the second last byte can be:
|
If the last byte is a UTF8 continuation byte, the second last byte can be:
|
||||||
- continuation byte: the next byte is probably ASCII or lead byte (assuming
|
- continuation byte: the next byte is probably ASCII or lead byte (assuming
|
||||||
4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
||||||
- lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
- lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
||||||
- lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
- lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
||||||
|
|
||||||
The possible value combinations of the previous two bytes, the range of
|
The possible value combinations of the previous two bytes, the range of
|
||||||
context ids and the type of the next byte is summarized in the table below:
|
context ids and the type of the next byte is summarized in the table below:
|
||||||
|
|
||||||
|--------\-----------------------------------------------------------------|
|
|--------\-----------------------------------------------------------------|
|
||||||
| \ Last byte |
|
| \ Last byte |
|
||||||
| Second \---------------------------------------------------------------|
|
| Second \---------------------------------------------------------------|
|
||||||
| last byte \ ASCII | cont. byte | lead byte |
|
| last byte \ ASCII | cont. byte | lead byte |
|
||||||
| \ (0-127) | (128-191) | (192-) |
|
| \ (0-127) | (128-191) | (192-) |
|
||||||
|=============|===================|=====================|==================|
|
|=============|===================|=====================|==================|
|
||||||
| ASCII | next: ASCII/lead | not valid | next: cont. |
|
| ASCII | next: ASCII/lead | not valid | next: cont. |
|
||||||
| (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
| (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|-------------|-------------------|---------------------|------------------|
|
||||||
| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
||||||
| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|-------------|-------------------|---------------------|------------------|
|
||||||
| lead byte | not valid | next: ASCII/lead | not valid |
|
| lead byte | not valid | next: ASCII/lead | not valid |
|
||||||
| (192-207) | | context: 0 - 1 | |
|
| (192-207) | | context: 0 - 1 | |
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|-------------|-------------------|---------------------|------------------|
|
||||||
| lead byte | not valid | next: cont. | not valid |
|
| lead byte | not valid | next: cont. | not valid |
|
||||||
| (208-) | | context: 2 - 3 | |
|
| (208-) | | context: 2 - 3 | |
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|-------------|-------------------|---------------------|------------------|
|
||||||
|
|
||||||
The context id for the signed context mode is calculated as:
|
|
||||||
|
|
||||||
context = (kContextLookup[512 + p1] << 3) | kContextLookup[512 + p2].
|
|
||||||
|
|
||||||
For any context modeling modes, the context ids can be calculated by |-ing
|
|
||||||
together two lookups from one table using context model dependent offsets:
|
|
||||||
|
|
||||||
context = kContextLookup[offset1 + p1] | kContextLookup[offset2 + p2].
|
|
||||||
|
|
||||||
where offset1 and offset2 are dependent on the context mode.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_CONTEXT_H_
|
#ifndef BROTLI_COMMON_CONTEXT_H_
|
||||||
#define BROTLI_DEC_CONTEXT_H_
|
#define BROTLI_COMMON_CONTEXT_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
enum ContextType {
|
typedef enum ContextType {
|
||||||
CONTEXT_LSB6 = 0,
|
CONTEXT_LSB6 = 0,
|
||||||
CONTEXT_MSB6 = 1,
|
CONTEXT_MSB6 = 1,
|
||||||
CONTEXT_UTF8 = 2,
|
CONTEXT_UTF8 = 2,
|
||||||
CONTEXT_SIGNED = 3
|
CONTEXT_SIGNED = 3
|
||||||
};
|
} ContextType;
|
||||||
|
|
||||||
/* Common context lookup table for all context modes. */
|
/* Common context lookup table for all context modes. */
|
||||||
static const uint8_t kContextLookup[1792] = {
|
static const uint8_t kContextLookup[2048] = {
|
||||||
|
/* CONTEXT_LSB6, last byte. */
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
|
||||||
|
/* CONTEXT_LSB6, second last byte, */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
|
/* CONTEXT_MSB6, last byte. */
|
||||||
|
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
||||||
|
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
||||||
|
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
|
||||||
|
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
||||||
|
16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
|
||||||
|
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
|
||||||
|
24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
|
||||||
|
28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
|
||||||
|
32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
|
||||||
|
36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
|
||||||
|
40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
|
||||||
|
44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
|
||||||
|
48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
|
||||||
|
52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
|
||||||
|
56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
|
||||||
|
60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63,
|
||||||
|
|
||||||
|
/* CONTEXT_MSB6, second last byte, */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
||||||
/* CONTEXT_UTF8, last byte. */
|
/* CONTEXT_UTF8, last byte. */
|
||||||
/* ASCII range. */
|
/* ASCII range. */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0,
|
||||||
|
@ -130,6 +191,7 @@ static const uint8_t kContextLookup[1792] = {
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||||
|
|
||||||
/* CONTEXT_UTF8 second last byte. */
|
/* CONTEXT_UTF8 second last byte. */
|
||||||
/* ASCII range. */
|
/* ASCII range. */
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
@ -150,23 +212,7 @@ static const uint8_t kContextLookup[1792] = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
/* CONTEXT_SIGNED, second last byte. */
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
/* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */
|
/* CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits. */
|
||||||
0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
||||||
|
@ -184,68 +230,32 @@ static const uint8_t kContextLookup[1792] = {
|
||||||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
||||||
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
|
||||||
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56,
|
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 56,
|
||||||
/* CONTEXT_LSB6, last byte. */
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
/* CONTEXT_SIGNED, second last byte. */
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||||
/* CONTEXT_MSB6, last byte. */
|
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||||
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
|
|
||||||
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
|
|
||||||
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
|
|
||||||
16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
|
|
||||||
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
|
|
||||||
24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27,
|
|
||||||
28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31,
|
|
||||||
32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35,
|
|
||||||
36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39,
|
|
||||||
40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43,
|
|
||||||
44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47,
|
|
||||||
48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51,
|
|
||||||
52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55,
|
|
||||||
56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59,
|
|
||||||
60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63,
|
|
||||||
/* CONTEXT_{M,L}SB6, second last byte, */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int kContextLookupOffsets[8] = {
|
typedef const uint8_t* ContextLut;
|
||||||
/* CONTEXT_LSB6 */
|
|
||||||
1024, 1536,
|
|
||||||
/* CONTEXT_MSB6 */
|
|
||||||
1280, 1536,
|
|
||||||
/* CONTEXT_UTF8 */
|
|
||||||
0, 256,
|
|
||||||
/* CONTEXT_SIGNED */
|
|
||||||
768, 512,
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_CONTEXT_H_ */
|
/* typeof(MODE) == ContextType; returns ContextLut */
|
||||||
|
#define BROTLI_CONTEXT_LUT(MODE) (&kContextLookup[(MODE) << 9])
|
||||||
|
|
||||||
|
/* typeof(LUT) == ContextLut */
|
||||||
|
#define BROTLI_CONTEXT(P1, P2, LUT) ((LUT)[P1] | ((LUT) + 256)[P2])
|
||||||
|
|
||||||
|
#endif /* BROTLI_COMMON_CONTEXT_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -9,18 +9,53 @@
|
||||||
#ifndef BROTLI_COMMON_DICTIONARY_H_
|
#ifndef BROTLI_COMMON_DICTIONARY_H_
|
||||||
#define BROTLI_COMMON_DICTIONARY_H_
|
#define BROTLI_COMMON_DICTIONARY_H_
|
||||||
|
|
||||||
#include "./types.h"
|
#include <brotli/port.h>
|
||||||
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern const uint8_t kBrotliDictionary[122784];
|
typedef struct BrotliDictionary {
|
||||||
extern const uint32_t kBrotliDictionaryOffsetsByLength[25];
|
/**
|
||||||
extern const uint8_t kBrotliDictionarySizeBitsByLength[25];
|
* Number of bits to encode index of dictionary word in a bucket.
|
||||||
|
*
|
||||||
|
* Specification: Appendix A. Static Dictionary Data
|
||||||
|
*
|
||||||
|
* Words in a dictionary are bucketed by length.
|
||||||
|
* @c 0 means that there are no words of a given length.
|
||||||
|
* Dictionary consists of words with length of [4..24] bytes.
|
||||||
|
* Values at [0..3] and [25..31] indices should not be addressed.
|
||||||
|
*/
|
||||||
|
uint8_t size_bits_by_length[32];
|
||||||
|
|
||||||
#define kBrotliMinDictionaryWordLength 4
|
/* assert(offset[i + 1] == offset[i] + (bits[i] ? (i << bits[i]) : 0)) */
|
||||||
#define kBrotliMaxDictionaryWordLength 24
|
uint32_t offsets_by_length[32];
|
||||||
|
|
||||||
|
/* assert(data_size == offsets_by_length[31]) */
|
||||||
|
size_t data_size;
|
||||||
|
|
||||||
|
/* Data array is not bound, and should obey to size_bits_by_length values.
|
||||||
|
Specified size matches default (RFC 7932) dictionary. Its size is
|
||||||
|
defined by data_size */
|
||||||
|
const uint8_t* data;
|
||||||
|
} BrotliDictionary;
|
||||||
|
|
||||||
|
BROTLI_COMMON_API const BrotliDictionary* BrotliGetDictionary(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets dictionary data.
|
||||||
|
*
|
||||||
|
* When dictionary data is already set / present, this method is no-op.
|
||||||
|
*
|
||||||
|
* Dictionary data MUST be provided before BrotliGetDictionary is invoked.
|
||||||
|
* This method is used ONLY in multi-client environment (e.g. C + Java),
|
||||||
|
* to reduce storage by sharing single dictionary between implementations.
|
||||||
|
*/
|
||||||
|
BROTLI_COMMON_API void BrotliSetDictionaryData(const uint8_t* data);
|
||||||
|
|
||||||
|
#define BROTLI_MIN_DICTIONARY_WORD_LENGTH 4
|
||||||
|
#define BROTLI_MAX_DICTIONARY_WORD_LENGTH 24
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -0,0 +1,558 @@
|
||||||
|
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Macros for compiler / platform specific features and build options.
|
||||||
|
|
||||||
|
Build options are:
|
||||||
|
* BROTLI_BUILD_32_BIT disables 64-bit optimizations
|
||||||
|
* BROTLI_BUILD_64_BIT forces to use 64-bit optimizations
|
||||||
|
* BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations
|
||||||
|
* BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations
|
||||||
|
* BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations
|
||||||
|
* BROTLI_BUILD_PORTABLE disables dangerous optimizations, like unaligned
|
||||||
|
read and overlapping memcpy; this reduces decompression speed by 5%
|
||||||
|
* BROTLI_BUILD_NO_RBIT disables "rbit" optimization for ARM CPUs
|
||||||
|
* BROTLI_DEBUG dumps file name and line number when decoder detects stream
|
||||||
|
or memory error
|
||||||
|
* BROTLI_ENABLE_LOG enables asserts and dumps various state information
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROTLI_COMMON_PLATFORM_H_
|
||||||
|
#define BROTLI_COMMON_PLATFORM_H_
|
||||||
|
|
||||||
|
#include <string.h> /* memcpy */
|
||||||
|
#include <stdlib.h> /* malloc, free */
|
||||||
|
|
||||||
|
#include <brotli/port.h>
|
||||||
|
#include <brotli/types.h>
|
||||||
|
|
||||||
|
#if defined(OS_LINUX) || defined(OS_CYGWIN)
|
||||||
|
#include <endian.h>
|
||||||
|
#elif defined(OS_FREEBSD)
|
||||||
|
#include <machine/endian.h>
|
||||||
|
#elif defined(OS_MACOSX)
|
||||||
|
#include <machine/endian.h>
|
||||||
|
/* Let's try and follow the Linux convention */
|
||||||
|
#define BROTLI_X_BYTE_ORDER BYTE_ORDER
|
||||||
|
#define BROTLI_X_LITTLE_ENDIAN LITTLE_ENDIAN
|
||||||
|
#define BROTLI_X_BIG_ENDIAN BIG_ENDIAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG)
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The following macros were borrowed from https://github.com/nemequ/hedley
|
||||||
|
* with permission of original author - Evan Nemerson <evan@nemerson.com> */
|
||||||
|
|
||||||
|
/* >>> >>> >>> hedley macros */
|
||||||
|
|
||||||
|
/* Define "BROTLI_PREDICT_TRUE" and "BROTLI_PREDICT_FALSE" macros for capable
|
||||||
|
compilers.
|
||||||
|
|
||||||
|
To apply compiler hint, enclose the branching condition into macros, like this:
|
||||||
|
|
||||||
|
if (BROTLI_PREDICT_TRUE(zero == 0)) {
|
||||||
|
// main execution path
|
||||||
|
} else {
|
||||||
|
// compiler should place this code outside of main execution path
|
||||||
|
}
|
||||||
|
|
||||||
|
OR:
|
||||||
|
|
||||||
|
if (BROTLI_PREDICT_FALSE(something_rare_or_unexpected_happens)) {
|
||||||
|
// compiler should place this code outside of main execution path
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_expect, 3, 0, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||||
|
BROTLI_SUNPRO_VERSION_CHECK(5, 12, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||||
|
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||||
|
BROTLI_TI_VERSION_CHECK(7, 3, 0) || \
|
||||||
|
BROTLI_TINYC_VERSION_CHECK(0, 9, 27)
|
||||||
|
#define BROTLI_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
|
||||||
|
#define BROTLI_PREDICT_FALSE(x) (__builtin_expect(x, 0))
|
||||||
|
#else
|
||||||
|
#define BROTLI_PREDICT_FALSE(x) (x)
|
||||||
|
#define BROTLI_PREDICT_TRUE(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
|
||||||
|
!defined(__cplusplus)
|
||||||
|
#define BROTLI_RESTRICT restrict
|
||||||
|
#elif BROTLI_GNUC_VERSION_CHECK(3, 1, 0) || \
|
||||||
|
BROTLI_MSVC_VERSION_CHECK(14, 0, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||||
|
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||||
|
BROTLI_PGI_VERSION_CHECK(17, 10, 0) || \
|
||||||
|
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||||
|
BROTLI_IAR_VERSION_CHECK(8, 0, 0) || \
|
||||||
|
(BROTLI_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus))
|
||||||
|
#define BROTLI_RESTRICT __restrict
|
||||||
|
#elif BROTLI_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus)
|
||||||
|
#define BROTLI_RESTRICT _Restrict
|
||||||
|
#else
|
||||||
|
#define BROTLI_RESTRICT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
|
||||||
|
(defined(__cplusplus) && (__cplusplus >= 199711L))
|
||||||
|
#define BROTLI_MAYBE_INLINE inline
|
||||||
|
#elif defined(__GNUC_STDC_INLINE__) || defined(__GNUC_GNU_INLINE__) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(6, 2, 0)
|
||||||
|
#define BROTLI_MAYBE_INLINE __inline__
|
||||||
|
#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || BROTLI_TI_VERSION_CHECK(8, 0, 0)
|
||||||
|
#define BROTLI_MAYBE_INLINE __inline
|
||||||
|
#else
|
||||||
|
#define BROTLI_MAYBE_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BROTLI_GNUC_HAS_ATTRIBUTE(always_inline, 4, 0, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||||
|
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||||
|
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||||
|
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||||
|
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
|
||||||
|
#define BROTLI_INLINE BROTLI_MAYBE_INLINE __attribute__((__always_inline__))
|
||||||
|
#elif BROTLI_MSVC_VERSION_CHECK(12, 0, 0)
|
||||||
|
#define BROTLI_INLINE BROTLI_MAYBE_INLINE __forceinline
|
||||||
|
#elif BROTLI_TI_VERSION_CHECK(7, 0, 0) && defined(__cplusplus)
|
||||||
|
#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
|
||||||
|
#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0)
|
||||||
|
#define BROTLI_INLINE BROTLI_MAYBE_INLINE _Pragma("inline=forced")
|
||||||
|
#else
|
||||||
|
#define BROTLI_INLINE BROTLI_MAYBE_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BROTLI_GNUC_HAS_ATTRIBUTE(noinline, 4, 0, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||||
|
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||||
|
BROTLI_IBM_VERSION_CHECK(10, 1, 0) || \
|
||||||
|
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||||
|
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
|
||||||
|
#define BROTLI_NOINLINE __attribute__((__noinline__))
|
||||||
|
#elif BROTLI_MSVC_VERSION_CHECK(13, 10, 0)
|
||||||
|
#define BROTLI_NOINLINE __declspec(noinline)
|
||||||
|
#elif BROTLI_PGI_VERSION_CHECK(10, 2, 0)
|
||||||
|
#define BROTLI_NOINLINE _Pragma("noinline")
|
||||||
|
#elif BROTLI_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus)
|
||||||
|
#define BROTLI_NOINLINE _Pragma("FUNC_CANNOT_INLINE;")
|
||||||
|
#elif BROTLI_IAR_VERSION_CHECK(8, 0, 0)
|
||||||
|
#define BROTLI_NOINLINE _Pragma("inline=never")
|
||||||
|
#else
|
||||||
|
#define BROTLI_NOINLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */
|
||||||
|
#if !defined(BROTLI_INTERNAL)
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#define BROTLI_INTERNAL
|
||||||
|
#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \
|
||||||
|
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||||
|
BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \
|
||||||
|
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||||
|
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && \
|
||||||
|
defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
|
||||||
|
#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden")))
|
||||||
|
#else
|
||||||
|
#define BROTLI_INTERNAL
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* <<< <<< <<< end of hedley macros. */
|
||||||
|
|
||||||
|
#if BROTLI_GNUC_HAS_ATTRIBUTE(unused, 2, 7, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||||
|
#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE __attribute__ ((unused))
|
||||||
|
#else
|
||||||
|
#define BROTLI_UNUSED_FUNCTION static BROTLI_INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \
|
||||||
|
(defined(M_ARM) && (M_ARM == 7))
|
||||||
|
#define BROTLI_TARGET_ARMV7
|
||||||
|
#endif /* ARMv7 */
|
||||||
|
|
||||||
|
#if (defined(__ARM_ARCH) && (__ARM_ARCH == 8)) || \
|
||||||
|
defined(__aarch64__) || defined(__ARM64_ARCH_8__)
|
||||||
|
#define BROTLI_TARGET_ARMV8_ANY
|
||||||
|
|
||||||
|
#if defined(__ARM_32BIT_STATE)
|
||||||
|
#define BROTLI_TARGET_ARMV8_32
|
||||||
|
#elif defined(__ARM_64BIT_STATE)
|
||||||
|
#define BROTLI_TARGET_ARMV8_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ARMv8 */
|
||||||
|
|
||||||
|
#if defined(__i386) || defined(_M_IX86)
|
||||||
|
#define BROTLI_TARGET_X86
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__) || defined(_M_X64)
|
||||||
|
#define BROTLI_TARGET_X64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__PPC64__)
|
||||||
|
#define BROTLI_TARGET_POWERPC64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64
|
||||||
|
#define BROTLI_TARGET_RISCV64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_BUILD_64_BIT)
|
||||||
|
#define BROTLI_64_BITS 1
|
||||||
|
#elif defined(BROTLI_BUILD_32_BIT)
|
||||||
|
#define BROTLI_64_BITS 0
|
||||||
|
#elif defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8_64) || \
|
||||||
|
defined(BROTLI_TARGET_POWERPC64) || defined(BROTLI_TARGET_RISCV64)
|
||||||
|
#define BROTLI_64_BITS 1
|
||||||
|
#else
|
||||||
|
#define BROTLI_64_BITS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (BROTLI_64_BITS)
|
||||||
|
#define brotli_reg_t uint64_t
|
||||||
|
#else
|
||||||
|
#define brotli_reg_t uint32_t
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_BUILD_BIG_ENDIAN)
|
||||||
|
#define BROTLI_BIG_ENDIAN 1
|
||||||
|
#elif defined(BROTLI_BUILD_LITTLE_ENDIAN)
|
||||||
|
#define BROTLI_LITTLE_ENDIAN 1
|
||||||
|
#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL)
|
||||||
|
/* Just break elif chain. */
|
||||||
|
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||||
|
#define BROTLI_LITTLE_ENDIAN 1
|
||||||
|
#elif defined(_WIN32) || defined(BROTLI_TARGET_X64)
|
||||||
|
/* Win32 & x64 can currently always be assumed to be little endian */
|
||||||
|
#define BROTLI_LITTLE_ENDIAN 1
|
||||||
|
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
|
#define BROTLI_BIG_ENDIAN 1
|
||||||
|
#elif defined(BROTLI_X_BYTE_ORDER)
|
||||||
|
#if BROTLI_X_BYTE_ORDER == BROTLI_X_LITTLE_ENDIAN
|
||||||
|
#define BROTLI_LITTLE_ENDIAN 1
|
||||||
|
#elif BROTLI_X_BYTE_ORDER == BROTLI_X_BIG_ENDIAN
|
||||||
|
#define BROTLI_BIG_ENDIAN 1
|
||||||
|
#endif
|
||||||
|
#endif /* BROTLI_X_BYTE_ORDER */
|
||||||
|
|
||||||
|
#if !defined(BROTLI_LITTLE_ENDIAN)
|
||||||
|
#define BROTLI_LITTLE_ENDIAN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(BROTLI_BIG_ENDIAN)
|
||||||
|
#define BROTLI_BIG_ENDIAN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_X_BYTE_ORDER)
|
||||||
|
#undef BROTLI_X_BYTE_ORDER
|
||||||
|
#undef BROTLI_X_LITTLE_ENDIAN
|
||||||
|
#undef BROTLI_X_BIG_ENDIAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_BUILD_PORTABLE)
|
||||||
|
#define BROTLI_ALIGNED_READ (!!1)
|
||||||
|
#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \
|
||||||
|
defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY) || \
|
||||||
|
defined(BROTLI_TARGET_RISCV64)
|
||||||
|
/* Allow unaligned read only for white-listed CPUs. */
|
||||||
|
#define BROTLI_ALIGNED_READ (!!0)
|
||||||
|
#else
|
||||||
|
#define BROTLI_ALIGNED_READ (!!1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BROTLI_ALIGNED_READ
|
||||||
|
/* Portable unaligned memory access: read / write values via memcpy. */
|
||||||
|
static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) {
|
||||||
|
uint16_t t;
|
||||||
|
memcpy(&t, p, sizeof t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) {
|
||||||
|
uint32_t t;
|
||||||
|
memcpy(&t, p, sizeof t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||||
|
uint64_t t;
|
||||||
|
memcpy(&t, p, sizeof t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||||
|
memcpy(p, &v, sizeof v);
|
||||||
|
}
|
||||||
|
#else /* BROTLI_ALIGNED_READ */
|
||||||
|
/* Unaligned memory access is allowed: just cast pointer to requested type. */
|
||||||
|
#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
|
||||||
|
defined(MEMORY_SANITIZER)
|
||||||
|
/* Consider we have an unaligned load/store of 4 bytes from address 0x...05.
|
||||||
|
AddressSanitizer will treat it as a 3-byte access to the range 05:07 and
|
||||||
|
will miss a bug if 08 is the first unaddressable byte.
|
||||||
|
ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will
|
||||||
|
miss a race between this access and some other accesses to 08.
|
||||||
|
MemorySanitizer will correctly propagate the shadow on unaligned stores
|
||||||
|
and correctly report bugs on unaligned loads, but it may not properly
|
||||||
|
update and report the origin of the uninitialized memory.
|
||||||
|
For all three tools, replacing an unaligned access with a tool-specific
|
||||||
|
callback solves the problem. */
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
uint16_t __sanitizer_unaligned_load16(const void* p);
|
||||||
|
uint32_t __sanitizer_unaligned_load32(const void* p);
|
||||||
|
uint64_t __sanitizer_unaligned_load64(const void* p);
|
||||||
|
void __sanitizer_unaligned_store64(void* p, uint64_t v);
|
||||||
|
#if defined(__cplusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
#define BrotliUnalignedRead16 __sanitizer_unaligned_load16
|
||||||
|
#define BrotliUnalignedRead32 __sanitizer_unaligned_load32
|
||||||
|
#define BrotliUnalignedRead64 __sanitizer_unaligned_load64
|
||||||
|
#define BrotliUnalignedWrite64 __sanitizer_unaligned_store64
|
||||||
|
#else
|
||||||
|
static BROTLI_INLINE uint16_t BrotliUnalignedRead16(const void* p) {
|
||||||
|
return *(const uint16_t*)p;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE uint32_t BrotliUnalignedRead32(const void* p) {
|
||||||
|
return *(const uint32_t*)p;
|
||||||
|
}
|
||||||
|
#if (BROTLI_64_BITS)
|
||||||
|
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||||
|
return *(const uint64_t*)p;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||||
|
*(uint64_t*)p = v;
|
||||||
|
}
|
||||||
|
#else /* BROTLI_64_BITS */
|
||||||
|
/* Avoid emitting LDRD / STRD, which require properly aligned address. */
|
||||||
|
/* If __attribute__(aligned) is available, use that. Otherwise, memcpy. */
|
||||||
|
|
||||||
|
#if BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0)
|
||||||
|
typedef __attribute__((aligned(1))) uint64_t brotli_unaligned_uint64_t;
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||||
|
return (uint64_t) ((brotli_unaligned_uint64_t*) p)[0];
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||||
|
brotli_unaligned_uint64_t* dwords = (brotli_unaligned_uint64_t*) p;
|
||||||
|
dwords[0] = (brotli_unaligned_uint64_t) v;
|
||||||
|
}
|
||||||
|
#else /* BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) */
|
||||||
|
static BROTLI_INLINE uint64_t BrotliUnalignedRead64(const void* p) {
|
||||||
|
uint64_t v;
|
||||||
|
memcpy(&v, p, sizeof(uint64_t));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void BrotliUnalignedWrite64(void* p, uint64_t v) {
|
||||||
|
memcpy(p, &v, sizeof(uint64_t));
|
||||||
|
}
|
||||||
|
#endif /* BROTLI_GNUC_HAS_ATTRIBUTE(aligned, 2, 7, 0) */
|
||||||
|
#endif /* BROTLI_64_BITS */
|
||||||
|
#endif /* ASAN / TSAN / MSAN */
|
||||||
|
#endif /* BROTLI_ALIGNED_READ */
|
||||||
|
|
||||||
|
#if BROTLI_LITTLE_ENDIAN
|
||||||
|
/* Straight endianness. Just read / write values. */
|
||||||
|
#define BROTLI_UNALIGNED_LOAD16LE BrotliUnalignedRead16
|
||||||
|
#define BROTLI_UNALIGNED_LOAD32LE BrotliUnalignedRead32
|
||||||
|
#define BROTLI_UNALIGNED_LOAD64LE BrotliUnalignedRead64
|
||||||
|
#define BROTLI_UNALIGNED_STORE64LE BrotliUnalignedWrite64
|
||||||
|
#elif BROTLI_BIG_ENDIAN /* BROTLI_LITTLE_ENDIAN */
|
||||||
|
/* Explain compiler to byte-swap values. */
|
||||||
|
#define BROTLI_BSWAP16_(V) ((uint16_t)( \
|
||||||
|
(((V) & 0xFFU) << 8) | \
|
||||||
|
(((V) >> 8) & 0xFFU)))
|
||||||
|
static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) {
|
||||||
|
uint16_t value = BrotliUnalignedRead16(p);
|
||||||
|
return BROTLI_BSWAP16_(value);
|
||||||
|
}
|
||||||
|
#define BROTLI_BSWAP32_(V) ( \
|
||||||
|
(((V) & 0xFFU) << 24) | (((V) & 0xFF00U) << 8) | \
|
||||||
|
(((V) >> 8) & 0xFF00U) | (((V) >> 24) & 0xFFU))
|
||||||
|
static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) {
|
||||||
|
uint32_t value = BrotliUnalignedRead32(p);
|
||||||
|
return BROTLI_BSWAP32_(value);
|
||||||
|
}
|
||||||
|
#define BROTLI_BSWAP64_(V) ( \
|
||||||
|
(((V) & 0xFFU) << 56) | (((V) & 0xFF00U) << 40) | \
|
||||||
|
(((V) & 0xFF0000U) << 24) | (((V) & 0xFF000000U) << 8) | \
|
||||||
|
(((V) >> 8) & 0xFF000000U) | (((V) >> 24) & 0xFF0000U) | \
|
||||||
|
(((V) >> 40) & 0xFF00U) | (((V) >> 56) & 0xFFU))
|
||||||
|
static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) {
|
||||||
|
uint64_t value = BrotliUnalignedRead64(p);
|
||||||
|
return BROTLI_BSWAP64_(value);
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) {
|
||||||
|
uint64_t value = BROTLI_BSWAP64_(v);
|
||||||
|
BrotliUnalignedWrite64(p, value);
|
||||||
|
}
|
||||||
|
#else /* BROTLI_LITTLE_ENDIAN */
|
||||||
|
/* Read / store values byte-wise; hopefully compiler will understand. */
|
||||||
|
static BROTLI_INLINE uint16_t BROTLI_UNALIGNED_LOAD16LE(const void* p) {
|
||||||
|
const uint8_t* in = (const uint8_t*)p;
|
||||||
|
return (uint16_t)(in[0] | (in[1] << 8));
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32LE(const void* p) {
|
||||||
|
const uint8_t* in = (const uint8_t*)p;
|
||||||
|
uint32_t value = (uint32_t)(in[0]);
|
||||||
|
value |= (uint32_t)(in[1]) << 8;
|
||||||
|
value |= (uint32_t)(in[2]) << 16;
|
||||||
|
value |= (uint32_t)(in[3]) << 24;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64LE(const void* p) {
|
||||||
|
const uint8_t* in = (const uint8_t*)p;
|
||||||
|
uint64_t value = (uint64_t)(in[0]);
|
||||||
|
value |= (uint64_t)(in[1]) << 8;
|
||||||
|
value |= (uint64_t)(in[2]) << 16;
|
||||||
|
value |= (uint64_t)(in[3]) << 24;
|
||||||
|
value |= (uint64_t)(in[4]) << 32;
|
||||||
|
value |= (uint64_t)(in[5]) << 40;
|
||||||
|
value |= (uint64_t)(in[6]) << 48;
|
||||||
|
value |= (uint64_t)(in[7]) << 56;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64LE(void* p, uint64_t v) {
|
||||||
|
uint8_t* out = (uint8_t*)p;
|
||||||
|
out[0] = (uint8_t)v;
|
||||||
|
out[1] = (uint8_t)(v >> 8);
|
||||||
|
out[2] = (uint8_t)(v >> 16);
|
||||||
|
out[3] = (uint8_t)(v >> 24);
|
||||||
|
out[4] = (uint8_t)(v >> 32);
|
||||||
|
out[5] = (uint8_t)(v >> 40);
|
||||||
|
out[6] = (uint8_t)(v >> 48);
|
||||||
|
out[7] = (uint8_t)(v >> 56);
|
||||||
|
}
|
||||||
|
#endif /* BROTLI_LITTLE_ENDIAN */
|
||||||
|
|
||||||
|
/* BROTLI_IS_CONSTANT macros returns true for compile-time constants. */
|
||||||
|
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_constant_p, 3, 0, 1) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||||
|
#define BROTLI_IS_CONSTANT(x) (!!__builtin_constant_p(x))
|
||||||
|
#else
|
||||||
|
#define BROTLI_IS_CONSTANT(x) (!!0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY)
|
||||||
|
#define BROTLI_HAS_UBFX (!!1)
|
||||||
|
#else
|
||||||
|
#define BROTLI_HAS_UBFX (!!0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_ENABLE_LOG)
|
||||||
|
#define BROTLI_DCHECK(x) assert(x)
|
||||||
|
#define BROTLI_LOG(x) printf x
|
||||||
|
#else
|
||||||
|
#define BROTLI_DCHECK(x)
|
||||||
|
#define BROTLI_LOG(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
|
||||||
|
static BROTLI_INLINE void BrotliDump(const char* f, int l, const char* fn) {
|
||||||
|
fprintf(stderr, "%s:%d (%s)\n", f, l, fn);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__)
|
||||||
|
#else
|
||||||
|
#define BROTLI_DUMP() (void)(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* TODO: add appropriate icc/sunpro/arm/ibm/ti checks. */
|
||||||
|
#if (BROTLI_GNUC_VERSION_CHECK(3, 0, 0) || defined(__llvm__)) && \
|
||||||
|
!defined(BROTLI_BUILD_NO_RBIT)
|
||||||
|
#if defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8_ANY)
|
||||||
|
/* TODO: detect ARMv6T2 and enable this code for it. */
|
||||||
|
static BROTLI_INLINE brotli_reg_t BrotliRBit(brotli_reg_t input) {
|
||||||
|
brotli_reg_t output;
|
||||||
|
__asm__("rbit %0, %1\n" : "=r"(output) : "r"(input));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
#define BROTLI_RBIT(x) BrotliRBit(x)
|
||||||
|
#endif /* armv7 / armv8 */
|
||||||
|
#endif /* gcc || clang */
|
||||||
|
#if !defined(BROTLI_RBIT)
|
||||||
|
static BROTLI_INLINE void BrotliRBit(void) { /* Should break build if used. */ }
|
||||||
|
#endif /* BROTLI_RBIT */
|
||||||
|
|
||||||
|
#define BROTLI_REPEAT(N, X) { \
|
||||||
|
if ((N & 1) != 0) {X;} \
|
||||||
|
if ((N & 2) != 0) {X; X;} \
|
||||||
|
if ((N & 4) != 0) {X; X; X; X;} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BROTLI_UNUSED(X) (void)(X)
|
||||||
|
|
||||||
|
#define BROTLI_MIN_MAX(T) \
|
||||||
|
static BROTLI_INLINE T brotli_min_ ## T (T a, T b) { return a < b ? a : b; } \
|
||||||
|
static BROTLI_INLINE T brotli_max_ ## T (T a, T b) { return a > b ? a : b; }
|
||||||
|
BROTLI_MIN_MAX(double) BROTLI_MIN_MAX(float) BROTLI_MIN_MAX(int)
|
||||||
|
BROTLI_MIN_MAX(size_t) BROTLI_MIN_MAX(uint32_t) BROTLI_MIN_MAX(uint8_t)
|
||||||
|
#undef BROTLI_MIN_MAX
|
||||||
|
#define BROTLI_MIN(T, A, B) (brotli_min_ ## T((A), (B)))
|
||||||
|
#define BROTLI_MAX(T, A, B) (brotli_max_ ## T((A), (B)))
|
||||||
|
|
||||||
|
#define BROTLI_SWAP(T, A, I, J) { \
|
||||||
|
T __brotli_swap_tmp = (A)[(I)]; \
|
||||||
|
(A)[(I)] = (A)[(J)]; \
|
||||||
|
(A)[(J)] = __brotli_swap_tmp; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default brotli_alloc_func */
|
||||||
|
static void* BrotliDefaultAllocFunc(void* opaque, size_t size) {
|
||||||
|
BROTLI_UNUSED(opaque);
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default brotli_free_func */
|
||||||
|
static void BrotliDefaultFreeFunc(void* opaque, void* address) {
|
||||||
|
BROTLI_UNUSED(opaque);
|
||||||
|
free(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
BROTLI_UNUSED_FUNCTION void BrotliSuppressUnusedFunctions(void) {
|
||||||
|
BROTLI_UNUSED(&BrotliSuppressUnusedFunctions);
|
||||||
|
BROTLI_UNUSED(&BrotliUnalignedRead16);
|
||||||
|
BROTLI_UNUSED(&BrotliUnalignedRead32);
|
||||||
|
BROTLI_UNUSED(&BrotliUnalignedRead64);
|
||||||
|
BROTLI_UNUSED(&BrotliUnalignedWrite64);
|
||||||
|
BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD16LE);
|
||||||
|
BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD32LE);
|
||||||
|
BROTLI_UNUSED(&BROTLI_UNALIGNED_LOAD64LE);
|
||||||
|
BROTLI_UNUSED(&BROTLI_UNALIGNED_STORE64LE);
|
||||||
|
BROTLI_UNUSED(&BrotliRBit);
|
||||||
|
BROTLI_UNUSED(&brotli_min_double);
|
||||||
|
BROTLI_UNUSED(&brotli_max_double);
|
||||||
|
BROTLI_UNUSED(&brotli_min_float);
|
||||||
|
BROTLI_UNUSED(&brotli_max_float);
|
||||||
|
BROTLI_UNUSED(&brotli_min_int);
|
||||||
|
BROTLI_UNUSED(&brotli_max_int);
|
||||||
|
BROTLI_UNUSED(&brotli_min_size_t);
|
||||||
|
BROTLI_UNUSED(&brotli_max_size_t);
|
||||||
|
BROTLI_UNUSED(&brotli_min_uint32_t);
|
||||||
|
BROTLI_UNUSED(&brotli_max_uint32_t);
|
||||||
|
BROTLI_UNUSED(&brotli_min_uint8_t);
|
||||||
|
BROTLI_UNUSED(&brotli_max_uint8_t);
|
||||||
|
BROTLI_UNUSED(&BrotliDefaultAllocFunc);
|
||||||
|
BROTLI_UNUSED(&BrotliDefaultFreeFunc);
|
||||||
|
#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
|
||||||
|
BROTLI_UNUSED(&BrotliDump);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* BROTLI_COMMON_PLATFORM_H_ */
|
|
@ -1,107 +0,0 @@
|
||||||
/* Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Macros for compiler / platform specific features and build options. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_COMMON_PORT_H_
|
|
||||||
#define BROTLI_COMMON_PORT_H_
|
|
||||||
|
|
||||||
/* Compatibility with non-clang compilers. */
|
|
||||||
#ifndef __has_builtin
|
|
||||||
#define __has_builtin(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __has_attribute
|
|
||||||
#define __has_attribute(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __has_feature
|
|
||||||
#define __has_feature(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
|
||||||
#define BROTLI_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
|
||||||
#else
|
|
||||||
#define BROTLI_GCC_VERSION 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__ICC)
|
|
||||||
#define BROTLI_ICC_VERSION __ICC
|
|
||||||
#else
|
|
||||||
#define BROTLI_ICC_VERSION 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(BROTLI_BUILD_MODERN_COMPILER)
|
|
||||||
#define BROTLI_MODERN_COMPILER 1
|
|
||||||
#elif BROTLI_GCC_VERSION > 300 || BROTLI_ICC_VERSION >= 1600
|
|
||||||
#define BROTLI_MODERN_COMPILER 1
|
|
||||||
#else
|
|
||||||
#define BROTLI_MODERN_COMPILER 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Define "PREDICT_TRUE" and "PREDICT_FALSE" macros for capable compilers.
|
|
||||||
|
|
||||||
To apply compiler hint, enclose the branching condition into macros, like this:
|
|
||||||
|
|
||||||
if (PREDICT_TRUE(zero == 0)) {
|
|
||||||
// main execution path
|
|
||||||
} else {
|
|
||||||
// compiler should place this code outside of main execution path
|
|
||||||
}
|
|
||||||
|
|
||||||
OR:
|
|
||||||
|
|
||||||
if (PREDICT_FALSE(something_rare_or_unexpected_happens)) {
|
|
||||||
// compiler should place this code outside of main execution path
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
#if BROTLI_MODERN_COMPILER || __has_builtin(__builtin_expect)
|
|
||||||
#define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1))
|
|
||||||
#define PREDICT_FALSE(x) (__builtin_expect(x, 0))
|
|
||||||
#else
|
|
||||||
#define PREDICT_FALSE(x) (x)
|
|
||||||
#define PREDICT_TRUE(x) (x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if BROTLI_MODERN_COMPILER || __has_attribute(always_inline)
|
|
||||||
#define ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline))
|
|
||||||
#else
|
|
||||||
#define ATTRIBUTE_ALWAYS_INLINE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
||||||
#define ATTRIBUTE_VISIBILITY_HIDDEN
|
|
||||||
#elif BROTLI_MODERN_COMPILER || __has_attribute(visibility)
|
|
||||||
#define ATTRIBUTE_VISIBILITY_HIDDEN __attribute__ ((visibility ("hidden")))
|
|
||||||
#else
|
|
||||||
#define ATTRIBUTE_VISIBILITY_HIDDEN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BROTLI_INTERNAL
|
|
||||||
#define BROTLI_INTERNAL ATTRIBUTE_VISIBILITY_HIDDEN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
|
|
||||||
__STDC_VERSION__ >= 199901L
|
|
||||||
#define BROTLI_INLINE inline ATTRIBUTE_ALWAYS_INLINE
|
|
||||||
#else
|
|
||||||
#define BROTLI_INLINE
|
|
||||||
#endif
|
|
||||||
#else /* _MSC_VER */
|
|
||||||
#define BROTLI_INLINE __forceinline
|
|
||||||
#endif /* _MSC_VER */
|
|
||||||
|
|
||||||
#if BROTLI_MODERN_COMPILER || __has_attribute(noinline)
|
|
||||||
#define BROTLI_NOINLINE __attribute__((noinline))
|
|
||||||
#else
|
|
||||||
#define BROTLI_NOINLINE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BROTLI_UNUSED(X) (void)(X)
|
|
||||||
|
|
||||||
#endif /* BROTLI_COMMON_PORT_H_ */
|
|
|
@ -0,0 +1,235 @@
|
||||||
|
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "./transform.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* RFC 7932 transforms string data */
|
||||||
|
static const char kPrefixSuffix[217] =
|
||||||
|
"\1 \2, \10 of the \4 of \2s \1.\5 and \4 "
|
||||||
|
/* 0x _0 _2 __5 _E _3 _6 _8 _E */
|
||||||
|
"in \1\"\4 to \2\">\1\n\2. \1]\5 for \3 a \6 "
|
||||||
|
/* 2x _3_ _5 _A_ _D_ _F _2 _4 _A _E */
|
||||||
|
"that \1\'\6 with \6 from \4 by \1(\6. T"
|
||||||
|
/* 4x _5_ _7 _E _5 _A _C */
|
||||||
|
"he \4 on \4 as \4 is \4ing \2\n\t\1:\3ed "
|
||||||
|
/* 6x _3 _8 _D _2 _7_ _ _A _C */
|
||||||
|
"\2=\"\4 at \3ly \1,\2=\'\5.com/\7. This \5"
|
||||||
|
/* 8x _0 _ _3 _8 _C _E _ _1 _7 _F */
|
||||||
|
" not \3er \3al \4ful \4ive \5less \4es"
|
||||||
|
/* Ax _5 _9 _D _2 _7 _D */
|
||||||
|
"t \4ize \2\xc2\xa0\4ous \5 the \2e \0";
|
||||||
|
/* Cx _2 _7___ ___ _A _F _5 _8 */
|
||||||
|
|
||||||
|
static const uint16_t kPrefixSuffixMap[50] = {
|
||||||
|
0x00, 0x02, 0x05, 0x0E, 0x13, 0x16, 0x18, 0x1E, 0x23, 0x25,
|
||||||
|
0x2A, 0x2D, 0x2F, 0x32, 0x34, 0x3A, 0x3E, 0x45, 0x47, 0x4E,
|
||||||
|
0x55, 0x5A, 0x5C, 0x63, 0x68, 0x6D, 0x72, 0x77, 0x7A, 0x7C,
|
||||||
|
0x80, 0x83, 0x88, 0x8C, 0x8E, 0x91, 0x97, 0x9F, 0xA5, 0xA9,
|
||||||
|
0xAD, 0xB2, 0xB7, 0xBD, 0xC2, 0xC7, 0xCA, 0xCF, 0xD5, 0xD8
|
||||||
|
};
|
||||||
|
|
||||||
|
/* RFC 7932 transforms */
|
||||||
|
static const uint8_t kTransformsData[] = {
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_1, 49,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 47,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||||
|
4, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 3,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 6,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_2, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_1, 49,
|
||||||
|
1, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 1,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 0,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 7,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 9,
|
||||||
|
48, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 8,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 5,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 10,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 11,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_3, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 13,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 14,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_3, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_2, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 15,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 16,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 12,
|
||||||
|
5, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 1,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_4, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 18,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 17,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 19,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 20,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_5, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_6, 49,
|
||||||
|
47, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_4, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 22,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 23,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 24,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 25,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_7, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_1, 26,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 27,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 28,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 12,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 29,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_9, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_FIRST_7, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_6, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 21,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_8, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 31,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 32,
|
||||||
|
47, BROTLI_TRANSFORM_IDENTITY, 3,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_5, 49,
|
||||||
|
49, BROTLI_TRANSFORM_OMIT_LAST_9, 49,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 1,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 8,
|
||||||
|
5, BROTLI_TRANSFORM_IDENTITY, 21,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 0,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 10,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 30,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 5,
|
||||||
|
35, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||||
|
47, BROTLI_TRANSFORM_IDENTITY, 2,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 17,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 36,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 33,
|
||||||
|
5, BROTLI_TRANSFORM_IDENTITY, 0,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 21,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 37,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 30,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 38,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 0,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 39,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 49,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 34,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 8,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 21,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 40,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 12,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 41,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 42,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 17,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 43,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 5,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 10,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 34,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 44,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 5,
|
||||||
|
45, BROTLI_TRANSFORM_IDENTITY, 49,
|
||||||
|
0, BROTLI_TRANSFORM_IDENTITY, 33,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 30,
|
||||||
|
49, BROTLI_TRANSFORM_IDENTITY, 46,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 1,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 33,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 30,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 1,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 33,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 21,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 12,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 5,
|
||||||
|
49, BROTLI_TRANSFORM_UPPERCASE_ALL, 34,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 12,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 30,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_ALL, 34,
|
||||||
|
0, BROTLI_TRANSFORM_UPPERCASE_FIRST, 34,
|
||||||
|
};
|
||||||
|
|
||||||
|
static BrotliTransforms kBrotliTransforms = {
|
||||||
|
sizeof(kPrefixSuffix),
|
||||||
|
(const uint8_t*)kPrefixSuffix,
|
||||||
|
kPrefixSuffixMap,
|
||||||
|
sizeof(kTransformsData) / (3 * sizeof(kTransformsData[0])),
|
||||||
|
kTransformsData,
|
||||||
|
{0, 12, 27, 23, 42, 63, 56, 48, 59, 64}
|
||||||
|
};
|
||||||
|
|
||||||
|
const BrotliTransforms* BrotliGetTransforms(void) {
|
||||||
|
return &kBrotliTransforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ToUpperCase(uint8_t* p) {
|
||||||
|
if (p[0] < 0xC0) {
|
||||||
|
if (p[0] >= 'a' && p[0] <= 'z') {
|
||||||
|
p[0] ^= 32;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* An overly simplified uppercasing model for UTF-8. */
|
||||||
|
if (p[0] < 0xE0) {
|
||||||
|
p[1] ^= 32;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
/* An arbitrary transform for three byte characters. */
|
||||||
|
p[2] ^= 5;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BrotliTransformDictionaryWord(uint8_t* dst, const uint8_t* word, int len,
|
||||||
|
const BrotliTransforms* transforms, int transfom_idx) {
|
||||||
|
int idx = 0;
|
||||||
|
const uint8_t* prefix = BROTLI_TRANSFORM_PREFIX(transforms, transfom_idx);
|
||||||
|
uint8_t type = BROTLI_TRANSFORM_TYPE(transforms, transfom_idx);
|
||||||
|
const uint8_t* suffix = BROTLI_TRANSFORM_SUFFIX(transforms, transfom_idx);
|
||||||
|
{
|
||||||
|
int prefix_len = *prefix++;
|
||||||
|
while (prefix_len--) { dst[idx++] = *prefix++; }
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const int t = type;
|
||||||
|
int i = 0;
|
||||||
|
if (t <= BROTLI_TRANSFORM_OMIT_LAST_9) {
|
||||||
|
len -= t;
|
||||||
|
} else if (t >= BROTLI_TRANSFORM_OMIT_FIRST_1
|
||||||
|
&& t <= BROTLI_TRANSFORM_OMIT_FIRST_9) {
|
||||||
|
int skip = t - (BROTLI_TRANSFORM_OMIT_FIRST_1 - 1);
|
||||||
|
word += skip;
|
||||||
|
len -= skip;
|
||||||
|
}
|
||||||
|
while (i < len) { dst[idx++] = word[i++]; }
|
||||||
|
if (t == BROTLI_TRANSFORM_UPPERCASE_FIRST) {
|
||||||
|
ToUpperCase(&dst[idx - len]);
|
||||||
|
} else if (t == BROTLI_TRANSFORM_UPPERCASE_ALL) {
|
||||||
|
uint8_t* uppercase = &dst[idx - len];
|
||||||
|
while (len > 0) {
|
||||||
|
int step = ToUpperCase(uppercase);
|
||||||
|
uppercase += step;
|
||||||
|
len -= step;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int suffix_len = *suffix++;
|
||||||
|
while (suffix_len--) { dst[idx++] = *suffix++; }
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
|
@ -0,0 +1,80 @@
|
||||||
|
/* transforms is a part of ABI, but not API.
|
||||||
|
|
||||||
|
It means that there are some functions that are supposed to be in "common"
|
||||||
|
library, but header itself is not placed into include/brotli. This way,
|
||||||
|
aforementioned functions will be available only to brotli internals.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROTLI_COMMON_TRANSFORM_H_
|
||||||
|
#define BROTLI_COMMON_TRANSFORM_H_
|
||||||
|
|
||||||
|
#include <brotli/port.h>
|
||||||
|
#include <brotli/types.h>
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum BrotliWordTransformType {
|
||||||
|
BROTLI_TRANSFORM_IDENTITY = 0,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_1 = 1,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_2 = 2,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_3 = 3,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_4 = 4,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_5 = 5,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_6 = 6,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_7 = 7,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_8 = 8,
|
||||||
|
BROTLI_TRANSFORM_OMIT_LAST_9 = 9,
|
||||||
|
BROTLI_TRANSFORM_UPPERCASE_FIRST = 10,
|
||||||
|
BROTLI_TRANSFORM_UPPERCASE_ALL = 11,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_1 = 12,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_2 = 13,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_3 = 14,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_4 = 15,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_5 = 16,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_6 = 17,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_7 = 18,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_8 = 19,
|
||||||
|
BROTLI_TRANSFORM_OMIT_FIRST_9 = 20,
|
||||||
|
BROTLI_NUM_TRANSFORM_TYPES /* Counts transforms, not a transform itself. */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BROTLI_TRANSFORMS_MAX_CUT_OFF BROTLI_TRANSFORM_OMIT_LAST_9
|
||||||
|
|
||||||
|
typedef struct BrotliTransforms {
|
||||||
|
uint16_t prefix_suffix_size;
|
||||||
|
/* Last character must be null, so prefix_suffix_size must be at least 1. */
|
||||||
|
const uint8_t* prefix_suffix;
|
||||||
|
const uint16_t* prefix_suffix_map;
|
||||||
|
uint32_t num_transforms;
|
||||||
|
/* Each entry is a [prefix_id, transform, suffix_id] triplet. */
|
||||||
|
const uint8_t* transforms;
|
||||||
|
/* Indices of transforms like ["", BROTLI_TRANSFORM_OMIT_LAST_#, ""].
|
||||||
|
0-th element corresponds to ["", BROTLI_TRANSFORM_IDENTITY, ""].
|
||||||
|
-1, if cut-off transform does not exist. */
|
||||||
|
int16_t cutOffTransforms[BROTLI_TRANSFORMS_MAX_CUT_OFF + 1];
|
||||||
|
} BrotliTransforms;
|
||||||
|
|
||||||
|
/* T is BrotliTransforms*; result is uint8_t. */
|
||||||
|
#define BROTLI_TRANSFORM_PREFIX_ID(T, I) ((T)->transforms[((I) * 3) + 0])
|
||||||
|
#define BROTLI_TRANSFORM_TYPE(T, I) ((T)->transforms[((I) * 3) + 1])
|
||||||
|
#define BROTLI_TRANSFORM_SUFFIX_ID(T, I) ((T)->transforms[((I) * 3) + 2])
|
||||||
|
|
||||||
|
/* T is BrotliTransforms*; result is const uint8_t*. */
|
||||||
|
#define BROTLI_TRANSFORM_PREFIX(T, I) (&(T)->prefix_suffix[ \
|
||||||
|
(T)->prefix_suffix_map[BROTLI_TRANSFORM_PREFIX_ID(T, I)]])
|
||||||
|
#define BROTLI_TRANSFORM_SUFFIX(T, I) (&(T)->prefix_suffix[ \
|
||||||
|
(T)->prefix_suffix_map[BROTLI_TRANSFORM_SUFFIX_ID(T, I)]])
|
||||||
|
|
||||||
|
BROTLI_COMMON_API const BrotliTransforms* BrotliGetTransforms(void);
|
||||||
|
|
||||||
|
BROTLI_COMMON_API int BrotliTransformDictionaryWord(
|
||||||
|
uint8_t* dst, const uint8_t* word, int len,
|
||||||
|
const BrotliTransforms* transforms, int transform_idx);
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BROTLI_COMMON_TRANSFORM_H_ */
|
|
@ -1,58 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Common types */
|
|
||||||
|
|
||||||
#ifndef BROTLI_COMMON_TYPES_H_
|
|
||||||
#define BROTLI_COMMON_TYPES_H_
|
|
||||||
|
|
||||||
#include <stddef.h> /* for size_t */
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
|
||||||
typedef __int8 int8_t;
|
|
||||||
typedef unsigned __int8 uint8_t;
|
|
||||||
typedef __int16 int16_t;
|
|
||||||
typedef unsigned __int16 uint16_t;
|
|
||||||
typedef __int32 int32_t;
|
|
||||||
typedef unsigned __int32 uint32_t;
|
|
||||||
typedef unsigned __int64 uint64_t;
|
|
||||||
typedef __int64 int64_t;
|
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */
|
|
||||||
|
|
||||||
#if (!defined(_MSC_VER) || (_MSC_VER >= 1800)) && \
|
|
||||||
(defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L))
|
|
||||||
#include <stdbool.h>
|
|
||||||
#define BROTLI_BOOL bool
|
|
||||||
#define BROTLI_TRUE true
|
|
||||||
#define BROTLI_FALSE false
|
|
||||||
#define TO_BROTLI_BOOL(X) (!!(X))
|
|
||||||
#else
|
|
||||||
typedef enum {
|
|
||||||
BROTLI_FALSE = 0,
|
|
||||||
BROTLI_TRUE = !BROTLI_FALSE
|
|
||||||
} BROTLI_BOOL;
|
|
||||||
#define TO_BROTLI_BOOL(X) (!!(X) ? BROTLI_TRUE : BROTLI_FALSE)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAKE_UINT64_T(high, low) ((((uint64_t)(high)) << 32) | low)
|
|
||||||
|
|
||||||
#define BROTLI_UINT32_MAX (~((uint32_t)0))
|
|
||||||
#define BROTLI_SIZE_MAX (~((size_t)0))
|
|
||||||
|
|
||||||
/* Allocating function pointer. Function MUST return 0 in the case of failure.
|
|
||||||
Otherwise it MUST return a valid pointer to a memory region of at least
|
|
||||||
size length. Neither items nor size are allowed to be 0.
|
|
||||||
opaque argument is a pointer provided by client and could be used to bind
|
|
||||||
function to specific object (memory pool). */
|
|
||||||
typedef void* (*brotli_alloc_func)(void* opaque, size_t size);
|
|
||||||
|
|
||||||
/* Deallocating function pointer. Function SHOULD be no-op in the case the
|
|
||||||
address is 0. */
|
|
||||||
typedef void (*brotli_free_func)(void* opaque, void* address);
|
|
||||||
|
|
||||||
#endif /* BROTLI_COMMON_TYPES_H_ */
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Version definition. */
|
||||||
|
|
||||||
|
#ifndef BROTLI_COMMON_VERSION_H_
|
||||||
|
#define BROTLI_COMMON_VERSION_H_
|
||||||
|
|
||||||
|
/* This macro should only be used when library is compiled together with client.
|
||||||
|
If library is dynamically linked, use BrotliDecoderVersion and
|
||||||
|
BrotliEncoderVersion methods. */
|
||||||
|
|
||||||
|
/* Semantic version, calculated as (MAJOR << 24) | (MINOR << 12) | PATCH */
|
||||||
|
#define BROTLI_VERSION 0x1000006
|
||||||
|
|
||||||
|
/* This macro is used by build system to produce Libtool-friendly soname. See
|
||||||
|
https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ABI version, calculated as (CURRENT << 24) | (REVISION << 12) | AGE */
|
||||||
|
#define BROTLI_ABI_VERSION 0x1006000
|
||||||
|
|
||||||
|
#endif /* BROTLI_COMMON_VERSION_H_ */
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
#include "./bit_reader.h"
|
#include "./bit_reader.h"
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -24,7 +24,7 @@ BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br) {
|
||||||
size_t aligned_read_mask = (sizeof(br->val_) >> 1) - 1;
|
size_t aligned_read_mask = (sizeof(br->val_) >> 1) - 1;
|
||||||
/* Fixing alignment after unaligned BrotliFillWindow would result accumulator
|
/* Fixing alignment after unaligned BrotliFillWindow would result accumulator
|
||||||
overflow. If unalignment is caused by BrotliSafeReadBits, then there is
|
overflow. If unalignment is caused by BrotliSafeReadBits, then there is
|
||||||
enough space in accumulator to fix aligment. */
|
enough space in accumulator to fix alignment. */
|
||||||
if (!BROTLI_ALIGNED_READ) {
|
if (!BROTLI_ALIGNED_READ) {
|
||||||
aligned_read_mask = 0;
|
aligned_read_mask = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,22 +11,16 @@
|
||||||
|
|
||||||
#include <string.h> /* memcpy */
|
#include <string.h> /* memcpy */
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (BROTLI_64_BITS)
|
#define BROTLI_SHORT_FILL_BIT_WINDOW_READ (sizeof(brotli_reg_t) >> 1)
|
||||||
#define BROTLI_SHORT_FILL_BIT_WINDOW_READ 4
|
|
||||||
typedef uint64_t reg_t;
|
|
||||||
#else
|
|
||||||
#define BROTLI_SHORT_FILL_BIT_WINDOW_READ 2
|
|
||||||
typedef uint32_t reg_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const uint32_t kBitMask[33] = { 0x0000,
|
static const uint32_t kBitMask[33] = { 0x00000000,
|
||||||
0x00000001, 0x00000003, 0x00000007, 0x0000000F,
|
0x00000001, 0x00000003, 0x00000007, 0x0000000F,
|
||||||
0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
|
0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,
|
||||||
0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
|
0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,
|
||||||
|
@ -38,34 +32,35 @@ static const uint32_t kBitMask[33] = { 0x0000,
|
||||||
};
|
};
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t BitMask(uint32_t n) {
|
static BROTLI_INLINE uint32_t BitMask(uint32_t n) {
|
||||||
if (IS_CONSTANT(n) || BROTLI_HAS_UBFX) {
|
if (BROTLI_IS_CONSTANT(n) || BROTLI_HAS_UBFX) {
|
||||||
/* Masking with this expression turns to a single
|
/* Masking with this expression turns to a single
|
||||||
"Unsigned Bit Field Extract" UBFX instruction on ARM. */
|
"Unsigned Bit Field Extract" UBFX instruction on ARM. */
|
||||||
return ~((0xffffffffU) << n);
|
return ~((0xFFFFFFFFu) << n);
|
||||||
} else {
|
} else {
|
||||||
return kBitMask[n];
|
return kBitMask[n];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
reg_t val_; /* pre-fetched bits */
|
brotli_reg_t val_; /* pre-fetched bits */
|
||||||
uint32_t bit_pos_; /* current bit-reading position in val_ */
|
uint32_t bit_pos_; /* current bit-reading position in val_ */
|
||||||
const uint8_t* next_in; /* the byte we're reading from */
|
const uint8_t* next_in; /* the byte we're reading from */
|
||||||
size_t avail_in;
|
size_t avail_in;
|
||||||
} BrotliBitReader;
|
} BrotliBitReader;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
reg_t val_;
|
brotli_reg_t val_;
|
||||||
uint32_t bit_pos_;
|
uint32_t bit_pos_;
|
||||||
const uint8_t* next_in;
|
const uint8_t* next_in;
|
||||||
size_t avail_in;
|
size_t avail_in;
|
||||||
} BrotliBitReaderState;
|
} BrotliBitReaderState;
|
||||||
|
|
||||||
/* Initializes the bitreader fields. */
|
/* Initializes the BrotliBitReader fields. */
|
||||||
BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* const br);
|
BROTLI_INTERNAL void BrotliInitBitReader(BrotliBitReader* const br);
|
||||||
|
|
||||||
/* Ensures that accumulator is not empty. May consume one byte of input.
|
/* Ensures that accumulator is not empty.
|
||||||
Returns 0 if data is required but there is no input available.
|
May consume up to sizeof(brotli_reg_t) - 1 bytes of input.
|
||||||
|
Returns BROTLI_FALSE if data is required but there is no input available.
|
||||||
For BROTLI_ALIGNED_READ this function also prepares bit reader for aligned
|
For BROTLI_ALIGNED_READ this function also prepares bit reader for aligned
|
||||||
reading. */
|
reading. */
|
||||||
BROTLI_INTERNAL BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br);
|
BROTLI_INTERNAL BROTLI_BOOL BrotliWarmupBitReader(BrotliBitReader* const br);
|
||||||
|
@ -97,89 +92,34 @@ static BROTLI_INLINE size_t BrotliGetRemainingBytes(BrotliBitReader* br) {
|
||||||
return br->avail_in + (BrotliGetAvailableBits(br) >> 3);
|
return br->avail_in + (BrotliGetAvailableBits(br) >> 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Checks if there is at least num bytes left in the input ringbuffer (excluding
|
/* Checks if there is at least |num| bytes left in the input ring-buffer
|
||||||
the bits remaining in br->val_). */
|
(excluding the bits remaining in br->val_). */
|
||||||
static BROTLI_INLINE BROTLI_BOOL BrotliCheckInputAmount(
|
static BROTLI_INLINE BROTLI_BOOL BrotliCheckInputAmount(
|
||||||
BrotliBitReader* const br, size_t num) {
|
BrotliBitReader* const br, size_t num) {
|
||||||
return TO_BROTLI_BOOL(br->avail_in >= num);
|
return TO_BROTLI_BOOL(br->avail_in >= num);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE uint16_t BrotliLoad16LE(const uint8_t* in) {
|
/* Guarantees that there are at least |n_bits| + 1 bits in accumulator.
|
||||||
if (BROTLI_LITTLE_ENDIAN) {
|
|
||||||
return *((const uint16_t*)in);
|
|
||||||
} else if (BROTLI_BIG_ENDIAN) {
|
|
||||||
uint16_t value = *((const uint16_t*)in);
|
|
||||||
return (uint16_t)(((value & 0xFFU) << 8) | ((value & 0xFF00U) >> 8));
|
|
||||||
} else {
|
|
||||||
return (uint16_t)(in[0] | (in[1] << 8));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t BrotliLoad32LE(const uint8_t* in) {
|
|
||||||
if (BROTLI_LITTLE_ENDIAN) {
|
|
||||||
return *((const uint32_t*)in);
|
|
||||||
} else if (BROTLI_BIG_ENDIAN) {
|
|
||||||
uint32_t value = *((const uint32_t*)in);
|
|
||||||
return ((value & 0xFFU) << 24) | ((value & 0xFF00U) << 8) |
|
|
||||||
((value & 0xFF0000U) >> 8) | ((value & 0xFF000000U) >> 24);
|
|
||||||
} else {
|
|
||||||
uint32_t value = (uint32_t)(*(in++));
|
|
||||||
value |= (uint32_t)(*(in++)) << 8;
|
|
||||||
value |= (uint32_t)(*(in++)) << 16;
|
|
||||||
value |= (uint32_t)(*(in++)) << 24;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (BROTLI_64_BITS)
|
|
||||||
static BROTLI_INLINE uint64_t BrotliLoad64LE(const uint8_t* in) {
|
|
||||||
if (BROTLI_LITTLE_ENDIAN) {
|
|
||||||
return *((const uint64_t*)in);
|
|
||||||
} else if (BROTLI_BIG_ENDIAN) {
|
|
||||||
uint64_t value = *((const uint64_t*)in);
|
|
||||||
return
|
|
||||||
((value & 0xFFU) << 56) |
|
|
||||||
((value & 0xFF00U) << 40) |
|
|
||||||
((value & 0xFF0000U) << 24) |
|
|
||||||
((value & 0xFF000000U) << 8) |
|
|
||||||
((value & 0xFF00000000U) >> 8) |
|
|
||||||
((value & 0xFF0000000000U) >> 24) |
|
|
||||||
((value & 0xFF000000000000U) >> 40) |
|
|
||||||
((value & 0xFF00000000000000U) >> 56);
|
|
||||||
} else {
|
|
||||||
uint64_t value = (uint64_t)(*(in++));
|
|
||||||
value |= (uint64_t)(*(in++)) << 8;
|
|
||||||
value |= (uint64_t)(*(in++)) << 16;
|
|
||||||
value |= (uint64_t)(*(in++)) << 24;
|
|
||||||
value |= (uint64_t)(*(in++)) << 32;
|
|
||||||
value |= (uint64_t)(*(in++)) << 40;
|
|
||||||
value |= (uint64_t)(*(in++)) << 48;
|
|
||||||
value |= (uint64_t)(*(in++)) << 56;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Guarantees that there are at least n_bits + 1 bits in accumulator.
|
|
||||||
Precondition: accumulator contains at least 1 bit.
|
Precondition: accumulator contains at least 1 bit.
|
||||||
n_bits should be in the range [1..24] for regular build. For portable
|
|n_bits| should be in the range [1..24] for regular build. For portable
|
||||||
non-64-bit little endian build only 16 bits are safe to request. */
|
non-64-bit little-endian build only 16 bits are safe to request. */
|
||||||
static BROTLI_INLINE void BrotliFillBitWindow(
|
static BROTLI_INLINE void BrotliFillBitWindow(
|
||||||
BrotliBitReader* const br, uint32_t n_bits) {
|
BrotliBitReader* const br, uint32_t n_bits) {
|
||||||
#if (BROTLI_64_BITS)
|
#if (BROTLI_64_BITS)
|
||||||
if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
if (!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
||||||
if (br->bit_pos_ >= 56) {
|
if (br->bit_pos_ >= 56) {
|
||||||
br->val_ >>= 56;
|
br->val_ >>= 56;
|
||||||
br->bit_pos_ ^= 56; /* here same as -= 56 because of the if condition */
|
br->bit_pos_ ^= 56; /* here same as -= 56 because of the if condition */
|
||||||
br->val_ |= BrotliLoad64LE(br->next_in) << 8;
|
br->val_ |= BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 8;
|
||||||
br->avail_in -= 7;
|
br->avail_in -= 7;
|
||||||
br->next_in += 7;
|
br->next_in += 7;
|
||||||
}
|
}
|
||||||
} else if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 16)) {
|
} else if (
|
||||||
|
!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 16)) {
|
||||||
if (br->bit_pos_ >= 48) {
|
if (br->bit_pos_ >= 48) {
|
||||||
br->val_ >>= 48;
|
br->val_ >>= 48;
|
||||||
br->bit_pos_ ^= 48; /* here same as -= 48 because of the if condition */
|
br->bit_pos_ ^= 48; /* here same as -= 48 because of the if condition */
|
||||||
br->val_ |= BrotliLoad64LE(br->next_in) << 16;
|
br->val_ |= BROTLI_UNALIGNED_LOAD64LE(br->next_in) << 16;
|
||||||
br->avail_in -= 6;
|
br->avail_in -= 6;
|
||||||
br->next_in += 6;
|
br->next_in += 6;
|
||||||
}
|
}
|
||||||
|
@ -187,17 +127,17 @@ static BROTLI_INLINE void BrotliFillBitWindow(
|
||||||
if (br->bit_pos_ >= 32) {
|
if (br->bit_pos_ >= 32) {
|
||||||
br->val_ >>= 32;
|
br->val_ >>= 32;
|
||||||
br->bit_pos_ ^= 32; /* here same as -= 32 because of the if condition */
|
br->bit_pos_ ^= 32; /* here same as -= 32 because of the if condition */
|
||||||
br->val_ |= ((uint64_t)BrotliLoad32LE(br->next_in)) << 32;
|
br->val_ |= ((uint64_t)BROTLI_UNALIGNED_LOAD32LE(br->next_in)) << 32;
|
||||||
br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||||
br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (!BROTLI_ALIGNED_READ && IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
if (!BROTLI_ALIGNED_READ && BROTLI_IS_CONSTANT(n_bits) && (n_bits <= 8)) {
|
||||||
if (br->bit_pos_ >= 24) {
|
if (br->bit_pos_ >= 24) {
|
||||||
br->val_ >>= 24;
|
br->val_ >>= 24;
|
||||||
br->bit_pos_ ^= 24; /* here same as -= 24 because of the if condition */
|
br->bit_pos_ ^= 24; /* here same as -= 24 because of the if condition */
|
||||||
br->val_ |= BrotliLoad32LE(br->next_in) << 8;
|
br->val_ |= BROTLI_UNALIGNED_LOAD32LE(br->next_in) << 8;
|
||||||
br->avail_in -= 3;
|
br->avail_in -= 3;
|
||||||
br->next_in += 3;
|
br->next_in += 3;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +145,7 @@ static BROTLI_INLINE void BrotliFillBitWindow(
|
||||||
if (br->bit_pos_ >= 16) {
|
if (br->bit_pos_ >= 16) {
|
||||||
br->val_ >>= 16;
|
br->val_ >>= 16;
|
||||||
br->bit_pos_ ^= 16; /* here same as -= 16 because of the if condition */
|
br->bit_pos_ ^= 16; /* here same as -= 16 because of the if condition */
|
||||||
br->val_ |= ((uint32_t)BrotliLoad16LE(br->next_in)) << 16;
|
br->val_ |= ((uint32_t)BROTLI_UNALIGNED_LOAD16LE(br->next_in)) << 16;
|
||||||
br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
br->avail_in -= BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||||
br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
br->next_in += BROTLI_SHORT_FILL_BIT_WINDOW_READ;
|
||||||
}
|
}
|
||||||
|
@ -213,13 +153,14 @@ static BROTLI_INLINE void BrotliFillBitWindow(
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mosltly like BrotliFillBitWindow, but guarantees only 16 bits and reads no
|
/* Mostly like BrotliFillBitWindow, but guarantees only 16 bits and reads no
|
||||||
more than BROTLI_SHORT_FILL_BIT_WINDOW_READ bytes of input. */
|
more than BROTLI_SHORT_FILL_BIT_WINDOW_READ bytes of input. */
|
||||||
static BROTLI_INLINE void BrotliFillBitWindow16(BrotliBitReader* const br) {
|
static BROTLI_INLINE void BrotliFillBitWindow16(BrotliBitReader* const br) {
|
||||||
BrotliFillBitWindow(br, 17);
|
BrotliFillBitWindow(br, 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pulls one byte of input to accumulator. */
|
/* Tries to pull one byte of input to accumulator.
|
||||||
|
Returns BROTLI_FALSE if there is no input available. */
|
||||||
static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) {
|
static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) {
|
||||||
if (br->avail_in == 0) {
|
if (br->avail_in == 0) {
|
||||||
return BROTLI_FALSE;
|
return BROTLI_FALSE;
|
||||||
|
@ -237,8 +178,9 @@ static BROTLI_INLINE BROTLI_BOOL BrotliPullByte(BrotliBitReader* const br) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns currently available bits.
|
/* Returns currently available bits.
|
||||||
The number of valid bits could be calclulated by BrotliGetAvailableBits. */
|
The number of valid bits could be calculated by BrotliGetAvailableBits. */
|
||||||
static BROTLI_INLINE reg_t BrotliGetBitsUnmasked(BrotliBitReader* const br) {
|
static BROTLI_INLINE brotli_reg_t BrotliGetBitsUnmasked(
|
||||||
|
BrotliBitReader* const br) {
|
||||||
return br->val_ >> br->bit_pos_;
|
return br->val_ >> br->bit_pos_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,15 +192,16 @@ static BROTLI_INLINE uint32_t BrotliGet16BitsUnmasked(
|
||||||
return (uint32_t)BrotliGetBitsUnmasked(br);
|
return (uint32_t)BrotliGetBitsUnmasked(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the specified number of bits from br without advancing bit pos. */
|
/* Returns the specified number of bits from |br| without advancing bit
|
||||||
|
position. */
|
||||||
static BROTLI_INLINE uint32_t BrotliGetBits(
|
static BROTLI_INLINE uint32_t BrotliGetBits(
|
||||||
BrotliBitReader* const br, uint32_t n_bits) {
|
BrotliBitReader* const br, uint32_t n_bits) {
|
||||||
BrotliFillBitWindow(br, n_bits);
|
BrotliFillBitWindow(br, n_bits);
|
||||||
return (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
|
return (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tries to peek the specified amount of bits. Returns 0, if there is not
|
/* Tries to peek the specified amount of bits. Returns BROTLI_FALSE, if there
|
||||||
enough input. */
|
is not enough input. */
|
||||||
static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits(
|
static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits(
|
||||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||||
while (BrotliGetAvailableBits(br) < n_bits) {
|
while (BrotliGetAvailableBits(br) < n_bits) {
|
||||||
|
@ -270,7 +213,7 @@ static BROTLI_INLINE BROTLI_BOOL BrotliSafeGetBits(
|
||||||
return BROTLI_TRUE;
|
return BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advances the bit pos by n_bits. */
|
/* Advances the bit pos by |n_bits|. */
|
||||||
static BROTLI_INLINE void BrotliDropBits(
|
static BROTLI_INLINE void BrotliDropBits(
|
||||||
BrotliBitReader* const br, uint32_t n_bits) {
|
BrotliBitReader* const br, uint32_t n_bits) {
|
||||||
br->bit_pos_ += n_bits;
|
br->bit_pos_ += n_bits;
|
||||||
|
@ -289,17 +232,17 @@ static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) {
|
||||||
br->bit_pos_ += unused_bits;
|
br->bit_pos_ += unused_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reads the specified number of bits from br and advances the bit pos.
|
/* Reads the specified number of bits from |br| and advances the bit pos.
|
||||||
Precondition: accumulator MUST contain at least n_bits. */
|
Precondition: accumulator MUST contain at least |n_bits|. */
|
||||||
static BROTLI_INLINE void BrotliTakeBits(
|
static BROTLI_INLINE void BrotliTakeBits(
|
||||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||||
*val = (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
|
*val = (uint32_t)BrotliGetBitsUnmasked(br) & BitMask(n_bits);
|
||||||
BROTLI_LOG(("[BrotliReadBits] %d %d %d val: %6x\n",
|
BROTLI_LOG(("[BrotliReadBits] %d %d %d val: %6x\n",
|
||||||
(int)br->avail_in, (int)br->bit_pos_, n_bits, (int)*val));
|
(int)br->avail_in, (int)br->bit_pos_, (int)n_bits, (int)*val));
|
||||||
BrotliDropBits(br, n_bits);
|
BrotliDropBits(br, n_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reads the specified number of bits from br and advances the bit pos.
|
/* Reads the specified number of bits from |br| and advances the bit pos.
|
||||||
Assumes that there is enough input to perform BrotliFillBitWindow. */
|
Assumes that there is enough input to perform BrotliFillBitWindow. */
|
||||||
static BROTLI_INLINE uint32_t BrotliReadBits(
|
static BROTLI_INLINE uint32_t BrotliReadBits(
|
||||||
BrotliBitReader* const br, uint32_t n_bits) {
|
BrotliBitReader* const br, uint32_t n_bits) {
|
||||||
|
@ -319,8 +262,8 @@ static BROTLI_INLINE uint32_t BrotliReadBits(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tries to read the specified amount of bits. Returns 0, if there is not
|
/* Tries to read the specified amount of bits. Returns BROTLI_FALSE, if there
|
||||||
enough input. n_bits MUST be positive. */
|
is not enough input. |n_bits| MUST be positive. */
|
||||||
static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits(
|
static BROTLI_INLINE BROTLI_BOOL BrotliSafeReadBits(
|
||||||
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
BrotliBitReader* const br, uint32_t n_bits, uint32_t* val) {
|
||||||
while (BrotliGetAvailableBits(br) < n_bits) {
|
while (BrotliGetAvailableBits(br) < n_bits) {
|
||||||
|
@ -343,25 +286,8 @@ static BROTLI_INLINE BROTLI_BOOL BrotliJumpToByteBoundary(BrotliBitReader* br) {
|
||||||
return TO_BROTLI_BOOL(pad_bits == 0);
|
return TO_BROTLI_BOOL(pad_bits == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Peeks a byte at specified offset.
|
|
||||||
Precondition: bit reader is parked to a byte boundary.
|
|
||||||
Returns -1 if operation is not feasible. */
|
|
||||||
static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, size_t offset) {
|
|
||||||
uint32_t available_bits = BrotliGetAvailableBits(br);
|
|
||||||
size_t bytes_left = available_bits >> 3;
|
|
||||||
BROTLI_DCHECK((available_bits & 7) == 0);
|
|
||||||
if (offset < bytes_left) {
|
|
||||||
return (BrotliGetBitsUnmasked(br) >> (unsigned)(offset << 3)) & 0xFF;
|
|
||||||
}
|
|
||||||
offset -= bytes_left;
|
|
||||||
if (offset < br->avail_in) {
|
|
||||||
return br->next_in[offset];
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copies remaining input bytes stored in the bit reader to the output. Value
|
/* 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
|
|num| may not be larger than BrotliGetRemainingBytes. The bit reader must be
|
||||||
warmed up again after this. */
|
warmed up again after this. */
|
||||||
static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest,
|
static BROTLI_INLINE void BrotliCopyBytes(uint8_t* dest,
|
||||||
BrotliBitReader* br, size_t num) {
|
BrotliBitReader* br, size_t num) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,188 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* API for Brotli decompression */
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_DECODE_H_
|
|
||||||
#define BROTLI_DEC_DECODE_H_
|
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct BrotliDecoderStateStruct BrotliDecoderState;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
/* Decoding error, e.g. corrupt input or memory allocation problem */
|
|
||||||
BROTLI_DECODER_RESULT_ERROR = 0,
|
|
||||||
/* Decoding successfully completed */
|
|
||||||
BROTLI_DECODER_RESULT_SUCCESS = 1,
|
|
||||||
/* Partially done; should be called again with more input */
|
|
||||||
BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2,
|
|
||||||
/* Partially done; should be called again with more output */
|
|
||||||
BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3
|
|
||||||
} BrotliDecoderResult;
|
|
||||||
|
|
||||||
#define BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \
|
|
||||||
BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \
|
|
||||||
/* Same as BrotliDecoderResult values */ \
|
|
||||||
BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \
|
|
||||||
\
|
|
||||||
/* Errors caused by invalid input */ \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \
|
|
||||||
\
|
|
||||||
/* -16..-20 codes are reserved */ \
|
|
||||||
\
|
|
||||||
/* Memory allocation problems */ \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \
|
|
||||||
/* Literal, insert and distance trees together */ \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \
|
|
||||||
/* -23..-24 codes are reserved for distinct tree groups */ \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \
|
|
||||||
/* -28..-29 codes are reserved for dynamic ringbuffer allocation */ \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \
|
|
||||||
\
|
|
||||||
/* "Impossible" states */ \
|
|
||||||
BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31)
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
#define _BROTLI_COMMA ,
|
|
||||||
#define _BROTLI_ERROR_CODE_ENUM_ITEM(PREFIX, NAME, CODE) \
|
|
||||||
BROTLI_DECODER ## PREFIX ## NAME = CODE
|
|
||||||
BROTLI_DECODER_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_ENUM_ITEM, _BROTLI_COMMA)
|
|
||||||
#undef _BROTLI_ERROR_CODE_ENUM_ITEM
|
|
||||||
#undef _BROTLI_COMMA
|
|
||||||
} BrotliDecoderErrorCode;
|
|
||||||
|
|
||||||
#define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE
|
|
||||||
|
|
||||||
/* Creates the instance of BrotliDecoderState and initializes it. |alloc_func|
|
|
||||||
and |free_func| MUST be both zero or both non-zero. In the case they are both
|
|
||||||
zero, default memory allocators are used. |opaque| is passed to |alloc_func|
|
|
||||||
and |free_func| when they are called. */
|
|
||||||
BrotliDecoderState* BrotliDecoderCreateInstance(
|
|
||||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
|
||||||
|
|
||||||
/* Deinitializes and frees BrotliDecoderState instance. */
|
|
||||||
void BrotliDecoderDestroyInstance(BrotliDecoderState* state);
|
|
||||||
|
|
||||||
/* Decompresses the data in |encoded_buffer| into |decoded_buffer|, and sets
|
|
||||||
|*decoded_size| to the decompressed length. */
|
|
||||||
BrotliDecoderResult BrotliDecoderDecompress(
|
|
||||||
size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size,
|
|
||||||
uint8_t* decoded_buffer);
|
|
||||||
|
|
||||||
/* Decompresses the data. Supports partial input and output.
|
|
||||||
|
|
||||||
Must be called with an allocated input buffer in |*next_in| and an allocated
|
|
||||||
output buffer in |*next_out|. The values |*available_in| and |*available_out|
|
|
||||||
must specify the allocated size in |*next_in| and |*next_out| respectively.
|
|
||||||
|
|
||||||
After each call, |*available_in| will be decremented by the amount of input
|
|
||||||
bytes consumed, and the |*next_in| pointer will be incremented by that
|
|
||||||
amount. Similarly, |*available_out| will be decremented by the amount of
|
|
||||||
output bytes written, and the |*next_out| pointer will be incremented by that
|
|
||||||
amount. |total_out|, if it is not a null-pointer, will be set to the number
|
|
||||||
of bytes decompressed since the last state initialization.
|
|
||||||
|
|
||||||
Input is never overconsumed, so |next_in| and |available_in| could be passed
|
|
||||||
to the next consumer after decoding is complete. */
|
|
||||||
BrotliDecoderResult BrotliDecoderDecompressStream(
|
|
||||||
BrotliDecoderState* s, size_t* available_in, const uint8_t** next_in,
|
|
||||||
size_t* available_out, uint8_t** next_out, size_t* total_out);
|
|
||||||
|
|
||||||
/* Fills the new state with a dictionary for LZ77, warming up the ringbuffer,
|
|
||||||
e.g. for custom static dictionaries for data formats.
|
|
||||||
Not to be confused with the built-in transformable dictionary of Brotli.
|
|
||||||
|size| should be less or equal to 2^24 (16MiB), otherwise the dictionary will
|
|
||||||
be ignored. The dictionary must exist in memory until decoding is done and
|
|
||||||
is owned by the caller. To use:
|
|
||||||
1) Allocate and initialize state with BrotliCreateInstance
|
|
||||||
2) Use BrotliSetCustomDictionary
|
|
||||||
3) Use BrotliDecompressStream
|
|
||||||
4) Clean up and free state with BrotliDestroyState
|
|
||||||
*/
|
|
||||||
void BrotliDecoderSetCustomDictionary(
|
|
||||||
BrotliDecoderState* s, size_t size, const uint8_t* dict);
|
|
||||||
|
|
||||||
/* Returns true, if decoder has some unconsumed output.
|
|
||||||
Otherwise returns false. */
|
|
||||||
BROTLI_BOOL BrotliDecoderHasMoreOutput(const BrotliDecoderState* s);
|
|
||||||
|
|
||||||
/* Returns true, if decoder has already received some input bytes.
|
|
||||||
Otherwise returns false. */
|
|
||||||
BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* s);
|
|
||||||
|
|
||||||
/* Returns true, if decoder is in a state where we reached the end of the input
|
|
||||||
and produced all of the output; returns false otherwise. */
|
|
||||||
BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* s);
|
|
||||||
|
|
||||||
/* Returns detailed error code after BrotliDecompressStream returns
|
|
||||||
BROTLI_DECODER_RESULT_ERROR. */
|
|
||||||
BrotliDecoderErrorCode BrotliDecoderGetErrorCode(const BrotliDecoderState* s);
|
|
||||||
|
|
||||||
const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c);
|
|
||||||
|
|
||||||
/* DEPRECATED >>> */
|
|
||||||
typedef enum {
|
|
||||||
BROTLI_RESULT_ERROR = 0,
|
|
||||||
BROTLI_RESULT_SUCCESS = 1,
|
|
||||||
BROTLI_RESULT_NEEDS_MORE_INPUT = 2,
|
|
||||||
BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3
|
|
||||||
} BrotliResult;
|
|
||||||
typedef enum {
|
|
||||||
#define _BROTLI_COMMA ,
|
|
||||||
#define _BROTLI_ERROR_CODE_ENUM_ITEM(PREFIX, NAME, CODE) \
|
|
||||||
BROTLI ## PREFIX ## NAME = CODE
|
|
||||||
BROTLI_DECODER_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_ENUM_ITEM, _BROTLI_COMMA)
|
|
||||||
#undef _BROTLI_ERROR_CODE_ENUM_ITEM
|
|
||||||
#undef _BROTLI_COMMA
|
|
||||||
} BrotliErrorCode;
|
|
||||||
typedef struct BrotliStateStruct BrotliState;
|
|
||||||
BrotliState* BrotliCreateState(
|
|
||||||
brotli_alloc_func alloc, brotli_free_func free, void* opaque);
|
|
||||||
void BrotliDestroyState(BrotliState* state);
|
|
||||||
BROTLI_BOOL BrotliDecompressedSize(
|
|
||||||
size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size);
|
|
||||||
BrotliResult BrotliDecompressBuffer(
|
|
||||||
size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size,
|
|
||||||
uint8_t* decoded_buffer);
|
|
||||||
BrotliResult BrotliDecompressStream(
|
|
||||||
size_t* available_in, const uint8_t** next_in, size_t* available_out,
|
|
||||||
uint8_t** next_out, size_t* total_out, BrotliState* s);
|
|
||||||
void BrotliSetCustomDictionary(
|
|
||||||
size_t size, const uint8_t* dict, BrotliState* s);
|
|
||||||
BROTLI_BOOL BrotliStateIsStreamStart(const BrotliState* s);
|
|
||||||
BROTLI_BOOL BrotliStateIsStreamEnd(const BrotliState* s);
|
|
||||||
BrotliErrorCode BrotliGetErrorCode(const BrotliState* s);
|
|
||||||
const char* BrotliErrorString(BrotliErrorCode c);
|
|
||||||
/* <<< DEPRECATED */
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_DECODE_H_ */
|
|
|
@ -11,8 +11,8 @@
|
||||||
#include <string.h> /* memcpy, memset */
|
#include <string.h> /* memcpy, memset */
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -20,8 +20,9 @@ extern "C" {
|
||||||
|
|
||||||
#define BROTLI_REVERSE_BITS_MAX 8
|
#define BROTLI_REVERSE_BITS_MAX 8
|
||||||
|
|
||||||
#ifdef BROTLI_RBIT
|
#if defined(BROTLI_RBIT)
|
||||||
#define BROTLI_REVERSE_BITS_BASE (32 - BROTLI_REVERSE_BITS_MAX)
|
#define BROTLI_REVERSE_BITS_BASE \
|
||||||
|
((sizeof(brotli_reg_t) << 3) - BROTLI_REVERSE_BITS_MAX)
|
||||||
#else
|
#else
|
||||||
#define BROTLI_REVERSE_BITS_BASE 0
|
#define BROTLI_REVERSE_BITS_BASE 0
|
||||||
static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = {
|
static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = {
|
||||||
|
@ -61,13 +62,13 @@ static uint8_t kReverseBits[1 << BROTLI_REVERSE_BITS_MAX] = {
|
||||||
#endif /* BROTLI_RBIT */
|
#endif /* BROTLI_RBIT */
|
||||||
|
|
||||||
#define BROTLI_REVERSE_BITS_LOWEST \
|
#define BROTLI_REVERSE_BITS_LOWEST \
|
||||||
(1U << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE))
|
((brotli_reg_t)1 << (BROTLI_REVERSE_BITS_MAX - 1 + BROTLI_REVERSE_BITS_BASE))
|
||||||
|
|
||||||
/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX),
|
/* Returns reverse(num >> BROTLI_REVERSE_BITS_BASE, BROTLI_REVERSE_BITS_MAX),
|
||||||
where reverse(value, len) is the bit-wise reversal of the len least
|
where reverse(value, len) is the bit-wise reversal of the len least
|
||||||
significant bits of value. */
|
significant bits of value. */
|
||||||
static BROTLI_INLINE uint32_t BrotliReverseBits(uint32_t num) {
|
static BROTLI_INLINE brotli_reg_t BrotliReverseBits(brotli_reg_t num) {
|
||||||
#ifdef BROTLI_RBIT
|
#if defined(BROTLI_RBIT)
|
||||||
return BROTLI_RBIT(num);
|
return BROTLI_RBIT(num);
|
||||||
#else
|
#else
|
||||||
return kReverseBits[num];
|
return kReverseBits[num];
|
||||||
|
@ -85,9 +86,9 @@ static BROTLI_INLINE void ReplicateValue(HuffmanCode* table,
|
||||||
} while (end > 0);
|
} while (end > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the table width of the next 2nd level table. count is the histogram
|
/* Returns the table width of the next 2nd level table. |count| is the histogram
|
||||||
of bit lengths for the remaining symbols, len is the code length of the next
|
of bit lengths for the remaining symbols, |len| is the code length of the
|
||||||
processed symbol */
|
next processed symbol. */
|
||||||
static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count,
|
static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count,
|
||||||
int len, int root_bits) {
|
int len, int root_bits) {
|
||||||
int left = 1 << (len - root_bits);
|
int left = 1 << (len - root_bits);
|
||||||
|
@ -103,12 +104,12 @@ static BROTLI_INLINE int NextTableBitSize(const uint16_t* const count,
|
||||||
void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
||||||
const uint8_t* const code_lengths,
|
const uint8_t* const code_lengths,
|
||||||
uint16_t* count) {
|
uint16_t* count) {
|
||||||
HuffmanCode code; /* current table entry */
|
HuffmanCode code; /* current table entry */
|
||||||
int symbol; /* symbol index in original or sorted table */
|
int symbol; /* symbol index in original or sorted table */
|
||||||
uint32_t key; /* prefix code */
|
brotli_reg_t key; /* prefix code */
|
||||||
uint32_t key_step; /* prefix code addend */
|
brotli_reg_t key_step; /* prefix code addend */
|
||||||
int step; /* step size to replicate values in current table */
|
int step; /* step size to replicate values in current table */
|
||||||
int table_size; /* size of current table */
|
int table_size; /* size of current table */
|
||||||
int sorted[BROTLI_CODE_LENGTH_CODES]; /* symbols sorted by code length */
|
int sorted[BROTLI_CODE_LENGTH_CODES]; /* symbols sorted by code length */
|
||||||
/* offsets in sorted table for each length */
|
/* offsets in sorted table for each length */
|
||||||
int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1];
|
int offset[BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1];
|
||||||
|
@ -117,7 +118,7 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
||||||
BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <=
|
BROTLI_DCHECK(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH <=
|
||||||
BROTLI_REVERSE_BITS_MAX);
|
BROTLI_REVERSE_BITS_MAX);
|
||||||
|
|
||||||
/* generate offsets into sorted symbol table by code length */
|
/* Generate offsets into sorted symbol table by code length. */
|
||||||
symbol = -1;
|
symbol = -1;
|
||||||
bits = 1;
|
bits = 1;
|
||||||
BROTLI_REPEAT(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH, {
|
BROTLI_REPEAT(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH, {
|
||||||
|
@ -128,7 +129,7 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
||||||
/* Symbols with code length 0 are placed after all other symbols. */
|
/* Symbols with code length 0 are placed after all other symbols. */
|
||||||
offset[0] = BROTLI_CODE_LENGTH_CODES - 1;
|
offset[0] = BROTLI_CODE_LENGTH_CODES - 1;
|
||||||
|
|
||||||
/* sort symbols by length, by symbol order within each length */
|
/* Sort symbols by length, by symbol order within each length. */
|
||||||
symbol = BROTLI_CODE_LENGTH_CODES;
|
symbol = BROTLI_CODE_LENGTH_CODES;
|
||||||
do {
|
do {
|
||||||
BROTLI_REPEAT(6, {
|
BROTLI_REPEAT(6, {
|
||||||
|
@ -143,13 +144,13 @@ void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* table,
|
||||||
if (offset[0] == 0) {
|
if (offset[0] == 0) {
|
||||||
code.bits = 0;
|
code.bits = 0;
|
||||||
code.value = (uint16_t)sorted[0];
|
code.value = (uint16_t)sorted[0];
|
||||||
for (key = 0; key < (uint32_t)table_size; ++key) {
|
for (key = 0; key < (brotli_reg_t)table_size; ++key) {
|
||||||
table[key] = code;
|
table[key] = code;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill in table */
|
/* Fill in table. */
|
||||||
key = 0;
|
key = 0;
|
||||||
key_step = BROTLI_REVERSE_BITS_LOWEST;
|
key_step = BROTLI_REVERSE_BITS_LOWEST;
|
||||||
symbol = 0;
|
symbol = 0;
|
||||||
|
@ -175,10 +176,10 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
||||||
HuffmanCode* table; /* next available space in table */
|
HuffmanCode* table; /* next available space in table */
|
||||||
int len; /* current code length */
|
int len; /* current code length */
|
||||||
int symbol; /* symbol index in original or sorted table */
|
int symbol; /* symbol index in original or sorted table */
|
||||||
uint32_t key; /* prefix code */
|
brotli_reg_t key; /* prefix code */
|
||||||
uint32_t key_step; /* prefix code addend */
|
brotli_reg_t key_step; /* prefix code addend */
|
||||||
uint32_t sub_key; /* 2nd level table prefix code */
|
brotli_reg_t sub_key; /* 2nd level table prefix code */
|
||||||
uint32_t sub_key_step; /* 2nd level table prefix code addend */
|
brotli_reg_t sub_key_step; /* 2nd level table prefix code addend */
|
||||||
int step; /* step size to replicate values in current table */
|
int step; /* step size to replicate values in current table */
|
||||||
int table_bits; /* key length of current table */
|
int table_bits; /* key length of current table */
|
||||||
int table_size; /* size of current table */
|
int table_size; /* size of current table */
|
||||||
|
@ -199,9 +200,8 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
||||||
table_size = 1 << table_bits;
|
table_size = 1 << table_bits;
|
||||||
total_size = table_size;
|
total_size = table_size;
|
||||||
|
|
||||||
/* fill in root table */
|
/* Fill in the root table. Reduce the table size to if possible,
|
||||||
/* let's reduce the table size to a smaller size if possible, and */
|
and create the repetitions by memcpy. */
|
||||||
/* create the repetitions by memcpy if possible in the coming loop */
|
|
||||||
if (table_bits > max_length) {
|
if (table_bits > max_length) {
|
||||||
table_bits = max_length;
|
table_bits = max_length;
|
||||||
table_size = 1 << table_bits;
|
table_size = 1 << table_bits;
|
||||||
|
@ -223,15 +223,14 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
||||||
key_step >>= 1;
|
key_step >>= 1;
|
||||||
} while (++bits <= table_bits);
|
} while (++bits <= table_bits);
|
||||||
|
|
||||||
/* if root_bits != table_bits we only created one fraction of the */
|
/* If root_bits != table_bits then replicate to fill the remaining slots. */
|
||||||
/* table, and we need to replicate it now. */
|
|
||||||
while (total_size != table_size) {
|
while (total_size != table_size) {
|
||||||
memcpy(&table[table_size], &table[0],
|
memcpy(&table[table_size], &table[0],
|
||||||
(size_t)table_size * sizeof(table[0]));
|
(size_t)table_size * sizeof(table[0]));
|
||||||
table_size <<= 1;
|
table_size <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill in 2nd level tables and add pointers to root table */
|
/* Fill in 2nd level tables and add pointers to root table. */
|
||||||
key_step = BROTLI_REVERSE_BITS_LOWEST >> (root_bits - 1);
|
key_step = BROTLI_REVERSE_BITS_LOWEST >> (root_bits - 1);
|
||||||
sub_key = (BROTLI_REVERSE_BITS_LOWEST << 1);
|
sub_key = (BROTLI_REVERSE_BITS_LOWEST << 1);
|
||||||
sub_key_step = BROTLI_REVERSE_BITS_LOWEST;
|
sub_key_step = BROTLI_REVERSE_BITS_LOWEST;
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef BROTLI_DEC_HUFFMAN_H_
|
#ifndef BROTLI_DEC_HUFFMAN_H_
|
||||||
#define BROTLI_DEC_HUFFMAN_H_
|
#define BROTLI_DEC_HUFFMAN_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -19,10 +19,11 @@ extern "C" {
|
||||||
#define BROTLI_HUFFMAN_MAX_CODE_LENGTH 15
|
#define BROTLI_HUFFMAN_MAX_CODE_LENGTH 15
|
||||||
|
|
||||||
/* Maximum possible Huffman table size for an alphabet size of (index * 32),
|
/* Maximum possible Huffman table size for an alphabet size of (index * 32),
|
||||||
* max code length 15 and root table bits 8. */
|
max code length 15 and root table bits 8. */
|
||||||
static const uint16_t kMaxHuffmanTableSize[] = {
|
static const uint16_t kMaxHuffmanTableSize[] = {
|
||||||
256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822,
|
256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822,
|
||||||
854, 886, 920, 952, 984, 1016, 1048, 1080};
|
854, 886, 920, 952, 984, 1016, 1048, 1080, 1112, 1144, 1176, 1208, 1240, 1272,
|
||||||
|
1304, 1336, 1368, 1400, 1432, 1464, 1496, 1528};
|
||||||
/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */
|
/* BROTLI_NUM_BLOCK_LEN_SYMBOLS == 26 */
|
||||||
#define BROTLI_HUFFMAN_MAX_SIZE_26 396
|
#define BROTLI_HUFFMAN_MAX_SIZE_26 396
|
||||||
/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */
|
/* BROTLI_MAX_BLOCK_TYPE_SYMBOLS == 258 */
|
||||||
|
@ -41,23 +42,26 @@ typedef struct {
|
||||||
BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table,
|
BROTLI_INTERNAL void BrotliBuildCodeLengthsHuffmanTable(HuffmanCode* root_table,
|
||||||
const uint8_t* const code_lengths, uint16_t* count);
|
const uint8_t* const code_lengths, uint16_t* count);
|
||||||
|
|
||||||
/* Builds Huffman lookup table assuming code lengths are in symbol order. */
|
/* Builds Huffman lookup table assuming code lengths are in symbol order.
|
||||||
/* Returns size of resulting table. */
|
Returns size of resulting table. */
|
||||||
BROTLI_INTERNAL uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
BROTLI_INTERNAL uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
||||||
int root_bits, const uint16_t* const symbol_lists, uint16_t* count_arg);
|
int root_bits, const uint16_t* const symbol_lists, uint16_t* count_arg);
|
||||||
|
|
||||||
/* Builds a simple Huffman table. The num_symbols parameter is to be */
|
/* Builds a simple Huffman table. The |num_symbols| parameter is to be
|
||||||
/* interpreted as follows: 0 means 1 symbol, 1 means 2 symbols, 2 means 3 */
|
interpreted as follows: 0 means 1 symbol, 1 means 2 symbols,
|
||||||
/* symbols, 3 means 4 symbols with lengths 2,2,2,2, 4 means 4 symbols with */
|
2 means 3 symbols, 3 means 4 symbols with lengths [2, 2, 2, 2],
|
||||||
/* lengths 1,2,3,3. */
|
4 means 4 symbols with lengths [1, 2, 3, 3]. */
|
||||||
BROTLI_INTERNAL uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
|
BROTLI_INTERNAL uint32_t BrotliBuildSimpleHuffmanTable(HuffmanCode* table,
|
||||||
int root_bits, uint16_t* symbols, uint32_t num_symbols);
|
int root_bits, uint16_t* symbols, uint32_t num_symbols);
|
||||||
|
|
||||||
/* Contains a collection of Huffman trees with the same alphabet size. */
|
/* Contains a collection of Huffman trees with the same alphabet size. */
|
||||||
|
/* max_symbol is needed due to simple codes since log2(alphabet_size) could be
|
||||||
|
greater than log2(max_symbol). */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HuffmanCode** htrees;
|
HuffmanCode** htrees;
|
||||||
HuffmanCode* codes;
|
HuffmanCode* codes;
|
||||||
uint16_t alphabet_size;
|
uint16_t alphabet_size;
|
||||||
|
uint16_t max_symbol;
|
||||||
uint16_t num_htrees;
|
uint16_t num_htrees;
|
||||||
} HuffmanTreeGroup;
|
} HuffmanTreeGroup;
|
||||||
|
|
||||||
|
|
|
@ -1,159 +0,0 @@
|
||||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Macros for compiler / platform specific features and build options.
|
|
||||||
|
|
||||||
Build options are:
|
|
||||||
* BROTLI_BUILD_32_BIT disables 64-bit optimizations
|
|
||||||
* BROTLI_BUILD_64_BIT forces to use 64-bit optimizations
|
|
||||||
* BROTLI_BUILD_BIG_ENDIAN forces to use big-endian optimizations
|
|
||||||
* BROTLI_BUILD_ENDIAN_NEUTRAL disables endian-aware optimizations
|
|
||||||
* BROTLI_BUILD_LITTLE_ENDIAN forces to use little-endian optimizations
|
|
||||||
* BROTLI_BUILD_MODERN_COMPILER forces to use modern compilers built-ins,
|
|
||||||
features and attributes
|
|
||||||
* BROTLI_BUILD_PORTABLE disables dangerous optimizations, like unaligned
|
|
||||||
read and overlapping memcpy; this reduces decompression speed by 5%
|
|
||||||
* BROTLI_DEBUG dumps file name and line number when decoder detects stream
|
|
||||||
or memory error
|
|
||||||
* BROTLI_ENABLE_LOG enables asserts and dumps various state information
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_PORT_H_
|
|
||||||
#define BROTLI_DEC_PORT_H_
|
|
||||||
|
|
||||||
#if defined(BROTLI_ENABLE_LOG) || defined(BROTLI_DEBUG)
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../common/port.h"
|
|
||||||
|
|
||||||
#if defined(__arm__) || defined(__thumb__) || \
|
|
||||||
defined(_M_ARM) || defined(_M_ARMT)
|
|
||||||
#define BROTLI_TARGET_ARM
|
|
||||||
#if (defined(__ARM_ARCH) && (__ARM_ARCH >= 7)) || \
|
|
||||||
(defined(M_ARM) && (M_ARM >= 7))
|
|
||||||
#define BROTLI_TARGET_ARMV7
|
|
||||||
#endif /* ARMv7 */
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
#define BROTLI_TARGET_ARMV8
|
|
||||||
#endif /* ARMv8 */
|
|
||||||
#endif /* ARM */
|
|
||||||
|
|
||||||
#if defined(__i386) || defined(_M_IX86)
|
|
||||||
#define BROTLI_TARGET_X86
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_X64)
|
|
||||||
#define BROTLI_TARGET_X64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__PPC64__)
|
|
||||||
#define BROTLI_TARGET_POWERPC64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BROTLI_BUILD_PORTABLE
|
|
||||||
#define BROTLI_ALIGNED_READ (!!1)
|
|
||||||
#elif defined(BROTLI_TARGET_X86) || defined(BROTLI_TARGET_X64) || \
|
|
||||||
defined(BROTLI_TARGET_ARMV7) || defined(BROTLI_TARGET_ARMV8)
|
|
||||||
/* Allow unaligned read only for whitelisted CPUs. */
|
|
||||||
#define BROTLI_ALIGNED_READ (!!0)
|
|
||||||
#else
|
|
||||||
#define BROTLI_ALIGNED_READ (!!1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* IS_CONSTANT macros returns true for compile-time constant expressions. */
|
|
||||||
#if BROTLI_MODERN_COMPILER || __has_builtin(__builtin_constant_p)
|
|
||||||
#define IS_CONSTANT(x) (!!__builtin_constant_p(x))
|
|
||||||
#else
|
|
||||||
#define IS_CONSTANT(x) (!!0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BROTLI_ENABLE_LOG
|
|
||||||
#define BROTLI_DCHECK(x) assert(x)
|
|
||||||
#define BROTLI_LOG(x) printf x
|
|
||||||
#else
|
|
||||||
#define BROTLI_DCHECK(x)
|
|
||||||
#define BROTLI_LOG(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(BROTLI_DEBUG) || defined(BROTLI_ENABLE_LOG)
|
|
||||||
static BROTLI_INLINE void BrotliDump(const char* f, int l, const char* fn) {
|
|
||||||
fprintf(stderr, "%s:%d (%s)\n", f, l, fn);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
#define BROTLI_DUMP() BrotliDump(__FILE__, __LINE__, __FUNCTION__)
|
|
||||||
#else
|
|
||||||
#define BROTLI_DUMP() (void)(0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(BROTLI_BUILD_64_BIT)
|
|
||||||
#define BROTLI_64_BITS 1
|
|
||||||
#elif defined(BROTLI_BUILD_32_BIT)
|
|
||||||
#define BROTLI_64_BITS 0
|
|
||||||
#elif defined(BROTLI_TARGET_X64) || defined(BROTLI_TARGET_ARMV8) || \
|
|
||||||
defined(BROTLI_TARGET_POWERPC64)
|
|
||||||
#define BROTLI_64_BITS 1
|
|
||||||
#else
|
|
||||||
#define BROTLI_64_BITS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(BROTLI_BUILD_BIG_ENDIAN)
|
|
||||||
#define BROTLI_LITTLE_ENDIAN 0
|
|
||||||
#define BROTLI_BIG_ENDIAN 1
|
|
||||||
#elif defined(BROTLI_BUILD_LITTLE_ENDIAN)
|
|
||||||
#define BROTLI_LITTLE_ENDIAN 1
|
|
||||||
#define BROTLI_BIG_ENDIAN 0
|
|
||||||
#elif defined(BROTLI_BUILD_ENDIAN_NEUTRAL)
|
|
||||||
#define BROTLI_LITTLE_ENDIAN 0
|
|
||||||
#define BROTLI_BIG_ENDIAN 0
|
|
||||||
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
||||||
#define BROTLI_LITTLE_ENDIAN 1
|
|
||||||
#define BROTLI_BIG_ENDIAN 0
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
/* Win32 can currently always be assumed to be little endian */
|
|
||||||
#define BROTLI_LITTLE_ENDIAN 1
|
|
||||||
#define BROTLI_BIG_ENDIAN 0
|
|
||||||
#else
|
|
||||||
#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
|
||||||
#define BROTLI_BIG_ENDIAN 1
|
|
||||||
#else
|
|
||||||
#define BROTLI_BIG_ENDIAN 0
|
|
||||||
#endif
|
|
||||||
#define BROTLI_LITTLE_ENDIAN 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BROTLI_REPEAT(N, X) { \
|
|
||||||
if ((N & 1) != 0) {X;} \
|
|
||||||
if ((N & 2) != 0) {X; X;} \
|
|
||||||
if ((N & 4) != 0) {X; X; X; X;} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#if BROTLI_MODERN_COMPILER || defined(__llvm__)
|
|
||||||
#if defined(BROTLI_TARGET_ARMV7)
|
|
||||||
static BROTLI_INLINE unsigned BrotliRBit(unsigned input) {
|
|
||||||
unsigned output;
|
|
||||||
__asm__("rbit %0, %1\n" : "=r"(output) : "r"(input));
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
#define BROTLI_RBIT(x) BrotliRBit(x)
|
|
||||||
#endif /* armv7 */
|
|
||||||
#endif /* gcc || clang */
|
|
||||||
|
|
||||||
#if defined(BROTLI_TARGET_ARM)
|
|
||||||
#define BROTLI_HAS_UBFX (!!1)
|
|
||||||
#else
|
|
||||||
#define BROTLI_HAS_UBFX (!!0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define BROTLI_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L)
|
|
||||||
|
|
||||||
#define BROTLI_FREE(S, X) { \
|
|
||||||
S->free_func(S->memory_manager_opaque, X); \
|
|
||||||
X = NULL; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_PORT_H_ */
|
|
|
@ -5,17 +5,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Lookup tables to map prefix codes to value ranges. This is used during
|
/* Lookup tables to map prefix codes to value ranges. This is used during
|
||||||
decoding of the block lengths, literal insertion lengths and copy lengths.
|
decoding of the block lengths, literal insertion lengths and copy lengths. */
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_PREFIX_H_
|
#ifndef BROTLI_DEC_PREFIX_H_
|
||||||
#define BROTLI_DEC_PREFIX_H_
|
#define BROTLI_DEC_PREFIX_H_
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
/* Represents the range of values belonging to a prefix code: */
|
/* Represents the range of values belonging to a prefix code:
|
||||||
/* [offset, offset + 2^nbits) */
|
[offset, offset + 2^nbits) */
|
||||||
struct PrefixCodeRange {
|
struct PrefixCodeRange {
|
||||||
uint16_t offset;
|
uint16_t offset;
|
||||||
uint8_t nbits;
|
uint8_t nbits;
|
||||||
|
|
|
@ -8,32 +8,18 @@
|
||||||
|
|
||||||
#include <stdlib.h> /* free, malloc */
|
#include <stdlib.h> /* free, malloc */
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include <brotli/types.h>
|
||||||
#include "./huffman.h"
|
#include "./huffman.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void* DefaultAllocFunc(void* opaque, size_t size) {
|
BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||||
BROTLI_UNUSED(opaque);
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DefaultFreeFunc(void* opaque, void* address) {
|
|
||||||
BROTLI_UNUSED(opaque);
|
|
||||||
free(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrotliDecoderStateInit(BrotliDecoderState* s) {
|
|
||||||
BrotliDecoderStateInitWithCustomAllocators(s, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
|
||||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
|
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
|
||||||
if (!alloc_func) {
|
if (!alloc_func) {
|
||||||
s->alloc_func = DefaultAllocFunc;
|
s->alloc_func = BrotliDefaultAllocFunc;
|
||||||
s->free_func = DefaultFreeFunc;
|
s->free_func = BrotliDefaultFreeFunc;
|
||||||
s->memory_manager_opaque = 0;
|
s->memory_manager_opaque = 0;
|
||||||
} else {
|
} else {
|
||||||
s->alloc_func = alloc_func;
|
s->alloc_func = alloc_func;
|
||||||
|
@ -41,8 +27,11 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||||
s->memory_manager_opaque = opaque;
|
s->memory_manager_opaque = opaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->error_code = 0; /* BROTLI_DECODER_NO_ERROR */
|
||||||
|
|
||||||
BrotliInitBitReader(&s->br);
|
BrotliInitBitReader(&s->br);
|
||||||
s->state = BROTLI_STATE_UNINITED;
|
s->state = BROTLI_STATE_UNINITED;
|
||||||
|
s->large_window = 0;
|
||||||
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
|
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
|
||||||
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
|
s->substate_tree_group = BROTLI_STATE_TREE_GROUP_NONE;
|
||||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
||||||
|
@ -60,6 +49,9 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||||
s->block_type_trees = NULL;
|
s->block_type_trees = NULL;
|
||||||
s->block_len_trees = NULL;
|
s->block_len_trees = NULL;
|
||||||
s->ringbuffer = NULL;
|
s->ringbuffer = NULL;
|
||||||
|
s->ringbuffer_size = 0;
|
||||||
|
s->new_ringbuffer_size = 0;
|
||||||
|
s->ringbuffer_mask = 0;
|
||||||
|
|
||||||
s->context_map = NULL;
|
s->context_map = NULL;
|
||||||
s->context_modes = NULL;
|
s->context_modes = NULL;
|
||||||
|
@ -76,10 +68,12 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||||
s->distance_hgroup.codes = NULL;
|
s->distance_hgroup.codes = NULL;
|
||||||
s->distance_hgroup.htrees = NULL;
|
s->distance_hgroup.htrees = NULL;
|
||||||
|
|
||||||
s->custom_dict = NULL;
|
|
||||||
s->custom_dict_size = 0;
|
|
||||||
|
|
||||||
s->is_last_metablock = 0;
|
s->is_last_metablock = 0;
|
||||||
|
s->is_uncompressed = 0;
|
||||||
|
s->is_metadata = 0;
|
||||||
|
s->should_wrap_ringbuffer = 0;
|
||||||
|
s->canny_ringbuffer_allocation = 1;
|
||||||
|
|
||||||
s->window_bits = 0;
|
s->window_bits = 0;
|
||||||
s->max_distance = 0;
|
s->max_distance = 0;
|
||||||
s->dist_rb[0] = 16;
|
s->dist_rb[0] = 16;
|
||||||
|
@ -93,14 +87,19 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||||
/* Make small negative indexes addressable. */
|
/* Make small negative indexes addressable. */
|
||||||
s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
||||||
|
|
||||||
s->mtf_upper_bound = 255;
|
s->mtf_upper_bound = 63;
|
||||||
|
|
||||||
|
s->dictionary = BrotliGetDictionary();
|
||||||
|
s->transforms = BrotliGetTransforms();
|
||||||
|
|
||||||
|
return BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
||||||
s->meta_block_remaining_len = 0;
|
s->meta_block_remaining_len = 0;
|
||||||
s->block_length[0] = 1U << 28;
|
s->block_length[0] = 1U << 24;
|
||||||
s->block_length[1] = 1U << 28;
|
s->block_length[1] = 1U << 24;
|
||||||
s->block_length[2] = 1U << 28;
|
s->block_length[2] = 1U << 24;
|
||||||
s->num_block_types[0] = 1;
|
s->num_block_types[0] = 1;
|
||||||
s->num_block_types[1] = 1;
|
s->num_block_types[1] = 1;
|
||||||
s->num_block_types[2] = 1;
|
s->num_block_types[2] = 1;
|
||||||
|
@ -117,8 +116,7 @@ void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
||||||
s->literal_htree = NULL;
|
s->literal_htree = NULL;
|
||||||
s->dist_context_map_slice = NULL;
|
s->dist_context_map_slice = NULL;
|
||||||
s->dist_htree_index = 0;
|
s->dist_htree_index = 0;
|
||||||
s->context_lookup1 = NULL;
|
s->context_lookup = NULL;
|
||||||
s->context_lookup2 = NULL;
|
|
||||||
s->literal_hgroup.codes = NULL;
|
s->literal_hgroup.codes = NULL;
|
||||||
s->literal_hgroup.htrees = NULL;
|
s->literal_hgroup.htrees = NULL;
|
||||||
s->insert_copy_hgroup.codes = NULL;
|
s->insert_copy_hgroup.codes = NULL;
|
||||||
|
@ -128,39 +126,37 @@ void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliDecoderStateCleanupAfterMetablock(BrotliDecoderState* s) {
|
void BrotliDecoderStateCleanupAfterMetablock(BrotliDecoderState* s) {
|
||||||
BROTLI_FREE(s, s->context_modes);
|
BROTLI_DECODER_FREE(s, s->context_modes);
|
||||||
BROTLI_FREE(s, s->context_map);
|
BROTLI_DECODER_FREE(s, s->context_map);
|
||||||
BROTLI_FREE(s, s->dist_context_map);
|
BROTLI_DECODER_FREE(s, s->dist_context_map);
|
||||||
|
BROTLI_DECODER_FREE(s, s->literal_hgroup.htrees);
|
||||||
BrotliDecoderHuffmanTreeGroupRelease(s, &s->literal_hgroup);
|
BROTLI_DECODER_FREE(s, s->insert_copy_hgroup.htrees);
|
||||||
BrotliDecoderHuffmanTreeGroupRelease(s, &s->insert_copy_hgroup);
|
BROTLI_DECODER_FREE(s, s->distance_hgroup.htrees);
|
||||||
BrotliDecoderHuffmanTreeGroupRelease(s, &s->distance_hgroup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliDecoderStateCleanup(BrotliDecoderState* s) {
|
void BrotliDecoderStateCleanup(BrotliDecoderState* s) {
|
||||||
BrotliDecoderStateCleanupAfterMetablock(s);
|
BrotliDecoderStateCleanupAfterMetablock(s);
|
||||||
|
|
||||||
BROTLI_FREE(s, s->ringbuffer);
|
BROTLI_DECODER_FREE(s, s->ringbuffer);
|
||||||
BROTLI_FREE(s, s->block_type_trees);
|
BROTLI_DECODER_FREE(s, s->block_type_trees);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliDecoderHuffmanTreeGroupInit(BrotliDecoderState* s,
|
BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(BrotliDecoderState* s,
|
||||||
HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t ntrees) {
|
HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t max_symbol,
|
||||||
|
uint32_t ntrees) {
|
||||||
/* Pack two allocations into one */
|
/* Pack two allocations into one */
|
||||||
const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5];
|
const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5];
|
||||||
const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size;
|
const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size;
|
||||||
const size_t htree_size = sizeof(HuffmanCode*) * ntrees;
|
const size_t htree_size = sizeof(HuffmanCode*) * ntrees;
|
||||||
char* p = (char*)BROTLI_ALLOC(s, code_size + htree_size);
|
/* Pointer alignment is, hopefully, wider than sizeof(HuffmanCode). */
|
||||||
|
HuffmanCode** p = (HuffmanCode**)BROTLI_DECODER_ALLOC(s,
|
||||||
|
code_size + htree_size);
|
||||||
group->alphabet_size = (uint16_t)alphabet_size;
|
group->alphabet_size = (uint16_t)alphabet_size;
|
||||||
|
group->max_symbol = (uint16_t)max_symbol;
|
||||||
group->num_htrees = (uint16_t)ntrees;
|
group->num_htrees = (uint16_t)ntrees;
|
||||||
group->codes = (HuffmanCode*)p;
|
group->htrees = p;
|
||||||
group->htrees = (HuffmanCode**)(p + code_size);
|
group->codes = (HuffmanCode*)(&p[ntrees]);
|
||||||
}
|
return !!p;
|
||||||
|
|
||||||
void BrotliDecoderHuffmanTreeGroupRelease(
|
|
||||||
BrotliDecoderState* s, HuffmanTreeGroup* group) {
|
|
||||||
BROTLI_FREE(s, group->codes);
|
|
||||||
group->htrees = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
#define BROTLI_DEC_STATE_H_
|
#define BROTLI_DEC_STATE_H_
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include "../common/transform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./bit_reader.h"
|
#include "./bit_reader.h"
|
||||||
#include "./huffman.h"
|
#include "./huffman.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -21,6 +23,8 @@ extern "C" {
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BROTLI_STATE_UNINITED,
|
BROTLI_STATE_UNINITED,
|
||||||
|
BROTLI_STATE_LARGE_WINDOW_BITS,
|
||||||
|
BROTLI_STATE_INITIALIZE,
|
||||||
BROTLI_STATE_METABLOCK_BEGIN,
|
BROTLI_STATE_METABLOCK_BEGIN,
|
||||||
BROTLI_STATE_METABLOCK_HEADER,
|
BROTLI_STATE_METABLOCK_HEADER,
|
||||||
BROTLI_STATE_METABLOCK_HEADER_2,
|
BROTLI_STATE_METABLOCK_HEADER_2,
|
||||||
|
@ -115,7 +119,6 @@ struct BrotliDecoderStateStruct {
|
||||||
|
|
||||||
int pos;
|
int pos;
|
||||||
int max_backward_distance;
|
int max_backward_distance;
|
||||||
int max_backward_distance_minus_custom_dict_size;
|
|
||||||
int max_distance;
|
int max_distance;
|
||||||
int ringbuffer_size;
|
int ringbuffer_size;
|
||||||
int ringbuffer_mask;
|
int ringbuffer_mask;
|
||||||
|
@ -126,21 +129,22 @@ struct BrotliDecoderStateStruct {
|
||||||
uint8_t* ringbuffer;
|
uint8_t* ringbuffer;
|
||||||
uint8_t* ringbuffer_end;
|
uint8_t* ringbuffer_end;
|
||||||
HuffmanCode* htree_command;
|
HuffmanCode* htree_command;
|
||||||
const uint8_t* context_lookup1;
|
const uint8_t* context_lookup;
|
||||||
const uint8_t* context_lookup2;
|
|
||||||
uint8_t* context_map_slice;
|
uint8_t* context_map_slice;
|
||||||
uint8_t* dist_context_map_slice;
|
uint8_t* dist_context_map_slice;
|
||||||
|
|
||||||
/* This ring buffer holds a few past copy distances that will be used by */
|
/* This ring buffer holds a few past copy distances that will be used by
|
||||||
/* some special distance codes. */
|
some special distance codes. */
|
||||||
HuffmanTreeGroup literal_hgroup;
|
HuffmanTreeGroup literal_hgroup;
|
||||||
HuffmanTreeGroup insert_copy_hgroup;
|
HuffmanTreeGroup insert_copy_hgroup;
|
||||||
HuffmanTreeGroup distance_hgroup;
|
HuffmanTreeGroup distance_hgroup;
|
||||||
HuffmanCode* block_type_trees;
|
HuffmanCode* block_type_trees;
|
||||||
HuffmanCode* block_len_trees;
|
HuffmanCode* block_len_trees;
|
||||||
/* This is true if the literal context map histogram type always matches the
|
/* This is true if the literal context map histogram type always matches the
|
||||||
block type. It is then not needed to keep the context (faster decoding). */
|
block type. It is then not needed to keep the context (faster decoding). */
|
||||||
int trivial_literal_context;
|
int trivial_literal_context;
|
||||||
|
/* Distance context is actual after command is decoded and before distance is
|
||||||
|
computed. After distance computation it is used as a temporary variable. */
|
||||||
int distance_context;
|
int distance_context;
|
||||||
int meta_block_remaining_len;
|
int meta_block_remaining_len;
|
||||||
uint32_t block_length_index;
|
uint32_t block_length_index;
|
||||||
|
@ -160,17 +164,17 @@ struct BrotliDecoderStateStruct {
|
||||||
int copy_length;
|
int copy_length;
|
||||||
int distance_code;
|
int distance_code;
|
||||||
|
|
||||||
/* For partial write operations */
|
/* For partial write operations. */
|
||||||
size_t rb_roundtrips; /* How many times we went around the ringbuffer */
|
size_t rb_roundtrips; /* how many times we went around the ring-buffer */
|
||||||
size_t partial_pos_out; /* How much output to the user in total (<= rb) */
|
size_t partial_pos_out; /* how much output to the user in total */
|
||||||
|
|
||||||
/* For ReadHuffmanCode */
|
/* For ReadHuffmanCode. */
|
||||||
uint32_t symbol;
|
uint32_t symbol;
|
||||||
uint32_t repeat;
|
uint32_t repeat;
|
||||||
uint32_t space;
|
uint32_t space;
|
||||||
|
|
||||||
HuffmanCode table[32];
|
HuffmanCode table[32];
|
||||||
/* List of of symbol chains. */
|
/* List of heads of symbol chains. */
|
||||||
uint16_t* symbol_lists;
|
uint16_t* symbol_lists;
|
||||||
/* Storage from symbol_lists. */
|
/* Storage from symbol_lists. */
|
||||||
uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
|
uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
|
||||||
|
@ -178,29 +182,26 @@ struct BrotliDecoderStateStruct {
|
||||||
/* Tails of symbol chains. */
|
/* Tails of symbol chains. */
|
||||||
int next_symbol[32];
|
int next_symbol[32];
|
||||||
uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES];
|
uint8_t code_length_code_lengths[BROTLI_CODE_LENGTH_CODES];
|
||||||
/* Population counts for the code lengths */
|
/* Population counts for the code lengths. */
|
||||||
uint16_t code_length_histo[16];
|
uint16_t code_length_histo[16];
|
||||||
|
|
||||||
/* For HuffmanTreeGroupDecode */
|
/* For HuffmanTreeGroupDecode. */
|
||||||
int htree_index;
|
int htree_index;
|
||||||
HuffmanCode* next;
|
HuffmanCode* next;
|
||||||
|
|
||||||
/* For DecodeContextMap */
|
/* For DecodeContextMap. */
|
||||||
uint32_t context_index;
|
uint32_t context_index;
|
||||||
uint32_t max_run_length_prefix;
|
uint32_t max_run_length_prefix;
|
||||||
uint32_t code;
|
uint32_t code;
|
||||||
HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272];
|
HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272];
|
||||||
|
|
||||||
/* For InverseMoveToFrontTransform */
|
/* For InverseMoveToFrontTransform. */
|
||||||
uint32_t mtf_upper_bound;
|
uint32_t mtf_upper_bound;
|
||||||
uint8_t mtf[256 + 4];
|
uint32_t mtf[64 + 1];
|
||||||
|
|
||||||
/* For custom dictionaries */
|
/* Less used attributes are at the end of this struct. */
|
||||||
const uint8_t* custom_dict;
|
|
||||||
int custom_dict_size;
|
|
||||||
|
|
||||||
/* less used attributes are in the end of this struct */
|
/* States inside function calls. */
|
||||||
/* States inside function calls */
|
|
||||||
BrotliRunningMetablockHeaderState substate_metablock_header;
|
BrotliRunningMetablockHeaderState substate_metablock_header;
|
||||||
BrotliRunningTreeGroupState substate_tree_group;
|
BrotliRunningTreeGroupState substate_tree_group;
|
||||||
BrotliRunningContextMapState substate_context_map;
|
BrotliRunningContextMapState substate_context_map;
|
||||||
|
@ -209,35 +210,46 @@ struct BrotliDecoderStateStruct {
|
||||||
BrotliRunningDecodeUint8State substate_decode_uint8;
|
BrotliRunningDecodeUint8State substate_decode_uint8;
|
||||||
BrotliRunningReadBlockLengthState substate_read_block_length;
|
BrotliRunningReadBlockLengthState substate_read_block_length;
|
||||||
|
|
||||||
uint8_t is_last_metablock;
|
unsigned int is_last_metablock : 1;
|
||||||
uint8_t is_uncompressed;
|
unsigned int is_uncompressed : 1;
|
||||||
uint8_t is_metadata;
|
unsigned int is_metadata : 1;
|
||||||
uint8_t size_nibbles;
|
unsigned int should_wrap_ringbuffer : 1;
|
||||||
|
unsigned int canny_ringbuffer_allocation : 1;
|
||||||
|
unsigned int large_window : 1;
|
||||||
|
unsigned int size_nibbles : 8;
|
||||||
uint32_t window_bits;
|
uint32_t window_bits;
|
||||||
|
|
||||||
|
int new_ringbuffer_size;
|
||||||
|
|
||||||
uint32_t num_literal_htrees;
|
uint32_t num_literal_htrees;
|
||||||
uint8_t* context_map;
|
uint8_t* context_map;
|
||||||
uint8_t* context_modes;
|
uint8_t* context_modes;
|
||||||
|
|
||||||
|
const BrotliDictionary* dictionary;
|
||||||
|
const BrotliTransforms* transforms;
|
||||||
|
|
||||||
uint32_t trivial_literal_contexts[8]; /* 256 bits */
|
uint32_t trivial_literal_contexts[8]; /* 256 bits */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct BrotliDecoderStateStruct BrotliDecoderStateInternal;
|
typedef struct BrotliDecoderStateStruct BrotliDecoderStateInternal;
|
||||||
#define BrotliDecoderState BrotliDecoderStateInternal
|
#define BrotliDecoderState BrotliDecoderStateInternal
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliDecoderStateInit(BrotliDecoderState* s);
|
BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderStateInit(BrotliDecoderState* s,
|
||||||
BROTLI_INTERNAL void BrotliDecoderStateInitWithCustomAllocators(
|
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||||
BrotliDecoderState* s, brotli_alloc_func alloc_func,
|
|
||||||
brotli_free_func free_func, void* opaque);
|
|
||||||
BROTLI_INTERNAL void BrotliDecoderStateCleanup(BrotliDecoderState* s);
|
BROTLI_INTERNAL void BrotliDecoderStateCleanup(BrotliDecoderState* s);
|
||||||
BROTLI_INTERNAL void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s);
|
BROTLI_INTERNAL void BrotliDecoderStateMetablockBegin(BrotliDecoderState* s);
|
||||||
BROTLI_INTERNAL void BrotliDecoderStateCleanupAfterMetablock(
|
BROTLI_INTERNAL void BrotliDecoderStateCleanupAfterMetablock(
|
||||||
BrotliDecoderState* s);
|
BrotliDecoderState* s);
|
||||||
BROTLI_INTERNAL void BrotliDecoderHuffmanTreeGroupInit(
|
BROTLI_INTERNAL BROTLI_BOOL BrotliDecoderHuffmanTreeGroupInit(
|
||||||
BrotliDecoderState* s, HuffmanTreeGroup* group, uint32_t alphabet_size,
|
BrotliDecoderState* s, HuffmanTreeGroup* group, uint32_t alphabet_size,
|
||||||
uint32_t ntrees);
|
uint32_t max_symbol, uint32_t ntrees);
|
||||||
BROTLI_INTERNAL void BrotliDecoderHuffmanTreeGroupRelease(
|
|
||||||
BrotliDecoderState* s, HuffmanTreeGroup* group);
|
#define BROTLI_DECODER_ALLOC(S, L) S->alloc_func(S->memory_manager_opaque, L)
|
||||||
|
|
||||||
|
#define BROTLI_DECODER_FREE(S, X) { \
|
||||||
|
S->free_func(S->memory_manager_opaque, X); \
|
||||||
|
X = NULL; \
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -1,300 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Transformations on dictionary words. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_DEC_TRANSFORM_H_
|
|
||||||
#define BROTLI_DEC_TRANSFORM_H_
|
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum WordTransformType {
|
|
||||||
kIdentity = 0,
|
|
||||||
kOmitLast1 = 1,
|
|
||||||
kOmitLast2 = 2,
|
|
||||||
kOmitLast3 = 3,
|
|
||||||
kOmitLast4 = 4,
|
|
||||||
kOmitLast5 = 5,
|
|
||||||
kOmitLast6 = 6,
|
|
||||||
kOmitLast7 = 7,
|
|
||||||
kOmitLast8 = 8,
|
|
||||||
kOmitLast9 = 9,
|
|
||||||
kUppercaseFirst = 10,
|
|
||||||
kUppercaseAll = 11,
|
|
||||||
kOmitFirst1 = 12,
|
|
||||||
kOmitFirst2 = 13,
|
|
||||||
kOmitFirst3 = 14,
|
|
||||||
kOmitFirst4 = 15,
|
|
||||||
kOmitFirst5 = 16,
|
|
||||||
kOmitFirst6 = 17,
|
|
||||||
kOmitFirst7 = 18,
|
|
||||||
kOmitFirst8 = 19,
|
|
||||||
kOmitFirst9 = 20
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const uint8_t prefix_id;
|
|
||||||
const uint8_t transform;
|
|
||||||
const uint8_t suffix_id;
|
|
||||||
} Transform;
|
|
||||||
|
|
||||||
static const char kPrefixSuffix[208] =
|
|
||||||
"\0 \0, \0 of the \0 of \0s \0.\0 and \0 in \0\"\0 to \0\">\0\n\0. \0]\0"
|
|
||||||
" for \0 a \0 that \0\'\0 with \0 from \0 by \0(\0. The \0 on \0 as \0"
|
|
||||||
" is \0ing \0\n\t\0:\0ed \0=\"\0 at \0ly \0,\0=\'\0.com/\0. This \0"
|
|
||||||
" not \0er \0al \0ful \0ive \0less \0est \0ize \0\xc2\xa0\0ous ";
|
|
||||||
|
|
||||||
enum {
|
|
||||||
/* EMPTY = ""
|
|
||||||
SP = " "
|
|
||||||
DQUOT = "\""
|
|
||||||
SQUOT = "'"
|
|
||||||
CLOSEBR = "]"
|
|
||||||
OPEN = "("
|
|
||||||
SLASH = "/"
|
|
||||||
NBSP = non-breaking space "\0xc2\xa0"
|
|
||||||
*/
|
|
||||||
kPFix_EMPTY = 0,
|
|
||||||
kPFix_SP = 1,
|
|
||||||
kPFix_COMMASP = 3,
|
|
||||||
kPFix_SPofSPtheSP = 6,
|
|
||||||
kPFix_SPtheSP = 9,
|
|
||||||
kPFix_eSP = 12,
|
|
||||||
kPFix_SPofSP = 15,
|
|
||||||
kPFix_sSP = 20,
|
|
||||||
kPFix_DOT = 23,
|
|
||||||
kPFix_SPandSP = 25,
|
|
||||||
kPFix_SPinSP = 31,
|
|
||||||
kPFix_DQUOT = 36,
|
|
||||||
kPFix_SPtoSP = 38,
|
|
||||||
kPFix_DQUOTGT = 43,
|
|
||||||
kPFix_NEWLINE = 46,
|
|
||||||
kPFix_DOTSP = 48,
|
|
||||||
kPFix_CLOSEBR = 51,
|
|
||||||
kPFix_SPforSP = 53,
|
|
||||||
kPFix_SPaSP = 59,
|
|
||||||
kPFix_SPthatSP = 63,
|
|
||||||
kPFix_SQUOT = 70,
|
|
||||||
kPFix_SPwithSP = 72,
|
|
||||||
kPFix_SPfromSP = 79,
|
|
||||||
kPFix_SPbySP = 86,
|
|
||||||
kPFix_OPEN = 91,
|
|
||||||
kPFix_DOTSPTheSP = 93,
|
|
||||||
kPFix_SPonSP = 100,
|
|
||||||
kPFix_SPasSP = 105,
|
|
||||||
kPFix_SPisSP = 110,
|
|
||||||
kPFix_ingSP = 115,
|
|
||||||
kPFix_NEWLINETAB = 120,
|
|
||||||
kPFix_COLON = 123,
|
|
||||||
kPFix_edSP = 125,
|
|
||||||
kPFix_EQDQUOT = 129,
|
|
||||||
kPFix_SPatSP = 132,
|
|
||||||
kPFix_lySP = 137,
|
|
||||||
kPFix_COMMA = 141,
|
|
||||||
kPFix_EQSQUOT = 143,
|
|
||||||
kPFix_DOTcomSLASH = 146,
|
|
||||||
kPFix_DOTSPThisSP = 152,
|
|
||||||
kPFix_SPnotSP = 160,
|
|
||||||
kPFix_erSP = 166,
|
|
||||||
kPFix_alSP = 170,
|
|
||||||
kPFix_fulSP = 174,
|
|
||||||
kPFix_iveSP = 179,
|
|
||||||
kPFix_lessSP = 184,
|
|
||||||
kPFix_estSP = 190,
|
|
||||||
kPFix_izeSP = 195,
|
|
||||||
kPFix_NBSP = 200,
|
|
||||||
kPFix_ousSP = 203
|
|
||||||
};
|
|
||||||
|
|
||||||
static const Transform kTransforms[] = {
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SP },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst1, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPtheSP },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_EMPTY },
|
|
||||||
{ kPFix_sSP, kIdentity, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPofSP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPandSP },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst2, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitLast1, kPFix_EMPTY },
|
|
||||||
{ kPFix_COMMASP, kIdentity, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_COMMASP },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPinSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPtoSP },
|
|
||||||
{ kPFix_eSP, kIdentity, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_DQUOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_DOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_DQUOTGT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_NEWLINE },
|
|
||||||
{ kPFix_EMPTY, kOmitLast3, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_CLOSEBR },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPforSP },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst3, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitLast2, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPaSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPthatSP },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_DOTSP },
|
|
||||||
{ kPFix_DOT, kIdentity, kPFix_EMPTY },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_COMMASP },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst4, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPwithSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SQUOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPfromSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPbySP },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst5, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst6, kPFix_EMPTY },
|
|
||||||
{ kPFix_SPtheSP, kIdentity, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitLast4, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_DOTSPTheSP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPonSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPasSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPisSP },
|
|
||||||
{ kPFix_EMPTY, kOmitLast7, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitLast1, kPFix_ingSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_NEWLINETAB },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_COLON },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_DOTSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_edSP },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst9, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitFirst7, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitLast6, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_OPEN },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_COMMASP },
|
|
||||||
{ kPFix_EMPTY, kOmitLast8, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPatSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_lySP },
|
|
||||||
{ kPFix_SPtheSP, kIdentity, kPFix_SPofSP },
|
|
||||||
{ kPFix_EMPTY, kOmitLast5, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kOmitLast9, kPFix_EMPTY },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_COMMASP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DQUOT },
|
|
||||||
{ kPFix_DOT, kIdentity, kPFix_OPEN },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DQUOTGT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_EQDQUOT },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_DOT },
|
|
||||||
{ kPFix_DOTcomSLASH, kIdentity, kPFix_EMPTY },
|
|
||||||
{ kPFix_SPtheSP, kIdentity, kPFix_SPofSPtheSP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_SQUOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_DOTSPThisSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_COMMA },
|
|
||||||
{ kPFix_DOT, kIdentity, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_OPEN },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_SPnotSP },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_EQDQUOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_erSP },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_SP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_alSP },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_EMPTY },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_EQSQUOT },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DQUOT },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_DOTSP },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_OPEN },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_fulSP },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_DOTSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_iveSP },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_lessSP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_SQUOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_estSP },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_DOT },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DQUOTGT },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_EQSQUOT },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_COMMA },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_izeSP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DOT },
|
|
||||||
{ kPFix_NBSP, kIdentity, kPFix_EMPTY },
|
|
||||||
{ kPFix_SP, kIdentity, kPFix_COMMA },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_EQDQUOT },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_EQDQUOT },
|
|
||||||
{ kPFix_EMPTY, kIdentity, kPFix_ousSP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_COMMASP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseFirst, kPFix_EQSQUOT },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_COMMA },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_EQDQUOT },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_COMMASP },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_COMMA },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_OPEN },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_DOTSP },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_DOT },
|
|
||||||
{ kPFix_EMPTY, kUppercaseAll, kPFix_EQSQUOT },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_DOTSP },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_EQDQUOT },
|
|
||||||
{ kPFix_SP, kUppercaseAll, kPFix_EQSQUOT },
|
|
||||||
{ kPFix_SP, kUppercaseFirst, kPFix_EQSQUOT },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int kNumTransforms = sizeof(kTransforms) / sizeof(kTransforms[0]);
|
|
||||||
|
|
||||||
static int ToUpperCase(uint8_t* p) {
|
|
||||||
if (p[0] < 0xc0) {
|
|
||||||
if (p[0] >= 'a' && p[0] <= 'z') {
|
|
||||||
p[0] ^= 32;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
/* An overly simplified uppercasing model for utf-8. */
|
|
||||||
if (p[0] < 0xe0) {
|
|
||||||
p[1] ^= 32;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
/* An arbitrary transform for three byte characters. */
|
|
||||||
p[2] ^= 5;
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_NOINLINE int TransformDictionaryWord(
|
|
||||||
uint8_t* dst, const uint8_t* word, int len, int transform) {
|
|
||||||
int idx = 0;
|
|
||||||
{
|
|
||||||
const char* prefix = &kPrefixSuffix[kTransforms[transform].prefix_id];
|
|
||||||
while (*prefix) { dst[idx++] = (uint8_t)*prefix++; }
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const int t = kTransforms[transform].transform;
|
|
||||||
int i = 0;
|
|
||||||
int skip = t - (kOmitFirst1 - 1);
|
|
||||||
if (skip > 0) {
|
|
||||||
word += skip;
|
|
||||||
len -= skip;
|
|
||||||
} else if (t <= kOmitLast9) {
|
|
||||||
len -= t;
|
|
||||||
}
|
|
||||||
while (i < len) { dst[idx++] = word[i++]; }
|
|
||||||
if (t == kUppercaseFirst) {
|
|
||||||
ToUpperCase(&dst[idx - len]);
|
|
||||||
} else if (t == kUppercaseAll) {
|
|
||||||
uint8_t* uppercase = &dst[idx - len];
|
|
||||||
while (len > 0) {
|
|
||||||
int step = ToUpperCase(uppercase);
|
|
||||||
uppercase += step;
|
|
||||||
len -= step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const char* suffix = &kPrefixSuffix[kTransforms[transform].suffix_id];
|
|
||||||
while (*suffix) { dst[idx++] = (uint8_t)*suffix++; }
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_DEC_TRANSFORM_H_ */
|
|
|
@ -8,208 +8,19 @@
|
||||||
|
|
||||||
#include "./backward_references.h"
|
#include "./backward_references.h"
|
||||||
|
|
||||||
#include <math.h> /* INFINITY */
|
|
||||||
#include <string.h> /* memcpy, memset */
|
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./fast_log.h"
|
#include "./dictionary_hash.h"
|
||||||
#include "./find_match_length.h"
|
|
||||||
#include "./literal_cost.h"
|
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./prefix.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef INFINITY
|
|
||||||
static const float kInfinity = INFINITY;
|
|
||||||
#else
|
|
||||||
static const float kInfinity = 3.4028e38f;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void BrotliInitZopfliNodes(ZopfliNode* array, size_t length) {
|
|
||||||
ZopfliNode stub;
|
|
||||||
size_t i;
|
|
||||||
stub.length = 1;
|
|
||||||
stub.distance = 0;
|
|
||||||
stub.insert_length = 0;
|
|
||||||
stub.u.cost = kInfinity;
|
|
||||||
for (i = 0; i < length; ++i) array[i] = stub;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t ZopfliNodeCopyLength(const ZopfliNode* self) {
|
|
||||||
return self->length & 0xffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t ZopfliNodeLengthCode(const ZopfliNode* self) {
|
|
||||||
const uint32_t modifier = self->length >> 24;
|
|
||||||
return ZopfliNodeCopyLength(self) + 9u - modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t ZopfliNodeCopyDistance(const ZopfliNode* self) {
|
|
||||||
return self->distance & 0x1ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t ZopfliNodeDistanceCode(const ZopfliNode* self) {
|
|
||||||
const uint32_t short_code = self->distance >> 25;
|
|
||||||
return short_code == 0 ? ZopfliNodeCopyDistance(self) + 15 : short_code - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) {
|
|
||||||
return ZopfliNodeCopyLength(self) + self->insert_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Histogram based cost model for zopflification. */
|
|
||||||
typedef struct ZopfliCostModel {
|
|
||||||
/* The insert and copy length symbols. */
|
|
||||||
float cost_cmd_[BROTLI_NUM_COMMAND_SYMBOLS];
|
|
||||||
float cost_dist_[BROTLI_NUM_DISTANCE_SYMBOLS];
|
|
||||||
/* Cumulative costs of literals per position in the stream. */
|
|
||||||
float* literal_costs_;
|
|
||||||
float min_cost_cmd_;
|
|
||||||
size_t num_bytes_;
|
|
||||||
} ZopfliCostModel;
|
|
||||||
|
|
||||||
static void InitZopfliCostModel(
|
|
||||||
MemoryManager* m, ZopfliCostModel* self, size_t num_bytes) {
|
|
||||||
self->num_bytes_ = num_bytes;
|
|
||||||
self->literal_costs_ = BROTLI_ALLOC(m, float, num_bytes + 2);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CleanupZopfliCostModel(MemoryManager* m, ZopfliCostModel* self) {
|
|
||||||
BROTLI_FREE(m, self->literal_costs_);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetCost(const uint32_t* histogram, size_t histogram_size,
|
|
||||||
float* cost) {
|
|
||||||
size_t sum = 0;
|
|
||||||
float log2sum;
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < histogram_size; i++) {
|
|
||||||
sum += histogram[i];
|
|
||||||
}
|
|
||||||
log2sum = (float)FastLog2(sum);
|
|
||||||
for (i = 0; i < histogram_size; i++) {
|
|
||||||
if (histogram[i] == 0) {
|
|
||||||
cost[i] = log2sum + 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Shannon bits for this symbol. */
|
|
||||||
cost[i] = log2sum - (float)FastLog2(histogram[i]);
|
|
||||||
|
|
||||||
/* Cannot be coded with less than 1 bit */
|
|
||||||
if (cost[i] < 1) cost[i] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const Command* commands,
|
|
||||||
size_t num_commands,
|
|
||||||
size_t last_insert_len) {
|
|
||||||
uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];
|
|
||||||
uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
|
|
||||||
uint32_t histogram_dist[BROTLI_NUM_DISTANCE_SYMBOLS];
|
|
||||||
float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
|
|
||||||
size_t pos = position - last_insert_len;
|
|
||||||
float min_cost_cmd = kInfinity;
|
|
||||||
size_t i;
|
|
||||||
float* cost_cmd = self->cost_cmd_;
|
|
||||||
|
|
||||||
memset(histogram_literal, 0, sizeof(histogram_literal));
|
|
||||||
memset(histogram_cmd, 0, sizeof(histogram_cmd));
|
|
||||||
memset(histogram_dist, 0, sizeof(histogram_dist));
|
|
||||||
|
|
||||||
for (i = 0; i < num_commands; i++) {
|
|
||||||
size_t inslength = commands[i].insert_len_;
|
|
||||||
size_t copylength = CommandCopyLen(&commands[i]);
|
|
||||||
size_t distcode = commands[i].dist_prefix_;
|
|
||||||
size_t cmdcode = commands[i].cmd_prefix_;
|
|
||||||
size_t j;
|
|
||||||
|
|
||||||
histogram_cmd[cmdcode]++;
|
|
||||||
if (cmdcode >= 128) histogram_dist[distcode]++;
|
|
||||||
|
|
||||||
for (j = 0; j < inslength; j++) {
|
|
||||||
histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += inslength + copylength;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, cost_literal);
|
|
||||||
SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, cost_cmd);
|
|
||||||
SetCost(histogram_dist, BROTLI_NUM_DISTANCE_SYMBOLS, self->cost_dist_);
|
|
||||||
|
|
||||||
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
|
|
||||||
min_cost_cmd = BROTLI_MIN(float, min_cost_cmd, cost_cmd[i]);
|
|
||||||
}
|
|
||||||
self->min_cost_cmd_ = min_cost_cmd;
|
|
||||||
|
|
||||||
{
|
|
||||||
float* literal_costs = self->literal_costs_;
|
|
||||||
size_t num_bytes = self->num_bytes_;
|
|
||||||
literal_costs[0] = 0.0;
|
|
||||||
for (i = 0; i < num_bytes; ++i) {
|
|
||||||
literal_costs[i + 1] = literal_costs[i] +
|
|
||||||
cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t ringbuffer_mask) {
|
|
||||||
float* literal_costs = self->literal_costs_;
|
|
||||||
float* cost_dist = self->cost_dist_;
|
|
||||||
float* cost_cmd = self->cost_cmd_;
|
|
||||||
size_t num_bytes = self->num_bytes_;
|
|
||||||
size_t i;
|
|
||||||
BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,
|
|
||||||
ringbuffer, &literal_costs[1]);
|
|
||||||
literal_costs[0] = 0.0;
|
|
||||||
for (i = 0; i < num_bytes; ++i) {
|
|
||||||
literal_costs[i + 1] += literal_costs[i];
|
|
||||||
}
|
|
||||||
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
|
|
||||||
cost_cmd[i] = (float)FastLog2(11 + (uint32_t)i);
|
|
||||||
}
|
|
||||||
for (i = 0; i < BROTLI_NUM_DISTANCE_SYMBOLS; ++i) {
|
|
||||||
cost_dist[i] = (float)FastLog2(20 + (uint32_t)i);
|
|
||||||
}
|
|
||||||
self->min_cost_cmd_ = (float)FastLog2(11);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE float ZopfliCostModelGetCommandCost(
|
|
||||||
const ZopfliCostModel* self, uint16_t cmdcode) {
|
|
||||||
return self->cost_cmd_[cmdcode];
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE float ZopfliCostModelGetDistanceCost(
|
|
||||||
const ZopfliCostModel* self, size_t distcode) {
|
|
||||||
return self->cost_dist_[distcode];
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE float ZopfliCostModelGetLiteralCosts(
|
|
||||||
const ZopfliCostModel* self, size_t from, size_t to) {
|
|
||||||
return self->literal_costs_[to] - self->literal_costs_[from];
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE float ZopfliCostModelGetMinCostCmd(
|
|
||||||
const ZopfliCostModel* self) {
|
|
||||||
return self->min_cost_cmd_;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,
|
static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,
|
||||||
size_t max_distance,
|
size_t max_distance,
|
||||||
const int* dist_cache) {
|
const int* dist_cache) {
|
||||||
|
@ -231,440 +42,15 @@ static BROTLI_INLINE size_t ComputeDistanceCode(size_t distance,
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return distance + 15;
|
return distance + BROTLI_NUM_DISTANCE_SHORT_CODES - 1;
|
||||||
}
|
|
||||||
|
|
||||||
/* REQUIRES: len >= 2, start_pos <= pos */
|
|
||||||
/* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */
|
|
||||||
/* Maintains the "ZopfliNode array invariant". */
|
|
||||||
static BROTLI_INLINE void UpdateZopfliNode(ZopfliNode* nodes, size_t pos,
|
|
||||||
size_t start_pos, size_t len, size_t len_code, size_t dist,
|
|
||||||
size_t short_code, float cost) {
|
|
||||||
ZopfliNode* next = &nodes[pos + len];
|
|
||||||
next->length = (uint32_t)(len | ((len + 9u - len_code) << 24));
|
|
||||||
next->distance = (uint32_t)(dist | (short_code << 25));
|
|
||||||
next->insert_length = (uint32_t)(pos - start_pos);
|
|
||||||
next->u.cost = cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct PosData {
|
|
||||||
size_t pos;
|
|
||||||
int distance_cache[4];
|
|
||||||
float costdiff;
|
|
||||||
float cost;
|
|
||||||
} PosData;
|
|
||||||
|
|
||||||
/* Maintains the smallest 8 cost difference together with their positions */
|
|
||||||
typedef struct StartPosQueue {
|
|
||||||
PosData q_[8];
|
|
||||||
size_t idx_;
|
|
||||||
} StartPosQueue;
|
|
||||||
|
|
||||||
static BROTLI_INLINE void InitStartPosQueue(StartPosQueue* self) {
|
|
||||||
self->idx_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t StartPosQueueSize(const StartPosQueue* self) {
|
|
||||||
return BROTLI_MIN(size_t, self->idx_, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void StartPosQueuePush(StartPosQueue* self, const PosData* posdata) {
|
|
||||||
size_t offset = ~(self->idx_++) & 7;
|
|
||||||
size_t len = StartPosQueueSize(self);
|
|
||||||
size_t i;
|
|
||||||
PosData* q = self->q_;
|
|
||||||
q[offset] = *posdata;
|
|
||||||
/* Restore the sorted order. In the list of |len| items at most |len - 1|
|
|
||||||
adjacent element comparisons / swaps are required. */
|
|
||||||
for (i = 1; i < len; ++i) {
|
|
||||||
if (q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff) {
|
|
||||||
BROTLI_SWAP(PosData, q, offset & 7, (offset + 1) & 7);
|
|
||||||
}
|
|
||||||
++offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const PosData* StartPosQueueAt(const StartPosQueue* self, size_t k) {
|
|
||||||
return &self->q_[(k - self->idx_) & 7];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the minimum possible copy length that can improve the cost of any */
|
|
||||||
/* future position. */
|
|
||||||
static size_t ComputeMinimumCopyLength(const float start_cost,
|
|
||||||
const ZopfliNode* nodes,
|
|
||||||
const size_t num_bytes,
|
|
||||||
const size_t pos) {
|
|
||||||
/* Compute the minimum possible cost of reaching any future position. */
|
|
||||||
float min_cost = start_cost;
|
|
||||||
size_t len = 2;
|
|
||||||
size_t next_len_bucket = 4;
|
|
||||||
size_t next_len_offset = 10;
|
|
||||||
while (pos + len <= num_bytes && nodes[pos + len].u.cost <= min_cost) {
|
|
||||||
/* We already reached (pos + len) with no more cost than the minimum
|
|
||||||
possible cost of reaching anything from this pos, so there is no point in
|
|
||||||
looking for lengths <= len. */
|
|
||||||
++len;
|
|
||||||
if (len == next_len_offset) {
|
|
||||||
/* We reached the next copy length code bucket, so we add one more
|
|
||||||
extra bit to the minimum cost. */
|
|
||||||
min_cost += 1.0f;
|
|
||||||
next_len_offset += next_len_bucket;
|
|
||||||
next_len_bucket *= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* REQUIRES: nodes[pos].cost < kInfinity
|
|
||||||
REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */
|
|
||||||
static uint32_t ComputeDistanceShortcut(const size_t block_start,
|
|
||||||
const size_t pos,
|
|
||||||
const size_t max_backward,
|
|
||||||
const ZopfliNode* nodes) {
|
|
||||||
const size_t clen = ZopfliNodeCopyLength(&nodes[pos]);
|
|
||||||
const size_t ilen = nodes[pos].insert_length;
|
|
||||||
const size_t dist = ZopfliNodeCopyDistance(&nodes[pos]);
|
|
||||||
/* Since |block_start + pos| is the end position of the command, the copy part
|
|
||||||
starts from |block_start + pos - clen|. Distances that are greater than
|
|
||||||
this or greater than |max_backward| are static dictionary references, and
|
|
||||||
do not update the last distances. Also distance code 0 (last distance)
|
|
||||||
does not update the last distances. */
|
|
||||||
if (pos == 0) {
|
|
||||||
return 0;
|
|
||||||
} else if (dist + clen <= block_start + pos &&
|
|
||||||
dist <= max_backward &&
|
|
||||||
ZopfliNodeDistanceCode(&nodes[pos]) > 0) {
|
|
||||||
return (uint32_t)pos;
|
|
||||||
} else {
|
|
||||||
return nodes[pos - clen - ilen].u.shortcut;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fills in dist_cache[0..3] with the last four distances (as defined by
|
|
||||||
Section 4. of the Spec) that would be used at (block_start + pos) if we
|
|
||||||
used the shortest path of commands from block_start, computed from
|
|
||||||
nodes[0..pos]. The last four distances at block_start are in
|
|
||||||
starting_dist_cach[0..3].
|
|
||||||
REQUIRES: nodes[pos].cost < kInfinity
|
|
||||||
REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */
|
|
||||||
static void ComputeDistanceCache(const size_t pos,
|
|
||||||
const int* starting_dist_cache,
|
|
||||||
const ZopfliNode* nodes,
|
|
||||||
int* dist_cache) {
|
|
||||||
int idx = 0;
|
|
||||||
size_t p = nodes[pos].u.shortcut;
|
|
||||||
while (idx < 4 && p > 0) {
|
|
||||||
const size_t ilen = nodes[p].insert_length;
|
|
||||||
const size_t clen = ZopfliNodeCopyLength(&nodes[p]);
|
|
||||||
const size_t dist = ZopfliNodeCopyDistance(&nodes[p]);
|
|
||||||
dist_cache[idx++] = (int)dist;
|
|
||||||
/* Because of prerequisite, p >= clen + ilen >= 2. */
|
|
||||||
p = nodes[p - clen - ilen].u.shortcut;
|
|
||||||
}
|
|
||||||
for (; idx < 4; ++idx) {
|
|
||||||
dist_cache[idx] = *starting_dist_cache++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateNodes(const size_t num_bytes,
|
|
||||||
const size_t block_start,
|
|
||||||
const size_t pos,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
const size_t ringbuffer_mask,
|
|
||||||
const BrotliEncoderParams* params,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
const int* starting_dist_cache,
|
|
||||||
const size_t num_matches,
|
|
||||||
const BackwardMatch* matches,
|
|
||||||
const ZopfliCostModel* model,
|
|
||||||
StartPosQueue* queue,
|
|
||||||
ZopfliNode* nodes) {
|
|
||||||
const size_t cur_ix = block_start + pos;
|
|
||||||
const size_t cur_ix_masked = cur_ix & ringbuffer_mask;
|
|
||||||
const size_t max_distance = BROTLI_MIN(size_t, cur_ix, max_backward_limit);
|
|
||||||
const size_t max_len = num_bytes - pos;
|
|
||||||
const size_t max_zopfli_len = MaxZopfliLen(params);
|
|
||||||
const size_t max_iters = MaxZopfliCandidates(params);
|
|
||||||
size_t min_len;
|
|
||||||
size_t k;
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Save cost, because ComputeDistanceCache invalidates it. */
|
|
||||||
float node_cost = nodes[pos].u.cost;
|
|
||||||
nodes[pos].u.shortcut = ComputeDistanceShortcut(
|
|
||||||
block_start, pos, max_backward_limit, nodes);
|
|
||||||
if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) {
|
|
||||||
PosData posdata;
|
|
||||||
posdata.pos = pos;
|
|
||||||
posdata.cost = node_cost;
|
|
||||||
posdata.costdiff = node_cost -
|
|
||||||
ZopfliCostModelGetLiteralCosts(model, 0, pos);
|
|
||||||
ComputeDistanceCache(
|
|
||||||
pos, starting_dist_cache, nodes, posdata.distance_cache);
|
|
||||||
StartPosQueuePush(queue, &posdata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const PosData* posdata = StartPosQueueAt(queue, 0);
|
|
||||||
float min_cost = (posdata->cost + ZopfliCostModelGetMinCostCmd(model) +
|
|
||||||
ZopfliCostModelGetLiteralCosts(model, posdata->pos, pos));
|
|
||||||
min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go over the command starting positions in order of increasing cost
|
|
||||||
difference. */
|
|
||||||
for (k = 0; k < max_iters && k < StartPosQueueSize(queue); ++k) {
|
|
||||||
const PosData* posdata = StartPosQueueAt(queue, k);
|
|
||||||
const size_t start = posdata->pos;
|
|
||||||
const uint16_t inscode = GetInsertLengthCode(pos - start);
|
|
||||||
const float start_costdiff = posdata->costdiff;
|
|
||||||
const float base_cost = start_costdiff + (float)GetInsertExtra(inscode) +
|
|
||||||
ZopfliCostModelGetLiteralCosts(model, 0, pos);
|
|
||||||
|
|
||||||
/* Look for last distance matches using the distance cache from this
|
|
||||||
starting position. */
|
|
||||||
size_t best_len = min_len - 1;
|
|
||||||
size_t j = 0;
|
|
||||||
for (; j < BROTLI_NUM_DISTANCE_SHORT_CODES && best_len < max_len; ++j) {
|
|
||||||
const size_t idx = kDistanceCacheIndex[j];
|
|
||||||
const size_t backward =
|
|
||||||
(size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]);
|
|
||||||
size_t prev_ix = cur_ix - backward;
|
|
||||||
if (prev_ix >= cur_ix) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (PREDICT_FALSE(backward > max_distance)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
prev_ix &= ringbuffer_mask;
|
|
||||||
|
|
||||||
if (cur_ix_masked + best_len > ringbuffer_mask ||
|
|
||||||
prev_ix + best_len > ringbuffer_mask ||
|
|
||||||
ringbuffer[cur_ix_masked + best_len] !=
|
|
||||||
ringbuffer[prev_ix + best_len]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const size_t len =
|
|
||||||
FindMatchLengthWithLimit(&ringbuffer[prev_ix],
|
|
||||||
&ringbuffer[cur_ix_masked],
|
|
||||||
max_len);
|
|
||||||
const float dist_cost = base_cost +
|
|
||||||
ZopfliCostModelGetDistanceCost(model, j);
|
|
||||||
size_t l;
|
|
||||||
for (l = best_len + 1; l <= len; ++l) {
|
|
||||||
const uint16_t copycode = GetCopyLengthCode(l);
|
|
||||||
const uint16_t cmdcode =
|
|
||||||
CombineLengthCodes(inscode, copycode, j == 0);
|
|
||||||
const float cost = (cmdcode < 128 ? base_cost : dist_cost) +
|
|
||||||
(float)GetCopyExtra(copycode) +
|
|
||||||
ZopfliCostModelGetCommandCost(model, cmdcode);
|
|
||||||
if (cost < nodes[pos + l].u.cost) {
|
|
||||||
UpdateZopfliNode(nodes, pos, start, l, l, backward, j + 1, cost);
|
|
||||||
}
|
|
||||||
best_len = l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At higher iterations look only for new last distance matches, since
|
|
||||||
looking only for new command start positions with the same distances
|
|
||||||
does not help much. */
|
|
||||||
if (k >= 2) continue;
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Loop through all possible copy lengths at this position. */
|
|
||||||
size_t len = min_len;
|
|
||||||
for (j = 0; j < num_matches; ++j) {
|
|
||||||
BackwardMatch match = matches[j];
|
|
||||||
size_t dist = match.distance;
|
|
||||||
BROTLI_BOOL is_dictionary_match = TO_BROTLI_BOOL(dist > max_distance);
|
|
||||||
/* We already tried all possible last distance matches, so we can use
|
|
||||||
normal distance code here. */
|
|
||||||
size_t dist_code = dist + 15;
|
|
||||||
uint16_t dist_symbol;
|
|
||||||
uint32_t distextra;
|
|
||||||
uint32_t distnumextra;
|
|
||||||
float dist_cost;
|
|
||||||
size_t max_match_len;
|
|
||||||
PrefixEncodeCopyDistance(dist_code, 0, 0, &dist_symbol, &distextra);
|
|
||||||
distnumextra = distextra >> 24;
|
|
||||||
dist_cost = base_cost + (float)distnumextra +
|
|
||||||
ZopfliCostModelGetDistanceCost(model, dist_symbol);
|
|
||||||
|
|
||||||
/* Try all copy lengths up until the maximum copy length corresponding
|
|
||||||
to this distance. If the distance refers to the static dictionary, or
|
|
||||||
the maximum length is long enough, try only one maximum length. */
|
|
||||||
max_match_len = BackwardMatchLength(&match);
|
|
||||||
if (len < max_match_len &&
|
|
||||||
(is_dictionary_match || max_match_len > max_zopfli_len)) {
|
|
||||||
len = max_match_len;
|
|
||||||
}
|
|
||||||
for (; len <= max_match_len; ++len) {
|
|
||||||
const size_t len_code =
|
|
||||||
is_dictionary_match ? BackwardMatchLengthCode(&match) : len;
|
|
||||||
const uint16_t copycode = GetCopyLengthCode(len_code);
|
|
||||||
const uint16_t cmdcode = CombineLengthCodes(inscode, copycode, 0);
|
|
||||||
const float cost = dist_cost + (float)GetCopyExtra(copycode) +
|
|
||||||
ZopfliCostModelGetCommandCost(model, cmdcode);
|
|
||||||
if (cost < nodes[pos + len].u.cost) {
|
|
||||||
UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0, cost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t ComputeShortestPathFromNodes(size_t num_bytes,
|
|
||||||
ZopfliNode* nodes) {
|
|
||||||
size_t index = num_bytes;
|
|
||||||
size_t num_commands = 0;
|
|
||||||
while (nodes[index].insert_length == 0 && nodes[index].length == 1) --index;
|
|
||||||
nodes[index].u.next = BROTLI_UINT32_MAX;
|
|
||||||
while (index != 0) {
|
|
||||||
size_t len = ZopfliNodeCommandLength(&nodes[index]);
|
|
||||||
index -= len;
|
|
||||||
nodes[index].u.next = (uint32_t)len;
|
|
||||||
num_commands++;
|
|
||||||
}
|
|
||||||
return num_commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrotliZopfliCreateCommands(const size_t num_bytes,
|
|
||||||
const size_t block_start,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
const ZopfliNode* nodes,
|
|
||||||
int* dist_cache,
|
|
||||||
size_t* last_insert_len,
|
|
||||||
Command* commands,
|
|
||||||
size_t* num_literals) {
|
|
||||||
size_t pos = 0;
|
|
||||||
uint32_t offset = nodes[0].u.next;
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; offset != BROTLI_UINT32_MAX; i++) {
|
|
||||||
const ZopfliNode* next = &nodes[pos + offset];
|
|
||||||
size_t copy_length = ZopfliNodeCopyLength(next);
|
|
||||||
size_t insert_length = next->insert_length;
|
|
||||||
pos += insert_length;
|
|
||||||
offset = next->u.next;
|
|
||||||
if (i == 0) {
|
|
||||||
insert_length += *last_insert_len;
|
|
||||||
*last_insert_len = 0;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
size_t distance = ZopfliNodeCopyDistance(next);
|
|
||||||
size_t len_code = ZopfliNodeLengthCode(next);
|
|
||||||
size_t max_distance =
|
|
||||||
BROTLI_MIN(size_t, block_start + pos, max_backward_limit);
|
|
||||||
BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance);
|
|
||||||
size_t dist_code = ZopfliNodeDistanceCode(next);
|
|
||||||
|
|
||||||
InitCommand(
|
|
||||||
&commands[i], insert_length, copy_length, len_code, dist_code);
|
|
||||||
|
|
||||||
if (!is_dictionary && dist_code > 0) {
|
|
||||||
dist_cache[3] = dist_cache[2];
|
|
||||||
dist_cache[2] = dist_cache[1];
|
|
||||||
dist_cache[1] = dist_cache[0];
|
|
||||||
dist_cache[0] = (int)distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*num_literals += insert_length;
|
|
||||||
pos += copy_length;
|
|
||||||
}
|
|
||||||
*last_insert_len += num_bytes - pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t ZopfliIterate(size_t num_bytes,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const BrotliEncoderParams* params,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
const int* dist_cache,
|
|
||||||
const ZopfliCostModel* model,
|
|
||||||
const uint32_t* num_matches,
|
|
||||||
const BackwardMatch* matches,
|
|
||||||
ZopfliNode* nodes) {
|
|
||||||
const size_t max_zopfli_len = MaxZopfliLen(params);
|
|
||||||
StartPosQueue queue;
|
|
||||||
size_t cur_match_pos = 0;
|
|
||||||
size_t i;
|
|
||||||
nodes[0].length = 0;
|
|
||||||
nodes[0].u.cost = 0;
|
|
||||||
InitStartPosQueue(&queue);
|
|
||||||
for (i = 0; i + 3 < num_bytes; i++) {
|
|
||||||
UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
|
|
||||||
params, max_backward_limit, dist_cache, num_matches[i],
|
|
||||||
&matches[cur_match_pos], model, &queue, nodes);
|
|
||||||
cur_match_pos += num_matches[i];
|
|
||||||
/* The zopflification can be too slow in case of very long lengths, so in
|
|
||||||
such case skip it all, it does not cost a lot of compression ratio. */
|
|
||||||
if (num_matches[i] == 1 &&
|
|
||||||
BackwardMatchLength(&matches[cur_match_pos - 1]) > max_zopfli_len) {
|
|
||||||
i += BackwardMatchLength(&matches[cur_match_pos - 1]) - 1;
|
|
||||||
InitStartPosQueue(&queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ComputeShortestPathFromNodes(num_bytes, nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
|
||||||
size_t num_bytes,
|
|
||||||
size_t position,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const BrotliEncoderParams* params,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
const int* dist_cache,
|
|
||||||
H10* hasher,
|
|
||||||
ZopfliNode* nodes) {
|
|
||||||
const size_t max_zopfli_len = MaxZopfliLen(params);
|
|
||||||
ZopfliCostModel model;
|
|
||||||
StartPosQueue queue;
|
|
||||||
BackwardMatch matches[MAX_NUM_MATCHES_H10];
|
|
||||||
const size_t store_end = num_bytes >= StoreLookaheadH10() ?
|
|
||||||
position + num_bytes - StoreLookaheadH10() + 1 : position;
|
|
||||||
size_t i;
|
|
||||||
nodes[0].length = 0;
|
|
||||||
nodes[0].u.cost = 0;
|
|
||||||
InitZopfliCostModel(m, &model, num_bytes);
|
|
||||||
if (BROTLI_IS_OOM(m)) return 0;
|
|
||||||
ZopfliCostModelSetFromLiteralCosts(
|
|
||||||
&model, position, ringbuffer, ringbuffer_mask);
|
|
||||||
InitStartPosQueue(&queue);
|
|
||||||
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) {
|
|
||||||
const size_t pos = position + i;
|
|
||||||
const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);
|
|
||||||
size_t num_matches = FindAllMatchesH10(hasher, ringbuffer, ringbuffer_mask,
|
|
||||||
pos, num_bytes - i, max_distance, params, matches);
|
|
||||||
if (num_matches > 0 &&
|
|
||||||
BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) {
|
|
||||||
matches[0] = matches[num_matches - 1];
|
|
||||||
num_matches = 1;
|
|
||||||
}
|
|
||||||
UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
|
|
||||||
params, max_backward_limit, dist_cache, num_matches, matches,
|
|
||||||
&model, &queue, nodes);
|
|
||||||
if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) {
|
|
||||||
/* Add the tail of the copy to the hasher. */
|
|
||||||
StoreRangeH10(hasher, ringbuffer, ringbuffer_mask, pos + 1, BROTLI_MIN(
|
|
||||||
size_t, pos + BackwardMatchLength(&matches[0]), store_end));
|
|
||||||
i += BackwardMatchLength(&matches[0]) - 1;
|
|
||||||
InitStartPosQueue(&queue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CleanupZopfliCostModel(m, &model);
|
|
||||||
return ComputeShortestPathFromNodes(num_bytes, nodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EXPAND_CAT(a, b) CAT(a, b)
|
#define EXPAND_CAT(a, b) CAT(a, b)
|
||||||
#define CAT(a, b) a ## b
|
#define CAT(a, b) a ## b
|
||||||
#define FN(X) EXPAND_CAT(X, HASHER())
|
#define FN(X) EXPAND_CAT(X, HASHER())
|
||||||
|
#define EXPORT_FN(X) EXPAND_CAT(X, EXPAND_CAT(PREFIX(), HASHER()))
|
||||||
|
|
||||||
|
#define PREFIX() N
|
||||||
|
|
||||||
#define HASHER() H2
|
#define HASHER() H2
|
||||||
/* NOLINTNEXTLINE(build/include) */
|
/* NOLINTNEXTLINE(build/include) */
|
||||||
|
@ -691,21 +77,6 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
||||||
#include "./backward_references_inc.h"
|
#include "./backward_references_inc.h"
|
||||||
#undef HASHER
|
#undef HASHER
|
||||||
|
|
||||||
#define HASHER() H7
|
|
||||||
/* NOLINTNEXTLINE(build/include) */
|
|
||||||
#include "./backward_references_inc.h"
|
|
||||||
#undef HASHER
|
|
||||||
|
|
||||||
#define HASHER() H8
|
|
||||||
/* NOLINTNEXTLINE(build/include) */
|
|
||||||
#include "./backward_references_inc.h"
|
|
||||||
#undef HASHER
|
|
||||||
|
|
||||||
#define HASHER() H9
|
|
||||||
/* NOLINTNEXTLINE(build/include) */
|
|
||||||
#include "./backward_references_inc.h"
|
|
||||||
#undef HASHER
|
|
||||||
|
|
||||||
#define HASHER() H40
|
#define HASHER() H40
|
||||||
/* NOLINTNEXTLINE(build/include) */
|
/* NOLINTNEXTLINE(build/include) */
|
||||||
#include "./backward_references_inc.h"
|
#include "./backward_references_inc.h"
|
||||||
|
@ -721,170 +92,51 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
||||||
#include "./backward_references_inc.h"
|
#include "./backward_references_inc.h"
|
||||||
#undef HASHER
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H54
|
||||||
|
/* NOLINTNEXTLINE(build/include) */
|
||||||
|
#include "./backward_references_inc.h"
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H35
|
||||||
|
/* NOLINTNEXTLINE(build/include) */
|
||||||
|
#include "./backward_references_inc.h"
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H55
|
||||||
|
/* NOLINTNEXTLINE(build/include) */
|
||||||
|
#include "./backward_references_inc.h"
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H65
|
||||||
|
/* NOLINTNEXTLINE(build/include) */
|
||||||
|
#include "./backward_references_inc.h"
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#undef PREFIX
|
||||||
|
|
||||||
|
#undef EXPORT_FN
|
||||||
#undef FN
|
#undef FN
|
||||||
#undef CAT
|
#undef CAT
|
||||||
#undef EXPAND_CAT
|
#undef EXPAND_CAT
|
||||||
|
|
||||||
static BROTLI_NOINLINE void CreateZopfliBackwardReferences(
|
void BrotliCreateBackwardReferences(
|
||||||
MemoryManager* m, size_t num_bytes, size_t position, BROTLI_BOOL is_last,
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
const BrotliEncoderParams* params, H10* hasher, int* dist_cache,
|
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
Command* commands, size_t* num_commands, size_t* num_literals) {
|
||||||
size_t* num_literals) {
|
switch (params->hasher.type) {
|
||||||
const size_t max_backward_limit = MaxBackwardLimit(params->lgwin);
|
#define CASE_(N) \
|
||||||
ZopfliNode* nodes;
|
case N: \
|
||||||
InitH10(m, hasher, ringbuffer, params, position, num_bytes, is_last);
|
CreateBackwardReferencesNH ## N( \
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
num_bytes, position, ringbuffer, \
|
||||||
StitchToPreviousBlockH10(hasher, num_bytes, position,
|
ringbuffer_mask, params, hasher, dist_cache, \
|
||||||
ringbuffer, ringbuffer_mask);
|
last_insert_len, commands, num_commands, num_literals); \
|
||||||
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
|
return;
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
FOR_GENERIC_HASHERS(CASE_)
|
||||||
BrotliInitZopfliNodes(nodes, num_bytes + 1);
|
#undef CASE_
|
||||||
*num_commands += BrotliZopfliComputeShortestPath(m, num_bytes, position,
|
|
||||||
ringbuffer, ringbuffer_mask, params, max_backward_limit,
|
|
||||||
dist_cache, hasher, nodes);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit, nodes,
|
|
||||||
dist_cache, last_insert_len, commands, num_literals);
|
|
||||||
BROTLI_FREE(m, nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_NOINLINE void CreateHqZopfliBackwardReferences(
|
|
||||||
MemoryManager* m, size_t num_bytes, size_t position, BROTLI_BOOL is_last,
|
|
||||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
|
||||||
const BrotliEncoderParams* params, H10* hasher, int* dist_cache,
|
|
||||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
|
||||||
size_t* num_literals) {
|
|
||||||
const size_t max_backward_limit = MaxBackwardLimit(params->lgwin);
|
|
||||||
uint32_t* num_matches = BROTLI_ALLOC(m, uint32_t, num_bytes);
|
|
||||||
size_t matches_size = 4 * num_bytes;
|
|
||||||
const size_t store_end = num_bytes >= StoreLookaheadH10() ?
|
|
||||||
position + num_bytes - StoreLookaheadH10() + 1 : position;
|
|
||||||
size_t cur_match_pos = 0;
|
|
||||||
size_t i;
|
|
||||||
size_t orig_num_literals;
|
|
||||||
size_t orig_last_insert_len;
|
|
||||||
int orig_dist_cache[4];
|
|
||||||
size_t orig_num_commands;
|
|
||||||
ZopfliCostModel model;
|
|
||||||
ZopfliNode* nodes;
|
|
||||||
BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
InitH10(m, hasher, ringbuffer, params, position, num_bytes, is_last);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
StitchToPreviousBlockH10(hasher, num_bytes, position,
|
|
||||||
ringbuffer, ringbuffer_mask);
|
|
||||||
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {
|
|
||||||
const size_t pos = position + i;
|
|
||||||
size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);
|
|
||||||
size_t max_length = num_bytes - i;
|
|
||||||
size_t num_found_matches;
|
|
||||||
size_t cur_match_end;
|
|
||||||
size_t j;
|
|
||||||
/* Ensure that we have enough free slots. */
|
|
||||||
BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size,
|
|
||||||
cur_match_pos + MAX_NUM_MATCHES_H10);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
num_found_matches = FindAllMatchesH10(hasher, ringbuffer, ringbuffer_mask,
|
|
||||||
pos, max_length, max_distance, params, &matches[cur_match_pos]);
|
|
||||||
cur_match_end = cur_match_pos + num_found_matches;
|
|
||||||
for (j = cur_match_pos; j + 1 < cur_match_end; ++j) {
|
|
||||||
assert(BackwardMatchLength(&matches[j]) <
|
|
||||||
BackwardMatchLength(&matches[j + 1]));
|
|
||||||
assert(matches[j].distance > max_distance ||
|
|
||||||
matches[j].distance <= matches[j + 1].distance);
|
|
||||||
}
|
|
||||||
num_matches[i] = (uint32_t)num_found_matches;
|
|
||||||
if (num_found_matches > 0) {
|
|
||||||
const size_t match_len = BackwardMatchLength(&matches[cur_match_end - 1]);
|
|
||||||
if (match_len > MAX_ZOPFLI_LEN_QUALITY_11) {
|
|
||||||
const size_t skip = match_len - 1;
|
|
||||||
matches[cur_match_pos++] = matches[cur_match_end - 1];
|
|
||||||
num_matches[i] = 1;
|
|
||||||
/* Add the tail of the copy to the hasher. */
|
|
||||||
StoreRangeH10(hasher, ringbuffer, ringbuffer_mask, pos + 1,
|
|
||||||
BROTLI_MIN(size_t, pos + match_len, store_end));
|
|
||||||
memset(&num_matches[i + 1], 0, skip * sizeof(num_matches[0]));
|
|
||||||
i += skip;
|
|
||||||
} else {
|
|
||||||
cur_match_pos = cur_match_end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
orig_num_literals = *num_literals;
|
|
||||||
orig_last_insert_len = *last_insert_len;
|
|
||||||
memcpy(orig_dist_cache, dist_cache, 4 * sizeof(dist_cache[0]));
|
|
||||||
orig_num_commands = *num_commands;
|
|
||||||
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
InitZopfliCostModel(m, &model, num_bytes);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
BrotliInitZopfliNodes(nodes, num_bytes + 1);
|
|
||||||
if (i == 0) {
|
|
||||||
ZopfliCostModelSetFromLiteralCosts(
|
|
||||||
&model, position, ringbuffer, ringbuffer_mask);
|
|
||||||
} else {
|
|
||||||
ZopfliCostModelSetFromCommands(&model, position, ringbuffer,
|
|
||||||
ringbuffer_mask, commands, *num_commands - orig_num_commands,
|
|
||||||
orig_last_insert_len);
|
|
||||||
}
|
|
||||||
*num_commands = orig_num_commands;
|
|
||||||
*num_literals = orig_num_literals;
|
|
||||||
*last_insert_len = orig_last_insert_len;
|
|
||||||
memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
|
|
||||||
*num_commands += ZopfliIterate(num_bytes, position, ringbuffer,
|
|
||||||
ringbuffer_mask, params, max_backward_limit, dist_cache,
|
|
||||||
&model, num_matches, matches, nodes);
|
|
||||||
BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit,
|
|
||||||
nodes, dist_cache, last_insert_len, commands, num_literals);
|
|
||||||
}
|
|
||||||
CleanupZopfliCostModel(m, &model);
|
|
||||||
BROTLI_FREE(m, nodes);
|
|
||||||
BROTLI_FREE(m, matches);
|
|
||||||
BROTLI_FREE(m, num_matches);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrotliCreateBackwardReferences(MemoryManager* m,
|
|
||||||
size_t num_bytes,
|
|
||||||
size_t position,
|
|
||||||
BROTLI_BOOL is_last,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t ringbuffer_mask,
|
|
||||||
const BrotliEncoderParams* params,
|
|
||||||
Hashers* hashers,
|
|
||||||
int* dist_cache,
|
|
||||||
size_t* last_insert_len,
|
|
||||||
Command* commands,
|
|
||||||
size_t* num_commands,
|
|
||||||
size_t* num_literals) {
|
|
||||||
if (params->quality == ZOPFLIFICATION_QUALITY) {
|
|
||||||
CreateZopfliBackwardReferences(
|
|
||||||
m, num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
|
||||||
params, hashers->h10, dist_cache,
|
|
||||||
last_insert_len, commands, num_commands, num_literals);
|
|
||||||
return;
|
|
||||||
} else if (params->quality == HQ_ZOPFLIFICATION_QUALITY) {
|
|
||||||
CreateHqZopfliBackwardReferences(
|
|
||||||
m, num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
|
||||||
params, hashers->h10, dist_cache,
|
|
||||||
last_insert_len, commands, num_commands, num_literals);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ChooseHasher(params)) {
|
|
||||||
#define _CASE(N) \
|
|
||||||
case N: \
|
|
||||||
CreateBackwardReferencesH ## N(m, num_bytes, position, is_last, \
|
|
||||||
ringbuffer, ringbuffer_mask, params, hashers->h ## N, dist_cache, \
|
|
||||||
last_insert_len, commands, num_commands, num_literals); \
|
|
||||||
break;
|
|
||||||
FOR_GENERIC_HASHERS(_CASE)
|
|
||||||
#undef _CASE
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
#ifndef BROTLI_ENC_BACKWARD_REFERENCES_H_
|
#ifndef BROTLI_ENC_BACKWARD_REFERENCES_H_
|
||||||
#define BROTLI_ENC_BACKWARD_REFERENCES_H_
|
#define BROTLI_ENC_BACKWARD_REFERENCES_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/constants.h"
|
||||||
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./hash.h"
|
#include "./hash.h"
|
||||||
#include "./memory.h"
|
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -25,72 +26,10 @@ extern "C" {
|
||||||
CreateBackwardReferences calls, and must be incremented by the amount written
|
CreateBackwardReferences calls, and must be incremented by the amount written
|
||||||
by this call. */
|
by this call. */
|
||||||
BROTLI_INTERNAL void BrotliCreateBackwardReferences(
|
BROTLI_INTERNAL void BrotliCreateBackwardReferences(
|
||||||
MemoryManager* m, size_t num_bytes, size_t position, BROTLI_BOOL is_last,
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
const BrotliEncoderParams* params, Hashers* hashers, int* dist_cache,
|
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
Command* commands, size_t* num_commands, size_t* num_literals);
|
||||||
size_t* num_literals);
|
|
||||||
|
|
||||||
typedef struct ZopfliNode {
|
|
||||||
/* best length to get up to this byte (not including this byte itself)
|
|
||||||
highest 8 bit is used to reconstruct the length code */
|
|
||||||
uint32_t length;
|
|
||||||
/* distance associated with the length
|
|
||||||
highest 7 bit contains distance short code + 1 (or zero if no short code)
|
|
||||||
*/
|
|
||||||
uint32_t distance;
|
|
||||||
/* number of literal inserts before this copy */
|
|
||||||
uint32_t insert_length;
|
|
||||||
|
|
||||||
/* This union holds information used by dynamic-programming. During forward
|
|
||||||
pass |cost| it used to store the goal function. When node is processed its
|
|
||||||
|cost| is invalidated in favor of |shortcut|. On path backtracing pass
|
|
||||||
|next| is assigned the offset to next node on the path. */
|
|
||||||
union {
|
|
||||||
/* Smallest cost to get to this byte from the beginning, as found so far. */
|
|
||||||
float cost;
|
|
||||||
/* Offset to the next node on the path. Equals to command_length() of the
|
|
||||||
next node on the path. For last node equals to BROTLI_UINT32_MAX */
|
|
||||||
uint32_t next;
|
|
||||||
/* Node position that provides next distance for distance cache. */
|
|
||||||
uint32_t shortcut;
|
|
||||||
} u;
|
|
||||||
} ZopfliNode;
|
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliInitZopfliNodes(ZopfliNode* array, size_t length);
|
|
||||||
|
|
||||||
/* Computes the shortest path of commands from position to at most
|
|
||||||
position + num_bytes.
|
|
||||||
|
|
||||||
On return, path->size() is the number of commands found and path[i] is the
|
|
||||||
length of the ith command (copy length plus insert length).
|
|
||||||
Note that the sum of the lengths of all commands can be less than num_bytes.
|
|
||||||
|
|
||||||
On return, the nodes[0..num_bytes] array will have the following
|
|
||||||
"ZopfliNode array invariant":
|
|
||||||
For each i in [1..num_bytes], if nodes[i].cost < kInfinity, then
|
|
||||||
(1) nodes[i].copy_length() >= 2
|
|
||||||
(2) nodes[i].command_length() <= i and
|
|
||||||
(3) nodes[i - nodes[i].command_length()].cost < kInfinity */
|
|
||||||
BROTLI_INTERNAL size_t BrotliZopfliComputeShortestPath(
|
|
||||||
MemoryManager* m, size_t num_bytes, size_t position,
|
|
||||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
|
||||||
const BrotliEncoderParams* params, const size_t max_backward_limit,
|
|
||||||
const int* dist_cache, H10* hasher, ZopfliNode* nodes);
|
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliZopfliCreateCommands(const size_t num_bytes,
|
|
||||||
const size_t block_start,
|
|
||||||
const size_t max_backward_limit,
|
|
||||||
const ZopfliNode* nodes,
|
|
||||||
int* dist_cache,
|
|
||||||
size_t* last_insert_len,
|
|
||||||
Command* commands,
|
|
||||||
size_t* num_literals);
|
|
||||||
|
|
||||||
/* Maximum distance, see section 9.1. of the spec. */
|
|
||||||
static BROTLI_INLINE size_t MaxBackwardLimit(int lgwin) {
|
|
||||||
return (1u << lgwin) - 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -0,0 +1,830 @@
|
||||||
|
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Function to find backward reference copies. */
|
||||||
|
|
||||||
|
#include "./backward_references_hq.h"
|
||||||
|
|
||||||
|
#include <string.h> /* memcpy, memset */
|
||||||
|
|
||||||
|
#include "../common/constants.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
|
#include "./command.h"
|
||||||
|
#include "./fast_log.h"
|
||||||
|
#include "./find_match_length.h"
|
||||||
|
#include "./literal_cost.h"
|
||||||
|
#include "./memory.h"
|
||||||
|
#include "./params.h"
|
||||||
|
#include "./prefix.h"
|
||||||
|
#include "./quality.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE 544
|
||||||
|
|
||||||
|
static const float kInfinity = 1.7e38f; /* ~= 2 ^ 127 */
|
||||||
|
|
||||||
|
static const uint32_t kDistanceCacheIndex[] = {
|
||||||
|
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||||
|
};
|
||||||
|
static const int kDistanceCacheOffset[] = {
|
||||||
|
0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3
|
||||||
|
};
|
||||||
|
|
||||||
|
void BrotliInitZopfliNodes(ZopfliNode* array, size_t length) {
|
||||||
|
ZopfliNode stub;
|
||||||
|
size_t i;
|
||||||
|
stub.length = 1;
|
||||||
|
stub.distance = 0;
|
||||||
|
stub.dcode_insert_length = 0;
|
||||||
|
stub.u.cost = kInfinity;
|
||||||
|
for (i = 0; i < length; ++i) array[i] = stub;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t ZopfliNodeCopyLength(const ZopfliNode* self) {
|
||||||
|
return self->length & 0x1FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t ZopfliNodeLengthCode(const ZopfliNode* self) {
|
||||||
|
const uint32_t modifier = self->length >> 25;
|
||||||
|
return ZopfliNodeCopyLength(self) + 9u - modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t ZopfliNodeCopyDistance(const ZopfliNode* self) {
|
||||||
|
return self->distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t ZopfliNodeDistanceCode(const ZopfliNode* self) {
|
||||||
|
const uint32_t short_code = self->dcode_insert_length >> 27;
|
||||||
|
return short_code == 0 ?
|
||||||
|
ZopfliNodeCopyDistance(self) + BROTLI_NUM_DISTANCE_SHORT_CODES - 1 :
|
||||||
|
short_code - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) {
|
||||||
|
return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Histogram based cost model for zopflification. */
|
||||||
|
typedef struct ZopfliCostModel {
|
||||||
|
/* The insert and copy length symbols. */
|
||||||
|
float cost_cmd_[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
|
float* cost_dist_;
|
||||||
|
uint32_t distance_histogram_size;
|
||||||
|
/* Cumulative costs of literals per position in the stream. */
|
||||||
|
float* literal_costs_;
|
||||||
|
float min_cost_cmd_;
|
||||||
|
size_t num_bytes_;
|
||||||
|
} ZopfliCostModel;
|
||||||
|
|
||||||
|
static void InitZopfliCostModel(
|
||||||
|
MemoryManager* m, ZopfliCostModel* self, const BrotliDistanceParams* dist,
|
||||||
|
size_t num_bytes) {
|
||||||
|
uint32_t distance_histogram_size = dist->alphabet_size;
|
||||||
|
if (distance_histogram_size > BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE) {
|
||||||
|
distance_histogram_size = BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE;
|
||||||
|
}
|
||||||
|
self->num_bytes_ = num_bytes;
|
||||||
|
self->literal_costs_ = BROTLI_ALLOC(m, float, num_bytes + 2);
|
||||||
|
self->cost_dist_ = BROTLI_ALLOC(m, float, dist->alphabet_size);
|
||||||
|
self->distance_histogram_size = distance_histogram_size;
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CleanupZopfliCostModel(MemoryManager* m, ZopfliCostModel* self) {
|
||||||
|
BROTLI_FREE(m, self->literal_costs_);
|
||||||
|
BROTLI_FREE(m, self->cost_dist_);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetCost(const uint32_t* histogram, size_t histogram_size,
|
||||||
|
BROTLI_BOOL literal_histogram, float* cost) {
|
||||||
|
size_t sum = 0;
|
||||||
|
size_t missing_symbol_sum;
|
||||||
|
float log2sum;
|
||||||
|
float missing_symbol_cost;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < histogram_size; i++) {
|
||||||
|
sum += histogram[i];
|
||||||
|
}
|
||||||
|
log2sum = (float)FastLog2(sum);
|
||||||
|
missing_symbol_sum = sum;
|
||||||
|
if (!literal_histogram) {
|
||||||
|
for (i = 0; i < histogram_size; i++) {
|
||||||
|
if (histogram[i] == 0) missing_symbol_sum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missing_symbol_cost = (float)FastLog2(missing_symbol_sum) + 2;
|
||||||
|
for (i = 0; i < histogram_size; i++) {
|
||||||
|
if (histogram[i] == 0) {
|
||||||
|
cost[i] = missing_symbol_cost;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shannon bits for this symbol. */
|
||||||
|
cost[i] = log2sum - (float)FastLog2(histogram[i]);
|
||||||
|
|
||||||
|
/* Cannot be coded with less than 1 bit */
|
||||||
|
if (cost[i] < 1) cost[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
|
||||||
|
size_t position,
|
||||||
|
const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask,
|
||||||
|
const Command* commands,
|
||||||
|
size_t num_commands,
|
||||||
|
size_t last_insert_len) {
|
||||||
|
uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||||
|
uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
|
uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE];
|
||||||
|
float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||||
|
size_t pos = position - last_insert_len;
|
||||||
|
float min_cost_cmd = kInfinity;
|
||||||
|
size_t i;
|
||||||
|
float* cost_cmd = self->cost_cmd_;
|
||||||
|
|
||||||
|
memset(histogram_literal, 0, sizeof(histogram_literal));
|
||||||
|
memset(histogram_cmd, 0, sizeof(histogram_cmd));
|
||||||
|
memset(histogram_dist, 0, sizeof(histogram_dist));
|
||||||
|
|
||||||
|
for (i = 0; i < num_commands; i++) {
|
||||||
|
size_t inslength = commands[i].insert_len_;
|
||||||
|
size_t copylength = CommandCopyLen(&commands[i]);
|
||||||
|
size_t distcode = commands[i].dist_prefix_ & 0x3FF;
|
||||||
|
size_t cmdcode = commands[i].cmd_prefix_;
|
||||||
|
size_t j;
|
||||||
|
|
||||||
|
histogram_cmd[cmdcode]++;
|
||||||
|
if (cmdcode >= 128) histogram_dist[distcode]++;
|
||||||
|
|
||||||
|
for (j = 0; j < inslength; j++) {
|
||||||
|
histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += inslength + copylength;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE,
|
||||||
|
cost_literal);
|
||||||
|
SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE,
|
||||||
|
cost_cmd);
|
||||||
|
SetCost(histogram_dist, self->distance_histogram_size, BROTLI_FALSE,
|
||||||
|
self->cost_dist_);
|
||||||
|
|
||||||
|
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
|
||||||
|
min_cost_cmd = BROTLI_MIN(float, min_cost_cmd, cost_cmd[i]);
|
||||||
|
}
|
||||||
|
self->min_cost_cmd_ = min_cost_cmd;
|
||||||
|
|
||||||
|
{
|
||||||
|
float* literal_costs = self->literal_costs_;
|
||||||
|
float literal_carry = 0.0;
|
||||||
|
size_t num_bytes = self->num_bytes_;
|
||||||
|
literal_costs[0] = 0.0;
|
||||||
|
for (i = 0; i < num_bytes; ++i) {
|
||||||
|
literal_carry +=
|
||||||
|
cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];
|
||||||
|
literal_costs[i + 1] = literal_costs[i] + literal_carry;
|
||||||
|
literal_carry -= literal_costs[i + 1] - literal_costs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self,
|
||||||
|
size_t position,
|
||||||
|
const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask) {
|
||||||
|
float* literal_costs = self->literal_costs_;
|
||||||
|
float literal_carry = 0.0;
|
||||||
|
float* cost_dist = self->cost_dist_;
|
||||||
|
float* cost_cmd = self->cost_cmd_;
|
||||||
|
size_t num_bytes = self->num_bytes_;
|
||||||
|
size_t i;
|
||||||
|
BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,
|
||||||
|
ringbuffer, &literal_costs[1]);
|
||||||
|
literal_costs[0] = 0.0;
|
||||||
|
for (i = 0; i < num_bytes; ++i) {
|
||||||
|
literal_carry += literal_costs[i + 1];
|
||||||
|
literal_costs[i + 1] = literal_costs[i] + literal_carry;
|
||||||
|
literal_carry -= literal_costs[i + 1] - literal_costs[i];
|
||||||
|
}
|
||||||
|
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
|
||||||
|
cost_cmd[i] = (float)FastLog2(11 + (uint32_t)i);
|
||||||
|
}
|
||||||
|
for (i = 0; i < self->distance_histogram_size; ++i) {
|
||||||
|
cost_dist[i] = (float)FastLog2(20 + (uint32_t)i);
|
||||||
|
}
|
||||||
|
self->min_cost_cmd_ = (float)FastLog2(11);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE float ZopfliCostModelGetCommandCost(
|
||||||
|
const ZopfliCostModel* self, uint16_t cmdcode) {
|
||||||
|
return self->cost_cmd_[cmdcode];
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE float ZopfliCostModelGetDistanceCost(
|
||||||
|
const ZopfliCostModel* self, size_t distcode) {
|
||||||
|
return self->cost_dist_[distcode];
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE float ZopfliCostModelGetLiteralCosts(
|
||||||
|
const ZopfliCostModel* self, size_t from, size_t to) {
|
||||||
|
return self->literal_costs_[to] - self->literal_costs_[from];
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE float ZopfliCostModelGetMinCostCmd(
|
||||||
|
const ZopfliCostModel* self) {
|
||||||
|
return self->min_cost_cmd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REQUIRES: len >= 2, start_pos <= pos */
|
||||||
|
/* REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity */
|
||||||
|
/* Maintains the "ZopfliNode array invariant". */
|
||||||
|
static BROTLI_INLINE void UpdateZopfliNode(ZopfliNode* nodes, size_t pos,
|
||||||
|
size_t start_pos, size_t len, size_t len_code, size_t dist,
|
||||||
|
size_t short_code, float cost) {
|
||||||
|
ZopfliNode* next = &nodes[pos + len];
|
||||||
|
next->length = (uint32_t)(len | ((len + 9u - len_code) << 25));
|
||||||
|
next->distance = (uint32_t)dist;
|
||||||
|
next->dcode_insert_length = (uint32_t)(
|
||||||
|
(short_code << 27) | (pos - start_pos));
|
||||||
|
next->u.cost = cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct PosData {
|
||||||
|
size_t pos;
|
||||||
|
int distance_cache[4];
|
||||||
|
float costdiff;
|
||||||
|
float cost;
|
||||||
|
} PosData;
|
||||||
|
|
||||||
|
/* Maintains the smallest 8 cost difference together with their positions */
|
||||||
|
typedef struct StartPosQueue {
|
||||||
|
PosData q_[8];
|
||||||
|
size_t idx_;
|
||||||
|
} StartPosQueue;
|
||||||
|
|
||||||
|
static BROTLI_INLINE void InitStartPosQueue(StartPosQueue* self) {
|
||||||
|
self->idx_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t StartPosQueueSize(const StartPosQueue* self) {
|
||||||
|
return BROTLI_MIN(size_t, self->idx_, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void StartPosQueuePush(StartPosQueue* self, const PosData* posdata) {
|
||||||
|
size_t offset = ~(self->idx_++) & 7;
|
||||||
|
size_t len = StartPosQueueSize(self);
|
||||||
|
size_t i;
|
||||||
|
PosData* q = self->q_;
|
||||||
|
q[offset] = *posdata;
|
||||||
|
/* Restore the sorted order. In the list of |len| items at most |len - 1|
|
||||||
|
adjacent element comparisons / swaps are required. */
|
||||||
|
for (i = 1; i < len; ++i) {
|
||||||
|
if (q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff) {
|
||||||
|
BROTLI_SWAP(PosData, q, offset & 7, (offset + 1) & 7);
|
||||||
|
}
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const PosData* StartPosQueueAt(const StartPosQueue* self, size_t k) {
|
||||||
|
return &self->q_[(k - self->idx_) & 7];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the minimum possible copy length that can improve the cost of any */
|
||||||
|
/* future position. */
|
||||||
|
static size_t ComputeMinimumCopyLength(const float start_cost,
|
||||||
|
const ZopfliNode* nodes,
|
||||||
|
const size_t num_bytes,
|
||||||
|
const size_t pos) {
|
||||||
|
/* Compute the minimum possible cost of reaching any future position. */
|
||||||
|
float min_cost = start_cost;
|
||||||
|
size_t len = 2;
|
||||||
|
size_t next_len_bucket = 4;
|
||||||
|
size_t next_len_offset = 10;
|
||||||
|
while (pos + len <= num_bytes && nodes[pos + len].u.cost <= min_cost) {
|
||||||
|
/* We already reached (pos + len) with no more cost than the minimum
|
||||||
|
possible cost of reaching anything from this pos, so there is no point in
|
||||||
|
looking for lengths <= len. */
|
||||||
|
++len;
|
||||||
|
if (len == next_len_offset) {
|
||||||
|
/* We reached the next copy length code bucket, so we add one more
|
||||||
|
extra bit to the minimum cost. */
|
||||||
|
min_cost += 1.0f;
|
||||||
|
next_len_offset += next_len_bucket;
|
||||||
|
next_len_bucket *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REQUIRES: nodes[pos].cost < kInfinity
|
||||||
|
REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */
|
||||||
|
static uint32_t ComputeDistanceShortcut(const size_t block_start,
|
||||||
|
const size_t pos,
|
||||||
|
const size_t max_backward,
|
||||||
|
const size_t gap,
|
||||||
|
const ZopfliNode* nodes) {
|
||||||
|
const size_t clen = ZopfliNodeCopyLength(&nodes[pos]);
|
||||||
|
const size_t ilen = nodes[pos].dcode_insert_length & 0x7FFFFFF;
|
||||||
|
const size_t dist = ZopfliNodeCopyDistance(&nodes[pos]);
|
||||||
|
/* Since |block_start + pos| is the end position of the command, the copy part
|
||||||
|
starts from |block_start + pos - clen|. Distances that are greater than
|
||||||
|
this or greater than |max_backward| are static dictionary references, and
|
||||||
|
do not update the last distances. Also distance code 0 (last distance)
|
||||||
|
does not update the last distances. */
|
||||||
|
if (pos == 0) {
|
||||||
|
return 0;
|
||||||
|
} else if (dist + clen <= block_start + pos + gap &&
|
||||||
|
dist <= max_backward + gap &&
|
||||||
|
ZopfliNodeDistanceCode(&nodes[pos]) > 0) {
|
||||||
|
return (uint32_t)pos;
|
||||||
|
} else {
|
||||||
|
return nodes[pos - clen - ilen].u.shortcut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fills in dist_cache[0..3] with the last four distances (as defined by
|
||||||
|
Section 4. of the Spec) that would be used at (block_start + pos) if we
|
||||||
|
used the shortest path of commands from block_start, computed from
|
||||||
|
nodes[0..pos]. The last four distances at block_start are in
|
||||||
|
starting_dist_cache[0..3].
|
||||||
|
REQUIRES: nodes[pos].cost < kInfinity
|
||||||
|
REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant". */
|
||||||
|
static void ComputeDistanceCache(const size_t pos,
|
||||||
|
const int* starting_dist_cache,
|
||||||
|
const ZopfliNode* nodes,
|
||||||
|
int* dist_cache) {
|
||||||
|
int idx = 0;
|
||||||
|
size_t p = nodes[pos].u.shortcut;
|
||||||
|
while (idx < 4 && p > 0) {
|
||||||
|
const size_t ilen = nodes[p].dcode_insert_length & 0x7FFFFFF;
|
||||||
|
const size_t clen = ZopfliNodeCopyLength(&nodes[p]);
|
||||||
|
const size_t dist = ZopfliNodeCopyDistance(&nodes[p]);
|
||||||
|
dist_cache[idx++] = (int)dist;
|
||||||
|
/* Because of prerequisite, p >= clen + ilen >= 2. */
|
||||||
|
p = nodes[p - clen - ilen].u.shortcut;
|
||||||
|
}
|
||||||
|
for (; idx < 4; ++idx) {
|
||||||
|
dist_cache[idx] = *starting_dist_cache++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Maintains "ZopfliNode array invariant" and pushes node to the queue, if it
|
||||||
|
is eligible. */
|
||||||
|
static void EvaluateNode(
|
||||||
|
const size_t block_start, const size_t pos, const size_t max_backward_limit,
|
||||||
|
const size_t gap, const int* starting_dist_cache,
|
||||||
|
const ZopfliCostModel* model, StartPosQueue* queue, ZopfliNode* nodes) {
|
||||||
|
/* Save cost, because ComputeDistanceCache invalidates it. */
|
||||||
|
float node_cost = nodes[pos].u.cost;
|
||||||
|
nodes[pos].u.shortcut = ComputeDistanceShortcut(
|
||||||
|
block_start, pos, max_backward_limit, gap, nodes);
|
||||||
|
if (node_cost <= ZopfliCostModelGetLiteralCosts(model, 0, pos)) {
|
||||||
|
PosData posdata;
|
||||||
|
posdata.pos = pos;
|
||||||
|
posdata.cost = node_cost;
|
||||||
|
posdata.costdiff = node_cost -
|
||||||
|
ZopfliCostModelGetLiteralCosts(model, 0, pos);
|
||||||
|
ComputeDistanceCache(
|
||||||
|
pos, starting_dist_cache, nodes, posdata.distance_cache);
|
||||||
|
StartPosQueuePush(queue, &posdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns longest copy length. */
|
||||||
|
static size_t UpdateNodes(
|
||||||
|
const size_t num_bytes, const size_t block_start, const size_t pos,
|
||||||
|
const uint8_t* ringbuffer, const size_t ringbuffer_mask,
|
||||||
|
const BrotliEncoderParams* params, const size_t max_backward_limit,
|
||||||
|
const int* starting_dist_cache, const size_t num_matches,
|
||||||
|
const BackwardMatch* matches, const ZopfliCostModel* model,
|
||||||
|
StartPosQueue* queue, ZopfliNode* nodes) {
|
||||||
|
const size_t cur_ix = block_start + pos;
|
||||||
|
const size_t cur_ix_masked = cur_ix & ringbuffer_mask;
|
||||||
|
const size_t max_distance = BROTLI_MIN(size_t, cur_ix, max_backward_limit);
|
||||||
|
const size_t max_len = num_bytes - pos;
|
||||||
|
const size_t max_zopfli_len = MaxZopfliLen(params);
|
||||||
|
const size_t max_iters = MaxZopfliCandidates(params);
|
||||||
|
size_t min_len;
|
||||||
|
size_t result = 0;
|
||||||
|
size_t k;
|
||||||
|
size_t gap = 0;
|
||||||
|
|
||||||
|
EvaluateNode(block_start, pos, max_backward_limit, gap, starting_dist_cache,
|
||||||
|
model, queue, nodes);
|
||||||
|
|
||||||
|
{
|
||||||
|
const PosData* posdata = StartPosQueueAt(queue, 0);
|
||||||
|
float min_cost = (posdata->cost + ZopfliCostModelGetMinCostCmd(model) +
|
||||||
|
ZopfliCostModelGetLiteralCosts(model, posdata->pos, pos));
|
||||||
|
min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go over the command starting positions in order of increasing cost
|
||||||
|
difference. */
|
||||||
|
for (k = 0; k < max_iters && k < StartPosQueueSize(queue); ++k) {
|
||||||
|
const PosData* posdata = StartPosQueueAt(queue, k);
|
||||||
|
const size_t start = posdata->pos;
|
||||||
|
const uint16_t inscode = GetInsertLengthCode(pos - start);
|
||||||
|
const float start_costdiff = posdata->costdiff;
|
||||||
|
const float base_cost = start_costdiff + (float)GetInsertExtra(inscode) +
|
||||||
|
ZopfliCostModelGetLiteralCosts(model, 0, pos);
|
||||||
|
|
||||||
|
/* Look for last distance matches using the distance cache from this
|
||||||
|
starting position. */
|
||||||
|
size_t best_len = min_len - 1;
|
||||||
|
size_t j = 0;
|
||||||
|
for (; j < BROTLI_NUM_DISTANCE_SHORT_CODES && best_len < max_len; ++j) {
|
||||||
|
const size_t idx = kDistanceCacheIndex[j];
|
||||||
|
const size_t backward =
|
||||||
|
(size_t)(posdata->distance_cache[idx] + kDistanceCacheOffset[j]);
|
||||||
|
size_t prev_ix = cur_ix - backward;
|
||||||
|
size_t len = 0;
|
||||||
|
uint8_t continuation = ringbuffer[cur_ix_masked + best_len];
|
||||||
|
if (cur_ix_masked + best_len > ringbuffer_mask) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (BROTLI_PREDICT_FALSE(backward > max_distance + gap)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (backward <= max_distance) {
|
||||||
|
if (prev_ix >= cur_ix) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_ix &= ringbuffer_mask;
|
||||||
|
if (prev_ix + best_len > ringbuffer_mask ||
|
||||||
|
continuation != ringbuffer[prev_ix + best_len]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
len = FindMatchLengthWithLimit(&ringbuffer[prev_ix],
|
||||||
|
&ringbuffer[cur_ix_masked],
|
||||||
|
max_len);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const float dist_cost = base_cost +
|
||||||
|
ZopfliCostModelGetDistanceCost(model, j);
|
||||||
|
size_t l;
|
||||||
|
for (l = best_len + 1; l <= len; ++l) {
|
||||||
|
const uint16_t copycode = GetCopyLengthCode(l);
|
||||||
|
const uint16_t cmdcode =
|
||||||
|
CombineLengthCodes(inscode, copycode, j == 0);
|
||||||
|
const float cost = (cmdcode < 128 ? base_cost : dist_cost) +
|
||||||
|
(float)GetCopyExtra(copycode) +
|
||||||
|
ZopfliCostModelGetCommandCost(model, cmdcode);
|
||||||
|
if (cost < nodes[pos + l].u.cost) {
|
||||||
|
UpdateZopfliNode(nodes, pos, start, l, l, backward, j + 1, cost);
|
||||||
|
result = BROTLI_MAX(size_t, result, l);
|
||||||
|
}
|
||||||
|
best_len = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* At higher iterations look only for new last distance matches, since
|
||||||
|
looking only for new command start positions with the same distances
|
||||||
|
does not help much. */
|
||||||
|
if (k >= 2) continue;
|
||||||
|
|
||||||
|
{
|
||||||
|
/* Loop through all possible copy lengths at this position. */
|
||||||
|
size_t len = min_len;
|
||||||
|
for (j = 0; j < num_matches; ++j) {
|
||||||
|
BackwardMatch match = matches[j];
|
||||||
|
size_t dist = match.distance;
|
||||||
|
BROTLI_BOOL is_dictionary_match =
|
||||||
|
TO_BROTLI_BOOL(dist > max_distance + gap);
|
||||||
|
/* We already tried all possible last distance matches, so we can use
|
||||||
|
normal distance code here. */
|
||||||
|
size_t dist_code = dist + BROTLI_NUM_DISTANCE_SHORT_CODES - 1;
|
||||||
|
uint16_t dist_symbol;
|
||||||
|
uint32_t distextra;
|
||||||
|
uint32_t distnumextra;
|
||||||
|
float dist_cost;
|
||||||
|
size_t max_match_len;
|
||||||
|
PrefixEncodeCopyDistance(
|
||||||
|
dist_code, params->dist.num_direct_distance_codes,
|
||||||
|
params->dist.distance_postfix_bits, &dist_symbol, &distextra);
|
||||||
|
distnumextra = dist_symbol >> 10;
|
||||||
|
dist_cost = base_cost + (float)distnumextra +
|
||||||
|
ZopfliCostModelGetDistanceCost(model, dist_symbol & 0x3FF);
|
||||||
|
|
||||||
|
/* Try all copy lengths up until the maximum copy length corresponding
|
||||||
|
to this distance. If the distance refers to the static dictionary, or
|
||||||
|
the maximum length is long enough, try only one maximum length. */
|
||||||
|
max_match_len = BackwardMatchLength(&match);
|
||||||
|
if (len < max_match_len &&
|
||||||
|
(is_dictionary_match || max_match_len > max_zopfli_len)) {
|
||||||
|
len = max_match_len;
|
||||||
|
}
|
||||||
|
for (; len <= max_match_len; ++len) {
|
||||||
|
const size_t len_code =
|
||||||
|
is_dictionary_match ? BackwardMatchLengthCode(&match) : len;
|
||||||
|
const uint16_t copycode = GetCopyLengthCode(len_code);
|
||||||
|
const uint16_t cmdcode = CombineLengthCodes(inscode, copycode, 0);
|
||||||
|
const float cost = dist_cost + (float)GetCopyExtra(copycode) +
|
||||||
|
ZopfliCostModelGetCommandCost(model, cmdcode);
|
||||||
|
if (cost < nodes[pos + len].u.cost) {
|
||||||
|
UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0, cost);
|
||||||
|
result = BROTLI_MAX(size_t, result, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ComputeShortestPathFromNodes(size_t num_bytes,
|
||||||
|
ZopfliNode* nodes) {
|
||||||
|
size_t index = num_bytes;
|
||||||
|
size_t num_commands = 0;
|
||||||
|
while ((nodes[index].dcode_insert_length & 0x7FFFFFF) == 0 &&
|
||||||
|
nodes[index].length == 1) --index;
|
||||||
|
nodes[index].u.next = BROTLI_UINT32_MAX;
|
||||||
|
while (index != 0) {
|
||||||
|
size_t len = ZopfliNodeCommandLength(&nodes[index]);
|
||||||
|
index -= len;
|
||||||
|
nodes[index].u.next = (uint32_t)len;
|
||||||
|
num_commands++;
|
||||||
|
}
|
||||||
|
return num_commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */
|
||||||
|
void BrotliZopfliCreateCommands(const size_t num_bytes,
|
||||||
|
const size_t block_start,
|
||||||
|
const size_t max_backward_limit,
|
||||||
|
const ZopfliNode* nodes,
|
||||||
|
int* dist_cache,
|
||||||
|
size_t* last_insert_len,
|
||||||
|
const BrotliEncoderParams* params,
|
||||||
|
Command* commands,
|
||||||
|
size_t* num_literals) {
|
||||||
|
size_t pos = 0;
|
||||||
|
uint32_t offset = nodes[0].u.next;
|
||||||
|
size_t i;
|
||||||
|
size_t gap = 0;
|
||||||
|
for (i = 0; offset != BROTLI_UINT32_MAX; i++) {
|
||||||
|
const ZopfliNode* next = &nodes[pos + offset];
|
||||||
|
size_t copy_length = ZopfliNodeCopyLength(next);
|
||||||
|
size_t insert_length = next->dcode_insert_length & 0x7FFFFFF;
|
||||||
|
pos += insert_length;
|
||||||
|
offset = next->u.next;
|
||||||
|
if (i == 0) {
|
||||||
|
insert_length += *last_insert_len;
|
||||||
|
*last_insert_len = 0;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
size_t distance = ZopfliNodeCopyDistance(next);
|
||||||
|
size_t len_code = ZopfliNodeLengthCode(next);
|
||||||
|
size_t max_distance =
|
||||||
|
BROTLI_MIN(size_t, block_start + pos, max_backward_limit);
|
||||||
|
BROTLI_BOOL is_dictionary = TO_BROTLI_BOOL(distance > max_distance + gap);
|
||||||
|
size_t dist_code = ZopfliNodeDistanceCode(next);
|
||||||
|
InitCommand(&commands[i], ¶ms->dist, insert_length,
|
||||||
|
copy_length, (int)len_code - (int)copy_length, dist_code);
|
||||||
|
|
||||||
|
if (!is_dictionary && dist_code > 0) {
|
||||||
|
dist_cache[3] = dist_cache[2];
|
||||||
|
dist_cache[2] = dist_cache[1];
|
||||||
|
dist_cache[1] = dist_cache[0];
|
||||||
|
dist_cache[0] = (int)distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*num_literals += insert_length;
|
||||||
|
pos += copy_length;
|
||||||
|
}
|
||||||
|
*last_insert_len += num_bytes - pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ZopfliIterate(size_t num_bytes,
|
||||||
|
size_t position,
|
||||||
|
const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask,
|
||||||
|
const BrotliEncoderParams* params,
|
||||||
|
const size_t max_backward_limit,
|
||||||
|
const size_t gap,
|
||||||
|
const int* dist_cache,
|
||||||
|
const ZopfliCostModel* model,
|
||||||
|
const uint32_t* num_matches,
|
||||||
|
const BackwardMatch* matches,
|
||||||
|
ZopfliNode* nodes) {
|
||||||
|
const size_t max_zopfli_len = MaxZopfliLen(params);
|
||||||
|
StartPosQueue queue;
|
||||||
|
size_t cur_match_pos = 0;
|
||||||
|
size_t i;
|
||||||
|
nodes[0].length = 0;
|
||||||
|
nodes[0].u.cost = 0;
|
||||||
|
InitStartPosQueue(&queue);
|
||||||
|
for (i = 0; i + 3 < num_bytes; i++) {
|
||||||
|
size_t skip = UpdateNodes(num_bytes, position, i, ringbuffer,
|
||||||
|
ringbuffer_mask, params, max_backward_limit, dist_cache,
|
||||||
|
num_matches[i], &matches[cur_match_pos], model, &queue, nodes);
|
||||||
|
if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0;
|
||||||
|
cur_match_pos += num_matches[i];
|
||||||
|
if (num_matches[i] == 1 &&
|
||||||
|
BackwardMatchLength(&matches[cur_match_pos - 1]) > max_zopfli_len) {
|
||||||
|
skip = BROTLI_MAX(size_t,
|
||||||
|
BackwardMatchLength(&matches[cur_match_pos - 1]), skip);
|
||||||
|
}
|
||||||
|
if (skip > 1) {
|
||||||
|
skip--;
|
||||||
|
while (skip) {
|
||||||
|
i++;
|
||||||
|
if (i + 3 >= num_bytes) break;
|
||||||
|
EvaluateNode(position, i, max_backward_limit, gap, dist_cache, model,
|
||||||
|
&queue, nodes);
|
||||||
|
cur_match_pos += num_matches[i];
|
||||||
|
skip--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ComputeShortestPathFromNodes(num_bytes, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* REQUIRES: nodes != NULL and len(nodes) >= num_bytes + 1 */
|
||||||
|
size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
|
const size_t max_backward_limit, const int* dist_cache, HasherHandle hasher,
|
||||||
|
ZopfliNode* nodes) {
|
||||||
|
const size_t max_zopfli_len = MaxZopfliLen(params);
|
||||||
|
ZopfliCostModel model;
|
||||||
|
StartPosQueue queue;
|
||||||
|
BackwardMatch matches[2 * (MAX_NUM_MATCHES_H10 + 64)];
|
||||||
|
const size_t store_end = num_bytes >= StoreLookaheadH10() ?
|
||||||
|
position + num_bytes - StoreLookaheadH10() + 1 : position;
|
||||||
|
size_t i;
|
||||||
|
size_t gap = 0;
|
||||||
|
size_t lz_matches_offset = 0;
|
||||||
|
nodes[0].length = 0;
|
||||||
|
nodes[0].u.cost = 0;
|
||||||
|
InitZopfliCostModel(m, &model, ¶ms->dist, num_bytes);
|
||||||
|
if (BROTLI_IS_OOM(m)) return 0;
|
||||||
|
ZopfliCostModelSetFromLiteralCosts(
|
||||||
|
&model, position, ringbuffer, ringbuffer_mask);
|
||||||
|
InitStartPosQueue(&queue);
|
||||||
|
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) {
|
||||||
|
const size_t pos = position + i;
|
||||||
|
const size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);
|
||||||
|
size_t skip;
|
||||||
|
size_t num_matches = FindAllMatchesH10(hasher, ¶ms->dictionary,
|
||||||
|
ringbuffer, ringbuffer_mask, pos, num_bytes - i, max_distance, gap,
|
||||||
|
params, &matches[lz_matches_offset]);
|
||||||
|
if (num_matches > 0 &&
|
||||||
|
BackwardMatchLength(&matches[num_matches - 1]) > max_zopfli_len) {
|
||||||
|
matches[0] = matches[num_matches - 1];
|
||||||
|
num_matches = 1;
|
||||||
|
}
|
||||||
|
skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
|
||||||
|
params, max_backward_limit, dist_cache, num_matches, matches, &model,
|
||||||
|
&queue, nodes);
|
||||||
|
if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0;
|
||||||
|
if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) {
|
||||||
|
skip = BROTLI_MAX(size_t, BackwardMatchLength(&matches[0]), skip);
|
||||||
|
}
|
||||||
|
if (skip > 1) {
|
||||||
|
/* Add the tail of the copy to the hasher. */
|
||||||
|
StoreRangeH10(hasher, ringbuffer, ringbuffer_mask, pos + 1, BROTLI_MIN(
|
||||||
|
size_t, pos + skip, store_end));
|
||||||
|
skip--;
|
||||||
|
while (skip) {
|
||||||
|
i++;
|
||||||
|
if (i + HashTypeLengthH10() - 1 >= num_bytes) break;
|
||||||
|
EvaluateNode(position, i, max_backward_limit, gap, dist_cache, &model,
|
||||||
|
&queue, nodes);
|
||||||
|
skip--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CleanupZopfliCostModel(m, &model);
|
||||||
|
return ComputeShortestPathFromNodes(num_bytes, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrotliCreateZopfliBackwardReferences(MemoryManager* m,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
|
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||||
|
Command* commands, size_t* num_commands, size_t* num_literals) {
|
||||||
|
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||||
|
ZopfliNode* nodes;
|
||||||
|
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
BrotliInitZopfliNodes(nodes, num_bytes + 1);
|
||||||
|
*num_commands += BrotliZopfliComputeShortestPath(m,
|
||||||
|
num_bytes, position, ringbuffer, ringbuffer_mask,
|
||||||
|
params, max_backward_limit, dist_cache, hasher, nodes);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit, nodes,
|
||||||
|
dist_cache, last_insert_len, params, commands, num_literals);
|
||||||
|
BROTLI_FREE(m, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
|
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||||
|
Command* commands, size_t* num_commands, size_t* num_literals) {
|
||||||
|
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||||
|
uint32_t* num_matches = BROTLI_ALLOC(m, uint32_t, num_bytes);
|
||||||
|
size_t matches_size = 4 * num_bytes;
|
||||||
|
const size_t store_end = num_bytes >= StoreLookaheadH10() ?
|
||||||
|
position + num_bytes - StoreLookaheadH10() + 1 : position;
|
||||||
|
size_t cur_match_pos = 0;
|
||||||
|
size_t i;
|
||||||
|
size_t orig_num_literals;
|
||||||
|
size_t orig_last_insert_len;
|
||||||
|
int orig_dist_cache[4];
|
||||||
|
size_t orig_num_commands;
|
||||||
|
ZopfliCostModel model;
|
||||||
|
ZopfliNode* nodes;
|
||||||
|
BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);
|
||||||
|
size_t gap = 0;
|
||||||
|
size_t shadow_matches = 0;
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {
|
||||||
|
const size_t pos = position + i;
|
||||||
|
size_t max_distance = BROTLI_MIN(size_t, pos, max_backward_limit);
|
||||||
|
size_t max_length = num_bytes - i;
|
||||||
|
size_t num_found_matches;
|
||||||
|
size_t cur_match_end;
|
||||||
|
size_t j;
|
||||||
|
/* Ensure that we have enough free slots. */
|
||||||
|
BROTLI_ENSURE_CAPACITY(m, BackwardMatch, matches, matches_size,
|
||||||
|
cur_match_pos + MAX_NUM_MATCHES_H10 + shadow_matches);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
num_found_matches = FindAllMatchesH10(hasher,
|
||||||
|
¶ms->dictionary, ringbuffer, ringbuffer_mask, pos, max_length,
|
||||||
|
max_distance, gap, params, &matches[cur_match_pos + shadow_matches]);
|
||||||
|
cur_match_end = cur_match_pos + num_found_matches;
|
||||||
|
for (j = cur_match_pos; j + 1 < cur_match_end; ++j) {
|
||||||
|
BROTLI_DCHECK(BackwardMatchLength(&matches[j]) <=
|
||||||
|
BackwardMatchLength(&matches[j + 1]));
|
||||||
|
}
|
||||||
|
num_matches[i] = (uint32_t)num_found_matches;
|
||||||
|
if (num_found_matches > 0) {
|
||||||
|
const size_t match_len = BackwardMatchLength(&matches[cur_match_end - 1]);
|
||||||
|
if (match_len > MAX_ZOPFLI_LEN_QUALITY_11) {
|
||||||
|
const size_t skip = match_len - 1;
|
||||||
|
matches[cur_match_pos++] = matches[cur_match_end - 1];
|
||||||
|
num_matches[i] = 1;
|
||||||
|
/* Add the tail of the copy to the hasher. */
|
||||||
|
StoreRangeH10(hasher, ringbuffer, ringbuffer_mask, pos + 1,
|
||||||
|
BROTLI_MIN(size_t, pos + match_len, store_end));
|
||||||
|
memset(&num_matches[i + 1], 0, skip * sizeof(num_matches[0]));
|
||||||
|
i += skip;
|
||||||
|
} else {
|
||||||
|
cur_match_pos = cur_match_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
orig_num_literals = *num_literals;
|
||||||
|
orig_last_insert_len = *last_insert_len;
|
||||||
|
memcpy(orig_dist_cache, dist_cache, 4 * sizeof(dist_cache[0]));
|
||||||
|
orig_num_commands = *num_commands;
|
||||||
|
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
InitZopfliCostModel(m, &model, ¶ms->dist, num_bytes);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
BrotliInitZopfliNodes(nodes, num_bytes + 1);
|
||||||
|
if (i == 0) {
|
||||||
|
ZopfliCostModelSetFromLiteralCosts(
|
||||||
|
&model, position, ringbuffer, ringbuffer_mask);
|
||||||
|
} else {
|
||||||
|
ZopfliCostModelSetFromCommands(&model, position, ringbuffer,
|
||||||
|
ringbuffer_mask, commands, *num_commands - orig_num_commands,
|
||||||
|
orig_last_insert_len);
|
||||||
|
}
|
||||||
|
*num_commands = orig_num_commands;
|
||||||
|
*num_literals = orig_num_literals;
|
||||||
|
*last_insert_len = orig_last_insert_len;
|
||||||
|
memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
|
||||||
|
*num_commands += ZopfliIterate(num_bytes, position, ringbuffer,
|
||||||
|
ringbuffer_mask, params, max_backward_limit, gap, dist_cache,
|
||||||
|
&model, num_matches, matches, nodes);
|
||||||
|
BrotliZopfliCreateCommands(num_bytes, position, max_backward_limit,
|
||||||
|
nodes, dist_cache, last_insert_len, params, commands, num_literals);
|
||||||
|
}
|
||||||
|
CleanupZopfliCostModel(m, &model);
|
||||||
|
BROTLI_FREE(m, nodes);
|
||||||
|
BROTLI_FREE(m, matches);
|
||||||
|
BROTLI_FREE(m, num_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Function to find backward reference copies. */
|
||||||
|
|
||||||
|
#ifndef BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_
|
||||||
|
#define BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_
|
||||||
|
|
||||||
|
#include "../common/constants.h"
|
||||||
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
|
#include "./command.h"
|
||||||
|
#include "./hash.h"
|
||||||
|
#include "./memory.h"
|
||||||
|
#include "./quality.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BROTLI_INTERNAL void BrotliCreateZopfliBackwardReferences(MemoryManager* m,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
|
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||||
|
Command* commands, size_t* num_commands, size_t* num_literals);
|
||||||
|
|
||||||
|
BROTLI_INTERNAL void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
|
HasherHandle hasher, int* dist_cache, size_t* last_insert_len,
|
||||||
|
Command* commands, size_t* num_commands, size_t* num_literals);
|
||||||
|
|
||||||
|
typedef struct ZopfliNode {
|
||||||
|
/* Best length to get up to this byte (not including this byte itself)
|
||||||
|
highest 7 bit is used to reconstruct the length code. */
|
||||||
|
uint32_t length;
|
||||||
|
/* Distance associated with the length. */
|
||||||
|
uint32_t distance;
|
||||||
|
/* Number of literal inserts before this copy; highest 5 bits contain
|
||||||
|
distance short code + 1 (or zero if no short code). */
|
||||||
|
uint32_t dcode_insert_length;
|
||||||
|
|
||||||
|
/* This union holds information used by dynamic-programming. During forward
|
||||||
|
pass |cost| it used to store the goal function. When node is processed its
|
||||||
|
|cost| is invalidated in favor of |shortcut|. On path back-tracing pass
|
||||||
|
|next| is assigned the offset to next node on the path. */
|
||||||
|
union {
|
||||||
|
/* Smallest cost to get to this byte from the beginning, as found so far. */
|
||||||
|
float cost;
|
||||||
|
/* Offset to the next node on the path. Equals to command_length() of the
|
||||||
|
next node on the path. For last node equals to BROTLI_UINT32_MAX */
|
||||||
|
uint32_t next;
|
||||||
|
/* Node position that provides next distance for distance cache. */
|
||||||
|
uint32_t shortcut;
|
||||||
|
} u;
|
||||||
|
} ZopfliNode;
|
||||||
|
|
||||||
|
BROTLI_INTERNAL void BrotliInitZopfliNodes(ZopfliNode* array, size_t length);
|
||||||
|
|
||||||
|
/* Computes the shortest path of commands from position to at most
|
||||||
|
position + num_bytes.
|
||||||
|
|
||||||
|
On return, path->size() is the number of commands found and path[i] is the
|
||||||
|
length of the i-th command (copy length plus insert length).
|
||||||
|
Note that the sum of the lengths of all commands can be less than num_bytes.
|
||||||
|
|
||||||
|
On return, the nodes[0..num_bytes] array will have the following
|
||||||
|
"ZopfliNode array invariant":
|
||||||
|
For each i in [1..num_bytes], if nodes[i].cost < kInfinity, then
|
||||||
|
(1) nodes[i].copy_length() >= 2
|
||||||
|
(2) nodes[i].command_length() <= i and
|
||||||
|
(3) nodes[i - nodes[i].command_length()].cost < kInfinity */
|
||||||
|
BROTLI_INTERNAL size_t BrotliZopfliComputeShortestPath(MemoryManager* m,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask, const BrotliEncoderParams* params,
|
||||||
|
const size_t max_backward_limit, const int* dist_cache, HasherHandle hasher,
|
||||||
|
ZopfliNode* nodes);
|
||||||
|
|
||||||
|
BROTLI_INTERNAL void BrotliZopfliCreateCommands(
|
||||||
|
const size_t num_bytes, const size_t block_start,
|
||||||
|
const size_t max_backward_limit, const ZopfliNode* nodes,
|
||||||
|
int* dist_cache, size_t* last_insert_len, const BrotliEncoderParams* params,
|
||||||
|
Command* commands, size_t* num_literals);
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BROTLI_ENC_BACKWARD_REFERENCES_HQ_H_ */
|
|
@ -5,18 +5,16 @@
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* template parameters: FN */
|
/* template parameters: EXPORT_FN, FN */
|
||||||
|
|
||||||
#define Hasher HASHER()
|
static BROTLI_NOINLINE void EXPORT_FN(CreateBackwardReferences)(
|
||||||
|
size_t num_bytes, size_t position,
|
||||||
static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
|
|
||||||
MemoryManager* m, size_t num_bytes, size_t position, BROTLI_BOOL is_last,
|
|
||||||
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
const uint8_t* ringbuffer, size_t ringbuffer_mask,
|
||||||
const BrotliEncoderParams* params, Hasher* hasher, int* dist_cache,
|
const BrotliEncoderParams* params, HasherHandle hasher, int* dist_cache,
|
||||||
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
size_t* last_insert_len, Command* commands, size_t* num_commands,
|
||||||
size_t* num_literals) {
|
size_t* num_literals) {
|
||||||
/* Set maximum distance, see section 9.1. of the spec. */
|
/* Set maximum distance, see section 9.1. of the spec. */
|
||||||
const size_t max_backward_limit = MaxBackwardLimit(params->lgwin);
|
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
|
||||||
|
|
||||||
const Command* const orig_commands = commands;
|
const Command* const orig_commands = commands;
|
||||||
size_t insert_length = *last_insert_len;
|
size_t insert_length = *last_insert_len;
|
||||||
|
@ -28,42 +26,42 @@ static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
|
||||||
const size_t random_heuristics_window_size =
|
const size_t random_heuristics_window_size =
|
||||||
LiteralSpreeLengthForSparseSearch(params);
|
LiteralSpreeLengthForSparseSearch(params);
|
||||||
size_t apply_random_heuristics = position + random_heuristics_window_size;
|
size_t apply_random_heuristics = position + random_heuristics_window_size;
|
||||||
|
const size_t gap = 0;
|
||||||
|
|
||||||
/* Minimum score to accept a backward reference. */
|
/* Minimum score to accept a backward reference. */
|
||||||
const score_t kMinScore = BROTLI_SCORE_BASE + 400;
|
const score_t kMinScore = BROTLI_SCORE_BASE + 100;
|
||||||
|
|
||||||
FN(Init)(m, hasher, ringbuffer, params, position, num_bytes, is_last);
|
FN(PrepareDistanceCache)(hasher, dist_cache);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
FN(StitchToPreviousBlock)(hasher, num_bytes, position,
|
|
||||||
ringbuffer, ringbuffer_mask);
|
|
||||||
|
|
||||||
while (position + FN(HashTypeLength)() < pos_end) {
|
while (position + FN(HashTypeLength)() < pos_end) {
|
||||||
size_t max_length = pos_end - position;
|
size_t max_length = pos_end - position;
|
||||||
size_t max_distance = BROTLI_MIN(size_t, position, max_backward_limit);
|
size_t max_distance = BROTLI_MIN(size_t, position, max_backward_limit);
|
||||||
HasherSearchResult sr;
|
HasherSearchResult sr;
|
||||||
sr.len = 0;
|
sr.len = 0;
|
||||||
sr.len_x_code = 0;
|
sr.len_code_delta = 0;
|
||||||
sr.distance = 0;
|
sr.distance = 0;
|
||||||
sr.score = kMinScore;
|
sr.score = kMinScore;
|
||||||
if (FN(FindLongestMatch)(hasher, ringbuffer, ringbuffer_mask, dist_cache,
|
FN(FindLongestMatch)(hasher, ¶ms->dictionary,
|
||||||
position, max_length, max_distance, &sr)) {
|
ringbuffer, ringbuffer_mask, dist_cache, position,
|
||||||
|
max_length, max_distance, gap,
|
||||||
|
params->dist.max_distance, &sr);
|
||||||
|
if (sr.score > kMinScore) {
|
||||||
/* Found a match. Let's look for something even better ahead. */
|
/* Found a match. Let's look for something even better ahead. */
|
||||||
int delayed_backward_references_in_row = 0;
|
int delayed_backward_references_in_row = 0;
|
||||||
--max_length;
|
--max_length;
|
||||||
for (;; --max_length) {
|
for (;; --max_length) {
|
||||||
const score_t cost_diff_lazy = 700;
|
const score_t cost_diff_lazy = 175;
|
||||||
BROTLI_BOOL is_match_found;
|
|
||||||
HasherSearchResult sr2;
|
HasherSearchResult sr2;
|
||||||
sr2.len = params->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH ?
|
sr2.len = params->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH ?
|
||||||
BROTLI_MIN(size_t, sr.len - 1, max_length) : 0;
|
BROTLI_MIN(size_t, sr.len - 1, max_length) : 0;
|
||||||
sr2.len_x_code = 0;
|
sr2.len_code_delta = 0;
|
||||||
sr2.distance = 0;
|
sr2.distance = 0;
|
||||||
sr2.score = kMinScore;
|
sr2.score = kMinScore;
|
||||||
max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit);
|
max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit);
|
||||||
is_match_found = FN(FindLongestMatch)(hasher, ringbuffer,
|
FN(FindLongestMatch)(hasher, ¶ms->dictionary,
|
||||||
ringbuffer_mask, dist_cache, position + 1, max_length, max_distance,
|
ringbuffer, ringbuffer_mask, dist_cache, position + 1, max_length,
|
||||||
&sr2);
|
max_distance, gap, params->dist.max_distance, &sr2);
|
||||||
if (is_match_found && sr2.score >= sr.score + cost_diff_lazy) {
|
if (sr2.score >= sr.score + cost_diff_lazy) {
|
||||||
/* Ok, let's just write one byte for now and start a match from the
|
/* Ok, let's just write one byte for now and start a match from the
|
||||||
next byte. */
|
next byte. */
|
||||||
++position;
|
++position;
|
||||||
|
@ -80,26 +78,36 @@ static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
|
||||||
position + 2 * sr.len + random_heuristics_window_size;
|
position + 2 * sr.len + random_heuristics_window_size;
|
||||||
max_distance = BROTLI_MIN(size_t, position, max_backward_limit);
|
max_distance = BROTLI_MIN(size_t, position, max_backward_limit);
|
||||||
{
|
{
|
||||||
/* The first 16 codes are special shortcodes,
|
/* The first 16 codes are special short-codes,
|
||||||
and the minimum offset is 1. */
|
and the minimum offset is 1. */
|
||||||
size_t distance_code =
|
size_t distance_code =
|
||||||
ComputeDistanceCode(sr.distance, max_distance, dist_cache);
|
ComputeDistanceCode(sr.distance, max_distance + gap, dist_cache);
|
||||||
if (sr.distance <= max_distance && distance_code > 0) {
|
if ((sr.distance <= (max_distance + gap)) && distance_code > 0) {
|
||||||
dist_cache[3] = dist_cache[2];
|
dist_cache[3] = dist_cache[2];
|
||||||
dist_cache[2] = dist_cache[1];
|
dist_cache[2] = dist_cache[1];
|
||||||
dist_cache[1] = dist_cache[0];
|
dist_cache[1] = dist_cache[0];
|
||||||
dist_cache[0] = (int)sr.distance;
|
dist_cache[0] = (int)sr.distance;
|
||||||
|
FN(PrepareDistanceCache)(hasher, dist_cache);
|
||||||
}
|
}
|
||||||
InitCommand(commands++, insert_length, sr.len, sr.len ^ sr.len_x_code,
|
InitCommand(commands++, ¶ms->dist, insert_length,
|
||||||
distance_code);
|
sr.len, sr.len_code_delta, distance_code);
|
||||||
}
|
}
|
||||||
*num_literals += insert_length;
|
*num_literals += insert_length;
|
||||||
insert_length = 0;
|
insert_length = 0;
|
||||||
/* Put the hash keys into the table, if there are enough bytes left.
|
/* Put the hash keys into the table, if there are enough bytes left.
|
||||||
Depending on the hasher implementation, it can push all positions
|
Depending on the hasher implementation, it can push all positions
|
||||||
in the given range or only a subset of them. */
|
in the given range or only a subset of them.
|
||||||
FN(StoreRange)(hasher, ringbuffer, ringbuffer_mask, position + 2,
|
Avoid hash poisoning with RLE data. */
|
||||||
BROTLI_MIN(size_t, position + sr.len, store_end));
|
{
|
||||||
|
size_t range_start = position + 2;
|
||||||
|
size_t range_end = BROTLI_MIN(size_t, position + sr.len, store_end);
|
||||||
|
if (sr.distance < (sr.len >> 2)) {
|
||||||
|
range_start = BROTLI_MIN(size_t, range_end, BROTLI_MAX(size_t,
|
||||||
|
range_start, position + sr.len - (sr.distance << 2)));
|
||||||
|
}
|
||||||
|
FN(StoreRange)(hasher, ringbuffer, ringbuffer_mask, range_start,
|
||||||
|
range_end);
|
||||||
|
}
|
||||||
position += sr.len;
|
position += sr.len;
|
||||||
} else {
|
} else {
|
||||||
++insert_length;
|
++insert_length;
|
||||||
|
@ -143,5 +151,3 @@ static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
|
||||||
*last_insert_len = insert_length;
|
*last_insert_len = insert_length;
|
||||||
*num_commands += (size_t)(commands - orig_commands);
|
*num_commands += (size_t)(commands - orig_commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef Hasher
|
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
#include "./bit_cost.h"
|
#include "./bit_cost.h"
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -9,20 +9,20 @@
|
||||||
#ifndef BROTLI_ENC_BIT_COST_H_
|
#ifndef BROTLI_ENC_BIT_COST_H_
|
||||||
#define BROTLI_ENC_BIT_COST_H_
|
#define BROTLI_ENC_BIT_COST_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static BROTLI_INLINE double ShannonEntropy(const uint32_t *population,
|
static BROTLI_INLINE double ShannonEntropy(
|
||||||
size_t size, size_t *total) {
|
const uint32_t* population, size_t size, size_t* total) {
|
||||||
size_t sum = 0;
|
size_t sum = 0;
|
||||||
double retval = 0;
|
double retval = 0;
|
||||||
const uint32_t *population_end = population + size;
|
const uint32_t* population_end = population + size;
|
||||||
size_t p;
|
size_t p;
|
||||||
if (size & 1) {
|
if (size & 1) {
|
||||||
goto odd_number_of_elements_left;
|
goto odd_number_of_elements_left;
|
||||||
|
@ -42,7 +42,7 @@ static BROTLI_INLINE double ShannonEntropy(const uint32_t *population,
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE double BitsEntropy(
|
static BROTLI_INLINE double BitsEntropy(
|
||||||
const uint32_t *population, size_t size) {
|
const uint32_t* population, size_t size) {
|
||||||
size_t sum;
|
size_t sum;
|
||||||
double retval = ShannonEntropy(population, size, &sum);
|
double retval = ShannonEntropy(population, size, &sum);
|
||||||
if (retval < sum) {
|
if (retval < sum) {
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
stream. */
|
stream. */
|
||||||
static void FN(BuildAndStoreEntropyCodes)(MemoryManager* m, BlockEncoder* self,
|
static void FN(BuildAndStoreEntropyCodes)(MemoryManager* m, BlockEncoder* self,
|
||||||
const HistogramType* histograms, const size_t histograms_size,
|
const HistogramType* histograms, const size_t histograms_size,
|
||||||
HuffmanTree* tree, size_t* storage_ix, uint8_t* storage) {
|
const size_t alphabet_size, HuffmanTree* tree,
|
||||||
const size_t alphabet_size = self->alphabet_size_;
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
const size_t table_size = histograms_size * alphabet_size;
|
const size_t table_size = histograms_size * self->histogram_length_;
|
||||||
self->depths_ = BROTLI_ALLOC(m, uint8_t, table_size);
|
self->depths_ = BROTLI_ALLOC(m, uint8_t, table_size);
|
||||||
self->bits_ = BROTLI_ALLOC(m, uint16_t, table_size);
|
self->bits_ = BROTLI_ALLOC(m, uint16_t, table_size);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
@ -23,9 +23,10 @@ static void FN(BuildAndStoreEntropyCodes)(MemoryManager* m, BlockEncoder* self,
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < histograms_size; ++i) {
|
for (i = 0; i < histograms_size; ++i) {
|
||||||
size_t ix = i * alphabet_size;
|
size_t ix = i * self->histogram_length_;
|
||||||
BuildAndStoreHuffmanTree(&histograms[i].data_[0], alphabet_size, tree,
|
BuildAndStoreHuffmanTree(&histograms[i].data_[0], self->histogram_length_,
|
||||||
&self->depths_[ix], &self->bits_[ix], storage_ix, storage);
|
alphabet_size, tree, &self->depths_[ix], &self->bits_[ix],
|
||||||
|
storage_ix, storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,15 @@
|
||||||
|
|
||||||
#include "./block_splitter.h"
|
#include "./block_splitter.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h> /* memcpy, memset */
|
#include <string.h> /* memcpy, memset */
|
||||||
|
|
||||||
|
#include "../common/platform.h"
|
||||||
#include "./bit_cost.h"
|
#include "./bit_cost.h"
|
||||||
#include "./cluster.h"
|
#include "./cluster.h"
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -74,11 +73,9 @@ static void CopyLiteralsToByteArray(const Command* cmds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE unsigned int MyRand(unsigned int* seed) {
|
static BROTLI_INLINE uint32_t MyRand(uint32_t* seed) {
|
||||||
|
/* Initial seed should be 7. In this case, loop length is (1 << 29). */
|
||||||
*seed *= 16807U;
|
*seed *= 16807U;
|
||||||
if (*seed == 0) {
|
|
||||||
*seed = 1;
|
|
||||||
}
|
|
||||||
return *seed;
|
return *seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +174,7 @@ void BrotliSplitBlock(MemoryManager* m,
|
||||||
for (i = 0; i < num_commands; ++i) {
|
for (i = 0; i < num_commands; ++i) {
|
||||||
const Command* cmd = &cmds[i];
|
const Command* cmd = &cmds[i];
|
||||||
if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) {
|
if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) {
|
||||||
distance_prefixes[j++] = cmd->dist_prefix_;
|
distance_prefixes[j++] = cmd->dist_prefix_ & 0x3FF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Create the block split on the array of distance prefixes. */
|
/* Create the block split on the array of distance prefixes. */
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
#ifndef BROTLI_ENC_BLOCK_SPLITTER_H_
|
#ifndef BROTLI_ENC_BLOCK_SPLITTER_H_
|
||||||
#define BROTLI_ENC_BLOCK_SPLITTER_H_
|
#define BROTLI_ENC_BLOCK_SPLITTER_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
|
|
@ -13,7 +13,7 @@ static void FN(InitialEntropyCodes)(const DataType* data, size_t length,
|
||||||
size_t stride,
|
size_t stride,
|
||||||
size_t num_histograms,
|
size_t num_histograms,
|
||||||
HistogramType* histograms) {
|
HistogramType* histograms) {
|
||||||
unsigned int seed = 7;
|
uint32_t seed = 7;
|
||||||
size_t block_length = length / num_histograms;
|
size_t block_length = length / num_histograms;
|
||||||
size_t i;
|
size_t i;
|
||||||
FN(ClearHistograms)(histograms, num_histograms);
|
FN(ClearHistograms)(histograms, num_histograms);
|
||||||
|
@ -29,14 +29,13 @@ static void FN(InitialEntropyCodes)(const DataType* data, size_t length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(RandomSample)(unsigned int* seed,
|
static void FN(RandomSample)(uint32_t* seed,
|
||||||
const DataType* data,
|
const DataType* data,
|
||||||
size_t length,
|
size_t length,
|
||||||
size_t stride,
|
size_t stride,
|
||||||
HistogramType* sample) {
|
HistogramType* sample) {
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
if (stride >= length) {
|
if (stride >= length) {
|
||||||
pos = 0;
|
|
||||||
stride = length;
|
stride = length;
|
||||||
} else {
|
} else {
|
||||||
pos = MyRand(seed) % (length - stride + 1);
|
pos = MyRand(seed) % (length - stride + 1);
|
||||||
|
@ -50,7 +49,7 @@ static void FN(RefineEntropyCodes)(const DataType* data, size_t length,
|
||||||
HistogramType* histograms) {
|
HistogramType* histograms) {
|
||||||
size_t iters =
|
size_t iters =
|
||||||
kIterMulForRefining * length / stride + kMinItersForRefining;
|
kIterMulForRefining * length / stride + kMinItersForRefining;
|
||||||
unsigned int seed = 7;
|
uint32_t seed = 7;
|
||||||
size_t iter;
|
size_t iter;
|
||||||
iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms;
|
iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms;
|
||||||
for (iter = 0; iter < iters; ++iter) {
|
for (iter = 0; iter < iters; ++iter) {
|
||||||
|
@ -61,7 +60,7 @@ static void FN(RefineEntropyCodes)(const DataType* data, size_t length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assigns a block id from the range [0, vec.size()) to each data element
|
/* Assigns a block id from the range [0, num_histograms) to each data element
|
||||||
in data[0..length) and fills in block_id[0..length) with the assigned values.
|
in data[0..length) and fills in block_id[0..length) with the assigned values.
|
||||||
Returns the number of blocks, i.e. one plus the number of block switches. */
|
Returns the number of blocks, i.e. one plus the number of block switches. */
|
||||||
static size_t FN(FindBlocks)(const DataType* data, const size_t length,
|
static size_t FN(FindBlocks)(const DataType* data, const size_t length,
|
||||||
|
@ -71,13 +70,13 @@ static size_t FN(FindBlocks)(const DataType* data, const size_t length,
|
||||||
double* insert_cost,
|
double* insert_cost,
|
||||||
double* cost,
|
double* cost,
|
||||||
uint8_t* switch_signal,
|
uint8_t* switch_signal,
|
||||||
uint8_t *block_id) {
|
uint8_t* block_id) {
|
||||||
const size_t data_size = FN(HistogramDataSize)();
|
const size_t data_size = FN(HistogramDataSize)();
|
||||||
const size_t bitmaplen = (num_histograms + 7) >> 3;
|
const size_t bitmaplen = (num_histograms + 7) >> 3;
|
||||||
size_t num_blocks = 1;
|
size_t num_blocks = 1;
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t j;
|
size_t j;
|
||||||
assert(num_histograms <= 256);
|
BROTLI_DCHECK(num_histograms <= 256);
|
||||||
if (num_histograms <= 1) {
|
if (num_histograms <= 1) {
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
block_id[i] = 0;
|
block_id[i] = 0;
|
||||||
|
@ -127,7 +126,7 @@ static size_t FN(FindBlocks)(const DataType* data, const size_t length,
|
||||||
if (cost[k] >= block_switch_cost) {
|
if (cost[k] >= block_switch_cost) {
|
||||||
const uint8_t mask = (uint8_t)(1u << (k & 7));
|
const uint8_t mask = (uint8_t)(1u << (k & 7));
|
||||||
cost[k] = block_switch_cost;
|
cost[k] = block_switch_cost;
|
||||||
assert((k >> 3) < bitmaplen);
|
BROTLI_DCHECK((k >> 3) < bitmaplen);
|
||||||
switch_signal[ix + (k >> 3)] |= mask;
|
switch_signal[ix + (k >> 3)] |= mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +137,7 @@ static size_t FN(FindBlocks)(const DataType* data, const size_t length,
|
||||||
uint8_t cur_id = block_id[byte_ix];
|
uint8_t cur_id = block_id[byte_ix];
|
||||||
while (byte_ix > 0) {
|
while (byte_ix > 0) {
|
||||||
const uint8_t mask = (uint8_t)(1u << (cur_id & 7));
|
const uint8_t mask = (uint8_t)(1u << (cur_id & 7));
|
||||||
assert(((size_t)cur_id >> 3) < bitmaplen);
|
BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmaplen);
|
||||||
--byte_ix;
|
--byte_ix;
|
||||||
ix -= bitmaplen;
|
ix -= bitmaplen;
|
||||||
if (switch_signal[ix + (cur_id >> 3)] & mask) {
|
if (switch_signal[ix + (cur_id >> 3)] & mask) {
|
||||||
|
@ -162,16 +161,16 @@ static size_t FN(RemapBlockIds)(uint8_t* block_ids, const size_t length,
|
||||||
new_id[i] = kInvalidId;
|
new_id[i] = kInvalidId;
|
||||||
}
|
}
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
assert(block_ids[i] < num_histograms);
|
BROTLI_DCHECK(block_ids[i] < num_histograms);
|
||||||
if (new_id[block_ids[i]] == kInvalidId) {
|
if (new_id[block_ids[i]] == kInvalidId) {
|
||||||
new_id[block_ids[i]] = next_id++;
|
new_id[block_ids[i]] = next_id++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
block_ids[i] = (uint8_t)new_id[block_ids[i]];
|
block_ids[i] = (uint8_t)new_id[block_ids[i]];
|
||||||
assert(block_ids[i] < num_histograms);
|
BROTLI_DCHECK(block_ids[i] < num_histograms);
|
||||||
}
|
}
|
||||||
assert(next_id <= num_histograms);
|
BROTLI_DCHECK(next_id <= num_histograms);
|
||||||
return next_id;
|
return next_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +213,6 @@ static void FN(ClusterBlocks)(MemoryManager* m,
|
||||||
size_t num_final_clusters;
|
size_t num_final_clusters;
|
||||||
static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX;
|
static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX;
|
||||||
uint32_t* new_index;
|
uint32_t* new_index;
|
||||||
uint8_t max_type = 0;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
uint32_t sizes[HISTOGRAMS_PER_BATCH] = { 0 };
|
uint32_t sizes[HISTOGRAMS_PER_BATCH] = { 0 };
|
||||||
uint32_t new_clusters[HISTOGRAMS_PER_BATCH] = { 0 };
|
uint32_t new_clusters[HISTOGRAMS_PER_BATCH] = { 0 };
|
||||||
|
@ -228,13 +226,13 @@ static void FN(ClusterBlocks)(MemoryManager* m,
|
||||||
{
|
{
|
||||||
size_t block_idx = 0;
|
size_t block_idx = 0;
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
assert(block_idx < num_blocks);
|
BROTLI_DCHECK(block_idx < num_blocks);
|
||||||
++block_lengths[block_idx];
|
++block_lengths[block_idx];
|
||||||
if (i + 1 == length || block_ids[i] != block_ids[i + 1]) {
|
if (i + 1 == length || block_ids[i] != block_ids[i + 1]) {
|
||||||
++block_idx;
|
++block_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(block_idx == num_blocks);
|
BROTLI_DCHECK(block_idx == num_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < num_blocks; i += HISTOGRAMS_PER_BATCH) {
|
for (i = 0; i < num_blocks; i += HISTOGRAMS_PER_BATCH) {
|
||||||
|
@ -270,8 +268,8 @@ static void FN(ClusterBlocks)(MemoryManager* m,
|
||||||
histogram_symbols[i + j] = (uint32_t)num_clusters + remap[symbols[j]];
|
histogram_symbols[i + j] = (uint32_t)num_clusters + remap[symbols[j]];
|
||||||
}
|
}
|
||||||
num_clusters += num_new_clusters;
|
num_clusters += num_new_clusters;
|
||||||
assert(num_clusters == cluster_size_size);
|
BROTLI_DCHECK(num_clusters == cluster_size_size);
|
||||||
assert(num_clusters == all_histograms_size);
|
BROTLI_DCHECK(num_clusters == all_histograms_size);
|
||||||
}
|
}
|
||||||
BROTLI_FREE(m, histograms);
|
BROTLI_FREE(m, histograms);
|
||||||
|
|
||||||
|
@ -337,6 +335,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
|
||||||
{
|
{
|
||||||
uint32_t cur_length = 0;
|
uint32_t cur_length = 0;
|
||||||
size_t block_idx = 0;
|
size_t block_idx = 0;
|
||||||
|
uint8_t max_type = 0;
|
||||||
for (i = 0; i < num_blocks; ++i) {
|
for (i = 0; i < num_blocks; ++i) {
|
||||||
cur_length += block_lengths[i];
|
cur_length += block_lengths[i];
|
||||||
if (i + 1 == num_blocks ||
|
if (i + 1 == num_blocks ||
|
||||||
|
@ -398,7 +397,7 @@ static void FN(SplitByteVector)(MemoryManager* m,
|
||||||
{
|
{
|
||||||
/* Find a good path through literals with the good entropy codes. */
|
/* Find a good path through literals with the good entropy codes. */
|
||||||
uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length);
|
uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length);
|
||||||
size_t num_blocks;
|
size_t num_blocks = 0;
|
||||||
const size_t bitmaplen = (num_histograms + 7) >> 3;
|
const size_t bitmaplen = (num_histograms + 7) >> 3;
|
||||||
double* insert_cost = BROTLI_ALLOC(m, double, data_size * num_histograms);
|
double* insert_cost = BROTLI_ALLOC(m, double, data_size * num_histograms);
|
||||||
double* cost = BROTLI_ALLOC(m, double, num_histograms);
|
double* cost = BROTLI_ALLOC(m, double, num_histograms);
|
||||||
|
|
|
@ -13,13 +13,14 @@
|
||||||
#include <string.h> /* memcpy, memset */
|
#include <string.h> /* memcpy, memset */
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/context.h"
|
||||||
#include "./context.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./entropy_encode.h"
|
#include "./entropy_encode.h"
|
||||||
#include "./entropy_encode_static.h"
|
#include "./entropy_encode_static.h"
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
|
#include "./histogram.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./write_bits.h"
|
#include "./write_bits.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -27,6 +28,11 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_HUFFMAN_TREE_SIZE (2 * BROTLI_NUM_COMMAND_SYMBOLS + 1)
|
#define MAX_HUFFMAN_TREE_SIZE (2 * BROTLI_NUM_COMMAND_SYMBOLS + 1)
|
||||||
|
/* The maximum size of Huffman dictionary for distances assuming that
|
||||||
|
NPOSTFIX = 0 and NDIRECT = 0. */
|
||||||
|
#define MAX_SIMPLE_DISTANCE_ALPHABET_SIZE \
|
||||||
|
BROTLI_DISTANCE_ALPHABET_SIZE(0, 0, BROTLI_LARGE_MAX_DISTANCE_BITS)
|
||||||
|
/* MAX_SIMPLE_DISTANCE_ALPHABET_SIZE == 140 */
|
||||||
|
|
||||||
/* Represents the range of values belonging to a prefix code:
|
/* Represents the range of values belonging to a prefix code:
|
||||||
[offset, offset + 2^nbits) */
|
[offset, offset + 2^nbits) */
|
||||||
|
@ -76,16 +82,16 @@ static BROTLI_INLINE size_t NextBlockTypeCode(
|
||||||
return type_code;
|
return type_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nibblesbits represents the 2 bits to encode MNIBBLES (0-3)
|
/* |nibblesbits| represents the 2 bits to encode MNIBBLES (0-3)
|
||||||
REQUIRES: length > 0
|
REQUIRES: length > 0
|
||||||
REQUIRES: length <= (1 << 24) */
|
REQUIRES: length <= (1 << 24) */
|
||||||
static void BrotliEncodeMlen(size_t length, uint64_t* bits,
|
static void BrotliEncodeMlen(size_t length, uint64_t* bits,
|
||||||
size_t* numbits, uint64_t* nibblesbits) {
|
size_t* numbits, uint64_t* nibblesbits) {
|
||||||
size_t lg = (length == 1) ? 1 : Log2FloorNonZero((uint32_t)(length - 1)) + 1;
|
size_t lg = (length == 1) ? 1 : Log2FloorNonZero((uint32_t)(length - 1)) + 1;
|
||||||
size_t mnibbles = (lg < 16 ? 16 : (lg + 3)) / 4;
|
size_t mnibbles = (lg < 16 ? 16 : (lg + 3)) / 4;
|
||||||
assert(length > 0);
|
BROTLI_DCHECK(length > 0);
|
||||||
assert(length <= (1 << 24));
|
BROTLI_DCHECK(length <= (1 << 24));
|
||||||
assert(lg <= 24);
|
BROTLI_DCHECK(lg <= 24);
|
||||||
*nibblesbits = mnibbles - 4;
|
*nibblesbits = mnibbles - 4;
|
||||||
*numbits = mnibbles * 4;
|
*numbits = mnibbles * 4;
|
||||||
*bits = length - 1;
|
*bits = length - 1;
|
||||||
|
@ -252,7 +258,7 @@ static void StoreSimpleHuffmanTree(const uint8_t* depths,
|
||||||
size_t symbols[4],
|
size_t symbols[4],
|
||||||
size_t num_symbols,
|
size_t num_symbols,
|
||||||
size_t max_bits,
|
size_t max_bits,
|
||||||
size_t *storage_ix, uint8_t *storage) {
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
/* value of 1 indicates a simple Huffman code */
|
/* value of 1 indicates a simple Huffman code */
|
||||||
BrotliWriteBits(2, 1, storage_ix, storage);
|
BrotliWriteBits(2, 1, storage_ix, storage);
|
||||||
BrotliWriteBits(2, num_symbols - 1, storage_ix, storage); /* NSYM - 1 */
|
BrotliWriteBits(2, num_symbols - 1, storage_ix, storage); /* NSYM - 1 */
|
||||||
|
@ -291,7 +297,7 @@ static void StoreSimpleHuffmanTree(const uint8_t* depths,
|
||||||
depths = symbol depths */
|
depths = symbol depths */
|
||||||
void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
||||||
HuffmanTree* tree,
|
HuffmanTree* tree,
|
||||||
size_t *storage_ix, uint8_t *storage) {
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
/* Write the Huffman tree into the brotli-representation.
|
/* Write the Huffman tree into the brotli-representation.
|
||||||
The command alphabet is the largest, so this allocation will fit all
|
The command alphabet is the largest, so this allocation will fit all
|
||||||
alphabets. */
|
alphabets. */
|
||||||
|
@ -305,7 +311,7 @@ void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
||||||
int num_codes = 0;
|
int num_codes = 0;
|
||||||
size_t code = 0;
|
size_t code = 0;
|
||||||
|
|
||||||
assert(num <= BROTLI_NUM_COMMAND_SYMBOLS);
|
BROTLI_DCHECK(num <= BROTLI_NUM_COMMAND_SYMBOLS);
|
||||||
|
|
||||||
BrotliWriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree,
|
BrotliWriteHuffmanTree(depths, num, &huffman_tree_size, huffman_tree,
|
||||||
huffman_tree_extra_bits);
|
huffman_tree_extra_bits);
|
||||||
|
@ -343,7 +349,7 @@ void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
||||||
code_length_bitdepth[code] = 0;
|
code_length_bitdepth[code] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store the real huffman tree now. */
|
/* Store the real Huffman tree now. */
|
||||||
BrotliStoreHuffmanTreeToBitMask(huffman_tree_size,
|
BrotliStoreHuffmanTreeToBitMask(huffman_tree_size,
|
||||||
huffman_tree,
|
huffman_tree,
|
||||||
huffman_tree_extra_bits,
|
huffman_tree_extra_bits,
|
||||||
|
@ -354,8 +360,9 @@ void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
||||||
|
|
||||||
/* Builds a Huffman tree from histogram[0:length] into depth[0:length] and
|
/* Builds a Huffman tree from histogram[0:length] into depth[0:length] and
|
||||||
bits[0:length] and stores the encoded tree to the bit stream. */
|
bits[0:length] and stores the encoded tree to the bit stream. */
|
||||||
static void BuildAndStoreHuffmanTree(const uint32_t *histogram,
|
static void BuildAndStoreHuffmanTree(const uint32_t* histogram,
|
||||||
const size_t length,
|
const size_t histogram_length,
|
||||||
|
const size_t alphabet_size,
|
||||||
HuffmanTree* tree,
|
HuffmanTree* tree,
|
||||||
uint8_t* depth,
|
uint8_t* depth,
|
||||||
uint16_t* bits,
|
uint16_t* bits,
|
||||||
|
@ -365,7 +372,7 @@ static void BuildAndStoreHuffmanTree(const uint32_t *histogram,
|
||||||
size_t s4[4] = { 0 };
|
size_t s4[4] = { 0 };
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t max_bits = 0;
|
size_t max_bits = 0;
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < histogram_length; i++) {
|
||||||
if (histogram[i]) {
|
if (histogram[i]) {
|
||||||
if (count < 4) {
|
if (count < 4) {
|
||||||
s4[count] = i;
|
s4[count] = i;
|
||||||
|
@ -377,7 +384,7 @@ static void BuildAndStoreHuffmanTree(const uint32_t *histogram,
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
size_t max_bits_counter = length - 1;
|
size_t max_bits_counter = alphabet_size - 1;
|
||||||
while (max_bits_counter) {
|
while (max_bits_counter) {
|
||||||
max_bits_counter >>= 1;
|
max_bits_counter >>= 1;
|
||||||
++max_bits;
|
++max_bits;
|
||||||
|
@ -392,14 +399,14 @@ static void BuildAndStoreHuffmanTree(const uint32_t *histogram,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(depth, 0, length * sizeof(depth[0]));
|
memset(depth, 0, histogram_length * sizeof(depth[0]));
|
||||||
BrotliCreateHuffmanTree(histogram, length, 15, tree, depth);
|
BrotliCreateHuffmanTree(histogram, histogram_length, 15, tree, depth);
|
||||||
BrotliConvertBitDepthsToSymbols(depth, length, bits);
|
BrotliConvertBitDepthsToSymbols(depth, histogram_length, bits);
|
||||||
|
|
||||||
if (count <= 4) {
|
if (count <= 4) {
|
||||||
StoreSimpleHuffmanTree(depth, s4, count, max_bits, storage_ix, storage);
|
StoreSimpleHuffmanTree(depth, s4, count, max_bits, storage_ix, storage);
|
||||||
} else {
|
} else {
|
||||||
BrotliStoreHuffmanTree(depth, length, tree, storage_ix, storage);
|
BrotliStoreHuffmanTree(depth, histogram_length, tree, storage_ix, storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +457,7 @@ void BrotliBuildAndStoreHuffmanTreeFast(MemoryManager* m,
|
||||||
for (l = length; l != 0;) {
|
for (l = length; l != 0;) {
|
||||||
--l;
|
--l;
|
||||||
if (histogram[l]) {
|
if (histogram[l]) {
|
||||||
if (PREDICT_TRUE(histogram[l] >= count_limit)) {
|
if (BROTLI_PREDICT_TRUE(histogram[l] >= count_limit)) {
|
||||||
InitHuffmanTree(node, histogram[l], -1, (int16_t)l);
|
InitHuffmanTree(node, histogram[l], -1, (int16_t)l);
|
||||||
} else {
|
} else {
|
||||||
InitHuffmanTree(node, count_limit, -1, (int16_t)l);
|
InitHuffmanTree(node, count_limit, -1, (int16_t)l);
|
||||||
|
@ -548,7 +555,7 @@ void BrotliBuildAndStoreHuffmanTreeFast(MemoryManager* m,
|
||||||
/* Complex Huffman Tree */
|
/* Complex Huffman Tree */
|
||||||
StoreStaticCodeLengthCode(storage_ix, storage);
|
StoreStaticCodeLengthCode(storage_ix, storage);
|
||||||
|
|
||||||
/* Actual rle coding. */
|
/* Actual RLE coding. */
|
||||||
for (i = 0; i < length;) {
|
for (i = 0; i < length;) {
|
||||||
const uint8_t value = depth[i];
|
const uint8_t value = depth[i];
|
||||||
size_t reps = 1;
|
size_t reps = 1;
|
||||||
|
@ -613,7 +620,7 @@ static void MoveToFrontTransform(const uint32_t* BROTLI_RESTRICT v_in,
|
||||||
for (i = 1; i < v_size; ++i) {
|
for (i = 1; i < v_size; ++i) {
|
||||||
if (v_in[i] > max_value) max_value = v_in[i];
|
if (v_in[i] > max_value) max_value = v_in[i];
|
||||||
}
|
}
|
||||||
assert(max_value < 256u);
|
BROTLI_DCHECK(max_value < 256u);
|
||||||
for (i = 0; i <= max_value; ++i) {
|
for (i = 0; i <= max_value; ++i) {
|
||||||
mtf[i] = (uint8_t)i;
|
mtf[i] = (uint8_t)i;
|
||||||
}
|
}
|
||||||
|
@ -621,7 +628,7 @@ static void MoveToFrontTransform(const uint32_t* BROTLI_RESTRICT v_in,
|
||||||
size_t mtf_size = max_value + 1;
|
size_t mtf_size = max_value + 1;
|
||||||
for (i = 0; i < v_size; ++i) {
|
for (i = 0; i < v_size; ++i) {
|
||||||
size_t index = IndexOf(mtf, mtf_size, (uint8_t)v_in[i]);
|
size_t index = IndexOf(mtf, mtf_size, (uint8_t)v_in[i]);
|
||||||
assert(index < mtf_size);
|
BROTLI_DCHECK(index < mtf_size);
|
||||||
v_out[i] = (uint32_t)index;
|
v_out[i] = (uint32_t)index;
|
||||||
MoveToFront(mtf, index);
|
MoveToFront(mtf, index);
|
||||||
}
|
}
|
||||||
|
@ -653,7 +660,7 @@ static void RunLengthCodeZeros(const size_t in_size,
|
||||||
*max_run_length_prefix = max_prefix;
|
*max_run_length_prefix = max_prefix;
|
||||||
*out_size = 0;
|
*out_size = 0;
|
||||||
for (i = 0; i < in_size;) {
|
for (i = 0; i < in_size;) {
|
||||||
assert(*out_size <= i);
|
BROTLI_DCHECK(*out_size <= i);
|
||||||
if (v[i] != 0) {
|
if (v[i] != 0) {
|
||||||
v[*out_size] = v[i] + *max_run_length_prefix;
|
v[*out_size] = v[i] + *max_run_length_prefix;
|
||||||
++i;
|
++i;
|
||||||
|
@ -723,6 +730,7 @@ static void EncodeContextMap(MemoryManager* m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuildAndStoreHuffmanTree(histogram, num_clusters + max_run_length_prefix,
|
BuildAndStoreHuffmanTree(histogram, num_clusters + max_run_length_prefix,
|
||||||
|
num_clusters + max_run_length_prefix,
|
||||||
tree, depths, bits, storage_ix, storage);
|
tree, depths, bits, storage_ix, storage);
|
||||||
for (i = 0; i < num_rle_symbols; ++i) {
|
for (i = 0; i < num_rle_symbols; ++i) {
|
||||||
const uint32_t rle_symbol = rle_symbols[i] & kSymbolMask;
|
const uint32_t rle_symbol = rle_symbols[i] & kSymbolMask;
|
||||||
|
@ -782,10 +790,11 @@ static void BuildAndStoreBlockSplitCode(const uint8_t* types,
|
||||||
}
|
}
|
||||||
StoreVarLenUint8(num_types - 1, storage_ix, storage);
|
StoreVarLenUint8(num_types - 1, storage_ix, storage);
|
||||||
if (num_types > 1) { /* TODO: else? could StoreBlockSwitch occur? */
|
if (num_types > 1) { /* TODO: else? could StoreBlockSwitch occur? */
|
||||||
BuildAndStoreHuffmanTree(&type_histo[0], num_types + 2, tree,
|
BuildAndStoreHuffmanTree(&type_histo[0], num_types + 2, num_types + 2, tree,
|
||||||
&code->type_depths[0], &code->type_bits[0],
|
&code->type_depths[0], &code->type_bits[0],
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
BuildAndStoreHuffmanTree(&length_histo[0], BROTLI_NUM_BLOCK_LEN_SYMBOLS,
|
BuildAndStoreHuffmanTree(&length_histo[0], BROTLI_NUM_BLOCK_LEN_SYMBOLS,
|
||||||
|
BROTLI_NUM_BLOCK_LEN_SYMBOLS,
|
||||||
tree, &code->length_depths[0],
|
tree, &code->length_depths[0],
|
||||||
&code->length_bits[0], storage_ix, storage);
|
&code->length_bits[0], storage_ix, storage);
|
||||||
StoreBlockSwitch(code, lengths[0], types[0], 1, storage_ix, storage);
|
StoreBlockSwitch(code, lengths[0], types[0], 1, storage_ix, storage);
|
||||||
|
@ -816,8 +825,8 @@ static void StoreTrivialContextMap(size_t num_types,
|
||||||
for (i = context_bits; i < alphabet_size; ++i) {
|
for (i = context_bits; i < alphabet_size; ++i) {
|
||||||
histogram[i] = 1;
|
histogram[i] = 1;
|
||||||
}
|
}
|
||||||
BuildAndStoreHuffmanTree(histogram, alphabet_size, tree,
|
BuildAndStoreHuffmanTree(histogram, alphabet_size, alphabet_size,
|
||||||
depths, bits, storage_ix, storage);
|
tree, depths, bits, storage_ix, storage);
|
||||||
for (i = 0; i < num_types; ++i) {
|
for (i = 0; i < num_types; ++i) {
|
||||||
size_t code = (i == 0 ? 0 : i + context_bits - 1);
|
size_t code = (i == 0 ? 0 : i + context_bits - 1);
|
||||||
BrotliWriteBits(depths[code], bits[code], storage_ix, storage);
|
BrotliWriteBits(depths[code], bits[code], storage_ix, storage);
|
||||||
|
@ -832,7 +841,7 @@ static void StoreTrivialContextMap(size_t num_types,
|
||||||
|
|
||||||
/* Manages the encoding of one block category (literal, command or distance). */
|
/* Manages the encoding of one block category (literal, command or distance). */
|
||||||
typedef struct BlockEncoder {
|
typedef struct BlockEncoder {
|
||||||
size_t alphabet_size_;
|
size_t histogram_length_;
|
||||||
size_t num_block_types_;
|
size_t num_block_types_;
|
||||||
const uint8_t* block_types_; /* Not owned. */
|
const uint8_t* block_types_; /* Not owned. */
|
||||||
const uint32_t* block_lengths_; /* Not owned. */
|
const uint32_t* block_lengths_; /* Not owned. */
|
||||||
|
@ -845,10 +854,10 @@ typedef struct BlockEncoder {
|
||||||
uint16_t* bits_;
|
uint16_t* bits_;
|
||||||
} BlockEncoder;
|
} BlockEncoder;
|
||||||
|
|
||||||
static void InitBlockEncoder(BlockEncoder* self, size_t alphabet_size,
|
static void InitBlockEncoder(BlockEncoder* self, size_t histogram_length,
|
||||||
size_t num_block_types, const uint8_t* block_types,
|
size_t num_block_types, const uint8_t* block_types,
|
||||||
const uint32_t* block_lengths, const size_t num_blocks) {
|
const uint32_t* block_lengths, const size_t num_blocks) {
|
||||||
self->alphabet_size_ = alphabet_size;
|
self->histogram_length_ = histogram_length;
|
||||||
self->num_block_types_ = num_block_types;
|
self->num_block_types_ = num_block_types;
|
||||||
self->block_types_ = block_types;
|
self->block_types_ = block_types;
|
||||||
self->block_lengths_ = block_lengths;
|
self->block_lengths_ = block_lengths;
|
||||||
|
@ -884,7 +893,7 @@ static void StoreSymbol(BlockEncoder* self, size_t symbol, size_t* storage_ix,
|
||||||
uint32_t block_len = self->block_lengths_[block_ix];
|
uint32_t block_len = self->block_lengths_[block_ix];
|
||||||
uint8_t block_type = self->block_types_[block_ix];
|
uint8_t block_type = self->block_types_[block_ix];
|
||||||
self->block_len_ = block_len;
|
self->block_len_ = block_len;
|
||||||
self->entropy_ix_ = block_type * self->alphabet_size_;
|
self->entropy_ix_ = block_type * self->histogram_length_;
|
||||||
StoreBlockSwitch(&self->block_split_code_, block_len, block_type, 0,
|
StoreBlockSwitch(&self->block_split_code_, block_len, block_type, 0,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
@ -913,7 +922,7 @@ static void StoreSymbolWithContext(BlockEncoder* self, size_t symbol,
|
||||||
--self->block_len_;
|
--self->block_len_;
|
||||||
{
|
{
|
||||||
size_t histo_ix = context_map[self->entropy_ix_ + context];
|
size_t histo_ix = context_map[self->entropy_ix_ + context];
|
||||||
size_t ix = histo_ix * self->alphabet_size_ + symbol;
|
size_t ix = histo_ix * self->histogram_length_ + symbol;
|
||||||
BrotliWriteBits(self->depths_[ix], self->bits_[ix], storage_ix, storage);
|
BrotliWriteBits(self->depths_[ix], self->bits_[ix], storage_ix, storage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -939,42 +948,38 @@ static void JumpToByteBoundary(size_t* storage_ix, uint8_t* storage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliStoreMetaBlock(MemoryManager* m,
|
void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
|
||||||
size_t start_pos,
|
uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last,
|
||||||
size_t length,
|
const BrotliEncoderParams* params, ContextType literal_context_mode,
|
||||||
size_t mask,
|
const Command* commands, size_t n_commands, const MetaBlockSplit* mb,
|
||||||
uint8_t prev_byte,
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
uint8_t prev_byte2,
|
|
||||||
BROTLI_BOOL is_last,
|
|
||||||
uint32_t num_direct_distance_codes,
|
|
||||||
uint32_t distance_postfix_bits,
|
|
||||||
ContextType literal_context_mode,
|
|
||||||
const Command *commands,
|
|
||||||
size_t n_commands,
|
|
||||||
const MetaBlockSplit* mb,
|
|
||||||
size_t *storage_ix,
|
|
||||||
uint8_t *storage) {
|
|
||||||
size_t pos = start_pos;
|
size_t pos = start_pos;
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t num_distance_codes =
|
uint32_t num_distance_symbols = params->dist.alphabet_size;
|
||||||
BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_distance_codes +
|
uint32_t num_effective_distance_symbols = num_distance_symbols;
|
||||||
(48u << distance_postfix_bits);
|
|
||||||
HuffmanTree* tree;
|
HuffmanTree* tree;
|
||||||
|
ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
|
||||||
BlockEncoder literal_enc;
|
BlockEncoder literal_enc;
|
||||||
BlockEncoder command_enc;
|
BlockEncoder command_enc;
|
||||||
BlockEncoder distance_enc;
|
BlockEncoder distance_enc;
|
||||||
|
const BrotliDistanceParams* dist = ¶ms->dist;
|
||||||
|
if (params->large_window &&
|
||||||
|
num_effective_distance_symbols > BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS) {
|
||||||
|
num_effective_distance_symbols = BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS;
|
||||||
|
}
|
||||||
|
|
||||||
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
|
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
|
||||||
|
|
||||||
tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
|
tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
InitBlockEncoder(&literal_enc, 256, mb->literal_split.num_types,
|
InitBlockEncoder(&literal_enc, BROTLI_NUM_LITERAL_SYMBOLS,
|
||||||
mb->literal_split.types, mb->literal_split.lengths,
|
mb->literal_split.num_types, mb->literal_split.types,
|
||||||
mb->literal_split.num_blocks);
|
mb->literal_split.lengths, mb->literal_split.num_blocks);
|
||||||
InitBlockEncoder(&command_enc, BROTLI_NUM_COMMAND_SYMBOLS,
|
InitBlockEncoder(&command_enc, BROTLI_NUM_COMMAND_SYMBOLS,
|
||||||
mb->command_split.num_types, mb->command_split.types,
|
mb->command_split.num_types, mb->command_split.types,
|
||||||
mb->command_split.lengths, mb->command_split.num_blocks);
|
mb->command_split.lengths, mb->command_split.num_blocks);
|
||||||
InitBlockEncoder(&distance_enc, num_distance_codes,
|
InitBlockEncoder(&distance_enc, num_effective_distance_symbols,
|
||||||
mb->distance_split.num_types, mb->distance_split.types,
|
mb->distance_split.num_types, mb->distance_split.types,
|
||||||
mb->distance_split.lengths, mb->distance_split.num_blocks);
|
mb->distance_split.lengths, mb->distance_split.num_blocks);
|
||||||
|
|
||||||
|
@ -983,9 +988,10 @@ void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
BuildAndStoreBlockSwitchEntropyCodes(
|
BuildAndStoreBlockSwitchEntropyCodes(
|
||||||
&distance_enc, tree, storage_ix, storage);
|
&distance_enc, tree, storage_ix, storage);
|
||||||
|
|
||||||
BrotliWriteBits(2, distance_postfix_bits, storage_ix, storage);
|
BrotliWriteBits(2, dist->distance_postfix_bits, storage_ix, storage);
|
||||||
BrotliWriteBits(4, num_direct_distance_codes >> distance_postfix_bits,
|
BrotliWriteBits(
|
||||||
storage_ix, storage);
|
4, dist->num_direct_distance_codes >> dist->distance_postfix_bits,
|
||||||
|
storage_ix, storage);
|
||||||
for (i = 0; i < mb->literal_split.num_types; ++i) {
|
for (i = 0; i < mb->literal_split.num_types; ++i) {
|
||||||
BrotliWriteBits(2, literal_context_mode, storage_ix, storage);
|
BrotliWriteBits(2, literal_context_mode, storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
@ -1011,13 +1017,16 @@ void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildAndStoreEntropyCodesLiteral(m, &literal_enc, mb->literal_histograms,
|
BuildAndStoreEntropyCodesLiteral(m, &literal_enc, mb->literal_histograms,
|
||||||
mb->literal_histograms_size, tree, storage_ix, storage);
|
mb->literal_histograms_size, BROTLI_NUM_LITERAL_SYMBOLS, tree,
|
||||||
|
storage_ix, storage);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BuildAndStoreEntropyCodesCommand(m, &command_enc, mb->command_histograms,
|
BuildAndStoreEntropyCodesCommand(m, &command_enc, mb->command_histograms,
|
||||||
mb->command_histograms_size, tree, storage_ix, storage);
|
mb->command_histograms_size, BROTLI_NUM_COMMAND_SYMBOLS, tree,
|
||||||
|
storage_ix, storage);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BuildAndStoreEntropyCodesDistance(m, &distance_enc, mb->distance_histograms,
|
BuildAndStoreEntropyCodesDistance(m, &distance_enc, mb->distance_histograms,
|
||||||
mb->distance_histograms_size, tree, storage_ix, storage);
|
mb->distance_histograms_size, num_distance_symbols, tree,
|
||||||
|
storage_ix, storage);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BROTLI_FREE(m, tree);
|
BROTLI_FREE(m, tree);
|
||||||
|
|
||||||
|
@ -1035,7 +1044,8 @@ void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
} else {
|
} else {
|
||||||
size_t j;
|
size_t j;
|
||||||
for (j = cmd.insert_len_; j != 0; --j) {
|
for (j = cmd.insert_len_; j != 0; --j) {
|
||||||
size_t context = Context(prev_byte, prev_byte2, literal_context_mode);
|
size_t context =
|
||||||
|
BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut);
|
||||||
uint8_t literal = input[pos & mask];
|
uint8_t literal = input[pos & mask];
|
||||||
StoreSymbolWithContext(&literal_enc, literal, context,
|
StoreSymbolWithContext(&literal_enc, literal, context,
|
||||||
mb->literal_context_map, storage_ix, storage,
|
mb->literal_context_map, storage_ix, storage,
|
||||||
|
@ -1050,9 +1060,9 @@ void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
prev_byte2 = input[(pos - 2) & mask];
|
prev_byte2 = input[(pos - 2) & mask];
|
||||||
prev_byte = input[(pos - 1) & mask];
|
prev_byte = input[(pos - 1) & mask];
|
||||||
if (cmd.cmd_prefix_ >= 128) {
|
if (cmd.cmd_prefix_ >= 128) {
|
||||||
size_t dist_code = cmd.dist_prefix_;
|
size_t dist_code = cmd.dist_prefix_ & 0x3FF;
|
||||||
uint32_t distnumextra = cmd.dist_extra_ >> 24;
|
uint32_t distnumextra = cmd.dist_prefix_ >> 10;
|
||||||
uint64_t distextra = cmd.dist_extra_ & 0xffffff;
|
uint64_t distextra = cmd.dist_extra_;
|
||||||
if (mb->distance_context_map_size == 0) {
|
if (mb->distance_context_map_size == 0) {
|
||||||
StoreSymbol(&distance_enc, dist_code, storage_ix, storage);
|
StoreSymbol(&distance_enc, dist_code, storage_ix, storage);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1076,7 +1086,7 @@ void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
static void BuildHistograms(const uint8_t* input,
|
static void BuildHistograms(const uint8_t* input,
|
||||||
size_t start_pos,
|
size_t start_pos,
|
||||||
size_t mask,
|
size_t mask,
|
||||||
const Command *commands,
|
const Command* commands,
|
||||||
size_t n_commands,
|
size_t n_commands,
|
||||||
HistogramLiteral* lit_histo,
|
HistogramLiteral* lit_histo,
|
||||||
HistogramCommand* cmd_histo,
|
HistogramCommand* cmd_histo,
|
||||||
|
@ -1093,7 +1103,7 @@ static void BuildHistograms(const uint8_t* input,
|
||||||
}
|
}
|
||||||
pos += CommandCopyLen(&cmd);
|
pos += CommandCopyLen(&cmd);
|
||||||
if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) {
|
if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) {
|
||||||
HistogramAddDistance(dist_histo, cmd.dist_prefix_);
|
HistogramAddDistance(dist_histo, cmd.dist_prefix_ & 0x3FF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1101,7 +1111,7 @@ static void BuildHistograms(const uint8_t* input,
|
||||||
static void StoreDataWithHuffmanCodes(const uint8_t* input,
|
static void StoreDataWithHuffmanCodes(const uint8_t* input,
|
||||||
size_t start_pos,
|
size_t start_pos,
|
||||||
size_t mask,
|
size_t mask,
|
||||||
const Command *commands,
|
const Command* commands,
|
||||||
size_t n_commands,
|
size_t n_commands,
|
||||||
const uint8_t* lit_depth,
|
const uint8_t* lit_depth,
|
||||||
const uint16_t* lit_bits,
|
const uint16_t* lit_bits,
|
||||||
|
@ -1128,9 +1138,9 @@ static void StoreDataWithHuffmanCodes(const uint8_t* input,
|
||||||
}
|
}
|
||||||
pos += CommandCopyLen(&cmd);
|
pos += CommandCopyLen(&cmd);
|
||||||
if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) {
|
if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) {
|
||||||
const size_t dist_code = cmd.dist_prefix_;
|
const size_t dist_code = cmd.dist_prefix_ & 0x3FF;
|
||||||
const uint32_t distnumextra = cmd.dist_extra_ >> 24;
|
const uint32_t distnumextra = cmd.dist_prefix_ >> 10;
|
||||||
const uint32_t distextra = cmd.dist_extra_ & 0xffffff;
|
const uint32_t distextra = cmd.dist_extra_;
|
||||||
BrotliWriteBits(dist_depth[dist_code], dist_bits[dist_code],
|
BrotliWriteBits(dist_depth[dist_code], dist_bits[dist_code],
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
BrotliWriteBits(distnumextra, distextra, storage_ix, storage);
|
BrotliWriteBits(distnumextra, distextra, storage_ix, storage);
|
||||||
|
@ -1139,25 +1149,21 @@ static void StoreDataWithHuffmanCodes(const uint8_t* input,
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliStoreMetaBlockTrivial(MemoryManager* m,
|
void BrotliStoreMetaBlockTrivial(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
|
||||||
size_t start_pos,
|
BROTLI_BOOL is_last, const BrotliEncoderParams* params,
|
||||||
size_t length,
|
const Command* commands, size_t n_commands,
|
||||||
size_t mask,
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
BROTLI_BOOL is_last,
|
|
||||||
const Command *commands,
|
|
||||||
size_t n_commands,
|
|
||||||
size_t *storage_ix,
|
|
||||||
uint8_t *storage) {
|
|
||||||
HistogramLiteral lit_histo;
|
HistogramLiteral lit_histo;
|
||||||
HistogramCommand cmd_histo;
|
HistogramCommand cmd_histo;
|
||||||
HistogramDistance dist_histo;
|
HistogramDistance dist_histo;
|
||||||
uint8_t lit_depth[256];
|
uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||||
uint16_t lit_bits[256];
|
uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||||
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
|
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
|
uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
uint8_t dist_depth[64];
|
uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
|
||||||
uint16_t dist_bits[64];
|
uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
|
||||||
HuffmanTree* tree;
|
HuffmanTree* tree;
|
||||||
|
uint32_t num_distance_symbols = params->dist.alphabet_size;
|
||||||
|
|
||||||
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
|
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
|
||||||
|
|
||||||
|
@ -1172,13 +1178,16 @@ void BrotliStoreMetaBlockTrivial(MemoryManager* m,
|
||||||
|
|
||||||
tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
|
tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BuildAndStoreHuffmanTree(lit_histo.data_, 256, tree,
|
BuildAndStoreHuffmanTree(lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS,
|
||||||
|
BROTLI_NUM_LITERAL_SYMBOLS, tree,
|
||||||
lit_depth, lit_bits,
|
lit_depth, lit_bits,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
BuildAndStoreHuffmanTree(cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS, tree,
|
BuildAndStoreHuffmanTree(cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS,
|
||||||
|
BROTLI_NUM_COMMAND_SYMBOLS, tree,
|
||||||
cmd_depth, cmd_bits,
|
cmd_depth, cmd_bits,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
BuildAndStoreHuffmanTree(dist_histo.data_, 64, tree,
|
BuildAndStoreHuffmanTree(dist_histo.data_, MAX_SIMPLE_DISTANCE_ALPHABET_SIZE,
|
||||||
|
num_distance_symbols, tree,
|
||||||
dist_depth, dist_bits,
|
dist_depth, dist_bits,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
BROTLI_FREE(m, tree);
|
BROTLI_FREE(m, tree);
|
||||||
|
@ -1193,15 +1202,14 @@ void BrotliStoreMetaBlockTrivial(MemoryManager* m,
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliStoreMetaBlockFast(MemoryManager* m,
|
void BrotliStoreMetaBlockFast(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
|
||||||
size_t start_pos,
|
BROTLI_BOOL is_last, const BrotliEncoderParams* params,
|
||||||
size_t length,
|
const Command* commands, size_t n_commands,
|
||||||
size_t mask,
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
BROTLI_BOOL is_last,
|
uint32_t num_distance_symbols = params->dist.alphabet_size;
|
||||||
const Command *commands,
|
uint32_t distance_alphabet_bits =
|
||||||
size_t n_commands,
|
Log2FloorNonZero(num_distance_symbols - 1) + 1;
|
||||||
size_t *storage_ix,
|
|
||||||
uint8_t *storage) {
|
|
||||||
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
|
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
|
||||||
|
|
||||||
BrotliWriteBits(13, 0, storage_ix, storage);
|
BrotliWriteBits(13, 0, storage_ix, storage);
|
||||||
|
@ -1245,8 +1253,8 @@ void BrotliStoreMetaBlockFast(MemoryManager* m,
|
||||||
uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
|
uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
|
||||||
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
|
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
|
uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
uint8_t dist_depth[64];
|
uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
|
||||||
uint16_t dist_bits[64];
|
uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
|
||||||
HistogramClearLiteral(&lit_histo);
|
HistogramClearLiteral(&lit_histo);
|
||||||
HistogramClearCommand(&cmd_histo);
|
HistogramClearCommand(&cmd_histo);
|
||||||
HistogramClearDistance(&dist_histo);
|
HistogramClearDistance(&dist_histo);
|
||||||
|
@ -1266,7 +1274,8 @@ void BrotliStoreMetaBlockFast(MemoryManager* m,
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BrotliBuildAndStoreHuffmanTreeFast(m, dist_histo.data_,
|
BrotliBuildAndStoreHuffmanTreeFast(m, dist_histo.data_,
|
||||||
dist_histo.total_count_,
|
dist_histo.total_count_,
|
||||||
/* max_bits = */ 6,
|
/* max_bits = */
|
||||||
|
distance_alphabet_bits,
|
||||||
dist_depth, dist_bits,
|
dist_depth, dist_bits,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
@ -1285,11 +1294,11 @@ void BrotliStoreMetaBlockFast(MemoryManager* m,
|
||||||
/* This is for storing uncompressed blocks (simple raw storage of
|
/* This is for storing uncompressed blocks (simple raw storage of
|
||||||
bytes-as-bytes). */
|
bytes-as-bytes). */
|
||||||
void BrotliStoreUncompressedMetaBlock(BROTLI_BOOL is_final_block,
|
void BrotliStoreUncompressedMetaBlock(BROTLI_BOOL is_final_block,
|
||||||
const uint8_t * BROTLI_RESTRICT input,
|
const uint8_t* BROTLI_RESTRICT input,
|
||||||
size_t position, size_t mask,
|
size_t position, size_t mask,
|
||||||
size_t len,
|
size_t len,
|
||||||
size_t * BROTLI_RESTRICT storage_ix,
|
size_t* BROTLI_RESTRICT storage_ix,
|
||||||
uint8_t * BROTLI_RESTRICT storage) {
|
uint8_t* BROTLI_RESTRICT storage) {
|
||||||
size_t masked_pos = position & mask;
|
size_t masked_pos = position & mask;
|
||||||
BrotliStoreUncompressedMetaBlockHeader(len, storage_ix, storage);
|
BrotliStoreUncompressedMetaBlockHeader(len, storage_ix, storage);
|
||||||
JumpToByteBoundary(storage_ix, storage);
|
JumpToByteBoundary(storage_ix, storage);
|
||||||
|
@ -1317,18 +1326,6 @@ void BrotliStoreUncompressedMetaBlock(BROTLI_BOOL is_final_block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliStoreSyncMetaBlock(size_t* BROTLI_RESTRICT storage_ix,
|
|
||||||
uint8_t* BROTLI_RESTRICT storage) {
|
|
||||||
/* Empty metadata meta-block bit pattern:
|
|
||||||
1 bit: is_last (0)
|
|
||||||
2 bits: num nibbles (3)
|
|
||||||
1 bit: reserved (0)
|
|
||||||
2 bits: metadata length bytes (0) */
|
|
||||||
BrotliWriteBits(6, 6, storage_ix, storage);
|
|
||||||
JumpToByteBoundary(storage_ix, storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
#ifndef BROTLI_ENC_BROTLI_BIT_STREAM_H_
|
#ifndef BROTLI_ENC_BROTLI_BIT_STREAM_H_
|
||||||
#define BROTLI_ENC_BROTLI_BIT_STREAM_H_
|
#define BROTLI_ENC_BROTLI_BIT_STREAM_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/context.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./context.h"
|
|
||||||
#include "./entropy_encode.h"
|
#include "./entropy_encode.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./metablock.h"
|
#include "./metablock.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -32,7 +32,7 @@ extern "C" {
|
||||||
position for the current storage. */
|
position for the current storage. */
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
BROTLI_INTERNAL void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
|
||||||
HuffmanTree* tree, size_t *storage_ix, uint8_t *storage);
|
HuffmanTree* tree, size_t* storage_ix, uint8_t* storage);
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast(
|
BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast(
|
||||||
MemoryManager* m, const uint32_t* histogram, const size_t histogram_total,
|
MemoryManager* m, const uint32_t* histogram, const size_t histogram_total,
|
||||||
|
@ -42,63 +42,40 @@ BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast(
|
||||||
/* REQUIRES: length > 0 */
|
/* REQUIRES: length > 0 */
|
||||||
/* REQUIRES: length <= (1 << 24) */
|
/* REQUIRES: length <= (1 << 24) */
|
||||||
BROTLI_INTERNAL void BrotliStoreMetaBlock(MemoryManager* m,
|
BROTLI_INTERNAL void BrotliStoreMetaBlock(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
|
||||||
size_t start_pos,
|
uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last,
|
||||||
size_t length,
|
const BrotliEncoderParams* params, ContextType literal_context_mode,
|
||||||
size_t mask,
|
const Command* commands, size_t n_commands, const MetaBlockSplit* mb,
|
||||||
uint8_t prev_byte,
|
size_t* storage_ix, uint8_t* storage);
|
||||||
uint8_t prev_byte2,
|
|
||||||
BROTLI_BOOL is_final_block,
|
|
||||||
uint32_t num_direct_distance_codes,
|
|
||||||
uint32_t distance_postfix_bits,
|
|
||||||
ContextType literal_context_mode,
|
|
||||||
const Command* commands,
|
|
||||||
size_t n_commands,
|
|
||||||
const MetaBlockSplit* mb,
|
|
||||||
size_t* storage_ix,
|
|
||||||
uint8_t* storage);
|
|
||||||
|
|
||||||
/* Stores the meta-block without doing any block splitting, just collects
|
/* Stores the meta-block without doing any block splitting, just collects
|
||||||
one histogram per block category and uses that for entropy coding.
|
one histogram per block category and uses that for entropy coding.
|
||||||
REQUIRES: length > 0
|
REQUIRES: length > 0
|
||||||
REQUIRES: length <= (1 << 24) */
|
REQUIRES: length <= (1 << 24) */
|
||||||
BROTLI_INTERNAL void BrotliStoreMetaBlockTrivial(MemoryManager* m,
|
BROTLI_INTERNAL void BrotliStoreMetaBlockTrivial(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
|
||||||
size_t start_pos,
|
BROTLI_BOOL is_last, const BrotliEncoderParams* params,
|
||||||
size_t length,
|
const Command* commands, size_t n_commands,
|
||||||
size_t mask,
|
size_t* storage_ix, uint8_t* storage);
|
||||||
BROTLI_BOOL is_last,
|
|
||||||
const Command *commands,
|
|
||||||
size_t n_commands,
|
|
||||||
size_t* storage_ix,
|
|
||||||
uint8_t* storage);
|
|
||||||
|
|
||||||
/* Same as above, but uses static prefix codes for histograms with a only a few
|
/* Same as above, but uses static prefix codes for histograms with a only a few
|
||||||
symbols, and uses static code length prefix codes for all other histograms.
|
symbols, and uses static code length prefix codes for all other histograms.
|
||||||
REQUIRES: length > 0
|
REQUIRES: length > 0
|
||||||
REQUIRES: length <= (1 << 24) */
|
REQUIRES: length <= (1 << 24) */
|
||||||
BROTLI_INTERNAL void BrotliStoreMetaBlockFast(MemoryManager* m,
|
BROTLI_INTERNAL void BrotliStoreMetaBlockFast(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
|
||||||
size_t start_pos,
|
BROTLI_BOOL is_last, const BrotliEncoderParams* params,
|
||||||
size_t length,
|
const Command* commands, size_t n_commands,
|
||||||
size_t mask,
|
size_t* storage_ix, uint8_t* storage);
|
||||||
BROTLI_BOOL is_last,
|
|
||||||
const Command *commands,
|
|
||||||
size_t n_commands,
|
|
||||||
size_t* storage_ix,
|
|
||||||
uint8_t* storage);
|
|
||||||
|
|
||||||
/* This is for storing uncompressed blocks (simple raw storage of
|
/* This is for storing uncompressed blocks (simple raw storage of
|
||||||
bytes-as-bytes).
|
bytes-as-bytes).
|
||||||
REQUIRES: length > 0
|
REQUIRES: length > 0
|
||||||
REQUIRES: length <= (1 << 24) */
|
REQUIRES: length <= (1 << 24) */
|
||||||
BROTLI_INTERNAL void BrotliStoreUncompressedMetaBlock(
|
BROTLI_INTERNAL void BrotliStoreUncompressedMetaBlock(
|
||||||
BROTLI_BOOL is_final_block, const uint8_t* input, size_t position,
|
BROTLI_BOOL is_final_block, const uint8_t* BROTLI_RESTRICT input,
|
||||||
size_t mask, size_t len, size_t* storage_ix, uint8_t* storage);
|
size_t position, size_t mask, size_t len,
|
||||||
|
size_t* BROTLI_RESTRICT storage_ix, uint8_t* BROTLI_RESTRICT storage);
|
||||||
/* Stores an empty metadata meta-block and syncs to a byte boundary. */
|
|
||||||
BROTLI_INTERNAL void BrotliStoreSyncMetaBlock(size_t* storage_ix,
|
|
||||||
uint8_t* storage);
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
#include "./cluster.h"
|
#include "./cluster.h"
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./bit_cost.h" /* BrotliPopulationCost */
|
#include "./bit_cost.h" /* BrotliPopulationCost */
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
#ifndef BROTLI_ENC_CLUSTER_H_
|
#ifndef BROTLI_ENC_CLUSTER_H_
|
||||||
#define BROTLI_ENC_CLUSTER_H_
|
#define BROTLI_ENC_CLUSTER_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -17,6 +17,8 @@ BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
|
||||||
size_t* num_pairs) CODE({
|
size_t* num_pairs) CODE({
|
||||||
BROTLI_BOOL is_good_pair = BROTLI_FALSE;
|
BROTLI_BOOL is_good_pair = BROTLI_FALSE;
|
||||||
HistogramPair p;
|
HistogramPair p;
|
||||||
|
p.idx1 = p.idx2 = 0;
|
||||||
|
p.cost_diff = p.cost_combo = 0;
|
||||||
if (idx1 == idx2) {
|
if (idx1 == idx2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,11 @@
|
||||||
#ifndef BROTLI_ENC_COMMAND_H_
|
#ifndef BROTLI_ENC_COMMAND_H_
|
||||||
#define BROTLI_ENC_COMMAND_H_
|
#define BROTLI_ENC_COMMAND_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/port.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
|
#include "./params.h"
|
||||||
#include "./prefix.h"
|
#include "./prefix.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -60,15 +62,21 @@ static BROTLI_INLINE uint16_t GetCopyLengthCode(size_t copylen) {
|
||||||
static BROTLI_INLINE uint16_t CombineLengthCodes(
|
static BROTLI_INLINE uint16_t CombineLengthCodes(
|
||||||
uint16_t inscode, uint16_t copycode, BROTLI_BOOL use_last_distance) {
|
uint16_t inscode, uint16_t copycode, BROTLI_BOOL use_last_distance) {
|
||||||
uint16_t bits64 =
|
uint16_t bits64 =
|
||||||
(uint16_t)((copycode & 0x7u) | ((inscode & 0x7u) << 3));
|
(uint16_t)((copycode & 0x7u) | ((inscode & 0x7u) << 3u));
|
||||||
if (use_last_distance && inscode < 8 && copycode < 16) {
|
if (use_last_distance && inscode < 8u && copycode < 16u) {
|
||||||
return (copycode < 8) ? bits64 : (bits64 | 64);
|
return (copycode < 8u) ? bits64 : (bits64 | 64u);
|
||||||
} else {
|
} else {
|
||||||
/* "To convert an insert-and-copy length code to an insert length code and
|
/* Specification: 5 Encoding of ... (last table) */
|
||||||
a copy length code, the following table can be used" */
|
/* offset = 2 * index, where index is in range [0..8] */
|
||||||
static const uint16_t cells[9] = { 128u, 192u, 384u, 256u, 320u, 512u,
|
uint32_t offset = 2u * ((copycode >> 3u) + 3u * (inscode >> 3u));
|
||||||
448u, 576u, 640u };
|
/* All values in specification are K * 64,
|
||||||
return cells[(copycode >> 3) + 3 * (inscode >> 3)] | bits64;
|
where K = [2, 3, 6, 4, 5, 8, 7, 9, 10],
|
||||||
|
i + 1 = [1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||||
|
K - i - 1 = [1, 1, 3, 0, 0, 2, 0, 1, 2] = D.
|
||||||
|
All values in D require only 2 bits to encode.
|
||||||
|
Magic constant is shifted 6 bits left, to avoid final multiplication. */
|
||||||
|
offset = (offset << 5u) + 0x40u + ((0x520D40u >> offset) & 0xC0u);
|
||||||
|
return (uint16_t)(offset | bits64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,44 +106,61 @@ static BROTLI_INLINE uint32_t GetCopyExtra(uint16_t copycode) {
|
||||||
|
|
||||||
typedef struct Command {
|
typedef struct Command {
|
||||||
uint32_t insert_len_;
|
uint32_t insert_len_;
|
||||||
/* Stores copy_len in low 24 bits and copy_len XOR copy_code in high 8 bit. */
|
/* Stores copy_len in low 25 bits and copy_code - copy_len in high 7 bit. */
|
||||||
uint32_t copy_len_;
|
uint32_t copy_len_;
|
||||||
|
/* Stores distance extra bits. */
|
||||||
uint32_t dist_extra_;
|
uint32_t dist_extra_;
|
||||||
uint16_t cmd_prefix_;
|
uint16_t cmd_prefix_;
|
||||||
|
/* Stores distance code in low 10 bits
|
||||||
|
and number of extra bits in high 6 bits. */
|
||||||
uint16_t dist_prefix_;
|
uint16_t dist_prefix_;
|
||||||
} Command;
|
} Command;
|
||||||
|
|
||||||
/* distance_code is e.g. 0 for same-as-last short code, or 16 for offset 1. */
|
/* distance_code is e.g. 0 for same-as-last short code, or 16 for offset 1. */
|
||||||
static BROTLI_INLINE void InitCommand(Command* self, size_t insertlen,
|
static BROTLI_INLINE void InitCommand(Command* self,
|
||||||
size_t copylen, size_t copylen_code, size_t distance_code) {
|
const BrotliDistanceParams* dist, size_t insertlen,
|
||||||
|
size_t copylen, int copylen_code_delta, size_t distance_code) {
|
||||||
|
/* Don't rely on signed int representation, use honest casts. */
|
||||||
|
uint32_t delta = (uint8_t)((int8_t)copylen_code_delta);
|
||||||
self->insert_len_ = (uint32_t)insertlen;
|
self->insert_len_ = (uint32_t)insertlen;
|
||||||
self->copy_len_ = (uint32_t)(copylen | ((copylen_code ^ copylen) << 24));
|
self->copy_len_ = (uint32_t)(copylen | (delta << 25));
|
||||||
/* The distance prefix and extra bits are stored in this Command as if
|
/* The distance prefix and extra bits are stored in this Command as if
|
||||||
npostfix and ndirect were 0, they are only recomputed later after the
|
npostfix and ndirect were 0, they are only recomputed later after the
|
||||||
clustering if needed. */
|
clustering if needed. */
|
||||||
PrefixEncodeCopyDistance(
|
PrefixEncodeCopyDistance(
|
||||||
distance_code, 0, 0, &self->dist_prefix_, &self->dist_extra_);
|
distance_code, dist->num_direct_distance_codes,
|
||||||
|
dist->distance_postfix_bits, &self->dist_prefix_, &self->dist_extra_);
|
||||||
GetLengthCode(
|
GetLengthCode(
|
||||||
insertlen, copylen_code, TO_BROTLI_BOOL(self->dist_prefix_ == 0),
|
insertlen, (size_t)((int)copylen + copylen_code_delta),
|
||||||
&self->cmd_prefix_);
|
TO_BROTLI_BOOL((self->dist_prefix_ & 0x3FF) == 0), &self->cmd_prefix_);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void InitInsertCommand(Command* self, size_t insertlen) {
|
static BROTLI_INLINE void InitInsertCommand(Command* self, size_t insertlen) {
|
||||||
self->insert_len_ = (uint32_t)insertlen;
|
self->insert_len_ = (uint32_t)insertlen;
|
||||||
self->copy_len_ = 4 << 24;
|
self->copy_len_ = 4 << 25;
|
||||||
self->dist_extra_ = 0;
|
self->dist_extra_ = 0;
|
||||||
self->dist_prefix_ = 16;
|
self->dist_prefix_ = BROTLI_NUM_DISTANCE_SHORT_CODES;
|
||||||
GetLengthCode(insertlen, 4, BROTLI_FALSE, &self->cmd_prefix_);
|
GetLengthCode(insertlen, 4, BROTLI_FALSE, &self->cmd_prefix_);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t CommandDistanceCode(const Command* self) {
|
static BROTLI_INLINE uint32_t CommandRestoreDistanceCode(
|
||||||
if (self->dist_prefix_ < 16) {
|
const Command* self, const BrotliDistanceParams* dist) {
|
||||||
return self->dist_prefix_;
|
if ((self->dist_prefix_ & 0x3FFu) <
|
||||||
|
BROTLI_NUM_DISTANCE_SHORT_CODES + dist->num_direct_distance_codes) {
|
||||||
|
return self->dist_prefix_ & 0x3FFu;
|
||||||
} else {
|
} else {
|
||||||
uint32_t nbits = self->dist_extra_ >> 24;
|
uint32_t dcode = self->dist_prefix_ & 0x3FFu;
|
||||||
uint32_t extra = self->dist_extra_ & 0xffffff;
|
uint32_t nbits = self->dist_prefix_ >> 10;
|
||||||
uint32_t prefix = self->dist_prefix_ - 12u - 2u * nbits;
|
uint32_t extra = self->dist_extra_;
|
||||||
return (prefix << nbits) + extra + 12;
|
uint32_t postfix_mask = (1U << dist->distance_postfix_bits) - 1U;
|
||||||
|
uint32_t hcode = (dcode - dist->num_direct_distance_codes -
|
||||||
|
BROTLI_NUM_DISTANCE_SHORT_CODES) >>
|
||||||
|
dist->distance_postfix_bits;
|
||||||
|
uint32_t lcode = (dcode - dist->num_direct_distance_codes -
|
||||||
|
BROTLI_NUM_DISTANCE_SHORT_CODES) & postfix_mask;
|
||||||
|
uint32_t offset = ((2U + (hcode & 1U)) << nbits) - 4U;
|
||||||
|
return ((offset + extra) << dist->distance_postfix_bits) + lcode +
|
||||||
|
dist->num_direct_distance_codes + BROTLI_NUM_DISTANCE_SHORT_CODES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,11 +174,13 @@ static BROTLI_INLINE uint32_t CommandDistanceContext(const Command* self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t CommandCopyLen(const Command* self) {
|
static BROTLI_INLINE uint32_t CommandCopyLen(const Command* self) {
|
||||||
return self->copy_len_ & 0xFFFFFF;
|
return self->copy_len_ & 0x1FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t CommandCopyLenCode(const Command* self) {
|
static BROTLI_INLINE uint32_t CommandCopyLenCode(const Command* self) {
|
||||||
return (self->copy_len_ & 0xFFFFFF) ^ (self->copy_len_ >> 24);
|
uint32_t modifier = self->copy_len_ >> 25;
|
||||||
|
int32_t delta = (int8_t)((uint8_t)(modifier | ((modifier & 0x40) << 1)));
|
||||||
|
return (uint32_t)((int32_t)(self->copy_len_ & 0x1FFFFFF) + delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
|
|
@ -16,37 +16,39 @@
|
||||||
|
|
||||||
#include <string.h> /* memcmp, memcpy, memset */
|
#include <string.h> /* memcmp, memcpy, memset */
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/constants.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./brotli_bit_stream.h"
|
#include "./brotli_bit_stream.h"
|
||||||
#include "./entropy_encode.h"
|
#include "./entropy_encode.h"
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./find_match_length.h"
|
#include "./find_match_length.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./write_bits.h"
|
#include "./write_bits.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_DISTANCE (long)BROTLI_MAX_BACKWARD_LIMIT(18)
|
||||||
|
|
||||||
/* kHashMul32 multiplier has these properties:
|
/* kHashMul32 multiplier has these properties:
|
||||||
* The multiplier must be odd. Otherwise we may lose the highest bit.
|
* The multiplier must be odd. Otherwise we may lose the highest bit.
|
||||||
* No long streaks of 1s or 0s.
|
* No long streaks of ones or zeros.
|
||||||
* There is no effort to ensure that it is a prime, the oddity is enough
|
* There is no effort to ensure that it is a prime, the oddity is enough
|
||||||
for this use.
|
for this use.
|
||||||
* The number has been tuned heuristically against compression benchmarks. */
|
* The number has been tuned heuristically against compression benchmarks. */
|
||||||
static const uint32_t kHashMul32 = 0x1e35a7bd;
|
static const uint32_t kHashMul32 = 0x1E35A7BD;
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) {
|
static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) {
|
||||||
const uint64_t h = (BROTLI_UNALIGNED_LOAD64(p) << 24) * kHashMul32;
|
const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(p) << 24) * kHashMul32;
|
||||||
return (uint32_t)(h >> shift);
|
return (uint32_t)(h >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t HashBytesAtOffset(
|
static BROTLI_INLINE uint32_t HashBytesAtOffset(
|
||||||
uint64_t v, int offset, size_t shift) {
|
uint64_t v, int offset, size_t shift) {
|
||||||
assert(offset >= 0);
|
BROTLI_DCHECK(offset >= 0);
|
||||||
assert(offset <= 3);
|
BROTLI_DCHECK(offset <= 3);
|
||||||
{
|
{
|
||||||
const uint64_t h = ((v >> (8 * offset)) << 24) * kHashMul32;
|
const uint64_t h = ((v >> (8 * offset)) << 24) * kHashMul32;
|
||||||
return (uint32_t)(h >> shift);
|
return (uint32_t)(h >> shift);
|
||||||
|
@ -55,7 +57,7 @@ static BROTLI_INLINE uint32_t HashBytesAtOffset(
|
||||||
|
|
||||||
static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2) {
|
static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2) {
|
||||||
return TO_BROTLI_BOOL(
|
return TO_BROTLI_BOOL(
|
||||||
BROTLI_UNALIGNED_LOAD32(p1) == BROTLI_UNALIGNED_LOAD32(p2) &&
|
BrotliUnalignedRead32(p1) == BrotliUnalignedRead32(p2) &&
|
||||||
p1[4] == p2[4]);
|
p1[4] == p2[4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +134,7 @@ static void BuildAndStoreCommandPrefixCode(const uint32_t histogram[128],
|
||||||
|
|
||||||
BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth);
|
BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth);
|
||||||
BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]);
|
BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]);
|
||||||
/* We have to jump through a few hoopes here in order to compute
|
/* We have to jump through a few hoops here in order to compute
|
||||||
the command bits because the symbols are in a different order than in
|
the command bits because the symbols are in a different order than in
|
||||||
the full alphabet. This looks complicated, but having the symbols
|
the full alphabet. This looks complicated, but having the symbols
|
||||||
in this order in the command bits saves a few branches in the Emit*
|
in this order in the command bits saves a few branches in the Emit*
|
||||||
|
@ -200,7 +202,7 @@ static BROTLI_INLINE void EmitInsertLen(size_t insertlen,
|
||||||
} else {
|
} else {
|
||||||
BrotliWriteBits(depth[61], bits[61], storage_ix, storage);
|
BrotliWriteBits(depth[61], bits[61], storage_ix, storage);
|
||||||
BrotliWriteBits(12, insertlen - 2114, storage_ix, storage);
|
BrotliWriteBits(12, insertlen - 2114, storage_ix, storage);
|
||||||
++histo[21];
|
++histo[61];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,11 +215,11 @@ static BROTLI_INLINE void EmitLongInsertLen(size_t insertlen,
|
||||||
if (insertlen < 22594) {
|
if (insertlen < 22594) {
|
||||||
BrotliWriteBits(depth[62], bits[62], storage_ix, storage);
|
BrotliWriteBits(depth[62], bits[62], storage_ix, storage);
|
||||||
BrotliWriteBits(14, insertlen - 6210, storage_ix, storage);
|
BrotliWriteBits(14, insertlen - 6210, storage_ix, storage);
|
||||||
++histo[22];
|
++histo[62];
|
||||||
} else {
|
} else {
|
||||||
BrotliWriteBits(depth[63], bits[63], storage_ix, storage);
|
BrotliWriteBits(depth[63], bits[63], storage_ix, storage);
|
||||||
BrotliWriteBits(24, insertlen - 22594, storage_ix, storage);
|
BrotliWriteBits(24, insertlen - 22594, storage_ix, storage);
|
||||||
++histo[23];
|
++histo[63];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +251,7 @@ static BROTLI_INLINE void EmitCopyLen(size_t copylen,
|
||||||
} else {
|
} else {
|
||||||
BrotliWriteBits(depth[39], bits[39], storage_ix, storage);
|
BrotliWriteBits(depth[39], bits[39], storage_ix, storage);
|
||||||
BrotliWriteBits(24, copylen - 2118, storage_ix, storage);
|
BrotliWriteBits(24, copylen - 2118, storage_ix, storage);
|
||||||
++histo[47];
|
++histo[39];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +293,7 @@ static BROTLI_INLINE void EmitCopyLenLastDistance(size_t copylen,
|
||||||
BrotliWriteBits(depth[39], bits[39], storage_ix, storage);
|
BrotliWriteBits(depth[39], bits[39], storage_ix, storage);
|
||||||
BrotliWriteBits(24, copylen - 2120, storage_ix, storage);
|
BrotliWriteBits(24, copylen - 2120, storage_ix, storage);
|
||||||
BrotliWriteBits(depth[64], bits[64], storage_ix, storage);
|
BrotliWriteBits(depth[64], bits[64], storage_ix, storage);
|
||||||
++histo[47];
|
++histo[39];
|
||||||
++histo[64];
|
++histo[64];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -322,27 +324,26 @@ static BROTLI_INLINE void EmitLiterals(const uint8_t* input, const size_t len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REQUIRES: len <= 1 << 20. */
|
/* REQUIRES: len <= 1 << 24. */
|
||||||
static void BrotliStoreMetaBlockHeader(
|
static void BrotliStoreMetaBlockHeader(
|
||||||
size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix,
|
size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix,
|
||||||
uint8_t* storage) {
|
uint8_t* storage) {
|
||||||
|
size_t nibbles = 6;
|
||||||
/* ISLAST */
|
/* ISLAST */
|
||||||
BrotliWriteBits(1, 0, storage_ix, storage);
|
BrotliWriteBits(1, 0, storage_ix, storage);
|
||||||
if (len <= (1U << 16)) {
|
if (len <= (1U << 16)) {
|
||||||
/* MNIBBLES is 4 */
|
nibbles = 4;
|
||||||
BrotliWriteBits(2, 0, storage_ix, storage);
|
} else if (len <= (1U << 20)) {
|
||||||
BrotliWriteBits(16, len - 1, storage_ix, storage);
|
nibbles = 5;
|
||||||
} else {
|
|
||||||
/* MNIBBLES is 5 */
|
|
||||||
BrotliWriteBits(2, 1, storage_ix, storage);
|
|
||||||
BrotliWriteBits(20, len - 1, storage_ix, storage);
|
|
||||||
}
|
}
|
||||||
|
BrotliWriteBits(2, nibbles - 4, storage_ix, storage);
|
||||||
|
BrotliWriteBits(nibbles * 4, len - 1, storage_ix, storage);
|
||||||
/* ISUNCOMPRESSED */
|
/* ISUNCOMPRESSED */
|
||||||
BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage);
|
BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpdateBits(size_t n_bits, uint32_t bits, size_t pos,
|
static void UpdateBits(size_t n_bits, uint32_t bits, size_t pos,
|
||||||
uint8_t *array) {
|
uint8_t* array) {
|
||||||
while (n_bits > 0) {
|
while (n_bits > 0) {
|
||||||
size_t byte_pos = pos >> 3;
|
size_t byte_pos = pos >> 3;
|
||||||
size_t n_unchanged_bits = pos & 7;
|
size_t n_unchanged_bits = pos & 7;
|
||||||
|
@ -421,13 +422,11 @@ static uint32_t kCmdHistoSeed[128] = {
|
||||||
1, 1, 1, 1, 0, 0, 0, 0,
|
1, 1, 1, 1, 0, 0, 0, 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
void BrotliCompressFragmentFast(MemoryManager* m,
|
static BROTLI_INLINE void BrotliCompressFragmentFastImpl(
|
||||||
const uint8_t* input, size_t input_size,
|
MemoryManager* m, const uint8_t* input, size_t input_size,
|
||||||
BROTLI_BOOL is_last,
|
BROTLI_BOOL is_last, int* table, size_t table_bits, uint8_t cmd_depth[128],
|
||||||
int* table, size_t table_size,
|
uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,
|
||||||
uint8_t cmd_depth[128], uint16_t cmd_bits[128],
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
size_t* cmd_code_numbits, uint8_t* cmd_code,
|
|
||||||
size_t* storage_ix, uint8_t* storage) {
|
|
||||||
uint32_t cmd_histo[128];
|
uint32_t cmd_histo[128];
|
||||||
const uint8_t* ip_end;
|
const uint8_t* ip_end;
|
||||||
|
|
||||||
|
@ -442,7 +441,7 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
static const size_t kFirstBlockSize = 3 << 15;
|
static const size_t kFirstBlockSize = 3 << 15;
|
||||||
static const size_t kMergeBlockSize = 1 << 16;
|
static const size_t kMergeBlockSize = 1 << 16;
|
||||||
|
|
||||||
const size_t kInputMarginBytes = 16;
|
const size_t kInputMarginBytes = BROTLI_WINDOW_GAP;
|
||||||
const size_t kMinMatchLen = 5;
|
const size_t kMinMatchLen = 5;
|
||||||
|
|
||||||
const uint8_t* metablock_start = input;
|
const uint8_t* metablock_start = input;
|
||||||
|
@ -460,21 +459,7 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
const uint8_t* ip;
|
const uint8_t* ip;
|
||||||
int last_distance;
|
int last_distance;
|
||||||
|
|
||||||
const size_t shift = 64u - Log2FloorNonZero(table_size);
|
const size_t shift = 64u - table_bits;
|
||||||
assert(table_size);
|
|
||||||
assert(table_size <= (1u << 31));
|
|
||||||
/* table must be power of two */
|
|
||||||
assert((table_size & (table_size - 1)) == 0);
|
|
||||||
assert(table_size - 1 ==
|
|
||||||
(size_t)(MAKE_UINT64_T(0xFFFFFFFF, 0xFFFFFF) >> shift));
|
|
||||||
|
|
||||||
if (input_size == 0) {
|
|
||||||
assert(is_last);
|
|
||||||
BrotliWriteBits(1, 1, storage_ix, storage); /* islast */
|
|
||||||
BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */
|
|
||||||
*storage_ix = (*storage_ix + 7u) & ~7u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage);
|
BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage);
|
||||||
/* No block splits, no contexts. */
|
/* No block splits, no contexts. */
|
||||||
|
@ -506,7 +491,7 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
last_distance = -1;
|
last_distance = -1;
|
||||||
ip_end = input + block_size;
|
ip_end = input + block_size;
|
||||||
|
|
||||||
if (PREDICT_TRUE(block_size >= kInputMarginBytes)) {
|
if (BROTLI_PREDICT_TRUE(block_size >= kInputMarginBytes)) {
|
||||||
/* For the last block, we need to keep a 16 bytes margin so that we can be
|
/* For the last block, we need to keep a 16 bytes margin so that we can be
|
||||||
sure that all distances are at most window size - 16.
|
sure that all distances are at most window size - 16.
|
||||||
For all other blocks, we only need to keep a margin of 5 bytes so that
|
For all other blocks, we only need to keep a margin of 5 bytes so that
|
||||||
|
@ -530,40 +515,44 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
and doesn't bother looking for matches everywhere.
|
and doesn't bother looking for matches everywhere.
|
||||||
|
|
||||||
The "skip" variable keeps track of how many bytes there are since the
|
The "skip" variable keeps track of how many bytes there are since the
|
||||||
last match; dividing it by 32 (ie. right-shifting by five) gives the
|
last match; dividing it by 32 (i.e. right-shifting by five) gives the
|
||||||
number of bytes to move ahead for each iteration. */
|
number of bytes to move ahead for each iteration. */
|
||||||
uint32_t skip = 32;
|
uint32_t skip = 32;
|
||||||
|
|
||||||
const uint8_t* next_ip = ip;
|
const uint8_t* next_ip = ip;
|
||||||
const uint8_t* candidate;
|
const uint8_t* candidate;
|
||||||
assert(next_emit < ip);
|
BROTLI_DCHECK(next_emit < ip);
|
||||||
|
trawl:
|
||||||
do {
|
do {
|
||||||
uint32_t hash = next_hash;
|
uint32_t hash = next_hash;
|
||||||
uint32_t bytes_between_hash_lookups = skip++ >> 5;
|
uint32_t bytes_between_hash_lookups = skip++ >> 5;
|
||||||
assert(hash == Hash(next_ip, shift));
|
BROTLI_DCHECK(hash == Hash(next_ip, shift));
|
||||||
ip = next_ip;
|
ip = next_ip;
|
||||||
next_ip = ip + bytes_between_hash_lookups;
|
next_ip = ip + bytes_between_hash_lookups;
|
||||||
if (PREDICT_FALSE(next_ip > ip_limit)) {
|
if (BROTLI_PREDICT_FALSE(next_ip > ip_limit)) {
|
||||||
goto emit_remainder;
|
goto emit_remainder;
|
||||||
}
|
}
|
||||||
next_hash = Hash(next_ip, shift);
|
next_hash = Hash(next_ip, shift);
|
||||||
candidate = ip - last_distance;
|
candidate = ip - last_distance;
|
||||||
if (IsMatch(ip, candidate)) {
|
if (IsMatch(ip, candidate)) {
|
||||||
if (PREDICT_TRUE(candidate < ip)) {
|
if (BROTLI_PREDICT_TRUE(candidate < ip)) {
|
||||||
table[hash] = (int)(ip - base_ip);
|
table[hash] = (int)(ip - base_ip);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
candidate = base_ip + table[hash];
|
candidate = base_ip + table[hash];
|
||||||
assert(candidate >= base_ip);
|
BROTLI_DCHECK(candidate >= base_ip);
|
||||||
assert(candidate < ip);
|
BROTLI_DCHECK(candidate < ip);
|
||||||
|
|
||||||
table[hash] = (int)(ip - base_ip);
|
table[hash] = (int)(ip - base_ip);
|
||||||
} while (PREDICT_TRUE(!IsMatch(ip, candidate)));
|
} while (BROTLI_PREDICT_TRUE(!IsMatch(ip, candidate)));
|
||||||
|
|
||||||
|
/* Check copy distance. If candidate is not feasible, continue search.
|
||||||
|
Checking is done outside of hot loop to reduce overhead. */
|
||||||
|
if (ip - candidate > MAX_DISTANCE) goto trawl;
|
||||||
|
|
||||||
/* Step 2: Emit the found match together with the literal bytes from
|
/* Step 2: Emit the found match together with the literal bytes from
|
||||||
"next_emit" to the bit stream, and then see if we can find a next macth
|
"next_emit" to the bit stream, and then see if we can find a next match
|
||||||
immediately afterwards. Repeat until we find no match for the input
|
immediately afterwards. Repeat until we find no match for the input
|
||||||
without emitting some literal bytes. */
|
without emitting some literal bytes. */
|
||||||
|
|
||||||
|
@ -576,8 +565,8 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
int distance = (int)(base - candidate); /* > 0 */
|
int distance = (int)(base - candidate); /* > 0 */
|
||||||
size_t insert = (size_t)(base - next_emit);
|
size_t insert = (size_t)(base - next_emit);
|
||||||
ip += matched;
|
ip += matched;
|
||||||
assert(0 == memcmp(base, candidate, matched));
|
BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
|
||||||
if (PREDICT_TRUE(insert < 6210)) {
|
if (BROTLI_PREDICT_TRUE(insert < 6210)) {
|
||||||
EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo,
|
EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
} else if (ShouldUseUncompressedMode(metablock_start, next_emit, insert,
|
} else if (ShouldUseUncompressedMode(metablock_start, next_emit, insert,
|
||||||
|
@ -606,14 +595,14 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
|
|
||||||
next_emit = ip;
|
next_emit = ip;
|
||||||
if (PREDICT_FALSE(ip >= ip_limit)) {
|
if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
|
||||||
goto emit_remainder;
|
goto emit_remainder;
|
||||||
}
|
}
|
||||||
/* We could immediately start working at ip now, but to improve
|
/* We could immediately start working at ip now, but to improve
|
||||||
compression we first update "table" with the hashes of some positions
|
compression we first update "table" with the hashes of some positions
|
||||||
within the last copy. */
|
within the last copy. */
|
||||||
{
|
{
|
||||||
uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 3);
|
uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3);
|
||||||
uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
||||||
uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift);
|
uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 3);
|
table[prev_hash] = (int)(ip - base_ip - 3);
|
||||||
|
@ -633,23 +622,24 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
const uint8_t* base = ip;
|
const uint8_t* base = ip;
|
||||||
size_t matched = 5 + FindMatchLengthWithLimit(
|
size_t matched = 5 + FindMatchLengthWithLimit(
|
||||||
candidate + 5, ip + 5, (size_t)(ip_end - ip) - 5);
|
candidate + 5, ip + 5, (size_t)(ip_end - ip) - 5);
|
||||||
|
if (ip - candidate > MAX_DISTANCE) break;
|
||||||
ip += matched;
|
ip += matched;
|
||||||
last_distance = (int)(base - candidate); /* > 0 */
|
last_distance = (int)(base - candidate); /* > 0 */
|
||||||
assert(0 == memcmp(base, candidate, matched));
|
BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
|
||||||
EmitCopyLen(matched, cmd_depth, cmd_bits, cmd_histo,
|
EmitCopyLen(matched, cmd_depth, cmd_bits, cmd_histo,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
EmitDistance((size_t)last_distance, cmd_depth, cmd_bits,
|
EmitDistance((size_t)last_distance, cmd_depth, cmd_bits,
|
||||||
cmd_histo, storage_ix, storage);
|
cmd_histo, storage_ix, storage);
|
||||||
|
|
||||||
next_emit = ip;
|
next_emit = ip;
|
||||||
if (PREDICT_FALSE(ip >= ip_limit)) {
|
if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
|
||||||
goto emit_remainder;
|
goto emit_remainder;
|
||||||
}
|
}
|
||||||
/* We could immediately start working at ip now, but to improve
|
/* We could immediately start working at ip now, but to improve
|
||||||
compression we first update "table" with the hashes of some positions
|
compression we first update "table" with the hashes of some positions
|
||||||
within the last copy. */
|
within the last copy. */
|
||||||
{
|
{
|
||||||
uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 3);
|
uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3);
|
||||||
uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
||||||
uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift);
|
uint32_t cur_hash = HashBytesAtOffset(input_bytes, 3, shift);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 3);
|
table[prev_hash] = (int)(ip - base_ip - 3);
|
||||||
|
@ -668,7 +658,7 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_remainder:
|
emit_remainder:
|
||||||
assert(next_emit <= ip_end);
|
BROTLI_DCHECK(next_emit <= ip_end);
|
||||||
input += block_size;
|
input += block_size;
|
||||||
input_size -= block_size;
|
input_size -= block_size;
|
||||||
block_size = BROTLI_MIN(size_t, input_size, kMergeBlockSize);
|
block_size = BROTLI_MIN(size_t, input_size, kMergeBlockSize);
|
||||||
|
@ -678,7 +668,7 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
if (input_size > 0 &&
|
if (input_size > 0 &&
|
||||||
total_block_size + block_size <= (1 << 20) &&
|
total_block_size + block_size <= (1 << 20) &&
|
||||||
ShouldMergeBlock(input, block_size, lit_depth)) {
|
ShouldMergeBlock(input, block_size, lit_depth)) {
|
||||||
assert(total_block_size > (1 << 16));
|
BROTLI_DCHECK(total_block_size > (1 << 16));
|
||||||
/* Update the size of the current meta-block and continue emitting commands.
|
/* Update the size of the current meta-block and continue emitting commands.
|
||||||
We can do this because the current size and the new size both have 5
|
We can do this because the current size and the new size both have 5
|
||||||
nibbles. */
|
nibbles. */
|
||||||
|
@ -690,7 +680,7 @@ void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
/* Emit the remaining bytes as literals. */
|
/* Emit the remaining bytes as literals. */
|
||||||
if (next_emit < ip_end) {
|
if (next_emit < ip_end) {
|
||||||
const size_t insert = (size_t)(ip_end - next_emit);
|
const size_t insert = (size_t)(ip_end - next_emit);
|
||||||
if (PREDICT_TRUE(insert < 6210)) {
|
if (BROTLI_PREDICT_TRUE(insert < 6210)) {
|
||||||
EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo,
|
EmitInsertLen(insert, cmd_depth, cmd_bits, cmd_histo,
|
||||||
storage_ix, storage);
|
storage_ix, storage);
|
||||||
EmitLiterals(next_emit, insert, lit_depth, lit_bits, storage_ix, storage);
|
EmitLiterals(next_emit, insert, lit_depth, lit_bits, storage_ix, storage);
|
||||||
|
@ -728,11 +718,7 @@ next_block:
|
||||||
goto emit_commands;
|
goto emit_commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_last) {
|
if (!is_last) {
|
||||||
BrotliWriteBits(1, 1, storage_ix, storage); /* islast */
|
|
||||||
BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */
|
|
||||||
*storage_ix = (*storage_ix + 7u) & ~7u;
|
|
||||||
} else {
|
|
||||||
/* If this is not the last block, update the command and distance prefix
|
/* If this is not the last block, update the command and distance prefix
|
||||||
codes for the next block and store the compressed forms. */
|
codes for the next block and store the compressed forms. */
|
||||||
cmd_code[0] = 0;
|
cmd_code[0] = 0;
|
||||||
|
@ -742,6 +728,63 @@ next_block:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FOR_TABLE_BITS_(X) X(9) X(11) X(13) X(15)
|
||||||
|
|
||||||
|
#define BAKE_METHOD_PARAM_(B) \
|
||||||
|
static BROTLI_NOINLINE void BrotliCompressFragmentFastImpl ## B( \
|
||||||
|
MemoryManager* m, const uint8_t* input, size_t input_size, \
|
||||||
|
BROTLI_BOOL is_last, int* table, uint8_t cmd_depth[128], \
|
||||||
|
uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code, \
|
||||||
|
size_t* storage_ix, uint8_t* storage) { \
|
||||||
|
BrotliCompressFragmentFastImpl(m, input, input_size, is_last, table, B, \
|
||||||
|
cmd_depth, cmd_bits, cmd_code_numbits, cmd_code, storage_ix, storage); \
|
||||||
|
}
|
||||||
|
FOR_TABLE_BITS_(BAKE_METHOD_PARAM_)
|
||||||
|
#undef BAKE_METHOD_PARAM_
|
||||||
|
|
||||||
|
void BrotliCompressFragmentFast(
|
||||||
|
MemoryManager* m, const uint8_t* input, size_t input_size,
|
||||||
|
BROTLI_BOOL is_last, int* table, size_t table_size, uint8_t cmd_depth[128],
|
||||||
|
uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,
|
||||||
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
|
const size_t initial_storage_ix = *storage_ix;
|
||||||
|
const size_t table_bits = Log2FloorNonZero(table_size);
|
||||||
|
|
||||||
|
if (input_size == 0) {
|
||||||
|
BROTLI_DCHECK(is_last);
|
||||||
|
BrotliWriteBits(1, 1, storage_ix, storage); /* islast */
|
||||||
|
BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */
|
||||||
|
*storage_ix = (*storage_ix + 7u) & ~7u;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (table_bits) {
|
||||||
|
#define CASE_(B) \
|
||||||
|
case B: \
|
||||||
|
BrotliCompressFragmentFastImpl ## B( \
|
||||||
|
m, input, input_size, is_last, table, cmd_depth, cmd_bits, \
|
||||||
|
cmd_code_numbits, cmd_code, storage_ix, storage); \
|
||||||
|
break;
|
||||||
|
FOR_TABLE_BITS_(CASE_)
|
||||||
|
#undef CASE_
|
||||||
|
default: BROTLI_DCHECK(0); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If output is larger than single uncompressed block, rewrite it. */
|
||||||
|
if (*storage_ix - initial_storage_ix > 31 + (input_size << 3)) {
|
||||||
|
EmitUncompressedMetaBlock(input, input + input_size, initial_storage_ix,
|
||||||
|
storage_ix, storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_last) {
|
||||||
|
BrotliWriteBits(1, 1, storage_ix, storage); /* islast */
|
||||||
|
BrotliWriteBits(1, 1, storage_ix, storage); /* isempty */
|
||||||
|
*storage_ix = (*storage_ix + 7u) & ~7u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef FOR_TABLE_BITS_
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_
|
#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_
|
||||||
#define BROTLI_ENC_COMPRESS_FRAGMENT_H_
|
#define BROTLI_ENC_COMPRESS_FRAGMENT_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -37,8 +37,11 @@ extern "C" {
|
||||||
updated to represent the updated "cmd_depth" and "cmd_bits".
|
updated to represent the updated "cmd_depth" and "cmd_bits".
|
||||||
|
|
||||||
REQUIRES: "input_size" is greater than zero, or "is_last" is 1.
|
REQUIRES: "input_size" is greater than zero, or "is_last" is 1.
|
||||||
|
REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24).
|
||||||
REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
|
REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
|
||||||
REQUIRES: "table_size" is a power of two */
|
REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two
|
||||||
|
OUTPUT: maximal copy distance <= |input_size|
|
||||||
|
OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */
|
||||||
BROTLI_INTERNAL void BrotliCompressFragmentFast(MemoryManager* m,
|
BROTLI_INTERNAL void BrotliCompressFragmentFast(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input,
|
||||||
size_t input_size,
|
size_t input_size,
|
||||||
|
|
|
@ -14,49 +14,54 @@
|
||||||
|
|
||||||
#include <string.h> /* memcmp, memcpy, memset */
|
#include <string.h> /* memcmp, memcpy, memset */
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/constants.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./bit_cost.h"
|
#include "./bit_cost.h"
|
||||||
#include "./brotli_bit_stream.h"
|
#include "./brotli_bit_stream.h"
|
||||||
#include "./entropy_encode.h"
|
#include "./entropy_encode.h"
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./find_match_length.h"
|
#include "./find_match_length.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./write_bits.h"
|
#include "./write_bits.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_DISTANCE (long)BROTLI_MAX_BACKWARD_LIMIT(18)
|
||||||
|
|
||||||
/* kHashMul32 multiplier has these properties:
|
/* kHashMul32 multiplier has these properties:
|
||||||
* The multiplier must be odd. Otherwise we may lose the highest bit.
|
* The multiplier must be odd. Otherwise we may lose the highest bit.
|
||||||
* No long streaks of 1s or 0s.
|
* No long streaks of ones or zeros.
|
||||||
* There is no effort to ensure that it is a prime, the oddity is enough
|
* There is no effort to ensure that it is a prime, the oddity is enough
|
||||||
for this use.
|
for this use.
|
||||||
* The number has been tuned heuristically against compression benchmarks. */
|
* The number has been tuned heuristically against compression benchmarks. */
|
||||||
static const uint32_t kHashMul32 = 0x1e35a7bd;
|
static const uint32_t kHashMul32 = 0x1E35A7BD;
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t Hash(const uint8_t* p, size_t shift) {
|
static BROTLI_INLINE uint32_t Hash(const uint8_t* p,
|
||||||
const uint64_t h = (BROTLI_UNALIGNED_LOAD64(p) << 16) * kHashMul32;
|
size_t shift, size_t length) {
|
||||||
|
const uint64_t h =
|
||||||
|
(BROTLI_UNALIGNED_LOAD64LE(p) << ((8 - length) * 8)) * kHashMul32;
|
||||||
return (uint32_t)(h >> shift);
|
return (uint32_t)(h >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t HashBytesAtOffset(
|
static BROTLI_INLINE uint32_t HashBytesAtOffset(uint64_t v, size_t offset,
|
||||||
uint64_t v, int offset, size_t shift) {
|
size_t shift, size_t length) {
|
||||||
assert(offset >= 0);
|
BROTLI_DCHECK(offset <= 8 - length);
|
||||||
assert(offset <= 2);
|
|
||||||
{
|
{
|
||||||
const uint64_t h = ((v >> (8 * offset)) << 16) * kHashMul32;
|
const uint64_t h = ((v >> (8 * offset)) << ((8 - length) * 8)) * kHashMul32;
|
||||||
return (uint32_t)(h >> shift);
|
return (uint32_t)(h >> shift);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2) {
|
static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2,
|
||||||
return TO_BROTLI_BOOL(
|
size_t length) {
|
||||||
BROTLI_UNALIGNED_LOAD32(p1) == BROTLI_UNALIGNED_LOAD32(p2) &&
|
if (BrotliUnalignedRead32(p1) == BrotliUnalignedRead32(p2)) {
|
||||||
p1[4] == p2[4] &&
|
if (length == 4) return BROTLI_TRUE;
|
||||||
p1[5] == p2[5]);
|
return TO_BROTLI_BOOL(p1[4] == p2[4] && p1[5] == p2[5]);
|
||||||
|
}
|
||||||
|
return BROTLI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Builds a command and distance prefix code (each 64 symbols) into "depth" and
|
/* Builds a command and distance prefix code (each 64 symbols) into "depth" and
|
||||||
|
@ -71,7 +76,7 @@ static void BuildAndStoreCommandPrefixCode(
|
||||||
uint16_t cmd_bits[64];
|
uint16_t cmd_bits[64];
|
||||||
BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth);
|
BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth);
|
||||||
BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]);
|
BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]);
|
||||||
/* We have to jump through a few hoopes here in order to compute
|
/* We have to jump through a few hoops here in order to compute
|
||||||
the command bits because the symbols are in a different order than in
|
the command bits because the symbols are in a different order than in
|
||||||
the full alphabet. This looks complicated, but having the symbols
|
the full alphabet. This looks complicated, but having the symbols
|
||||||
in this order in the command bits saves a few branches in the Emit*
|
in this order in the command bits saves a few branches in the Emit*
|
||||||
|
@ -213,31 +218,31 @@ static BROTLI_INLINE void EmitDistance(uint32_t distance, uint32_t** commands) {
|
||||||
++(*commands);
|
++(*commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REQUIRES: len <= 1 << 20. */
|
/* REQUIRES: len <= 1 << 24. */
|
||||||
static void BrotliStoreMetaBlockHeader(
|
static void BrotliStoreMetaBlockHeader(
|
||||||
size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix,
|
size_t len, BROTLI_BOOL is_uncompressed, size_t* storage_ix,
|
||||||
uint8_t* storage) {
|
uint8_t* storage) {
|
||||||
|
size_t nibbles = 6;
|
||||||
/* ISLAST */
|
/* ISLAST */
|
||||||
BrotliWriteBits(1, 0, storage_ix, storage);
|
BrotliWriteBits(1, 0, storage_ix, storage);
|
||||||
if (len <= (1U << 16)) {
|
if (len <= (1U << 16)) {
|
||||||
/* MNIBBLES is 4 */
|
nibbles = 4;
|
||||||
BrotliWriteBits(2, 0, storage_ix, storage);
|
} else if (len <= (1U << 20)) {
|
||||||
BrotliWriteBits(16, len - 1, storage_ix, storage);
|
nibbles = 5;
|
||||||
} else {
|
|
||||||
/* MNIBBLES is 5 */
|
|
||||||
BrotliWriteBits(2, 1, storage_ix, storage);
|
|
||||||
BrotliWriteBits(20, len - 1, storage_ix, storage);
|
|
||||||
}
|
}
|
||||||
|
BrotliWriteBits(2, nibbles - 4, storage_ix, storage);
|
||||||
|
BrotliWriteBits(nibbles * 4, len - 1, storage_ix, storage);
|
||||||
/* ISUNCOMPRESSED */
|
/* ISUNCOMPRESSED */
|
||||||
BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage);
|
BrotliWriteBits(1, (uint64_t)is_uncompressed, storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CreateCommands(const uint8_t* input, size_t block_size,
|
static BROTLI_INLINE void CreateCommands(const uint8_t* input,
|
||||||
size_t input_size, const uint8_t* base_ip, int* table, size_t table_size,
|
size_t block_size, size_t input_size, const uint8_t* base_ip, int* table,
|
||||||
|
size_t table_bits, size_t min_match,
|
||||||
uint8_t** literals, uint32_t** commands) {
|
uint8_t** literals, uint32_t** commands) {
|
||||||
/* "ip" is the input pointer. */
|
/* "ip" is the input pointer. */
|
||||||
const uint8_t* ip = input;
|
const uint8_t* ip = input;
|
||||||
const size_t shift = 64u - Log2FloorNonZero(table_size);
|
const size_t shift = 64u - table_bits;
|
||||||
const uint8_t* ip_end = input + block_size;
|
const uint8_t* ip_end = input + block_size;
|
||||||
/* "next_emit" is a pointer to the first byte that is not covered by a
|
/* "next_emit" is a pointer to the first byte that is not covered by a
|
||||||
previous copy. Bytes between "next_emit" and the start of the next copy or
|
previous copy. Bytes between "next_emit" and the start of the next copy or
|
||||||
|
@ -245,27 +250,19 @@ static void CreateCommands(const uint8_t* input, size_t block_size,
|
||||||
const uint8_t* next_emit = input;
|
const uint8_t* next_emit = input;
|
||||||
|
|
||||||
int last_distance = -1;
|
int last_distance = -1;
|
||||||
const size_t kInputMarginBytes = 16;
|
const size_t kInputMarginBytes = BROTLI_WINDOW_GAP;
|
||||||
const size_t kMinMatchLen = 6;
|
|
||||||
|
|
||||||
assert(table_size);
|
if (BROTLI_PREDICT_TRUE(block_size >= kInputMarginBytes)) {
|
||||||
assert(table_size <= (1u << 31));
|
|
||||||
/* table must be power of two */
|
|
||||||
assert((table_size & (table_size - 1)) == 0);
|
|
||||||
assert(table_size - 1 ==
|
|
||||||
(size_t)(MAKE_UINT64_T(0xFFFFFFFF, 0xFFFFFF) >> shift));
|
|
||||||
|
|
||||||
if (PREDICT_TRUE(block_size >= kInputMarginBytes)) {
|
|
||||||
/* For the last block, we need to keep a 16 bytes margin so that we can be
|
/* For the last block, we need to keep a 16 bytes margin so that we can be
|
||||||
sure that all distances are at most window size - 16.
|
sure that all distances are at most window size - 16.
|
||||||
For all other blocks, we only need to keep a margin of 5 bytes so that
|
For all other blocks, we only need to keep a margin of 5 bytes so that
|
||||||
we don't go over the block size with a copy. */
|
we don't go over the block size with a copy. */
|
||||||
const size_t len_limit = BROTLI_MIN(size_t, block_size - kMinMatchLen,
|
const size_t len_limit = BROTLI_MIN(size_t, block_size - min_match,
|
||||||
input_size - kInputMarginBytes);
|
input_size - kInputMarginBytes);
|
||||||
const uint8_t* ip_limit = input + len_limit;
|
const uint8_t* ip_limit = input + len_limit;
|
||||||
|
|
||||||
uint32_t next_hash;
|
uint32_t next_hash;
|
||||||
for (next_hash = Hash(++ip, shift); ; ) {
|
for (next_hash = Hash(++ip, shift, min_match); ; ) {
|
||||||
/* Step 1: Scan forward in the input looking for a 6-byte-long match.
|
/* Step 1: Scan forward in the input looking for a 6-byte-long match.
|
||||||
If we get close to exhausting the input then goto emit_remainder.
|
If we get close to exhausting the input then goto emit_remainder.
|
||||||
|
|
||||||
|
@ -286,34 +283,38 @@ static void CreateCommands(const uint8_t* input, size_t block_size,
|
||||||
const uint8_t* next_ip = ip;
|
const uint8_t* next_ip = ip;
|
||||||
const uint8_t* candidate;
|
const uint8_t* candidate;
|
||||||
|
|
||||||
assert(next_emit < ip);
|
BROTLI_DCHECK(next_emit < ip);
|
||||||
|
trawl:
|
||||||
do {
|
do {
|
||||||
uint32_t hash = next_hash;
|
uint32_t hash = next_hash;
|
||||||
uint32_t bytes_between_hash_lookups = skip++ >> 5;
|
uint32_t bytes_between_hash_lookups = skip++ >> 5;
|
||||||
ip = next_ip;
|
ip = next_ip;
|
||||||
assert(hash == Hash(ip, shift));
|
BROTLI_DCHECK(hash == Hash(ip, shift, min_match));
|
||||||
next_ip = ip + bytes_between_hash_lookups;
|
next_ip = ip + bytes_between_hash_lookups;
|
||||||
if (PREDICT_FALSE(next_ip > ip_limit)) {
|
if (BROTLI_PREDICT_FALSE(next_ip > ip_limit)) {
|
||||||
goto emit_remainder;
|
goto emit_remainder;
|
||||||
}
|
}
|
||||||
next_hash = Hash(next_ip, shift);
|
next_hash = Hash(next_ip, shift, min_match);
|
||||||
candidate = ip - last_distance;
|
candidate = ip - last_distance;
|
||||||
if (IsMatch(ip, candidate)) {
|
if (IsMatch(ip, candidate, min_match)) {
|
||||||
if (PREDICT_TRUE(candidate < ip)) {
|
if (BROTLI_PREDICT_TRUE(candidate < ip)) {
|
||||||
table[hash] = (int)(ip - base_ip);
|
table[hash] = (int)(ip - base_ip);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
candidate = base_ip + table[hash];
|
candidate = base_ip + table[hash];
|
||||||
assert(candidate >= base_ip);
|
BROTLI_DCHECK(candidate >= base_ip);
|
||||||
assert(candidate < ip);
|
BROTLI_DCHECK(candidate < ip);
|
||||||
|
|
||||||
table[hash] = (int)(ip - base_ip);
|
table[hash] = (int)(ip - base_ip);
|
||||||
} while (PREDICT_TRUE(!IsMatch(ip, candidate)));
|
} while (BROTLI_PREDICT_TRUE(!IsMatch(ip, candidate, min_match)));
|
||||||
|
|
||||||
|
/* Check copy distance. If candidate is not feasible, continue search.
|
||||||
|
Checking is done outside of hot loop to reduce overhead. */
|
||||||
|
if (ip - candidate > MAX_DISTANCE) goto trawl;
|
||||||
|
|
||||||
/* Step 2: Emit the found match together with the literal bytes from
|
/* Step 2: Emit the found match together with the literal bytes from
|
||||||
"next_emit", and then see if we can find a next macth immediately
|
"next_emit", and then see if we can find a next match immediately
|
||||||
afterwards. Repeat until we find no match for the input
|
afterwards. Repeat until we find no match for the input
|
||||||
without emitting some literal bytes. */
|
without emitting some literal bytes. */
|
||||||
|
|
||||||
|
@ -321,12 +322,13 @@ static void CreateCommands(const uint8_t* input, size_t block_size,
|
||||||
/* We have a 6-byte match at ip, and we need to emit bytes in
|
/* We have a 6-byte match at ip, and we need to emit bytes in
|
||||||
[next_emit, ip). */
|
[next_emit, ip). */
|
||||||
const uint8_t* base = ip;
|
const uint8_t* base = ip;
|
||||||
size_t matched = 6 + FindMatchLengthWithLimit(
|
size_t matched = min_match + FindMatchLengthWithLimit(
|
||||||
candidate + 6, ip + 6, (size_t)(ip_end - ip) - 6);
|
candidate + min_match, ip + min_match,
|
||||||
|
(size_t)(ip_end - ip) - min_match);
|
||||||
int distance = (int)(base - candidate); /* > 0 */
|
int distance = (int)(base - candidate); /* > 0 */
|
||||||
int insert = (int)(base - next_emit);
|
int insert = (int)(base - next_emit);
|
||||||
ip += matched;
|
ip += matched;
|
||||||
assert(0 == memcmp(base, candidate, matched));
|
BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
|
||||||
EmitInsertLen((uint32_t)insert, commands);
|
EmitInsertLen((uint32_t)insert, commands);
|
||||||
memcpy(*literals, next_emit, (size_t)insert);
|
memcpy(*literals, next_emit, (size_t)insert);
|
||||||
*literals += insert;
|
*literals += insert;
|
||||||
|
@ -340,79 +342,107 @@ static void CreateCommands(const uint8_t* input, size_t block_size,
|
||||||
EmitCopyLenLastDistance(matched, commands);
|
EmitCopyLenLastDistance(matched, commands);
|
||||||
|
|
||||||
next_emit = ip;
|
next_emit = ip;
|
||||||
if (PREDICT_FALSE(ip >= ip_limit)) {
|
if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
|
||||||
goto emit_remainder;
|
goto emit_remainder;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
/* We could immediately start working at ip now, but to improve
|
/* We could immediately start working at ip now, but to improve
|
||||||
compression we first update "table" with the hashes of some
|
compression we first update "table" with the hashes of some
|
||||||
positions within the last copy. */
|
positions within the last copy. */
|
||||||
uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 5);
|
uint64_t input_bytes;
|
||||||
uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
|
||||||
uint32_t cur_hash;
|
uint32_t cur_hash;
|
||||||
table[prev_hash] = (int)(ip - base_ip - 5);
|
uint32_t prev_hash;
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 1, shift);
|
if (min_match == 4) {
|
||||||
table[prev_hash] = (int)(ip - base_ip - 4);
|
input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3);
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 2, shift);
|
cur_hash = HashBytesAtOffset(input_bytes, 3, shift, min_match);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 3);
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 2);
|
table[prev_hash] = (int)(ip - base_ip - 3);
|
||||||
cur_hash = HashBytesAtOffset(input_bytes, 2, shift);
|
prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match);
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
table[prev_hash] = (int)(ip - base_ip - 2);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 2);
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 1, shift);
|
table[prev_hash] = (int)(ip - base_ip - 1);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 1);
|
} else {
|
||||||
|
input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 5);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 4);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 3);
|
||||||
|
input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2);
|
||||||
|
cur_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 2);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 1);
|
||||||
|
}
|
||||||
|
|
||||||
candidate = base_ip + table[cur_hash];
|
candidate = base_ip + table[cur_hash];
|
||||||
table[cur_hash] = (int)(ip - base_ip);
|
table[cur_hash] = (int)(ip - base_ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (IsMatch(ip, candidate)) {
|
while (ip - candidate <= MAX_DISTANCE &&
|
||||||
|
IsMatch(ip, candidate, min_match)) {
|
||||||
/* We have a 6-byte match at ip, and no need to emit any
|
/* We have a 6-byte match at ip, and no need to emit any
|
||||||
literal bytes prior to ip. */
|
literal bytes prior to ip. */
|
||||||
const uint8_t* base = ip;
|
const uint8_t* base = ip;
|
||||||
size_t matched = 6 + FindMatchLengthWithLimit(
|
size_t matched = min_match + FindMatchLengthWithLimit(
|
||||||
candidate + 6, ip + 6, (size_t)(ip_end - ip) - 6);
|
candidate + min_match, ip + min_match,
|
||||||
|
(size_t)(ip_end - ip) - min_match);
|
||||||
ip += matched;
|
ip += matched;
|
||||||
last_distance = (int)(base - candidate); /* > 0 */
|
last_distance = (int)(base - candidate); /* > 0 */
|
||||||
assert(0 == memcmp(base, candidate, matched));
|
BROTLI_DCHECK(0 == memcmp(base, candidate, matched));
|
||||||
EmitCopyLen(matched, commands);
|
EmitCopyLen(matched, commands);
|
||||||
EmitDistance((uint32_t)last_distance, commands);
|
EmitDistance((uint32_t)last_distance, commands);
|
||||||
|
|
||||||
next_emit = ip;
|
next_emit = ip;
|
||||||
if (PREDICT_FALSE(ip >= ip_limit)) {
|
if (BROTLI_PREDICT_FALSE(ip >= ip_limit)) {
|
||||||
goto emit_remainder;
|
goto emit_remainder;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
/* We could immediately start working at ip now, but to improve
|
/* We could immediately start working at ip now, but to improve
|
||||||
compression we first update "table" with the hashes of some
|
compression we first update "table" with the hashes of some
|
||||||
positions within the last copy. */
|
positions within the last copy. */
|
||||||
uint64_t input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 5);
|
uint64_t input_bytes;
|
||||||
uint32_t prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
|
||||||
uint32_t cur_hash;
|
uint32_t cur_hash;
|
||||||
table[prev_hash] = (int)(ip - base_ip - 5);
|
uint32_t prev_hash;
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 1, shift);
|
if (min_match == 4) {
|
||||||
table[prev_hash] = (int)(ip - base_ip - 4);
|
input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 3);
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 2, shift);
|
cur_hash = HashBytesAtOffset(input_bytes, 3, shift, min_match);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 3);
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
input_bytes = BROTLI_UNALIGNED_LOAD64(ip - 2);
|
table[prev_hash] = (int)(ip - base_ip - 3);
|
||||||
cur_hash = HashBytesAtOffset(input_bytes, 2, shift);
|
prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match);
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 0, shift);
|
table[prev_hash] = (int)(ip - base_ip - 2);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 2);
|
prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match);
|
||||||
prev_hash = HashBytesAtOffset(input_bytes, 1, shift);
|
table[prev_hash] = (int)(ip - base_ip - 1);
|
||||||
table[prev_hash] = (int)(ip - base_ip - 1);
|
} else {
|
||||||
|
input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 5);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 5);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 4);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 3);
|
||||||
|
input_bytes = BROTLI_UNALIGNED_LOAD64LE(ip - 2);
|
||||||
|
cur_hash = HashBytesAtOffset(input_bytes, 2, shift, min_match);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 0, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 2);
|
||||||
|
prev_hash = HashBytesAtOffset(input_bytes, 1, shift, min_match);
|
||||||
|
table[prev_hash] = (int)(ip - base_ip - 1);
|
||||||
|
}
|
||||||
|
|
||||||
candidate = base_ip + table[cur_hash];
|
candidate = base_ip + table[cur_hash];
|
||||||
table[cur_hash] = (int)(ip - base_ip);
|
table[cur_hash] = (int)(ip - base_ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next_hash = Hash(++ip, shift);
|
next_hash = Hash(++ip, shift, min_match);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_remainder:
|
emit_remainder:
|
||||||
assert(next_emit <= ip_end);
|
BROTLI_DCHECK(next_emit <= ip_end);
|
||||||
/* Emit the remaining bytes as literals. */
|
/* Emit the remaining bytes as literals. */
|
||||||
if (next_emit < ip_end) {
|
if (next_emit < ip_end) {
|
||||||
const uint32_t insert = (uint32_t)(ip_end - next_emit);
|
const uint32_t insert = (uint32_t)(ip_end - next_emit);
|
||||||
|
@ -457,7 +487,9 @@ static void StoreCommands(MemoryManager* m,
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
|
||||||
for (i = 0; i < num_commands; ++i) {
|
for (i = 0; i < num_commands; ++i) {
|
||||||
++cmd_histo[commands[i] & 0xff];
|
const uint32_t code = commands[i] & 0xFF;
|
||||||
|
BROTLI_DCHECK(code < 128);
|
||||||
|
++cmd_histo[code];
|
||||||
}
|
}
|
||||||
cmd_histo[1] += 1;
|
cmd_histo[1] += 1;
|
||||||
cmd_histo[2] += 1;
|
cmd_histo[2] += 1;
|
||||||
|
@ -468,8 +500,9 @@ static void StoreCommands(MemoryManager* m,
|
||||||
|
|
||||||
for (i = 0; i < num_commands; ++i) {
|
for (i = 0; i < num_commands; ++i) {
|
||||||
const uint32_t cmd = commands[i];
|
const uint32_t cmd = commands[i];
|
||||||
const uint32_t code = cmd & 0xff;
|
const uint32_t code = cmd & 0xFF;
|
||||||
const uint32_t extra = cmd >> 8;
|
const uint32_t extra = cmd >> 8;
|
||||||
|
BROTLI_DCHECK(code < 128);
|
||||||
BrotliWriteBits(cmd_depths[code], cmd_bits[code], storage_ix, storage);
|
BrotliWriteBits(cmd_depths[code], cmd_bits[code], storage_ix, storage);
|
||||||
BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage);
|
BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage);
|
||||||
if (code < 24) {
|
if (code < 24) {
|
||||||
|
@ -504,15 +537,32 @@ static BROTLI_BOOL ShouldCompress(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliCompressFragmentTwoPass(MemoryManager* m,
|
static void RewindBitPosition(const size_t new_storage_ix,
|
||||||
const uint8_t* input, size_t input_size,
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
BROTLI_BOOL is_last,
|
const size_t bitpos = new_storage_ix & 7;
|
||||||
uint32_t* command_buf, uint8_t* literal_buf,
|
const size_t mask = (1u << bitpos) - 1;
|
||||||
int* table, size_t table_size,
|
storage[new_storage_ix >> 3] &= (uint8_t)mask;
|
||||||
size_t* storage_ix, uint8_t* storage) {
|
*storage_ix = new_storage_ix;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmitUncompressedMetaBlock(const uint8_t* input, size_t input_size,
|
||||||
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
|
BrotliStoreMetaBlockHeader(input_size, 1, storage_ix, storage);
|
||||||
|
*storage_ix = (*storage_ix + 7u) & ~7u;
|
||||||
|
memcpy(&storage[*storage_ix >> 3], input, input_size);
|
||||||
|
*storage_ix += input_size << 3;
|
||||||
|
storage[*storage_ix >> 3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl(
|
||||||
|
MemoryManager* m, const uint8_t* input, size_t input_size,
|
||||||
|
BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,
|
||||||
|
int* table, size_t table_bits, size_t min_match,
|
||||||
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
/* Save the start of the first block for position and distance computations.
|
/* Save the start of the first block for position and distance computations.
|
||||||
*/
|
*/
|
||||||
const uint8_t* base_ip = input;
|
const uint8_t* base_ip = input;
|
||||||
|
BROTLI_UNUSED(is_last);
|
||||||
|
|
||||||
while (input_size > 0) {
|
while (input_size > 0) {
|
||||||
size_t block_size =
|
size_t block_size =
|
||||||
|
@ -520,8 +570,8 @@ void BrotliCompressFragmentTwoPass(MemoryManager* m,
|
||||||
uint32_t* commands = command_buf;
|
uint32_t* commands = command_buf;
|
||||||
uint8_t* literals = literal_buf;
|
uint8_t* literals = literal_buf;
|
||||||
size_t num_literals;
|
size_t num_literals;
|
||||||
CreateCommands(input, block_size, input_size, base_ip, table, table_size,
|
CreateCommands(input, block_size, input_size, base_ip, table,
|
||||||
&literals, &commands);
|
table_bits, min_match, &literals, &commands);
|
||||||
num_literals = (size_t)(literals - literal_buf);
|
num_literals = (size_t)(literals - literal_buf);
|
||||||
if (ShouldCompress(input, block_size, num_literals)) {
|
if (ShouldCompress(input, block_size, num_literals)) {
|
||||||
const size_t num_commands = (size_t)(commands - command_buf);
|
const size_t num_commands = (size_t)(commands - command_buf);
|
||||||
|
@ -535,15 +585,51 @@ void BrotliCompressFragmentTwoPass(MemoryManager* m,
|
||||||
/* Since we did not find many backward references and the entropy of
|
/* Since we did not find many backward references and the entropy of
|
||||||
the data is close to 8 bits, we can simply emit an uncompressed block.
|
the data is close to 8 bits, we can simply emit an uncompressed block.
|
||||||
This makes compression speed of uncompressible data about 3x faster. */
|
This makes compression speed of uncompressible data about 3x faster. */
|
||||||
BrotliStoreMetaBlockHeader(block_size, 1, storage_ix, storage);
|
EmitUncompressedMetaBlock(input, block_size, storage_ix, storage);
|
||||||
*storage_ix = (*storage_ix + 7u) & ~7u;
|
|
||||||
memcpy(&storage[*storage_ix >> 3], input, block_size);
|
|
||||||
*storage_ix += block_size << 3;
|
|
||||||
storage[*storage_ix >> 3] = 0;
|
|
||||||
}
|
}
|
||||||
input += block_size;
|
input += block_size;
|
||||||
input_size -= block_size;
|
input_size -= block_size;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FOR_TABLE_BITS_(X) \
|
||||||
|
X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17)
|
||||||
|
|
||||||
|
#define BAKE_METHOD_PARAM_(B) \
|
||||||
|
static BROTLI_NOINLINE void BrotliCompressFragmentTwoPassImpl ## B( \
|
||||||
|
MemoryManager* m, const uint8_t* input, size_t input_size, \
|
||||||
|
BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, \
|
||||||
|
int* table, size_t* storage_ix, uint8_t* storage) { \
|
||||||
|
size_t min_match = (B <= 15) ? 4 : 6; \
|
||||||
|
BrotliCompressFragmentTwoPassImpl(m, input, input_size, is_last, command_buf,\
|
||||||
|
literal_buf, table, B, min_match, storage_ix, storage); \
|
||||||
|
}
|
||||||
|
FOR_TABLE_BITS_(BAKE_METHOD_PARAM_)
|
||||||
|
#undef BAKE_METHOD_PARAM_
|
||||||
|
|
||||||
|
void BrotliCompressFragmentTwoPass(
|
||||||
|
MemoryManager* m, const uint8_t* input, size_t input_size,
|
||||||
|
BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,
|
||||||
|
int* table, size_t table_size, size_t* storage_ix, uint8_t* storage) {
|
||||||
|
const size_t initial_storage_ix = *storage_ix;
|
||||||
|
const size_t table_bits = Log2FloorNonZero(table_size);
|
||||||
|
switch (table_bits) {
|
||||||
|
#define CASE_(B) \
|
||||||
|
case B: \
|
||||||
|
BrotliCompressFragmentTwoPassImpl ## B( \
|
||||||
|
m, input, input_size, is_last, command_buf, \
|
||||||
|
literal_buf, table, storage_ix, storage); \
|
||||||
|
break;
|
||||||
|
FOR_TABLE_BITS_(CASE_)
|
||||||
|
#undef CASE_
|
||||||
|
default: BROTLI_DCHECK(0); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If output is larger than single uncompressed block, rewrite it. */
|
||||||
|
if (*storage_ix - initial_storage_ix > 31 + (input_size << 3)) {
|
||||||
|
RewindBitPosition(initial_storage_ix, storage_ix, storage);
|
||||||
|
EmitUncompressedMetaBlock(input, input_size, storage_ix, storage);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_last) {
|
if (is_last) {
|
||||||
BrotliWriteBits(1, 1, storage_ix, storage); /* islast */
|
BrotliWriteBits(1, 1, storage_ix, storage); /* islast */
|
||||||
|
@ -552,6 +638,8 @@ void BrotliCompressFragmentTwoPass(MemoryManager* m,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef FOR_TABLE_BITS_
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
|
#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
|
||||||
#define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
|
#define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -29,10 +29,13 @@ static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17;
|
||||||
If "is_last" is 1, emits an additional empty last meta-block.
|
If "is_last" is 1, emits an additional empty last meta-block.
|
||||||
|
|
||||||
REQUIRES: "input_size" is greater than zero, or "is_last" is 1.
|
REQUIRES: "input_size" is greater than zero, or "is_last" is 1.
|
||||||
|
REQUIRES: "input_size" is less or equal to maximal metablock size (1 << 24).
|
||||||
REQUIRES: "command_buf" and "literal_buf" point to at least
|
REQUIRES: "command_buf" and "literal_buf" point to at least
|
||||||
kCompressFragmentTwoPassBlockSize long arrays.
|
kCompressFragmentTwoPassBlockSize long arrays.
|
||||||
REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
|
REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero.
|
||||||
REQUIRES: "table_size" is a power of two */
|
REQUIRES: "table_size" is a power of two
|
||||||
|
OUTPUT: maximal copy distance <= |input_size|
|
||||||
|
OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */
|
||||||
BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(MemoryManager* m,
|
BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(MemoryManager* m,
|
||||||
const uint8_t* input,
|
const uint8_t* input,
|
||||||
size_t input_size,
|
size_t input_size,
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
/* Copyright 2016 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* C++ API for Brotli compression. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_COMPRESSOR_H_
|
|
||||||
#define BROTLI_ENC_COMPRESSOR_H_
|
|
||||||
|
|
||||||
#include "./encode.h"
|
|
||||||
#include "./streams.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
static const int kMinWindowBits = kBrotliMinWindowBits;
|
|
||||||
static const int kMaxWindowBits = kBrotliMaxWindowBits;
|
|
||||||
static const int kMinInputBlockBits = kBrotliMinInputBlockBits;
|
|
||||||
static const int kMaxInputBlockBits = kBrotliMaxInputBlockBits;
|
|
||||||
|
|
||||||
struct BrotliParams {
|
|
||||||
BrotliParams(void)
|
|
||||||
: mode(MODE_GENERIC),
|
|
||||||
quality(11),
|
|
||||||
lgwin(22),
|
|
||||||
lgblock(0),
|
|
||||||
enable_dictionary(true),
|
|
||||||
enable_transforms(false),
|
|
||||||
greedy_block_split(false),
|
|
||||||
enable_context_modeling(true) {}
|
|
||||||
|
|
||||||
enum Mode {
|
|
||||||
/* Default compression mode. The compressor does not know anything in
|
|
||||||
advance about the properties of the input. */
|
|
||||||
MODE_GENERIC = 0,
|
|
||||||
/* Compression mode for UTF-8 format text input. */
|
|
||||||
MODE_TEXT = 1,
|
|
||||||
/* Compression mode used in WOFF 2.0. */
|
|
||||||
MODE_FONT = 2
|
|
||||||
};
|
|
||||||
Mode mode;
|
|
||||||
|
|
||||||
/* Controls the compression-speed vs compression-density tradeoffs. The higher
|
|
||||||
the |quality|, the slower the compression. Range is 0 to 11. */
|
|
||||||
int quality;
|
|
||||||
/* Base 2 logarithm of the sliding window size. Range is 10 to 24. */
|
|
||||||
int lgwin;
|
|
||||||
/* Base 2 logarithm of the maximum input block size. Range is 16 to 24.
|
|
||||||
If set to 0, the value will be set based on the quality. */
|
|
||||||
int lgblock;
|
|
||||||
|
|
||||||
/* These settings are deprecated and will be ignored.
|
|
||||||
All speed vs. size compromises are controlled by the |quality| param. */
|
|
||||||
bool enable_dictionary;
|
|
||||||
bool enable_transforms;
|
|
||||||
bool greedy_block_split;
|
|
||||||
bool enable_context_modeling;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* An instance can not be reused for multiple brotli streams. */
|
|
||||||
class BrotliCompressor {
|
|
||||||
public:
|
|
||||||
explicit BrotliCompressor(BrotliParams params);
|
|
||||||
~BrotliCompressor(void);
|
|
||||||
|
|
||||||
/* The maximum input size that can be processed at once. */
|
|
||||||
size_t input_block_size(void) const {
|
|
||||||
return BrotliEncoderInputBlockSize(state_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Encodes the data in |input_buffer| as a meta-block and writes it to
|
|
||||||
|encoded_buffer| (|*encoded_size should| be set to the size of
|
|
||||||
|encoded_buffer|) and sets |*encoded_size| to the number of bytes that
|
|
||||||
was written. The |input_size| must not be greater than input_block_size().
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
bool WriteMetaBlock(const size_t input_size,
|
|
||||||
const uint8_t* input_buffer,
|
|
||||||
const bool is_last,
|
|
||||||
size_t* encoded_size,
|
|
||||||
uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Writes a metadata meta-block containing the given input to encoded_buffer.
|
|
||||||
|*encoded_size| should be set to the size of the encoded_buffer.
|
|
||||||
Sets |*encoded_size| to the number of bytes that was written.
|
|
||||||
Note that the given input data will not be part of the sliding window and
|
|
||||||
thus no backward references can be made to this data from subsequent
|
|
||||||
metablocks. |input_size| must not be greater than 2^24 and provided
|
|
||||||
|*encoded_size| must not be less than |input_size| + 6.
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
bool WriteMetadata(const size_t input_size,
|
|
||||||
const uint8_t* input_buffer,
|
|
||||||
const bool is_last,
|
|
||||||
size_t* encoded_size,
|
|
||||||
uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Writes a zero-length meta-block with end-of-input bit set to the
|
|
||||||
internal output buffer and copies the output buffer to |encoded_buffer|
|
|
||||||
(|*encoded_size| should be set to the size of |encoded_buffer|) and sets
|
|
||||||
|*encoded_size| to the number of bytes written.
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
bool FinishStream(size_t* encoded_size, uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Copies the given input data to the internal ring buffer of the compressor.
|
|
||||||
No processing of the data occurs at this time and this function can be
|
|
||||||
called multiple times before calling WriteBrotliData() to process the
|
|
||||||
accumulated input. At most input_block_size() bytes of input data can be
|
|
||||||
copied to the ring buffer, otherwise the next WriteBrotliData() will fail.
|
|
||||||
*/
|
|
||||||
void CopyInputToRingBuffer(const size_t input_size,
|
|
||||||
const uint8_t* input_buffer);
|
|
||||||
|
|
||||||
/* Processes the accumulated input data and sets |*out_size| to the length of
|
|
||||||
the new output meta-block, or to zero if no new output meta-block has been
|
|
||||||
created (in this case the processed input data is buffered internally).
|
|
||||||
If |*out_size| is positive, |*output| points to the start of the output
|
|
||||||
data. If |is_last| or |force_flush| is true, an output meta-block is always
|
|
||||||
created. However, until |is_last| is true encoder may retain up to 7 bits
|
|
||||||
of the last byte of output. To force encoder to dump the remaining bits
|
|
||||||
use WriteMetadata() to append an empty meta-data block.
|
|
||||||
Returns false if the size of the input data is larger than
|
|
||||||
input_block_size(). */
|
|
||||||
bool WriteBrotliData(const bool is_last, const bool force_flush,
|
|
||||||
size_t* out_size, uint8_t** output);
|
|
||||||
|
|
||||||
/* Fills the new state with a dictionary for LZ77, warming up the ringbuffer,
|
|
||||||
e.g. for custom static dictionaries for data formats.
|
|
||||||
Not to be confused with the built-in transformable dictionary of Brotli.
|
|
||||||
To decode, use BrotliSetCustomDictionary() of the decoder with the same
|
|
||||||
dictionary. */
|
|
||||||
void BrotliSetCustomDictionary(size_t size, const uint8_t* dict);
|
|
||||||
|
|
||||||
/* No-op, but we keep it here for API backward-compatibility. */
|
|
||||||
void WriteStreamHeader(void) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
BrotliEncoderState* state_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Compresses the data in |input_buffer| into |encoded_buffer|, and sets
|
|
||||||
|*encoded_size| to the compressed length.
|
|
||||||
Returns 0 if there was an error and 1 otherwise. */
|
|
||||||
int BrotliCompressBuffer(BrotliParams params,
|
|
||||||
size_t input_size,
|
|
||||||
const uint8_t* input_buffer,
|
|
||||||
size_t* encoded_size,
|
|
||||||
uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Same as above, but uses the specified input and output classes instead
|
|
||||||
of reading from and writing to pre-allocated memory buffers. */
|
|
||||||
int BrotliCompress(BrotliParams params, BrotliIn* in, BrotliOut* out);
|
|
||||||
|
|
||||||
/* Before compressing the data, sets a custom LZ77 dictionary with
|
|
||||||
BrotliCompressor::BrotliSetCustomDictionary. */
|
|
||||||
int BrotliCompressWithCustomDictionary(size_t dictsize, const uint8_t* dict,
|
|
||||||
BrotliParams params,
|
|
||||||
BrotliIn* in, BrotliOut* out);
|
|
||||||
|
|
||||||
} /* namespace brotli */
|
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_COMPRESSOR_H_ */
|
|
|
@ -1,184 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Functions to map previous bytes into a context id. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_CONTEXT_H_
|
|
||||||
#define BROTLI_ENC_CONTEXT_H_
|
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
#include "../common/port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Second-order context lookup table for UTF8 byte streams.
|
|
||||||
|
|
||||||
If p1 and p2 are the previous two bytes, we calculate the context as
|
|
||||||
|
|
||||||
context = kUTF8ContextLookup[p1] | kUTF8ContextLookup[p2 + 256].
|
|
||||||
|
|
||||||
If the previous two bytes are ASCII characters (i.e. < 128), this will be
|
|
||||||
equivalent to
|
|
||||||
|
|
||||||
context = 4 * context1(p1) + context2(p2),
|
|
||||||
|
|
||||||
where context1 is based on the previous byte in the following way:
|
|
||||||
|
|
||||||
0 : non-ASCII control
|
|
||||||
1 : \t, \n, \r
|
|
||||||
2 : space
|
|
||||||
3 : other punctuation
|
|
||||||
4 : " '
|
|
||||||
5 : %
|
|
||||||
6 : ( < [ {
|
|
||||||
7 : ) > ] }
|
|
||||||
8 : , ; :
|
|
||||||
9 : .
|
|
||||||
10 : =
|
|
||||||
11 : number
|
|
||||||
12 : upper-case vowel
|
|
||||||
13 : upper-case consonant
|
|
||||||
14 : lower-case vowel
|
|
||||||
15 : lower-case consonant
|
|
||||||
|
|
||||||
and context2 is based on the second last byte:
|
|
||||||
|
|
||||||
0 : control, space
|
|
||||||
1 : punctuation
|
|
||||||
2 : upper-case letter, number
|
|
||||||
3 : lower-case letter
|
|
||||||
|
|
||||||
If the last byte is ASCII, and the second last byte is not (in a valid UTF8
|
|
||||||
stream it will be a continuation byte, value between 128 and 191), the
|
|
||||||
context is the same as if the second last byte was an ASCII control or space.
|
|
||||||
|
|
||||||
If the last byte is a UTF8 lead byte (value >= 192), then the next byte will
|
|
||||||
be a continuation byte and the context id is 2 or 3 depending on the LSB of
|
|
||||||
the last byte and to a lesser extent on the second last byte if it is ASCII.
|
|
||||||
|
|
||||||
If the last byte is a UTF8 continuation byte, the second last byte can be:
|
|
||||||
- continuation byte: the next byte is probably ASCII or lead byte (assuming
|
|
||||||
4-byte UTF8 characters are rare) and the context id is 0 or 1.
|
|
||||||
- lead byte (192 - 207): next byte is ASCII or lead byte, context is 0 or 1
|
|
||||||
- lead byte (208 - 255): next byte is continuation byte, context is 2 or 3
|
|
||||||
|
|
||||||
The possible value combinations of the previous two bytes, the range of
|
|
||||||
context ids and the type of the next byte is summarized in the table below:
|
|
||||||
|
|
||||||
|--------\-----------------------------------------------------------------|
|
|
||||||
| \ Last byte |
|
|
||||||
| Second \---------------------------------------------------------------|
|
|
||||||
| last byte \ ASCII | cont. byte | lead byte |
|
|
||||||
| \ (0-127) | (128-191) | (192-) |
|
|
||||||
|=============|===================|=====================|==================|
|
|
||||||
| ASCII | next: ASCII/lead | not valid | next: cont. |
|
|
||||||
| (0-127) | context: 4 - 63 | | context: 2 - 3 |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
| cont. byte | next: ASCII/lead | next: ASCII/lead | next: cont. |
|
|
||||||
| (128-191) | context: 4 - 63 | context: 0 - 1 | context: 2 - 3 |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
| lead byte | not valid | next: ASCII/lead | not valid |
|
|
||||||
| (192-207) | | context: 0 - 1 | |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
| lead byte | not valid | next: cont. | not valid |
|
|
||||||
| (208-) | | context: 2 - 3 | |
|
|
||||||
|-------------|-------------------|---------------------|------------------|
|
|
||||||
*/
|
|
||||||
static const uint8_t kUTF8ContextLookup[512] = {
|
|
||||||
/* Last byte. */
|
|
||||||
/* */
|
|
||||||
/* ASCII range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 4, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
8, 12, 16, 12, 12, 20, 12, 16, 24, 28, 12, 12, 32, 12, 36, 12,
|
|
||||||
44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 32, 32, 24, 40, 28, 12,
|
|
||||||
12, 48, 52, 52, 52, 48, 52, 52, 52, 48, 52, 52, 52, 52, 52, 48,
|
|
||||||
52, 52, 52, 52, 52, 48, 52, 52, 52, 52, 52, 24, 12, 28, 12, 12,
|
|
||||||
12, 56, 60, 60, 60, 56, 60, 60, 60, 56, 60, 60, 60, 60, 60, 56,
|
|
||||||
60, 60, 60, 60, 60, 56, 60, 60, 60, 60, 60, 24, 12, 28, 12, 0,
|
|
||||||
/* UTF8 continuation byte range. */
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
|
||||||
/* UTF8 lead byte range. */
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
|
||||||
/* Second last byte. */
|
|
||||||
/* */
|
|
||||||
/* ASCII range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
|
|
||||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1,
|
|
||||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 0,
|
|
||||||
/* UTF8 continuation byte range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
/* UTF8 lead byte range. */
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Context lookup table for small signed integers. */
|
|
||||||
static const uint8_t kSigned3BitContextLookup[] = {
|
|
||||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum ContextType {
|
|
||||||
CONTEXT_LSB6 = 0,
|
|
||||||
CONTEXT_MSB6 = 1,
|
|
||||||
CONTEXT_UTF8 = 2,
|
|
||||||
CONTEXT_SIGNED = 3
|
|
||||||
} ContextType;
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint8_t Context(uint8_t p1, uint8_t p2, ContextType mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case CONTEXT_LSB6:
|
|
||||||
return p1 & 0x3f;
|
|
||||||
case CONTEXT_MSB6:
|
|
||||||
return (uint8_t)(p1 >> 2);
|
|
||||||
case CONTEXT_UTF8:
|
|
||||||
return kUTF8ContextLookup[p1] | kUTF8ContextLookup[p2 + 256];
|
|
||||||
case CONTEXT_SIGNED:
|
|
||||||
return (uint8_t)((kSigned3BitContextLookup[p1] << 3) +
|
|
||||||
kSigned3BitContextLookup[p2]);
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_CONTEXT_H_ */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,221 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* API for Brotli compression. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_ENCODE_H_
|
|
||||||
#define BROTLI_ENC_ENCODE_H_
|
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const int kBrotliMaxWindowBits = 24;
|
|
||||||
static const int kBrotliMinWindowBits = 10;
|
|
||||||
static const int kBrotliMinInputBlockBits = 16;
|
|
||||||
static const int kBrotliMaxInputBlockBits = 24;
|
|
||||||
|
|
||||||
#define BROTLI_MIN_QUALITY 0
|
|
||||||
#define BROTLI_MAX_QUALITY 11
|
|
||||||
|
|
||||||
typedef enum BrotliEncoderMode {
|
|
||||||
/* Default compression mode. The compressor does not know anything in
|
|
||||||
advance about the properties of the input. */
|
|
||||||
BROTLI_MODE_GENERIC = 0,
|
|
||||||
/* Compression mode for UTF-8 format text input. */
|
|
||||||
BROTLI_MODE_TEXT = 1,
|
|
||||||
/* Compression mode used in WOFF 2.0. */
|
|
||||||
BROTLI_MODE_FONT = 2
|
|
||||||
} BrotliEncoderMode;
|
|
||||||
|
|
||||||
#define BROTLI_DEFAULT_QUALITY 11
|
|
||||||
#define BROTLI_DEFAULT_WINDOW 22
|
|
||||||
#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC
|
|
||||||
|
|
||||||
typedef enum BrotliEncoderOperation {
|
|
||||||
BROTLI_OPERATION_PROCESS = 0,
|
|
||||||
/* Request output stream to flush. Performed when input stream is depleted
|
|
||||||
and there is enough space in output stream. */
|
|
||||||
BROTLI_OPERATION_FLUSH = 1,
|
|
||||||
/* Request output stream to finish. Performed when input stream is depleted
|
|
||||||
and there is enough space in output stream. */
|
|
||||||
BROTLI_OPERATION_FINISH = 2
|
|
||||||
} BrotliEncoderOperation;
|
|
||||||
|
|
||||||
typedef enum BrotliEncoderParameter {
|
|
||||||
BROTLI_PARAM_MODE = 0,
|
|
||||||
/* Controls the compression-speed vs compression-density tradeoffs. The higher
|
|
||||||
the quality, the slower the compression. Range is 0 to 11. */
|
|
||||||
BROTLI_PARAM_QUALITY = 1,
|
|
||||||
/* Base 2 logarithm of the sliding window size. Range is 10 to 24. */
|
|
||||||
BROTLI_PARAM_LGWIN = 2,
|
|
||||||
/* Base 2 logarithm of the maximum input block size. Range is 16 to 24.
|
|
||||||
If set to 0, the value will be set based on the quality. */
|
|
||||||
BROTLI_PARAM_LGBLOCK = 3
|
|
||||||
} BrotliEncoderParameter;
|
|
||||||
|
|
||||||
/* A state can not be reused for multiple brotli streams. */
|
|
||||||
typedef struct BrotliEncoderStateStruct BrotliEncoderState;
|
|
||||||
|
|
||||||
BROTLI_BOOL BrotliEncoderSetParameter(
|
|
||||||
BrotliEncoderState* state, BrotliEncoderParameter p, uint32_t value);
|
|
||||||
|
|
||||||
/* Creates the instance of BrotliEncoderState and initializes it.
|
|
||||||
|alloc_func| and |free_func| MUST be both zero or both non-zero. In the case
|
|
||||||
they are both zero, default memory allocators are used. |opaque| is passed to
|
|
||||||
|alloc_func| and |free_func| when they are called. */
|
|
||||||
BrotliEncoderState* BrotliEncoderCreateInstance(brotli_alloc_func alloc_func,
|
|
||||||
brotli_free_func free_func,
|
|
||||||
void* opaque);
|
|
||||||
|
|
||||||
/* Deinitializes and frees BrotliEncoderState instance. */
|
|
||||||
void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
|
|
||||||
/* The maximum input size that can be processed at once. */
|
|
||||||
size_t BrotliEncoderInputBlockSize(BrotliEncoderState* state);
|
|
||||||
|
|
||||||
/* Encodes the data in |input_buffer| as a meta-block and writes it to
|
|
||||||
|encoded_buffer| (|*encoded_size should| be set to the size of
|
|
||||||
|encoded_buffer|) and sets |*encoded_size| to the number of bytes that
|
|
||||||
was written. The |input_size| must not be greater than input_block_size().
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
BROTLI_BOOL BrotliEncoderWriteMetaBlock(
|
|
||||||
BrotliEncoderState* state, const size_t input_size,
|
|
||||||
const uint8_t* input_buffer, const BROTLI_BOOL is_last,
|
|
||||||
size_t* encoded_size, uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Writes a metadata meta-block containing the given input to encoded_buffer.
|
|
||||||
|*encoded_size| should be set to the size of the encoded_buffer.
|
|
||||||
Sets |*encoded_size| to the number of bytes that was written.
|
|
||||||
Note that the given input data will not be part of the sliding window and
|
|
||||||
thus no backward references can be made to this data from subsequent
|
|
||||||
metablocks. |input_size| must not be greater than 2^24 and provided
|
|
||||||
|*encoded_size| must not be less than |input_size| + 6.
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
BROTLI_BOOL BrotliEncoderWriteMetadata(
|
|
||||||
BrotliEncoderState* state, const size_t input_size,
|
|
||||||
const uint8_t* input_buffer, const BROTLI_BOOL is_last,
|
|
||||||
size_t* encoded_size, uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Writes a zero-length meta-block with end-of-input bit set to the
|
|
||||||
internal output buffer and copies the output buffer to |encoded_buffer|
|
|
||||||
(|*encoded_size| should be set to the size of |encoded_buffer|) and sets
|
|
||||||
|*encoded_size| to the number of bytes written.
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
BROTLI_BOOL BrotliEncoderFinishStream(
|
|
||||||
BrotliEncoderState* state, size_t* encoded_size, uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Copies the given input data to the internal ring buffer of the compressor.
|
|
||||||
No processing of the data occurs at this time and this function can be
|
|
||||||
called multiple times before calling WriteBrotliData() to process the
|
|
||||||
accumulated input. At most input_block_size() bytes of input data can be
|
|
||||||
copied to the ring buffer, otherwise the next WriteBrotliData() will fail.
|
|
||||||
*/
|
|
||||||
void BrotliEncoderCopyInputToRingBuffer(BrotliEncoderState* state,
|
|
||||||
const size_t input_size,
|
|
||||||
const uint8_t* input_buffer);
|
|
||||||
|
|
||||||
/* Processes the accumulated input data and sets |*out_size| to the length of
|
|
||||||
the new output meta-block, or to zero if no new output meta-block has been
|
|
||||||
created (in this case the processed input data is buffered internally).
|
|
||||||
If |*out_size| is positive, |*output| points to the start of the output
|
|
||||||
data. If |is_last| or |force_flush| is 1, an output meta-block is always
|
|
||||||
created. However, until |is_last| is 1 encoder may retain up to 7 bits
|
|
||||||
of the last byte of output. To force encoder to dump the remaining bits
|
|
||||||
use WriteMetadata() to append an empty meta-data block.
|
|
||||||
Returns false if the size of the input data is larger than
|
|
||||||
input_block_size(). */
|
|
||||||
BROTLI_BOOL BrotliEncoderWriteData(
|
|
||||||
BrotliEncoderState* state, const BROTLI_BOOL is_last,
|
|
||||||
const BROTLI_BOOL force_flush, size_t* out_size, uint8_t** output);
|
|
||||||
|
|
||||||
/* Fills the new state with a dictionary for LZ77, warming up the ringbuffer,
|
|
||||||
e.g. for custom static dictionaries for data formats.
|
|
||||||
Not to be confused with the built-in transformable dictionary of Brotli.
|
|
||||||
To decode, use BrotliSetCustomDictionary() of the decoder with the same
|
|
||||||
dictionary. */
|
|
||||||
void BrotliEncoderSetCustomDictionary(BrotliEncoderState* state, size_t size,
|
|
||||||
const uint8_t* dict);
|
|
||||||
|
|
||||||
/* Returns buffer size that is large enough to contain BrotliEncoderCompress
|
|
||||||
output for any input.
|
|
||||||
Returns 0 if result does not fit size_t. */
|
|
||||||
size_t BrotliEncoderMaxCompressedSize(size_t input_size);
|
|
||||||
|
|
||||||
/* Compresses the data in |input_buffer| into |encoded_buffer|, and sets
|
|
||||||
|*encoded_size| to the compressed length.
|
|
||||||
BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW and BROTLI_DEFAULT_MODE should
|
|
||||||
be used as |quality|, |lgwin| and |mode| if there are no specific
|
|
||||||
requirements to encoder speed and compression ratio.
|
|
||||||
If compression fails, |*encoded_size| is set to 0.
|
|
||||||
If BrotliEncoderMaxCompressedSize(|input_size|) is not zero, then
|
|
||||||
|*encoded_size| is never set to the bigger value.
|
|
||||||
Returns false if there was an error and true otherwise. */
|
|
||||||
BROTLI_BOOL BrotliEncoderCompress(
|
|
||||||
int quality, int lgwin, BrotliEncoderMode mode, size_t input_size,
|
|
||||||
const uint8_t* input_buffer, size_t* encoded_size, uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
/* Progressively compress input stream and push produced bytes to output stream.
|
|
||||||
Internally workflow consists of 3 tasks:
|
|
||||||
* (optional) copy input data to internal buffer
|
|
||||||
* actually compress data and (optionally) store it to internal buffer
|
|
||||||
* (optional) copy compressed bytes from internal buffer to output stream
|
|
||||||
Whenever all 3 tasks can't move forward anymore, or error occurs, this
|
|
||||||
method returns.
|
|
||||||
|
|
||||||
|available_in| and |next_in| represent input stream; when X bytes of input
|
|
||||||
are consumed, X is subtracted from |available_in| and added to |next_in|.
|
|
||||||
|available_out| and |next_out| represent output stream; when Y bytes are
|
|
||||||
pushed to output, Y is subtracted from |available_out| and added to
|
|
||||||
|next_out|. |total_out|, if it is not a null-pointer, is assigned to the
|
|
||||||
total amount of bytes pushed by the instance of encoder to output.
|
|
||||||
|
|
||||||
|op| is used to perform flush or finish the stream.
|
|
||||||
|
|
||||||
Flushing the stream means forcing encoding of all input passed to encoder and
|
|
||||||
completing the current output block, so it could be fully decoded by stream
|
|
||||||
decoder. To perform flush |op| must be set to BROTLI_OPERATION_FLUSH. Under
|
|
||||||
some circumstances (e.g. lack of output stream capacity) this operation would
|
|
||||||
require several calls to BrotliEncoderCompressStream. The method must be
|
|
||||||
called again until both input stream is depleted and encoder has no more
|
|
||||||
output (see BrotliEncoderHasMoreOutput) after the method is called.
|
|
||||||
|
|
||||||
Finishing the stream means encoding of all input passed to encoder and
|
|
||||||
adding specific "final" marks, so stream decoder could determine that stream
|
|
||||||
is complete. To perform finish |op| must be set to BROTLI_OPERATION_FINISH.
|
|
||||||
Under some circumstances (e.g. lack of output stream capacity) this operation
|
|
||||||
would require several calls to BrotliEncoderCompressStream. The method must
|
|
||||||
be called again until both input stream is depleted and encoder has no more
|
|
||||||
output (see BrotliEncoderHasMoreOutput) after the method is called.
|
|
||||||
|
|
||||||
WARNING: when flushing and finishing, |op| should not change until operation
|
|
||||||
is complete; input stream should not be refilled as well.
|
|
||||||
|
|
||||||
Returns false if there was an error and true otherwise.
|
|
||||||
*/
|
|
||||||
BROTLI_BOOL BrotliEncoderCompressStream(
|
|
||||||
BrotliEncoderState* s, BrotliEncoderOperation op, size_t* available_in,
|
|
||||||
const uint8_t** next_in, size_t* available_out, uint8_t** next_out,
|
|
||||||
size_t* total_out);
|
|
||||||
|
|
||||||
/* Check if encoder is in "finished" state, i.e. no more input is acceptable and
|
|
||||||
no more output will be produced.
|
|
||||||
Works only with BrotliEncoderCompressStream workflow.
|
|
||||||
Returns 1 if stream is finished and 0 otherwise. */
|
|
||||||
BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* s);
|
|
||||||
|
|
||||||
/* Check if encoder has more output bytes in internal buffer.
|
|
||||||
Works only with BrotliEncoderCompressStream workflow.
|
|
||||||
Returns 1 if has more output (in internal buffer) and 0 otherwise. */
|
|
||||||
BROTLI_BOOL BrotliEncoderHasMoreOutput(BrotliEncoderState* s);
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_ENCODE_H_ */
|
|
|
@ -1,27 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* API for parallel Brotli compression
|
|
||||||
Note that this is only a proof of concept currently and not part of the
|
|
||||||
final API yet. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_ENCODE_PARALLEL_H_
|
|
||||||
#define BROTLI_ENC_ENCODE_PARALLEL_H_
|
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
#include "./compressor.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
int BrotliCompressBufferParallel(BrotliParams params,
|
|
||||||
size_t input_size,
|
|
||||||
const uint8_t* input_buffer,
|
|
||||||
size_t* encoded_size,
|
|
||||||
uint8_t* encoded_buffer);
|
|
||||||
|
|
||||||
} /* namespace brotli */
|
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_ENCODE_PARALLEL_H_ */
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Copyright 2017 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "./encoder_dict.h"
|
||||||
|
|
||||||
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/transform.h"
|
||||||
|
#include "./dictionary_hash.h"
|
||||||
|
#include "./hash.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void BrotliInitEncoderDictionary(BrotliEncoderDictionary* dict) {
|
||||||
|
dict->words = BrotliGetDictionary();
|
||||||
|
|
||||||
|
dict->hash_table = kStaticDictionaryHash;
|
||||||
|
dict->buckets = kStaticDictionaryBuckets;
|
||||||
|
dict->dict_words = kStaticDictionaryWords;
|
||||||
|
|
||||||
|
dict->cutoffTransformsCount = kCutoffTransformsCount;
|
||||||
|
dict->cutoffTransforms = kCutoffTransforms;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* Copyright 2017 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROTLI_ENC_ENCODER_DICT_H_
|
||||||
|
#define BROTLI_ENC_ENCODER_DICT_H_
|
||||||
|
|
||||||
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
|
#include "./static_dict_lut.h"
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Dictionary data (words and transforms) for 1 possible context */
|
||||||
|
typedef struct BrotliEncoderDictionary {
|
||||||
|
const BrotliDictionary* words;
|
||||||
|
|
||||||
|
/* cut off for fast encoder */
|
||||||
|
uint32_t cutoffTransformsCount;
|
||||||
|
uint64_t cutoffTransforms;
|
||||||
|
|
||||||
|
/* from dictionary_hash.h, for fast encoder */
|
||||||
|
const uint16_t* hash_table;
|
||||||
|
|
||||||
|
/* from static_dict_lut.h, for slow encoder */
|
||||||
|
const uint16_t* buckets;
|
||||||
|
const DictWord* dict_words;
|
||||||
|
} BrotliEncoderDictionary;
|
||||||
|
|
||||||
|
BROTLI_INTERNAL void BrotliInitEncoderDictionary(BrotliEncoderDictionary* dict);
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BROTLI_ENC_ENCODER_DICT_H_ */
|
|
@ -11,8 +11,8 @@
|
||||||
#include <string.h> /* memset */
|
#include <string.h> /* memset */
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -23,7 +23,7 @@ BROTLI_BOOL BrotliSetDepth(
|
||||||
int stack[16];
|
int stack[16];
|
||||||
int level = 0;
|
int level = 0;
|
||||||
int p = p0;
|
int p = p0;
|
||||||
assert(max_depth <= 15);
|
BROTLI_DCHECK(max_depth <= 15);
|
||||||
stack[0] = -1;
|
stack[0] = -1;
|
||||||
while (BROTLI_TRUE) {
|
while (BROTLI_TRUE) {
|
||||||
if (pool[p].index_left_ >= 0) {
|
if (pool[p].index_left_ >= 0) {
|
||||||
|
@ -66,11 +66,11 @@ static BROTLI_INLINE BROTLI_BOOL SortHuffmanTree(
|
||||||
we are not planning to use this with extremely long blocks.
|
we are not planning to use this with extremely long blocks.
|
||||||
|
|
||||||
See http://en.wikipedia.org/wiki/Huffman_coding */
|
See http://en.wikipedia.org/wiki/Huffman_coding */
|
||||||
void BrotliCreateHuffmanTree(const uint32_t *data,
|
void BrotliCreateHuffmanTree(const uint32_t* data,
|
||||||
const size_t length,
|
const size_t length,
|
||||||
const int tree_limit,
|
const int tree_limit,
|
||||||
HuffmanTree* tree,
|
HuffmanTree* tree,
|
||||||
uint8_t *depth) {
|
uint8_t* depth) {
|
||||||
uint32_t count_limit;
|
uint32_t count_limit;
|
||||||
HuffmanTree sentinel;
|
HuffmanTree sentinel;
|
||||||
InitHuffmanTree(&sentinel, BROTLI_UINT32_MAX, -1, -1);
|
InitHuffmanTree(&sentinel, BROTLI_UINT32_MAX, -1, -1);
|
||||||
|
@ -165,7 +165,7 @@ static void BrotliWriteHuffmanTreeRepetitions(
|
||||||
size_t* tree_size,
|
size_t* tree_size,
|
||||||
uint8_t* tree,
|
uint8_t* tree,
|
||||||
uint8_t* extra_bits_data) {
|
uint8_t* extra_bits_data) {
|
||||||
assert(repetitions > 0);
|
BROTLI_DCHECK(repetitions > 0);
|
||||||
if (previous_value != value) {
|
if (previous_value != value) {
|
||||||
tree[*tree_size] = value;
|
tree[*tree_size] = value;
|
||||||
extra_bits_data[*tree_size] = 0;
|
extra_bits_data[*tree_size] = 0;
|
||||||
|
@ -246,7 +246,7 @@ void BrotliOptimizeHuffmanCountsForRle(size_t length, uint32_t* counts,
|
||||||
size_t limit;
|
size_t limit;
|
||||||
size_t sum;
|
size_t sum;
|
||||||
const size_t streak_limit = 1240;
|
const size_t streak_limit = 1240;
|
||||||
/* Let's make the Huffman code more compatible with rle encoding. */
|
/* Let's make the Huffman code more compatible with RLE encoding. */
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
if (counts[i]) {
|
if (counts[i]) {
|
||||||
|
@ -293,10 +293,10 @@ void BrotliOptimizeHuffmanCountsForRle(size_t length, uint32_t* counts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 2) Let's mark all population counts that already can be encoded
|
/* 2) Let's mark all population counts that already can be encoded
|
||||||
with an rle code. */
|
with an RLE code. */
|
||||||
memset(good_for_rle, 0, length);
|
memset(good_for_rle, 0, length);
|
||||||
{
|
{
|
||||||
/* Let's not spoil any of the existing good rle codes.
|
/* Let's not spoil any of the existing good RLE codes.
|
||||||
Mark any seq of 0's that is longer as 5 as a good_for_rle.
|
Mark any seq of 0's that is longer as 5 as a good_for_rle.
|
||||||
Mark any seq of non-0's that is longer as 7 as a good_for_rle. */
|
Mark any seq of non-0's that is longer as 7 as a good_for_rle. */
|
||||||
uint32_t symbol = counts[0];
|
uint32_t symbol = counts[0];
|
||||||
|
@ -319,7 +319,7 @@ void BrotliOptimizeHuffmanCountsForRle(size_t length, uint32_t* counts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 3) Let's replace those population counts that lead to more rle codes.
|
/* 3) Let's replace those population counts that lead to more RLE codes.
|
||||||
Math here is in 24.8 fixed point representation. */
|
Math here is in 24.8 fixed point representation. */
|
||||||
stride = 0;
|
stride = 0;
|
||||||
limit = 256 * (counts[0] + counts[1] + counts[2]) / 3 + 420;
|
limit = 256 * (counts[0] + counts[1] + counts[2]) / 3 + 420;
|
||||||
|
@ -371,8 +371,8 @@ void BrotliOptimizeHuffmanCountsForRle(size_t length, uint32_t* counts,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DecideOverRleUse(const uint8_t* depth, const size_t length,
|
static void DecideOverRleUse(const uint8_t* depth, const size_t length,
|
||||||
BROTLI_BOOL *use_rle_for_non_zero,
|
BROTLI_BOOL* use_rle_for_non_zero,
|
||||||
BROTLI_BOOL *use_rle_for_zero) {
|
BROTLI_BOOL* use_rle_for_zero) {
|
||||||
size_t total_reps_zero = 0;
|
size_t total_reps_zero = 0;
|
||||||
size_t total_reps_non_zero = 0;
|
size_t total_reps_non_zero = 0;
|
||||||
size_t count_reps_zero = 1;
|
size_t count_reps_zero = 1;
|
||||||
|
@ -420,15 +420,15 @@ void BrotliWriteHuffmanTree(const uint8_t* depth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First gather statistics on if it is a good idea to do rle. */
|
/* First gather statistics on if it is a good idea to do RLE. */
|
||||||
if (length > 50) {
|
if (length > 50) {
|
||||||
/* Find rle coding for longer codes.
|
/* Find RLE coding for longer codes.
|
||||||
Shorter codes seem not to benefit from rle. */
|
Shorter codes seem not to benefit from RLE. */
|
||||||
DecideOverRleUse(depth, new_length,
|
DecideOverRleUse(depth, new_length,
|
||||||
&use_rle_for_non_zero, &use_rle_for_zero);
|
&use_rle_for_non_zero, &use_rle_for_zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actual rle coding. */
|
/* Actual RLE coding. */
|
||||||
for (i = 0; i < new_length;) {
|
for (i = 0; i < new_length;) {
|
||||||
const uint8_t value = depth[i];
|
const uint8_t value = depth[i];
|
||||||
size_t reps = 1;
|
size_t reps = 1;
|
||||||
|
@ -454,26 +454,26 @@ void BrotliWriteHuffmanTree(const uint8_t* depth,
|
||||||
|
|
||||||
static uint16_t BrotliReverseBits(size_t num_bits, uint16_t bits) {
|
static uint16_t BrotliReverseBits(size_t num_bits, uint16_t bits) {
|
||||||
static const size_t kLut[16] = { /* Pre-reversed 4-bit values. */
|
static const size_t kLut[16] = { /* Pre-reversed 4-bit values. */
|
||||||
0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
|
0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E,
|
||||||
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
|
0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F
|
||||||
};
|
};
|
||||||
size_t retval = kLut[bits & 0xf];
|
size_t retval = kLut[bits & 0x0F];
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 4; i < num_bits; i += 4) {
|
for (i = 4; i < num_bits; i += 4) {
|
||||||
retval <<= 4;
|
retval <<= 4;
|
||||||
bits = (uint16_t)(bits >> 4);
|
bits = (uint16_t)(bits >> 4);
|
||||||
retval |= kLut[bits & 0xf];
|
retval |= kLut[bits & 0x0F];
|
||||||
}
|
}
|
||||||
retval >>= ((0 - num_bits) & 0x3);
|
retval >>= ((0 - num_bits) & 0x03);
|
||||||
return (uint16_t)retval;
|
return (uint16_t)retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 0..15 are values for bits */
|
/* 0..15 are values for bits */
|
||||||
#define MAX_HUFFMAN_BITS 16
|
#define MAX_HUFFMAN_BITS 16
|
||||||
|
|
||||||
void BrotliConvertBitDepthsToSymbols(const uint8_t *depth,
|
void BrotliConvertBitDepthsToSymbols(const uint8_t* depth,
|
||||||
size_t len,
|
size_t len,
|
||||||
uint16_t *bits) {
|
uint16_t* bits) {
|
||||||
/* In Brotli, all bit depths are [1..15]
|
/* In Brotli, all bit depths are [1..15]
|
||||||
0 bit depth means that the symbol does not exist. */
|
0 bit depth means that the symbol does not exist. */
|
||||||
uint16_t bl_count[MAX_HUFFMAN_BITS] = { 0 };
|
uint16_t bl_count[MAX_HUFFMAN_BITS] = { 0 };
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef BROTLI_ENC_ENTROPY_ENCODE_H_
|
#ifndef BROTLI_ENC_ENTROPY_ENCODE_H_
|
||||||
#define BROTLI_ENC_ENTROPY_ENCODE_H_
|
#define BROTLI_ENC_ENTROPY_ENCODE_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -30,7 +30,7 @@ static BROTLI_INLINE void InitHuffmanTree(HuffmanTree* self, uint32_t count,
|
||||||
self->index_right_or_value_ = right;
|
self->index_right_or_value_ = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 is assignment of depths succeded, otherwise 0. */
|
/* Returns 1 is assignment of depths succeeded, otherwise 0. */
|
||||||
BROTLI_INTERNAL BROTLI_BOOL BrotliSetDepth(
|
BROTLI_INTERNAL BROTLI_BOOL BrotliSetDepth(
|
||||||
int p, HuffmanTree* pool, uint8_t* depth, int max_depth);
|
int p, HuffmanTree* pool, uint8_t* depth, int max_depth);
|
||||||
|
|
||||||
|
@ -46,14 +46,14 @@ BROTLI_INTERNAL BROTLI_BOOL BrotliSetDepth(
|
||||||
be at least 2 * length + 1 long.
|
be at least 2 * length + 1 long.
|
||||||
|
|
||||||
See http://en.wikipedia.org/wiki/Huffman_coding */
|
See http://en.wikipedia.org/wiki/Huffman_coding */
|
||||||
BROTLI_INTERNAL void BrotliCreateHuffmanTree(const uint32_t *data,
|
BROTLI_INTERNAL void BrotliCreateHuffmanTree(const uint32_t* data,
|
||||||
const size_t length,
|
const size_t length,
|
||||||
const int tree_limit,
|
const int tree_limit,
|
||||||
HuffmanTree* tree,
|
HuffmanTree* tree,
|
||||||
uint8_t *depth);
|
uint8_t* depth);
|
||||||
|
|
||||||
/* Change the population counts in a way that the consequent
|
/* Change the population counts in a way that the consequent
|
||||||
Huffman tree compression, especially its rle-part will be more
|
Huffman tree compression, especially its RLE-part will be more
|
||||||
likely to compress this data more efficiently.
|
likely to compress this data more efficiently.
|
||||||
|
|
||||||
length contains the size of the histogram.
|
length contains the size of the histogram.
|
||||||
|
@ -62,7 +62,7 @@ BROTLI_INTERNAL void BrotliCreateHuffmanTree(const uint32_t *data,
|
||||||
BROTLI_INTERNAL void BrotliOptimizeHuffmanCountsForRle(
|
BROTLI_INTERNAL void BrotliOptimizeHuffmanCountsForRle(
|
||||||
size_t length, uint32_t* counts, uint8_t* good_for_rle);
|
size_t length, uint32_t* counts, uint8_t* good_for_rle);
|
||||||
|
|
||||||
/* Write a Huffman tree from bit depths into the bitstream representation
|
/* Write a Huffman tree from bit depths into the bit-stream representation
|
||||||
of a Huffman tree. The generated Huffman tree is to be compressed once
|
of a Huffman tree. The generated Huffman tree is to be compressed once
|
||||||
more using a Huffman tree */
|
more using a Huffman tree */
|
||||||
BROTLI_INTERNAL void BrotliWriteHuffmanTree(const uint8_t* depth,
|
BROTLI_INTERNAL void BrotliWriteHuffmanTree(const uint8_t* depth,
|
||||||
|
@ -72,9 +72,9 @@ BROTLI_INTERNAL void BrotliWriteHuffmanTree(const uint8_t* depth,
|
||||||
uint8_t* extra_bits_data);
|
uint8_t* extra_bits_data);
|
||||||
|
|
||||||
/* Get the actual bit values for a tree of bit depths. */
|
/* Get the actual bit values for a tree of bit depths. */
|
||||||
BROTLI_INTERNAL void BrotliConvertBitDepthsToSymbols(const uint8_t *depth,
|
BROTLI_INTERNAL void BrotliConvertBitDepthsToSymbols(const uint8_t* depth,
|
||||||
size_t len,
|
size_t len,
|
||||||
uint16_t *bits);
|
uint16_t* bits);
|
||||||
|
|
||||||
/* Input size optimized Shell sort. */
|
/* Input size optimized Shell sort. */
|
||||||
typedef BROTLI_BOOL (*HuffmanTreeComparator)(
|
typedef BROTLI_BOOL (*HuffmanTreeComparator)(
|
||||||
|
|
|
@ -10,8 +10,8 @@
|
||||||
#define BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_
|
#define BROTLI_ENC_ENTROPY_ENCODE_STATIC_H_
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/port.h"
|
#include "../common/platform.h"
|
||||||
#include "../common/types.h"
|
#include <brotli/types.h>
|
||||||
#include "./write_bits.h"
|
#include "./write_bits.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -83,7 +83,7 @@ static const uint32_t kCodeLengthBits[18] = {
|
||||||
static BROTLI_INLINE void StoreStaticCodeLengthCode(
|
static BROTLI_INLINE void StoreStaticCodeLengthCode(
|
||||||
size_t* storage_ix, uint8_t* storage) {
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
BrotliWriteBits(
|
BrotliWriteBits(
|
||||||
40, MAKE_UINT64_T(0x0000ffU, 0x55555554U), storage_ix, storage);
|
40, BROTLI_MAKE_UINT64_T(0x0000FFu, 0x55555554u), storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint64_t kZeroRepsBits[BROTLI_NUM_COMMAND_SYMBOLS] = {
|
static const uint64_t kZeroRepsBits[BROTLI_NUM_COMMAND_SYMBOLS] = {
|
||||||
|
@ -516,7 +516,7 @@ static const uint16_t kStaticCommandCodeBits[BROTLI_NUM_COMMAND_SYMBOLS] = {
|
||||||
static BROTLI_INLINE void StoreStaticCommandHuffmanTree(
|
static BROTLI_INLINE void StoreStaticCommandHuffmanTree(
|
||||||
size_t* storage_ix, uint8_t* storage) {
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
BrotliWriteBits(
|
BrotliWriteBits(
|
||||||
56, MAKE_UINT64_T(0x926244U, 0x16307003U), storage_ix, storage);
|
56, BROTLI_MAKE_UINT64_T(0x926244U, 0x16307003U), storage_ix, storage);
|
||||||
BrotliWriteBits(3, 0x00000000U, storage_ix, storage);
|
BrotliWriteBits(3, 0x00000000U, storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +529,7 @@ static const uint16_t kStaticDistanceCodeBits[64] = {
|
||||||
|
|
||||||
static BROTLI_INLINE void StoreStaticDistanceHuffmanTree(
|
static BROTLI_INLINE void StoreStaticDistanceHuffmanTree(
|
||||||
size_t* storage_ix, uint8_t* storage) {
|
size_t* storage_ix, uint8_t* storage) {
|
||||||
BrotliWriteBits(28, 0x0369dc03U, storage_ix, storage);
|
BrotliWriteBits(28, 0x0369DC03u, storage_ix, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
|
|
@ -11,15 +11,17 @@
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "../common/port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t Log2FloorNonZero(size_t n) {
|
static BROTLI_INLINE uint32_t Log2FloorNonZero(size_t n) {
|
||||||
#ifdef __GNUC__
|
/* TODO: generalize and move to platform.h */
|
||||||
|
#if BROTLI_GNUC_HAS_BUILTIN(__builtin_clz, 3, 4, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0)
|
||||||
return 31u ^ (uint32_t)__builtin_clz((uint32_t)n);
|
return 31u ^ (uint32_t)__builtin_clz((uint32_t)n);
|
||||||
#else
|
#else
|
||||||
uint32_t result = 0;
|
uint32_t result = 0;
|
||||||
|
|
|
@ -9,29 +9,29 @@
|
||||||
#ifndef BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
#ifndef BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
||||||
#define BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
#define BROTLI_ENC_FIND_MATCH_LENGTH_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Separate implementation for little-endian 64-bit targets, for speed. */
|
/* Separate implementation for little-endian 64-bit targets, for speed. */
|
||||||
#if defined(__GNUC__) && defined(_LP64) && defined(IS_LITTLE_ENDIAN)
|
#if defined(__GNUC__) && defined(_LP64) && defined(BROTLI_LITTLE_ENDIAN)
|
||||||
|
|
||||||
static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1,
|
static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1,
|
||||||
const uint8_t* s2,
|
const uint8_t* s2,
|
||||||
size_t limit) {
|
size_t limit) {
|
||||||
size_t matched = 0;
|
size_t matched = 0;
|
||||||
size_t limit2 = (limit >> 3) + 1; /* + 1 is for pre-decrement in while */
|
size_t limit2 = (limit >> 3) + 1; /* + 1 is for pre-decrement in while */
|
||||||
while (PREDICT_TRUE(--limit2)) {
|
while (BROTLI_PREDICT_TRUE(--limit2)) {
|
||||||
if (PREDICT_FALSE(BROTLI_UNALIGNED_LOAD64(s2) ==
|
if (BROTLI_PREDICT_FALSE(BROTLI_UNALIGNED_LOAD64LE(s2) ==
|
||||||
BROTLI_UNALIGNED_LOAD64(s1 + matched))) {
|
BROTLI_UNALIGNED_LOAD64LE(s1 + matched))) {
|
||||||
s2 += 8;
|
s2 += 8;
|
||||||
matched += 8;
|
matched += 8;
|
||||||
} else {
|
} else {
|
||||||
uint64_t x =
|
uint64_t x = BROTLI_UNALIGNED_LOAD64LE(s2) ^
|
||||||
BROTLI_UNALIGNED_LOAD64(s2) ^ BROTLI_UNALIGNED_LOAD64(s1 + matched);
|
BROTLI_UNALIGNED_LOAD64LE(s1 + matched);
|
||||||
size_t matching_bits = (size_t)__builtin_ctzll(x);
|
size_t matching_bits = (size_t)__builtin_ctzll(x);
|
||||||
matched += matching_bits >> 3;
|
matched += matching_bits >> 3;
|
||||||
return matched;
|
return matched;
|
||||||
|
@ -39,7 +39,7 @@ static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1,
|
||||||
}
|
}
|
||||||
limit = (limit & 7) + 1; /* + 1 is for pre-decrement in while */
|
limit = (limit & 7) + 1; /* + 1 is for pre-decrement in while */
|
||||||
while (--limit) {
|
while (--limit) {
|
||||||
if (PREDICT_TRUE(s1[matched] == *s2)) {
|
if (BROTLI_PREDICT_TRUE(s1[matched] == *s2)) {
|
||||||
++s2;
|
++s2;
|
||||||
++matched;
|
++matched;
|
||||||
} else {
|
} else {
|
||||||
|
@ -60,8 +60,8 @@ static BROTLI_INLINE size_t FindMatchLengthWithLimit(const uint8_t* s1,
|
||||||
the first non-matching bit and use that to calculate the total
|
the first non-matching bit and use that to calculate the total
|
||||||
length of the match. */
|
length of the match. */
|
||||||
while (s2_ptr <= s2_limit - 4 &&
|
while (s2_ptr <= s2_limit - 4 &&
|
||||||
BROTLI_UNALIGNED_LOAD32(s2_ptr) ==
|
BrotliUnalignedRead32(s2_ptr) ==
|
||||||
BROTLI_UNALIGNED_LOAD32(s1 + matched)) {
|
BrotliUnalignedRead32(s1 + matched)) {
|
||||||
s2_ptr += 4;
|
s2_ptr += 4;
|
||||||
matched += 4;
|
matched += 4;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/dictionary.h"
|
#include "../common/dictionary.h"
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./dictionary_hash.h"
|
#include <brotli/types.h>
|
||||||
|
#include "./encoder_dict.h"
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./find_match_length.h"
|
#include "./find_match_length.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
#include "./static_dict.h"
|
#include "./static_dict.h"
|
||||||
|
|
||||||
|
@ -27,51 +27,91 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_TREE_SEARCH_DEPTH 64
|
/* Pointer to hasher data.
|
||||||
#define MAX_TREE_COMP_LENGTH 128
|
*
|
||||||
|
* Excluding initialization and destruction, hasher can be passed as
|
||||||
|
* HasherHandle by value.
|
||||||
|
*
|
||||||
|
* Typically hasher data consists of 3 sections:
|
||||||
|
* * HasherCommon structure
|
||||||
|
* * private structured hasher data, depending on hasher type
|
||||||
|
* * private dynamic hasher data, depending on hasher type and parameters
|
||||||
|
*
|
||||||
|
* Using "define" instead of "typedef", because on MSVC __restrict does not work
|
||||||
|
* on typedef pointer types. */
|
||||||
|
#define HasherHandle uint8_t*
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BrotliHasherParams params;
|
||||||
|
|
||||||
|
/* False if hasher needs to be "prepared" before use. */
|
||||||
|
BROTLI_BOOL is_prepared_;
|
||||||
|
|
||||||
|
size_t dict_num_lookups;
|
||||||
|
size_t dict_num_matches;
|
||||||
|
} HasherCommon;
|
||||||
|
|
||||||
|
static BROTLI_INLINE HasherCommon* GetHasherCommon(HasherHandle handle) {
|
||||||
|
return (HasherCommon*)handle;
|
||||||
|
}
|
||||||
|
|
||||||
#define score_t size_t
|
#define score_t size_t
|
||||||
|
|
||||||
static const uint32_t kDistanceCacheIndex[] = {
|
|
||||||
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
|
||||||
};
|
|
||||||
static const int kDistanceCacheOffset[] = {
|
|
||||||
0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3
|
|
||||||
};
|
|
||||||
|
|
||||||
static const uint32_t kCutoffTransformsCount = 10;
|
static const uint32_t kCutoffTransformsCount = 10;
|
||||||
static const uint8_t kCutoffTransforms[] = {
|
/* 0, 12, 27, 23, 42, 63, 56, 48, 59, 64 */
|
||||||
0, 12, 27, 23, 42, 63, 56, 48, 59, 64
|
/* 0+0, 4+8, 8+19, 12+11, 16+26, 20+43, 24+32, 28+20, 32+27, 36+28 */
|
||||||
};
|
static const uint64_t kCutoffTransforms =
|
||||||
|
BROTLI_MAKE_UINT64_T(0x071B520A, 0xDA2D3200);
|
||||||
|
|
||||||
typedef struct HasherSearchResult {
|
typedef struct HasherSearchResult {
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t len_x_code; /* == len ^ len_code */
|
|
||||||
size_t distance;
|
size_t distance;
|
||||||
score_t score;
|
score_t score;
|
||||||
|
int len_code_delta; /* == len_code - len */
|
||||||
} HasherSearchResult;
|
} HasherSearchResult;
|
||||||
|
|
||||||
typedef struct DictionarySearchStatictics {
|
|
||||||
size_t num_lookups;
|
|
||||||
size_t num_matches;
|
|
||||||
} DictionarySearchStatictics;
|
|
||||||
|
|
||||||
/* kHashMul32 multiplier has these properties:
|
/* kHashMul32 multiplier has these properties:
|
||||||
* The multiplier must be odd. Otherwise we may lose the highest bit.
|
* The multiplier must be odd. Otherwise we may lose the highest bit.
|
||||||
* No long streaks of 1s or 0s.
|
* No long streaks of ones or zeros.
|
||||||
* There is no effort to ensure that it is a prime, the oddity is enough
|
* There is no effort to ensure that it is a prime, the oddity is enough
|
||||||
for this use.
|
for this use.
|
||||||
* The number has been tuned heuristically against compression benchmarks. */
|
* The number has been tuned heuristically against compression benchmarks. */
|
||||||
static const uint32_t kHashMul32 = 0x1e35a7bd;
|
static const uint32_t kHashMul32 = 0x1E35A7BD;
|
||||||
|
static const uint64_t kHashMul64 = BROTLI_MAKE_UINT64_T(0x1E35A7BD, 0x1E35A7BD);
|
||||||
|
static const uint64_t kHashMul64Long =
|
||||||
|
BROTLI_MAKE_UINT64_T(0x1FE35A7Bu, 0xD3579BD3u);
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t Hash14(const uint8_t* data) {
|
static BROTLI_INLINE uint32_t Hash14(const uint8_t* data) {
|
||||||
uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;
|
uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
|
||||||
/* The higher bits contain more mixture from the multiplication,
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
so we take our results from there. */
|
so we take our results from there. */
|
||||||
return h >> (32 - 14);
|
return h >> (32 - 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BROTLI_LITERAL_BYTE_SCORE 540
|
static BROTLI_INLINE void PrepareDistanceCache(
|
||||||
#define BROTLI_DISTANCE_BIT_PENALTY 120
|
int* BROTLI_RESTRICT distance_cache, const int num_distances) {
|
||||||
|
if (num_distances > 4) {
|
||||||
|
int last_distance = distance_cache[0];
|
||||||
|
distance_cache[4] = last_distance - 1;
|
||||||
|
distance_cache[5] = last_distance + 1;
|
||||||
|
distance_cache[6] = last_distance - 2;
|
||||||
|
distance_cache[7] = last_distance + 2;
|
||||||
|
distance_cache[8] = last_distance - 3;
|
||||||
|
distance_cache[9] = last_distance + 3;
|
||||||
|
if (num_distances > 10) {
|
||||||
|
int next_last_distance = distance_cache[1];
|
||||||
|
distance_cache[10] = next_last_distance - 1;
|
||||||
|
distance_cache[11] = next_last_distance + 1;
|
||||||
|
distance_cache[12] = next_last_distance - 2;
|
||||||
|
distance_cache[13] = next_last_distance + 2;
|
||||||
|
distance_cache[14] = next_last_distance - 3;
|
||||||
|
distance_cache[15] = next_last_distance + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BROTLI_LITERAL_BYTE_SCORE 135
|
||||||
|
#define BROTLI_DISTANCE_BIT_PENALTY 30
|
||||||
/* Score must be positive after applying maximal penalty. */
|
/* Score must be positive after applying maximal penalty. */
|
||||||
#define BROTLI_SCORE_BASE (BROTLI_DISTANCE_BIT_PENALTY * 8 * sizeof(size_t))
|
#define BROTLI_SCORE_BASE (BROTLI_DISTANCE_BIT_PENALTY * 8 * sizeof(size_t))
|
||||||
|
|
||||||
|
@ -97,97 +137,83 @@ static BROTLI_INLINE score_t BackwardReferenceScore(
|
||||||
BROTLI_DISTANCE_BIT_PENALTY * Log2FloorNonZero(backward_reference_offset);
|
BROTLI_DISTANCE_BIT_PENALTY * Log2FloorNonZero(backward_reference_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const score_t kDistanceShortCodeCost[BROTLI_NUM_DISTANCE_SHORT_CODES] = {
|
|
||||||
/* Repeat last */
|
|
||||||
BROTLI_SCORE_BASE + 60,
|
|
||||||
/* 2nd, 3rd, 4th last */
|
|
||||||
BROTLI_SCORE_BASE - 95,
|
|
||||||
BROTLI_SCORE_BASE - 117,
|
|
||||||
BROTLI_SCORE_BASE - 127,
|
|
||||||
/* Last with offset */
|
|
||||||
BROTLI_SCORE_BASE - 93,
|
|
||||||
BROTLI_SCORE_BASE - 93,
|
|
||||||
BROTLI_SCORE_BASE - 96,
|
|
||||||
BROTLI_SCORE_BASE - 96,
|
|
||||||
BROTLI_SCORE_BASE - 99,
|
|
||||||
BROTLI_SCORE_BASE - 99,
|
|
||||||
/* 2nd last with offset */
|
|
||||||
BROTLI_SCORE_BASE - 105,
|
|
||||||
BROTLI_SCORE_BASE - 105,
|
|
||||||
BROTLI_SCORE_BASE - 115,
|
|
||||||
BROTLI_SCORE_BASE - 115,
|
|
||||||
BROTLI_SCORE_BASE - 125,
|
|
||||||
BROTLI_SCORE_BASE - 125
|
|
||||||
};
|
|
||||||
|
|
||||||
static BROTLI_INLINE score_t BackwardReferenceScoreUsingLastDistance(
|
static BROTLI_INLINE score_t BackwardReferenceScoreUsingLastDistance(
|
||||||
size_t copy_length, size_t distance_short_code) {
|
size_t copy_length) {
|
||||||
return BROTLI_LITERAL_BYTE_SCORE * (score_t)copy_length +
|
return BROTLI_LITERAL_BYTE_SCORE * (score_t)copy_length +
|
||||||
kDistanceShortCodeCost[distance_short_code];
|
BROTLI_SCORE_BASE + 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void DictionarySearchStaticticsReset(
|
static BROTLI_INLINE score_t BackwardReferencePenaltyUsingLastDistance(
|
||||||
DictionarySearchStatictics* self) {
|
size_t distance_short_code) {
|
||||||
self->num_lookups = 0;
|
return (score_t)39 + ((0x1CA10 >> (distance_short_code & 0xE)) & 0xE);
|
||||||
self->num_matches = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE BROTLI_BOOL TestStaticDictionaryItem(
|
static BROTLI_INLINE BROTLI_BOOL TestStaticDictionaryItem(
|
||||||
size_t item, const uint8_t* data, size_t max_length, size_t max_backward,
|
const BrotliEncoderDictionary* dictionary, size_t item, const uint8_t* data,
|
||||||
|
size_t max_length, size_t max_backward, size_t max_distance,
|
||||||
HasherSearchResult* out) {
|
HasherSearchResult* out) {
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t dist;
|
size_t word_idx;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
size_t matchlen;
|
size_t matchlen;
|
||||||
size_t backward;
|
size_t backward;
|
||||||
score_t score;
|
score_t score;
|
||||||
len = item & 31;
|
len = item & 0x1F;
|
||||||
dist = item >> 5;
|
word_idx = item >> 5;
|
||||||
offset = kBrotliDictionaryOffsetsByLength[len] + len * dist;
|
offset = dictionary->words->offsets_by_length[len] + len * word_idx;
|
||||||
if (len > max_length) {
|
if (len > max_length) {
|
||||||
return BROTLI_FALSE;
|
return BROTLI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchlen = FindMatchLengthWithLimit(data, &kBrotliDictionary[offset], len);
|
matchlen =
|
||||||
if (matchlen + kCutoffTransformsCount <= len || matchlen == 0) {
|
FindMatchLengthWithLimit(data, &dictionary->words->data[offset], len);
|
||||||
|
if (matchlen + dictionary->cutoffTransformsCount <= len || matchlen == 0) {
|
||||||
return BROTLI_FALSE;
|
return BROTLI_FALSE;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
size_t transform_id = kCutoffTransforms[len - matchlen];
|
size_t cut = len - matchlen;
|
||||||
backward = max_backward + dist + 1 +
|
size_t transform_id = (cut << 2) +
|
||||||
(transform_id << kBrotliDictionarySizeBitsByLength[len]);
|
(size_t)((dictionary->cutoffTransforms >> (cut * 6)) & 0x3F);
|
||||||
|
backward = max_backward + 1 + word_idx +
|
||||||
|
(transform_id << dictionary->words->size_bits_by_length[len]);
|
||||||
|
}
|
||||||
|
if (backward > max_distance) {
|
||||||
|
return BROTLI_FALSE;
|
||||||
}
|
}
|
||||||
score = BackwardReferenceScore(matchlen, backward);
|
score = BackwardReferenceScore(matchlen, backward);
|
||||||
if (score < out->score) {
|
if (score < out->score) {
|
||||||
return BROTLI_FALSE;
|
return BROTLI_FALSE;
|
||||||
}
|
}
|
||||||
out->len = matchlen;
|
out->len = matchlen;
|
||||||
out->len_x_code = len ^ matchlen;
|
out->len_code_delta = (int)len - (int)matchlen;
|
||||||
out->distance = backward;
|
out->distance = backward;
|
||||||
out->score = score;
|
out->score = score;
|
||||||
return BROTLI_TRUE;
|
return BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE BROTLI_BOOL SearchInStaticDictionary(
|
static BROTLI_INLINE void SearchInStaticDictionary(
|
||||||
DictionarySearchStatictics* self, const uint8_t* data, size_t max_length,
|
const BrotliEncoderDictionary* dictionary,
|
||||||
size_t max_backward, HasherSearchResult* out, BROTLI_BOOL shallow) {
|
HasherHandle handle, const uint8_t* data, size_t max_length,
|
||||||
|
size_t max_backward, size_t max_distance,
|
||||||
|
HasherSearchResult* out, BROTLI_BOOL shallow) {
|
||||||
size_t key;
|
size_t key;
|
||||||
size_t i;
|
size_t i;
|
||||||
BROTLI_BOOL is_match_found = BROTLI_FALSE;
|
HasherCommon* self = GetHasherCommon(handle);
|
||||||
if (self->num_matches < (self->num_lookups >> 7)) {
|
if (self->dict_num_matches < (self->dict_num_lookups >> 7)) {
|
||||||
return BROTLI_FALSE;
|
return;
|
||||||
}
|
}
|
||||||
key = Hash14(data) << 1;
|
key = Hash14(data) << 1;
|
||||||
for (i = 0; i < (shallow ? 1 : 2); ++i, ++key) {
|
for (i = 0; i < (shallow ? 1u : 2u); ++i, ++key) {
|
||||||
size_t item = kStaticDictionaryHash[key];
|
size_t item = dictionary->hash_table[key];
|
||||||
self->num_lookups++;
|
self->dict_num_lookups++;
|
||||||
if (item != 0 &&
|
if (item != 0) {
|
||||||
TestStaticDictionaryItem(item, data, max_length, max_backward, out)) {
|
BROTLI_BOOL item_matches = TestStaticDictionaryItem(
|
||||||
self->num_matches++;
|
dictionary, item, data, max_length, max_backward, max_distance, out);
|
||||||
is_match_found = BROTLI_TRUE;
|
if (item_matches) {
|
||||||
|
self->dict_num_matches++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return is_match_found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct BackwardMatch {
|
typedef struct BackwardMatch {
|
||||||
|
@ -221,320 +247,26 @@ static BROTLI_INLINE size_t BackwardMatchLengthCode(const BackwardMatch* self) {
|
||||||
#define CAT(a, b) a ## b
|
#define CAT(a, b) a ## b
|
||||||
#define FN(X) EXPAND_CAT(X, HASHER())
|
#define FN(X) EXPAND_CAT(X, HASHER())
|
||||||
|
|
||||||
#define MAX_NUM_MATCHES_H10 (64 + MAX_TREE_SEARCH_DEPTH)
|
|
||||||
|
|
||||||
#define HASHER() H10
|
#define HASHER() H10
|
||||||
#define HashToBinaryTree HASHER()
|
|
||||||
|
|
||||||
#define BUCKET_BITS 17
|
#define BUCKET_BITS 17
|
||||||
#define BUCKET_SIZE (1 << BUCKET_BITS)
|
#define MAX_TREE_SEARCH_DEPTH 64
|
||||||
|
#define MAX_TREE_COMP_LENGTH 128
|
||||||
static size_t FN(HashTypeLength)(void) { return 4; }
|
#include "./hash_to_binary_tree_inc.h" /* NOLINT(build/include) */
|
||||||
static size_t FN(StoreLookahead)(void) { return MAX_TREE_COMP_LENGTH; }
|
#undef MAX_TREE_SEARCH_DEPTH
|
||||||
|
#undef MAX_TREE_COMP_LENGTH
|
||||||
static uint32_t FN(HashBytes)(const uint8_t *data) {
|
|
||||||
uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;
|
|
||||||
/* The higher bits contain more mixture from the multiplication,
|
|
||||||
so we take our results from there. */
|
|
||||||
return h >> (32 - BUCKET_BITS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A (forgetful) hash table where each hash bucket contains a binary tree of
|
|
||||||
sequences whose first 4 bytes share the same hash code.
|
|
||||||
Each sequence is MAX_TREE_COMP_LENGTH long and is identified by its starting
|
|
||||||
position in the input data. The binary tree is sorted by the lexicographic
|
|
||||||
order of the sequences, and it is also a max-heap with respect to the
|
|
||||||
starting positions. */
|
|
||||||
typedef struct HashToBinaryTree {
|
|
||||||
/* The window size minus 1 */
|
|
||||||
size_t window_mask_;
|
|
||||||
|
|
||||||
/* Hash table that maps the 4-byte hashes of the sequence to the last
|
|
||||||
position where this hash was found, which is the root of the binary
|
|
||||||
tree of sequences that share this hash bucket. */
|
|
||||||
uint32_t buckets_[BUCKET_SIZE];
|
|
||||||
|
|
||||||
/* The union of the binary trees of each hash bucket. The root of the tree
|
|
||||||
corresponding to a hash is a sequence starting at buckets_[hash] and
|
|
||||||
the left and right children of a sequence starting at pos are
|
|
||||||
forest_[2 * pos] and forest_[2 * pos + 1]. */
|
|
||||||
uint32_t* forest_;
|
|
||||||
|
|
||||||
/* A position used to mark a non-existent sequence, i.e. a tree is empty if
|
|
||||||
its root is at invalid_pos_ and a node is a leaf if both its children
|
|
||||||
are at invalid_pos_. */
|
|
||||||
uint32_t invalid_pos_;
|
|
||||||
|
|
||||||
size_t forest_size_;
|
|
||||||
BROTLI_BOOL is_dirty_;
|
|
||||||
} HashToBinaryTree;
|
|
||||||
|
|
||||||
static void FN(Reset)(HashToBinaryTree* self) {
|
|
||||||
self->is_dirty_ = BROTLI_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FN(Initialize)(HashToBinaryTree* self) {
|
|
||||||
self->forest_ = NULL;
|
|
||||||
self->forest_size_ = 0;
|
|
||||||
FN(Reset)(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FN(Cleanup)(MemoryManager* m, HashToBinaryTree* self) {
|
|
||||||
BROTLI_FREE(m, self->forest_);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FN(Init)(
|
|
||||||
MemoryManager* m, HashToBinaryTree* self, const uint8_t* data,
|
|
||||||
const BrotliEncoderParams* params, size_t position, size_t bytes,
|
|
||||||
BROTLI_BOOL is_last) {
|
|
||||||
if (self->is_dirty_) {
|
|
||||||
uint32_t invalid_pos;
|
|
||||||
size_t num_nodes;
|
|
||||||
uint32_t i;
|
|
||||||
BROTLI_UNUSED(data);
|
|
||||||
self->window_mask_ = (1u << params->lgwin) - 1u;
|
|
||||||
invalid_pos = (uint32_t)(0 - self->window_mask_);
|
|
||||||
self->invalid_pos_ = invalid_pos;
|
|
||||||
for (i = 0; i < BUCKET_SIZE; i++) {
|
|
||||||
self->buckets_[i] = invalid_pos;
|
|
||||||
}
|
|
||||||
num_nodes = (position == 0 && is_last) ? bytes : self->window_mask_ + 1;
|
|
||||||
if (num_nodes > self->forest_size_) {
|
|
||||||
BROTLI_FREE(m, self->forest_);
|
|
||||||
self->forest_ = BROTLI_ALLOC(m, uint32_t, 2 * num_nodes);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
self->forest_size_ = num_nodes;
|
|
||||||
}
|
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE size_t FN(LeftChildIndex)(HashToBinaryTree* self,
|
|
||||||
const size_t pos) {
|
|
||||||
return 2 * (pos & self->window_mask_);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE size_t FN(RightChildIndex)(HashToBinaryTree* self,
|
|
||||||
const size_t pos) {
|
|
||||||
return 2 * (pos & self->window_mask_) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stores the hash of the next 4 bytes and in a single tree-traversal, the
|
|
||||||
hash bucket's binary tree is searched for matches and is re-rooted at the
|
|
||||||
current position.
|
|
||||||
|
|
||||||
If less than MAX_TREE_COMP_LENGTH data is available, the hash bucket of the
|
|
||||||
current position is searched for matches, but the state of the hash table
|
|
||||||
is not changed, since we can not know the final sorting order of the
|
|
||||||
current (incomplete) sequence.
|
|
||||||
|
|
||||||
This function must be called with increasing cur_ix positions. */
|
|
||||||
static BROTLI_INLINE BackwardMatch* FN(StoreAndFindMatches)(
|
|
||||||
HashToBinaryTree* self, const uint8_t* const BROTLI_RESTRICT data,
|
|
||||||
const size_t cur_ix, const size_t ring_buffer_mask, const size_t max_length,
|
|
||||||
const size_t max_backward, size_t* const BROTLI_RESTRICT best_len,
|
|
||||||
BackwardMatch* BROTLI_RESTRICT matches) {
|
|
||||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
|
||||||
const size_t max_comp_len =
|
|
||||||
BROTLI_MIN(size_t, max_length, MAX_TREE_COMP_LENGTH);
|
|
||||||
const BROTLI_BOOL should_reroot_tree =
|
|
||||||
TO_BROTLI_BOOL(max_length >= MAX_TREE_COMP_LENGTH);
|
|
||||||
const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
|
||||||
size_t prev_ix = self->buckets_[key];
|
|
||||||
/* The forest index of the rightmost node of the left subtree of the new
|
|
||||||
root, updated as we traverse and reroot the tree of the hash bucket. */
|
|
||||||
size_t node_left = FN(LeftChildIndex)(self, cur_ix);
|
|
||||||
/* The forest index of the leftmost node of the right subtree of the new
|
|
||||||
root, updated as we traverse and reroot the tree of the hash bucket. */
|
|
||||||
size_t node_right = FN(RightChildIndex)(self, cur_ix);
|
|
||||||
/* The match length of the rightmost node of the left subtree of the new
|
|
||||||
root, updated as we traverse and reroot the tree of the hash bucket. */
|
|
||||||
size_t best_len_left = 0;
|
|
||||||
/* The match length of the leftmost node of the right subtree of the new
|
|
||||||
root, updated as we traverse and reroot the tree of the hash bucket. */
|
|
||||||
size_t best_len_right = 0;
|
|
||||||
size_t depth_remaining;
|
|
||||||
if (should_reroot_tree) {
|
|
||||||
self->buckets_[key] = (uint32_t)cur_ix;
|
|
||||||
}
|
|
||||||
for (depth_remaining = MAX_TREE_SEARCH_DEPTH; ; --depth_remaining) {
|
|
||||||
const size_t backward = cur_ix - prev_ix;
|
|
||||||
const size_t prev_ix_masked = prev_ix & ring_buffer_mask;
|
|
||||||
if (backward == 0 || backward > max_backward || depth_remaining == 0) {
|
|
||||||
if (should_reroot_tree) {
|
|
||||||
self->forest_[node_left] = self->invalid_pos_;
|
|
||||||
self->forest_[node_right] = self->invalid_pos_;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const size_t cur_len = BROTLI_MIN(size_t, best_len_left, best_len_right);
|
|
||||||
size_t len;
|
|
||||||
assert(cur_len <= MAX_TREE_COMP_LENGTH);
|
|
||||||
len = cur_len +
|
|
||||||
FindMatchLengthWithLimit(&data[cur_ix_masked + cur_len],
|
|
||||||
&data[prev_ix_masked + cur_len],
|
|
||||||
max_length - cur_len);
|
|
||||||
assert(0 == memcmp(&data[cur_ix_masked], &data[prev_ix_masked], len));
|
|
||||||
if (matches && len > *best_len) {
|
|
||||||
*best_len = len;
|
|
||||||
InitBackwardMatch(matches++, backward, len);
|
|
||||||
}
|
|
||||||
if (len >= max_comp_len) {
|
|
||||||
if (should_reroot_tree) {
|
|
||||||
self->forest_[node_left] =
|
|
||||||
self->forest_[FN(LeftChildIndex)(self, prev_ix)];
|
|
||||||
self->forest_[node_right] =
|
|
||||||
self->forest_[FN(RightChildIndex)(self, prev_ix)];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (data[cur_ix_masked + len] > data[prev_ix_masked + len]) {
|
|
||||||
best_len_left = len;
|
|
||||||
if (should_reroot_tree) {
|
|
||||||
self->forest_[node_left] = (uint32_t)prev_ix;
|
|
||||||
}
|
|
||||||
node_left = FN(RightChildIndex)(self, prev_ix);
|
|
||||||
prev_ix = self->forest_[node_left];
|
|
||||||
} else {
|
|
||||||
best_len_right = len;
|
|
||||||
if (should_reroot_tree) {
|
|
||||||
self->forest_[node_right] = (uint32_t)prev_ix;
|
|
||||||
}
|
|
||||||
node_right = FN(LeftChildIndex)(self, prev_ix);
|
|
||||||
prev_ix = self->forest_[node_right];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the
|
|
||||||
length of max_length and stores the position cur_ix in the hash table.
|
|
||||||
|
|
||||||
Sets *num_matches to the number of matches found, and stores the found
|
|
||||||
matches in matches[0] to matches[*num_matches - 1]. The matches will be
|
|
||||||
sorted by strictly increasing length and (non-strictly) increasing
|
|
||||||
distance. */
|
|
||||||
static BROTLI_INLINE size_t FN(FindAllMatches)(HashToBinaryTree* self,
|
|
||||||
const uint8_t* data, const size_t ring_buffer_mask, const size_t cur_ix,
|
|
||||||
const size_t max_length, const size_t max_backward,
|
|
||||||
const BrotliEncoderParams* params, BackwardMatch* matches) {
|
|
||||||
BackwardMatch* const orig_matches = matches;
|
|
||||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
|
||||||
size_t best_len = 1;
|
|
||||||
const size_t short_match_max_backward =
|
|
||||||
params->quality != HQ_ZOPFLIFICATION_QUALITY ? 16 : 64;
|
|
||||||
size_t stop = cur_ix - short_match_max_backward;
|
|
||||||
uint32_t dict_matches[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1];
|
|
||||||
size_t i;
|
|
||||||
if (cur_ix < short_match_max_backward) { stop = 0; }
|
|
||||||
for (i = cur_ix - 1; i > stop && best_len <= 2; --i) {
|
|
||||||
size_t prev_ix = i;
|
|
||||||
const size_t backward = cur_ix - prev_ix;
|
|
||||||
if (PREDICT_FALSE(backward > max_backward)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prev_ix &= ring_buffer_mask;
|
|
||||||
if (data[cur_ix_masked] != data[prev_ix] ||
|
|
||||||
data[cur_ix_masked + 1] != data[prev_ix + 1]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const size_t len =
|
|
||||||
FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked],
|
|
||||||
max_length);
|
|
||||||
if (len > best_len) {
|
|
||||||
best_len = len;
|
|
||||||
InitBackwardMatch(matches++, backward, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (best_len < max_length) {
|
|
||||||
matches = FN(StoreAndFindMatches)(self, data, cur_ix, ring_buffer_mask,
|
|
||||||
max_length, max_backward, &best_len, matches);
|
|
||||||
}
|
|
||||||
for (i = 0; i <= BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN; ++i) {
|
|
||||||
dict_matches[i] = kInvalidMatch;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
size_t minlen = BROTLI_MAX(size_t, 4, best_len + 1);
|
|
||||||
if (BrotliFindAllStaticDictionaryMatches(&data[cur_ix_masked], minlen,
|
|
||||||
max_length, &dict_matches[0])) {
|
|
||||||
size_t maxlen = BROTLI_MIN(
|
|
||||||
size_t, BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN, max_length);
|
|
||||||
size_t l;
|
|
||||||
for (l = minlen; l <= maxlen; ++l) {
|
|
||||||
uint32_t dict_id = dict_matches[l];
|
|
||||||
if (dict_id < kInvalidMatch) {
|
|
||||||
InitDictionaryBackwardMatch(matches++,
|
|
||||||
max_backward + (dict_id >> 5) + 1, l, dict_id & 31);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (size_t)(matches - orig_matches);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stores the hash of the next 4 bytes and re-roots the binary tree at the
|
|
||||||
current sequence, without returning any matches.
|
|
||||||
REQUIRES: ix + MAX_TREE_COMP_LENGTH <= end-of-current-block */
|
|
||||||
static BROTLI_INLINE void FN(Store)(HashToBinaryTree* self, const uint8_t *data,
|
|
||||||
const size_t mask, const size_t ix) {
|
|
||||||
/* Maximum distance is window size - 16, see section 9.1. of the spec. */
|
|
||||||
const size_t max_backward = self->window_mask_ - 15;
|
|
||||||
FN(StoreAndFindMatches)(self, data, ix, mask, MAX_TREE_COMP_LENGTH,
|
|
||||||
max_backward, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StoreRange)(HashToBinaryTree* self,
|
|
||||||
const uint8_t *data, const size_t mask, const size_t ix_start,
|
|
||||||
const size_t ix_end) {
|
|
||||||
size_t i = ix_start + 63 <= ix_end ? ix_end - 63 : ix_start;
|
|
||||||
for (; i < ix_end; ++i) {
|
|
||||||
FN(Store)(self, data, mask, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HashToBinaryTree* self,
|
|
||||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
|
||||||
size_t ringbuffer_mask) {
|
|
||||||
if (num_bytes >= FN(HashTypeLength)() - 1 &&
|
|
||||||
position >= MAX_TREE_COMP_LENGTH) {
|
|
||||||
/* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher.
|
|
||||||
These could not be calculated before, since they require knowledge
|
|
||||||
of both the previous and the current block. */
|
|
||||||
const size_t i_start = position - MAX_TREE_COMP_LENGTH + 1;
|
|
||||||
const size_t i_end = BROTLI_MIN(size_t, position, i_start + num_bytes);
|
|
||||||
size_t i;
|
|
||||||
for (i = i_start; i < i_end; ++i) {
|
|
||||||
/* Maximum distance is window size - 16, see section 9.1. of the spec.
|
|
||||||
Furthermore, we have to make sure that we don't look further back
|
|
||||||
from the start of the next block than the window size, otherwise we
|
|
||||||
could access already overwritten areas of the ringbuffer. */
|
|
||||||
const size_t max_backward =
|
|
||||||
self->window_mask_ - BROTLI_MAX(size_t, 15, position - i);
|
|
||||||
/* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the
|
|
||||||
end of the current block and that we have at least
|
|
||||||
MAX_TREE_COMP_LENGTH tail in the ringbuffer. */
|
|
||||||
FN(StoreAndFindMatches)(self, ringbuffer, i, ringbuffer_mask,
|
|
||||||
MAX_TREE_COMP_LENGTH, max_backward, NULL, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef BUCKET_SIZE
|
|
||||||
#undef BUCKET_BITS
|
#undef BUCKET_BITS
|
||||||
|
|
||||||
#undef HASHER
|
#undef HASHER
|
||||||
|
/* MAX_NUM_MATCHES == 64 + MAX_TREE_SEARCH_DEPTH */
|
||||||
|
#define MAX_NUM_MATCHES_H10 128
|
||||||
|
|
||||||
/* For BUCKET_SWEEP == 1, enabling the dictionary lookup makes compression
|
/* For BUCKET_SWEEP == 1, enabling the dictionary lookup makes compression
|
||||||
a little faster (0.5% - 1%) and it compresses 0.15% better on small text
|
a little faster (0.5% - 1%) and it compresses 0.15% better on small text
|
||||||
and html inputs. */
|
and HTML inputs. */
|
||||||
|
|
||||||
#define HASHER() H2
|
#define HASHER() H2
|
||||||
#define BUCKET_BITS 16
|
#define BUCKET_BITS 16
|
||||||
#define BUCKET_SWEEP 1
|
#define BUCKET_SWEEP 1
|
||||||
|
#define HASH_LEN 5
|
||||||
#define USE_DICTIONARY 1
|
#define USE_DICTIONARY 1
|
||||||
#include "./hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */
|
#include "./hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */
|
||||||
#undef BUCKET_SWEEP
|
#undef BUCKET_SWEEP
|
||||||
|
@ -556,48 +288,17 @@ static BROTLI_INLINE void FN(StitchToPreviousBlock)(HashToBinaryTree* self,
|
||||||
#define USE_DICTIONARY 1
|
#define USE_DICTIONARY 1
|
||||||
#include "./hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */
|
#include "./hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */
|
||||||
#undef USE_DICTIONARY
|
#undef USE_DICTIONARY
|
||||||
|
#undef HASH_LEN
|
||||||
#undef BUCKET_SWEEP
|
#undef BUCKET_SWEEP
|
||||||
#undef BUCKET_BITS
|
#undef BUCKET_BITS
|
||||||
#undef HASHER
|
#undef HASHER
|
||||||
|
|
||||||
#define HASHER() H5
|
#define HASHER() H5
|
||||||
#define BUCKET_BITS 14
|
|
||||||
#define BLOCK_BITS 4
|
|
||||||
#define NUM_LAST_DISTANCES_TO_CHECK 4
|
|
||||||
#include "./hash_longest_match_inc.h" /* NOLINT(build/include) */
|
#include "./hash_longest_match_inc.h" /* NOLINT(build/include) */
|
||||||
#undef BLOCK_BITS
|
|
||||||
#undef HASHER
|
#undef HASHER
|
||||||
|
|
||||||
#define HASHER() H6
|
#define HASHER() H6
|
||||||
#define BLOCK_BITS 5
|
#include "./hash_longest_match64_inc.h" /* NOLINT(build/include) */
|
||||||
#include "./hash_longest_match_inc.h" /* NOLINT(build/include) */
|
|
||||||
#undef NUM_LAST_DISTANCES_TO_CHECK
|
|
||||||
#undef BLOCK_BITS
|
|
||||||
#undef BUCKET_BITS
|
|
||||||
#undef HASHER
|
|
||||||
|
|
||||||
#define HASHER() H7
|
|
||||||
#define BUCKET_BITS 15
|
|
||||||
#define BLOCK_BITS 6
|
|
||||||
#define NUM_LAST_DISTANCES_TO_CHECK 10
|
|
||||||
#include "./hash_longest_match_inc.h" /* NOLINT(build/include) */
|
|
||||||
#undef BLOCK_BITS
|
|
||||||
#undef HASHER
|
|
||||||
|
|
||||||
#define HASHER() H8
|
|
||||||
#define BLOCK_BITS 7
|
|
||||||
#include "./hash_longest_match_inc.h" /* NOLINT(build/include) */
|
|
||||||
#undef NUM_LAST_DISTANCES_TO_CHECK
|
|
||||||
#undef BLOCK_BITS
|
|
||||||
#undef HASHER
|
|
||||||
|
|
||||||
#define HASHER() H9
|
|
||||||
#define BLOCK_BITS 8
|
|
||||||
#define NUM_LAST_DISTANCES_TO_CHECK 16
|
|
||||||
#include "./hash_longest_match_inc.h" /* NOLINT(build/include) */
|
|
||||||
#undef NUM_LAST_DISTANCES_TO_CHECK
|
|
||||||
#undef BLOCK_BITS
|
|
||||||
#undef BUCKET_BITS
|
|
||||||
#undef HASHER
|
#undef HASHER
|
||||||
|
|
||||||
#define BUCKET_BITS 15
|
#define BUCKET_BITS 15
|
||||||
|
@ -630,86 +331,165 @@ static BROTLI_INLINE void FN(StitchToPreviousBlock)(HashToBinaryTree* self,
|
||||||
|
|
||||||
#undef BUCKET_BITS
|
#undef BUCKET_BITS
|
||||||
|
|
||||||
|
#define HASHER() H54
|
||||||
|
#define BUCKET_BITS 20
|
||||||
|
#define BUCKET_SWEEP 4
|
||||||
|
#define HASH_LEN 7
|
||||||
|
#define USE_DICTIONARY 0
|
||||||
|
#include "./hash_longest_match_quickly_inc.h" /* NOLINT(build/include) */
|
||||||
|
#undef USE_DICTIONARY
|
||||||
|
#undef HASH_LEN
|
||||||
|
#undef BUCKET_SWEEP
|
||||||
|
#undef BUCKET_BITS
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
/* fast large window hashers */
|
||||||
|
|
||||||
|
#define HASHER() HROLLING_FAST
|
||||||
|
#define CHUNKLEN 32
|
||||||
|
#define JUMP 4
|
||||||
|
#define NUMBUCKETS 16777216
|
||||||
|
#define MASK ((NUMBUCKETS * 64) - 1)
|
||||||
|
#include "./hash_rolling_inc.h" /* NOLINT(build/include) */
|
||||||
|
#undef JUMP
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
|
||||||
|
#define HASHER() HROLLING
|
||||||
|
#define JUMP 1
|
||||||
|
#include "./hash_rolling_inc.h" /* NOLINT(build/include) */
|
||||||
|
#undef MASK
|
||||||
|
#undef NUMBUCKETS
|
||||||
|
#undef JUMP
|
||||||
|
#undef CHUNKLEN
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H35
|
||||||
|
#define HASHER_A H3
|
||||||
|
#define HASHER_B HROLLING_FAST
|
||||||
|
#include "./hash_composite_inc.h" /* NOLINT(build/include) */
|
||||||
|
#undef HASHER_A
|
||||||
|
#undef HASHER_B
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H55
|
||||||
|
#define HASHER_A H54
|
||||||
|
#define HASHER_B HROLLING_FAST
|
||||||
|
#include "./hash_composite_inc.h" /* NOLINT(build/include) */
|
||||||
|
#undef HASHER_A
|
||||||
|
#undef HASHER_B
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
|
#define HASHER() H65
|
||||||
|
#define HASHER_A H6
|
||||||
|
#define HASHER_B HROLLING
|
||||||
|
#include "./hash_composite_inc.h" /* NOLINT(build/include) */
|
||||||
|
#undef HASHER_A
|
||||||
|
#undef HASHER_B
|
||||||
|
#undef HASHER
|
||||||
|
|
||||||
#undef FN
|
#undef FN
|
||||||
#undef CAT
|
#undef CAT
|
||||||
#undef EXPAND_CAT
|
#undef EXPAND_CAT
|
||||||
|
|
||||||
#define FOR_GENERIC_HASHERS(H) H(2) H(3) H(4) H(5) H(6) H(7) H(8) H(9) \
|
#define FOR_GENERIC_HASHERS(H) H(2) H(3) H(4) H(5) H(6) H(40) H(41) H(42) H(54)\
|
||||||
H(40) H(41) H(42)
|
H(35) H(55) H(65)
|
||||||
#define FOR_ALL_HASHERS(H) FOR_GENERIC_HASHERS(H) H(10)
|
#define FOR_ALL_HASHERS(H) FOR_GENERIC_HASHERS(H) H(10)
|
||||||
|
|
||||||
typedef struct Hashers {
|
static BROTLI_INLINE void DestroyHasher(
|
||||||
#define _MEMBER(N) H ## N* h ## N;
|
MemoryManager* m, HasherHandle* handle) {
|
||||||
FOR_ALL_HASHERS(_MEMBER)
|
if (*handle == NULL) return;
|
||||||
#undef _MEMBER
|
BROTLI_FREE(m, *handle);
|
||||||
} Hashers;
|
|
||||||
|
|
||||||
static BROTLI_INLINE void InitHashers(Hashers* self) {
|
|
||||||
#define _INIT(N) self->h ## N = 0;
|
|
||||||
FOR_ALL_HASHERS(_INIT)
|
|
||||||
#undef _INIT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void DestroyHashers(MemoryManager* m, Hashers* self) {
|
static BROTLI_INLINE void HasherReset(HasherHandle handle) {
|
||||||
if (self->h10) CleanupH10(m, self->h10);
|
if (handle == NULL) return;
|
||||||
#define _CLEANUP(N) BROTLI_FREE(m, self->h ## N)
|
GetHasherCommon(handle)->is_prepared_ = BROTLI_FALSE;
|
||||||
FOR_ALL_HASHERS(_CLEANUP)
|
|
||||||
#undef _CLEANUP
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void HashersReset(Hashers* self, int type) {
|
static BROTLI_INLINE size_t HasherSize(const BrotliEncoderParams* params,
|
||||||
switch (type) {
|
BROTLI_BOOL one_shot, const size_t input_size) {
|
||||||
#define _RESET(N) case N: ResetH ## N(self->h ## N); break;
|
size_t result = sizeof(HasherCommon);
|
||||||
FOR_ALL_HASHERS(_RESET)
|
switch (params->hasher.type) {
|
||||||
#undef _RESET
|
#define SIZE_(N) \
|
||||||
default: break;
|
case N: \
|
||||||
|
result += HashMemAllocInBytesH ## N(params, one_shot, input_size); \
|
||||||
|
break;
|
||||||
|
FOR_ALL_HASHERS(SIZE_)
|
||||||
|
#undef SIZE_
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void HasherSetup(MemoryManager* m, HasherHandle* handle,
|
||||||
|
BrotliEncoderParams* params, const uint8_t* data, size_t position,
|
||||||
|
size_t input_size, BROTLI_BOOL is_last) {
|
||||||
|
HasherHandle self = NULL;
|
||||||
|
HasherCommon* common = NULL;
|
||||||
|
BROTLI_BOOL one_shot = (position == 0 && is_last);
|
||||||
|
if (*handle == NULL) {
|
||||||
|
size_t alloc_size;
|
||||||
|
ChooseHasher(params, ¶ms->hasher);
|
||||||
|
alloc_size = HasherSize(params, one_shot, input_size);
|
||||||
|
self = BROTLI_ALLOC(m, uint8_t, alloc_size);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
*handle = self;
|
||||||
|
common = GetHasherCommon(self);
|
||||||
|
common->params = params->hasher;
|
||||||
|
switch (common->params.type) {
|
||||||
|
#define INITIALIZE_(N) \
|
||||||
|
case N: \
|
||||||
|
InitializeH ## N(*handle, params); \
|
||||||
|
break;
|
||||||
|
FOR_ALL_HASHERS(INITIALIZE_);
|
||||||
|
#undef INITIALIZE_
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
HasherReset(*handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
self = *handle;
|
||||||
|
common = GetHasherCommon(self);
|
||||||
|
if (!common->is_prepared_) {
|
||||||
|
switch (common->params.type) {
|
||||||
|
#define PREPARE_(N) \
|
||||||
|
case N: \
|
||||||
|
PrepareH ## N(self, one_shot, input_size, data); \
|
||||||
|
break;
|
||||||
|
FOR_ALL_HASHERS(PREPARE_)
|
||||||
|
#undef PREPARE_
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (position == 0) {
|
||||||
|
common->dict_num_lookups = 0;
|
||||||
|
common->dict_num_matches = 0;
|
||||||
|
}
|
||||||
|
common->is_prepared_ = BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void HashersSetup(
|
static BROTLI_INLINE void InitOrStitchToPreviousBlock(
|
||||||
MemoryManager* m, Hashers* self, int type) {
|
MemoryManager* m, HasherHandle* handle, const uint8_t* data, size_t mask,
|
||||||
switch (type) {
|
BrotliEncoderParams* params, size_t position, size_t input_size,
|
||||||
#define _SETUP(N) case N: self->h ## N = BROTLI_ALLOC(m, H ## N, 1); break;
|
BROTLI_BOOL is_last) {
|
||||||
FOR_ALL_HASHERS(_SETUP)
|
HasherHandle self;
|
||||||
#undef _SETUP
|
HasherSetup(m, handle, params, data, position, input_size, is_last);
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
if (type == 10) InitializeH10(self->h10);
|
self = *handle;
|
||||||
HashersReset(self, type);
|
switch (GetHasherCommon(self)->params.type) {
|
||||||
}
|
#define INIT_(N) \
|
||||||
|
case N: \
|
||||||
#define _WARMUP_HASH(N) \
|
StitchToPreviousBlockH ## N(self, input_size, position, data, mask); \
|
||||||
static BROTLI_INLINE void WarmupHashH ## N(MemoryManager* m, \
|
break;
|
||||||
const BrotliEncoderParams* params, const size_t size, const uint8_t* dict, \
|
FOR_ALL_HASHERS(INIT_)
|
||||||
H ## N* hasher) { \
|
#undef INIT_
|
||||||
size_t overlap = (StoreLookaheadH ## N()) - 1; \
|
|
||||||
size_t i; \
|
|
||||||
InitH ## N(m, hasher, dict, params, 0, size, BROTLI_FALSE); \
|
|
||||||
if (BROTLI_IS_OOM(m)) return; \
|
|
||||||
for (i = 0; i + overlap < size; i++) { \
|
|
||||||
StoreH ## N(hasher, dict, ~(size_t)0, i); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
FOR_ALL_HASHERS(_WARMUP_HASH)
|
|
||||||
#undef _WARMUP_HASH
|
|
||||||
|
|
||||||
/* Custom LZ77 window. */
|
|
||||||
static BROTLI_INLINE void HashersPrependCustomDictionary(
|
|
||||||
MemoryManager* m, Hashers* self, const BrotliEncoderParams* params,
|
|
||||||
const size_t size, const uint8_t* dict) {
|
|
||||||
int hasher_type = ChooseHasher(params);
|
|
||||||
switch (hasher_type) {
|
|
||||||
#define _PREPEND(N) \
|
|
||||||
case N: WarmupHashH ## N(m, params, size, dict, self->h ## N); break;
|
|
||||||
FOR_ALL_HASHERS(_PREPEND)
|
|
||||||
#undef _PREPEND
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
/* NOLINT(build/header_guard) */
|
||||||
|
/* Copyright 2018 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* template parameters: FN, HASHER_A, HASHER_B */
|
||||||
|
|
||||||
|
/* Composite hasher: This hasher allows to combine two other hashers, HASHER_A
|
||||||
|
and HASHER_B. */
|
||||||
|
|
||||||
|
#define HashComposite HASHER()
|
||||||
|
|
||||||
|
#define FN_A(X) EXPAND_CAT(X, HASHER_A)
|
||||||
|
#define FN_B(X) EXPAND_CAT(X, HASHER_B)
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashTypeLength)(void) {
|
||||||
|
size_t a = FN_A(HashTypeLength)();
|
||||||
|
size_t b = FN_B(HashTypeLength)();
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(StoreLookahead)(void) {
|
||||||
|
size_t a = FN_A(StoreLookahead)();
|
||||||
|
size_t b = FN_B(StoreLookahead)();
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct HashComposite {
|
||||||
|
HasherHandle ha;
|
||||||
|
HasherHandle hb;
|
||||||
|
const BrotliEncoderParams* params;
|
||||||
|
} HashComposite;
|
||||||
|
|
||||||
|
static BROTLI_INLINE HashComposite* FN(Self)(HasherHandle handle) {
|
||||||
|
return (HashComposite*)&(GetHasherCommon(handle)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Initialize)(
|
||||||
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
self->ha = 0;
|
||||||
|
self->hb = 0;
|
||||||
|
self->params = params;
|
||||||
|
/* TODO: Initialize of the hashers is defered to Prepare (and params
|
||||||
|
remembered here) because we don't get the one_shot and input_size params
|
||||||
|
here that are needed to know the memory size of them. Instead provide
|
||||||
|
those params to all hashers FN(Initialize) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
if (!self->ha) {
|
||||||
|
HasherCommon* common_a;
|
||||||
|
HasherCommon* common_b;
|
||||||
|
|
||||||
|
self->ha = handle + sizeof(HasherCommon) + sizeof(HashComposite);
|
||||||
|
common_a = (HasherCommon*)self->ha;
|
||||||
|
common_a->params = self->params->hasher;
|
||||||
|
common_a->is_prepared_ = BROTLI_FALSE;
|
||||||
|
common_a->dict_num_lookups = 0;
|
||||||
|
common_a->dict_num_matches = 0;
|
||||||
|
FN_A(Initialize)(self->ha, self->params);
|
||||||
|
|
||||||
|
self->hb = self->ha + sizeof(HasherCommon) + FN_A(HashMemAllocInBytes)(
|
||||||
|
self->params, one_shot, input_size);
|
||||||
|
common_b = (HasherCommon*)self->hb;
|
||||||
|
common_b->params = self->params->hasher;
|
||||||
|
common_b->is_prepared_ = BROTLI_FALSE;
|
||||||
|
common_b->dict_num_lookups = 0;
|
||||||
|
common_b->dict_num_matches = 0;
|
||||||
|
FN_B(Initialize)(self->hb, self->params);
|
||||||
|
}
|
||||||
|
FN_A(Prepare)(self->ha, one_shot, input_size, data);
|
||||||
|
FN_B(Prepare)(self->hb, one_shot, input_size, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size) {
|
||||||
|
return sizeof(HashComposite) + 2 * sizeof(HasherCommon) +
|
||||||
|
FN_A(HashMemAllocInBytes)(params, one_shot, input_size) +
|
||||||
|
FN_B(HashMemAllocInBytes)(params, one_shot, input_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(Store)(HasherHandle BROTLI_RESTRICT handle,
|
||||||
|
const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
FN_A(Store)(self->ha, data, mask, ix);
|
||||||
|
FN_B(Store)(self->hb, data, mask, ix);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
|
const size_t ix_end) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
FN_A(StoreRange)(self->ha, data, mask, ix_start, ix_end);
|
||||||
|
FN_B(StoreRange)(self->hb, data, mask, ix_start, ix_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ring_buffer_mask) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
FN_A(StitchToPreviousBlock)(self->ha, num_bytes, position, ringbuffer,
|
||||||
|
ring_buffer_mask);
|
||||||
|
FN_B(StitchToPreviousBlock)(self->hb, num_bytes, position, ringbuffer,
|
||||||
|
ring_buffer_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||||
|
HasherHandle handle, int* BROTLI_RESTRICT distance_cache) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
FN_A(PrepareDistanceCache)(self->ha, distance_cache);
|
||||||
|
FN_B(PrepareDistanceCache)(self->hb, distance_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||||
|
const BrotliEncoderDictionary* dictionary,
|
||||||
|
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||||
|
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
||||||
|
const size_t max_length, const size_t max_backward, const size_t gap,
|
||||||
|
const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) {
|
||||||
|
HashComposite* self = FN(Self)(handle);
|
||||||
|
FN_A(FindLongestMatch)(self->ha, dictionary, data, ring_buffer_mask,
|
||||||
|
distance_cache, cur_ix, max_length, max_backward, gap, max_distance, out);
|
||||||
|
FN_B(FindLongestMatch)(self->hb, dictionary, data, ring_buffer_mask,
|
||||||
|
distance_cache, cur_ix, max_length, max_backward, gap, max_distance, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef HashComposite
|
|
@ -28,8 +28,8 @@ static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
|
||||||
static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; }
|
static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; }
|
||||||
|
|
||||||
/* HashBytes is the function that chooses the bucket to place the address in.*/
|
/* HashBytes is the function that chooses the bucket to place the address in.*/
|
||||||
static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t *data) {
|
static BROTLI_INLINE size_t FN(HashBytes)(const uint8_t* data) {
|
||||||
const uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;
|
const uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
|
||||||
/* The higher bits contain more mixture from the multiplication,
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
so we take our results from there. */
|
so we take our results from there. */
|
||||||
return h >> (32 - BUCKET_BITS);
|
return h >> (32 - BUCKET_BITS);
|
||||||
|
@ -51,65 +51,57 @@ typedef struct HashForgetfulChain {
|
||||||
uint8_t tiny_hash[65536];
|
uint8_t tiny_hash[65536];
|
||||||
FN(Bank) banks[NUM_BANKS];
|
FN(Bank) banks[NUM_BANKS];
|
||||||
uint16_t free_slot_idx[NUM_BANKS];
|
uint16_t free_slot_idx[NUM_BANKS];
|
||||||
BROTLI_BOOL is_dirty_;
|
|
||||||
DictionarySearchStatictics dict_search_stats_;
|
|
||||||
size_t max_hops;
|
size_t max_hops;
|
||||||
} HashForgetfulChain;
|
} HashForgetfulChain;
|
||||||
|
|
||||||
static void FN(Reset)(HashForgetfulChain* self) {
|
static BROTLI_INLINE HashForgetfulChain* FN(Self)(HasherHandle handle) {
|
||||||
self->is_dirty_ = BROTLI_TRUE;
|
return (HashForgetfulChain*)&(GetHasherCommon(handle)[1]);
|
||||||
DictionarySearchStaticticsReset(&self->dict_search_stats_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(InitEmpty)(HashForgetfulChain* self) {
|
static void FN(Initialize)(
|
||||||
if (self->is_dirty_) {
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
|
FN(Self)(handle)->max_hops =
|
||||||
|
(params->quality > 6 ? 7u : 8u) << (params->quality - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashForgetfulChain* self = FN(Self)(handle);
|
||||||
|
/* Partial preparation is 100 times slower (per socket). */
|
||||||
|
size_t partial_prepare_threshold = BUCKET_SIZE >> 6;
|
||||||
|
if (one_shot && input_size <= partial_prepare_threshold) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < input_size; ++i) {
|
||||||
|
size_t bucket = FN(HashBytes)(&data[i]);
|
||||||
|
/* See InitEmpty comment. */
|
||||||
|
self->addr[bucket] = 0xCCCCCCCC;
|
||||||
|
self->head[bucket] = 0xCCCC;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
/* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position
|
/* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position
|
||||||
processed by hasher never reaches 3GB + 64M; this makes all new chains
|
processed by hasher never reaches 3GB + 64M; this makes all new chains
|
||||||
to be terminated after the first node. */
|
to be terminated after the first node. */
|
||||||
memset(self->addr, 0xCC, sizeof(self->addr));
|
memset(self->addr, 0xCC, sizeof(self->addr));
|
||||||
memset(self->head, 0, sizeof(self->head));
|
memset(self->head, 0, sizeof(self->head));
|
||||||
memset(self->tiny_hash, 0, sizeof(self->tiny_hash));
|
|
||||||
memset(self->free_slot_idx, 0, sizeof(self->free_slot_idx));
|
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FN(InitForData)(HashForgetfulChain* self, const uint8_t* data,
|
|
||||||
size_t num) {
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < num; ++i) {
|
|
||||||
size_t bucket = FN(HashBytes)(&data[i]);
|
|
||||||
/* See InitEmpty comment. */
|
|
||||||
self->addr[bucket] = 0xCCCCCCCC;
|
|
||||||
self->head[bucket] = 0xCCCC;
|
|
||||||
}
|
}
|
||||||
memset(self->tiny_hash, 0, sizeof(self->tiny_hash));
|
memset(self->tiny_hash, 0, sizeof(self->tiny_hash));
|
||||||
memset(self->free_slot_idx, 0, sizeof(self->free_slot_idx));
|
memset(self->free_slot_idx, 0, sizeof(self->free_slot_idx));
|
||||||
if (num != 0) {
|
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(Init)(
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
MemoryManager* m, HashForgetfulChain* self, const uint8_t* data,
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
const BrotliEncoderParams* params, size_t position, size_t bytes,
|
size_t input_size) {
|
||||||
BROTLI_BOOL is_last) {
|
BROTLI_UNUSED(params);
|
||||||
/* Choose which init method is faster.
|
BROTLI_UNUSED(one_shot);
|
||||||
Init() is about 100 times faster than InitForData(). */
|
BROTLI_UNUSED(input_size);
|
||||||
const size_t kMaxBytesForPartialHashInit = BUCKET_SIZE >> 6;
|
return sizeof(HashForgetfulChain);
|
||||||
BROTLI_UNUSED(m);
|
|
||||||
self->max_hops = (params->quality > 6 ? 7u : 8u) << (params->quality - 4);
|
|
||||||
if (position == 0 && is_last && bytes <= kMaxBytesForPartialHashInit) {
|
|
||||||
FN(InitForData)(self, data, bytes);
|
|
||||||
} else {
|
|
||||||
FN(InitEmpty)(self);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend
|
/* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend
|
||||||
node to corresponding chain; also update tiny_hash for current position. */
|
node to corresponding chain; also update tiny_hash for current position. */
|
||||||
static BROTLI_INLINE void FN(Store)(HashForgetfulChain* BROTLI_RESTRICT self,
|
static BROTLI_INLINE void FN(Store)(HasherHandle BROTLI_RESTRICT handle,
|
||||||
const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) {
|
const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) {
|
||||||
|
HashForgetfulChain* self = FN(Self)(handle);
|
||||||
const size_t key = FN(HashBytes)(&data[ix & mask]);
|
const size_t key = FN(HashBytes)(&data[ix & mask]);
|
||||||
const size_t bank = key & (NUM_BANKS - 1);
|
const size_t bank = key & (NUM_BANKS - 1);
|
||||||
const size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1);
|
const size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1);
|
||||||
|
@ -122,56 +114,68 @@ static BROTLI_INLINE void FN(Store)(HashForgetfulChain* BROTLI_RESTRICT self,
|
||||||
self->head[key] = (uint16_t)idx;
|
self->head[key] = (uint16_t)idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StoreRange)(HashForgetfulChain* self,
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
const uint8_t *data, const size_t mask, const size_t ix_start,
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
const size_t ix_end) {
|
const size_t ix_end) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = ix_start; i < ix_end; ++i) {
|
for (i = ix_start; i < ix_end; ++i) {
|
||||||
FN(Store)(self, data, mask, i);
|
FN(Store)(handle, data, mask, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HashForgetfulChain* self,
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
|
||||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
size_t ring_buffer_mask) {
|
size_t ring_buffer_mask) {
|
||||||
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
||||||
/* Prepare the hashes for three last bytes of the last write.
|
/* Prepare the hashes for three last bytes of the last write.
|
||||||
These could not be calculated before, since they require knowledge
|
These could not be calculated before, since they require knowledge
|
||||||
of both the previous and the current block. */
|
of both the previous and the current block. */
|
||||||
FN(Store)(self, ringbuffer, ring_buffer_mask, position - 3);
|
FN(Store)(handle, ringbuffer, ring_buffer_mask, position - 3);
|
||||||
FN(Store)(self, ringbuffer, ring_buffer_mask, position - 2);
|
FN(Store)(handle, ringbuffer, ring_buffer_mask, position - 2);
|
||||||
FN(Store)(self, ringbuffer, ring_buffer_mask, position - 1);
|
FN(Store)(handle, ringbuffer, ring_buffer_mask, position - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||||
|
HasherHandle handle, int* BROTLI_RESTRICT distance_cache) {
|
||||||
|
BROTLI_UNUSED(handle);
|
||||||
|
PrepareDistanceCache(distance_cache, NUM_LAST_DISTANCES_TO_CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
/* Find a longest backward match of &data[cur_ix] up to the length of
|
/* Find a longest backward match of &data[cur_ix] up to the length of
|
||||||
max_length and stores the position cur_ix in the hash table.
|
max_length and stores the position cur_ix in the hash table.
|
||||||
|
|
||||||
|
REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache
|
||||||
|
values; if this method is invoked repeatedly with the same distance
|
||||||
|
cache values, it is enough to invoke FN(PrepareDistanceCache) once.
|
||||||
|
|
||||||
Does not look for matches longer than max_length.
|
Does not look for matches longer than max_length.
|
||||||
Does not look for matches further away than max_backward.
|
Does not look for matches further away than max_backward.
|
||||||
Writes the best match into |out|.
|
Writes the best match into |out|.
|
||||||
Returns 1 when match is found, otherwise 0. */
|
|out|->score is updated only if a better match is found. */
|
||||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||||
HashForgetfulChain* self, const uint8_t* BROTLI_RESTRICT data,
|
const BrotliEncoderDictionary* dictionary,
|
||||||
const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
|
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||||
|
const int* BROTLI_RESTRICT distance_cache,
|
||||||
const size_t cur_ix, const size_t max_length, const size_t max_backward,
|
const size_t cur_ix, const size_t max_length, const size_t max_backward,
|
||||||
|
const size_t gap, const size_t max_distance,
|
||||||
HasherSearchResult* BROTLI_RESTRICT out) {
|
HasherSearchResult* BROTLI_RESTRICT out) {
|
||||||
|
HashForgetfulChain* self = FN(Self)(handle);
|
||||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
BROTLI_BOOL is_match_found = BROTLI_FALSE;
|
|
||||||
/* Don't accept a short copy from far away. */
|
/* Don't accept a short copy from far away. */
|
||||||
|
score_t min_score = out->score;
|
||||||
score_t best_score = out->score;
|
score_t best_score = out->score;
|
||||||
size_t best_len = out->len;
|
size_t best_len = out->len;
|
||||||
size_t i;
|
size_t i;
|
||||||
const size_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
const size_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
||||||
const uint8_t tiny_hash = (uint8_t)(key);
|
const uint8_t tiny_hash = (uint8_t)(key);
|
||||||
out->len = 0;
|
out->len = 0;
|
||||||
out->len_x_code = 0;
|
out->len_code_delta = 0;
|
||||||
/* Try last distance first. */
|
/* Try last distance first. */
|
||||||
for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
|
for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
|
||||||
const size_t idx = kDistanceCacheIndex[i];
|
const size_t backward = (size_t)distance_cache[i];
|
||||||
const size_t backward =
|
|
||||||
(size_t)(distance_cache[idx] + kDistanceCacheOffset[i]);
|
|
||||||
size_t prev_ix = (cur_ix - backward);
|
size_t prev_ix = (cur_ix - backward);
|
||||||
|
/* For distance code 0 we want to consider 2-byte matches. */
|
||||||
if (i > 0 && self->tiny_hash[(uint16_t)prev_ix] != tiny_hash) continue;
|
if (i > 0 && self->tiny_hash[(uint16_t)prev_ix] != tiny_hash) continue;
|
||||||
if (prev_ix >= cur_ix || backward > max_backward) {
|
if (prev_ix >= cur_ix || backward > max_backward) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -182,14 +186,16 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||||
&data[cur_ix_masked],
|
&data[cur_ix_masked],
|
||||||
max_length);
|
max_length);
|
||||||
if (len >= 2) {
|
if (len >= 2) {
|
||||||
score_t score = BackwardReferenceScoreUsingLastDistance(len, i);
|
score_t score = BackwardReferenceScoreUsingLastDistance(len);
|
||||||
if (best_score < score) {
|
if (best_score < score) {
|
||||||
best_score = score;
|
if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
|
||||||
best_len = len;
|
if (best_score < score) {
|
||||||
out->len = best_len;
|
best_score = score;
|
||||||
out->distance = backward;
|
best_len = len;
|
||||||
out->score = best_score;
|
out->len = best_len;
|
||||||
is_match_found = BROTLI_TRUE;
|
out->distance = backward;
|
||||||
|
out->score = best_score;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,18 +234,17 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||||
out->len = best_len;
|
out->len = best_len;
|
||||||
out->distance = backward;
|
out->distance = backward;
|
||||||
out->score = best_score;
|
out->score = best_score;
|
||||||
is_match_found = BROTLI_TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FN(Store)(self, data, ring_buffer_mask, cur_ix);
|
FN(Store)(handle, data, ring_buffer_mask, cur_ix);
|
||||||
}
|
}
|
||||||
if (!is_match_found) {
|
if (out->score == min_score) {
|
||||||
is_match_found = SearchInStaticDictionary(&self->dict_search_stats_,
|
SearchInStaticDictionary(dictionary,
|
||||||
&data[cur_ix_masked], max_length, max_backward, out, BROTLI_FALSE);
|
handle, &data[cur_ix_masked], max_length, max_backward + gap,
|
||||||
|
max_distance, out, BROTLI_FALSE);
|
||||||
}
|
}
|
||||||
return is_match_found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef BANK_SIZE
|
#undef BANK_SIZE
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
/* NOLINT(build/header_guard) */
|
||||||
|
/* Copyright 2010 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* template parameters: FN */
|
||||||
|
|
||||||
|
/* A (forgetful) hash table to the data seen by the compressor, to
|
||||||
|
help create backward references to previous data.
|
||||||
|
|
||||||
|
This is a hash map of fixed size (bucket_size_) to a ring buffer of
|
||||||
|
fixed size (block_size_). The ring buffer contains the last block_size_
|
||||||
|
index positions of the given hash key in the compressed data. */
|
||||||
|
|
||||||
|
#define HashLongestMatch HASHER()
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 8; }
|
||||||
|
static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; }
|
||||||
|
|
||||||
|
/* HashBytes is the function that chooses the bucket to place the address in. */
|
||||||
|
static BROTLI_INLINE uint32_t FN(HashBytes)(const uint8_t* data,
|
||||||
|
const uint64_t mask,
|
||||||
|
const int shift) {
|
||||||
|
const uint64_t h = (BROTLI_UNALIGNED_LOAD64LE(data) & mask) * kHashMul64Long;
|
||||||
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
|
so we take our results from there. */
|
||||||
|
return (uint32_t)(h >> shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct HashLongestMatch {
|
||||||
|
/* Number of hash buckets. */
|
||||||
|
size_t bucket_size_;
|
||||||
|
/* Only block_size_ newest backward references are kept,
|
||||||
|
and the older are forgotten. */
|
||||||
|
size_t block_size_;
|
||||||
|
/* Left-shift for computing hash bucket index from hash value. */
|
||||||
|
int hash_shift_;
|
||||||
|
/* Mask for selecting the next 4-8 bytes of input */
|
||||||
|
uint64_t hash_mask_;
|
||||||
|
/* Mask for accessing entries in a block (in a ring-buffer manner). */
|
||||||
|
uint32_t block_mask_;
|
||||||
|
|
||||||
|
/* --- Dynamic size members --- */
|
||||||
|
|
||||||
|
/* Number of entries in a particular bucket. */
|
||||||
|
/* uint16_t num[bucket_size]; */
|
||||||
|
|
||||||
|
/* Buckets containing block_size_ of backward references. */
|
||||||
|
/* uint32_t* buckets[bucket_size * block_size]; */
|
||||||
|
} HashLongestMatch;
|
||||||
|
|
||||||
|
static BROTLI_INLINE HashLongestMatch* FN(Self)(HasherHandle handle) {
|
||||||
|
return (HashLongestMatch*)&(GetHasherCommon(handle)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint16_t* FN(Num)(HashLongestMatch* self) {
|
||||||
|
return (uint16_t*)(&self[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t* FN(Buckets)(HashLongestMatch* self) {
|
||||||
|
return (uint32_t*)(&FN(Num)(self)[self->bucket_size_]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Initialize)(
|
||||||
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
|
HasherCommon* common = GetHasherCommon(handle);
|
||||||
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
|
BROTLI_UNUSED(params);
|
||||||
|
self->hash_shift_ = 64 - common->params.bucket_bits;
|
||||||
|
self->hash_mask_ = (~((uint64_t)0U)) >> (64 - 8 * common->params.hash_len);
|
||||||
|
self->bucket_size_ = (size_t)1 << common->params.bucket_bits;
|
||||||
|
self->block_size_ = (size_t)1 << common->params.block_bits;
|
||||||
|
self->block_mask_ = (uint32_t)(self->block_size_ - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
|
uint16_t* num = FN(Num)(self);
|
||||||
|
/* Partial preparation is 100 times slower (per socket). */
|
||||||
|
size_t partial_prepare_threshold = self->bucket_size_ >> 6;
|
||||||
|
if (one_shot && input_size <= partial_prepare_threshold) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < input_size; ++i) {
|
||||||
|
const uint32_t key = FN(HashBytes)(&data[i], self->hash_mask_,
|
||||||
|
self->hash_shift_);
|
||||||
|
num[key] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memset(num, 0, self->bucket_size_ * sizeof(num[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size) {
|
||||||
|
size_t bucket_size = (size_t)1 << params->hasher.bucket_bits;
|
||||||
|
size_t block_size = (size_t)1 << params->hasher.block_bits;
|
||||||
|
BROTLI_UNUSED(one_shot);
|
||||||
|
BROTLI_UNUSED(input_size);
|
||||||
|
return sizeof(HashLongestMatch) + bucket_size * (2 + 4 * block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look at 4 bytes at &data[ix & mask].
|
||||||
|
Compute a hash from these, and store the value of ix at that position. */
|
||||||
|
static BROTLI_INLINE void FN(Store)(HasherHandle handle, const uint8_t* data,
|
||||||
|
const size_t mask, const size_t ix) {
|
||||||
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
|
uint16_t* num = FN(Num)(self);
|
||||||
|
const uint32_t key = FN(HashBytes)(&data[ix & mask], self->hash_mask_,
|
||||||
|
self->hash_shift_);
|
||||||
|
const size_t minor_ix = num[key] & self->block_mask_;
|
||||||
|
const size_t offset =
|
||||||
|
minor_ix + (key << GetHasherCommon(handle)->params.block_bits);
|
||||||
|
FN(Buckets)(self)[offset] = (uint32_t)ix;
|
||||||
|
++num[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
|
const size_t ix_end) {
|
||||||
|
size_t i;
|
||||||
|
for (i = ix_start; i < ix_end; ++i) {
|
||||||
|
FN(Store)(handle, data, mask, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask) {
|
||||||
|
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
||||||
|
/* Prepare the hashes for three last bytes of the last write.
|
||||||
|
These could not be calculated before, since they require knowledge
|
||||||
|
of both the previous and the current block. */
|
||||||
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 3);
|
||||||
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 2);
|
||||||
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||||
|
HasherHandle handle, int* BROTLI_RESTRICT distance_cache) {
|
||||||
|
PrepareDistanceCache(distance_cache,
|
||||||
|
GetHasherCommon(handle)->params.num_last_distances_to_check);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a longest backward match of &data[cur_ix] up to the length of
|
||||||
|
max_length and stores the position cur_ix in the hash table.
|
||||||
|
|
||||||
|
REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache
|
||||||
|
values; if this method is invoked repeatedly with the same distance
|
||||||
|
cache values, it is enough to invoke FN(PrepareDistanceCache) once.
|
||||||
|
|
||||||
|
Does not look for matches longer than max_length.
|
||||||
|
Does not look for matches further away than max_backward.
|
||||||
|
Writes the best match into |out|.
|
||||||
|
|out|->score is updated only if a better match is found. */
|
||||||
|
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||||
|
const BrotliEncoderDictionary* dictionary,
|
||||||
|
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||||
|
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
||||||
|
const size_t max_length, const size_t max_backward, const size_t gap,
|
||||||
|
const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) {
|
||||||
|
HasherCommon* common = GetHasherCommon(handle);
|
||||||
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
|
uint16_t* num = FN(Num)(self);
|
||||||
|
uint32_t* buckets = FN(Buckets)(self);
|
||||||
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
|
/* Don't accept a short copy from far away. */
|
||||||
|
score_t min_score = out->score;
|
||||||
|
score_t best_score = out->score;
|
||||||
|
size_t best_len = out->len;
|
||||||
|
size_t i;
|
||||||
|
out->len = 0;
|
||||||
|
out->len_code_delta = 0;
|
||||||
|
/* Try last distance first. */
|
||||||
|
for (i = 0; i < (size_t)common->params.num_last_distances_to_check; ++i) {
|
||||||
|
const size_t backward = (size_t)distance_cache[i];
|
||||||
|
size_t prev_ix = (size_t)(cur_ix - backward);
|
||||||
|
if (prev_ix >= cur_ix) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (BROTLI_PREDICT_FALSE(backward > max_backward)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
prev_ix &= ring_buffer_mask;
|
||||||
|
|
||||||
|
if (cur_ix_masked + best_len > ring_buffer_mask ||
|
||||||
|
prev_ix + best_len > ring_buffer_mask ||
|
||||||
|
data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const size_t len = FindMatchLengthWithLimit(&data[prev_ix],
|
||||||
|
&data[cur_ix_masked],
|
||||||
|
max_length);
|
||||||
|
if (len >= 3 || (len == 2 && i < 2)) {
|
||||||
|
/* Comparing for >= 2 does not change the semantics, but just saves for
|
||||||
|
a few unnecessary binary logarithms in backward reference score,
|
||||||
|
since we are not interested in such short matches. */
|
||||||
|
score_t score = BackwardReferenceScoreUsingLastDistance(len);
|
||||||
|
if (best_score < score) {
|
||||||
|
if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
|
||||||
|
if (best_score < score) {
|
||||||
|
best_score = score;
|
||||||
|
best_len = len;
|
||||||
|
out->len = best_len;
|
||||||
|
out->distance = backward;
|
||||||
|
out->score = best_score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const uint32_t key = FN(HashBytes)(
|
||||||
|
&data[cur_ix_masked], self->hash_mask_, self->hash_shift_);
|
||||||
|
uint32_t* BROTLI_RESTRICT bucket =
|
||||||
|
&buckets[key << common->params.block_bits];
|
||||||
|
const size_t down =
|
||||||
|
(num[key] > self->block_size_) ?
|
||||||
|
(num[key] - self->block_size_) : 0u;
|
||||||
|
for (i = num[key]; i > down;) {
|
||||||
|
size_t prev_ix = bucket[--i & self->block_mask_];
|
||||||
|
const size_t backward = cur_ix - prev_ix;
|
||||||
|
if (BROTLI_PREDICT_FALSE(backward > max_backward)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_ix &= ring_buffer_mask;
|
||||||
|
if (cur_ix_masked + best_len > ring_buffer_mask ||
|
||||||
|
prev_ix + best_len > ring_buffer_mask ||
|
||||||
|
data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const size_t len = FindMatchLengthWithLimit(&data[prev_ix],
|
||||||
|
&data[cur_ix_masked],
|
||||||
|
max_length);
|
||||||
|
if (len >= 4) {
|
||||||
|
/* Comparing for >= 3 does not change the semantics, but just saves
|
||||||
|
for a few unnecessary binary logarithms in backward reference
|
||||||
|
score, since we are not interested in such short matches. */
|
||||||
|
score_t score = BackwardReferenceScore(len, backward);
|
||||||
|
if (best_score < score) {
|
||||||
|
best_score = score;
|
||||||
|
best_len = len;
|
||||||
|
out->len = best_len;
|
||||||
|
out->distance = backward;
|
||||||
|
out->score = best_score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
|
||||||
|
++num[key];
|
||||||
|
}
|
||||||
|
if (min_score == out->score) {
|
||||||
|
SearchInStaticDictionary(dictionary,
|
||||||
|
handle, &data[cur_ix_masked], max_length, max_backward + gap,
|
||||||
|
max_distance, out, BROTLI_FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef HashLongestMatch
|
|
@ -5,158 +5,177 @@
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* template parameters: FN, BUCKET_BITS, BLOCK_BITS,
|
/* template parameters: FN */
|
||||||
NUM_LAST_DISTANCES_TO_CHECK */
|
|
||||||
|
|
||||||
/* A (forgetful) hash table to the data seen by the compressor, to
|
/* A (forgetful) hash table to the data seen by the compressor, to
|
||||||
help create backward references to previous data.
|
help create backward references to previous data.
|
||||||
|
|
||||||
This is a hash map of fixed size (BUCKET_SIZE) to a ring buffer of
|
This is a hash map of fixed size (bucket_size_) to a ring buffer of
|
||||||
fixed size (BLOCK_SIZE). The ring buffer contains the last BLOCK_SIZE
|
fixed size (block_size_). The ring buffer contains the last block_size_
|
||||||
index positions of the given hash key in the compressed data. */
|
index positions of the given hash key in the compressed data. */
|
||||||
|
|
||||||
#define HashLongestMatch HASHER()
|
#define HashLongestMatch HASHER()
|
||||||
|
|
||||||
/* Number of hash buckets. */
|
|
||||||
#define BUCKET_SIZE (1 << BUCKET_BITS)
|
|
||||||
|
|
||||||
/* Only BLOCK_SIZE newest backward references are kept,
|
|
||||||
and the older are forgotten. */
|
|
||||||
#define BLOCK_SIZE (1u << BLOCK_BITS)
|
|
||||||
|
|
||||||
/* Mask for accessing entries in a block (in a ringbuffer manner). */
|
|
||||||
#define BLOCK_MASK ((1 << BLOCK_BITS) - 1)
|
|
||||||
|
|
||||||
#define HASH_MAP_SIZE (2 << BUCKET_BITS)
|
|
||||||
|
|
||||||
static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
|
static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
|
||||||
static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; }
|
static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; }
|
||||||
|
|
||||||
/* HashBytes is the function that chooses the bucket to place
|
/* HashBytes is the function that chooses the bucket to place the address in. */
|
||||||
the address in. The HashLongestMatch and HashLongestMatchQuickly
|
static uint32_t FN(HashBytes)(const uint8_t* data, const int shift) {
|
||||||
classes have separate, different implementations of hashing. */
|
uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
|
||||||
static uint32_t FN(HashBytes)(const uint8_t *data) {
|
|
||||||
uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;
|
|
||||||
/* The higher bits contain more mixture from the multiplication,
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
so we take our results from there. */
|
so we take our results from there. */
|
||||||
return h >> (32 - BUCKET_BITS);
|
return (uint32_t)(h >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct HashLongestMatch {
|
typedef struct HashLongestMatch {
|
||||||
|
/* Number of hash buckets. */
|
||||||
|
size_t bucket_size_;
|
||||||
|
/* Only block_size_ newest backward references are kept,
|
||||||
|
and the older are forgotten. */
|
||||||
|
size_t block_size_;
|
||||||
|
/* Left-shift for computing hash bucket index from hash value. */
|
||||||
|
int hash_shift_;
|
||||||
|
/* Mask for accessing entries in a block (in a ring-buffer manner). */
|
||||||
|
uint32_t block_mask_;
|
||||||
|
|
||||||
|
/* --- Dynamic size members --- */
|
||||||
|
|
||||||
/* Number of entries in a particular bucket. */
|
/* Number of entries in a particular bucket. */
|
||||||
uint16_t num_[BUCKET_SIZE];
|
/* uint16_t num[bucket_size]; */
|
||||||
|
|
||||||
/* Buckets containing BLOCK_SIZE of backward references. */
|
/* Buckets containing block_size_ of backward references. */
|
||||||
uint32_t buckets_[BLOCK_SIZE << BUCKET_BITS];
|
/* uint32_t* buckets[bucket_size * block_size]; */
|
||||||
|
|
||||||
/* True if num_ array needs to be initialized. */
|
|
||||||
BROTLI_BOOL is_dirty_;
|
|
||||||
|
|
||||||
DictionarySearchStatictics dict_search_stats_;
|
|
||||||
} HashLongestMatch;
|
} HashLongestMatch;
|
||||||
|
|
||||||
static void FN(Reset)(HashLongestMatch* self) {
|
static BROTLI_INLINE HashLongestMatch* FN(Self)(HasherHandle handle) {
|
||||||
self->is_dirty_ = BROTLI_TRUE;
|
return (HashLongestMatch*)&(GetHasherCommon(handle)[1]);
|
||||||
DictionarySearchStaticticsReset(&self->dict_search_stats_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(InitEmpty)(HashLongestMatch* self) {
|
static BROTLI_INLINE uint16_t* FN(Num)(HashLongestMatch* self) {
|
||||||
if (self->is_dirty_) {
|
return (uint16_t*)(&self[1]);
|
||||||
memset(self->num_, 0, sizeof(self->num_));
|
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(InitForData)(HashLongestMatch* self, const uint8_t* data,
|
static BROTLI_INLINE uint32_t* FN(Buckets)(HashLongestMatch* self) {
|
||||||
size_t num) {
|
return (uint32_t*)(&FN(Num)(self)[self->bucket_size_]);
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < num; ++i) {
|
|
||||||
const uint32_t key = FN(HashBytes)(&data[i]);
|
|
||||||
self->num_[key] = 0;
|
|
||||||
}
|
|
||||||
if (num != 0) {
|
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(Init)(
|
static void FN(Initialize)(
|
||||||
MemoryManager* m, HashLongestMatch* self, const uint8_t* data,
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
const BrotliEncoderParams* params, size_t position, size_t bytes,
|
HasherCommon* common = GetHasherCommon(handle);
|
||||||
BROTLI_BOOL is_last) {
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
/* Choose which init method is faster.
|
|
||||||
Init() is about 100 times faster than InitForData(). */
|
|
||||||
const size_t kMaxBytesForPartialHashInit = HASH_MAP_SIZE >> 7;
|
|
||||||
BROTLI_UNUSED(m);
|
|
||||||
BROTLI_UNUSED(params);
|
BROTLI_UNUSED(params);
|
||||||
if (position == 0 && is_last && bytes <= kMaxBytesForPartialHashInit) {
|
self->hash_shift_ = 32 - common->params.bucket_bits;
|
||||||
FN(InitForData)(self, data, bytes);
|
self->bucket_size_ = (size_t)1 << common->params.bucket_bits;
|
||||||
|
self->block_size_ = (size_t)1 << common->params.block_bits;
|
||||||
|
self->block_mask_ = (uint32_t)(self->block_size_ - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
|
uint16_t* num = FN(Num)(self);
|
||||||
|
/* Partial preparation is 100 times slower (per socket). */
|
||||||
|
size_t partial_prepare_threshold = self->bucket_size_ >> 6;
|
||||||
|
if (one_shot && input_size <= partial_prepare_threshold) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < input_size; ++i) {
|
||||||
|
const uint32_t key = FN(HashBytes)(&data[i], self->hash_shift_);
|
||||||
|
num[key] = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
FN(InitEmpty)(self);
|
memset(num, 0, self->bucket_size_ * sizeof(num[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size) {
|
||||||
|
size_t bucket_size = (size_t)1 << params->hasher.bucket_bits;
|
||||||
|
size_t block_size = (size_t)1 << params->hasher.block_bits;
|
||||||
|
BROTLI_UNUSED(one_shot);
|
||||||
|
BROTLI_UNUSED(input_size);
|
||||||
|
return sizeof(HashLongestMatch) + bucket_size * (2 + 4 * block_size);
|
||||||
|
}
|
||||||
|
|
||||||
/* Look at 4 bytes at &data[ix & mask].
|
/* Look at 4 bytes at &data[ix & mask].
|
||||||
Compute a hash from these, and store the value of ix at that position. */
|
Compute a hash from these, and store the value of ix at that position. */
|
||||||
static BROTLI_INLINE void FN(Store)(HashLongestMatch* self, const uint8_t *data,
|
static BROTLI_INLINE void FN(Store)(HasherHandle handle, const uint8_t* data,
|
||||||
const size_t mask, const size_t ix) {
|
const size_t mask, const size_t ix) {
|
||||||
const uint32_t key = FN(HashBytes)(&data[ix & mask]);
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
const size_t minor_ix = self->num_[key] & BLOCK_MASK;
|
uint16_t* num = FN(Num)(self);
|
||||||
self->buckets_[minor_ix + (key << BLOCK_BITS)] = (uint32_t)ix;
|
const uint32_t key = FN(HashBytes)(&data[ix & mask], self->hash_shift_);
|
||||||
++self->num_[key];
|
const size_t minor_ix = num[key] & self->block_mask_;
|
||||||
|
const size_t offset =
|
||||||
|
minor_ix + (key << GetHasherCommon(handle)->params.block_bits);
|
||||||
|
FN(Buckets)(self)[offset] = (uint32_t)ix;
|
||||||
|
++num[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* self,
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
const uint8_t *data, const size_t mask, const size_t ix_start,
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
const size_t ix_end) {
|
const size_t ix_end) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = ix_start; i < ix_end; ++i) {
|
for (i = ix_start; i < ix_end; ++i) {
|
||||||
FN(Store)(self, data, mask, i);
|
FN(Store)(handle, data, mask, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HashLongestMatch* self,
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
|
||||||
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
size_t ringbuffer_mask) {
|
size_t ringbuffer_mask) {
|
||||||
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
||||||
/* Prepare the hashes for three last bytes of the last write.
|
/* Prepare the hashes for three last bytes of the last write.
|
||||||
These could not be calculated before, since they require knowledge
|
These could not be calculated before, since they require knowledge
|
||||||
of both the previous and the current block. */
|
of both the previous and the current block. */
|
||||||
FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3);
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 3);
|
||||||
FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2);
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 2);
|
||||||
FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1);
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||||
|
HasherHandle handle, int* BROTLI_RESTRICT distance_cache) {
|
||||||
|
PrepareDistanceCache(distance_cache,
|
||||||
|
GetHasherCommon(handle)->params.num_last_distances_to_check);
|
||||||
|
}
|
||||||
|
|
||||||
/* Find a longest backward match of &data[cur_ix] up to the length of
|
/* Find a longest backward match of &data[cur_ix] up to the length of
|
||||||
max_length and stores the position cur_ix in the hash table.
|
max_length and stores the position cur_ix in the hash table.
|
||||||
|
|
||||||
|
REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache
|
||||||
|
values; if this method is invoked repeatedly with the same distance
|
||||||
|
cache values, it is enough to invoke FN(PrepareDistanceCache) once.
|
||||||
|
|
||||||
Does not look for matches longer than max_length.
|
Does not look for matches longer than max_length.
|
||||||
Does not look for matches further away than max_backward.
|
Does not look for matches further away than max_backward.
|
||||||
Writes the best match into |out|.
|
Writes the best match into |out|.
|
||||||
Returns true when match is found, otherwise false. */
|
|out|->score is updated only if a better match is found. */
|
||||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HashLongestMatch* self,
|
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||||
|
const BrotliEncoderDictionary* dictionary,
|
||||||
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||||
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
||||||
const size_t max_length, const size_t max_backward,
|
const size_t max_length, const size_t max_backward, const size_t gap,
|
||||||
HasherSearchResult* BROTLI_RESTRICT out) {
|
const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) {
|
||||||
|
HasherCommon* common = GetHasherCommon(handle);
|
||||||
|
HashLongestMatch* self = FN(Self)(handle);
|
||||||
|
uint16_t* num = FN(Num)(self);
|
||||||
|
uint32_t* buckets = FN(Buckets)(self);
|
||||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
BROTLI_BOOL is_match_found = BROTLI_FALSE;
|
|
||||||
/* Don't accept a short copy from far away. */
|
/* Don't accept a short copy from far away. */
|
||||||
|
score_t min_score = out->score;
|
||||||
score_t best_score = out->score;
|
score_t best_score = out->score;
|
||||||
size_t best_len = out->len;
|
size_t best_len = out->len;
|
||||||
size_t i;
|
size_t i;
|
||||||
out->len = 0;
|
out->len = 0;
|
||||||
out->len_x_code = 0;
|
out->len_code_delta = 0;
|
||||||
/* Try last distance first. */
|
/* Try last distance first. */
|
||||||
for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
|
for (i = 0; i < (size_t)common->params.num_last_distances_to_check; ++i) {
|
||||||
const size_t idx = kDistanceCacheIndex[i];
|
const size_t backward = (size_t)distance_cache[i];
|
||||||
const size_t backward =
|
|
||||||
(size_t)(distance_cache[idx] + kDistanceCacheOffset[i]);
|
|
||||||
size_t prev_ix = (size_t)(cur_ix - backward);
|
size_t prev_ix = (size_t)(cur_ix - backward);
|
||||||
if (prev_ix >= cur_ix) {
|
if (prev_ix >= cur_ix) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (PREDICT_FALSE(backward > max_backward)) {
|
if (BROTLI_PREDICT_FALSE(backward > max_backward)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
prev_ix &= ring_buffer_mask;
|
prev_ix &= ring_buffer_mask;
|
||||||
|
@ -174,27 +193,31 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HashLongestMatch* self,
|
||||||
/* Comparing for >= 2 does not change the semantics, but just saves for
|
/* Comparing for >= 2 does not change the semantics, but just saves for
|
||||||
a few unnecessary binary logarithms in backward reference score,
|
a few unnecessary binary logarithms in backward reference score,
|
||||||
since we are not interested in such short matches. */
|
since we are not interested in such short matches. */
|
||||||
score_t score = BackwardReferenceScoreUsingLastDistance(len, i);
|
score_t score = BackwardReferenceScoreUsingLastDistance(len);
|
||||||
if (best_score < score) {
|
if (best_score < score) {
|
||||||
best_score = score;
|
if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
|
||||||
best_len = len;
|
if (best_score < score) {
|
||||||
out->len = best_len;
|
best_score = score;
|
||||||
out->distance = backward;
|
best_len = len;
|
||||||
out->score = best_score;
|
out->len = best_len;
|
||||||
is_match_found = BROTLI_TRUE;
|
out->distance = backward;
|
||||||
|
out->score = best_score;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
const uint32_t key =
|
||||||
uint32_t* BROTLI_RESTRICT bucket = &self->buckets_[key << BLOCK_BITS];
|
FN(HashBytes)(&data[cur_ix_masked], self->hash_shift_);
|
||||||
|
uint32_t* BROTLI_RESTRICT bucket =
|
||||||
|
&buckets[key << common->params.block_bits];
|
||||||
const size_t down =
|
const size_t down =
|
||||||
(self->num_[key] > BLOCK_SIZE) ? (self->num_[key] - BLOCK_SIZE) : 0u;
|
(num[key] > self->block_size_) ? (num[key] - self->block_size_) : 0u;
|
||||||
for (i = self->num_[key]; i > down;) {
|
for (i = num[key]; i > down;) {
|
||||||
size_t prev_ix = bucket[--i & BLOCK_MASK];
|
size_t prev_ix = bucket[--i & self->block_mask_];
|
||||||
const size_t backward = cur_ix - prev_ix;
|
const size_t backward = cur_ix - prev_ix;
|
||||||
if (PREDICT_FALSE(backward > max_backward)) {
|
if (BROTLI_PREDICT_FALSE(backward > max_backward)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
prev_ix &= ring_buffer_mask;
|
prev_ix &= ring_buffer_mask;
|
||||||
|
@ -218,24 +241,18 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HashLongestMatch* self,
|
||||||
out->len = best_len;
|
out->len = best_len;
|
||||||
out->distance = backward;
|
out->distance = backward;
|
||||||
out->score = best_score;
|
out->score = best_score;
|
||||||
is_match_found = BROTLI_TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bucket[self->num_[key] & BLOCK_MASK] = (uint32_t)cur_ix;
|
bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
|
||||||
++self->num_[key];
|
++num[key];
|
||||||
}
|
}
|
||||||
if (!is_match_found) {
|
if (min_score == out->score) {
|
||||||
is_match_found = SearchInStaticDictionary(&self->dict_search_stats_,
|
SearchInStaticDictionary(dictionary,
|
||||||
&data[cur_ix_masked], max_length, max_backward, out, BROTLI_FALSE);
|
handle, &data[cur_ix_masked], max_length, max_backward + gap,
|
||||||
|
max_distance, out, BROTLI_FALSE);
|
||||||
}
|
}
|
||||||
return is_match_found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef HASH_MAP_SIZE
|
|
||||||
#undef BLOCK_MASK
|
|
||||||
#undef BLOCK_SIZE
|
|
||||||
#undef BUCKET_SIZE
|
|
||||||
|
|
||||||
#undef HashLongestMatch
|
#undef HashLongestMatch
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* template parameters: FN, BUCKET_BITS, BUCKET_SWEEP, USE_DICTIONARY */
|
/* template parameters: FN, BUCKET_BITS, BUCKET_SWEEP, HASH_LEN,
|
||||||
|
USE_DICTIONARY
|
||||||
|
*/
|
||||||
|
|
||||||
#define HashLongestMatchQuickly HASHER()
|
#define HashLongestMatchQuickly HASHER()
|
||||||
|
|
||||||
|
@ -19,10 +21,9 @@ static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 8; }
|
||||||
/* HashBytes is the function that chooses the bucket to place
|
/* HashBytes is the function that chooses the bucket to place
|
||||||
the address in. The HashLongestMatch and HashLongestMatchQuickly
|
the address in. The HashLongestMatch and HashLongestMatchQuickly
|
||||||
classes have separate, different implementations of hashing. */
|
classes have separate, different implementations of hashing. */
|
||||||
static uint32_t FN(HashBytes)(const uint8_t *data) {
|
static uint32_t FN(HashBytes)(const uint8_t* data) {
|
||||||
/* Computing a hash based on 5 bytes works much better for
|
const uint64_t h = ((BROTLI_UNALIGNED_LOAD64LE(data) << (64 - 8 * HASH_LEN)) *
|
||||||
qualities 1 and 3, where the next hash value is likely to replace */
|
kHashMul64);
|
||||||
uint64_t h = (BROTLI_UNALIGNED_LOAD64(data) << 24) * kHashMul32;
|
|
||||||
/* The higher bits contain more mixture from the multiplication,
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
so we take our results from there. */
|
so we take our results from there. */
|
||||||
return (uint32_t)(h >> (64 - BUCKET_BITS));
|
return (uint32_t)(h >> (64 - BUCKET_BITS));
|
||||||
|
@ -35,88 +36,86 @@ static uint32_t FN(HashBytes)(const uint8_t *data) {
|
||||||
given index, BUCKET_SWEEP buckets are used to store values of a key. */
|
given index, BUCKET_SWEEP buckets are used to store values of a key. */
|
||||||
typedef struct HashLongestMatchQuickly {
|
typedef struct HashLongestMatchQuickly {
|
||||||
uint32_t buckets_[BUCKET_SIZE + BUCKET_SWEEP];
|
uint32_t buckets_[BUCKET_SIZE + BUCKET_SWEEP];
|
||||||
/* True if buckets_ array needs to be initialized. */
|
|
||||||
BROTLI_BOOL is_dirty_;
|
|
||||||
DictionarySearchStatictics dict_search_stats_;
|
|
||||||
} HashLongestMatchQuickly;
|
} HashLongestMatchQuickly;
|
||||||
|
|
||||||
static void FN(Reset)(HashLongestMatchQuickly* self) {
|
static BROTLI_INLINE HashLongestMatchQuickly* FN(Self)(HasherHandle handle) {
|
||||||
self->is_dirty_ = BROTLI_TRUE;
|
return (HashLongestMatchQuickly*)&(GetHasherCommon(handle)[1]);
|
||||||
DictionarySearchStaticticsReset(&self->dict_search_stats_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(InitEmpty)(HashLongestMatchQuickly* self) {
|
static void FN(Initialize)(
|
||||||
if (self->is_dirty_) {
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
|
BROTLI_UNUSED(handle);
|
||||||
|
BROTLI_UNUSED(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashLongestMatchQuickly* self = FN(Self)(handle);
|
||||||
|
/* Partial preparation is 100 times slower (per socket). */
|
||||||
|
size_t partial_prepare_threshold = HASH_MAP_SIZE >> 7;
|
||||||
|
if (one_shot && input_size <= partial_prepare_threshold) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < input_size; ++i) {
|
||||||
|
const uint32_t key = FN(HashBytes)(&data[i]);
|
||||||
|
memset(&self->buckets_[key], 0, BUCKET_SWEEP * sizeof(self->buckets_[0]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
/* It is not strictly necessary to fill this buffer here, but
|
/* It is not strictly necessary to fill this buffer here, but
|
||||||
not filling will make the results of the compression stochastic
|
not filling will make the results of the compression stochastic
|
||||||
(but correct). This is because random data would cause the
|
(but correct). This is because random data would cause the
|
||||||
system to find accidentally good backward references here and there. */
|
system to find accidentally good backward references here and there. */
|
||||||
memset(&self->buckets_[0], 0, sizeof(self->buckets_));
|
memset(&self->buckets_[0], 0, sizeof(self->buckets_));
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FN(InitForData)(HashLongestMatchQuickly* self, const uint8_t* data,
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
size_t num) {
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
size_t i;
|
size_t input_size) {
|
||||||
for (i = 0; i < num; ++i) {
|
|
||||||
const uint32_t key = FN(HashBytes)(&data[i]);
|
|
||||||
memset(&self->buckets_[key], 0, BUCKET_SWEEP * sizeof(self->buckets_[0]));
|
|
||||||
}
|
|
||||||
if (num != 0) {
|
|
||||||
self->is_dirty_ = BROTLI_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FN(Init)(
|
|
||||||
MemoryManager* m, HashLongestMatchQuickly* self, const uint8_t* data,
|
|
||||||
const BrotliEncoderParams* params, size_t position, size_t bytes,
|
|
||||||
BROTLI_BOOL is_last) {
|
|
||||||
/* Choose which init method is faster.
|
|
||||||
Init() is about 100 times faster than InitForData(). */
|
|
||||||
const size_t kMaxBytesForPartialHashInit = HASH_MAP_SIZE >> 7;
|
|
||||||
BROTLI_UNUSED(m);
|
|
||||||
BROTLI_UNUSED(params);
|
BROTLI_UNUSED(params);
|
||||||
if (position == 0 && is_last && bytes <= kMaxBytesForPartialHashInit) {
|
BROTLI_UNUSED(one_shot);
|
||||||
FN(InitForData)(self, data, bytes);
|
BROTLI_UNUSED(input_size);
|
||||||
} else {
|
return sizeof(HashLongestMatchQuickly);
|
||||||
FN(InitEmpty)(self);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Look at 5 bytes at &data[ix & mask].
|
/* Look at 5 bytes at &data[ix & mask].
|
||||||
Compute a hash from these, and store the value somewhere within
|
Compute a hash from these, and store the value somewhere within
|
||||||
[ix .. ix+3]. */
|
[ix .. ix+3]. */
|
||||||
static BROTLI_INLINE void FN(Store)(HashLongestMatchQuickly* self,
|
static BROTLI_INLINE void FN(Store)(HasherHandle handle,
|
||||||
const uint8_t *data, const size_t mask, const size_t ix) {
|
const uint8_t* data, const size_t mask, const size_t ix) {
|
||||||
const uint32_t key = FN(HashBytes)(&data[ix & mask]);
|
const uint32_t key = FN(HashBytes)(&data[ix & mask]);
|
||||||
/* Wiggle the value with the bucket sweep range. */
|
/* Wiggle the value with the bucket sweep range. */
|
||||||
const uint32_t off = (ix >> 3) % BUCKET_SWEEP;
|
const uint32_t off = (ix >> 3) % BUCKET_SWEEP;
|
||||||
self->buckets_[key + off] = (uint32_t)ix;
|
FN(Self)(handle)->buckets_[key + off] = (uint32_t)ix;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StoreRange)(HashLongestMatchQuickly* self,
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
const uint8_t *data, const size_t mask, const size_t ix_start,
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
const size_t ix_end) {
|
const size_t ix_end) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = ix_start; i < ix_end; ++i) {
|
for (i = ix_start; i < ix_end; ++i) {
|
||||||
FN(Store)(self, data, mask, i);
|
FN(Store)(handle, data, mask, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(StitchToPreviousBlock)(
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(
|
||||||
HashLongestMatchQuickly* self, size_t num_bytes, size_t position,
|
HasherHandle handle, size_t num_bytes, size_t position,
|
||||||
const uint8_t* ringbuffer, size_t ringbuffer_mask) {
|
const uint8_t* ringbuffer, size_t ringbuffer_mask) {
|
||||||
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {
|
||||||
/* Prepare the hashes for three last bytes of the last write.
|
/* Prepare the hashes for three last bytes of the last write.
|
||||||
These could not be calculated before, since they require knowledge
|
These could not be calculated before, since they require knowledge
|
||||||
of both the previous and the current block. */
|
of both the previous and the current block. */
|
||||||
FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3);
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 3);
|
||||||
FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2);
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 2);
|
||||||
FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1);
|
FN(Store)(handle, ringbuffer, ringbuffer_mask, position - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||||
|
HasherHandle handle, int* BROTLI_RESTRICT distance_cache) {
|
||||||
|
BROTLI_UNUSED(handle);
|
||||||
|
BROTLI_UNUSED(distance_cache);
|
||||||
|
}
|
||||||
|
|
||||||
/* Find a longest backward match of &data[cur_ix & ring_buffer_mask]
|
/* Find a longest backward match of &data[cur_ix & ring_buffer_mask]
|
||||||
up to the length of max_length and stores the position cur_ix in the
|
up to the length of max_length and stores the position cur_ix in the
|
||||||
hash table.
|
hash table.
|
||||||
|
@ -124,22 +123,25 @@ static BROTLI_INLINE void FN(StitchToPreviousBlock)(
|
||||||
Does not look for matches longer than max_length.
|
Does not look for matches longer than max_length.
|
||||||
Does not look for matches further away than max_backward.
|
Does not look for matches further away than max_backward.
|
||||||
Writes the best match into |out|.
|
Writes the best match into |out|.
|
||||||
Returns true if match is found, otherwise false. */
|
|out|->score is updated only if a better match is found. */
|
||||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
static BROTLI_INLINE void FN(FindLongestMatch)(
|
||||||
HashLongestMatchQuickly* self, const uint8_t* BROTLI_RESTRICT data,
|
HasherHandle handle, const BrotliEncoderDictionary* dictionary,
|
||||||
|
const uint8_t* BROTLI_RESTRICT data,
|
||||||
const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
|
const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
|
||||||
const size_t cur_ix, const size_t max_length, const size_t max_backward,
|
const size_t cur_ix, const size_t max_length, const size_t max_backward,
|
||||||
|
const size_t gap, const size_t max_distance,
|
||||||
HasherSearchResult* BROTLI_RESTRICT out) {
|
HasherSearchResult* BROTLI_RESTRICT out) {
|
||||||
|
HashLongestMatchQuickly* self = FN(Self)(handle);
|
||||||
const size_t best_len_in = out->len;
|
const size_t best_len_in = out->len;
|
||||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
||||||
int compare_char = data[cur_ix_masked + best_len_in];
|
int compare_char = data[cur_ix_masked + best_len_in];
|
||||||
|
score_t min_score = out->score;
|
||||||
score_t best_score = out->score;
|
score_t best_score = out->score;
|
||||||
size_t best_len = best_len_in;
|
size_t best_len = best_len_in;
|
||||||
size_t cached_backward = (size_t)distance_cache[0];
|
size_t cached_backward = (size_t)distance_cache[0];
|
||||||
size_t prev_ix = cur_ix - cached_backward;
|
size_t prev_ix = cur_ix - cached_backward;
|
||||||
BROTLI_BOOL is_match_found = BROTLI_FALSE;
|
out->len_code_delta = 0;
|
||||||
out->len_x_code = 0;
|
|
||||||
if (prev_ix < cur_ix) {
|
if (prev_ix < cur_ix) {
|
||||||
prev_ix &= (uint32_t)ring_buffer_mask;
|
prev_ix &= (uint32_t)ring_buffer_mask;
|
||||||
if (compare_char == data[prev_ix + best_len]) {
|
if (compare_char == data[prev_ix + best_len]) {
|
||||||
|
@ -147,17 +149,18 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||||
&data[cur_ix_masked],
|
&data[cur_ix_masked],
|
||||||
max_length);
|
max_length);
|
||||||
if (len >= 4) {
|
if (len >= 4) {
|
||||||
best_score = BackwardReferenceScoreUsingLastDistance(len, 0);
|
const score_t score = BackwardReferenceScoreUsingLastDistance(len);
|
||||||
best_len = len;
|
if (best_score < score) {
|
||||||
out->len = len;
|
best_score = score;
|
||||||
out->distance = cached_backward;
|
best_len = len;
|
||||||
out->score = best_score;
|
out->len = len;
|
||||||
compare_char = data[cur_ix_masked + best_len];
|
out->distance = cached_backward;
|
||||||
if (BUCKET_SWEEP == 1) {
|
out->score = best_score;
|
||||||
self->buckets_[key] = (uint32_t)cur_ix;
|
compare_char = data[cur_ix_masked + best_len];
|
||||||
return BROTLI_TRUE;
|
if (BUCKET_SWEEP == 1) {
|
||||||
} else {
|
self->buckets_[key] = (uint32_t)cur_ix;
|
||||||
is_match_found = BROTLI_TRUE;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,22 +174,25 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||||
backward = cur_ix - prev_ix;
|
backward = cur_ix - prev_ix;
|
||||||
prev_ix &= (uint32_t)ring_buffer_mask;
|
prev_ix &= (uint32_t)ring_buffer_mask;
|
||||||
if (compare_char != data[prev_ix + best_len_in]) {
|
if (compare_char != data[prev_ix + best_len_in]) {
|
||||||
return BROTLI_FALSE;
|
return;
|
||||||
}
|
}
|
||||||
if (PREDICT_FALSE(backward == 0 || backward > max_backward)) {
|
if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) {
|
||||||
return BROTLI_FALSE;
|
return;
|
||||||
}
|
}
|
||||||
len = FindMatchLengthWithLimit(&data[prev_ix],
|
len = FindMatchLengthWithLimit(&data[prev_ix],
|
||||||
&data[cur_ix_masked],
|
&data[cur_ix_masked],
|
||||||
max_length);
|
max_length);
|
||||||
if (len >= 4) {
|
if (len >= 4) {
|
||||||
out->len = len;
|
const score_t score = BackwardReferenceScore(len, backward);
|
||||||
out->distance = backward;
|
if (best_score < score) {
|
||||||
out->score = BackwardReferenceScore(len, backward);
|
out->len = len;
|
||||||
return BROTLI_TRUE;
|
out->distance = backward;
|
||||||
|
out->score = score;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint32_t *bucket = self->buckets_ + key;
|
uint32_t* bucket = self->buckets_ + key;
|
||||||
int i;
|
int i;
|
||||||
prev_ix = *bucket++;
|
prev_ix = *bucket++;
|
||||||
for (i = 0; i < BUCKET_SWEEP; ++i, prev_ix = *bucket++) {
|
for (i = 0; i < BUCKET_SWEEP; ++i, prev_ix = *bucket++) {
|
||||||
|
@ -196,7 +202,7 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||||
if (compare_char != data[prev_ix + best_len]) {
|
if (compare_char != data[prev_ix + best_len]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (PREDICT_FALSE(backward == 0 || backward > max_backward)) {
|
if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
len = FindMatchLengthWithLimit(&data[prev_ix],
|
len = FindMatchLengthWithLimit(&data[prev_ix],
|
||||||
|
@ -211,17 +217,16 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||||
out->distance = backward;
|
out->distance = backward;
|
||||||
out->score = score;
|
out->score = score;
|
||||||
compare_char = data[cur_ix_masked + best_len];
|
compare_char = data[cur_ix_masked + best_len];
|
||||||
is_match_found = BROTLI_TRUE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (USE_DICTIONARY && !is_match_found) {
|
if (USE_DICTIONARY && min_score == out->score) {
|
||||||
is_match_found = SearchInStaticDictionary(&self->dict_search_stats_,
|
SearchInStaticDictionary(dictionary,
|
||||||
&data[cur_ix_masked], max_length, max_backward, out, BROTLI_TRUE);
|
handle, &data[cur_ix_masked], max_length, max_backward + gap,
|
||||||
|
max_distance, out, BROTLI_TRUE);
|
||||||
}
|
}
|
||||||
self->buckets_[key + ((cur_ix >> 3) % BUCKET_SWEEP)] = (uint32_t)cur_ix;
|
self->buckets_[key + ((cur_ix >> 3) % BUCKET_SWEEP)] = (uint32_t)cur_ix;
|
||||||
return is_match_found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef HASH_MAP_SIZE
|
#undef HASH_MAP_SIZE
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
/* NOLINT(build/header_guard) */
|
||||||
|
/* Copyright 2018 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* template parameters: FN, JUMP, NUMBUCKETS, MASK, CHUNKLEN */
|
||||||
|
/* NUMBUCKETS / (MASK + 1) = probability of storing and using hash code. */
|
||||||
|
/* JUMP = skip bytes for speedup */
|
||||||
|
|
||||||
|
/* Rolling hash for long distance long string matches. Stores one position
|
||||||
|
per bucket, bucket key is computed over a long region. */
|
||||||
|
|
||||||
|
#define HashRolling HASHER()
|
||||||
|
|
||||||
|
static const uint32_t FN(kRollingHashMul32) = 69069;
|
||||||
|
static const uint32_t FN(kInvalidPos) = 0xffffffff;
|
||||||
|
|
||||||
|
/* This hasher uses a longer forward length, but returning a higher value here
|
||||||
|
will hurt compression by the main hasher when combined with a composite
|
||||||
|
hasher. The hasher tests for forward itself instead. */
|
||||||
|
static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
|
||||||
|
static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; }
|
||||||
|
|
||||||
|
/* Computes a code from a single byte. A lookup table of 256 values could be
|
||||||
|
used, but simply adding 1 works about as good. */
|
||||||
|
static uint32_t FN(HashByte)(uint8_t byte) {
|
||||||
|
return (uint32_t)byte + 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t FN(HashRollingFunctionInitial)(uint32_t state, uint8_t add,
|
||||||
|
uint32_t factor) {
|
||||||
|
return (uint32_t)(factor * state + FN(HashByte)(add));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t FN(HashRollingFunction)(uint32_t state, uint8_t add,
|
||||||
|
uint8_t rem, uint32_t factor,
|
||||||
|
uint32_t factor_remove) {
|
||||||
|
return (uint32_t)(factor * state +
|
||||||
|
FN(HashByte)(add) - factor_remove * FN(HashByte)(rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct HashRolling {
|
||||||
|
uint32_t state;
|
||||||
|
uint32_t* table;
|
||||||
|
size_t next_ix;
|
||||||
|
|
||||||
|
uint32_t chunk_len;
|
||||||
|
uint32_t factor;
|
||||||
|
uint32_t factor_remove;
|
||||||
|
} HashRolling;
|
||||||
|
|
||||||
|
static BROTLI_INLINE HashRolling* FN(Self)(HasherHandle handle) {
|
||||||
|
return (HashRolling*)&(GetHasherCommon(handle)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Initialize)(
|
||||||
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
|
HashRolling* self = FN(Self)(handle);
|
||||||
|
size_t i;
|
||||||
|
self->state = 0;
|
||||||
|
self->next_ix = 0;
|
||||||
|
|
||||||
|
self->factor = FN(kRollingHashMul32);
|
||||||
|
|
||||||
|
/* Compute the factor of the oldest byte to remove: factor**steps modulo
|
||||||
|
0xffffffff (the multiplications rely on 32-bit overflow) */
|
||||||
|
self->factor_remove = 1;
|
||||||
|
for (i = 0; i < CHUNKLEN; i += JUMP) {
|
||||||
|
self->factor_remove *= self->factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->table = (uint32_t*)((HasherHandle)self + sizeof(HashRolling));
|
||||||
|
for (i = 0; i < NUMBUCKETS; i++) {
|
||||||
|
self->table[i] = FN(kInvalidPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
BROTLI_UNUSED(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashRolling* self = FN(Self)(handle);
|
||||||
|
size_t i;
|
||||||
|
/* Too small size, cannot use this hasher. */
|
||||||
|
if (input_size < CHUNKLEN) return;
|
||||||
|
self->state = 0;
|
||||||
|
for (i = 0; i < CHUNKLEN; i += JUMP) {
|
||||||
|
self->state = FN(HashRollingFunctionInitial)(
|
||||||
|
self->state, data[i], self->factor);
|
||||||
|
}
|
||||||
|
BROTLI_UNUSED(one_shot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size) {
|
||||||
|
return sizeof(HashRolling) + NUMBUCKETS * sizeof(uint32_t);
|
||||||
|
BROTLI_UNUSED(params);
|
||||||
|
BROTLI_UNUSED(one_shot);
|
||||||
|
BROTLI_UNUSED(input_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(Store)(HasherHandle BROTLI_RESTRICT handle,
|
||||||
|
const uint8_t* BROTLI_RESTRICT data, const size_t mask, const size_t ix) {
|
||||||
|
BROTLI_UNUSED(handle);
|
||||||
|
BROTLI_UNUSED(data);
|
||||||
|
BROTLI_UNUSED(mask);
|
||||||
|
BROTLI_UNUSED(ix);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
|
const size_t ix_end) {
|
||||||
|
BROTLI_UNUSED(handle);
|
||||||
|
BROTLI_UNUSED(data);
|
||||||
|
BROTLI_UNUSED(mask);
|
||||||
|
BROTLI_UNUSED(ix_start);
|
||||||
|
BROTLI_UNUSED(ix_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ring_buffer_mask) {
|
||||||
|
/* In this case we must re-initialize the hasher from scratch from the
|
||||||
|
current position. */
|
||||||
|
HashRolling* self = FN(Self)(handle);
|
||||||
|
size_t position_masked;
|
||||||
|
size_t available = num_bytes;
|
||||||
|
if ((position & (JUMP - 1)) != 0) {
|
||||||
|
size_t diff = JUMP - (position & (JUMP - 1));
|
||||||
|
available = (diff > available) ? 0 : (available - diff);
|
||||||
|
position += diff;
|
||||||
|
}
|
||||||
|
position_masked = position & ring_buffer_mask;
|
||||||
|
/* wrapping around ringbuffer not handled. */
|
||||||
|
if (available > ring_buffer_mask - position_masked) {
|
||||||
|
available = ring_buffer_mask - position_masked;
|
||||||
|
}
|
||||||
|
|
||||||
|
FN(Prepare)(handle, BROTLI_FALSE, available,
|
||||||
|
ringbuffer + (position & ring_buffer_mask));
|
||||||
|
self->next_ix = position;
|
||||||
|
BROTLI_UNUSED(num_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||||
|
HasherHandle handle, int* BROTLI_RESTRICT distance_cache) {
|
||||||
|
BROTLI_UNUSED(handle);
|
||||||
|
BROTLI_UNUSED(distance_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||||
|
const BrotliEncoderDictionary* dictionary,
|
||||||
|
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||||
|
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
||||||
|
const size_t max_length, const size_t max_backward, const size_t gap,
|
||||||
|
const size_t max_distance, HasherSearchResult* BROTLI_RESTRICT out) {
|
||||||
|
HashRolling* self = FN(Self)(handle);
|
||||||
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
|
size_t pos = self->next_ix;
|
||||||
|
|
||||||
|
if ((cur_ix & (JUMP - 1)) != 0) return;
|
||||||
|
|
||||||
|
/* Not enough lookahead */
|
||||||
|
if (max_length < CHUNKLEN) return;
|
||||||
|
|
||||||
|
for (pos = self->next_ix; pos <= cur_ix; pos += JUMP) {
|
||||||
|
uint32_t code = self->state & MASK;
|
||||||
|
|
||||||
|
uint8_t rem = data[pos & ring_buffer_mask];
|
||||||
|
uint8_t add = data[(pos + CHUNKLEN) & ring_buffer_mask];
|
||||||
|
size_t found_ix = FN(kInvalidPos);
|
||||||
|
|
||||||
|
self->state = FN(HashRollingFunction)(
|
||||||
|
self->state, add, rem, self->factor, self->factor_remove);
|
||||||
|
|
||||||
|
if (code < NUMBUCKETS) {
|
||||||
|
found_ix = self->table[code];
|
||||||
|
self->table[code] = (uint32_t)pos;
|
||||||
|
if (pos == cur_ix && found_ix != FN(kInvalidPos)) {
|
||||||
|
/* The cast to 32-bit makes backward distances up to 4GB work even
|
||||||
|
if cur_ix is above 4GB, despite using 32-bit values in the table. */
|
||||||
|
size_t backward = (uint32_t)(cur_ix - found_ix);
|
||||||
|
if (backward <= max_backward) {
|
||||||
|
const size_t found_ix_masked = found_ix & ring_buffer_mask;
|
||||||
|
const size_t len = FindMatchLengthWithLimit(&data[found_ix_masked],
|
||||||
|
&data[cur_ix_masked],
|
||||||
|
max_length);
|
||||||
|
if (len >= 4 && len > out->len) {
|
||||||
|
score_t score = BackwardReferenceScore(len, backward);
|
||||||
|
if (score > out->score) {
|
||||||
|
out->len = len;
|
||||||
|
out->distance = backward;
|
||||||
|
out->score = score;
|
||||||
|
out->len_code_delta = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->next_ix = cur_ix + JUMP;
|
||||||
|
|
||||||
|
/* NOTE: this hasher does not search in the dictionary. It is used as
|
||||||
|
backup-hasher, the main hasher already searches in it. */
|
||||||
|
BROTLI_UNUSED(dictionary);
|
||||||
|
BROTLI_UNUSED(distance_cache);
|
||||||
|
BROTLI_UNUSED(gap);
|
||||||
|
BROTLI_UNUSED(max_distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef HashRolling
|
|
@ -0,0 +1,327 @@
|
||||||
|
/* NOLINT(build/header_guard) */
|
||||||
|
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* template parameters: FN, BUCKET_BITS, MAX_TREE_COMP_LENGTH,
|
||||||
|
MAX_TREE_SEARCH_DEPTH */
|
||||||
|
|
||||||
|
/* A (forgetful) hash table where each hash bucket contains a binary tree of
|
||||||
|
sequences whose first 4 bytes share the same hash code.
|
||||||
|
Each sequence is MAX_TREE_COMP_LENGTH long and is identified by its starting
|
||||||
|
position in the input data. The binary tree is sorted by the lexicographic
|
||||||
|
order of the sequences, and it is also a max-heap with respect to the
|
||||||
|
starting positions. */
|
||||||
|
|
||||||
|
#define HashToBinaryTree HASHER()
|
||||||
|
|
||||||
|
#define BUCKET_SIZE (1 << BUCKET_BITS)
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
|
||||||
|
static BROTLI_INLINE size_t FN(StoreLookahead)(void) {
|
||||||
|
return MAX_TREE_COMP_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t FN(HashBytes)(const uint8_t* data) {
|
||||||
|
uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
|
||||||
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
|
so we take our results from there. */
|
||||||
|
return h >> (32 - BUCKET_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct HashToBinaryTree {
|
||||||
|
/* The window size minus 1 */
|
||||||
|
size_t window_mask_;
|
||||||
|
|
||||||
|
/* Hash table that maps the 4-byte hashes of the sequence to the last
|
||||||
|
position where this hash was found, which is the root of the binary
|
||||||
|
tree of sequences that share this hash bucket. */
|
||||||
|
uint32_t buckets_[BUCKET_SIZE];
|
||||||
|
|
||||||
|
/* A position used to mark a non-existent sequence, i.e. a tree is empty if
|
||||||
|
its root is at invalid_pos_ and a node is a leaf if both its children
|
||||||
|
are at invalid_pos_. */
|
||||||
|
uint32_t invalid_pos_;
|
||||||
|
|
||||||
|
/* --- Dynamic size members --- */
|
||||||
|
|
||||||
|
/* The union of the binary trees of each hash bucket. The root of the tree
|
||||||
|
corresponding to a hash is a sequence starting at buckets_[hash] and
|
||||||
|
the left and right children of a sequence starting at pos are
|
||||||
|
forest_[2 * pos] and forest_[2 * pos + 1]. */
|
||||||
|
/* uint32_t forest[2 * num_nodes] */
|
||||||
|
} HashToBinaryTree;
|
||||||
|
|
||||||
|
static BROTLI_INLINE HashToBinaryTree* FN(Self)(HasherHandle handle) {
|
||||||
|
return (HashToBinaryTree*)&(GetHasherCommon(handle)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE uint32_t* FN(Forest)(HashToBinaryTree* self) {
|
||||||
|
return (uint32_t*)(&self[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Initialize)(
|
||||||
|
HasherHandle handle, const BrotliEncoderParams* params) {
|
||||||
|
HashToBinaryTree* self = FN(Self)(handle);
|
||||||
|
self->window_mask_ = (1u << params->lgwin) - 1u;
|
||||||
|
self->invalid_pos_ = (uint32_t)(0 - self->window_mask_);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size, const uint8_t* data) {
|
||||||
|
HashToBinaryTree* self = FN(Self)(handle);
|
||||||
|
uint32_t invalid_pos = self->invalid_pos_;
|
||||||
|
uint32_t i;
|
||||||
|
BROTLI_UNUSED(data);
|
||||||
|
BROTLI_UNUSED(one_shot);
|
||||||
|
BROTLI_UNUSED(input_size);
|
||||||
|
for (i = 0; i < BUCKET_SIZE; i++) {
|
||||||
|
self->buckets_[i] = invalid_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
|
||||||
|
const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
|
||||||
|
size_t input_size) {
|
||||||
|
size_t num_nodes = (size_t)1 << params->lgwin;
|
||||||
|
if (one_shot && input_size < num_nodes) {
|
||||||
|
num_nodes = input_size;
|
||||||
|
}
|
||||||
|
return sizeof(HashToBinaryTree) + 2 * sizeof(uint32_t) * num_nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(LeftChildIndex)(HashToBinaryTree* self,
|
||||||
|
const size_t pos) {
|
||||||
|
return 2 * (pos & self->window_mask_);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE size_t FN(RightChildIndex)(HashToBinaryTree* self,
|
||||||
|
const size_t pos) {
|
||||||
|
return 2 * (pos & self->window_mask_) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stores the hash of the next 4 bytes and in a single tree-traversal, the
|
||||||
|
hash bucket's binary tree is searched for matches and is re-rooted at the
|
||||||
|
current position.
|
||||||
|
|
||||||
|
If less than MAX_TREE_COMP_LENGTH data is available, the hash bucket of the
|
||||||
|
current position is searched for matches, but the state of the hash table
|
||||||
|
is not changed, since we can not know the final sorting order of the
|
||||||
|
current (incomplete) sequence.
|
||||||
|
|
||||||
|
This function must be called with increasing cur_ix positions. */
|
||||||
|
static BROTLI_INLINE BackwardMatch* FN(StoreAndFindMatches)(
|
||||||
|
HashToBinaryTree* self, const uint8_t* const BROTLI_RESTRICT data,
|
||||||
|
const size_t cur_ix, const size_t ring_buffer_mask, const size_t max_length,
|
||||||
|
const size_t max_backward, size_t* const BROTLI_RESTRICT best_len,
|
||||||
|
BackwardMatch* BROTLI_RESTRICT matches) {
|
||||||
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
|
const size_t max_comp_len =
|
||||||
|
BROTLI_MIN(size_t, max_length, MAX_TREE_COMP_LENGTH);
|
||||||
|
const BROTLI_BOOL should_reroot_tree =
|
||||||
|
TO_BROTLI_BOOL(max_length >= MAX_TREE_COMP_LENGTH);
|
||||||
|
const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
||||||
|
uint32_t* forest = FN(Forest)(self);
|
||||||
|
size_t prev_ix = self->buckets_[key];
|
||||||
|
/* The forest index of the rightmost node of the left subtree of the new
|
||||||
|
root, updated as we traverse and re-root the tree of the hash bucket. */
|
||||||
|
size_t node_left = FN(LeftChildIndex)(self, cur_ix);
|
||||||
|
/* The forest index of the leftmost node of the right subtree of the new
|
||||||
|
root, updated as we traverse and re-root the tree of the hash bucket. */
|
||||||
|
size_t node_right = FN(RightChildIndex)(self, cur_ix);
|
||||||
|
/* The match length of the rightmost node of the left subtree of the new
|
||||||
|
root, updated as we traverse and re-root the tree of the hash bucket. */
|
||||||
|
size_t best_len_left = 0;
|
||||||
|
/* The match length of the leftmost node of the right subtree of the new
|
||||||
|
root, updated as we traverse and re-root the tree of the hash bucket. */
|
||||||
|
size_t best_len_right = 0;
|
||||||
|
size_t depth_remaining;
|
||||||
|
if (should_reroot_tree) {
|
||||||
|
self->buckets_[key] = (uint32_t)cur_ix;
|
||||||
|
}
|
||||||
|
for (depth_remaining = MAX_TREE_SEARCH_DEPTH; ; --depth_remaining) {
|
||||||
|
const size_t backward = cur_ix - prev_ix;
|
||||||
|
const size_t prev_ix_masked = prev_ix & ring_buffer_mask;
|
||||||
|
if (backward == 0 || backward > max_backward || depth_remaining == 0) {
|
||||||
|
if (should_reroot_tree) {
|
||||||
|
forest[node_left] = self->invalid_pos_;
|
||||||
|
forest[node_right] = self->invalid_pos_;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const size_t cur_len = BROTLI_MIN(size_t, best_len_left, best_len_right);
|
||||||
|
size_t len;
|
||||||
|
BROTLI_DCHECK(cur_len <= MAX_TREE_COMP_LENGTH);
|
||||||
|
len = cur_len +
|
||||||
|
FindMatchLengthWithLimit(&data[cur_ix_masked + cur_len],
|
||||||
|
&data[prev_ix_masked + cur_len],
|
||||||
|
max_length - cur_len);
|
||||||
|
BROTLI_DCHECK(
|
||||||
|
0 == memcmp(&data[cur_ix_masked], &data[prev_ix_masked], len));
|
||||||
|
if (matches && len > *best_len) {
|
||||||
|
*best_len = len;
|
||||||
|
InitBackwardMatch(matches++, backward, len);
|
||||||
|
}
|
||||||
|
if (len >= max_comp_len) {
|
||||||
|
if (should_reroot_tree) {
|
||||||
|
forest[node_left] = forest[FN(LeftChildIndex)(self, prev_ix)];
|
||||||
|
forest[node_right] = forest[FN(RightChildIndex)(self, prev_ix)];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (data[cur_ix_masked + len] > data[prev_ix_masked + len]) {
|
||||||
|
best_len_left = len;
|
||||||
|
if (should_reroot_tree) {
|
||||||
|
forest[node_left] = (uint32_t)prev_ix;
|
||||||
|
}
|
||||||
|
node_left = FN(RightChildIndex)(self, prev_ix);
|
||||||
|
prev_ix = forest[node_left];
|
||||||
|
} else {
|
||||||
|
best_len_right = len;
|
||||||
|
if (should_reroot_tree) {
|
||||||
|
forest[node_right] = (uint32_t)prev_ix;
|
||||||
|
}
|
||||||
|
node_right = FN(LeftChildIndex)(self, prev_ix);
|
||||||
|
prev_ix = forest[node_right];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the
|
||||||
|
length of max_length and stores the position cur_ix in the hash table.
|
||||||
|
|
||||||
|
Sets *num_matches to the number of matches found, and stores the found
|
||||||
|
matches in matches[0] to matches[*num_matches - 1]. The matches will be
|
||||||
|
sorted by strictly increasing length and (non-strictly) increasing
|
||||||
|
distance. */
|
||||||
|
static BROTLI_INLINE size_t FN(FindAllMatches)(HasherHandle handle,
|
||||||
|
const BrotliEncoderDictionary* dictionary, const uint8_t* data,
|
||||||
|
const size_t ring_buffer_mask, const size_t cur_ix,
|
||||||
|
const size_t max_length, const size_t max_backward, const size_t gap,
|
||||||
|
const BrotliEncoderParams* params, BackwardMatch* matches) {
|
||||||
|
BackwardMatch* const orig_matches = matches;
|
||||||
|
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||||
|
size_t best_len = 1;
|
||||||
|
const size_t short_match_max_backward =
|
||||||
|
params->quality != HQ_ZOPFLIFICATION_QUALITY ? 16 : 64;
|
||||||
|
size_t stop = cur_ix - short_match_max_backward;
|
||||||
|
uint32_t dict_matches[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1];
|
||||||
|
size_t i;
|
||||||
|
if (cur_ix < short_match_max_backward) { stop = 0; }
|
||||||
|
for (i = cur_ix - 1; i > stop && best_len <= 2; --i) {
|
||||||
|
size_t prev_ix = i;
|
||||||
|
const size_t backward = cur_ix - prev_ix;
|
||||||
|
if (BROTLI_PREDICT_FALSE(backward > max_backward)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_ix &= ring_buffer_mask;
|
||||||
|
if (data[cur_ix_masked] != data[prev_ix] ||
|
||||||
|
data[cur_ix_masked + 1] != data[prev_ix + 1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const size_t len =
|
||||||
|
FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked],
|
||||||
|
max_length);
|
||||||
|
if (len > best_len) {
|
||||||
|
best_len = len;
|
||||||
|
InitBackwardMatch(matches++, backward, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best_len < max_length) {
|
||||||
|
matches = FN(StoreAndFindMatches)(FN(Self)(handle), data, cur_ix,
|
||||||
|
ring_buffer_mask, max_length, max_backward, &best_len, matches);
|
||||||
|
}
|
||||||
|
for (i = 0; i <= BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN; ++i) {
|
||||||
|
dict_matches[i] = kInvalidMatch;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
size_t minlen = BROTLI_MAX(size_t, 4, best_len + 1);
|
||||||
|
if (BrotliFindAllStaticDictionaryMatches(dictionary,
|
||||||
|
&data[cur_ix_masked], minlen, max_length, &dict_matches[0])) {
|
||||||
|
size_t maxlen = BROTLI_MIN(
|
||||||
|
size_t, BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN, max_length);
|
||||||
|
size_t l;
|
||||||
|
for (l = minlen; l <= maxlen; ++l) {
|
||||||
|
uint32_t dict_id = dict_matches[l];
|
||||||
|
if (dict_id < kInvalidMatch) {
|
||||||
|
size_t distance = max_backward + gap + (dict_id >> 5) + 1;
|
||||||
|
if (distance <= params->dist.max_distance) {
|
||||||
|
InitDictionaryBackwardMatch(matches++, distance, l, dict_id & 31);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (size_t)(matches - orig_matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stores the hash of the next 4 bytes and re-roots the binary tree at the
|
||||||
|
current sequence, without returning any matches.
|
||||||
|
REQUIRES: ix + MAX_TREE_COMP_LENGTH <= end-of-current-block */
|
||||||
|
static BROTLI_INLINE void FN(Store)(HasherHandle handle, const uint8_t* data,
|
||||||
|
const size_t mask, const size_t ix) {
|
||||||
|
HashToBinaryTree* self = FN(Self)(handle);
|
||||||
|
/* Maximum distance is window size - 16, see section 9.1. of the spec. */
|
||||||
|
const size_t max_backward = self->window_mask_ - BROTLI_WINDOW_GAP + 1;
|
||||||
|
FN(StoreAndFindMatches)(self, data, ix, mask, MAX_TREE_COMP_LENGTH,
|
||||||
|
max_backward, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
|
||||||
|
const uint8_t* data, const size_t mask, const size_t ix_start,
|
||||||
|
const size_t ix_end) {
|
||||||
|
size_t i = ix_start;
|
||||||
|
size_t j = ix_start;
|
||||||
|
if (ix_start + 63 <= ix_end) {
|
||||||
|
i = ix_end - 63;
|
||||||
|
}
|
||||||
|
if (ix_start + 512 <= i) {
|
||||||
|
for (; j < i; j += 8) {
|
||||||
|
FN(Store)(handle, data, mask, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; i < ix_end; ++i) {
|
||||||
|
FN(Store)(handle, data, mask, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
|
||||||
|
size_t num_bytes, size_t position, const uint8_t* ringbuffer,
|
||||||
|
size_t ringbuffer_mask) {
|
||||||
|
HashToBinaryTree* self = FN(Self)(handle);
|
||||||
|
if (num_bytes >= FN(HashTypeLength)() - 1 &&
|
||||||
|
position >= MAX_TREE_COMP_LENGTH) {
|
||||||
|
/* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher.
|
||||||
|
These could not be calculated before, since they require knowledge
|
||||||
|
of both the previous and the current block. */
|
||||||
|
const size_t i_start = position - MAX_TREE_COMP_LENGTH + 1;
|
||||||
|
const size_t i_end = BROTLI_MIN(size_t, position, i_start + num_bytes);
|
||||||
|
size_t i;
|
||||||
|
for (i = i_start; i < i_end; ++i) {
|
||||||
|
/* Maximum distance is window size - 16, see section 9.1. of the spec.
|
||||||
|
Furthermore, we have to make sure that we don't look further back
|
||||||
|
from the start of the next block than the window size, otherwise we
|
||||||
|
could access already overwritten areas of the ring-buffer. */
|
||||||
|
const size_t max_backward =
|
||||||
|
self->window_mask_ - BROTLI_MAX(size_t,
|
||||||
|
BROTLI_WINDOW_GAP - 1,
|
||||||
|
position - i);
|
||||||
|
/* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the
|
||||||
|
end of the current block and that we have at least
|
||||||
|
MAX_TREE_COMP_LENGTH tail in the ring-buffer. */
|
||||||
|
FN(StoreAndFindMatches)(self, ringbuffer, i, ringbuffer_mask,
|
||||||
|
MAX_TREE_COMP_LENGTH, max_backward, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef BUCKET_SIZE
|
||||||
|
|
||||||
|
#undef HashToBinaryTree
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
|
|
||||||
|
#include "../common/context.h"
|
||||||
#include "./block_splitter.h"
|
#include "./block_splitter.h"
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./context.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -63,11 +63,16 @@ void BrotliBuildHistogramsWithContext(
|
||||||
BlockSplitIteratorNext(&insert_and_copy_it);
|
BlockSplitIteratorNext(&insert_and_copy_it);
|
||||||
HistogramAddCommand(&insert_and_copy_histograms[insert_and_copy_it.type_],
|
HistogramAddCommand(&insert_and_copy_histograms[insert_and_copy_it.type_],
|
||||||
cmd->cmd_prefix_);
|
cmd->cmd_prefix_);
|
||||||
|
/* TODO: unwrap iterator blocks. */
|
||||||
for (j = cmd->insert_len_; j != 0; --j) {
|
for (j = cmd->insert_len_; j != 0; --j) {
|
||||||
size_t context;
|
size_t context;
|
||||||
BlockSplitIteratorNext(&literal_it);
|
BlockSplitIteratorNext(&literal_it);
|
||||||
context = (literal_it.type_ << BROTLI_LITERAL_CONTEXT_BITS) +
|
context = literal_it.type_;
|
||||||
Context(prev_byte, prev_byte2, context_modes[literal_it.type_]);
|
if (context_modes) {
|
||||||
|
ContextLut lut = BROTLI_CONTEXT_LUT(context_modes[context]);
|
||||||
|
context = (context << BROTLI_LITERAL_CONTEXT_BITS) +
|
||||||
|
BROTLI_CONTEXT(prev_byte, prev_byte2, lut);
|
||||||
|
}
|
||||||
HistogramAddLiteral(&literal_histograms[context],
|
HistogramAddLiteral(&literal_histograms[context],
|
||||||
ringbuffer[pos & mask]);
|
ringbuffer[pos & mask]);
|
||||||
prev_byte2 = prev_byte;
|
prev_byte2 = prev_byte;
|
||||||
|
@ -84,7 +89,7 @@ void BrotliBuildHistogramsWithContext(
|
||||||
context = (dist_it.type_ << BROTLI_DISTANCE_CONTEXT_BITS) +
|
context = (dist_it.type_ << BROTLI_DISTANCE_CONTEXT_BITS) +
|
||||||
CommandDistanceContext(cmd);
|
CommandDistanceContext(cmd);
|
||||||
HistogramAddDistance(©_dist_histograms[context],
|
HistogramAddDistance(©_dist_histograms[context],
|
||||||
cmd->dist_prefix_);
|
cmd->dist_prefix_ & 0x3FF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,19 @@
|
||||||
#include <string.h> /* memset */
|
#include <string.h> /* memset */
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/context.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./block_splitter.h"
|
#include "./block_splitter.h"
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./context.h"
|
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* The distance symbols effectively used by "Large Window Brotli" (32-bit). */
|
||||||
|
#define BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS 544
|
||||||
|
|
||||||
#define FN(X) X ## Literal
|
#define FN(X) X ## Literal
|
||||||
#define DATA_SIZE BROTLI_NUM_LITERAL_SYMBOLS
|
#define DATA_SIZE BROTLI_NUM_LITERAL_SYMBOLS
|
||||||
#define DataType uint8_t
|
#define DataType uint8_t
|
||||||
|
@ -38,7 +41,7 @@ extern "C" {
|
||||||
#undef FN
|
#undef FN
|
||||||
|
|
||||||
#define FN(X) X ## Distance
|
#define FN(X) X ## Distance
|
||||||
#define DATA_SIZE BROTLI_NUM_DISTANCE_SYMBOLS
|
#define DATA_SIZE BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS
|
||||||
#include "./histogram_inc.h" /* NOLINT(build/include) */
|
#include "./histogram_inc.h" /* NOLINT(build/include) */
|
||||||
#undef DataType
|
#undef DataType
|
||||||
#undef DATA_SIZE
|
#undef DATA_SIZE
|
||||||
|
|
|
@ -33,7 +33,7 @@ static BROTLI_INLINE void FN(HistogramAdd)(FN(Histogram)* self, size_t val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void FN(HistogramAddVector)(FN(Histogram)* self,
|
static BROTLI_INLINE void FN(HistogramAddVector)(FN(Histogram)* self,
|
||||||
const DataType *p, size_t n) {
|
const DataType* p, size_t n) {
|
||||||
self->total_count_ += n;
|
self->total_count_ += n;
|
||||||
n += 1;
|
n += 1;
|
||||||
while (--n) ++self->data_[*p++];
|
while (--n) ++self->data_[*p++];
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
#include "./literal_cost.h"
|
#include "./literal_cost.h"
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./utf8_util.h"
|
#include "./utf8_util.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -25,7 +25,7 @@ static size_t UTF8Position(size_t last, size_t c, size_t clamp) {
|
||||||
return BROTLI_MIN(size_t, 1, clamp);
|
return BROTLI_MIN(size_t, 1, clamp);
|
||||||
} else {
|
} else {
|
||||||
/* Let's decide over the last byte if this ends the sequence. */
|
/* Let's decide over the last byte if this ends the sequence. */
|
||||||
if (last < 0xe0) {
|
if (last < 0xE0) {
|
||||||
return 0; /* Completed two or three byte coding. */
|
return 0; /* Completed two or three byte coding. */
|
||||||
} else { /* Next one is the 'Byte 3' of utf-8 encoding. */
|
} else { /* Next one is the 'Byte 3' of utf-8 encoding. */
|
||||||
return BROTLI_MIN(size_t, 2, clamp);
|
return BROTLI_MIN(size_t, 2, clamp);
|
||||||
|
@ -34,16 +34,14 @@ static size_t UTF8Position(size_t last, size_t c, size_t clamp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask,
|
static size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask,
|
||||||
const uint8_t *data) {
|
const uint8_t* data) {
|
||||||
size_t counts[3] = { 0 };
|
size_t counts[3] = { 0 };
|
||||||
size_t max_utf8 = 1; /* should be 2, but 1 compresses better. */
|
size_t max_utf8 = 1; /* should be 2, but 1 compresses better. */
|
||||||
size_t last_c = 0;
|
size_t last_c = 0;
|
||||||
size_t utf8_pos = 0;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
size_t c = data[(pos + i) & mask];
|
size_t c = data[(pos + i) & mask];
|
||||||
utf8_pos = UTF8Position(last_c, c, 2);
|
++counts[UTF8Position(last_c, c, 2)];
|
||||||
++counts[utf8_pos];
|
|
||||||
last_c = c;
|
last_c = c;
|
||||||
}
|
}
|
||||||
if (counts[2] < 500) {
|
if (counts[2] < 500) {
|
||||||
|
@ -56,16 +54,15 @@ static size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
|
static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
|
||||||
const uint8_t *data, float *cost) {
|
const uint8_t* data, float* cost) {
|
||||||
/* max_utf8 is 0 (normal ascii single byte modeling),
|
/* max_utf8 is 0 (normal ASCII single byte modeling),
|
||||||
1 (for 2-byte utf-8 modeling), or 2 (for 3-byte utf-8 modeling). */
|
1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */
|
||||||
const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data);
|
const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data);
|
||||||
size_t histogram[3][256] = { { 0 } };
|
size_t histogram[3][256] = { { 0 } };
|
||||||
size_t window_half = 495;
|
size_t window_half = 495;
|
||||||
size_t in_window = BROTLI_MIN(size_t, window_half, len);
|
size_t in_window = BROTLI_MIN(size_t, window_half, len);
|
||||||
size_t in_window_utf8[3] = { 0 };
|
size_t in_window_utf8[3] = { 0 };
|
||||||
|
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
{ /* Bootstrap histograms. */
|
{ /* Bootstrap histograms. */
|
||||||
size_t last_c = 0;
|
size_t last_c = 0;
|
||||||
|
@ -128,7 +125,7 @@ static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask,
|
void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask,
|
||||||
const uint8_t *data, float *cost) {
|
const uint8_t* data, float* cost) {
|
||||||
if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) {
|
if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) {
|
||||||
EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, cost);
|
EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, cost);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -10,18 +10,18 @@
|
||||||
#ifndef BROTLI_ENC_LITERAL_COST_H_
|
#ifndef BROTLI_ENC_LITERAL_COST_H_
|
||||||
#define BROTLI_ENC_LITERAL_COST_H_
|
#define BROTLI_ENC_LITERAL_COST_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Estimates how many bits the literals in the interval [pos, pos + len) in the
|
/* Estimates how many bits the literals in the interval [pos, pos + len) in the
|
||||||
ringbuffer (data, mask) will take entropy coded and writes these estimates
|
ring-buffer (data, mask) will take entropy coded and writes these estimates
|
||||||
to the cost[0..len) array. */
|
to the cost[0..len) array. */
|
||||||
BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals(
|
BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals(
|
||||||
size_t pos, size_t len, size_t mask, const uint8_t *data, float *cost);
|
size_t pos, size_t len, size_t mask, const uint8_t* data, float* cost);
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -9,12 +9,11 @@
|
||||||
|
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h> /* exit, free, malloc */
|
#include <stdlib.h> /* exit, free, malloc */
|
||||||
#include <string.h> /* memcpy */
|
#include <string.h> /* memcpy */
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -28,22 +27,12 @@ extern "C" {
|
||||||
#define NEW_ALLOCATED_OFFSET MAX_PERM_ALLOCATED
|
#define NEW_ALLOCATED_OFFSET MAX_PERM_ALLOCATED
|
||||||
#define NEW_FREED_OFFSET (MAX_PERM_ALLOCATED + MAX_NEW_ALLOCATED)
|
#define NEW_FREED_OFFSET (MAX_PERM_ALLOCATED + MAX_NEW_ALLOCATED)
|
||||||
|
|
||||||
static void* DefaultAllocFunc(void* opaque, size_t size) {
|
|
||||||
BROTLI_UNUSED(opaque);
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DefaultFreeFunc(void* opaque, void* address) {
|
|
||||||
BROTLI_UNUSED(opaque);
|
|
||||||
free(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrotliInitMemoryManager(
|
void BrotliInitMemoryManager(
|
||||||
MemoryManager* m, brotli_alloc_func alloc_func, brotli_free_func free_func,
|
MemoryManager* m, brotli_alloc_func alloc_func, brotli_free_func free_func,
|
||||||
void* opaque) {
|
void* opaque) {
|
||||||
if (!alloc_func) {
|
if (!alloc_func) {
|
||||||
m->alloc_func = DefaultAllocFunc;
|
m->alloc_func = BrotliDefaultAllocFunc;
|
||||||
m->free_func = DefaultFreeFunc;
|
m->free_func = BrotliDefaultFreeFunc;
|
||||||
m->opaque = 0;
|
m->opaque = 0;
|
||||||
} else {
|
} else {
|
||||||
m->alloc_func = alloc_func;
|
m->alloc_func = alloc_func;
|
||||||
|
@ -132,11 +121,11 @@ static void CollectGarbagePointers(MemoryManager* m) {
|
||||||
m->pointers + NEW_FREED_OFFSET, m->new_freed);
|
m->pointers + NEW_FREED_OFFSET, m->new_freed);
|
||||||
m->perm_allocated -= annihilated;
|
m->perm_allocated -= annihilated;
|
||||||
m->new_freed -= annihilated;
|
m->new_freed -= annihilated;
|
||||||
assert(m->new_freed == 0);
|
BROTLI_DCHECK(m->new_freed == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->new_allocated != 0) {
|
if (m->new_allocated != 0) {
|
||||||
assert(m->perm_allocated + m->new_allocated <= MAX_PERM_ALLOCATED);
|
BROTLI_DCHECK(m->perm_allocated + m->new_allocated <= MAX_PERM_ALLOCATED);
|
||||||
memcpy(m->pointers + PERM_ALLOCATED_OFFSET + m->perm_allocated,
|
memcpy(m->pointers + PERM_ALLOCATED_OFFSET + m->perm_allocated,
|
||||||
m->pointers + NEW_ALLOCATED_OFFSET,
|
m->pointers + NEW_ALLOCATED_OFFSET,
|
||||||
sizeof(void*) * m->new_allocated);
|
sizeof(void*) * m->new_allocated);
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
#ifndef BROTLI_ENC_MEMORY_H_
|
#ifndef BROTLI_ENC_MEMORY_H_
|
||||||
#define BROTLI_ENC_MEMORY_H_
|
#define BROTLI_ENC_MEMORY_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include <string.h> /* memcpy */
|
||||||
#include "./port.h"
|
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -39,7 +41,8 @@ BROTLI_INTERNAL void BrotliInitMemoryManager(
|
||||||
void* opaque);
|
void* opaque);
|
||||||
|
|
||||||
BROTLI_INTERNAL void* BrotliAllocate(MemoryManager* m, size_t n);
|
BROTLI_INTERNAL void* BrotliAllocate(MemoryManager* m, size_t n);
|
||||||
#define BROTLI_ALLOC(M, T, N) ((T*)BrotliAllocate((M), (N) * sizeof(T)))
|
#define BROTLI_ALLOC(M, T, N) \
|
||||||
|
((N) > 0 ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL)
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p);
|
BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p);
|
||||||
#define BROTLI_FREE(M, P) { \
|
#define BROTLI_FREE(M, P) { \
|
||||||
|
@ -55,6 +58,43 @@ BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p);
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliWipeOutMemoryManager(MemoryManager* m);
|
BROTLI_INTERNAL void BrotliWipeOutMemoryManager(MemoryManager* m);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dynamically grows array capacity to at least the requested size
|
||||||
|
M: MemoryManager
|
||||||
|
T: data type
|
||||||
|
A: array
|
||||||
|
C: capacity
|
||||||
|
R: requested size
|
||||||
|
*/
|
||||||
|
#define BROTLI_ENSURE_CAPACITY(M, T, A, C, R) { \
|
||||||
|
if (C < (R)) { \
|
||||||
|
size_t _new_size = (C == 0) ? (R) : C; \
|
||||||
|
T* new_array; \
|
||||||
|
while (_new_size < (R)) _new_size *= 2; \
|
||||||
|
new_array = BROTLI_ALLOC((M), T, _new_size); \
|
||||||
|
if (!BROTLI_IS_OOM(M) && C != 0) \
|
||||||
|
memcpy(new_array, A, C * sizeof(T)); \
|
||||||
|
BROTLI_FREE((M), A); \
|
||||||
|
A = new_array; \
|
||||||
|
C = _new_size; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Appends value and dynamically grows array capacity when needed
|
||||||
|
M: MemoryManager
|
||||||
|
T: data type
|
||||||
|
A: array
|
||||||
|
C: array capacity
|
||||||
|
S: array size
|
||||||
|
V: value to append
|
||||||
|
*/
|
||||||
|
#define BROTLI_ENSURE_CAPACITY_APPEND(M, T, A, C, S, V) { \
|
||||||
|
(S)++; \
|
||||||
|
BROTLI_ENSURE_CAPACITY(M, T, A, C, S); \
|
||||||
|
A[(S) - 1] = (V); \
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,29 +10,131 @@
|
||||||
#include "./metablock.h"
|
#include "./metablock.h"
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/types.h"
|
#include "../common/context.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./bit_cost.h"
|
#include "./bit_cost.h"
|
||||||
#include "./block_splitter.h"
|
#include "./block_splitter.h"
|
||||||
#include "./cluster.h"
|
#include "./cluster.h"
|
||||||
#include "./context.h"
|
|
||||||
#include "./entropy_encode.h"
|
#include "./entropy_encode.h"
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void BrotliInitDistanceParams(BrotliEncoderParams* params,
|
||||||
|
uint32_t npostfix, uint32_t ndirect) {
|
||||||
|
BrotliDistanceParams* dist_params = ¶ms->dist;
|
||||||
|
uint32_t alphabet_size, max_distance;
|
||||||
|
|
||||||
|
dist_params->distance_postfix_bits = npostfix;
|
||||||
|
dist_params->num_direct_distance_codes = ndirect;
|
||||||
|
|
||||||
|
alphabet_size = BROTLI_DISTANCE_ALPHABET_SIZE(
|
||||||
|
npostfix, ndirect, BROTLI_MAX_DISTANCE_BITS);
|
||||||
|
max_distance = ndirect + (1U << (BROTLI_MAX_DISTANCE_BITS + npostfix + 2)) -
|
||||||
|
(1U << (npostfix + 2));
|
||||||
|
|
||||||
|
if (params->large_window) {
|
||||||
|
static const uint32_t bound[BROTLI_MAX_NPOSTFIX + 1] = {0, 4, 12, 28};
|
||||||
|
uint32_t postfix = 1U << npostfix;
|
||||||
|
alphabet_size = BROTLI_DISTANCE_ALPHABET_SIZE(
|
||||||
|
npostfix, ndirect, BROTLI_LARGE_MAX_DISTANCE_BITS);
|
||||||
|
/* The maximum distance is set so that no distance symbol used can encode
|
||||||
|
a distance larger than BROTLI_MAX_ALLOWED_DISTANCE with all
|
||||||
|
its extra bits set. */
|
||||||
|
if (ndirect < bound[npostfix]) {
|
||||||
|
max_distance = BROTLI_MAX_ALLOWED_DISTANCE - (bound[npostfix] - ndirect);
|
||||||
|
} else if (ndirect >= bound[npostfix] + postfix) {
|
||||||
|
max_distance = (3U << 29) - 4 + (ndirect - bound[npostfix]);
|
||||||
|
} else {
|
||||||
|
max_distance = BROTLI_MAX_ALLOWED_DISTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dist_params->alphabet_size = alphabet_size;
|
||||||
|
dist_params->max_distance = max_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RecomputeDistancePrefixes(Command* cmds,
|
||||||
|
size_t num_commands,
|
||||||
|
const BrotliDistanceParams* orig_params,
|
||||||
|
const BrotliDistanceParams* new_params) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits &&
|
||||||
|
orig_params->num_direct_distance_codes ==
|
||||||
|
new_params->num_direct_distance_codes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < num_commands; ++i) {
|
||||||
|
Command* cmd = &cmds[i];
|
||||||
|
if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) {
|
||||||
|
PrefixEncodeCopyDistance(CommandRestoreDistanceCode(cmd, orig_params),
|
||||||
|
new_params->num_direct_distance_codes,
|
||||||
|
new_params->distance_postfix_bits,
|
||||||
|
&cmd->dist_prefix_,
|
||||||
|
&cmd->dist_extra_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_BOOL ComputeDistanceCost(const Command* cmds,
|
||||||
|
size_t num_commands,
|
||||||
|
const BrotliDistanceParams* orig_params,
|
||||||
|
const BrotliDistanceParams* new_params,
|
||||||
|
double* cost) {
|
||||||
|
size_t i;
|
||||||
|
BROTLI_BOOL equal_params = BROTLI_FALSE;
|
||||||
|
uint16_t dist_prefix;
|
||||||
|
uint32_t dist_extra;
|
||||||
|
double extra_bits = 0.0;
|
||||||
|
HistogramDistance histo;
|
||||||
|
HistogramClearDistance(&histo);
|
||||||
|
|
||||||
|
if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits &&
|
||||||
|
orig_params->num_direct_distance_codes ==
|
||||||
|
new_params->num_direct_distance_codes) {
|
||||||
|
equal_params = BROTLI_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < num_commands; i++) {
|
||||||
|
const Command* cmd = &cmds[i];
|
||||||
|
if (CommandCopyLen(cmd) && cmd->cmd_prefix_ >= 128) {
|
||||||
|
if (equal_params) {
|
||||||
|
dist_prefix = cmd->dist_prefix_;
|
||||||
|
} else {
|
||||||
|
uint32_t distance = CommandRestoreDistanceCode(cmd, orig_params);
|
||||||
|
if (distance > new_params->max_distance) {
|
||||||
|
return BROTLI_FALSE;
|
||||||
|
}
|
||||||
|
PrefixEncodeCopyDistance(distance,
|
||||||
|
new_params->num_direct_distance_codes,
|
||||||
|
new_params->distance_postfix_bits,
|
||||||
|
&dist_prefix,
|
||||||
|
&dist_extra);
|
||||||
|
}
|
||||||
|
HistogramAddDistance(&histo, dist_prefix & 0x3FF);
|
||||||
|
extra_bits += dist_prefix >> 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*cost = BrotliPopulationCostDistance(&histo) + extra_bits;
|
||||||
|
return BROTLI_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
void BrotliBuildMetaBlock(MemoryManager* m,
|
void BrotliBuildMetaBlock(MemoryManager* m,
|
||||||
const uint8_t* ringbuffer,
|
const uint8_t* ringbuffer,
|
||||||
const size_t pos,
|
const size_t pos,
|
||||||
const size_t mask,
|
const size_t mask,
|
||||||
const BrotliEncoderParams* params,
|
BrotliEncoderParams* params,
|
||||||
uint8_t prev_byte,
|
uint8_t prev_byte,
|
||||||
uint8_t prev_byte2,
|
uint8_t prev_byte2,
|
||||||
const Command* cmds,
|
Command* cmds,
|
||||||
size_t num_commands,
|
size_t num_commands,
|
||||||
ContextType literal_context_mode,
|
ContextType literal_context_mode,
|
||||||
MetaBlockSplit* mb) {
|
MetaBlockSplit* mb) {
|
||||||
|
@ -40,10 +142,52 @@ void BrotliBuildMetaBlock(MemoryManager* m,
|
||||||
static const size_t kMaxNumberOfHistograms = 256;
|
static const size_t kMaxNumberOfHistograms = 256;
|
||||||
HistogramDistance* distance_histograms;
|
HistogramDistance* distance_histograms;
|
||||||
HistogramLiteral* literal_histograms;
|
HistogramLiteral* literal_histograms;
|
||||||
ContextType* literal_context_modes;
|
ContextType* literal_context_modes = NULL;
|
||||||
size_t num_literal_contexts;
|
size_t literal_histograms_size;
|
||||||
size_t num_distance_contexts;
|
size_t distance_histograms_size;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
size_t literal_context_multiplier = 1;
|
||||||
|
uint32_t npostfix;
|
||||||
|
uint32_t ndirect_msb = 0;
|
||||||
|
BROTLI_BOOL check_orig = BROTLI_TRUE;
|
||||||
|
double best_dist_cost = 1e99;
|
||||||
|
BrotliEncoderParams orig_params = *params;
|
||||||
|
BrotliEncoderParams new_params = *params;
|
||||||
|
|
||||||
|
for (npostfix = 0; npostfix <= BROTLI_MAX_NPOSTFIX; npostfix++) {
|
||||||
|
for (; ndirect_msb < 16; ndirect_msb++) {
|
||||||
|
uint32_t ndirect = ndirect_msb << npostfix;
|
||||||
|
BROTLI_BOOL skip;
|
||||||
|
double dist_cost;
|
||||||
|
BrotliInitDistanceParams(&new_params, npostfix, ndirect);
|
||||||
|
if (npostfix == orig_params.dist.distance_postfix_bits &&
|
||||||
|
ndirect == orig_params.dist.num_direct_distance_codes) {
|
||||||
|
check_orig = BROTLI_FALSE;
|
||||||
|
}
|
||||||
|
skip = !ComputeDistanceCost(
|
||||||
|
cmds, num_commands,
|
||||||
|
&orig_params.dist, &new_params.dist, &dist_cost);
|
||||||
|
if (skip || (dist_cost > best_dist_cost)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
best_dist_cost = dist_cost;
|
||||||
|
params->dist = new_params.dist;
|
||||||
|
}
|
||||||
|
if (ndirect_msb > 0) ndirect_msb--;
|
||||||
|
ndirect_msb /= 2;
|
||||||
|
}
|
||||||
|
if (check_orig) {
|
||||||
|
double dist_cost;
|
||||||
|
ComputeDistanceCost(cmds, num_commands,
|
||||||
|
&orig_params.dist, &orig_params.dist, &dist_cost);
|
||||||
|
if (dist_cost < best_dist_cost) {
|
||||||
|
/* NB: currently unused; uncomment when more param tuning is added. */
|
||||||
|
/* best_dist_cost = dist_cost; */
|
||||||
|
params->dist = orig_params.dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RecomputeDistancePrefixes(cmds, num_commands,
|
||||||
|
&orig_params.dist, ¶ms->dist);
|
||||||
|
|
||||||
BrotliSplitBlock(m, cmds, num_commands,
|
BrotliSplitBlock(m, cmds, num_commands,
|
||||||
ringbuffer, pos, mask, params,
|
ringbuffer, pos, mask, params,
|
||||||
|
@ -52,68 +196,87 @@ void BrotliBuildMetaBlock(MemoryManager* m,
|
||||||
&mb->distance_split);
|
&mb->distance_split);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
|
||||||
literal_context_modes =
|
if (!params->disable_literal_context_modeling) {
|
||||||
BROTLI_ALLOC(m, ContextType, mb->literal_split.num_types);
|
literal_context_multiplier = 1 << BROTLI_LITERAL_CONTEXT_BITS;
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
literal_context_modes =
|
||||||
for (i = 0; i < mb->literal_split.num_types; ++i) {
|
BROTLI_ALLOC(m, ContextType, mb->literal_split.num_types);
|
||||||
literal_context_modes[i] = literal_context_mode;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
for (i = 0; i < mb->literal_split.num_types; ++i) {
|
||||||
|
literal_context_modes[i] = literal_context_mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
num_literal_contexts =
|
literal_histograms_size =
|
||||||
mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
|
mb->literal_split.num_types * literal_context_multiplier;
|
||||||
num_distance_contexts =
|
literal_histograms =
|
||||||
mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS;
|
BROTLI_ALLOC(m, HistogramLiteral, literal_histograms_size);
|
||||||
literal_histograms = BROTLI_ALLOC(m, HistogramLiteral, num_literal_contexts);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
ClearHistogramsLiteral(literal_histograms, num_literal_contexts);
|
ClearHistogramsLiteral(literal_histograms, literal_histograms_size);
|
||||||
|
|
||||||
assert(mb->command_histograms == 0);
|
distance_histograms_size =
|
||||||
|
mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS;
|
||||||
|
distance_histograms =
|
||||||
|
BROTLI_ALLOC(m, HistogramDistance, distance_histograms_size);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
ClearHistogramsDistance(distance_histograms, distance_histograms_size);
|
||||||
|
|
||||||
|
BROTLI_DCHECK(mb->command_histograms == 0);
|
||||||
mb->command_histograms_size = mb->command_split.num_types;
|
mb->command_histograms_size = mb->command_split.num_types;
|
||||||
mb->command_histograms =
|
mb->command_histograms =
|
||||||
BROTLI_ALLOC(m, HistogramCommand, mb->command_histograms_size);
|
BROTLI_ALLOC(m, HistogramCommand, mb->command_histograms_size);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
ClearHistogramsCommand(mb->command_histograms, mb->command_histograms_size);
|
ClearHistogramsCommand(mb->command_histograms, mb->command_histograms_size);
|
||||||
distance_histograms =
|
|
||||||
BROTLI_ALLOC(m, HistogramDistance, num_distance_contexts);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
ClearHistogramsDistance(distance_histograms, num_distance_contexts);
|
|
||||||
BrotliBuildHistogramsWithContext(cmds, num_commands,
|
BrotliBuildHistogramsWithContext(cmds, num_commands,
|
||||||
&mb->literal_split, &mb->command_split, &mb->distance_split,
|
&mb->literal_split, &mb->command_split, &mb->distance_split,
|
||||||
ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes,
|
ringbuffer, pos, mask, prev_byte, prev_byte2, literal_context_modes,
|
||||||
literal_histograms, mb->command_histograms, distance_histograms);
|
literal_histograms, mb->command_histograms, distance_histograms);
|
||||||
BROTLI_FREE(m, literal_context_modes);
|
BROTLI_FREE(m, literal_context_modes);
|
||||||
|
|
||||||
assert(mb->literal_context_map == 0);
|
BROTLI_DCHECK(mb->literal_context_map == 0);
|
||||||
mb->literal_context_map_size =
|
mb->literal_context_map_size =
|
||||||
mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
|
mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
|
||||||
mb->literal_context_map =
|
mb->literal_context_map =
|
||||||
BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size);
|
BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
assert(mb->literal_histograms == 0);
|
|
||||||
|
BROTLI_DCHECK(mb->literal_histograms == 0);
|
||||||
mb->literal_histograms_size = mb->literal_context_map_size;
|
mb->literal_histograms_size = mb->literal_context_map_size;
|
||||||
mb->literal_histograms =
|
mb->literal_histograms =
|
||||||
BROTLI_ALLOC(m, HistogramLiteral, mb->literal_histograms_size);
|
BROTLI_ALLOC(m, HistogramLiteral, mb->literal_histograms_size);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BrotliClusterHistogramsLiteral(m, literal_histograms,
|
|
||||||
mb->literal_context_map_size,
|
BrotliClusterHistogramsLiteral(m, literal_histograms, literal_histograms_size,
|
||||||
kMaxNumberOfHistograms,
|
kMaxNumberOfHistograms, mb->literal_histograms,
|
||||||
mb->literal_histograms,
|
&mb->literal_histograms_size, mb->literal_context_map);
|
||||||
&mb->literal_histograms_size,
|
|
||||||
mb->literal_context_map);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
BROTLI_FREE(m, literal_histograms);
|
BROTLI_FREE(m, literal_histograms);
|
||||||
|
|
||||||
assert(mb->distance_context_map == 0);
|
if (params->disable_literal_context_modeling) {
|
||||||
|
/* Distribute assignment to all contexts. */
|
||||||
|
for (i = mb->literal_split.num_types; i != 0;) {
|
||||||
|
size_t j = 0;
|
||||||
|
i--;
|
||||||
|
for (; j < (1 << BROTLI_LITERAL_CONTEXT_BITS); j++) {
|
||||||
|
mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] =
|
||||||
|
mb->literal_context_map[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BROTLI_DCHECK(mb->distance_context_map == 0);
|
||||||
mb->distance_context_map_size =
|
mb->distance_context_map_size =
|
||||||
mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS;
|
mb->distance_split.num_types << BROTLI_DISTANCE_CONTEXT_BITS;
|
||||||
mb->distance_context_map =
|
mb->distance_context_map =
|
||||||
BROTLI_ALLOC(m, uint32_t, mb->distance_context_map_size);
|
BROTLI_ALLOC(m, uint32_t, mb->distance_context_map_size);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
assert(mb->distance_histograms == 0);
|
|
||||||
|
BROTLI_DCHECK(mb->distance_histograms == 0);
|
||||||
mb->distance_histograms_size = mb->distance_context_map_size;
|
mb->distance_histograms_size = mb->distance_context_map_size;
|
||||||
mb->distance_histograms =
|
mb->distance_histograms =
|
||||||
BROTLI_ALLOC(m, HistogramDistance, mb->distance_histograms_size);
|
BROTLI_ALLOC(m, HistogramDistance, mb->distance_histograms_size);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
|
||||||
BrotliClusterHistogramsDistance(m, distance_histograms,
|
BrotliClusterHistogramsDistance(m, distance_histograms,
|
||||||
mb->distance_context_map_size,
|
mb->distance_context_map_size,
|
||||||
kMaxNumberOfHistograms,
|
kMaxNumberOfHistograms,
|
||||||
|
@ -136,53 +299,7 @@ void BrotliBuildMetaBlock(MemoryManager* m,
|
||||||
#include "./metablock_inc.h" /* NOLINT(build/include) */
|
#include "./metablock_inc.h" /* NOLINT(build/include) */
|
||||||
#undef FN
|
#undef FN
|
||||||
|
|
||||||
void BrotliBuildMetaBlockGreedy(MemoryManager* m,
|
#define BROTLI_MAX_STATIC_CONTEXTS 13
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t pos,
|
|
||||||
size_t mask,
|
|
||||||
const Command *commands,
|
|
||||||
size_t n_commands,
|
|
||||||
MetaBlockSplit* mb) {
|
|
||||||
BlockSplitterLiteral lit_blocks;
|
|
||||||
BlockSplitterCommand cmd_blocks;
|
|
||||||
BlockSplitterDistance dist_blocks;
|
|
||||||
size_t num_literals = 0;
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < n_commands; ++i) {
|
|
||||||
num_literals += commands[i].insert_len_;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitBlockSplitterLiteral(m, &lit_blocks, 256, 512, 400.0, num_literals,
|
|
||||||
&mb->literal_split, &mb->literal_histograms,
|
|
||||||
&mb->literal_histograms_size);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
InitBlockSplitterCommand(m, &cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, 1024,
|
|
||||||
500.0, n_commands, &mb->command_split, &mb->command_histograms,
|
|
||||||
&mb->command_histograms_size);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
InitBlockSplitterDistance(m, &dist_blocks, 64, 512, 100.0, n_commands,
|
|
||||||
&mb->distance_split, &mb->distance_histograms,
|
|
||||||
&mb->distance_histograms_size);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
|
|
||||||
for (i = 0; i < n_commands; ++i) {
|
|
||||||
const Command cmd = commands[i];
|
|
||||||
size_t j;
|
|
||||||
BlockSplitterAddSymbolCommand(&cmd_blocks, cmd.cmd_prefix_);
|
|
||||||
for (j = cmd.insert_len_; j != 0; --j) {
|
|
||||||
BlockSplitterAddSymbolLiteral(&lit_blocks, ringbuffer[pos & mask]);
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
pos += CommandCopyLen(&cmd);
|
|
||||||
if (CommandCopyLen(&cmd) && cmd.cmd_prefix_ >= 128) {
|
|
||||||
BlockSplitterAddSymbolDistance(&dist_blocks, cmd.dist_prefix_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockSplitterFinishBlockLiteral(&lit_blocks, /* is_final = */ BROTLI_TRUE);
|
|
||||||
BlockSplitterFinishBlockCommand(&cmd_blocks, /* is_final = */ BROTLI_TRUE);
|
|
||||||
BlockSplitterFinishBlockDistance(&dist_blocks, /* is_final = */ BROTLI_TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Greedy block splitter for one block category (literal, command or distance).
|
/* Greedy block splitter for one block category (literal, command or distance).
|
||||||
Gathers histograms for all context buckets. */
|
Gathers histograms for all context buckets. */
|
||||||
|
@ -214,7 +331,7 @@ typedef struct ContextBlockSplitter {
|
||||||
/* Offset of the histograms of the previous two block types. */
|
/* Offset of the histograms of the previous two block types. */
|
||||||
size_t last_histogram_ix_[2];
|
size_t last_histogram_ix_[2];
|
||||||
/* Entropy of the previous two block types. */
|
/* Entropy of the previous two block types. */
|
||||||
double* last_entropy_;
|
double last_entropy_[2 * BROTLI_MAX_STATIC_CONTEXTS];
|
||||||
/* The number of times we merged the current block with the last one. */
|
/* The number of times we merged the current block with the last one. */
|
||||||
size_t merge_last_count_;
|
size_t merge_last_count_;
|
||||||
} ContextBlockSplitter;
|
} ContextBlockSplitter;
|
||||||
|
@ -226,6 +343,7 @@ static void InitContextBlockSplitter(
|
||||||
size_t* histograms_size) {
|
size_t* histograms_size) {
|
||||||
size_t max_num_blocks = num_symbols / min_block_size + 1;
|
size_t max_num_blocks = num_symbols / min_block_size + 1;
|
||||||
size_t max_num_types;
|
size_t max_num_types;
|
||||||
|
BROTLI_DCHECK(num_contexts <= BROTLI_MAX_STATIC_CONTEXTS);
|
||||||
|
|
||||||
self->alphabet_size_ = alphabet_size;
|
self->alphabet_size_ = alphabet_size;
|
||||||
self->num_contexts_ = num_contexts;
|
self->num_contexts_ = num_contexts;
|
||||||
|
@ -250,29 +368,23 @@ static void InitContextBlockSplitter(
|
||||||
split->lengths, split->lengths_alloc_size, max_num_blocks);
|
split->lengths, split->lengths_alloc_size, max_num_blocks);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
split->num_blocks = max_num_blocks;
|
split->num_blocks = max_num_blocks;
|
||||||
self->last_entropy_ = BROTLI_ALLOC(m, double, 2 * num_contexts);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
assert(*histograms == 0);
|
BROTLI_DCHECK(*histograms == 0);
|
||||||
*histograms_size = max_num_types * num_contexts;
|
*histograms_size = max_num_types * num_contexts;
|
||||||
*histograms = BROTLI_ALLOC(m, HistogramLiteral, *histograms_size);
|
*histograms = BROTLI_ALLOC(m, HistogramLiteral, *histograms_size);
|
||||||
self->histograms_ = *histograms;
|
self->histograms_ = *histograms;
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
/* Clear only current historgram. */
|
/* Clear only current histogram. */
|
||||||
ClearHistogramsLiteral(&self->histograms_[0], num_contexts);
|
ClearHistogramsLiteral(&self->histograms_[0], num_contexts);
|
||||||
self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0;
|
self->last_histogram_ix_[0] = self->last_histogram_ix_[1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CleanupContextBlockSplitter(
|
|
||||||
MemoryManager* m, ContextBlockSplitter* self) {
|
|
||||||
BROTLI_FREE(m, self->last_entropy_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does either of three things:
|
/* Does either of three things:
|
||||||
(1) emits the current block with a new block type;
|
(1) emits the current block with a new block type;
|
||||||
(2) emits the current block with the type of the second last block;
|
(2) emits the current block with the type of the second last block;
|
||||||
(3) merges the current block with the last block. */
|
(3) merges the current block with the last block. */
|
||||||
static void ContextBlockSplitterFinishBlock(
|
static void ContextBlockSplitterFinishBlock(
|
||||||
MemoryManager* m, ContextBlockSplitter* self, BROTLI_BOOL is_final) {
|
ContextBlockSplitter* self, MemoryManager* m, BROTLI_BOOL is_final) {
|
||||||
BlockSplit* split = self->split_;
|
BlockSplit* split = self->split_;
|
||||||
const size_t num_contexts = self->num_contexts_;
|
const size_t num_contexts = self->num_contexts_;
|
||||||
double* last_entropy = self->last_entropy_;
|
double* last_entropy = self->last_entropy_;
|
||||||
|
@ -305,10 +417,10 @@ static void ContextBlockSplitterFinishBlock(
|
||||||
respective set of histograms for the last and second last block types.
|
respective set of histograms for the last and second last block types.
|
||||||
Decide over the split based on the total reduction of entropy across
|
Decide over the split based on the total reduction of entropy across
|
||||||
all contexts. */
|
all contexts. */
|
||||||
double* entropy = BROTLI_ALLOC(m, double, num_contexts);
|
double entropy[BROTLI_MAX_STATIC_CONTEXTS];
|
||||||
HistogramLiteral* combined_histo =
|
HistogramLiteral* combined_histo =
|
||||||
BROTLI_ALLOC(m, HistogramLiteral, 2 * num_contexts);
|
BROTLI_ALLOC(m, HistogramLiteral, 2 * num_contexts);
|
||||||
double* combined_entropy = BROTLI_ALLOC(m, double, 2 * num_contexts);
|
double combined_entropy[2 * BROTLI_MAX_STATIC_CONTEXTS];
|
||||||
double diff[2] = { 0.0 };
|
double diff[2] = { 0.0 };
|
||||||
size_t i;
|
size_t i;
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
@ -383,9 +495,7 @@ static void ContextBlockSplitterFinishBlock(
|
||||||
self->target_block_size_ += self->min_block_size_;
|
self->target_block_size_ += self->min_block_size_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BROTLI_FREE(m, combined_entropy);
|
|
||||||
BROTLI_FREE(m, combined_histo);
|
BROTLI_FREE(m, combined_histo);
|
||||||
BROTLI_FREE(m, entropy);
|
|
||||||
}
|
}
|
||||||
if (is_final) {
|
if (is_final) {
|
||||||
*self->histograms_size_ = split->num_types * num_contexts;
|
*self->histograms_size_ = split->num_types * num_contexts;
|
||||||
|
@ -395,30 +505,49 @@ static void ContextBlockSplitterFinishBlock(
|
||||||
|
|
||||||
/* Adds the next symbol to the current block type and context. When the
|
/* Adds the next symbol to the current block type and context. When the
|
||||||
current block reaches the target size, decides on merging the block. */
|
current block reaches the target size, decides on merging the block. */
|
||||||
static void ContextBlockSplitterAddSymbol(MemoryManager* m,
|
static void ContextBlockSplitterAddSymbol(
|
||||||
ContextBlockSplitter* self, size_t symbol, size_t context) {
|
ContextBlockSplitter* self, MemoryManager* m,
|
||||||
|
size_t symbol, size_t context) {
|
||||||
HistogramAddLiteral(&self->histograms_[self->curr_histogram_ix_ + context],
|
HistogramAddLiteral(&self->histograms_[self->curr_histogram_ix_ + context],
|
||||||
symbol);
|
symbol);
|
||||||
++self->block_size_;
|
++self->block_size_;
|
||||||
if (self->block_size_ == self->target_block_size_) {
|
if (self->block_size_ == self->target_block_size_) {
|
||||||
ContextBlockSplitterFinishBlock(m, self, /* is_final = */ BROTLI_FALSE);
|
ContextBlockSplitterFinishBlock(self, m, /* is_final = */ BROTLI_FALSE);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliBuildMetaBlockGreedyWithContexts(MemoryManager* m,
|
static void MapStaticContexts(MemoryManager* m,
|
||||||
const uint8_t* ringbuffer,
|
size_t num_contexts,
|
||||||
size_t pos,
|
const uint32_t* static_context_map,
|
||||||
size_t mask,
|
MetaBlockSplit* mb) {
|
||||||
uint8_t prev_byte,
|
size_t i;
|
||||||
uint8_t prev_byte2,
|
BROTLI_DCHECK(mb->literal_context_map == 0);
|
||||||
ContextType literal_context_mode,
|
mb->literal_context_map_size =
|
||||||
size_t num_contexts,
|
mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
|
||||||
const uint32_t* static_context_map,
|
mb->literal_context_map =
|
||||||
const Command *commands,
|
BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size);
|
||||||
size_t n_commands,
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
MetaBlockSplit* mb) {
|
|
||||||
ContextBlockSplitter lit_blocks;
|
for (i = 0; i < mb->literal_split.num_types; ++i) {
|
||||||
|
uint32_t offset = (uint32_t)(i * num_contexts);
|
||||||
|
size_t j;
|
||||||
|
for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS); ++j) {
|
||||||
|
mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] =
|
||||||
|
offset + static_context_map[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
|
||||||
|
MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask,
|
||||||
|
uint8_t prev_byte, uint8_t prev_byte2, ContextLut literal_context_lut,
|
||||||
|
const size_t num_contexts, const uint32_t* static_context_map,
|
||||||
|
const Command* commands, size_t n_commands, MetaBlockSplit* mb) {
|
||||||
|
union {
|
||||||
|
BlockSplitterLiteral plain;
|
||||||
|
ContextBlockSplitter ctx;
|
||||||
|
} lit_blocks;
|
||||||
BlockSplitterCommand cmd_blocks;
|
BlockSplitterCommand cmd_blocks;
|
||||||
BlockSplitterDistance dist_blocks;
|
BlockSplitterDistance dist_blocks;
|
||||||
size_t num_literals = 0;
|
size_t num_literals = 0;
|
||||||
|
@ -427,9 +556,15 @@ void BrotliBuildMetaBlockGreedyWithContexts(MemoryManager* m,
|
||||||
num_literals += commands[i].insert_len_;
|
num_literals += commands[i].insert_len_;
|
||||||
}
|
}
|
||||||
|
|
||||||
InitContextBlockSplitter(m, &lit_blocks, 256, num_contexts, 512, 400.0,
|
if (num_contexts == 1) {
|
||||||
num_literals, &mb->literal_split, &mb->literal_histograms,
|
InitBlockSplitterLiteral(m, &lit_blocks.plain, 256, 512, 400.0,
|
||||||
&mb->literal_histograms_size);
|
num_literals, &mb->literal_split, &mb->literal_histograms,
|
||||||
|
&mb->literal_histograms_size);
|
||||||
|
} else {
|
||||||
|
InitContextBlockSplitter(m, &lit_blocks.ctx, 256, num_contexts, 512, 400.0,
|
||||||
|
num_literals, &mb->literal_split, &mb->literal_histograms,
|
||||||
|
&mb->literal_histograms_size);
|
||||||
|
}
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
InitBlockSplitterCommand(m, &cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, 1024,
|
InitBlockSplitterCommand(m, &cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, 1024,
|
||||||
500.0, n_commands, &mb->command_split, &mb->command_histograms,
|
500.0, n_commands, &mb->command_split, &mb->command_histograms,
|
||||||
|
@ -445,12 +580,17 @@ void BrotliBuildMetaBlockGreedyWithContexts(MemoryManager* m,
|
||||||
size_t j;
|
size_t j;
|
||||||
BlockSplitterAddSymbolCommand(&cmd_blocks, cmd.cmd_prefix_);
|
BlockSplitterAddSymbolCommand(&cmd_blocks, cmd.cmd_prefix_);
|
||||||
for (j = cmd.insert_len_; j != 0; --j) {
|
for (j = cmd.insert_len_; j != 0; --j) {
|
||||||
size_t context = Context(prev_byte, prev_byte2, literal_context_mode);
|
|
||||||
uint8_t literal = ringbuffer[pos & mask];
|
uint8_t literal = ringbuffer[pos & mask];
|
||||||
ContextBlockSplitterAddSymbol(
|
if (num_contexts == 1) {
|
||||||
m, &lit_blocks, literal, static_context_map[context]);
|
BlockSplitterAddSymbolLiteral(&lit_blocks.plain, literal);
|
||||||
|
} else {
|
||||||
|
size_t context =
|
||||||
|
BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut);
|
||||||
|
ContextBlockSplitterAddSymbol(&lit_blocks.ctx, m, literal,
|
||||||
|
static_context_map[context]);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
}
|
||||||
prev_byte2 = prev_byte;
|
prev_byte2 = prev_byte;
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
prev_byte = literal;
|
prev_byte = literal;
|
||||||
++pos;
|
++pos;
|
||||||
}
|
}
|
||||||
|
@ -459,38 +599,52 @@ void BrotliBuildMetaBlockGreedyWithContexts(MemoryManager* m,
|
||||||
prev_byte2 = ringbuffer[(pos - 2) & mask];
|
prev_byte2 = ringbuffer[(pos - 2) & mask];
|
||||||
prev_byte = ringbuffer[(pos - 1) & mask];
|
prev_byte = ringbuffer[(pos - 1) & mask];
|
||||||
if (cmd.cmd_prefix_ >= 128) {
|
if (cmd.cmd_prefix_ >= 128) {
|
||||||
BlockSplitterAddSymbolDistance(&dist_blocks, cmd.dist_prefix_);
|
BlockSplitterAddSymbolDistance(&dist_blocks, cmd.dist_prefix_ & 0x3FF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextBlockSplitterFinishBlock(m, &lit_blocks, /* is_final = */ BROTLI_TRUE);
|
if (num_contexts == 1) {
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
BlockSplitterFinishBlockLiteral(
|
||||||
CleanupContextBlockSplitter(m, &lit_blocks);
|
&lit_blocks.plain, /* is_final = */ BROTLI_TRUE);
|
||||||
|
} else {
|
||||||
|
ContextBlockSplitterFinishBlock(
|
||||||
|
&lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE);
|
||||||
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
|
}
|
||||||
BlockSplitterFinishBlockCommand(&cmd_blocks, /* is_final = */ BROTLI_TRUE);
|
BlockSplitterFinishBlockCommand(&cmd_blocks, /* is_final = */ BROTLI_TRUE);
|
||||||
BlockSplitterFinishBlockDistance(&dist_blocks, /* is_final = */ BROTLI_TRUE);
|
BlockSplitterFinishBlockDistance(&dist_blocks, /* is_final = */ BROTLI_TRUE);
|
||||||
|
|
||||||
assert(mb->literal_context_map == 0);
|
if (num_contexts > 1) {
|
||||||
mb->literal_context_map_size =
|
MapStaticContexts(m, num_contexts, static_context_map, mb);
|
||||||
mb->literal_split.num_types << BROTLI_LITERAL_CONTEXT_BITS;
|
|
||||||
mb->literal_context_map =
|
|
||||||
BROTLI_ALLOC(m, uint32_t, mb->literal_context_map_size);
|
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
|
||||||
|
|
||||||
for (i = 0; i < mb->literal_split.num_types; ++i) {
|
|
||||||
size_t j;
|
|
||||||
for (j = 0; j < (1u << BROTLI_LITERAL_CONTEXT_BITS); ++j) {
|
|
||||||
mb->literal_context_map[(i << BROTLI_LITERAL_CONTEXT_BITS) + j] =
|
|
||||||
(uint32_t)(i * num_contexts) + static_context_map[j];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrotliOptimizeHistograms(size_t num_direct_distance_codes,
|
void BrotliBuildMetaBlockGreedy(MemoryManager* m,
|
||||||
size_t distance_postfix_bits,
|
const uint8_t* ringbuffer,
|
||||||
|
size_t pos,
|
||||||
|
size_t mask,
|
||||||
|
uint8_t prev_byte,
|
||||||
|
uint8_t prev_byte2,
|
||||||
|
ContextLut literal_context_lut,
|
||||||
|
size_t num_contexts,
|
||||||
|
const uint32_t* static_context_map,
|
||||||
|
const Command* commands,
|
||||||
|
size_t n_commands,
|
||||||
|
MetaBlockSplit* mb) {
|
||||||
|
if (num_contexts == 1) {
|
||||||
|
BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte,
|
||||||
|
prev_byte2, literal_context_lut, 1, NULL, commands, n_commands, mb);
|
||||||
|
} else {
|
||||||
|
BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte,
|
||||||
|
prev_byte2, literal_context_lut, num_contexts, static_context_map,
|
||||||
|
commands, n_commands, mb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrotliOptimizeHistograms(uint32_t num_distance_codes,
|
||||||
MetaBlockSplit* mb) {
|
MetaBlockSplit* mb) {
|
||||||
uint8_t good_for_rle[BROTLI_NUM_COMMAND_SYMBOLS];
|
uint8_t good_for_rle[BROTLI_NUM_COMMAND_SYMBOLS];
|
||||||
size_t num_distance_codes;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < mb->literal_histograms_size; ++i) {
|
for (i = 0; i < mb->literal_histograms_size; ++i) {
|
||||||
BrotliOptimizeHuffmanCountsForRle(256, mb->literal_histograms[i].data_,
|
BrotliOptimizeHuffmanCountsForRle(256, mb->literal_histograms[i].data_,
|
||||||
|
@ -501,8 +655,6 @@ void BrotliOptimizeHistograms(size_t num_direct_distance_codes,
|
||||||
mb->command_histograms[i].data_,
|
mb->command_histograms[i].data_,
|
||||||
good_for_rle);
|
good_for_rle);
|
||||||
}
|
}
|
||||||
num_distance_codes = BROTLI_NUM_DISTANCE_SHORT_CODES +
|
|
||||||
num_direct_distance_codes + (48u << distance_postfix_bits);
|
|
||||||
for (i = 0; i < mb->distance_histograms_size; ++i) {
|
for (i = 0; i < mb->distance_histograms_size; ++i) {
|
||||||
BrotliOptimizeHuffmanCountsForRle(num_distance_codes,
|
BrotliOptimizeHuffmanCountsForRle(num_distance_codes,
|
||||||
mb->distance_histograms[i].data_,
|
mb->distance_histograms[i].data_,
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
#ifndef BROTLI_ENC_METABLOCK_H_
|
#ifndef BROTLI_ENC_METABLOCK_H_
|
||||||
#define BROTLI_ENC_METABLOCK_H_
|
#define BROTLI_ENC_METABLOCK_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/context.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./block_splitter.h"
|
#include "./block_splitter.h"
|
||||||
#include "./command.h"
|
#include "./command.h"
|
||||||
#include "./context.h"
|
|
||||||
#include "./histogram.h"
|
#include "./histogram.h"
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -67,42 +67,37 @@ static BROTLI_INLINE void DestroyMetaBlockSplit(
|
||||||
BROTLI_FREE(m, mb->distance_histograms);
|
BROTLI_FREE(m, mb->distance_histograms);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Uses the slow shortest-path block splitter and does context clustering. */
|
/* Uses the slow shortest-path block splitter and does context clustering.
|
||||||
|
The distance parameters are dynamically selected based on the commands
|
||||||
|
which get recomputed under the new distance parameters. The new distance
|
||||||
|
parameters are stored into *params. */
|
||||||
BROTLI_INTERNAL void BrotliBuildMetaBlock(MemoryManager* m,
|
BROTLI_INTERNAL void BrotliBuildMetaBlock(MemoryManager* m,
|
||||||
const uint8_t* ringbuffer,
|
const uint8_t* ringbuffer,
|
||||||
const size_t pos,
|
const size_t pos,
|
||||||
const size_t mask,
|
const size_t mask,
|
||||||
const BrotliEncoderParams* params,
|
BrotliEncoderParams* params,
|
||||||
uint8_t prev_byte,
|
uint8_t prev_byte,
|
||||||
uint8_t prev_byte2,
|
uint8_t prev_byte2,
|
||||||
const Command* cmds,
|
Command* cmds,
|
||||||
size_t num_commands,
|
size_t num_commands,
|
||||||
ContextType literal_context_mode,
|
ContextType literal_context_mode,
|
||||||
MetaBlockSplit* mb);
|
MetaBlockSplit* mb);
|
||||||
|
|
||||||
/* Uses a fast greedy block splitter that tries to merge current block with the
|
|
||||||
last or the second last block and does not do any context modeling. */
|
|
||||||
BROTLI_INTERNAL void BrotliBuildMetaBlockGreedy(MemoryManager* m,
|
|
||||||
const uint8_t* ringbuffer,
|
|
||||||
size_t pos,
|
|
||||||
size_t mask,
|
|
||||||
const Command* commands,
|
|
||||||
size_t n_commands,
|
|
||||||
MetaBlockSplit* mb);
|
|
||||||
|
|
||||||
/* Uses a fast greedy block splitter that tries to merge current block with the
|
/* Uses a fast greedy block splitter that tries to merge current block with the
|
||||||
last or the second last block and uses a static context clustering which
|
last or the second last block and uses a static context clustering which
|
||||||
is the same for all block types. */
|
is the same for all block types. */
|
||||||
BROTLI_INTERNAL void BrotliBuildMetaBlockGreedyWithContexts(
|
BROTLI_INTERNAL void BrotliBuildMetaBlockGreedy(
|
||||||
MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask,
|
MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask,
|
||||||
uint8_t prev_byte, uint8_t prev_byte2, ContextType literal_context_mode,
|
uint8_t prev_byte, uint8_t prev_byte2, ContextLut literal_context_lut,
|
||||||
size_t num_contexts, const uint32_t* static_context_map,
|
size_t num_contexts, const uint32_t* static_context_map,
|
||||||
const Command* commands, size_t n_commands, MetaBlockSplit* mb);
|
const Command* commands, size_t n_commands, MetaBlockSplit* mb);
|
||||||
|
|
||||||
BROTLI_INTERNAL void BrotliOptimizeHistograms(size_t num_direct_distance_codes,
|
BROTLI_INTERNAL void BrotliOptimizeHistograms(uint32_t num_distance_codes,
|
||||||
size_t distance_postfix_bits,
|
|
||||||
MetaBlockSplit* mb);
|
MetaBlockSplit* mb);
|
||||||
|
|
||||||
|
BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliEncoderParams* params,
|
||||||
|
uint32_t npostfix, uint32_t ndirect);
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -67,7 +67,7 @@ static void FN(InitBlockSplitter)(
|
||||||
split->lengths, split->lengths_alloc_size, max_num_blocks);
|
split->lengths, split->lengths_alloc_size, max_num_blocks);
|
||||||
if (BROTLI_IS_OOM(m)) return;
|
if (BROTLI_IS_OOM(m)) return;
|
||||||
self->split_->num_blocks = max_num_blocks;
|
self->split_->num_blocks = max_num_blocks;
|
||||||
assert(*histograms == 0);
|
BROTLI_DCHECK(*histograms == 0);
|
||||||
*histograms_size = max_num_types;
|
*histograms_size = max_num_types;
|
||||||
*histograms = BROTLI_ALLOC(m, HistogramType, *histograms_size);
|
*histograms = BROTLI_ALLOC(m, HistogramType, *histograms_size);
|
||||||
self->histograms_ = *histograms;
|
self->histograms_ = *histograms;
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/* Copyright 2017 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Parameters for the Brotli encoder with chosen quality levels. */
|
||||||
|
|
||||||
|
#ifndef BROTLI_ENC_PARAMS_H_
|
||||||
|
#define BROTLI_ENC_PARAMS_H_
|
||||||
|
|
||||||
|
#include <brotli/encode.h>
|
||||||
|
#include "./encoder_dict.h"
|
||||||
|
|
||||||
|
typedef struct BrotliHasherParams {
|
||||||
|
int type;
|
||||||
|
int bucket_bits;
|
||||||
|
int block_bits;
|
||||||
|
int hash_len;
|
||||||
|
int num_last_distances_to_check;
|
||||||
|
} BrotliHasherParams;
|
||||||
|
|
||||||
|
typedef struct BrotliDistanceParams {
|
||||||
|
uint32_t distance_postfix_bits;
|
||||||
|
uint32_t num_direct_distance_codes;
|
||||||
|
uint32_t alphabet_size;
|
||||||
|
size_t max_distance;
|
||||||
|
} BrotliDistanceParams;
|
||||||
|
|
||||||
|
/* Encoding parameters */
|
||||||
|
typedef struct BrotliEncoderParams {
|
||||||
|
BrotliEncoderMode mode;
|
||||||
|
int quality;
|
||||||
|
int lgwin;
|
||||||
|
int lgblock;
|
||||||
|
size_t size_hint;
|
||||||
|
BROTLI_BOOL disable_literal_context_modeling;
|
||||||
|
BROTLI_BOOL large_window;
|
||||||
|
BrotliHasherParams hasher;
|
||||||
|
BrotliDistanceParams dist;
|
||||||
|
BrotliEncoderDictionary dictionary;
|
||||||
|
} BrotliEncoderParams;
|
||||||
|
|
||||||
|
#endif /* BROTLI_ENC_PARAMS_H_ */
|
|
@ -1,168 +0,0 @@
|
||||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Macros for endianness, branch prediction and unaligned loads and stores. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_PORT_H_
|
|
||||||
#define BROTLI_ENC_PORT_H_
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h> /* memcpy */
|
|
||||||
|
|
||||||
#include "../common/port.h"
|
|
||||||
#include "../common/types.h"
|
|
||||||
|
|
||||||
#if defined OS_LINUX || defined OS_CYGWIN
|
|
||||||
#include <endian.h>
|
|
||||||
#elif defined OS_FREEBSD
|
|
||||||
#include <machine/endian.h>
|
|
||||||
#elif defined OS_MACOSX
|
|
||||||
#include <machine/endian.h>
|
|
||||||
/* Let's try and follow the Linux convention */
|
|
||||||
#define __BYTE_ORDER BYTE_ORDER
|
|
||||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* define the macro IS_LITTLE_ENDIAN
|
|
||||||
using the above endian definitions from endian.h if
|
|
||||||
endian.h was included */
|
|
||||||
#ifdef __BYTE_ORDER
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
#define IS_LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if defined(__LITTLE_ENDIAN__)
|
|
||||||
#define IS_LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
#endif /* __BYTE_ORDER */
|
|
||||||
|
|
||||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
||||||
#define IS_LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Enable little-endian optimization for x64 architecture on Windows. */
|
|
||||||
#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_X64)
|
|
||||||
#define IS_LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Portable handling of unaligned loads, stores, and copies.
|
|
||||||
On some platforms, like ARM, the copy functions can be more efficient
|
|
||||||
then a load and a store. */
|
|
||||||
|
|
||||||
#if defined(ARCH_PIII) || \
|
|
||||||
defined(ARCH_ATHLON) || defined(ARCH_K8) || defined(_ARCH_PPC)
|
|
||||||
|
|
||||||
/* x86 and x86-64 can perform unaligned loads/stores directly;
|
|
||||||
modern PowerPC hardware can also do unaligned integer loads and stores;
|
|
||||||
but note: the FPU still sends unaligned loads and stores to a trap handler!
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define BROTLI_UNALIGNED_LOAD32(_p) (*(const uint32_t *)(_p))
|
|
||||||
#define BROTLI_UNALIGNED_LOAD64(_p) (*(const uint64_t *)(_p))
|
|
||||||
|
|
||||||
#define BROTLI_UNALIGNED_STORE32(_p, _val) \
|
|
||||||
(*(uint32_t *)(_p) = (_val))
|
|
||||||
#define BROTLI_UNALIGNED_STORE64(_p, _val) \
|
|
||||||
(*(uint64_t *)(_p) = (_val))
|
|
||||||
|
|
||||||
#elif defined(__arm__) && \
|
|
||||||
!defined(__ARM_ARCH_5__) && \
|
|
||||||
!defined(__ARM_ARCH_5T__) && \
|
|
||||||
!defined(__ARM_ARCH_5TE__) && \
|
|
||||||
!defined(__ARM_ARCH_5TEJ__) && \
|
|
||||||
!defined(__ARM_ARCH_6__) && \
|
|
||||||
!defined(__ARM_ARCH_6J__) && \
|
|
||||||
!defined(__ARM_ARCH_6K__) && \
|
|
||||||
!defined(__ARM_ARCH_6Z__) && \
|
|
||||||
!defined(__ARM_ARCH_6ZK__) && \
|
|
||||||
!defined(__ARM_ARCH_6T2__)
|
|
||||||
|
|
||||||
/* ARMv7 and newer support native unaligned accesses, but only of 16-bit
|
|
||||||
and 32-bit values (not 64-bit); older versions either raise a fatal signal,
|
|
||||||
do an unaligned read and rotate the words around a bit, or do the reads very
|
|
||||||
slowly (trip through kernel mode). */
|
|
||||||
|
|
||||||
#define BROTLI_UNALIGNED_LOAD32(_p) (*(const uint32_t *)(_p))
|
|
||||||
#define BROTLI_UNALIGNED_STORE32(_p, _val) \
|
|
||||||
(*(uint32_t *)(_p) = (_val))
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64(const void *p) {
|
|
||||||
uint64_t t;
|
|
||||||
memcpy(&t, p, sizeof t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64(void *p, uint64_t v) {
|
|
||||||
memcpy(p, &v, sizeof v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/* These functions are provided for architectures that don't support */
|
|
||||||
/* unaligned loads and stores. */
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t BROTLI_UNALIGNED_LOAD32(const void *p) {
|
|
||||||
uint32_t t;
|
|
||||||
memcpy(&t, p, sizeof t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint64_t BROTLI_UNALIGNED_LOAD64(const void *p) {
|
|
||||||
uint64_t t;
|
|
||||||
memcpy(&t, p, sizeof t);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE32(void *p, uint32_t v) {
|
|
||||||
memcpy(p, &v, sizeof v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BROTLI_INLINE void BROTLI_UNALIGNED_STORE64(void *p, uint64_t v) {
|
|
||||||
memcpy(p, &v, sizeof v);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(__cplusplus) && !defined(c_plusplus) && __STDC_VERSION__ >= 199901L
|
|
||||||
#define BROTLI_RESTRICT restrict
|
|
||||||
#elif BROTLI_GCC_VERSION > 295 || defined(__llvm__)
|
|
||||||
#define BROTLI_RESTRICT __restrict
|
|
||||||
#else
|
|
||||||
#define BROTLI_RESTRICT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define _TEMPLATE(T) \
|
|
||||||
static BROTLI_INLINE T brotli_min_ ## T (T a, T b) { return a < b ? a : b; } \
|
|
||||||
static BROTLI_INLINE T brotli_max_ ## T (T a, T b) { return a > b ? a : b; }
|
|
||||||
_TEMPLATE(double) _TEMPLATE(float) _TEMPLATE(int)
|
|
||||||
_TEMPLATE(size_t) _TEMPLATE(uint32_t) _TEMPLATE(uint8_t)
|
|
||||||
#undef _TEMPLATE
|
|
||||||
#define BROTLI_MIN(T, A, B) (brotli_min_ ## T((A), (B)))
|
|
||||||
#define BROTLI_MAX(T, A, B) (brotli_max_ ## T((A), (B)))
|
|
||||||
|
|
||||||
#define BROTLI_SWAP(T, A, I, J) { \
|
|
||||||
T __brotli_swap_tmp = (A)[(I)]; \
|
|
||||||
(A)[(I)] = (A)[(J)]; \
|
|
||||||
(A)[(J)] = __brotli_swap_tmp; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BROTLI_ENSURE_CAPACITY(M, T, A, C, R) { \
|
|
||||||
if (C < (R)) { \
|
|
||||||
size_t _new_size = (C == 0) ? (R) : C; \
|
|
||||||
T* new_array; \
|
|
||||||
while (_new_size < (R)) _new_size *= 2; \
|
|
||||||
new_array = BROTLI_ALLOC((M), T, _new_size); \
|
|
||||||
if (!BROTLI_IS_OOM(m) && C != 0) \
|
|
||||||
memcpy(new_array, A, C * sizeof(T)); \
|
|
||||||
BROTLI_FREE((M), A); \
|
|
||||||
A = new_array; \
|
|
||||||
C = _new_size; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_PORT_H_ */
|
|
|
@ -11,14 +11,16 @@
|
||||||
#define BROTLI_ENC_PREFIX_H_
|
#define BROTLI_ENC_PREFIX_H_
|
||||||
|
|
||||||
#include "../common/constants.h"
|
#include "../common/constants.h"
|
||||||
#include "../common/port.h"
|
#include "../common/platform.h"
|
||||||
#include "../common/types.h"
|
#include <brotli/types.h>
|
||||||
#include "./fast_log.h"
|
#include "./fast_log.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Here distance_code is an intermediate code, i.e. one of the special codes or
|
||||||
|
the actual distance increased by BROTLI_NUM_DISTANCE_SHORT_CODES - 1. */
|
||||||
static BROTLI_INLINE void PrefixEncodeCopyDistance(size_t distance_code,
|
static BROTLI_INLINE void PrefixEncodeCopyDistance(size_t distance_code,
|
||||||
size_t num_direct_codes,
|
size_t num_direct_codes,
|
||||||
size_t postfix_bits,
|
size_t postfix_bits,
|
||||||
|
@ -37,11 +39,10 @@ static BROTLI_INLINE void PrefixEncodeCopyDistance(size_t distance_code,
|
||||||
size_t prefix = (dist >> bucket) & 1;
|
size_t prefix = (dist >> bucket) & 1;
|
||||||
size_t offset = (2 + prefix) << bucket;
|
size_t offset = (2 + prefix) << bucket;
|
||||||
size_t nbits = bucket - postfix_bits;
|
size_t nbits = bucket - postfix_bits;
|
||||||
*code = (uint16_t)(
|
*code = (uint16_t)((nbits << 10) |
|
||||||
(BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes +
|
(BROTLI_NUM_DISTANCE_SHORT_CODES + num_direct_codes +
|
||||||
((2 * (nbits - 1) + prefix) << postfix_bits) + postfix));
|
((2 * (nbits - 1) + prefix) << postfix_bits) + postfix));
|
||||||
*extra_bits = (uint32_t)(
|
*extra_bits = (uint32_t)((dist - offset) >> postfix_bits);
|
||||||
(nbits << 24) | ((dist - offset) >> postfix_bits));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,36 +10,29 @@
|
||||||
#ifndef BROTLI_ENC_QUALITY_H_
|
#ifndef BROTLI_ENC_QUALITY_H_
|
||||||
#define BROTLI_ENC_QUALITY_H_
|
#define BROTLI_ENC_QUALITY_H_
|
||||||
|
|
||||||
#include "./encode.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/encode.h>
|
||||||
|
#include "./params.h"
|
||||||
|
|
||||||
#define FAST_ONE_PASS_COMPRESSION_QUALITY 0
|
#define FAST_ONE_PASS_COMPRESSION_QUALITY 0
|
||||||
#define FAST_TWO_PASS_COMPRESSION_QUALITY 1
|
#define FAST_TWO_PASS_COMPRESSION_QUALITY 1
|
||||||
#define ZOPFLIFICATION_QUALITY 10
|
#define ZOPFLIFICATION_QUALITY 10
|
||||||
#define HQ_ZOPFLIFICATION_QUALITY 11
|
#define HQ_ZOPFLIFICATION_QUALITY 11
|
||||||
|
|
||||||
#define MAX_QUALITY_FOR_STATIC_ENRTOPY_CODES 2
|
#define MAX_QUALITY_FOR_STATIC_ENTROPY_CODES 2
|
||||||
#define MIN_QUALITY_FOR_BLOCK_SPLIT 4
|
#define MIN_QUALITY_FOR_BLOCK_SPLIT 4
|
||||||
|
#define MIN_QUALITY_FOR_NONZERO_DISTANCE_PARAMS 4
|
||||||
#define MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS 4
|
#define MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS 4
|
||||||
#define MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH 5
|
#define MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH 5
|
||||||
#define MIN_QUALITY_FOR_CONTEXT_MODELING 5
|
#define MIN_QUALITY_FOR_CONTEXT_MODELING 5
|
||||||
#define MIN_QUALITY_FOR_HQ_CONTEXT_MODELING 7
|
#define MIN_QUALITY_FOR_HQ_CONTEXT_MODELING 7
|
||||||
#define MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING 10
|
#define MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING 10
|
||||||
/* Only for "font" mode. */
|
|
||||||
#define MIN_QUALITY_FOR_RECOMPUTE_DISTANCE_PREFIXES 10
|
|
||||||
|
|
||||||
/* For quality below MIN_QUALITY_FOR_BLOCK_SPLIT there is no block splitting,
|
/* For quality below MIN_QUALITY_FOR_BLOCK_SPLIT there is no block splitting,
|
||||||
so we buffer at most this much literals and commands. */
|
so we buffer at most this much literals and commands. */
|
||||||
#define MAX_NUM_DELAYED_SYMBOLS 0x2fff
|
#define MAX_NUM_DELAYED_SYMBOLS 0x2FFF
|
||||||
|
|
||||||
/* Encoding parameters */
|
/* Returns hash-table size for quality levels 0 and 1. */
|
||||||
typedef struct BrotliEncoderParams {
|
|
||||||
BrotliEncoderMode mode;
|
|
||||||
int quality;
|
|
||||||
int lgwin;
|
|
||||||
int lgblock;
|
|
||||||
} BrotliEncoderParams;
|
|
||||||
|
|
||||||
/* Returns hashtable size for quality levels 0 and 1. */
|
|
||||||
static BROTLI_INLINE size_t MaxHashTableSize(int quality) {
|
static BROTLI_INLINE size_t MaxHashTableSize(int quality) {
|
||||||
return quality == FAST_ONE_PASS_COMPRESSION_QUALITY ? 1 << 15 : 1 << 17;
|
return quality == FAST_ONE_PASS_COMPRESSION_QUALITY ? 1 << 15 : 1 << 17;
|
||||||
}
|
}
|
||||||
|
@ -48,13 +41,16 @@ static BROTLI_INLINE size_t MaxHashTableSize(int quality) {
|
||||||
#define MAX_ZOPFLI_LEN_QUALITY_10 150
|
#define MAX_ZOPFLI_LEN_QUALITY_10 150
|
||||||
#define MAX_ZOPFLI_LEN_QUALITY_11 325
|
#define MAX_ZOPFLI_LEN_QUALITY_11 325
|
||||||
|
|
||||||
|
/* Do not thoroughly search when a long copy is found. */
|
||||||
|
#define BROTLI_LONG_COPY_QUICK_STEP 16384
|
||||||
|
|
||||||
static BROTLI_INLINE size_t MaxZopfliLen(const BrotliEncoderParams* params) {
|
static BROTLI_INLINE size_t MaxZopfliLen(const BrotliEncoderParams* params) {
|
||||||
return params->quality <= 10 ?
|
return params->quality <= 10 ?
|
||||||
MAX_ZOPFLI_LEN_QUALITY_10 :
|
MAX_ZOPFLI_LEN_QUALITY_10 :
|
||||||
MAX_ZOPFLI_LEN_QUALITY_11;
|
MAX_ZOPFLI_LEN_QUALITY_11;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Number of best candidates to evaluate to expand zopfli chain. */
|
/* Number of best candidates to evaluate to expand Zopfli chain. */
|
||||||
static BROTLI_INLINE size_t MaxZopfliCandidates(
|
static BROTLI_INLINE size_t MaxZopfliCandidates(
|
||||||
const BrotliEncoderParams* params) {
|
const BrotliEncoderParams* params) {
|
||||||
return params->quality <= 10 ? 1 : 5;
|
return params->quality <= 10 ? 1 : 5;
|
||||||
|
@ -63,10 +59,15 @@ static BROTLI_INLINE size_t MaxZopfliCandidates(
|
||||||
static BROTLI_INLINE void SanitizeParams(BrotliEncoderParams* params) {
|
static BROTLI_INLINE void SanitizeParams(BrotliEncoderParams* params) {
|
||||||
params->quality = BROTLI_MIN(int, BROTLI_MAX_QUALITY,
|
params->quality = BROTLI_MIN(int, BROTLI_MAX_QUALITY,
|
||||||
BROTLI_MAX(int, BROTLI_MIN_QUALITY, params->quality));
|
BROTLI_MAX(int, BROTLI_MIN_QUALITY, params->quality));
|
||||||
if (params->lgwin < kBrotliMinWindowBits) {
|
if (params->quality <= MAX_QUALITY_FOR_STATIC_ENTROPY_CODES) {
|
||||||
params->lgwin = kBrotliMinWindowBits;
|
params->large_window = BROTLI_FALSE;
|
||||||
} else if (params->lgwin > kBrotliMaxWindowBits) {
|
}
|
||||||
params->lgwin = kBrotliMaxWindowBits;
|
if (params->lgwin < BROTLI_MIN_WINDOW_BITS) {
|
||||||
|
params->lgwin = BROTLI_MIN_WINDOW_BITS;
|
||||||
|
} else {
|
||||||
|
int max_lgwin = params->large_window ? BROTLI_LARGE_MAX_WINDOW_BITS :
|
||||||
|
BROTLI_MAX_WINDOW_BITS;
|
||||||
|
if (params->lgwin > max_lgwin) params->lgwin = max_lgwin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +85,8 @@ static BROTLI_INLINE int ComputeLgBlock(const BrotliEncoderParams* params) {
|
||||||
lgblock = BROTLI_MIN(int, 18, params->lgwin);
|
lgblock = BROTLI_MIN(int, 18, params->lgwin);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lgblock = BROTLI_MIN(int, kBrotliMaxInputBlockBits,
|
lgblock = BROTLI_MIN(int, BROTLI_MAX_INPUT_BLOCK_BITS,
|
||||||
BROTLI_MAX(int, kBrotliMinInputBlockBits, lgblock));
|
BROTLI_MAX(int, BROTLI_MIN_INPUT_BLOCK_BITS, lgblock));
|
||||||
}
|
}
|
||||||
return lgblock;
|
return lgblock;
|
||||||
}
|
}
|
||||||
|
@ -94,14 +95,15 @@ static BROTLI_INLINE int ComputeLgBlock(const BrotliEncoderParams* params) {
|
||||||
Allocate at least lgwin + 1 bits for the ring buffer so that the newly
|
Allocate at least lgwin + 1 bits for the ring buffer so that the newly
|
||||||
added block fits there completely and we still get lgwin bits and at least
|
added block fits there completely and we still get lgwin bits and at least
|
||||||
read_block_size_bits + 1 bits because the copy tail length needs to be
|
read_block_size_bits + 1 bits because the copy tail length needs to be
|
||||||
smaller than ringbuffer size. */
|
smaller than ring-buffer size. */
|
||||||
static BROTLI_INLINE int ComputeRbBits(const BrotliEncoderParams* params) {
|
static BROTLI_INLINE int ComputeRbBits(const BrotliEncoderParams* params) {
|
||||||
return 1 + BROTLI_MAX(int, params->lgwin, params->lgblock);
|
return 1 + BROTLI_MAX(int, params->lgwin, params->lgblock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE size_t MaxMetablockSize(
|
static BROTLI_INLINE size_t MaxMetablockSize(
|
||||||
const BrotliEncoderParams* params) {
|
const BrotliEncoderParams* params) {
|
||||||
int bits = BROTLI_MIN(int, ComputeRbBits(params), kBrotliMaxInputBlockBits);
|
int bits =
|
||||||
|
BROTLI_MIN(int, ComputeRbBits(params), BROTLI_MAX_INPUT_BLOCK_BITS);
|
||||||
return (size_t)1 << bits;
|
return (size_t)1 << bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,15 +118,48 @@ static BROTLI_INLINE size_t LiteralSpreeLengthForSparseSearch(
|
||||||
return params->quality < 9 ? 64 : 512;
|
return params->quality < 9 ? 64 : 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE int ChooseHasher(const BrotliEncoderParams* params) {
|
static BROTLI_INLINE void ChooseHasher(const BrotliEncoderParams* params,
|
||||||
|
BrotliHasherParams* hparams) {
|
||||||
if (params->quality > 9) {
|
if (params->quality > 9) {
|
||||||
return 10;
|
hparams->type = 10;
|
||||||
|
} else if (params->quality == 4 && params->size_hint >= (1 << 20)) {
|
||||||
|
hparams->type = 54;
|
||||||
} else if (params->quality < 5) {
|
} else if (params->quality < 5) {
|
||||||
return params->quality;
|
hparams->type = params->quality;
|
||||||
} else if (params->lgwin <= 16) {
|
} else if (params->lgwin <= 16) {
|
||||||
return params->quality < 7 ? 40 : params->quality < 9 ? 41 : 42;
|
hparams->type = params->quality < 7 ? 40 : params->quality < 9 ? 41 : 42;
|
||||||
|
} else if (params->size_hint >= (1 << 20) && params->lgwin >= 19) {
|
||||||
|
hparams->type = 6;
|
||||||
|
hparams->block_bits = params->quality - 1;
|
||||||
|
hparams->bucket_bits = 15;
|
||||||
|
hparams->hash_len = 5;
|
||||||
|
hparams->num_last_distances_to_check =
|
||||||
|
params->quality < 7 ? 4 : params->quality < 9 ? 10 : 16;
|
||||||
|
} else {
|
||||||
|
hparams->type = 5;
|
||||||
|
hparams->block_bits = params->quality - 1;
|
||||||
|
hparams->bucket_bits = params->quality < 7 ? 14 : 15;
|
||||||
|
hparams->num_last_distances_to_check =
|
||||||
|
params->quality < 7 ? 4 : params->quality < 9 ? 10 : 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params->lgwin > 24) {
|
||||||
|
/* Different hashers for large window brotli: not for qualities <= 2,
|
||||||
|
these are too fast for large window. Not for qualities >= 10: their
|
||||||
|
hasher already works well with large window. So the changes are:
|
||||||
|
H3 --> H35: for quality 3.
|
||||||
|
H54 --> H55: for quality 4 with size hint > 1MB
|
||||||
|
H6 --> H65: for qualities 5, 6, 7, 8, 9. */
|
||||||
|
if (hparams->type == 3) {
|
||||||
|
hparams->type = 35;
|
||||||
|
}
|
||||||
|
if (hparams->type == 54) {
|
||||||
|
hparams->type = 55;
|
||||||
|
}
|
||||||
|
if (hparams->type == 6) {
|
||||||
|
hparams->type = 65;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return params->quality;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_QUALITY_H_ */
|
#endif /* BROTLI_ENC_QUALITY_H_ */
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
#include <string.h> /* memcpy */
|
#include <string.h> /* memcpy */
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
#include "./memory.h"
|
#include "./memory.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./quality.h"
|
#include "./quality.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
@ -30,7 +30,7 @@ extern "C" {
|
||||||
buffer_[-1] == buffer_[(1 << window_bits) - 1] and
|
buffer_[-1] == buffer_[(1 << window_bits) - 1] and
|
||||||
buffer_[-2] == buffer_[(1 << window_bits) - 2]. */
|
buffer_[-2] == buffer_[(1 << window_bits) - 2]. */
|
||||||
typedef struct RingBuffer {
|
typedef struct RingBuffer {
|
||||||
/* Size of the ringbuffer is (1 << window_bits) + tail_size_. */
|
/* Size of the ring-buffer is (1 << window_bits) + tail_size_. */
|
||||||
const uint32_t size_;
|
const uint32_t size_;
|
||||||
const uint32_t mask_;
|
const uint32_t mask_;
|
||||||
const uint32_t tail_size_;
|
const uint32_t tail_size_;
|
||||||
|
@ -41,9 +41,9 @@ typedef struct RingBuffer {
|
||||||
uint32_t pos_;
|
uint32_t pos_;
|
||||||
/* The actual ring buffer containing the copy of the last two bytes, the data,
|
/* The actual ring buffer containing the copy of the last two bytes, the data,
|
||||||
and the copy of the beginning as a tail. */
|
and the copy of the beginning as a tail. */
|
||||||
uint8_t *data_;
|
uint8_t* data_;
|
||||||
/* The start of the ringbuffer. */
|
/* The start of the ring-buffer. */
|
||||||
uint8_t *buffer_;
|
uint8_t* buffer_;
|
||||||
} RingBuffer;
|
} RingBuffer;
|
||||||
|
|
||||||
static BROTLI_INLINE void RingBufferInit(RingBuffer* rb) {
|
static BROTLI_INLINE void RingBufferInit(RingBuffer* rb) {
|
||||||
|
@ -91,9 +91,9 @@ static BROTLI_INLINE void RingBufferInitBuffer(
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void RingBufferWriteTail(
|
static BROTLI_INLINE void RingBufferWriteTail(
|
||||||
const uint8_t *bytes, size_t n, RingBuffer* rb) {
|
const uint8_t* bytes, size_t n, RingBuffer* rb) {
|
||||||
const size_t masked_pos = rb->pos_ & rb->mask_;
|
const size_t masked_pos = rb->pos_ & rb->mask_;
|
||||||
if (PREDICT_FALSE(masked_pos < rb->tail_size_)) {
|
if (BROTLI_PREDICT_FALSE(masked_pos < rb->tail_size_)) {
|
||||||
/* Just fill the tail buffer with the beginning data. */
|
/* Just fill the tail buffer with the beginning data. */
|
||||||
const size_t p = rb->size_ + masked_pos;
|
const size_t p = rb->size_ + masked_pos;
|
||||||
memcpy(&rb->buffer_[p], bytes,
|
memcpy(&rb->buffer_[p], bytes,
|
||||||
|
@ -103,10 +103,10 @@ static BROTLI_INLINE void RingBufferWriteTail(
|
||||||
|
|
||||||
/* Push bytes into the ring buffer. */
|
/* Push bytes into the ring buffer. */
|
||||||
static BROTLI_INLINE void RingBufferWrite(
|
static BROTLI_INLINE void RingBufferWrite(
|
||||||
MemoryManager* m, const uint8_t *bytes, size_t n, RingBuffer* rb) {
|
MemoryManager* m, const uint8_t* bytes, size_t n, RingBuffer* rb) {
|
||||||
if (rb->pos_ == 0 && n < rb->tail_size_) {
|
if (rb->pos_ == 0 && n < rb->tail_size_) {
|
||||||
/* Special case for the first write: to process the first block, we don't
|
/* Special case for the first write: to process the first block, we don't
|
||||||
need to allocate the whole ringbuffer and we don't need the tail
|
need to allocate the whole ring-buffer and we don't need the tail
|
||||||
either. However, we do this memory usage optimization only if the
|
either. However, we do this memory usage optimization only if the
|
||||||
first write is less than the tail size, which is also the input block
|
first write is less than the tail size, which is also the input block
|
||||||
size, otherwise it is likely that other blocks will follow and we
|
size, otherwise it is likely that other blocks will follow and we
|
||||||
|
@ -131,7 +131,7 @@ static BROTLI_INLINE void RingBufferWrite(
|
||||||
/* The length of the writes is limited so that we do not need to worry
|
/* The length of the writes is limited so that we do not need to worry
|
||||||
about a write */
|
about a write */
|
||||||
RingBufferWriteTail(bytes, n, rb);
|
RingBufferWriteTail(bytes, n, rb);
|
||||||
if (PREDICT_TRUE(masked_pos + n <= rb->size_)) {
|
if (BROTLI_PREDICT_TRUE(masked_pos + n <= rb->size_)) {
|
||||||
/* A single write fits. */
|
/* A single write fits. */
|
||||||
memcpy(&rb->buffer_[masked_pos], bytes, n);
|
memcpy(&rb->buffer_[masked_pos], bytes, n);
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,12 +144,16 @@ static BROTLI_INLINE void RingBufferWrite(
|
||||||
n - (rb->size_ - masked_pos));
|
n - (rb->size_ - masked_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rb->buffer_[-2] = rb->buffer_[rb->size_ - 2];
|
{
|
||||||
rb->buffer_[-1] = rb->buffer_[rb->size_ - 1];
|
BROTLI_BOOL not_first_lap = (rb->pos_ & (1u << 31)) != 0;
|
||||||
rb->pos_ += (uint32_t)n;
|
uint32_t rb_pos_mask = (1u << 31) - 1;
|
||||||
if (rb->pos_ > (1u << 30)) {
|
rb->buffer_[-2] = rb->buffer_[rb->size_ - 2];
|
||||||
/* Wrap, but preserve not-a-first-lap feature. */
|
rb->buffer_[-1] = rb->buffer_[rb->size_ - 1];
|
||||||
rb->pos_ = (rb->pos_ & ((1u << 30) - 1)) | (1u << 30);
|
rb->pos_ = (rb->pos_ & rb_pos_mask) + (uint32_t)(n & rb_pos_mask);
|
||||||
|
if (not_first_lap) {
|
||||||
|
/* Wrap, but preserve not-a-first-lap feature. */
|
||||||
|
rb->pos_ |= 1u << 31;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,17 @@
|
||||||
#include "./static_dict.h"
|
#include "./static_dict.h"
|
||||||
|
|
||||||
#include "../common/dictionary.h"
|
#include "../common/dictionary.h"
|
||||||
|
#include "../common/platform.h"
|
||||||
|
#include "../common/transform.h"
|
||||||
|
#include "./encoder_dict.h"
|
||||||
#include "./find_match_length.h"
|
#include "./find_match_length.h"
|
||||||
#include "./port.h"
|
|
||||||
#include "./static_dict_lut.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const uint8_t kUppercaseFirst = 10;
|
static BROTLI_INLINE uint32_t Hash(const uint8_t* data) {
|
||||||
static const uint8_t kOmitLastNTransforms[10] = {
|
uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kDictHashMul32;
|
||||||
0, 12, 27, 23, 42, 63, 56, 48, 59, 64,
|
|
||||||
};
|
|
||||||
|
|
||||||
static BROTLI_INLINE uint32_t Hash(const uint8_t *data) {
|
|
||||||
uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kDictHashMul32;
|
|
||||||
/* The higher bits contain more mixture from the multiplication,
|
/* The higher bits contain more mixture from the multiplication,
|
||||||
so we take our results from there. */
|
so we take our results from there. */
|
||||||
return h >> (32 - kDictNumBits);
|
return h >> (32 - kDictNumBits);
|
||||||
|
@ -33,23 +29,24 @@ static BROTLI_INLINE void AddMatch(size_t distance, size_t len, size_t len_code,
|
||||||
matches[len] = BROTLI_MIN(uint32_t, matches[len], match);
|
matches[len] = BROTLI_MIN(uint32_t, matches[len], match);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE size_t DictMatchLength(const uint8_t* data,
|
static BROTLI_INLINE size_t DictMatchLength(const BrotliDictionary* dictionary,
|
||||||
|
const uint8_t* data,
|
||||||
size_t id,
|
size_t id,
|
||||||
size_t len,
|
size_t len,
|
||||||
size_t maxlen) {
|
size_t maxlen) {
|
||||||
const size_t offset = kBrotliDictionaryOffsetsByLength[len] + len * id;
|
const size_t offset = dictionary->offsets_by_length[len] + len * id;
|
||||||
return FindMatchLengthWithLimit(&kBrotliDictionary[offset], data,
|
return FindMatchLengthWithLimit(&dictionary->data[offset], data,
|
||||||
BROTLI_MIN(size_t, len, maxlen));
|
BROTLI_MIN(size_t, len, maxlen));
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE BROTLI_BOOL IsMatch(
|
static BROTLI_INLINE BROTLI_BOOL IsMatch(const BrotliDictionary* dictionary,
|
||||||
DictWord w, const uint8_t* data, size_t max_length) {
|
DictWord w, const uint8_t* data, size_t max_length) {
|
||||||
if (w.len > max_length) {
|
if (w.len > max_length) {
|
||||||
return BROTLI_FALSE;
|
return BROTLI_FALSE;
|
||||||
} else {
|
} else {
|
||||||
const size_t offset = kBrotliDictionaryOffsetsByLength[w.len] +
|
const size_t offset = dictionary->offsets_by_length[w.len] +
|
||||||
(size_t)w.len * (size_t)w.idx;
|
(size_t)w.len * (size_t)w.idx;
|
||||||
const uint8_t* dict = &kBrotliDictionary[offset];
|
const uint8_t* dict = &dictionary->data[offset];
|
||||||
if (w.transform == 0) {
|
if (w.transform == 0) {
|
||||||
/* Match against base dictionary word. */
|
/* Match against base dictionary word. */
|
||||||
return
|
return
|
||||||
|
@ -78,31 +75,33 @@ static BROTLI_INLINE BROTLI_BOOL IsMatch(
|
||||||
}
|
}
|
||||||
|
|
||||||
BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
const uint8_t* data, size_t min_length, size_t max_length,
|
const BrotliEncoderDictionary* dictionary, const uint8_t* data,
|
||||||
uint32_t* matches) {
|
size_t min_length, size_t max_length, uint32_t* matches) {
|
||||||
BROTLI_BOOL has_found_match = BROTLI_FALSE;
|
BROTLI_BOOL has_found_match = BROTLI_FALSE;
|
||||||
{
|
{
|
||||||
size_t offset = kStaticDictionaryBuckets[Hash(data)];
|
size_t offset = dictionary->buckets[Hash(data)];
|
||||||
BROTLI_BOOL end = !offset;
|
BROTLI_BOOL end = !offset;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
DictWord w = kStaticDictionaryWords[offset++];
|
DictWord w = dictionary->dict_words[offset++];
|
||||||
const size_t l = w.len & 0x7F;
|
const size_t l = w.len & 0x1F;
|
||||||
const size_t n = (size_t)1 << kBrotliDictionarySizeBitsByLength[l];
|
const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l];
|
||||||
const size_t id = w.idx;
|
const size_t id = w.idx;
|
||||||
end = !!(w.len & 0x80);
|
end = !!(w.len & 0x80);
|
||||||
w.len = (uint8_t)l;
|
w.len = (uint8_t)l;
|
||||||
if (w.transform == 0) {
|
if (w.transform == 0) {
|
||||||
const size_t matchlen = DictMatchLength(data, id, l, max_length);
|
const size_t matchlen =
|
||||||
|
DictMatchLength(dictionary->words, data, id, l, max_length);
|
||||||
const uint8_t* s;
|
const uint8_t* s;
|
||||||
size_t minlen;
|
size_t minlen;
|
||||||
size_t maxlen;
|
size_t maxlen;
|
||||||
size_t len;
|
size_t len;
|
||||||
/* Transform "" + kIdentity + "" */
|
/* Transform "" + BROTLI_TRANSFORM_IDENTITY + "" */
|
||||||
if (matchlen == l) {
|
if (matchlen == l) {
|
||||||
AddMatch(id, l, l, matches);
|
AddMatch(id, l, l, matches);
|
||||||
has_found_match = BROTLI_TRUE;
|
has_found_match = BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
/* Transforms "" + kOmitLast1 + "" and "" + kOmitLast1 + "ing " */
|
/* Transforms "" + BROTLI_TRANSFORM_OMIT_LAST_1 + "" and
|
||||||
|
"" + BROTLI_TRANSFORM_OMIT_LAST_1 + "ing " */
|
||||||
if (matchlen >= l - 1) {
|
if (matchlen >= l - 1) {
|
||||||
AddMatch(id + 12 * n, l - 1, l, matches);
|
AddMatch(id + 12 * n, l - 1, l, matches);
|
||||||
if (l + 2 < max_length &&
|
if (l + 2 < max_length &&
|
||||||
|
@ -112,19 +111,22 @@ BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
}
|
}
|
||||||
has_found_match = BROTLI_TRUE;
|
has_found_match = BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
/* Transform "" + kOmitLastN + "" (N = 2 .. 9) */
|
/* Transform "" + BROTLI_TRANSFORM_OMIT_LAST_# + "" (# = 2 .. 9) */
|
||||||
minlen = min_length;
|
minlen = min_length;
|
||||||
if (l > 9) minlen = BROTLI_MAX(size_t, minlen, l - 9);
|
if (l > 9) minlen = BROTLI_MAX(size_t, minlen, l - 9);
|
||||||
maxlen = BROTLI_MIN(size_t, matchlen, l - 2);
|
maxlen = BROTLI_MIN(size_t, matchlen, l - 2);
|
||||||
for (len = minlen; len <= maxlen; ++len) {
|
for (len = minlen; len <= maxlen; ++len) {
|
||||||
AddMatch(id + kOmitLastNTransforms[l - len] * n, len, l, matches);
|
size_t cut = l - len;
|
||||||
|
size_t transform_id = (cut << 2) +
|
||||||
|
(size_t)((dictionary->cutoffTransforms >> (cut * 6)) & 0x3F);
|
||||||
|
AddMatch(id + transform_id * n, len, l, matches);
|
||||||
has_found_match = BROTLI_TRUE;
|
has_found_match = BROTLI_TRUE;
|
||||||
}
|
}
|
||||||
if (matchlen < l || l + 6 >= max_length) {
|
if (matchlen < l || l + 6 >= max_length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
s = &data[l];
|
s = &data[l];
|
||||||
/* Transforms "" + kIdentity + <suffix> */
|
/* Transforms "" + BROTLI_TRANSFORM_IDENTITY + <suffix> */
|
||||||
if (s[0] == ' ') {
|
if (s[0] == ' ') {
|
||||||
AddMatch(id + n, l + 1, l, matches);
|
AddMatch(id + n, l + 1, l, matches);
|
||||||
if (s[1] == 'a') {
|
if (s[1] == 'a') {
|
||||||
|
@ -271,12 +273,13 @@ BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Set is_all_caps=0 for kUppercaseFirst and
|
/* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and
|
||||||
is_all_caps=1 otherwise (kUppercaseAll) transform. */
|
is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL)
|
||||||
|
transform. */
|
||||||
const BROTLI_BOOL is_all_caps =
|
const BROTLI_BOOL is_all_caps =
|
||||||
TO_BROTLI_BOOL(w.transform != kUppercaseFirst);
|
TO_BROTLI_BOOL(w.transform != BROTLI_TRANSFORM_UPPERCASE_FIRST);
|
||||||
const uint8_t* s;
|
const uint8_t* s;
|
||||||
if (!IsMatch(w, data, max_length)) {
|
if (!IsMatch(dictionary->words, w, data, max_length)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Transform "" + kUppercase{First,All} + "" */
|
/* Transform "" + kUppercase{First,All} + "" */
|
||||||
|
@ -321,27 +324,29 @@ BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
/* Transforms with prefixes " " and "." */
|
/* Transforms with prefixes " " and "." */
|
||||||
if (max_length >= 5 && (data[0] == ' ' || data[0] == '.')) {
|
if (max_length >= 5 && (data[0] == ' ' || data[0] == '.')) {
|
||||||
BROTLI_BOOL is_space = TO_BROTLI_BOOL(data[0] == ' ');
|
BROTLI_BOOL is_space = TO_BROTLI_BOOL(data[0] == ' ');
|
||||||
size_t offset = kStaticDictionaryBuckets[Hash(&data[1])];
|
size_t offset = dictionary->buckets[Hash(&data[1])];
|
||||||
BROTLI_BOOL end = !offset;
|
BROTLI_BOOL end = !offset;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
DictWord w = kStaticDictionaryWords[offset++];
|
DictWord w = dictionary->dict_words[offset++];
|
||||||
const size_t l = w.len & 0x7F;
|
const size_t l = w.len & 0x1F;
|
||||||
const size_t n = (size_t)1 << kBrotliDictionarySizeBitsByLength[l];
|
const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l];
|
||||||
const size_t id = w.idx;
|
const size_t id = w.idx;
|
||||||
end = !!(w.len & 0x80);
|
end = !!(w.len & 0x80);
|
||||||
w.len = (uint8_t)l;
|
w.len = (uint8_t)l;
|
||||||
if (w.transform == 0) {
|
if (w.transform == 0) {
|
||||||
const uint8_t* s;
|
const uint8_t* s;
|
||||||
if (!IsMatch(w, &data[1], max_length - 1)) {
|
if (!IsMatch(dictionary->words, w, &data[1], max_length - 1)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Transforms " " + kIdentity + "" and "." + kIdentity + "" */
|
/* Transforms " " + BROTLI_TRANSFORM_IDENTITY + "" and
|
||||||
|
"." + BROTLI_TRANSFORM_IDENTITY + "" */
|
||||||
AddMatch(id + (is_space ? 6 : 32) * n, l + 1, l, matches);
|
AddMatch(id + (is_space ? 6 : 32) * n, l + 1, l, matches);
|
||||||
has_found_match = BROTLI_TRUE;
|
has_found_match = BROTLI_TRUE;
|
||||||
if (l + 2 >= max_length) {
|
if (l + 2 >= max_length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Transforms " " + kIdentity + <suffix> and "." + kIdentity + <suffix>
|
/* Transforms " " + BROTLI_TRANSFORM_IDENTITY + <suffix> and
|
||||||
|
"." + BROTLI_TRANSFORM_IDENTITY + <suffix>
|
||||||
*/
|
*/
|
||||||
s = &data[l + 1];
|
s = &data[l + 1];
|
||||||
if (s[0] == ' ') {
|
if (s[0] == ' ') {
|
||||||
|
@ -368,12 +373,13 @@ BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (is_space) {
|
} else if (is_space) {
|
||||||
/* Set is_all_caps=0 for kUppercaseFirst and
|
/* Set is_all_caps=0 for BROTLI_TRANSFORM_UPPERCASE_FIRST and
|
||||||
is_all_caps=1 otherwise (kUppercaseAll) transform. */
|
is_all_caps=1 otherwise (BROTLI_TRANSFORM_UPPERCASE_ALL)
|
||||||
|
transform. */
|
||||||
const BROTLI_BOOL is_all_caps =
|
const BROTLI_BOOL is_all_caps =
|
||||||
TO_BROTLI_BOOL(w.transform != kUppercaseFirst);
|
TO_BROTLI_BOOL(w.transform != BROTLI_TRANSFORM_UPPERCASE_FIRST);
|
||||||
const uint8_t* s;
|
const uint8_t* s;
|
||||||
if (!IsMatch(w, &data[1], max_length - 1)) {
|
if (!IsMatch(dictionary->words, w, &data[1], max_length - 1)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/* Transforms " " + kUppercase{First,All} + "" */
|
/* Transforms " " + kUppercase{First,All} + "" */
|
||||||
|
@ -409,21 +415,22 @@ BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (max_length >= 6) {
|
if (max_length >= 6) {
|
||||||
/* Transforms with prefixes "e ", "s ", ", " and "\xc2\xa0" */
|
/* Transforms with prefixes "e ", "s ", ", " and "\xC2\xA0" */
|
||||||
if ((data[1] == ' ' &&
|
if ((data[1] == ' ' &&
|
||||||
(data[0] == 'e' || data[0] == 's' || data[0] == ',')) ||
|
(data[0] == 'e' || data[0] == 's' || data[0] == ',')) ||
|
||||||
(data[0] == 0xc2 && data[1] == 0xa0)) {
|
(data[0] == 0xC2 && data[1] == 0xA0)) {
|
||||||
size_t offset = kStaticDictionaryBuckets[Hash(&data[2])];
|
size_t offset = dictionary->buckets[Hash(&data[2])];
|
||||||
BROTLI_BOOL end = !offset;
|
BROTLI_BOOL end = !offset;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
DictWord w = kStaticDictionaryWords[offset++];
|
DictWord w = dictionary->dict_words[offset++];
|
||||||
const size_t l = w.len & 0x7F;
|
const size_t l = w.len & 0x1F;
|
||||||
const size_t n = (size_t)1 << kBrotliDictionarySizeBitsByLength[l];
|
const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l];
|
||||||
const size_t id = w.idx;
|
const size_t id = w.idx;
|
||||||
end = !!(w.len & 0x80);
|
end = !!(w.len & 0x80);
|
||||||
w.len = (uint8_t)l;
|
w.len = (uint8_t)l;
|
||||||
if (w.transform == 0 && IsMatch(w, &data[2], max_length - 2)) {
|
if (w.transform == 0 &&
|
||||||
if (data[0] == 0xc2) {
|
IsMatch(dictionary->words, w, &data[2], max_length - 2)) {
|
||||||
|
if (data[0] == 0xC2) {
|
||||||
AddMatch(id + 102 * n, l + 2, l, matches);
|
AddMatch(id + 102 * n, l + 2, l, matches);
|
||||||
has_found_match = BROTLI_TRUE;
|
has_found_match = BROTLI_TRUE;
|
||||||
} else if (l + 2 < max_length && data[l + 2] == ' ') {
|
} else if (l + 2 < max_length && data[l + 2] == ' ') {
|
||||||
|
@ -441,16 +448,17 @@ BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
data[3] == 'e' && data[4] == ' ') ||
|
data[3] == 'e' && data[4] == ' ') ||
|
||||||
(data[0] == '.' && data[1] == 'c' && data[2] == 'o' &&
|
(data[0] == '.' && data[1] == 'c' && data[2] == 'o' &&
|
||||||
data[3] == 'm' && data[4] == '/')) {
|
data[3] == 'm' && data[4] == '/')) {
|
||||||
size_t offset = kStaticDictionaryBuckets[Hash(&data[5])];
|
size_t offset = dictionary->buckets[Hash(&data[5])];
|
||||||
BROTLI_BOOL end = !offset;
|
BROTLI_BOOL end = !offset;
|
||||||
while (!end) {
|
while (!end) {
|
||||||
DictWord w = kStaticDictionaryWords[offset++];
|
DictWord w = dictionary->dict_words[offset++];
|
||||||
const size_t l = w.len & 0x7F;
|
const size_t l = w.len & 0x1F;
|
||||||
const size_t n = (size_t)1 << kBrotliDictionarySizeBitsByLength[l];
|
const size_t n = (size_t)1 << dictionary->words->size_bits_by_length[l];
|
||||||
const size_t id = w.idx;
|
const size_t id = w.idx;
|
||||||
end = !!(w.len & 0x80);
|
end = !!(w.len & 0x80);
|
||||||
w.len = (uint8_t)l;
|
w.len = (uint8_t)l;
|
||||||
if (w.transform == 0 && IsMatch(w, &data[5], max_length - 5)) {
|
if (w.transform == 0 &&
|
||||||
|
IsMatch(dictionary->words, w, &data[5], max_length - 5)) {
|
||||||
AddMatch(id + (data[0] == ' ' ? 41 : 72) * n, l + 5, l, matches);
|
AddMatch(id + (data[0] == ' ' ? 41 : 72) * n, l + 5, l, matches);
|
||||||
has_found_match = BROTLI_TRUE;
|
has_found_match = BROTLI_TRUE;
|
||||||
if (l + 5 < max_length) {
|
if (l + 5 < max_length) {
|
||||||
|
|
|
@ -9,15 +9,17 @@
|
||||||
#ifndef BROTLI_ENC_STATIC_DICT_H_
|
#ifndef BROTLI_ENC_STATIC_DICT_H_
|
||||||
#define BROTLI_ENC_STATIC_DICT_H_
|
#define BROTLI_ENC_STATIC_DICT_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/dictionary.h"
|
||||||
#include "./port.h"
|
#include "../common/platform.h"
|
||||||
|
#include <brotli/types.h>
|
||||||
|
#include "./encoder_dict.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN 37
|
#define BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN 37
|
||||||
static const uint32_t kInvalidMatch = 0xfffffff;
|
static const uint32_t kInvalidMatch = 0xFFFFFFF;
|
||||||
|
|
||||||
/* Matches data against static dictionary words, and for each length l,
|
/* Matches data against static dictionary words, and for each length l,
|
||||||
for which a match is found, updates matches[l] to be the minimum possible
|
for which a match is found, updates matches[l] to be the minimum possible
|
||||||
|
@ -27,6 +29,7 @@ static const uint32_t kInvalidMatch = 0xfffffff;
|
||||||
matches array is at least BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1 long
|
matches array is at least BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1 long
|
||||||
all elements are initialized to kInvalidMatch */
|
all elements are initialized to kInvalidMatch */
|
||||||
BROTLI_INTERNAL BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
BROTLI_INTERNAL BROTLI_BOOL BrotliFindAllStaticDictionaryMatches(
|
||||||
|
const BrotliEncoderDictionary* dictionary,
|
||||||
const uint8_t* data, size_t min_length, size_t max_length,
|
const uint8_t* data, size_t min_length, size_t max_length,
|
||||||
uint32_t* matches);
|
uint32_t* matches);
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,121 +0,0 @@
|
||||||
/* Copyright 2009 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Input and output classes for streaming brotli compression. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_ENC_STREAMS_H_
|
|
||||||
#define BROTLI_ENC_STREAMS_H_
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
|
|
||||||
namespace brotli {
|
|
||||||
|
|
||||||
/* Input interface for the compression routines. */
|
|
||||||
class BrotliIn {
|
|
||||||
public:
|
|
||||||
virtual ~BrotliIn(void) {}
|
|
||||||
|
|
||||||
/* Return a pointer to the next block of input of at most n bytes.
|
|
||||||
Return the actual length in *nread.
|
|
||||||
At end of data, return NULL. Don't return NULL if there is more data
|
|
||||||
to read, even if called with n == 0.
|
|
||||||
Read will only be called if some of its bytes are needed. */
|
|
||||||
virtual const void* Read(size_t n, size_t* nread) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Output interface for the compression routines. */
|
|
||||||
class BrotliOut {
|
|
||||||
public:
|
|
||||||
virtual ~BrotliOut(void) {}
|
|
||||||
|
|
||||||
/* Write n bytes of data from buf.
|
|
||||||
Return true if all written, false otherwise. */
|
|
||||||
virtual bool Write(const void *buf, size_t n) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Adapter class to make BrotliIn objects from raw memory. */
|
|
||||||
class BrotliMemIn : public BrotliIn {
|
|
||||||
public:
|
|
||||||
BrotliMemIn(const void* buf, size_t len);
|
|
||||||
|
|
||||||
void Reset(const void* buf, size_t len);
|
|
||||||
|
|
||||||
/* returns the amount of data consumed */
|
|
||||||
size_t position(void) const { return pos_; }
|
|
||||||
|
|
||||||
const void* Read(size_t n, size_t* OUTPUT);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const void* buf_; /* start of input buffer */
|
|
||||||
size_t len_; /* length of input */
|
|
||||||
size_t pos_; /* current read position within input */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Adapter class to make BrotliOut objects from raw memory. */
|
|
||||||
class BrotliMemOut : public BrotliOut {
|
|
||||||
public:
|
|
||||||
BrotliMemOut(void* buf, size_t len);
|
|
||||||
|
|
||||||
void Reset(void* buf, size_t len);
|
|
||||||
|
|
||||||
/* returns the amount of data written */
|
|
||||||
size_t position(void) const { return pos_; }
|
|
||||||
|
|
||||||
bool Write(const void* buf, size_t n);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void* buf_; /* start of output buffer */
|
|
||||||
size_t len_; /* length of output */
|
|
||||||
size_t pos_; /* current write position within output */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Adapter class to make BrotliOut objects from a string. */
|
|
||||||
class BrotliStringOut : public BrotliOut {
|
|
||||||
public:
|
|
||||||
/* Create a writer that appends its data to buf.
|
|
||||||
buf->size() will grow to at most max_size
|
|
||||||
buf is expected to be empty when constructing BrotliStringOut. */
|
|
||||||
BrotliStringOut(std::string* buf, size_t max_size);
|
|
||||||
|
|
||||||
void Reset(std::string* buf, size_t max_len);
|
|
||||||
|
|
||||||
bool Write(const void* buf, size_t n);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string* buf_; /* start of output buffer */
|
|
||||||
size_t max_size_; /* max length of output */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Adapter class to make BrotliIn object from a file. */
|
|
||||||
class BrotliFileIn : public BrotliIn {
|
|
||||||
public:
|
|
||||||
BrotliFileIn(FILE* f, size_t max_read_size);
|
|
||||||
~BrotliFileIn(void);
|
|
||||||
|
|
||||||
const void* Read(size_t n, size_t* bytes_read);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE* f_;
|
|
||||||
char* buf_;
|
|
||||||
size_t buf_size_;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Adapter class to make BrotliOut object from a file. */
|
|
||||||
class BrotliFileOut : public BrotliOut {
|
|
||||||
public:
|
|
||||||
explicit BrotliFileOut(FILE* f);
|
|
||||||
|
|
||||||
bool Write(const void* buf, size_t n);
|
|
||||||
private:
|
|
||||||
FILE* f_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} /* namespace brotli */
|
|
||||||
|
|
||||||
#endif /* BROTLI_ENC_STREAMS_H_ */
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include "./utf8_util.h"
|
#include "./utf8_util.h"
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -25,37 +25,37 @@ static size_t BrotliParseAsUTF8(
|
||||||
}
|
}
|
||||||
/* 2-byte UTF8 */
|
/* 2-byte UTF8 */
|
||||||
if (size > 1u &&
|
if (size > 1u &&
|
||||||
(input[0] & 0xe0) == 0xc0 &&
|
(input[0] & 0xE0) == 0xC0 &&
|
||||||
(input[1] & 0xc0) == 0x80) {
|
(input[1] & 0xC0) == 0x80) {
|
||||||
*symbol = (((input[0] & 0x1f) << 6) |
|
*symbol = (((input[0] & 0x1F) << 6) |
|
||||||
(input[1] & 0x3f));
|
(input[1] & 0x3F));
|
||||||
if (*symbol > 0x7f) {
|
if (*symbol > 0x7F) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 3-byte UFT8 */
|
/* 3-byte UFT8 */
|
||||||
if (size > 2u &&
|
if (size > 2u &&
|
||||||
(input[0] & 0xf0) == 0xe0 &&
|
(input[0] & 0xF0) == 0xE0 &&
|
||||||
(input[1] & 0xc0) == 0x80 &&
|
(input[1] & 0xC0) == 0x80 &&
|
||||||
(input[2] & 0xc0) == 0x80) {
|
(input[2] & 0xC0) == 0x80) {
|
||||||
*symbol = (((input[0] & 0x0f) << 12) |
|
*symbol = (((input[0] & 0x0F) << 12) |
|
||||||
((input[1] & 0x3f) << 6) |
|
((input[1] & 0x3F) << 6) |
|
||||||
(input[2] & 0x3f));
|
(input[2] & 0x3F));
|
||||||
if (*symbol > 0x7ff) {
|
if (*symbol > 0x7FF) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 4-byte UFT8 */
|
/* 4-byte UFT8 */
|
||||||
if (size > 3u &&
|
if (size > 3u &&
|
||||||
(input[0] & 0xf8) == 0xf0 &&
|
(input[0] & 0xF8) == 0xF0 &&
|
||||||
(input[1] & 0xc0) == 0x80 &&
|
(input[1] & 0xC0) == 0x80 &&
|
||||||
(input[2] & 0xc0) == 0x80 &&
|
(input[2] & 0xC0) == 0x80 &&
|
||||||
(input[3] & 0xc0) == 0x80) {
|
(input[3] & 0xC0) == 0x80) {
|
||||||
*symbol = (((input[0] & 0x07) << 18) |
|
*symbol = (((input[0] & 0x07) << 18) |
|
||||||
((input[1] & 0x3f) << 12) |
|
((input[1] & 0x3F) << 12) |
|
||||||
((input[2] & 0x3f) << 6) |
|
((input[2] & 0x3F) << 6) |
|
||||||
(input[3] & 0x3f));
|
(input[3] & 0x3F));
|
||||||
if (*symbol > 0xffff && *symbol <= 0x10ffff) {
|
if (*symbol > 0xFFFF && *symbol <= 0x10FFFF) {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef BROTLI_ENC_UTF8_UTIL_H_
|
#ifndef BROTLI_ENC_UTF8_UTIL_H_
|
||||||
#define BROTLI_ENC_UTF8_UTIL_H_
|
#define BROTLI_ENC_UTF8_UTIL_H_
|
||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/platform.h"
|
||||||
#include "./port.h"
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -19,7 +19,7 @@ extern "C" {
|
||||||
static const double kMinUTF8Ratio = 0.75;
|
static const double kMinUTF8Ratio = 0.75;
|
||||||
|
|
||||||
/* Returns 1 if at least min_fraction of the bytes between pos and
|
/* Returns 1 if at least min_fraction of the bytes between pos and
|
||||||
pos + length in the (data, mask) ringbuffer is UTF8-encoded, otherwise
|
pos + length in the (data, mask) ring-buffer is UTF8-encoded, otherwise
|
||||||
returns 0. */
|
returns 0. */
|
||||||
BROTLI_INTERNAL BROTLI_BOOL BrotliIsMostlyUTF8(
|
BROTLI_INTERNAL BROTLI_BOOL BrotliIsMostlyUTF8(
|
||||||
const uint8_t* data, const size_t pos, const size_t mask,
|
const uint8_t* data, const size_t pos, const size_t mask,
|
||||||
|
|
|
@ -9,11 +9,8 @@
|
||||||
#ifndef BROTLI_ENC_WRITE_BITS_H_
|
#ifndef BROTLI_ENC_WRITE_BITS_H_
|
||||||
#define BROTLI_ENC_WRITE_BITS_H_
|
#define BROTLI_ENC_WRITE_BITS_H_
|
||||||
|
|
||||||
#include <assert.h>
|
#include "../common/platform.h"
|
||||||
#include <stdio.h> /* printf */
|
#include <brotli/types.h>
|
||||||
|
|
||||||
#include "../common/types.h"
|
|
||||||
#include "./port.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -38,27 +35,27 @@ extern "C" {
|
||||||
and locate the rest in BYTE+1, BYTE+2, etc. */
|
and locate the rest in BYTE+1, BYTE+2, etc. */
|
||||||
static BROTLI_INLINE void BrotliWriteBits(size_t n_bits,
|
static BROTLI_INLINE void BrotliWriteBits(size_t n_bits,
|
||||||
uint64_t bits,
|
uint64_t bits,
|
||||||
size_t * BROTLI_RESTRICT pos,
|
size_t* BROTLI_RESTRICT pos,
|
||||||
uint8_t * BROTLI_RESTRICT array) {
|
uint8_t* BROTLI_RESTRICT array) {
|
||||||
#ifdef IS_LITTLE_ENDIAN
|
#if defined(BROTLI_LITTLE_ENDIAN)
|
||||||
/* This branch of the code can write up to 56 bits at a time,
|
/* This branch of the code can write up to 56 bits at a time,
|
||||||
7 bits are lost by being perhaps already in *p and at least
|
7 bits are lost by being perhaps already in *p and at least
|
||||||
1 bit is needed to initialize the bit-stream ahead (i.e. if 7
|
1 bit is needed to initialize the bit-stream ahead (i.e. if 7
|
||||||
bits are in *p and we write 57 bits, then the next write will
|
bits are in *p and we write 57 bits, then the next write will
|
||||||
access a byte that was never initialized). */
|
access a byte that was never initialized). */
|
||||||
uint8_t *p = &array[*pos >> 3];
|
uint8_t* p = &array[*pos >> 3];
|
||||||
uint64_t v = *p;
|
uint64_t v = (uint64_t)(*p); /* Zero-extend 8 to 64 bits. */
|
||||||
#ifdef BIT_WRITER_DEBUG
|
BROTLI_LOG(("WriteBits %2d 0x%08x%08x %10d\n", (int)n_bits,
|
||||||
printf("WriteBits %2d 0x%016llx %10d\n", n_bits, bits, *pos);
|
(uint32_t)(bits >> 32), (uint32_t)(bits & 0xFFFFFFFF),
|
||||||
#endif
|
(int)*pos));
|
||||||
assert((bits >> n_bits) == 0);
|
BROTLI_DCHECK((bits >> n_bits) == 0);
|
||||||
assert(n_bits <= 56);
|
BROTLI_DCHECK(n_bits <= 56);
|
||||||
v |= bits << (*pos & 7);
|
v |= bits << (*pos & 7);
|
||||||
BROTLI_UNALIGNED_STORE64(p, v); /* Set some bits. */
|
BROTLI_UNALIGNED_STORE64LE(p, v); /* Set some bits. */
|
||||||
*pos += n_bits;
|
*pos += n_bits;
|
||||||
#else
|
#else
|
||||||
/* implicit & 0xff is assumed for uint8_t arithmetics */
|
/* implicit & 0xFF is assumed for uint8_t arithmetics */
|
||||||
uint8_t *array_pos = &array[*pos >> 3];
|
uint8_t* array_pos = &array[*pos >> 3];
|
||||||
const size_t bits_reserved_in_first_byte = (*pos & 7);
|
const size_t bits_reserved_in_first_byte = (*pos & 7);
|
||||||
size_t bits_left_to_write;
|
size_t bits_left_to_write;
|
||||||
bits <<= bits_reserved_in_first_byte;
|
bits <<= bits_reserved_in_first_byte;
|
||||||
|
@ -75,11 +72,9 @@ static BROTLI_INLINE void BrotliWriteBits(size_t n_bits,
|
||||||
}
|
}
|
||||||
|
|
||||||
static BROTLI_INLINE void BrotliWriteBitsPrepareStorage(
|
static BROTLI_INLINE void BrotliWriteBitsPrepareStorage(
|
||||||
size_t pos, uint8_t *array) {
|
size_t pos, uint8_t* array) {
|
||||||
#ifdef BIT_WRITER_DEBUG
|
BROTLI_LOG(("WriteBitsPrepareStorage %10d\n", (int)pos));
|
||||||
printf("WriteBitsPrepareStorage %10d\n", pos);
|
BROTLI_DCHECK((pos & 7) == 0);
|
||||||
#endif
|
|
||||||
assert((pos & 7) == 0);
|
|
||||||
array[pos >> 3] = 0;
|
array[pos >> 3] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,344 @@
|
||||||
|
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* API for Brotli decompression.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROTLI_DEC_DECODE_H_
|
||||||
|
#define BROTLI_DEC_DECODE_H_
|
||||||
|
|
||||||
|
#include <brotli/port.h>
|
||||||
|
#include <brotli/types.h>
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque structure that holds decoder state.
|
||||||
|
*
|
||||||
|
* Allocated and initialized with ::BrotliDecoderCreateInstance.
|
||||||
|
* Cleaned up and deallocated with ::BrotliDecoderDestroyInstance.
|
||||||
|
*/
|
||||||
|
typedef struct BrotliDecoderStateStruct BrotliDecoderState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result type for ::BrotliDecoderDecompress and
|
||||||
|
* ::BrotliDecoderDecompressStream functions.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/** Decoding error, e.g. corrupted input or memory allocation problem. */
|
||||||
|
BROTLI_DECODER_RESULT_ERROR = 0,
|
||||||
|
/** Decoding successfully completed. */
|
||||||
|
BROTLI_DECODER_RESULT_SUCCESS = 1,
|
||||||
|
/** Partially done; should be called again with more input. */
|
||||||
|
BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2,
|
||||||
|
/** Partially done; should be called again with more output. */
|
||||||
|
BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3
|
||||||
|
} BrotliDecoderResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template that evaluates items of ::BrotliDecoderErrorCode.
|
||||||
|
*
|
||||||
|
* Example: @code {.cpp}
|
||||||
|
* // Log Brotli error code.
|
||||||
|
* switch (brotliDecoderErrorCode) {
|
||||||
|
* #define CASE_(PREFIX, NAME, CODE) \
|
||||||
|
* case BROTLI_DECODER ## PREFIX ## NAME: \
|
||||||
|
* LOG(INFO) << "error code:" << #NAME; \
|
||||||
|
* break;
|
||||||
|
* #define NEWLINE_
|
||||||
|
* BROTLI_DECODER_ERROR_CODES_LIST(CASE_, NEWLINE_)
|
||||||
|
* #undef CASE_
|
||||||
|
* #undef NEWLINE_
|
||||||
|
* default: LOG(FATAL) << "unknown brotli error code";
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \
|
||||||
|
BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \
|
||||||
|
/* Same as BrotliDecoderResult values */ \
|
||||||
|
BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \
|
||||||
|
\
|
||||||
|
/* Errors caused by invalid input */ \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_FORMAT_, DISTANCE, -16) SEPARATOR \
|
||||||
|
\
|
||||||
|
/* -17..-18 codes are reserved */ \
|
||||||
|
\
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR \
|
||||||
|
\
|
||||||
|
/* Memory allocation problems */ \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \
|
||||||
|
/* Literal, insert and distance trees together */ \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \
|
||||||
|
/* -23..-24 codes are reserved for distinct tree groups */ \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \
|
||||||
|
/* -28..-29 codes are reserved for dynamic ring-buffer allocation */ \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \
|
||||||
|
\
|
||||||
|
/* "Impossible" states */ \
|
||||||
|
BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error code for detailed logging / production debugging.
|
||||||
|
*
|
||||||
|
* See ::BrotliDecoderGetErrorCode and ::BROTLI_LAST_ERROR_CODE.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
#define BROTLI_COMMA_ ,
|
||||||
|
#define BROTLI_ERROR_CODE_ENUM_ITEM_(PREFIX, NAME, CODE) \
|
||||||
|
BROTLI_DECODER ## PREFIX ## NAME = CODE
|
||||||
|
BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_ENUM_ITEM_, BROTLI_COMMA_)
|
||||||
|
} BrotliDecoderErrorCode;
|
||||||
|
#undef BROTLI_ERROR_CODE_ENUM_ITEM_
|
||||||
|
#undef BROTLI_COMMA_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of the last error code, negative integer.
|
||||||
|
*
|
||||||
|
* All other error code values are in the range from ::BROTLI_LAST_ERROR_CODE
|
||||||
|
* to @c -1. There are also 4 other possible non-error codes @c 0 .. @c 3 in
|
||||||
|
* ::BrotliDecoderErrorCode enumeration.
|
||||||
|
*/
|
||||||
|
#define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE
|
||||||
|
|
||||||
|
/** Options to be used with ::BrotliDecoderSetParameter. */
|
||||||
|
typedef enum BrotliDecoderParameter {
|
||||||
|
/**
|
||||||
|
* Disable "canny" ring buffer allocation strategy.
|
||||||
|
*
|
||||||
|
* Ring buffer is allocated according to window size, despite the real size of
|
||||||
|
* the content.
|
||||||
|
*/
|
||||||
|
BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION = 0,
|
||||||
|
/**
|
||||||
|
* Flag that determines if "Large Window Brotli" is used.
|
||||||
|
*/
|
||||||
|
BROTLI_DECODER_PARAM_LARGE_WINDOW = 1
|
||||||
|
} BrotliDecoderParameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the specified parameter to the given decoder instance.
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @param param parameter to set
|
||||||
|
* @param value new parameter value
|
||||||
|
* @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
|
||||||
|
* @returns ::BROTLI_TRUE if value is accepted
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderSetParameter(
|
||||||
|
BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of ::BrotliDecoderState and initializes it.
|
||||||
|
*
|
||||||
|
* The instance can be used once for decoding and should then be destroyed with
|
||||||
|
* ::BrotliDecoderDestroyInstance, it cannot be reused for a new decoding
|
||||||
|
* session.
|
||||||
|
*
|
||||||
|
* @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
|
||||||
|
* case they are both zero, default memory allocators are used. @p opaque is
|
||||||
|
* passed to @p alloc_func and @p free_func when they are called. @p free_func
|
||||||
|
* has to return without doing anything when asked to free a NULL pointer.
|
||||||
|
*
|
||||||
|
* @param alloc_func custom memory allocation function
|
||||||
|
* @param free_func custom memory free function
|
||||||
|
* @param opaque custom memory manager handle
|
||||||
|
* @returns @c 0 if instance can not be allocated or initialized
|
||||||
|
* @returns pointer to initialized ::BrotliDecoderState otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BrotliDecoderState* BrotliDecoderCreateInstance(
|
||||||
|
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitializes and frees ::BrotliDecoderState instance.
|
||||||
|
*
|
||||||
|
* @param state decoder instance to be cleaned up and deallocated
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API void BrotliDecoderDestroyInstance(BrotliDecoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs one-shot memory-to-memory decompression.
|
||||||
|
*
|
||||||
|
* Decompresses the data in @p encoded_buffer into @p decoded_buffer, and sets
|
||||||
|
* @p *decoded_size to the decompressed length.
|
||||||
|
*
|
||||||
|
* @param encoded_size size of @p encoded_buffer
|
||||||
|
* @param encoded_buffer compressed data buffer with at least @p encoded_size
|
||||||
|
* addressable bytes
|
||||||
|
* @param[in, out] decoded_size @b in: size of @p decoded_buffer; \n
|
||||||
|
* @b out: length of decompressed data written to
|
||||||
|
* @p decoded_buffer
|
||||||
|
* @param decoded_buffer decompressed data destination buffer
|
||||||
|
* @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory
|
||||||
|
* allocation failed, or @p decoded_buffer is not large enough;
|
||||||
|
* @returns ::BROTLI_DECODER_RESULT_SUCCESS otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompress(
|
||||||
|
size_t encoded_size,
|
||||||
|
const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)],
|
||||||
|
size_t* decoded_size,
|
||||||
|
uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompresses the input stream to the output stream.
|
||||||
|
*
|
||||||
|
* The values @p *available_in and @p *available_out must specify the number of
|
||||||
|
* bytes addressable at @p *next_in and @p *next_out respectively.
|
||||||
|
* When @p *available_out is @c 0, @p next_out is allowed to be @c NULL.
|
||||||
|
*
|
||||||
|
* After each call, @p *available_in will be decremented by the amount of input
|
||||||
|
* bytes consumed, and the @p *next_in pointer will be incremented by that
|
||||||
|
* amount. Similarly, @p *available_out will be decremented by the amount of
|
||||||
|
* output bytes written, and the @p *next_out pointer will be incremented by
|
||||||
|
* that amount.
|
||||||
|
*
|
||||||
|
* @p total_out, if it is not a null-pointer, will be set to the number
|
||||||
|
* of bytes decompressed since the last @p state initialization.
|
||||||
|
*
|
||||||
|
* @note Input is never overconsumed, so @p next_in and @p available_in could be
|
||||||
|
* passed to the next consumer after decoding is complete.
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @param[in, out] available_in @b in: amount of available input; \n
|
||||||
|
* @b out: amount of unused input
|
||||||
|
* @param[in, out] next_in pointer to the next compressed byte
|
||||||
|
* @param[in, out] available_out @b in: length of output buffer; \n
|
||||||
|
* @b out: remaining size of output buffer
|
||||||
|
* @param[in, out] next_out output buffer cursor;
|
||||||
|
* can be @c NULL if @p available_out is @c 0
|
||||||
|
* @param[out] total_out number of bytes decompressed so far; can be @c NULL
|
||||||
|
* @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory
|
||||||
|
* allocation failed, arguments were invalid, etc.;
|
||||||
|
* use ::BrotliDecoderGetErrorCode to get detailed error code
|
||||||
|
* @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT decoding is blocked until
|
||||||
|
* more input data is provided
|
||||||
|
* @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT decoding is blocked until
|
||||||
|
* more output space is provided
|
||||||
|
* @returns ::BROTLI_DECODER_RESULT_SUCCESS decoding is finished, no more
|
||||||
|
* input might be consumed and no more output will be produced
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||||
|
BrotliDecoderState* state, size_t* available_in, const uint8_t** next_in,
|
||||||
|
size_t* available_out, uint8_t** next_out, size_t* total_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if decoder has more output.
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @returns ::BROTLI_TRUE, if decoder has some unconsumed output
|
||||||
|
* @returns ::BROTLI_FALSE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderHasMoreOutput(
|
||||||
|
const BrotliDecoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquires pointer to internal output buffer.
|
||||||
|
*
|
||||||
|
* This method is used to make language bindings easier and more efficient:
|
||||||
|
* -# push data to ::BrotliDecoderDecompressStream,
|
||||||
|
* until ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT is reported
|
||||||
|
* -# use ::BrotliDecoderTakeOutput to peek bytes and copy to language-specific
|
||||||
|
* entity
|
||||||
|
*
|
||||||
|
* Also this could be useful if there is an output stream that is able to
|
||||||
|
* consume all the provided data (e.g. when data is saved to file system).
|
||||||
|
*
|
||||||
|
* @attention After every call to ::BrotliDecoderTakeOutput @p *size bytes of
|
||||||
|
* output are considered consumed for all consecutive calls to the
|
||||||
|
* instance methods; returned pointer becomes invalidated as well.
|
||||||
|
*
|
||||||
|
* @note Decoder output is not guaranteed to be contiguous. This means that
|
||||||
|
* after the size-unrestricted call to ::BrotliDecoderTakeOutput,
|
||||||
|
* immediate next call to ::BrotliDecoderTakeOutput may return more data.
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if
|
||||||
|
* any amount could be handled; \n
|
||||||
|
* @b out: amount of data pointed by returned pointer and
|
||||||
|
* considered consumed; \n
|
||||||
|
* out value is never greater than in value, unless it is @c 0
|
||||||
|
* @returns pointer to output data
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API const uint8_t* BrotliDecoderTakeOutput(
|
||||||
|
BrotliDecoderState* state, size_t* size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if instance has already consumed input.
|
||||||
|
*
|
||||||
|
* Instance that returns ::BROTLI_FALSE is considered "fresh" and could be
|
||||||
|
* reused.
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @returns ::BROTLI_TRUE if decoder has already used some input bytes
|
||||||
|
* @returns ::BROTLI_FALSE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if decoder instance reached the final state.
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @returns ::BROTLI_TRUE if decoder is in a state where it reached the end of
|
||||||
|
* the input and produced all of the output
|
||||||
|
* @returns ::BROTLI_FALSE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished(
|
||||||
|
const BrotliDecoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquires a detailed error code.
|
||||||
|
*
|
||||||
|
* Should be used only after ::BrotliDecoderDecompressStream returns
|
||||||
|
* ::BROTLI_DECODER_RESULT_ERROR.
|
||||||
|
*
|
||||||
|
* See also ::BrotliDecoderErrorString
|
||||||
|
*
|
||||||
|
* @param state decoder instance
|
||||||
|
* @returns last saved error code
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
|
||||||
|
const BrotliDecoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts error code to a c-string.
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a decoder library version.
|
||||||
|
*
|
||||||
|
* Look at BROTLI_VERSION for more information.
|
||||||
|
*/
|
||||||
|
BROTLI_DEC_API uint32_t BrotliDecoderVersion(void);
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BROTLI_DEC_DECODE_H_ */
|
|
@ -0,0 +1,427 @@
|
||||||
|
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* API for Brotli compression.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROTLI_ENC_ENCODE_H_
|
||||||
|
#define BROTLI_ENC_ENCODE_H_
|
||||||
|
|
||||||
|
#include <brotli/port.h>
|
||||||
|
#include <brotli/types.h>
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Minimal value for ::BROTLI_PARAM_LGWIN parameter. */
|
||||||
|
#define BROTLI_MIN_WINDOW_BITS 10
|
||||||
|
/**
|
||||||
|
* Maximal value for ::BROTLI_PARAM_LGWIN parameter.
|
||||||
|
*
|
||||||
|
* @note equal to @c BROTLI_MAX_DISTANCE_BITS constant.
|
||||||
|
*/
|
||||||
|
#define BROTLI_MAX_WINDOW_BITS 24
|
||||||
|
/**
|
||||||
|
* Maximal value for ::BROTLI_PARAM_LGWIN parameter
|
||||||
|
* in "Large Window Brotli" (32-bit).
|
||||||
|
*/
|
||||||
|
#define BROTLI_LARGE_MAX_WINDOW_BITS 30
|
||||||
|
/** Minimal value for ::BROTLI_PARAM_LGBLOCK parameter. */
|
||||||
|
#define BROTLI_MIN_INPUT_BLOCK_BITS 16
|
||||||
|
/** Maximal value for ::BROTLI_PARAM_LGBLOCK parameter. */
|
||||||
|
#define BROTLI_MAX_INPUT_BLOCK_BITS 24
|
||||||
|
/** Minimal value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||||
|
#define BROTLI_MIN_QUALITY 0
|
||||||
|
/** Maximal value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||||
|
#define BROTLI_MAX_QUALITY 11
|
||||||
|
|
||||||
|
/** Options for ::BROTLI_PARAM_MODE parameter. */
|
||||||
|
typedef enum BrotliEncoderMode {
|
||||||
|
/**
|
||||||
|
* Default compression mode.
|
||||||
|
*
|
||||||
|
* In this mode compressor does not know anything in advance about the
|
||||||
|
* properties of the input.
|
||||||
|
*/
|
||||||
|
BROTLI_MODE_GENERIC = 0,
|
||||||
|
/** Compression mode for UTF-8 formatted text input. */
|
||||||
|
BROTLI_MODE_TEXT = 1,
|
||||||
|
/** Compression mode used in WOFF 2.0. */
|
||||||
|
BROTLI_MODE_FONT = 2
|
||||||
|
} BrotliEncoderMode;
|
||||||
|
|
||||||
|
/** Default value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||||
|
#define BROTLI_DEFAULT_QUALITY 11
|
||||||
|
/** Default value for ::BROTLI_PARAM_LGWIN parameter. */
|
||||||
|
#define BROTLI_DEFAULT_WINDOW 22
|
||||||
|
/** Default value for ::BROTLI_PARAM_MODE parameter. */
|
||||||
|
#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC
|
||||||
|
|
||||||
|
/** Operations that can be performed by streaming encoder. */
|
||||||
|
typedef enum BrotliEncoderOperation {
|
||||||
|
/**
|
||||||
|
* Process input.
|
||||||
|
*
|
||||||
|
* Encoder may postpone producing output, until it has processed enough input.
|
||||||
|
*/
|
||||||
|
BROTLI_OPERATION_PROCESS = 0,
|
||||||
|
/**
|
||||||
|
* Produce output for all processed input.
|
||||||
|
*
|
||||||
|
* Actual flush is performed when input stream is depleted and there is enough
|
||||||
|
* space in output stream. This means that client should repeat
|
||||||
|
* ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and
|
||||||
|
* ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
|
||||||
|
* via ::BrotliEncoderTakeOutput, then operation should be repeated after
|
||||||
|
* output buffer is drained.
|
||||||
|
*
|
||||||
|
* @warning Until flush is complete, client @b SHOULD @b NOT swap,
|
||||||
|
* reduce or extend input stream.
|
||||||
|
*
|
||||||
|
* When flush is complete, output data will be sufficient for decoder to
|
||||||
|
* reproduce all the given input.
|
||||||
|
*/
|
||||||
|
BROTLI_OPERATION_FLUSH = 1,
|
||||||
|
/**
|
||||||
|
* Finalize the stream.
|
||||||
|
*
|
||||||
|
* Actual finalization is performed when input stream is depleted and there is
|
||||||
|
* enough space in output stream. This means that client should repeat
|
||||||
|
* ::BROTLI_OPERATION_FINISH operation until @p available_in becomes @c 0, and
|
||||||
|
* ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
|
||||||
|
* via ::BrotliEncoderTakeOutput, then operation should be repeated after
|
||||||
|
* output buffer is drained.
|
||||||
|
*
|
||||||
|
* @warning Until finalization is complete, client @b SHOULD @b NOT swap,
|
||||||
|
* reduce or extend input stream.
|
||||||
|
*
|
||||||
|
* Helper function ::BrotliEncoderIsFinished checks if stream is finalized and
|
||||||
|
* output fully dumped.
|
||||||
|
*
|
||||||
|
* Adding more input data to finalized stream is impossible.
|
||||||
|
*/
|
||||||
|
BROTLI_OPERATION_FINISH = 2,
|
||||||
|
/**
|
||||||
|
* Emit metadata block to stream.
|
||||||
|
*
|
||||||
|
* Metadata is opaque to Brotli: neither encoder, nor decoder processes this
|
||||||
|
* data or relies on it. It may be used to pass some extra information from
|
||||||
|
* encoder client to decoder client without interfering with main data stream.
|
||||||
|
*
|
||||||
|
* @note Encoder may emit empty metadata blocks internally, to pad encoded
|
||||||
|
* stream to byte boundary.
|
||||||
|
*
|
||||||
|
* @warning Until emitting metadata is complete client @b SHOULD @b NOT swap,
|
||||||
|
* reduce or extend input stream.
|
||||||
|
*
|
||||||
|
* @warning The whole content of input buffer is considered to be the content
|
||||||
|
* of metadata block. Do @b NOT @e append metadata to input stream,
|
||||||
|
* before it is depleted with other operations.
|
||||||
|
*
|
||||||
|
* Stream is soft-flushed before metadata block is emitted. Metadata block
|
||||||
|
* @b MUST be no longer than than 16MiB.
|
||||||
|
*/
|
||||||
|
BROTLI_OPERATION_EMIT_METADATA = 3
|
||||||
|
} BrotliEncoderOperation;
|
||||||
|
|
||||||
|
/** Options to be used with ::BrotliEncoderSetParameter. */
|
||||||
|
typedef enum BrotliEncoderParameter {
|
||||||
|
/**
|
||||||
|
* Tune encoder for specific input.
|
||||||
|
*
|
||||||
|
* ::BrotliEncoderMode enumerates all available values.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_MODE = 0,
|
||||||
|
/**
|
||||||
|
* The main compression speed-density lever.
|
||||||
|
*
|
||||||
|
* The higher the quality, the slower the compression. Range is
|
||||||
|
* from ::BROTLI_MIN_QUALITY to ::BROTLI_MAX_QUALITY.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_QUALITY = 1,
|
||||||
|
/**
|
||||||
|
* Recommended sliding LZ77 window size.
|
||||||
|
*
|
||||||
|
* Encoder may reduce this value, e.g. if input is much smaller than
|
||||||
|
* window size.
|
||||||
|
*
|
||||||
|
* Window size is `(1 << value) - 16`.
|
||||||
|
*
|
||||||
|
* Range is from ::BROTLI_MIN_WINDOW_BITS to ::BROTLI_MAX_WINDOW_BITS.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_LGWIN = 2,
|
||||||
|
/**
|
||||||
|
* Recommended input block size.
|
||||||
|
*
|
||||||
|
* Encoder may reduce this value, e.g. if input is much smaller than input
|
||||||
|
* block size.
|
||||||
|
*
|
||||||
|
* Range is from ::BROTLI_MIN_INPUT_BLOCK_BITS to
|
||||||
|
* ::BROTLI_MAX_INPUT_BLOCK_BITS.
|
||||||
|
*
|
||||||
|
* @note Bigger input block size allows better compression, but consumes more
|
||||||
|
* memory. \n The rough formula of memory used for temporary input
|
||||||
|
* storage is `3 << lgBlock`.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_LGBLOCK = 3,
|
||||||
|
/**
|
||||||
|
* Flag that affects usage of "literal context modeling" format feature.
|
||||||
|
*
|
||||||
|
* This flag is a "decoding-speed vs compression ratio" trade-off.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING = 4,
|
||||||
|
/**
|
||||||
|
* Estimated total input size for all ::BrotliEncoderCompressStream calls.
|
||||||
|
*
|
||||||
|
* The default value is 0, which means that the total input size is unknown.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_SIZE_HINT = 5,
|
||||||
|
/**
|
||||||
|
* Flag that determines if "Large Window Brotli" is used.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_LARGE_WINDOW = 6,
|
||||||
|
/**
|
||||||
|
* Recommended number of postfix bits (NPOSTFIX).
|
||||||
|
*
|
||||||
|
* Encoder may change this value.
|
||||||
|
*
|
||||||
|
* Range is from 0 to ::BROTLI_MAX_NPOSTFIX.
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_NPOSTFIX = 7,
|
||||||
|
/**
|
||||||
|
* Recommended number of direct distance codes (NDIRECT).
|
||||||
|
*
|
||||||
|
* Encoder may change this value.
|
||||||
|
*
|
||||||
|
* Range is from 0 to (15 << NPOSTFIX) in steps of (1 << NPOSTFIX).
|
||||||
|
*/
|
||||||
|
BROTLI_PARAM_NDIRECT = 8
|
||||||
|
} BrotliEncoderParameter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque structure that holds encoder state.
|
||||||
|
*
|
||||||
|
* Allocated and initialized with ::BrotliEncoderCreateInstance.
|
||||||
|
* Cleaned up and deallocated with ::BrotliEncoderDestroyInstance.
|
||||||
|
*/
|
||||||
|
typedef struct BrotliEncoderStateStruct BrotliEncoderState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the specified parameter to the given encoder instance.
|
||||||
|
*
|
||||||
|
* @param state encoder instance
|
||||||
|
* @param param parameter to set
|
||||||
|
* @param value new parameter value
|
||||||
|
* @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
|
||||||
|
* @returns ::BROTLI_FALSE if value of parameter can not be changed at current
|
||||||
|
* encoder state (e.g. when encoding is started, window size might be
|
||||||
|
* already encoded and therefore it is impossible to change it)
|
||||||
|
* @returns ::BROTLI_TRUE if value is accepted
|
||||||
|
* @warning invalid values might be accepted in case they would not break
|
||||||
|
* encoding process.
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderSetParameter(
|
||||||
|
BrotliEncoderState* state, BrotliEncoderParameter param, uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of ::BrotliEncoderState and initializes it.
|
||||||
|
*
|
||||||
|
* @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
|
||||||
|
* case they are both zero, default memory allocators are used. @p opaque is
|
||||||
|
* passed to @p alloc_func and @p free_func when they are called. @p free_func
|
||||||
|
* has to return without doing anything when asked to free a NULL pointer.
|
||||||
|
*
|
||||||
|
* @param alloc_func custom memory allocation function
|
||||||
|
* @param free_func custom memory free function
|
||||||
|
* @param opaque custom memory manager handle
|
||||||
|
* @returns @c 0 if instance can not be allocated or initialized
|
||||||
|
* @returns pointer to initialized ::BrotliEncoderState otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API BrotliEncoderState* BrotliEncoderCreateInstance(
|
||||||
|
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitializes and frees ::BrotliEncoderState instance.
|
||||||
|
*
|
||||||
|
* @param state decoder instance to be cleaned up and deallocated
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the output size bound for the given @p input_size.
|
||||||
|
*
|
||||||
|
* @warning Result is only valid if quality is at least @c 2 and, in
|
||||||
|
* case ::BrotliEncoderCompressStream was used, no flushes
|
||||||
|
* (::BROTLI_OPERATION_FLUSH) were performed.
|
||||||
|
*
|
||||||
|
* @param input_size size of projected input
|
||||||
|
* @returns @c 0 if result does not fit @c size_t
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API size_t BrotliEncoderMaxCompressedSize(size_t input_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs one-shot memory-to-memory compression.
|
||||||
|
*
|
||||||
|
* Compresses the data in @p input_buffer into @p encoded_buffer, and sets
|
||||||
|
* @p *encoded_size to the compressed length.
|
||||||
|
*
|
||||||
|
* @note If ::BrotliEncoderMaxCompressedSize(@p input_size) returns non-zero
|
||||||
|
* value, then output is guaranteed to be no longer than that.
|
||||||
|
*
|
||||||
|
* @param quality quality parameter value, e.g. ::BROTLI_DEFAULT_QUALITY
|
||||||
|
* @param lgwin lgwin parameter value, e.g. ::BROTLI_DEFAULT_WINDOW
|
||||||
|
* @param mode mode parameter value, e.g. ::BROTLI_DEFAULT_MODE
|
||||||
|
* @param input_size size of @p input_buffer
|
||||||
|
* @param input_buffer input data buffer with at least @p input_size
|
||||||
|
* addressable bytes
|
||||||
|
* @param[in, out] encoded_size @b in: size of @p encoded_buffer; \n
|
||||||
|
* @b out: length of compressed data written to
|
||||||
|
* @p encoded_buffer, or @c 0 if compression fails
|
||||||
|
* @param encoded_buffer compressed data destination buffer
|
||||||
|
* @returns ::BROTLI_FALSE in case of compression error
|
||||||
|
* @returns ::BROTLI_FALSE if output buffer is too small
|
||||||
|
* @returns ::BROTLI_TRUE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompress(
|
||||||
|
int quality, int lgwin, BrotliEncoderMode mode, size_t input_size,
|
||||||
|
const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)],
|
||||||
|
size_t* encoded_size,
|
||||||
|
uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compresses input stream to output stream.
|
||||||
|
*
|
||||||
|
* The values @p *available_in and @p *available_out must specify the number of
|
||||||
|
* bytes addressable at @p *next_in and @p *next_out respectively.
|
||||||
|
* When @p *available_out is @c 0, @p next_out is allowed to be @c NULL.
|
||||||
|
*
|
||||||
|
* After each call, @p *available_in will be decremented by the amount of input
|
||||||
|
* bytes consumed, and the @p *next_in pointer will be incremented by that
|
||||||
|
* amount. Similarly, @p *available_out will be decremented by the amount of
|
||||||
|
* output bytes written, and the @p *next_out pointer will be incremented by
|
||||||
|
* that amount.
|
||||||
|
*
|
||||||
|
* @p total_out, if it is not a null-pointer, will be set to the number
|
||||||
|
* of bytes compressed since the last @p state initialization.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Internally workflow consists of 3 tasks:
|
||||||
|
* -# (optionally) copy input data to internal buffer
|
||||||
|
* -# actually compress data and (optionally) store it to internal buffer
|
||||||
|
* -# (optionally) copy compressed bytes from internal buffer to output stream
|
||||||
|
*
|
||||||
|
* Whenever all 3 tasks can't move forward anymore, or error occurs, this
|
||||||
|
* method returns the control flow to caller.
|
||||||
|
*
|
||||||
|
* @p op is used to perform flush, finish the stream, or inject metadata block.
|
||||||
|
* See ::BrotliEncoderOperation for more information.
|
||||||
|
*
|
||||||
|
* Flushing the stream means forcing encoding of all input passed to encoder and
|
||||||
|
* completing the current output block, so it could be fully decoded by stream
|
||||||
|
* decoder. To perform flush set @p op to ::BROTLI_OPERATION_FLUSH.
|
||||||
|
* Under some circumstances (e.g. lack of output stream capacity) this operation
|
||||||
|
* would require several calls to ::BrotliEncoderCompressStream. The method must
|
||||||
|
* be called again until both input stream is depleted and encoder has no more
|
||||||
|
* output (see ::BrotliEncoderHasMoreOutput) after the method is called.
|
||||||
|
*
|
||||||
|
* Finishing the stream means encoding of all input passed to encoder and
|
||||||
|
* adding specific "final" marks, so stream decoder could determine that stream
|
||||||
|
* is complete. To perform finish set @p op to ::BROTLI_OPERATION_FINISH.
|
||||||
|
* Under some circumstances (e.g. lack of output stream capacity) this operation
|
||||||
|
* would require several calls to ::BrotliEncoderCompressStream. The method must
|
||||||
|
* be called again until both input stream is depleted and encoder has no more
|
||||||
|
* output (see ::BrotliEncoderHasMoreOutput) after the method is called.
|
||||||
|
*
|
||||||
|
* @warning When flushing and finishing, @p op should not change until operation
|
||||||
|
* is complete; input stream should not be swapped, reduced or
|
||||||
|
* extended as well.
|
||||||
|
*
|
||||||
|
* @param state encoder instance
|
||||||
|
* @param op requested operation
|
||||||
|
* @param[in, out] available_in @b in: amount of available input; \n
|
||||||
|
* @b out: amount of unused input
|
||||||
|
* @param[in, out] next_in pointer to the next input byte
|
||||||
|
* @param[in, out] available_out @b in: length of output buffer; \n
|
||||||
|
* @b out: remaining size of output buffer
|
||||||
|
* @param[in, out] next_out compressed output buffer cursor;
|
||||||
|
* can be @c NULL if @p available_out is @c 0
|
||||||
|
* @param[out] total_out number of bytes produced so far; can be @c NULL
|
||||||
|
* @returns ::BROTLI_FALSE if there was an error
|
||||||
|
* @returns ::BROTLI_TRUE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompressStream(
|
||||||
|
BrotliEncoderState* state, BrotliEncoderOperation op, size_t* available_in,
|
||||||
|
const uint8_t** next_in, size_t* available_out, uint8_t** next_out,
|
||||||
|
size_t* total_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if encoder instance reached the final state.
|
||||||
|
*
|
||||||
|
* @param state encoder instance
|
||||||
|
* @returns ::BROTLI_TRUE if encoder is in a state where it reached the end of
|
||||||
|
* the input and produced all of the output
|
||||||
|
* @returns ::BROTLI_FALSE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if encoder has more output.
|
||||||
|
*
|
||||||
|
* @param state encoder instance
|
||||||
|
* @returns ::BROTLI_TRUE, if encoder has some unconsumed output
|
||||||
|
* @returns ::BROTLI_FALSE otherwise
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderHasMoreOutput(
|
||||||
|
BrotliEncoderState* state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquires pointer to internal output buffer.
|
||||||
|
*
|
||||||
|
* This method is used to make language bindings easier and more efficient:
|
||||||
|
* -# push data to ::BrotliEncoderCompressStream,
|
||||||
|
* until ::BrotliEncoderHasMoreOutput returns BROTL_TRUE
|
||||||
|
* -# use ::BrotliEncoderTakeOutput to peek bytes and copy to language-specific
|
||||||
|
* entity
|
||||||
|
*
|
||||||
|
* Also this could be useful if there is an output stream that is able to
|
||||||
|
* consume all the provided data (e.g. when data is saved to file system).
|
||||||
|
*
|
||||||
|
* @attention After every call to ::BrotliEncoderTakeOutput @p *size bytes of
|
||||||
|
* output are considered consumed for all consecutive calls to the
|
||||||
|
* instance methods; returned pointer becomes invalidated as well.
|
||||||
|
*
|
||||||
|
* @note Encoder output is not guaranteed to be contiguous. This means that
|
||||||
|
* after the size-unrestricted call to ::BrotliEncoderTakeOutput,
|
||||||
|
* immediate next call to ::BrotliEncoderTakeOutput may return more data.
|
||||||
|
*
|
||||||
|
* @param state encoder instance
|
||||||
|
* @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if
|
||||||
|
* any amount could be handled; \n
|
||||||
|
* @b out: amount of data pointed by returned pointer and
|
||||||
|
* considered consumed; \n
|
||||||
|
* out value is never greater than in value, unless it is @c 0
|
||||||
|
* @returns pointer to output data
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API const uint8_t* BrotliEncoderTakeOutput(
|
||||||
|
BrotliEncoderState* state, size_t* size);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an encoder library version.
|
||||||
|
*
|
||||||
|
* Look at BROTLI_VERSION for more information.
|
||||||
|
*/
|
||||||
|
BROTLI_ENC_API uint32_t BrotliEncoderVersion(void);
|
||||||
|
|
||||||
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BROTLI_ENC_ENCODE_H_ */
|
|
@ -0,0 +1,274 @@
|
||||||
|
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Macros for compiler / platform specific API declarations. */
|
||||||
|
|
||||||
|
#ifndef BROTLI_COMMON_PORT_H_
|
||||||
|
#define BROTLI_COMMON_PORT_H_
|
||||||
|
|
||||||
|
/* The following macros were borrowed from https://github.com/nemequ/hedley
|
||||||
|
* with permission of original author - Evan Nemerson <evan@nemerson.com> */
|
||||||
|
|
||||||
|
/* >>> >>> >>> hedley macros */
|
||||||
|
|
||||||
|
#define BROTLI_MAKE_VERSION(major, minor, revision) \
|
||||||
|
(((major) * 1000000) + ((minor) * 1000) + (revision))
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
|
||||||
|
#define BROTLI_GNUC_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#define BROTLI_GNUC_VERSION BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_GNUC_VERSION)
|
||||||
|
#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_GNUC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)
|
||||||
|
#define BROTLI_MSVC_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((_MSC_FULL_VER / 10000000), \
|
||||||
|
(_MSC_FULL_VER % 10000000) / 100000, \
|
||||||
|
(_MSC_FULL_VER % 100000) / 100)
|
||||||
|
#elif defined(_MSC_FULL_VER)
|
||||||
|
#define BROTLI_MSVC_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((_MSC_FULL_VER / 1000000), \
|
||||||
|
(_MSC_FULL_VER % 1000000) / 10000, \
|
||||||
|
(_MSC_FULL_VER % 10000) / 10)
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define BROTLI_MSVC_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(_MSC_VER / 100, _MSC_VER % 100, 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_MSC_VER)
|
||||||
|
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||||
|
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
|
||||||
|
#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||||
|
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
|
||||||
|
#else
|
||||||
|
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(_MSC_VER >= ((major * 100) + (minor)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)
|
||||||
|
#define BROTLI_INTEL_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, \
|
||||||
|
__INTEL_COMPILER % 100, \
|
||||||
|
__INTEL_COMPILER_UPDATE)
|
||||||
|
#elif defined(__INTEL_COMPILER)
|
||||||
|
#define BROTLI_INTEL_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_INTEL_VERSION)
|
||||||
|
#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_INTEL_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__PGI) && \
|
||||||
|
defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
|
||||||
|
#define BROTLI_PGI_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_PGI_VERSION)
|
||||||
|
#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_PGI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
|
||||||
|
#define BROTLI_SUNPRO_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION( \
|
||||||
|
(((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \
|
||||||
|
(((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), \
|
||||||
|
(__SUNPRO_C & 0xf) * 10)
|
||||||
|
#elif defined(__SUNPRO_C)
|
||||||
|
#define BROTLI_SUNPRO_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((__SUNPRO_C >> 8) & 0xf, \
|
||||||
|
(__SUNPRO_C >> 4) & 0xf, \
|
||||||
|
(__SUNPRO_C) & 0xf)
|
||||||
|
#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
|
||||||
|
#define BROTLI_SUNPRO_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION( \
|
||||||
|
(((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \
|
||||||
|
(((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \
|
||||||
|
(__SUNPRO_CC & 0xf) * 10)
|
||||||
|
#elif defined(__SUNPRO_CC)
|
||||||
|
#define BROTLI_SUNPRO_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((__SUNPRO_CC >> 8) & 0xf, \
|
||||||
|
(__SUNPRO_CC >> 4) & 0xf, \
|
||||||
|
(__SUNPRO_CC) & 0xf)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_SUNPRO_VERSION)
|
||||||
|
#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_SUNPRO_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
|
||||||
|
#define BROTLI_ARM_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((__ARMCOMPILER_VERSION / 1000000), \
|
||||||
|
(__ARMCOMPILER_VERSION % 1000000) / 10000, \
|
||||||
|
(__ARMCOMPILER_VERSION % 10000) / 100)
|
||||||
|
#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
|
||||||
|
#define BROTLI_ARM_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((__ARMCC_VERSION / 1000000), \
|
||||||
|
(__ARMCC_VERSION % 1000000) / 10000, \
|
||||||
|
(__ARMCC_VERSION % 10000) / 100)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_ARM_VERSION)
|
||||||
|
#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_ARM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ibmxl__)
|
||||||
|
#define BROTLI_IBM_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__ibmxl_version__, \
|
||||||
|
__ibmxl_release__, \
|
||||||
|
__ibmxl_modification__)
|
||||||
|
#elif defined(__xlC__) && defined(__xlC_ver__)
|
||||||
|
#define BROTLI_IBM_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
|
||||||
|
#elif defined(__xlC__)
|
||||||
|
#define BROTLI_IBM_VERSION BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, 0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_IBM_VERSION)
|
||||||
|
#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_IBM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__TI_COMPILER_VERSION__)
|
||||||
|
#define BROTLI_TI_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((__TI_COMPILER_VERSION__ / 1000000), \
|
||||||
|
(__TI_COMPILER_VERSION__ % 1000000) / 1000, \
|
||||||
|
(__TI_COMPILER_VERSION__ % 1000))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_TI_VERSION)
|
||||||
|
#define BROTLI_TI_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_TI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_TI_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__IAR_SYSTEMS_ICC__)
|
||||||
|
#if __VER__ > 1000
|
||||||
|
#define BROTLI_IAR_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION((__VER__ / 1000000), \
|
||||||
|
(__VER__ / 1000) % 1000, \
|
||||||
|
(__VER__ % 1000))
|
||||||
|
#else
|
||||||
|
#define BROTLI_IAR_VERSION BROTLI_MAKE_VERSION(VER / 100, __VER__ % 100, 0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_IAR_VERSION)
|
||||||
|
#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_IAR_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__TINYC__)
|
||||||
|
#define BROTLI_TINYC_VERSION \
|
||||||
|
BROTLI_MAKE_VERSION(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BROTLI_TINYC_VERSION)
|
||||||
|
#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) \
|
||||||
|
(BROTLI_TINYC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||||
|
#else
|
||||||
|
#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__has_attribute)
|
||||||
|
#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \
|
||||||
|
__has_attribute(attribute)
|
||||||
|
#else
|
||||||
|
#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \
|
||||||
|
BROTLI_GNUC_VERSION_CHECK(major, minor, patch)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__has_builtin)
|
||||||
|
#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \
|
||||||
|
__has_builtin(builtin)
|
||||||
|
#else
|
||||||
|
#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \
|
||||||
|
BROTLI_GNUC_VERSION_CHECK(major, minor, patch)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#define BROTLI_PUBLIC
|
||||||
|
#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \
|
||||||
|
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||||
|
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||||
|
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||||
|
BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \
|
||||||
|
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||||
|
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && \
|
||||||
|
defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
|
||||||
|
#define BROTLI_PUBLIC __attribute__ ((visibility ("default")))
|
||||||
|
#else
|
||||||
|
#define BROTLI_PUBLIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
|
||||||
|
!defined(__STDC_NO_VLA__) && !defined(__cplusplus) && \
|
||||||
|
!defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__)
|
||||||
|
#define BROTLI_ARRAY_PARAM(name) (name)
|
||||||
|
#else
|
||||||
|
#define BROTLI_ARRAY_PARAM(name)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* <<< <<< <<< end of hedley macros. */
|
||||||
|
|
||||||
|
#if defined(BROTLI_SHARED_COMPILATION)
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#if defined(BROTLICOMMON_SHARED_COMPILATION)
|
||||||
|
#define BROTLI_COMMON_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define BROTLI_COMMON_API __declspec(dllimport)
|
||||||
|
#endif /* BROTLICOMMON_SHARED_COMPILATION */
|
||||||
|
#if defined(BROTLIDEC_SHARED_COMPILATION)
|
||||||
|
#define BROTLI_DEC_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define BROTLI_DEC_API __declspec(dllimport)
|
||||||
|
#endif /* BROTLIDEC_SHARED_COMPILATION */
|
||||||
|
#if defined(BROTLIENC_SHARED_COMPILATION)
|
||||||
|
#define BROTLI_ENC_API __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define BROTLI_ENC_API __declspec(dllimport)
|
||||||
|
#endif /* BROTLIENC_SHARED_COMPILATION */
|
||||||
|
#else /* _WIN32 */
|
||||||
|
#define BROTLI_COMMON_API BROTLI_PUBLIC
|
||||||
|
#define BROTLI_DEC_API BROTLI_PUBLIC
|
||||||
|
#define BROTLI_ENC_API BROTLI_PUBLIC
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
#else /* BROTLI_SHARED_COMPILATION */
|
||||||
|
#define BROTLI_COMMON_API
|
||||||
|
#define BROTLI_DEC_API
|
||||||
|
#define BROTLI_ENC_API
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* BROTLI_COMMON_PORT_H_ */
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
Distributed under MIT license.
|
||||||
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Common types used in decoder and encoder API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROTLI_COMMON_TYPES_H_
|
||||||
|
#define BROTLI_COMMON_TYPES_H_
|
||||||
|
|
||||||
|
#include <stddef.h> /* for size_t */
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||||
|
typedef __int8 int8_t;
|
||||||
|
typedef unsigned __int8 uint8_t;
|
||||||
|
typedef __int16 int16_t;
|
||||||
|
typedef unsigned __int16 uint16_t;
|
||||||
|
typedef __int32 int32_t;
|
||||||
|
typedef unsigned __int32 uint32_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
typedef __int64 int64_t;
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A portable @c bool replacement.
|
||||||
|
*
|
||||||
|
* ::BROTLI_BOOL is a "documentation" type: actually it is @c int, but in API it
|
||||||
|
* denotes a type, whose only values are ::BROTLI_TRUE and ::BROTLI_FALSE.
|
||||||
|
*
|
||||||
|
* ::BROTLI_BOOL values passed to Brotli should either be ::BROTLI_TRUE or
|
||||||
|
* ::BROTLI_FALSE, or be a result of ::TO_BROTLI_BOOL macros.
|
||||||
|
*
|
||||||
|
* ::BROTLI_BOOL values returned by Brotli should not be tested for equality
|
||||||
|
* with @c true, @c false, ::BROTLI_TRUE, ::BROTLI_FALSE, but rather should be
|
||||||
|
* evaluated, for example: @code{.cpp}
|
||||||
|
* if (SomeBrotliFunction(encoder, BROTLI_TRUE) &&
|
||||||
|
* !OtherBrotliFunction(decoder, BROTLI_FALSE)) {
|
||||||
|
* bool x = !!YetAnotherBrotliFunction(encoder, TO_BROLTI_BOOL(2 * 2 == 4));
|
||||||
|
* DoSomething(x);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define BROTLI_BOOL int
|
||||||
|
/** Portable @c true replacement. */
|
||||||
|
#define BROTLI_TRUE 1
|
||||||
|
/** Portable @c false replacement. */
|
||||||
|
#define BROTLI_FALSE 0
|
||||||
|
/** @c bool to ::BROTLI_BOOL conversion macros. */
|
||||||
|
#define TO_BROTLI_BOOL(X) (!!(X) ? BROTLI_TRUE : BROTLI_FALSE)
|
||||||
|
|
||||||
|
#define BROTLI_MAKE_UINT64_T(high, low) ((((uint64_t)(high)) << 32) | low)
|
||||||
|
|
||||||
|
#define BROTLI_UINT32_MAX (~((uint32_t)0))
|
||||||
|
#define BROTLI_SIZE_MAX (~((size_t)0))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocating function pointer type.
|
||||||
|
*
|
||||||
|
* @param opaque custom memory manager handle provided by client
|
||||||
|
* @param size requested memory region size; can not be @c 0
|
||||||
|
* @returns @c 0 in the case of failure
|
||||||
|
* @returns a valid pointer to a memory region of at least @p size bytes
|
||||||
|
* long otherwise
|
||||||
|
*/
|
||||||
|
typedef void* (*brotli_alloc_func)(void* opaque, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deallocating function pointer type.
|
||||||
|
*
|
||||||
|
* This function @b SHOULD do nothing if @p address is @c 0.
|
||||||
|
*
|
||||||
|
* @param opaque custom memory manager handle provided by client
|
||||||
|
* @param address memory region pointer returned by ::brotli_alloc_func, or @c 0
|
||||||
|
*/
|
||||||
|
typedef void (*brotli_free_func)(void* opaque, void* address);
|
||||||
|
|
||||||
|
#endif /* BROTLI_COMMON_TYPES_H_ */
|
|
@ -1,554 +0,0 @@
|
||||||
/* Copyright 2014 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Example main() function for Brotli library. */
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <Common/BuildVersion.h>
|
|
||||||
|
|
||||||
#include "../dec/decode.h"
|
|
||||||
#include "../enc/encode.h"
|
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
#include <unistd.h>
|
|
||||||
#else
|
|
||||||
#include <io.h>
|
|
||||||
#include <share.h>
|
|
||||||
|
|
||||||
#define MAKE_BINARY(FILENO) (_setmode((FILENO), _O_BINARY), (FILENO))
|
|
||||||
|
|
||||||
#if !defined(__MINGW32__)
|
|
||||||
#define STDIN_FILENO MAKE_BINARY(_fileno(stdin))
|
|
||||||
#define STDOUT_FILENO MAKE_BINARY(_fileno(stdout))
|
|
||||||
#define S_IRUSR S_IREAD
|
|
||||||
#define S_IWUSR S_IWRITE
|
|
||||||
#endif
|
|
||||||
#define fdopen _fdopen
|
|
||||||
#define unlink _unlink
|
|
||||||
|
|
||||||
#define fopen ms_fopen
|
|
||||||
#define open ms_open
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
|
||||||
#define fseek _fseeki64
|
|
||||||
#define ftell _ftelli64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static FILE* ms_fopen(const char *filename, const char *mode) {
|
|
||||||
FILE* result = 0;
|
|
||||||
fopen_s(&result, filename, mode);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ms_open(const char *filename, int oflag, int pmode) {
|
|
||||||
int result = -1;
|
|
||||||
_sopen_s(&result, filename, oflag | O_BINARY, _SH_DENYNO, pmode);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif /* WIN32 */
|
|
||||||
|
|
||||||
static int ParseQuality(const char* s, int* quality) {
|
|
||||||
if (s[0] >= '0' && s[0] <= '9') {
|
|
||||||
*quality = s[0] - '0';
|
|
||||||
if (s[1] >= '0' && s[1] <= '9') {
|
|
||||||
*quality = *quality * 10 + s[1] - '0';
|
|
||||||
return (s[2] == 0) ? 1 : 0;
|
|
||||||
}
|
|
||||||
return (s[1] == 0) ? 1 : 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define UTILITY_NAME "Brotli"
|
|
||||||
#define UTILITY_MAJOR_VERSION 0
|
|
||||||
#define UTILITY_MINOR_VERSION 5
|
|
||||||
#define UTILITY_REVERSION 2
|
|
||||||
|
|
||||||
static void ParseArgv(int argc, char **argv,
|
|
||||||
char **input_path,
|
|
||||||
char **output_path,
|
|
||||||
char **dictionary_path,
|
|
||||||
int *force,
|
|
||||||
int *quality,
|
|
||||||
int *gapmem,
|
|
||||||
int *decompress,
|
|
||||||
int *repeat,
|
|
||||||
int *verbose,
|
|
||||||
int *lgwin) {
|
|
||||||
int k;
|
|
||||||
*force = 0;
|
|
||||||
*input_path = 0;
|
|
||||||
*output_path = 0;
|
|
||||||
*repeat = 1;
|
|
||||||
*verbose = 0;
|
|
||||||
*lgwin = 22;
|
|
||||||
{
|
|
||||||
size_t argv0_len = strlen(argv[0]);
|
|
||||||
*decompress =
|
|
||||||
argv0_len >= 5 && strcmp(&argv[0][argv0_len - 5], "unbro") == 0;
|
|
||||||
}
|
|
||||||
for (k = 1; k < argc; ++k) {
|
|
||||||
if (!strcmp("--force", argv[k]) ||
|
|
||||||
!strcmp("-f", argv[k])) {
|
|
||||||
if (*force != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
*force = 1;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--decompress", argv[k]) ||
|
|
||||||
!strcmp("--uncompress", argv[k]) ||
|
|
||||||
!strcmp("-d", argv[k])) {
|
|
||||||
*decompress = 1;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--verbose", argv[k]) ||
|
|
||||||
!strcmp("-v", argv[k])) {
|
|
||||||
if (*verbose != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
*verbose = 1;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--version", argv[k])) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"%s Version %d.%d.%d %s\n",
|
|
||||||
UTILITY_NAME,
|
|
||||||
UTILITY_MAJOR_VERSION,
|
|
||||||
UTILITY_MINOR_VERSION,
|
|
||||||
UTILITY_REVERSION,
|
|
||||||
__BUILD_VERSION);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (k < argc - 1) {
|
|
||||||
if (!strcmp("--input", argv[k]) ||
|
|
||||||
!strcmp("--in", argv[k]) ||
|
|
||||||
!strcmp("-i", argv[k])) {
|
|
||||||
if (*input_path != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
*input_path = argv[k + 1];
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--output", argv[k]) ||
|
|
||||||
!strcmp("--out", argv[k]) ||
|
|
||||||
!strcmp("-o", argv[k])) {
|
|
||||||
if (*output_path != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
*output_path = argv[k + 1];
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--custom-dictionary", argv[k])) {
|
|
||||||
if (*dictionary_path != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
*dictionary_path = argv[k + 1];
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--quality", argv[k]) ||
|
|
||||||
!strcmp("-q", argv[k])) {
|
|
||||||
if (!ParseQuality(argv[k + 1], quality)) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--repeat", argv[k]) ||
|
|
||||||
!strcmp("-r", argv[k])) {
|
|
||||||
if (!ParseQuality(argv[k + 1], repeat)) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--window", argv[k]) ||
|
|
||||||
!strcmp("-w", argv[k])) {
|
|
||||||
if (!ParseQuality(argv[k + 1], lgwin)) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (*lgwin < 10 || *lgwin >= 25) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
} else if (!strcmp("--gap", argv[k]) ||
|
|
||||||
!strcmp("-g", argv[k])) {
|
|
||||||
if (!ParseQuality(argv[k + 1], gapmem)) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
++k;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
error:
|
|
||||||
fprintf(stderr,
|
|
||||||
"Usage: %s [--force] [--quality n] [--gap n] [--decompress]"
|
|
||||||
" [--input filename] [--output filename] [--repeat iters]"
|
|
||||||
" [--verbose] [--window n] [--custom-dictionary filename]"
|
|
||||||
" [--version]\n",
|
|
||||||
argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILE* OpenInputFile(const char* input_path) {
|
|
||||||
FILE* f;
|
|
||||||
if (input_path == 0) {
|
|
||||||
return fdopen(STDIN_FILENO, "rb");
|
|
||||||
}
|
|
||||||
f = fopen(input_path, "rb");
|
|
||||||
if (f == 0) {
|
|
||||||
perror("fopen");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILE *OpenOutputFile(const char *output_path, const int force) {
|
|
||||||
int fd;
|
|
||||||
if (output_path == 0) {
|
|
||||||
return fdopen(STDOUT_FILENO, "wb");
|
|
||||||
}
|
|
||||||
fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC,
|
|
||||||
S_IRUSR | S_IWUSR);
|
|
||||||
if (fd < 0) {
|
|
||||||
if (!force) {
|
|
||||||
struct stat statbuf;
|
|
||||||
if (stat(output_path, &statbuf) == 0) {
|
|
||||||
fprintf(stderr, "output file exists\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
perror("open");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return fdopen(fd, "wb");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int64_t FileSize(const char *path) {
|
|
||||||
FILE *f = fopen(path, "rb");
|
|
||||||
int64_t retval;
|
|
||||||
if (f == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (fseek(f, 0L, SEEK_END) != 0) {
|
|
||||||
fclose(f);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
retval = ftell(f);
|
|
||||||
if (fclose(f) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Brotli specified memory allocate function */
|
|
||||||
static void *BrAlloc (void *memsize, size_t size) {
|
|
||||||
*(int64_t *)memsize += size;
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Brotli specified memory free function */
|
|
||||||
static void BrFree (void *memsize, void *ptr) {
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Result ownersip is passed to caller.
|
|
||||||
|*dictionary_size| is set to resulting buffer size. */
|
|
||||||
static uint8_t* ReadDictionary(const char* path, size_t* dictionary_size, void *memsize) {
|
|
||||||
static const int kMaxDictionarySize = (1 << 24) - 16;
|
|
||||||
FILE *f = fopen(path, "rb");
|
|
||||||
int64_t file_size_64;
|
|
||||||
uint8_t* buffer;
|
|
||||||
size_t bytes_read;
|
|
||||||
|
|
||||||
if (f == NULL) {
|
|
||||||
perror("fopen");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
file_size_64 = FileSize(path);
|
|
||||||
if (file_size_64 == -1) {
|
|
||||||
fprintf(stderr, "could not get size of dictionary file");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_size_64 > kMaxDictionarySize) {
|
|
||||||
fprintf(stderr, "dictionary is larger than maximum allowed: %d\n",
|
|
||||||
kMaxDictionarySize);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
*dictionary_size = (size_t)file_size_64;
|
|
||||||
|
|
||||||
buffer = (uint8_t *)BrAlloc(memsize, *dictionary_size);
|
|
||||||
if (!buffer) {
|
|
||||||
fprintf(stderr, "could not read dictionary: out of memory\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
bytes_read = fread(buffer, sizeof(uint8_t), *dictionary_size, f);
|
|
||||||
if (bytes_read != *dictionary_size) {
|
|
||||||
fprintf(stderr, "could not read dictionary\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const size_t kFileBufferSize = 65536;
|
|
||||||
|
|
||||||
static int Decompress(FILE* fin, FILE* fout, const char* dictionary_path, void *memsize) {
|
|
||||||
/* Dictionary should be kept during first rounds of decompression. */
|
|
||||||
uint8_t* dictionary = NULL;
|
|
||||||
uint8_t* input;
|
|
||||||
uint8_t* output;
|
|
||||||
size_t total_out;
|
|
||||||
size_t available_in;
|
|
||||||
const uint8_t* next_in;
|
|
||||||
size_t available_out = kFileBufferSize;
|
|
||||||
uint8_t* next_out;
|
|
||||||
BrotliResult result = BROTLI_RESULT_ERROR;
|
|
||||||
BrotliState* s = BrotliCreateState(BrAlloc, BrFree, memsize);
|
|
||||||
if (!s) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (dictionary_path != NULL) {
|
|
||||||
size_t dictionary_size = 0;
|
|
||||||
dictionary = ReadDictionary(dictionary_path, &dictionary_size, memsize);
|
|
||||||
BrotliSetCustomDictionary(dictionary_size, dictionary, s);
|
|
||||||
}
|
|
||||||
input = (uint8_t*)BrAlloc(memsize, kFileBufferSize);
|
|
||||||
output = (uint8_t*)BrAlloc(memsize, kFileBufferSize);
|
|
||||||
if (!input || !output) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
next_out = output;
|
|
||||||
result = BROTLI_RESULT_NEEDS_MORE_INPUT;
|
|
||||||
while (1) {
|
|
||||||
if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) {
|
|
||||||
if (feof(fin)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
available_in = fread(input, 1, kFileBufferSize, fin);
|
|
||||||
next_in = input;
|
|
||||||
if (ferror(fin)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) {
|
|
||||||
fwrite(output, 1, kFileBufferSize, fout);
|
|
||||||
if (ferror(fout)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
available_out = kFileBufferSize;
|
|
||||||
next_out = output;
|
|
||||||
} else {
|
|
||||||
break; /* Error or success. */
|
|
||||||
}
|
|
||||||
result = BrotliDecompressStream(&available_in, &next_in,
|
|
||||||
&available_out, &next_out, &total_out, s);
|
|
||||||
}
|
|
||||||
if (next_out != output) {
|
|
||||||
fwrite(output, 1, (size_t)(next_out - output), fout);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((result == BROTLI_RESULT_NEEDS_MORE_OUTPUT) || ferror(fout)) {
|
|
||||||
fprintf(stderr, "failed to write output\n");
|
|
||||||
} else if (result != BROTLI_RESULT_SUCCESS) { /* Error or needs more input. */
|
|
||||||
fprintf(stderr, "corrupt input\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
BrFree(memsize, dictionary);
|
|
||||||
BrFree(memsize, input);
|
|
||||||
BrFree(memsize, output);
|
|
||||||
BrotliDestroyState(s);
|
|
||||||
return (result == BROTLI_RESULT_SUCCESS) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Compress(int quality, int lgwin, FILE* fin, FILE* fout,
|
|
||||||
const char *dictionary_path, void *memsize) {
|
|
||||||
BrotliEncoderState* s = BrotliEncoderCreateInstance(BrAlloc, BrFree, memsize);
|
|
||||||
uint8_t* buffer = (uint8_t*)BrAlloc(memsize, kFileBufferSize << 1);
|
|
||||||
uint8_t* input = buffer;
|
|
||||||
uint8_t* output = buffer + kFileBufferSize;
|
|
||||||
size_t available_in = 0;
|
|
||||||
const uint8_t* next_in = NULL;
|
|
||||||
size_t available_out = kFileBufferSize;
|
|
||||||
uint8_t* next_out = output;
|
|
||||||
int is_eof = 0;
|
|
||||||
int is_ok = 1;
|
|
||||||
|
|
||||||
if (!s || !buffer) {
|
|
||||||
is_ok = 0;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
BrotliEncoderSetParameter(s, BROTLI_PARAM_QUALITY, (uint32_t)quality);
|
|
||||||
BrotliEncoderSetParameter(s, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
|
|
||||||
if (dictionary_path != NULL) {
|
|
||||||
size_t dictionary_size = 0;
|
|
||||||
uint8_t* dictionary = ReadDictionary(dictionary_path, &dictionary_size, memsize);
|
|
||||||
BrotliEncoderSetCustomDictionary(s, dictionary_size, dictionary);
|
|
||||||
BrFree(memsize, dictionary);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
if (available_in == 0 && !is_eof) {
|
|
||||||
available_in = fread(input, 1, kFileBufferSize, fin);
|
|
||||||
next_in = input;
|
|
||||||
if (ferror(fin)) break;
|
|
||||||
is_eof = feof(fin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BrotliEncoderCompressStream(s,
|
|
||||||
is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,
|
|
||||||
&available_in, &next_in, &available_out, &next_out, NULL)) {
|
|
||||||
is_ok = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (available_out != kFileBufferSize) {
|
|
||||||
size_t out_size = kFileBufferSize - available_out;
|
|
||||||
fwrite(output, 1, out_size, fout);
|
|
||||||
if (ferror(fout)) break;
|
|
||||||
available_out = kFileBufferSize;
|
|
||||||
next_out = output;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BrotliEncoderIsFinished(s)) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish:
|
|
||||||
BrFree(memsize, buffer);
|
|
||||||
BrotliEncoderDestroyInstance(s);
|
|
||||||
|
|
||||||
if (!is_ok) {
|
|
||||||
/* Should detect OOM? */
|
|
||||||
fprintf(stderr, "failed to compress data\n");
|
|
||||||
return 0;
|
|
||||||
} else if (ferror(fout)) {
|
|
||||||
fprintf(stderr, "failed to write output\n");
|
|
||||||
return 0;
|
|
||||||
} else if (ferror(fin)) {
|
|
||||||
fprintf(stderr, "failed to read input\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GAP_MEM_BLOCK 4096
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
char *input_path = 0;
|
|
||||||
char *output_path = 0;
|
|
||||||
char *dictionary_path = 0;
|
|
||||||
int force = 0;
|
|
||||||
int quality = 11;
|
|
||||||
int gmem = 1;
|
|
||||||
int decompress = 0;
|
|
||||||
int repeat = 1;
|
|
||||||
int verbose = 0;
|
|
||||||
int lgwin = 0;
|
|
||||||
clock_t clock_start;
|
|
||||||
int i;
|
|
||||||
int64_t originsize = 0;
|
|
||||||
int64_t msize = 0;
|
|
||||||
ParseArgv(argc, argv, &input_path, &output_path, &dictionary_path, &force,
|
|
||||||
&quality, &gmem, &decompress, &repeat, &verbose, &lgwin);
|
|
||||||
clock_start = clock();
|
|
||||||
for (i = 0; i < repeat; ++i) {
|
|
||||||
FILE* fin = OpenInputFile(input_path);
|
|
||||||
FILE* fout = OpenOutputFile(output_path, force || repeat);
|
|
||||||
int is_ok = 0;
|
|
||||||
if (decompress) {
|
|
||||||
if (fseek(fin, 16, SEEK_SET) != 0) {
|
|
||||||
fclose(fin);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
is_ok = Decompress(fin, fout, dictionary_path, (void *)&msize);
|
|
||||||
} else {
|
|
||||||
originsize = FileSize(input_path); /* get original file size */
|
|
||||||
fwrite(&originsize, 1, sizeof(int64_t), fout); /* add in original binary file size */
|
|
||||||
fwrite(&msize, 1, sizeof(int64_t), fout); /* add in dummy decompression required memory size */
|
|
||||||
is_ok = Compress(quality, lgwin, fin, fout, dictionary_path, (void *)&msize);
|
|
||||||
}
|
|
||||||
if (!is_ok) {
|
|
||||||
unlink(output_path);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fclose(fin) != 0) {
|
|
||||||
perror("fclose");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fclose(fout) != 0) {
|
|
||||||
perror("fclose");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* after compression operation then execute decompression operation
|
|
||||||
to get decompression required memory size. */
|
|
||||||
if (decompress == 0) {
|
|
||||||
fin = OpenInputFile(output_path);
|
|
||||||
fout = tmpfile ();
|
|
||||||
msize = 0;
|
|
||||||
if (fseek(fin, 16, SEEK_SET) != 0) {
|
|
||||||
fclose(fin);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
is_ok = Decompress(fin, fout, dictionary_path, (void *)&msize);
|
|
||||||
if (!is_ok) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fclose(fin) != 0) {
|
|
||||||
perror("fclose");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fclose(fout) != 0) {
|
|
||||||
perror("fclose");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fout = fopen(output_path, "rb+"); /* open output_path file and add in head info */
|
|
||||||
/* seek to the offset of decompression required memory size */
|
|
||||||
if (fseek(fout, 8, SEEK_SET) != 0) {
|
|
||||||
fclose(fout);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
msize += gmem * GAP_MEM_BLOCK; /* there is a memory gap between IA32 and X64 environment*/
|
|
||||||
fwrite(&msize, 1, sizeof(int64_t), fout); /* update final decompression required memory size */
|
|
||||||
if (fclose(fout) != 0) {
|
|
||||||
perror("fclose");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (verbose) {
|
|
||||||
clock_t clock_end = clock();
|
|
||||||
double duration = (double)(clock_end - clock_start) / CLOCKS_PER_SEC;
|
|
||||||
int64_t uncompressed_size;
|
|
||||||
double uncompressed_bytes_in_MB;
|
|
||||||
if (duration < 1e-9) {
|
|
||||||
duration = 1e-9;
|
|
||||||
}
|
|
||||||
uncompressed_size = FileSize(decompress ? output_path : input_path);
|
|
||||||
if (uncompressed_size == -1) {
|
|
||||||
fprintf(stderr, "failed to determine uncompressed file size\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
uncompressed_bytes_in_MB =
|
|
||||||
(double)(repeat * uncompressed_size) / (1024.0 * 1024.0);
|
|
||||||
if (decompress) {
|
|
||||||
printf("Brotli decompression speed: ");
|
|
||||||
} else {
|
|
||||||
printf("Brotli compression speed: ");
|
|
||||||
}
|
|
||||||
printf("%g MB/s\n", uncompressed_bytes_in_MB / duration);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
brotli(1) -- brotli, unbrotli - compress or decompress files
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
|
||||||
|
`brotli` [*OPTION|FILE*]...
|
||||||
|
|
||||||
|
`unbrotli` is equivalent to `brotli --decompress`
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
`brotli` is a generic-purpose lossless compression algorithm that compresses
|
||||||
|
data using a combination of a modern variant of the **LZ77** algorithm, Huffman
|
||||||
|
coding and 2-nd order context modeling, with a compression ratio comparable to
|
||||||
|
the best currently available general-purpose compression methods. It is similar
|
||||||
|
in speed with deflate but offers more dense compression.
|
||||||
|
|
||||||
|
`brotli` command line syntax similar to `gzip (1)` and `zstd (1)`.
|
||||||
|
Unlike `gzip (1)`, source files are preserved by default. It is possible to
|
||||||
|
remove them after processing by using the `--rm` _option_.
|
||||||
|
|
||||||
|
Arguments that look like "`--name`" or "`--name=value`" are _options_. Every
|
||||||
|
_option_ has a short form "`-x`" or "`-x value`". Multiple short form _options_
|
||||||
|
could be coalesced:
|
||||||
|
|
||||||
|
* "`--decompress --stdout --suffix=.b`" works the same as
|
||||||
|
* "`-d -s -S .b`" and
|
||||||
|
* "`-dsS .b`"
|
||||||
|
|
||||||
|
`brotli` has 3 operation modes:
|
||||||
|
|
||||||
|
* default mode is compression;
|
||||||
|
* `--decompress` option activates decompression mode;
|
||||||
|
* `--test` option switches to integrity test mode; this option is equivalent to
|
||||||
|
"`--decompress --stdout`" except that the decompressed data is discarded
|
||||||
|
instead of being written to standard output.
|
||||||
|
|
||||||
|
Every non-option argument is a _file_ entry. If no _files_ are given or _file_
|
||||||
|
is "`-`", `brotli` reads from standard input. All arguments after "`--`" are
|
||||||
|
_file_ entries.
|
||||||
|
|
||||||
|
Unless `--stdout` or `--output` is specified, _files_ are written to a new file
|
||||||
|
whose name is derived from the source _file_ name:
|
||||||
|
|
||||||
|
* when compressing, a suffix is appended to the source filename to
|
||||||
|
get the target filename
|
||||||
|
* when decompressing, a suffix is removed from the source filename to
|
||||||
|
get the target filename
|
||||||
|
|
||||||
|
Default suffix is `.br`, but it could be specified with `--suffix` option.
|
||||||
|
|
||||||
|
Conflicting or duplicate _options_ are not allowed.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-------
|
||||||
|
|
||||||
|
* `-#`:
|
||||||
|
compression level (0-9); bigger values cause denser, but slower compression
|
||||||
|
* `-c`, `--stdout`:
|
||||||
|
write on standard output
|
||||||
|
* `-d`, `--decompress`:
|
||||||
|
decompress mode
|
||||||
|
* `-f`, `--force`:
|
||||||
|
force output file overwrite
|
||||||
|
* `-h`, `--help`:
|
||||||
|
display this help and exit
|
||||||
|
* `-j`, `--rm`:
|
||||||
|
remove source file(s); `gzip (1)`-like behaviour
|
||||||
|
* `-k`, `--keep`:
|
||||||
|
keep source file(s); `zstd (1)`-like behaviour
|
||||||
|
* `-n`, `--no-copy-stat`:
|
||||||
|
do not copy source file(s) attributes
|
||||||
|
* `-o FILE`, `--output=FILE`
|
||||||
|
output file; valid only if there is a single input entry
|
||||||
|
* `-q NUM`, `--quality=NUM`:
|
||||||
|
compression level (0-11); bigger values cause denser, but slower compression
|
||||||
|
* `-t`, `--test`:
|
||||||
|
test file integrity mode
|
||||||
|
* `-v`, `--verbose`:
|
||||||
|
increase output verbosity
|
||||||
|
* `-w NUM`, `--lgwin=NUM`:
|
||||||
|
set LZ77 window size (0, 10-24) (default: 22); window size is
|
||||||
|
`(2**NUM - 16)`; 0 lets compressor decide over the optimal value; bigger
|
||||||
|
windows size improve density; decoder might require up to window size
|
||||||
|
memory to operate
|
||||||
|
* `-S SUF`, `--suffix=SUF`:
|
||||||
|
output file suffix (default: `.br`)
|
||||||
|
* `-V`, `--version`:
|
||||||
|
display version and exit
|
||||||
|
* `-Z`, `--best`:
|
||||||
|
use best compression level (default); same as "`-q 11`"
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
`brotli` file format is defined in
|
||||||
|
[RFC 7932](https://www.ietf.org/rfc/rfc7932.txt).
|
||||||
|
|
||||||
|
`brotli` is open-sourced under the
|
||||||
|
[MIT License](https://opensource.org/licenses/MIT).
|
||||||
|
|
||||||
|
Mailing list: https://groups.google.com/forum/#!forum/brotli
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
----
|
||||||
|
Report bugs at: https://github.com/google/brotli/issues
|
|
@ -1,14 +0,0 @@
|
||||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
Distributed under MIT license.
|
|
||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Defines a common version string used by all of the brotli tools. */
|
|
||||||
|
|
||||||
#ifndef BROTLI_TOOLS_VERSION_H_
|
|
||||||
#define BROTLI_TOOLS_VERSION_H_
|
|
||||||
|
|
||||||
#define BROTLI_VERSION "0.5.2"
|
|
||||||
|
|
||||||
#endif /* BROTLI_TOOLS_VERSION_H_ */
|
|
Loading…
Reference in New Issue