2022-07-03 10:08:30 +00:00
|
|
|
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
|
|
|
// Copyright (c) 2019 Klaus Post. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package s2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
2023-03-30 21:39:55 +00:00
|
|
|
"fmt"
|
2022-07-03 10:08:30 +00:00
|
|
|
"math/bits"
|
|
|
|
)
|
|
|
|
|
|
|
|
func load32(b []byte, i int) uint32 {
|
|
|
|
return binary.LittleEndian.Uint32(b[i:])
|
|
|
|
}
|
|
|
|
|
|
|
|
func load64(b []byte, i int) uint64 {
|
|
|
|
return binary.LittleEndian.Uint64(b[i:])
|
|
|
|
}
|
|
|
|
|
|
|
|
// hash6 returns the hash of the lowest 6 bytes of u to fit in a hash table with h bits.
|
|
|
|
// Preferably h should be a constant and should always be <64.
|
|
|
|
func hash6(u uint64, h uint8) uint32 {
|
|
|
|
const prime6bytes = 227718039650203
|
|
|
|
return uint32(((u << (64 - 48)) * prime6bytes) >> ((64 - h) & 63))
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodeGo(dst, src []byte) []byte {
|
|
|
|
if n := MaxEncodedLen(len(src)); n < 0 {
|
|
|
|
panic(ErrTooLarge)
|
|
|
|
} else if len(dst) < n {
|
|
|
|
dst = make([]byte, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The block starts with the varint-encoded length of the decompressed bytes.
|
|
|
|
d := binary.PutUvarint(dst, uint64(len(src)))
|
|
|
|
|
|
|
|
if len(src) == 0 {
|
|
|
|
return dst[:d]
|
|
|
|
}
|
|
|
|
if len(src) < minNonLiteralBlockSize {
|
|
|
|
d += emitLiteral(dst[d:], src)
|
|
|
|
return dst[:d]
|
|
|
|
}
|
|
|
|
n := encodeBlockGo(dst[d:], src)
|
|
|
|
if n > 0 {
|
|
|
|
d += n
|
|
|
|
return dst[:d]
|
|
|
|
}
|
|
|
|
// Not compressible
|
|
|
|
d += emitLiteral(dst[d:], src)
|
|
|
|
return dst[:d]
|
|
|
|
}
|
|
|
|
|
|
|
|
// encodeBlockGo encodes a non-empty src to a guaranteed-large-enough dst. It
|
|
|
|
// assumes that the varint-encoded length of the decompressed bytes has already
|
|
|
|
// been written.
|
|
|
|
//
|
|
|
|
// It also assumes that:
|
2023-02-27 09:21:58 +00:00
|
|
|
//
|
2022-07-03 10:08:30 +00:00
|
|
|
// len(dst) >= MaxEncodedLen(len(src)) &&
|
2023-02-27 09:21:58 +00:00
|
|
|
// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
|
2022-07-03 10:08:30 +00:00
|
|
|
func encodeBlockGo(dst, src []byte) (d int) {
|
|
|
|
// Initialize the hash table.
|
|
|
|
const (
|
|
|
|
tableBits = 14
|
|
|
|
maxTableSize = 1 << tableBits
|
|
|
|
|
|
|
|
debug = false
|
|
|
|
)
|
|
|
|
|
|
|
|
var table [maxTableSize]uint32
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
sLimit := len(src) - inputMargin
|
|
|
|
|
|
|
|
// Bail if we can't compress to at least this.
|
|
|
|
dstLimit := len(src) - len(src)>>5 - 5
|
|
|
|
|
|
|
|
// nextEmit is where in src the next emitLiteral should start from.
|
|
|
|
nextEmit := 0
|
|
|
|
|
|
|
|
// The encoded form must start with a literal, as there are no previous
|
|
|
|
// bytes to copy, so we start looking for hash matches at s == 1.
|
|
|
|
s := 1
|
|
|
|
cv := load64(src, s)
|
|
|
|
|
|
|
|
// We search for a repeat at -1, but don't output repeats when nextEmit == 0
|
|
|
|
repeat := 1
|
|
|
|
|
|
|
|
for {
|
|
|
|
candidate := 0
|
|
|
|
for {
|
|
|
|
// Next src position to check
|
|
|
|
nextS := s + (s-nextEmit)>>6 + 4
|
|
|
|
if nextS > sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
hash0 := hash6(cv, tableBits)
|
|
|
|
hash1 := hash6(cv>>8, tableBits)
|
|
|
|
candidate = int(table[hash0])
|
|
|
|
candidate2 := int(table[hash1])
|
|
|
|
table[hash0] = uint32(s)
|
|
|
|
table[hash1] = uint32(s + 1)
|
|
|
|
hash2 := hash6(cv>>16, tableBits)
|
|
|
|
|
|
|
|
// Check repeat at offset checkRep.
|
|
|
|
const checkRep = 1
|
|
|
|
if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) {
|
|
|
|
base := s + checkRep
|
|
|
|
// Extend back
|
|
|
|
for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; {
|
|
|
|
i--
|
|
|
|
base--
|
|
|
|
}
|
2024-03-25 11:00:36 +00:00
|
|
|
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(base-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2022-07-03 10:08:30 +00:00
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:base])
|
|
|
|
|
|
|
|
// Extend forward
|
|
|
|
candidate := s - repeat + 4 + checkRep
|
|
|
|
s += 4 + checkRep
|
|
|
|
for s <= sLimit {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nextEmit > 0 {
|
|
|
|
// same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset.
|
|
|
|
d += emitRepeat(dst[d:], repeat, s-base)
|
|
|
|
} else {
|
|
|
|
// First match, cannot be repeat.
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
}
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
cv = load64(src, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if uint32(cv) == load32(src, candidate) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
candidate = int(table[hash2])
|
|
|
|
if uint32(cv>>8) == load32(src, candidate2) {
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
candidate = candidate2
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
if uint32(cv>>16) == load32(src, candidate) {
|
|
|
|
s += 2
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
cv = load64(src, nextS)
|
|
|
|
s = nextS
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extend backwards.
|
|
|
|
// The top bytes will be rechecked to get the full match.
|
|
|
|
for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] {
|
|
|
|
candidate--
|
|
|
|
s--
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(s-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:s])
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
base := s
|
|
|
|
repeat = base - candidate
|
|
|
|
|
|
|
|
// Extend the 4-byte match as long as possible.
|
|
|
|
s += 4
|
|
|
|
candidate += 4
|
|
|
|
for s <= len(src)-8 {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
if debug {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
|
|
|
|
if d > dstLimit {
|
|
|
|
// Do we have space for more, if not bail.
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
// Check for an immediate match, otherwise start search at s+1
|
|
|
|
x := load64(src, s-2)
|
|
|
|
m2Hash := hash6(x, tableBits)
|
|
|
|
currHash := hash6(x>>16, tableBits)
|
|
|
|
candidate = int(table[currHash])
|
|
|
|
table[m2Hash] = uint32(s - 2)
|
|
|
|
table[currHash] = uint32(s)
|
|
|
|
if debug && s == candidate {
|
|
|
|
panic("s == candidate")
|
|
|
|
}
|
|
|
|
if uint32(x>>16) != load32(src, candidate) {
|
|
|
|
cv = load64(src, s+1)
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emitRemainder:
|
|
|
|
if nextEmit < len(src) {
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+len(src)-nextEmit > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:])
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
func encodeBlockSnappyGo(dst, src []byte) (d int) {
|
|
|
|
// Initialize the hash table.
|
|
|
|
const (
|
|
|
|
tableBits = 14
|
|
|
|
maxTableSize = 1 << tableBits
|
|
|
|
)
|
|
|
|
|
|
|
|
var table [maxTableSize]uint32
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
sLimit := len(src) - inputMargin
|
|
|
|
|
|
|
|
// Bail if we can't compress to at least this.
|
|
|
|
dstLimit := len(src) - len(src)>>5 - 5
|
|
|
|
|
|
|
|
// nextEmit is where in src the next emitLiteral should start from.
|
|
|
|
nextEmit := 0
|
|
|
|
|
|
|
|
// The encoded form must start with a literal, as there are no previous
|
|
|
|
// bytes to copy, so we start looking for hash matches at s == 1.
|
|
|
|
s := 1
|
|
|
|
cv := load64(src, s)
|
|
|
|
|
|
|
|
// We search for a repeat at -1, but don't output repeats when nextEmit == 0
|
|
|
|
repeat := 1
|
|
|
|
|
|
|
|
for {
|
|
|
|
candidate := 0
|
|
|
|
for {
|
|
|
|
// Next src position to check
|
|
|
|
nextS := s + (s-nextEmit)>>6 + 4
|
|
|
|
if nextS > sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
hash0 := hash6(cv, tableBits)
|
|
|
|
hash1 := hash6(cv>>8, tableBits)
|
|
|
|
candidate = int(table[hash0])
|
|
|
|
candidate2 := int(table[hash1])
|
|
|
|
table[hash0] = uint32(s)
|
|
|
|
table[hash1] = uint32(s + 1)
|
|
|
|
hash2 := hash6(cv>>16, tableBits)
|
|
|
|
|
|
|
|
// Check repeat at offset checkRep.
|
|
|
|
const checkRep = 1
|
|
|
|
if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) {
|
|
|
|
base := s + checkRep
|
|
|
|
// Extend back
|
|
|
|
for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; {
|
|
|
|
i--
|
|
|
|
base--
|
|
|
|
}
|
2024-03-25 11:00:36 +00:00
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(base-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2022-07-03 10:08:30 +00:00
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:base])
|
|
|
|
|
|
|
|
// Extend forward
|
|
|
|
candidate := s - repeat + 4 + checkRep
|
|
|
|
s += 4 + checkRep
|
|
|
|
for s <= sLimit {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
|
|
|
|
d += emitCopyNoRepeat(dst[d:], repeat, s-base)
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
|
|
|
|
cv = load64(src, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if uint32(cv) == load32(src, candidate) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
candidate = int(table[hash2])
|
|
|
|
if uint32(cv>>8) == load32(src, candidate2) {
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
candidate = candidate2
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
if uint32(cv>>16) == load32(src, candidate) {
|
|
|
|
s += 2
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
cv = load64(src, nextS)
|
|
|
|
s = nextS
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extend backwards
|
|
|
|
for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] {
|
|
|
|
candidate--
|
|
|
|
s--
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(s-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:s])
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
base := s
|
|
|
|
repeat = base - candidate
|
|
|
|
|
|
|
|
// Extend the 4-byte match as long as possible.
|
|
|
|
s += 4
|
|
|
|
candidate += 4
|
|
|
|
for s <= len(src)-8 {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
|
|
|
|
d += emitCopyNoRepeat(dst[d:], repeat, s-base)
|
|
|
|
if false {
|
|
|
|
// Validate match.
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
|
|
|
|
if d > dstLimit {
|
|
|
|
// Do we have space for more, if not bail.
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
// Check for an immediate match, otherwise start search at s+1
|
|
|
|
x := load64(src, s-2)
|
|
|
|
m2Hash := hash6(x, tableBits)
|
|
|
|
currHash := hash6(x>>16, tableBits)
|
|
|
|
candidate = int(table[currHash])
|
|
|
|
table[m2Hash] = uint32(s - 2)
|
|
|
|
table[currHash] = uint32(s)
|
|
|
|
if uint32(x>>16) != load32(src, candidate) {
|
|
|
|
cv = load64(src, s+1)
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emitRemainder:
|
|
|
|
if nextEmit < len(src) {
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+len(src)-nextEmit > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:])
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|
2023-03-30 21:39:55 +00:00
|
|
|
|
|
|
|
// encodeBlockGo encodes a non-empty src to a guaranteed-large-enough dst. It
|
|
|
|
// assumes that the varint-encoded length of the decompressed bytes has already
|
|
|
|
// been written.
|
|
|
|
//
|
|
|
|
// It also assumes that:
|
|
|
|
//
|
|
|
|
// len(dst) >= MaxEncodedLen(len(src)) &&
|
|
|
|
// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
|
|
|
|
func encodeBlockDictGo(dst, src []byte, dict *Dict) (d int) {
|
|
|
|
// Initialize the hash table.
|
|
|
|
const (
|
|
|
|
tableBits = 14
|
|
|
|
maxTableSize = 1 << tableBits
|
|
|
|
maxAhead = 8 // maximum bytes ahead without checking sLimit
|
|
|
|
|
|
|
|
debug = false
|
|
|
|
)
|
|
|
|
dict.initFast()
|
|
|
|
|
|
|
|
var table [maxTableSize]uint32
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
sLimit := len(src) - inputMargin
|
|
|
|
if sLimit > MaxDictSrcOffset-maxAhead {
|
|
|
|
sLimit = MaxDictSrcOffset - maxAhead
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bail if we can't compress to at least this.
|
|
|
|
dstLimit := len(src) - len(src)>>5 - 5
|
|
|
|
|
|
|
|
// nextEmit is where in src the next emitLiteral should start from.
|
|
|
|
nextEmit := 0
|
|
|
|
|
|
|
|
// The encoded form can start with a dict entry (copy or repeat).
|
|
|
|
s := 0
|
|
|
|
|
|
|
|
// Convert dict repeat to offset
|
|
|
|
repeat := len(dict.dict) - dict.repeat
|
|
|
|
cv := load64(src, 0)
|
|
|
|
|
|
|
|
// While in dict
|
|
|
|
searchDict:
|
|
|
|
for {
|
|
|
|
// Next src position to check
|
|
|
|
nextS := s + (s-nextEmit)>>6 + 4
|
|
|
|
hash0 := hash6(cv, tableBits)
|
|
|
|
hash1 := hash6(cv>>8, tableBits)
|
|
|
|
if nextS > sLimit {
|
|
|
|
if debug {
|
|
|
|
fmt.Println("slimit reached", s, nextS)
|
|
|
|
}
|
|
|
|
break searchDict
|
|
|
|
}
|
|
|
|
candidateDict := int(dict.fastTable[hash0])
|
|
|
|
candidateDict2 := int(dict.fastTable[hash1])
|
|
|
|
candidate2 := int(table[hash1])
|
|
|
|
candidate := int(table[hash0])
|
|
|
|
table[hash0] = uint32(s)
|
|
|
|
table[hash1] = uint32(s + 1)
|
|
|
|
hash2 := hash6(cv>>16, tableBits)
|
|
|
|
|
|
|
|
// Check repeat at offset checkRep.
|
|
|
|
const checkRep = 1
|
|
|
|
|
|
|
|
if repeat > s {
|
|
|
|
candidate := len(dict.dict) - repeat + s
|
|
|
|
if repeat-s >= 4 && uint32(cv) == load32(dict.dict, candidate) {
|
|
|
|
// Extend back
|
|
|
|
base := s
|
|
|
|
for i := candidate; base > nextEmit && i > 0 && dict.dict[i-1] == src[base-1]; {
|
|
|
|
i--
|
|
|
|
base--
|
|
|
|
}
|
2024-03-25 11:00:36 +00:00
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(base-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2023-03-30 21:39:55 +00:00
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:base])
|
|
|
|
if debug && nextEmit != base {
|
|
|
|
fmt.Println("emitted ", base-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
s += 4
|
|
|
|
candidate += 4
|
|
|
|
for candidate < len(dict.dict)-8 && s <= len(src)-8 {
|
|
|
|
if diff := load64(src, s) ^ load64(dict.dict, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
d += emitRepeat(dst[d:], repeat, s-base)
|
|
|
|
if debug {
|
|
|
|
fmt.Println("emitted dict repeat length", s-base, "offset:", repeat, "s:", s)
|
|
|
|
}
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
break searchDict
|
|
|
|
}
|
|
|
|
cv = load64(src, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) {
|
|
|
|
base := s + checkRep
|
|
|
|
// Extend back
|
|
|
|
for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; {
|
|
|
|
i--
|
|
|
|
base--
|
|
|
|
}
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:base])
|
|
|
|
if debug && nextEmit != base {
|
|
|
|
fmt.Println("emitted ", base-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extend forward
|
|
|
|
candidate := s - repeat + 4 + checkRep
|
|
|
|
s += 4 + checkRep
|
|
|
|
for s <= sLimit {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if nextEmit > 0 {
|
|
|
|
// same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset.
|
|
|
|
d += emitRepeat(dst[d:], repeat, s-base)
|
|
|
|
} else {
|
|
|
|
// First match, cannot be repeat.
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
}
|
|
|
|
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
break searchDict
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
fmt.Println("emitted reg repeat", s-base, "s:", s)
|
|
|
|
}
|
|
|
|
cv = load64(src, s)
|
|
|
|
continue searchDict
|
|
|
|
}
|
|
|
|
if s == 0 {
|
|
|
|
cv = load64(src, nextS)
|
|
|
|
s = nextS
|
|
|
|
continue searchDict
|
|
|
|
}
|
|
|
|
// Start with table. These matches will always be closer.
|
|
|
|
if uint32(cv) == load32(src, candidate) {
|
|
|
|
goto emitMatch
|
|
|
|
}
|
|
|
|
candidate = int(table[hash2])
|
|
|
|
if uint32(cv>>8) == load32(src, candidate2) {
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
candidate = candidate2
|
|
|
|
s++
|
|
|
|
goto emitMatch
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check dict. Dicts have longer offsets, so we want longer matches.
|
|
|
|
if cv == load64(dict.dict, candidateDict) {
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
goto emitDict
|
|
|
|
}
|
|
|
|
|
|
|
|
candidateDict = int(dict.fastTable[hash2])
|
|
|
|
// Check if upper 7 bytes match
|
|
|
|
if candidateDict2 >= 1 {
|
|
|
|
if cv^load64(dict.dict, candidateDict2-1) < (1 << 8) {
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
candidateDict = candidateDict2
|
|
|
|
s++
|
|
|
|
goto emitDict
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
if uint32(cv>>16) == load32(src, candidate) {
|
|
|
|
s += 2
|
|
|
|
goto emitMatch
|
|
|
|
}
|
|
|
|
if candidateDict >= 2 {
|
|
|
|
// Check if upper 6 bytes match
|
|
|
|
if cv^load64(dict.dict, candidateDict-2) < (1 << 16) {
|
|
|
|
s += 2
|
|
|
|
goto emitDict
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cv = load64(src, nextS)
|
|
|
|
s = nextS
|
|
|
|
continue searchDict
|
|
|
|
|
|
|
|
emitDict:
|
|
|
|
{
|
|
|
|
if debug {
|
|
|
|
if load32(dict.dict, candidateDict) != load32(src, s) {
|
|
|
|
panic("dict emit mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Extend backwards.
|
|
|
|
// The top bytes will be rechecked to get the full match.
|
|
|
|
for candidateDict > 0 && s > nextEmit && dict.dict[candidateDict-1] == src[s-1] {
|
|
|
|
candidateDict--
|
|
|
|
s--
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(s-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:s])
|
|
|
|
if debug && nextEmit != s {
|
|
|
|
fmt.Println("emitted ", s-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Invariant: we have a 4-byte match at s, and no need to emit any
|
|
|
|
// literal bytes prior to s.
|
|
|
|
base := s
|
|
|
|
repeat = s + (len(dict.dict)) - candidateDict
|
|
|
|
|
|
|
|
// Extend the 4-byte match as long as possible.
|
|
|
|
s += 4
|
|
|
|
candidateDict += 4
|
|
|
|
for s <= len(src)-8 && len(dict.dict)-candidateDict >= 8 {
|
|
|
|
if diff := load64(src, s) ^ load64(dict.dict, candidateDict); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidateDict += 8
|
|
|
|
}
|
|
|
|
|
|
|
|
// Matches longer than 64 are split.
|
|
|
|
if s <= sLimit || s-base < 8 {
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
} else {
|
|
|
|
// Split to ensure we don't start a copy within next block
|
|
|
|
d += emitCopy(dst[d:], repeat, 4)
|
|
|
|
d += emitRepeat(dst[d:], repeat, s-base-4)
|
|
|
|
}
|
|
|
|
if false {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := dict.dict[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
fmt.Println("emitted dict copy, length", s-base, "offset:", repeat, "s:", s)
|
|
|
|
}
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
break searchDict
|
|
|
|
}
|
|
|
|
|
|
|
|
if d > dstLimit {
|
|
|
|
// Do we have space for more, if not bail.
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Index and continue loop to try new candidate.
|
|
|
|
x := load64(src, s-2)
|
|
|
|
m2Hash := hash6(x, tableBits)
|
|
|
|
currHash := hash6(x>>8, tableBits)
|
|
|
|
table[m2Hash] = uint32(s - 2)
|
|
|
|
table[currHash] = uint32(s - 1)
|
|
|
|
cv = load64(src, s)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
emitMatch:
|
|
|
|
|
|
|
|
// Extend backwards.
|
|
|
|
// The top bytes will be rechecked to get the full match.
|
|
|
|
for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] {
|
|
|
|
candidate--
|
|
|
|
s--
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(s-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:s])
|
|
|
|
if debug && nextEmit != s {
|
|
|
|
fmt.Println("emitted ", s-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
// 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.
|
|
|
|
base := s
|
|
|
|
repeat = base - candidate
|
|
|
|
|
|
|
|
// Extend the 4-byte match as long as possible.
|
|
|
|
s += 4
|
|
|
|
candidate += 4
|
|
|
|
for s <= len(src)-8 {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
if debug {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
fmt.Println("emitted src copy, length", s-base, "offset:", repeat, "s:", s)
|
|
|
|
}
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
break searchDict
|
|
|
|
}
|
|
|
|
|
|
|
|
if d > dstLimit {
|
|
|
|
// Do we have space for more, if not bail.
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
// Check for an immediate match, otherwise start search at s+1
|
|
|
|
x := load64(src, s-2)
|
|
|
|
m2Hash := hash6(x, tableBits)
|
|
|
|
currHash := hash6(x>>16, tableBits)
|
|
|
|
candidate = int(table[currHash])
|
|
|
|
table[m2Hash] = uint32(s - 2)
|
|
|
|
table[currHash] = uint32(s)
|
|
|
|
if debug && s == candidate {
|
|
|
|
panic("s == candidate")
|
|
|
|
}
|
|
|
|
if uint32(x>>16) != load32(src, candidate) {
|
|
|
|
cv = load64(src, s+1)
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search without dict:
|
|
|
|
if repeat > s {
|
|
|
|
repeat = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// No more dict
|
|
|
|
sLimit = len(src) - inputMargin
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
fmt.Println("non-dict matching at", s, "repeat:", repeat)
|
|
|
|
}
|
|
|
|
cv = load64(src, s)
|
|
|
|
if debug {
|
|
|
|
fmt.Println("now", s, "->", sLimit, "out:", d, "left:", len(src)-s, "nextemit:", nextEmit, "dstLimit:", dstLimit, "s:", s)
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
candidate := 0
|
|
|
|
for {
|
|
|
|
// Next src position to check
|
|
|
|
nextS := s + (s-nextEmit)>>6 + 4
|
|
|
|
if nextS > sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
hash0 := hash6(cv, tableBits)
|
|
|
|
hash1 := hash6(cv>>8, tableBits)
|
|
|
|
candidate = int(table[hash0])
|
|
|
|
candidate2 := int(table[hash1])
|
|
|
|
table[hash0] = uint32(s)
|
|
|
|
table[hash1] = uint32(s + 1)
|
|
|
|
hash2 := hash6(cv>>16, tableBits)
|
|
|
|
|
|
|
|
// Check repeat at offset checkRep.
|
|
|
|
const checkRep = 1
|
|
|
|
if repeat > 0 && uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) {
|
|
|
|
base := s + checkRep
|
|
|
|
// Extend back
|
|
|
|
for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; {
|
|
|
|
i--
|
|
|
|
base--
|
|
|
|
}
|
2024-03-25 11:00:36 +00:00
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(base-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2023-03-30 21:39:55 +00:00
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:base])
|
|
|
|
if debug && nextEmit != base {
|
|
|
|
fmt.Println("emitted ", base-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
// Extend forward
|
|
|
|
candidate := s - repeat + 4 + checkRep
|
|
|
|
s += 4 + checkRep
|
|
|
|
for s <= sLimit {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if nextEmit > 0 {
|
|
|
|
// same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset.
|
|
|
|
d += emitRepeat(dst[d:], repeat, s-base)
|
|
|
|
} else {
|
|
|
|
// First match, cannot be repeat.
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
fmt.Println("emitted src repeat length", s-base, "offset:", repeat, "s:", s)
|
|
|
|
}
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
|
|
|
|
cv = load64(src, s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if uint32(cv) == load32(src, candidate) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
candidate = int(table[hash2])
|
|
|
|
if uint32(cv>>8) == load32(src, candidate2) {
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
candidate = candidate2
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
table[hash2] = uint32(s + 2)
|
|
|
|
if uint32(cv>>16) == load32(src, candidate) {
|
|
|
|
s += 2
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
cv = load64(src, nextS)
|
|
|
|
s = nextS
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extend backwards.
|
|
|
|
// The top bytes will be rechecked to get the full match.
|
|
|
|
for candidate > 0 && s > nextEmit && src[candidate-1] == src[s-1] {
|
|
|
|
candidate--
|
|
|
|
s--
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+(s-nextEmit) > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:s])
|
|
|
|
if debug && nextEmit != s {
|
|
|
|
fmt.Println("emitted ", s-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
// 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.
|
|
|
|
base := s
|
|
|
|
repeat = base - candidate
|
|
|
|
|
|
|
|
// Extend the 4-byte match as long as possible.
|
|
|
|
s += 4
|
|
|
|
candidate += 4
|
|
|
|
for s <= len(src)-8 {
|
|
|
|
if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
|
|
|
|
s += bits.TrailingZeros64(diff) >> 3
|
|
|
|
break
|
|
|
|
}
|
|
|
|
s += 8
|
|
|
|
candidate += 8
|
|
|
|
}
|
|
|
|
|
|
|
|
d += emitCopy(dst[d:], repeat, s-base)
|
|
|
|
if debug {
|
|
|
|
// Validate match.
|
|
|
|
if s <= candidate {
|
|
|
|
panic("s <= candidate")
|
|
|
|
}
|
|
|
|
a := src[base:s]
|
|
|
|
b := src[base-repeat : base-repeat+(s-base)]
|
|
|
|
if !bytes.Equal(a, b) {
|
|
|
|
panic("mismatch")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if debug {
|
|
|
|
fmt.Println("emitted src copy, length", s-base, "offset:", repeat, "s:", s)
|
|
|
|
}
|
|
|
|
nextEmit = s
|
|
|
|
if s >= sLimit {
|
|
|
|
goto emitRemainder
|
|
|
|
}
|
|
|
|
|
|
|
|
if d > dstLimit {
|
|
|
|
// Do we have space for more, if not bail.
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
// Check for an immediate match, otherwise start search at s+1
|
|
|
|
x := load64(src, s-2)
|
|
|
|
m2Hash := hash6(x, tableBits)
|
|
|
|
currHash := hash6(x>>16, tableBits)
|
|
|
|
candidate = int(table[currHash])
|
|
|
|
table[m2Hash] = uint32(s - 2)
|
|
|
|
table[currHash] = uint32(s)
|
|
|
|
if debug && s == candidate {
|
|
|
|
panic("s == candidate")
|
|
|
|
}
|
|
|
|
if uint32(x>>16) != load32(src, candidate) {
|
|
|
|
cv = load64(src, s+1)
|
|
|
|
s++
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emitRemainder:
|
|
|
|
if nextEmit < len(src) {
|
|
|
|
// Bail if we exceed the maximum size.
|
|
|
|
if d+len(src)-nextEmit > dstLimit {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
d += emitLiteral(dst[d:], src[nextEmit:])
|
|
|
|
if debug && nextEmit != s {
|
|
|
|
fmt.Println("emitted ", len(src)-nextEmit, "literals")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return d
|
|
|
|
}
|