| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- package matchfinder
- import "encoding/binary"
- const (
- zmTableBits = 15
- zmTableSize = 1 << zmTableBits
- zmLongTableBits = 17
- zmLongTableSize = 1 << zmLongTableBits
- )
- // ZM is a MatchFinder that combines the cache tables of ZDFast with the
- // overlap-based parsing of M4.
- type ZM struct {
- MaxDistance int
- history []byte
- table [zmTableSize]tableEntry
- longTable [zmLongTableSize]tableEntry
- }
- func (z *ZM) Reset() {
- z.table = [zmTableSize]tableEntry{}
- z.longTable = [zmLongTableSize]tableEntry{}
- z.history = z.history[:0]
- }
- func (z *ZM) FindMatches(dst []Match, src []byte) []Match {
- if z.MaxDistance == 0 {
- z.MaxDistance = 1 << 16
- }
- if len(z.history) > z.MaxDistance*2 {
- delta := len(z.history) - z.MaxDistance
- copy(z.history, z.history[delta:])
- z.history = z.history[:z.MaxDistance]
- for i := range z.table {
- v := z.table[i].offset
- v -= int32(delta)
- if v < 0 {
- z.table[i] = tableEntry{}
- } else {
- z.table[i].offset = v
- }
- }
- for i := range z.longTable {
- v := z.longTable[i].offset
- v -= int32(delta)
- if v < 0 {
- z.longTable[i] = tableEntry{}
- } else {
- z.longTable[i].offset = v
- }
- }
- }
- if len(src) < 16 {
- return append(dst, Match{
- Unmatched: len(src),
- })
- }
- e := matchEmitter{
- Dst: dst,
- NextEmit: len(z.history),
- }
- z.history = append(z.history, src...)
- src = z.history
- // matches stores the matches that have been found but not emitted,
- // in reverse order. (matches[0] is the most recent one.)
- var matches [3]absoluteMatch
- sLimit := int32(len(src)) - 10
- mainLoop:
- for {
- // Search for a match, starting after the last match emitted.
- s := int32(e.NextEmit)
- if s > sLimit {
- break mainLoop
- }
- // t will contain the match offset when we find one.
- var t int32
- cv := binary.LittleEndian.Uint64(src[s:])
- for {
- nextHashL := z.hashLong(cv)
- nextHashS := z.hashShort(cv)
- candidateL := z.longTable[nextHashL]
- candidateS := z.table[nextHashS]
- entry := tableEntry{offset: s, val: uint32(cv)}
- z.longTable[nextHashL] = entry
- z.table[nextHashS] = entry
- // Look for a repeat match one byte after the current position.
- if len(e.Dst) > 0 {
- prevDistance := int32(e.Dst[len(e.Dst)-1].Distance)
- if prevDistance != 0 {
- repIndex := s - prevDistance + 1
- if repIndex >= 0 && binary.LittleEndian.Uint32(src[repIndex:]) == uint32(cv>>8) {
- // There is a repeated match at s+2.
- s++
- t = repIndex
- break
- }
- }
- }
- if candidateL.offset < s && s-candidateL.offset < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
- binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
- // There is a long match at s.
- t = candidateL.offset
- break
- }
- if candidateS.offset < s && s-candidateS.offset < int32(z.MaxDistance) && uint32(cv) == candidateS.val &&
- binary.LittleEndian.Uint32(src[candidateS.offset:]) == uint32(cv) {
- // There is a regular match at s.
- // See if we can find a long match at s+1.
- cv := binary.LittleEndian.Uint64(src[s+1:])
- nextHashL = z.hashLong(cv)
- candidateL = z.longTable[nextHashL]
- coffsetL := s - candidateL.offset + 1
- z.longTable[nextHashL] = tableEntry{offset: s + 1, val: uint32(cv)}
- if candidateL.offset < s+1 && coffsetL < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
- binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
- // We found a long match at s+1, so we'll use that instead
- // of the regular match at s.
- t = candidateL.offset
- s++
- break
- }
- t = candidateS.offset
- break
- }
- s += 1 + ((s - int32(e.NextEmit)) >> 7)
- if s > sLimit {
- break mainLoop
- }
- cv = binary.LittleEndian.Uint64(src[s:])
- }
- currentMatch := extendMatch2(src, int(s), int(t), e.NextEmit)
- matches[0] = currentMatch
- // Store some table entries after s.
- index0 := s + 1
- cv0 := binary.LittleEndian.Uint64(src[index0:])
- te0 := tableEntry{offset: index0, val: uint32(cv0)}
- z.longTable[z.hashLong(cv0)] = te0
- cv0 >>= 8
- te0.offset++
- te0.val = uint32(cv0)
- z.table[z.hashShort(cv0)] = te0
- // We have a match in matches[0].
- // Now look for overlapping matches.
- for {
- if matches[0].End > int(sLimit) {
- break
- }
- s = int32(max(matches[0].Start+2, matches[0].End-6))
- cv = binary.LittleEndian.Uint64(src[s:])
- nextHashL := z.hashLong(cv)
- nextHashS := z.hashShort(cv)
- candidateL := z.longTable[nextHashL]
- candidateS := z.table[nextHashS]
- entry := tableEntry{offset: s, val: uint32(cv)}
- z.longTable[nextHashL] = entry
- z.table[nextHashS] = entry
- t = -1
- if candidateL.offset < s && s-candidateL.offset < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
- binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
- // There is a long match at s.
- t = candidateL.offset
- } else if candidateS.offset < s && s-candidateS.offset < int32(z.MaxDistance) && uint32(cv) == candidateS.val &&
- binary.LittleEndian.Uint32(src[candidateS.offset:]) == uint32(cv) {
- // There is a regular match at s.
- t = candidateS.offset
- // See if we can find a long match at s+1.
- cv := binary.LittleEndian.Uint64(src[s+1:])
- nextHashL = z.hashLong(cv)
- candidateL = z.longTable[nextHashL]
- coffsetL := s - candidateL.offset + 1
- z.longTable[nextHashL] = tableEntry{offset: s + 1, val: uint32(cv)}
- if candidateL.offset < s+1 && coffsetL < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
- binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
- // We found a long match at s+1, so we'll use that instead
- // of the regular match at s.
- t = candidateL.offset
- s++
- }
- }
- if t == -1 {
- // No overlapping match was found.
- break
- }
- newMatch := extendMatch2(src, int(s), int(t), e.NextEmit)
- if newMatch.End-newMatch.Start <= matches[0].End-matches[0].Start {
- // The new match isn't longer than the old one, so we break out of the loop
- // of looking for overlapping matches.
- break
- }
- matches = [3]absoluteMatch{
- newMatch,
- matches[0],
- matches[1],
- }
- if matches[2] == (absoluteMatch{}) {
- continue
- }
- // We have three matches, so it's time to emit one and/or eliminate one.
- switch {
- case matches[0].Start < matches[2].End:
- // The first and third matches overlap; discard the one in between.
- matches = [3]absoluteMatch{
- matches[0],
- matches[2],
- {},
- }
- case matches[0].Start < matches[2].End+4:
- // The first and third matches don't overlap, but there's no room for
- // another match between them. Emit the first match and discard the second.
- e.emit(matches[2])
- matches = [3]absoluteMatch{
- matches[0],
- {},
- {},
- }
- default:
- // Emit the first match, shortening it if necessary to avoid overlap with the second.
- if matches[2].End > matches[1].Start {
- matches[2].End = matches[1].Start
- }
- if matches[2].End-matches[2].Start >= 4 {
- e.emit(matches[2])
- }
- matches[2] = absoluteMatch{}
- }
- }
- // Store some table entries at the end of the last match.
- index1 := int32(matches[0].End - 2)
- if index1 < sLimit {
- cv1 := binary.LittleEndian.Uint64(src[index1:])
- te1 := tableEntry{offset: index1, val: uint32(cv1)}
- z.longTable[z.hashLong(cv1)] = te1
- cv1 >>= 8
- te1.offset++
- te1.val = uint32(cv1)
- z.table[z.hashShort(cv1)] = te1
- }
- // We're done looking for overlapping matches; emit the ones we have.
- if matches[1] != (absoluteMatch{}) {
- if matches[1].End > matches[0].Start {
- matches[1].End = matches[0].Start
- }
- if matches[1].End-matches[1].Start >= 4 {
- e.emit(matches[1])
- }
- }
- e.emit(matches[0])
- matches = [3]absoluteMatch{}
- }
- dst = e.Dst
- if e.NextEmit < len(src) {
- dst = append(dst, Match{
- Unmatched: len(src) - e.NextEmit,
- })
- }
- return dst
- }
- func (z *ZM) hashShort(u uint64) uint32 {
- return uint32(((u << 24) * 889523592379) >> (64 - zmTableBits))
- }
- func (z *ZM) hashLong(u uint64) uint32 {
- return uint32((u * 0xcf1bbcdcb7a56463) >> (64 - zmLongTableBits))
- }
|