Merge pull request #7582 from rhatdan/VENDOR

vendor containers/storage v1.23.5
This commit is contained in:
OpenShift Merge Robot
2020-09-10 14:04:16 -04:00
committed by GitHub
31 changed files with 692 additions and 501 deletions

2
go.mod
View File

@ -15,7 +15,7 @@ require (
github.com/containers/conmon v2.0.20+incompatible github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.5.2 github.com/containers/image/v5 v5.5.2
github.com/containers/psgo v1.5.1 github.com/containers/psgo v1.5.1
github.com/containers/storage v1.23.4 github.com/containers/storage v1.23.5
github.com/coreos/go-systemd/v22 v22.1.0 github.com/coreos/go-systemd/v22 v22.1.0
github.com/cri-o/ocicni v0.2.0 github.com/cri-o/ocicni v0.2.0
github.com/cyphar/filepath-securejoin v0.2.2 github.com/cyphar/filepath-securejoin v0.2.2

6
go.sum
View File

@ -85,8 +85,8 @@ github.com/containers/psgo v1.5.1 h1:MQNb7FLbXqBdqz6u4lI2QWizVz4RSTzs1+Nk9XT1iVA
github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU=
github.com/containers/storage v1.23.0/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw= github.com/containers/storage v1.23.0/go.mod h1:I1EIAA7B4OwWRSA0b4yq2AW1wjvvfcY0zLWQuwTa4zw=
github.com/containers/storage v1.23.3/go.mod h1:0azTMiuBhArp/VUmH1o4DJAGaaH+qLtEu17pJ/iKJCg= github.com/containers/storage v1.23.3/go.mod h1:0azTMiuBhArp/VUmH1o4DJAGaaH+qLtEu17pJ/iKJCg=
github.com/containers/storage v1.23.4 h1:1raHKGNs2C52tEq2ydHqZ+wu2u1d79BHMO6O5JO20xQ= github.com/containers/storage v1.23.5 h1:He9I6y1vRVXYoQg4v2Q9HFAcX4dI3V5MCCrjeBcjkCY=
github.com/containers/storage v1.23.4/go.mod h1:KzpVgmUucelPYHq2YsseUTiTuucdVh3xfpPNmxmPZRU= github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
@ -258,6 +258,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.11 h1:K9z59aO18Aywg2b/WSgBaUX99mHy2BES18Cr5lBKZHk= github.com/klauspost/compress v1.10.11 h1:K9z59aO18Aywg2b/WSgBaUX99mHy2BES18Cr5lBKZHk=
github.com/klauspost/compress v1.10.11/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.11/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=

View File

@ -1 +1 @@
1.23.4 1.23.5

View File

@ -8,7 +8,7 @@ require (
github.com/Microsoft/hcsshim v0.8.9 github.com/Microsoft/hcsshim v0.8.9
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-multierror v1.1.0
github.com/klauspost/compress v1.10.11 github.com/klauspost/compress v1.11.0
github.com/klauspost/pgzip v1.2.5 github.com/klauspost/pgzip v1.2.5
github.com/mattn/go-shellwords v1.0.10 github.com/mattn/go-shellwords v1.0.10
github.com/mistifyio/go-zfs v2.1.1+incompatible github.com/mistifyio/go-zfs v2.1.1+incompatible

View File

@ -62,8 +62,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.11 h1:K9z59aO18Aywg2b/WSgBaUX99mHy2BES18Cr5lBKZHk= github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg=
github.com/klauspost/compress v1.10.11/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=

View File

@ -198,7 +198,7 @@ func getRootlessDirInfo(rootlessUID int) (string, string, error) {
} }
// getRootlessStorageOpts returns the storage opts for containers running as non root // getRootlessStorageOpts returns the storage opts for containers running as non root
func getRootlessStorageOpts(rootlessUID int) (StoreOptions, error) { func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOptions, error) {
var opts StoreOptions var opts StoreOptions
dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUID) dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUID)
@ -207,10 +207,20 @@ func getRootlessStorageOpts(rootlessUID int) (StoreOptions, error) {
} }
opts.RunRoot = rootlessRuntime opts.RunRoot = rootlessRuntime
opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
opts.RootlessStoragePath = opts.GraphRoot if systemOpts.RootlessStoragePath != "" {
opts.RootlessStoragePath = systemOpts.RootlessStoragePath
} else {
opts.RootlessStoragePath = opts.GraphRoot
}
if path, err := exec.LookPath("fuse-overlayfs"); err == nil { if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
opts.GraphDriverName = "overlay" opts.GraphDriverName = "overlay"
opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)} opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
for _, o := range systemOpts.GraphDriverOptions {
if strings.Contains(o, "ignore_chown_errors") {
opts.GraphDriverOptions = append(opts.GraphDriverOptions, o)
break
}
}
} else { } else {
opts.GraphDriverName = "vfs" opts.GraphDriverName = "vfs"
} }
@ -242,7 +252,7 @@ func defaultStoreOptionsIsolated(rootless bool, rootlessUID int, storageConf str
) )
storageOpts := defaultStoreOptions storageOpts := defaultStoreOptions
if rootless && rootlessUID != 0 { if rootless && rootlessUID != 0 {
storageOpts, err = getRootlessStorageOpts(rootlessUID) storageOpts, err = getRootlessStorageOpts(rootlessUID, storageOpts)
if err != nil { if err != nil {
return storageOpts, err return storageOpts, err
} }

View File

@ -127,7 +127,7 @@ func (e *fastGen) addBlock(src []byte) int32 {
// hash4 returns the hash of u to fit in a hash table with h bits. // hash4 returns the hash of u to fit in a hash table with h bits.
// Preferably h should be a constant and should always be <32. // Preferably h should be a constant and should always be <32.
func hash4u(u uint32, h uint8) uint32 { func hash4u(u uint32, h uint8) uint32 {
return (u * prime4bytes) >> ((32 - h) & 31) return (u * prime4bytes) >> ((32 - h) & reg8SizeMask32)
} }
type tableEntryPrev struct { type tableEntryPrev struct {
@ -138,25 +138,25 @@ type tableEntryPrev struct {
// hash4x64 returns the hash of the lowest 4 bytes of u to fit in a hash table with h bits. // hash4x64 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. // Preferably h should be a constant and should always be <32.
func hash4x64(u uint64, h uint8) uint32 { func hash4x64(u uint64, h uint8) uint32 {
return (uint32(u) * prime4bytes) >> ((32 - h) & 31) return (uint32(u) * prime4bytes) >> ((32 - h) & reg8SizeMask32)
} }
// hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits. // 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. // Preferably h should be a constant and should always be <64.
func hash7(u uint64, h uint8) uint32 { func hash7(u uint64, h uint8) uint32 {
return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & 63)) return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & reg8SizeMask64))
} }
// hash8 returns the hash of u to fit in a hash table with h bits. // 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. // Preferably h should be a constant and should always be <64.
func hash8(u uint64, h uint8) uint32 { func hash8(u uint64, h uint8) uint32 {
return uint32((u * prime8bytes) >> ((64 - h) & 63)) return uint32((u * prime8bytes) >> ((64 - h) & reg8SizeMask64))
} }
// hash6 returns the hash of the lowest 6 bytes of u to fit in a hash table with h bits. // 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. // Preferably h should be a constant and should always be <64.
func hash6(u uint64, h uint8) uint32 { func hash6(u uint64, h uint8) uint32 {
return uint32(((u << (64 - 48)) * prime6bytes) >> ((64 - h) & 63)) return uint32(((u << (64 - 48)) * prime6bytes) >> ((64 - h) & reg8SizeMask64))
} }
// matchlen will return the match length between offsets and t in src. // matchlen will return the match length between offsets and t in src.

View File

@ -85,7 +85,7 @@ readLiteral:
return return
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := f.hl.chunks[b&(huffmanNumChunks-1)] chunk := f.hl.chunks[b&(huffmanNumChunks-1)]
@ -104,7 +104,7 @@ readLiteral:
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return return
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
v = int(chunk >> huffmanValueShift) v = int(chunk >> huffmanValueShift)
break break
@ -167,15 +167,15 @@ readLiteral:
return return
} }
} }
length += int(f.b & uint32(1<<n-1)) length += int(f.b & uint32(1<<(n&regSizeMaskUint32)-1))
f.b >>= n f.b >>= n & regSizeMaskUint32
f.nb -= n f.nb -= n
} }
var dist int var dist uint32
if f.hd == nil { if f.hd == nil {
for f.nb < 5 { for f.nb < 5 {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<5:", err) fmt.Println("morebits f.nb<5:", err)
} }
@ -183,17 +183,19 @@ readLiteral:
return return
} }
} }
dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3)))
f.b >>= 5 f.b >>= 5
f.nb -= 5 f.nb -= 5
} else { } else {
if dist, err = f.huffSym(f.hd); err != nil { sym, err := f.huffSym(f.hd)
if err != nil {
if debugDecode { if debugDecode {
fmt.Println("huffsym:", err) fmt.Println("huffsym:", err)
} }
f.err = err f.err = err
return return
} }
dist = uint32(sym)
} }
switch { switch {
@ -202,9 +204,9 @@ readLiteral:
case dist < maxNumDist: case dist < maxNumDist:
nb := uint(dist-2) >> 1 nb := uint(dist-2) >> 1
// have 1 bit in bottom of dist, need nb more. // have 1 bit in bottom of dist, need nb more.
extra := (dist & 1) << nb extra := (dist & 1) << (nb & regSizeMaskUint32)
for f.nb < nb { for f.nb < nb {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<nb:", err) fmt.Println("morebits f.nb<nb:", err)
} }
@ -212,10 +214,10 @@ readLiteral:
return return
} }
} }
extra |= int(f.b & uint32(1<<nb-1)) extra |= f.b & uint32(1<<(nb&regSizeMaskUint32)-1)
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
dist = 1<<(nb+1) + 1 + extra dist = 1<<((nb+1)&regSizeMaskUint32) + 1 + extra
default: default:
if debugDecode { if debugDecode {
fmt.Println("dist too big:", dist, maxNumDist) fmt.Println("dist too big:", dist, maxNumDist)
@ -225,7 +227,7 @@ readLiteral:
} }
// No check on length; encoding can be prescient. // No check on length; encoding can be prescient.
if dist > f.dict.histSize() { if dist > uint32(f.dict.histSize()) {
if debugDecode { if debugDecode {
fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize()) fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize())
} }
@ -233,7 +235,7 @@ readLiteral:
return return
} }
f.copyLen, f.copyDist = length, dist f.copyLen, f.copyDist = length, int(dist)
goto copyHistory goto copyHistory
} }

View File

@ -206,7 +206,7 @@ func (w *huffmanBitWriter) write(b []byte) {
} }
func (w *huffmanBitWriter) writeBits(b int32, nb uint16) { func (w *huffmanBitWriter) writeBits(b int32, nb uint16) {
w.bits |= uint64(b) << (w.nbits & 63) w.bits |= uint64(b) << (w.nbits & reg16SizeMask64)
w.nbits += nb w.nbits += nb
if w.nbits >= 48 { if w.nbits >= 48 {
w.writeOutBits() w.writeOutBits()
@ -759,7 +759,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
} else { } else {
// inlined // inlined
c := lengths[lengthCode&31] c := lengths[lengthCode&31]
w.bits |= uint64(c.code) << (w.nbits & 63) w.bits |= uint64(c.code) << (w.nbits & reg16SizeMask64)
w.nbits += c.len w.nbits += c.len
if w.nbits >= 48 { if w.nbits >= 48 {
w.writeOutBits() w.writeOutBits()
@ -779,7 +779,7 @@ func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode)
} else { } else {
// inlined // inlined
c := offs[offsetCode&31] c := offs[offsetCode&31]
w.bits |= uint64(c.code) << (w.nbits & 63) w.bits |= uint64(c.code) << (w.nbits & reg16SizeMask64)
w.nbits += c.len w.nbits += c.len
if w.nbits >= 48 { if w.nbits >= 48 {
w.writeOutBits() w.writeOutBits()
@ -878,7 +878,7 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte, sync bool) {
for _, t := range input { for _, t := range input {
// Bitwriting inlined, ~30% speedup // Bitwriting inlined, ~30% speedup
c := encoding[t] c := encoding[t]
w.bits |= uint64(c.code) << ((w.nbits) & 63) w.bits |= uint64(c.code) << ((w.nbits) & reg16SizeMask64)
w.nbits += c.len w.nbits += c.len
if w.nbits >= 48 { if w.nbits >= 48 {
bits := w.bits bits := w.bits

View File

@ -522,8 +522,8 @@ func (f *decompressor) readHuffman() error {
return err return err
} }
} }
rep += int(f.b & uint32(1<<nb-1)) rep += int(f.b & uint32(1<<(nb&regSizeMaskUint32)-1))
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
if i+rep > n { if i+rep > n {
if debugDecode { if debugDecode {
@ -603,7 +603,7 @@ readLiteral:
return return
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := f.hl.chunks[b&(huffmanNumChunks-1)] chunk := f.hl.chunks[b&(huffmanNumChunks-1)]
@ -622,7 +622,7 @@ readLiteral:
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return return
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
v = int(chunk >> huffmanValueShift) v = int(chunk >> huffmanValueShift)
break break
@ -685,12 +685,12 @@ readLiteral:
return return
} }
} }
length += int(f.b & uint32(1<<n-1)) length += int(f.b & uint32(1<<(n&regSizeMaskUint32)-1))
f.b >>= n f.b >>= n & regSizeMaskUint32
f.nb -= n f.nb -= n
} }
var dist int var dist uint32
if f.hd == nil { if f.hd == nil {
for f.nb < 5 { for f.nb < 5 {
if err = f.moreBits(); err != nil { if err = f.moreBits(); err != nil {
@ -701,17 +701,19 @@ readLiteral:
return return
} }
} }
dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3)))
f.b >>= 5 f.b >>= 5
f.nb -= 5 f.nb -= 5
} else { } else {
if dist, err = f.huffSym(f.hd); err != nil { sym, err := f.huffSym(f.hd)
if err != nil {
if debugDecode { if debugDecode {
fmt.Println("huffsym:", err) fmt.Println("huffsym:", err)
} }
f.err = err f.err = err
return return
} }
dist = uint32(sym)
} }
switch { switch {
@ -720,7 +722,7 @@ readLiteral:
case dist < maxNumDist: case dist < maxNumDist:
nb := uint(dist-2) >> 1 nb := uint(dist-2) >> 1
// have 1 bit in bottom of dist, need nb more. // have 1 bit in bottom of dist, need nb more.
extra := (dist & 1) << nb extra := (dist & 1) << (nb & regSizeMaskUint32)
for f.nb < nb { for f.nb < nb {
if err = f.moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
@ -730,10 +732,10 @@ readLiteral:
return return
} }
} }
extra |= int(f.b & uint32(1<<nb-1)) extra |= f.b & uint32(1<<(nb&regSizeMaskUint32)-1)
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
dist = 1<<(nb+1) + 1 + extra dist = 1<<((nb+1)&regSizeMaskUint32) + 1 + extra
default: default:
if debugDecode { if debugDecode {
fmt.Println("dist too big:", dist, maxNumDist) fmt.Println("dist too big:", dist, maxNumDist)
@ -743,7 +745,7 @@ readLiteral:
} }
// No check on length; encoding can be prescient. // No check on length; encoding can be prescient.
if dist > f.dict.histSize() { if dist > uint32(f.dict.histSize()) {
if debugDecode { if debugDecode {
fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize()) fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize())
} }
@ -751,7 +753,7 @@ readLiteral:
return return
} }
f.copyLen, f.copyDist = length, dist f.copyLen, f.copyDist = length, int(dist)
goto copyHistory goto copyHistory
} }
@ -869,7 +871,7 @@ func (f *decompressor) moreBits() error {
return noEOF(err) return noEOF(err)
} }
f.roffset++ f.roffset++
f.b |= uint32(c) << f.nb f.b |= uint32(c) << (f.nb & regSizeMaskUint32)
f.nb += 8 f.nb += 8
return nil return nil
} }
@ -894,7 +896,7 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
return 0, noEOF(err) return 0, noEOF(err)
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := h.chunks[b&(huffmanNumChunks-1)] chunk := h.chunks[b&(huffmanNumChunks-1)]
@ -913,7 +915,7 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return 0, f.err return 0, f.err
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
return int(chunk >> huffmanValueShift), nil return int(chunk >> huffmanValueShift), nil
} }

View File

@ -63,7 +63,7 @@ readLiteral:
return return
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := f.hl.chunks[b&(huffmanNumChunks-1)] chunk := f.hl.chunks[b&(huffmanNumChunks-1)]
@ -82,7 +82,7 @@ readLiteral:
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return return
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
v = int(chunk >> huffmanValueShift) v = int(chunk >> huffmanValueShift)
break break
@ -145,15 +145,15 @@ readLiteral:
return return
} }
} }
length += int(f.b & uint32(1<<n-1)) length += int(f.b & uint32(1<<(n&regSizeMaskUint32)-1))
f.b >>= n f.b >>= n & regSizeMaskUint32
f.nb -= n f.nb -= n
} }
var dist int var dist uint32
if f.hd == nil { if f.hd == nil {
for f.nb < 5 { for f.nb < 5 {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<5:", err) fmt.Println("morebits f.nb<5:", err)
} }
@ -161,17 +161,19 @@ readLiteral:
return return
} }
} }
dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3)))
f.b >>= 5 f.b >>= 5
f.nb -= 5 f.nb -= 5
} else { } else {
if dist, err = f.huffSym(f.hd); err != nil { sym, err := f.huffSym(f.hd)
if err != nil {
if debugDecode { if debugDecode {
fmt.Println("huffsym:", err) fmt.Println("huffsym:", err)
} }
f.err = err f.err = err
return return
} }
dist = uint32(sym)
} }
switch { switch {
@ -180,9 +182,9 @@ readLiteral:
case dist < maxNumDist: case dist < maxNumDist:
nb := uint(dist-2) >> 1 nb := uint(dist-2) >> 1
// have 1 bit in bottom of dist, need nb more. // have 1 bit in bottom of dist, need nb more.
extra := (dist & 1) << nb extra := (dist & 1) << (nb & regSizeMaskUint32)
for f.nb < nb { for f.nb < nb {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<nb:", err) fmt.Println("morebits f.nb<nb:", err)
} }
@ -190,10 +192,10 @@ readLiteral:
return return
} }
} }
extra |= int(f.b & uint32(1<<nb-1)) extra |= f.b & uint32(1<<(nb&regSizeMaskUint32)-1)
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
dist = 1<<(nb+1) + 1 + extra dist = 1<<((nb+1)&regSizeMaskUint32) + 1 + extra
default: default:
if debugDecode { if debugDecode {
fmt.Println("dist too big:", dist, maxNumDist) fmt.Println("dist too big:", dist, maxNumDist)
@ -203,7 +205,7 @@ readLiteral:
} }
// No check on length; encoding can be prescient. // No check on length; encoding can be prescient.
if dist > f.dict.histSize() { if dist > uint32(f.dict.histSize()) {
if debugDecode { if debugDecode {
fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize()) fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize())
} }
@ -211,7 +213,7 @@ readLiteral:
return return
} }
f.copyLen, f.copyDist = length, dist f.copyLen, f.copyDist = length, int(dist)
goto copyHistory goto copyHistory
} }
@ -287,7 +289,7 @@ readLiteral:
return return
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := f.hl.chunks[b&(huffmanNumChunks-1)] chunk := f.hl.chunks[b&(huffmanNumChunks-1)]
@ -306,7 +308,7 @@ readLiteral:
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return return
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
v = int(chunk >> huffmanValueShift) v = int(chunk >> huffmanValueShift)
break break
@ -369,15 +371,15 @@ readLiteral:
return return
} }
} }
length += int(f.b & uint32(1<<n-1)) length += int(f.b & uint32(1<<(n&regSizeMaskUint32)-1))
f.b >>= n f.b >>= n & regSizeMaskUint32
f.nb -= n f.nb -= n
} }
var dist int var dist uint32
if f.hd == nil { if f.hd == nil {
for f.nb < 5 { for f.nb < 5 {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<5:", err) fmt.Println("morebits f.nb<5:", err)
} }
@ -385,17 +387,19 @@ readLiteral:
return return
} }
} }
dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3)))
f.b >>= 5 f.b >>= 5
f.nb -= 5 f.nb -= 5
} else { } else {
if dist, err = f.huffSym(f.hd); err != nil { sym, err := f.huffSym(f.hd)
if err != nil {
if debugDecode { if debugDecode {
fmt.Println("huffsym:", err) fmt.Println("huffsym:", err)
} }
f.err = err f.err = err
return return
} }
dist = uint32(sym)
} }
switch { switch {
@ -404,9 +408,9 @@ readLiteral:
case dist < maxNumDist: case dist < maxNumDist:
nb := uint(dist-2) >> 1 nb := uint(dist-2) >> 1
// have 1 bit in bottom of dist, need nb more. // have 1 bit in bottom of dist, need nb more.
extra := (dist & 1) << nb extra := (dist & 1) << (nb & regSizeMaskUint32)
for f.nb < nb { for f.nb < nb {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<nb:", err) fmt.Println("morebits f.nb<nb:", err)
} }
@ -414,10 +418,10 @@ readLiteral:
return return
} }
} }
extra |= int(f.b & uint32(1<<nb-1)) extra |= f.b & uint32(1<<(nb&regSizeMaskUint32)-1)
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
dist = 1<<(nb+1) + 1 + extra dist = 1<<((nb+1)&regSizeMaskUint32) + 1 + extra
default: default:
if debugDecode { if debugDecode {
fmt.Println("dist too big:", dist, maxNumDist) fmt.Println("dist too big:", dist, maxNumDist)
@ -427,7 +431,7 @@ readLiteral:
} }
// No check on length; encoding can be prescient. // No check on length; encoding can be prescient.
if dist > f.dict.histSize() { if dist > uint32(f.dict.histSize()) {
if debugDecode { if debugDecode {
fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize()) fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize())
} }
@ -435,7 +439,7 @@ readLiteral:
return return
} }
f.copyLen, f.copyDist = length, dist f.copyLen, f.copyDist = length, int(dist)
goto copyHistory goto copyHistory
} }
@ -511,7 +515,7 @@ readLiteral:
return return
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := f.hl.chunks[b&(huffmanNumChunks-1)] chunk := f.hl.chunks[b&(huffmanNumChunks-1)]
@ -530,7 +534,7 @@ readLiteral:
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return return
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
v = int(chunk >> huffmanValueShift) v = int(chunk >> huffmanValueShift)
break break
@ -593,15 +597,15 @@ readLiteral:
return return
} }
} }
length += int(f.b & uint32(1<<n-1)) length += int(f.b & uint32(1<<(n&regSizeMaskUint32)-1))
f.b >>= n f.b >>= n & regSizeMaskUint32
f.nb -= n f.nb -= n
} }
var dist int var dist uint32
if f.hd == nil { if f.hd == nil {
for f.nb < 5 { for f.nb < 5 {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<5:", err) fmt.Println("morebits f.nb<5:", err)
} }
@ -609,17 +613,19 @@ readLiteral:
return return
} }
} }
dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3)))
f.b >>= 5 f.b >>= 5
f.nb -= 5 f.nb -= 5
} else { } else {
if dist, err = f.huffSym(f.hd); err != nil { sym, err := f.huffSym(f.hd)
if err != nil {
if debugDecode { if debugDecode {
fmt.Println("huffsym:", err) fmt.Println("huffsym:", err)
} }
f.err = err f.err = err
return return
} }
dist = uint32(sym)
} }
switch { switch {
@ -628,9 +634,9 @@ readLiteral:
case dist < maxNumDist: case dist < maxNumDist:
nb := uint(dist-2) >> 1 nb := uint(dist-2) >> 1
// have 1 bit in bottom of dist, need nb more. // have 1 bit in bottom of dist, need nb more.
extra := (dist & 1) << nb extra := (dist & 1) << (nb & regSizeMaskUint32)
for f.nb < nb { for f.nb < nb {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<nb:", err) fmt.Println("morebits f.nb<nb:", err)
} }
@ -638,10 +644,10 @@ readLiteral:
return return
} }
} }
extra |= int(f.b & uint32(1<<nb-1)) extra |= f.b & uint32(1<<(nb&regSizeMaskUint32)-1)
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
dist = 1<<(nb+1) + 1 + extra dist = 1<<((nb+1)&regSizeMaskUint32) + 1 + extra
default: default:
if debugDecode { if debugDecode {
fmt.Println("dist too big:", dist, maxNumDist) fmt.Println("dist too big:", dist, maxNumDist)
@ -651,7 +657,7 @@ readLiteral:
} }
// No check on length; encoding can be prescient. // No check on length; encoding can be prescient.
if dist > f.dict.histSize() { if dist > uint32(f.dict.histSize()) {
if debugDecode { if debugDecode {
fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize()) fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize())
} }
@ -659,7 +665,7 @@ readLiteral:
return return
} }
f.copyLen, f.copyDist = length, dist f.copyLen, f.copyDist = length, int(dist)
goto copyHistory goto copyHistory
} }
@ -735,7 +741,7 @@ readLiteral:
return return
} }
f.roffset++ f.roffset++
b |= uint32(c) << (nb & 31) b |= uint32(c) << (nb & regSizeMaskUint32)
nb += 8 nb += 8
} }
chunk := f.hl.chunks[b&(huffmanNumChunks-1)] chunk := f.hl.chunks[b&(huffmanNumChunks-1)]
@ -754,7 +760,7 @@ readLiteral:
f.err = CorruptInputError(f.roffset) f.err = CorruptInputError(f.roffset)
return return
} }
f.b = b >> (n & 31) f.b = b >> (n & regSizeMaskUint32)
f.nb = nb - n f.nb = nb - n
v = int(chunk >> huffmanValueShift) v = int(chunk >> huffmanValueShift)
break break
@ -817,15 +823,15 @@ readLiteral:
return return
} }
} }
length += int(f.b & uint32(1<<n-1)) length += int(f.b & uint32(1<<(n&regSizeMaskUint32)-1))
f.b >>= n f.b >>= n & regSizeMaskUint32
f.nb -= n f.nb -= n
} }
var dist int var dist uint32
if f.hd == nil { if f.hd == nil {
for f.nb < 5 { for f.nb < 5 {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<5:", err) fmt.Println("morebits f.nb<5:", err)
} }
@ -833,17 +839,19 @@ readLiteral:
return return
} }
} }
dist = int(bits.Reverse8(uint8(f.b & 0x1F << 3))) dist = uint32(bits.Reverse8(uint8(f.b & 0x1F << 3)))
f.b >>= 5 f.b >>= 5
f.nb -= 5 f.nb -= 5
} else { } else {
if dist, err = f.huffSym(f.hd); err != nil { sym, err := f.huffSym(f.hd)
if err != nil {
if debugDecode { if debugDecode {
fmt.Println("huffsym:", err) fmt.Println("huffsym:", err)
} }
f.err = err f.err = err
return return
} }
dist = uint32(sym)
} }
switch { switch {
@ -852,9 +860,9 @@ readLiteral:
case dist < maxNumDist: case dist < maxNumDist:
nb := uint(dist-2) >> 1 nb := uint(dist-2) >> 1
// have 1 bit in bottom of dist, need nb more. // have 1 bit in bottom of dist, need nb more.
extra := (dist & 1) << nb extra := (dist & 1) << (nb & regSizeMaskUint32)
for f.nb < nb { for f.nb < nb {
if err = moreBits(); err != nil { if err = f.moreBits(); err != nil {
if debugDecode { if debugDecode {
fmt.Println("morebits f.nb<nb:", err) fmt.Println("morebits f.nb<nb:", err)
} }
@ -862,10 +870,10 @@ readLiteral:
return return
} }
} }
extra |= int(f.b & uint32(1<<nb-1)) extra |= f.b & uint32(1<<(nb&regSizeMaskUint32)-1)
f.b >>= nb f.b >>= nb & regSizeMaskUint32
f.nb -= nb f.nb -= nb
dist = 1<<(nb+1) + 1 + extra dist = 1<<((nb+1)&regSizeMaskUint32) + 1 + extra
default: default:
if debugDecode { if debugDecode {
fmt.Println("dist too big:", dist, maxNumDist) fmt.Println("dist too big:", dist, maxNumDist)
@ -875,7 +883,7 @@ readLiteral:
} }
// No check on length; encoding can be prescient. // No check on length; encoding can be prescient.
if dist > f.dict.histSize() { if dist > uint32(f.dict.histSize()) {
if debugDecode { if debugDecode {
fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize()) fmt.Println("dist > f.dict.histSize():", dist, f.dict.histSize())
} }
@ -883,7 +891,7 @@ readLiteral:
return return
} }
f.copyLen, f.copyDist = length, dist f.copyLen, f.copyDist = length, int(dist)
goto copyHistory goto copyHistory
} }

View File

@ -0,0 +1,37 @@
package flate
const (
// Masks for shifts with register sizes of the shift value.
// This can be used to work around the x86 design of shifting by mod register size.
// It can be used when a variable shift is always smaller than the register size.
// reg8SizeMaskX - shift value is 8 bits, shifted is X
reg8SizeMask8 = 7
reg8SizeMask16 = 15
reg8SizeMask32 = 31
reg8SizeMask64 = 63
// reg16SizeMaskX - shift value is 16 bits, shifted is X
reg16SizeMask8 = reg8SizeMask8
reg16SizeMask16 = reg8SizeMask16
reg16SizeMask32 = reg8SizeMask32
reg16SizeMask64 = reg8SizeMask64
// reg32SizeMaskX - shift value is 32 bits, shifted is X
reg32SizeMask8 = reg8SizeMask8
reg32SizeMask16 = reg8SizeMask16
reg32SizeMask32 = reg8SizeMask32
reg32SizeMask64 = reg8SizeMask64
// reg64SizeMaskX - shift value is 64 bits, shifted is X
reg64SizeMask8 = reg8SizeMask8
reg64SizeMask16 = reg8SizeMask16
reg64SizeMask32 = reg8SizeMask32
reg64SizeMask64 = reg8SizeMask64
// regSizeMaskUintX - shift value is uint, shifted is X
regSizeMaskUint8 = reg8SizeMask8
regSizeMaskUint16 = reg8SizeMask16
regSizeMaskUint32 = reg8SizeMask32
regSizeMaskUint64 = reg8SizeMask64
)

View File

@ -0,0 +1,39 @@
//+build !amd64
package flate
const (
// Masks for shifts with register sizes of the shift value.
// This can be used to work around the x86 design of shifting by mod register size.
// It can be used when a variable shift is always smaller than the register size.
// reg8SizeMaskX - shift value is 8 bits, shifted is X
reg8SizeMask8 = 0xff
reg8SizeMask16 = 0xff
reg8SizeMask32 = 0xff
reg8SizeMask64 = 0xff
// reg16SizeMaskX - shift value is 16 bits, shifted is X
reg16SizeMask8 = 0xffff
reg16SizeMask16 = 0xffff
reg16SizeMask32 = 0xffff
reg16SizeMask64 = 0xffff
// reg32SizeMaskX - shift value is 32 bits, shifted is X
reg32SizeMask8 = 0xffffffff
reg32SizeMask16 = 0xffffffff
reg32SizeMask32 = 0xffffffff
reg32SizeMask64 = 0xffffffff
// reg64SizeMaskX - shift value is 64 bits, shifted is X
reg64SizeMask8 = 0xffffffffffffffff
reg64SizeMask16 = 0xffffffffffffffff
reg64SizeMask32 = 0xffffffffffffffff
reg64SizeMask64 = 0xffffffffffffffff
// regSizeMaskUintX - shift value is uint, shifted is X
regSizeMaskUint8 = ^uint(0)
regSizeMaskUint16 = ^uint(0)
regSizeMaskUint32 = ^uint(0)
regSizeMaskUint64 = ^uint(0)
)

View File

@ -119,6 +119,16 @@ type Scratch struct {
huffWeight [maxSymbolValue + 1]byte huffWeight [maxSymbolValue + 1]byte
} }
// TransferCTable will transfer the previously used compression table.
func (s *Scratch) TransferCTable(src *Scratch) {
if cap(s.prevTable) < len(src.prevTable) {
s.prevTable = make(cTable, 0, maxSymbolValue+1)
}
s.prevTable = s.prevTable[:len(src.prevTable)]
copy(s.prevTable, src.prevTable)
s.prevTableLog = src.prevTableLog
}
func (s *Scratch) prepare(in []byte) (*Scratch, error) { func (s *Scratch) prepare(in []byte) (*Scratch, error) {
if len(in) > BlockSizeMax { if len(in) > BlockSizeMax {
return nil, ErrTooBig return nil, ErrTooBig

View File

@ -5,7 +5,6 @@ It offers a very wide range of compression / speed trade-off, while being backed
A high performance compression algorithm is implemented. For now focused on speed. A high performance compression algorithm is implemented. For now focused on speed.
This package provides [compression](#Compressor) to and [decompression](#Decompressor) of Zstandard content. This package provides [compression](#Compressor) to and [decompression](#Decompressor) of Zstandard content.
Note that custom dictionaries are only supported for decompression.
This package is pure Go and without use of "unsafe". This package is pure Go and without use of "unsafe".
@ -232,41 +231,6 @@ nyc-taxi-data-10M.csv gzstd 1 3325605752 928656485 23876 132.83
nyc-taxi-data-10M.csv gzkp 1 3325605752 924718719 16388 193.53 nyc-taxi-data-10M.csv gzkp 1 3325605752 924718719 16388 193.53
``` ```
### Converters
As part of the development process a *Snappy* -> *Zstandard* converter was also built.
This can convert a *framed* [Snappy Stream](https://godoc.org/github.com/golang/snappy#Writer) to a zstd stream.
Note that a single block is not framed.
Conversion is done by converting the stream directly from Snappy without intermediate full decoding.
Therefore the compression ratio is much less than what can be done by a full decompression
and compression, and a faulty Snappy stream may lead to a faulty Zstandard stream without
any errors being generated.
No CRC value is being generated and not all CRC values of the Snappy stream are checked.
However, it provides really fast re-compression of Snappy streams.
```
BenchmarkSnappy_ConvertSilesia-8 1 1156001600 ns/op 183.35 MB/s
Snappy len 103008711 -> zstd len 82687318
BenchmarkSnappy_Enwik9-8 1 6472998400 ns/op 154.49 MB/s
Snappy len 508028601 -> zstd len 390921079
```
```Go
s := zstd.SnappyConverter{}
n, err = s.Convert(input, output)
if err != nil {
fmt.Println("Re-compressed stream to", n, "bytes")
}
```
The converter `s` can be reused to avoid allocations, even after errors.
## Decompressor ## Decompressor
Staus: STABLE - there may still be subtle bugs, but a wide variety of content has been tested. Staus: STABLE - there may still be subtle bugs, but a wide variety of content has been tested.
@ -337,6 +301,21 @@ A re-used Decoder will still contain the dictionaries registered.
When registering multiple dictionaries with the same ID, the last one will be used. When registering multiple dictionaries with the same ID, the last one will be used.
It is possible to use dictionaries when compressing data.
To enable a dictionary use `WithEncoderDict(dict []byte)`. Here only one dictionary will be used
and it will likely be used even if it doesn't improve compression.
The used dictionary must be used to decompress the content.
For any real gains, the dictionary should be built with similar data.
If an unsuitable dictionary is used the output may be slightly larger than using no dictionary.
Use the [zstd commandline tool](https://github.com/facebook/zstd/releases) to build a dictionary from sample data.
For information see [zstd dictionary information](https://github.com/facebook/zstd#the-case-for-small-data-compression).
For now there is a fixed startup performance penalty for compressing content with dictionaries.
This will likely be improved over time. Just be aware to test performance when implementing.
### Allocation-less operation ### Allocation-less operation
The decoder has been designed to operate without allocations after a warmup. The decoder has been designed to operate without allocations after a warmup.

View File

@ -646,7 +646,7 @@ func (b *blockDec) decodeCompressed(hist *history) error {
} }
} else { } else {
if hist.huffTree != nil && huff != nil { if hist.huffTree != nil && huff != nil {
if hist.dict == nil || hist.dict.litDec != hist.huffTree { if hist.dict == nil || hist.dict.litEnc != hist.huffTree {
huffDecoderPool.Put(hist.huffTree) huffDecoderPool.Put(hist.huffTree)
} }
hist.huffTree = nil hist.huffTree = nil

View File

@ -14,12 +14,13 @@ import (
) )
type blockEnc struct { type blockEnc struct {
size int size int
literals []byte literals []byte
sequences []seq sequences []seq
coders seqCoders coders seqCoders
litEnc *huff0.Scratch litEnc *huff0.Scratch
wr bitWriter dictLitEnc *huff0.Scratch
wr bitWriter
extraLits int extraLits int
last bool last bool
@ -314,19 +315,19 @@ func (b *blockEnc) encodeRawTo(dst, src []byte) []byte {
} }
// encodeLits can be used if the block is only litLen. // encodeLits can be used if the block is only litLen.
func (b *blockEnc) encodeLits(raw bool) error { func (b *blockEnc) encodeLits(lits []byte, raw bool) error {
var bh blockHeader var bh blockHeader
bh.setLast(b.last) bh.setLast(b.last)
bh.setSize(uint32(len(b.literals))) bh.setSize(uint32(len(lits)))
// Don't compress extremely small blocks // Don't compress extremely small blocks
if len(b.literals) < 32 || raw { if len(lits) < 8 || (len(lits) < 32 && b.dictLitEnc == nil) || raw {
if debug { if debug {
println("Adding RAW block, length", len(b.literals), "last:", b.last) println("Adding RAW block, length", len(lits), "last:", b.last)
} }
bh.setType(blockTypeRaw) bh.setType(blockTypeRaw)
b.output = bh.appendTo(b.output) b.output = bh.appendTo(b.output)
b.output = append(b.output, b.literals...) b.output = append(b.output, lits...)
return nil return nil
} }
@ -335,13 +336,18 @@ func (b *blockEnc) encodeLits(raw bool) error {
reUsed, single bool reUsed, single bool
err error err error
) )
if len(b.literals) >= 1024 { if b.dictLitEnc != nil {
b.litEnc.TransferCTable(b.dictLitEnc)
b.litEnc.Reuse = huff0.ReusePolicyAllow
b.dictLitEnc = nil
}
if len(lits) >= 1024 {
// Use 4 Streams. // Use 4 Streams.
out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc) out, reUsed, err = huff0.Compress4X(lits, b.litEnc)
} else if len(b.literals) > 32 { } else if len(lits) > 32 {
// Use 1 stream // Use 1 stream
single = true single = true
out, reUsed, err = huff0.Compress1X(b.literals, b.litEnc) out, reUsed, err = huff0.Compress1X(lits, b.litEnc)
} else { } else {
err = huff0.ErrIncompressible err = huff0.ErrIncompressible
} }
@ -349,19 +355,19 @@ func (b *blockEnc) encodeLits(raw bool) error {
switch err { switch err {
case huff0.ErrIncompressible: case huff0.ErrIncompressible:
if debug { if debug {
println("Adding RAW block, length", len(b.literals), "last:", b.last) println("Adding RAW block, length", len(lits), "last:", b.last)
} }
bh.setType(blockTypeRaw) bh.setType(blockTypeRaw)
b.output = bh.appendTo(b.output) b.output = bh.appendTo(b.output)
b.output = append(b.output, b.literals...) b.output = append(b.output, lits...)
return nil return nil
case huff0.ErrUseRLE: case huff0.ErrUseRLE:
if debug { if debug {
println("Adding RLE block, length", len(b.literals)) println("Adding RLE block, length", len(lits))
} }
bh.setType(blockTypeRLE) bh.setType(blockTypeRLE)
b.output = bh.appendTo(b.output) b.output = bh.appendTo(b.output)
b.output = append(b.output, b.literals[0]) b.output = append(b.output, lits[0])
return nil return nil
default: default:
return err return err
@ -384,7 +390,7 @@ func (b *blockEnc) encodeLits(raw bool) error {
lh.setType(literalsBlockCompressed) lh.setType(literalsBlockCompressed)
} }
// Set sizes // Set sizes
lh.setSizes(len(out), len(b.literals), single) lh.setSizes(len(out), len(lits), single)
bh.setSize(uint32(len(out) + lh.size() + 1)) bh.setSize(uint32(len(out) + lh.size() + 1))
// Write block headers. // Write block headers.
@ -444,13 +450,19 @@ func fuzzFseEncoder(data []byte) int {
} }
// encode will encode the block and append the output in b.output. // encode will encode the block and append the output in b.output.
func (b *blockEnc) encode(raw, rawAllLits bool) error { // Previous offset codes must be pushed if more blocks are expected.
func (b *blockEnc) encode(org []byte, raw, rawAllLits bool) error {
if len(b.sequences) == 0 { if len(b.sequences) == 0 {
return b.encodeLits(rawAllLits) return b.encodeLits(b.literals, rawAllLits)
} }
// We want some difference // We want some difference to at least account for the headers.
if len(b.literals) > (b.size - (b.size >> 5)) { saved := b.size - len(b.literals) - (b.size >> 5)
return errIncompressible if saved < 16 {
if org == nil {
return errIncompressible
}
b.popOffsets()
return b.encodeLits(org, rawAllLits)
} }
var bh blockHeader var bh blockHeader
@ -466,6 +478,11 @@ func (b *blockEnc) encode(raw, rawAllLits bool) error {
reUsed, single bool reUsed, single bool
err error err error
) )
if b.dictLitEnc != nil {
b.litEnc.TransferCTable(b.dictLitEnc)
b.litEnc.Reuse = huff0.ReusePolicyAllow
b.dictLitEnc = nil
}
if len(b.literals) >= 1024 && !raw { if len(b.literals) >= 1024 && !raw {
// Use 4 Streams. // Use 4 Streams.
out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc) out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc)

View File

@ -187,7 +187,7 @@ func (d *Decoder) Reset(r io.Reader) error {
d.current.err = err d.current.err = err
d.current.flushed = true d.current.flushed = true
if debug { if debug {
println("sync decode to ", len(dst), "bytes, err:", err) println("sync decode to", len(dst), "bytes, err:", err)
} }
return nil return nil
} }
@ -303,6 +303,9 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
frame.history.reset() frame.history.reset()
err := frame.reset(&frame.bBuf) err := frame.reset(&frame.bBuf)
if err == io.EOF { if err == io.EOF {
if debug {
println("frame reset return EOF")
}
return dst, nil return dst, nil
} }
if frame.DictionaryID != nil { if frame.DictionaryID != nil {
@ -341,6 +344,9 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
return dst, err return dst, err
} }
if len(frame.bBuf) == 0 { if len(frame.bBuf) == 0 {
if debug {
println("frame dbuf empty")
}
break break
} }
} }

View File

@ -13,14 +13,31 @@ import (
type dict struct { type dict struct {
id uint32 id uint32
litDec *huff0.Scratch litEnc *huff0.Scratch
llDec, ofDec, mlDec sequenceDec llDec, ofDec, mlDec sequenceDec
offsets [3]int //llEnc, ofEnc, mlEnc []*fseEncoder
content []byte offsets [3]int
content []byte
} }
var dictMagic = [4]byte{0x37, 0xa4, 0x30, 0xec} var dictMagic = [4]byte{0x37, 0xa4, 0x30, 0xec}
// ID returns the dictionary id or 0 if d is nil.
func (d *dict) ID() uint32 {
if d == nil {
return 0
}
return d.id
}
// DictContentSize returns the dictionary content size or 0 if d is nil.
func (d *dict) DictContentSize() int {
if d == nil {
return 0
}
return len(d.content)
}
// Load a dictionary as described in // Load a dictionary as described in
// https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format // https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format
func loadDict(b []byte) (*dict, error) { func loadDict(b []byte) (*dict, error) {
@ -43,10 +60,11 @@ func loadDict(b []byte) (*dict, error) {
// Read literal table // Read literal table
var err error var err error
d.litDec, b, err = huff0.ReadTable(b[8:], nil) d.litEnc, b, err = huff0.ReadTable(b[8:], nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
d.litEnc.Reuse = huff0.ReusePolicyMust
br := byteReader{ br := byteReader{
b: b, b: b,

155
vendor/github.com/klauspost/compress/zstd/enc_base.go generated vendored Normal file
View File

@ -0,0 +1,155 @@
package zstd
import (
"fmt"
"math/bits"
"github.com/klauspost/compress/zstd/internal/xxhash"
)
type fastBase struct {
// cur is the offset at the start of hist
cur int32
// maximum offset. Should be at least 2x block size.
maxMatchOff int32
hist []byte
crc *xxhash.Digest
tmp [8]byte
blk *blockEnc
lastDictID uint32
}
// CRC returns the underlying CRC writer.
func (e *fastBase) CRC() *xxhash.Digest {
return e.crc
}
// AppendCRC will append the CRC to the destination slice and return it.
func (e *fastBase) AppendCRC(dst []byte) []byte {
crc := e.crc.Sum(e.tmp[:0])
dst = append(dst, crc[7], crc[6], crc[5], crc[4])
return dst
}
// WindowSize returns the window size of the encoder,
// or a window size small enough to contain the input size, if > 0.
func (e *fastBase) WindowSize(size int) int32 {
if size > 0 && size < int(e.maxMatchOff) {
b := int32(1) << uint(bits.Len(uint(size)))
// Keep minimum window.
if b < 1024 {
b = 1024
}
return b
}
return e.maxMatchOff
}
// Block returns the current block.
func (e *fastBase) Block() *blockEnc {
return e.blk
}
func (e *fastBase) addBlock(src []byte) int32 {
if debugAsserts && e.cur > bufferReset {
panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, bufferReset))
}
// check if we have space already
if len(e.hist)+len(src) > cap(e.hist) {
if cap(e.hist) == 0 {
l := e.maxMatchOff * 2
// Make it at least 1MB.
if l < 1<<20 {
l = 1 << 20
}
e.hist = make([]byte, 0, l)
} else {
if cap(e.hist) < int(e.maxMatchOff*2) {
panic("unexpected buffer size")
}
// Move down
offset := int32(len(e.hist)) - e.maxMatchOff
copy(e.hist[0:e.maxMatchOff], e.hist[offset:])
e.cur += offset
e.hist = e.hist[:e.maxMatchOff]
}
}
s := int32(len(e.hist))
e.hist = append(e.hist, src...)
return s
}
// useBlock will replace the block with the provided one,
// but transfer recent offsets from the previous.
func (e *fastBase) UseBlock(enc *blockEnc) {
enc.reset(e.blk)
e.blk = enc
}
func (e *fastBase) matchlenNoHist(s, t int32, src []byte) int32 {
// Extend the match to be as long as possible.
return int32(matchLen(src[s:], src[t:]))
}
func (e *fastBase) matchlen(s, t int32, src []byte) int32 {
if debugAsserts {
if s < 0 {
err := fmt.Sprintf("s (%d) < 0", s)
panic(err)
}
if t < 0 {
err := fmt.Sprintf("s (%d) < 0", s)
panic(err)
}
if s-t > e.maxMatchOff {
err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff)
panic(err)
}
if len(src)-int(s) > maxCompressedBlockSize {
panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize))
}
}
// Extend the match to be as long as possible.
return int32(matchLen(src[s:], src[t:]))
}
// Reset the encoding table.
func (e *fastBase) resetBase(d *dict, singleBlock bool) {
if e.blk == nil {
e.blk = &blockEnc{}
e.blk.init()
} else {
e.blk.reset(nil)
}
e.blk.initNewEncode()
if e.crc == nil {
e.crc = xxhash.New()
} else {
e.crc.Reset()
}
if (!singleBlock || d.DictContentSize() > 0) && cap(e.hist) < int(e.maxMatchOff*2)+d.DictContentSize() {
l := e.maxMatchOff*2 + int32(d.DictContentSize())
// Make it at least 1MB.
if l < 1<<20 {
l = 1 << 20
}
e.hist = make([]byte, 0, l)
}
// We offset current position so everything will be out of reach.
// If above reset line, history will be purged.
if e.cur < bufferReset {
e.cur += e.maxMatchOff + int32(len(e.hist))
}
e.hist = e.hist[:0]
if d != nil {
// Set offsets (currently not used)
for i, off := range d.offsets {
e.blk.recentOffsets[i] = uint32(off)
e.blk.prevRecentOffsets[i] = e.blk.recentOffsets[i]
}
// Transfer litenc.
e.blk.dictLitEnc = d.litEnc
e.hist = append(e.hist, d.content...)
}
}

View File

@ -31,8 +31,10 @@ type prevEntry struct {
// and that it is longer (lazy matching). // and that it is longer (lazy matching).
type betterFastEncoder struct { type betterFastEncoder struct {
fastBase fastBase
table [betterShortTableSize]tableEntry table [betterShortTableSize]tableEntry
longTable [betterLongTableSize]prevEntry longTable [betterLongTableSize]prevEntry
dictTable []tableEntry
dictLongTable []prevEntry
} }
// Encode improves compression... // Encode improves compression...
@ -516,3 +518,78 @@ encodeLoop:
func (e *betterFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { func (e *betterFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) {
e.Encode(blk, src) e.Encode(blk, src)
} }
// ResetDict will reset and set a dictionary if not nil
func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) {
e.resetBase(d, singleBlock)
if d == nil {
return
}
// Init or copy dict table
if len(e.dictTable) != len(e.table) || d.id != e.lastDictID {
if len(e.dictTable) != len(e.table) {
e.dictTable = make([]tableEntry, len(e.table))
}
end := int32(len(d.content)) - 8 + e.maxMatchOff
for i := e.maxMatchOff; i < end; i += 4 {
const hashLog = betterShortTableBits
cv := load6432(d.content, i-e.maxMatchOff)
nextHash := hash5(cv, hashLog) // 0 -> 4
nextHash1 := hash5(cv>>8, hashLog) // 1 -> 5
nextHash2 := hash5(cv>>16, hashLog) // 2 -> 6
nextHash3 := hash5(cv>>24, hashLog) // 3 -> 7
e.dictTable[nextHash] = tableEntry{
val: uint32(cv),
offset: i,
}
e.dictTable[nextHash1] = tableEntry{
val: uint32(cv >> 8),
offset: i + 1,
}
e.dictTable[nextHash2] = tableEntry{
val: uint32(cv >> 16),
offset: i + 2,
}
e.dictTable[nextHash3] = tableEntry{
val: uint32(cv >> 24),
offset: i + 3,
}
}
e.lastDictID = d.id
}
// Init or copy dict table
if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID {
if len(e.dictLongTable) != len(e.longTable) {
e.dictLongTable = make([]prevEntry, len(e.longTable))
}
if len(d.content) >= 8 {
cv := load6432(d.content, 0)
h := hash8(cv, betterLongTableBits)
e.dictLongTable[h] = prevEntry{
offset: e.maxMatchOff,
prev: e.dictLongTable[h].offset,
}
end := int32(len(d.content)) - 8 + e.maxMatchOff
off := 8 // First to read
for i := e.maxMatchOff + 1; i < end; i++ {
cv = cv>>8 | (uint64(d.content[off]) << 56)
h := hash8(cv, betterLongTableBits)
e.dictLongTable[h] = prevEntry{
offset: i,
prev: e.dictLongTable[h].offset,
}
off++
}
}
e.lastDictID = d.id
}
// Reset table to initial state
copy(e.longTable[:], e.dictLongTable)
e.cur = e.maxMatchOff
// Reset table to initial state
copy(e.table[:], e.dictTable)
}

View File

@ -18,7 +18,8 @@ const (
type doubleFastEncoder struct { type doubleFastEncoder struct {
fastEncoder fastEncoder
longTable [dFastLongTableSize]tableEntry longTable [dFastLongTableSize]tableEntry
dictLongTable []tableEntry
} }
// Encode mimmics functionality in zstd_dfast.c // Encode mimmics functionality in zstd_dfast.c
@ -494,7 +495,7 @@ encodeLoop:
// but the likelihood of both the first 4 bytes and the hash matching should be enough. // but the likelihood of both the first 4 bytes and the hash matching should be enough.
t = candidateL.offset - e.cur t = candidateL.offset - e.cur
if debugAsserts && s <= t { if debugAsserts && s <= t {
panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) panic(fmt.Sprintf("s (%d) <= t (%d). cur: %d", s, t, e.cur))
} }
if debugAsserts && s-t > e.maxMatchOff { if debugAsserts && s-t > e.maxMatchOff {
panic("s - t >e.maxMatchOff") panic("s - t >e.maxMatchOff")
@ -676,3 +677,37 @@ encodeLoop:
e.cur += int32(len(src)) e.cur += int32(len(src))
} }
} }
// ResetDict will reset and set a dictionary if not nil
func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) {
e.fastEncoder.Reset(d, singleBlock)
if d == nil {
return
}
// Init or copy dict table
if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID {
if len(e.dictLongTable) != len(e.longTable) {
e.dictLongTable = make([]tableEntry, len(e.longTable))
}
if len(d.content) >= 8 {
cv := load6432(d.content, 0)
e.dictLongTable[hash8(cv, dFastLongTableBits)] = tableEntry{
val: uint32(cv),
offset: e.maxMatchOff,
}
end := int32(len(d.content)) - 8 + e.maxMatchOff
for i := e.maxMatchOff + 1; i < end; i++ {
cv = cv>>8 | (uint64(d.content[i-e.maxMatchOff+7]) << 56)
e.dictLongTable[hash8(cv, dFastLongTableBits)] = tableEntry{
val: uint32(cv),
offset: i,
}
}
}
e.lastDictID = d.id
}
// Reset table to initial state
e.cur = e.maxMatchOff
copy(e.longTable[:], e.dictLongTable)
}

View File

@ -8,8 +8,6 @@ import (
"fmt" "fmt"
"math" "math"
"math/bits" "math/bits"
"github.com/klauspost/compress/zstd/internal/xxhash"
) )
const ( const (
@ -24,51 +22,10 @@ type tableEntry struct {
offset int32 offset int32
} }
type fastBase struct {
// cur is the offset at the start of hist
cur int32
// maximum offset. Should be at least 2x block size.
maxMatchOff int32
hist []byte
crc *xxhash.Digest
tmp [8]byte
blk *blockEnc
}
type fastEncoder struct { type fastEncoder struct {
fastBase fastBase
table [tableSize]tableEntry table [tableSize]tableEntry
} dictTable []tableEntry
// CRC returns the underlying CRC writer.
func (e *fastBase) CRC() *xxhash.Digest {
return e.crc
}
// AppendCRC will append the CRC to the destination slice and return it.
func (e *fastBase) AppendCRC(dst []byte) []byte {
crc := e.crc.Sum(e.tmp[:0])
dst = append(dst, crc[7], crc[6], crc[5], crc[4])
return dst
}
// WindowSize returns the window size of the encoder,
// or a window size small enough to contain the input size, if > 0.
func (e *fastBase) WindowSize(size int) int32 {
if size > 0 && size < int(e.maxMatchOff) {
b := int32(1) << uint(bits.Len(uint(size)))
// Keep minimum window.
if b < 1024 {
b = 1024
}
return b
}
return e.maxMatchOff
}
// Block returns the current block.
func (e *fastBase) Block() *blockEnc {
return e.blk
} }
// Encode mimmics functionality in zstd_fast.c // Encode mimmics functionality in zstd_fast.c
@ -660,96 +617,45 @@ encodeLoop:
} }
} }
func (e *fastBase) addBlock(src []byte) int32 { // ResetDict will reset and set a dictionary if not nil
if debugAsserts && e.cur > bufferReset { func (e *fastEncoder) Reset(d *dict, singleBlock bool) {
panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, bufferReset)) e.resetBase(d, singleBlock)
if d == nil {
return
} }
// check if we have space already
if len(e.hist)+len(src) > cap(e.hist) { // Init or copy dict table
if cap(e.hist) == 0 { if len(e.dictTable) != len(e.table) || d.id != e.lastDictID {
l := e.maxMatchOff * 2 if len(e.dictTable) != len(e.table) {
// Make it at least 1MB. e.dictTable = make([]tableEntry, len(e.table))
if l < 1<<20 { }
l = 1 << 20 if true {
end := e.maxMatchOff + int32(len(d.content)) - 8
for i := e.maxMatchOff; i < end; i += 3 {
const hashLog = tableBits
cv := load6432(d.content, i-e.maxMatchOff)
nextHash := hash6(cv, hashLog) // 0 -> 5
nextHash1 := hash6(cv>>8, hashLog) // 1 -> 6
nextHash2 := hash6(cv>>16, hashLog) // 2 -> 7
e.dictTable[nextHash] = tableEntry{
val: uint32(cv),
offset: i,
}
e.dictTable[nextHash1] = tableEntry{
val: uint32(cv >> 8),
offset: i + 1,
}
e.dictTable[nextHash2] = tableEntry{
val: uint32(cv >> 16),
offset: i + 2,
}
} }
e.hist = make([]byte, 0, l)
} else {
if cap(e.hist) < int(e.maxMatchOff*2) {
panic("unexpected buffer size")
}
// Move down
offset := int32(len(e.hist)) - e.maxMatchOff
copy(e.hist[0:e.maxMatchOff], e.hist[offset:])
e.cur += offset
e.hist = e.hist[:e.maxMatchOff]
}
}
s := int32(len(e.hist))
e.hist = append(e.hist, src...)
return s
}
// useBlock will replace the block with the provided one,
// but transfer recent offsets from the previous.
func (e *fastBase) UseBlock(enc *blockEnc) {
enc.reset(e.blk)
e.blk = enc
}
func (e *fastBase) matchlenNoHist(s, t int32, src []byte) int32 {
// Extend the match to be as long as possible.
return int32(matchLen(src[s:], src[t:]))
}
func (e *fastBase) matchlen(s, t int32, src []byte) int32 {
if debugAsserts {
if s < 0 {
err := fmt.Sprintf("s (%d) < 0", s)
panic(err)
}
if t < 0 {
err := fmt.Sprintf("s (%d) < 0", s)
panic(err)
}
if s-t > e.maxMatchOff {
err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff)
panic(err)
}
if len(src)-int(s) > maxCompressedBlockSize {
panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize))
} }
e.lastDictID = d.id
} }
// Extend the match to be as long as possible. e.cur = e.maxMatchOff
return int32(matchLen(src[s:], src[t:])) // Reset table to initial state
} copy(e.table[:], e.dictTable)
// Reset the encoding table.
func (e *fastBase) Reset(singleBlock bool) {
if e.blk == nil {
e.blk = &blockEnc{}
e.blk.init()
} else {
e.blk.reset(nil)
}
e.blk.initNewEncode()
if e.crc == nil {
e.crc = xxhash.New()
} else {
e.crc.Reset()
}
if !singleBlock && cap(e.hist) < int(e.maxMatchOff*2) {
l := e.maxMatchOff * 2
// Make it at least 1MB.
if l < 1<<20 {
l = 1 << 20
}
e.hist = make([]byte, 0, l)
}
// We offset current position so everything will be out of reach.
// If above reset line, history will be purged.
if e.cur < bufferReset {
e.cur += e.maxMatchOff + int32(len(e.hist))
}
e.hist = e.hist[:0]
} }

View File

@ -1,157 +0,0 @@
// Copyright 2019+ Klaus Post. All rights reserved.
// License information can be found in the LICENSE file.
// Based on work by Yann Collet, released under BSD License.
package zstd
/*
// encParams are not really used, just here for reference.
type encParams struct {
// largest match distance : larger == more compression, more memory needed during decompression
windowLog uint8
// fully searched segment : larger == more compression, slower, more memory (useless for fast)
chainLog uint8
// dispatch table : larger == faster, more memory
hashLog uint8
// < nb of searches : larger == more compression, slower
searchLog uint8
// < match length searched : larger == faster decompression, sometimes less compression
minMatch uint8
// acceptable match size for optimal parser (only) : larger == more compression, slower
targetLength uint32
// see ZSTD_strategy definition above
strategy strategy
}
// strategy defines the algorithm to use when generating sequences.
type strategy uint8
const (
// Compression strategies, listed from fastest to strongest
strategyFast strategy = iota + 1
strategyDfast
strategyGreedy
strategyLazy
strategyLazy2
strategyBtlazy2
strategyBtopt
strategyBtultra
strategyBtultra2
// note : new strategies _might_ be added in the future.
// Only the order (from fast to strong) is guaranteed
)
var defEncParams = [4][]encParams{
{ // "default" - for any srcSize > 256 KB
// W, C, H, S, L, TL, strat
{19, 12, 13, 1, 6, 1, strategyFast}, // base for negative levels
{19, 13, 14, 1, 7, 0, strategyFast}, // level 1
{20, 15, 16, 1, 6, 0, strategyFast}, // level 2
{21, 16, 17, 1, 5, 1, strategyDfast}, // level 3
{21, 18, 18, 1, 5, 1, strategyDfast}, // level 4
{21, 18, 19, 2, 5, 2, strategyGreedy}, // level 5
{21, 19, 19, 3, 5, 4, strategyGreedy}, // level 6
{21, 19, 19, 3, 5, 8, strategyLazy}, // level 7
{21, 19, 19, 3, 5, 16, strategyLazy2}, // level 8
{21, 19, 20, 4, 5, 16, strategyLazy2}, // level 9
{22, 20, 21, 4, 5, 16, strategyLazy2}, // level 10
{22, 21, 22, 4, 5, 16, strategyLazy2}, // level 11
{22, 21, 22, 5, 5, 16, strategyLazy2}, // level 12
{22, 21, 22, 5, 5, 32, strategyBtlazy2}, // level 13
{22, 22, 23, 5, 5, 32, strategyBtlazy2}, // level 14
{22, 23, 23, 6, 5, 32, strategyBtlazy2}, // level 15
{22, 22, 22, 5, 5, 48, strategyBtopt}, // level 16
{23, 23, 22, 5, 4, 64, strategyBtopt}, // level 17
{23, 23, 22, 6, 3, 64, strategyBtultra}, // level 18
{23, 24, 22, 7, 3, 256, strategyBtultra2}, // level 19
{25, 25, 23, 7, 3, 256, strategyBtultra2}, // level 20
{26, 26, 24, 7, 3, 512, strategyBtultra2}, // level 21
{27, 27, 25, 9, 3, 999, strategyBtultra2}, // level 22
},
{ // for srcSize <= 256 KB
// W, C, H, S, L, T, strat
{18, 12, 13, 1, 5, 1, strategyFast}, // base for negative levels
{18, 13, 14, 1, 6, 0, strategyFast}, // level 1
{18, 14, 14, 1, 5, 1, strategyDfast}, // level 2
{18, 16, 16, 1, 4, 1, strategyDfast}, // level 3
{18, 16, 17, 2, 5, 2, strategyGreedy}, // level 4.
{18, 18, 18, 3, 5, 2, strategyGreedy}, // level 5.
{18, 18, 19, 3, 5, 4, strategyLazy}, // level 6.
{18, 18, 19, 4, 4, 4, strategyLazy}, // level 7
{18, 18, 19, 4, 4, 8, strategyLazy2}, // level 8
{18, 18, 19, 5, 4, 8, strategyLazy2}, // level 9
{18, 18, 19, 6, 4, 8, strategyLazy2}, // level 10
{18, 18, 19, 5, 4, 12, strategyBtlazy2}, // level 11.
{18, 19, 19, 7, 4, 12, strategyBtlazy2}, // level 12.
{18, 18, 19, 4, 4, 16, strategyBtopt}, // level 13
{18, 18, 19, 4, 3, 32, strategyBtopt}, // level 14.
{18, 18, 19, 6, 3, 128, strategyBtopt}, // level 15.
{18, 19, 19, 6, 3, 128, strategyBtultra}, // level 16.
{18, 19, 19, 8, 3, 256, strategyBtultra}, // level 17.
{18, 19, 19, 6, 3, 128, strategyBtultra2}, // level 18.
{18, 19, 19, 8, 3, 256, strategyBtultra2}, // level 19.
{18, 19, 19, 10, 3, 512, strategyBtultra2}, // level 20.
{18, 19, 19, 12, 3, 512, strategyBtultra2}, // level 21.
{18, 19, 19, 13, 3, 999, strategyBtultra2}, // level 22.
},
{ // for srcSize <= 128 KB
// W, C, H, S, L, T, strat
{17, 12, 12, 1, 5, 1, strategyFast}, // base for negative levels
{17, 12, 13, 1, 6, 0, strategyFast}, // level 1
{17, 13, 15, 1, 5, 0, strategyFast}, // level 2
{17, 15, 16, 2, 5, 1, strategyDfast}, // level 3
{17, 17, 17, 2, 4, 1, strategyDfast}, // level 4
{17, 16, 17, 3, 4, 2, strategyGreedy}, // level 5
{17, 17, 17, 3, 4, 4, strategyLazy}, // level 6
{17, 17, 17, 3, 4, 8, strategyLazy2}, // level 7
{17, 17, 17, 4, 4, 8, strategyLazy2}, // level 8
{17, 17, 17, 5, 4, 8, strategyLazy2}, // level 9
{17, 17, 17, 6, 4, 8, strategyLazy2}, // level 10
{17, 17, 17, 5, 4, 8, strategyBtlazy2}, // level 11
{17, 18, 17, 7, 4, 12, strategyBtlazy2}, // level 12
{17, 18, 17, 3, 4, 12, strategyBtopt}, // level 13.
{17, 18, 17, 4, 3, 32, strategyBtopt}, // level 14.
{17, 18, 17, 6, 3, 256, strategyBtopt}, // level 15.
{17, 18, 17, 6, 3, 128, strategyBtultra}, // level 16.
{17, 18, 17, 8, 3, 256, strategyBtultra}, // level 17.
{17, 18, 17, 10, 3, 512, strategyBtultra}, // level 18.
{17, 18, 17, 5, 3, 256, strategyBtultra2}, // level 19.
{17, 18, 17, 7, 3, 512, strategyBtultra2}, // level 20.
{17, 18, 17, 9, 3, 512, strategyBtultra2}, // level 21.
{17, 18, 17, 11, 3, 999, strategyBtultra2}, // level 22.
},
{ // for srcSize <= 16 KB
// W, C, H, S, L, T, strat
{14, 12, 13, 1, 5, 1, strategyFast}, // base for negative levels
{14, 14, 15, 1, 5, 0, strategyFast}, // level 1
{14, 14, 15, 1, 4, 0, strategyFast}, // level 2
{14, 14, 15, 2, 4, 1, strategyDfast}, // level 3
{14, 14, 14, 4, 4, 2, strategyGreedy}, // level 4
{14, 14, 14, 3, 4, 4, strategyLazy}, // level 5.
{14, 14, 14, 4, 4, 8, strategyLazy2}, // level 6
{14, 14, 14, 6, 4, 8, strategyLazy2}, // level 7
{14, 14, 14, 8, 4, 8, strategyLazy2}, // level 8.
{14, 15, 14, 5, 4, 8, strategyBtlazy2}, // level 9.
{14, 15, 14, 9, 4, 8, strategyBtlazy2}, // level 10.
{14, 15, 14, 3, 4, 12, strategyBtopt}, // level 11.
{14, 15, 14, 4, 3, 24, strategyBtopt}, // level 12.
{14, 15, 14, 5, 3, 32, strategyBtultra}, // level 13.
{14, 15, 15, 6, 3, 64, strategyBtultra}, // level 14.
{14, 15, 15, 7, 3, 256, strategyBtultra}, // level 15.
{14, 15, 15, 5, 3, 48, strategyBtultra2}, // level 16.
{14, 15, 15, 6, 3, 128, strategyBtultra2}, // level 17.
{14, 15, 15, 7, 3, 256, strategyBtultra2}, // level 18.
{14, 15, 15, 8, 3, 256, strategyBtultra2}, // level 19.
{14, 15, 15, 8, 3, 512, strategyBtultra2}, // level 20.
{14, 15, 15, 9, 3, 512, strategyBtultra2}, // level 21.
{14, 15, 15, 10, 3, 999, strategyBtultra2}, // level 22.
},
}
*/

View File

@ -35,7 +35,7 @@ type encoder interface {
AppendCRC([]byte) []byte AppendCRC([]byte) []byte
WindowSize(size int) int32 WindowSize(size int) int32
UseBlock(*blockEnc) UseBlock(*blockEnc)
Reset(singleBlock bool) Reset(d *dict, singleBlock bool)
} }
type encoderState struct { type encoderState struct {
@ -83,8 +83,6 @@ func (e *Encoder) initialize() {
e.encoders = make(chan encoder, e.o.concurrent) e.encoders = make(chan encoder, e.o.concurrent)
for i := 0; i < e.o.concurrent; i++ { for i := 0; i < e.o.concurrent; i++ {
enc := e.o.encoder() enc := e.o.encoder()
// If not single block, history will be allocated on first use.
enc.Reset(true)
e.encoders <- enc e.encoders <- enc
} }
} }
@ -115,7 +113,7 @@ func (e *Encoder) Reset(w io.Writer) {
s.filling = s.filling[:0] s.filling = s.filling[:0]
s.current = s.current[:0] s.current = s.current[:0]
s.previous = s.previous[:0] s.previous = s.previous[:0]
s.encoder.Reset(false) s.encoder.Reset(e.o.dict, false)
s.headerWritten = false s.headerWritten = false
s.eofWritten = false s.eofWritten = false
s.fullFrameWritten = false s.fullFrameWritten = false
@ -200,8 +198,9 @@ func (e *Encoder) nextBlock(final bool) error {
WindowSize: uint32(s.encoder.WindowSize(0)), WindowSize: uint32(s.encoder.WindowSize(0)),
SingleSegment: false, SingleSegment: false,
Checksum: e.o.crc, Checksum: e.o.crc,
DictID: 0, DictID: e.o.dict.ID(),
} }
dst, err := fh.appendTo(tmp[:0]) dst, err := fh.appendTo(tmp[:0])
if err != nil { if err != nil {
return err return err
@ -281,7 +280,7 @@ func (e *Encoder) nextBlock(final bool) error {
// If we got the exact same number of literals as input, // If we got the exact same number of literals as input,
// assume the literals cannot be compressed. // assume the literals cannot be compressed.
if len(src) != len(blk.literals) || len(src) != e.o.blockSize { if len(src) != len(blk.literals) || len(src) != e.o.blockSize {
err = blk.encode(e.o.noEntropy, !e.o.allLitEntropy) err = blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy)
} }
switch err { switch err {
case errIncompressible: case errIncompressible:
@ -311,7 +310,13 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) {
if debug { if debug {
println("Using ReadFrom") println("Using ReadFrom")
} }
// Maybe handle stuff queued?
// Flush any current writes.
if len(e.state.filling) > 0 {
if err := e.nextBlock(false); err != nil {
return 0, err
}
}
e.state.filling = e.state.filling[:e.o.blockSize] e.state.filling = e.state.filling[:e.o.blockSize]
src := e.state.filling src := e.state.filling
for { for {
@ -328,7 +333,7 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) {
if debug { if debug {
println("ReadFrom: got EOF final block:", len(e.state.filling)) println("ReadFrom: got EOF final block:", len(e.state.filling))
} }
return n, e.nextBlock(true) return n, nil
default: default:
if debug { if debug {
println("ReadFrom: got error:", err) println("ReadFrom: got error:", err)
@ -450,7 +455,6 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
defer func() { defer func() {
// Release encoder reference to last block. // Release encoder reference to last block.
// If a non-single block is needed the encoder will reset again. // If a non-single block is needed the encoder will reset again.
enc.Reset(true)
e.encoders <- enc e.encoders <- enc
}() }()
// Use single segments when above minimum window and below 1MB. // Use single segments when above minimum window and below 1MB.
@ -463,7 +467,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
WindowSize: uint32(enc.WindowSize(len(src))), WindowSize: uint32(enc.WindowSize(len(src))),
SingleSegment: single, SingleSegment: single,
Checksum: e.o.crc, Checksum: e.o.crc,
DictID: 0, DictID: e.o.dict.ID(),
} }
// If less than 1MB, allocate a buffer up front. // If less than 1MB, allocate a buffer up front.
@ -477,13 +481,18 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
// If we can do everything in one block, prefer that. // If we can do everything in one block, prefer that.
if len(src) <= maxCompressedBlockSize { if len(src) <= maxCompressedBlockSize {
enc.Reset(e.o.dict, true)
// Slightly faster with no history and everything in one block. // Slightly faster with no history and everything in one block.
if e.o.crc { if e.o.crc {
_, _ = enc.CRC().Write(src) _, _ = enc.CRC().Write(src)
} }
blk := enc.Block() blk := enc.Block()
blk.last = true blk.last = true
enc.EncodeNoHist(blk, src) if e.o.dict == nil {
enc.EncodeNoHist(blk, src)
} else {
enc.Encode(blk, src)
}
// If we got the exact same number of literals as input, // If we got the exact same number of literals as input,
// assume the literals cannot be compressed. // assume the literals cannot be compressed.
@ -492,7 +501,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
if len(blk.literals) != len(src) || len(src) != e.o.blockSize { if len(blk.literals) != len(src) || len(src) != e.o.blockSize {
// Output directly to dst // Output directly to dst
blk.output = dst blk.output = dst
err = blk.encode(e.o.noEntropy, !e.o.allLitEntropy) err = blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy)
} }
switch err { switch err {
@ -508,7 +517,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
} }
blk.output = oldout blk.output = oldout
} else { } else {
enc.Reset(false) enc.Reset(e.o.dict, false)
blk := enc.Block() blk := enc.Block()
for len(src) > 0 { for len(src) > 0 {
todo := src todo := src
@ -519,7 +528,6 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
if e.o.crc { if e.o.crc {
_, _ = enc.CRC().Write(todo) _, _ = enc.CRC().Write(todo)
} }
blk.reset(nil)
blk.pushOffsets() blk.pushOffsets()
enc.Encode(blk, todo) enc.Encode(blk, todo)
if len(src) == 0 { if len(src) == 0 {
@ -529,7 +537,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
// If we got the exact same number of literals as input, // If we got the exact same number of literals as input,
// assume the literals cannot be compressed. // assume the literals cannot be compressed.
if len(blk.literals) != len(todo) || len(todo) != e.o.blockSize { if len(blk.literals) != len(todo) || len(todo) != e.o.blockSize {
err = blk.encode(e.o.noEntropy, !e.o.allLitEntropy) err = blk.encode(todo, e.o.noEntropy, !e.o.allLitEntropy)
} }
switch err { switch err {
@ -544,6 +552,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte {
default: default:
panic(err) panic(err)
} }
blk.reset(nil)
} }
} }
if e.o.crc { if e.o.crc {

View File

@ -24,6 +24,7 @@ type encoderOptions struct {
allLitEntropy bool allLitEntropy bool
customWindow bool customWindow bool
customALEntropy bool customALEntropy bool
dict *dict
} }
func (o *encoderOptions) setDefault() { func (o *encoderOptions) setDefault() {
@ -265,3 +266,16 @@ func WithSingleSegment(b bool) EOption {
return nil return nil
} }
} }
// WithEncoderDict allows to register a dictionary that will be used for the encode.
// The encoder *may* choose to use no dictionary instead for certain payloads.
func WithEncoderDict(dict []byte) EOption {
return func(o *encoderOptions) error {
d, err := loadDict(dict)
if err != nil {
return err
}
o.dict = d
return nil
}
}

View File

@ -5,6 +5,7 @@
package zstd package zstd
import ( import (
"encoding/binary"
"fmt" "fmt"
"io" "io"
"math" "math"
@ -16,7 +17,7 @@ type frameHeader struct {
WindowSize uint32 WindowSize uint32
SingleSegment bool SingleSegment bool
Checksum bool Checksum bool
DictID uint32 // Not stored. DictID uint32
} }
const maxHeaderSize = 14 const maxHeaderSize = 14
@ -30,6 +31,24 @@ func (f frameHeader) appendTo(dst []byte) ([]byte, error) {
if f.SingleSegment { if f.SingleSegment {
fhd |= 1 << 5 fhd |= 1 << 5
} }
var dictIDContent []byte
if f.DictID > 0 {
var tmp [4]byte
if f.DictID < 256 {
fhd |= 1
tmp[0] = uint8(f.DictID)
dictIDContent = tmp[:1]
} else if f.DictID < 1<<16 {
fhd |= 2
binary.LittleEndian.PutUint16(tmp[:2], uint16(f.DictID))
dictIDContent = tmp[:2]
} else {
fhd |= 3
binary.LittleEndian.PutUint32(tmp[:4], f.DictID)
dictIDContent = tmp[:4]
}
}
var fcs uint8 var fcs uint8
if f.ContentSize >= 256 { if f.ContentSize >= 256 {
fcs++ fcs++
@ -40,6 +59,7 @@ func (f frameHeader) appendTo(dst []byte) ([]byte, error) {
if f.ContentSize >= 0xffffffff { if f.ContentSize >= 0xffffffff {
fcs++ fcs++
} }
fhd |= fcs << 6 fhd |= fcs << 6
dst = append(dst, fhd) dst = append(dst, fhd)
@ -48,7 +68,9 @@ func (f frameHeader) appendTo(dst []byte) ([]byte, error) {
windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3 windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3
dst = append(dst, uint8(windowLog)) dst = append(dst, uint8(windowLog))
} }
if f.DictID > 0 {
dst = append(dst, dictIDContent...)
}
switch fcs { switch fcs {
case 0: case 0:
if f.SingleSegment { if f.SingleSegment {

View File

@ -37,7 +37,7 @@ func (h *history) reset() {
} }
h.decoders = sequenceDecs{} h.decoders = sequenceDecs{}
if h.huffTree != nil { if h.huffTree != nil {
if h.dict == nil || h.dict.litDec != h.huffTree { if h.dict == nil || h.dict.litEnc != h.huffTree {
huffDecoderPool.Put(h.huffTree) huffDecoderPool.Put(h.huffTree)
} }
} }
@ -55,7 +55,7 @@ func (h *history) setDict(dict *dict) {
h.decoders.offsets = dict.ofDec h.decoders.offsets = dict.ofDec
h.decoders.matchLengths = dict.mlDec h.decoders.matchLengths = dict.mlDec
h.recentOffsets = dict.offsets h.recentOffsets = dict.offsets
h.huffTree = dict.litDec h.huffTree = dict.litEnc
} }
// append bytes to history. // append bytes to history.

View File

@ -196,6 +196,10 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error {
s.literals = s.literals[ll:] s.literals = s.literals[ll:]
out := s.out out := s.out
if mo == 0 && ml > 0 {
return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml)
}
if mo > len(s.out)+len(hist) || mo > s.windowSize { if mo > len(s.out)+len(hist) || mo > s.windowSize {
if len(s.dict) == 0 { if len(s.dict) == 0 {
return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(s.out)+len(hist)) return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(s.out)+len(hist))
@ -218,10 +222,6 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error {
} }
} }
if mo == 0 && ml > 0 {
return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml)
}
// Copy from history. // Copy from history.
// TODO: Blocks without history could be made to ignore this completely. // TODO: Blocks without history could be made to ignore this completely.
if v := mo - len(s.out); v > 0 { if v := mo - len(s.out); v > 0 {

View File

@ -111,7 +111,7 @@ func (r *SnappyConverter) Convert(in io.Reader, w io.Writer) (int64, error) {
// Add empty last block // Add empty last block
r.block.reset(nil) r.block.reset(nil)
r.block.last = true r.block.last = true
err := r.block.encodeLits(false) err := r.block.encodeLits(r.block.literals, false)
if err != nil { if err != nil {
return written, err return written, err
} }
@ -178,7 +178,7 @@ func (r *SnappyConverter) Convert(in io.Reader, w io.Writer) (int64, error) {
r.err = ErrSnappyCorrupt r.err = ErrSnappyCorrupt
return written, r.err return written, r.err
} }
err = r.block.encode(false, false) err = r.block.encode(nil, false, false)
switch err { switch err {
case errIncompressible: case errIncompressible:
r.block.popOffsets() r.block.popOffsets()
@ -188,7 +188,7 @@ func (r *SnappyConverter) Convert(in io.Reader, w io.Writer) (int64, error) {
println("snappy.Decode:", err) println("snappy.Decode:", err)
return written, err return written, err
} }
err = r.block.encodeLits(false) err = r.block.encodeLits(r.block.literals, false)
if err != nil { if err != nil {
return written, err return written, err
} }
@ -235,7 +235,7 @@ func (r *SnappyConverter) Convert(in io.Reader, w io.Writer) (int64, error) {
r.err = ErrSnappyCorrupt r.err = ErrSnappyCorrupt
return written, r.err return written, r.err
} }
err := r.block.encodeLits(false) err := r.block.encodeLits(r.block.literals, false)
if err != nil { if err != nil {
return written, err return written, err
} }

4
vendor/modules.txt vendored
View File

@ -159,7 +159,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process github.com/containers/psgo/internal/process
# github.com/containers/storage v1.23.4 # github.com/containers/storage v1.23.5
github.com/containers/storage github.com/containers/storage
github.com/containers/storage/drivers github.com/containers/storage/drivers
github.com/containers/storage/drivers/aufs github.com/containers/storage/drivers/aufs
@ -330,7 +330,7 @@ github.com/inconshreveable/mousetrap
github.com/ishidawataru/sctp github.com/ishidawataru/sctp
# github.com/json-iterator/go v1.1.10 # github.com/json-iterator/go v1.1.10
github.com/json-iterator/go github.com/json-iterator/go
# github.com/klauspost/compress v1.10.11 # github.com/klauspost/compress v1.11.0
github.com/klauspost/compress/flate github.com/klauspost/compress/flate
github.com/klauspost/compress/fse github.com/klauspost/compress/fse
github.com/klauspost/compress/huff0 github.com/klauspost/compress/huff0