package websocket
import (
"compress/flate"
"io"
"sync"
)
type CompressionMode int
const (
CompressionDisabled CompressionMode = iota
CompressionContextTakeover
CompressionNoContextTakeover
)
func (m CompressionMode ) opts () *compressionOptions {
return &compressionOptions {
clientNoContextTakeover : m == CompressionNoContextTakeover ,
serverNoContextTakeover : m == CompressionNoContextTakeover ,
}
}
type compressionOptions struct {
clientNoContextTakeover bool
serverNoContextTakeover bool
}
func (copts *compressionOptions ) String () string {
s := "permessage-deflate"
if copts .clientNoContextTakeover {
s += "; client_no_context_takeover"
}
if copts .serverNoContextTakeover {
s += "; server_no_context_takeover"
}
return s
}
const deflateMessageTail = "\x00\x00\xff\xff"
type trimLastFourBytesWriter struct {
w io .Writer
tail []byte
}
func (tw *trimLastFourBytesWriter ) reset () {
if tw != nil && tw .tail != nil {
tw .tail = tw .tail [:0 ]
}
}
func (tw *trimLastFourBytesWriter ) Write (p []byte ) (int , error ) {
if tw .tail == nil {
tw .tail = make ([]byte , 0 , 4 )
}
extra := len (tw .tail ) + len (p ) - 4
if extra <= 0 {
tw .tail = append (tw .tail , p ...)
return len (p ), nil
}
if extra > len (tw .tail ) {
extra = len (tw .tail )
}
if extra > 0 {
_ , err := tw .w .Write (tw .tail [:extra ])
if err != nil {
return 0 , err
}
n := copy (tw .tail , tw .tail [extra :])
tw .tail = tw .tail [:n ]
}
if len (p ) <= 4 {
tw .tail = append (tw .tail , p ...)
return len (p ), nil
}
tw .tail = append (tw .tail , p [len (p )-4 :]...)
p = p [:len (p )-4 ]
n , err := tw .w .Write (p )
return n + 4 , err
}
var flateReaderPool sync .Pool
func getFlateReader (r io .Reader , dict []byte ) io .Reader {
fr , ok := flateReaderPool .Get ().(io .Reader )
if !ok {
return flate .NewReaderDict (r , dict )
}
fr .(flate .Resetter ).Reset (r , dict )
return fr
}
func putFlateReader (fr io .Reader ) {
flateReaderPool .Put (fr )
}
var flateWriterPool sync .Pool
func getFlateWriter (w io .Writer ) *flate .Writer {
fw , ok := flateWriterPool .Get ().(*flate .Writer )
if !ok {
fw , _ = flate .NewWriter (w , flate .BestSpeed )
return fw
}
fw .Reset (w )
return fw
}
func putFlateWriter (w *flate .Writer ) {
flateWriterPool .Put (w )
}
type slidingWindow struct {
buf []byte
}
var swPoolMu sync .RWMutex
var swPool = map [int ]*sync .Pool {}
func slidingWindowPool (n int ) *sync .Pool {
swPoolMu .RLock ()
p , ok := swPool [n ]
swPoolMu .RUnlock ()
if ok {
return p
}
p = &sync .Pool {}
swPoolMu .Lock ()
swPool [n ] = p
swPoolMu .Unlock ()
return p
}
func (sw *slidingWindow ) init (n int ) {
if sw .buf != nil {
return
}
if n == 0 {
n = 32768
}
p := slidingWindowPool (n )
sw2 , ok := p .Get ().(*slidingWindow )
if ok {
*sw = *sw2
} else {
sw .buf = make ([]byte , 0 , n )
}
}
func (sw *slidingWindow ) close () {
sw .buf = sw .buf [:0 ]
swPoolMu .Lock ()
swPool [cap (sw .buf )].Put (sw )
swPoolMu .Unlock ()
}
func (sw *slidingWindow ) write (p []byte ) {
if len (p ) >= cap (sw .buf ) {
sw .buf = sw .buf [:cap (sw .buf )]
p = p [len (p )-cap (sw .buf ):]
copy (sw .buf , p )
return
}
left := cap (sw .buf ) - len (sw .buf )
if left < len (p ) {
spaceNeeded := len (p ) - left
copy (sw .buf , sw .buf [spaceNeeded :])
sw .buf = sw .buf [:len (sw .buf )-spaceNeeded ]
}
sw .buf = append (sw .buf , p ...)
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .