encoder_fast.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package brotli
  2. import (
  3. "math"
  4. "github.com/andybalholm/brotli/matchfinder"
  5. )
  6. func gaussianProbability(x, mean, stdDev float64) float64 {
  7. return math.Exp(-(x-mean)*(x-mean)/(2*stdDev*stdDev)) / math.Sqrt(2*math.Pi*stdDev*stdDev)
  8. }
  9. // A FastEncoder implements the matchfinder.Encoder interface, writing in Brotli
  10. // format. It uses a simplified encoding (like level 0 in the reference
  11. // implementation) to save time.
  12. type FastEncoder struct {
  13. wroteHeader bool
  14. bw bitWriter
  15. commandHisto [704]uint32
  16. distanceHisto [64]uint32
  17. }
  18. func (e *FastEncoder) Reset() {
  19. e.wroteHeader = false
  20. e.bw = bitWriter{}
  21. }
  22. func (e *FastEncoder) Encode(dst []byte, src []byte, matches []matchfinder.Match, lastBlock bool) []byte {
  23. e.bw.dst = dst
  24. if !e.wroteHeader {
  25. e.bw.writeBits(4, 15)
  26. e.wroteHeader = true
  27. // Fill the histograms with default statistics.
  28. // For the command codes we're using for insert lengths (insert + 2-byte copy),
  29. // fill the histogram with a Zipf-squared distribution.
  30. for i := range 24 {
  31. e.commandHisto[combineLengthCodes(uint16(i), 0, false)] = uint32(2000 / (i + 1) / (i + 1))
  32. }
  33. // For the command codes we're using for copy lengths (0 insert + copy
  34. // (length - 2), with repeat distance),
  35. // fill the histogram with Zipf distribution starting at code 1 (match length 5),
  36. // but a smaller frequency for code 0.
  37. e.commandHisto[combineLengthCodes(0, 0, true)] = 50
  38. for i := 1; i < 24; i++ {
  39. e.commandHisto[combineLengthCodes(0, uint16(i), i < 16)] = uint32(800 / i)
  40. }
  41. // Fill in the combined codes for short insert and copy lengths.
  42. for insertCode := range 6 {
  43. copyCode := 2
  44. e.commandHisto[128+insertCode<<3+copyCode] = uint32(100 / (insertCode + 1) / (insertCode + 1) / copyCode)
  45. for copyCode := 3; copyCode < 8; copyCode++ {
  46. e.commandHisto[128+insertCode<<3+copyCode] = uint32(343 / (insertCode + 1) / (insertCode + 1) / copyCode)
  47. }
  48. }
  49. // Fill e.distanceHisto with a normal distribution.
  50. e.distanceHisto[0] = 100
  51. for i := 16; i < 64; i++ {
  52. e.distanceHisto[i] = max(uint32(gaussianProbability(float64(i), 32, 8)*10000), 1)
  53. }
  54. }
  55. if len(src) == 0 {
  56. if lastBlock {
  57. e.bw.writeBits(2, 3) // islast + isempty
  58. e.bw.jumpToByteBoundary()
  59. return e.bw.dst
  60. }
  61. return dst
  62. }
  63. var literalHisto [256]uint32
  64. for _, c := range src {
  65. literalHisto[c]++
  66. }
  67. storeMetaBlockHeaderBW(uint(len(src)), false, &e.bw)
  68. e.bw.writeBits(13, 0)
  69. var literalDepths [256]byte
  70. var literalBits [256]uint16
  71. buildAndStoreHuffmanTreeFastBW(literalHisto[:], uint(len(src)), 8, literalDepths[:], literalBits[:], &e.bw)
  72. var commandDepths [704]byte
  73. var commandBits [704]uint16
  74. commandCount := 0
  75. for _, n := range e.commandHisto {
  76. commandCount += int(n)
  77. }
  78. buildAndStoreHuffmanTreeFastBW(e.commandHisto[:], uint(commandCount), 10, commandDepths[:], commandBits[:], &e.bw)
  79. var distanceDepths [64]byte
  80. var distanceBits [64]uint16
  81. distanceCount := 0
  82. for _, n := range e.distanceHisto {
  83. distanceCount += int(n)
  84. }
  85. buildAndStoreHuffmanTreeFastBW(e.distanceHisto[:], uint(distanceCount), 6, distanceDepths[:], distanceBits[:], &e.bw)
  86. // Reset the statistics, starting with a count of 1 for each symbol we might use.
  87. for i := range 24 {
  88. e.commandHisto[combineLengthCodes(uint16(i), 0, false)] = 1
  89. }
  90. for i := range 24 {
  91. e.commandHisto[combineLengthCodes(0, uint16(i), i < 16)] = 1
  92. }
  93. for insertCode := range 6 {
  94. for copyCode := 2; copyCode < 8; copyCode++ {
  95. e.commandHisto[128+insertCode<<3+copyCode] = 1
  96. }
  97. }
  98. e.distanceHisto[0] = 1
  99. for i := 16; i < 64; i++ {
  100. e.distanceHisto[i] = 1
  101. }
  102. pos := 0
  103. for i, m := range matches {
  104. lengthFinished := false
  105. // Write a command with the appropriate insert length, and a copy length of 2.
  106. if m.Unmatched < 6 {
  107. var command int
  108. if m.Length < 10 && m.Length != 0 {
  109. // We can use a combined insert/copy code with no extra bits.
  110. command = m.Unmatched<<3 + m.Length - 2 + 128
  111. lengthFinished = true
  112. } else {
  113. command = m.Unmatched<<3 + 128
  114. }
  115. e.bw.writeBits(uint(commandDepths[command]), uint64(commandBits[command]))
  116. e.commandHisto[command]++
  117. } else {
  118. insertCode := getInsertLengthCode(uint(m.Unmatched))
  119. command := combineLengthCodes(insertCode, 0, false)
  120. e.bw.writeBits(uint(commandDepths[command]), uint64(commandBits[command]))
  121. e.bw.writeBits(uint(kInsExtra[insertCode]), uint64(m.Unmatched)-uint64(kInsBase[insertCode]))
  122. e.commandHisto[command]++
  123. }
  124. // Write the literals, if any.
  125. if m.Unmatched > 0 {
  126. for _, c := range src[pos : pos+m.Unmatched] {
  127. e.bw.writeBits(uint(literalDepths[c]), uint64(literalBits[c]))
  128. }
  129. }
  130. if m.Length != 0 {
  131. // Write the distance code.
  132. var distCode distanceCode
  133. if i == 0 || m.Distance != matches[i-1].Distance {
  134. distCode = getDistanceCode(m.Distance)
  135. }
  136. e.bw.writeBits(uint(distanceDepths[distCode.code]), uint64(distanceBits[distCode.code]))
  137. if distCode.nExtra > 0 {
  138. e.bw.writeBits(distCode.nExtra, distCode.extraBits)
  139. }
  140. e.distanceHisto[distCode.code]++
  141. // Write a command for the remainder of the match (after the first two bytes
  142. // from before), using the previous distance.
  143. switch {
  144. case lengthFinished:
  145. // We don't need to finish the length.
  146. case m.Length < 12:
  147. command := m.Length - 4
  148. e.bw.writeBits(uint(commandDepths[command]), uint64(commandBits[command]))
  149. e.commandHisto[command]++
  150. case m.Length < 72:
  151. copyCode := getCopyLengthCode(uint(m.Length - 2))
  152. command := combineLengthCodes(0, copyCode, true)
  153. e.bw.writeBits(uint(commandDepths[command]), uint64(commandBits[command]))
  154. e.bw.writeBits(uint(kCopyExtra[copyCode]), uint64(m.Length-2)-uint64(kCopyBase[copyCode]))
  155. e.commandHisto[command]++
  156. default:
  157. copyCode := getCopyLengthCode(uint(m.Length - 2))
  158. command := combineLengthCodes(0, copyCode, false)
  159. e.bw.writeBits(uint(commandDepths[command]), uint64(commandBits[command]))
  160. e.bw.writeBits(uint(kCopyExtra[copyCode]), uint64(m.Length-2)-uint64(kCopyBase[copyCode]))
  161. e.bw.writeBits(uint(distanceDepths[0]), uint64(distanceBits[0]))
  162. e.commandHisto[command]++
  163. e.distanceHisto[0]++
  164. }
  165. }
  166. pos += m.Unmatched + m.Length
  167. }
  168. if lastBlock {
  169. e.bw.writeBits(2, 3) // islast + isempty
  170. e.bw.jumpToByteBoundary()
  171. }
  172. return e.bw.dst
  173. }