trio.go 8.7 KB

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