gzip.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. *
  3. * Copyright 2017 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Package gzip implements and registers the gzip compressor
  19. // during the initialization.
  20. //
  21. // # Experimental
  22. //
  23. // Notice: This package is EXPERIMENTAL and may be changed or removed in a
  24. // later release.
  25. package gzip
  26. import (
  27. "compress/gzip"
  28. "encoding/binary"
  29. "fmt"
  30. "io"
  31. "sync"
  32. "google.golang.org/grpc/encoding"
  33. )
  34. // Name is the name registered for the gzip compressor.
  35. const Name = "gzip"
  36. func init() {
  37. c := &compressor{}
  38. c.poolCompressor.New = func() interface{} {
  39. return &writer{Writer: gzip.NewWriter(io.Discard), pool: &c.poolCompressor}
  40. }
  41. encoding.RegisterCompressor(c)
  42. }
  43. type writer struct {
  44. *gzip.Writer
  45. pool *sync.Pool
  46. }
  47. // SetLevel updates the registered gzip compressor to use the compression level specified (gzip.HuffmanOnly is not supported).
  48. // NOTE: this function must only be called during initialization time (i.e. in an init() function),
  49. // and is not thread-safe.
  50. //
  51. // The error returned will be nil if the specified level is valid.
  52. func SetLevel(level int) error {
  53. if level < gzip.DefaultCompression || level > gzip.BestCompression {
  54. return fmt.Errorf("grpc: invalid gzip compression level: %d", level)
  55. }
  56. c := encoding.GetCompressor(Name).(*compressor)
  57. c.poolCompressor.New = func() interface{} {
  58. w, err := gzip.NewWriterLevel(io.Discard, level)
  59. if err != nil {
  60. panic(err)
  61. }
  62. return &writer{Writer: w, pool: &c.poolCompressor}
  63. }
  64. return nil
  65. }
  66. func (c *compressor) Compress(w io.Writer) (io.WriteCloser, error) {
  67. z := c.poolCompressor.Get().(*writer)
  68. z.Writer.Reset(w)
  69. return z, nil
  70. }
  71. func (z *writer) Close() error {
  72. defer z.pool.Put(z)
  73. return z.Writer.Close()
  74. }
  75. type reader struct {
  76. *gzip.Reader
  77. pool *sync.Pool
  78. }
  79. func (c *compressor) Decompress(r io.Reader) (io.Reader, error) {
  80. z, inPool := c.poolDecompressor.Get().(*reader)
  81. if !inPool {
  82. newZ, err := gzip.NewReader(r)
  83. if err != nil {
  84. return nil, err
  85. }
  86. return &reader{Reader: newZ, pool: &c.poolDecompressor}, nil
  87. }
  88. if err := z.Reset(r); err != nil {
  89. c.poolDecompressor.Put(z)
  90. return nil, err
  91. }
  92. return z, nil
  93. }
  94. func (z *reader) Read(p []byte) (n int, err error) {
  95. n, err = z.Reader.Read(p)
  96. if err == io.EOF {
  97. z.pool.Put(z)
  98. }
  99. return n, err
  100. }
  101. // RFC1952 specifies that the last four bytes "contains the size of
  102. // the original (uncompressed) input data modulo 2^32."
  103. // gRPC has a max message size of 2GB so we don't need to worry about wraparound.
  104. func (c *compressor) DecompressedSize(buf []byte) int {
  105. last := len(buf)
  106. if last < 4 {
  107. return -1
  108. }
  109. return int(binary.LittleEndian.Uint32(buf[last-4 : last]))
  110. }
  111. func (c *compressor) Name() string {
  112. return Name
  113. }
  114. type compressor struct {
  115. poolCompressor sync.Pool
  116. poolDecompressor sync.Pool
  117. }