m4.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. package matchfinder
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "math/bits"
  6. "runtime"
  7. )
  8. // M4 is an implementation of the MatchFinder
  9. // interface that uses a hash table to find matches,
  10. // optional match chains,
  11. // and the advanced parsing technique from
  12. // https://fastcompression.blogspot.com/2011/12/advanced-parsing-strategies.html.
  13. type M4 struct {
  14. // MaxDistance is the maximum distance (in bytes) to look back for
  15. // a match. The default is 65535.
  16. MaxDistance int
  17. // MinLength is the length of the shortest match to return.
  18. // The default is 4.
  19. MinLength int
  20. // HashLen is the number of bytes to use to calculate the hashes.
  21. // The maximum is 8 and the default is 6.
  22. HashLen int
  23. // TableBits is the number of bits in the hash table indexes.
  24. // The default is 17 (128K entries).
  25. TableBits int
  26. // ChainLength is how many entries to search on the "match chain" of older
  27. // locations with the same hash as the current location.
  28. ChainLength int
  29. // DistanceBitCost is used when comparing two matches to see
  30. // which is better. The comparison is primarily based on the length
  31. // of the matches, but it can also take the distance into account,
  32. // in terms of the number of bits needed to represent the distance.
  33. // One byte of length is given a score of 256, so 32 (256/8) would
  34. // be a reasonable first guess for the value of one bit.
  35. // (The default is 0, which bases the comparison solely on length.)
  36. DistanceBitCost int
  37. table []uint32
  38. chain []uint32
  39. history []byte
  40. }
  41. func (q *M4) Reset() {
  42. for i := range q.table {
  43. q.table[i] = 0
  44. }
  45. q.history = q.history[:0]
  46. q.chain = q.chain[:0]
  47. }
  48. func (q *M4) score(m absoluteMatch) int {
  49. return (m.End-m.Start)*256 + (bits.LeadingZeros32(uint32(m.Start-m.Match))-32)*q.DistanceBitCost
  50. }
  51. func (q *M4) FindMatches(dst []Match, src []byte) []Match {
  52. if q.MaxDistance == 0 {
  53. q.MaxDistance = 65535
  54. }
  55. if q.MinLength == 0 {
  56. q.MinLength = 4
  57. }
  58. if q.HashLen == 0 {
  59. q.HashLen = 6
  60. }
  61. if q.TableBits == 0 {
  62. q.TableBits = 17
  63. }
  64. if len(q.table) < 1<<q.TableBits {
  65. q.table = make([]uint32, 1<<q.TableBits)
  66. }
  67. e := matchEmitter{Dst: dst}
  68. if len(q.history) > q.MaxDistance*2 {
  69. // Trim down the history buffer.
  70. delta := len(q.history) - q.MaxDistance
  71. copy(q.history, q.history[delta:])
  72. q.history = q.history[:q.MaxDistance]
  73. if q.ChainLength > 0 {
  74. copy(q.chain, q.chain[delta:])
  75. q.chain = q.chain[:q.MaxDistance]
  76. }
  77. for i, v := range q.table {
  78. newV := int(v) - delta
  79. if newV < 0 {
  80. newV = 0
  81. }
  82. q.table[i] = uint32(newV)
  83. }
  84. }
  85. // Append src to the history buffer.
  86. e.NextEmit = len(q.history)
  87. q.history = append(q.history, src...)
  88. if q.ChainLength > 0 {
  89. q.chain = append(q.chain, make([]uint32, len(src))...)
  90. }
  91. src = q.history
  92. // matches stores the matches that have been found but not emitted,
  93. // in reverse order. (matches[0] is the most recent one.)
  94. var matches [3]absoluteMatch
  95. for i := e.NextEmit; i < len(src)-7; i++ {
  96. if matches[0] != (absoluteMatch{}) && i >= matches[0].End {
  97. // We have found some matches, and we're far enough along that we probably
  98. // won't find overlapping matches, so we might as well emit them.
  99. if matches[1] != (absoluteMatch{}) {
  100. if matches[1].End > matches[0].Start {
  101. matches[1].End = matches[0].Start
  102. }
  103. if matches[1].End-matches[1].Start >= q.MinLength && q.score(matches[1]) > 0 {
  104. e.emit(matches[1])
  105. }
  106. }
  107. e.emit(matches[0])
  108. matches = [3]absoluteMatch{}
  109. }
  110. // Look for a repeat match one byte after the current position.
  111. if matches[0] == (absoluteMatch{}) && len(e.Dst) > 0 {
  112. prevDistance := e.Dst[len(e.Dst)-1].Distance
  113. if binary.LittleEndian.Uint32(src[i+1:]) == binary.LittleEndian.Uint32(src[i+1-prevDistance:]) {
  114. // We have a 4-byte match.
  115. m := extendMatch2(src, i+1, i+1-prevDistance, e.NextEmit+1)
  116. if m.End-m.Start >= q.MinLength {
  117. matches[0] = m
  118. }
  119. }
  120. }
  121. // Calculate and store the hash.
  122. h := ((binary.LittleEndian.Uint64(src[i:]) & (1<<(8*q.HashLen) - 1)) * hashMul64) >> (64 - q.TableBits)
  123. candidate := int(q.table[h])
  124. q.table[h] = uint32(i)
  125. if q.ChainLength > 0 && candidate != 0 {
  126. delta := i - candidate
  127. q.chain[i] = uint32(delta)
  128. }
  129. if i < matches[0].End && i != matches[0].End+2-q.HashLen {
  130. continue
  131. }
  132. if candidate == 0 || i-candidate > q.MaxDistance {
  133. continue
  134. }
  135. // Look for a match.
  136. var currentMatch absoluteMatch
  137. if binary.LittleEndian.Uint32(src[candidate:]) == binary.LittleEndian.Uint32(src[i:]) {
  138. m := extendMatch2(src, i, candidate, e.NextEmit)
  139. if m.End-m.Start > q.MinLength && q.score(m) > 0 {
  140. currentMatch = m
  141. }
  142. }
  143. for j := 0; j < q.ChainLength; j++ {
  144. delta := q.chain[candidate]
  145. if delta == 0 {
  146. break
  147. }
  148. candidate -= int(delta)
  149. if candidate <= 0 || i-candidate > q.MaxDistance {
  150. break
  151. }
  152. if binary.LittleEndian.Uint32(src[candidate:]) == binary.LittleEndian.Uint32(src[i:]) {
  153. m := extendMatch2(src, i, candidate, e.NextEmit)
  154. if m.End-m.Start > q.MinLength && q.score(m) > q.score(currentMatch) {
  155. currentMatch = m
  156. }
  157. }
  158. }
  159. if currentMatch.End-currentMatch.Start < q.MinLength {
  160. continue
  161. }
  162. overlapPenalty := 0
  163. if matches[0] != (absoluteMatch{}) {
  164. overlapPenalty = 275
  165. if currentMatch.Start <= matches[1].End {
  166. // This match would completely replace the previous match,
  167. // so there is no penalty for overlap.
  168. overlapPenalty = 0
  169. }
  170. }
  171. if q.score(currentMatch) <= q.score(matches[0])+overlapPenalty {
  172. continue
  173. }
  174. matches = [3]absoluteMatch{
  175. currentMatch,
  176. matches[0],
  177. matches[1],
  178. }
  179. if matches[2] == (absoluteMatch{}) {
  180. continue
  181. }
  182. // We have three matches, so it's time to emit one and/or eliminate one.
  183. switch {
  184. case matches[0].Start < matches[2].End:
  185. // The first and third matches overlap; discard the one in between.
  186. matches = [3]absoluteMatch{
  187. matches[0],
  188. matches[2],
  189. absoluteMatch{},
  190. }
  191. case matches[0].Start < matches[2].End+q.MinLength:
  192. // The first and third matches don't overlap, but there's no room for
  193. // another match between them. Emit the first match and discard the second.
  194. e.emit(matches[2])
  195. matches = [3]absoluteMatch{
  196. matches[0],
  197. absoluteMatch{},
  198. absoluteMatch{},
  199. }
  200. default:
  201. // Emit the first match, shortening it if necessary to avoid overlap with the second.
  202. if matches[2].End > matches[1].Start {
  203. matches[2].End = matches[1].Start
  204. if q.ChainLength > 0 && matches[2].End-matches[2].Start >= q.MinLength {
  205. // Since the match length was trimmed, we may be able to find a closer match
  206. // to replace it.
  207. pos := matches[2].Start
  208. for {
  209. delta := int(q.chain[pos])
  210. if delta == 0 {
  211. break
  212. }
  213. pos -= delta
  214. if pos <= matches[2].Match {
  215. break
  216. }
  217. if bytes.Equal(src[matches[2].Start:matches[2].End], src[pos:pos+matches[2].End-matches[2].Start]) {
  218. matches[2].Match = pos
  219. break
  220. }
  221. }
  222. }
  223. }
  224. if matches[2].End-matches[2].Start >= q.MinLength && q.score(matches[2]) > 0 {
  225. e.emit(matches[2])
  226. }
  227. matches[2] = absoluteMatch{}
  228. }
  229. }
  230. // We've found all the matches now; emit the remaining ones.
  231. if matches[1] != (absoluteMatch{}) {
  232. if matches[1].End > matches[0].Start {
  233. matches[1].End = matches[0].Start
  234. }
  235. if matches[1].End-matches[1].Start >= q.MinLength && q.score(matches[1]) > 0 {
  236. e.emit(matches[1])
  237. }
  238. }
  239. if matches[0] != (absoluteMatch{}) {
  240. e.emit(matches[0])
  241. }
  242. dst = e.Dst
  243. if e.NextEmit < len(src) {
  244. dst = append(dst, Match{
  245. Unmatched: len(src) - e.NextEmit,
  246. })
  247. }
  248. return dst
  249. }
  250. const hashMul64 = 0x1E35A7BD1E35A7BD
  251. // extendMatch returns the largest k such that k <= len(src) and that
  252. // src[i:i+k-j] and src[j:k] have the same contents.
  253. //
  254. // It assumes that:
  255. //
  256. // 0 <= i && i < j && j <= len(src)
  257. func extendMatch(src []byte, i, j int) int {
  258. switch runtime.GOARCH {
  259. case "amd64", "arm64":
  260. // As long as we are 8 or more bytes before the end of src, we can load and
  261. // compare 8 bytes at a time. If those 8 bytes are equal, repeat.
  262. for j+8 < len(src) {
  263. iBytes := binary.LittleEndian.Uint64(src[i:])
  264. jBytes := binary.LittleEndian.Uint64(src[j:])
  265. if iBytes != jBytes {
  266. // If those 8 bytes were not equal, XOR the two 8 byte values, and return
  267. // the index of the first byte that differs. The BSF instruction finds the
  268. // least significant 1 bit, the amd64 architecture is little-endian, and
  269. // the shift by 3 converts a bit index to a byte index.
  270. return j + bits.TrailingZeros64(iBytes^jBytes)>>3
  271. }
  272. i, j = i+8, j+8
  273. }
  274. case "386":
  275. // On a 32-bit CPU, we do it 4 bytes at a time.
  276. for j+4 < len(src) {
  277. iBytes := binary.LittleEndian.Uint32(src[i:])
  278. jBytes := binary.LittleEndian.Uint32(src[j:])
  279. if iBytes != jBytes {
  280. return j + bits.TrailingZeros32(iBytes^jBytes)>>3
  281. }
  282. i, j = i+4, j+4
  283. }
  284. }
  285. for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
  286. }
  287. return j
  288. }
  289. // Given a 4-byte match at src[start] and src[candidate], extendMatch2 extends it
  290. // upward as far as possible, and downward no farther than to min.
  291. func extendMatch2(src []byte, start, candidate, min int) absoluteMatch {
  292. end := extendMatch(src, candidate+4, start+4)
  293. for start > min && candidate > 0 && src[start-1] == src[candidate-1] {
  294. start--
  295. candidate--
  296. }
  297. return absoluteMatch{
  298. Start: start,
  299. End: end,
  300. Match: candidate,
  301. }
  302. }