feat(util): add compressor for http gzip

Benchmark_Gzip-4    4005    317424 ns/op    855350 B/op    29 allocs/op

Signed-off-by: slasher <mcq.sejust@gmail.com>
This commit is contained in:
slasher 2024-02-04 11:35:15 +08:00 committed by 梁曟風
parent 96a24a26e9
commit 58101cf142
4 changed files with 181 additions and 0 deletions

View File

@ -0,0 +1,43 @@
// Copyright 2023 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package compressor
const EncodingGzip = "gzip"
// Compressor bytes compressor.
// TODO: add stream Compressor.
type Compressor interface {
Compress([]byte) ([]byte, error)
Decompress([]byte) ([]byte, error)
}
type none struct{}
func (none) Compress(pb []byte) ([]byte, error) { return pb, nil }
func (none) Decompress(cb []byte) ([]byte, error) { return cb, nil }
var compressors = make(map[string]func() Compressor)
func init() {
compressors[""] = func() Compressor { return none{} }
compressors[EncodingGzip] = func() Compressor { return gzipCompressor{} }
}
func New(encoding string) Compressor {
if newCompressor, ok := compressors[encoding]; ok {
return newCompressor()
}
return compressors[""]()
}

View File

@ -0,0 +1,39 @@
// Copyright 2023 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package compressor_test
import (
"crypto/rand"
"testing"
"github.com/cubefs/cubefs/util/compressor"
"github.com/stretchr/testify/require"
)
func TestCompressor_New(t *testing.T) {
for _, encoding := range []string{"", "none", "balaa"} {
buf := make([]byte, 1024)
rand.Read(buf)
c := compressor.New(encoding)
require.NotNil(t, c)
cbuf, err := c.Compress(buf)
require.NoError(t, err)
require.Equal(t, buf, cbuf)
pbuf, err := c.Decompress(cbuf)
require.NoError(t, err)
require.Equal(t, buf, pbuf)
}
}

52
util/compressor/gzip.go Normal file
View File

@ -0,0 +1,52 @@
// Copyright 2023 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package compressor
import (
"bytes"
"compress/gzip"
"io"
)
// TODO: reuse bytes.Buffer
type gzipCompressor struct{}
func (gzipCompressor) Compress(pb []byte) ([]byte, error) {
buffer := new(bytes.Buffer)
gw := gzip.NewWriter(buffer)
if _, err := gw.Write(pb); err != nil {
return nil, err
}
if err := gw.Close(); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
func (gzipCompressor) Decompress(cb []byte) ([]byte, error) {
gr, err := gzip.NewReader(bytes.NewBuffer(cb))
if err != nil {
return nil, err
}
buffer := new(bytes.Buffer)
if _, err := io.Copy(buffer, gr); err != nil {
return nil, err
}
if err := gr.Close(); err != nil {
return nil, err
}
return buffer.Bytes(), nil
}

View File

@ -0,0 +1,47 @@
// Copyright 2023 The CubeFS Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
package compressor_test
import (
"crypto/rand"
"testing"
"github.com/cubefs/cubefs/util/compressor"
"github.com/stretchr/testify/require"
)
func TestCompressor_Gzip(t *testing.T) {
for range [100]struct{}{} {
buf := make([]byte, 1024)
rand.Read(buf)
c := compressor.New(compressor.EncodingGzip)
require.NotNil(t, c)
cbuf, err := c.Compress(buf)
require.NoError(t, err)
pbuf, err := c.Decompress(cbuf)
require.NoError(t, err)
require.Equal(t, buf, pbuf)
}
}
func Benchmark_Gzip(b *testing.B) {
buf := make([]byte, 1024)
rand.Read(buf)
for ii := 0; ii < b.N; ii++ {
c := compressor.New(compressor.EncodingGzip)
cbuf, _ := c.Compress(buf)
c.Decompress(cbuf)
}
}