diff --git a/content_test.go b/content_test.go index 3ccd9ab..f86fbb3 100644 --- a/content_test.go +++ b/content_test.go @@ -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}, } diff --git a/lib.go b/lib.go index c7c2ed3..dd1f94e 100644 --- a/lib.go +++ b/lib.go @@ -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} } diff --git a/lib_test.go b/lib_test.go index 093b5b5..1f2d9c9 100644 --- a/lib_test.go +++ b/lib_test.go @@ -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) +func TestThroughputPooled(t *testing.T) { + p := &foreverPool{allocSize: 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 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) { - 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} - dst.Reset() + 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) { - dst := bytes.NewBuffer(nil) - wo := writerOnly{dst} for pb.Next() { + r, _ := p.Get() src := &gen{max: 1 << 18} - r := bufio.NewReader(src) + r.Reset(src) ro := readerOnly{r} - dst.Reset() + dst := bytes.NewBuffer(nil) + wo := writerOnly{dst} 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) - } -} diff --git a/pool_test.go b/pool_test.go index 5fa5d78..99143e7 100644 --- a/pool_test.go +++ b/pool_test.go @@ -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}, } diff --git a/readbytes_test.go b/readbytes_test.go index 30e1153..1ee32d5 100644 --- a/readbytes_test.go +++ b/readbytes_test.go @@ -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)} diff --git a/reader.go b/reader.go index 9c99b3d..44f0dd9 100644 --- a/reader.go +++ b/reader.go @@ -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 }