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

View File

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

View File

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

View File

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

115
lib.go
View File

@ -1,8 +1,8 @@
// Package buffer provides pooled Buffer IO for Go programs. // 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 // 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 // pool. It implements a writer that can be used to write data to an underlying writer in larger, buffered,
// writer. // chunks.
package buffer package buffer
import ( import (
@ -10,9 +10,9 @@ import (
"io" "io"
) )
// Pool defines the interface for the used buffer pool. The buffered reader can be used either with the built-in // BufferPool defines the interface for the used buffer pool. The buffered reader or writer can be used either
// default pool, noop pool, or with a custom pool implementation. // with the built-in default pool, noop pool, or with a custom pool implementation.
type Pool interface { type BufferPool interface {
// Get should return a non-zero length byte slice. In case it returns a zero length byte slice, or an // 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. // 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. // It is OK but not recommended to return varying sizes of byte slices.
Get() ([]byte, error) 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) Put([]byte)
} }
// Options provides options for the Reader. // Options provides options for the Reader.
type Options struct { type Options struct {
// Pool defines the buffer pool to be used. It defaults to the pool created by DefaultPool(). It is // BufferPool defines the buffer pool to be used. It defaults to the pool created by DefaultPool(). It
// expected to explicitly set the Pool instance, otherwise, not defining any globals, each Reader // is expected to explicitly set the Pool instance, otherwise, not defining any globals, each Reader
// instance will create its own pool. // 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 // 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) 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. // via BufferedReader or BufferedContent.
// //
// It reads from the underlying source until the first error, but only returns an error when the buffer is // 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. // The reader does not support concurrent access.
type Reader struct { type Reader struct {
reader *reader 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. // BufferedWriter.
// //
// It writes the input bytes into an internal buffer, and flushes them to the underlying writer only when the // 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 is returned when the used pool returned a zero length byte slice.
ErrZeroAllocation = errors.New("zero allocation") 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 // experienced an error. ErrAbort is returned to the reader process, if Close() was called and no read
// error was received before it. // error was received before it.
ErrAbort = errors.New("read aborted") 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 // DefultPool initializes a synchronized pool that stores and returns byte slices of allocSize length. It can be
// used with multiple readers concurrently. // used with multiple readers concurrently.
func DefaultPool(allocSize int) Pool { func DefaultPool(allocSize int) BufferPool {
if allocSize <= 0 { if allocSize <= 0 {
allocSize = 1 << 12 allocSize = 1 << 12
} }
@ -79,8 +83,8 @@ func DefaultPool(allocSize int) Pool {
return newPool(allocSize) return newPool(allocSize)
} }
// NoPool returns a noop pool. // NoPool returns a noop pool that allocates memory on every call to Get() and does nothing on Put().
func NoPool(allocSize int) Pool { func NoPool(allocSize int) BufferPool {
if allocSize <= 0 { if allocSize <= 0 {
allocSize = 1 << 12 allocSize = 1 << 12
} }
@ -99,11 +103,16 @@ func BufferedReader(in io.Reader, o Options) Reader {
return Reader{} return Reader{}
} }
if o.Pool == nil { if o.BufferPool == nil {
o.Pool = DefaultPool(1 << 12) 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. // 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 // 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. // 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 // 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 // side experiences an error, and the writer still has content to be written, the passed in io.Writer will
// return an ErrAbort error. // return an ErrAbort error.
@ -120,8 +129,8 @@ func BufferedContent(c io.WriterTo, o Options) Reader {
return Reader{} return Reader{}
} }
if o.Pool == nil { if o.BufferPool == nil {
o.Pool = DefaultPool(1 << 12) o.BufferPool = DefaultPool(1 << 12)
} }
return Reader{reader: &reader{options: o, in: mkcontent(c)}} 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. // 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 // It only returns an error when the buffer is empty. It only returns an error when the BufferPool.Get fails or
// underlying reader returns an error. // the underlying reader returns an error.
// //
// It may return zero read length and nil error, but only if the underlying reader did so. // 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) { func (r Reader) Read(p []byte) (int, error) {
if r.reader == nil { if r.reader == nil {
return 0, io.EOF 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. // 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, // 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. // 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 // It returns zero length bytes and nil error, when the delimiter was not found within max.
// underlying reader didn't return an error.
// //
// It returns max or less bytes, false and nil error, if the delimiter was not found within max, there are // It blocks only when less data is buffered than max, the delimiter was not found within the range defined by
// buffered bytes, the underlying reader returned an error. It consumes the returned bytes from the buffer. The // max, and the underlying reader blocks.
// delimiter may still be found in the remaining buffered bytes during subsequent calls. func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, error) {
//
// 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) {
if r.reader == nil { if r.reader == nil {
return nil, false, io.EOF return nil, io.EOF
} }
return r.reader.readBytes(delimiter, max) return r.reader.readBytes(delimiter, max)
@ -175,8 +183,11 @@ func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, bool, error) {
// nil error. // nil error.
// //
// It supports recovery of UTF8 streams by skipping the invalid characters. In such cases, first it returns the // 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. // 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) { func (r Reader) ReadUTF8(max int) ([]rune, int, error) {
if r.reader == nil { if r.reader == nil {
return nil, 0, io.EOF return nil, 0, io.EOF
@ -191,6 +202,8 @@ func (r Reader) ReadUTF8(max int) ([]rune, int, error) {
// by Read, ReadBytes, ReadUTF8 or WriteTo. // by Read, ReadBytes, ReadUTF8 or WriteTo.
// //
// The returned byte slice is not a copy of the buffered bytes, and therefore should not be modified. // 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) { func (r Reader) Peek(max int) ([]byte, error) {
if r.reader == nil { if r.reader == nil {
return nil, io.EOF 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 // It is important that the provided writer must not modify the slice data, as defined in the io.Writer
// interface documentation. // interface documentation.
//
// It blocks when the underlying reader blocks.
func (r Reader) WriteTo(w io.Writer) (int64, error) { func (r Reader) WriteTo(w io.Writer) (int64, error) {
if r.reader == nil { if r.reader == nil {
return 0, nil return 0, nil
@ -223,8 +238,11 @@ func (r Reader) WriteTo(w io.Writer) (int64, error) {
return r.reader.writeTo(w) return r.reader.writeTo(w)
} }
// Close releases the resource held by the Reader, and puts back the underlying byte buffers into the used pool. // Close releases the resource held by the Reader, and puts the underlying byte buffers, that were
// The reader cannot be used for read operations after Close() was called. // 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() { func (r Reader) Close() {
if r.reader == nil { if r.reader == nil {
return return
@ -239,15 +257,22 @@ func BufferedWriter(out io.Writer, o Options) Writer {
return Writer{} return Writer{}
} }
if o.Pool == nil { if o.BufferPool == nil {
o.Pool = DefaultPool(1 << 12) 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 // 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. // 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) { func (w Writer) Write(p []byte) (int, error) {
if w.writer == nil { if w.writer == nil {
return 0, errors.New("unitialized writer") 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 // Flush forces the writer to flush the buffered content to the underlying writer. After flushed, the writer
// still accepts further writes. // still accepts further writes.
//
// It blocks when there is buffered data to write out and the underlying writer blocks.
func (w Writer) Flush() error { func (w Writer) Flush() error {
if w.writer == nil { if w.writer == nil {
return 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 // Close flushes the buffered content if any and closes the writer. After closed, the writer does not accept
// further writes. // 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 { func (w Writer) Close() error {
if w.writer == nil { if w.writer == nil {
return nil return nil

View File

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

View File

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

View File

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

View File

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

181
reader.go
View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ import (
func TestWriter(t *testing.T) { func TestWriter(t *testing.T) {
t.Run("write out", func(t *testing.T) { t.Run("write out", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(32)} o := buffer.Options{BufferPool: buffer.NoPool(32)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -35,7 +35,7 @@ func TestWriter(t *testing.T) {
t.Run("zero bytes when empty", func(t *testing.T) { t.Run("zero bytes when empty", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(32)} o := buffer.Options{BufferPool: buffer.NoPool(32)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write(nil); n != 0 || err != nil { if n, err := b.Write(nil); n != 0 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -44,7 +44,7 @@ func TestWriter(t *testing.T) {
t.Run("zero bytes when erred", func(t *testing.T) { t.Run("zero bytes when erred", func(t *testing.T) {
w := &writer{errAfter: []int{3}} w := &writer{errAfter: []int{3}}
o := buffer.Options{Pool: buffer.NoPool(2)} o := buffer.Options{BufferPool: buffer.NoPool(2)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -69,7 +69,7 @@ func TestWriter(t *testing.T) {
t.Run("zero bytes when not empty", func(t *testing.T) { t.Run("zero bytes when not empty", func(t *testing.T) {
w := &writer{errAfter: []int{3}} w := &writer{errAfter: []int{3}}
o := buffer.Options{Pool: buffer.NoPool(32)} o := buffer.Options{BufferPool: buffer.NoPool(32)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -91,7 +91,7 @@ func TestWriter(t *testing.T) {
errAfter: []int{0}, errAfter: []int{0},
} }
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, errTest) { if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, errTest) {
t.Fatal(n, err) t.Fatal(n, err)
@ -100,7 +100,7 @@ func TestWriter(t *testing.T) {
t.Run("no underlying write until full", func(t *testing.T) { t.Run("no underlying write until full", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)} o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) 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) { t.Run("auto flush when write larger than buffer", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)} o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123456")); err != nil { if n, err := b.Write([]byte("123456")); err != nil {
t.Fatal(n, err) 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) { t.Run("auto flush when multiple smaller writes fill the buffer", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)} o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -170,7 +170,7 @@ func TestWriter(t *testing.T) {
t.Run("flush on close", func(t *testing.T) { t.Run("flush on close", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)} o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -191,7 +191,7 @@ func TestWriter(t *testing.T) {
t.Run("manual flush", func(t *testing.T) { t.Run("manual flush", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)} o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -212,7 +212,7 @@ func TestWriter(t *testing.T) {
t.Run("write after flush", func(t *testing.T) { t.Run("write after flush", func(t *testing.T) {
w := &writer{} w := &writer{}
o := buffer.Options{Pool: buffer.NoPool(4)} o := buffer.Options{BufferPool: buffer.NoPool(4)}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -246,7 +246,7 @@ func TestWriter(t *testing.T) {
t.Run("zero write recover", func(t *testing.T) { t.Run("zero write recover", func(t *testing.T) {
w := &writer{zeroAfter: []int{2}} w := &writer{zeroAfter: []int{2}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -268,7 +268,7 @@ func TestWriter(t *testing.T) {
t.Run("zero write terminal", func(t *testing.T) { t.Run("zero write terminal", func(t *testing.T) {
w := &writer{zeroAfter: []int{2, 2}} w := &writer{zeroAfter: []int{2, 2}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -282,7 +282,7 @@ func TestWriter(t *testing.T) {
t.Run("partial write", func(t *testing.T) { t.Run("partial write", func(t *testing.T) {
w := &writer{shortAfter: []int{2}} w := &writer{shortAfter: []int{2}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); err != nil { if n, err := b.Write([]byte("123")); err != nil {
t.Fatal(n, err) 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) { t.Run("buffer released on write error immediately", func(t *testing.T) {
w := &writer{errAfter: []int{0}} w := &writer{errAfter: []int{0}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 2 || !errors.Is(err, errTest) { if n, err := b.Write([]byte("123")); n != 2 || !errors.Is(err, errTest) {
t.Fatal(n, err) t.Fatal(n, err)
@ -318,7 +318,7 @@ func TestWriter(t *testing.T) {
t.Run("buffer released on write error", func(t *testing.T) { t.Run("buffer released on write error", func(t *testing.T) {
w := &writer{errAfter: []int{3}} w := &writer{errAfter: []int{3}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -340,7 +340,7 @@ func TestWriter(t *testing.T) {
t.Run("close on err", func(t *testing.T) { t.Run("close on err", func(t *testing.T) {
w := &writer{errAfter: []int{3}} w := &writer{errAfter: []int{3}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -366,7 +366,7 @@ func TestWriter(t *testing.T) {
t.Run("close and flush err", func(t *testing.T) { t.Run("close and flush err", func(t *testing.T) {
w := &writer{errAfter: []int{3}} w := &writer{errAfter: []int{3}}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -384,7 +384,7 @@ func TestWriter(t *testing.T) {
t.Run("close", func(t *testing.T) { t.Run("close", func(t *testing.T) {
w := &writer{} w := &writer{}
p := &fakePool{allocSize: 2} p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 3 || err != nil { if n, err := b.Write([]byte("123")); n != 3 || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
@ -406,7 +406,7 @@ func TestWriter(t *testing.T) {
t.Run("zero allocation", func(t *testing.T) { t.Run("zero allocation", func(t *testing.T) {
w := &writer{} w := &writer{}
p := &fakePool{} p := &fakePool{}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) { if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) {
t.Fatal(n, err) t.Fatal(n, err)
@ -418,7 +418,7 @@ func TestWriter(t *testing.T) {
w := &writer{} w := &writer{}
r := &gen{max: 1 << 12} r := &gen{max: 1 << 12}
p := buffer.NoPool(1 << 9) p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r) n, err := b.ReadFrom(r)
if n != 1<<12 || err != nil { if n != 1<<12 || err != nil {
@ -434,7 +434,7 @@ func TestWriter(t *testing.T) {
} }
p := buffer.NoPool(1 << 9) p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r) n, err := b.ReadFrom(r)
if n != 1<<12 || !errors.Is(err, errTest) { if n != 1<<12 || !errors.Is(err, errTest) {
@ -450,7 +450,7 @@ func TestWriter(t *testing.T) {
} }
p := buffer.NoPool(1 << 9) p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r) n, err := b.ReadFrom(r)
if n != 1<<11 || !errors.Is(err, errTest) { if n != 1<<11 || !errors.Is(err, errTest) {
@ -462,7 +462,7 @@ func TestWriter(t *testing.T) {
w := &writer{errAfter: []int{1 << 11}} w := &writer{errAfter: []int{1 << 11}}
r := &gen{max: 1 << 12} r := &gen{max: 1 << 12}
p := buffer.NoPool(1 << 9) p := buffer.NoPool(1 << 9)
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r) n, err := b.ReadFrom(r)
if n != 1<<11+1<<9 || !errors.Is(err, errTest) { if n != 1<<11+1<<9 || !errors.Is(err, errTest) {
@ -474,7 +474,7 @@ func TestWriter(t *testing.T) {
w := &writer{} w := &writer{}
r := &gen{max: 1 << 12} r := &gen{max: 1 << 12}
p := &fakePool{} p := &fakePool{}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r) n, err := b.ReadFrom(r)
if n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) { if n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) {
@ -490,7 +490,7 @@ func TestWriter(t *testing.T) {
} }
p := &fakePool{allocSize: 1 << 9} p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{Pool: p} o := buffer.Options{BufferPool: p}
b := buffer.BufferedWriter(w, o) b := buffer.BufferedWriter(w, o)
n, err := b.ReadFrom(r) n, err := b.ReadFrom(r)
if n != 512 || !errors.Is(err, io.ErrNoProgress) { 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(title, func(t *testing.T) {
t.Run("write out from zero", func(t *testing.T) { t.Run("write out from zero", func(t *testing.T) {
g := &gen{max: 1 << 15} 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) r := cr(g, o)
var b bytes.Buffer var b bytes.Buffer
@ -33,7 +33,7 @@ func TestWriteTo(t *testing.T) {
t.Run("write out from started", func(t *testing.T) { t.Run("write out from started", func(t *testing.T) {
g := &gen{max: 1 << 15} 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) r := cr(g, o)
p := make([]byte, 256) p := make([]byte, 256)
n, err := r.Read(p) n, err := r.Read(p)
@ -66,7 +66,7 @@ func TestWriteTo(t *testing.T) {
t.Run("after EOF", func(t *testing.T) { t.Run("after EOF", func(t *testing.T) {
g := &gen{max: 256} g := &gen{max: 256}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)} o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o) r := cr(g, o)
b, err := r.Peek(512) b, err := r.Peek(512)
if err != nil { if err != nil {
@ -94,7 +94,7 @@ func TestWriteTo(t *testing.T) {
t.Run("after eof empty", func(t *testing.T) { t.Run("after eof empty", func(t *testing.T) {
g := &gen{max: 256} g := &gen{max: 256}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)} o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o) r := cr(g, o)
p := make([]byte, 512) p := make([]byte, 512)
n, err := r.Read(p) n, err := r.Read(p)
@ -127,7 +127,7 @@ func TestWriteTo(t *testing.T) {
t.Run("write error", func(t *testing.T) { t.Run("write error", func(t *testing.T) {
g := &gen{max: 1 << 15} 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) r := cr(g, o)
w := &writer{errAfter: []int{256}} w := &writer{errAfter: []int{256}}
n, err := r.WriteTo(w) n, err := r.WriteTo(w)
@ -146,7 +146,7 @@ func TestWriteTo(t *testing.T) {
t.Run("short write", func(t *testing.T) { t.Run("short write", func(t *testing.T) {
g := &gen{max: 1 << 15} 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) r := cr(g, o)
w := &writer{shortAfter: []int{256}} w := &writer{shortAfter: []int{256}}
n, err := r.WriteTo(w) n, err := r.WriteTo(w)
@ -174,7 +174,7 @@ func TestWriteTo(t *testing.T) {
fastErr: true, fastErr: true,
} }
o := buffer.Options{Pool: buffer.NoPool(64)} o := buffer.Options{BufferPool: buffer.NoPool(64)}
r := cr(g, o) r := cr(g, o)
b, err := r.Peek(512) b, err := r.Peek(512)
if err != nil { if err != nil {
@ -206,7 +206,7 @@ func TestWriteTo(t *testing.T) {
errAfter: []int{256}, errAfter: []int{256},
} }
o := buffer.Options{Pool: buffer.NoPool(1 << 12)} o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o) r := cr(g, o)
p := make([]byte, 1<<15) p := make([]byte, 1<<15)
n, err := r.Read(p) n, err := r.Read(p)
@ -243,7 +243,7 @@ func TestWriteTo(t *testing.T) {
nullReadAfter: []int{256, 256}, nullReadAfter: []int{256, 256},
} }
o := buffer.Options{Pool: buffer.NoPool(1 << 12)} o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o) r := cr(g, o)
var b bytes.Buffer var b bytes.Buffer
@ -267,7 +267,7 @@ func TestWriteTo(t *testing.T) {
errAfter: []int{256}, errAfter: []int{256},
} }
o := buffer.Options{Pool: buffer.NoPool(1 << 12)} o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)}
r := cr(g, o) r := cr(g, o)
var b bytes.Buffer var b bytes.Buffer