From e5a08d4ea5eea1c7571f3b11cde3d59f8b89e473 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 8 Dec 2022 16:13:26 +0800 Subject: [PATCH] :sparkles: feat: add new sub package byteutil provide some bytes utils --- byteutil/buffer.go | 65 ++++++++++++++++++++++++++++++++++++++++ byteutil/buffer_test.go | 26 ++++++++++++++++ byteutil/bytex.go | 19 ++++++++++++ byteutil/encoder.go | 63 ++++++++++++++++++++++++++++++++++++++ byteutil/encoder_test.go | 28 +++++++++++++++++ byteutil/pool.go | 64 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 265 insertions(+) create mode 100644 byteutil/buffer.go create mode 100644 byteutil/buffer_test.go create mode 100644 byteutil/bytex.go create mode 100644 byteutil/encoder.go create mode 100644 byteutil/encoder_test.go create mode 100644 byteutil/pool.go diff --git a/byteutil/buffer.go b/byteutil/buffer.go new file mode 100644 index 000000000..c0a6917fe --- /dev/null +++ b/byteutil/buffer.go @@ -0,0 +1,65 @@ +package byteutil + +import ( + "bytes" + "fmt" + "strings" +) + +// Buffer wrap and extends the bytes.Buffer +type Buffer struct { + bytes.Buffer +} + +// NewBuffer instance +func NewBuffer() *Buffer { + return &Buffer{} +} + +// WriteAny type value to buffer +func (b *Buffer) WriteAny(vs ...any) { + for _, v := range vs { + _, _ = b.Buffer.WriteString(fmt.Sprint(v)) + } +} + +// QuietWriteByte to buffer +func (b *Buffer) QuietWriteByte(c byte) { + _ = b.WriteByte(c) +} + +// QuietWritef write message to buffer +func (b *Buffer) QuietWritef(tpl string, vs ...any) { + _, _ = b.WriteString(fmt.Sprintf(tpl, vs...)) +} + +// Writeln write message to buffer with newline +func (b *Buffer) Writeln(ss ...string) { + b.QuietWriteln(ss...) +} + +// QuietWriteln write message to buffer with newline +func (b *Buffer) QuietWriteln(ss ...string) { + _, _ = b.WriteString(strings.Join(ss, "")) + _ = b.WriteByte('\n') +} + +// QuietWriteString to buffer +func (b *Buffer) QuietWriteString(ss ...string) { + _, _ = b.WriteString(strings.Join(ss, "")) +} + +// MustWriteString to buffer +func (b *Buffer) MustWriteString(ss ...string) { + _, err := b.WriteString(strings.Join(ss, "")) + if err != nil { + panic(err) + } +} + +// ResetAndGet buffer string. +func (b *Buffer) ResetAndGet() string { + s := b.String() + b.Reset() + return s +} diff --git a/byteutil/buffer_test.go b/byteutil/buffer_test.go new file mode 100644 index 000000000..843c364c5 --- /dev/null +++ b/byteutil/buffer_test.go @@ -0,0 +1,26 @@ +package byteutil_test + +import ( + "testing" + + "github.com/gookit/goutil/byteutil" + "github.com/gookit/goutil/testutil/assert" +) + +func TestBuffer_WriteAny(t *testing.T) { + buf := byteutil.NewBuffer() + + buf.QuietWritef("ab-%s", "c") + buf.QuietWriteByte('d') + assert.Eq(t, "ab-cd", buf.ResetAndGet()) + + buf.QuietWriteString("ab", "-", "cd") + buf.MustWriteString("-ef") + assert.Eq(t, "ab-cd-ef", buf.ResetAndGet()) + + buf.WriteAny(23, "abc") + assert.Eq(t, "23abc", buf.ResetAndGet()) + + buf.Writeln("abc") + assert.Eq(t, "abc\n", buf.ResetAndGet()) +} diff --git a/byteutil/bytex.go b/byteutil/bytex.go new file mode 100644 index 000000000..e66ea61f5 --- /dev/null +++ b/byteutil/bytex.go @@ -0,0 +1,19 @@ +// Package byteutil Provide some bytes utils functions or structs +package byteutil + +import ( + "crypto/md5" + "fmt" +) + +// Md5 Generate a 32-bit md5 bytes +func Md5(src any) []byte { + h := md5.New() + + if s, ok := src.(string); ok { + h.Write([]byte(s)) + } else { + h.Write([]byte(fmt.Sprint(src))) + } + return h.Sum(nil) +} diff --git a/byteutil/encoder.go b/byteutil/encoder.go new file mode 100644 index 000000000..8ec965e5d --- /dev/null +++ b/byteutil/encoder.go @@ -0,0 +1,63 @@ +package byteutil + +import ( + "encoding/base64" + "encoding/hex" +) + +// BytesEncoder interface +type BytesEncoder interface { + Encode(src []byte) []byte + Decode(src []byte) ([]byte, error) +} + +// StdEncoder implement the BytesEncoder +type StdEncoder struct { + encodeFn func(src []byte) []byte + decodeFn func(src []byte) ([]byte, error) +} + +// NewStdEncoder instance +func NewStdEncoder(encFn func(src []byte) []byte, decFn func(src []byte) ([]byte, error)) *StdEncoder { + return &StdEncoder{ + encodeFn: encFn, + decodeFn: decFn, + } +} + +// Encode input +func (e *StdEncoder) Encode(src []byte) []byte { + return e.encodeFn(src) +} + +// Decode input +func (e *StdEncoder) Decode(src []byte) ([]byte, error) { + return e.decodeFn(src) +} + +var ( + // HexEncoder instance + HexEncoder = NewStdEncoder(func(src []byte) []byte { + dst := make([]byte, hex.EncodedLen(len(src))) + hex.Encode(dst, src) + return dst + }, func(src []byte) ([]byte, error) { + n, err := hex.Decode(src, src) + return src[:n], err + }) + + // B64Encoder instance + B64Encoder = NewStdEncoder(func(src []byte) []byte { + b64Dst := make([]byte, base64.StdEncoding.EncodedLen(len(src))) + base64.StdEncoding.Encode(b64Dst, src) + return b64Dst + }, func(src []byte) ([]byte, error) { + dBuf := make([]byte, base64.StdEncoding.DecodedLen(len(src))) + n, err := base64.StdEncoding.Decode(dBuf, src) + if err != nil { + return nil, err + } + + return dBuf[:n], err + }) +) diff --git a/byteutil/encoder_test.go b/byteutil/encoder_test.go new file mode 100644 index 000000000..38c943d2c --- /dev/null +++ b/byteutil/encoder_test.go @@ -0,0 +1,28 @@ +package byteutil_test + +import ( + "testing" + + "github.com/gookit/goutil/byteutil" + "github.com/gookit/goutil/testutil/assert" +) + +func TestB64Encoder(t *testing.T) { + src := []byte("abc1234566") + dst := byteutil.B64Encoder.Encode(src) + assert.NotEmpty(t, dst) + + decSrc, err := byteutil.B64Encoder.Decode(dst) + assert.NoError(t, err) + assert.Eq(t, src, decSrc) +} + +func TestHexEncoder(t *testing.T) { + src := []byte("abc1234566") + dst := byteutil.HexEncoder.Encode(src) + assert.NotEmpty(t, dst) + + decSrc, err := byteutil.HexEncoder.Decode(dst) + assert.NoError(t, err) + assert.Eq(t, src, decSrc) +} diff --git a/byteutil/pool.go b/byteutil/pool.go new file mode 100644 index 000000000..c6f09e018 --- /dev/null +++ b/byteutil/pool.go @@ -0,0 +1,64 @@ +package byteutil + +// ChanPool struct +// +// Usage: +// +// bp := strutil.NewByteChanPool(500, 1024, 1024) +// buf:=bp.Get() +// defer bp.Put(buf) +// // use buf do something ... +// +// refer https://www.flysnow.org/2020/08/21/golang-chan-byte-pool.html +// from https://github.com/minio/minio/blob/master/internal/bpool/bpool.go +type ChanPool struct { + c chan []byte + w int + wcap int +} + +// NewChanPool instance +func NewChanPool(maxSize int, width int, capWidth int) *ChanPool { + return &ChanPool{ + c: make(chan []byte, maxSize), + w: width, + wcap: capWidth, + } +} + +// Get gets a []byte from the BytePool, or creates a new one if none are +// available in the pool. +func (bp *ChanPool) Get() (b []byte) { + select { + case b = <-bp.c: + // reuse existing buffer + default: + // create new buffer + if bp.wcap > 0 { + b = make([]byte, bp.w, bp.wcap) + } else { + b = make([]byte, bp.w) + } + } + return +} + +// Put returns the given Buffer to the BytePool. +func (bp *ChanPool) Put(b []byte) { + select { + case bp.c <- b: + // buffer went back into pool + default: + // buffer didn't go back into pool, just discard + } +} + +// Width returns the width of the byte arrays in this pool. +func (bp *ChanPool) Width() (n int) { + return bp.w +} + +// WidthCap returns the cap width of the byte arrays in this pool. +func (bp *ChanPool) WidthCap() (n int) { + return bp.wcap +}