Skip to content

Commit

Permalink
✨ feat: add new sub package byteutil provide some bytes utils
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Dec 8, 2022
1 parent 449331c commit e5a08d4
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 0 deletions.
65 changes: 65 additions & 0 deletions byteutil/buffer.go
Original file line number Diff line number Diff line change
@@ -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
}
26 changes: 26 additions & 0 deletions byteutil/buffer_test.go
Original file line number Diff line number Diff line change
@@ -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())
}
19 changes: 19 additions & 0 deletions byteutil/bytex.go
Original file line number Diff line number Diff line change
@@ -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)
}
63 changes: 63 additions & 0 deletions byteutil/encoder.go
Original file line number Diff line number Diff line change
@@ -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
})
)
28 changes: 28 additions & 0 deletions byteutil/encoder_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
64 changes: 64 additions & 0 deletions byteutil/pool.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit e5a08d4

Please sign in to comment.