level4.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. package flate
  2. import "fmt"
  3. type fastEncL4 struct {
  4. fastGen
  5. table [tableSize]tableEntry
  6. bTable [tableSize]tableEntry
  7. }
  8. func (e *fastEncL4) Encode(dst *tokens, src []byte) {
  9. const (
  10. inputMargin = 12 - 1
  11. minNonLiteralBlockSize = 1 + 1 + inputMargin
  12. hashShortBytes = 4
  13. )
  14. if debugDeflate && e.cur < 0 {
  15. panic(fmt.Sprint("e.cur < 0: ", e.cur))
  16. }
  17. // Protect against e.cur wraparound.
  18. for e.cur >= bufferReset {
  19. if len(e.hist) == 0 {
  20. for i := range e.table[:] {
  21. e.table[i] = tableEntry{}
  22. }
  23. for i := range e.bTable[:] {
  24. e.bTable[i] = tableEntry{}
  25. }
  26. e.cur = maxMatchOffset
  27. break
  28. }
  29. // Shift down everything in the table that isn't already too far away.
  30. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
  31. for i := range e.table[:] {
  32. v := e.table[i].offset
  33. if v <= minOff {
  34. v = 0
  35. } else {
  36. v = v - e.cur + maxMatchOffset
  37. }
  38. e.table[i].offset = v
  39. }
  40. for i := range e.bTable[:] {
  41. v := e.bTable[i].offset
  42. if v <= minOff {
  43. v = 0
  44. } else {
  45. v = v - e.cur + maxMatchOffset
  46. }
  47. e.bTable[i].offset = v
  48. }
  49. e.cur = maxMatchOffset
  50. }
  51. s := e.addBlock(src)
  52. // This check isn't in the Snappy implementation, but there, the caller
  53. // instead of the callee handles this case.
  54. if len(src) < minNonLiteralBlockSize {
  55. // We do not fill the token table.
  56. // This will be picked up by caller.
  57. dst.n = uint16(len(src))
  58. return
  59. }
  60. // Override src
  61. src = e.hist
  62. nextEmit := s
  63. // sLimit is when to stop looking for offset/length copies. The inputMargin
  64. // lets us use a fast path for emitLiteral in the main loop, while we are
  65. // looking for copies.
  66. sLimit := int32(len(src) - inputMargin)
  67. // nextEmit is where in src the next emitLiteral should start from.
  68. cv := load6432(src, s)
  69. for {
  70. const skipLog = 6
  71. const doEvery = 1
  72. nextS := s
  73. var t int32
  74. for {
  75. nextHashS := hashLen(cv, tableBits, hashShortBytes)
  76. nextHashL := hash7(cv, tableBits)
  77. s = nextS
  78. nextS = s + doEvery + (s-nextEmit)>>skipLog
  79. if nextS > sLimit {
  80. goto emitRemainder
  81. }
  82. // Fetch a short+long candidate
  83. sCandidate := e.table[nextHashS]
  84. lCandidate := e.bTable[nextHashL]
  85. next := load6432(src, nextS)
  86. entry := tableEntry{offset: s + e.cur}
  87. e.table[nextHashS] = entry
  88. e.bTable[nextHashL] = entry
  89. t = lCandidate.offset - e.cur
  90. if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.offset-e.cur) {
  91. // We got a long match. Use that.
  92. break
  93. }
  94. t = sCandidate.offset - e.cur
  95. if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) {
  96. // Found a 4 match...
  97. lCandidate = e.bTable[hash7(next, tableBits)]
  98. // If the next long is a candidate, check if we should use that instead...
  99. lOff := nextS - (lCandidate.offset - e.cur)
  100. if lOff < maxMatchOffset && load3232(src, lCandidate.offset-e.cur) == uint32(next) {
  101. l1, l2 := matchLen(src[s+4:], src[t+4:]), matchLen(src[nextS+4:], src[nextS-lOff+4:])
  102. if l2 > l1 {
  103. s = nextS
  104. t = lCandidate.offset - e.cur
  105. }
  106. }
  107. break
  108. }
  109. cv = next
  110. }
  111. // A 4-byte match has been found. We'll later see if more than 4 bytes
  112. // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
  113. // them as literal bytes.
  114. // Extend the 4-byte match as long as possible.
  115. l := e.matchlenLong(s+4, t+4, src) + 4
  116. // Extend backwards
  117. for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
  118. s--
  119. t--
  120. l++
  121. }
  122. if nextEmit < s {
  123. if false {
  124. emitLiteral(dst, src[nextEmit:s])
  125. } else {
  126. for _, v := range src[nextEmit:s] {
  127. dst.tokens[dst.n] = token(v)
  128. dst.litHist[v]++
  129. dst.n++
  130. }
  131. }
  132. }
  133. if debugDeflate {
  134. if t >= s {
  135. panic("s-t")
  136. }
  137. if (s - t) > maxMatchOffset {
  138. panic(fmt.Sprintln("mmo", t))
  139. }
  140. if l < baseMatchLength {
  141. panic("bml")
  142. }
  143. }
  144. dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
  145. s += l
  146. nextEmit = s
  147. if nextS >= s {
  148. s = nextS + 1
  149. }
  150. if s >= sLimit {
  151. // Index first pair after match end.
  152. if int(s+8) < len(src) {
  153. cv := load6432(src, s)
  154. e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: s + e.cur}
  155. e.bTable[hash7(cv, tableBits)] = tableEntry{offset: s + e.cur}
  156. }
  157. goto emitRemainder
  158. }
  159. // Store every 3rd hash in-between
  160. if true {
  161. i := nextS
  162. if i < s-1 {
  163. cv := load6432(src, i)
  164. t := tableEntry{offset: i + e.cur}
  165. t2 := tableEntry{offset: t.offset + 1}
  166. e.bTable[hash7(cv, tableBits)] = t
  167. e.bTable[hash7(cv>>8, tableBits)] = t2
  168. e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2
  169. i += 3
  170. for ; i < s-1; i += 3 {
  171. cv := load6432(src, i)
  172. t := tableEntry{offset: i + e.cur}
  173. t2 := tableEntry{offset: t.offset + 1}
  174. e.bTable[hash7(cv, tableBits)] = t
  175. e.bTable[hash7(cv>>8, tableBits)] = t2
  176. e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2
  177. }
  178. }
  179. }
  180. // We could immediately start working at s now, but to improve
  181. // compression we first update the hash table at s-1 and at s.
  182. x := load6432(src, s-1)
  183. o := e.cur + s - 1
  184. prevHashS := hashLen(x, tableBits, hashShortBytes)
  185. prevHashL := hash7(x, tableBits)
  186. e.table[prevHashS] = tableEntry{offset: o}
  187. e.bTable[prevHashL] = tableEntry{offset: o}
  188. cv = x >> 8
  189. }
  190. emitRemainder:
  191. if int(nextEmit) < len(src) {
  192. // If nothing was added, don't encode literals.
  193. if dst.n == 0 {
  194. return
  195. }
  196. emitLiteral(dst, src[nextEmit:])
  197. }
  198. }