source: code/trunk/vendor/github.com/valyala/fasthttp/brotli.go@ 145

Last change on this file since 145 was 145, checked in by Izuru Yakumo, 22 months ago

Updated the Makefile and vendored depedencies

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 5.1 KB
Line 
1package fasthttp
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "sync"
8
9 "github.com/andybalholm/brotli"
10 "github.com/valyala/bytebufferpool"
11 "github.com/valyala/fasthttp/stackless"
12)
13
14// Supported compression levels.
15const (
16 CompressBrotliNoCompression = 0
17 CompressBrotliBestSpeed = brotli.BestSpeed
18 CompressBrotliBestCompression = brotli.BestCompression
19
20 // Choose a default brotli compression level comparable to
21 // CompressDefaultCompression (gzip 6)
22 // See: https://github.com/valyala/fasthttp/issues/798#issuecomment-626293806
23 CompressBrotliDefaultCompression = 4
24)
25
26func acquireBrotliReader(r io.Reader) (*brotli.Reader, error) {
27 v := brotliReaderPool.Get()
28 if v == nil {
29 return brotli.NewReader(r), nil
30 }
31 zr := v.(*brotli.Reader)
32 if err := zr.Reset(r); err != nil {
33 return nil, err
34 }
35 return zr, nil
36}
37
38func releaseBrotliReader(zr *brotli.Reader) {
39 brotliReaderPool.Put(zr)
40}
41
42var brotliReaderPool sync.Pool
43
44func acquireStacklessBrotliWriter(w io.Writer, level int) stackless.Writer {
45 nLevel := normalizeBrotliCompressLevel(level)
46 p := stacklessBrotliWriterPoolMap[nLevel]
47 v := p.Get()
48 if v == nil {
49 return stackless.NewWriter(w, func(w io.Writer) stackless.Writer {
50 return acquireRealBrotliWriter(w, level)
51 })
52 }
53 sw := v.(stackless.Writer)
54 sw.Reset(w)
55 return sw
56}
57
58func releaseStacklessBrotliWriter(sw stackless.Writer, level int) {
59 sw.Close()
60 nLevel := normalizeBrotliCompressLevel(level)
61 p := stacklessBrotliWriterPoolMap[nLevel]
62 p.Put(sw)
63}
64
65func acquireRealBrotliWriter(w io.Writer, level int) *brotli.Writer {
66 nLevel := normalizeBrotliCompressLevel(level)
67 p := realBrotliWriterPoolMap[nLevel]
68 v := p.Get()
69 if v == nil {
70 zw := brotli.NewWriterLevel(w, level)
71 return zw
72 }
73 zw := v.(*brotli.Writer)
74 zw.Reset(w)
75 return zw
76}
77
78func releaseRealBrotliWriter(zw *brotli.Writer, level int) {
79 zw.Close()
80 nLevel := normalizeBrotliCompressLevel(level)
81 p := realBrotliWriterPoolMap[nLevel]
82 p.Put(zw)
83}
84
85var (
86 stacklessBrotliWriterPoolMap = newCompressWriterPoolMap()
87 realBrotliWriterPoolMap = newCompressWriterPoolMap()
88)
89
90// AppendBrotliBytesLevel appends brotlied src to dst using the given
91// compression level and returns the resulting dst.
92//
93// Supported compression levels are:
94//
95// * CompressBrotliNoCompression
96// * CompressBrotliBestSpeed
97// * CompressBrotliBestCompression
98// * CompressBrotliDefaultCompression
99func AppendBrotliBytesLevel(dst, src []byte, level int) []byte {
100 w := &byteSliceWriter{dst}
101 WriteBrotliLevel(w, src, level) //nolint:errcheck
102 return w.b
103}
104
105// WriteBrotliLevel writes brotlied p to w using the given compression level
106// and returns the number of compressed bytes written to w.
107//
108// Supported compression levels are:
109//
110// * CompressBrotliNoCompression
111// * CompressBrotliBestSpeed
112// * CompressBrotliBestCompression
113// * CompressBrotliDefaultCompression
114func WriteBrotliLevel(w io.Writer, p []byte, level int) (int, error) {
115 switch w.(type) {
116 case *byteSliceWriter,
117 *bytes.Buffer,
118 *bytebufferpool.ByteBuffer:
119 // These writers don't block, so we can just use stacklessWriteBrotli
120 ctx := &compressCtx{
121 w: w,
122 p: p,
123 level: level,
124 }
125 stacklessWriteBrotli(ctx)
126 return len(p), nil
127 default:
128 zw := acquireStacklessBrotliWriter(w, level)
129 n, err := zw.Write(p)
130 releaseStacklessBrotliWriter(zw, level)
131 return n, err
132 }
133}
134
135var stacklessWriteBrotli = stackless.NewFunc(nonblockingWriteBrotli)
136
137func nonblockingWriteBrotli(ctxv interface{}) {
138 ctx := ctxv.(*compressCtx)
139 zw := acquireRealBrotliWriter(ctx.w, ctx.level)
140
141 _, err := zw.Write(ctx.p)
142 if err != nil {
143 panic(fmt.Sprintf("BUG: brotli.Writer.Write for len(p)=%d returned unexpected error: %s", len(ctx.p), err))
144 }
145
146 releaseRealBrotliWriter(zw, ctx.level)
147}
148
149// WriteBrotli writes brotlied p to w and returns the number of compressed
150// bytes written to w.
151func WriteBrotli(w io.Writer, p []byte) (int, error) {
152 return WriteBrotliLevel(w, p, CompressBrotliDefaultCompression)
153}
154
155// AppendBrotliBytes appends brotlied src to dst and returns the resulting dst.
156func AppendBrotliBytes(dst, src []byte) []byte {
157 return AppendBrotliBytesLevel(dst, src, CompressBrotliDefaultCompression)
158}
159
160// WriteUnbrotli writes unbrotlied p to w and returns the number of uncompressed
161// bytes written to w.
162func WriteUnbrotli(w io.Writer, p []byte) (int, error) {
163 r := &byteSliceReader{p}
164 zr, err := acquireBrotliReader(r)
165 if err != nil {
166 return 0, err
167 }
168 n, err := copyZeroAlloc(w, zr)
169 releaseBrotliReader(zr)
170 nn := int(n)
171 if int64(nn) != n {
172 return 0, fmt.Errorf("too much data unbrotlied: %d", n)
173 }
174 return nn, err
175}
176
177// AppendUnbrotliBytes appends unbrotlied src to dst and returns the resulting dst.
178func AppendUnbrotliBytes(dst, src []byte) ([]byte, error) {
179 w := &byteSliceWriter{dst}
180 _, err := WriteUnbrotli(w, src)
181 return w.b, err
182}
183
184// normalizes compression level into [0..11], so it could be used as an index
185// in *PoolMap.
186func normalizeBrotliCompressLevel(level int) int {
187 // -2 is the lowest compression level - CompressHuffmanOnly
188 // 9 is the highest compression level - CompressBestCompression
189 if level < 0 || level > 11 {
190 level = CompressBrotliDefaultCompression
191 }
192 return level
193}
Note: See TracBrowser for help on using the repository browser.