compress.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. package fasthttp
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "os"
  7. "sync"
  8. "github.com/klauspost/compress/flate"
  9. "github.com/klauspost/compress/gzip"
  10. "github.com/klauspost/compress/zlib"
  11. "github.com/valyala/bytebufferpool"
  12. "github.com/valyala/fasthttp/stackless"
  13. )
  14. // Supported compression levels.
  15. const (
  16. CompressNoCompression = flate.NoCompression
  17. CompressBestSpeed = flate.BestSpeed
  18. CompressBestCompression = flate.BestCompression
  19. CompressDefaultCompression = 6 // flate.DefaultCompression
  20. CompressHuffmanOnly = -2 // flate.HuffmanOnly
  21. )
  22. func acquireGzipReader(r io.Reader) (*gzip.Reader, error) {
  23. v := gzipReaderPool.Get()
  24. if v == nil {
  25. return gzip.NewReader(r)
  26. }
  27. zr := v.(*gzip.Reader)
  28. if err := zr.Reset(r); err != nil {
  29. return nil, err
  30. }
  31. return zr, nil
  32. }
  33. func releaseGzipReader(zr *gzip.Reader) {
  34. zr.Close()
  35. gzipReaderPool.Put(zr)
  36. }
  37. var gzipReaderPool sync.Pool
  38. func acquireFlateReader(r io.Reader) (io.ReadCloser, error) {
  39. v := flateReaderPool.Get()
  40. if v == nil {
  41. zr, err := zlib.NewReader(r)
  42. if err != nil {
  43. return nil, err
  44. }
  45. return zr, nil
  46. }
  47. zr := v.(io.ReadCloser)
  48. if err := resetFlateReader(zr, r); err != nil {
  49. return nil, err
  50. }
  51. return zr, nil
  52. }
  53. func releaseFlateReader(zr io.ReadCloser) {
  54. zr.Close()
  55. flateReaderPool.Put(zr)
  56. }
  57. func resetFlateReader(zr io.ReadCloser, r io.Reader) error {
  58. zrr, ok := zr.(zlib.Resetter)
  59. if !ok {
  60. // sanity check. should only be called with a zlib.Reader
  61. panic("BUG: zlib.Reader doesn't implement zlib.Resetter???")
  62. }
  63. return zrr.Reset(r, nil)
  64. }
  65. var flateReaderPool sync.Pool
  66. func acquireStacklessGzipWriter(w io.Writer, level int) stackless.Writer {
  67. nLevel := normalizeCompressLevel(level)
  68. p := stacklessGzipWriterPoolMap[nLevel]
  69. v := p.Get()
  70. if v == nil {
  71. return stackless.NewWriter(w, func(w io.Writer) stackless.Writer {
  72. return acquireRealGzipWriter(w, level)
  73. })
  74. }
  75. sw := v.(stackless.Writer)
  76. sw.Reset(w)
  77. return sw
  78. }
  79. func releaseStacklessGzipWriter(sw stackless.Writer, level int) {
  80. sw.Close()
  81. nLevel := normalizeCompressLevel(level)
  82. p := stacklessGzipWriterPoolMap[nLevel]
  83. p.Put(sw)
  84. }
  85. func acquireRealGzipWriter(w io.Writer, level int) *gzip.Writer {
  86. nLevel := normalizeCompressLevel(level)
  87. p := realGzipWriterPoolMap[nLevel]
  88. v := p.Get()
  89. if v == nil {
  90. zw, err := gzip.NewWriterLevel(w, level)
  91. if err != nil {
  92. // gzip.NewWriterLevel only errors for invalid
  93. // compression levels. Clamp it to be min or max.
  94. if level < gzip.HuffmanOnly {
  95. level = gzip.HuffmanOnly
  96. } else {
  97. level = gzip.BestCompression
  98. }
  99. zw, _ = gzip.NewWriterLevel(w, level)
  100. }
  101. return zw
  102. }
  103. zw := v.(*gzip.Writer)
  104. zw.Reset(w)
  105. return zw
  106. }
  107. func releaseRealGzipWriter(zw *gzip.Writer, level int) {
  108. zw.Close()
  109. nLevel := normalizeCompressLevel(level)
  110. p := realGzipWriterPoolMap[nLevel]
  111. p.Put(zw)
  112. }
  113. var (
  114. stacklessGzipWriterPoolMap = newCompressWriterPoolMap()
  115. realGzipWriterPoolMap = newCompressWriterPoolMap()
  116. )
  117. // AppendGzipBytesLevel appends gzipped src to dst using the given
  118. // compression level and returns the resulting dst.
  119. //
  120. // Supported compression levels are:
  121. //
  122. // - CompressNoCompression
  123. // - CompressBestSpeed
  124. // - CompressBestCompression
  125. // - CompressDefaultCompression
  126. // - CompressHuffmanOnly
  127. func AppendGzipBytesLevel(dst, src []byte, level int) []byte {
  128. w := &byteSliceWriter{dst}
  129. WriteGzipLevel(w, src, level) //nolint:errcheck
  130. return w.b
  131. }
  132. // WriteGzipLevel writes gzipped p to w using the given compression level
  133. // and returns the number of compressed bytes written to w.
  134. //
  135. // Supported compression levels are:
  136. //
  137. // - CompressNoCompression
  138. // - CompressBestSpeed
  139. // - CompressBestCompression
  140. // - CompressDefaultCompression
  141. // - CompressHuffmanOnly
  142. func WriteGzipLevel(w io.Writer, p []byte, level int) (int, error) {
  143. switch w.(type) {
  144. case *byteSliceWriter,
  145. *bytes.Buffer,
  146. *bytebufferpool.ByteBuffer:
  147. // These writers don't block, so we can just use stacklessWriteGzip
  148. ctx := &compressCtx{
  149. w: w,
  150. p: p,
  151. level: level,
  152. }
  153. stacklessWriteGzip(ctx)
  154. return len(p), nil
  155. default:
  156. zw := acquireStacklessGzipWriter(w, level)
  157. n, err := zw.Write(p)
  158. releaseStacklessGzipWriter(zw, level)
  159. return n, err
  160. }
  161. }
  162. var stacklessWriteGzip = stackless.NewFunc(nonblockingWriteGzip)
  163. func nonblockingWriteGzip(ctxv interface{}) {
  164. ctx := ctxv.(*compressCtx)
  165. zw := acquireRealGzipWriter(ctx.w, ctx.level)
  166. zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway
  167. releaseRealGzipWriter(zw, ctx.level)
  168. }
  169. // WriteGzip writes gzipped p to w and returns the number of compressed
  170. // bytes written to w.
  171. func WriteGzip(w io.Writer, p []byte) (int, error) {
  172. return WriteGzipLevel(w, p, CompressDefaultCompression)
  173. }
  174. // AppendGzipBytes appends gzipped src to dst and returns the resulting dst.
  175. func AppendGzipBytes(dst, src []byte) []byte {
  176. return AppendGzipBytesLevel(dst, src, CompressDefaultCompression)
  177. }
  178. // WriteGunzip writes ungzipped p to w and returns the number of uncompressed
  179. // bytes written to w.
  180. func WriteGunzip(w io.Writer, p []byte) (int, error) {
  181. r := &byteSliceReader{p}
  182. zr, err := acquireGzipReader(r)
  183. if err != nil {
  184. return 0, err
  185. }
  186. n, err := copyZeroAlloc(w, zr)
  187. releaseGzipReader(zr)
  188. nn := int(n)
  189. if int64(nn) != n {
  190. return 0, fmt.Errorf("too much data gunzipped: %d", n)
  191. }
  192. return nn, err
  193. }
  194. // AppendGunzipBytes appends gunzipped src to dst and returns the resulting dst.
  195. func AppendGunzipBytes(dst, src []byte) ([]byte, error) {
  196. w := &byteSliceWriter{dst}
  197. _, err := WriteGunzip(w, src)
  198. return w.b, err
  199. }
  200. // AppendDeflateBytesLevel appends deflated src to dst using the given
  201. // compression level and returns the resulting dst.
  202. //
  203. // Supported compression levels are:
  204. //
  205. // - CompressNoCompression
  206. // - CompressBestSpeed
  207. // - CompressBestCompression
  208. // - CompressDefaultCompression
  209. // - CompressHuffmanOnly
  210. func AppendDeflateBytesLevel(dst, src []byte, level int) []byte {
  211. w := &byteSliceWriter{dst}
  212. WriteDeflateLevel(w, src, level) //nolint:errcheck
  213. return w.b
  214. }
  215. // WriteDeflateLevel writes deflated p to w using the given compression level
  216. // and returns the number of compressed bytes written to w.
  217. //
  218. // Supported compression levels are:
  219. //
  220. // - CompressNoCompression
  221. // - CompressBestSpeed
  222. // - CompressBestCompression
  223. // - CompressDefaultCompression
  224. // - CompressHuffmanOnly
  225. func WriteDeflateLevel(w io.Writer, p []byte, level int) (int, error) {
  226. switch w.(type) {
  227. case *byteSliceWriter,
  228. *bytes.Buffer,
  229. *bytebufferpool.ByteBuffer:
  230. // These writers don't block, so we can just use stacklessWriteDeflate
  231. ctx := &compressCtx{
  232. w: w,
  233. p: p,
  234. level: level,
  235. }
  236. stacklessWriteDeflate(ctx)
  237. return len(p), nil
  238. default:
  239. zw := acquireStacklessDeflateWriter(w, level)
  240. n, err := zw.Write(p)
  241. releaseStacklessDeflateWriter(zw, level)
  242. return n, err
  243. }
  244. }
  245. var stacklessWriteDeflate = stackless.NewFunc(nonblockingWriteDeflate)
  246. func nonblockingWriteDeflate(ctxv interface{}) {
  247. ctx := ctxv.(*compressCtx)
  248. zw := acquireRealDeflateWriter(ctx.w, ctx.level)
  249. zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway
  250. releaseRealDeflateWriter(zw, ctx.level)
  251. }
  252. type compressCtx struct {
  253. w io.Writer
  254. p []byte
  255. level int
  256. }
  257. // WriteDeflate writes deflated p to w and returns the number of compressed
  258. // bytes written to w.
  259. func WriteDeflate(w io.Writer, p []byte) (int, error) {
  260. return WriteDeflateLevel(w, p, CompressDefaultCompression)
  261. }
  262. // AppendDeflateBytes appends deflated src to dst and returns the resulting dst.
  263. func AppendDeflateBytes(dst, src []byte) []byte {
  264. return AppendDeflateBytesLevel(dst, src, CompressDefaultCompression)
  265. }
  266. // WriteInflate writes inflated p to w and returns the number of uncompressed
  267. // bytes written to w.
  268. func WriteInflate(w io.Writer, p []byte) (int, error) {
  269. r := &byteSliceReader{p}
  270. zr, err := acquireFlateReader(r)
  271. if err != nil {
  272. return 0, err
  273. }
  274. n, err := copyZeroAlloc(w, zr)
  275. releaseFlateReader(zr)
  276. nn := int(n)
  277. if int64(nn) != n {
  278. return 0, fmt.Errorf("too much data inflated: %d", n)
  279. }
  280. return nn, err
  281. }
  282. // AppendInflateBytes appends inflated src to dst and returns the resulting dst.
  283. func AppendInflateBytes(dst, src []byte) ([]byte, error) {
  284. w := &byteSliceWriter{dst}
  285. _, err := WriteInflate(w, src)
  286. return w.b, err
  287. }
  288. type byteSliceWriter struct {
  289. b []byte
  290. }
  291. func (w *byteSliceWriter) Write(p []byte) (int, error) {
  292. w.b = append(w.b, p...)
  293. return len(p), nil
  294. }
  295. type byteSliceReader struct {
  296. b []byte
  297. }
  298. func (r *byteSliceReader) Read(p []byte) (int, error) {
  299. if len(r.b) == 0 {
  300. return 0, io.EOF
  301. }
  302. n := copy(p, r.b)
  303. r.b = r.b[n:]
  304. return n, nil
  305. }
  306. func (r *byteSliceReader) ReadByte() (byte, error) {
  307. if len(r.b) == 0 {
  308. return 0, io.EOF
  309. }
  310. n := r.b[0]
  311. r.b = r.b[1:]
  312. return n, nil
  313. }
  314. func acquireStacklessDeflateWriter(w io.Writer, level int) stackless.Writer {
  315. nLevel := normalizeCompressLevel(level)
  316. p := stacklessDeflateWriterPoolMap[nLevel]
  317. v := p.Get()
  318. if v == nil {
  319. return stackless.NewWriter(w, func(w io.Writer) stackless.Writer {
  320. return acquireRealDeflateWriter(w, level)
  321. })
  322. }
  323. sw := v.(stackless.Writer)
  324. sw.Reset(w)
  325. return sw
  326. }
  327. func releaseStacklessDeflateWriter(sw stackless.Writer, level int) {
  328. sw.Close()
  329. nLevel := normalizeCompressLevel(level)
  330. p := stacklessDeflateWriterPoolMap[nLevel]
  331. p.Put(sw)
  332. }
  333. func acquireRealDeflateWriter(w io.Writer, level int) *zlib.Writer {
  334. nLevel := normalizeCompressLevel(level)
  335. p := realDeflateWriterPoolMap[nLevel]
  336. v := p.Get()
  337. if v == nil {
  338. zw, err := zlib.NewWriterLevel(w, level)
  339. if err != nil {
  340. // zlib.NewWriterLevel only errors for invalid
  341. // compression levels. Clamp it to be min or max.
  342. if level < zlib.HuffmanOnly {
  343. level = zlib.HuffmanOnly
  344. } else {
  345. level = zlib.BestCompression
  346. }
  347. zw, _ = zlib.NewWriterLevel(w, level)
  348. }
  349. return zw
  350. }
  351. zw := v.(*zlib.Writer)
  352. zw.Reset(w)
  353. return zw
  354. }
  355. func releaseRealDeflateWriter(zw *zlib.Writer, level int) {
  356. zw.Close()
  357. nLevel := normalizeCompressLevel(level)
  358. p := realDeflateWriterPoolMap[nLevel]
  359. p.Put(zw)
  360. }
  361. var (
  362. stacklessDeflateWriterPoolMap = newCompressWriterPoolMap()
  363. realDeflateWriterPoolMap = newCompressWriterPoolMap()
  364. )
  365. func newCompressWriterPoolMap() []*sync.Pool {
  366. // Initialize pools for all the compression levels defined
  367. // in https://pkg.go.dev/compress/flate#pkg-constants .
  368. // Compression levels are normalized with normalizeCompressLevel,
  369. // so the fit [0..11].
  370. var m []*sync.Pool
  371. for i := 0; i < 12; i++ {
  372. m = append(m, &sync.Pool{})
  373. }
  374. return m
  375. }
  376. func isFileCompressible(f *os.File, minCompressRatio float64) bool {
  377. // Try compressing the first 4kb of the file
  378. // and see if it can be compressed by more than
  379. // the given minCompressRatio.
  380. b := bytebufferpool.Get()
  381. zw := acquireStacklessGzipWriter(b, CompressDefaultCompression)
  382. lr := &io.LimitedReader{
  383. R: f,
  384. N: 4096,
  385. }
  386. _, err := copyZeroAlloc(zw, lr)
  387. releaseStacklessGzipWriter(zw, CompressDefaultCompression)
  388. f.Seek(0, 0) //nolint:errcheck
  389. if err != nil {
  390. return false
  391. }
  392. n := 4096 - lr.N
  393. zn := len(b.B)
  394. bytebufferpool.Put(b)
  395. return float64(zn) < float64(n)*minCompressRatio
  396. }
  397. // normalizes compression level into [0..11], so it could be used as an index
  398. // in *PoolMap.
  399. func normalizeCompressLevel(level int) int {
  400. // -2 is the lowest compression level - CompressHuffmanOnly
  401. // 9 is the highest compression level - CompressBestCompression
  402. if level < -2 || level > 9 {
  403. level = CompressDefaultCompression
  404. }
  405. return level + 2
  406. }