decoder_options.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright 2019+ Klaus Post. All rights reserved.
  2. // License information can be found in the LICENSE file.
  3. // Based on work by Yann Collet, released under BSD License.
  4. package zstd
  5. import (
  6. "errors"
  7. "fmt"
  8. "math/bits"
  9. "runtime"
  10. )
  11. // DOption is an option for creating a decoder.
  12. type DOption func(*decoderOptions) error
  13. // options retains accumulated state of multiple options.
  14. type decoderOptions struct {
  15. lowMem bool
  16. concurrent int
  17. maxDecodedSize uint64
  18. maxWindowSize uint64
  19. dicts map[uint32]*dict
  20. ignoreChecksum bool
  21. limitToCap bool
  22. decodeBufsBelow int
  23. resetOpt bool
  24. }
  25. func (o *decoderOptions) setDefault() {
  26. *o = decoderOptions{
  27. // use less ram: true for now, but may change.
  28. lowMem: true,
  29. concurrent: runtime.GOMAXPROCS(0),
  30. maxWindowSize: MaxWindowSize,
  31. decodeBufsBelow: 128 << 10,
  32. }
  33. if o.concurrent > 4 {
  34. o.concurrent = 4
  35. }
  36. o.maxDecodedSize = 64 << 30
  37. }
  38. // WithDecoderLowmem will set whether to use a lower amount of memory,
  39. // but possibly have to allocate more while running.
  40. // Cannot be changed with ResetWithOptions.
  41. func WithDecoderLowmem(b bool) DOption {
  42. return func(o *decoderOptions) error {
  43. if o.resetOpt && b != o.lowMem {
  44. return errors.New("WithDecoderLowmem cannot be changed on Reset")
  45. }
  46. o.lowMem = b
  47. return nil
  48. }
  49. }
  50. // WithDecoderConcurrency sets the number of created decoders.
  51. // When decoding block with DecodeAll, this will limit the number
  52. // of possible concurrently running decodes.
  53. // When decoding streams, this will limit the number of
  54. // inflight blocks.
  55. // When decoding streams and setting maximum to 1,
  56. // no async decoding will be done.
  57. // The value supplied must be at least 0.
  58. // When a value of 0 is provided GOMAXPROCS will be used.
  59. // By default this will be set to 4 or GOMAXPROCS, whatever is lower.
  60. // Cannot be changed with ResetWithOptions.
  61. func WithDecoderConcurrency(n int) DOption {
  62. return func(o *decoderOptions) error {
  63. if n < 0 {
  64. return errors.New("concurrency must be at least 0")
  65. }
  66. newVal := n
  67. if n == 0 {
  68. newVal = runtime.GOMAXPROCS(0)
  69. }
  70. if o.resetOpt && newVal != o.concurrent {
  71. return errors.New("WithDecoderConcurrency cannot be changed on Reset")
  72. }
  73. o.concurrent = newVal
  74. return nil
  75. }
  76. }
  77. // WithDecoderMaxMemory allows to set a maximum decoded size for in-memory
  78. // non-streaming operations or maximum window size for streaming operations.
  79. // This can be used to control memory usage of potentially hostile content.
  80. // Maximum is 1 << 63 bytes. Default is 64GiB.
  81. // Can be changed with ResetWithOptions.
  82. func WithDecoderMaxMemory(n uint64) DOption {
  83. return func(o *decoderOptions) error {
  84. if n == 0 {
  85. return errors.New("WithDecoderMaxMemory must be at least 1")
  86. }
  87. if n > 1<<63 {
  88. return errors.New("WithDecoderMaxmemory must be less than 1 << 63")
  89. }
  90. o.maxDecodedSize = n
  91. return nil
  92. }
  93. }
  94. // WithDecoderDicts allows to register one or more dictionaries for the decoder.
  95. //
  96. // Each slice in dict must be in the [dictionary format] produced by
  97. // "zstd --train" from the Zstandard reference implementation.
  98. //
  99. // If several dictionaries with the same ID are provided, the last one will be used.
  100. // Can be changed with ResetWithOptions.
  101. //
  102. // [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format
  103. func WithDecoderDicts(dicts ...[]byte) DOption {
  104. return func(o *decoderOptions) error {
  105. if o.dicts == nil {
  106. o.dicts = make(map[uint32]*dict)
  107. }
  108. for _, b := range dicts {
  109. d, err := loadDict(b)
  110. if err != nil {
  111. return err
  112. }
  113. o.dicts[d.id] = d
  114. }
  115. return nil
  116. }
  117. }
  118. // WithDecoderDictRaw registers a dictionary that may be used by the decoder.
  119. // The slice content can be arbitrary data.
  120. // Can be changed with ResetWithOptions.
  121. func WithDecoderDictRaw(id uint32, content []byte) DOption {
  122. return func(o *decoderOptions) error {
  123. if bits.UintSize > 32 && uint(len(content)) > dictMaxLength {
  124. return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content))
  125. }
  126. if o.dicts == nil {
  127. o.dicts = make(map[uint32]*dict)
  128. }
  129. o.dicts[id] = &dict{id: id, content: content, offsets: [3]int{1, 4, 8}}
  130. return nil
  131. }
  132. }
  133. // WithDecoderMaxWindow allows to set a maximum window size for decodes.
  134. // This allows rejecting packets that will cause big memory usage.
  135. // The Decoder will likely allocate more memory based on the WithDecoderLowmem setting.
  136. // If WithDecoderMaxMemory is set to a lower value, that will be used.
  137. // Default is 512MB, Maximum is ~3.75 TB as per zstandard spec.
  138. // Can be changed with ResetWithOptions.
  139. func WithDecoderMaxWindow(size uint64) DOption {
  140. return func(o *decoderOptions) error {
  141. if size < MinWindowSize {
  142. return errors.New("WithMaxWindowSize must be at least 1KB, 1024 bytes")
  143. }
  144. if size > (1<<41)+7*(1<<38) {
  145. return errors.New("WithMaxWindowSize must be less than (1<<41) + 7*(1<<38) ~ 3.75TB")
  146. }
  147. o.maxWindowSize = size
  148. return nil
  149. }
  150. }
  151. // WithDecodeAllCapLimit will limit DecodeAll to decoding cap(dst)-len(dst) bytes,
  152. // or any size set in WithDecoderMaxMemory.
  153. // This can be used to limit decoding to a specific maximum output size.
  154. // Disabled by default.
  155. // Can be changed with ResetWithOptions.
  156. func WithDecodeAllCapLimit(b bool) DOption {
  157. return func(o *decoderOptions) error {
  158. o.limitToCap = b
  159. return nil
  160. }
  161. }
  162. // WithDecodeBuffersBelow will fully decode readers that have a
  163. // `Bytes() []byte` and `Len() int` interface similar to bytes.Buffer.
  164. // This typically uses less allocations but will have the full decompressed object in memory.
  165. // Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less.
  166. // Default is 128KiB.
  167. // Cannot be changed with ResetWithOptions.
  168. func WithDecodeBuffersBelow(size int) DOption {
  169. return func(o *decoderOptions) error {
  170. if o.resetOpt && size != o.decodeBufsBelow {
  171. return errors.New("WithDecodeBuffersBelow cannot be changed on Reset")
  172. }
  173. o.decodeBufsBelow = size
  174. return nil
  175. }
  176. }
  177. // IgnoreChecksum allows to forcibly ignore checksum checking.
  178. // Can be changed with ResetWithOptions.
  179. func IgnoreChecksum(b bool) DOption {
  180. return func(o *decoderOptions) error {
  181. o.ignoreChecksum = b
  182. return nil
  183. }
  184. }
  185. // WithDecoderDictDelete removes dictionaries by ID.
  186. // If no ids are passed, all dictionaries are deleted.
  187. // Should be used with ResetWithOptions.
  188. func WithDecoderDictDelete(ids ...uint32) DOption {
  189. return func(o *decoderOptions) error {
  190. if len(ids) == 0 {
  191. clear(o.dicts)
  192. }
  193. for _, id := range ids {
  194. delete(o.dicts, id)
  195. }
  196. return nil
  197. }
  198. }