// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package zip

import (
	
	
	
	
)

// A Compressor returns a new compressing writer, writing to w.
// The WriteCloser's Close method must be used to flush pending data to w.
// The Compressor itself must be safe to invoke from multiple goroutines
// simultaneously, but each returned writer will be used only by
// one goroutine at a time.
type Compressor func(w io.Writer) (io.WriteCloser, error)

// A Decompressor returns a new decompressing reader, reading from r.
// The [io.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 flateWriterPool sync.Pool

func newFlateWriter( io.Writer) io.WriteCloser {
	,  := flateWriterPool.Get().(*flate.Writer)
	if  {
		.Reset()
	} else {
		, _ = flate.NewWriter(, 5)
	}
	return &pooledFlateWriter{fw: }
}

type pooledFlateWriter struct {
	mu sync.Mutex // guards Close and Write
	fw *flate.Writer
}

func ( *pooledFlateWriter) ( []byte) ( int,  error) {
	.mu.Lock()
	defer .mu.Unlock()
	if .fw == nil {
		return 0, errors.New("Write after Close")
	}
	return .fw.Write()
}

func ( *pooledFlateWriter) () error {
	.mu.Lock()
	defer .mu.Unlock()
	var  error
	if .fw != nil {
		 = .fw.Close()
		flateWriterPool.Put(.fw)
		.fw = nil
	}
	return 
}

var flateReaderPool sync.Pool

func newFlateReader( io.Reader) io.ReadCloser {
	,  := flateReaderPool.Get().(io.ReadCloser)
	if  {
		.(flate.Resetter).Reset(, nil)
	} else {
		 = flate.NewReader()
	}
	return &pooledFlateReader{fr: }
}

type pooledFlateReader struct {
	mu sync.Mutex // guards Close and Read
	fr io.ReadCloser
}

func ( *pooledFlateReader) ( []byte) ( int,  error) {
	.mu.Lock()
	defer .mu.Unlock()
	if .fr == nil {
		return 0, errors.New("Read after Close")
	}
	return .fr.Read()
}

func ( *pooledFlateReader) () error {
	.mu.Lock()
	defer .mu.Unlock()
	var  error
	if .fr != nil {
		 = .fr.Close()
		flateReaderPool.Put(.fr)
		.fr = nil
	}
	return 
}

var (
	compressors   sync.Map // map[uint16]Compressor
	decompressors sync.Map // map[uint16]Decompressor
)

func init() {
	compressors.Store(Store, Compressor(func( io.Writer) (io.WriteCloser, error) { return &nopCloser{}, nil }))
	compressors.Store(Deflate, Compressor(func( io.Writer) (io.WriteCloser, error) { return newFlateWriter(), nil }))

	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 ( uint16,  Decompressor) {
	if ,  := decompressors.LoadOrStore(, );  {
		panic("decompressor already registered")
	}
}

// RegisterCompressor registers custom compressors for a specified method ID.
// The common methods [Store] and [Deflate] are built in.
func ( uint16,  Compressor) {
	if ,  := compressors.LoadOrStore(, );  {
		panic("compressor already registered")
	}
}

func compressor( uint16) Compressor {
	,  := compressors.Load()
	if ! {
		return nil
	}
	return .(Compressor)
}

func decompressor( uint16) Decompressor {
	,  := decompressors.Load()
	if ! {
		return nil
	}
	return .(Decompressor)
}