level3.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package flate
  2. import "fmt"
  3. // fastEncL3
  4. type fastEncL3 struct {
  5. fastGen
  6. table [1 << 16]tableEntryPrev
  7. }
  8. // Encode uses a similar algorithm to level 2, will check up to two candidates.
  9. func (e *fastEncL3) Encode(dst *tokens, src []byte) {
  10. const (
  11. inputMargin = 12 - 1
  12. minNonLiteralBlockSize = 1 + 1 + inputMargin
  13. tableBits = 16
  14. tableSize = 1 << tableBits
  15. hashBytes = 5
  16. )
  17. if debugDeflate && e.cur < 0 {
  18. panic(fmt.Sprint("e.cur < 0: ", e.cur))
  19. }
  20. // Protect against e.cur wraparound.
  21. for e.cur >= bufferReset {
  22. if len(e.hist) == 0 {
  23. for i := range e.table[:] {
  24. e.table[i] = tableEntryPrev{}
  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]
  33. if v.Cur.offset <= minOff {
  34. v.Cur.offset = 0
  35. } else {
  36. v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset
  37. }
  38. if v.Prev.offset <= minOff {
  39. v.Prev.offset = 0
  40. } else {
  41. v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset
  42. }
  43. e.table[i] = v
  44. }
  45. e.cur = maxMatchOffset
  46. }
  47. s := e.addBlock(src)
  48. // Skip if too small.
  49. if len(src) < minNonLiteralBlockSize {
  50. // We do not fill the token table.
  51. // This will be picked up by caller.
  52. dst.n = uint16(len(src))
  53. return
  54. }
  55. // Override src
  56. src = e.hist
  57. nextEmit := s
  58. // sLimit is when to stop looking for offset/length copies. The inputMargin
  59. // lets us use a fast path for emitLiteral in the main loop, while we are
  60. // looking for copies.
  61. sLimit := int32(len(src) - inputMargin)
  62. // nextEmit is where in src the next emitLiteral should start from.
  63. cv := load6432(src, s)
  64. for {
  65. const skipLog = 7
  66. nextS := s
  67. var candidate tableEntry
  68. for {
  69. nextHash := hashLen(cv, tableBits, hashBytes)
  70. s = nextS
  71. nextS = s + 1 + (s-nextEmit)>>skipLog
  72. if nextS > sLimit {
  73. goto emitRemainder
  74. }
  75. candidates := e.table[nextHash]
  76. now := load6432(src, nextS)
  77. // Safe offset distance until s + 4...
  78. minOffset := e.cur + s - (maxMatchOffset - 4)
  79. e.table[nextHash] = tableEntryPrev{Prev: candidates.Cur, Cur: tableEntry{offset: s + e.cur}}
  80. // Check both candidates
  81. candidate = candidates.Cur
  82. if candidate.offset < minOffset {
  83. cv = now
  84. // Previous will also be invalid, we have nothing.
  85. continue
  86. }
  87. if uint32(cv) == load3232(src, candidate.offset-e.cur) {
  88. if candidates.Prev.offset < minOffset || uint32(cv) != load3232(src, candidates.Prev.offset-e.cur) {
  89. break
  90. }
  91. // Both match and are valid, pick longest.
  92. offset := s - (candidate.offset - e.cur)
  93. o2 := s - (candidates.Prev.offset - e.cur)
  94. l1, l2 := matchLen(src[s+4:], src[s-offset+4:]), matchLen(src[s+4:], src[s-o2+4:])
  95. if l2 > l1 {
  96. candidate = candidates.Prev
  97. }
  98. break
  99. } else {
  100. // We only check if value mismatches.
  101. // Offset will always be invalid in other cases.
  102. candidate = candidates.Prev
  103. if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
  104. break
  105. }
  106. }
  107. cv = now
  108. }
  109. // Call emitCopy, and then see if another emitCopy could be our next
  110. // move. Repeat until we find no match for the input immediately after
  111. // what was consumed by the last emitCopy call.
  112. //
  113. // If we exit this loop normally then we need to call emitLiteral next,
  114. // though we don't yet know how big the literal will be. We handle that
  115. // by proceeding to the next iteration of the main loop. We also can
  116. // exit this loop via goto if we get close to exhausting the input.
  117. for {
  118. // Invariant: we have a 4-byte match at s, and no need to emit any
  119. // literal bytes prior to s.
  120. // Extend the 4-byte match as long as possible.
  121. //
  122. t := candidate.offset - e.cur
  123. l := e.matchlenLong(s+4, t+4, src) + 4
  124. // Extend backwards
  125. for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
  126. s--
  127. t--
  128. l++
  129. }
  130. if nextEmit < s {
  131. if false {
  132. emitLiteral(dst, src[nextEmit:s])
  133. } else {
  134. for _, v := range src[nextEmit:s] {
  135. dst.tokens[dst.n] = token(v)
  136. dst.litHist[v]++
  137. dst.n++
  138. }
  139. }
  140. }
  141. dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
  142. s += l
  143. nextEmit = s
  144. if nextS >= s {
  145. s = nextS + 1
  146. }
  147. if s >= sLimit {
  148. t += l
  149. // Index first pair after match end.
  150. if int(t+8) < len(src) && t > 0 {
  151. cv = load6432(src, t)
  152. nextHash := hashLen(cv, tableBits, hashBytes)
  153. e.table[nextHash] = tableEntryPrev{
  154. Prev: e.table[nextHash].Cur,
  155. Cur: tableEntry{offset: e.cur + t},
  156. }
  157. }
  158. goto emitRemainder
  159. }
  160. // Store every 5th hash in-between.
  161. for i := s - l + 2; i < s-5; i += 6 {
  162. nextHash := hashLen(load6432(src, i), tableBits, hashBytes)
  163. e.table[nextHash] = tableEntryPrev{
  164. Prev: e.table[nextHash].Cur,
  165. Cur: tableEntry{offset: e.cur + i}}
  166. }
  167. // We could immediately start working at s now, but to improve
  168. // compression we first update the hash table at s-2 to s.
  169. x := load6432(src, s-2)
  170. prevHash := hashLen(x, tableBits, hashBytes)
  171. e.table[prevHash] = tableEntryPrev{
  172. Prev: e.table[prevHash].Cur,
  173. Cur: tableEntry{offset: e.cur + s - 2},
  174. }
  175. x >>= 8
  176. prevHash = hashLen(x, tableBits, hashBytes)
  177. e.table[prevHash] = tableEntryPrev{
  178. Prev: e.table[prevHash].Cur,
  179. Cur: tableEntry{offset: e.cur + s - 1},
  180. }
  181. x >>= 8
  182. currHash := hashLen(x, tableBits, hashBytes)
  183. candidates := e.table[currHash]
  184. cv = x
  185. e.table[currHash] = tableEntryPrev{
  186. Prev: candidates.Cur,
  187. Cur: tableEntry{offset: s + e.cur},
  188. }
  189. // Check both candidates
  190. candidate = candidates.Cur
  191. minOffset := e.cur + s - (maxMatchOffset - 4)
  192. if candidate.offset > minOffset {
  193. if uint32(cv) == load3232(src, candidate.offset-e.cur) {
  194. // Found a match...
  195. continue
  196. }
  197. candidate = candidates.Prev
  198. if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) {
  199. // Match at prev...
  200. continue
  201. }
  202. }
  203. cv = x >> 8
  204. s++
  205. break
  206. }
  207. }
  208. emitRemainder:
  209. if int(nextEmit) < len(src) {
  210. // If nothing was added, don't encode literals.
  211. if dst.n == 0 {
  212. return
  213. }
  214. emitLiteral(dst, src[nextEmit:])
  215. }
  216. }