brotli/quality.go

197 lines
5.5 KiB
Go

package brotli
const FAST_ONE_PASS_COMPRESSION_QUALITY = 0
const FAST_TWO_PASS_COMPRESSION_QUALITY = 1
const ZOPFLIFICATION_QUALITY = 10
const HQ_ZOPFLIFICATION_QUALITY = 11
const MAX_QUALITY_FOR_STATIC_ENTROPY_CODES = 2
const MIN_QUALITY_FOR_BLOCK_SPLIT = 4
const MIN_QUALITY_FOR_NONZERO_DISTANCE_PARAMS = 4
const MIN_QUALITY_FOR_OPTIMIZE_HISTOGRAMS = 4
const MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH = 5
const MIN_QUALITY_FOR_CONTEXT_MODELING = 5
const MIN_QUALITY_FOR_HQ_CONTEXT_MODELING = 7
const MIN_QUALITY_FOR_HQ_BLOCK_SPLITTING = 10
/* For quality below MIN_QUALITY_FOR_BLOCK_SPLIT there is no block splitting,
so we buffer at most this much literals and commands. */
const MAX_NUM_DELAYED_SYMBOLS = 0x2FFF
/* Returns hash-table size for quality levels 0 and 1. */
func MaxHashTableSize(quality int) uint {
if quality == FAST_ONE_PASS_COMPRESSION_QUALITY {
return 1 << 15
} else {
return 1 << 17
}
}
/* The maximum length for which the zopflification uses distinct distances. */
const MAX_ZOPFLI_LEN_QUALITY_10 = 150
const MAX_ZOPFLI_LEN_QUALITY_11 = 325
/* Do not thoroughly search when a long copy is found. */
const BROTLI_LONG_COPY_QUICK_STEP = 16384
func MaxZopfliLen(params *BrotliEncoderParams) uint {
if params.quality <= 10 {
return MAX_ZOPFLI_LEN_QUALITY_10
} else {
return MAX_ZOPFLI_LEN_QUALITY_11
}
}
/* Number of best candidates to evaluate to expand Zopfli chain. */
func MaxZopfliCandidates(params *BrotliEncoderParams) uint {
if params.quality <= 10 {
return 1
} else {
return 5
}
}
func SanitizeParams(params *BrotliEncoderParams) {
params.quality = brotli_min_int(BROTLI_MAX_QUALITY, brotli_max_int(BROTLI_MIN_QUALITY, params.quality))
if params.quality <= MAX_QUALITY_FOR_STATIC_ENTROPY_CODES {
params.large_window = false
}
if params.lgwin < BROTLI_MIN_WINDOW_BITS {
params.lgwin = BROTLI_MIN_WINDOW_BITS
} else {
var max_lgwin int
if params.large_window {
max_lgwin = BROTLI_LARGE_MAX_WINDOW_BITS
} else {
max_lgwin = BROTLI_MAX_WINDOW_BITS
}
if params.lgwin > uint(max_lgwin) {
params.lgwin = uint(max_lgwin)
}
}
}
/* Returns optimized lg_block value. */
func ComputeLgBlock(params *BrotliEncoderParams) int {
var lgblock int = params.lgblock
if params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY || params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY {
lgblock = int(params.lgwin)
} else if params.quality < MIN_QUALITY_FOR_BLOCK_SPLIT {
lgblock = 14
} else if lgblock == 0 {
lgblock = 16
if params.quality >= 9 && params.lgwin > uint(lgblock) {
lgblock = brotli_min_int(18, int(params.lgwin))
}
} else {
lgblock = brotli_min_int(BROTLI_MAX_INPUT_BLOCK_BITS, brotli_max_int(BROTLI_MIN_INPUT_BLOCK_BITS, lgblock))
}
return lgblock
}
/* Returns log2 of the size of main ring buffer area.
Allocate at least lgwin + 1 bits for the ring buffer so that the newly
added block fits there completely and we still get lgwin bits and at least
read_block_size_bits + 1 bits because the copy tail length needs to be
smaller than ring-buffer size. */
func ComputeRbBits(params *BrotliEncoderParams) int {
return 1 + brotli_max_int(int(params.lgwin), params.lgblock)
}
func MaxMetablockSize(params *BrotliEncoderParams) uint {
var bits int = brotli_min_int(ComputeRbBits(params), BROTLI_MAX_INPUT_BLOCK_BITS)
return uint(1) << uint(bits)
}
/* When searching for backward references and have not seen matches for a long
time, we can skip some match lookups. Unsuccessful match lookups are very
expensive and this kind of a heuristic speeds up compression quite a lot.
At first 8 byte strides are taken and every second byte is put to hasher.
After 4x more literals stride by 16 bytes, every put 4-th byte to hasher.
Applied only to qualities 2 to 9. */
func LiteralSpreeLengthForSparseSearch(params *BrotliEncoderParams) uint {
if params.quality < 9 {
return 64
} else {
return 512
}
}
func ChooseHasher(params *BrotliEncoderParams, hparams *BrotliHasherParams) {
if params.quality > 9 {
hparams.type_ = 10
} else if params.quality == 4 && params.size_hint >= 1<<20 {
hparams.type_ = 54
} else if params.quality < 5 {
hparams.type_ = params.quality
} else if params.lgwin <= 16 {
if params.quality < 7 {
hparams.type_ = 40
} else if params.quality < 9 {
hparams.type_ = 41
} else {
hparams.type_ = 42
}
} else if params.size_hint >= 1<<20 && params.lgwin >= 19 {
hparams.type_ = 6
hparams.block_bits = params.quality - 1
hparams.bucket_bits = 15
hparams.hash_len = 5
if params.quality < 7 {
hparams.num_last_distances_to_check = 4
} else if params.quality < 9 {
hparams.num_last_distances_to_check = 10
} else {
hparams.num_last_distances_to_check = 16
}
} else {
hparams.type_ = 5
hparams.block_bits = params.quality - 1
if params.quality < 7 {
hparams.bucket_bits = 14
} else {
hparams.bucket_bits = 15
}
if params.quality < 7 {
hparams.num_last_distances_to_check = 4
} else if params.quality < 9 {
hparams.num_last_distances_to_check = 10
} else {
hparams.num_last_distances_to_check = 16
}
}
if params.lgwin > 24 {
/* Different hashers for large window brotli: not for qualities <= 2,
these are too fast for large window. Not for qualities >= 10: their
hasher already works well with large window. So the changes are:
H3 --> H35: for quality 3.
H54 --> H55: for quality 4 with size hint > 1MB
H6 --> H65: for qualities 5, 6, 7, 8, 9. */
if hparams.type_ == 3 {
hparams.type_ = 35
}
if hparams.type_ == 54 {
hparams.type_ = 55
}
if hparams.type_ == 6 {
hparams.type_ = 65
}
}
}