github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/compress/compress.go (about)

     1  // MIT License
     2  
     3  // Copyright (c) 2020 Tree Xie
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  
    23  package compress
    24  
    25  import (
    26  	"compress/gzip"
    27  	"errors"
    28  	"sync"
    29  
    30  	"github.com/andybalholm/brotli"
    31  	"github.com/vicanso/pike/config"
    32  	"go.uber.org/atomic"
    33  )
    34  
    35  const (
    36  	EncodingGzip   = "gzip"
    37  	EncodingBrotli = "br"
    38  	EncodingLZ4    = "lz4"
    39  	EncodingSnappy = "snz"
    40  	EncodingZSTD   = "zst"
    41  )
    42  
    43  type (
    44  	compressSrv struct {
    45  		levels map[string]*atomic.Int32
    46  	}
    47  	CompressOption struct {
    48  		Name   string
    49  		Levels map[string]int
    50  	}
    51  	compressSrvs struct {
    52  		m *sync.Map
    53  	}
    54  )
    55  
    56  const BestCompression = "bestCompression"
    57  
    58  var defaultCompressSrvList = NewServices([]CompressOption{
    59  	{
    60  		Name: BestCompression,
    61  		Levels: map[string]int{
    62  			// -1则会选择默认的压缩级别
    63  			"br":   -1,
    64  			"gzip": gzip.BestCompression,
    65  		},
    66  	},
    67  })
    68  var defaultCompressSrv = NewService()
    69  var notSupportedEncoding = errors.New("not supported encoding")
    70  
    71  // NewServices new compress services
    72  func NewServices(opts []CompressOption) *compressSrvs {
    73  	cs := &compressSrvs{
    74  		m: &sync.Map{},
    75  	}
    76  	for _, opt := range opts {
    77  		srv := NewService()
    78  		srv.SetLevels(opt.Levels)
    79  		cs.m.Store(opt.Name, srv)
    80  	}
    81  	return cs
    82  }
    83  
    84  // NewService new compress service
    85  func NewService() *compressSrv {
    86  	// 配置压缩级别,只设置了gzip与br
    87  	levels := map[string]*atomic.Int32{
    88  		EncodingGzip:   atomic.NewInt32(gzip.DefaultCompression),
    89  		EncodingBrotli: atomic.NewInt32(brotli.DefaultCompression),
    90  	}
    91  	return &compressSrv{
    92  		levels: levels,
    93  	}
    94  }
    95  
    96  // Get get service by name
    97  func (cs *compressSrvs) Get(name string) *compressSrv {
    98  	value, ok := cs.m.Load(name)
    99  	if !ok {
   100  		return defaultCompressSrv
   101  	}
   102  	srv, ok := value.(*compressSrv)
   103  	if !ok {
   104  		return defaultCompressSrv
   105  	}
   106  	return srv
   107  }
   108  
   109  // Reset reset the services
   110  func (cs *compressSrvs) Reset(opts []CompressOption) {
   111  	// 此处不删除存在的压缩服务,因为compress实例并不占多少内存
   112  	// 也避免配置了bestCompression后删除
   113  	for _, opt := range opts {
   114  		srv := NewService()
   115  		srv.SetLevels(opt.Levels)
   116  		cs.m.Store(opt.Name, srv)
   117  	}
   118  }
   119  
   120  func convertConfigs(configs []config.CompressConfig) []CompressOption {
   121  	opts := make([]CompressOption, 0)
   122  	for _, item := range configs {
   123  		levels := make(map[string]int)
   124  		for key, value := range item.Levels {
   125  			levels[key] = int(value)
   126  		}
   127  		opts = append(opts, CompressOption{
   128  			Name:   item.Name,
   129  			Levels: levels,
   130  		})
   131  	}
   132  	return opts
   133  }
   134  
   135  // Reset reset default compress services
   136  func Reset(configs []config.CompressConfig) {
   137  	defaultCompressSrvList.Reset(convertConfigs(configs))
   138  }
   139  
   140  // Get get default compress service
   141  func Get(name string) *compressSrv {
   142  	return defaultCompressSrvList.Get(name)
   143  }
   144  
   145  // GetLevel get compress level
   146  func (srv *compressSrv) GetLevel(encoding string) int {
   147  	levelValue, ok := srv.levels[encoding]
   148  	if !ok {
   149  		return 0
   150  	}
   151  	return int(levelValue.Load())
   152  }
   153  
   154  // SetLevels set compres levels
   155  func (srv *compressSrv) SetLevels(levels map[string]int) {
   156  	for name, value := range levels {
   157  		levelValue, ok := srv.levels[name]
   158  		if ok {
   159  			levelValue.Store(int32(value))
   160  		}
   161  	}
   162  }
   163  
   164  // Decompress decompress data
   165  func (srv *compressSrv) Decompress(encoding string, data []byte) ([]byte, error) {
   166  	switch encoding {
   167  	case EncodingGzip:
   168  		return srv.Gunzip(data)
   169  	case EncodingBrotli:
   170  		return srv.BrotliDecode(data)
   171  	case EncodingLZ4:
   172  		return srv.LZ4Decode(data)
   173  	case EncodingSnappy:
   174  		return srv.SnappyDecode(data)
   175  	case EncodingZSTD:
   176  		return srv.ZSTDDecode(data)
   177  	case "":
   178  		return data, nil
   179  	}
   180  	return nil, notSupportedEncoding
   181  }
   182  
   183  // Gzip compress data by gzip
   184  func (srv *compressSrv) Gzip(data []byte) ([]byte, error) {
   185  	level := srv.GetLevel(EncodingGzip)
   186  	return doGzip(data, level)
   187  }
   188  
   189  // Gunzip decompress data by gzip
   190  func (srv *compressSrv) Gunzip(data []byte) ([]byte, error) {
   191  	return doGunzip(data)
   192  }
   193  
   194  // Brotli compress data by br
   195  func (srv *compressSrv) Brotli(data []byte) ([]byte, error) {
   196  	level := srv.GetLevel(EncodingBrotli)
   197  	return doBrotli(data, level)
   198  }
   199  
   200  // BrotliDecode decompress data by brotli
   201  func (srv *compressSrv) BrotliDecode(data []byte) ([]byte, error) {
   202  	return doBrotliDecode(data)
   203  }
   204  
   205  // LZ4Decode decompress data by lz4
   206  func (srv *compressSrv) LZ4Decode(data []byte) ([]byte, error) {
   207  	return doLZ4Decode(data)
   208  }
   209  
   210  // SnappyDecode decompress data by snappy
   211  func (srv *compressSrv) SnappyDecode(data []byte) ([]byte, error) {
   212  	return doSnappyDecode(data)
   213  }
   214  
   215  // ZSTDDecode decompress data by zstd
   216  func (srv *compressSrv) ZSTDDecode(data []byte) ([]byte, error) {
   217  	return doZSTDDecode(data)
   218  }