348 lines
6.6 KiB
Go
348 lines
6.6 KiB
Go
package buffer_test
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"code.squareroundforest.org/arpio/buffer"
|
|
"errors"
|
|
"io"
|
|
"testing"
|
|
)
|
|
|
|
func TestLib(t *testing.T) {
|
|
t.Run("default pool", func(t *testing.T) {
|
|
t.Run("buffered reader", func(t *testing.T) {
|
|
g := &gen{max: 1 << 18}
|
|
r := buffer.BufferedReader(g, buffer.Options{})
|
|
b, err := io.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(b, generate(1<<18)) {
|
|
t.Fatal("output does not match", len(b))
|
|
}
|
|
})
|
|
|
|
t.Run("buffered content", func(t *testing.T) {
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
|
g := &gen{max: 1 << 18}
|
|
return io.Copy(w, g)
|
|
})
|
|
|
|
r := buffer.BufferedContent(c, buffer.Options{})
|
|
b, err := io.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !bytes.Equal(b, generate(1<<18)) {
|
|
t.Fatal("output does not match", len(b))
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("zero reader", func(t *testing.T) {
|
|
t.Run("buffered reader", func(t *testing.T) {
|
|
r := buffer.BufferedReader(nil, buffer.Options{})
|
|
b, err := io.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(b) != 0 {
|
|
t.Fatal("output does not match", len(b))
|
|
}
|
|
})
|
|
|
|
t.Run("buffered content", func(t *testing.T) {
|
|
r := buffer.BufferedContent(nil, buffer.Options{})
|
|
b, err := io.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(b) != 0 {
|
|
t.Fatal("output does not match", len(b))
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("uninitialized reader", func(t *testing.T) {
|
|
t.Run("read", func(t *testing.T) {
|
|
var r buffer.Reader
|
|
p := make([]byte, 512)
|
|
n, err := r.Read(p)
|
|
if !errors.Is(err, io.EOF) {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if n != 0 {
|
|
t.Fatal(n)
|
|
}
|
|
})
|
|
|
|
t.Run("read bytes", func(t *testing.T) {
|
|
var r buffer.Reader
|
|
b, ok, 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))
|
|
}
|
|
})
|
|
|
|
t.Run("read utf8", func(t *testing.T) {
|
|
var r buffer.Reader
|
|
runes, n, err := r.ReadUTF8(512)
|
|
if !errors.Is(err, io.EOF) {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if n != 0 {
|
|
t.Fatal(n)
|
|
}
|
|
|
|
if len(runes) != 0 {
|
|
t.Fatal(len(runes))
|
|
}
|
|
})
|
|
|
|
t.Run("peek", func(t *testing.T) {
|
|
var r buffer.Reader
|
|
b, err := r.Peek(512)
|
|
if !errors.Is(err, io.EOF) {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(b) != 0 {
|
|
t.Fatal(len(b))
|
|
}
|
|
})
|
|
|
|
t.Run("buffered", func(t *testing.T) {
|
|
var r buffer.Reader
|
|
b := r.Buffered()
|
|
if len(b) != 0 {
|
|
t.Fatal(len(b))
|
|
}
|
|
})
|
|
|
|
t.Run("write to", func(t *testing.T) {
|
|
var (
|
|
r buffer.Reader
|
|
b bytes.Buffer
|
|
)
|
|
|
|
n, err := r.WriteTo(&b)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if n != 0 {
|
|
t.Fatal(n)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// -- bench
|
|
|
|
type readerOnly struct {
|
|
in io.Reader
|
|
}
|
|
|
|
type writerOnly struct {
|
|
in io.Writer
|
|
}
|
|
|
|
func (r readerOnly) Read(p []byte) (int, error) {
|
|
return r.in.Read(p)
|
|
}
|
|
|
|
func (w writerOnly) Write(p []byte) (int, error) {
|
|
return w.in.Write(p)
|
|
}
|
|
|
|
func TestBenchmarkThroughput(t *testing.T) {
|
|
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}
|
|
n, err := io.Copy(wo, ro)
|
|
if n != 1<<18 || err != nil {
|
|
t.Fatal(n, err)
|
|
}
|
|
}
|
|
|
|
func TestBenchmarkThroughputCompare(t *testing.T) {
|
|
dst := bytes.NewBuffer(nil)
|
|
wo := writerOnly{dst}
|
|
src := &gen{max: 1 << 18}
|
|
r := bufio.NewReader(src)
|
|
ro := readerOnly{r}
|
|
n, err := io.Copy(wo, ro)
|
|
if n != 1<<18 || err != nil {
|
|
t.Fatal(n, err)
|
|
}
|
|
}
|
|
|
|
func BenchmarkThroughput(b *testing.B) {
|
|
p := buffer.NoPool(0)
|
|
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 BenchmarkThroughputCompare(b *testing.B) {
|
|
dst := bytes.NewBuffer(nil)
|
|
wo := writerOnly{dst}
|
|
for i := 0; i < b.N; i++ {
|
|
src := &gen{max: 1 << 18}
|
|
r := bufio.NewReader(src)
|
|
ro := readerOnly{r}
|
|
dst.Reset()
|
|
io.Copy(wo, ro)
|
|
}
|
|
}
|
|
|
|
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) {
|
|
for pb.Next() {
|
|
src := &gen{max: 1 << 18}
|
|
r := buffer.BufferedReader(src, buffer.Options{Pool: p})
|
|
ro := readerOnly{r}
|
|
dst := bytes.NewBuffer(nil)
|
|
wo := writerOnly{dst}
|
|
io.Copy(wo, ro)
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkThroughputPooledParallelCompare(b *testing.B) {
|
|
p := newSyncedForeverPool(func() *bufio.Reader {
|
|
return bufio.NewReader(nil)
|
|
})
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
r, _ := p.Get()
|
|
src := &gen{max: 1 << 18}
|
|
r.Reset(src)
|
|
ro := readerOnly{r}
|
|
dst := bytes.NewBuffer(nil)
|
|
wo := writerOnly{dst}
|
|
io.Copy(wo, ro)
|
|
}
|
|
})
|
|
}
|