zm.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package matchfinder
  2. import "encoding/binary"
  3. const (
  4. zmTableBits = 15
  5. zmTableSize = 1 << zmTableBits
  6. zmLongTableBits = 17
  7. zmLongTableSize = 1 << zmLongTableBits
  8. )
  9. // ZM is a MatchFinder that combines the cache tables of ZDFast with the
  10. // overlap-based parsing of M4.
  11. type ZM struct {
  12. MaxDistance int
  13. history []byte
  14. table [zmTableSize]tableEntry
  15. longTable [zmLongTableSize]tableEntry
  16. }
  17. func (z *ZM) Reset() {
  18. z.table = [zmTableSize]tableEntry{}
  19. z.longTable = [zmLongTableSize]tableEntry{}
  20. z.history = z.history[:0]
  21. }
  22. func (z *ZM) FindMatches(dst []Match, src []byte) []Match {
  23. if z.MaxDistance == 0 {
  24. z.MaxDistance = 1 << 16
  25. }
  26. if len(z.history) > z.MaxDistance*2 {
  27. delta := len(z.history) - z.MaxDistance
  28. copy(z.history, z.history[delta:])
  29. z.history = z.history[:z.MaxDistance]
  30. for i := range z.table {
  31. v := z.table[i].offset
  32. v -= int32(delta)
  33. if v < 0 {
  34. z.table[i] = tableEntry{}
  35. } else {
  36. z.table[i].offset = v
  37. }
  38. }
  39. for i := range z.longTable {
  40. v := z.longTable[i].offset
  41. v -= int32(delta)
  42. if v < 0 {
  43. z.longTable[i] = tableEntry{}
  44. } else {
  45. z.longTable[i].offset = v
  46. }
  47. }
  48. }
  49. if len(src) < 16 {
  50. return append(dst, Match{
  51. Unmatched: len(src),
  52. })
  53. }
  54. e := matchEmitter{
  55. Dst: dst,
  56. NextEmit: len(z.history),
  57. }
  58. z.history = append(z.history, src...)
  59. src = z.history
  60. // matches stores the matches that have been found but not emitted,
  61. // in reverse order. (matches[0] is the most recent one.)
  62. var matches [3]absoluteMatch
  63. sLimit := int32(len(src)) - 10
  64. mainLoop:
  65. for {
  66. // Search for a match, starting after the last match emitted.
  67. s := int32(e.NextEmit)
  68. if s > sLimit {
  69. break mainLoop
  70. }
  71. // t will contain the match offset when we find one.
  72. var t int32
  73. cv := binary.LittleEndian.Uint64(src[s:])
  74. for {
  75. nextHashL := z.hashLong(cv)
  76. nextHashS := z.hashShort(cv)
  77. candidateL := z.longTable[nextHashL]
  78. candidateS := z.table[nextHashS]
  79. entry := tableEntry{offset: s, val: uint32(cv)}
  80. z.longTable[nextHashL] = entry
  81. z.table[nextHashS] = entry
  82. // Look for a repeat match one byte after the current position.
  83. if len(e.Dst) > 0 {
  84. prevDistance := int32(e.Dst[len(e.Dst)-1].Distance)
  85. if prevDistance != 0 {
  86. repIndex := s - prevDistance + 1
  87. if repIndex >= 0 && binary.LittleEndian.Uint32(src[repIndex:]) == uint32(cv>>8) {
  88. // There is a repeated match at s+2.
  89. s++
  90. t = repIndex
  91. break
  92. }
  93. }
  94. }
  95. if candidateL.offset < s && s-candidateL.offset < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
  96. binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
  97. // There is a long match at s.
  98. t = candidateL.offset
  99. break
  100. }
  101. if candidateS.offset < s && s-candidateS.offset < int32(z.MaxDistance) && uint32(cv) == candidateS.val &&
  102. binary.LittleEndian.Uint32(src[candidateS.offset:]) == uint32(cv) {
  103. // There is a regular match at s.
  104. // See if we can find a long match at s+1.
  105. cv := binary.LittleEndian.Uint64(src[s+1:])
  106. nextHashL = z.hashLong(cv)
  107. candidateL = z.longTable[nextHashL]
  108. coffsetL := s - candidateL.offset + 1
  109. z.longTable[nextHashL] = tableEntry{offset: s + 1, val: uint32(cv)}
  110. if candidateL.offset < s+1 && coffsetL < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
  111. binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
  112. // We found a long match at s+1, so we'll use that instead
  113. // of the regular match at s.
  114. t = candidateL.offset
  115. s++
  116. break
  117. }
  118. t = candidateS.offset
  119. break
  120. }
  121. s += 1 + ((s - int32(e.NextEmit)) >> 7)
  122. if s > sLimit {
  123. break mainLoop
  124. }
  125. cv = binary.LittleEndian.Uint64(src[s:])
  126. }
  127. currentMatch := extendMatch2(src, int(s), int(t), e.NextEmit)
  128. matches[0] = currentMatch
  129. // Store some table entries after s.
  130. index0 := s + 1
  131. cv0 := binary.LittleEndian.Uint64(src[index0:])
  132. te0 := tableEntry{offset: index0, val: uint32(cv0)}
  133. z.longTable[z.hashLong(cv0)] = te0
  134. cv0 >>= 8
  135. te0.offset++
  136. te0.val = uint32(cv0)
  137. z.table[z.hashShort(cv0)] = te0
  138. // We have a match in matches[0].
  139. // Now look for overlapping matches.
  140. for {
  141. if matches[0].End > int(sLimit) {
  142. break
  143. }
  144. s = int32(max(matches[0].Start+2, matches[0].End-6))
  145. cv = binary.LittleEndian.Uint64(src[s:])
  146. nextHashL := z.hashLong(cv)
  147. nextHashS := z.hashShort(cv)
  148. candidateL := z.longTable[nextHashL]
  149. candidateS := z.table[nextHashS]
  150. entry := tableEntry{offset: s, val: uint32(cv)}
  151. z.longTable[nextHashL] = entry
  152. z.table[nextHashS] = entry
  153. t = -1
  154. if candidateL.offset < s && s-candidateL.offset < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
  155. binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
  156. // There is a long match at s.
  157. t = candidateL.offset
  158. } else if candidateS.offset < s && s-candidateS.offset < int32(z.MaxDistance) && uint32(cv) == candidateS.val &&
  159. binary.LittleEndian.Uint32(src[candidateS.offset:]) == uint32(cv) {
  160. // There is a regular match at s.
  161. t = candidateS.offset
  162. // See if we can find a long match at s+1.
  163. cv := binary.LittleEndian.Uint64(src[s+1:])
  164. nextHashL = z.hashLong(cv)
  165. candidateL = z.longTable[nextHashL]
  166. coffsetL := s - candidateL.offset + 1
  167. z.longTable[nextHashL] = tableEntry{offset: s + 1, val: uint32(cv)}
  168. if candidateL.offset < s+1 && coffsetL < int32(z.MaxDistance) && uint32(cv) == candidateL.val &&
  169. binary.LittleEndian.Uint32(src[candidateL.offset:]) == uint32(cv) {
  170. // We found a long match at s+1, so we'll use that instead
  171. // of the regular match at s.
  172. t = candidateL.offset
  173. s++
  174. }
  175. }
  176. if t == -1 {
  177. // No overlapping match was found.
  178. break
  179. }
  180. newMatch := extendMatch2(src, int(s), int(t), e.NextEmit)
  181. if newMatch.End-newMatch.Start <= matches[0].End-matches[0].Start {
  182. // The new match isn't longer than the old one, so we break out of the loop
  183. // of looking for overlapping matches.
  184. break
  185. }
  186. matches = [3]absoluteMatch{
  187. newMatch,
  188. matches[0],
  189. matches[1],
  190. }
  191. if matches[2] == (absoluteMatch{}) {
  192. continue
  193. }
  194. // We have three matches, so it's time to emit one and/or eliminate one.
  195. switch {
  196. case matches[0].Start < matches[2].End:
  197. // The first and third matches overlap; discard the one in between.
  198. matches = [3]absoluteMatch{
  199. matches[0],
  200. matches[2],
  201. {},
  202. }
  203. case matches[0].Start < matches[2].End+4:
  204. // The first and third matches don't overlap, but there's no room for
  205. // another match between them. Emit the first match and discard the second.
  206. e.emit(matches[2])
  207. matches = [3]absoluteMatch{
  208. matches[0],
  209. {},
  210. {},
  211. }
  212. default:
  213. // Emit the first match, shortening it if necessary to avoid overlap with the second.
  214. if matches[2].End > matches[1].Start {
  215. matches[2].End = matches[1].Start
  216. }
  217. if matches[2].End-matches[2].Start >= 4 {
  218. e.emit(matches[2])
  219. }
  220. matches[2] = absoluteMatch{}
  221. }
  222. }
  223. // Store some table entries at the end of the last match.
  224. index1 := int32(matches[0].End - 2)
  225. if index1 < sLimit {
  226. cv1 := binary.LittleEndian.Uint64(src[index1:])
  227. te1 := tableEntry{offset: index1, val: uint32(cv1)}
  228. z.longTable[z.hashLong(cv1)] = te1
  229. cv1 >>= 8
  230. te1.offset++
  231. te1.val = uint32(cv1)
  232. z.table[z.hashShort(cv1)] = te1
  233. }
  234. // We're done looking for overlapping matches; emit the ones we have.
  235. if matches[1] != (absoluteMatch{}) {
  236. if matches[1].End > matches[0].Start {
  237. matches[1].End = matches[0].Start
  238. }
  239. if matches[1].End-matches[1].Start >= 4 {
  240. e.emit(matches[1])
  241. }
  242. }
  243. e.emit(matches[0])
  244. matches = [3]absoluteMatch{}
  245. }
  246. dst = e.Dst
  247. if e.NextEmit < len(src) {
  248. dst = append(dst, Match{
  249. Unmatched: len(src) - e.NextEmit,
  250. })
  251. }
  252. return dst
  253. }
  254. func (z *ZM) hashShort(u uint64) uint32 {
  255. return uint32(((u << 24) * 889523592379) >> (64 - zmTableBits))
  256. }
  257. func (z *ZM) hashLong(u uint64) uint32 {
  258. return uint32((u * 0xcf1bbcdcb7a56463) >> (64 - zmLongTableBits))
  259. }