1
0

refactor: simplify based on blocking/non-blocking behavior changes; allow initial segment from outside of the used pool

This commit is contained in:
Arpad Ryszka 2026-04-15 18:34:23 +02:00
parent 513f9a3585
commit cdbf48fa2f
16 changed files with 773 additions and 626 deletions

198
block_test.go Normal file
View File

@ -0,0 +1,198 @@
package buffer_test
import (
"bytes"
"code.squareroundforest.org/arpio/buffer"
"testing"
"time"
)
func TestBlock(t *testing.T) {
t.Run("read", func(t *testing.T) {
segmentSize := 1 << 12
blockAfter := segmentSize
bc := make(chan struct{})
g := &gen{
max: 1 << 15,
blockAfter: []int{blockAfter},
unblock: map[int]<-chan struct{}{blockAfter: bc},
}
r := buffer.BufferedReader(g, buffer.Options{BufferPool: buffer.NoPool(segmentSize)})
b := make([]byte, 2*segmentSize)
if n, err := r.Read(b); n != segmentSize || err != nil {
t.Fatal(n, err)
}
if string(b[:segmentSize]) != string(generate(segmentSize)) {
t.Log(string(b))
t.Log(string(generate(segmentSize)))
t.Fatal("invalid content 1")
}
ubc := make(chan struct{})
go func() {
if n, err := r.Read(b); n != segmentSize || err != nil {
t.Fatal(n, err)
}
if string(b[:segmentSize]) != string(generate(2 * segmentSize)[segmentSize:]) {
t.Fatal("invalid content 2")
}
close(ubc)
}()
select {
case <-time.After(10 * time.Millisecond):
case <-ubc:
t.Fatal("unexpected read return")
}
close(bc)
select {
case <-time.After(10 * time.Millisecond):
t.Fatal("timeout")
case <-ubc:
}
})
t.Run("read bytes", func(t *testing.T) {
segmentSize := 1 << 12
blockAfter := segmentSize
bc := make(chan struct{})
g := &gen{
max: 1 << 15,
blockAfter: []int{blockAfter},
unblock: map[int]<-chan struct{}{blockAfter: bc},
}
r := buffer.BufferedReader(g, buffer.Options{BufferPool: buffer.NoPool(segmentSize)})
ubc := make(chan struct{})
go func() {
if b, err := r.ReadBytes([]byte("123"), 2*segmentSize); len(b) != 0 || err != nil {
t.Fatal(len(b), err)
}
close(ubc)
}()
select {
case <-time.After(10 * time.Millisecond):
case <-ubc:
t.Fatal("unexpected read return")
}
close(bc)
select {
case <-time.After(10 * time.Millisecond):
t.Fatal("timeout")
case <-ubc:
}
})
t.Run("read utf8", func(t *testing.T) {
segmentSize := 1 << 12
blockAfter := segmentSize
bc := make(chan struct{})
g := &gen{
max: 1 << 15,
blockAfter: []int{blockAfter},
unblock: map[int]<-chan struct{}{blockAfter: bc},
}
r := buffer.BufferedReader(g, buffer.Options{BufferPool: buffer.NoPool(segmentSize)})
ubc := make(chan struct{})
go func() {
if r, n, err := r.ReadUTF8(2 * segmentSize); len(r) != 2*segmentSize ||
n != 2*segmentSize ||
err != nil {
t.Fatal(len(r), err)
}
close(ubc)
}()
select {
case <-time.After(10 * time.Millisecond):
case <-ubc:
t.Fatal("unexpected read return")
}
close(bc)
select {
case <-time.After(10 * time.Millisecond):
t.Fatal("timeout")
case <-ubc:
}
})
t.Run("peek", func(t *testing.T) {
segmentSize := 1 << 12
blockAfter := segmentSize
bc := make(chan struct{})
g := &gen{
max: 1 << 15,
blockAfter: []int{blockAfter},
unblock: map[int]<-chan struct{}{blockAfter: bc},
}
r := buffer.BufferedReader(g, buffer.Options{BufferPool: buffer.NoPool(segmentSize)})
ubc := make(chan struct{})
go func() {
if b, err := r.Peek(2 * segmentSize); len(b) != 2*segmentSize || err != nil {
t.Fatal(len(b), err)
}
close(ubc)
}()
select {
case <-time.After(10 * time.Millisecond):
case <-ubc:
t.Fatal("unexpected read return")
}
close(bc)
select {
case <-time.After(10 * time.Millisecond):
t.Fatal("timeout")
case <-ubc:
}
})
t.Run("write to", func(t *testing.T) {
segmentSize := 1 << 12
blockAfter := segmentSize
bc := make(chan struct{})
g := &gen{
max: 1 << 15,
blockAfter: []int{blockAfter},
unblock: map[int]<-chan struct{}{blockAfter: bc},
}
r := buffer.BufferedReader(g, buffer.Options{BufferPool: buffer.NoPool(segmentSize)})
ubc := make(chan struct{})
go func() {
var b bytes.Buffer
if n, err := r.WriteTo(&b); n != 1<<15 || err != nil {
t.Fatal(b, err)
}
close(ubc)
}()
select {
case <-time.After(10 * time.Millisecond):
case <-ubc:
t.Fatal("unexpected read return")
}
close(bc)
select {
case <-time.After(10 * time.Millisecond):
t.Fatal("timeout")
case <-ubc:
}
})
}

View File

