1
0
mirror of https://github.com/fumiama/orbyte.git synced 2026-06-05 02:00:30 +08:00

feat(pbuf): add USRDAT

This commit is contained in:
源文雨
2025-02-26 16:35:01 +09:00
parent 72938b1e5f
commit 8c0982b885
7 changed files with 105 additions and 62 deletions

View File

@@ -3,16 +3,27 @@ Lightweight & Safe (buffer-writer | general object) pool.
## Quick Start
```go
import (
"crypto/rand"
package main
"github.com/fumiama/orbyte/pbuf"
import (
"crypto/rand"
"io"
"github.com/fumiama/orbyte/pbuf"
)
func main() {
b := pbuf.NewBytes(1024) // Allocate Bytes from pool.
rand.Read(b.Bytes()) // Do sth.
b.KeepAlive() // Mark as reachable.
// After that, b will be auto-reused on GC.
buf := pbuf.NewBuffer(nil) // Allocate Buffer from pool.
buf.P(func(buf *pbuf.Buffer) {
io.CopyN(buf, rand.Reader, 4096) // Do sth.
})
// After that, buf will be auto-reused on GC.
b := pbuf.NewBytes(1024) // Allocate Bytes from pool.
b.V(func(b []byte) {
rand.Read(b) // Do sth.
})
// After that, b will be auto-reused on GC.
}
```

View File

