package fasthttp
import (
"bytes"
"fmt"
"io"
"sync"
"github.com/andybalholm/brotli"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp/stackless"
)
const (
CompressBrotliNoCompression = 0
CompressBrotliBestSpeed = brotli .BestSpeed
CompressBrotliBestCompression = brotli .BestCompression
CompressBrotliDefaultCompression = 4
)
func acquireBrotliReader(r io .Reader ) (*brotli .Reader , error ) {
v := brotliReaderPool .Get ()
if v == nil {
return brotli .NewReader (r ), nil
}
zr := v .(*brotli .Reader )
if err := zr .Reset (r ); err != nil {
return nil , err
}
return zr , nil
}
func releaseBrotliReader(zr *brotli .Reader ) {
brotliReaderPool .Put (zr )
}
var brotliReaderPool sync .Pool
func acquireStacklessBrotliWriter(w io .Writer , level int ) stackless .Writer {
nLevel := normalizeBrotliCompressLevel (level )
p := stacklessBrotliWriterPoolMap [nLevel ]
v := p .Get ()
if v == nil {
return stackless .NewWriter (w , func (w io .Writer ) stackless .Writer {
return acquireRealBrotliWriter (w , level )
})
}
sw := v .(stackless .Writer )
sw .Reset (w )
return sw
}
func releaseStacklessBrotliWriter(sw stackless .Writer , level int ) {
sw .Close ()
nLevel := normalizeBrotliCompressLevel (level )
p := stacklessBrotliWriterPoolMap [nLevel ]
p .Put (sw )
}
func acquireRealBrotliWriter(w io .Writer , level int ) *brotli .Writer {
nLevel := normalizeBrotliCompressLevel (level )
p := realBrotliWriterPoolMap [nLevel ]
v := p .Get ()
if v == nil {
zw := brotli .NewWriterLevel (w , level )
return zw
}
zw := v .(*brotli .Writer )
zw .Reset (w )
return zw
}
func releaseRealBrotliWriter(zw *brotli .Writer , level int ) {
zw .Close ()
nLevel := normalizeBrotliCompressLevel (level )
p := realBrotliWriterPoolMap [nLevel ]
p .Put (zw )
}
var (
stacklessBrotliWriterPoolMap = newCompressWriterPoolMap ()
realBrotliWriterPoolMap = newCompressWriterPoolMap ()
)
func AppendBrotliBytesLevel (dst , src []byte , level int ) []byte {
w := &byteSliceWriter {dst }
WriteBrotliLevel (w , src , level )
return w .b
}
func WriteBrotliLevel (w io .Writer , p []byte , level int ) (int , error ) {
switch w .(type ) {
case *byteSliceWriter ,
*bytes .Buffer ,
*bytebufferpool .ByteBuffer :
ctx := &compressCtx {
w : w ,
p : p ,
level : level ,
}
stacklessWriteBrotli (ctx )
return len (p ), nil
default :
zw := acquireStacklessBrotliWriter (w , level )
n , err := zw .Write (p )
releaseStacklessBrotliWriter (zw , level )
return n , err
}
}
var stacklessWriteBrotli = stackless .NewFunc (nonblockingWriteBrotli )
func nonblockingWriteBrotli(ctxv interface {}) {
ctx := ctxv .(*compressCtx )
zw := acquireRealBrotliWriter (ctx .w , ctx .level )
zw .Write (ctx .p )
releaseRealBrotliWriter (zw , ctx .level )
}
func WriteBrotli (w io .Writer , p []byte ) (int , error ) {
return WriteBrotliLevel (w , p , CompressBrotliDefaultCompression )
}
func AppendBrotliBytes (dst , src []byte ) []byte {
return AppendBrotliBytesLevel (dst , src , CompressBrotliDefaultCompression )
}
func WriteUnbrotli (w io .Writer , p []byte ) (int , error ) {
r := &byteSliceReader {p }
zr , err := acquireBrotliReader (r )
if err != nil {
return 0 , err
}
n , err := copyZeroAlloc (w , zr )
releaseBrotliReader (zr )
nn := int (n )
if int64 (nn ) != n {
return 0 , fmt .Errorf ("too much data unbrotlied: %d" , n )
}
return nn , err
}
func AppendUnbrotliBytes (dst , src []byte ) ([]byte , error ) {
w := &byteSliceWriter {dst }
_ , err := WriteUnbrotli (w , src )
return w .b , err
}
func normalizeBrotliCompressLevel(level int ) int {
if level < 0 || level > 11 {
level = CompressBrotliDefaultCompression
}
return level
}
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 .