options.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. package lz4
  2. import (
  3. "fmt"
  4. "reflect"
  5. "runtime"
  6. "sync"
  7. )
  8. //go:generate go run golang.org/x/tools/cmd/stringer -type=BlockSize,CompressionLevel -output options_gen.go
  9. type (
  10. applier interface {
  11. Apply(...Option) error
  12. private()
  13. }
  14. // Option defines the parameters to setup an LZ4 Writer or Reader.
  15. Option func(applier) error
  16. )
  17. func (o Option) String() string {
  18. return o(nil).Error()
  19. }
  20. // Default options.
  21. var (
  22. DefaultBlockSizeOption = BlockSizeOption(Block4Mb)
  23. DefaultChecksumOption = ChecksumOption(true)
  24. DefaultConcurrency = ConcurrencyOption(1)
  25. defaultOnBlockDone = OnBlockDoneOption(nil)
  26. )
  27. const (
  28. Block64Kb BlockSize = 1 << (16 + iota*2)
  29. Block256Kb
  30. Block1Mb
  31. Block4Mb
  32. )
  33. var (
  34. blockPool64K = sync.Pool{New: func() interface{} { return make([]byte, Block64Kb) }}
  35. blockPool256K = sync.Pool{New: func() interface{} { return make([]byte, Block256Kb) }}
  36. blockPool1M = sync.Pool{New: func() interface{} { return make([]byte, Block1Mb) }}
  37. blockPool4M = sync.Pool{New: func() interface{} { return make([]byte, Block4Mb) }}
  38. )
  39. // BlockSizeIndex defines the size of the blocks to be compressed.
  40. type BlockSize uint32
  41. func (b BlockSize) isValid() bool {
  42. return b.index() > 0
  43. }
  44. func (b BlockSize) index() BlockSizeIndex {
  45. switch b {
  46. case Block64Kb:
  47. return 4
  48. case Block256Kb:
  49. return 5
  50. case Block1Mb:
  51. return 6
  52. case Block4Mb:
  53. return 7
  54. }
  55. return 0
  56. }
  57. type BlockSizeIndex uint8
  58. func (b BlockSizeIndex) isValid() bool {
  59. switch b {
  60. case 4, 5, 6, 7:
  61. return true
  62. }
  63. return false
  64. }
  65. func (b BlockSizeIndex) get() []byte {
  66. var buf interface{}
  67. switch b {
  68. case 4:
  69. buf = blockPool64K.Get()
  70. case 5:
  71. buf = blockPool256K.Get()
  72. case 6:
  73. buf = blockPool1M.Get()
  74. case 7:
  75. buf = blockPool4M.Get()
  76. }
  77. return buf.([]byte)
  78. }
  79. func (b BlockSizeIndex) put(buf []byte) {
  80. switch b {
  81. case 4:
  82. blockPool64K.Put(buf)
  83. case 5:
  84. blockPool256K.Put(buf)
  85. case 6:
  86. blockPool1M.Put(buf)
  87. case 7:
  88. blockPool4M.Put(buf)
  89. }
  90. }
  91. // BlockSizeOption defines the maximum size of compressed blocks (default=Block4Mb).
  92. func BlockSizeOption(size BlockSize) Option {
  93. return func(a applier) error {
  94. switch w := a.(type) {
  95. case nil:
  96. s := fmt.Sprintf("BlockSizeOption(%s)", size)
  97. return _error(s)
  98. case *Writer:
  99. if !size.isValid() {
  100. return fmt.Errorf("%w: %d", ErrOptionInvalidBlockSize, size)
  101. }
  102. w.frame.Descriptor.Flags.BlockSizeIndexSet(size.index())
  103. return nil
  104. }
  105. return ErrOptionNotApplicable
  106. }
  107. }
  108. // BlockChecksumOption enables or disables block checksum (default=false).
  109. func BlockChecksumOption(flag bool) Option {
  110. return func(a applier) error {
  111. switch w := a.(type) {
  112. case nil:
  113. s := fmt.Sprintf("BlockChecksumOption(%v)", flag)
  114. return _error(s)
  115. case *Writer:
  116. w.frame.Descriptor.Flags.BlockChecksumSet(flag)
  117. return nil
  118. }
  119. return ErrOptionNotApplicable
  120. }
  121. }
  122. // ChecksumOption enables/disables all blocks checksum (default=true).
  123. func ChecksumOption(flag bool) Option {
  124. return func(a applier) error {
  125. switch w := a.(type) {
  126. case nil:
  127. s := fmt.Sprintf("BlockChecksumOption(%v)", flag)
  128. return _error(s)
  129. case *Writer:
  130. w.frame.Descriptor.Flags.ContentChecksumSet(flag)
  131. return nil
  132. }
  133. return ErrOptionNotApplicable
  134. }
  135. }
  136. // SizeOption sets the size of the original uncompressed data (default=0).
  137. func SizeOption(size uint64) Option {
  138. return func(a applier) error {
  139. switch w := a.(type) {
  140. case nil:
  141. s := fmt.Sprintf("SizeOption(%d)", size)
  142. return _error(s)
  143. case *Writer:
  144. w.frame.Descriptor.Flags.SizeSet(size > 0)
  145. w.frame.Descriptor.ContentSize = size
  146. return nil
  147. }
  148. return ErrOptionNotApplicable
  149. }
  150. }
  151. // ConcurrencyOption sets the number of go routines used for compression.
  152. // If n<0, then the output of runtime.GOMAXPROCS(0) is used.
  153. func ConcurrencyOption(n int) Option {
  154. return func(a applier) error {
  155. switch w := a.(type) {
  156. case nil:
  157. s := fmt.Sprintf("ConcurrencyOption(%d)", n)
  158. return _error(s)
  159. case *Writer:
  160. switch n {
  161. case 0, 1:
  162. default:
  163. if n < 0 {
  164. n = runtime.GOMAXPROCS(0)
  165. }
  166. }
  167. w.num = n
  168. return nil
  169. }
  170. return ErrOptionNotApplicable
  171. }
  172. }
  173. // CompressionLevel defines the level of compression to use. The higher the better, but slower, compression.
  174. type CompressionLevel uint32
  175. const (
  176. Fast CompressionLevel = 0
  177. Level1 CompressionLevel = 1 << (8 + iota)
  178. Level2
  179. Level3
  180. Level4
  181. Level5
  182. Level6
  183. Level7
  184. Level8
  185. Level9
  186. )
  187. // CompressionLevelOption defines the compression level (default=Fast).
  188. func CompressionLevelOption(level CompressionLevel) Option {
  189. return func(a applier) error {
  190. switch w := a.(type) {
  191. case nil:
  192. s := fmt.Sprintf("CompressionLevelOption(%s)", level)
  193. return _error(s)
  194. case *Writer:
  195. switch level {
  196. case Fast, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9:
  197. default:
  198. return fmt.Errorf("%w: %d", ErrOptionInvalidCompressionLevel, level)
  199. }
  200. w.level = level
  201. return nil
  202. }
  203. return ErrOptionNotApplicable
  204. }
  205. }
  206. func onBlockDone(int) {}
  207. // OnBlockDoneOption is triggered
  208. func OnBlockDoneOption(handler func(size int)) Option {
  209. if handler == nil {
  210. handler = onBlockDone
  211. }
  212. return func(a applier) error {
  213. switch rw := a.(type) {
  214. case nil:
  215. s := fmt.Sprintf("OnBlockDoneOption(%s)", reflect.TypeOf(handler).String())
  216. return _error(s)
  217. case *Writer:
  218. rw.handler = handler
  219. case *Reader:
  220. rw.handler = handler
  221. }
  222. return nil
  223. }
  224. }