@@ -7,16 +7,22 @@ import (
)
// NewBuffer wraps bytes.NewBuffer into Item.
func (bufferPool BufferPool) NewBuffer(buf []byte) *orbyte.Item[bytes.Buffer] {
func (bufferPool BufferPool[USRDAT]) NewBuffer(
buf []byte,
) *orbyte.Item[UserBuffer[USRDAT]] {
return bufferPool.p.New(buf)
}
// InvolveBuffer involve external *bytes.Buffer into Item.
func (bufferPool BufferPool) InvolveBuffer(buf *bytes.Buffer) *orbyte.Item[bytes.Buffer] {
func (bufferPool BufferPool[USRDAT]) InvolveBuffer(
buf *bytes.Buffer,
) *orbyte.Item[UserBuffer[USRDAT]] {
return bufferPool.p.Involve(buf.Len(), buf)
}
// ParseBuffer convert external *bytes.Buffer into Item.
func (bufferPool BufferPool) ParseBuffer(buf *bytes.Buffer) *orbyte.Item[bytes.Buffer] {
func (bufferPool BufferPool[USRDAT]) ParseBuffer(
buf *bytes.Buffer,
) *orbyte.Item[UserBuffer[USRDAT]] {
return bufferPool.p.Parse(buf.Len(), buf)
}

View File

@@ -6,8 +6,6 @@ import (
"io"
"runtime"
"testing"
"github.com/fumiama/orbyte"
)
func TestBuffer(t *testing.T) {
@@ -19,8 +17,8 @@ func TestBuffer(t *testing.T) {
testBuffer(InvolveBuffer(bytes.NewBuffer(make([]byte, 0, 8192))), t)
}
func testBuffer(buf *orbyte.Item[bytes.Buffer], t *testing.T) {
buf.P(func(buf *bytes.Buffer) {
func testBuffer(buf *OBuffer, t *testing.T) {
buf.P(func(buf *Buffer) {
if buf.Len() != 4096 {
io.CopyN(buf, rand.Reader, 4096)
if buf.Len() != 4096 {
@@ -30,7 +28,7 @@ func testBuffer(buf *orbyte.Item[bytes.Buffer], t *testing.T) {
})
bufcp := buf.Copy()
dat := buf.Trans()
bufcp.P(func(bufcp *bytes.Buffer) {
bufcp.P(func(bufcp *Buffer) {
if bufcp.Len() != 4096 {
t.Fatal("got", bufcp.Len())
}

View File

@@ -6,41 +6,43 @@ import (
"github.com/fumiama/orbyte"
)
// Bytes wrap pooled buffer into []byte
// UserBytes wrap pooled buffer into []byte
// while sharing the same pool.
type Bytes struct {
buf *orbyte.Item[bytes.Buffer]
type UserBytes[USRDAT any] struct {
buf *orbyte.Item[UserBuffer[USRDAT]]
a, b int
}
// BufferItemToBytes convert between *orbyte.Item[bytes.Buffer]
// BufferItemToBytes convert between *Buffer
// and Bytes.
//
// Please notice that Bytes cannnot convert back to
// *orbyte.Item[bytes.Buffer] again.
func BufferItemToBytes(buf *orbyte.Item[bytes.Buffer]) (b Bytes) {
// *Buffer again.
func BufferItemToBytes[USRDAT any](
buf *orbyte.Item[UserBuffer[USRDAT]],
) (b UserBytes[USRDAT]) {
b.buf = buf
buf.P(func(buf *bytes.Buffer) {
buf.P(func(buf *UserBuffer[USRDAT]) {
b.b = buf.Len()
})
return
}
// NewBytes alloc sz bytes.
func (bufferPool BufferPool) NewBytes(sz int) (b Bytes) {
func (bufferPool BufferPool[USRDAT]) NewBytes(sz int) (b UserBytes[USRDAT]) {
buf := bufferPool.p.New(sz)
b.buf = buf
buf.P(func(buf *bytes.Buffer) {
buf.P(func(buf *UserBuffer[USRDAT]) {
b.b = buf.Len()
})
return
}
// InvolveBytes involve outside buf into pool.
func (bufferPool BufferPool) InvolveBytes(p ...byte) (b Bytes) {
func (bufferPool BufferPool[USRDAT]) InvolveBytes(p ...byte) (b UserBytes[USRDAT]) {
buf := bufferPool.p.Involve(len(p), bytes.NewBuffer(p))
b.buf = buf
buf.P(func(buf *bytes.Buffer) {
buf.P(func(buf *UserBuffer[USRDAT]) {
b.b = buf.Len()
})
return
@@ -48,10 +50,10 @@ func (bufferPool BufferPool) InvolveBytes(p ...byte) (b Bytes) {
// ParseBytes convert outside bytes to Bytes safely
// without adding it into pool.
func (bufferPool BufferPool) ParseBytes(p ...byte) (b Bytes) {
func (bufferPool BufferPool[USRDAT]) ParseBytes(p ...byte) (b UserBytes[USRDAT]) {
buf := bufferPool.p.Parse(len(p), bytes.NewBuffer(p))
b.buf = buf
buf.P(func(buf *bytes.Buffer) {
buf.P(func(buf *UserBuffer[USRDAT]) {
b.b = buf.Len()
})
return
@@ -59,54 +61,54 @@ func (bufferPool BufferPool) ParseBytes(p ...byte) (b Bytes) {
// HasInit whether this Bytes is made by pool or
// just declared.
func (b Bytes) HasInit() bool {
func (b UserBytes[USRDAT]) HasInit() bool {
return b.buf != nil
}
// Trans please refer to Item.Trans().
func (b Bytes) Trans() []byte {
func (b UserBytes[USRDAT]) Trans() []byte {
buf := b.buf.Trans()
return buf.Bytes()[b.a:b.b]
}
// Len of slice.
func (b Bytes) Len() int {
func (b UserBytes[USRDAT]) Len() int {
return b.b - b.a
}
// Cap of slice.
func (b Bytes) Cap() (c int) {
b.buf.P(func(b *bytes.Buffer) {
func (b UserBytes[USRDAT]) Cap() (c int) {
b.buf.P(func(b *UserBuffer[USRDAT]) {
c = b.Cap()
})
return c
}
// V use the inner value safely
func (b Bytes) V(f func([]byte)) {
b.buf.P(func(buf *bytes.Buffer) {
func (b UserBytes[USRDAT]) V(f func([]byte)) {
b.buf.P(func(buf *UserBuffer[USRDAT]) {
f(buf.Bytes()[b.a:b.b])
})
}
// Copy please refer to Item.Copy().
func (b Bytes) Copy() (cb Bytes) {
func (b UserBytes[USRDAT]) Copy() (cb UserBytes[USRDAT]) {
cb.buf = b.buf.Copy()
cb.a, cb.b = b.a, b.b
return
}
// SliceFrom dat[from:] with Ref.
func (b Bytes) SliceFrom(from int) Bytes {
return Bytes{buf: b.buf, a: b.a + from, b: b.b}
func (b UserBytes[USRDAT]) SliceFrom(from int) UserBytes[USRDAT] {
return UserBytes[USRDAT]{buf: b.buf, a: b.a + from, b: b.b}
}
// SliceTo dat[:to] with Ref.
func (b Bytes) SliceTo(to int) Bytes {
return Bytes{buf: b.buf, a: b.a, b: b.a + to}
func (b UserBytes[USRDAT]) SliceTo(to int) UserBytes[USRDAT] {
return UserBytes[USRDAT]{buf: b.buf, a: b.a, b: b.a + to}
}
// Slice dat[from:to] with Ref.
func (b Bytes) Slice(from, to int) Bytes {
return Bytes{buf: b.buf, a: b.a + from, b: b.a + to}
func (b UserBytes[USRDAT]) Slice(from, to int) UserBytes[USRDAT] {
return UserBytes[USRDAT]{buf: b.buf, a: b.a + from, b: b.a + to}
}

View File

@@ -7,28 +7,36 @@ import (
"github.com/fumiama/orbyte"
)
var bufferPool = NewBufferPool()
var bufferPool = NewBufferPool[struct{}]()
type BufferPool struct {
p *orbyte.Pool[bytes.Buffer]
type (
OBuffer = orbyte.Item[Buffer]
Buffer = UserBuffer[struct{}]
Bytes = UserBytes[struct{}]
)
type BufferPool[USRDAT any] struct {
p *orbyte.Pool[UserBuffer[USRDAT]]
}
func NewBufferPool() BufferPool {
return BufferPool{p: orbyte.NewPool[bytes.Buffer](bufpooler{})}
func NewBufferPool[USRDAT any]() BufferPool[USRDAT] {
return BufferPool[USRDAT]{
p: orbyte.NewPool[UserBuffer[USRDAT]](bufpooler[USRDAT]{}),
}
}
// NewBuffer wraps bytes.NewBuffer into Item.
func NewBuffer(buf []byte) *orbyte.Item[bytes.Buffer] {
func NewBuffer(buf []byte) *OBuffer {
return bufferPool.NewBuffer(buf)
}
// InvolveBuffer involve external *bytes.Buffer into Item.
func InvolveBuffer(buf *bytes.Buffer) *orbyte.Item[bytes.Buffer] {
func InvolveBuffer(buf *bytes.Buffer) *OBuffer {
return bufferPool.InvolveBuffer(buf)
}
// ParseBuffer convert external *bytes.Buffer into Item.
func ParseBuffer(buf *bytes.Buffer) *orbyte.Item[bytes.Buffer] {
func ParseBuffer(buf *bytes.Buffer) *OBuffer {
return bufferPool.ParseBuffer(buf)
}

View File

@@ -7,9 +7,15 @@ import (
"unsafe"
)
type bufpooler struct{}
// UserBuffer with customizable user data structure inside.
type UserBuffer[USRDAT any] struct {
bytes.Buffer
DAT USRDAT
}
func (bufpooler) New(config any, pooled bytes.Buffer) bytes.Buffer {
type bufpooler[USRDAT any] struct{}
func (bufpooler[USRDAT]) New(config any, pooled UserBuffer[USRDAT]) UserBuffer[USRDAT] {
switch c := config.(type) {
case int:
pooled.Grow(c)
@@ -24,7 +30,7 @@ func (bufpooler) New(config any, pooled bytes.Buffer) bytes.Buffer {
if len(c) != buf.Len() {
panic("unexpected bad bytes.NewBuffer")
}
return *buf
return UserBuffer[USRDAT]{Buffer: *buf}
}
return pooled
case string:
@@ -35,12 +41,12 @@ func (bufpooler) New(config any, pooled bytes.Buffer) bytes.Buffer {
}
}
func (bufpooler) Parse(obj any, pooled bytes.Buffer) bytes.Buffer {
func (bufpooler[USRDAT]) Parse(obj any, pooled UserBuffer[USRDAT]) UserBuffer[USRDAT] {
switch o := obj.(type) {
case *bytes.Buffer:
return *o
return UserBuffer[USRDAT]{Buffer: *o}
case bytes.Buffer:
return o
return UserBuffer[USRDAT]{Buffer: o}
case []byte:
pooled.Write(o)
return pooled
@@ -58,17 +64,17 @@ func (bufpooler) Parse(obj any, pooled bytes.Buffer) bytes.Buffer {
}
}
func (bufpooler) Reset(item *bytes.Buffer) {
func (bufpooler[USRDAT]) Reset(item *UserBuffer[USRDAT]) {
// See https://golang.org/issue/23199
const maxSize = 1 << 16
if item.Cap() > maxSize { // drop large buffer
*item = bytes.Buffer{}
*item = UserBuffer[USRDAT]{}
return
}
item.Reset()
}
func (bufpooler) Copy(dst, src *bytes.Buffer) {
func (bufpooler[USRDAT]) Copy(dst, src *UserBuffer[USRDAT]) {
dst.Reset()
srccp := *src
_, err := io.Copy(dst, &srccp)

View File

@@ -1,7 +1,9 @@
package orbyte
import (
"bytes"
"crypto/rand"
"encoding/hex"
"runtime"
"sync"
"testing"
@@ -29,13 +31,21 @@ func TestPool(t *testing.T) {
if out != 0 {
t.Fatal("unexpected behavior")
}
item := p.New(4096)
item.V(func(b []byte) {
rand.Read(b)
})
exp := item.Copy().Trans()
wg := sync.WaitGroup{}
for i := 0; i < 4096; i++ {
item := p.New(i)
item.V(func(b []byte) {
copy(b, exp)
})
wg.Add(1)
go useranddes(item.Copy(), &wg)
wg.Add(1)
go userv(item.Trans(), &wg)
go userv(item.Trans(), &wg, exp[:i])
}
wg.Wait()
runtime.GC()
@@ -54,9 +64,11 @@ func useranddes(item *Item[[]byte], wg *sync.WaitGroup) {
item.ManualDestroy()
}
func userv(b []byte, wg *sync.WaitGroup) {
func userv(b []byte, wg *sync.WaitGroup, exp []byte) {
defer wg.Done()
rand.Read(b)
if !bytes.Equal(b, exp) {
panic("expect " + hex.EncodeToString(exp) + " got " + hex.EncodeToString(b))
}
}
type simplepooler struct{}