// Copyright 2017 The Gorilla WebSocket 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 websocket

import (
	
	
	
	
	
)

const (
	minCompressionLevel     = -2 // flate.HuffmanOnly not defined in Go < 1.6
	maxCompressionLevel     = flate.BestCompression
	defaultCompressionLevel = 1
)

var (
	flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
	flateReaderPool  = sync.Pool{New: func() interface{} {
		return flate.NewReader(nil)
	}}
)

func decompressNoContextTakeover( io.Reader) io.ReadCloser {
	const  =
	// Add four bytes as specified in RFC
	"\x00\x00\xff\xff" +
		// Add final block to squelch unexpected EOF error from flate reader.
		"\x01\x00\x00\xff\xff"

	,  := flateReaderPool.Get().(io.ReadCloser)
	.(flate.Resetter).Reset(io.MultiReader(, strings.NewReader()), nil)
	return &flateReadWrapper{}
}

func isValidCompressionLevel( int) bool {
	return minCompressionLevel <=  &&  <= maxCompressionLevel
}

func compressNoContextTakeover( io.WriteCloser,  int) io.WriteCloser {
	 := &flateWriterPools[-minCompressionLevel]
	 := &truncWriter{w: }
	,  := .Get().(*flate.Writer)
	if  == nil {
		, _ = flate.NewWriter(, )
	} else {
		.Reset()
	}
	return &flateWriteWrapper{fw: , tw: , p: }
}

// truncWriter is an io.Writer that writes all but the last four bytes of the
// stream to another io.Writer.
type truncWriter struct {
	w io.WriteCloser
	n int
	p [4]byte
}

func ( *truncWriter) ( []byte) (int, error) {
	 := 0

	// fill buffer first for simplicity.
	if .n < len(.p) {
		 = copy(.p[.n:], )
		 = [:]
		.n += 
		if len() == 0 {
			return , nil
		}
	}

	 := len()
	if  > len(.p) {
		 = len(.p)
	}

	if ,  := .w.Write(.p[:]);  != nil {
		return  + , 
	}

	copy(.p[:], .p[:])
	copy(.p[len(.p)-:], [len()-:])
	,  := .w.Write([:len()-])
	return  + , 
}

type flateWriteWrapper struct {
	fw *flate.Writer
	tw *truncWriter
	p  *sync.Pool
}

func ( *flateWriteWrapper) ( []byte) (int, error) {
	if .fw == nil {
		return 0, errWriteClosed
	}
	return .fw.Write()
}

func ( *flateWriteWrapper) () error {
	if .fw == nil {
		return errWriteClosed
	}
	 := .fw.Flush()
	.p.Put(.fw)
	.fw = nil
	if .tw.p != [4]byte{0, 0, 0xff, 0xff} {
		return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
	}
	 := .tw.w.Close()
	if  != nil {
		return 
	}
	return 
}

type flateReadWrapper struct {
	fr io.ReadCloser
}

func ( *flateReadWrapper) ( []byte) (int, error) {
	if .fr == nil {
		return 0, io.ErrClosedPipe
	}
	,  := .fr.Read()
	if  == io.EOF {
		// Preemptively place the reader back in the pool. This helps with
		// scenarios where the application does not call NextReader() soon after
		// this final read.
		.Close()
	}
	return , 
}

func ( *flateReadWrapper) () error {
	if .fr == nil {
		return io.ErrClosedPipe
	}
	 := .fr.Close()
	flateReaderPool.Put(.fr)
	.fr = nil
	return 
}