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:
25
README.md
25
README.md
@@ -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.
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}
|
||||||
}
|
}
|
||||||
|
|||||||
24
pbuf/pbuf.go
24
pbuf/pbuf.go
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
18
pool_test.go
18
pool_test.go
@@ -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{}
|
||||||
|
|||||||
Reference in New Issue
Block a user