123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // 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 (
- "math/bits"
- )
- // hash4 returns the hash of the lowest 4 bytes of u to fit in a hash table with h bits.
- // Preferably h should be a constant and should always be <32.
- func hash4(u uint64, h uint8) uint32 {
- const prime4bytes = 2654435761
- return (uint32(u) * prime4bytes) >> ((32 - h) & 31)
- }
- // hash5 returns the hash of the lowest 5 bytes of u to fit in a hash table with h bits.
- // Preferably h should be a constant and should always be <64.
- func hash5(u uint64, h uint8) uint32 {
- const prime5bytes = 889523592379
- return uint32(((u << (64 - 40)) * prime5bytes) >> ((64 - h) & 63))
- }
- // hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits.
- // Preferably h should be a constant and should always be <64.
- func hash7(u uint64, h uint8) uint32 {
- const prime7bytes = 58295818150454627
- return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & 63))
- }
- // hash8 returns the hash of u to fit in a hash table with h bits.
- // Preferably h should be a constant and should always be <64.
- func hash8(u uint64, h uint8) uint32 {
- const prime8bytes = 0xcf1bbcdcb7a56463
- return uint32((u * prime8bytes) >> ((64 - h) & 63))
- }
- // encodeBlockBetter 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 encodeBlockBetter(dst, src []byte) (d int) {
- // Initialize the hash tables.
- const (
- // Long hash matches.
- lTableBits = 16
- maxLTableSize = 1 << lTableBits
- // Short hash matches.
- sTableBits = 14
- maxSTableSize = 1 << sTableBits
- )
- var lTable [maxLTableSize]uint32
- var sTable [maxSTableSize]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 {
- candidateL := 0
- for {
- // Next src position to check
- nextS := s + (s-nextEmit)>>7 + 1
- if nextS > sLimit {
- goto emitRemainder
- }
- hashL := hash7(cv, lTableBits)
- hashS := hash4(cv, sTableBits)
- candidateL = int(lTable[hashL])
- candidateS := int(sTable[hashS])
- lTable[hashL] = uint32(s)
- sTable[hashS] = uint32(s)
- // 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--
- }
- 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 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, candidateL) {
- break
- }
- // Check our short candidate
- if uint32(cv) == load32(src, candidateS) {
- // Try a long candidate at s+1
- hashL = hash7(cv>>8, lTableBits)
- candidateL = int(lTable[hashL])
- lTable[hashL] = uint32(s + 1)
- if uint32(cv>>8) == load32(src, candidateL) {
- s++
- break
- }
- // Use our short candidate.
- candidateL = candidateS
- break
- }
- cv = load64(src, nextS)
- s = nextS
- }
- // Extend backwards
- for candidateL > 0 && s > nextEmit && src[candidateL-1] == src[s-1] {
- candidateL--
- s--
- }
- // Bail if we exceed the maximum size.
- if d+(s-nextEmit) > dstLimit {
- return 0
- }
- base := s
- offset := base - candidateL
- // Extend the 4-byte match as long as possible.
- s += 4
- candidateL += 4
- for s <= len(src)-8 {
- if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidateL += 8
- }
- if offset > 65535 && s-base <= 5 {
- // Bail if the match is equal or worse to the encoding.
- s = base + 3
- cv = load64(src, s)
- continue
- }
- repeat = offset
- d += emitLiteral(dst[d:], src[nextEmit:base])
- d += emitCopy(dst[d:], offset, s-base)
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index match start+1 (long) and start+2 (short)
- index0 := base + 1
- // Index match end-2 (long) and end-1 (short)
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- cv = load64(src, s)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- }
- 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
- }
|