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