1
0
This commit is contained in:
Arpad Ryszka 2026-02-28 14:12:34 +01:00
parent 49f465619d
commit a921d02997
6 changed files with 282 additions and 122 deletions

View File

@ -22,7 +22,7 @@ func TestContent(t *testing.T) {
return n, nil
})
p := &pool{allocSize: 2}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -48,7 +48,7 @@ func TestContent(t *testing.T) {
return 0, nil
})
p := &pool{allocSize: 2}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -72,7 +72,7 @@ func TestContent(t *testing.T) {
return n, errTest
})
p := &pool{allocSize: 2}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -98,7 +98,7 @@ func TestContent(t *testing.T) {
return 0, errTest
})
p := &pool{allocSize: 2}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -122,7 +122,7 @@ func TestContent(t *testing.T) {
return n, nil
})
p := &pool{
p := &fakePool{
allocSize: 2,
errAfter: []int{1},
}
@ -154,7 +154,7 @@ func TestContent(t *testing.T) {
return n, nil
})
p := &pool{
p := &fakePool{
allocSize: 2,
errAfter: []int{0},
}
@ -175,7 +175,7 @@ func TestContent(t *testing.T) {
return 0, nil
})
p := &pool{
p := &fakePool{
allocSize: 2,
errAfter: []int{1},
}
@ -202,7 +202,7 @@ func TestContent(t *testing.T) {
return 0, nil
})
p := &pool{allocSize: 2}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -232,7 +232,7 @@ func TestContent(t *testing.T) {
return 0, nil
})
p := &pool{allocSize: 2}
p := &fakePool{allocSize: 2}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -267,7 +267,7 @@ func TestContent(t *testing.T) {
return n, errTest
})
p := &pool{allocSize: 3}
p := &fakePool{allocSize: 3}
o := buffer.Options{Pool: p}
r := buffer.BufferedContent(c, o)
b := make([]byte, 3)
@ -296,7 +296,7 @@ func TestContent(t *testing.T) {
return 0, errTest2
})
p := &pool{
p := &fakePool{
allocSize: 2,
errAfter: []int{1},
}

6
lib.go
View File

@ -59,7 +59,7 @@ var (
// DefultPool initializes a synchronized pool that stores and returns byte slices of allocSize length. It can be
// used with multiple readers concurrently.
func DefaultPool(allocSize int) Pool {
if allocSize == 0 {
if allocSize <= 0 {
allocSize = 1 << 12
}
@ -68,6 +68,10 @@ func DefaultPool(allocSize int) Pool {
// NoPool returns a noop pool.
func NoPool(allocSize int) Pool {
if allocSize <= 0 {
allocSize = 1 << 12
}
return noPool{allocSize: allocSize}
}

View File

@ -6,7 +6,6 @@ import (
"code.squareroundforest.org/arpio/buffer"
"errors"
"io"
"runtime"
"testing"
)
@ -172,57 +171,32 @@ func (w writerOnly) Write(p []byte) (int, error) {
}
func TestBenchmarkThroughput(t *testing.T) {
p := buffer.DefaultPool(0)
p := buffer.NoPool(0)
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
dst.Reset()
if n, err := io.Copy(wo, ro); n != 1<<18 || err != nil {
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func TestBenchmarkCompare(t *testing.T) {
func TestBenchmarkThroughputCompare(t *testing.T) {
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
src := &gen{max: 1 << 18}
r := bufio.NewReader(src)
ro := readerOnly{r}
dst.Reset()
if n, err := io.Copy(wo, ro); n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func TestBenchmarkThroughputSTDLib(t *testing.T) {
p := buffer.DefaultPool(0)
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
src := bytes.NewBuffer(make([]byte, 1<<18))
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
dst.Reset()
if n, err := io.Copy(wo, ro); n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func TestBenchmarkCompareSTDLib(t *testing.T) {
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
src := bytes.NewBuffer(make([]byte, 1<<18))
r := bufio.NewReader(src)
ro := readerOnly{r}
dst.Reset()
if n, err := io.Copy(wo, ro); n != 1<<18 || err != nil {
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func BenchmarkThroughput(b *testing.B) {
p := buffer.DefaultPool(0)
p := buffer.NoPool(0)
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for i := 0; i < b.N; i++ {
@ -234,7 +208,7 @@ func BenchmarkThroughput(b *testing.B) {
}
}
func BenchmarkCompare(b *testing.B) {
func BenchmarkThroughputCompare(b *testing.B) {
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for i := 0; i < b.N; i++ {
@ -246,58 +220,128 @@ func BenchmarkCompare(b *testing.B) {
}
}
func BenchmarkThroughputP(b *testing.B) {
p := buffer.DefaultPool(0)
b.SetParallelism(runtime.GOMAXPROCS(-1) * 64)
b.RunParallel(func(pb *testing.PB) {
func TestThroughputPooled(t *testing.T) {
p := &foreverPool{allocSize: 1 << 12}
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for pb.Next() {
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func TestThroughputPooledCompare(t *testing.T) {
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
r := bufio.NewReader(nil)
src := &gen{max: 1 << 18}
r.Reset(src)
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func BenchmarkThroughputPooled(b *testing.B) {
p := &foreverPool{allocSize: 1 << 12}
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for i := 0; i < b.N; i++ {
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
dst.Reset()
io.Copy(wo, ro)
}
}
func BenchmarkThroughputPooledCompare(b *testing.B) {
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
r := bufio.NewReader(nil)
for i := 0; i < b.N; i++ {
src := &gen{max: 1 << 18}
r.Reset(src)
ro := readerOnly{r}
dst.Reset()
io.Copy(wo, ro)
}
}
func TestThroughputPooledParallel(t *testing.T) {
p := newSyncedForeverPool(func() []byte {
return make([]byte, 1<<12)
})
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func TestThroughputPooledParallelCompare(t *testing.T) {
p := newSyncedForeverPool(func() *bufio.Reader {
return bufio.NewReader(nil)
})
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
r, err := p.Get()
if err != nil {
t.Fatal(err)
}
src := &gen{max: 1 << 18}
r.Reset(src)
ro := readerOnly{r}
n, err := io.Copy(wo, ro)
if n != 1<<18 || err != nil {
t.Fatal(n, err)
}
}
func BenchmarkThroughputPooledParallel(b *testing.B) {
p := newSyncedForeverPool(func() []byte {
return make([]byte, 1<<12)
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
src := &gen{max: 1 << 18}
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
io.Copy(wo, ro)
}
})
}
func BenchmarkCompareP(b *testing.B) {
b.SetParallelism(runtime.GOMAXPROCS(-1) * 64)
func BenchmarkThroughputPooledParallelCompare(b *testing.B) {
p := newSyncedForeverPool(func() *bufio.Reader {
return bufio.NewReader(nil)
})
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
r, _ := p.Get()
src := &gen{max: 1 << 18}
r.Reset(src)
ro := readerOnly{r}
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for pb.Next() {
src := &gen{max: 1 << 18}
r := bufio.NewReader(src)
ro := readerOnly{r}
dst.Reset()
io.Copy(wo, ro)
}
})
}
func BenchmarkThroughputSTDLib(b *testing.B) {
p := buffer.DefaultPool(0)
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for i := 0; i < b.N; i++ {
src := bytes.NewBuffer(make([]byte, 1<<18))
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
ro := readerOnly{r}
dst.Reset()
io.Copy(wo, ro)
}
}
func BenchmarkCompareSTDLib(b *testing.B) {
dst := bytes.NewBuffer(nil)
wo := writerOnly{dst}
for i := 0; i < b.N; i++ {
src := bytes.NewBuffer(make([]byte, 1<<18))
r := bufio.NewReader(src)
ro := readerOnly{r}
dst.Reset()
io.Copy(wo, ro)
}
}

View File

@ -9,7 +9,7 @@ import (
"testing"
)
type pool struct {
type fakePool struct {
allocSize int
alloc, free int
errAfter []int
@ -18,7 +18,17 @@ type pool struct {
rand *rand.Rand
}
func (p pool) allocCondition(c *[]int) bool {
type foreverPool struct {
allocSize int
items [][]byte
}
type syncedForeverPool[T any] struct {
create func() T
items chan []T
}
func (p fakePool) allocCondition(c *[]int) bool {
if len(*c) == 0 {
return false
}
@ -31,7 +41,7 @@ func (p pool) allocCondition(c *[]int) bool {
return true
}
func (p *pool) ensureRand() {
func (p *fakePool) ensureRand() {
if p.rand != nil {
return
}
@ -39,7 +49,7 @@ func (p *pool) ensureRand() {
p.rand = rand.New(rand.NewSource(9))
}
func (p *pool) Get() ([]byte, error) {
func (p *fakePool) Get() ([]byte, error) {
defer func() {
p.alloc++
}()
@ -66,16 +76,63 @@ func (p *pool) Get() ([]byte, error) {
return make([]byte, n), nil
}
func (p *pool) Put([]byte) {
func (p *fakePool) Put([]byte) {
p.free++
}
func (p *foreverPool) Get() ([]byte, error) {
if len(p.items) == 0 {
return make([]byte, p.allocSize), nil
}
var i []byte
i, p.items = p.items[0], p.items[1:]
return i, nil
}
func (p *foreverPool) Put(i []byte) {
p.items = append(p.items, i)
}
func newSyncedForeverPool[T any](c func() T) *syncedForeverPool[T] {
items := make(chan []T, 1)
items <- nil
return &syncedForeverPool[T]{
create: c,
items: items,
}
}
func (p *syncedForeverPool[T]) Get() (T, error) {
items := <-p.items
defer func() {
p.items <- items
}()
if len(items) == 0 {
return p.create(), nil
}
var i T
i, items = items[0], items[1:]
return i, nil
}
func (p *syncedForeverPool[T]) Put(i T) {
items := <-p.items
defer func() {
p.items <- items
}()
items = append(items, i)
}
func TestPoolUsage(t *testing.T) {
for _, cr := range []createReader{buffer.BufferedReader, testContent} {
t.Run("allocate", func(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
b := make([]byte, 256)
@ -99,7 +156,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 9}
p := &fakePool{allocSize: 1 << 9}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<12)
@ -122,7 +179,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read utf8", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
runes, n, err := r.ReadUTF8(1 << 12)
@ -145,7 +202,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, err := r.Peek(3 * 1 << 12)
@ -164,7 +221,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("buffered", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
b := r.Buffered()
@ -179,7 +236,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
@ -206,7 +263,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("free", func(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
b := make([]byte, 1<<9)
@ -232,7 +289,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
_, _, err := r.ReadBytes([]byte("123"), 1<<15+3)
@ -256,7 +313,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read utf8", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
for {
@ -285,7 +342,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, err := r.Peek(3 * 1 << 12)
@ -328,7 +385,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("buffered", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
b := r.Buffered()
@ -343,7 +400,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1 << 12}
p := &fakePool{allocSize: 1 << 12}
o := buffer.Options{Pool: p}
r := cr(g, o)
@ -374,7 +431,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("null segment", func(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 12,
zeroAfter: []int{0},
}
@ -390,7 +447,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 9,
zeroAfter: []int{1},
}
@ -410,7 +467,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read utf8", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 12,
zeroAfter: []int{0},
}
@ -425,7 +482,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 12,
zeroAfter: []int{0},
}
@ -440,7 +497,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 12,
zeroAfter: []int{0},
}
@ -465,7 +522,7 @@ func TestPoolUsage(t *testing.T) {
customContent: map[int][]byte{1 << 11: []byte("123")},
}
p := &pool{varyingSize: []int{8, 256}}
p := &fakePool{varyingSize: []int{8, 256}}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
@ -484,7 +541,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("find not", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{varyingSize: []int{8, 256}}
p := &fakePool{varyingSize: []int{8, 256}}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
@ -504,7 +561,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{varyingSize: []int{8, 256}}
p := &fakePool{varyingSize: []int{8, 256}}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, err := r.Peek(1 << 11)
@ -521,7 +578,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("one byte segments", func(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, err := io.ReadAll(r)
@ -542,7 +599,7 @@ func TestPoolUsage(t *testing.T) {
customContent: map[int][]byte{1 << 11: []byte("123")},
}
p := &pool{allocSize: 1}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
@ -561,7 +618,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("find not", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 1<<15)
@ -585,7 +642,7 @@ func TestPoolUsage(t *testing.T) {
rng: utf8W2Range,
}
p := &pool{allocSize: 1}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
r := cr(g, o)
runes, n, err := r.ReadUTF8(1 << 14)
@ -604,7 +661,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
r := cr(g, o)
b, err := r.Peek(1 << 14)
@ -619,7 +676,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{allocSize: 1}
p := &fakePool{allocSize: 1}
o := buffer.Options{Pool: p}
r := cr(g, o)
@ -642,7 +699,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("pool error on allocate", func(t *testing.T) {
t.Run("read", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{0},
}
@ -662,7 +719,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("read bytes", func(t *testing.T) {
t.Run("immediate", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{0},
}
@ -685,7 +742,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("on grow", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{1},
}
@ -726,7 +783,7 @@ func TestPoolUsage(t *testing.T) {
rng: utf8W2Range,
}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{0},
}
@ -750,7 +807,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("peek", func(t *testing.T) {
t.Run("immediate", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{0},
}
@ -769,7 +826,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("on grow", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{1},
}
@ -811,7 +868,7 @@ func TestPoolUsage(t *testing.T) {
t.Run("write to", func(t *testing.T) {
g := &gen{max: 1 << 15}
p := &pool{
p := &fakePool{
allocSize: 1 << 11,
errAfter: []int{0},
}

View File

@ -437,6 +437,58 @@ func TestReadBytes(t *testing.T) {
}
})
t.Run("single char delimiter", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("1")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte{'1'}, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), '1')) {
t.Fatal("failed to generate right content")
}
})
t.Run("large delimiter", func(t *testing.T) {
src := &gen{
max: 1 << 18,
customContentAfter: []int{1<<17 - 3},
customContent: map[int][]byte{1<<17 - 3: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: buffer.NoPool(1 << 12)})
r.ReadBytes([]byte{'1'}, 1<<17+1<<16)
})
t.Run("delimiter longer than content", func(t *testing.T) {
g := &gen{max: 1 << 9}
o := buffer.Options{Pool: buffer.NoPool(16)}
r := cr(g, o)
b, ok, err := r.ReadBytes(generate(1<<10), 1<<11)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(1<<9)) {
t.Fatal("content")
}
})
t.Run("find not none consumed", func(t *testing.T) {
g := &gen{}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}

View File

@ -225,8 +225,11 @@ func (r *reader) fillToDelimiter(delimiter []byte, max int) (int, bool) {
m, c := match(d, r.segments[seg][first:last])
if !m {
i += 1 - len(delimiter) + len(d)
seg, segStart = r.findSegmentDown(i)
d = delimiter
if i < r.len {
seg, segStart = r.findSegmentDown(i)
}
continue
}