zip.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // Copyright 2019+ Klaus Post. All rights reserved.
  2. // License information can be found in the LICENSE file.
  3. package zstd
  4. import (
  5. "errors"
  6. "io"
  7. "sync"
  8. )
  9. // ZipMethodWinZip is the method for Zstandard compressed data inside Zip files for WinZip.
  10. // See https://www.winzip.com/win/en/comp_info.html
  11. const ZipMethodWinZip = 93
  12. // ZipMethodPKWare is the original method number used by PKWARE to indicate Zstandard compression.
  13. // Deprecated: This has been deprecated by PKWARE, use ZipMethodWinZip instead for compression.
  14. // See https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.9.TXT
  15. const ZipMethodPKWare = 20
  16. var zipReaderPool sync.Pool
  17. // newZipReader creates a pooled zip decompressor.
  18. func newZipReader(r io.Reader) io.ReadCloser {
  19. dec, ok := zipReaderPool.Get().(*Decoder)
  20. if ok {
  21. dec.Reset(r)
  22. } else {
  23. d, err := NewReader(r, WithDecoderConcurrency(1), WithDecoderLowmem(true))
  24. if err != nil {
  25. panic(err)
  26. }
  27. dec = d
  28. }
  29. return &pooledZipReader{dec: dec}
  30. }
  31. type pooledZipReader struct {
  32. mu sync.Mutex // guards Close and Read
  33. dec *Decoder
  34. }
  35. func (r *pooledZipReader) Read(p []byte) (n int, err error) {
  36. r.mu.Lock()
  37. defer r.mu.Unlock()
  38. if r.dec == nil {
  39. return 0, errors.New("read after close or EOF")
  40. }
  41. dec, err := r.dec.Read(p)
  42. if err == io.EOF {
  43. err = r.dec.Reset(nil)
  44. zipReaderPool.Put(r.dec)
  45. r.dec = nil
  46. }
  47. return dec, err
  48. }
  49. func (r *pooledZipReader) Close() error {
  50. r.mu.Lock()
  51. defer r.mu.Unlock()
  52. var err error
  53. if r.dec != nil {
  54. err = r.dec.Reset(nil)
  55. zipReaderPool.Put(r.dec)
  56. r.dec = nil
  57. }
  58. return err
  59. }
  60. type pooledZipWriter struct {
  61. mu sync.Mutex // guards Close and Read
  62. enc *Encoder
  63. pool *sync.Pool
  64. }
  65. func (w *pooledZipWriter) Write(p []byte) (n int, err error) {
  66. w.mu.Lock()
  67. defer w.mu.Unlock()
  68. if w.enc == nil {
  69. return 0, errors.New("Write after Close")
  70. }
  71. return w.enc.Write(p)
  72. }
  73. func (w *pooledZipWriter) Close() error {
  74. w.mu.Lock()
  75. defer w.mu.Unlock()
  76. var err error
  77. if w.enc != nil {
  78. err = w.enc.Close()
  79. w.pool.Put(w.enc)
  80. w.enc = nil
  81. }
  82. return err
  83. }
  84. // ZipCompressor returns a compressor that can be registered with zip libraries.
  85. // The provided encoder options will be used on all encodes.
  86. func ZipCompressor(opts ...EOption) func(w io.Writer) (io.WriteCloser, error) {
  87. var pool sync.Pool
  88. return func(w io.Writer) (io.WriteCloser, error) {
  89. enc, ok := pool.Get().(*Encoder)
  90. if ok {
  91. enc.Reset(w)
  92. } else {
  93. var err error
  94. enc, err = NewWriter(w, opts...)
  95. if err != nil {
  96. return nil, err
  97. }
  98. }
  99. return &pooledZipWriter{enc: enc, pool: &pool}, nil
  100. }
  101. }
  102. // ZipDecompressor returns a decompressor that can be registered with zip libraries.
  103. // See ZipCompressor for example.
  104. func ZipDecompressor() func(r io.Reader) io.ReadCloser {
  105. return newZipReader
  106. }