Skip to content

Commit

Permalink
Feat (pool): further compatibility interface work
Browse files Browse the repository at this point in the history
This allows for a struct to require a pool.Pool[pool.ByteBuffer] that can be satisfied by BufferFactoryByteBufferCompat or a light wrapper around a sync.Pool that deals in *bytes.Buffer instances
  • Loading branch information
yunginnanet committed Jul 3, 2024
1 parent cbfd4b8 commit 809b06f
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 2 deletions.
65 changes: 63 additions & 2 deletions pool/interface.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,80 @@
package pool

import (
"bytes"
"io"
"sync"
)

type Pool[T any] interface {
Get() T
Put(T)
}

// ByteBuffer is satisfied by [*pool.Buffer] and [*bytes.Buffer].
//
// Note, we can't include Reset and Grow as our implementations return an error while a [*bytes.Buffer] does not.
type ByteBuffer interface {
Len() int
Cap() int
Bytes() []byte
WriteRune(rune) (int, error)
WriteString(string) (int, error)

io.WriterTo
io.ByteWriter
io.ReadWriter

io.ReaderFrom
io.ByteReader
io.RuneReader
}

type WithPutError[T any] interface {
Get() T
Put(T) error
}

func (b BufferFactoryInterfaceCompat) Put(buf *Buffer) {
_ = b.BufferFactory.Put(buf)
}

type BufferFactoryInterfaceCompat struct {
BufferFactory
}

func (b BufferFactoryInterfaceCompat) Put(buf *Buffer) {
_ = b.BufferFactory.Put(buf)
type BufferFactoryByteBufferCompat struct {
BufferFactory
}

func (bf BufferFactoryByteBufferCompat) Put(buf ByteBuffer) {
if b, ok := buf.(*Buffer); ok {
err := bf.BufferFactory.Put(b)
if err != nil {
panic(err)
}
return
}
if b, ok := buf.(*bytes.Buffer); ok {
newB := &Buffer{
o: &sync.Once{},
Buffer: b,
}
_ = bf.BufferFactory.Put(newB)
return
}
// unfortunately this compatibility shim cannot be used with any other types implementing ByteBuffer
// this is because we can't wrap them in a *Buffer
panic("invalid type, need *pool.Buffer or *bytes.Buffer")
}

func (bf BufferFactoryByteBufferCompat) Get() ByteBuffer {
b := bf.BufferFactory.Get()
return ByteBuffer(b)
}

var (
_ ByteBuffer = (*Buffer)(nil)
_ ByteBuffer = (*bytes.Buffer)(nil)
_ Pool[ByteBuffer] = (*BufferFactoryByteBufferCompat)(nil)
)
46 changes: 46 additions & 0 deletions pool/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import (
"testing"
)

type othaBuffa struct {
ByteBuffer
}

// ensure compatibility with interface
func TestInterfaces(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -65,4 +69,46 @@ func TestInterfaces(t *testing.T) {
}
})

t.Run("BufferFactoryByteBufferCompat", func(t *testing.T) {
t.Parallel()
bf := BufferFactoryByteBufferCompat{NewBufferFactory()}
b := bf.Get()
if _, err := b.WriteString("test"); err != nil {
t.Fatal(err)
}
bf.Put(b)
b = bf.Get()
if b.Len() != 0 {
t.Fatal("buffer not reset")
}
foreign := &bytes.Buffer{}
foreign.WriteString("test")
bf.Put(foreign)
if foreign.Len() != 0 {
t.Fatal("buffer not reset")
}
foreignGot := bf.Get()
if foreignGot.Len() != 0 {
t.Fatal("buffer not reset")
}
bf.Put(foreignGot)
t.Run("must panic after wrapped and put twice", func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("panic expected")
}
}()
bf.Put(foreignGot)
})
t.Run("must panic on invalid type", func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Error("panic expected")
}
}()
bf.Put(&othaBuffa{})
})

})

}

0 comments on commit 809b06f

Please sign in to comment.