mirror of
https://github.com/google/brotli.git
synced 2026-02-16 13:45:32 +00:00
Compare commits
24 Commits
v0.6.0
...
custom-dic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52441069ef | ||
|
|
172a378deb | ||
|
|
5aabc7a6ab | ||
|
|
1becbbf231 | ||
|
|
58f5c37f3b | ||
|
|
efdff3f14e | ||
|
|
e51eae564f | ||
|
|
a423b33a77 | ||
|
|
a4d2956ded | ||
|
|
00cacfdff6 | ||
|
|
05d5f3d77a | ||
|
|
0fceb906ec | ||
|
|
31d0629b1d | ||
|
|
19dc934e39 | ||
|
|
03739d2b11 | ||
|
|
2c001010aa | ||
|
|
0a84e9bf86 | ||
|
|
4363f2d74b | ||
|
|
a015b42683 | ||
|
|
d00ccae57f | ||
|
|
6ece1d8791 | ||
|
|
04de756ad2 | ||
|
|
f2aa4d1e8c | ||
|
|
66e798d46a |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -13,5 +13,5 @@ __pycache__/
|
||||
|
||||
# Tests
|
||||
*.txt.uncompressed
|
||||
*.bro
|
||||
*.unbro
|
||||
*.br
|
||||
*.unbr
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,5 +1,5 @@
|
||||
[submodule "terryfy"]
|
||||
path = terryfy
|
||||
path = scripts/terryfy
|
||||
url = https://github.com/MacPython/terryfy.git
|
||||
[submodule "research/esaxx"]
|
||||
path = research/esaxx
|
||||
|
||||
19
.travis.yml
19
.travis.yml
@@ -230,14 +230,17 @@ matrix:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
language: java
|
||||
env: BUILD_SYSTEM=bazel
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8"
|
||||
key_url: "https://storage.googleapis.com/bazel-apt/doc/apt-key.pub.gpg"
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- wget
|
||||
- pkg-config
|
||||
- oracle-java8-installer
|
||||
- bazel
|
||||
|
||||
- os: osx
|
||||
env: BUILD_SYSTEM=bazel
|
||||
@@ -250,17 +253,17 @@ before_install:
|
||||
###
|
||||
- if [ -n "${C_COMPILER}" ]; then export CC="${C_COMPILER}"; fi
|
||||
- if [ -n "${CXX_COMPILER}" ]; then export CXX="${CXX_COMPILER}"; fi
|
||||
- ./.travis.sh before_install
|
||||
- scripts/.travis.sh before_install
|
||||
install:
|
||||
- ./.travis.sh install
|
||||
- scripts/.travis.sh install
|
||||
script:
|
||||
- ./.travis.sh script
|
||||
- scripts/.travis.sh script
|
||||
after_success:
|
||||
- ./.travis.sh after_success
|
||||
- scripts/.travis.sh after_success
|
||||
|
||||
before_deploy:
|
||||
- if [ "${BUILD_SYSTEM}" = "python" ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then export WHEELS=$(ls ./dist/*.whl); fi
|
||||
- ./.travis.sh before_deploy
|
||||
- scripts/.travis.sh before_deploy
|
||||
|
||||
deploy:
|
||||
- provider: releases
|
||||
@@ -273,7 +276,7 @@ deploy:
|
||||
tags: true
|
||||
condition: "${BUILD_SYSTEM} = python && ${TRAVIS_OS_NAME} = osx"
|
||||
- provider: bintray
|
||||
file: ".bintray.json"
|
||||
file: "scripts/.bintray.json"
|
||||
user: "eustas"
|
||||
key:
|
||||
secure: "Kbam/lTAdz72fZivbs6riJT+Y4PbuKP7r6t5PAWxJxAAykjwnYTRe3zF472g9HCE14KYMsdB+KSYSgg6TGJnqGC9gL9xhhGU9U/WmA+vbMWS/MSnMWpK9IRpp77pM2i2NKZD4v33JuEwKFCBJP3Vj6QQ5Qd1NKdobuXJyznhgnw="
|
||||
|
||||
125
BUILD
125
BUILD
@@ -9,6 +9,49 @@ licenses(["notice"]) # MIT
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
# >>> JNI headers
|
||||
|
||||
config_setting(
|
||||
name = "darwin",
|
||||
values = {"cpu": "darwin"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "darwin_x86_64",
|
||||
values = {"cpu": "darwin_x86_64"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "copy_link_jni_header",
|
||||
srcs = ["@openjdk_linux//:jni_h"],
|
||||
outs = ["jni/jni.h"],
|
||||
cmd = "cp -f $< $@",
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "copy_link_jni_md_header",
|
||||
srcs = select({
|
||||
":darwin": ["@openjdk_macos//:jni_md_h"],
|
||||
":darwin_x86_64": ["@openjdk_macos//:jni_md_h"],
|
||||
"//conditions:default": ["@openjdk_linux//:jni_md_h"],
|
||||
}),
|
||||
outs = ["jni/jni_md.h"],
|
||||
cmd = "cp -f $< $@",
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "jni_inc",
|
||||
hdrs = [
|
||||
":jni/jni.h",
|
||||
":jni/jni_md.h",
|
||||
],
|
||||
includes = ["jni"],
|
||||
)
|
||||
|
||||
# <<< JNI headers
|
||||
|
||||
STRICT_C_OPTIONS = [
|
||||
"--pedantic-errors",
|
||||
"-Wall",
|
||||
@@ -25,44 +68,44 @@ STRICT_C_OPTIONS = [
|
||||
|
||||
filegroup(
|
||||
name = "public_headers",
|
||||
srcs = glob(["include/brotli/*.h"]),
|
||||
srcs = glob(["c/include/brotli/*.h"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "common_headers",
|
||||
srcs = glob(["common/*.h"]),
|
||||
srcs = glob(["c/common/*.h"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "common_sources",
|
||||
srcs = glob(["common/*.c"]),
|
||||
srcs = glob(["c/common/*.c"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "dec_headers",
|
||||
srcs = glob(["dec/*.h"]),
|
||||
srcs = glob(["c/dec/*.h"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "dec_sources",
|
||||
srcs = glob(["dec/*.c"]),
|
||||
srcs = glob(["c/dec/*.c"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "enc_headers",
|
||||
srcs = glob(["enc/*.h"]),
|
||||
srcs = glob(["c/enc/*.h"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "enc_sources",
|
||||
srcs = glob(["enc/*.c"]),
|
||||
srcs = glob(["c/enc/*.c"]),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "brotli",
|
||||
name = "brotli_inc",
|
||||
hdrs = [":public_headers"],
|
||||
copts = STRICT_C_OPTIONS,
|
||||
includes = ["include"],
|
||||
includes = ["c/include"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -70,7 +113,7 @@ cc_library(
|
||||
srcs = [":common_sources"],
|
||||
hdrs = [":common_headers"],
|
||||
copts = STRICT_C_OPTIONS,
|
||||
deps = [":brotli"],
|
||||
deps = [":brotli_inc"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -91,8 +134,8 @@ cc_library(
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "bro",
|
||||
srcs = ["tools/bro.c"],
|
||||
name = "brotli",
|
||||
srcs = ["c/tools/brotli.c"],
|
||||
copts = STRICT_C_OPTIONS,
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
@@ -101,6 +144,64 @@ cc_binary(
|
||||
],
|
||||
)
|
||||
|
||||
########################################################
|
||||
# WARNING: do not (transitively) depend on this target!
|
||||
########################################################
|
||||
cc_library(
|
||||
name = "jni",
|
||||
srcs = [
|
||||
":common_sources",
|
||||
":dec_sources",
|
||||
":enc_sources",
|
||||
"//java/org/brotli/wrapper/common:jni_src",
|
||||
"//java/org/brotli/wrapper/dec:jni_src",
|
||||
"//java/org/brotli/wrapper/enc:jni_src",
|
||||
],
|
||||
hdrs = [
|
||||
":common_headers",
|
||||
":dec_headers",
|
||||
":enc_headers",
|
||||
],
|
||||
deps = [
|
||||
":brotli_inc",
|
||||
":jni_inc",
|
||||
],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
########################################################
|
||||
# WARNING: do not (transitively) depend on this target!
|
||||
########################################################
|
||||
cc_library(
|
||||
name = "jni_no_dictionary_data",
|
||||
srcs = [
|
||||
":common_sources",
|
||||
":dec_sources",
|
||||
":enc_sources",
|
||||
"//java/org/brotli/wrapper/common:jni_src",
|
||||
"//java/org/brotli/wrapper/dec:jni_src",
|
||||
"//java/org/brotli/wrapper/enc:jni_src",
|
||||
],
|
||||
hdrs = [
|
||||
":common_headers",
|
||||
":dec_headers",
|
||||
":enc_headers",
|
||||
],
|
||||
defines = [
|
||||
"BROTLI_EXTERNAL_DICTIONARY_DATA=",
|
||||
],
|
||||
deps = [
|
||||
":brotli_inc",
|
||||
":jni_inc",
|
||||
],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "dictionary",
|
||||
srcs = ["c/common/dictionary.bin"],
|
||||
)
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_prefix")
|
||||
|
||||
go_prefix("github.com/google/brotli")
|
||||
|
||||
@@ -73,7 +73,7 @@ function(hex_to_dec HEXADECIMAL DECIMAL)
|
||||
endfunction(hex_to_dec)
|
||||
|
||||
# Version information
|
||||
file(STRINGS "common/version.h" _brotli_version_line REGEX "^#define BROTLI_VERSION (0x[0-9a-fA-F]+)$")
|
||||
file(STRINGS "c/common/version.h" _brotli_version_line REGEX "^#define BROTLI_VERSION (0x[0-9a-fA-F]+)$")
|
||||
string(REGEX REPLACE "^#define BROTLI_VERSION 0x([0-9a-fA-F]+)$" "\\1" _brotli_version_hex "${_brotli_version_line}")
|
||||
hex_to_dec("${_brotli_version_hex}" _brotli_version)
|
||||
math(EXPR BROTLI_VERSION_MAJOR "${_brotli_version} >> 24")
|
||||
@@ -112,7 +112,7 @@ if(NOT LOG2_RES)
|
||||
endif()
|
||||
unset(LOG2_RES)
|
||||
|
||||
set(BROTLI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
set(BROTLI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/c/include")
|
||||
set(BROTLI_LIBRARIES_CORE brotlienc brotlidec brotlicommon)
|
||||
set(BROTLI_LIBRARIES ${BROTLI_LIBRARIES_CORE} ${LIBM_LIBRARY})
|
||||
mark_as_advanced(BROTLI_INCLUDE_DIRS BROTLI_LIBRARIES)
|
||||
@@ -126,30 +126,30 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
endif()
|
||||
|
||||
add_library(brotlicommon
|
||||
common/dictionary.c)
|
||||
c/common/dictionary.c)
|
||||
add_library(brotlidec
|
||||
dec/bit_reader.c
|
||||
dec/decode.c
|
||||
dec/huffman.c
|
||||
dec/state.c)
|
||||
c/dec/bit_reader.c
|
||||
c/dec/decode.c
|
||||
c/dec/huffman.c
|
||||
c/dec/state.c)
|
||||
add_library(brotlienc
|
||||
enc/backward_references.c
|
||||
enc/backward_references_hq.c
|
||||
enc/bit_cost.c
|
||||
enc/block_splitter.c
|
||||
enc/brotli_bit_stream.c
|
||||
enc/cluster.c
|
||||
enc/compress_fragment.c
|
||||
enc/compress_fragment_two_pass.c
|
||||
enc/dictionary_hash.c
|
||||
enc/encode.c
|
||||
enc/entropy_encode.c
|
||||
enc/histogram.c
|
||||
enc/literal_cost.c
|
||||
enc/memory.c
|
||||
enc/metablock.c
|
||||
enc/static_dict.c
|
||||
enc/utf8_util.c)
|
||||
c/enc/backward_references.c
|
||||
c/enc/backward_references_hq.c
|
||||
c/enc/bit_cost.c
|
||||
c/enc/block_splitter.c
|
||||
c/enc/brotli_bit_stream.c
|
||||
c/enc/cluster.c
|
||||
c/enc/compress_fragment.c
|
||||
c/enc/compress_fragment_two_pass.c
|
||||
c/enc/dictionary_hash.c
|
||||
c/enc/encode.c
|
||||
c/enc/entropy_encode.c
|
||||
c/enc/histogram.c
|
||||
c/enc/literal_cost.c
|
||||
c/enc/memory.c
|
||||
c/enc/metablock.c
|
||||
c/enc/static_dict.c
|
||||
c/enc/utf8_util.c)
|
||||
|
||||
# Older CMake versions does not understand INCLUDE_DIRECTORIES property.
|
||||
include_directories(${BROTLI_INCLUDE_DIRS})
|
||||
@@ -181,28 +181,28 @@ if(BROTLI_PARENT_DIRECTORY)
|
||||
set(BROTLI_LIBRARIES "${BROTLI_LIBRARIES}" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
# Build the bro executable
|
||||
add_executable(bro tools/bro.c)
|
||||
target_link_libraries(bro ${BROTLI_LIBRARIES})
|
||||
# Build the brotli executable
|
||||
add_executable(brotli c/tools/brotli.c)
|
||||
target_link_libraries(brotli ${BROTLI_LIBRARIES})
|
||||
|
||||
# Installation
|
||||
if(NOT BROTLI_BUNDLED_MODE)
|
||||
install (TARGETS bro RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(
|
||||
TARGETS brotli
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
install(
|
||||
TARGETS ${BROTLI_LIBRARIES_CORE}
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
else()
|
||||
install(TARGETS ${BROTLI_LIBRARIES_CORE}
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
)
|
||||
install(DIRECTORY ${BROTLI_INCLUDE_DIRS}/brotli DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
install(
|
||||
TARGETS ${BROTLI_LIBRARIES_CORE}
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY ${BROTLI_INCLUDE_DIRS}/brotli
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Tests
|
||||
@@ -229,9 +229,9 @@ if(NOT BROTLI_DISABLE_TESTS)
|
||||
tests/testdata/asyoulik.txt
|
||||
tests/testdata/lcet10.txt
|
||||
tests/testdata/plrabn12.txt
|
||||
enc/encode.c
|
||||
common/dictionary.h
|
||||
dec/decode.c)
|
||||
c/enc/encode.c
|
||||
c/common/dictionary.h
|
||||
c/dec/decode.c)
|
||||
|
||||
foreach(INPUT ${ROUNDTRIP_INPUTS})
|
||||
get_filename_component(OUTPUT_NAME "${INPUT}" NAME)
|
||||
@@ -243,7 +243,7 @@ if(NOT BROTLI_DISABLE_TESTS)
|
||||
add_test(NAME "${BROTLI_TEST_PREFIX}roundtrip/${INPUT}/${quality}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DBROTLI_WRAPPER=${BROTLI_WINE}
|
||||
-DBROTLI_CLI=$<TARGET_FILE:bro>
|
||||
-DBROTLI_CLI=$<TARGET_FILE:brotli>
|
||||
-DQUALITY=${quality}
|
||||
-DINPUT=${INPUT_FILE}
|
||||
-DOUTPUT=${OUTPUT_FILE}.${quality}
|
||||
@@ -260,7 +260,7 @@ if(NOT BROTLI_DISABLE_TESTS)
|
||||
add_test(NAME "${BROTLI_TEST_PREFIX}compatibility/${INPUT}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DBROTLI_WRAPPER=${BROTLI_WINE}
|
||||
-DBROTLI_CLI=$<TARGET_FILE:bro>
|
||||
-DBROTLI_CLI=$<TARGET_FILE:brotli>
|
||||
-DINPUT=${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/tests/run-compatibility-test.cmake)
|
||||
endforeach()
|
||||
|
||||
16
MANIFEST.in
16
MANIFEST.in
@@ -1,11 +1,11 @@
|
||||
include CONTRIBUTING.md
|
||||
include common/*.c
|
||||
include common/*.h
|
||||
include dec/*.c
|
||||
include dec/*.h
|
||||
include enc/*.c
|
||||
include enc/*.h
|
||||
include include/brotli/*.h
|
||||
include c/common/*.c
|
||||
include c/common/*.h
|
||||
include c/dec/*.c
|
||||
include c/dec/*.h
|
||||
include c/enc/*.c
|
||||
include c/enc/*.h
|
||||
include c/include/brotli/*.h
|
||||
include LICENSE
|
||||
include MANIFEST.in
|
||||
include python/bro.py
|
||||
@@ -13,4 +13,4 @@ include python/brotlimodule.cc
|
||||
include python/README.md
|
||||
include README.md
|
||||
include setup.py
|
||||
include tools/bro.c
|
||||
include c/tools/brotli.c
|
||||
|
||||
13
Makefile
13
Makefile
@@ -1,14 +1,15 @@
|
||||
OS := $(shell uname)
|
||||
LIBSOURCES = $(wildcard common/*.c) $(wildcard dec/*.c) $(wildcard enc/*.c)
|
||||
SOURCES = $(LIBSOURCES) tools/bro.c
|
||||
LIBSOURCES = $(wildcard c/common/*.c) $(wildcard c/dec/*.c) \
|
||||
$(wildcard c/enc/*.c)
|
||||
SOURCES = $(LIBSOURCES) c/tools/brotli.c
|
||||
BINDIR = bin
|
||||
OBJDIR = $(BINDIR)/obj
|
||||
LIBOBJECTS = $(addprefix $(OBJDIR)/, $(LIBSOURCES:.c=.o))
|
||||
OBJECTS = $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o))
|
||||
LIB_A = libbrotli.a
|
||||
EXECUTABLE = bro
|
||||
DIRS = $(OBJDIR)/common $(OBJDIR)/dec $(OBJDIR)/enc \
|
||||
$(OBJDIR)/tools $(BINDIR)/tmp
|
||||
EXECUTABLE = brotli
|
||||
DIRS = $(OBJDIR)/c/common $(OBJDIR)/c/dec $(OBJDIR)/c/enc \
|
||||
$(OBJDIR)/c/tools $(BINDIR)/tmp
|
||||
CFLAGS += -O2
|
||||
ifeq ($(os), Darwin)
|
||||
CPPFLAGS += -DOS_MACOSX
|
||||
@@ -38,5 +39,5 @@ clean:
|
||||
|
||||
.SECONDEXPANSION:
|
||||
$(OBJECTS): $$(patsubst %.o,%.c,$$(patsubst $$(OBJDIR)/%,%,$$@)) | $(DIRS)
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -Iinclude \
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -Ic/include \
|
||||
-c $(patsubst %.o,%.c,$(patsubst $(OBJDIR)/%,%,$@)) -o $@
|
||||
|
||||
39
README.md
39
README.md
@@ -8,24 +8,36 @@ and 2nd 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.
|
||||
|
||||
The specification of the Brotli Compressed Data Format is defined in [RFC 7932](https://www.ietf.org/rfc/rfc7932.txt).
|
||||
The specification of the Brotli Compressed Data Format is defined in [RFC 7932](https://tools.ietf.org/html/rfc7932).
|
||||
|
||||
Brotli is open-sourced under the MIT License, see the LICENSE file.
|
||||
|
||||
Brotli mailing list:
|
||||
https://groups.google.com/forum/#!forum/brotli
|
||||
|
||||
[](https://travis-ci.org/google/brotli)
|
||||
[](https://travis-ci.org/google/brotli)
|
||||
[](https://ci.appveyor.com/project/szabadka/brotli)
|
||||
|
||||
### Build instructions
|
||||
|
||||
#### Make
|
||||
#### Autotools-style CMake
|
||||
|
||||
To build and run tests, simply do:
|
||||
[configure-cmake](https://github.com/nemequ/configure-cmake) is an
|
||||
autotools-style configure script for CMake-based projects.
|
||||
|
||||
$ ./configure && make
|
||||
The basic commands to build, test and install brotli are:
|
||||
|
||||
If you want to install brotli, use one of the more advanced build systems below.
|
||||
$ mkdir out && cd out
|
||||
$ ../configure-cmake
|
||||
$ make
|
||||
$ make test
|
||||
$ make install
|
||||
|
||||
To build static libraries use `--disable-shared-libs` argument:
|
||||
|
||||
$ mkdir out-static && cd out-static
|
||||
$ ../configure-cmake --disable-shared-libs
|
||||
$ make install
|
||||
|
||||
#### Bazel
|
||||
|
||||
@@ -35,17 +47,18 @@ See [Bazel](http://www.bazel.build/)
|
||||
|
||||
The basic commands to build, test and install brotli are:
|
||||
|
||||
$ mkdir out && cd out && ../configure-cmake && make
|
||||
$ mkdir out && cd out
|
||||
$ cmake ..
|
||||
$ make
|
||||
$ make test
|
||||
$ make install
|
||||
|
||||
You can use other [CMake](https://cmake.org/) configuration. For example, to
|
||||
build static libraries and use a custom installation directory:
|
||||
build static libraries:
|
||||
|
||||
$ mkdir out-static && \
|
||||
cd out-static && \
|
||||
../configure-cmake --disable-shared-libs --prefix='/my/prefix/dir/'
|
||||
$ make install
|
||||
$ mkdir out-static && cd out-static
|
||||
$ cmake .. -DBUILD_SHARED_LIBS=OFF
|
||||
$ make
|
||||
|
||||
#### Premake5
|
||||
|
||||
@@ -69,3 +82,5 @@ and development.
|
||||
Independent [decoder](https://github.com/madler/brotli) implementation by Mark Adler, based entirely on format specification.
|
||||
|
||||
JavaScript port of brotli [decoder](https://github.com/devongovett/brotli.js). Could be used directly via `npm install brotli`
|
||||
|
||||
Hand ported [decoder / encoder](https://github.com/dominikhlbg/BrotliHaxe) in haxe by Dominik Homberger. Output source code: JavaScript, PHP, Python, Java and C#
|
||||
|
||||
36
WORKSPACE
36
WORKSPACE
@@ -11,8 +11,40 @@ maven_jar(
|
||||
git_repository(
|
||||
name = "io_bazel_rules_go",
|
||||
remote = "https://github.com/bazelbuild/rules_go.git",
|
||||
tag = "0.4.1",
|
||||
tag = "0.4.4",
|
||||
)
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_repositories")
|
||||
|
||||
new_http_archive(
|
||||
name = "openjdk_linux",
|
||||
url = "https://bazel-mirror.storage.googleapis.com/openjdk/azul-zulu-8.20.0.5-jdk8.0.121/zulu8.20.0.5-jdk8.0.121-linux_x64.tar.gz",
|
||||
sha256 = "7fdfb17d890406470b2303d749d3138e7f353749e67a0a22f542e1ab3e482745",
|
||||
build_file_content = """
|
||||
package(
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
filegroup(
|
||||
name = "jni_h",
|
||||
srcs = ["zulu8.20.0.5-jdk8.0.121-linux_x64/include/jni.h"],
|
||||
)
|
||||
filegroup(
|
||||
name = "jni_md_h",
|
||||
srcs = ["zulu8.20.0.5-jdk8.0.121-linux_x64/include/linux/jni_md.h"],
|
||||
)""",
|
||||
)
|
||||
|
||||
new_http_archive(
|
||||
name = "openjdk_macos",
|
||||
url = "https://bazel-mirror.storage.googleapis.com/openjdk/azul-zulu-8.20.0.5-jdk8.0.121/zulu8.20.0.5-jdk8.0.121-macosx_x64.zip",
|
||||
sha256 = "2a58bd1d9b0cbf0b3d8d1bcdd117c407e3d5a0ec01e2f53565c9bec5cf9ea78b",
|
||||
build_file_content = """
|
||||
package(
|
||||
default_visibility = ["//visibility:public"],
|
||||
)
|
||||
filegroup(
|
||||
name = "jni_md_h",
|
||||
srcs = ["zulu8.20.0.5-jdk8.0.121-macosx_x64/include/darwin/jni_md.h"],
|
||||
)""",
|
||||
)
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_repositories")
|
||||
go_repositories()
|
||||
|
||||
432
c/common/dictionary.bin
Executable file
432
c/common/dictionary.bin
Executable file
File diff suppressed because one or more lines are too long
@@ -10,24 +10,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static const BrotliDictionary kBrotliDictionary = {
|
||||
/* size_bits_by_length */
|
||||
{
|
||||
0, 0, 0, 0, 10, 10, 11, 11,
|
||||
10, 10, 10, 10, 10, 9, 9, 8,
|
||||
7, 7, 8, 7, 7, 6, 6, 5,
|
||||
5, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
|
||||
/* offsets_by_length */
|
||||
{
|
||||
0, 0, 0, 0, 0, 4096, 9216, 21504,
|
||||
35840, 44032, 53248, 63488, 74752, 87040, 93696, 100864,
|
||||
104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280,
|
||||
122016, 122784, 122784, 122784, 122784, 122784, 122784, 122784
|
||||
},
|
||||
|
||||
/* data */
|
||||
#ifndef BROTLI_EXTERNAL_DICTIONARY_DATA
|
||||
static const uint8_t kBrotliDictionaryData[] =
|
||||
{
|
||||
116,105,109,101,100,111,119,110,108,105,102,101,108,101,102,116,98,97,99,107,99,
|
||||
111,100,101,100,97,116,97,115,104,111,119,111,110,108,121,115,105,116,101,99,105
|
||||
@@ -5875,12 +5859,47 @@ static const BrotliDictionary kBrotliDictionary = {
|
||||
,164,181,224,164,190,224,164,136,224,164,184,224,164,149,224,165,141,224,164,176
|
||||
,224,164,191,224,164,175,224,164,164,224,164,190
|
||||
}
|
||||
;
|
||||
#endif /* !BROTLI_EXTERNAL_DICTIONARY_DATA */
|
||||
|
||||
static BrotliDictionary kBrotliDictionary = {
|
||||
/* size_bits_by_length */
|
||||
{
|
||||
0, 0, 0, 0, 10, 10, 11, 11,
|
||||
10, 10, 10, 10, 10, 9, 9, 8,
|
||||
7, 7, 8, 7, 7, 6, 6, 5,
|
||||
5, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
|
||||
/* offsets_by_length */
|
||||
{
|
||||
0, 0, 0, 0, 0, 4096, 9216, 21504,
|
||||
35840, 44032, 53248, 63488, 74752, 87040, 93696, 100864,
|
||||
104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280,
|
||||
122016, 122784, 122784, 122784, 122784, 122784, 122784, 122784
|
||||
},
|
||||
|
||||
/* data_size == sizeof(kBrotliDictionaryData) */
|
||||
122784,
|
||||
|
||||
/* data */
|
||||
#ifdef BROTLI_EXTERNAL_DICTIONARY_DATA
|
||||
NULL
|
||||
#else
|
||||
kBrotliDictionaryData
|
||||
#endif
|
||||
};
|
||||
|
||||
const BrotliDictionary* BrotliGetDictionary() {
|
||||
return &kBrotliDictionary;
|
||||
}
|
||||
|
||||
void BrotliSetDictionaryData(const uint8_t* data) {
|
||||
if (!!data && !kBrotliDictionary.data) {
|
||||
kBrotliDictionary.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -27,19 +27,33 @@ typedef struct BrotliDictionary {
|
||||
* 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];
|
||||
const uint8_t size_bits_by_length[32];
|
||||
|
||||
/* assert(offset[i + 1] == offset[i] + (bits[i] ? (i << bits[i]) : 0)) */
|
||||
uint32_t offsets_by_length[32];
|
||||
const uint32_t offsets_by_length[32];
|
||||
|
||||
/* assert(data_size == offsets_by_length[31]) */
|
||||
const 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. */
|
||||
/* assert(sizeof(data) == offsets_by_length[31]) */
|
||||
uint8_t data[122784];
|
||||
Specified size matches default (RFC 7932) dictionary. Its size is
|
||||
defined by data_size */
|
||||
const uint8_t* data;
|
||||
} BrotliDictionary;
|
||||
|
||||
BROTLI_COMMON_API extern 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
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
BrotliEncoderVersion methods. */
|
||||
|
||||
/* Semantic version, calculated as (MAJOR << 24) | (MINOR << 12) | PATCH */
|
||||
#define BROTLI_VERSION 0x0006000
|
||||
#define BROTLI_VERSION 0x1000000
|
||||
|
||||
#endif /* BROTLI_COMMON_VERSION_H_ */
|
||||
@@ -39,6 +39,11 @@ extern "C" {
|
||||
#define HUFFMAN_TABLE_BITS 8U
|
||||
#define HUFFMAN_TABLE_MASK 0xff
|
||||
|
||||
/* We need the slack region for the following reasons:
|
||||
- doing up to two 16-byte copies for fast backward copying
|
||||
- inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */
|
||||
static const uint32_t kRingBufferWriteAheadSlack = 42;
|
||||
|
||||
static const uint8_t kCodeLengthCodeOrder[BROTLI_CODE_LENGTH_CODES] = {
|
||||
1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
};
|
||||
@@ -52,6 +57,17 @@ static const uint8_t kCodeLengthPrefixValue[16] = {
|
||||
0, 4, 3, 2, 0, 4, 3, 1, 0, 4, 3, 2, 0, 4, 3, 5,
|
||||
};
|
||||
|
||||
BROTLI_BOOL BrotliDecoderSetParameter(
|
||||
BrotliDecoderState* state, BrotliDecoderParameter p, uint32_t value) {
|
||||
switch (p) {
|
||||
case BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION:
|
||||
state->canny_ringbuffer_allocation = !!value ? 0 : 1;
|
||||
return BROTLI_TRUE;
|
||||
|
||||
default: return BROTLI_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BrotliDecoderState* BrotliDecoderCreateInstance(
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
|
||||
BrotliDecoderState* state = 0;
|
||||
@@ -66,7 +82,6 @@ BrotliDecoderState* BrotliDecoderCreateInstance(
|
||||
}
|
||||
BrotliDecoderStateInitWithCustomAllocators(
|
||||
state, alloc_func, free_func, opaque);
|
||||
state->error_code = BROTLI_DECODER_NO_ERROR;
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1248,17 +1263,13 @@ static void BROTLI_NOINLINE WrapRingBuffer(BrotliDecoderState* s) {
|
||||
*/
|
||||
static BROTLI_BOOL BROTLI_NOINLINE BrotliEnsureRingBuffer(
|
||||
BrotliDecoderState* s) {
|
||||
/* We need the slack region for the following reasons:
|
||||
- doing up to two 16-byte copies for fast backward copying
|
||||
- inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */
|
||||
static const int kRingBufferWriteAheadSlack = 42;
|
||||
uint8_t* old_ringbuffer = s->ringbuffer;
|
||||
if (s->ringbuffer_size == s->new_ringbuffer_size) {
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->new_ringbuffer_size +
|
||||
kRingBufferWriteAheadSlack));
|
||||
s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->new_ringbuffer_size) +
|
||||
kRingBufferWriteAheadSlack);
|
||||
if (s->ringbuffer == 0) {
|
||||
/* Restore previous value. */
|
||||
s->ringbuffer = old_ringbuffer;
|
||||
@@ -1370,8 +1381,13 @@ static void BROTLI_NOINLINE BrotliCalculateRingBufferSize(
|
||||
output_size += s->meta_block_remaining_len;
|
||||
min_size = min_size < output_size ? output_size : min_size;
|
||||
|
||||
while ((new_ringbuffer_size >> 1) >= min_size) {
|
||||
new_ringbuffer_size >>= 1;
|
||||
if (!!s->canny_ringbuffer_allocation) {
|
||||
/* Reduce ring buffer size to save memory when server is unscrupulous.
|
||||
In worst case memory usage might be 1.5x bigger for a short period of
|
||||
ring buffer reallocation.*/
|
||||
while ((new_ringbuffer_size >> 1) >= min_size) {
|
||||
new_ringbuffer_size >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
s->new_ringbuffer_size = new_ringbuffer_size;
|
||||
@@ -1747,6 +1763,9 @@ CommandPostDecodeLiterals:
|
||||
/* Compensate double distance-ring-buffer roll. */
|
||||
s->dist_rb_idx += s->distance_context;
|
||||
offset += word_idx * i;
|
||||
if (BROTLI_PREDICT_FALSE(!s->dictionary->data)) {
|
||||
return BROTLI_FAILURE(BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET);
|
||||
}
|
||||
if (transform_idx < kNumTransforms) {
|
||||
const uint8_t* word = &s->dictionary->data[offset];
|
||||
int len = i;
|
||||
@@ -1899,6 +1918,10 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
size_t* available_out, uint8_t** next_out, size_t* total_out) {
|
||||
BrotliDecoderErrorCode result = BROTLI_DECODER_SUCCESS;
|
||||
BrotliBitReader* br = &s->br;
|
||||
/* Do not try to process further in a case of unrecoverable error. */
|
||||
if ((int)s->error_code < 0) {
|
||||
return BROTLI_DECODER_RESULT_ERROR;
|
||||
}
|
||||
if (*available_out && (!next_out || !*next_out)) {
|
||||
return SaveErrorCode(
|
||||
s, BROTLI_FAILURE(BROTLI_DECODER_ERROR_INVALID_ARGUMENTS));
|
||||
@@ -1919,7 +1942,13 @@ BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
if (result != BROTLI_DECODER_SUCCESS) { /* Error, needs more input/output */
|
||||
if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) {
|
||||
if (s->ringbuffer != 0) { /* Pro-actively push output. */
|
||||
WriteRingBuffer(s, available_out, next_out, total_out, BROTLI_TRUE);
|
||||
BrotliDecoderErrorCode intermediate_result = WriteRingBuffer(s,
|
||||
available_out, next_out, total_out, BROTLI_TRUE);
|
||||
/* WriteRingBuffer checks s->meta_block_remaining_len validity. */
|
||||
if ((int)intermediate_result < 0) {
|
||||
result = intermediate_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s->buffer_length != 0) { /* Used with internal buffer. */
|
||||
if (br->avail_in == 0) { /* Successfully finished read transaction. */
|
||||
@@ -2304,6 +2333,10 @@ void BrotliDecoderSetCustomDictionary(
|
||||
}
|
||||
|
||||
BROTLI_BOOL BrotliDecoderHasMoreOutput(const BrotliDecoderState* s) {
|
||||
/* After unrecoverable error remaining output is considered nonsensical. */
|
||||
if ((int)s->error_code < 0) {
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
return TO_BROTLI_BOOL(
|
||||
s->ringbuffer != 0 && UnwrittenBytes(s, BROTLI_FALSE) != 0);
|
||||
}
|
||||
@@ -2313,17 +2346,20 @@ const uint8_t* BrotliDecoderTakeOutput(BrotliDecoderState* s, size_t* size) {
|
||||
size_t available_out = *size ? *size : 1u << 24;
|
||||
size_t requested_out = available_out;
|
||||
BrotliDecoderErrorCode status;
|
||||
if (s->ringbuffer == 0) {
|
||||
if ((s->ringbuffer == 0) || ((int)s->error_code < 0)) {
|
||||
*size = 0;
|
||||
return 0;
|
||||
}
|
||||
WrapRingBuffer(s);
|
||||
status = WriteRingBuffer(s, &available_out, &result, 0, BROTLI_TRUE);
|
||||
/* Either WriteRingBuffer returns those "success" codes... */
|
||||
if (status == BROTLI_DECODER_SUCCESS ||
|
||||
status == BROTLI_DECODER_NEEDS_MORE_OUTPUT) {
|
||||
*size = requested_out - available_out;
|
||||
} else {
|
||||
/* This might happen if previous decoder error code was ignored. */
|
||||
/* ... or stream is broken. Normally this should be caught by
|
||||
BrotliDecoderDecompressStream, this is just a safeguard. */
|
||||
if ((int)status < 0) SaveErrorCode(s, status);
|
||||
*size = 0;
|
||||
result = 0;
|
||||
}
|
||||
@@ -41,6 +41,8 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||
s->memory_manager_opaque = opaque;
|
||||
}
|
||||
|
||||
s->error_code = 0; /* BROTLI_DECODER_NO_ERROR */
|
||||
|
||||
BrotliInitBitReader(&s->br);
|
||||
s->state = BROTLI_STATE_UNINITED;
|
||||
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
|
||||
@@ -85,7 +87,11 @@ void BrotliDecoderStateInitWithCustomAllocators(BrotliDecoderState* s,
|
||||
s->custom_dict_size = 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->max_distance = 0;
|
||||
s->dist_rb[0] = 16;
|
||||
@@ -172,7 +172,7 @@ struct BrotliDecoderStateStruct {
|
||||
uint32_t space;
|
||||
|
||||
HuffmanCode table[32];
|
||||
/* List of of symbol chains. */
|
||||
/* List of heads of symbol chains. */
|
||||
uint16_t* symbol_lists;
|
||||
/* Storage from symbol_lists. */
|
||||
uint16_t symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1 +
|
||||
@@ -215,6 +215,7 @@ struct BrotliDecoderStateStruct {
|
||||
unsigned int is_uncompressed : 1;
|
||||
unsigned int is_metadata : 1;
|
||||
unsigned int should_wrap_ringbuffer : 1;
|
||||
unsigned int canny_ringbuffer_allocation : 1;
|
||||
unsigned int size_nibbles : 8;
|
||||
uint32_t window_bits;
|
||||
|
||||
@@ -549,8 +549,8 @@ void BrotliZopfliCreateCommands(const size_t num_bytes,
|
||||
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);
|
||||
InitCommand(&commands[i], insert_length,
|
||||
copy_length, (int)len_code - (int)copy_length, dist_code);
|
||||
|
||||
if (!is_dictionary && dist_code > 0) {
|
||||
dist_cache[3] = dist_cache[2];
|
||||
@@ -38,29 +38,29 @@ static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
|
||||
size_t max_distance = BROTLI_MIN(size_t, position, max_backward_limit);
|
||||
HasherSearchResult sr;
|
||||
sr.len = 0;
|
||||
sr.len_x_code = 0;
|
||||
sr.len_code_delta = 0;
|
||||
sr.distance = 0;
|
||||
sr.score = kMinScore;
|
||||
if (FN(FindLongestMatch)(hasher, dictionary, dictionary_hash,
|
||||
ringbuffer, ringbuffer_mask, dist_cache,
|
||||
position, max_length, max_distance, &sr)) {
|
||||
FN(FindLongestMatch)(hasher, dictionary, dictionary_hash, ringbuffer,
|
||||
ringbuffer_mask, dist_cache, position,
|
||||
max_length, max_distance, &sr);
|
||||
if (sr.score > kMinScore) {
|
||||
/* Found a match. Let's look for something even better ahead. */
|
||||
int delayed_backward_references_in_row = 0;
|
||||
--max_length;
|
||||
for (;; --max_length) {
|
||||
const score_t cost_diff_lazy = 175;
|
||||
BROTLI_BOOL is_match_found;
|
||||
HasherSearchResult sr2;
|
||||
sr2.len = params->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH ?
|
||||
BROTLI_MIN(size_t, sr.len - 1, max_length) : 0;
|
||||
sr2.len_x_code = 0;
|
||||
sr2.len_code_delta = 0;
|
||||
sr2.distance = 0;
|
||||
sr2.score = kMinScore;
|
||||
max_distance = BROTLI_MIN(size_t, position + 1, max_backward_limit);
|
||||
is_match_found = FN(FindLongestMatch)(hasher, dictionary,
|
||||
dictionary_hash, ringbuffer, ringbuffer_mask, dist_cache,
|
||||
position + 1, max_length, max_distance, &sr2);
|
||||
if (is_match_found && sr2.score >= sr.score + cost_diff_lazy) {
|
||||
FN(FindLongestMatch)(hasher, dictionary, dictionary_hash, ringbuffer,
|
||||
ringbuffer_mask, dist_cache, position + 1,
|
||||
max_length, max_distance, &sr2);
|
||||
if (sr2.score >= sr.score + cost_diff_lazy) {
|
||||
/* Ok, let's just write one byte for now and start a match from the
|
||||
next byte. */
|
||||
++position;
|
||||
@@ -88,7 +88,7 @@ static BROTLI_NOINLINE void FN(CreateBackwardReferences)(
|
||||
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++, insert_length, sr.len, sr.len_code_delta,
|
||||
distance_code);
|
||||
}
|
||||
*num_literals += insert_length;
|
||||
@@ -1325,17 +1325,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)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -96,10 +96,6 @@ BROTLI_INTERNAL void BrotliStoreUncompressedMetaBlock(
|
||||
BROTLI_BOOL is_final_block, const uint8_t* input, size_t position,
|
||||
size_t mask, size_t len, size_t* storage_ix, uint8_t* 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)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -17,6 +17,8 @@ BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
|
||||
size_t* num_pairs) CODE({
|
||||
BROTLI_BOOL is_good_pair = BROTLI_FALSE;
|
||||
HistogramPair p;
|
||||
p.idx1 = p.idx2 = 0;
|
||||
p.cost_diff = p.cost_combo = 0;
|
||||
if (idx1 == idx2) {
|
||||
return;
|
||||
}
|
||||
@@ -114,17 +114,19 @@ typedef struct Command {
|
||||
|
||||
/* 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,
|
||||
size_t copylen, size_t copylen_code, size_t distance_code) {
|
||||
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->copy_len_ = (uint32_t)(copylen | ((copylen_code ^ copylen) << 24));
|
||||
self->copy_len_ = (uint32_t)(copylen | (delta << 24));
|
||||
/* 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
|
||||
clustering if needed. */
|
||||
PrefixEncodeCopyDistance(
|
||||
distance_code, 0, 0, &self->dist_prefix_, &self->dist_extra_);
|
||||
GetLengthCode(
|
||||
insertlen, copylen_code, TO_BROTLI_BOOL(self->dist_prefix_ == 0),
|
||||
&self->cmd_prefix_);
|
||||
insertlen, (size_t)((int)copylen + copylen_code_delta),
|
||||
TO_BROTLI_BOOL(self->dist_prefix_ == 0), &self->cmd_prefix_);
|
||||
}
|
||||
|
||||
static BROTLI_INLINE void InitInsertCommand(Command* self, size_t insertlen) {
|
||||
@@ -167,7 +169,8 @@ static BROTLI_INLINE uint32_t CommandCopyLen(const Command* self) {
|
||||
}
|
||||
|
||||
static BROTLI_INLINE uint32_t CommandCopyLenCode(const Command* self) {
|
||||
return (self->copy_len_ & 0xFFFFFF) ^ (self->copy_len_ >> 24);
|
||||
int32_t delta = (int8_t)((uint8_t)(self->copy_len_ >> 24));
|
||||
return (uint32_t)((int32_t)(self->copy_len_ & 0xFFFFFF) + delta);
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
@@ -1792,23 +1792,6 @@ uint32_t BrotliEncoderVersion(void) {
|
||||
return BROTLI_VERSION;
|
||||
}
|
||||
|
||||
|
||||
/* DEPRECATED >>> */
|
||||
size_t BrotliEncoderInputBlockSize(BrotliEncoderState* s) {
|
||||
return InputBlockSize(s);
|
||||
}
|
||||
void BrotliEncoderCopyInputToRingBuffer(BrotliEncoderState* s,
|
||||
const size_t input_size,
|
||||
const uint8_t* input_buffer) {
|
||||
CopyInputToRingBuffer(s, input_size, input_buffer);
|
||||
}
|
||||
BROTLI_BOOL BrotliEncoderWriteData(
|
||||
BrotliEncoderState* s, const BROTLI_BOOL is_last,
|
||||
const BROTLI_BOOL force_flush, size_t* out_size, uint8_t** output) {
|
||||
return EncodeData(s, is_last, force_flush, out_size, output);
|
||||
}
|
||||
/* <<< DEPRECATED */
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -62,9 +62,9 @@ static const uint64_t kCutoffTransforms =
|
||||
|
||||
typedef struct HasherSearchResult {
|
||||
size_t len;
|
||||
size_t len_x_code; /* == len ^ len_code */
|
||||
size_t distance;
|
||||
score_t score;
|
||||
int len_code_delta; /* == len_code - len */
|
||||
} HasherSearchResult;
|
||||
|
||||
/* kHashMul32 multiplier has these properties:
|
||||
@@ -178,22 +178,21 @@ static BROTLI_INLINE BROTLI_BOOL TestStaticDictionaryItem(
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
out->len = matchlen;
|
||||
out->len_x_code = len ^ matchlen;
|
||||
out->len_code_delta = (int)len - (int)matchlen;
|
||||
out->distance = backward;
|
||||
out->score = score;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
static BROTLI_INLINE BROTLI_BOOL SearchInStaticDictionary(
|
||||
static BROTLI_INLINE void SearchInStaticDictionary(
|
||||
const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
|
||||
HasherHandle handle, const uint8_t* data, size_t max_length,
|
||||
size_t max_backward, HasherSearchResult* out, BROTLI_BOOL shallow) {
|
||||
size_t key;
|
||||
size_t i;
|
||||
BROTLI_BOOL is_match_found = BROTLI_FALSE;
|
||||
HasherCommon* self = GetHasherCommon(handle);
|
||||
if (self->dict_num_matches < (self->dict_num_lookups >> 7)) {
|
||||
return BROTLI_FALSE;
|
||||
return;
|
||||
}
|
||||
key = Hash14(data) << 1;
|
||||
for (i = 0; i < (shallow ? 1u : 2u); ++i, ++key) {
|
||||
@@ -204,11 +203,9 @@ static BROTLI_INLINE BROTLI_BOOL SearchInStaticDictionary(
|
||||
dictionary, item, data, max_length, max_backward, out);
|
||||
if (item_matches) {
|
||||
self->dict_num_matches++;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return is_match_found;
|
||||
}
|
||||
|
||||
typedef struct BackwardMatch {
|
||||
@@ -152,8 +152,8 @@ static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||
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|.
|
||||
Returns 1 when match is found, otherwise 0. */
|
||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
|out|->score is updated only if a better match is found. */
|
||||
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||
const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
|
||||
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||
const int* BROTLI_RESTRICT distance_cache,
|
||||
@@ -161,15 +161,15 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
HasherSearchResult* BROTLI_RESTRICT out) {
|
||||
HashForgetfulChain* self = FN(Self)(handle);
|
||||
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. */
|
||||
score_t min_score = out->score;
|
||||
score_t best_score = out->score;
|
||||
size_t best_len = out->len;
|
||||
size_t i;
|
||||
const size_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
||||
const uint8_t tiny_hash = (uint8_t)(key);
|
||||
out->len = 0;
|
||||
out->len_x_code = 0;
|
||||
out->len_code_delta = 0;
|
||||
/* Try last distance first. */
|
||||
for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
|
||||
const size_t backward = (size_t)distance_cache[i];
|
||||
@@ -194,7 +194,6 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
out->len = best_len;
|
||||
out->distance = backward;
|
||||
out->score = best_score;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,19 +233,17 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
out->len = best_len;
|
||||
out->distance = backward;
|
||||
out->score = best_score;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FN(Store)(handle, data, ring_buffer_mask, cur_ix);
|
||||
}
|
||||
if (!is_match_found) {
|
||||
is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
if (out->score == min_score) {
|
||||
SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
handle, &data[cur_ix_masked], max_length, max_backward, out,
|
||||
BROTLI_FALSE);
|
||||
}
|
||||
return is_match_found;
|
||||
}
|
||||
|
||||
#undef BANK_SIZE
|
||||
@@ -156,8 +156,8 @@ static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||
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|.
|
||||
Returns true when match is found, otherwise false. */
|
||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
|out|->score is updated only if a better match is found. */
|
||||
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||
const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
|
||||
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
||||
@@ -168,13 +168,13 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
uint16_t* num = FN(Num)(self);
|
||||
uint32_t* buckets = FN(Buckets)(self);
|
||||
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. */
|
||||
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_x_code = 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];
|
||||
@@ -209,7 +209,6 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
out->len = best_len;
|
||||
out->distance = backward;
|
||||
out->score = best_score;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,7 +249,6 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
out->len = best_len;
|
||||
out->distance = backward;
|
||||
out->score = best_score;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,12 +256,11 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
|
||||
++num[key];
|
||||
}
|
||||
if (!is_match_found) {
|
||||
is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
if (min_score == out->score) {
|
||||
SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
handle, &data[cur_ix_masked], max_length, max_backward, out,
|
||||
BROTLI_FALSE);
|
||||
}
|
||||
return is_match_found;
|
||||
}
|
||||
|
||||
#undef HashLongestMatch
|
||||
@@ -149,8 +149,8 @@ static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||
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|.
|
||||
Returns true when match is found, otherwise false. */
|
||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
|out|->score is updated only if a better match is found. */
|
||||
static BROTLI_INLINE void FN(FindLongestMatch)(HasherHandle handle,
|
||||
const BrotliDictionary* dictionary, const uint16_t* dictionary_hash,
|
||||
const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,
|
||||
const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,
|
||||
@@ -161,13 +161,13 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
uint16_t* num = FN(Num)(self);
|
||||
uint32_t* buckets = FN(Buckets)(self);
|
||||
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. */
|
||||
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_x_code = 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];
|
||||
@@ -202,7 +202,6 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
out->len = best_len;
|
||||
out->distance = backward;
|
||||
out->score = best_score;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +241,6 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
out->len = best_len;
|
||||
out->distance = backward;
|
||||
out->score = best_score;
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,12 +248,11 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HasherHandle handle,
|
||||
bucket[num[key] & self->block_mask_] = (uint32_t)cur_ix;
|
||||
++num[key];
|
||||
}
|
||||
if (!is_match_found) {
|
||||
is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
if (min_score == out->score) {
|
||||
SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
handle, &data[cur_ix_masked], max_length, max_backward, out,
|
||||
BROTLI_FALSE);
|
||||
}
|
||||
return is_match_found;
|
||||
}
|
||||
|
||||
#undef HashLongestMatch
|
||||
@@ -123,8 +123,8 @@ static BROTLI_INLINE void FN(PrepareDistanceCache)(
|
||||
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|.
|
||||
Returns true if match is found, otherwise false. */
|
||||
static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||
|out|->score is updated only if a better match is found. */
|
||||
static BROTLI_INLINE void FN(FindLongestMatch)(
|
||||
HasherHandle handle, const BrotliDictionary* dictionary,
|
||||
const uint16_t* dictionary_hash, const uint8_t* BROTLI_RESTRICT data,
|
||||
const size_t ring_buffer_mask, const int* BROTLI_RESTRICT distance_cache,
|
||||
@@ -135,12 +135,12 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||
const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
|
||||
const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
|
||||
int compare_char = data[cur_ix_masked + best_len_in];
|
||||
score_t min_score = out->score;
|
||||
score_t best_score = out->score;
|
||||
size_t best_len = best_len_in;
|
||||
size_t cached_backward = (size_t)distance_cache[0];
|
||||
size_t prev_ix = cur_ix - cached_backward;
|
||||
BROTLI_BOOL is_match_found = BROTLI_FALSE;
|
||||
out->len_x_code = 0;
|
||||
out->len_code_delta = 0;
|
||||
if (prev_ix < cur_ix) {
|
||||
prev_ix &= (uint32_t)ring_buffer_mask;
|
||||
if (compare_char == data[prev_ix + best_len]) {
|
||||
@@ -148,17 +148,18 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||
&data[cur_ix_masked],
|
||||
max_length);
|
||||
if (len >= 4) {
|
||||
best_score = BackwardReferenceScoreUsingLastDistance(len);
|
||||
best_len = len;
|
||||
out->len = len;
|
||||
out->distance = cached_backward;
|
||||
out->score = best_score;
|
||||
compare_char = data[cur_ix_masked + best_len];
|
||||
if (BUCKET_SWEEP == 1) {
|
||||
self->buckets_[key] = (uint32_t)cur_ix;
|
||||
return BROTLI_TRUE;
|
||||
} else {
|
||||
is_match_found = BROTLI_TRUE;
|
||||
const score_t score = BackwardReferenceScoreUsingLastDistance(len);
|
||||
if (best_score < score) {
|
||||
best_score = score;
|
||||
best_len = len;
|
||||
out->len = len;
|
||||
out->distance = cached_backward;
|
||||
out->score = best_score;
|
||||
compare_char = data[cur_ix_masked + best_len];
|
||||
if (BUCKET_SWEEP == 1) {
|
||||
self->buckets_[key] = (uint32_t)cur_ix;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,19 +173,22 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||
backward = cur_ix - prev_ix;
|
||||
prev_ix &= (uint32_t)ring_buffer_mask;
|
||||
if (compare_char != data[prev_ix + best_len_in]) {
|
||||
return BROTLI_FALSE;
|
||||
return;
|
||||
}
|
||||
if (BROTLI_PREDICT_FALSE(backward == 0 || backward > max_backward)) {
|
||||
return BROTLI_FALSE;
|
||||
return;
|
||||
}
|
||||
len = FindMatchLengthWithLimit(&data[prev_ix],
|
||||
&data[cur_ix_masked],
|
||||
max_length);
|
||||
if (len >= 4) {
|
||||
out->len = len;
|
||||
out->distance = backward;
|
||||
out->score = BackwardReferenceScore(len, backward);
|
||||
return BROTLI_TRUE;
|
||||
const score_t score = BackwardReferenceScore(len, backward);
|
||||
if (best_score < score) {
|
||||
out->len = len;
|
||||
out->distance = backward;
|
||||
out->score = score;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint32_t *bucket = self->buckets_ + key;
|
||||
@@ -212,18 +216,16 @@ static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(
|
||||
out->distance = backward;
|
||||
out->score = score;
|
||||
compare_char = data[cur_ix_masked + best_len];
|
||||
is_match_found = BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (USE_DICTIONARY && !is_match_found) {
|
||||
is_match_found = SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
if (USE_DICTIONARY && min_score == out->score) {
|
||||
SearchInStaticDictionary(dictionary, dictionary_hash,
|
||||
handle, &data[cur_ix_masked], max_length, max_backward, out,
|
||||
BROTLI_TRUE);
|
||||
}
|
||||
self->buckets_[key + ((cur_ix >> 3) % BUCKET_SWEEP)] = (uint32_t)cur_ix;
|
||||
return is_match_found;
|
||||
}
|
||||
|
||||
#undef HASH_MAP_SIZE
|
||||
@@ -40,7 +40,7 @@ BROTLI_INTERNAL void BrotliInitMemoryManager(
|
||||
|
||||
BROTLI_INTERNAL void* BrotliAllocate(MemoryManager* m, size_t n);
|
||||
#define BROTLI_ALLOC(M, T, N) \
|
||||
((N) ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL)
|
||||
((N) > 0 ? ((T*)BrotliAllocate((M), (N) * sizeof(T))) : NULL)
|
||||
|
||||
BROTLI_INTERNAL void BrotliFree(MemoryManager* m, void* p);
|
||||
#define BROTLI_FREE(M, P) { \
|
||||
@@ -84,8 +84,9 @@ typedef enum {
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \
|
||||
\
|
||||
/* -16..-19 codes are reserved */ \
|
||||
/* -16..-18 codes are reserved */ \
|
||||
\
|
||||
BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR \
|
||||
\
|
||||
/* Memory allocation problems */ \
|
||||
@@ -125,6 +126,29 @@ typedef enum {
|
||||
*/
|
||||
#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
|
||||
} 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.
|
||||
*
|
||||
@@ -207,9 +231,9 @@ BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompress(
|
||||
* 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 output space is provided
|
||||
* @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT 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
|
||||
*/
|
||||
@@ -303,7 +327,7 @@ BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* state);
|
||||
* the input and produced all of the output
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* state);
|
||||
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
* Acquires a detailed error code.
|
||||
@@ -316,7 +340,7 @@ BROTLI_BOOL BrotliDecoderIsFinished(const BrotliDecoderState* state);
|
||||
* @param state decoder instance
|
||||
* @returns last saved error code
|
||||
*/
|
||||
BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
|
||||
BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
|
||||
const BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
@@ -36,11 +36,6 @@ extern "C" {
|
||||
/** Maximal value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||
#define BROTLI_MAX_QUALITY 11
|
||||
|
||||
BROTLI_DEPRECATED static const int kBrotliMinWindowBits =
|
||||
BROTLI_MIN_WINDOW_BITS;
|
||||
BROTLI_DEPRECATED static const int kBrotliMaxWindowBits =
|
||||
BROTLI_MAX_WINDOW_BITS;
|
||||
|
||||
/** Options for ::BROTLI_PARAM_MODE parameter. */
|
||||
typedef enum BrotliEncoderMode {
|
||||
/**
|
||||
@@ -228,20 +223,6 @@ BROTLI_ENC_API BrotliEncoderState* BrotliEncoderCreateInstance(
|
||||
*/
|
||||
BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
|
||||
|
||||
/* Calculates maximum input size that can be processed at once. */
|
||||
BROTLI_DEPRECATED BROTLI_ENC_API size_t BrotliEncoderInputBlockSize(
|
||||
BrotliEncoderState* state);
|
||||
|
||||
/* Copies the given input data to the internal ring buffer. */
|
||||
BROTLI_DEPRECATED BROTLI_ENC_API void BrotliEncoderCopyInputToRingBuffer(
|
||||
BrotliEncoderState* state, const size_t input_size,
|
||||
const uint8_t* input_buffer);
|
||||
|
||||
/* Processes the accumulated input. */
|
||||
BROTLI_DEPRECATED BROTLI_ENC_API BROTLI_BOOL BrotliEncoderWriteData(
|
||||
BrotliEncoderState* state, const BROTLI_BOOL is_last,
|
||||
const BROTLI_BOOL force_flush, size_t* out_size, uint8_t** output);
|
||||
|
||||
/**
|
||||
* Prepends imaginary LZ77 dictionary.
|
||||
*
|
||||
940
c/tools/brotli.c
Executable file
940
c/tools/brotli.c
Executable file
@@ -0,0 +1,940 @@
|
||||
/* Copyright 2014 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Command line interface for Brotli library. */
|
||||
|
||||
#include <errno.h>
|
||||
#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/version.h"
|
||||
#include <brotli/decode.h>
|
||||
#include <brotli/encode.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#include <share.h>
|
||||
#include <sys/utime.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 utimbuf _utimbuf
|
||||
#define utime _utime
|
||||
|
||||
#define fopen ms_fopen
|
||||
#define open ms_open
|
||||
|
||||
#define chmod(F, P) (0)
|
||||
#define chown(F, O, G) (0)
|
||||
|
||||
#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 */
|
||||
|
||||
typedef enum {
|
||||
COMMAND_COMPRESS,
|
||||
COMMAND_DECOMPRESS,
|
||||
COMMAND_HELP,
|
||||
COMMAND_INVALID,
|
||||
COMMAND_TEST_INTEGRITY,
|
||||
COMMAND_NOOP,
|
||||
COMMAND_VERSION
|
||||
} Command;
|
||||
|
||||
#define DEFAULT_LGWIN 22
|
||||
#define DEFAULT_SUFFIX ".br"
|
||||
#define MAX_OPTIONS 20
|
||||
|
||||
typedef struct {
|
||||
/* Parameters */
|
||||
int quality;
|
||||
int lgwin;
|
||||
BROTLI_BOOL force_overwrite;
|
||||
BROTLI_BOOL junk_source;
|
||||
BROTLI_BOOL copy_stat;
|
||||
BROTLI_BOOL verbose;
|
||||
BROTLI_BOOL write_to_stdout;
|
||||
BROTLI_BOOL test_integrity;
|
||||
BROTLI_BOOL decompress;
|
||||
const char* output_path;
|
||||
const char* dictionary_path;
|
||||
const char* suffix;
|
||||
int not_input_indices[MAX_OPTIONS];
|
||||
size_t longest_path_len;
|
||||
size_t input_count;
|
||||
|
||||
/* Inner state */
|
||||
int argc;
|
||||
char** argv;
|
||||
uint8_t* dictionary;
|
||||
size_t dictionary_size;
|
||||
char* modified_path; /* Storage for path with appended / cut suffix */
|
||||
int iterator;
|
||||
int ignore;
|
||||
BROTLI_BOOL iterator_error;
|
||||
uint8_t* buffer;
|
||||
uint8_t* input;
|
||||
uint8_t* output;
|
||||
const char* current_input_path;
|
||||
const char* current_output_path;
|
||||
FILE* fin;
|
||||
FILE* fout;
|
||||
} Context;
|
||||
|
||||
/* Parse up to 5 decimal digits. */
|
||||
static BROTLI_BOOL ParseInt(const char* s, int low, int high, int* result) {
|
||||
int value = 0;
|
||||
int i;
|
||||
for (i = 0; i < 5; ++i) {
|
||||
char c = s[i];
|
||||
if (c == 0) break;
|
||||
if (s[i] < '0' || s[i] > '9') return BROTLI_FALSE;
|
||||
value = (10 * value) + (c - '0');
|
||||
}
|
||||
if (i == 0) return BROTLI_FALSE;
|
||||
if (i > 1 && s[0] == '0') return BROTLI_FALSE;
|
||||
if (s[i] != 0) return BROTLI_FALSE;
|
||||
if (value < low || value > high) return BROTLI_FALSE;
|
||||
*result = value;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
/* Returns "base file name" or its tail, if it contains '/' or '\'. */
|
||||
static const char* FileName(const char* path) {
|
||||
const char* separator_position = strrchr(path, '/');
|
||||
if (separator_position) path = separator_position + 1;
|
||||
separator_position = strrchr(path, '\\');
|
||||
if (separator_position) path = separator_position + 1;
|
||||
return path;
|
||||
}
|
||||
|
||||
/* Detect if the program name is a special alias that infers a command type. */
|
||||
static Command ParseAlias(const char* name) {
|
||||
/* TODO: cast name to lower case? */
|
||||
const char* unbrotli = "unbrotli";
|
||||
size_t unbrotli_len = strlen(unbrotli);
|
||||
name = FileName(name);
|
||||
/* Partial comparison. On Windows there could be ".exe" suffix. */
|
||||
if (strncmp(name, unbrotli, unbrotli_len)) {
|
||||
char terminator = name[unbrotli_len];
|
||||
if (terminator == 0 || terminator == '.') return COMMAND_DECOMPRESS;
|
||||
}
|
||||
return COMMAND_COMPRESS;
|
||||
}
|
||||
|
||||
static Command ParseParams(Context* params) {
|
||||
int argc = params->argc;
|
||||
char** argv = params->argv;
|
||||
int i;
|
||||
int next_option_index = 0;
|
||||
size_t input_count = 0;
|
||||
size_t longest_path_len = 1;
|
||||
BROTLI_BOOL command_set = BROTLI_FALSE;
|
||||
BROTLI_BOOL quality_set = BROTLI_FALSE;
|
||||
BROTLI_BOOL output_set = BROTLI_FALSE;
|
||||
BROTLI_BOOL keep_set = BROTLI_FALSE;
|
||||
BROTLI_BOOL lgwin_set = BROTLI_FALSE;
|
||||
BROTLI_BOOL suffix_set = BROTLI_FALSE;
|
||||
BROTLI_BOOL after_dash_dash = BROTLI_FALSE;
|
||||
Command command = ParseAlias(argv[0]);
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
const char* arg = argv[i];
|
||||
size_t arg_len = strlen(arg);
|
||||
|
||||
/* C99 5.1.2.2.1: "members argv[0] through argv[argc-1] inclusive shall
|
||||
contain pointers to strings"; NULL and 0-length are not forbidden. */
|
||||
if (!arg || arg_len == 0) {
|
||||
params->not_input_indices[next_option_index++] = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Too many options. The expected longest option list is:
|
||||
"-q 0 -w 10 -o f -D d -S b -d -f -k -n -v --", i.e. 16 items in total.
|
||||
This check is an additinal guard that is never triggered, but provides an
|
||||
additional guard for future changes. */
|
||||
if (next_option_index > (MAX_OPTIONS - 2)) {
|
||||
return COMMAND_INVALID;
|
||||
}
|
||||
|
||||
/* Input file entry. */
|
||||
if (after_dash_dash || arg[0] != '-' || arg_len == 1) {
|
||||
input_count++;
|
||||
if (longest_path_len < arg_len) longest_path_len = arg_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Not a file entry. */
|
||||
params->not_input_indices[next_option_index++] = i;
|
||||
|
||||
/* '--' entry stop parsing arguments. */
|
||||
if (arg_len == 2 && arg[1] == '-') {
|
||||
after_dash_dash = BROTLI_TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Simple / coalesced options. */
|
||||
if (arg[1] != '-') {
|
||||
size_t j;
|
||||
for (j = 1; j < arg_len; ++j) {
|
||||
char c = arg[j];
|
||||
if (c >= '0' && c <= '9') {
|
||||
if (quality_set) return COMMAND_INVALID;
|
||||
quality_set = BROTLI_TRUE;
|
||||
params->quality = c - '0';
|
||||
continue;
|
||||
} else if (c == 'c') {
|
||||
if (output_set) return COMMAND_INVALID;
|
||||
output_set = BROTLI_TRUE;
|
||||
params->write_to_stdout = BROTLI_TRUE;
|
||||
continue;
|
||||
} else if (c == 'd') {
|
||||
if (command_set) return COMMAND_INVALID;
|
||||
command_set = BROTLI_TRUE;
|
||||
command = COMMAND_DECOMPRESS;
|
||||
continue;
|
||||
} else if (c == 'f') {
|
||||
if (params->force_overwrite) return COMMAND_INVALID;
|
||||
params->force_overwrite = BROTLI_TRUE;
|
||||
continue;
|
||||
} else if (c == 'h') {
|
||||
/* Don't parse further. */
|
||||
return COMMAND_HELP;
|
||||
} else if (c == 'j' || c == 'k') {
|
||||
if (keep_set) return COMMAND_INVALID;
|
||||
keep_set = BROTLI_TRUE;
|
||||
params->junk_source = TO_BROTLI_BOOL(c == 'j');
|
||||
continue;
|
||||
} else if (c == 'n') {
|
||||
if (!params->copy_stat) return COMMAND_INVALID;
|
||||
params->copy_stat = BROTLI_FALSE;
|
||||
continue;
|
||||
} else if (c == 't') {
|
||||
if (command_set) return COMMAND_INVALID;
|
||||
command_set = BROTLI_TRUE;
|
||||
command = COMMAND_TEST_INTEGRITY;
|
||||
continue;
|
||||
} else if (c == 'v') {
|
||||
if (params->verbose) return COMMAND_INVALID;
|
||||
params->verbose = BROTLI_TRUE;
|
||||
continue;
|
||||
} else if (c == 'V') {
|
||||
/* Don't parse further. */
|
||||
return COMMAND_VERSION;
|
||||
} else if (c == 'Z') {
|
||||
if (quality_set) return COMMAND_INVALID;
|
||||
quality_set = BROTLI_TRUE;
|
||||
params->quality = 11;
|
||||
continue;
|
||||
}
|
||||
/* o/q/w/D/S with parameter is expected */
|
||||
if (c != 'o' && c != 'q' && c != 'w' && c != 'D' && c != 'S') {
|
||||
return COMMAND_INVALID;
|
||||
}
|
||||
if (j + 1 != arg_len) return COMMAND_INVALID;
|
||||
i++;
|
||||
if (i == argc || !argv[i] || argv[i][0] == 0) return COMMAND_INVALID;
|
||||
params->not_input_indices[next_option_index++] = i;
|
||||
if (c == 'o') {
|
||||
if (output_set) return COMMAND_INVALID;
|
||||
params->output_path = argv[i];
|
||||
} else if (c == 'q') {
|
||||
if (quality_set) return COMMAND_INVALID;
|
||||
quality_set = ParseInt(argv[i], BROTLI_MIN_QUALITY,
|
||||
BROTLI_MAX_QUALITY, ¶ms->quality);
|
||||
if (!quality_set) return COMMAND_INVALID;
|
||||
} else if (c == 'w') {
|
||||
if (lgwin_set) return COMMAND_INVALID;
|
||||
lgwin_set = ParseInt(argv[i], 0,
|
||||
BROTLI_MAX_WINDOW_BITS, ¶ms->lgwin);
|
||||
if (!lgwin_set) return COMMAND_INVALID;
|
||||
if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) {
|
||||
return COMMAND_INVALID;
|
||||
}
|
||||
} else if (c == 'D') {
|
||||
if (params->dictionary_path) return COMMAND_INVALID;
|
||||
params->dictionary_path = argv[i];
|
||||
} else if (c == 'S') {
|
||||
if (suffix_set) return COMMAND_INVALID;
|
||||
suffix_set = BROTLI_TRUE;
|
||||
params->suffix = argv[i];
|
||||
}
|
||||
}
|
||||
} else { /* Double-dash. */
|
||||
arg = &arg[2];
|
||||
if (strcmp("best", arg) == 0) {
|
||||
if (quality_set) return COMMAND_INVALID;
|
||||
quality_set = BROTLI_TRUE;
|
||||
params->quality = 11;
|
||||
} else if (strcmp("decompress", arg) == 0) {
|
||||
if (command_set) return COMMAND_INVALID;
|
||||
command_set = BROTLI_TRUE;
|
||||
command = COMMAND_DECOMPRESS;
|
||||
} else if (strcmp("force", arg) == 0) {
|
||||
if (params->force_overwrite) return COMMAND_INVALID;
|
||||
params->force_overwrite = BROTLI_TRUE;
|
||||
} else if (strcmp("help", arg) == 0) {
|
||||
/* Don't parse further. */
|
||||
return COMMAND_HELP;
|
||||
} else if (strcmp("keep", arg) == 0) {
|
||||
if (keep_set) return COMMAND_INVALID;
|
||||
keep_set = BROTLI_TRUE;
|
||||
params->junk_source = BROTLI_FALSE;
|
||||
} else if (strcmp("no-copy-stat", arg) == 0) {
|
||||
if (!params->copy_stat) return COMMAND_INVALID;
|
||||
params->copy_stat = BROTLI_FALSE;
|
||||
} else if (strcmp("rm", arg) == 0) {
|
||||
if (keep_set) return COMMAND_INVALID;
|
||||
keep_set = BROTLI_TRUE;
|
||||
params->junk_source = BROTLI_TRUE;
|
||||
} else if (strcmp("stdout", arg) == 0) {
|
||||
if (output_set) return COMMAND_INVALID;
|
||||
output_set = BROTLI_TRUE;
|
||||
params->write_to_stdout = BROTLI_TRUE;
|
||||
} else if (strcmp("test", arg) == 0) {
|
||||
if (command_set) return COMMAND_INVALID;
|
||||
command_set = BROTLI_TRUE;
|
||||
command = COMMAND_TEST_INTEGRITY;
|
||||
} else if (strcmp("verbose", arg) == 0) {
|
||||
if (params->verbose) return COMMAND_INVALID;
|
||||
params->verbose = BROTLI_TRUE;
|
||||
} else if (strcmp("version", arg) == 0) {
|
||||
/* Don't parse further. */
|
||||
return COMMAND_VERSION;
|
||||
} else {
|
||||
/* key=value */
|
||||
const char* value = strrchr(arg, '=');
|
||||
size_t key_len;
|
||||
if (!value || value[1] == 0) return COMMAND_INVALID;
|
||||
key_len = (size_t)(value - arg);
|
||||
value++;
|
||||
if (strncmp("dictionary", arg, key_len) == 0) {
|
||||
if (params->dictionary_path) return COMMAND_INVALID;
|
||||
params->dictionary_path = value;
|
||||
} else if (strncmp("lgwin", arg, key_len) == 0) {
|
||||
if (lgwin_set) return COMMAND_INVALID;
|
||||
lgwin_set = ParseInt(value, 0,
|
||||
BROTLI_MAX_WINDOW_BITS, ¶ms->lgwin);
|
||||
if (!lgwin_set) return COMMAND_INVALID;
|
||||
if (params->lgwin != 0 && params->lgwin < BROTLI_MIN_WINDOW_BITS) {
|
||||
return COMMAND_INVALID;
|
||||
}
|
||||
} else if (strncmp("output", arg, key_len) == 0) {
|
||||
if (output_set) return COMMAND_INVALID;
|
||||
params->output_path = value;
|
||||
} else if (strncmp("quality", arg, key_len) == 0) {
|
||||
if (quality_set) return COMMAND_INVALID;
|
||||
quality_set = ParseInt(value, BROTLI_MIN_QUALITY,
|
||||
BROTLI_MAX_QUALITY, ¶ms->quality);
|
||||
if (!quality_set) return COMMAND_INVALID;
|
||||
} else if (strncmp("suffix", arg, key_len) == 0) {
|
||||
if (suffix_set) return COMMAND_INVALID;
|
||||
suffix_set = BROTLI_TRUE;
|
||||
params->suffix = value;
|
||||
} else {
|
||||
return COMMAND_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params->input_count = input_count;
|
||||
params->longest_path_len = longest_path_len;
|
||||
params->decompress = (command == COMMAND_DECOMPRESS);
|
||||
params->test_integrity = (command == COMMAND_TEST_INTEGRITY);
|
||||
|
||||
if (input_count > 1 && output_set) return COMMAND_INVALID;
|
||||
if (params->test_integrity) {
|
||||
if (params->output_path) return COMMAND_INVALID;
|
||||
if (params->write_to_stdout) return COMMAND_INVALID;
|
||||
}
|
||||
if (strchr(params->suffix, '/') || strchr(params->suffix, '\\')) {
|
||||
return COMMAND_INVALID;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
static void PrintVersion(void) {
|
||||
int major = BROTLI_VERSION >> 24;
|
||||
int minor = (BROTLI_VERSION >> 12) & 0xFFF;
|
||||
int patch = BROTLI_VERSION & 0xFFF;
|
||||
fprintf(stdout, "\
|
||||
brotli %d.%d.%d\n",
|
||||
major, minor, patch);
|
||||
}
|
||||
|
||||
static void PrintHelp(const char* name) {
|
||||
/* String is cut to pieces with length less than 509, to conform C90 spec. */
|
||||
fprintf(stdout, "\
|
||||
Usage: %s [OPTION]... [FILE]...\n",
|
||||
name);
|
||||
fprintf(stdout, "\
|
||||
Options:\n\
|
||||
-# compression level (0-9)\n\
|
||||
-c, --stdout write on standard output\n\
|
||||
-d, --decompress decompress\n\
|
||||
-f, --force force output file overwrite\n\
|
||||
-h, --help display this help and exit\n");
|
||||
fprintf(stdout, "\
|
||||
-j, --rm remove source file(s)\n\
|
||||
-k, --keep keep source file(s) (default)\n\
|
||||
-n, --no-copy-stat do not copy source file(s) attributes\n\
|
||||
-o FILE, --output=FILE output file (only if 1 input file)\n");
|
||||
fprintf(stdout, "\
|
||||
-q NUM, --quality=NUM compression level (%d-%d)\n",
|
||||
BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY);
|
||||
fprintf(stdout, "\
|
||||
-t, --test test compressed file integrity\n\
|
||||
-v, --verbose verbose mode\n");
|
||||
fprintf(stdout, "\
|
||||
-w NUM, --lgwin=NUM set LZ77 window size (0, %d-%d) (default:%d)\n",
|
||||
BROTLI_MIN_WINDOW_BITS, BROTLI_MAX_WINDOW_BITS, DEFAULT_LGWIN);
|
||||
fprintf(stdout, "\
|
||||
window size = 2**NUM - 16\n\
|
||||
0 lets compressor decide over the optimal value\n\
|
||||
-D FILE, --dictionary=FILE use FILE as LZ77 dictionary\n");
|
||||
fprintf(stdout, "\
|
||||
-S SUF, --suffix=SUF output file suffix (default:'%s')\n",
|
||||
DEFAULT_SUFFIX);
|
||||
fprintf(stdout, "\
|
||||
-V, --version display version and exit\n\
|
||||
-Z, --best use best compression level (11) (default)\n\
|
||||
Simple options could be coalesced, i.e. '-9kf' is equivalent to '-9 -k -f'.\n\
|
||||
With no FILE, or when FILE is -, read standard input.\n\
|
||||
All arguments after '--' are treated as files.\n");
|
||||
}
|
||||
|
||||
static const char* PrintablePath(const char* path) {
|
||||
return path ? path : "con";
|
||||
}
|
||||
|
||||
static BROTLI_BOOL OpenInputFile(const char* input_path, FILE** f) {
|
||||
*f = NULL;
|
||||
if (!input_path) {
|
||||
*f = fdopen(STDIN_FILENO, "rb");
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
*f = fopen(input_path, "rb");
|
||||
if (!*f) {
|
||||
fprintf(stderr, "failed to open input file [%s]: %s\n",
|
||||
PrintablePath(input_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
static BROTLI_BOOL OpenOutputFile(const char* output_path, FILE** f,
|
||||
BROTLI_BOOL force) {
|
||||
int fd;
|
||||
*f = NULL;
|
||||
if (!output_path) {
|
||||
*f = fdopen(STDOUT_FILENO, "wb");
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
fd = open(output_path, O_CREAT | (force ? 0 : O_EXCL) | O_WRONLY | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "failed to open output file [%s]: %s\n",
|
||||
PrintablePath(output_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
*f = fdopen(fd, "wb");
|
||||
if (!*f) {
|
||||
fprintf(stderr, "failed to open output file [%s]: %s\n",
|
||||
PrintablePath(output_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Copy file times and permissions.
|
||||
TODO(eustas): this is a "best effort" implementation; honest cross-platform
|
||||
fully featured implementation is way too hacky; add more hacks by request. */
|
||||
static void CopyStat(const char* input_path, const char* output_path) {
|
||||
struct stat statbuf;
|
||||
struct utimbuf times;
|
||||
int res;
|
||||
if (input_path == 0 || output_path == 0) {
|
||||
return;
|
||||
}
|
||||
if (stat(input_path, &statbuf) != 0) {
|
||||
return;
|
||||
}
|
||||
times.actime = statbuf.st_atime;
|
||||
times.modtime = statbuf.st_mtime;
|
||||
utime(output_path, ×);
|
||||
res = chmod(output_path, statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "setting access bits failed for [%s]: %s\n",
|
||||
PrintablePath(output_path), strerror(errno));
|
||||
}
|
||||
res = chown(output_path, (uid_t)-1, statbuf.st_gid);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "setting group failed for [%s]: %s\n",
|
||||
PrintablePath(output_path), strerror(errno));
|
||||
}
|
||||
res = chown(output_path, statbuf.st_uid, (gid_t)-1);
|
||||
if (res != 0) {
|
||||
fprintf(stderr, "setting user failed for [%s]: %s\n",
|
||||
PrintablePath(output_path), strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* Result ownership is passed to caller.
|
||||
|*dictionary_size| is set to resulting buffer size. */
|
||||
static BROTLI_BOOL ReadDictionary(Context* context) {
|
||||
static const int kMaxDictionarySize = (1 << 24) - 16;
|
||||
FILE* f;
|
||||
int64_t file_size_64;
|
||||
uint8_t* buffer;
|
||||
size_t bytes_read;
|
||||
|
||||
if (context->dictionary_path == NULL) return BROTLI_TRUE;
|
||||
f = fopen(context->dictionary_path, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "failed to open dictionary file [%s]: %s\n",
|
||||
PrintablePath(context->dictionary_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
|
||||
file_size_64 = FileSize(context->dictionary_path);
|
||||
if (file_size_64 == -1) {
|
||||
fprintf(stderr, "could not get size of dictionary file [%s]",
|
||||
PrintablePath(context->dictionary_path));
|
||||
fclose(f);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
|
||||
if (file_size_64 > kMaxDictionarySize) {
|
||||
fprintf(stderr, "dictionary [%s] is larger than maximum allowed: %d\n",
|
||||
PrintablePath(context->dictionary_path), kMaxDictionarySize);
|
||||
fclose(f);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
context->dictionary_size = (size_t)file_size_64;
|
||||
|
||||
buffer = (uint8_t*)malloc(context->dictionary_size);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "could not read dictionary: out of memory\n");
|
||||
fclose(f);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
bytes_read = fread(buffer, sizeof(uint8_t), context->dictionary_size, f);
|
||||
if (bytes_read != context->dictionary_size) {
|
||||
free(buffer);
|
||||
fprintf(stderr, "failed to read dictionary [%s]: %s\n",
|
||||
PrintablePath(context->dictionary_path), strerror(errno));
|
||||
fclose(f);
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
fclose(f);
|
||||
context->dictionary = buffer;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
static BROTLI_BOOL NextFile(Context* context) {
|
||||
const char* arg;
|
||||
size_t arg_len;
|
||||
|
||||
/* Iterator points to last used arg; increment to search for the next one. */
|
||||
context->iterator++;
|
||||
|
||||
/* No input path; read from console. */
|
||||
if (context->input_count == 0) {
|
||||
if (context->iterator > 1) return BROTLI_FALSE;
|
||||
context->current_input_path = NULL;
|
||||
/* Either write to the specified path, or to console. */
|
||||
context->current_output_path = context->output_path;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
/* Skip option arguments. */
|
||||
while (context->iterator == context->not_input_indices[context->ignore]) {
|
||||
context->iterator++;
|
||||
context->ignore++;
|
||||
}
|
||||
|
||||
/* All args are scanned already. */
|
||||
if (context->iterator >= context->argc) return BROTLI_FALSE;
|
||||
|
||||
/* Iterator now points to the input file name. */
|
||||
arg = context->argv[context->iterator];
|
||||
arg_len = strlen(arg);
|
||||
/* Read from console. */
|
||||
if (arg_len == 1 && arg[0] == '-') {
|
||||
context->current_input_path = NULL;
|
||||
context->current_output_path = context->output_path;
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
context->current_input_path = arg;
|
||||
context->current_output_path = context->output_path;
|
||||
|
||||
if (context->output_path) return BROTLI_TRUE;
|
||||
if (context->write_to_stdout) return BROTLI_TRUE;
|
||||
|
||||
strcpy(context->modified_path, arg);
|
||||
context->current_output_path = context->modified_path;
|
||||
/* If output is not specified, input path suffix should match. */
|
||||
if (context->decompress) {
|
||||
size_t suffix_len = strlen(context->suffix);
|
||||
char* name = (char*)FileName(context->modified_path);
|
||||
char* name_suffix;
|
||||
size_t name_len = strlen(name);
|
||||
if (name_len < suffix_len + 1) {
|
||||
fprintf(stderr, "empty output file name for [%s] input file\n",
|
||||
PrintablePath(arg));
|
||||
context->iterator_error = BROTLI_TRUE;
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
name_suffix = name + name_len - suffix_len;
|
||||
if (strcmp(context->suffix, name_suffix) != 0) {
|
||||
fprintf(stderr, "input file [%s] suffix mismatch\n",
|
||||
PrintablePath(arg));
|
||||
context->iterator_error = BROTLI_TRUE;
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
name_suffix[0] = 0;
|
||||
return BROTLI_TRUE;
|
||||
} else {
|
||||
strcpy(context->modified_path + arg_len, context->suffix);
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static BROTLI_BOOL OpenFiles(Context* context) {
|
||||
BROTLI_BOOL is_ok = OpenInputFile(context->current_input_path, &context->fin);
|
||||
if (!context->test_integrity && is_ok) {
|
||||
is_ok = OpenOutputFile(
|
||||
context->current_output_path, &context->fout, context->force_overwrite);
|
||||
}
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
static BROTLI_BOOL CloseFiles(Context* context, BROTLI_BOOL success) {
|
||||
BROTLI_BOOL is_ok = BROTLI_TRUE;
|
||||
if (!context->test_integrity && context->fout) {
|
||||
if (!success) unlink(context->current_output_path);
|
||||
if (fclose(context->fout) != 0) {
|
||||
if (success) {
|
||||
fprintf(stderr, "fclose failed [%s]: %s\n",
|
||||
PrintablePath(context->current_output_path), strerror(errno));
|
||||
}
|
||||
is_ok = BROTLI_FALSE;
|
||||
}
|
||||
|
||||
/* TOCTOU violation, but otherwise it is impossible to set file times. */
|
||||
if (success && is_ok && context->copy_stat) {
|
||||
CopyStat(context->current_input_path, context->current_output_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (context->fin) {
|
||||
if (fclose(context->fin) != 0) {
|
||||
if (is_ok) {
|
||||
fprintf(stderr, "fclose failed [%s]: %s\n",
|
||||
PrintablePath(context->current_input_path), strerror(errno));
|
||||
}
|
||||
is_ok = BROTLI_FALSE;
|
||||
}
|
||||
}
|
||||
if (success && context->junk_source) {
|
||||
unlink(context->current_input_path);
|
||||
}
|
||||
|
||||
context->fin = NULL;
|
||||
context->fout = NULL;
|
||||
|
||||
return is_ok;
|
||||
}
|
||||
|
||||
static const size_t kFileBufferSize = 1 << 16;
|
||||
|
||||
static BROTLI_BOOL DecompressFile(Context* context, BrotliDecoderState* s) {
|
||||
size_t available_in = 0;
|
||||
const uint8_t* next_in = NULL;
|
||||
size_t available_out = kFileBufferSize;
|
||||
uint8_t* next_out = context->output;
|
||||
BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
|
||||
for (;;) {
|
||||
if (next_out != context->output) {
|
||||
if (!context->test_integrity) {
|
||||
size_t out_size = (size_t)(next_out - context->output);
|
||||
fwrite(context->output, 1, out_size, context->fout);
|
||||
if (ferror(context->fout)) {
|
||||
fprintf(stderr, "failed to write output [%s]: %s\n",
|
||||
PrintablePath(context->current_output_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
}
|
||||
available_out = kFileBufferSize;
|
||||
next_out = context->output;
|
||||
}
|
||||
|
||||
if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
|
||||
if (feof(context->fin)) {
|
||||
fprintf(stderr, "corrupt input [%s]\n",
|
||||
PrintablePath(context->current_output_path));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
available_in = fread(context->input, 1, kFileBufferSize, context->fin);
|
||||
next_in = context->input;
|
||||
if (ferror(context->fin)) {
|
||||
fprintf(stderr, "failed to read input [%s]: %s\n",
|
||||
PrintablePath(context->current_input_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
} else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
|
||||
/* Nothing to do - output is already written. */
|
||||
} else if (result == BROTLI_DECODER_RESULT_SUCCESS) {
|
||||
if (available_in != 0 || !feof(context->fin)) {
|
||||
fprintf(stderr, "corrupt input [%s]\n",
|
||||
PrintablePath(context->current_output_path));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
return BROTLI_TRUE;
|
||||
} else {
|
||||
fprintf(stderr, "corrupt input [%s]\n",
|
||||
PrintablePath(context->current_output_path));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
|
||||
result = BrotliDecoderDecompressStream(
|
||||
s, &available_in, &next_in, &available_out, &next_out, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static BROTLI_BOOL DecompressFiles(Context* context) {
|
||||
while (NextFile(context)) {
|
||||
BROTLI_BOOL is_ok = BROTLI_TRUE;
|
||||
BrotliDecoderState* s = BrotliDecoderCreateInstance(NULL, NULL, NULL);
|
||||
if (!s) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
if (context->dictionary) {
|
||||
BrotliDecoderSetCustomDictionary(s,
|
||||
context->dictionary_size, context->dictionary);
|
||||
}
|
||||
is_ok = OpenFiles(context);
|
||||
if (is_ok) is_ok = DecompressFile(context, s);
|
||||
BrotliDecoderDestroyInstance(s);
|
||||
if (!CloseFiles(context, is_ok)) is_ok = BROTLI_FALSE;
|
||||
if (!is_ok) return BROTLI_FALSE;
|
||||
}
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
static BROTLI_BOOL CompressFile(Context* context, BrotliEncoderState* s) {
|
||||
size_t available_in = 0;
|
||||
const uint8_t* next_in = NULL;
|
||||
size_t available_out = kFileBufferSize;
|
||||
uint8_t* next_out = context->output;
|
||||
BROTLI_BOOL is_eof = BROTLI_FALSE;
|
||||
|
||||
for (;;) {
|
||||
if (available_in == 0 && !is_eof) {
|
||||
available_in = fread(context->input, 1, kFileBufferSize, context->fin);
|
||||
next_in = context->input;
|
||||
if (ferror(context->fin)) {
|
||||
fprintf(stderr, "failed to read input [%s]: %s\n",
|
||||
PrintablePath(context->current_input_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
is_eof = feof(context->fin) ? BROTLI_TRUE : BROTLI_FALSE;
|
||||
}
|
||||
|
||||
if (!BrotliEncoderCompressStream(s,
|
||||
is_eof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,
|
||||
&available_in, &next_in, &available_out, &next_out, NULL)) {
|
||||
/* Should detect OOM? */
|
||||
fprintf(stderr, "failed to compress data [%s]\n",
|
||||
PrintablePath(context->current_input_path));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
|
||||
if (available_out != kFileBufferSize) {
|
||||
size_t out_size = kFileBufferSize - available_out;
|
||||
fwrite(context->output, 1, out_size, context->fout);
|
||||
if (ferror(context->fout)) {
|
||||
fprintf(stderr, "failed to write output [%s]: %s\n",
|
||||
PrintablePath(context->current_output_path), strerror(errno));
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
available_out = kFileBufferSize;
|
||||
next_out = context->output;
|
||||
}
|
||||
|
||||
if (BrotliEncoderIsFinished(s)) return BROTLI_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static BROTLI_BOOL CompressFiles(Context* context) {
|
||||
while (NextFile(context)) {
|
||||
BROTLI_BOOL is_ok = BROTLI_TRUE;
|
||||
BrotliEncoderState* s = BrotliEncoderCreateInstance(NULL, NULL, NULL);
|
||||
if (!s) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
return BROTLI_FALSE;
|
||||
}
|
||||
BrotliEncoderSetParameter(s,
|
||||
BROTLI_PARAM_QUALITY, (uint32_t)context->quality);
|
||||
BrotliEncoderSetParameter(s,
|
||||
BROTLI_PARAM_LGWIN, (uint32_t)context->lgwin);
|
||||
if (context->dictionary) {
|
||||
BrotliEncoderSetCustomDictionary(s,
|
||||
context->dictionary_size, context->dictionary);
|
||||
}
|
||||
is_ok = OpenFiles(context);
|
||||
if (is_ok) is_ok = CompressFile(context, s);
|
||||
BrotliEncoderDestroyInstance(s);
|
||||
if (!CloseFiles(context, is_ok)) is_ok = BROTLI_FALSE;
|
||||
if (!is_ok) return BROTLI_FALSE;
|
||||
}
|
||||
return BROTLI_TRUE;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
Command command;
|
||||
Context context;
|
||||
BROTLI_BOOL is_ok = BROTLI_TRUE;
|
||||
int i;
|
||||
|
||||
context.quality = 11;
|
||||
context.lgwin = DEFAULT_LGWIN;
|
||||
context.force_overwrite = BROTLI_FALSE;
|
||||
context.junk_source = BROTLI_FALSE;
|
||||
context.copy_stat = BROTLI_TRUE;
|
||||
context.test_integrity = BROTLI_FALSE;
|
||||
context.verbose = BROTLI_FALSE;
|
||||
context.write_to_stdout = BROTLI_FALSE;
|
||||
context.decompress = BROTLI_FALSE;
|
||||
context.output_path = NULL;
|
||||
context.dictionary_path = NULL;
|
||||
context.suffix = DEFAULT_SUFFIX;
|
||||
for (i = 0; i < MAX_OPTIONS; ++i) context.not_input_indices[i] = 0;
|
||||
context.longest_path_len = 1;
|
||||
context.input_count = 0;
|
||||
|
||||
context.argc = argc;
|
||||
context.argv = argv;
|
||||
context.dictionary = NULL;
|
||||
context.dictionary_size = 0;
|
||||
context.modified_path = NULL;
|
||||
context.iterator = 0;
|
||||
context.ignore = 0;
|
||||
context.iterator_error = BROTLI_FALSE;
|
||||
context.buffer = NULL;
|
||||
context.current_input_path = NULL;
|
||||
context.current_output_path = NULL;
|
||||
context.fin = NULL;
|
||||
context.fout = NULL;
|
||||
|
||||
command = ParseParams(&context);
|
||||
|
||||
if (command == COMMAND_COMPRESS || command == COMMAND_DECOMPRESS ||
|
||||
command == COMMAND_TEST_INTEGRITY) {
|
||||
if (!ReadDictionary(&context)) is_ok = BROTLI_FALSE;
|
||||
if (is_ok) {
|
||||
size_t modified_path_len =
|
||||
context.longest_path_len + strlen(context.suffix) + 1;
|
||||
context.modified_path = (char*)malloc(modified_path_len);
|
||||
context.buffer = (uint8_t*)malloc(kFileBufferSize * 2);
|
||||
if (!context.modified_path || !context.buffer) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
is_ok = BROTLI_FALSE;
|
||||
} else {
|
||||
context.input = context.buffer;
|
||||
context.output = context.buffer + kFileBufferSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_ok) command = COMMAND_NOOP;
|
||||
|
||||
switch (command) {
|
||||
case COMMAND_NOOP:
|
||||
break;
|
||||
|
||||
case COMMAND_VERSION:
|
||||
PrintVersion();
|
||||
break;
|
||||
|
||||
case COMMAND_COMPRESS:
|
||||
is_ok = CompressFiles(&context);
|
||||
break;
|
||||
|
||||
case COMMAND_DECOMPRESS:
|
||||
case COMMAND_TEST_INTEGRITY:
|
||||
is_ok = DecompressFiles(&context);
|
||||
break;
|
||||
|
||||
case COMMAND_HELP:
|
||||
case COMMAND_INVALID:
|
||||
default:
|
||||
PrintHelp(FileName(argv[0]));
|
||||
is_ok = (command == COMMAND_HELP);
|
||||
break;
|
||||
}
|
||||
|
||||
if (context.iterator_error) is_ok = BROTLI_FALSE;
|
||||
|
||||
free(context.dictionary);
|
||||
free(context.modified_path);
|
||||
free(context.buffer);
|
||||
|
||||
if (!is_ok) exit(1);
|
||||
return 0;
|
||||
}
|
||||
110
c/tools/brotli.md
Executable file
110
c/tools/brotli.md
Executable file
@@ -0,0 +1,110 @@
|
||||
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
|
||||
* `-D FILE`, `--dictionary=FILE`:
|
||||
use FILE as LZ77 dictionary; same dictionary MUST be used both for
|
||||
compression and decompression
|
||||
* `-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
|
||||
@@ -32,8 +32,8 @@ PREFIX=/usr/local
|
||||
LIBDIR=
|
||||
CMAKE_ARGS=
|
||||
|
||||
if [ -e "${TOP_SRCDIR}/.configure-custom.sh" ]; then
|
||||
. "${TOP_SRCDIR}/.configure-custom.sh"
|
||||
if [ -e "${TOP_SRCDIR}/scripts/.configure-custom.sh" ]; then
|
||||
. "${TOP_SRCDIR}/scripts/.configure-custom.sh"
|
||||
fi
|
||||
|
||||
quote() {
|
||||
|
||||
32
csharp/injected_code.txt
Normal file
32
csharp/injected_code.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
// <{[INJECTED CODE]}>
|
||||
public override bool CanRead {
|
||||
get {return true;}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get {return false;}
|
||||
}
|
||||
public override long Length {
|
||||
get {throw new System.NotSupportedException();}
|
||||
}
|
||||
public override long Position {
|
||||
get {throw new System.NotSupportedException();}
|
||||
set {throw new System.NotSupportedException();}
|
||||
}
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin) {
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
public override void SetLength(long value){
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override bool CanWrite{get{return false;}}
|
||||
public override System.IAsyncResult BeginWrite(byte[] buffer, int offset,
|
||||
int count, System.AsyncCallback callback, object state) {
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush() {}
|
||||
271
csharp/org/brotli/dec/BitReader.cs
Normal file
271
csharp/org/brotli/dec/BitReader.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>Bit reading helpers.</summary>
|
||||
internal sealed class BitReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Input byte buffer, consist of a ring-buffer and a "slack" region where bytes from the start of
|
||||
/// the ring-buffer are copied.
|
||||
/// </summary>
|
||||
private const int Capacity = 1024;
|
||||
|
||||
private const int Slack = 16;
|
||||
|
||||
private const int IntBufferSize = Capacity + Slack;
|
||||
|
||||
private const int ByteReadSize = Capacity << 2;
|
||||
|
||||
private const int ByteBufferSize = IntBufferSize << 2;
|
||||
|
||||
private readonly byte[] byteBuffer = new byte[ByteBufferSize];
|
||||
|
||||
private readonly int[] intBuffer = new int[IntBufferSize];
|
||||
|
||||
private readonly Org.Brotli.Dec.IntReader intReader = new Org.Brotli.Dec.IntReader();
|
||||
|
||||
private System.IO.Stream input;
|
||||
|
||||
/// <summary>Input stream is finished.</summary>
|
||||
private bool endOfStreamReached;
|
||||
|
||||
/// <summary>Pre-fetched bits.</summary>
|
||||
internal long accumulator;
|
||||
|
||||
/// <summary>Current bit-reading position in accumulator.</summary>
|
||||
internal int bitOffset;
|
||||
|
||||
/// <summary>Offset of next item in intBuffer.</summary>
|
||||
private int intOffset;
|
||||
|
||||
private int tailBytes = 0;
|
||||
|
||||
/* Number of bytes in unfinished "int" item. */
|
||||
/// <summary>Fills up the input buffer.</summary>
|
||||
/// <remarks>
|
||||
/// Fills up the input buffer.
|
||||
/// <p> No-op if there are at least 36 bytes present after current position.
|
||||
/// <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
|
||||
/// buffer.
|
||||
/// </remarks>
|
||||
internal static void ReadMoreInput(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
// TODO: Split to check and read; move read outside of decoding loop.
|
||||
if (br.intOffset <= Capacity - 9)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (br.endOfStreamReached)
|
||||
{
|
||||
if (IntAvailable(br) >= -2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("No more input");
|
||||
}
|
||||
int readOffset = br.intOffset << 2;
|
||||
int bytesRead = ByteReadSize - readOffset;
|
||||
System.Array.Copy(br.byteBuffer, readOffset, br.byteBuffer, 0, bytesRead);
|
||||
br.intOffset = 0;
|
||||
try
|
||||
{
|
||||
while (bytesRead < ByteReadSize)
|
||||
{
|
||||
int len = br.input.Read(br.byteBuffer, bytesRead, ByteReadSize - bytesRead);
|
||||
// EOF is -1 in Java, but 0 in C#.
|
||||
if (len <= 0)
|
||||
{
|
||||
br.endOfStreamReached = true;
|
||||
br.tailBytes = bytesRead;
|
||||
bytesRead += 3;
|
||||
break;
|
||||
}
|
||||
bytesRead += len;
|
||||
}
|
||||
}
|
||||
catch (System.IO.IOException e)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Failed to read input", e);
|
||||
}
|
||||
Org.Brotli.Dec.IntReader.Convert(br.intReader, bytesRead >> 2);
|
||||
}
|
||||
|
||||
internal static void CheckHealth(Org.Brotli.Dec.BitReader br, bool endOfStream)
|
||||
{
|
||||
if (!br.endOfStreamReached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int byteOffset = (br.intOffset << 2) + ((br.bitOffset + 7) >> 3) - 8;
|
||||
if (byteOffset > br.tailBytes)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Read after end");
|
||||
}
|
||||
if (endOfStream && (byteOffset != br.tailBytes))
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Unused bytes after end");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advances the Read buffer by 5 bytes to make room for reading next 24 bits.</summary>
|
||||
internal static void FillBitWindow(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
if (br.bitOffset >= 32)
|
||||
{
|
||||
br.accumulator = ((long)br.intBuffer[br.intOffset++] << 32) | ((long)(((ulong)br.accumulator) >> 32));
|
||||
br.bitOffset -= 32;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Reads the specified number of bits from Read Buffer.</summary>
|
||||
internal static int ReadBits(Org.Brotli.Dec.BitReader br, int n)
|
||||
{
|
||||
FillBitWindow(br);
|
||||
int val = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)) & ((1 << n) - 1);
|
||||
br.bitOffset += n;
|
||||
return val;
|
||||
}
|
||||
|
||||
/// <summary>Initialize bit reader.</summary>
|
||||
/// <remarks>
|
||||
/// Initialize bit reader.
|
||||
/// <p> Initialisation turns bit reader to a ready state. Also a number of bytes is prefetched to
|
||||
/// accumulator. Because of that this method may block until enough data could be read from input.
|
||||
/// </remarks>
|
||||
/// <param name="br">BitReader POJO</param>
|
||||
/// <param name="input">data source</param>
|
||||
internal static void Init(Org.Brotli.Dec.BitReader br, System.IO.Stream input)
|
||||
{
|
||||
if (br.input != null)
|
||||
{
|
||||
throw new System.InvalidOperationException("Bit reader already has associated input stream");
|
||||
}
|
||||
Org.Brotli.Dec.IntReader.Init(br.intReader, br.byteBuffer, br.intBuffer);
|
||||
br.input = input;
|
||||
br.accumulator = 0;
|
||||
br.bitOffset = 64;
|
||||
br.intOffset = Capacity;
|
||||
br.endOfStreamReached = false;
|
||||
Prepare(br);
|
||||
}
|
||||
|
||||
private static void Prepare(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
ReadMoreInput(br);
|
||||
CheckHealth(br, false);
|
||||
FillBitWindow(br);
|
||||
FillBitWindow(br);
|
||||
}
|
||||
|
||||
internal static void Reload(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
if (br.bitOffset == 64)
|
||||
{
|
||||
Prepare(br);
|
||||
}
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
internal static void Close(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
System.IO.Stream @is = br.input;
|
||||
br.input = null;
|
||||
if (@is != null)
|
||||
{
|
||||
@is.Close();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void JumpToByteBoundary(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
int padding = (64 - br.bitOffset) & 7;
|
||||
if (padding != 0)
|
||||
{
|
||||
int paddingBits = Org.Brotli.Dec.BitReader.ReadBits(br, padding);
|
||||
if (paddingBits != 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Corrupted padding bits");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static int IntAvailable(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
int limit = Capacity;
|
||||
if (br.endOfStreamReached)
|
||||
{
|
||||
limit = (br.tailBytes + 3) >> 2;
|
||||
}
|
||||
return limit - br.intOffset;
|
||||
}
|
||||
|
||||
internal static void CopyBytes(Org.Brotli.Dec.BitReader br, byte[] data, int offset, int length)
|
||||
{
|
||||
if ((br.bitOffset & 7) != 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Unaligned copyBytes");
|
||||
}
|
||||
// Drain accumulator.
|
||||
while ((br.bitOffset != 64) && (length != 0))
|
||||
{
|
||||
data[offset++] = unchecked((byte)((long)(((ulong)br.accumulator) >> br.bitOffset)));
|
||||
br.bitOffset += 8;
|
||||
length--;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Get data from shadow buffer with "sizeof(int)" granularity.
|
||||
int copyInts = System.Math.Min(IntAvailable(br), length >> 2);
|
||||
if (copyInts > 0)
|
||||
{
|
||||
int readOffset = br.intOffset << 2;
|
||||
System.Array.Copy(br.byteBuffer, readOffset, data, offset, copyInts << 2);
|
||||
offset += copyInts << 2;
|
||||
length -= copyInts << 2;
|
||||
br.intOffset += copyInts;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Read tail bytes.
|
||||
if (IntAvailable(br) > 0)
|
||||
{
|
||||
// length = 1..3
|
||||
FillBitWindow(br);
|
||||
while (length != 0)
|
||||
{
|
||||
data[offset++] = unchecked((byte)((long)(((ulong)br.accumulator) >> br.bitOffset)));
|
||||
br.bitOffset += 8;
|
||||
length--;
|
||||
}
|
||||
CheckHealth(br, false);
|
||||
return;
|
||||
}
|
||||
// Now it is possible to copy bytes directly.
|
||||
try
|
||||
{
|
||||
while (length > 0)
|
||||
{
|
||||
int len = br.input.Read(data, offset, length);
|
||||
if (len == -1)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Unexpected end of input");
|
||||
}
|
||||
offset += len;
|
||||
length -= len;
|
||||
}
|
||||
}
|
||||
catch (System.IO.IOException e)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Failed to read input", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
csharp/org/brotli/dec/BitReaderTest.cs
Normal file
33
csharp/org/brotli/dec/BitReaderTest.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for
|
||||
/// <see cref="BitReader"/>
|
||||
/// .
|
||||
/// </summary>
|
||||
public class BitReaderTest
|
||||
{
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestReadAfterEos()
|
||||
{
|
||||
Org.Brotli.Dec.BitReader reader = new Org.Brotli.Dec.BitReader();
|
||||
Org.Brotli.Dec.BitReader.Init(reader, new System.IO.MemoryStream(new byte[1]));
|
||||
Org.Brotli.Dec.BitReader.ReadBits(reader, 9);
|
||||
try
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.CheckHealth(reader, false);
|
||||
}
|
||||
catch (Org.Brotli.Dec.BrotliRuntimeException)
|
||||
{
|
||||
// This exception is expected.
|
||||
return;
|
||||
}
|
||||
NUnit.Framework.Assert.Fail("BrotliRuntimeException should have been thrown by BitReader.checkHealth");
|
||||
}
|
||||
}
|
||||
}
|
||||
223
csharp/org/brotli/dec/BrotliInputStream.cs
Normal file
223
csharp/org/brotli/dec/BrotliInputStream.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="System.IO.Stream"/>
|
||||
/// decorator that decompresses brotli data.
|
||||
/// <p> Not thread-safe.
|
||||
/// </summary>
|
||||
public class BrotliInputStream : System.IO.Stream
|
||||
{
|
||||
public const int DefaultInternalBufferSize = 16384;
|
||||
|
||||
/// <summary>Internal buffer used for efficient byte-by-byte reading.</summary>
|
||||
private byte[] buffer;
|
||||
|
||||
/// <summary>Number of decoded but still unused bytes in internal buffer.</summary>
|
||||
private int remainingBufferBytes;
|
||||
|
||||
/// <summary>Next unused byte offset.</summary>
|
||||
private int bufferOffset;
|
||||
|
||||
/// <summary>Decoder state.</summary>
|
||||
private readonly Org.Brotli.Dec.State state = new Org.Brotli.Dec.State();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a
|
||||
/// <see cref="System.IO.Stream"/>
|
||||
/// wrapper that decompresses brotli data.
|
||||
/// <p> For byte-by-byte reading (
|
||||
/// <see cref="ReadByte()"/>
|
||||
/// ) internal buffer with
|
||||
/// <see cref="DefaultInternalBufferSize"/>
|
||||
/// size is allocated and used.
|
||||
/// <p> Will block the thread until first kilobyte of data of source is available.
|
||||
/// </summary>
|
||||
/// <param name="source">underlying data source</param>
|
||||
/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
|
||||
public BrotliInputStream(System.IO.Stream source)
|
||||
: this(source, DefaultInternalBufferSize, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a
|
||||
/// <see cref="System.IO.Stream"/>
|
||||
/// wrapper that decompresses brotli data.
|
||||
/// <p> For byte-by-byte reading (
|
||||
/// <see cref="ReadByte()"/>
|
||||
/// ) internal buffer of specified size is
|
||||
/// allocated and used.
|
||||
/// <p> Will block the thread until first kilobyte of data of source is available.
|
||||
/// </summary>
|
||||
/// <param name="source">compressed data source</param>
|
||||
/// <param name="byteReadBufferSize">
|
||||
/// size of internal buffer used in case of
|
||||
/// byte-by-byte reading
|
||||
/// </param>
|
||||
/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
|
||||
public BrotliInputStream(System.IO.Stream source, int byteReadBufferSize)
|
||||
: this(source, byteReadBufferSize, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a
|
||||
/// <see cref="System.IO.Stream"/>
|
||||
/// wrapper that decompresses brotli data.
|
||||
/// <p> For byte-by-byte reading (
|
||||
/// <see cref="ReadByte()"/>
|
||||
/// ) internal buffer of specified size is
|
||||
/// allocated and used.
|
||||
/// <p> Will block the thread until first kilobyte of data of source is available.
|
||||
/// </summary>
|
||||
/// <param name="source">compressed data source</param>
|
||||
/// <param name="byteReadBufferSize">
|
||||
/// size of internal buffer used in case of
|
||||
/// byte-by-byte reading
|
||||
/// </param>
|
||||
/// <param name="customDictionary">
|
||||
/// custom dictionary data;
|
||||
/// <see langword="null"/>
|
||||
/// if not used
|
||||
/// </param>
|
||||
/// <exception cref="System.IO.IOException">in case of corrupted data or source stream problems</exception>
|
||||
public BrotliInputStream(System.IO.Stream source, int byteReadBufferSize, byte[] customDictionary)
|
||||
{
|
||||
if (byteReadBufferSize <= 0)
|
||||
{
|
||||
throw new System.ArgumentException("Bad buffer size:" + byteReadBufferSize);
|
||||
}
|
||||
else if (source == null)
|
||||
{
|
||||
throw new System.ArgumentException("source is null");
|
||||
}
|
||||
this.buffer = new byte[byteReadBufferSize];
|
||||
this.remainingBufferBytes = 0;
|
||||
this.bufferOffset = 0;
|
||||
try
|
||||
{
|
||||
Org.Brotli.Dec.State.SetInput(state, source);
|
||||
}
|
||||
catch (Org.Brotli.Dec.BrotliRuntimeException ex)
|
||||
{
|
||||
throw new System.IO.IOException("Brotli decoder initialization failed", ex);
|
||||
}
|
||||
if (customDictionary != null)
|
||||
{
|
||||
Org.Brotli.Dec.Decode.SetCustomDictionary(state, customDictionary);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary><inheritDoc/></summary>
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
public override void Close()
|
||||
{
|
||||
Org.Brotli.Dec.State.Close(state);
|
||||
}
|
||||
|
||||
/// <summary><inheritDoc/></summary>
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
public override int ReadByte()
|
||||
{
|
||||
if (bufferOffset >= remainingBufferBytes)
|
||||
{
|
||||
remainingBufferBytes = Read(buffer, 0, buffer.Length);
|
||||
bufferOffset = 0;
|
||||
if (remainingBufferBytes == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return buffer[bufferOffset++] & unchecked((int)(0xFF));
|
||||
}
|
||||
|
||||
/// <summary><inheritDoc/></summary>
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
public override int Read(byte[] destBuffer, int destOffset, int destLen)
|
||||
{
|
||||
if (destOffset < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Bad offset: " + destOffset);
|
||||
}
|
||||
else if (destLen < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Bad length: " + destLen);
|
||||
}
|
||||
else if (destOffset + destLen > destBuffer.Length)
|
||||
{
|
||||
throw new System.ArgumentException("Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.Length);
|
||||
}
|
||||
else if (destLen == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int copyLen = System.Math.Max(remainingBufferBytes - bufferOffset, 0);
|
||||
if (copyLen != 0)
|
||||
{
|
||||
copyLen = System.Math.Min(copyLen, destLen);
|
||||
System.Array.Copy(buffer, bufferOffset, destBuffer, destOffset, copyLen);
|
||||
bufferOffset += copyLen;
|
||||
destOffset += copyLen;
|
||||
destLen -= copyLen;
|
||||
if (destLen == 0)
|
||||
{
|
||||
return copyLen;
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
state.output = destBuffer;
|
||||
state.outputOffset = destOffset;
|
||||
state.outputLength = destLen;
|
||||
state.outputUsed = 0;
|
||||
Org.Brotli.Dec.Decode.Decompress(state);
|
||||
if (state.outputUsed == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return state.outputUsed + copyLen;
|
||||
}
|
||||
catch (Org.Brotli.Dec.BrotliRuntimeException ex)
|
||||
{
|
||||
throw new System.IO.IOException("Brotli stream decoding failed", ex);
|
||||
}
|
||||
}
|
||||
// <{[INJECTED CODE]}>
|
||||
public override bool CanRead {
|
||||
get {return true;}
|
||||
}
|
||||
|
||||
public override bool CanSeek {
|
||||
get {return false;}
|
||||
}
|
||||
public override long Length {
|
||||
get {throw new System.NotSupportedException();}
|
||||
}
|
||||
public override long Position {
|
||||
get {throw new System.NotSupportedException();}
|
||||
set {throw new System.NotSupportedException();}
|
||||
}
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin) {
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
public override void SetLength(long value){
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override bool CanWrite{get{return false;}}
|
||||
public override System.IAsyncResult BeginWrite(byte[] buffer, int offset,
|
||||
int count, System.AsyncCallback callback, object state) {
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush() {}
|
||||
}
|
||||
}
|
||||
22
csharp/org/brotli/dec/BrotliRuntimeException.cs
Normal file
22
csharp/org/brotli/dec/BrotliRuntimeException.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>Unchecked exception used internally.</summary>
|
||||
[System.Serializable]
|
||||
internal class BrotliRuntimeException : System.Exception
|
||||
{
|
||||
internal BrotliRuntimeException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
internal BrotliRuntimeException(string message, System.Exception cause)
|
||||
: base(message, cause)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
57
csharp/org/brotli/dec/Context.cs
Normal file
57
csharp/org/brotli/dec/Context.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>Common context lookup table for all context modes.</summary>
|
||||
internal sealed class Context
|
||||
{
|
||||
internal static readonly int[] Lookup = new int[] { 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, 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,
|
||||
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
|
||||
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, 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, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
internal static readonly int[] LookupOffsets = new int[] { 1024, 1536, 1280, 1536, 0, 256, 768, 512 };
|
||||
// CONTEXT_UTF8, last byte.
|
||||
// ASCII range.
|
||||
// UTF8 continuation byte range.
|
||||
// UTF8 lead byte range.
|
||||
// CONTEXT_UTF8 second last byte.
|
||||
// ASCII range.
|
||||
// UTF8 continuation byte range.
|
||||
// UTF8 lead byte range.
|
||||
// CONTEXT_SIGNED, second last byte.
|
||||
// CONTEXT_SIGNED, last byte, same as the above values shifted by 3 bits.
|
||||
// CONTEXT_LSB6, last byte.
|
||||
// CONTEXT_MSB6, last byte.
|
||||
// CONTEXT_{M,L}SB6, second last byte,
|
||||
// CONTEXT_LSB6
|
||||
// CONTEXT_MSB6
|
||||
// CONTEXT_UTF8
|
||||
// CONTEXT_SIGNED
|
||||
}
|
||||
}
|
||||
992
csharp/org/brotli/dec/Decode.cs
Normal file
992
csharp/org/brotli/dec/Decode.cs
Normal file
@@ -0,0 +1,992 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>API for Brotli decompression.</summary>
|
||||
internal sealed class Decode
|
||||
{
|
||||
private const int DefaultCodeLength = 8;
|
||||
|
||||
private const int CodeLengthRepeatCode = 16;
|
||||
|
||||
private const int NumLiteralCodes = 256;
|
||||
|
||||
private const int NumInsertAndCopyCodes = 704;
|
||||
|
||||
private const int NumBlockLengthCodes = 26;
|
||||
|
||||
private const int LiteralContextBits = 6;
|
||||
|
||||
private const int DistanceContextBits = 2;
|
||||
|
||||
private const int HuffmanTableBits = 8;
|
||||
|
||||
private const int HuffmanTableMask = unchecked((int)(0xFF));
|
||||
|
||||
private const int CodeLengthCodes = 18;
|
||||
|
||||
private static readonly int[] CodeLengthCodeOrder = new int[] { 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
|
||||
private const int NumDistanceShortCodes = 16;
|
||||
|
||||
private static readonly int[] DistanceShortCodeIndexOffset = new int[] { 3, 2, 1, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2 };
|
||||
|
||||
private static readonly int[] DistanceShortCodeValueOffset = new int[] { 0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3 };
|
||||
|
||||
/// <summary>Static Huffman code for the code length code lengths.</summary>
|
||||
private static readonly int[] FixedTable = new int[] { unchecked((int)(0x020000)), unchecked((int)(0x020004)), unchecked((int)(0x020003)), unchecked((int)(0x030002)), unchecked((int)(0x020000)), unchecked((int)(0x020004)), unchecked((int)(0x020003
|
||||
)), unchecked((int)(0x040001)), unchecked((int)(0x020000)), unchecked((int)(0x020004)), unchecked((int)(0x020003)), unchecked((int)(0x030002)), unchecked((int)(0x020000)), unchecked((int)(0x020004)), unchecked((int)(0x020003)), unchecked((int
|
||||
)(0x040005)) };
|
||||
|
||||
/// <summary>Decodes a number in the range [0..255], by reading 1 - 11 bits.</summary>
|
||||
private static int DecodeVarLenUnsignedByte(Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
if (Org.Brotli.Dec.BitReader.ReadBits(br, 1) != 0)
|
||||
{
|
||||
int n = Org.Brotli.Dec.BitReader.ReadBits(br, 3);
|
||||
if (n == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Org.Brotli.Dec.BitReader.ReadBits(br, n) + (1 << n);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void DecodeMetaBlockLength(Org.Brotli.Dec.BitReader br, Org.Brotli.Dec.State state)
|
||||
{
|
||||
state.inputEnd = Org.Brotli.Dec.BitReader.ReadBits(br, 1) == 1;
|
||||
state.metaBlockLength = 0;
|
||||
state.isUncompressed = false;
|
||||
state.isMetadata = false;
|
||||
if (state.inputEnd && Org.Brotli.Dec.BitReader.ReadBits(br, 1) != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int sizeNibbles = Org.Brotli.Dec.BitReader.ReadBits(br, 2) + 4;
|
||||
if (sizeNibbles == 7)
|
||||
{
|
||||
state.isMetadata = true;
|
||||
if (Org.Brotli.Dec.BitReader.ReadBits(br, 1) != 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Corrupted reserved bit");
|
||||
}
|
||||
int sizeBytes = Org.Brotli.Dec.BitReader.ReadBits(br, 2);
|
||||
if (sizeBytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < sizeBytes; i++)
|
||||
{
|
||||
int bits = Org.Brotli.Dec.BitReader.ReadBits(br, 8);
|
||||
if (bits == 0 && i + 1 == sizeBytes && sizeBytes > 1)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Exuberant nibble");
|
||||
}
|
||||
state.metaBlockLength |= bits << (i * 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < sizeNibbles; i++)
|
||||
{
|
||||
int bits = Org.Brotli.Dec.BitReader.ReadBits(br, 4);
|
||||
if (bits == 0 && i + 1 == sizeNibbles && sizeNibbles > 4)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Exuberant nibble");
|
||||
}
|
||||
state.metaBlockLength |= bits << (i * 4);
|
||||
}
|
||||
}
|
||||
state.metaBlockLength++;
|
||||
if (!state.inputEnd)
|
||||
{
|
||||
state.isUncompressed = Org.Brotli.Dec.BitReader.ReadBits(br, 1) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Decodes the next Huffman code from bit-stream.</summary>
|
||||
private static int ReadSymbol(int[] table, int offset, Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
int val = (int)((long)(((ulong)br.accumulator) >> br.bitOffset));
|
||||
offset += val & HuffmanTableMask;
|
||||
int bits = table[offset] >> 16;
|
||||
int sym = table[offset] & unchecked((int)(0xFFFF));
|
||||
if (bits <= HuffmanTableBits)
|
||||
{
|
||||
br.bitOffset += bits;
|
||||
return sym;
|
||||
}
|
||||
offset += sym;
|
||||
int mask = (1 << bits) - 1;
|
||||
offset += (int)(((uint)(val & mask)) >> HuffmanTableBits);
|
||||
br.bitOffset += ((table[offset] >> 16) + HuffmanTableBits);
|
||||
return table[offset] & unchecked((int)(0xFFFF));
|
||||
}
|
||||
|
||||
private static int ReadBlockLength(int[] table, int offset, Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
int code = ReadSymbol(table, offset, br);
|
||||
int n = Org.Brotli.Dec.Prefix.BlockLengthNBits[code];
|
||||
return Org.Brotli.Dec.Prefix.BlockLengthOffset[code] + Org.Brotli.Dec.BitReader.ReadBits(br, n);
|
||||
}
|
||||
|
||||
private static int TranslateShortCodes(int code, int[] ringBuffer, int index)
|
||||
{
|
||||
if (code < NumDistanceShortCodes)
|
||||
{
|
||||
index += DistanceShortCodeIndexOffset[code];
|
||||
index &= 3;
|
||||
return ringBuffer[index] + DistanceShortCodeValueOffset[code];
|
||||
}
|
||||
return code - NumDistanceShortCodes + 1;
|
||||
}
|
||||
|
||||
private static void MoveToFront(int[] v, int index)
|
||||
{
|
||||
int value = v[index];
|
||||
for (; index > 0; index--)
|
||||
{
|
||||
v[index] = v[index - 1];
|
||||
}
|
||||
v[0] = value;
|
||||
}
|
||||
|
||||
private static void InverseMoveToFrontTransform(byte[] v, int vLen)
|
||||
{
|
||||
int[] mtf = new int[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
mtf[i] = i;
|
||||
}
|
||||
for (int i = 0; i < vLen; i++)
|
||||
{
|
||||
int index = v[i] & unchecked((int)(0xFF));
|
||||
v[i] = unchecked((byte)mtf[index]);
|
||||
if (index != 0)
|
||||
{
|
||||
MoveToFront(mtf, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadHuffmanCodeLengths(int[] codeLengthCodeLengths, int numSymbols, int[] codeLengths, Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
int symbol = 0;
|
||||
int prevCodeLen = DefaultCodeLength;
|
||||
int repeat = 0;
|
||||
int repeatCodeLen = 0;
|
||||
int space = 32768;
|
||||
int[] table = new int[32];
|
||||
Org.Brotli.Dec.Huffman.BuildHuffmanTable(table, 0, 5, codeLengthCodeLengths, CodeLengthCodes);
|
||||
while (symbol < numSymbols && space > 0)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
int p = (int)(((long)(((ulong)br.accumulator) >> br.bitOffset))) & 31;
|
||||
br.bitOffset += table[p] >> 16;
|
||||
int codeLen = table[p] & unchecked((int)(0xFFFF));
|
||||
if (codeLen < CodeLengthRepeatCode)
|
||||
{
|
||||
repeat = 0;
|
||||
codeLengths[symbol++] = codeLen;
|
||||
if (codeLen != 0)
|
||||
{
|
||||
prevCodeLen = codeLen;
|
||||
space -= 32768 >> codeLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int extraBits = codeLen - 14;
|
||||
int newLen = 0;
|
||||
if (codeLen == CodeLengthRepeatCode)
|
||||
{
|
||||
newLen = prevCodeLen;
|
||||
}
|
||||
if (repeatCodeLen != newLen)
|
||||
{
|
||||
repeat = 0;
|
||||
repeatCodeLen = newLen;
|
||||
}
|
||||
int oldRepeat = repeat;
|
||||
if (repeat > 0)
|
||||
{
|
||||
repeat -= 2;
|
||||
repeat <<= extraBits;
|
||||
}
|
||||
repeat += Org.Brotli.Dec.BitReader.ReadBits(br, extraBits) + 3;
|
||||
int repeatDelta = repeat - oldRepeat;
|
||||
if (symbol + repeatDelta > numSymbols)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("symbol + repeatDelta > numSymbols");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
for (int i = 0; i < repeatDelta; i++)
|
||||
{
|
||||
codeLengths[symbol++] = repeatCodeLen;
|
||||
}
|
||||
if (repeatCodeLen != 0)
|
||||
{
|
||||
space -= repeatDelta << (15 - repeatCodeLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (space != 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Unused space");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
// TODO: Pass max_symbol to Huffman table builder instead?
|
||||
Org.Brotli.Dec.Utils.FillWithZeroes(codeLengths, symbol, numSymbols - symbol);
|
||||
}
|
||||
|
||||
// TODO: Use specialized versions for smaller tables.
|
||||
internal static void ReadHuffmanCode(int alphabetSize, int[] table, int offset, Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
bool ok = true;
|
||||
int simpleCodeOrSkip;
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
// TODO: Avoid allocation.
|
||||
int[] codeLengths = new int[alphabetSize];
|
||||
simpleCodeOrSkip = Org.Brotli.Dec.BitReader.ReadBits(br, 2);
|
||||
if (simpleCodeOrSkip == 1)
|
||||
{
|
||||
// Read symbols, codes & code lengths directly.
|
||||
int maxBitsCounter = alphabetSize - 1;
|
||||
int maxBits = 0;
|
||||
int[] symbols = new int[4];
|
||||
int numSymbols = Org.Brotli.Dec.BitReader.ReadBits(br, 2) + 1;
|
||||
while (maxBitsCounter != 0)
|
||||
{
|
||||
maxBitsCounter >>= 1;
|
||||
maxBits++;
|
||||
}
|
||||
// TODO: uncomment when codeLengths is reused.
|
||||
// Utils.fillWithZeroes(codeLengths, 0, alphabetSize);
|
||||
for (int i = 0; i < numSymbols; i++)
|
||||
{
|
||||
symbols[i] = Org.Brotli.Dec.BitReader.ReadBits(br, maxBits) % alphabetSize;
|
||||
codeLengths[symbols[i]] = 2;
|
||||
}
|
||||
codeLengths[symbols[0]] = 1;
|
||||
switch (numSymbols)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
ok = symbols[0] != symbols[1];
|
||||
codeLengths[symbols[1]] = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
ok = symbols[0] != symbols[1] && symbols[0] != symbols[2] && symbols[1] != symbols[2];
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
default:
|
||||
{
|
||||
ok = symbols[0] != symbols[1] && symbols[0] != symbols[2] && symbols[0] != symbols[3] && symbols[1] != symbols[2] && symbols[1] != symbols[3] && symbols[2] != symbols[3];
|
||||
if (Org.Brotli.Dec.BitReader.ReadBits(br, 1) == 1)
|
||||
{
|
||||
codeLengths[symbols[2]] = 3;
|
||||
codeLengths[symbols[3]] = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
codeLengths[symbols[0]] = 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Decode Huffman-coded code lengths.
|
||||
int[] codeLengthCodeLengths = new int[CodeLengthCodes];
|
||||
int space = 32;
|
||||
int numCodes = 0;
|
||||
for (int i = simpleCodeOrSkip; i < CodeLengthCodes && space > 0; i++)
|
||||
{
|
||||
int codeLenIdx = CodeLengthCodeOrder[i];
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
int p = (int)((long)(((ulong)br.accumulator) >> br.bitOffset)) & 15;
|
||||
// TODO: Demultiplex FIXED_TABLE.
|
||||
br.bitOffset += FixedTable[p] >> 16;
|
||||
int v = FixedTable[p] & unchecked((int)(0xFFFF));
|
||||
codeLengthCodeLengths[codeLenIdx] = v;
|
||||
if (v != 0)
|
||||
{
|
||||
space -= (32 >> v);
|
||||
numCodes++;
|
||||
}
|
||||
}
|
||||
ok = (numCodes == 1 || space == 0);
|
||||
ReadHuffmanCodeLengths(codeLengthCodeLengths, alphabetSize, codeLengths, br);
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Can't readHuffmanCode");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
Org.Brotli.Dec.Huffman.BuildHuffmanTable(table, offset, HuffmanTableBits, codeLengths, alphabetSize);
|
||||
}
|
||||
|
||||
private static int DecodeContextMap(int contextMapSize, byte[] contextMap, Org.Brotli.Dec.BitReader br)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
int numTrees = DecodeVarLenUnsignedByte(br) + 1;
|
||||
if (numTrees == 1)
|
||||
{
|
||||
Org.Brotli.Dec.Utils.FillWithZeroes(contextMap, 0, contextMapSize);
|
||||
return numTrees;
|
||||
}
|
||||
bool useRleForZeros = Org.Brotli.Dec.BitReader.ReadBits(br, 1) == 1;
|
||||
int maxRunLengthPrefix = 0;
|
||||
if (useRleForZeros)
|
||||
{
|
||||
maxRunLengthPrefix = Org.Brotli.Dec.BitReader.ReadBits(br, 4) + 1;
|
||||
}
|
||||
int[] table = new int[Org.Brotli.Dec.Huffman.HuffmanMaxTableSize];
|
||||
ReadHuffmanCode(numTrees + maxRunLengthPrefix, table, 0, br);
|
||||
for (int i = 0; i < contextMapSize; )
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
int code = ReadSymbol(table, 0, br);
|
||||
if (code == 0)
|
||||
{
|
||||
contextMap[i] = 0;
|
||||
i++;
|
||||
}
|
||||
else if (code <= maxRunLengthPrefix)
|
||||
{
|
||||
int reps = (1 << code) + Org.Brotli.Dec.BitReader.ReadBits(br, code);
|
||||
while (reps != 0)
|
||||
{
|
||||
if (i >= contextMapSize)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Corrupted context map");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
contextMap[i] = 0;
|
||||
i++;
|
||||
reps--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
contextMap[i] = unchecked((byte)(code - maxRunLengthPrefix));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (Org.Brotli.Dec.BitReader.ReadBits(br, 1) == 1)
|
||||
{
|
||||
InverseMoveToFrontTransform(contextMap, contextMapSize);
|
||||
}
|
||||
return numTrees;
|
||||
}
|
||||
|
||||
private static void DecodeBlockTypeAndLength(Org.Brotli.Dec.State state, int treeType)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader br = state.br;
|
||||
int[] ringBuffers = state.blockTypeRb;
|
||||
int offset = treeType * 2;
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
int blockType = ReadSymbol(state.blockTypeTrees, treeType * Org.Brotli.Dec.Huffman.HuffmanMaxTableSize, br);
|
||||
state.blockLength[treeType] = ReadBlockLength(state.blockLenTrees, treeType * Org.Brotli.Dec.Huffman.HuffmanMaxTableSize, br);
|
||||
if (blockType == 1)
|
||||
{
|
||||
blockType = ringBuffers[offset + 1] + 1;
|
||||
}
|
||||
else if (blockType == 0)
|
||||
{
|
||||
blockType = ringBuffers[offset];
|
||||
}
|
||||
else
|
||||
{
|
||||
blockType -= 2;
|
||||
}
|
||||
if (blockType >= state.numBlockTypes[treeType])
|
||||
{
|
||||
blockType -= state.numBlockTypes[treeType];
|
||||
}
|
||||
ringBuffers[offset] = ringBuffers[offset + 1];
|
||||
ringBuffers[offset + 1] = blockType;
|
||||
}
|
||||
|
||||
private static void DecodeLiteralBlockSwitch(Org.Brotli.Dec.State state)
|
||||
{
|
||||
DecodeBlockTypeAndLength(state, 0);
|
||||
int literalBlockType = state.blockTypeRb[1];
|
||||
state.contextMapSlice = literalBlockType << LiteralContextBits;
|
||||
state.literalTreeIndex = state.contextMap[state.contextMapSlice] & unchecked((int)(0xFF));
|
||||
state.literalTree = state.hGroup0.trees[state.literalTreeIndex];
|
||||
int contextMode = state.contextModes[literalBlockType];
|
||||
state.contextLookupOffset1 = Org.Brotli.Dec.Context.LookupOffsets[contextMode];
|
||||
state.contextLookupOffset2 = Org.Brotli.Dec.Context.LookupOffsets[contextMode + 1];
|
||||
}
|
||||
|
||||
private static void DecodeCommandBlockSwitch(Org.Brotli.Dec.State state)
|
||||
{
|
||||
DecodeBlockTypeAndLength(state, 1);
|
||||
state.treeCommandOffset = state.hGroup1.trees[state.blockTypeRb[3]];
|
||||
}
|
||||
|
||||
private static void DecodeDistanceBlockSwitch(Org.Brotli.Dec.State state)
|
||||
{
|
||||
DecodeBlockTypeAndLength(state, 2);
|
||||
state.distContextMapSlice = state.blockTypeRb[5] << DistanceContextBits;
|
||||
}
|
||||
|
||||
private static void MaybeReallocateRingBuffer(Org.Brotli.Dec.State state)
|
||||
{
|
||||
int newSize = state.maxRingBufferSize;
|
||||
if ((long)newSize > state.expectedTotalSize)
|
||||
{
|
||||
/* TODO: Handle 2GB+ cases more gracefully. */
|
||||
int minimalNewSize = (int)state.expectedTotalSize + state.customDictionary.Length;
|
||||
while ((newSize >> 1) > minimalNewSize)
|
||||
{
|
||||
newSize >>= 1;
|
||||
}
|
||||
if (!state.inputEnd && newSize < 16384 && state.maxRingBufferSize >= 16384)
|
||||
{
|
||||
newSize = 16384;
|
||||
}
|
||||
}
|
||||
if (newSize <= state.ringBufferSize)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int ringBufferSizeWithSlack = newSize + Org.Brotli.Dec.Dictionary.MaxTransformedWordLength;
|
||||
byte[] newBuffer = new byte[ringBufferSizeWithSlack];
|
||||
if (state.ringBuffer != null)
|
||||
{
|
||||
System.Array.Copy(state.ringBuffer, 0, newBuffer, 0, state.ringBufferSize);
|
||||
}
|
||||
else if (state.customDictionary.Length != 0)
|
||||
{
|
||||
/* Prepend custom dictionary, if any. */
|
||||
int length = state.customDictionary.Length;
|
||||
int offset = 0;
|
||||
if (length > state.maxBackwardDistance)
|
||||
{
|
||||
offset = length - state.maxBackwardDistance;
|
||||
length = state.maxBackwardDistance;
|
||||
}
|
||||
System.Array.Copy(state.customDictionary, offset, newBuffer, 0, length);
|
||||
state.pos = length;
|
||||
state.bytesToIgnore = length;
|
||||
}
|
||||
state.ringBuffer = newBuffer;
|
||||
state.ringBufferSize = newSize;
|
||||
}
|
||||
|
||||
/// <summary>Reads next metablock header.</summary>
|
||||
/// <param name="state">decoding state</param>
|
||||
private static void ReadMetablockInfo(Org.Brotli.Dec.State state)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader br = state.br;
|
||||
if (state.inputEnd)
|
||||
{
|
||||
state.nextRunningState = Org.Brotli.Dec.RunningState.Finished;
|
||||
state.bytesToWrite = state.pos;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Write;
|
||||
return;
|
||||
}
|
||||
// TODO: Reset? Do we need this?
|
||||
state.hGroup0.codes = null;
|
||||
state.hGroup0.trees = null;
|
||||
state.hGroup1.codes = null;
|
||||
state.hGroup1.trees = null;
|
||||
state.hGroup2.codes = null;
|
||||
state.hGroup2.trees = null;
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
DecodeMetaBlockLength(br, state);
|
||||
if (state.metaBlockLength == 0 && !state.isMetadata)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (state.isUncompressed || state.isMetadata)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.JumpToByteBoundary(br);
|
||||
state.runningState = state.isMetadata ? Org.Brotli.Dec.RunningState.ReadMetadata : Org.Brotli.Dec.RunningState.CopyUncompressed;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.runningState = Org.Brotli.Dec.RunningState.CompressedBlockStart;
|
||||
}
|
||||
if (state.isMetadata)
|
||||
{
|
||||
return;
|
||||
}
|
||||
state.expectedTotalSize += state.metaBlockLength;
|
||||
if (state.ringBufferSize < state.maxRingBufferSize)
|
||||
{
|
||||
MaybeReallocateRingBuffer(state);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadMetablockHuffmanCodesAndContextMaps(Org.Brotli.Dec.State state)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader br = state.br;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
state.numBlockTypes[i] = DecodeVarLenUnsignedByte(br) + 1;
|
||||
state.blockLength[i] = 1 << 28;
|
||||
if (state.numBlockTypes[i] > 1)
|
||||
{
|
||||
ReadHuffmanCode(state.numBlockTypes[i] + 2, state.blockTypeTrees, i * Org.Brotli.Dec.Huffman.HuffmanMaxTableSize, br);
|
||||
ReadHuffmanCode(NumBlockLengthCodes, state.blockLenTrees, i * Org.Brotli.Dec.Huffman.HuffmanMaxTableSize, br);
|
||||
state.blockLength[i] = ReadBlockLength(state.blockLenTrees, i * Org.Brotli.Dec.Huffman.HuffmanMaxTableSize, br);
|
||||
}
|
||||
}
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
state.distancePostfixBits = Org.Brotli.Dec.BitReader.ReadBits(br, 2);
|
||||
state.numDirectDistanceCodes = NumDistanceShortCodes + (Org.Brotli.Dec.BitReader.ReadBits(br, 4) << state.distancePostfixBits);
|
||||
state.distancePostfixMask = (1 << state.distancePostfixBits) - 1;
|
||||
int numDistanceCodes = state.numDirectDistanceCodes + (48 << state.distancePostfixBits);
|
||||
// TODO: Reuse?
|
||||
state.contextModes = new byte[state.numBlockTypes[0]];
|
||||
for (int i = 0; i < state.numBlockTypes[0]; )
|
||||
{
|
||||
/* Ensure that less than 256 bits read between readMoreInput. */
|
||||
int limit = System.Math.Min(i + 96, state.numBlockTypes[0]);
|
||||
for (; i < limit; ++i)
|
||||
{
|
||||
state.contextModes[i] = unchecked((byte)(Org.Brotli.Dec.BitReader.ReadBits(br, 2) << 1));
|
||||
}
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
}
|
||||
// TODO: Reuse?
|
||||
state.contextMap = new byte[state.numBlockTypes[0] << LiteralContextBits];
|
||||
int numLiteralTrees = DecodeContextMap(state.numBlockTypes[0] << LiteralContextBits, state.contextMap, br);
|
||||
state.trivialLiteralContext = true;
|
||||
for (int j = 0; j < state.numBlockTypes[0] << LiteralContextBits; j++)
|
||||
{
|
||||
if (state.contextMap[j] != j >> LiteralContextBits)
|
||||
{
|
||||
state.trivialLiteralContext = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: Reuse?
|
||||
state.distContextMap = new byte[state.numBlockTypes[2] << DistanceContextBits];
|
||||
int numDistTrees = DecodeContextMap(state.numBlockTypes[2] << DistanceContextBits, state.distContextMap, br);
|
||||
Org.Brotli.Dec.HuffmanTreeGroup.Init(state.hGroup0, NumLiteralCodes, numLiteralTrees);
|
||||
Org.Brotli.Dec.HuffmanTreeGroup.Init(state.hGroup1, NumInsertAndCopyCodes, state.numBlockTypes[1]);
|
||||
Org.Brotli.Dec.HuffmanTreeGroup.Init(state.hGroup2, numDistanceCodes, numDistTrees);
|
||||
Org.Brotli.Dec.HuffmanTreeGroup.Decode(state.hGroup0, br);
|
||||
Org.Brotli.Dec.HuffmanTreeGroup.Decode(state.hGroup1, br);
|
||||
Org.Brotli.Dec.HuffmanTreeGroup.Decode(state.hGroup2, br);
|
||||
state.contextMapSlice = 0;
|
||||
state.distContextMapSlice = 0;
|
||||
state.contextLookupOffset1 = Org.Brotli.Dec.Context.LookupOffsets[state.contextModes[0]];
|
||||
state.contextLookupOffset2 = Org.Brotli.Dec.Context.LookupOffsets[state.contextModes[0] + 1];
|
||||
state.literalTreeIndex = 0;
|
||||
state.literalTree = state.hGroup0.trees[0];
|
||||
state.treeCommandOffset = state.hGroup1.trees[0];
|
||||
// TODO: == 0?
|
||||
state.blockTypeRb[0] = state.blockTypeRb[2] = state.blockTypeRb[4] = 1;
|
||||
state.blockTypeRb[1] = state.blockTypeRb[3] = state.blockTypeRb[5] = 0;
|
||||
}
|
||||
|
||||
private static void CopyUncompressedData(Org.Brotli.Dec.State state)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader br = state.br;
|
||||
byte[] ringBuffer = state.ringBuffer;
|
||||
// Could happen if block ends at ring buffer end.
|
||||
if (state.metaBlockLength <= 0)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.Reload(br);
|
||||
state.runningState = Org.Brotli.Dec.RunningState.BlockStart;
|
||||
return;
|
||||
}
|
||||
int chunkLength = System.Math.Min(state.ringBufferSize - state.pos, state.metaBlockLength);
|
||||
Org.Brotli.Dec.BitReader.CopyBytes(br, ringBuffer, state.pos, chunkLength);
|
||||
state.metaBlockLength -= chunkLength;
|
||||
state.pos += chunkLength;
|
||||
if (state.pos == state.ringBufferSize)
|
||||
{
|
||||
state.nextRunningState = Org.Brotli.Dec.RunningState.CopyUncompressed;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Write;
|
||||
return;
|
||||
}
|
||||
Org.Brotli.Dec.BitReader.Reload(br);
|
||||
state.runningState = Org.Brotli.Dec.RunningState.BlockStart;
|
||||
}
|
||||
|
||||
private static bool WriteRingBuffer(Org.Brotli.Dec.State state)
|
||||
{
|
||||
/* Ignore custom dictionary bytes. */
|
||||
if (state.bytesToIgnore != 0)
|
||||
{
|
||||
state.bytesWritten += state.bytesToIgnore;
|
||||
state.bytesToIgnore = 0;
|
||||
}
|
||||
int toWrite = System.Math.Min(state.outputLength - state.outputUsed, state.bytesToWrite - state.bytesWritten);
|
||||
if (toWrite != 0)
|
||||
{
|
||||
System.Array.Copy(state.ringBuffer, state.bytesWritten, state.output, state.outputOffset + state.outputUsed, toWrite);
|
||||
state.outputUsed += toWrite;
|
||||
state.bytesWritten += toWrite;
|
||||
}
|
||||
return state.outputUsed < state.outputLength;
|
||||
}
|
||||
|
||||
internal static void SetCustomDictionary(Org.Brotli.Dec.State state, byte[] data)
|
||||
{
|
||||
state.customDictionary = (data == null) ? new byte[0] : data;
|
||||
}
|
||||
|
||||
/// <summary>Actual decompress implementation.</summary>
|
||||
internal static void Decompress(Org.Brotli.Dec.State state)
|
||||
{
|
||||
if (state.runningState == Org.Brotli.Dec.RunningState.Uninitialized)
|
||||
{
|
||||
throw new System.InvalidOperationException("Can't decompress until initialized");
|
||||
}
|
||||
if (state.runningState == Org.Brotli.Dec.RunningState.Closed)
|
||||
{
|
||||
throw new System.InvalidOperationException("Can't decompress after close");
|
||||
}
|
||||
Org.Brotli.Dec.BitReader br = state.br;
|
||||
int ringBufferMask = state.ringBufferSize - 1;
|
||||
byte[] ringBuffer = state.ringBuffer;
|
||||
while (state.runningState != Org.Brotli.Dec.RunningState.Finished)
|
||||
{
|
||||
switch (state.runningState)
|
||||
{
|
||||
case Org.Brotli.Dec.RunningState.BlockStart:
|
||||
{
|
||||
// TODO: extract cases to methods for the better readability.
|
||||
if (state.metaBlockLength < 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Invalid metablock length");
|
||||
}
|
||||
ReadMetablockInfo(state);
|
||||
/* Ring-buffer would be reallocated here. */
|
||||
ringBufferMask = state.ringBufferSize - 1;
|
||||
ringBuffer = state.ringBuffer;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.CompressedBlockStart:
|
||||
{
|
||||
ReadMetablockHuffmanCodesAndContextMaps(state);
|
||||
state.runningState = Org.Brotli.Dec.RunningState.MainLoop;
|
||||
goto case Org.Brotli.Dec.RunningState.MainLoop;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.MainLoop:
|
||||
{
|
||||
// Fall through
|
||||
if (state.metaBlockLength <= 0)
|
||||
{
|
||||
state.runningState = Org.Brotli.Dec.RunningState.BlockStart;
|
||||
continue;
|
||||
}
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
if (state.blockLength[1] == 0)
|
||||
{
|
||||
DecodeCommandBlockSwitch(state);
|
||||
}
|
||||
state.blockLength[1]--;
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
int cmdCode = ReadSymbol(state.hGroup1.codes, state.treeCommandOffset, br);
|
||||
int rangeIdx = (int)(((uint)cmdCode) >> 6);
|
||||
state.distanceCode = 0;
|
||||
if (rangeIdx >= 2)
|
||||
{
|
||||
rangeIdx -= 2;
|
||||
state.distanceCode = -1;
|
||||
}
|
||||
int insertCode = Org.Brotli.Dec.Prefix.InsertRangeLut[rangeIdx] + (((int)(((uint)cmdCode) >> 3)) & 7);
|
||||
int copyCode = Org.Brotli.Dec.Prefix.CopyRangeLut[rangeIdx] + (cmdCode & 7);
|
||||
state.insertLength = Org.Brotli.Dec.Prefix.InsertLengthOffset[insertCode] + Org.Brotli.Dec.BitReader.ReadBits(br, Org.Brotli.Dec.Prefix.InsertLengthNBits[insertCode]);
|
||||
state.copyLength = Org.Brotli.Dec.Prefix.CopyLengthOffset[copyCode] + Org.Brotli.Dec.BitReader.ReadBits(br, Org.Brotli.Dec.Prefix.CopyLengthNBits[copyCode]);
|
||||
state.j = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.InsertLoop;
|
||||
goto case Org.Brotli.Dec.RunningState.InsertLoop;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.InsertLoop:
|
||||
{
|
||||
// Fall through
|
||||
if (state.trivialLiteralContext)
|
||||
{
|
||||
while (state.j < state.insertLength)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
if (state.blockLength[0] == 0)
|
||||
{
|
||||
DecodeLiteralBlockSwitch(state);
|
||||
}
|
||||
state.blockLength[0]--;
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
ringBuffer[state.pos] = unchecked((byte)ReadSymbol(state.hGroup0.codes, state.literalTree, br));
|
||||
state.j++;
|
||||
if (state.pos++ == ringBufferMask)
|
||||
{
|
||||
state.nextRunningState = Org.Brotli.Dec.RunningState.InsertLoop;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Write;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int prevByte1 = ringBuffer[(state.pos - 1) & ringBufferMask] & unchecked((int)(0xFF));
|
||||
int prevByte2 = ringBuffer[(state.pos - 2) & ringBufferMask] & unchecked((int)(0xFF));
|
||||
while (state.j < state.insertLength)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
if (state.blockLength[0] == 0)
|
||||
{
|
||||
DecodeLiteralBlockSwitch(state);
|
||||
}
|
||||
int literalTreeIndex = state.contextMap[state.contextMapSlice + (Org.Brotli.Dec.Context.Lookup[state.contextLookupOffset1 + prevByte1] | Org.Brotli.Dec.Context.Lookup[state.contextLookupOffset2 + prevByte2])] & unchecked((int)(0xFF));
|
||||
state.blockLength[0]--;
|
||||
prevByte2 = prevByte1;
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
prevByte1 = ReadSymbol(state.hGroup0.codes, state.hGroup0.trees[literalTreeIndex], br);
|
||||
ringBuffer[state.pos] = unchecked((byte)prevByte1);
|
||||
state.j++;
|
||||
if (state.pos++ == ringBufferMask)
|
||||
{
|
||||
state.nextRunningState = Org.Brotli.Dec.RunningState.InsertLoop;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Write;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.runningState != Org.Brotli.Dec.RunningState.InsertLoop)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
state.metaBlockLength -= state.insertLength;
|
||||
if (state.metaBlockLength <= 0)
|
||||
{
|
||||
state.runningState = Org.Brotli.Dec.RunningState.MainLoop;
|
||||
continue;
|
||||
}
|
||||
if (state.distanceCode < 0)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
if (state.blockLength[2] == 0)
|
||||
{
|
||||
DecodeDistanceBlockSwitch(state);
|
||||
}
|
||||
state.blockLength[2]--;
|
||||
Org.Brotli.Dec.BitReader.FillBitWindow(br);
|
||||
state.distanceCode = ReadSymbol(state.hGroup2.codes, state.hGroup2.trees[state.distContextMap[state.distContextMapSlice + (state.copyLength > 4 ? 3 : state.copyLength - 2)] & unchecked((int)(0xFF))], br);
|
||||
if (state.distanceCode >= state.numDirectDistanceCodes)
|
||||
{
|
||||
state.distanceCode -= state.numDirectDistanceCodes;
|
||||
int postfix = state.distanceCode & state.distancePostfixMask;
|
||||
state.distanceCode = (int)(((uint)state.distanceCode) >> state.distancePostfixBits);
|
||||
int n = ((int)(((uint)state.distanceCode) >> 1)) + 1;
|
||||
int offset = ((2 + (state.distanceCode & 1)) << n) - 4;
|
||||
state.distanceCode = state.numDirectDistanceCodes + postfix + ((offset + Org.Brotli.Dec.BitReader.ReadBits(br, n)) << state.distancePostfixBits);
|
||||
}
|
||||
}
|
||||
// Convert the distance code to the actual distance by possibly looking up past distances
|
||||
// from the ringBuffer.
|
||||
state.distance = TranslateShortCodes(state.distanceCode, state.distRb, state.distRbIdx);
|
||||
if (state.distance < 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Negative distance");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
if (state.maxDistance != state.maxBackwardDistance && state.pos < state.maxBackwardDistance)
|
||||
{
|
||||
state.maxDistance = state.pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.maxDistance = state.maxBackwardDistance;
|
||||
}
|
||||
state.copyDst = state.pos;
|
||||
if (state.distance > state.maxDistance)
|
||||
{
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Transform;
|
||||
continue;
|
||||
}
|
||||
if (state.distanceCode > 0)
|
||||
{
|
||||
state.distRb[state.distRbIdx & 3] = state.distance;
|
||||
state.distRbIdx++;
|
||||
}
|
||||
if (state.copyLength > state.metaBlockLength)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Invalid backward reference");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
state.j = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.CopyLoop;
|
||||
goto case Org.Brotli.Dec.RunningState.CopyLoop;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.CopyLoop:
|
||||
{
|
||||
// fall through
|
||||
int src = (state.pos - state.distance) & ringBufferMask;
|
||||
int dst = state.pos;
|
||||
int copyLength = state.copyLength - state.j;
|
||||
if ((src + copyLength < ringBufferMask) && (dst + copyLength < ringBufferMask))
|
||||
{
|
||||
for (int k = 0; k < copyLength; ++k)
|
||||
{
|
||||
ringBuffer[dst++] = ringBuffer[src++];
|
||||
}
|
||||
state.j += copyLength;
|
||||
state.metaBlockLength -= copyLength;
|
||||
state.pos += copyLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; state.j < state.copyLength; )
|
||||
{
|
||||
ringBuffer[state.pos] = ringBuffer[(state.pos - state.distance) & ringBufferMask];
|
||||
state.metaBlockLength--;
|
||||
state.j++;
|
||||
if (state.pos++ == ringBufferMask)
|
||||
{
|
||||
state.nextRunningState = Org.Brotli.Dec.RunningState.CopyLoop;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Write;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.runningState == Org.Brotli.Dec.RunningState.CopyLoop)
|
||||
{
|
||||
state.runningState = Org.Brotli.Dec.RunningState.MainLoop;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.Transform:
|
||||
{
|
||||
if (state.copyLength >= Org.Brotli.Dec.Dictionary.MinWordLength && state.copyLength <= Org.Brotli.Dec.Dictionary.MaxWordLength)
|
||||
{
|
||||
int offset = Org.Brotli.Dec.Dictionary.OffsetsByLength[state.copyLength];
|
||||
int wordId = state.distance - state.maxDistance - 1;
|
||||
int shift = Org.Brotli.Dec.Dictionary.SizeBitsByLength[state.copyLength];
|
||||
int mask = (1 << shift) - 1;
|
||||
int wordIdx = wordId & mask;
|
||||
int transformIdx = (int)(((uint)wordId) >> shift);
|
||||
offset += wordIdx * state.copyLength;
|
||||
if (transformIdx < Org.Brotli.Dec.Transform.Transforms.Length)
|
||||
{
|
||||
int len = Org.Brotli.Dec.Transform.TransformDictionaryWord(ringBuffer, state.copyDst, Org.Brotli.Dec.Dictionary.GetData(), offset, state.copyLength, Org.Brotli.Dec.Transform.Transforms[transformIdx]);
|
||||
state.copyDst += len;
|
||||
state.pos += len;
|
||||
state.metaBlockLength -= len;
|
||||
if (state.copyDst >= state.ringBufferSize)
|
||||
{
|
||||
state.nextRunningState = Org.Brotli.Dec.RunningState.CopyWrapBuffer;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = Org.Brotli.Dec.RunningState.Write;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Invalid backward reference");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// COV_NF_LINE
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Invalid backward reference");
|
||||
}
|
||||
// COV_NF_LINE
|
||||
state.runningState = Org.Brotli.Dec.RunningState.MainLoop;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.CopyWrapBuffer:
|
||||
{
|
||||
System.Array.Copy(ringBuffer, state.ringBufferSize, ringBuffer, 0, state.copyDst - state.ringBufferSize);
|
||||
state.runningState = Org.Brotli.Dec.RunningState.MainLoop;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.ReadMetadata:
|
||||
{
|
||||
while (state.metaBlockLength > 0)
|
||||
{
|
||||
Org.Brotli.Dec.BitReader.ReadMoreInput(br);
|
||||
// Optimize
|
||||
Org.Brotli.Dec.BitReader.ReadBits(br, 8);
|
||||
state.metaBlockLength--;
|
||||
}
|
||||
state.runningState = Org.Brotli.Dec.RunningState.BlockStart;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.CopyUncompressed:
|
||||
{
|
||||
CopyUncompressedData(state);
|
||||
continue;
|
||||
}
|
||||
|
||||
case Org.Brotli.Dec.RunningState.Write:
|
||||
{
|
||||
if (!WriteRingBuffer(state))
|
||||
{
|
||||
// Output buffer is full.
|
||||
return;
|
||||
}
|
||||
if (state.pos >= state.maxBackwardDistance)
|
||||
{
|
||||
state.maxDistance = state.maxBackwardDistance;
|
||||
}
|
||||
state.pos &= ringBufferMask;
|
||||
state.runningState = state.nextRunningState;
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Unexpected state " + state.runningState);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.runningState == Org.Brotli.Dec.RunningState.Finished)
|
||||
{
|
||||
if (state.metaBlockLength < 0)
|
||||
{
|
||||
throw new Org.Brotli.Dec.BrotliRuntimeException("Invalid metablock length");
|
||||
}
|
||||
Org.Brotli.Dec.BitReader.JumpToByteBoundary(br);
|
||||
Org.Brotli.Dec.BitReader.CheckHealth(state.br, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
171
csharp/org/brotli/dec/DecodeTest.cs
Normal file
171
csharp/org/brotli/dec/DecodeTest.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
/* Copyright 2015 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
namespace Org.Brotli.Dec
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for
|
||||
/// <see cref="Decode"/>
|
||||
/// .
|
||||
/// </summary>
|
||||
public class DecodeTest
|
||||
{
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
private byte[] Decompress(byte[] data, bool byByte)
|
||||
{
|
||||
byte[] buffer = new byte[65536];
|
||||
System.IO.MemoryStream input = new System.IO.MemoryStream(data);
|
||||
System.IO.MemoryStream output = new System.IO.MemoryStream();
|
||||
Org.Brotli.Dec.BrotliInputStream brotliInput = new Org.Brotli.Dec.BrotliInputStream(input);
|
||||
if (byByte)
|
||||
{
|
||||
byte[] oneByte = new byte[1];
|
||||
while (true)
|
||||
{
|
||||
int next = brotliInput.ReadByte();
|
||||
if (next == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
oneByte[0] = unchecked((byte)next);
|
||||
output.Write(oneByte, 0, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int len = brotliInput.Read(buffer, 0, buffer.Length);
|
||||
if (len <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
output.Write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
brotliInput.Close();
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
private byte[] DecompressWithDictionary(byte[] data, byte[] dictionary)
|
||||
{
|
||||
byte[] buffer = new byte[65536];
|
||||
System.IO.MemoryStream input = new System.IO.MemoryStream(data);
|
||||
System.IO.MemoryStream output = new System.IO.MemoryStream();
|
||||
Org.Brotli.Dec.BrotliInputStream brotliInput = new Org.Brotli.Dec.BrotliInputStream(input, Org.Brotli.Dec.BrotliInputStream.DefaultInternalBufferSize, dictionary);
|
||||
while (true)
|
||||
{
|
||||
int len = brotliInput.Read(buffer, 0, buffer.Length);
|
||||
if (len <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
output.Write(buffer, 0, len);
|
||||
}
|
||||
brotliInput.Close();
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
private void CheckDecodeResourceWithDictionary(string expected, string compressed, string dictionary)
|
||||
{
|
||||
byte[] expectedBytes = Org.Brotli.Dec.Transform.ReadUniBytes(expected);
|
||||
byte[] compressedBytes = Org.Brotli.Dec.Transform.ReadUniBytes(compressed);
|
||||
byte[] dictionaryBytes = Org.Brotli.Dec.Transform.ReadUniBytes(dictionary);
|
||||
byte[] actual = DecompressWithDictionary(compressedBytes, dictionaryBytes);
|
||||
NUnit.Framework.Assert.AreEqual(expectedBytes, actual);
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
private void CheckDecodeResource(string expected, string compressed)
|
||||
{
|
||||
byte[] expectedBytes = Org.Brotli.Dec.Transform.ReadUniBytes(expected);
|
||||
byte[] compressedBytes = Org.Brotli.Dec.Transform.ReadUniBytes(compressed);
|
||||
byte[] actual = Decompress(compressedBytes, false);
|
||||
NUnit.Framework.Assert.AreEqual(expectedBytes, actual);
|
||||
byte[] actualByByte = Decompress(compressedBytes, true);
|
||||
NUnit.Framework.Assert.AreEqual(expectedBytes, actualByByte);
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestEmpty()
|
||||
{
|
||||
CheckDecodeResource(string.Empty, "\u0006");
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestX()
|
||||
{
|
||||
CheckDecodeResource("X", "\u000B\u0000\u0080X\u0003");
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestX10Y10()
|
||||
{
|
||||
CheckDecodeResource("XXXXXXXXXXYYYYYYYYYY", "\u001B\u0013\u0000\u0000\u00A4\u00B0\u00B2\u00EA\u0081G\u0002\u008A");
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestX64()
|
||||
{
|
||||
CheckDecodeResource("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "\u001B\u003F\u0000\u0000$\u00B0\u00E2\u0099\u0080\u0012");
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestUkkonooa()
|
||||
{
|
||||
CheckDecodeResource("ukko nooa, ukko nooa oli kunnon mies, kun han meni saunaan, " + "pisti laukun naulaan, ukko nooa, ukko nooa oli kunnon mies.", "\u001Bv\u0000\u0000\u0014J\u00AC\u009Bz\u00BD\u00E1\u0097\u009D\u007F\u008E\u00C2\u0082" + "6\u000E\u009C\u00E0\u0090\u0003\u00F7\u008B\u009E8\u00E6\u00B6\u0000\u00AB\u00C3\u00CA"
|
||||
+ "\u00A0\u00C2\u00DAf6\u00DC\u00CD\u0080\u008D.!\u00D7n\u00E3\u00EAL\u00B8\u00F0\u00D2" + "\u00B8\u00C7\u00C2pM:\u00F0i~\u00A1\u00B8Es\u00AB\u00C4W\u001E");
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestMonkey()
|
||||
{
|
||||
CheckDecodeResource("znxcvnmz,xvnm.,zxcnv.,xcn.z,vn.zvn.zxcvn.,zxcn.vn.v,znm.,vnzx.,vnzxc.vn.z,vnz.,nv.z,nvmz" + "xc,nvzxcvcnm.,vczxvnzxcnvmxc.zmcnvzm.,nvmc,nzxmc,vn.mnnmzxc,vnxcnmv,znvzxcnmv,.xcnvm,zxc" + "nzxv.zx,qweryweurqioweupropqwutioweupqrioweutiopweuriopweuriopqwurioputiopqwuriowuqeriou"
|
||||
+ "pqweropuweropqwurweuqriopuropqwuriopuqwriopuqweopruioqweurqweuriouqweopruioupqiytioqtyio" + "wtyqptypryoqweutioioqtweqruowqeytiowquiourowetyoqwupiotweuqiorweuqroipituqwiorqwtioweuri" + "ouytuioerytuioweryuitoweytuiweyuityeruirtyuqriqweuropqweiruioqweurioqwuerioqwyuituierwot"
|
||||
+ "ueryuiotweyrtuiwertyioweryrueioqptyioruyiopqwtjkasdfhlafhlasdhfjklashjkfhasjklfhklasjdfh" + "klasdhfjkalsdhfklasdhjkflahsjdkfhklasfhjkasdfhasfjkasdhfklsdhalghhaf;hdklasfhjklashjklfa" + "sdhfasdjklfhsdjklafsd;hkldadfjjklasdhfjasddfjklfhakjklasdjfkl;asdjfasfljasdfhjklasdfhjka"
|
||||
+ "ghjkashf;djfklasdjfkljasdklfjklasdjfkljasdfkljaklfj", "\u001BJ\u0003\u0000\u008C\u0094n\u00DE\u00B4\u00D7\u0096\u00B1x\u0086\u00F2-\u00E1\u001A" + "\u00BC\u000B\u001C\u00BA\u00A9\u00C7\u00F7\u00CCn\u00B2B4QD\u008BN\u0013\b\u00A0\u00CDn"
|
||||
+ "\u00E8,\u00A5S\u00A1\u009C],\u001D#\u001A\u00D2V\u00BE\u00DB\u00EB&\u00BA\u0003e|\u0096j" + "\u00A2v\u00EC\u00EF\u0087G3\u00D6\'\u000Ec\u0095\u00E2\u001D\u008D,\u00C5\u00D1(\u009F`" + "\u0094o\u0002\u008B\u00DD\u00AAd\u0094,\u001E;e|\u0007EZ\u00B2\u00E2\u00FCI\u0081,\u009F"
|
||||
+ "@\u00AE\u00EFh\u0081\u00AC\u0016z\u000F\u00F5;m\u001C\u00B9\u001E-_\u00D5\u00C8\u00AF^" + "\u0085\u00AA\u0005\u00BESu\u00C2\u00B0\"\u008A\u0015\u00C6\u00A3\u00B1\u00E6B\u0014" + "\u00F4\u0084TS\u0019_\u00BE\u00C3\u00F2\u001D\u00D1\u00B7\u00E5\u00DD\u00B6\u00D9#\u00C6"
|
||||
+ "\u00F6\u009F\u009E\u00F6Me0\u00FB\u00C0qE\u0004\u00AD\u0003\u00B5\u00BE\u00C9\u00CB" + "\u00FD\u00E2PZFt\u0004\r\u00FF \u0004w\u00B2m\'\u00BFG\u00A9\u009D\u001B\u0096,b\u0090#" + "\u008B\u00E0\u00F8\u001D\u00CF\u00AF\u001D=\u00EE\u008A\u00C8u#f\u00DD\u00DE\u00D6m"
|
||||
+ "\u00E3*\u0082\u008Ax\u008A\u00DB\u00E6 L\u00B7\\c\u00BA0\u00E3?\u00B6\u00EE\u008C\"" + "\u00A2*\u00B0\"\n\u0099\u00FF=bQ\u00EE\b\u00F6=J\u00E4\u00CC\u00EF\"\u0087\u0011\u00E2" + "\u0083(\u00E4\u00F5\u008F5\u0019c[\u00E1Z\u0092s\u00DD\u00A1P\u009D8\\\u00EB\u00B5\u0003"
|
||||
+ "jd\u0090\u0094\u00C8\u008D\u00FB/\u008A\u0086\"\u00CC\u001D\u0087\u00E0H\n\u0096w\u00909" + "\u00C6##H\u00FB\u0011GV\u00CA \u00E3B\u0081\u00F7w2\u00C1\u00A5\\@!e\u0017@)\u0017\u0017" + "lV2\u00988\u0006\u00DC\u0099M3)\u00BB\u0002\u00DFL&\u0093l\u0017\u0082\u0086 \u00D7"
|
||||
+ "\u0003y}\u009A\u0000\u00D7\u0087\u0000\u00E7\u000Bf\u00E3Lfqg\b2\u00F9\b>\u00813\u00CD" + "\u0017r1\u00F0\u00B8\u0094RK\u00901\u008Eh\u00C1\u00EF\u0090\u00C9\u00E5\u00F2a\tr%" + "\u00AD\u00EC\u00C5b\u00C0\u000B\u0012\u0005\u00F7\u0091u\r\u00EEa..\u0019\t\u00C2\u0003"
|
||||
);
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestFox()
|
||||
{
|
||||
CheckDecodeResource("The quick brown fox jumps over the lazy dog", "\u001B*\u0000\u0000\u0004\u0004\u00BAF:\u0085\u0003\u00E9\u00FA\f\u0091\u0002H\u0011," + "\u00F3\u008A:\u00A3V\u007F\u001A\u00AE\u00BF\u00A4\u00AB\u008EM\u00BF\u00ED\u00E2\u0004K"
|
||||
+ "\u0091\u00FF\u0087\u00E9\u001E");
|
||||
}
|
||||
|
||||
/// <exception cref="System.IO.IOException"/>
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestFoxFox()
|
||||
{
|
||||
CheckDecodeResourceWithDictionary("The quick brown fox jumps over the lazy dog", "\u001B*\u0000\u0000 \u0000\u00C2\u0098\u00B0\u00CA\u0001", "The quick brown fox jumps over the lazy dog");
|
||||
}
|
||||
|
||||
[NUnit.Framework.Test]
|
||||
public virtual void TestUtils()
|
||||
{
|
||||
new Org.Brotli.Dec.Context();
|
||||
new Org.Brotli.Dec.Decode();
|
||||
new Org.Brotli.Dec.Dictionary();
|
||||
new Org.Brotli.Dec.Huffman();
|
||||
new Org.Brotli.Dec.Prefix();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user