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 ## Quick Start
```go ```go
import ( package main
"crypto/rand"
"github.com/fumiama/orbyte/pbuf" import (
"crypto/rand"
"io"
"github.com/fumiama/orbyte/pbuf"
) )
func main() { func main() {
b := pbuf.NewBytes(1024) // Allocate Bytes from pool. buf := pbuf.NewBuffer(nil) // Allocate Buffer from pool.
rand.Read(b.Bytes()) // Do sth. buf.P(func(buf *pbuf.Buffer) {
b.KeepAlive() // Mark as reachable. io.CopyN(buf, rand.Reader, 4096) // Do sth.
// After that, b will be auto-reused on GC. })
// 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. // 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) return bufferPool.p.New(buf)
} }
// InvolveBuffer involve external *bytes.Buffer into Item. // 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) return bufferPool.p.Involve(buf.Len(), buf)
} }
// ParseBuffer convert external *bytes.Buffer into Item. // 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) return bufferPool.p.Parse(buf.Len(), buf)
} }

View File

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

View File

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

View File

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

View File

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