package main import ( "errors" "io" "sync" "github.com/klauspost/compress/flate" ) // A Decompressor returns a new decompressing reader, reading from r. // The ReadCloser's Close method must be used to release associated resources. // The Decompressor itself must be safe to invoke from multiple goroutines // simultaneously, but each returned reader will be used only by // one goroutine at a time. type Decompressor func(r io.Reader) io.ReadCloser var flateReaderPool sync.Pool func newFlateReader(r io.Reader) io.ReadCloser { fr, ok := flateReaderPool.Get().(io.ReadCloser) if ok { fr.(flate.Resetter).Reset(r, nil) } else { fr = flate.NewReader(r) } return &pooledFlateReader{fr: fr} } type pooledFlateReader struct { mu sync.Mutex // guards Close and Read fr io.ReadCloser } func (r *pooledFlateReader) Read(p []byte) (n int, err error) { r.mu.Lock() defer r.mu.Unlock() if r.fr == nil { return 0, errors.New("Read after Close") } return r.fr.Read(p) } func (r *pooledFlateReader) Close() error { r.mu.Lock() defer r.mu.Unlock() var err error if r.fr != nil { err = r.fr.Close() flateReaderPool.Put(r.fr) r.fr = nil } return err } var ( decompressors sync.Map // map[uint16]Decompressor ) func init() { decompressors.Store(Store, Decompressor(io.NopCloser)) decompressors.Store(Deflate, Decompressor(newFlateReader)) } // RegisterDecompressor allows custom decompressors for a specified method ID. // The common methods Store and Deflate are built in. func RegisterDecompressor(method uint16, dcomp Decompressor) { if _, dup := decompressors.LoadOrStore(method, dcomp); dup { panic("decompressor already registered") } } func decompressor(method uint16) Decompressor { di, ok := decompressors.Load(method) if !ok { return nil } return di.(Decompressor) }