Source File
deflatefast.go
Belonging Package
compress/flate
// Copyright 2016 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package flateimport// This encoding algorithm, which prioritizes speed over output size, is// based on Snappy's LZ77-style encoder: github.com/golang/snappyconst (tableBits = 14 // Bits used in the table.tableSize = 1 << tableBits // Size of the table.tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks.tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32.// Reset the buffer offset when reaching this.// Offsets are stored between blocks as int32 values.// Since the offset we are checking against is at the beginning// of the buffer, we need to subtract the current and input// buffer to not risk overflowing the int32.bufferReset = math.MaxInt32 - maxStoreBlockSize*2)func ( []byte, int32) uint32 {= [ : +4 : len()] // Help the compiler eliminate bounds checks on the next line.return uint32([0]) | uint32([1])<<8 | uint32([2])<<16 | uint32([3])<<24}func ( []byte, int32) uint64 {= [ : +8 : len()] // Help the compiler eliminate bounds checks on the next line.return uint64([0]) | uint64([1])<<8 | uint64([2])<<16 | uint64([3])<<24 |uint64([4])<<32 | uint64([5])<<40 | uint64([6])<<48 | uint64([7])<<56}func ( uint32) uint32 {return ( * 0x1e35a7bd) >> tableShift}// These constants are defined by the Snappy implementation so that its// assembly implementation can fast-path some 16-bytes-at-a-time copies. They// aren't necessary in the pure Go implementation, as we don't use those same// optimizations, but using the same thresholds doesn't really hurt.const (inputMargin = 16 - 1minNonLiteralBlockSize = 1 + 1 + inputMargin)type tableEntry struct {val uint32 // Value at destinationoffset int32}// deflateFast maintains the table for matches,// and the previous byte block for cross block matching.type deflateFast struct {table [tableSize]tableEntryprev []byte // Previous block, zero length if unknown.cur int32 // Current match offset.}func () *deflateFast {return &deflateFast{cur: maxStoreBlockSize, prev: make([]byte, 0, maxStoreBlockSize)}}// encode encodes a block given in src and appends tokens// to dst and returns the result.func ( *deflateFast) ( []token, []byte) []token {// Ensure that e.cur doesn't wrap.if .cur >= bufferReset {.shiftOffsets()}// This check isn't in the Snappy implementation, but there, the caller// instead of the callee handles this case.if len() < minNonLiteralBlockSize {.cur += maxStoreBlockSize.prev = .prev[:0]return emitLiteral(, )}// sLimit is when to stop looking for offset/length copies. The inputMargin// lets us use a fast path for emitLiteral in the main loop, while we are// looking for copies.:= int32(len() - inputMargin)// nextEmit is where in src the next emitLiteral should start from.:= int32(0):= int32(0):= load32(, ):= hash()for {// Copied from the C++ snappy implementation://// Heuristic match skipping: If 32 bytes are scanned with no matches// found, start looking only at every other byte. If 32 more bytes are// scanned (or skipped), look at every third byte, etc.. When a match// is found, immediately go back to looking at every byte. This is a// small loss (~5% performance, ~0.1% density) for compressible data// due to more bookkeeping, but for non-compressible data (such as// JPEG) it's a huge win since the compressor quickly "realizes" the// data is incompressible and doesn't bother looking for matches// everywhere.//// The "skip" variable keeps track of how many bytes there are since// the last match; dividing it by 32 (ie. right-shifting by five) gives// the number of bytes to move ahead for each iteration.:= int32(32):=var tableEntryfor {=:= >> 5= ++=if > {goto}= .table[&tableMask]:= load32(, ).table[&tableMask] = tableEntry{offset: + .cur, val: }= hash():= - (.offset - .cur)if > maxMatchOffset || != .val {// Out of range or not matched.=continue}break}// A 4-byte match has been found. We'll later see if more than 4 bytes// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit// them as literal bytes.= emitLiteral(, [:])// Call emitCopy, and then see if another emitCopy could be our next// move. Repeat until we find no match for the input immediately after// what was consumed by the last emitCopy call.//// If we exit this loop normally then we need to call emitLiteral next,// though we don't yet know how big the literal will be. We handle that// by proceeding to the next iteration of the main loop. We also can// exit this loop via goto if we get close to exhausting the input.for {// Invariant: we have a 4-byte match at s, and no need to emit any// literal bytes prior to s.// Extend the 4-byte match as long as possible.//+= 4:= .offset - .cur + 4:= .matchLen(, , )// matchToken is flate's equivalent of Snappy's emitCopy. (length,offset)= append(, matchToken(uint32(+4-baseMatchLength), uint32(--baseMatchOffset)))+==if >= {goto}// We could immediately start working at s now, but to improve// compression we first update the hash table at s-1 and at s. If// another emitCopy is not our next move, also calculate nextHash// at s+1. At least on GOARCH=amd64, these three hash calculations// are faster as one load64 call (with some shifts) instead of// three load32 calls.:= load64(, -1):= hash(uint32()).table[&tableMask] = tableEntry{offset: .cur + - 1, val: uint32()}>>= 8:= hash(uint32())= .table[&tableMask].table[&tableMask] = tableEntry{offset: .cur + , val: uint32()}:= - (.offset - .cur)if > maxMatchOffset || uint32() != .val {= uint32( >> 8)= hash()++break}}}:if int() < len() {= emitLiteral(, [:])}.cur += int32(len()).prev = .prev[:len()]copy(.prev, )return}func ( []token, []byte) []token {for , := range {= append(, literalToken(uint32()))}return}// matchLen returns the match length between src[s:] and src[t:].// t can be negative to indicate the match is starting in e.prev.// We assume that src[s-4:s] and src[t-4:t] already match.func ( *deflateFast) (, int32, []byte) int32 {:= int() + maxMatchLength - 4if > len() {= len()}// If we are inside the current blockif >= 0 {:= [:]:= [:]= [:len()]// Extend the match to be as long as possible.for := range {if [] != [] {return int32()}}return int32(len())}// We found a match in the previous block.:= int32(len(.prev)) +if < 0 {return 0}// Extend the match to be as long as possible.:= [:]:= .prev[:]if len() > len() {= [:len()]}= [:len()]for := range {if [] != [] {return int32()}}// If we reached our limit, we matched everything we are// allowed to in the previous block and we return.:= int32(len())if int(+) == {return}// Continue looking for more matches in the current block.= [+ : ]= [:len()]for := range {if [] != [] {return int32() +}}return int32(len()) +}// Reset resets the encoding history.// This ensures that no matches are made to the previous block.func ( *deflateFast) () {.prev = .prev[:0]// Bump the offset, so all matches will fail distance check.// Nothing should be >= e.cur in the table..cur += maxMatchOffset// Protect against e.cur wraparound.if .cur >= bufferReset {.shiftOffsets()}}// shiftOffsets will shift down all match offset.// This is only called in rare situations to prevent integer overflow.//// See https://golang.org/issue/18636 and https://github.com/golang/go/issues/34121.func ( *deflateFast) () {if len(.prev) == 0 {// We have no history; just clear the table.for := range .table[:] {.table[] = tableEntry{}}.cur = maxMatchOffset + 1return}// Shift down everything in the table that isn't already too far away.for := range .table[:] {:= .table[].offset - .cur + maxMatchOffset + 1if < 0 {// We want to reset e.cur to maxMatchOffset + 1, so we need to shift// all table entries down by (e.cur - (maxMatchOffset + 1)).// Because we ignore matches > maxMatchOffset, we can cap// any negative offsets at 0.= 0}.table[].offset =}.cur = maxMatchOffset + 1}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |