| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510 |
- // 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"
- "fmt"
- "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 encodeBlockBetterGo(dst, src []byte) (d int) {
- // 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 len(src) < minNonLiteralBlockSize {
- return 0
- }
- // Initialize the hash tables.
- const (
- // Long hash matches.
- lTableBits = 17
- maxLTableSize = 1 << lTableBits
- // Short hash matches.
- sTableBits = 14
- maxSTableSize = 1 << sTableBits
- )
- var lTable [maxLTableSize]uint32
- var sTable [maxSTableSize]uint32
- // Bail if we can't compress to at least this.
- dstLimit := len(src) - len(src)>>5 - 6
- // 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 initialize repeat to 0, so we never match on first attempt
- repeat := 0
- for {
- candidateL := 0
- nextS := 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)
- valLong := load64(src, candidateL)
- valShort := load64(src, candidateS)
- // If long matches at least 8 bytes, use that.
- if cv == valLong {
- break
- }
- if cv == valShort {
- candidateL = candidateS
- break
- }
- // Check repeat at offset checkRep.
- const checkRep = 1
- // Minimum length of a repeat. Tested with various values.
- // While 4-5 offers improvements in some, 6 reduces
- // regressions significantly.
- const wantRepeatBytes = 6
- const repeatMask = ((1 << (wantRepeatBytes * 8)) - 1) << (8 * checkRep)
- if false && repeat > 0 && cv&repeatMask == load64(src, s-repeat)&repeatMask {
- 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 + wantRepeatBytes + checkRep
- s += wantRepeatBytes + checkRep
- for s < len(src) {
- if len(src)-s < 8 {
- if src[s] == src[candidate] {
- s++
- candidate++
- continue
- }
- break
- }
- if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidate += 8
- }
- // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset.
- d += emitRepeat(dst[d:], repeat, s-base)
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- // Index in-between
- index0 := base + 1
- index1 := s - 2
- for index0 < index1 {
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 2
- index1 -= 2
- }
- cv = load64(src, s)
- continue
- }
- // Long likely matches 7, so take that.
- if uint32(cv) == uint32(valLong) {
- break
- }
- // Check our short candidate
- if uint32(cv) == uint32(valShort) {
- // 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) {
- if len(src)-s < 8 {
- if src[s] == src[candidateL] {
- s++
- candidateL++
- continue
- }
- break
- }
- 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 && repeat != offset {
- // Bail if the match is equal or worse to the encoding.
- s = nextS + 1
- if s >= sLimit {
- goto emitRemainder
- }
- cv = load64(src, s)
- continue
- }
- d += emitLiteral(dst[d:], src[nextEmit:base])
- if repeat == offset {
- d += emitRepeat(dst[d:], offset, s-base)
- } else {
- d += emitCopy(dst[d:], offset, s-base)
- repeat = offset
- }
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- // lTable could be postponed, but very minor difference.
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // Index large values sparsely in between.
- // We do two starting from different offsets for speed.
- index2 := (index0 + index1 + 1) >> 1
- for index2 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
- lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
- index0 += 2
- index2 += 2
- }
- }
- 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
- }
- // encodeBlockBetterSnappyGo 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 encodeBlockBetterSnappyGo(dst, src []byte) (d int) {
- // 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 len(src) < minNonLiteralBlockSize {
- return 0
- }
- // 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
- // Bail if we can't compress to at least this.
- dstLimit := len(src) - len(src)>>5 - 6
- // 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 initialize repeat to 0, so we never match on first attempt
- repeat := 0
- const maxSkip = 100
- for {
- candidateL := 0
- nextS := 0
- for {
- // Next src position to check
- nextS = min(s+(s-nextEmit)>>7+1, s+maxSkip)
- 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)
- 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) {
- if len(src)-s < 8 {
- if src[s] == src[candidateL] {
- s++
- candidateL++
- continue
- }
- break
- }
- 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 && repeat != offset {
- // Bail if the match is equal or worse to the encoding.
- s = nextS + 1
- if s >= sLimit {
- goto emitRemainder
- }
- cv = load64(src, s)
- continue
- }
- d += emitLiteral(dst[d:], src[nextEmit:base])
- d += emitCopyNoRepeat(dst[d:], offset, s-base)
- repeat = offset
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // Index large values sparsely in between.
- // We do two starting from different offsets for speed.
- index2 := (index0 + index1 + 1) >> 1
- for index2 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
- lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
- index0 += 2
- index2 += 2
- }
- }
- 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 encodeBlockBetterGo64K(dst, src []byte) (d int) {
- // 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 len(src) < minNonLiteralBlockSize {
- return 0
- }
- // Initialize the hash tables.
- // Use smaller tables for smaller blocks
- const (
- // Long hash matches.
- lTableBits = 16
- maxLTableSize = 1 << lTableBits
- // Short hash matches.
- sTableBits = 13
- maxSTableSize = 1 << sTableBits
- )
- var lTable [maxLTableSize]uint16
- var sTable [maxSTableSize]uint16
- // Bail if we can't compress to at least this.
- dstLimit := len(src) - len(src)>>5 - 6
- // 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 initialize repeat to 0, so we never match on first attempt
- repeat := 0
- for {
- candidateL := 0
- nextS := 0
- for {
- // Next src position to check
- nextS = s + (s-nextEmit)>>6 + 1
- if nextS > sLimit {
- goto emitRemainder
- }
- hashL := hash7(cv, lTableBits)
- hashS := hash4(cv, sTableBits)
- candidateL = int(lTable[hashL])
- candidateS := int(sTable[hashS])
- lTable[hashL] = uint16(s)
- sTable[hashS] = uint16(s)
- valLong := load64(src, candidateL)
- valShort := load64(src, candidateS)
- // If long matches at least 8 bytes, use that.
- if cv == valLong {
- break
- }
- if cv == valShort {
- candidateL = candidateS
- break
- }
- // Check repeat at offset checkRep.
- const checkRep = 1
- // Minimum length of a repeat. Tested with various values.
- // While 4-5 offers improvements in some, 6 reduces
- // regressions significantly.
- const wantRepeatBytes = 6
- const repeatMask = ((1 << (wantRepeatBytes * 8)) - 1) << (8 * checkRep)
- if false && repeat > 0 && cv&repeatMask == load64(src, s-repeat)&repeatMask {
- 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 + wantRepeatBytes + checkRep
- s += wantRepeatBytes + checkRep
- for s < len(src) {
- if len(src)-s < 8 {
- if src[s] == src[candidate] {
- s++
- candidate++
- continue
- }
- break
- }
- if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidate += 8
- }
- // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset.
- d += emitRepeat(dst[d:], repeat, s-base)
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- // Index in-between
- index0 := base + 1
- index1 := s - 2
- for index0 < index1 {
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint16(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint16(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint16(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint16(index1 + 1)
- index0 += 2
- index1 -= 2
- }
- cv = load64(src, s)
- continue
- }
- // Long likely matches 7, so take that.
- if uint32(cv) == uint32(valLong) {
- break
- }
- // Check our short candidate
- if uint32(cv) == uint32(valShort) {
- // Try a long candidate at s+1
- hashL = hash7(cv>>8, lTableBits)
- candidateL = int(lTable[hashL])
- lTable[hashL] = uint16(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) {
- if len(src)-s < 8 {
- if src[s] == src[candidateL] {
- s++
- candidateL++
- continue
- }
- break
- }
- if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidateL += 8
- }
- d += emitLiteral(dst[d:], src[nextEmit:base])
- if repeat == offset {
- d += emitRepeat(dst[d:], offset, s-base)
- } else {
- d += emitCopy(dst[d:], offset, s-base)
- repeat = offset
- }
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint16(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint16(index0 + 1)
- // lTable could be postponed, but very minor difference.
- lTable[hash7(cv1, lTableBits)] = uint16(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint16(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // Index large values sparsely in between.
- // We do two starting from different offsets for speed.
- index2 := (index0 + index1 + 1) >> 1
- for index2 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint16(index0)
- lTable[hash7(load64(src, index2), lTableBits)] = uint16(index2)
- index0 += 2
- index2 += 2
- }
- }
- 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
- }
- // encodeBlockBetterSnappyGo 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 encodeBlockBetterSnappyGo64K(dst, src []byte) (d int) {
- // 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 len(src) < minNonLiteralBlockSize {
- return 0
- }
- // Initialize the hash tables.
- // Use smaller tables for smaller blocks
- const (
- // Long hash matches.
- lTableBits = 15
- maxLTableSize = 1 << lTableBits
- // Short hash matches.
- sTableBits = 13
- maxSTableSize = 1 << sTableBits
- )
- var lTable [maxLTableSize]uint16
- var sTable [maxSTableSize]uint16
- // Bail if we can't compress to at least this.
- dstLimit := len(src) - len(src)>>5 - 6
- // 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)
- const maxSkip = 100
- for {
- candidateL := 0
- nextS := 0
- for {
- // Next src position to check
- nextS = min(s+(s-nextEmit)>>6+1, s+maxSkip)
- if nextS > sLimit {
- goto emitRemainder
- }
- hashL := hash7(cv, lTableBits)
- hashS := hash4(cv, sTableBits)
- candidateL = int(lTable[hashL])
- candidateS := int(sTable[hashS])
- lTable[hashL] = uint16(s)
- sTable[hashS] = uint16(s)
- 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] = uint16(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) {
- if len(src)-s < 8 {
- if src[s] == src[candidateL] {
- s++
- candidateL++
- continue
- }
- break
- }
- if diff := load64(src, s) ^ load64(src, candidateL); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidateL += 8
- }
- d += emitLiteral(dst[d:], src[nextEmit:base])
- d += emitCopyNoRepeat(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 short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint16(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint16(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint16(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint16(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // Index large values sparsely in between.
- // We do two starting from different offsets for speed.
- index2 := (index0 + index1 + 1) >> 1
- for index2 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint16(index0)
- lTable[hash7(load64(src, index2), lTableBits)] = uint16(index2)
- index0 += 2
- index2 += 2
- }
- }
- 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
- }
- // encodeBlockBetterDict 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 encodeBlockBetterDict(dst, src []byte, dict *Dict) (d int) {
- // 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.
- // Initialize the hash tables.
- const (
- // Long hash matches.
- lTableBits = 17
- maxLTableSize = 1 << lTableBits
- // Short hash matches.
- sTableBits = 14
- maxSTableSize = 1 << sTableBits
- maxAhead = 8 // maximum bytes ahead without checking sLimit
- debug = false
- )
- sLimit := len(src) - inputMargin
- if sLimit > MaxDictSrcOffset-maxAhead {
- sLimit = MaxDictSrcOffset - maxAhead
- }
- if len(src) < minNonLiteralBlockSize {
- return 0
- }
- dict.initBetter()
- var lTable [maxLTableSize]uint32
- var sTable [maxSTableSize]uint32
- // Bail if we can't compress to at least this.
- dstLimit := len(src) - len(src)>>5 - 6
- // 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 := 0
- cv := load64(src, s)
- // We initialize repeat to 0, so we never match on first attempt
- repeat := len(dict.dict) - dict.repeat
- // While in dict
- searchDict:
- for {
- candidateL := 0
- nextS := 0
- for {
- // Next src position to check
- nextS = s + (s-nextEmit)>>7 + 1
- if nextS > sLimit {
- break searchDict
- }
- hashL := hash7(cv, lTableBits)
- hashS := hash4(cv, sTableBits)
- candidateL = int(lTable[hashL])
- candidateS := int(sTable[hashS])
- dictL := int(dict.betterTableLong[hashL])
- dictS := int(dict.betterTableShort[hashS])
- lTable[hashL] = uint32(s)
- sTable[hashS] = uint32(s)
- valLong := load64(src, candidateL)
- valShort := load64(src, candidateS)
- // If long matches at least 8 bytes, use that.
- if s != 0 {
- if cv == valLong {
- goto emitMatch
- }
- if cv == valShort {
- candidateL = candidateS
- goto emitMatch
- }
- }
- // Check dict repeat.
- if repeat >= s+4 {
- candidate := len(dict.dict) - repeat + s
- if candidate > 0 && 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--
- }
- 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
- }
- // Index in-between
- index0 := base + 1
- index1 := s - 2
- cv = load64(src, s)
- for index0 < index1 {
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 2
- index1 -= 2
- }
- continue
- }
- }
- // Don't try to find match at s==0
- if s == 0 {
- cv = load64(src, nextS)
- s = nextS
- continue
- }
- // Long likely matches 7, so take that.
- if uint32(cv) == uint32(valLong) {
- goto emitMatch
- }
- // Long dict...
- if uint32(cv) == load32(dict.dict, dictL) {
- candidateL = dictL
- goto emitDict
- }
- // Check our short candidate
- if uint32(cv) == uint32(valShort) {
- // 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++
- goto emitMatch
- }
- // Use our short candidate.
- candidateL = candidateS
- goto emitMatch
- }
- if uint32(cv) == load32(dict.dict, dictS) {
- // 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++
- goto emitMatch
- }
- candidateL = dictS
- goto emitDict
- }
- cv = load64(src, nextS)
- s = nextS
- }
- emitDict:
- {
- if debug {
- if load32(dict.dict, candidateL) != load32(src, s) {
- panic("dict emit mismatch")
- }
- }
- // Extend backwards.
- // The top bytes will be rechecked to get the full match.
- for candidateL > 0 && s > nextEmit && dict.dict[candidateL-1] == src[s-1] {
- candidateL--
- 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
- offset := s + (len(dict.dict)) - candidateL
- // Extend the 4-byte match as long as possible.
- s += 4
- candidateL += 4
- for s <= len(src)-8 && len(dict.dict)-candidateL >= 8 {
- if diff := load64(src, s) ^ load64(dict.dict, candidateL); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidateL += 8
- }
- if repeat == offset {
- if debug {
- fmt.Println("emitted dict repeat, length", s-base, "offset:", offset, "s:", s, "dict offset:", candidateL)
- }
- d += emitRepeat(dst[d:], offset, s-base)
- } else {
- if debug {
- fmt.Println("emitted dict copy, length", s-base, "offset:", offset, "s:", s, "dict offset:", candidateL)
- }
- // Matches longer than 64 are split.
- if s <= sLimit || s-base < 8 {
- d += emitCopy(dst[d:], offset, s-base)
- } else {
- // Split to ensure we don't start a copy within next block.
- d += emitCopy(dst[d:], offset, 4)
- d += emitRepeat(dst[d:], offset, s-base-4)
- }
- repeat = offset
- }
- if false {
- // Validate match.
- if s <= candidateL {
- panic("s <= candidate")
- }
- a := src[base:s]
- b := dict.dict[base-repeat : base-repeat+(s-base)]
- if !bytes.Equal(a, b) {
- panic("mismatch")
- }
- }
- nextEmit = s
- if s >= sLimit {
- break searchDict
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // index every second long in between.
- for index0 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
- lTable[hash7(load64(src, index1), lTableBits)] = uint32(index1)
- index0 += 2
- index1 -= 2
- }
- }
- continue
- }
- emitMatch:
- // 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) {
- if len(src)-s < 8 {
- if src[s] == src[candidateL] {
- s++
- candidateL++
- continue
- }
- break
- }
- 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 && repeat != offset {
- // Bail if the match is equal or worse to the encoding.
- s = nextS + 1
- if s >= sLimit {
- goto emitRemainder
- }
- cv = load64(src, s)
- continue
- }
- d += emitLiteral(dst[d:], src[nextEmit:base])
- if debug && nextEmit != s {
- fmt.Println("emitted ", s-nextEmit, "literals")
- }
- if repeat == offset {
- if debug {
- fmt.Println("emitted match repeat, length", s-base, "offset:", offset, "s:", s)
- }
- d += emitRepeat(dst[d:], offset, s-base)
- } else {
- if debug {
- fmt.Println("emitted match copy, length", s-base, "offset:", offset, "s:", s)
- }
- d += emitCopy(dst[d:], offset, s-base)
- repeat = offset
- }
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // Index large values sparsely in between.
- // We do two starting from different offsets for speed.
- index2 := (index0 + index1 + 1) >> 1
- for index2 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
- lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
- index0 += 2
- index2 += 2
- }
- }
- // Search without dict:
- if repeat > s {
- repeat = 0
- }
- // No more dict
- sLimit = len(src) - inputMargin
- if s >= sLimit {
- goto emitRemainder
- }
- cv = load64(src, s)
- if debug {
- fmt.Println("now", s, "->", sLimit, "out:", d, "left:", len(src)-s, "nextemit:", nextEmit, "dstLimit:", dstLimit, "s:", s)
- }
- for {
- candidateL := 0
- nextS := 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)
- valLong := load64(src, candidateL)
- valShort := load64(src, candidateS)
- // If long matches at least 8 bytes, use that.
- if cv == valLong {
- break
- }
- if cv == valShort {
- candidateL = candidateS
- break
- }
- // Check repeat at offset checkRep.
- const checkRep = 1
- // Minimum length of a repeat. Tested with various values.
- // While 4-5 offers improvements in some, 6 reduces
- // regressions significantly.
- const wantRepeatBytes = 6
- const repeatMask = ((1 << (wantRepeatBytes * 8)) - 1) << (8 * checkRep)
- if false && repeat > 0 && cv&repeatMask == load64(src, s-repeat)&repeatMask {
- 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 + wantRepeatBytes + checkRep
- s += wantRepeatBytes + checkRep
- for s < len(src) {
- if len(src)-s < 8 {
- if src[s] == src[candidate] {
- s++
- candidate++
- continue
- }
- break
- }
- if diff := load64(src, s) ^ load64(src, candidate); diff != 0 {
- s += bits.TrailingZeros64(diff) >> 3
- break
- }
- s += 8
- candidate += 8
- }
- // same as `add := emitCopy(dst[d:], repeat, s-base)` but skips storing offset.
- d += emitRepeat(dst[d:], repeat, s-base)
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- // Index in-between
- index0 := base + 1
- index1 := s - 2
- for index0 < index1 {
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 2
- index1 -= 2
- }
- cv = load64(src, s)
- continue
- }
- // Long likely matches 7, so take that.
- if uint32(cv) == uint32(valLong) {
- break
- }
- // Check our short candidate
- if uint32(cv) == uint32(valShort) {
- // 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) {
- if len(src)-s < 8 {
- if src[s] == src[candidateL] {
- s++
- candidateL++
- continue
- }
- break
- }
- 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 && repeat != offset {
- // Bail if the match is equal or worse to the encoding.
- s = nextS + 1
- if s >= sLimit {
- goto emitRemainder
- }
- cv = load64(src, s)
- continue
- }
- d += emitLiteral(dst[d:], src[nextEmit:base])
- if repeat == offset {
- d += emitRepeat(dst[d:], offset, s-base)
- } else {
- d += emitCopy(dst[d:], offset, s-base)
- repeat = offset
- }
- nextEmit = s
- if s >= sLimit {
- goto emitRemainder
- }
- if d > dstLimit {
- // Do we have space for more, if not bail.
- return 0
- }
- // Index short & long
- index0 := base + 1
- index1 := s - 2
- cv0 := load64(src, index0)
- cv1 := load64(src, index1)
- lTable[hash7(cv0, lTableBits)] = uint32(index0)
- sTable[hash4(cv0>>8, sTableBits)] = uint32(index0 + 1)
- lTable[hash7(cv1, lTableBits)] = uint32(index1)
- sTable[hash4(cv1>>8, sTableBits)] = uint32(index1 + 1)
- index0 += 1
- index1 -= 1
- cv = load64(src, s)
- // Index large values sparsely in between.
- // We do two starting from different offsets for speed.
- index2 := (index0 + index1 + 1) >> 1
- for index2 < index1 {
- lTable[hash7(load64(src, index0), lTableBits)] = uint32(index0)
- lTable[hash7(load64(src, index2), lTableBits)] = uint32(index2)
- index0 += 2
- index2 += 2
- }
- }
- 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
- }
|