@ -11,7 +11,7 @@ func TestBuffered(t *testing.T) {
t.Run(title, func(t *testing.T) {
t.Run("none buffered", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b := r.Buffered()
if len(b) != 0 {
@ -25,7 +25,7 @@ func TestBuffered(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(18)
if err != nil {
@ -44,7 +44,7 @@ func TestBuffered(t *testing.T) {
t.Run("all buffered", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(48)
if err != nil {
@ -63,7 +63,7 @@ func TestBuffered(t *testing.T) {
t.Run("buffered across segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(144)
if err != nil {
@ -82,7 +82,7 @@ func TestBuffered(t *testing.T) {
t.Run("buffered mid segment", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
b := make([]byte, 32)
n, err := r.Read(b)
@ -106,7 +106,7 @@ func TestBuffered(t *testing.T) {
t.Run("buffered mid segment across segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
b, err := r.Peek(288)
if err != nil {
@ -125,7 +125,7 @@ func TestBuffered(t *testing.T) {
t.Run("zero buffered mid segment", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b := make([]byte, 64)
n, err := r.Read(b)

View File

@ -63,7 +63,7 @@ func (c *content) writeTo() {
return
}
err := fmt.Errorf("panic provided WriterTo: %v", r)
err := fmt.Errorf("panic from provided WriterTo: %v", r)
w.w <- syncMessage{err: err}
close(w.w)
}()

View File

@ -11,8 +11,8 @@ func TestContent(t *testing.T) {
t.Run("eof", func(t *testing.T) {
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
var n int64
for i := 0; i < 3; i++ {
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
for i := 0; i < 4; i++ {
ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3])
n += int64(ni)
if err != nil {
return n, err
@ -23,18 +23,21 @@ func TestContent(t *testing.T) {
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 3; i++ {
exp := []byte("123456789012")
for i := 0; i < 8; i++ {
n, err := r.Read(b)
if n != 3 || err != nil {
t.Fatal(n, err)
if n != 2-i%2 || err != nil {
t.Fatal(i, n, err)
}
if string(b) != "123456789"[i*3:i*3+3] {
t.Fatal(string(b))
if string(b[:n]) != string(exp[:n]) {
t.Fatal(i, string(b[:n]), string(exp[:n]))
}
exp = exp[n:]
}
n, err := r.Read(b)
@ -49,7 +52,7 @@ func TestContent(t *testing.T) {
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
n, err := r.Read(b)
@ -61,8 +64,8 @@ func TestContent(t *testing.T) {
t.Run("writer error", func(t *testing.T) {
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
var n int64
for i := 0; i < 3; i++ {
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
for i := 0; i < 4; i++ {
ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3])
n += int64(ni)
if err != nil {
return n, err
@ -73,18 +76,21 @@ func TestContent(t *testing.T) {
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 3; i++ {
exp := []byte("123456789012")
for i := 0; i < 8; i++ {
n, err := r.Read(b)
if n != 3 || err != nil {
t.Fatal(n, err)
if n != 2-i%2 || err != nil {
t.Fatal(i, n, err)
}
if string(b) != "123456789"[i*3:i*3+3] {
if string(b[:n]) != string(exp[:n]) {
t.Fatal(string(b))
}
exp = exp[n:]
}
n, err := r.Read(b)
@ -99,7 +105,7 @@ func TestContent(t *testing.T) {
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
n, err := r.Read(b)
@ -127,16 +133,16 @@ func TestContent(t *testing.T) {
errAfter: []int{1},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b, ok, err := r.ReadBytes([]byte("67"), 12)
if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil {
t.Fatal(string(b), ok, err)
b, err := r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || err != nil {
t.Fatal(string(b), err)
}
b, ok, err = r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || ok || !errors.Is(err, errTest) {
t.Fatal(string(b), ok, err)
b, err = r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || err != nil {
t.Fatal(string(b), err)
}
})
@ -159,11 +165,11 @@ func TestContent(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b, ok, err := r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || ok || !errors.Is(err, errTest) {
t.Fatal(string(b), ok, err)
b, err := r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || !errors.Is(err, errTest) {
t.Fatal(string(b), err)
}
})
@ -180,16 +186,16 @@ func TestContent(t *testing.T) {
errAfter: []int{1},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b, ok, err := r.ReadBytes([]byte("67"), 12)
if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil {
t.Fatal(string(b), ok, err)
b, err := r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || err != nil {
t.Fatal(string(b), err)
}
b, ok, err = r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || ok || !errors.Is(err, errTest) {
t.Fatal(string(b), ok, err)
b, err = r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || err != nil {
t.Fatal(string(b), err)
}
})
@ -199,22 +205,26 @@ func TestContent(t *testing.T) {
w.Write(nil)
w.Write([]byte("456"))
w.Write([]byte("789"))
w.Write([]byte("012"))
return 0, nil
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 3; i++ {
exp := []byte("123456789012")
for i := 0; i < 8; i++ {
n, err := r.Read(b)
if n != 3 || err != nil {
if n != 2-i%2 || err != nil {
t.Fatal(n, err)
}
if string(b) != "123456789"[i*3:i*3+3] {
t.Fatal(string(b))
if string(b[:n]) != string(exp[:n]) {
t.Fatal(string(b[:n]))
}
exp = exp[n:]
}
n, err := r.Read(b)
@ -229,22 +239,26 @@ func TestContent(t *testing.T) {
w.Write([]byte("123"))
w.Write([]byte("456"))
w.Write([]byte("789"))
w.Write([]byte("012"))
return 0, nil
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 3; i++ {
exp := []byte("123456789012")
for i := 0; i < 8; i++ {
n, err := r.Read(b)
if n != 3 || err != nil {
if n != 2-i%2 || err != nil {
t.Fatal(n, err)
}
if string(b) != "123456789"[i*3:i*3+3] {
if string(b[:n]) != string(exp[:n]) {
t.Fatal(string(b))
}
exp = exp[n:]
}
n, err := r.Read(b)
@ -268,7 +282,7 @@ func TestContent(t *testing.T) {
})
p := &fakePool{allocSize: 3}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 3; i++ {
@ -301,16 +315,16 @@ func TestContent(t *testing.T) {
errAfter: []int{1},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b, ok, err := r.ReadBytes([]byte("67"), 12)
if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil {
t.Fatal(string(b), ok, err)
b, err := r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || err != nil {
t.Fatal(string(b), err)
}
b, ok, err = r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || ok || !errors.Is(err, errTest) || !errors.Is(err, errTest2) {
t.Fatal(string(b), ok, err)
b, err = r.ReadBytes([]byte("67"), 12)
if len(b) != 0 || err != nil {
t.Fatal(string(b), err)
}
})
}

View File

@ -17,6 +17,8 @@ type gen struct {
customContentAfter []int
customContent map[int][]byte
counter int
blockAfter []int
unblock map[int]<-chan struct{}
}
type writer struct {
@ -39,6 +41,11 @@ func (g *gen) Read(p []byte) (int, error) {
return 0, io.EOF
}
if len(g.blockAfter) > 0 && g.counter >= g.blockAfter[0] {
<-g.unblock[g.blockAfter[0]]
g.blockAfter = g.blockAfter[1:]
}
if len(g.nullReadAfter) > 0 && g.counter >= g.nullReadAfter[0] {
g.nullReadAfter = g.nullReadAfter[1:]
return 0, nil

115
lib.go
View File

@ -1,8 +1,8 @@
// Package buffer provides pooled Buffer IO for Go programs.
//
// It implements a reader similar to bufio.Reader. The underlying memory buffers can be used from a synchronized
// pool. It implements a writer that can be used to avoid writing too small number of bytes to an underlying
// writer.
// pool. It implements a writer that can be used to write data to an underlying writer in larger, buffered,
// chunks.
package buffer
import (
@ -10,9 +10,9 @@ import (
"io"
)
// Pool defines the interface for the used buffer pool. The buffered reader can be used either with the built-in
// default pool, noop pool, or with a custom pool implementation.
type Pool interface {
// BufferPool defines the interface for the used buffer pool. The buffered reader or writer can be used either
// with the built-in default pool, noop pool, or with a custom pool implementation.
type BufferPool interface {
// Get should return a non-zero length byte slice. In case it returns a zero length byte slice, or an
// explicit error, the read operations will fail.
@ -20,35 +20,39 @@ type Pool interface {
// It is OK but not recommended to return varying sizes of byte slices.
Get() ([]byte, error)
// The reader always puts back the byte slices taken by Get, using Put.
// The reader puts back the byte slices taken by Get, using Put, when it does not use them anymore.
Put([]byte)
}
// Options provides options for the Reader.
type Options struct {
// Pool defines the buffer pool to be used. It defaults to the pool created by DefaultPool(). It is
// expected to explicitly set the Pool instance, otherwise, not defining any globals, each Reader
// BufferPool defines the buffer pool to be used. It defaults to the pool created by DefaultPool(). It
// is expected to explicitly set the Pool instance, otherwise, not defining any globals, each Reader
// instance will create its own pool.
Pool Pool
BufferPool BufferPool
// InitialSegment allows to initialize the buffer with a memory space already available to the user code
// without requesting it from the used pool.
InitialSegment []byte
}
// ContentFunc wraps a function to implement the io.WriterTo interface. Function implementations should be
// reader to be executed in goroutines other than what they were created in.
// ready to be executed in goroutines other than what they were created in.
type ContentFunc func(io.Writer) (int64, error)
// Reader wraps an underlying io.Reader or io.WriterTo, and provides buffered io via its methods. Initialize it
// Reader wraps an underlying io.Reader or io.WriterTo, and provides buffered IO via its methods. Initialize it
// via BufferedReader or BufferedContent.
//
// It reads from the underlying source until the first error, but only returns an error when the buffer is
// empty. Once the underlying reader returned an error, it doesn't attempt to read from it anymore.
// empty. Once the underlying reader returned an error, it does not attempt to read from it anymore.
//
// The reader does not support concurrent access.
type Reader struct {
reader *reader
}
// Writer wraps an underlying io.Writer, and provides buffered io via its methods. Initialize it via
// Writer wraps an underlying io.Writer and provides buffered IO via its methods. Initialize it via
// BufferedWriter.
//
// It writes the input bytes into an internal buffer, and flushes them to the underlying writer only when the
@ -63,7 +67,7 @@ var (
// ErrZeroAllocation is returned when the used pool returned a zero length byte slice.
ErrZeroAllocation = errors.New("zero allocation")
// ErrAbort is returned to the writer process in case of buffered content, when the reader
// ErrAbort is returned to the writer process when using buffered content, when the reader
// experienced an error. ErrAbort is returned to the reader process, if Close() was called and no read
// error was received before it.
ErrAbort = errors.New("read aborted")
@ -71,7 +75,7 @@ var (
// DefultPool initializes a synchronized pool that stores and returns byte slices of allocSize length. It can be
// used with multiple readers concurrently.
func DefaultPool(allocSize int) Pool {
func DefaultPool(allocSize int) BufferPool {
if allocSize <= 0 {
allocSize = 1 << 12
}
@ -79,8 +83,8 @@ func DefaultPool(allocSize int) Pool {
return newPool(allocSize)
}
// NoPool returns a noop pool.
func NoPool(allocSize int) Pool {
// NoPool returns a noop pool that allocates memory on every call to Get() and does nothing on Put().
func NoPool(allocSize int) BufferPool {
if allocSize <= 0 {
allocSize = 1 << 12
}
@ -99,11 +103,16 @@ func BufferedReader(in io.Reader, o Options) Reader {
return Reader{}
}
if o.Pool == nil {
o.Pool = DefaultPool(1 << 12)
if o.BufferPool == nil {
o.BufferPool = DefaultPool(1 << 12)
}
return Reader{reader: &reader{options: o, in: in}}
r := Reader{reader: &reader{options: o, in: in}}
if len(o.InitialSegment) > 0 {
r.reader.segments = [][]byte{o.InitialSegment}
}
return r
}
// BufferedContent creates a buffered reader using the input content (io.WriterTo) as the underlying source.
@ -111,7 +120,7 @@ func BufferedReader(in io.Reader, o Options) Reader {
// It is similar to an io.Pipe, but with dynamic and pooled buffering internally. The individual Write calls are
// blocked until the reading side requests more data.
//
// The provided WriterTo instances need to be safe to call in goroutines other than they were created in. The
// The provided WriterTo instances need to be safe to call in goroutines other than they were created in. If the
// writer function returns with nil error, it will be interpreted as EOF on the reader side. When the reader
// side experiences an error, and the writer still has content to be written, the passed in io.Writer will
// return an ErrAbort error.
@ -120,8 +129,8 @@ func BufferedContent(c io.WriterTo, o Options) Reader {
return Reader{}
}
if o.Pool == nil {
o.Pool = DefaultPool(1 << 12)
if o.BufferPool == nil {
o.BufferPool = DefaultPool(1 << 12)
}
return Reader{reader: &reader{options: o, in: mkcontent(c)}}
@ -129,10 +138,14 @@ func BufferedContent(c io.WriterTo, o Options) Reader {
// Read reads max len(p) copied to p and returns how many bytes were read.
//
// It only returns an error when the buffer is empty. It only returns an error when the Pool.Get fails or the
// underlying reader returns an error.
// It only returns an error when the buffer is empty. It only returns an error when the BufferPool.Get fails or
// the underlying reader returns an error.
//
// It may return zero read length and nil error, but only if the underlying reader did so.
//
// It does not block when the buffer is not empty, even if the underlying reader would block on the next read.
//
// It only allocates a single segment of memory, or none if one was provided during innitialization.
func (r Reader) Read(p []byte) (int, error) {
if r.reader == nil {
return 0, io.EOF
@ -143,22 +156,17 @@ func (r Reader) Read(p []byte) (int, error) {
// ReadBytes reads until the first occurence of delimiter in the input, within max length.
//
// It returns the bytes, true and nil error, when the delimiter was found within max. In this case it consumes
// It returns the bytes and nil error, when the delimiter was found within max. In this case it consumes
// the buffer until and including the delimiter. If the underlying reader returned meanwhile a non-nil error,
// including EOF, it will be returned on subsequent reads, but only after the internal buffer was consumed.
//
// It returns zero length bytes, false and nil error, when the delimiter was not found within max, and the
// underlying reader didn't return an error.
// It returns zero length bytes and nil error, when the delimiter was not found within max.
//
// It returns max or less bytes, false and nil error, if the delimiter was not found within max, there are
// buffered bytes, the underlying reader returned an error. It consumes the returned bytes from the buffer. The
// delimiter may still be found in the remaining buffered bytes during subsequent calls.
//
// It returns zero length bytes, false and non-nil error, if the buffer is empty and the underlying reader
// previously returned an error.
func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, bool, error) {
// It blocks only when less data is buffered than max, the delimiter was not found within the range defined by
// max, and the underlying reader blocks.
func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, error) {
if r.reader == nil {
return nil, false, io.EOF
return nil, io.EOF
}
return r.reader.readBytes(delimiter, max)
@ -175,8 +183,11 @@ func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, bool, error) {
// nil error.
//
// It supports recovery of UTF8 streams by skipping the invalid characters. In such cases, first it returns the
// valid characters with the number of bytes consumed and nil error, then in the subsequent call, it returns
// valid characters with the number of bytes consumed and nil error, then in a subsequent call, it returns
// zero characters, 1 and nil error.
//
// It blocks only when there is not enough data buffered to determine the next UTF8 character and the underlying
// reader blocks.
func (r Reader) ReadUTF8(max int) ([]rune, int, error) {
if r.reader == nil {
return nil, 0, io.EOF
@ -191,6 +202,8 @@ func (r Reader) ReadUTF8(max int) ([]rune, int, error) {
// by Read, ReadBytes, ReadUTF8 or WriteTo.
//
// The returned byte slice is not a copy of the buffered bytes, and therefore should not be modified.
//
// It blocks when there is less data buffered than the range defined by max and the underlying reader blocks.
func (r Reader) Peek(max int) ([]byte, error) {
if r.reader == nil {
return nil, io.EOF
@ -215,6 +228,8 @@ func (r Reader) Buffered() []byte {
//
// It is important that the provided writer must not modify the slice data, as defined in the io.Writer
// interface documentation.
//
// It blocks when the underlying reader blocks.
func (r Reader) WriteTo(w io.Writer) (int64, error) {
if r.reader == nil {
return 0, nil
@ -223,8 +238,11 @@ func (r Reader) WriteTo(w io.Writer) (int64, error) {
return r.reader.writeTo(w)
}
// Close releases the resource held by the Reader, and puts back the underlying byte buffers into the used pool.
// The reader cannot be used for read operations after Close() was called.
// Close releases the resource held by the Reader, and puts the underlying byte buffers, that were
// requested from the pool, back into the used pool. The reader cannot be used for read operations after Close()
// was called.
//
// It does not close the underlying reader.
func (r Reader) Close() {
if r.reader == nil {
return
@ -239,15 +257,22 @@ func BufferedWriter(out io.Writer, o Options) Writer {
return Writer{}
}
if o.Pool == nil {
o.Pool = DefaultPool(1 << 12)
if o.BufferPool == nil {
o.BufferPool = DefaultPool(1 << 12)
}
return Writer{writer: &writer{out: out, options: o}}
w := Writer{writer: &writer{out: out, options: o}}
if len(o.InitialSegment) > 0 {
w.writer.buffer = o.InitialSegment
}
return w
}
// Write writes to the writer's buffer, and if the buffer is full, it causes writing out the buffer's contents
// to the underlying writer.
//
// It blocks when there is not enough buffer space for the data being written and the underlying writer blocks.
func (w Writer) Write(p []byte) (int, error) {
if w.writer == nil {
return 0, errors.New("unitialized writer")
@ -270,6 +295,8 @@ func (w Writer) ReadFrom(r io.Reader) (int64, error) {
// Flush forces the writer to flush the buffered content to the underlying writer. After flushed, the writer
// still accepts further writes.
//
// It blocks when there is buffered data to write out and the underlying writer blocks.
func (w Writer) Flush() error {
if w.writer == nil {
return nil
@ -280,6 +307,10 @@ func (w Writer) Flush() error {
// Close flushes the buffered content if any and closes the writer. After closed, the writer does not accept
// further writes.
//
// It blocks when there is buffered data to write out and the underlying writer blocks.
//
// It does not close the underlying writer.
func (w Writer) Close() error {
if w.writer == nil {
return nil

View File

@ -84,15 +84,11 @@ func TestLib(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
var r buffer.Reader
b, ok, err := r.ReadBytes([]byte("123"), 512)
b, err := r.ReadBytes([]byte("123"), 512)
if !errors.Is(err, io.EOF) {
t.Fatal(err)
}
if ok {
t.Fatal(ok)
}
if len(b) != 0 {
t.Fatal(len(b))
}
@ -154,7 +150,7 @@ func TestLib(t *testing.T) {
t.Run("abort", func(t *testing.T) {
t.Run("from blank state", func(t *testing.T) {
p := &fakePool{allocSize: 1 << 6}
r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{Pool: p})
r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{BufferPool: p})
r.Close()
b := make([]byte, 1<<9)
n, err := r.Read(b)
@ -169,10 +165,10 @@ func TestLib(t *testing.T) {
t.Run("with multiple segments", func(t *testing.T) {
p := &fakePool{allocSize: 1 << 6}
r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{Pool: p})
b, ok, err := r.ReadBytes([]byte("123"), 1<<6+1<<5)
if ok || err != nil || p.alloc != 2 {
t.Fatal(len(b), ok, err, p.alloc)
r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{BufferPool: p})
b, err := r.ReadBytes([]byte("123"), 1<<6+1<<5)
if len(b) != 0 || err != nil || p.alloc != 2 {
t.Fatal(len(b), err, p.alloc)
}
r.Close()
@ -194,7 +190,7 @@ func TestLib(t *testing.T) {
errAfter: []int{1 << 6},
}
r := buffer.BufferedReader(g, buffer.Options{Pool: p})
r := buffer.BufferedReader(g, buffer.Options{BufferPool: p})
b := make([]byte, 1<<9)
n, err := r.Read(b)
if n != 1<<6 || err != nil {
@ -221,8 +217,8 @@ func TestLib(t *testing.T) {
t.Run("with content", func(t *testing.T) {
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
var n int64
for i := 0; i < 3; i++ {
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
for i := 0; i < 4; i++ {
ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3])
n += int64(ni)
if err != nil {
return n, err
@ -233,18 +229,21 @@ func TestLib(t *testing.T) {
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 2; i++ {
exp := []byte("123456789012")
for i := 0; i < 8; i++ {
n, err := r.Read(b)
if n != 3 || err != nil {
if n != 2-i%2 || err != nil {
t.Fatal(n, err)
}
if string(b) != "123456789"[i*3:i*3+3] {
t.Fatal(string(b))
if string(b[:n]) != string(exp[:n]) {
t.Fatal(string(b[:n]))
}
exp = exp[n:]
}
r.Close()
@ -261,8 +260,8 @@ func TestLib(t *testing.T) {
t.Run("with content error", func(t *testing.T) {
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
var n int64
for i := 0; i < 3; i++ {
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
for i := 0; i < 4; i++ {
ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3])
n += int64(ni)
if err != nil {
return n, err
@ -273,18 +272,21 @@ func TestLib(t *testing.T) {
})
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
for i := 0; i < 3; i++ {
exp := []byte("123456789012")
for i := 0; i < 8; i++ {
n, err := r.Read(b)
if n != 3 || err != nil {
if n != 2-i%2 || err != nil {
t.Fatal(n, err)
}
if string(b) != "123456789"[i*3:i*3+3] {
if string(b[:n]) != string(exp[:n]) {
t.Fatal(string(b))
}
exp = exp[n:]
}
n, err := r.Read(b)
@ -364,7 +366,7 @@ func TestLib(t *testing.T) {
t.Run("double closing reader", func(t *testing.T) {
g := &gen{max: 1 << 12}
p := &fakePool{allocSize: 1 << 9}
r := buffer.BufferedReader(g, buffer.Options{Pool: p})
r := buffer.BufferedReader(g, buffer.Options{BufferPool: p})
b := bytes.NewBuffer(nil)
if n, err := io.Copy(b, r); n != 1<<12 || err != nil {
t.Fatal(n, err)
@ -392,7 +394,7 @@ func TestLib(t *testing.T) {
})
p := &fakePool{allocSize: 1 << 9}
r := buffer.BufferedContent(c, buffer.Options{Pool: p})
r := buffer.BufferedContent(c, buffer.Options{BufferPool: p})
b := bytes.NewBuffer(nil)
if n, err := io.Copy(b, r); n != 9 || err != nil {
t.Fatal(n, err)
@ -420,7 +422,7 @@ func TestLib(t *testing.T) {
})
p := &fakePool{allocSize: 1 << 9}
r := buffer.BufferedContent(c, buffer.Options{Pool: p})
r := buffer.BufferedContent(c, buffer.Options{BufferPool: p})
b := make([]byte, 3)
if n, err := r.Read(b); n != 3 || err != nil || string(b) != "123" {
t.Fatal(n, err, string(b))
@ -437,7 +439,7 @@ func TestLib(t *testing.T) {
w := &writer{}
r := &gen{max: 1 << 12}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 1<<12 || err != nil {
@ -465,7 +467,7 @@ func TestLib(t *testing.T) {
}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 1<<11 || !errors.Is(err, errTest) {
@ -485,6 +487,61 @@ func TestLib(t *testing.T) {
}
})
})
t.Run("initial segment", func(t *testing.T) {
t.Run("reader", func(t *testing.T) {
g := &gen{max: 1 << 12}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{
InitialSegment: make([]byte, 1<<9),
BufferPool: p,
}
b := buffer.BufferedReader(g, o)
var n int
pp := make([]byte, 1<<12)
for {
ni, err := b.Read(pp)
n += ni
if errors.Is(err, io.EOF) {
break
}
if err != nil {
t.Fatal(err)
}
}
if n != 1<<12 {
t.Fatal(n)
}
if p.alloc != 0 || p.free != 0 {
t.Fatal(p.alloc, p.free)
}
})
t.Run("writer", func(t *testing.T) {
w := &writer{}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{
InitialSegment: make([]byte, 1<<9),
BufferPool: p,
}
b := buffer.BufferedWriter(w, o)
pp := generate(1 << 12)
n, err := b.Write(pp)
if n != 1<<12 || err != nil {
t.Fatal(n, err)
}
if p.alloc != 0 || p.free != 0 {
t.Fatal(p.alloc, p.free)
}
})
})
}
// -- bench
@ -509,7 +566,7 @@ func TestBenchmarkThroughput(t *testing.T) {
p := buffer.NoPool(0)
wo := writerOnly{io.Discard}
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
@ -533,7 +590,7 @@ func BenchmarkThroughput(b *testing.B) {
wo := writerOnly{io.Discard}
for i := 0; i < b.N; i++ {
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
ro := readerOnly{r}
io.Copy(wo, ro)
}
@ -553,7 +610,7 @@ func TestBenchmarkThroughputPooled(t *testing.T) {
p := &foreverPool{allocSize: 1 << 12}
wo := writerOnly{io.Discard}
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
@ -578,7 +635,7 @@ func BenchmarkThroughputPooled(b *testing.B) {
wo := writerOnly{io.Discard}
for i := 0; i < b.N; i++ {
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
ro := readerOnly{r}
io.Copy(wo, ro)
}
@ -602,7 +659,7 @@ func TestBenchmarkThroughputPooledParallel(t *testing.T) {
wo := writerOnly{io.Discard}
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
@ -640,7 +697,7 @@ func BenchmarkThroughputPooledParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
ro := readerOnly{r}
wo := writerOnly{io.Discard}
io.Copy(wo, ro)
@ -676,16 +733,12 @@ func TestBenchmarkScan(t *testing.T) {
customContent: map[int][]byte{delimiterPosition: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
b, ok, err := r.ReadBytes([]byte{'1'}, 1<<18)
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
b, err := r.ReadBytes([]byte{'1'}, 1<<18)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(delimiterPosition), '1')) {
t.Fatal("content")
}
@ -720,7 +773,7 @@ func BenchmarkScan(b *testing.B) {
customContent: map[int][]byte{delimiterPosition: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
r.ReadBytes([]byte{'1'}, 1<<18)
}
}
@ -748,16 +801,12 @@ func TestBenchmarkScanPooled(t *testing.T) {
customContent: map[int][]byte{delimiterPosition: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
b, ok, err := r.ReadBytes([]byte{'1'}, 1<<18)
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
b, err := r.ReadBytes([]byte{'1'}, 1<<18)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(delimiterPosition), '1')) {
t.Fatal("content")
}
@ -793,7 +842,7 @@ func BenchmarkScanPooled(b *testing.B) {
customContent: map[int][]byte{delimiterPosition: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
r.ReadBytes([]byte{'1'}, 1<<18)
}
}
@ -825,16 +874,12 @@ func TestBenchmarkScanPooledParallel(t *testing.T) {
customContent: map[int][]byte{delimiterPosition: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
b, ok, err := r.ReadBytes([]byte{'1'}, 1<<18)
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
b, err := r.ReadBytes([]byte{'1'}, 1<<18)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(delimiterPosition), '1')) {
t.Fatal("content")
}
@ -884,7 +929,7 @@ func BenchmarkScanPooledParallel(b *testing.B) {
customContent: map[int][]byte{delimiterPosition: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: p})
r.ReadBytes([]byte{'1'}, 1<<18)
}
})

View File

@ -13,7 +13,7 @@ func TestPeek(t *testing.T) {
t.Run(title, func(t *testing.T) {
t.Run("peek across segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
p, err := r.Peek(2*128 + 30)
if err != nil {
@ -31,7 +31,7 @@ func TestPeek(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(18)
if err != nil {
@ -72,7 +72,7 @@ func TestPeek(t *testing.T) {
t.Run("err immediately on first try to fill", func(t *testing.T) {
g := &gen{}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(18)
if !errors.Is(err, io.EOF) {
@ -86,7 +86,7 @@ func TestPeek(t *testing.T) {
t.Run("peek on empty", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(18)
if err != nil {
@ -100,7 +100,7 @@ func TestPeek(t *testing.T) {
t.Run("peek multiple segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(160)
if err != nil {
@ -114,7 +114,7 @@ func TestPeek(t *testing.T) {
t.Run("peek on partially filled", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(16)
if err != nil {
@ -137,7 +137,7 @@ func TestPeek(t *testing.T) {
t.Run("peek on partially filled multiple segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(160)
if err != nil {
@ -164,7 +164,7 @@ func TestPeek(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(18)
if err != nil {
@ -182,7 +182,7 @@ func TestPeek(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(214)
if err != nil {
@ -196,7 +196,7 @@ func TestPeek(t *testing.T) {
t.Run("peek zero", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(0)
if err != nil {

View File

@ -134,7 +134,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b := make([]byte, 256)
n, err := r.Read(b)
@ -158,17 +158,13 @@ func TestPoolUsage(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<12)
b, err := r.ReadBytes([]byte("123"), 1<<12)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -181,7 +177,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read utf8", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
runes, n, err := r.ReadUTF8(1 << 12)
if err != nil {
@ -189,7 +185,7 @@ func TestPoolUsage(t *testing.T) {
}
if n != 1<<12 {
t.Fatal("unexpected delimiter")
t.Fatal("unexpected read length")
}
if len(runes) != 1<<12 {
@ -201,10 +197,37 @@ func TestPoolUsage(t *testing.T) {
}
})
t.Run("read utf8 off", func(t *testing.T) {
g := &gen{
max: 1 << 15,
rng: utf8Range,
}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
runes, n, err := r.ReadUTF8(1 << 12)
if err != nil {
t.Fatal(err)
}
if n < len(runes) {
t.Fatal("unexpected read length", n)
}
if len(runes) != 1<<12 {
t.Fatal("invalid content")
}
if p.alloc != 2 {
t.Fatal("invalid allocation count", p.alloc)
}
})
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := r.Peek(3 * 1 << 12)
if err != nil {
@ -223,7 +246,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("buffered", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b := r.Buffered()
if len(b) != 0 {
@ -238,7 +261,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
var b bytes.Buffer
@ -265,7 +288,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b := make([]byte, 1<<9)
for {
@ -291,23 +314,18 @@ func TestPoolUsage(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
_, _, err := r.ReadBytes([]byte("123"), 1<<15+3)
_, err := r.ReadBytes([]byte("123"), 1<<15+3)
if err != nil {
t.Fatal(err)
}
_, _, err = r.ReadBytes([]byte("123"), 1<<15+3)
if !errors.Is(err, io.EOF) {
t.Fatal(err)
}
if p.alloc != 9 {
t.Fatal("invalid allocation count", p.alloc)
}
if p.free != 9 {
if p.free != 0 {
t.Fatal("invalid free count", p.free)
}
})
@ -315,7 +333,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read utf8", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
for {
runes, n, err := r.ReadUTF8(1 << 12)
@ -344,7 +362,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := r.Peek(3 * 1 << 12)
if err != nil {
@ -387,7 +405,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("buffered", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b := r.Buffered()
if len(b) != 0 {
@ -402,7 +420,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
var b bytes.Buffer
@ -437,7 +455,7 @@ func TestPoolUsage(t *testing.T) {
zeroAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b := make([]byte, 256)
_, err := r.Read(b)
@ -453,15 +471,15 @@ func TestPoolUsage(t *testing.T) {
zeroAfter: []int{1},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
_, _, err := r.ReadBytes([]byte("123"), 1<<12)
_, err := r.ReadBytes([]byte("123"), 1<<12)
if err != nil {
t.Fatal(err)
}
_, _, err = r.ReadBytes([]byte("123"), 1<<12)
if !errors.Is(err, buffer.ErrZeroAllocation) {
_, err = r.ReadBytes([]byte("123"), 1<<12)
if err != nil {
t.Fatal("failed to fail", err)
}
})
@ -473,7 +491,7 @@ func TestPoolUsage(t *testing.T) {
zeroAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
_, _, err := r.ReadUTF8(1 << 12)
if !errors.Is(err, buffer.ErrZeroAllocation) {
@ -488,7 +506,7 @@ func TestPoolUsage(t *testing.T) {
zeroAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
_, err := r.Peek(3 * 1 << 12)
if !errors.Is(err, buffer.ErrZeroAllocation) {
@ -503,7 +521,7 @@ func TestPoolUsage(t *testing.T) {
zeroAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
var b bytes.Buffer
@ -524,17 +542,13 @@ func TestPoolUsage(t *testing.T) {
}
p := &fakePool{varyingSize: []int{8, 256}}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
b, err := r.ReadBytes([]byte("123"), 1<<15)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if !bytes.Equal(b, append(generate(1<<11), []byte("123")...)) {
t.Fatal("invalid content")
}
@ -543,15 +557,15 @@ func TestPoolUsage(t *testing.T) {
t.Run("find not", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{varyingSize: []int{8, 256}}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
b, err := r.ReadBytes([]byte("123"), 1<<15)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter", len(b))
if len(b) != 0 {
t.Fatal("invalid read length", len(b))
}
})
})
@ -559,7 +573,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{varyingSize: []int{8, 256}}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := r.Peek(1 << 11)
if err != nil {
@ -576,7 +590,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := io.ReadAll(r)
if err != nil {
@ -597,17 +611,13 @@ func TestPoolUsage(t *testing.T) {
}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
b, err := r.ReadBytes([]byte("123"), 1<<15)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if !bytes.Equal(b, append(generate(1<<11), []byte("123")...)) {
t.Fatal("invalid content")
}
@ -616,17 +626,13 @@ func TestPoolUsage(t *testing.T) {
t.Run("find not", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
b, err := r.ReadBytes([]byte("123"), 1<<15)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b))
}
@ -640,7 +646,7 @@ func TestPoolUsage(t *testing.T) {
}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
runes, n, err := r.ReadUTF8(1 << 14)
if err != nil {
@ -659,7 +665,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := r.Peek(1 << 14)
if err != nil {
@ -674,7 +680,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
var b bytes.Buffer
@ -701,7 +707,7 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := io.ReadAll(r)
if !errors.Is(err, errTest) {
@ -721,17 +727,13 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<12)
b, err := r.ReadBytes([]byte("123"), 1<<12)
if !errors.Is(err, errTest) {
t.Fatal("failed to fail with the right error", err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -744,30 +746,22 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{1},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
b, err := r.ReadBytes([]byte("123"), 1<<15)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if !bytes.Equal(b, generate(1<<11)) {
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 1<<15)
if !errors.Is(err, errTest) {
b, err = r.ReadBytes([]byte("123"), 1<<15)
if err != nil {
t.Fatal("failed to fail with the right error", err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -785,7 +779,7 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
runes, n, err := r.ReadUTF8(1 << 14)
if !errors.Is(err, errTest) {
@ -809,7 +803,7 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := r.Peek(1 << 12)
if !errors.Is(err, errTest) {
@ -828,7 +822,7 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{1},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
b, err := r.Peek(1 << 15)
if err != nil {
@ -839,26 +833,22 @@ func TestPoolUsage(t *testing.T) {
t.Fatal("invalid content")
}
b, ok, err := r.ReadBytes([]byte("123"), 1<<11)
b, err = r.ReadBytes([]byte("123"), 1<<11)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if !bytes.Equal(b, generate(1<<11)) {
if len(b) != 0 {
t.Fatal("invalid content")
}
b, err = r.Peek(1 << 15)
if !errors.Is(err, errTest) {
if err != nil {
t.Fatal("failed to fail with the right error")
}
if len(b) != 0 {
t.Fatal("invalid content")
if !bytes.Equal(b, generate(1<<11)) {
t.Fatal("invalid content", len(b))
}
})
})
@ -870,7 +860,7 @@ func TestPoolUsage(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
r := cr(g, o)
var b bytes.Buffer

View File

@ -13,7 +13,7 @@ func TestRead(t *testing.T) {
t.Run(title, func(t *testing.T) {
t.Run("small", func(t *testing.T) {
g := &gen{max: 3}
r := cr(g, buffer.Options{Pool: buffer.NoPool(1 << 12)})
r := cr(g, buffer.Options{BufferPool: buffer.NoPool(1 << 12)})
b, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
@ -26,7 +26,7 @@ func TestRead(t *testing.T) {
t.Run("large", func(t *testing.T) {
g := &gen{max: 1 << 18}
r := cr(g, buffer.Options{Pool: buffer.NoPool(1 << 12)})
r := cr(g, buffer.Options{BufferPool: buffer.NoPool(1 << 12)})
b, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
@ -39,7 +39,7 @@ func TestRead(t *testing.T) {
t.Run("zero first", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
var p []byte
@ -69,7 +69,7 @@ func TestRead(t *testing.T) {
t.Run("large with non-divisible fragments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12 / 2)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12 / 2)}
r := cr(g, o)
b, err := io.ReadAll(r)
if err != nil {
@ -83,7 +83,7 @@ func TestRead(t *testing.T) {
t.Run("partial segment", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
r.Peek(30)
p := make([]byte, 60)
@ -103,7 +103,7 @@ func TestRead(t *testing.T) {
t.Run("partial segment nth", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
r.Peek(2*128 + 30)
p := make([]byte, 2*128+60)
@ -123,7 +123,7 @@ func TestRead(t *testing.T) {
t.Run("read buffer larger than read size", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 12)
n, err := r.Read(p)
@ -142,11 +142,11 @@ func TestRead(t *testing.T) {
t.Run("read buffer larger than segment", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 192)
n, err := r.Read(p)
if n != 192 {
if n != 128 {
t.Fatal("invalid read size")
}
@ -154,14 +154,14 @@ func TestRead(t *testing.T) {
t.Fatal(err)
}
if !bytes.Equal(p, generate(192)) {
if !bytes.Equal(p[:128], generate(128)) {
t.Fatal("invalid content")
}
})
t.Run("read buffer larger than available data", func(t *testing.T) {
g := &gen{max: 256}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(512)}
r := cr(g, o)
p := make([]byte, 384)
n, err := r.Read(p)
@ -193,7 +193,7 @@ func TestRead(t *testing.T) {
nullReadAfter: []int{0, 0},
}
o := buffer.Options{Pool: buffer.NoPool(128)}
o := buffer.Options{BufferPool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 64)
n, err := r.Read(p)
@ -213,7 +213,7 @@ func TestRead(t *testing.T) {
errAfter: []int{32},
}
o := buffer.Options{Pool: buffer.NoPool(32)}
o := buffer.Options{BufferPool: buffer.NoPool(32)}
r := cr(g, o)
p := make([]byte, 64)
n, err := r.Read(p)
@ -234,7 +234,7 @@ func TestRead(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(32)}
o := buffer.Options{BufferPool: buffer.NoPool(32)}
r := cr(g, o)
p := make([]byte, 64)
n, err := r.Read(p)
@ -255,7 +255,7 @@ func TestRead(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(32)}
o := buffer.Options{BufferPool: buffer.NoPool(32)}
var result []byte
r := cr(g, o)
p := make([]byte, 9)

View File

@ -18,17 +18,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("failed to generate right content")
}
@ -36,14 +32,14 @@ func TestReadBytes(t *testing.T) {
t.Run("find not", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok || len(b) != 0 {
if len(b) != 0 {
t.Fatal("failed to not find delimiter")
}
})
@ -55,17 +51,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(14)}
o := buffer.Options{BufferPool: buffer.NoPool(14)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
@ -79,17 +71,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{6: d},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes(d, 64)
b, err := r.ReadBytes(d, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if !bytes.Equal(b, append(generate(6), d...)) {
t.Fatal("invalid content")
}
@ -97,17 +85,13 @@ func TestReadBytes(t *testing.T) {
t.Run("find not across segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(15)}
o := buffer.Options{BufferPool: buffer.NoPool(15)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 24)
b, err := r.ReadBytes([]byte("123"), 24)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
@ -115,17 +99,13 @@ func TestReadBytes(t *testing.T) {
t.Run("find not across multiple segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(15)}
o := buffer.Options{BufferPool: buffer.NoPool(15)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
@ -138,17 +118,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("8"), 64)
b, err := r.ReadBytes([]byte("8"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -161,17 +137,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("12")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -187,30 +159,22 @@ func TestReadBytes(t *testing.T) {
},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 16)
b, err := r.ReadBytes([]byte("123"), 16)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 24)
b, err = r.ReadBytes([]byte("123"), 24)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
check := append(
append(generate(12), append([]byte("12"), generate(18)[14:]...)...),
[]byte("123")...,
@ -228,17 +192,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{7: []byte("12")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -251,17 +211,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{7: []byte("1234567890")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("1234567890123"), 64)
b, err := r.ReadBytes([]byte("1234567890123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -277,30 +233,22 @@ func TestReadBytes(t *testing.T) {
},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 10)
b, err := r.ReadBytes([]byte("123"), 10)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
check := append(
append(generate(7), append([]byte("12"), generate(15)[9:]...)...),
[]byte("123")...,
@ -321,30 +269,22 @@ func TestReadBytes(t *testing.T) {
},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("1234567890123"), 20)
b, err := r.ReadBytes([]byte("1234567890123"), 20)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("1234567890123"), 64)
b, err = r.ReadBytes([]byte("1234567890123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
check := append(
append(append(generate(7), []byte("1234567890")...), generate(22)[17:]...),
[]byte("1234567890123")...,
@ -362,17 +302,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 14)
b, err := r.ReadBytes([]byte("123"), 14)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -385,30 +321,22 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 14)
b, err := r.ReadBytes([]byte("123"), 14)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
@ -421,17 +349,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes(nil, 64)
b, err := r.ReadBytes(nil, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if len(b) != 0 {
t.Fatal("failed to generate right content")
}
@ -444,17 +368,13 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("1")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte{'1'}, 64)
b, err := r.ReadBytes([]byte{'1'}, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), '1')) {
t.Fatal("failed to generate right content")
}
@ -467,41 +387,33 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{1<<17 - 3: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: buffer.NoPool(1 << 12)})
r := buffer.BufferedReader(src, buffer.Options{BufferPool: buffer.NoPool(1 << 12)})
r.ReadBytes([]byte{'1'}, 1<<17+1<<16)
})
t.Run("delimiter longer than content", func(t *testing.T) {
g := &gen{max: 1 << 9}
o := buffer.Options{Pool: buffer.NoPool(16)}
o := buffer.Options{BufferPool: buffer.NoPool(16)}
r := cr(g, o)
b, ok, err := r.ReadBytes(generate(1<<10), 1<<11)
b, err := r.ReadBytes(generate(1<<10), 1<<11)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(1<<9)) {
if len(b) != 0 {
t.Fatal("content")
}
})
t.Run("find not none consumed", func(t *testing.T) {
g := &gen{}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, io.EOF) {
t.Fatal(err)
}
if ok {
t.Fatal("failed to find delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -519,30 +431,22 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(8)) {
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, errTest) {
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal("failed to fail", err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
@ -557,43 +461,22 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter", len(b), string(b))
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if !bytes.Equal(b, generate(16)[15:]) {
t.Fatal("invalid content", len(b), string(b))
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, errTest) {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
@ -608,43 +491,31 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter", len(b), string(b))
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if !bytes.Equal(b, generate(24)[15:]) {
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, errTest) {
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
@ -658,30 +529,22 @@ func TestReadBytes(t *testing.T) {
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
o := buffer.Options{BufferPool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
b, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("failed to generate right content")
}
@ -693,18 +556,14 @@ func TestReadBytes(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(256)}
o := buffer.Options{BufferPool: buffer.NoPool(256)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
b, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(64)) {
if len(b) != 0 {
t.Fatal("failed to generate right content")
}
})

181
reader.go
View File

@ -16,30 +16,31 @@ type reader struct {
err error
}
func (r *reader) fillSegment() (fn int, full bool) {
if r.err != nil {
return
}
func (r *reader) fillSegment() int {
var n int
seg := r.segments[len(r.segments)-1]
start := r.offset + r.len - r.lastSegStart
if start == len(seg) {
full = true
return
n, r.err = r.in.Read(seg[start:len(seg)])
if n == 0 && r.err == nil {
n, r.err = r.in.Read(seg[start:len(seg)])
}
fn, r.err = r.in.Read(seg[start:len(seg)])
if fn == 0 && r.err == nil {
fn, r.err = r.in.Read(seg[start:len(seg)])
r.len += n
return n
}
r.len += fn
full = fn == len(seg)-start
return
func (r reader) segmentFull() bool {
if len(r.segments) == 0 {
return true
}
segmentPosition := r.offset + r.len - r.lastSegStart
lastSeg := r.segments[len(r.segments)-1]
return segmentPosition == len(lastSeg)
}
func (r *reader) allocate() {
segment, err := r.options.Pool.Get()
segment, err := r.options.BufferPool.Get()
if err != nil {
r.err = err
return
@ -51,7 +52,8 @@ func (r *reader) allocate() {
}
if len(r.segments) > 0 {
r.lastSegStart += len(r.segments[len(r.segments)-1])
lastSeg := r.segments[len(r.segments)-1]
r.lastSegStart += len(lastSeg)
}
r.segments = append(r.segments, segment)
@ -68,16 +70,14 @@ func (r *reader) fill(to int) int {
return n
}
if len(r.segments) == 0 || r.offset+r.len == r.lastSegStart+len(r.segments[len(r.segments)-1]) {
if r.segmentFull() {
r.allocate()
if r.err != nil {
return n
}
continue
}
fn, full := r.fillSegment()
fn := r.fillSegment()
n += fn
if fn == 0 || !full {
if fn == 0 || !r.segmentFull() {
return n
}
}
@ -112,41 +112,55 @@ func (r reader) copy(p []byte) (n int) {
func (r *reader) consume(n int) {
r.offset += n
r.len -= n
seg := 0
if len(r.options.InitialSegment) > 0 {
seg = 1
}
for {
if len(r.segments) <= 1 {
if r.len == 0 {
break
}
segEnd := len(r.segments[seg])
if seg == 1 {
segEnd += len(r.segments[0])
}
if r.offset < segEnd {
break
}
r.offset -= len(r.segments[seg])
r.lastSegStart -= len(r.segments[seg])
r.options.BufferPool.Put(r.segments[seg])
r.segments = r.segments[seg+1:]
}
if len(r.segments) == 1 {
copy(r.segments[0], r.segments[0][r.offset:r.offset+r.len])
r.offset = 0
}
return
}
if r.offset < len(r.segments[0]) {
return
}
r.offset -= len(r.segments[0])
if len(r.segments) > 1 {
r.lastSegStart -= len(r.segments[0])
}
r.options.Pool.Put(r.segments[0])
r.segments = r.segments[1:]
}
}
func (r *reader) free() {
if len(r.segments) > 0 && len(r.options.InitialSegment) > 0 {
r.segments = r.segments[1:]
}
for {
if len(r.segments) == 0 {
break
}
r.options.Pool.Put(r.segments[0])
r.options.BufferPool.Put(r.segments[0])
r.segments[0] = nil
r.segments = r.segments[1:]
}
r.segments = nil
r.len = 0
r.options.Pool = nil
r.options.BufferPool = nil
if c, ok := r.in.(interface{ close() error }); ok {
if err := c.close(); err != nil {
r.err = errors.Join(r.err, err)
@ -208,9 +222,8 @@ func (r *reader) fillToDelimiter(delimiter []byte, max int) (int, bool) {
}
if r.len < i+len(d) {
clen := r.len
r.fill(i + len(d))
if r.len == clen {
fn := r.fill(i + len(d))
if fn == 0 {
return 0, false
}
}
@ -297,34 +310,23 @@ func (r *reader) read(p []byte) (int, error) {
return 0, r.err
}
var n int
for len(p) > 0 {
if len(p) == 0 {
return 0, nil
}
if len(r.segments) == 0 {
r.allocate()
if r.err != nil {
break
return 0, r.err
}
}
var partialRead bool
if r.len == 0 {
_, full := r.fillSegment()
partialRead = !full
}
ni := r.copy(p)
if ni == 0 {
break
}
r.consume(ni)
p = p[ni:]
n += ni
if partialRead {
break
}
r.fillSegment()
}
n := r.copy(p)
r.consume(n)
if r.err != nil && r.len == 0 {
r.free()
if n == 0 {
@ -335,13 +337,13 @@ func (r *reader) read(p []byte) (int, error) {
return n, nil
}
func (r *reader) readBytes(delimiter []byte, max int) ([]byte, bool, error) {
func (r *reader) readBytes(delimiter []byte, max int) ([]byte, error) {
if r.err != nil && r.len == 0 {
return nil, false, r.err
return nil, r.err
}
if len(delimiter) == 0 {
return nil, true, nil
return nil, nil
}
var (
@ -356,25 +358,14 @@ func (r *reader) readBytes(delimiter []byte, max int) ([]byte, bool, error) {
r.consume(n)
}
if !ok && r.err != nil {
l = r.len
if l > max {
l = max
}
p = make([]byte, l)
n = r.copy(p)
r.consume(n)
}
if r.err != nil && r.len == 0 {
r.free()
if n == 0 {
return nil, ok, r.err
return nil, r.err
}
}
return p, ok, nil
return p, nil
}
func (r *reader) readUTF8(max int) ([]rune, int, error) {
@ -392,16 +383,23 @@ func (r *reader) readUTF8(max int) ([]rune, int, error) {
break
}
var nullRead bool
var zeroRead bool
b := r.view(n, 4)
if len(b) < 4 && !utf8.FullRune(b) {
clen := r.len
r.fill(n + 4)
b = r.view(n, 4)
nullRead = r.len == clen
for len(b) < 4 && !utf8.FullRune(b) {
fn := r.fill(n + 4)
zeroRead = fn == 0
if zeroRead {
break
}
if nullRead && r.err == nil || len(b) == 0 {
b = r.view(n, 4)
}
if len(b) == 0 {
break
}
if zeroRead && r.err == nil {
break
}
@ -435,12 +433,17 @@ func (r *reader) peek(max int) ([]byte, error) {
return nil, r.err
}
r.fill(max)
for r.len < max {
if fn := r.fill(max); fn == 0 {
break
}
}
v := r.view(0, max)
if r.err != nil && r.len == 0 {
r.free()
if len(v) == 0 {
return v, r.err
return nil, r.err
}
}
@ -478,7 +481,7 @@ func (r *reader) writeTo(w io.Writer) (int64, error) {
}
}
fn, _ := r.fillSegment()
fn := r.fillSegment()
if fn == 0 && r.err == nil {
return n, io.ErrNoProgress
}

View File

@ -12,23 +12,23 @@ func TestReadUTF8(t *testing.T) {
t.Run(title, func(t *testing.T) {
t.Run("read all after error", func(t *testing.T) {
g := &gen{
rng: utf8W2Range,
max: 30,
rng: utf8Range,
max: 24,
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
runes, n0, err := r.ReadUTF8(12)
runes0, n0, err := r.ReadUTF8(12)
if err != nil {
t.Fatal(err)
}
if len(runes) != 12 {
t.Fatal("invalid read 0", len(runes), n0)
if len(runes0) != 12 {
t.Fatal("invalid read 0", len(runes0), n0)
}
if string(runes) != string(generateFrom(utf8W2Range, n0)) {
if string(runes0) != string(generateFrom(utf8Range, n0)) {
t.Fatal("invalid content")
}
@ -41,8 +41,8 @@ func TestReadUTF8(t *testing.T) {
t.Fatal("invalid read 1", len(runes1), n0, n1)
}
if string(runes1) != string(generateFrom(utf8W2Range, n0+n1)[n0:]) {
t.Fatal("invalid content", string(runes1), string(generateFrom(utf8W2Range, n0+n1)[n0:]))
if string(runes1) != string(generateFrom(utf8Range, n0+n1)[n0:]) {
t.Fatal("invalid content", string(runes1), string(generateFrom(utf8Range, n0+n1)[n0:]))
}
runes2, n2, err := r.ReadUTF8(12)
@ -61,7 +61,7 @@ func TestReadUTF8(t *testing.T) {
t.Run("ascii", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
runes, n, err := r.ReadUTF8(12)
if err != nil {
@ -83,7 +83,7 @@ func TestReadUTF8(t *testing.T) {
max: 1 << 15,
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
runes, n, err := r.ReadUTF8(12)
if err != nil {
@ -105,7 +105,7 @@ func TestReadUTF8(t *testing.T) {
max: 1 << 15,
}
o := buffer.Options{Pool: buffer.NoPool(9)}
o := buffer.Options{BufferPool: buffer.NoPool(9)}
r := cr(g, o)
runes, n, err := r.ReadUTF8(12)
if err != nil {
@ -130,7 +130,7 @@ func TestReadUTF8(t *testing.T) {
customContent: map[int][]byte{len(utf8Range): brokenRange},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
runes, n, err := r.ReadUTF8(24)
if err != nil {
@ -160,7 +160,7 @@ func TestReadUTF8(t *testing.T) {
t.Run("immediate err", func(t *testing.T) {
g := &gen{rng: utf8Range}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
runes, n, err := r.ReadUTF8(12)
if !errors.Is(err, io.EOF) {
@ -189,7 +189,7 @@ func TestReadUTF8(t *testing.T) {
nullReadAfter: []int{nullReadAfter, nullReadAfter},
}
o := buffer.Options{Pool: buffer.NoPool(nullReadAfter)}
o := buffer.Options{BufferPool: buffer.NoPool(nullReadAfter)}
r := cr(g, o)
runes, _, err := r.ReadUTF8(numRunes - 2) // -2 for min read in readUTF8
if err != nil {

View File

@ -27,7 +27,7 @@ func (w *writer) write(p []byte) (int, error) {
}
if len(w.buffer) == 0 {
w.buffer, w.err = w.options.Pool.Get()
w.buffer, w.err = w.options.BufferPool.Get()
if len(w.buffer) == 0 && w.err == nil {
w.err = ErrZeroAllocation
}
@ -55,7 +55,7 @@ func (w *writer) readFrom(r io.Reader) (int64, error) {
}
if len(w.buffer) == 0 {
w.buffer, w.err = w.options.Pool.Get()
w.buffer, w.err = w.options.BufferPool.Get()
if len(w.buffer) == 0 && w.err == nil {
w.err = ErrZeroAllocation
}
@ -117,11 +117,11 @@ func (w *writer) flush() error {
func (w *writer) close() error {
newErr := w.err == nil
w.flush()
if len(w.buffer) > 0 {
w.options.Pool.Put(w.buffer)
w.buffer = nil
if len(w.buffer) > 0 && len(w.options.InitialSegment) == 0 {
w.options.BufferPool.Put(w.buffer)
}
w.buffer = nil
if newErr && w.err != nil {
return w.err
}

View File

@ -10,7 +10,7 @@ import (
func TestWriter(t *testing.T) {
t.Run("write out", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(32)}
o := buffer.Options{BufferPool: buffer.NoPool(32)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -35,7 +35,7 @@ func TestWriter(t *testing.T) {
t.Run("zero bytes when empty", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(32)}
o := buffer.Options{BufferPool: buffer.NoPool(32)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write(nil); n != 0 || err != nil {
t.Fatal(n, err)
@ -44,7 +44,7 @@ func TestWriter(t *testing.T) {
t.Run("zero bytes when erred", func(t *testing.T) {
w := &writer{errAfter: []int{3}}
o := buffer.Options{Pool: buffer.NoPool(2)}
o := buffer.Options{BufferPool: buffer.NoPool(2)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -69,7 +69,7 @@ func TestWriter(t *testing.T) {
t.Run("zero bytes when not empty", func(t *testing.T) {
w := &writer{errAfter: []int{3}}
o := buffer.Options{Pool: buffer.NoPool(32)}
o := buffer.Options{BufferPool: buffer.NoPool(32)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -91,7 +91,7 @@ func TestWriter(t *testing.T) {
errAfter: []int{0},
}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, errTest) {
t.Fatal(n, err)
@ -100,7 +100,7 @@ func TestWriter(t *testing.T) {
t.Run("no underlying write until full", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)}
o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -136,7 +136,7 @@ func TestWriter(t *testing.T) {
t.Run("auto flush when write larger than buffer", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)}
o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123456")); err != nil {
t.Fatal(n, err)
@ -149,7 +149,7 @@ func TestWriter(t *testing.T) {
t.Run("auto flush when multiple smaller writes fill the buffer", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)}
o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -170,7 +170,7 @@ func TestWriter(t *testing.T) {
t.Run("flush on close", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)}
o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -191,7 +191,7 @@ func TestWriter(t *testing.T) {
t.Run("manual flush", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)}
o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -212,7 +212,7 @@ func TestWriter(t *testing.T) {
t.Run("write after flush", func(t *testing.T) {
w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)}
o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -246,7 +246,7 @@ func TestWriter(t *testing.T) {
t.Run("zero write recover", func(t *testing.T) {
w := &writer{zeroAfter: []int{2}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -268,7 +268,7 @@ func TestWriter(t *testing.T) {
t.Run("zero write terminal", func(t *testing.T) {
w := &writer{zeroAfter: []int{2, 2}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -282,7 +282,7 @@ func TestWriter(t *testing.T) {
t.Run("partial write", func(t *testing.T) {
w := &writer{shortAfter: []int{2}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err)
@ -304,7 +304,7 @@ func TestWriter(t *testing.T) {
t.Run("buffer released on write error immediately", func(t *testing.T) {
w := &writer{errAfter: []int{0}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 2 || !errors.Is(err, errTest) {
t.Fatal(n, err)
@ -318,7 +318,7 @@ func TestWriter(t *testing.T) {
t.Run("buffer released on write error", func(t *testing.T) {
w := &writer{errAfter: []int{3}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -340,7 +340,7 @@ func TestWriter(t *testing.T) {
t.Run("close on err", func(t *testing.T) {
w := &writer{errAfter: []int{3}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -366,7 +366,7 @@ func TestWriter(t *testing.T) {
t.Run("close and flush err", func(t *testing.T) {
w := &writer{errAfter: []int{3}}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -384,7 +384,7 @@ func TestWriter(t *testing.T) {
t.Run("close", func(t *testing.T) {
w := &writer{}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err)
@ -406,7 +406,7 @@ func TestWriter(t *testing.T) {
t.Run("zero allocation", func(t *testing.T) {
w := &writer{}
p := &fakePool{}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) {
t.Fatal(n, err)
@ -418,7 +418,7 @@ func TestWriter(t *testing.T) {
w := &writer{}
r := &gen{max: 1 << 12}
p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 1<<12 || err != nil {
@ -434,7 +434,7 @@ func TestWriter(t *testing.T) {
}
p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 1<<12 || !errors.Is(err, errTest) {
@ -450,7 +450,7 @@ func TestWriter(t *testing.T) {
}
p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 1<<11 || !errors.Is(err, errTest) {
@ -462,7 +462,7 @@ func TestWriter(t *testing.T) {
w := &writer{errAfter: []int{1 << 11}}
r := &gen{max: 1 << 12}
p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 1<<11+1<<9 || !errors.Is(err, errTest) {
@ -474,7 +474,7 @@ func TestWriter(t *testing.T) {
w := &writer{}
r := &gen{max: 1 << 12}
p := &fakePool{}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) {
@ -490,7 +490,7 @@ func TestWriter(t *testing.T) {
}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{Pool: p}
o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r)
if n != 512 || !errors.Is(err, io.ErrNoProgress) {

View File

@ -13,7 +13,7 @@ func TestWriteTo(t *testing.T) {
t.Run(title, func(t *testing.T) {
t.Run("write out from zero", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
var b bytes.Buffer
@ -33,7 +33,7 @@ func TestWriteTo(t *testing.T) {
t.Run("write out from started", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
p := make([]byte, 256)
n, err := r.Read(p)
@ -66,7 +66,7 @@ func TestWriteTo(t *testing.T) {
t.Run("after EOF", func(t *testing.T) {
g := &gen{max: 256}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, err := r.Peek(512)
if err != nil {
@ -94,7 +94,7 @@ func TestWriteTo(t *testing.T) {
t.Run("after eof empty", func(t *testing.T) {
g := &gen{max: 256}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
p := make([]byte, 512)
n, err := r.Read(p)
@ -127,7 +127,7 @@ func TestWriteTo(t *testing.T) {
t.Run("write error", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
w := &writer{errAfter: []int{256}}
n, err := r.WriteTo(w)
@ -146,7 +146,7 @@ func TestWriteTo(t *testing.T) {
t.Run("short write", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
w := &writer{shortAfter: []int{256}}
n, err := r.WriteTo(w)
@ -174,7 +174,7 @@ func TestWriteTo(t *testing.T) {
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(64)}
o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o)
b, err := r.Peek(512)
if err != nil {
@ -206,7 +206,7 @@ func TestWriteTo(t *testing.T) {
errAfter: []int{256},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
p := make([]byte, 1<<15)
n, err := r.Read(p)
@ -243,7 +243,7 @@ func TestWriteTo(t *testing.T) {
nullReadAfter: []int{256, 256},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
var b bytes.Buffer
@ -267,7 +267,7 @@ func TestWriteTo(t *testing.T) {
errAfter: []int{256},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o)
var b bytes.Buffer