decoder_options.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. "runtime"
  8. )
  9. // DOption is an option for creating a decoder.
  10. type DOption func(*decoderOptions) error
  11. // options retains accumulated state of multiple options.
  12. type decoderOptions struct {
  13. lowMem bool
  14. concurrent int
  15. maxDecodedSize uint64
  16. maxWindowSize uint64
  17. dicts []dict
  18. ignoreChecksum bool
  19. limitToCap bool
  20. decodeBufsBelow int
  21. }
  22. func (o *decoderOptions) setDefault() {
  23. *o = decoderOptions{
  24. // use less ram: true for now, but may change.
  25. lowMem: true,
  26. concurrent: runtime.GOMAXPROCS(0),
  27. maxWindowSize: MaxWindowSize,
  28. decodeBufsBelow: 128 << 10,
  29. }
  30. if o.concurrent > 4 {
  31. o.concurrent = 4
  32. }
  33. o.maxDecodedSize = 64 << 30
  34. }
  35. // WithDecoderLowmem will set whether to use a lower amount of memory,
  36. // but possibly have to allocate more while running.
  37. func WithDecoderLowmem(b bool) DOption {
  38. return func(o *decoderOptions) error { o.lowMem = b; return nil }
  39. }
  40. // WithDecoderConcurrency sets the number of created decoders.
  41. // When decoding block with DecodeAll, this will limit the number
  42. // of possible concurrently running decodes.
  43. // When decoding streams, this will limit the number of
  44. // inflight blocks.
  45. // When decoding streams and setting maximum to 1,
  46. // no async decoding will be done.
  47. // When a value of 0 is provided GOMAXPROCS will be used.
  48. // By default this will be set to 4 or GOMAXPROCS, whatever is lower.
  49. func WithDecoderConcurrency(n int) DOption {
  50. return func(o *decoderOptions) error {
  51. if n < 0 {
  52. return errors.New("concurrency must be at least 1")
  53. }
  54. if n == 0 {
  55. o.concurrent = runtime.GOMAXPROCS(0)
  56. } else {
  57. o.concurrent = n
  58. }
  59. return nil
  60. }
  61. }
  62. // WithDecoderMaxMemory allows to set a maximum decoded size for in-memory
  63. // non-streaming operations or maximum window size for streaming operations.
  64. // This can be used to control memory usage of potentially hostile content.
  65. // Maximum is 1 << 63 bytes. Default is 64GiB.
  66. func WithDecoderMaxMemory(n uint64) DOption {
  67. return func(o *decoderOptions) error {
  68. if n == 0 {
  69. return errors.New("WithDecoderMaxMemory must be at least 1")
  70. }
  71. if n > 1<<63 {
  72. return errors.New("WithDecoderMaxmemory must be less than 1 << 63")
  73. }
  74. o.maxDecodedSize = n
  75. return nil
  76. }
  77. }
  78. // WithDecoderDicts allows to register one or more dictionaries for the decoder.
  79. // If several dictionaries with the same ID is provided the last one will be used.
  80. func WithDecoderDicts(dicts ...[]byte) DOption {
  81. return func(o *decoderOptions) error {
  82. for _, b := range dicts {
  83. d, err := loadDict(b)
  84. if err != nil {
  85. return err
  86. }
  87. o.dicts = append(o.dicts, *d)
  88. }
  89. return nil
  90. }
  91. }
  92. // WithDecoderMaxWindow allows to set a maximum window size for decodes.
  93. // This allows rejecting packets that will cause big memory usage.
  94. // The Decoder will likely allocate more memory based on the WithDecoderLowmem setting.
  95. // If WithDecoderMaxMemory is set to a lower value, that will be used.
  96. // Default is 512MB, Maximum is ~3.75 TB as per zstandard spec.
  97. func WithDecoderMaxWindow(size uint64) DOption {
  98. return func(o *decoderOptions) error {
  99. if size < MinWindowSize {
  100. return errors.New("WithMaxWindowSize must be at least 1KB, 1024 bytes")
  101. }
  102. if size > (1<<41)+7*(1<<38) {
  103. return errors.New("WithMaxWindowSize must be less than (1<<41) + 7*(1<<38) ~ 3.75TB")
  104. }
  105. o.maxWindowSize = size
  106. return nil
  107. }
  108. }
  109. // WithDecodeAllCapLimit will limit DecodeAll to decoding cap(dst)-len(dst) bytes,
  110. // or any size set in WithDecoderMaxMemory.
  111. // This can be used to limit decoding to a specific maximum output size.
  112. // Disabled by default.
  113. func WithDecodeAllCapLimit(b bool) DOption {
  114. return func(o *decoderOptions) error {
  115. o.limitToCap = b
  116. return nil
  117. }
  118. }
  119. // WithDecodeBuffersBelow will fully decode readers that have a
  120. // `Bytes() []byte` and `Len() int` interface similar to bytes.Buffer.
  121. // This typically uses less allocations but will have the full decompressed object in memory.
  122. // Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less.
  123. // Default is 128KiB.
  124. func WithDecodeBuffersBelow(size int) DOption {
  125. return func(o *decoderOptions) error {
  126. o.decodeBufsBelow = size
  127. return nil
  128. }
  129. }
  130. // IgnoreChecksum allows to forcibly ignore checksum checking.
  131. func IgnoreChecksum(b bool) DOption {
  132. return func(o *decoderOptions) error {
  133. o.ignoreChecksum = b
  134. return nil
  135. }
  136. }