From cdbf48fa2fd835630a48bcc5ad3fe73ff827370f Mon Sep 17 00:00:00 2001 From: Arpad Ryszka Date: Wed, 15 Apr 2026 18:34:23 +0200 Subject: [PATCH] refactor: simplify based on blocking/non-blocking behavior changes; allow initial segment from outside of the used pool --- block_test.go | 198 +++++++++++++++++++++++++++++++++ buffered_test.go | 14 +-- content.go | 2 +- content_test.go | 118 +++++++++++--------- io_test.go | 7 ++ lib.go | 115 +++++++++++++------- lib_test.go | 157 +++++++++++++++++---------- peek_test.go | 20 ++-- pool_test.go | 176 ++++++++++++++---------------- read_test.go | 30 ++--- readbytes_test.go | 271 +++++++++++----------------------------------- reader.go | 179 +++++++++++++++--------------- readutf8_test.go | 30 ++--- writer.go | 10 +- writer_test.go | 52 ++++----- writeto_test.go | 20 ++-- 16 files changed, 773 insertions(+), 626 deletions(-) create mode 100644 block_test.go diff --git a/block_test.go b/block_test.go new file mode 100644 index 0000000..19604e3 --- /dev/null +++ b/block_test.go @@ -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: + } + }) +} diff --git a/buffered_test.go b/buffered_test.go index fc358a8..5c6c46d 100644 --- a/buffered_test.go +++ b/buffered_test.go @@ -11,7 +11,7 @@ func TestBuffered(t *testing.T) { t.Run(title, func(t *testing.T) { t.Run("none buffered", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b := r.Buffered() if len(b) != 0 { @@ -25,7 +25,7 @@ func TestBuffered(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(18) if err != nil { @@ -44,7 +44,7 @@ func TestBuffered(t *testing.T) { t.Run("all buffered", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(48) if err != nil { @@ -63,7 +63,7 @@ func TestBuffered(t *testing.T) { t.Run("buffered across segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(144) if err != nil { @@ -82,7 +82,7 @@ func TestBuffered(t *testing.T) { t.Run("buffered mid segment", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) b := make([]byte, 32) n, err := r.Read(b) @@ -106,7 +106,7 @@ func TestBuffered(t *testing.T) { t.Run("buffered mid segment across segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) b, err := r.Peek(288) if err != nil { @@ -125,7 +125,7 @@ func TestBuffered(t *testing.T) { t.Run("zero buffered mid segment", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b := make([]byte, 64) n, err := r.Read(b) diff --git a/content.go b/content.go index 7cbcaf7..774b1bb 100644 --- a/content.go +++ b/content.go @@ -63,7 +63,7 @@ func (c *content) writeTo() { return } - err := fmt.Errorf("panic provided WriterTo: %v", r) + err := fmt.Errorf("panic from provided WriterTo: %v", r) w.w <- syncMessage{err: err} close(w.w) }() diff --git a/content_test.go b/content_test.go index f86fbb3..79c4ca7 100644 --- a/content_test.go +++ b/content_test.go @@ -11,8 +11,8 @@ func TestContent(t *testing.T) { t.Run("eof", func(t *testing.T) { c := buffer.ContentFunc(func(w io.Writer) (int64, error) { var n int64 - for i := 0; i < 3; i++ { - ni, err := w.Write([]byte("123456789")[i*3 : i*3+3]) + for i := 0; i < 4; i++ { + ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3]) n += int64(ni) if err != nil { return n, err @@ -23,18 +23,21 @@ func TestContent(t *testing.T) { }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) - for i := 0; i < 3; i++ { + exp := []byte("123456789012") + for i := 0; i < 8; i++ { n, err := r.Read(b) - if n != 3 || err != nil { - t.Fatal(n, err) + if n != 2-i%2 || err != nil { + t.Fatal(i, n, err) } - if string(b) != "123456789"[i*3:i*3+3] { - t.Fatal(string(b)) + if string(b[:n]) != string(exp[:n]) { + t.Fatal(i, string(b[:n]), string(exp[:n])) } + + exp = exp[n:] } n, err := r.Read(b) @@ -49,7 +52,7 @@ func TestContent(t *testing.T) { }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) n, err := r.Read(b) @@ -61,8 +64,8 @@ func TestContent(t *testing.T) { t.Run("writer error", func(t *testing.T) { c := buffer.ContentFunc(func(w io.Writer) (int64, error) { var n int64 - for i := 0; i < 3; i++ { - ni, err := w.Write([]byte("123456789")[i*3 : i*3+3]) + for i := 0; i < 4; i++ { + ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3]) n += int64(ni) if err != nil { return n, err @@ -73,18 +76,21 @@ func TestContent(t *testing.T) { }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) - for i := 0; i < 3; i++ { + exp := []byte("123456789012") + for i := 0; i < 8; i++ { n, err := r.Read(b) - if n != 3 || err != nil { - t.Fatal(n, err) + if n != 2-i%2 || err != nil { + t.Fatal(i, n, err) } - if string(b) != "123456789"[i*3:i*3+3] { + if string(b[:n]) != string(exp[:n]) { t.Fatal(string(b)) } + + exp = exp[n:] } n, err := r.Read(b) @@ -99,7 +105,7 @@ func TestContent(t *testing.T) { }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) n, err := r.Read(b) @@ -127,16 +133,16 @@ func TestContent(t *testing.T) { errAfter: []int{1}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) - b, ok, err := r.ReadBytes([]byte("67"), 12) - if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil { - t.Fatal(string(b), ok, err) + b, err := r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || err != nil { + t.Fatal(string(b), err) } - b, ok, err = r.ReadBytes([]byte("67"), 12) - if len(b) != 0 || ok || !errors.Is(err, errTest) { - t.Fatal(string(b), ok, err) + b, err = r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || err != nil { + t.Fatal(string(b), err) } }) @@ -159,11 +165,11 @@ func TestContent(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) - b, ok, err := r.ReadBytes([]byte("67"), 12) - if len(b) != 0 || ok || !errors.Is(err, errTest) { - t.Fatal(string(b), ok, err) + b, err := r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || !errors.Is(err, errTest) { + t.Fatal(string(b), err) } }) @@ -180,16 +186,16 @@ func TestContent(t *testing.T) { errAfter: []int{1}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) - b, ok, err := r.ReadBytes([]byte("67"), 12) - if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil { - t.Fatal(string(b), ok, err) + b, err := r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || err != nil { + t.Fatal(string(b), err) } - b, ok, err = r.ReadBytes([]byte("67"), 12) - if len(b) != 0 || ok || !errors.Is(err, errTest) { - t.Fatal(string(b), ok, err) + b, err = r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || err != nil { + t.Fatal(string(b), err) } }) @@ -199,22 +205,26 @@ func TestContent(t *testing.T) { w.Write(nil) w.Write([]byte("456")) w.Write([]byte("789")) + w.Write([]byte("012")) return 0, nil }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) - for i := 0; i < 3; i++ { + exp := []byte("123456789012") + for i := 0; i < 8; i++ { n, err := r.Read(b) - if n != 3 || err != nil { + if n != 2-i%2 || err != nil { t.Fatal(n, err) } - if string(b) != "123456789"[i*3:i*3+3] { - t.Fatal(string(b)) + if string(b[:n]) != string(exp[:n]) { + t.Fatal(string(b[:n])) } + + exp = exp[n:] } n, err := r.Read(b) @@ -229,22 +239,26 @@ func TestContent(t *testing.T) { w.Write([]byte("123")) w.Write([]byte("456")) w.Write([]byte("789")) + w.Write([]byte("012")) return 0, nil }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) - for i := 0; i < 3; i++ { + exp := []byte("123456789012") + for i := 0; i < 8; i++ { n, err := r.Read(b) - if n != 3 || err != nil { + if n != 2-i%2 || err != nil { t.Fatal(n, err) } - if string(b) != "123456789"[i*3:i*3+3] { + if string(b[:n]) != string(exp[:n]) { t.Fatal(string(b)) } + + exp = exp[n:] } n, err := r.Read(b) @@ -268,7 +282,7 @@ func TestContent(t *testing.T) { }) p := &fakePool{allocSize: 3} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) for i := 0; i < 3; i++ { @@ -301,16 +315,16 @@ func TestContent(t *testing.T) { errAfter: []int{1}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) - b, ok, err := r.ReadBytes([]byte("67"), 12) - if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil { - t.Fatal(string(b), ok, err) + b, err := r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || err != nil { + t.Fatal(string(b), err) } - b, ok, err = r.ReadBytes([]byte("67"), 12) - if len(b) != 0 || ok || !errors.Is(err, errTest) || !errors.Is(err, errTest2) { - t.Fatal(string(b), ok, err) + b, err = r.ReadBytes([]byte("67"), 12) + if len(b) != 0 || err != nil { + t.Fatal(string(b), err) } }) } diff --git a/io_test.go b/io_test.go index bc86880..5499936 100644 --- a/io_test.go +++ b/io_test.go @@ -17,6 +17,8 @@ type gen struct { customContentAfter []int customContent map[int][]byte counter int + blockAfter []int + unblock map[int]<-chan struct{} } type writer struct { @@ -39,6 +41,11 @@ func (g *gen) Read(p []byte) (int, error) { return 0, io.EOF } + if len(g.blockAfter) > 0 && g.counter >= g.blockAfter[0] { + <-g.unblock[g.blockAfter[0]] + g.blockAfter = g.blockAfter[1:] + } + if len(g.nullReadAfter) > 0 && g.counter >= g.nullReadAfter[0] { g.nullReadAfter = g.nullReadAfter[1:] return 0, nil diff --git a/lib.go b/lib.go index 6f92171..08481af 100644 --- a/lib.go +++ b/lib.go @@ -1,8 +1,8 @@ // Package buffer provides pooled Buffer IO for Go programs. // // It implements a reader similar to bufio.Reader. The underlying memory buffers can be used from a synchronized -// pool. It implements a writer that can be used to avoid writing too small number of bytes to an underlying -// writer. +// pool. It implements a writer that can be used to write data to an underlying writer in larger, buffered, +// chunks. package buffer import ( @@ -10,9 +10,9 @@ import ( "io" ) -// Pool defines the interface for the used buffer pool. The buffered reader can be used either with the built-in -// default pool, noop pool, or with a custom pool implementation. -type Pool interface { +// BufferPool defines the interface for the used buffer pool. The buffered reader or writer can be used either +// with the built-in default pool, noop pool, or with a custom pool implementation. +type BufferPool interface { // Get should return a non-zero length byte slice. In case it returns a zero length byte slice, or an // explicit error, the read operations will fail. @@ -20,35 +20,39 @@ type Pool interface { // It is OK but not recommended to return varying sizes of byte slices. Get() ([]byte, error) - // The reader always puts back the byte slices taken by Get, using Put. + // The reader puts back the byte slices taken by Get, using Put, when it does not use them anymore. Put([]byte) } // Options provides options for the Reader. type Options struct { - // Pool defines the buffer pool to be used. It defaults to the pool created by DefaultPool(). It is - // expected to explicitly set the Pool instance, otherwise, not defining any globals, each Reader + // BufferPool defines the buffer pool to be used. It defaults to the pool created by DefaultPool(). It + // is expected to explicitly set the Pool instance, otherwise, not defining any globals, each Reader // instance will create its own pool. - Pool Pool + BufferPool BufferPool + + // InitialSegment allows to initialize the buffer with a memory space already available to the user code + // without requesting it from the used pool. + InitialSegment []byte } // ContentFunc wraps a function to implement the io.WriterTo interface. Function implementations should be -// reader to be executed in goroutines other than what they were created in. +// ready to be executed in goroutines other than what they were created in. type ContentFunc func(io.Writer) (int64, error) -// Reader wraps an underlying io.Reader or io.WriterTo, and provides buffered io via its methods. Initialize it +// Reader wraps an underlying io.Reader or io.WriterTo, and provides buffered IO via its methods. Initialize it // via BufferedReader or BufferedContent. // // It reads from the underlying source until the first error, but only returns an error when the buffer is -// empty. Once the underlying reader returned an error, it doesn't attempt to read from it anymore. +// empty. Once the underlying reader returned an error, it does not attempt to read from it anymore. // // The reader does not support concurrent access. type Reader struct { reader *reader } -// Writer wraps an underlying io.Writer, and provides buffered io via its methods. Initialize it via +// Writer wraps an underlying io.Writer and provides buffered IO via its methods. Initialize it via // BufferedWriter. // // It writes the input bytes into an internal buffer, and flushes them to the underlying writer only when the @@ -63,7 +67,7 @@ var ( // ErrZeroAllocation is returned when the used pool returned a zero length byte slice. ErrZeroAllocation = errors.New("zero allocation") - // ErrAbort is returned to the writer process in case of buffered content, when the reader + // ErrAbort is returned to the writer process when using buffered content, when the reader // experienced an error. ErrAbort is returned to the reader process, if Close() was called and no read // error was received before it. ErrAbort = errors.New("read aborted") @@ -71,7 +75,7 @@ var ( // DefultPool initializes a synchronized pool that stores and returns byte slices of allocSize length. It can be // used with multiple readers concurrently. -func DefaultPool(allocSize int) Pool { +func DefaultPool(allocSize int) BufferPool { if allocSize <= 0 { allocSize = 1 << 12 } @@ -79,8 +83,8 @@ func DefaultPool(allocSize int) Pool { return newPool(allocSize) } -// NoPool returns a noop pool. -func NoPool(allocSize int) Pool { +// NoPool returns a noop pool that allocates memory on every call to Get() and does nothing on Put(). +func NoPool(allocSize int) BufferPool { if allocSize <= 0 { allocSize = 1 << 12 } @@ -99,11 +103,16 @@ func BufferedReader(in io.Reader, o Options) Reader { return Reader{} } - if o.Pool == nil { - o.Pool = DefaultPool(1 << 12) + if o.BufferPool == nil { + o.BufferPool = DefaultPool(1 << 12) } - return Reader{reader: &reader{options: o, in: in}} + r := Reader{reader: &reader{options: o, in: in}} + if len(o.InitialSegment) > 0 { + r.reader.segments = [][]byte{o.InitialSegment} + } + + return r } // BufferedContent creates a buffered reader using the input content (io.WriterTo) as the underlying source. @@ -111,7 +120,7 @@ func BufferedReader(in io.Reader, o Options) Reader { // It is similar to an io.Pipe, but with dynamic and pooled buffering internally. The individual Write calls are // blocked until the reading side requests more data. // -// The provided WriterTo instances need to be safe to call in goroutines other than they were created in. The +// The provided WriterTo instances need to be safe to call in goroutines other than they were created in. If the // writer function returns with nil error, it will be interpreted as EOF on the reader side. When the reader // side experiences an error, and the writer still has content to be written, the passed in io.Writer will // return an ErrAbort error. @@ -120,8 +129,8 @@ func BufferedContent(c io.WriterTo, o Options) Reader { return Reader{} } - if o.Pool == nil { - o.Pool = DefaultPool(1 << 12) + if o.BufferPool == nil { + o.BufferPool = DefaultPool(1 << 12) } return Reader{reader: &reader{options: o, in: mkcontent(c)}} @@ -129,10 +138,14 @@ func BufferedContent(c io.WriterTo, o Options) Reader { // Read reads max len(p) copied to p and returns how many bytes were read. // -// It only returns an error when the buffer is empty. It only returns an error when the Pool.Get fails or the -// underlying reader returns an error. +// It only returns an error when the buffer is empty. It only returns an error when the BufferPool.Get fails or +// the underlying reader returns an error. // // It may return zero read length and nil error, but only if the underlying reader did so. +// +// It does not block when the buffer is not empty, even if the underlying reader would block on the next read. +// +// It only allocates a single segment of memory, or none if one was provided during innitialization. func (r Reader) Read(p []byte) (int, error) { if r.reader == nil { return 0, io.EOF @@ -143,22 +156,17 @@ func (r Reader) Read(p []byte) (int, error) { // ReadBytes reads until the first occurence of delimiter in the input, within max length. // -// It returns the bytes, true and nil error, when the delimiter was found within max. In this case it consumes +// It returns the bytes and nil error, when the delimiter was found within max. In this case it consumes // the buffer until and including the delimiter. If the underlying reader returned meanwhile a non-nil error, // including EOF, it will be returned on subsequent reads, but only after the internal buffer was consumed. // -// It returns zero length bytes, false and nil error, when the delimiter was not found within max, and the -// underlying reader didn't return an error. +// It returns zero length bytes and nil error, when the delimiter was not found within max. // -// It returns max or less bytes, false and nil error, if the delimiter was not found within max, there are -// buffered bytes, the underlying reader returned an error. It consumes the returned bytes from the buffer. The -// delimiter may still be found in the remaining buffered bytes during subsequent calls. -// -// It returns zero length bytes, false and non-nil error, if the buffer is empty and the underlying reader -// previously returned an error. -func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, bool, error) { +// It blocks only when less data is buffered than max, the delimiter was not found within the range defined by +// max, and the underlying reader blocks. +func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, error) { if r.reader == nil { - return nil, false, io.EOF + return nil, io.EOF } return r.reader.readBytes(delimiter, max) @@ -175,8 +183,11 @@ func (r Reader) ReadBytes(delimiter []byte, max int) ([]byte, bool, error) { // nil error. // // It supports recovery of UTF8 streams by skipping the invalid characters. In such cases, first it returns the -// valid characters with the number of bytes consumed and nil error, then in the subsequent call, it returns +// valid characters with the number of bytes consumed and nil error, then in a subsequent call, it returns // zero characters, 1 and nil error. +// +// It blocks only when there is not enough data buffered to determine the next UTF8 character and the underlying +// reader blocks. func (r Reader) ReadUTF8(max int) ([]rune, int, error) { if r.reader == nil { return nil, 0, io.EOF @@ -191,6 +202,8 @@ func (r Reader) ReadUTF8(max int) ([]rune, int, error) { // by Read, ReadBytes, ReadUTF8 or WriteTo. // // The returned byte slice is not a copy of the buffered bytes, and therefore should not be modified. +// +// It blocks when there is less data buffered than the range defined by max and the underlying reader blocks. func (r Reader) Peek(max int) ([]byte, error) { if r.reader == nil { return nil, io.EOF @@ -215,6 +228,8 @@ func (r Reader) Buffered() []byte { // // It is important that the provided writer must not modify the slice data, as defined in the io.Writer // interface documentation. +// +// It blocks when the underlying reader blocks. func (r Reader) WriteTo(w io.Writer) (int64, error) { if r.reader == nil { return 0, nil @@ -223,8 +238,11 @@ func (r Reader) WriteTo(w io.Writer) (int64, error) { return r.reader.writeTo(w) } -// Close releases the resource held by the Reader, and puts back the underlying byte buffers into the used pool. -// The reader cannot be used for read operations after Close() was called. +// Close releases the resource held by the Reader, and puts the underlying byte buffers, that were +// requested from the pool, back into the used pool. The reader cannot be used for read operations after Close() +// was called. +// +// It does not close the underlying reader. func (r Reader) Close() { if r.reader == nil { return @@ -239,15 +257,22 @@ func BufferedWriter(out io.Writer, o Options) Writer { return Writer{} } - if o.Pool == nil { - o.Pool = DefaultPool(1 << 12) + if o.BufferPool == nil { + o.BufferPool = DefaultPool(1 << 12) } - return Writer{writer: &writer{out: out, options: o}} + w := Writer{writer: &writer{out: out, options: o}} + if len(o.InitialSegment) > 0 { + w.writer.buffer = o.InitialSegment + } + + return w } // Write writes to the writer's buffer, and if the buffer is full, it causes writing out the buffer's contents // to the underlying writer. +// +// It blocks when there is not enough buffer space for the data being written and the underlying writer blocks. func (w Writer) Write(p []byte) (int, error) { if w.writer == nil { return 0, errors.New("unitialized writer") @@ -270,6 +295,8 @@ func (w Writer) ReadFrom(r io.Reader) (int64, error) { // Flush forces the writer to flush the buffered content to the underlying writer. After flushed, the writer // still accepts further writes. +// +// It blocks when there is buffered data to write out and the underlying writer blocks. func (w Writer) Flush() error { if w.writer == nil { return nil @@ -280,6 +307,10 @@ func (w Writer) Flush() error { // Close flushes the buffered content if any and closes the writer. After closed, the writer does not accept // further writes. +// +// It blocks when there is buffered data to write out and the underlying writer blocks. +// +// It does not close the underlying writer. func (w Writer) Close() error { if w.writer == nil { return nil diff --git a/lib_test.go b/lib_test.go index fb6daef..44d46fd 100644 --- a/lib_test.go +++ b/lib_test.go @@ -84,15 +84,11 @@ func TestLib(t *testing.T) { t.Run("read bytes", func(t *testing.T) { var r buffer.Reader - b, ok, err := r.ReadBytes([]byte("123"), 512) + b, err := r.ReadBytes([]byte("123"), 512) if !errors.Is(err, io.EOF) { t.Fatal(err) } - if ok { - t.Fatal(ok) - } - if len(b) != 0 { t.Fatal(len(b)) } @@ -154,7 +150,7 @@ func TestLib(t *testing.T) { t.Run("abort", func(t *testing.T) { t.Run("from blank state", func(t *testing.T) { p := &fakePool{allocSize: 1 << 6} - r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{Pool: p}) + r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{BufferPool: p}) r.Close() b := make([]byte, 1<<9) n, err := r.Read(b) @@ -169,10 +165,10 @@ func TestLib(t *testing.T) { t.Run("with multiple segments", func(t *testing.T) { p := &fakePool{allocSize: 1 << 6} - r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{Pool: p}) - b, ok, err := r.ReadBytes([]byte("123"), 1<<6+1<<5) - if ok || err != nil || p.alloc != 2 { - t.Fatal(len(b), ok, err, p.alloc) + r := buffer.BufferedReader(&gen{max: 1 << 12}, buffer.Options{BufferPool: p}) + b, err := r.ReadBytes([]byte("123"), 1<<6+1<<5) + if len(b) != 0 || err != nil || p.alloc != 2 { + t.Fatal(len(b), err, p.alloc) } r.Close() @@ -194,7 +190,7 @@ func TestLib(t *testing.T) { errAfter: []int{1 << 6}, } - r := buffer.BufferedReader(g, buffer.Options{Pool: p}) + r := buffer.BufferedReader(g, buffer.Options{BufferPool: p}) b := make([]byte, 1<<9) n, err := r.Read(b) if n != 1<<6 || err != nil { @@ -221,8 +217,8 @@ func TestLib(t *testing.T) { t.Run("with content", func(t *testing.T) { c := buffer.ContentFunc(func(w io.Writer) (int64, error) { var n int64 - for i := 0; i < 3; i++ { - ni, err := w.Write([]byte("123456789")[i*3 : i*3+3]) + for i := 0; i < 4; i++ { + ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3]) n += int64(ni) if err != nil { return n, err @@ -233,18 +229,21 @@ func TestLib(t *testing.T) { }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) - for i := 0; i < 2; i++ { + exp := []byte("123456789012") + for i := 0; i < 8; i++ { n, err := r.Read(b) - if n != 3 || err != nil { + if n != 2-i%2 || err != nil { t.Fatal(n, err) } - if string(b) != "123456789"[i*3:i*3+3] { - t.Fatal(string(b)) + if string(b[:n]) != string(exp[:n]) { + t.Fatal(string(b[:n])) } + + exp = exp[n:] } r.Close() @@ -261,8 +260,8 @@ func TestLib(t *testing.T) { t.Run("with content error", func(t *testing.T) { c := buffer.ContentFunc(func(w io.Writer) (int64, error) { var n int64 - for i := 0; i < 3; i++ { - ni, err := w.Write([]byte("123456789")[i*3 : i*3+3]) + for i := 0; i < 4; i++ { + ni, err := w.Write([]byte("123456789012")[i*3 : i*3+3]) n += int64(ni) if err != nil { return n, err @@ -273,18 +272,21 @@ func TestLib(t *testing.T) { }) p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := buffer.BufferedContent(c, o) b := make([]byte, 3) - for i := 0; i < 3; i++ { + exp := []byte("123456789012") + for i := 0; i < 8; i++ { n, err := r.Read(b) - if n != 3 || err != nil { + if n != 2-i%2 || err != nil { t.Fatal(n, err) } - if string(b) != "123456789"[i*3:i*3+3] { + if string(b[:n]) != string(exp[:n]) { t.Fatal(string(b)) } + + exp = exp[n:] } n, err := r.Read(b) @@ -364,7 +366,7 @@ func TestLib(t *testing.T) { t.Run("double closing reader", func(t *testing.T) { g := &gen{max: 1 << 12} p := &fakePool{allocSize: 1 << 9} - r := buffer.BufferedReader(g, buffer.Options{Pool: p}) + r := buffer.BufferedReader(g, buffer.Options{BufferPool: p}) b := bytes.NewBuffer(nil) if n, err := io.Copy(b, r); n != 1<<12 || err != nil { t.Fatal(n, err) @@ -392,7 +394,7 @@ func TestLib(t *testing.T) { }) p := &fakePool{allocSize: 1 << 9} - r := buffer.BufferedContent(c, buffer.Options{Pool: p}) + r := buffer.BufferedContent(c, buffer.Options{BufferPool: p}) b := bytes.NewBuffer(nil) if n, err := io.Copy(b, r); n != 9 || err != nil { t.Fatal(n, err) @@ -420,7 +422,7 @@ func TestLib(t *testing.T) { }) p := &fakePool{allocSize: 1 << 9} - r := buffer.BufferedContent(c, buffer.Options{Pool: p}) + r := buffer.BufferedContent(c, buffer.Options{BufferPool: p}) b := make([]byte, 3) if n, err := r.Read(b); n != 3 || err != nil || string(b) != "123" { t.Fatal(n, err, string(b)) @@ -437,7 +439,7 @@ func TestLib(t *testing.T) { w := &writer{} r := &gen{max: 1 << 12} p := &fakePool{allocSize: 1 << 9} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 1<<12 || err != nil { @@ -465,7 +467,7 @@ func TestLib(t *testing.T) { } p := &fakePool{allocSize: 1 << 9} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 1<<11 || !errors.Is(err, errTest) { @@ -485,6 +487,61 @@ func TestLib(t *testing.T) { } }) }) + + t.Run("initial segment", func(t *testing.T) { + t.Run("reader", func(t *testing.T) { + g := &gen{max: 1 << 12} + p := &fakePool{allocSize: 1 << 9} + o := buffer.Options{ + InitialSegment: make([]byte, 1<<9), + BufferPool: p, + } + + b := buffer.BufferedReader(g, o) + + var n int + pp := make([]byte, 1<<12) + for { + ni, err := b.Read(pp) + n += ni + if errors.Is(err, io.EOF) { + break + } + + if err != nil { + t.Fatal(err) + } + } + + if n != 1<<12 { + t.Fatal(n) + } + + if p.alloc != 0 || p.free != 0 { + t.Fatal(p.alloc, p.free) + } + }) + + t.Run("writer", func(t *testing.T) { + w := &writer{} + p := &fakePool{allocSize: 1 << 9} + o := buffer.Options{ + InitialSegment: make([]byte, 1<<9), + BufferPool: p, + } + + b := buffer.BufferedWriter(w, o) + pp := generate(1 << 12) + n, err := b.Write(pp) + if n != 1<<12 || err != nil { + t.Fatal(n, err) + } + + if p.alloc != 0 || p.free != 0 { + t.Fatal(p.alloc, p.free) + } + }) + }) } // -- bench @@ -509,7 +566,7 @@ func TestBenchmarkThroughput(t *testing.T) { p := buffer.NoPool(0) wo := writerOnly{io.Discard} src := &gen{max: 1 << 18} - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) ro := readerOnly{r} n, err := io.Copy(wo, ro) if n != 1<<18 || err != nil { @@ -533,7 +590,7 @@ func BenchmarkThroughput(b *testing.B) { wo := writerOnly{io.Discard} for i := 0; i < b.N; i++ { src := &gen{max: 1 << 18} - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) ro := readerOnly{r} io.Copy(wo, ro) } @@ -553,7 +610,7 @@ func TestBenchmarkThroughputPooled(t *testing.T) { p := &foreverPool{allocSize: 1 << 12} wo := writerOnly{io.Discard} src := &gen{max: 1 << 18} - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) ro := readerOnly{r} n, err := io.Copy(wo, ro) if n != 1<<18 || err != nil { @@ -578,7 +635,7 @@ func BenchmarkThroughputPooled(b *testing.B) { wo := writerOnly{io.Discard} for i := 0; i < b.N; i++ { src := &gen{max: 1 << 18} - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) ro := readerOnly{r} io.Copy(wo, ro) } @@ -602,7 +659,7 @@ func TestBenchmarkThroughputPooledParallel(t *testing.T) { wo := writerOnly{io.Discard} src := &gen{max: 1 << 18} - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) ro := readerOnly{r} n, err := io.Copy(wo, ro) if n != 1<<18 || err != nil { @@ -640,7 +697,7 @@ func BenchmarkThroughputPooledParallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { src := &gen{max: 1 << 18} - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) ro := readerOnly{r} wo := writerOnly{io.Discard} io.Copy(wo, ro) @@ -676,16 +733,12 @@ func TestBenchmarkScan(t *testing.T) { customContent: map[int][]byte{delimiterPosition: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) - b, ok, err := r.ReadBytes([]byte{'1'}, 1<<18) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) + b, err := r.ReadBytes([]byte{'1'}, 1<<18) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(delimiterPosition), '1')) { t.Fatal("content") } @@ -720,7 +773,7 @@ func BenchmarkScan(b *testing.B) { customContent: map[int][]byte{delimiterPosition: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) r.ReadBytes([]byte{'1'}, 1<<18) } } @@ -748,16 +801,12 @@ func TestBenchmarkScanPooled(t *testing.T) { customContent: map[int][]byte{delimiterPosition: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) - b, ok, err := r.ReadBytes([]byte{'1'}, 1<<18) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) + b, err := r.ReadBytes([]byte{'1'}, 1<<18) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(delimiterPosition), '1')) { t.Fatal("content") } @@ -793,7 +842,7 @@ func BenchmarkScanPooled(b *testing.B) { customContent: map[int][]byte{delimiterPosition: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) r.ReadBytes([]byte{'1'}, 1<<18) } } @@ -825,16 +874,12 @@ func TestBenchmarkScanPooledParallel(t *testing.T) { customContent: map[int][]byte{delimiterPosition: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) - b, ok, err := r.ReadBytes([]byte{'1'}, 1<<18) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) + b, err := r.ReadBytes([]byte{'1'}, 1<<18) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(delimiterPosition), '1')) { t.Fatal("content") } @@ -884,7 +929,7 @@ func BenchmarkScanPooledParallel(b *testing.B) { customContent: map[int][]byte{delimiterPosition: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: p}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: p}) r.ReadBytes([]byte{'1'}, 1<<18) } }) diff --git a/peek_test.go b/peek_test.go index ef5c753..1cf2763 100644 --- a/peek_test.go +++ b/peek_test.go @@ -13,7 +13,7 @@ func TestPeek(t *testing.T) { t.Run(title, func(t *testing.T) { t.Run("peek across segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) p, err := r.Peek(2*128 + 30) if err != nil { @@ -31,7 +31,7 @@ func TestPeek(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(18) if err != nil { @@ -72,7 +72,7 @@ func TestPeek(t *testing.T) { t.Run("err immediately on first try to fill", func(t *testing.T) { g := &gen{} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(18) if !errors.Is(err, io.EOF) { @@ -86,7 +86,7 @@ func TestPeek(t *testing.T) { t.Run("peek on empty", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(18) if err != nil { @@ -100,7 +100,7 @@ func TestPeek(t *testing.T) { t.Run("peek multiple segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(160) if err != nil { @@ -114,7 +114,7 @@ func TestPeek(t *testing.T) { t.Run("peek on partially filled", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(16) if err != nil { @@ -137,7 +137,7 @@ func TestPeek(t *testing.T) { t.Run("peek on partially filled multiple segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(160) if err != nil { @@ -164,7 +164,7 @@ func TestPeek(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(18) if err != nil { @@ -182,7 +182,7 @@ func TestPeek(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(214) if err != nil { @@ -196,7 +196,7 @@ func TestPeek(t *testing.T) { t.Run("peek zero", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(0) if err != nil { diff --git a/pool_test.go b/pool_test.go index 4c6f682..913d1fb 100644 --- a/pool_test.go +++ b/pool_test.go @@ -134,7 +134,7 @@ func TestPoolUsage(t *testing.T) { t.Run("read", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b := make([]byte, 256) n, err := r.Read(b) @@ -158,17 +158,13 @@ func TestPoolUsage(t *testing.T) { t.Run("read bytes", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 9} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<12) + b, err := r.ReadBytes([]byte("123"), 1<<12) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("unexpected delimiter") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -181,7 +177,7 @@ func TestPoolUsage(t *testing.T) { t.Run("read utf8", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) runes, n, err := r.ReadUTF8(1 << 12) if err != nil { @@ -189,7 +185,7 @@ func TestPoolUsage(t *testing.T) { } if n != 1<<12 { - t.Fatal("unexpected delimiter") + t.Fatal("unexpected read length") } if len(runes) != 1<<12 { @@ -201,10 +197,37 @@ func TestPoolUsage(t *testing.T) { } }) + t.Run("read utf8 off", func(t *testing.T) { + g := &gen{ + max: 1 << 15, + rng: utf8Range, + } + + p := &fakePool{allocSize: 1 << 12} + o := buffer.Options{BufferPool: p} + r := cr(g, o) + runes, n, err := r.ReadUTF8(1 << 12) + if err != nil { + t.Fatal(err) + } + + if n < len(runes) { + t.Fatal("unexpected read length", n) + } + + if len(runes) != 1<<12 { + t.Fatal("invalid content") + } + + if p.alloc != 2 { + t.Fatal("invalid allocation count", p.alloc) + } + }) + t.Run("peek", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := r.Peek(3 * 1 << 12) if err != nil { @@ -223,7 +246,7 @@ func TestPoolUsage(t *testing.T) { t.Run("buffered", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b := r.Buffered() if len(b) != 0 { @@ -238,7 +261,7 @@ func TestPoolUsage(t *testing.T) { t.Run("write to", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) var b bytes.Buffer @@ -265,7 +288,7 @@ func TestPoolUsage(t *testing.T) { t.Run("read", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b := make([]byte, 1<<9) for { @@ -291,23 +314,18 @@ func TestPoolUsage(t *testing.T) { t.Run("read bytes", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - _, _, err := r.ReadBytes([]byte("123"), 1<<15+3) + _, err := r.ReadBytes([]byte("123"), 1<<15+3) if err != nil { t.Fatal(err) } - _, _, err = r.ReadBytes([]byte("123"), 1<<15+3) - if !errors.Is(err, io.EOF) { - t.Fatal(err) - } - if p.alloc != 9 { t.Fatal("invalid allocation count", p.alloc) } - if p.free != 9 { + if p.free != 0 { t.Fatal("invalid free count", p.free) } }) @@ -315,7 +333,7 @@ func TestPoolUsage(t *testing.T) { t.Run("read utf8", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) for { runes, n, err := r.ReadUTF8(1 << 12) @@ -344,7 +362,7 @@ func TestPoolUsage(t *testing.T) { t.Run("peek", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := r.Peek(3 * 1 << 12) if err != nil { @@ -387,7 +405,7 @@ func TestPoolUsage(t *testing.T) { t.Run("buffered", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b := r.Buffered() if len(b) != 0 { @@ -402,7 +420,7 @@ func TestPoolUsage(t *testing.T) { t.Run("write to", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1 << 12} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) var b bytes.Buffer @@ -437,7 +455,7 @@ func TestPoolUsage(t *testing.T) { zeroAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b := make([]byte, 256) _, err := r.Read(b) @@ -453,15 +471,15 @@ func TestPoolUsage(t *testing.T) { zeroAfter: []int{1}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - _, _, err := r.ReadBytes([]byte("123"), 1<<12) + _, err := r.ReadBytes([]byte("123"), 1<<12) if err != nil { t.Fatal(err) } - _, _, err = r.ReadBytes([]byte("123"), 1<<12) - if !errors.Is(err, buffer.ErrZeroAllocation) { + _, err = r.ReadBytes([]byte("123"), 1<<12) + if err != nil { t.Fatal("failed to fail", err) } }) @@ -473,7 +491,7 @@ func TestPoolUsage(t *testing.T) { zeroAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) _, _, err := r.ReadUTF8(1 << 12) if !errors.Is(err, buffer.ErrZeroAllocation) { @@ -488,7 +506,7 @@ func TestPoolUsage(t *testing.T) { zeroAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) _, err := r.Peek(3 * 1 << 12) if !errors.Is(err, buffer.ErrZeroAllocation) { @@ -503,7 +521,7 @@ func TestPoolUsage(t *testing.T) { zeroAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) var b bytes.Buffer @@ -524,17 +542,13 @@ func TestPoolUsage(t *testing.T) { } p := &fakePool{varyingSize: []int{8, 256}} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<15) + b, err := r.ReadBytes([]byte("123"), 1<<15) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("failed to find delimiter") - } - if !bytes.Equal(b, append(generate(1<<11), []byte("123")...)) { t.Fatal("invalid content") } @@ -543,15 +557,15 @@ func TestPoolUsage(t *testing.T) { t.Run("find not", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{varyingSize: []int{8, 256}} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<15) + b, err := r.ReadBytes([]byte("123"), 1<<15) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter", len(b)) + if len(b) != 0 { + t.Fatal("invalid read length", len(b)) } }) }) @@ -559,7 +573,7 @@ func TestPoolUsage(t *testing.T) { t.Run("peek", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{varyingSize: []int{8, 256}} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := r.Peek(1 << 11) if err != nil { @@ -576,7 +590,7 @@ func TestPoolUsage(t *testing.T) { t.Run("read", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := io.ReadAll(r) if err != nil { @@ -597,17 +611,13 @@ func TestPoolUsage(t *testing.T) { } p := &fakePool{allocSize: 1} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<15) + b, err := r.ReadBytes([]byte("123"), 1<<15) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("failed to find delimiter") - } - if !bytes.Equal(b, append(generate(1<<11), []byte("123")...)) { t.Fatal("invalid content") } @@ -616,17 +626,13 @@ func TestPoolUsage(t *testing.T) { t.Run("find not", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<15) + b, err := r.ReadBytes([]byte("123"), 1<<15) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content", len(b)) } @@ -640,7 +646,7 @@ func TestPoolUsage(t *testing.T) { } p := &fakePool{allocSize: 1} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) runes, n, err := r.ReadUTF8(1 << 14) if err != nil { @@ -659,7 +665,7 @@ func TestPoolUsage(t *testing.T) { t.Run("peek", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := r.Peek(1 << 14) if err != nil { @@ -674,7 +680,7 @@ func TestPoolUsage(t *testing.T) { t.Run("write to", func(t *testing.T) { g := &gen{max: 1 << 15} p := &fakePool{allocSize: 1} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) var b bytes.Buffer @@ -701,7 +707,7 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := io.ReadAll(r) if !errors.Is(err, errTest) { @@ -721,17 +727,13 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<12) + b, err := r.ReadBytes([]byte("123"), 1<<12) if !errors.Is(err, errTest) { t.Fatal("failed to fail with the right error", err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -744,30 +746,22 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{1}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 1<<15) + b, err := r.ReadBytes([]byte("123"), 1<<15) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - - if !bytes.Equal(b, generate(1<<11)) { + if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 1<<15) - if !errors.Is(err, errTest) { + b, err = r.ReadBytes([]byte("123"), 1<<15) + if err != nil { t.Fatal("failed to fail with the right error", err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -785,7 +779,7 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) runes, n, err := r.ReadUTF8(1 << 14) if !errors.Is(err, errTest) { @@ -809,7 +803,7 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := r.Peek(1 << 12) if !errors.Is(err, errTest) { @@ -828,7 +822,7 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{1}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) b, err := r.Peek(1 << 15) if err != nil { @@ -839,26 +833,22 @@ func TestPoolUsage(t *testing.T) { t.Fatal("invalid content") } - b, ok, err := r.ReadBytes([]byte("123"), 1<<11) + b, err = r.ReadBytes([]byte("123"), 1<<11) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - - if !bytes.Equal(b, generate(1<<11)) { + if len(b) != 0 { t.Fatal("invalid content") } b, err = r.Peek(1 << 15) - if !errors.Is(err, errTest) { + if err != nil { t.Fatal("failed to fail with the right error") } - if len(b) != 0 { - t.Fatal("invalid content") + if !bytes.Equal(b, generate(1<<11)) { + t.Fatal("invalid content", len(b)) } }) }) @@ -870,7 +860,7 @@ func TestPoolUsage(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} r := cr(g, o) var b bytes.Buffer diff --git a/read_test.go b/read_test.go index c303c50..7e3cef9 100644 --- a/read_test.go +++ b/read_test.go @@ -13,7 +13,7 @@ func TestRead(t *testing.T) { t.Run(title, func(t *testing.T) { t.Run("small", func(t *testing.T) { g := &gen{max: 3} - r := cr(g, buffer.Options{Pool: buffer.NoPool(1 << 12)}) + r := cr(g, buffer.Options{BufferPool: buffer.NoPool(1 << 12)}) b, err := io.ReadAll(r) if err != nil { t.Fatal(err) @@ -26,7 +26,7 @@ func TestRead(t *testing.T) { t.Run("large", func(t *testing.T) { g := &gen{max: 1 << 18} - r := cr(g, buffer.Options{Pool: buffer.NoPool(1 << 12)}) + r := cr(g, buffer.Options{BufferPool: buffer.NoPool(1 << 12)}) b, err := io.ReadAll(r) if err != nil { t.Fatal(err) @@ -39,7 +39,7 @@ func TestRead(t *testing.T) { t.Run("zero first", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) var p []byte @@ -69,7 +69,7 @@ func TestRead(t *testing.T) { t.Run("large with non-divisible fragments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12 / 2)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12 / 2)} r := cr(g, o) b, err := io.ReadAll(r) if err != nil { @@ -83,7 +83,7 @@ func TestRead(t *testing.T) { t.Run("partial segment", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) r.Peek(30) p := make([]byte, 60) @@ -103,7 +103,7 @@ func TestRead(t *testing.T) { t.Run("partial segment nth", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) r.Peek(2*128 + 30) p := make([]byte, 2*128+60) @@ -123,7 +123,7 @@ func TestRead(t *testing.T) { t.Run("read buffer larger than read size", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) p := make([]byte, 12) n, err := r.Read(p) @@ -142,11 +142,11 @@ func TestRead(t *testing.T) { t.Run("read buffer larger than segment", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) p := make([]byte, 192) n, err := r.Read(p) - if n != 192 { + if n != 128 { t.Fatal("invalid read size") } @@ -154,14 +154,14 @@ func TestRead(t *testing.T) { t.Fatal(err) } - if !bytes.Equal(p, generate(192)) { + if !bytes.Equal(p[:128], generate(128)) { t.Fatal("invalid content") } }) t.Run("read buffer larger than available data", func(t *testing.T) { g := &gen{max: 256} - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(512)} r := cr(g, o) p := make([]byte, 384) n, err := r.Read(p) @@ -193,7 +193,7 @@ func TestRead(t *testing.T) { nullReadAfter: []int{0, 0}, } - o := buffer.Options{Pool: buffer.NoPool(128)} + o := buffer.Options{BufferPool: buffer.NoPool(128)} r := cr(g, o) p := make([]byte, 64) n, err := r.Read(p) @@ -213,7 +213,7 @@ func TestRead(t *testing.T) { errAfter: []int{32}, } - o := buffer.Options{Pool: buffer.NoPool(32)} + o := buffer.Options{BufferPool: buffer.NoPool(32)} r := cr(g, o) p := make([]byte, 64) n, err := r.Read(p) @@ -234,7 +234,7 @@ func TestRead(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(32)} + o := buffer.Options{BufferPool: buffer.NoPool(32)} r := cr(g, o) p := make([]byte, 64) n, err := r.Read(p) @@ -255,7 +255,7 @@ func TestRead(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(32)} + o := buffer.Options{BufferPool: buffer.NoPool(32)} var result []byte r := cr(g, o) p := make([]byte, 9) diff --git a/readbytes_test.go b/readbytes_test.go index 1ee32d5..a0916bd 100644 --- a/readbytes_test.go +++ b/readbytes_test.go @@ -18,17 +18,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("failed to generate right content") } @@ -36,14 +32,14 @@ func TestReadBytes(t *testing.T) { t.Run("find not", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok || len(b) != 0 { + if len(b) != 0 { t.Fatal("failed to not find delimiter") } }) @@ -55,17 +51,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(14)} + o := buffer.Options{BufferPool: buffer.NoPool(14)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("failed to find delimiter") - } - if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } @@ -79,17 +71,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{6: d}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes(d, 64) + b, err := r.ReadBytes(d, 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("failed to find delimiter") - } - if !bytes.Equal(b, append(generate(6), d...)) { t.Fatal("invalid content") } @@ -97,17 +85,13 @@ func TestReadBytes(t *testing.T) { t.Run("find not across segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(15)} + o := buffer.Options{BufferPool: buffer.NoPool(15)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 24) + b, err := r.ReadBytes([]byte("123"), 24) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } @@ -115,17 +99,13 @@ func TestReadBytes(t *testing.T) { t.Run("find not across multiple segments", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(15)} + o := buffer.Options{BufferPool: buffer.NoPool(15)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } @@ -138,17 +118,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("8"), 64) + b, err := r.ReadBytes([]byte("8"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -161,17 +137,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("12")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -187,30 +159,22 @@ func TestReadBytes(t *testing.T) { }, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 16) + b, err := r.ReadBytes([]byte("123"), 16) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 24) + b, err = r.ReadBytes([]byte("123"), 24) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - check := append( append(generate(12), append([]byte("12"), generate(18)[14:]...)...), []byte("123")..., @@ -228,17 +192,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{7: []byte("12")}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -251,17 +211,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{7: []byte("1234567890")}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("1234567890123"), 64) + b, err := r.ReadBytes([]byte("1234567890123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -277,30 +233,22 @@ func TestReadBytes(t *testing.T) { }, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 10) + b, err := r.ReadBytes([]byte("123"), 10) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 64) + b, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - check := append( append(generate(7), append([]byte("12"), generate(15)[9:]...)...), []byte("123")..., @@ -321,30 +269,22 @@ func TestReadBytes(t *testing.T) { }, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("1234567890123"), 20) + b, err := r.ReadBytes([]byte("1234567890123"), 20) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("invalid delimiter found") - } - if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("1234567890123"), 64) + b, err = r.ReadBytes([]byte("1234567890123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - check := append( append(append(generate(7), []byte("1234567890")...), generate(22)[17:]...), []byte("1234567890123")..., @@ -362,17 +302,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 14) + b, err := r.ReadBytes([]byte("123"), 14) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("delimiter") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -385,30 +321,22 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 14) + b, err := r.ReadBytes([]byte("123"), 14) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("delimiter") - } - if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 64) + b, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } @@ -421,17 +349,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes(nil, 64) + b, err := r.ReadBytes(nil, 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("failed to find delimiter") - } - if len(b) != 0 { t.Fatal("failed to generate right content") } @@ -444,17 +368,13 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("1")}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte{'1'}, 64) + b, err := r.ReadBytes([]byte{'1'}, 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(12), '1')) { t.Fatal("failed to generate right content") } @@ -467,41 +387,33 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{1<<17 - 3: []byte("123")}, } - r := buffer.BufferedReader(src, buffer.Options{Pool: buffer.NoPool(1 << 12)}) + r := buffer.BufferedReader(src, buffer.Options{BufferPool: buffer.NoPool(1 << 12)}) r.ReadBytes([]byte{'1'}, 1<<17+1<<16) }) t.Run("delimiter longer than content", func(t *testing.T) { g := &gen{max: 1 << 9} - o := buffer.Options{Pool: buffer.NoPool(16)} + o := buffer.Options{BufferPool: buffer.NoPool(16)} r := cr(g, o) - b, ok, err := r.ReadBytes(generate(1<<10), 1<<11) + b, err := r.ReadBytes(generate(1<<10), 1<<11) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("delimiter") - } - - if !bytes.Equal(b, generate(1<<9)) { + if len(b) != 0 { t.Fatal("content") } }) t.Run("find not none consumed", func(t *testing.T) { g := &gen{} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if !errors.Is(err, io.EOF) { t.Fatal(err) } - if ok { - t.Fatal("failed to find delimiter") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -519,30 +431,22 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("delimiter") - } - - if !bytes.Equal(b, generate(8)) { + if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 64) - if !errors.Is(err, errTest) { + b, err = r.ReadBytes([]byte("123"), 64) + if err != nil { t.Fatal("failed to fail", err) } - if ok { - t.Fatal("delimiter") - } - if len(b) != 0 { t.Fatal("invalid content") } @@ -557,43 +461,22 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter", len(b), string(b)) - } - if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 64) + b, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("unexpected delimiter") - } - - if !bytes.Equal(b, generate(16)[15:]) { - t.Fatal("invalid content", len(b), string(b)) - } - - b, ok, err = r.ReadBytes([]byte("123"), 64) - if !errors.Is(err, errTest) { - t.Fatal(err) - } - - if ok { - t.Fatal("unexpected delimiter") - } - if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } @@ -608,43 +491,31 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter", len(b), string(b)) - } - if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 64) + b, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("unexpected delimiter") - } - - if !bytes.Equal(b, generate(24)[15:]) { + if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } - b, ok, err = r.ReadBytes([]byte("123"), 64) - if !errors.Is(err, errTest) { + b, err = r.ReadBytes([]byte("123"), 64) + if err != nil { t.Fatal(err) } - if ok { - t.Fatal("unexpected delimiter") - } - if len(b) != 0 { t.Fatal("invalid content", len(b), string(b)) } @@ -658,30 +529,22 @@ func TestReadBytes(t *testing.T) { customContent: map[int][]byte{12: []byte("123")}, } - o := buffer.Options{Pool: buffer.NoPool(8)} + o := buffer.Options{BufferPool: buffer.NoPool(8)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("delimiter") - } - if len(b) != 0 { t.Fatal("invalid content") } - b, ok, err = r.ReadBytes([]byte("123"), 64) + b, err = r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if !ok { - t.Fatal("delimiter") - } - if !bytes.Equal(b, append(generate(12), []byte("123")...)) { t.Fatal("failed to generate right content") } @@ -693,18 +556,14 @@ func TestReadBytes(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(256)} + o := buffer.Options{BufferPool: buffer.NoPool(256)} r := cr(g, o) - b, ok, err := r.ReadBytes([]byte("123"), 64) + b, err := r.ReadBytes([]byte("123"), 64) if err != nil { t.Fatal(err) } - if ok { - t.Fatal("delimiter") - } - - if !bytes.Equal(b, generate(64)) { + if len(b) != 0 { t.Fatal("failed to generate right content") } }) diff --git a/reader.go b/reader.go index 4f28d9f..6941965 100644 --- a/reader.go +++ b/reader.go @@ -16,30 +16,31 @@ type reader struct { err error } -func (r *reader) fillSegment() (fn int, full bool) { - if r.err != nil { - return - } - +func (r *reader) fillSegment() int { + var n int seg := r.segments[len(r.segments)-1] start := r.offset + r.len - r.lastSegStart - if start == len(seg) { - full = true - return + n, r.err = r.in.Read(seg[start:len(seg)]) + if n == 0 && r.err == nil { + n, r.err = r.in.Read(seg[start:len(seg)]) } - fn, r.err = r.in.Read(seg[start:len(seg)]) - if fn == 0 && r.err == nil { - fn, r.err = r.in.Read(seg[start:len(seg)]) + r.len += n + return n +} + +func (r reader) segmentFull() bool { + if len(r.segments) == 0 { + return true } - r.len += fn - full = fn == len(seg)-start - return + segmentPosition := r.offset + r.len - r.lastSegStart + lastSeg := r.segments[len(r.segments)-1] + return segmentPosition == len(lastSeg) } func (r *reader) allocate() { - segment, err := r.options.Pool.Get() + segment, err := r.options.BufferPool.Get() if err != nil { r.err = err return @@ -51,7 +52,8 @@ func (r *reader) allocate() { } if len(r.segments) > 0 { - r.lastSegStart += len(r.segments[len(r.segments)-1]) + lastSeg := r.segments[len(r.segments)-1] + r.lastSegStart += len(lastSeg) } r.segments = append(r.segments, segment) @@ -68,16 +70,14 @@ func (r *reader) fill(to int) int { return n } - if len(r.segments) == 0 || r.offset+r.len == r.lastSegStart+len(r.segments[len(r.segments)-1]) { + if r.segmentFull() { r.allocate() - if r.err != nil { - return n - } + continue } - fn, full := r.fillSegment() + fn := r.fillSegment() n += fn - if fn == 0 || !full { + if fn == 0 || !r.segmentFull() { return n } } @@ -112,41 +112,55 @@ func (r reader) copy(p []byte) (n int) { func (r *reader) consume(n int) { r.offset += n r.len -= n + seg := 0 + if len(r.options.InitialSegment) > 0 { + seg = 1 + } + for { if len(r.segments) <= 1 { - if r.len == 0 { - r.offset = 0 - } - - return + break } - if r.offset < len(r.segments[0]) { - return + segEnd := len(r.segments[seg]) + if seg == 1 { + segEnd += len(r.segments[0]) } - r.offset -= len(r.segments[0]) - if len(r.segments) > 1 { - r.lastSegStart -= len(r.segments[0]) + if r.offset < segEnd { + break } - r.options.Pool.Put(r.segments[0]) - r.segments = r.segments[1:] + 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 } } func (r *reader) free() { + if len(r.segments) > 0 && len(r.options.InitialSegment) > 0 { + r.segments = r.segments[1:] + } + for { if len(r.segments) == 0 { break } - r.options.Pool.Put(r.segments[0]) + r.options.BufferPool.Put(r.segments[0]) + r.segments[0] = nil r.segments = r.segments[1:] } + r.segments = nil r.len = 0 - r.options.Pool = nil + r.options.BufferPool = nil if c, ok := r.in.(interface{ close() error }); ok { if err := c.close(); err != nil { r.err = errors.Join(r.err, err) @@ -208,9 +222,8 @@ func (r *reader) fillToDelimiter(delimiter []byte, max int) (int, bool) { } if r.len < i+len(d) { - clen := r.len - r.fill(i + len(d)) - if r.len == clen { + fn := r.fill(i + len(d)) + if fn == 0 { return 0, false } } @@ -297,34 +310,23 @@ func (r *reader) read(p []byte) (int, error) { return 0, r.err } - var n int - for len(p) > 0 { - if len(r.segments) == 0 { - r.allocate() - if r.err != nil { - break - } - } + if len(p) == 0 { + return 0, nil + } - var partialRead bool - if r.len == 0 { - _, full := r.fillSegment() - partialRead = !full - } - - ni := r.copy(p) - if ni == 0 { - break - } - - r.consume(ni) - p = p[ni:] - n += ni - if partialRead { - break + if len(r.segments) == 0 { + r.allocate() + if r.err != nil { + return 0, r.err } } + if r.len == 0 { + r.fillSegment() + } + + n := r.copy(p) + r.consume(n) if r.err != nil && r.len == 0 { r.free() if n == 0 { @@ -335,13 +337,13 @@ func (r *reader) read(p []byte) (int, error) { return n, nil } -func (r *reader) readBytes(delimiter []byte, max int) ([]byte, bool, error) { +func (r *reader) readBytes(delimiter []byte, max int) ([]byte, error) { if r.err != nil && r.len == 0 { - return nil, false, r.err + return nil, r.err } if len(delimiter) == 0 { - return nil, true, nil + return nil, nil } var ( @@ -356,25 +358,14 @@ func (r *reader) readBytes(delimiter []byte, max int) ([]byte, bool, error) { r.consume(n) } - if !ok && r.err != nil { - l = r.len - if l > max { - l = max - } - - p = make([]byte, l) - n = r.copy(p) - r.consume(n) - } - if r.err != nil && r.len == 0 { r.free() if n == 0 { - return nil, ok, r.err + return nil, r.err } } - return p, ok, nil + return p, nil } func (r *reader) readUTF8(max int) ([]rune, int, error) { @@ -392,16 +383,23 @@ func (r *reader) readUTF8(max int) ([]rune, int, error) { break } - var nullRead bool + var zeroRead bool b := r.view(n, 4) - if len(b) < 4 && !utf8.FullRune(b) { - clen := r.len - r.fill(n + 4) + for len(b) < 4 && !utf8.FullRune(b) { + fn := r.fill(n + 4) + zeroRead = fn == 0 + if zeroRead { + break + } + b = r.view(n, 4) - nullRead = r.len == clen } - if nullRead && r.err == nil || len(b) == 0 { + if len(b) == 0 { + break + } + + if zeroRead && r.err == nil { break } @@ -435,12 +433,17 @@ func (r *reader) peek(max int) ([]byte, error) { return nil, r.err } - r.fill(max) + for r.len < max { + if fn := r.fill(max); fn == 0 { + break + } + } + v := r.view(0, max) if r.err != nil && r.len == 0 { r.free() if len(v) == 0 { - return v, r.err + return nil, r.err } } @@ -478,7 +481,7 @@ func (r *reader) writeTo(w io.Writer) (int64, error) { } } - fn, _ := r.fillSegment() + fn := r.fillSegment() if fn == 0 && r.err == nil { return n, io.ErrNoProgress } diff --git a/readutf8_test.go b/readutf8_test.go index 2089240..1cf3f6c 100644 --- a/readutf8_test.go +++ b/readutf8_test.go @@ -12,23 +12,23 @@ func TestReadUTF8(t *testing.T) { t.Run(title, func(t *testing.T) { t.Run("read all after error", func(t *testing.T) { g := &gen{ - rng: utf8W2Range, - max: 30, + rng: utf8Range, + max: 24, fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) - runes, n0, err := r.ReadUTF8(12) + runes0, n0, err := r.ReadUTF8(12) if err != nil { t.Fatal(err) } - if len(runes) != 12 { - t.Fatal("invalid read 0", len(runes), n0) + if len(runes0) != 12 { + t.Fatal("invalid read 0", len(runes0), n0) } - if string(runes) != string(generateFrom(utf8W2Range, n0)) { + if string(runes0) != string(generateFrom(utf8Range, n0)) { t.Fatal("invalid content") } @@ -41,8 +41,8 @@ func TestReadUTF8(t *testing.T) { t.Fatal("invalid read 1", len(runes1), n0, n1) } - if string(runes1) != string(generateFrom(utf8W2Range, n0+n1)[n0:]) { - t.Fatal("invalid content", string(runes1), string(generateFrom(utf8W2Range, n0+n1)[n0:])) + if string(runes1) != string(generateFrom(utf8Range, n0+n1)[n0:]) { + t.Fatal("invalid content", string(runes1), string(generateFrom(utf8Range, n0+n1)[n0:])) } runes2, n2, err := r.ReadUTF8(12) @@ -61,7 +61,7 @@ func TestReadUTF8(t *testing.T) { t.Run("ascii", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) runes, n, err := r.ReadUTF8(12) if err != nil { @@ -83,7 +83,7 @@ func TestReadUTF8(t *testing.T) { max: 1 << 15, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) runes, n, err := r.ReadUTF8(12) if err != nil { @@ -105,7 +105,7 @@ func TestReadUTF8(t *testing.T) { max: 1 << 15, } - o := buffer.Options{Pool: buffer.NoPool(9)} + o := buffer.Options{BufferPool: buffer.NoPool(9)} r := cr(g, o) runes, n, err := r.ReadUTF8(12) if err != nil { @@ -130,7 +130,7 @@ func TestReadUTF8(t *testing.T) { customContent: map[int][]byte{len(utf8Range): brokenRange}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) runes, n, err := r.ReadUTF8(24) if err != nil { @@ -160,7 +160,7 @@ func TestReadUTF8(t *testing.T) { t.Run("immediate err", func(t *testing.T) { g := &gen{rng: utf8Range} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) runes, n, err := r.ReadUTF8(12) if !errors.Is(err, io.EOF) { @@ -189,7 +189,7 @@ func TestReadUTF8(t *testing.T) { nullReadAfter: []int{nullReadAfter, nullReadAfter}, } - o := buffer.Options{Pool: buffer.NoPool(nullReadAfter)} + o := buffer.Options{BufferPool: buffer.NoPool(nullReadAfter)} r := cr(g, o) runes, _, err := r.ReadUTF8(numRunes - 2) // -2 for min read in readUTF8 if err != nil { diff --git a/writer.go b/writer.go index 7ffb779..7aea8f8 100644 --- a/writer.go +++ b/writer.go @@ -27,7 +27,7 @@ func (w *writer) write(p []byte) (int, error) { } if len(w.buffer) == 0 { - w.buffer, w.err = w.options.Pool.Get() + w.buffer, w.err = w.options.BufferPool.Get() if len(w.buffer) == 0 && w.err == nil { w.err = ErrZeroAllocation } @@ -55,7 +55,7 @@ func (w *writer) readFrom(r io.Reader) (int64, error) { } if len(w.buffer) == 0 { - w.buffer, w.err = w.options.Pool.Get() + w.buffer, w.err = w.options.BufferPool.Get() if len(w.buffer) == 0 && w.err == nil { w.err = ErrZeroAllocation } @@ -117,11 +117,11 @@ func (w *writer) flush() error { func (w *writer) close() error { newErr := w.err == nil w.flush() - if len(w.buffer) > 0 { - w.options.Pool.Put(w.buffer) - w.buffer = nil + if len(w.buffer) > 0 && len(w.options.InitialSegment) == 0 { + w.options.BufferPool.Put(w.buffer) } + w.buffer = nil if newErr && w.err != nil { return w.err } diff --git a/writer_test.go b/writer_test.go index ab54d85..7510908 100644 --- a/writer_test.go +++ b/writer_test.go @@ -10,7 +10,7 @@ import ( func TestWriter(t *testing.T) { t.Run("write out", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(32)} + o := buffer.Options{BufferPool: buffer.NoPool(32)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -35,7 +35,7 @@ func TestWriter(t *testing.T) { t.Run("zero bytes when empty", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(32)} + o := buffer.Options{BufferPool: buffer.NoPool(32)} b := buffer.BufferedWriter(w, o) if n, err := b.Write(nil); n != 0 || err != nil { t.Fatal(n, err) @@ -44,7 +44,7 @@ func TestWriter(t *testing.T) { t.Run("zero bytes when erred", func(t *testing.T) { w := &writer{errAfter: []int{3}} - o := buffer.Options{Pool: buffer.NoPool(2)} + o := buffer.Options{BufferPool: buffer.NoPool(2)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -69,7 +69,7 @@ func TestWriter(t *testing.T) { t.Run("zero bytes when not empty", func(t *testing.T) { w := &writer{errAfter: []int{3}} - o := buffer.Options{Pool: buffer.NoPool(32)} + o := buffer.Options{BufferPool: buffer.NoPool(32)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -91,7 +91,7 @@ func TestWriter(t *testing.T) { errAfter: []int{0}, } - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, errTest) { t.Fatal(n, err) @@ -100,7 +100,7 @@ func TestWriter(t *testing.T) { t.Run("no underlying write until full", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(4)} + o := buffer.Options{BufferPool: buffer.NoPool(4)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -136,7 +136,7 @@ func TestWriter(t *testing.T) { t.Run("auto flush when write larger than buffer", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(4)} + o := buffer.Options{BufferPool: buffer.NoPool(4)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123456")); err != nil { t.Fatal(n, err) @@ -149,7 +149,7 @@ func TestWriter(t *testing.T) { t.Run("auto flush when multiple smaller writes fill the buffer", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(4)} + o := buffer.Options{BufferPool: buffer.NoPool(4)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -170,7 +170,7 @@ func TestWriter(t *testing.T) { t.Run("flush on close", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(4)} + o := buffer.Options{BufferPool: buffer.NoPool(4)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -191,7 +191,7 @@ func TestWriter(t *testing.T) { t.Run("manual flush", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(4)} + o := buffer.Options{BufferPool: buffer.NoPool(4)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -212,7 +212,7 @@ func TestWriter(t *testing.T) { t.Run("write after flush", func(t *testing.T) { w := &writer{} - o := buffer.Options{Pool: buffer.NoPool(4)} + o := buffer.Options{BufferPool: buffer.NoPool(4)} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -246,7 +246,7 @@ func TestWriter(t *testing.T) { t.Run("zero write recover", func(t *testing.T) { w := &writer{zeroAfter: []int{2}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -268,7 +268,7 @@ func TestWriter(t *testing.T) { t.Run("zero write terminal", func(t *testing.T) { w := &writer{zeroAfter: []int{2, 2}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -282,7 +282,7 @@ func TestWriter(t *testing.T) { t.Run("partial write", func(t *testing.T) { w := &writer{shortAfter: []int{2}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); err != nil { t.Fatal(n, err) @@ -304,7 +304,7 @@ func TestWriter(t *testing.T) { t.Run("buffer released on write error immediately", func(t *testing.T) { w := &writer{errAfter: []int{0}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 2 || !errors.Is(err, errTest) { t.Fatal(n, err) @@ -318,7 +318,7 @@ func TestWriter(t *testing.T) { t.Run("buffer released on write error", func(t *testing.T) { w := &writer{errAfter: []int{3}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -340,7 +340,7 @@ func TestWriter(t *testing.T) { t.Run("close on err", func(t *testing.T) { w := &writer{errAfter: []int{3}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -366,7 +366,7 @@ func TestWriter(t *testing.T) { t.Run("close and flush err", func(t *testing.T) { w := &writer{errAfter: []int{3}} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -384,7 +384,7 @@ func TestWriter(t *testing.T) { t.Run("close", func(t *testing.T) { w := &writer{} p := &fakePool{allocSize: 2} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 3 || err != nil { t.Fatal(n, err) @@ -406,7 +406,7 @@ func TestWriter(t *testing.T) { t.Run("zero allocation", func(t *testing.T) { w := &writer{} p := &fakePool{} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) if n, err := b.Write([]byte("123")); n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) { t.Fatal(n, err) @@ -418,7 +418,7 @@ func TestWriter(t *testing.T) { w := &writer{} r := &gen{max: 1 << 12} p := buffer.NoPool(1 << 9) - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 1<<12 || err != nil { @@ -434,7 +434,7 @@ func TestWriter(t *testing.T) { } p := buffer.NoPool(1 << 9) - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 1<<12 || !errors.Is(err, errTest) { @@ -450,7 +450,7 @@ func TestWriter(t *testing.T) { } p := buffer.NoPool(1 << 9) - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 1<<11 || !errors.Is(err, errTest) { @@ -462,7 +462,7 @@ func TestWriter(t *testing.T) { w := &writer{errAfter: []int{1 << 11}} r := &gen{max: 1 << 12} p := buffer.NoPool(1 << 9) - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 1<<11+1<<9 || !errors.Is(err, errTest) { @@ -474,7 +474,7 @@ func TestWriter(t *testing.T) { w := &writer{} r := &gen{max: 1 << 12} p := &fakePool{} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 0 || !errors.Is(err, buffer.ErrZeroAllocation) { @@ -490,7 +490,7 @@ func TestWriter(t *testing.T) { } p := &fakePool{allocSize: 1 << 9} - o := buffer.Options{Pool: p} + o := buffer.Options{BufferPool: p} b := buffer.BufferedWriter(w, o) n, err := b.ReadFrom(r) if n != 512 || !errors.Is(err, io.ErrNoProgress) { diff --git a/writeto_test.go b/writeto_test.go index 5dfd199..5f5dd89 100644 --- a/writeto_test.go +++ b/writeto_test.go @@ -13,7 +13,7 @@ func TestWriteTo(t *testing.T) { t.Run(title, func(t *testing.T) { t.Run("write out from zero", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) var b bytes.Buffer @@ -33,7 +33,7 @@ func TestWriteTo(t *testing.T) { t.Run("write out from started", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) p := make([]byte, 256) n, err := r.Read(p) @@ -66,7 +66,7 @@ func TestWriteTo(t *testing.T) { t.Run("after EOF", func(t *testing.T) { g := &gen{max: 256} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) b, err := r.Peek(512) if err != nil { @@ -94,7 +94,7 @@ func TestWriteTo(t *testing.T) { t.Run("after eof empty", func(t *testing.T) { g := &gen{max: 256} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) p := make([]byte, 512) n, err := r.Read(p) @@ -127,7 +127,7 @@ func TestWriteTo(t *testing.T) { t.Run("write error", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) w := &writer{errAfter: []int{256}} n, err := r.WriteTo(w) @@ -146,7 +146,7 @@ func TestWriteTo(t *testing.T) { t.Run("short write", func(t *testing.T) { g := &gen{max: 1 << 15} - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) w := &writer{shortAfter: []int{256}} n, err := r.WriteTo(w) @@ -174,7 +174,7 @@ func TestWriteTo(t *testing.T) { fastErr: true, } - o := buffer.Options{Pool: buffer.NoPool(64)} + o := buffer.Options{BufferPool: buffer.NoPool(64)} r := cr(g, o) b, err := r.Peek(512) if err != nil { @@ -206,7 +206,7 @@ func TestWriteTo(t *testing.T) { errAfter: []int{256}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) p := make([]byte, 1<<15) n, err := r.Read(p) @@ -243,7 +243,7 @@ func TestWriteTo(t *testing.T) { nullReadAfter: []int{256, 256}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) var b bytes.Buffer @@ -267,7 +267,7 @@ func TestWriteTo(t *testing.T) { errAfter: []int{256}, } - o := buffer.Options{Pool: buffer.NoPool(1 << 12)} + o := buffer.Options{BufferPool: buffer.NoPool(1 << 12)} r := cr(g, o) var b bytes.Buffer