writer.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package brotli
  2. import (
  3. "errors"
  4. "io"
  5. "github.com/andybalholm/brotli/matchfinder"
  6. )
  7. const (
  8. BestSpeed = 0
  9. BestCompression = 11
  10. DefaultCompression = 6
  11. )
  12. // WriterOptions configures Writer.
  13. type WriterOptions struct {
  14. // Quality controls the compression-speed vs compression-density trade-offs.
  15. // The higher the quality, the slower the compression. Range is 0 to 11.
  16. Quality int
  17. // LGWin is the base 2 logarithm of the sliding window size.
  18. // Range is 10 to 24. 0 indicates automatic configuration based on Quality.
  19. LGWin int
  20. }
  21. var (
  22. errEncode = errors.New("brotli: encode error")
  23. errWriterClosed = errors.New("brotli: Writer is closed")
  24. )
  25. // Writes to the returned writer are compressed and written to dst.
  26. // It is the caller's responsibility to call Close on the Writer when done.
  27. // Writes may be buffered and not flushed until Close.
  28. func NewWriter(dst io.Writer) *Writer {
  29. return NewWriterLevel(dst, DefaultCompression)
  30. }
  31. // NewWriterLevel is like NewWriter but specifies the compression level instead
  32. // of assuming DefaultCompression.
  33. // The compression level can be DefaultCompression or any integer value between
  34. // BestSpeed and BestCompression inclusive.
  35. func NewWriterLevel(dst io.Writer, level int) *Writer {
  36. return NewWriterOptions(dst, WriterOptions{
  37. Quality: level,
  38. })
  39. }
  40. // NewWriterOptions is like NewWriter but specifies WriterOptions
  41. func NewWriterOptions(dst io.Writer, options WriterOptions) *Writer {
  42. w := new(Writer)
  43. w.options = options
  44. w.Reset(dst)
  45. return w
  46. }
  47. // Reset discards the Writer's state and makes it equivalent to the result of
  48. // its original state from NewWriter or NewWriterLevel, but writing to dst
  49. // instead. This permits reusing a Writer rather than allocating a new one.
  50. func (w *Writer) Reset(dst io.Writer) {
  51. encoderInitState(w)
  52. w.params.quality = w.options.Quality
  53. if w.options.LGWin > 0 {
  54. w.params.lgwin = uint(w.options.LGWin)
  55. }
  56. w.dst = dst
  57. w.err = nil
  58. }
  59. func (w *Writer) writeChunk(p []byte, op int) (n int, err error) {
  60. if w.dst == nil {
  61. return 0, errWriterClosed
  62. }
  63. if w.err != nil {
  64. return 0, w.err
  65. }
  66. for {
  67. availableIn := uint(len(p))
  68. nextIn := p
  69. success := encoderCompressStream(w, op, &availableIn, &nextIn)
  70. bytesConsumed := len(p) - int(availableIn)
  71. p = p[bytesConsumed:]
  72. n += bytesConsumed
  73. if !success {
  74. return n, errEncode
  75. }
  76. if len(p) == 0 || w.err != nil {
  77. return n, w.err
  78. }
  79. }
  80. }
  81. // Flush outputs encoded data for all input provided to Write. The resulting
  82. // output can be decoded to match all input before Flush, but the stream is
  83. // not yet complete until after Close.
  84. // Flush has a negative impact on compression.
  85. func (w *Writer) Flush() error {
  86. _, err := w.writeChunk(nil, operationFlush)
  87. return err
  88. }
  89. // Close flushes remaining data to the decorated writer.
  90. func (w *Writer) Close() error {
  91. // If stream is already closed, it is reported by `writeChunk`.
  92. _, err := w.writeChunk(nil, operationFinish)
  93. w.dst = nil
  94. return err
  95. }
  96. // Write implements io.Writer. Flush or Close must be called to ensure that the
  97. // encoded bytes are actually flushed to the underlying Writer.
  98. func (w *Writer) Write(p []byte) (n int, err error) {
  99. return w.writeChunk(p, operationProcess)
  100. }
  101. type nopCloser struct {
  102. io.Writer
  103. }
  104. func (nopCloser) Close() error { return nil }
  105. // NewWriterV2 is like NewWriterLevel, but it uses the new implementation
  106. // based on the matchfinder package. It currently supports up to level 7;
  107. // if a higher level is specified, level 7 will be used.
  108. func NewWriterV2(dst io.Writer, level int) *matchfinder.Writer {
  109. var mf matchfinder.MatchFinder
  110. if level < 2 {
  111. mf = matchfinder.M0{Lazy: level == 1}
  112. } else {
  113. hashLen := 6
  114. if level >= 6 {
  115. hashLen = 5
  116. }
  117. chainLen := 64
  118. switch level {
  119. case 2:
  120. chainLen = 0
  121. case 3:
  122. chainLen = 1
  123. case 4:
  124. chainLen = 2
  125. case 5:
  126. chainLen = 4
  127. case 6:
  128. chainLen = 8
  129. }
  130. mf = &matchfinder.M4{
  131. MaxDistance: 1 << 20,
  132. ChainLength: chainLen,
  133. HashLen: hashLen,
  134. DistanceBitCost: 57,
  135. }
  136. }
  137. return &matchfinder.Writer{
  138. Dest: dst,
  139. MatchFinder: mf,
  140. Encoder: &Encoder{},
  141. BlockSize: 1 << 16,
  142. }
  143. }