You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 rivejä
5.6 KiB

  1. package flate
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "math/bits"
  6. )
  7. // fastGen maintains the table for matches,
  8. // and the previous byte block for level 2.
  9. // This is the generic implementation.
  10. type fastEncL1 struct {
  11. fastGen
  12. table [tableSize]tableEntry
  13. }
  14. // EncodeL1 uses a similar algorithm to level 1
  15. func (e *fastEncL1) Encode(dst *tokens, src []byte) {
  16. const (
  17. inputMargin = 12 - 1
  18. minNonLiteralBlockSize = 1 + 1 + inputMargin
  19. )
  20. if debugDeflate && e.cur < 0 {
  21. panic(fmt.Sprint("e.cur < 0: ", e.cur))
  22. }
  23. // Protect against e.cur wraparound.
  24. for e.cur >= bufferReset {
  25. if len(e.hist) == 0 {
  26. for i := range e.table[:] {
  27. e.table[i] = tableEntry{}
  28. }
  29. e.cur = maxMatchOffset
  30. break
  31. }
  32. // Shift down everything in the table that isn't already too far away.
  33. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset
  34. for i := range e.table[:] {
  35. v := e.table[i].offset
  36. if v <= minOff {
  37. v = 0
  38. } else {
  39. v = v - e.cur + maxMatchOffset
  40. }
  41. e.table[i].offset = v
  42. }
  43. e.cur = maxMatchOffset
  44. }
  45. s := e.addBlock(src)
  46. // This check isn't in the Snappy implementation, but there, the caller
  47. // instead of the callee handles this case.
  48. if len(src) < minNonLiteralBlockSize {
  49. // We do not fill the token table.
  50. // This will be picked up by caller.
  51. dst.n = uint16(len(src))
  52. return
  53. }
  54. // Override src
  55. src = e.hist
  56. nextEmit := s
  57. // sLimit is when to stop looking for offset/length copies. The inputMargin
  58. // lets us use a fast path for emitLiteral in the main loop, while we are
  59. // looking for copies.
  60. sLimit := int32(len(src) - inputMargin)
  61. // nextEmit is where in src the next emitLiteral should start from.
  62. cv := load3232(src, s)
  63. for {
  64. const skipLog = 5
  65. const doEvery = 2
  66. nextS := s
  67. var candidate tableEntry
  68. for {
  69. nextHash := hash(cv)
  70. candidate = e.table[nextHash]
  71. nextS = s + doEvery + (s-nextEmit)>>skipLog
  72. if nextS > sLimit {
  73. goto emitRemainder
  74. }
  75. now := load6432(src, nextS)
  76. e.table[nextHash] = tableEntry{offset: s + e.cur}
  77. nextHash = hash(uint32(now))
  78. offset := s - (candidate.offset - e.cur)
  79. if offset < maxMatchOffset && cv == load3232(src, candidate.offset-e.cur) {
  80. e.table[nextHash] = tableEntry{offset: nextS + e.cur}
  81. break
  82. }
  83. // Do one right away...
  84. cv = uint32(now)
  85. s = nextS
  86. nextS++
  87. candidate = e.table[nextHash]
  88. now >>= 8
  89. e.table[nextHash] = tableEntry{offset: s + e.cur}
  90. offset = s - (candidate.offset - e.cur)
  91. if offset < maxMatchOffset && cv == load3232(src, candidate.offset-e.cur) {
  92. e.table[nextHash] = tableEntry{offset: nextS + e.cur}
  93. break
  94. }
  95. cv = uint32(now)
  96. s = nextS
  97. }
  98. // A 4-byte match has been found. We'll later see if more than 4 bytes
  99. // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
  100. // them as literal bytes.
  101. for {
  102. // Invariant: we have a 4-byte match at s, and no need to emit any
  103. // literal bytes prior to s.
  104. // Extend the 4-byte match as long as possible.
  105. t := candidate.offset - e.cur
  106. var l = int32(4)
  107. if false {
  108. l = e.matchlenLong(s+4, t+4, src) + 4
  109. } else {
  110. // inlined:
  111. a := src[s+4:]
  112. b := src[t+4:]
  113. for len(a) >= 8 {
  114. if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 {
  115. l += int32(bits.TrailingZeros64(diff) >> 3)
  116. break
  117. }
  118. l += 8
  119. a = a[8:]
  120. b = b[8:]
  121. }
  122. if len(a) < 8 {
  123. b = b[:len(a)]
  124. for i := range a {
  125. if a[i] != b[i] {
  126. break
  127. }
  128. l++
  129. }
  130. }
  131. }
  132. // Extend backwards
  133. for t > 0 && s > nextEmit && src[t-1] == src[s-1] {
  134. s--
  135. t--
  136. l++
  137. }
  138. if nextEmit < s {
  139. if false {
  140. emitLiteral(dst, src[nextEmit:s])
  141. } else {
  142. for _, v := range src[nextEmit:s] {
  143. dst.tokens[dst.n] = token(v)
  144. dst.litHist[v]++
  145. dst.n++
  146. }
  147. }
  148. }
  149. // Save the match found
  150. if false {
  151. dst.AddMatchLong(l, uint32(s-t-baseMatchOffset))
  152. } else {
  153. // Inlined...
  154. xoffset := uint32(s - t - baseMatchOffset)
  155. xlength := l
  156. oc := offsetCode(xoffset)
  157. xoffset |= oc << 16
  158. for xlength > 0 {
  159. xl := xlength
  160. if xl > 258 {
  161. if xl > 258+baseMatchLength {
  162. xl = 258
  163. } else {
  164. xl = 258 - baseMatchLength
  165. }
  166. }
  167. xlength -= xl
  168. xl -= baseMatchLength
  169. dst.extraHist[lengthCodes1[uint8(xl)]]++
  170. dst.offHist[oc]++
  171. dst.tokens[dst.n] = token(matchType | uint32(xl)<<lengthShift | xoffset)
  172. dst.n++
  173. }
  174. }
  175. s += l
  176. nextEmit = s
  177. if nextS >= s {
  178. s = nextS + 1
  179. }
  180. if s >= sLimit {
  181. // Index first pair after match end.
  182. if int(s+l+4) < len(src) {
  183. cv := load3232(src, s)
  184. e.table[hash(cv)] = tableEntry{offset: s + e.cur}
  185. }
  186. goto emitRemainder
  187. }
  188. // We could immediately start working at s now, but to improve
  189. // compression we first update the hash table at s-2 and at s. If
  190. // another emitCopy is not our next move, also calculate nextHash
  191. // at s+1. At least on GOARCH=amd64, these three hash calculations
  192. // are faster as one load64 call (with some shifts) instead of
  193. // three load32 calls.
  194. x := load6432(src, s-2)
  195. o := e.cur + s - 2
  196. prevHash := hash(uint32(x))
  197. e.table[prevHash] = tableEntry{offset: o}
  198. x >>= 16
  199. currHash := hash(uint32(x))
  200. candidate = e.table[currHash]
  201. e.table[currHash] = tableEntry{offset: o + 2}
  202. offset := s - (candidate.offset - e.cur)
  203. if offset > maxMatchOffset || uint32(x) != load3232(src, candidate.offset-e.cur) {
  204. cv = uint32(x >> 8)
  205. s++
  206. break
  207. }
  208. }
  209. }
  210. emitRemainder:
  211. if int(nextEmit) < len(src) {
  212. // If nothing was added, don't encode literals.
  213. if dst.n == 0 {
  214. return
  215. }
  216. emitLiteral(dst, src[nextEmit:])
  217. }
  218. }