options.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package lz4
  2. import (
  3. "fmt"
  4. "reflect"
  5. "runtime"
  6. "github.com/pierrec/lz4/v4/internal/lz4block"
  7. "github.com/pierrec/lz4/v4/internal/lz4errors"
  8. )
  9. //go:generate go run golang.org/x/tools/cmd/stringer -type=BlockSize,CompressionLevel -output options_gen.go
  10. type (
  11. applier interface {
  12. Apply(...Option) error
  13. private()
  14. }
  15. // Option defines the parameters to setup an LZ4 Writer or Reader.
  16. Option func(applier) error
  17. )
  18. // String returns a string representation of the option with its parameter(s).
  19. func (o Option) String() string {
  20. return o(nil).Error()
  21. }
  22. // Default options.
  23. var (
  24. DefaultBlockSizeOption = BlockSizeOption(Block4Mb)
  25. DefaultChecksumOption = ChecksumOption(true)
  26. DefaultConcurrency = ConcurrencyOption(1)
  27. defaultOnBlockDone = OnBlockDoneOption(nil)
  28. )
  29. const (
  30. Block64Kb BlockSize = 1 << (16 + iota*2)
  31. Block256Kb
  32. Block1Mb
  33. Block4Mb
  34. )
  35. // BlockSizeIndex defines the size of the blocks to be compressed.
  36. type BlockSize uint32
  37. // BlockSizeOption defines the maximum size of compressed blocks (default=Block4Mb).
  38. func BlockSizeOption(size BlockSize) Option {
  39. return func(a applier) error {
  40. switch w := a.(type) {
  41. case nil:
  42. s := fmt.Sprintf("BlockSizeOption(%s)", size)
  43. return lz4errors.Error(s)
  44. case *Writer:
  45. size := uint32(size)
  46. if !lz4block.IsValid(size) {
  47. return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidBlockSize, size)
  48. }
  49. w.frame.Descriptor.Flags.BlockSizeIndexSet(lz4block.Index(size))
  50. return nil
  51. }
  52. return lz4errors.ErrOptionNotApplicable
  53. }
  54. }
  55. // BlockChecksumOption enables or disables block checksum (default=false).
  56. func BlockChecksumOption(flag bool) Option {
  57. return func(a applier) error {
  58. switch w := a.(type) {
  59. case nil:
  60. s := fmt.Sprintf("BlockChecksumOption(%v)", flag)
  61. return lz4errors.Error(s)
  62. case *Writer:
  63. w.frame.Descriptor.Flags.BlockChecksumSet(flag)
  64. return nil
  65. }
  66. return lz4errors.ErrOptionNotApplicable
  67. }
  68. }
  69. // ChecksumOption enables/disables all blocks or content checksum (default=true).
  70. func ChecksumOption(flag bool) Option {
  71. return func(a applier) error {
  72. switch w := a.(type) {
  73. case nil:
  74. s := fmt.Sprintf("ChecksumOption(%v)", flag)
  75. return lz4errors.Error(s)
  76. case *Writer:
  77. w.frame.Descriptor.Flags.ContentChecksumSet(flag)
  78. return nil
  79. }
  80. return lz4errors.ErrOptionNotApplicable
  81. }
  82. }
  83. // SizeOption sets the size of the original uncompressed data (default=0). It is useful to know the size of the
  84. // whole uncompressed data stream.
  85. func SizeOption(size uint64) Option {
  86. return func(a applier) error {
  87. switch w := a.(type) {
  88. case nil:
  89. s := fmt.Sprintf("SizeOption(%d)", size)
  90. return lz4errors.Error(s)
  91. case *Writer:
  92. w.frame.Descriptor.Flags.SizeSet(size > 0)
  93. w.frame.Descriptor.ContentSize = size
  94. return nil
  95. }
  96. return lz4errors.ErrOptionNotApplicable
  97. }
  98. }
  99. // ConcurrencyOption sets the number of go routines used for compression.
  100. // If n <= 0, then the output of runtime.GOMAXPROCS(0) is used.
  101. func ConcurrencyOption(n int) Option {
  102. if n <= 0 {
  103. n = runtime.GOMAXPROCS(0)
  104. }
  105. return func(a applier) error {
  106. switch rw := a.(type) {
  107. case nil:
  108. s := fmt.Sprintf("ConcurrencyOption(%d)", n)
  109. return lz4errors.Error(s)
  110. case *Writer:
  111. rw.num = n
  112. return nil
  113. case *Reader:
  114. rw.num = n
  115. return nil
  116. }
  117. return lz4errors.ErrOptionNotApplicable
  118. }
  119. }
  120. // CompressionLevel defines the level of compression to use. The higher the better, but slower, compression.
  121. type CompressionLevel uint32
  122. const (
  123. Fast CompressionLevel = 0
  124. Level1 CompressionLevel = 1 << (8 + iota)
  125. Level2
  126. Level3
  127. Level4
  128. Level5
  129. Level6
  130. Level7
  131. Level8
  132. Level9
  133. )
  134. // CompressionLevelOption defines the compression level (default=Fast).
  135. func CompressionLevelOption(level CompressionLevel) Option {
  136. return func(a applier) error {
  137. switch w := a.(type) {
  138. case nil:
  139. s := fmt.Sprintf("CompressionLevelOption(%s)", level)
  140. return lz4errors.Error(s)
  141. case *Writer:
  142. switch level {
  143. case Fast, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9:
  144. default:
  145. return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidCompressionLevel, level)
  146. }
  147. w.level = lz4block.CompressionLevel(level)
  148. return nil
  149. }
  150. return lz4errors.ErrOptionNotApplicable
  151. }
  152. }
  153. func onBlockDone(int) {}
  154. // OnBlockDoneOption is triggered when a block has been processed. For a Writer, it is when is has been compressed,
  155. // for a Reader, it is when it has been uncompressed.
  156. func OnBlockDoneOption(handler func(size int)) Option {
  157. if handler == nil {
  158. handler = onBlockDone
  159. }
  160. return func(a applier) error {
  161. switch rw := a.(type) {
  162. case nil:
  163. s := fmt.Sprintf("OnBlockDoneOption(%s)", reflect.TypeOf(handler).String())
  164. return lz4errors.Error(s)
  165. case *Writer:
  166. rw.handler = handler
  167. return nil
  168. case *Reader:
  169. rw.handler = handler
  170. return nil
  171. }
  172. return lz4errors.ErrOptionNotApplicable
  173. }
  174. }
  175. // LegacyOption provides support for writing LZ4 frames in the legacy format.
  176. //
  177. // See https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md#legacy-frame.
  178. //
  179. // NB. compressed Linux kernel images use a tweaked LZ4 legacy format where
  180. // the compressed stream is followed by the original (uncompressed) size of
  181. // the kernel (https://events.static.linuxfound.org/sites/events/files/lcjpcojp13_klee.pdf).
  182. // This is also supported as a special case.
  183. func LegacyOption(legacy bool) Option {
  184. return func(a applier) error {
  185. switch rw := a.(type) {
  186. case nil:
  187. s := fmt.Sprintf("LegacyOption(%v)", legacy)
  188. return lz4errors.Error(s)
  189. case *Writer:
  190. rw.legacy = legacy
  191. return nil
  192. }
  193. return lz4errors.ErrOptionNotApplicable
  194. }
  195. }