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.

234 lines
6.6 KiB

  1. // Copyright 2011 The Snappy-Go Authors. All rights reserved.
  2. // Modified for deflate by Klaus Post (c) 2015.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package flate
  6. import (
  7. "encoding/binary"
  8. "fmt"
  9. "math/bits"
  10. )
  11. type fastEnc interface {
  12. Encode(dst *tokens, src []byte)
  13. Reset()
  14. }
  15. func newFastEnc(level int) fastEnc {
  16. switch level {
  17. case 1:
  18. return &fastEncL1{fastGen: fastGen{cur: maxStoreBlockSize}}
  19. case 2:
  20. return &fastEncL2{fastGen: fastGen{cur: maxStoreBlockSize}}
  21. case 3:
  22. return &fastEncL3{fastGen: fastGen{cur: maxStoreBlockSize}}
  23. case 4:
  24. return &fastEncL4{fastGen: fastGen{cur: maxStoreBlockSize}}
  25. case 5:
  26. return &fastEncL5{fastGen: fastGen{cur: maxStoreBlockSize}}
  27. case 6:
  28. return &fastEncL6{fastGen: fastGen{cur: maxStoreBlockSize}}
  29. default:
  30. panic("invalid level specified")
  31. }
  32. }
  33. const (
  34. tableBits = 15 // Bits used in the table
  35. tableSize = 1 << tableBits // Size of the table
  36. tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32.
  37. baseMatchOffset = 1 // The smallest match offset
  38. baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5
  39. maxMatchOffset = 1 << 15 // The largest match offset
  40. bTableBits = 17 // Bits used in the big tables
  41. bTableSize = 1 << bTableBits // Size of the table
  42. allocHistory = maxStoreBlockSize * 5 // Size to preallocate for history.
  43. bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize - 1 // Reset the buffer offset when reaching this.
  44. )
  45. const (
  46. prime3bytes = 506832829
  47. prime4bytes = 2654435761
  48. prime5bytes = 889523592379
  49. prime6bytes = 227718039650203
  50. prime7bytes = 58295818150454627
  51. prime8bytes = 0xcf1bbcdcb7a56463
  52. )
  53. func load32(b []byte, i int) uint32 {
  54. // Help the compiler eliminate bounds checks on the read so it can be done in a single read.
  55. b = b[i:]
  56. b = b[:4]
  57. return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
  58. }
  59. func load64(b []byte, i int) uint64 {
  60. return binary.LittleEndian.Uint64(b[i:])
  61. }
  62. func load3232(b []byte, i int32) uint32 {
  63. return binary.LittleEndian.Uint32(b[i:])
  64. }
  65. func load6432(b []byte, i int32) uint64 {
  66. return binary.LittleEndian.Uint64(b[i:])
  67. }
  68. func hash(u uint32) uint32 {
  69. return (u * 0x1e35a7bd) >> tableShift
  70. }
  71. type tableEntry struct {
  72. offset int32
  73. }
  74. // fastGen maintains the table for matches,
  75. // and the previous byte block for level 2.
  76. // This is the generic implementation.
  77. type fastGen struct {
  78. hist []byte
  79. cur int32
  80. }
  81. func (e *fastGen) addBlock(src []byte) int32 {
  82. // check if we have space already
  83. if len(e.hist)+len(src) > cap(e.hist) {
  84. if cap(e.hist) == 0 {
  85. e.hist = make([]byte, 0, allocHistory)
  86. } else {
  87. if cap(e.hist) < maxMatchOffset*2 {
  88. panic("unexpected buffer size")
  89. }
  90. // Move down
  91. offset := int32(len(e.hist)) - maxMatchOffset
  92. copy(e.hist[0:maxMatchOffset], e.hist[offset:])
  93. e.cur += offset
  94. e.hist = e.hist[:maxMatchOffset]
  95. }
  96. }
  97. s := int32(len(e.hist))
  98. e.hist = append(e.hist, src...)
  99. return s
  100. }
  101. // hash4 returns the hash of u to fit in a hash table with h bits.
  102. // Preferably h should be a constant and should always be <32.
  103. func hash4u(u uint32, h uint8) uint32 {
  104. return (u * prime4bytes) >> (32 - h)
  105. }
  106. type tableEntryPrev struct {
  107. Cur tableEntry
  108. Prev tableEntry
  109. }
  110. // hash4x64 returns the hash of the lowest 4 bytes of u to fit in a hash table with h bits.
  111. // Preferably h should be a constant and should always be <32.
  112. func hash4x64(u uint64, h uint8) uint32 {
  113. return (uint32(u) * prime4bytes) >> ((32 - h) & reg8SizeMask32)
  114. }
  115. // hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits.
  116. // Preferably h should be a constant and should always be <64.
  117. func hash7(u uint64, h uint8) uint32 {
  118. return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & reg8SizeMask64))
  119. }
  120. // hash8 returns the hash of u to fit in a hash table with h bits.
  121. // Preferably h should be a constant and should always be <64.
  122. func hash8(u uint64, h uint8) uint32 {
  123. return uint32((u * prime8bytes) >> ((64 - h) & reg8SizeMask64))
  124. }
  125. // hash6 returns the hash of the lowest 6 bytes of u to fit in a hash table with h bits.
  126. // Preferably h should be a constant and should always be <64.
  127. func hash6(u uint64, h uint8) uint32 {
  128. return uint32(((u << (64 - 48)) * prime6bytes) >> ((64 - h) & reg8SizeMask64))
  129. }
  130. // matchlen will return the match length between offsets and t in src.
  131. // The maximum length returned is maxMatchLength - 4.
  132. // It is assumed that s > t, that t >=0 and s < len(src).
  133. func (e *fastGen) matchlen(s, t int32, src []byte) int32 {
  134. if debugDecode {
  135. if t >= s {
  136. panic(fmt.Sprint("t >=s:", t, s))
  137. }
  138. if int(s) >= len(src) {
  139. panic(fmt.Sprint("s >= len(src):", s, len(src)))
  140. }
  141. if t < 0 {
  142. panic(fmt.Sprint("t < 0:", t))
  143. }
  144. if s-t > maxMatchOffset {
  145. panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
  146. }
  147. }
  148. s1 := int(s) + maxMatchLength - 4
  149. if s1 > len(src) {
  150. s1 = len(src)
  151. }
  152. // Extend the match to be as long as possible.
  153. return int32(matchLen(src[s:s1], src[t:]))
  154. }
  155. // matchlenLong will return the match length between offsets and t in src.
  156. // It is assumed that s > t, that t >=0 and s < len(src).
  157. func (e *fastGen) matchlenLong(s, t int32, src []byte) int32 {
  158. if debugDeflate {
  159. if t >= s {
  160. panic(fmt.Sprint("t >=s:", t, s))
  161. }
  162. if int(s) >= len(src) {
  163. panic(fmt.Sprint("s >= len(src):", s, len(src)))
  164. }
  165. if t < 0 {
  166. panic(fmt.Sprint("t < 0:", t))
  167. }
  168. if s-t > maxMatchOffset {
  169. panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
  170. }
  171. }
  172. // Extend the match to be as long as possible.
  173. return int32(matchLen(src[s:], src[t:]))
  174. }
  175. // Reset the encoding table.
  176. func (e *fastGen) Reset() {
  177. if cap(e.hist) < allocHistory {
  178. e.hist = make([]byte, 0, allocHistory)
  179. }
  180. // We offset current position so everything will be out of reach.
  181. // If we are above the buffer reset it will be cleared anyway since len(hist) == 0.
  182. if e.cur <= bufferReset {
  183. e.cur += maxMatchOffset + int32(len(e.hist))
  184. }
  185. e.hist = e.hist[:0]
  186. }
  187. // matchLen returns the maximum length.
  188. // 'a' must be the shortest of the two.
  189. func matchLen(a, b []byte) int {
  190. var checked int
  191. for len(a) >= 8 {
  192. if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 {
  193. return checked + (bits.TrailingZeros64(diff) >> 3)
  194. }
  195. checked += 8
  196. a = a[8:]
  197. b = b[8:]
  198. }
  199. b = b[:len(a)]
  200. for i := range a {
  201. if a[i] != b[i] {
  202. return i + checked
  203. }
  204. }
  205. return len(a) + checked
  206. }