compress.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. package fasthttp
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "io/fs"
  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{b: 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 (
  163. stacklessWriteGzipOnce sync.Once
  164. stacklessWriteGzipFunc func(ctx any) bool
  165. )
  166. func stacklessWriteGzip(ctx any) {
  167. stacklessWriteGzipOnce.Do(func() {
  168. stacklessWriteGzipFunc = stackless.NewFunc(nonblockingWriteGzip)
  169. })
  170. stacklessWriteGzipFunc(ctx)
  171. }
  172. func nonblockingWriteGzip(ctxv any) {
  173. ctx := ctxv.(*compressCtx)
  174. zw := acquireRealGzipWriter(ctx.w, ctx.level)
  175. zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway
  176. releaseRealGzipWriter(zw, ctx.level)
  177. }
  178. // WriteGzip writes gzipped p to w and returns the number of compressed
  179. // bytes written to w.
  180. func WriteGzip(w io.Writer, p []byte) (int, error) {
  181. return WriteGzipLevel(w, p, CompressDefaultCompression)
  182. }
  183. // AppendGzipBytes appends gzipped src to dst and returns the resulting dst.
  184. func AppendGzipBytes(dst, src []byte) []byte {
  185. return AppendGzipBytesLevel(dst, src, CompressDefaultCompression)
  186. }
  187. // WriteGunzip writes ungzipped p to w and returns the number of uncompressed
  188. // bytes written to w.
  189. func WriteGunzip(w io.Writer, p []byte) (int, error) {
  190. return writeGunzip(w, p, 0)
  191. }
  192. func writeGunzip(w io.Writer, p []byte, maxBodySize int) (int, error) {
  193. r := &byteSliceReader{b: p}
  194. zr, err := acquireGzipReader(r)
  195. if err != nil {
  196. return 0, err
  197. }
  198. n, err := copyZeroAllocWithLimit(w, zr, maxBodySize)
  199. releaseGzipReader(zr)
  200. nn := int(n)
  201. if int64(nn) != n {
  202. return 0, fmt.Errorf("too much data gunzipped: %d", n)
  203. }
  204. return nn, err
  205. }
  206. // AppendGunzipBytes appends gunzipped src to dst and returns the resulting dst.
  207. func AppendGunzipBytes(dst, src []byte) ([]byte, error) {
  208. w := &byteSliceWriter{b: dst}
  209. _, err := WriteGunzip(w, src)
  210. return w.b, err
  211. }
  212. // AppendDeflateBytesLevel appends deflated src to dst using the given
  213. // compression level and returns the resulting dst.
  214. //
  215. // Supported compression levels are:
  216. //
  217. // - CompressNoCompression
  218. // - CompressBestSpeed
  219. // - CompressBestCompression
  220. // - CompressDefaultCompression
  221. // - CompressHuffmanOnly
  222. func AppendDeflateBytesLevel(dst, src []byte, level int) []byte {
  223. w := &byteSliceWriter{b: dst}
  224. WriteDeflateLevel(w, src, level) //nolint:errcheck
  225. return w.b
  226. }
  227. // WriteDeflateLevel writes deflated p to w using the given compression level
  228. // and returns the number of compressed bytes written to w.
  229. //
  230. // Supported compression levels are:
  231. //
  232. // - CompressNoCompression
  233. // - CompressBestSpeed
  234. // - CompressBestCompression
  235. // - CompressDefaultCompression
  236. // - CompressHuffmanOnly
  237. func WriteDeflateLevel(w io.Writer, p []byte, level int) (int, error) {
  238. switch w.(type) {
  239. case *byteSliceWriter,
  240. *bytes.Buffer,
  241. *bytebufferpool.ByteBuffer:
  242. // These writers don't block, so we can just use stacklessWriteDeflate
  243. ctx := &compressCtx{
  244. w: w,
  245. p: p,
  246. level: level,
  247. }
  248. stacklessWriteDeflate(ctx)
  249. return len(p), nil
  250. default:
  251. zw := acquireStacklessDeflateWriter(w, level)
  252. n, err := zw.Write(p)
  253. releaseStacklessDeflateWriter(zw, level)
  254. return n, err
  255. }
  256. }
  257. var (
  258. stacklessWriteDeflateOnce sync.Once
  259. stacklessWriteDeflateFunc func(ctx any) bool
  260. )
  261. func stacklessWriteDeflate(ctx any) {
  262. stacklessWriteDeflateOnce.Do(func() {
  263. stacklessWriteDeflateFunc = stackless.NewFunc(nonblockingWriteDeflate)
  264. })
  265. stacklessWriteDeflateFunc(ctx)
  266. }
  267. func nonblockingWriteDeflate(ctxv any) {
  268. ctx := ctxv.(*compressCtx)
  269. zw := acquireRealDeflateWriter(ctx.w, ctx.level)
  270. zw.Write(ctx.p) //nolint:errcheck // no way to handle this error anyway
  271. releaseRealDeflateWriter(zw, ctx.level)
  272. }
  273. type compressCtx struct {
  274. w io.Writer
  275. p []byte
  276. level int
  277. }
  278. // WriteDeflate writes deflated p to w and returns the number of compressed
  279. // bytes written to w.
  280. func WriteDeflate(w io.Writer, p []byte) (int, error) {
  281. return WriteDeflateLevel(w, p, CompressDefaultCompression)
  282. }
  283. // AppendDeflateBytes appends deflated src to dst and returns the resulting dst.
  284. func AppendDeflateBytes(dst, src []byte) []byte {
  285. return AppendDeflateBytesLevel(dst, src, CompressDefaultCompression)
  286. }
  287. // WriteInflate writes inflated p to w and returns the number of uncompressed
  288. // bytes written to w.
  289. func WriteInflate(w io.Writer, p []byte) (int, error) {
  290. return writeInflate(w, p, 0)
  291. }
  292. func writeInflate(w io.Writer, p []byte, maxBodySize int) (int, error) {
  293. r := &byteSliceReader{b: p}
  294. zr, err := acquireFlateReader(r)
  295. if err != nil {
  296. return 0, err
  297. }
  298. n, err := copyZeroAllocWithLimit(w, zr, maxBodySize)
  299. releaseFlateReader(zr)
  300. nn := int(n)
  301. if int64(nn) != n {
  302. return 0, fmt.Errorf("too much data inflated: %d", n)
  303. }
  304. return nn, err
  305. }
  306. // AppendInflateBytes appends inflated src to dst and returns the resulting dst.
  307. func AppendInflateBytes(dst, src []byte) ([]byte, error) {
  308. w := &byteSliceWriter{b: dst}
  309. _, err := WriteInflate(w, src)
  310. return w.b, err
  311. }
  312. type byteSliceWriter struct {
  313. b []byte
  314. }
  315. func (w *byteSliceWriter) Write(p []byte) (int, error) {
  316. w.b = append(w.b, p...)
  317. return len(p), nil
  318. }
  319. func (w *byteSliceWriter) WriteString(s string) (int, error) {
  320. w.b = append(w.b, s...)
  321. return len(s), nil
  322. }
  323. type byteSliceReader struct {
  324. b []byte
  325. }
  326. func (r *byteSliceReader) Read(p []byte) (int, error) {
  327. if len(r.b) == 0 {
  328. return 0, io.EOF
  329. }
  330. n := copy(p, r.b)
  331. r.b = r.b[n:]
  332. return n, nil
  333. }
  334. func (r *byteSliceReader) ReadByte() (byte, error) {
  335. if len(r.b) == 0 {
  336. return 0, io.EOF
  337. }
  338. n := r.b[0]
  339. r.b = r.b[1:]
  340. return n, nil
  341. }
  342. func acquireStacklessDeflateWriter(w io.Writer, level int) stackless.Writer {
  343. nLevel := normalizeCompressLevel(level)
  344. p := stacklessDeflateWriterPoolMap[nLevel]
  345. v := p.Get()
  346. if v == nil {
  347. return stackless.NewWriter(w, func(w io.Writer) stackless.Writer {
  348. return acquireRealDeflateWriter(w, level)
  349. })
  350. }
  351. sw := v.(stackless.Writer)
  352. sw.Reset(w)
  353. return sw
  354. }
  355. func releaseStacklessDeflateWriter(sw stackless.Writer, level int) {
  356. sw.Close()
  357. nLevel := normalizeCompressLevel(level)
  358. p := stacklessDeflateWriterPoolMap[nLevel]
  359. p.Put(sw)
  360. }
  361. func acquireRealDeflateWriter(w io.Writer, level int) *zlib.Writer {
  362. nLevel := normalizeCompressLevel(level)
  363. p := realDeflateWriterPoolMap[nLevel]
  364. v := p.Get()
  365. if v == nil {
  366. zw, err := zlib.NewWriterLevel(w, level)
  367. if err != nil {
  368. // zlib.NewWriterLevel only errors for invalid
  369. // compression levels. Clamp it to be min or max.
  370. if level < zlib.HuffmanOnly {
  371. level = zlib.HuffmanOnly
  372. } else {
  373. level = zlib.BestCompression
  374. }
  375. zw, _ = zlib.NewWriterLevel(w, level)
  376. }
  377. return zw
  378. }
  379. zw := v.(*zlib.Writer)
  380. zw.Reset(w)
  381. return zw
  382. }
  383. func releaseRealDeflateWriter(zw *zlib.Writer, level int) {
  384. zw.Close()
  385. nLevel := normalizeCompressLevel(level)
  386. p := realDeflateWriterPoolMap[nLevel]
  387. p.Put(zw)
  388. }
  389. var (
  390. stacklessDeflateWriterPoolMap = newCompressWriterPoolMap()
  391. realDeflateWriterPoolMap = newCompressWriterPoolMap()
  392. )
  393. func newCompressWriterPoolMap() []*sync.Pool {
  394. // Initialize pools for all the compression levels defined
  395. // in https://pkg.go.dev/compress/flate#pkg-constants .
  396. // Compression levels are normalized with normalizeCompressLevel,
  397. // so the fit [0..11].
  398. m := make([]*sync.Pool, 0, 12)
  399. for range 12 {
  400. m = append(m, &sync.Pool{})
  401. }
  402. return m
  403. }
  404. func isFileCompressible(f fs.File, minCompressRatio float64) bool {
  405. // Try compressing the first 4kb of the file
  406. // and see if it can be compressed by more than
  407. // the given minCompressRatio.
  408. b := bytebufferpool.Get()
  409. zw := acquireStacklessGzipWriter(b, CompressDefaultCompression)
  410. lr := &io.LimitedReader{
  411. R: f,
  412. N: 4096,
  413. }
  414. _, err := copyZeroAlloc(zw, lr)
  415. releaseStacklessGzipWriter(zw, CompressDefaultCompression)
  416. seeker, ok := f.(io.Seeker)
  417. if !ok {
  418. return false
  419. }
  420. seeker.Seek(0, io.SeekStart) //nolint:errcheck
  421. if err != nil {
  422. return false
  423. }
  424. n := 4096 - lr.N
  425. zn := len(b.B)
  426. bytebufferpool.Put(b)
  427. return float64(zn) < float64(n)*minCompressRatio
  428. }
  429. // normalizes compression level into [0..11], so it could be used as an index
  430. // in *PoolMap.
  431. func normalizeCompressLevel(level int) int {
  432. // -2 is the lowest compression level - CompressHuffmanOnly
  433. // 9 is the highest compression level - CompressBestCompression
  434. if level < -2 || level > 9 {
  435. level = CompressDefaultCompression
  436. }
  437. return level + 2
  438. }