options.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  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. case *CompressingReader:
  52. size := uint32(size)
  53. if !lz4block.IsValid(size) {
  54. return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidBlockSize, size)
  55. }
  56. w.frame.Descriptor.Flags.BlockSizeIndexSet(lz4block.Index(size))
  57. return nil
  58. }
  59. return lz4errors.ErrOptionNotApplicable
  60. }
  61. }
  62. // BlockChecksumOption enables or disables block checksum (default=false).
  63. func BlockChecksumOption(flag bool) Option {
  64. return func(a applier) error {
  65. switch w := a.(type) {
  66. case nil:
  67. s := fmt.Sprintf("BlockChecksumOption(%v)", flag)
  68. return lz4errors.Error(s)
  69. case *Writer:
  70. w.frame.Descriptor.Flags.BlockChecksumSet(flag)
  71. return nil
  72. case *CompressingReader:
  73. w.frame.Descriptor.Flags.BlockChecksumSet(flag)
  74. return nil
  75. }
  76. return lz4errors.ErrOptionNotApplicable
  77. }
  78. }
  79. // ChecksumOption enables/disables all blocks or content checksum (default=true).
  80. func ChecksumOption(flag bool) Option {
  81. return func(a applier) error {
  82. switch w := a.(type) {
  83. case nil:
  84. s := fmt.Sprintf("ChecksumOption(%v)", flag)
  85. return lz4errors.Error(s)
  86. case *Writer:
  87. w.frame.Descriptor.Flags.ContentChecksumSet(flag)
  88. return nil
  89. case *CompressingReader:
  90. w.frame.Descriptor.Flags.ContentChecksumSet(flag)
  91. return nil
  92. }
  93. return lz4errors.ErrOptionNotApplicable
  94. }
  95. }
  96. // SizeOption sets the size of the original uncompressed data (default=0). It is useful to know the size of the
  97. // whole uncompressed data stream.
  98. func SizeOption(size uint64) Option {
  99. return func(a applier) error {
  100. switch w := a.(type) {
  101. case nil:
  102. s := fmt.Sprintf("SizeOption(%d)", size)
  103. return lz4errors.Error(s)
  104. case *Writer:
  105. w.frame.Descriptor.Flags.SizeSet(size > 0)
  106. w.frame.Descriptor.ContentSize = size
  107. return nil
  108. case *CompressingReader:
  109. w.frame.Descriptor.Flags.SizeSet(size > 0)
  110. w.frame.Descriptor.ContentSize = size
  111. return nil
  112. }
  113. return lz4errors.ErrOptionNotApplicable
  114. }
  115. }
  116. // ConcurrencyOption sets the number of go routines used for compression.
  117. // If n <= 0, then the output of runtime.GOMAXPROCS(0) is used.
  118. func ConcurrencyOption(n int) Option {
  119. if n <= 0 {
  120. n = runtime.GOMAXPROCS(0)
  121. }
  122. return func(a applier) error {
  123. switch rw := a.(type) {
  124. case nil:
  125. s := fmt.Sprintf("ConcurrencyOption(%d)", n)
  126. return lz4errors.Error(s)
  127. case *Writer:
  128. rw.num = n
  129. return nil
  130. case *Reader:
  131. rw.num = n
  132. return nil
  133. }
  134. return lz4errors.ErrOptionNotApplicable
  135. }
  136. }
  137. // CompressionLevel defines the level of compression to use. The higher the better, but slower, compression.
  138. type CompressionLevel uint32
  139. const (
  140. Fast CompressionLevel = 0
  141. Level1 CompressionLevel = 1 << (8 + iota)
  142. Level2
  143. Level3
  144. Level4
  145. Level5
  146. Level6
  147. Level7
  148. Level8
  149. Level9
  150. )
  151. // CompressionLevelOption defines the compression level (default=Fast).
  152. func CompressionLevelOption(level CompressionLevel) Option {
  153. return func(a applier) error {
  154. switch w := a.(type) {
  155. case nil:
  156. s := fmt.Sprintf("CompressionLevelOption(%s)", level)
  157. return lz4errors.Error(s)
  158. case *Writer:
  159. switch level {
  160. case Fast, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9:
  161. default:
  162. return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidCompressionLevel, level)
  163. }
  164. w.level = lz4block.CompressionLevel(level)
  165. return nil
  166. case *CompressingReader:
  167. switch level {
  168. case Fast, Level1, Level2, Level3, Level4, Level5, Level6, Level7, Level8, Level9:
  169. default:
  170. return fmt.Errorf("%w: %d", lz4errors.ErrOptionInvalidCompressionLevel, level)
  171. }
  172. w.level = lz4block.CompressionLevel(level)
  173. return nil
  174. }
  175. return lz4errors.ErrOptionNotApplicable
  176. }
  177. }
  178. func onBlockDone(int) {}
  179. // OnBlockDoneOption is triggered when a block has been processed. For a Writer, it is when is has been compressed,
  180. // for a Reader, it is when it has been uncompressed.
  181. func OnBlockDoneOption(handler func(size int)) Option {
  182. if handler == nil {
  183. handler = onBlockDone
  184. }
  185. return func(a applier) error {
  186. switch rw := a.(type) {
  187. case nil:
  188. s := fmt.Sprintf("OnBlockDoneOption(%s)", reflect.TypeOf(handler).String())
  189. return lz4errors.Error(s)
  190. case *Writer:
  191. rw.handler = handler
  192. return nil
  193. case *Reader:
  194. rw.handler = handler
  195. return nil
  196. case *CompressingReader:
  197. rw.handler = handler
  198. return nil
  199. }
  200. return lz4errors.ErrOptionNotApplicable
  201. }
  202. }
  203. // LegacyOption provides support for writing LZ4 frames in the legacy format.
  204. //
  205. // See https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md#legacy-frame.
  206. //
  207. // NB. compressed Linux kernel images use a tweaked LZ4 legacy format where
  208. // the compressed stream is followed by the original (uncompressed) size of
  209. // the kernel (https://events.static.linuxfound.org/sites/events/files/lcjpcojp13_klee.pdf).
  210. // This is also supported as a special case.
  211. func LegacyOption(legacy bool) Option {
  212. return func(a applier) error {
  213. switch rw := a.(type) {
  214. case nil:
  215. s := fmt.Sprintf("LegacyOption(%v)", legacy)
  216. return lz4errors.Error(s)
  217. case *Writer:
  218. rw.legacy = legacy
  219. return nil
  220. }
  221. return lz4errors.ErrOptionNotApplicable
  222. }
  223. }