Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/improve writeseeker #14

Merged
merged 3 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Go

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
-
name: Cache Go modules
uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
-
name: Tests
run: |
go mod tidy
go test -v ./...
8 changes: 4 additions & 4 deletions delegator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import (
func testDelegatorErrors(t *testing.T, d *Delegator, wantErr error) {
var err error
if _, err = d.Read([]byte{}); !errors.Is(err, wantErr) {
t.Errorf("Error unknown: %v", err)
t.Errorf("unknown: %v", err)
}
if _, err = d.Write([]byte{}); !errors.Is(err, wantErr) {
t.Errorf("Error unknown: %v", err)
t.Errorf("unknown: %v", err)
}
if _, err = d.Seek(0, io.SeekStart); !errors.Is(err, wantErr) {
t.Errorf("Error unknown: %v", err)
t.Errorf("unknown: %v", err)
}
if err = d.Close(); err != nil {
t.Errorf("Error unknown: %v", err)
t.Errorf("unknown: %v", err)
}
}

Expand Down
2 changes: 1 addition & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func ExampleDelegateReader() {
// Output: Error: custom
}

func ExampleNewWriteSeekerBuffer() {
func ExampleNewWriteSeekBuffer() {
o := io2.NewWriteSeekBuffer(0)
o.Write([]byte(`Hello!`))
o.Truncate(o.Len() - 1)
Expand Down
40 changes: 20 additions & 20 deletions writeseeker.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io2

import (
"bytes"
"io"
)

Expand All @@ -13,7 +14,7 @@ type WriteSeekCloser interface {

// WriteSeekBuffer implements io.WriteSeeker that using in-memory byte buffer.
type WriteSeekBuffer struct {
buf []byte
buf *bytes.Buffer
off int
len int
}
Expand All @@ -23,15 +24,15 @@ var _ WriteSeekCloser = (*WriteSeekBuffer)(nil)
// NewWriteSeekBuffer returns an WriteSeekBuffer with the initial capacity.
func NewWriteSeekBuffer(capacity int) *WriteSeekBuffer {
return &WriteSeekBuffer{
buf: make([]byte, capacity),
buf: bytes.NewBuffer(make([]byte, capacity)),
}
}

// NewWriteSeekBufferBytes returns an WriteSeekBuffer with the initial buffer.
func NewWriteSeekBufferBytes(buf []byte) *WriteSeekBuffer {
off := len(buf)
return &WriteSeekBuffer{
buf: buf,
buf: bytes.NewBuffer(buf),
off: off,
len: off,
}
Expand All @@ -40,25 +41,19 @@ func NewWriteSeekBufferBytes(buf []byte) *WriteSeekBuffer {
// Write appends the contents of p to the buffer, growing the buffer as needed.
// The return value n is the length of p; err is always nil.
func (b *WriteSeekBuffer) Write(p []byte) (int, error) {
capacity := len(b.buf)
n := len(p)
noff := b.off + n

// NOTE: Expand inner buffer simply.
if noff > capacity {
ncapacity := noff
nbuf := make([]byte, int(ncapacity))
copy(nbuf, b.buf)
b.buf = nbuf
if grow := noff - b.buf.Len(); grow > 0 {
b.buf.Write(make([]byte, grow))
}

copy(b.buf[b.off:], p)
copy(b.buf.Bytes()[b.off:noff], p)

b.off = noff
if b.len < noff {
if noff > b.len {
b.len = noff
}

return n, nil
}

Expand All @@ -76,7 +71,7 @@ func (b *WriteSeekBuffer) Seek(offset int64, whence int) (int64, error) {
case io.SeekCurrent:
noff = b.off + off
case io.SeekEnd:
noff = b.len + off
noff = b.buf.Len() + off
}
if noff < 0 {
noff = 0
Expand All @@ -103,19 +98,24 @@ func (b *WriteSeekBuffer) Len() int {

// Bytes returns a slice of length b.Len() of the buffer.
func (b *WriteSeekBuffer) Bytes() []byte {
return b.buf[:b.len]
n := b.buf.Len()
if n > b.len {
n = b.len
}
return b.buf.Bytes()[:n]
}

// Truncate changes the size of the buffer with offset.
func (b *WriteSeekBuffer) Truncate(n int) {
l := len(b.buf)
if n < 0 {
n = l + n
n = b.off + n
}
if n < 0 {
n = 0
}
if n >= l {
n = l
if n < b.buf.Len() {
b.buf.Truncate(n)
}
b.buf = b.buf[0:n]
b.off = n
b.len = n
}
132 changes: 71 additions & 61 deletions writeseeker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,69 @@ import (
)

func TestWrite(t *testing.T) {
testCases := []struct {
capacity int
p []byte
wantBuf []byte
wantOff int
wantLen int
tests := []struct {
capacity int
p []byte
wantBytes []byte
wantOff int
wantLen int
}{
{
capacity: 8,
p: []byte(`123`),
wantBuf: []byte{'1', '2', '3', 0, 0, 0, 0, 0},
wantOff: 3,
wantLen: 3,
capacity: 8,
p: []byte(`123`),
wantBytes: []byte{'1', '2', '3'},
wantOff: 3,
wantLen: 3,
}, {
p: []byte(`456`),
wantBuf: []byte{'1', '2', '3', '4', '5', '6', 0, 0},
wantOff: 6,
wantLen: 6,
p: []byte(`456`),
wantBytes: []byte{'1', '2', '3', '4', '5', '6'},
wantOff: 6,
wantLen: 6,
}, {
p: []byte(`789`),
wantBuf: []byte{'1', '2', '3', '4', '5', '6', '7', '8', '9'},
wantOff: 9,
wantLen: 9,
p: []byte(`789`),
wantBytes: []byte{'1', '2', '3', '4', '5', '6', '7', '8', '9'},
wantOff: 9,
wantLen: 9,
},
}

var buf *WriteSeekBuffer
for i, testCase := range testCases {
if testCase.capacity > 0 {
buf = NewWriteSeekBuffer(testCase.capacity)
defer buf.Close()
var b *WriteSeekBuffer
close := func() {
if b != nil {
b.Close()
}
n, err := buf.Write(testCase.p)
}
defer close()

for i, test := range tests {
if test.capacity > 0 {
close()
b = NewWriteSeekBuffer(test.capacity)
}
n, err := b.Write(test.p)
if err != nil {
t.Fatalf("Fatal[%d] write: %v", i, err)
t.Fatalf("tests[%d] write: %v", i, err)
}
if n != len(testCase.p) {
t.Errorf("Error[%d] write bytes %d; want %d", i, n, len(testCase.p))
if n != len(test.p) {
t.Errorf("tests[%d] write bytes %d; want %d", i, n, len(test.p))
}
if buf.Offset() != testCase.wantOff {
t.Errorf("Error[%d] off %d; want %d", i, buf.Offset(), testCase.wantOff)
if b.Offset() != test.wantOff {
t.Errorf("tests[%d] off %d; want %d", i, b.Offset(), test.wantOff)
}
if buf.Len() != testCase.wantLen {
t.Errorf("Error[%d] len %d; want %d", i, buf.Len(), testCase.wantLen)
if b.Len() != test.wantLen {
t.Errorf("tests[%d] len %d; want %d", i, b.Len(), test.wantLen)
}
if !reflect.DeepEqual(buf.buf, testCase.wantBuf) {
t.Errorf("Error[%d] buf %v; want %v", i, buf.buf, testCase.wantBuf)
if !reflect.DeepEqual(b.Bytes(), test.wantBytes) {
t.Errorf("tests[%d] bytes %v; want %v", i, b.Bytes(), test.wantBytes)
}
}
}

func TestSeek(t *testing.T) {
buf := NewWriteSeekBufferBytes([]byte(`123456789`))
defer buf.Close()
b := NewWriteSeekBufferBytes([]byte(`123456789`))
defer b.Close()

testCases := []struct {
tests := []struct {
off int64
whence int
wantOff int64
Expand Down Expand Up @@ -94,48 +101,48 @@ func TestSeek(t *testing.T) {
},
}

for i, testCase := range testCases {
n, err := buf.Seek(testCase.off, testCase.whence)
for i, test := range tests {
n, err := b.Seek(test.off, test.whence)
if err != nil {
t.Fatalf("Fatal seek: %v", err)
t.Fatalf("tests[%d] seek: %v", i, err)
}
if n != testCase.wantOff {
t.Errorf("Error[%d] off %d; want %d", i, n, testCase.wantOff)
if n != test.wantOff {
t.Errorf("tests[%d] off %d; want %d", i, n, test.wantOff)
}
}
}

func TestSeekWrite(t *testing.T) {
buf := NewWriteSeekBufferBytes([]byte(`123456789`))
defer buf.Close()
b := NewWriteSeekBufferBytes([]byte(`123456789`))
defer b.Close()

off, err := buf.Seek(int64(3), io.SeekStart)
off, err := b.Seek(int64(3), io.SeekStart)
if err != nil {
t.Fatalf("Fatal seek: %v", err)
t.Fatalf("seek: %v", err)
}
if off != 3 {
t.Errorf("Error seek off %d; want %d", off, 3)
t.Errorf("seek off %d; want %d", off, 3)
}
n, err := buf.Write([]byte(`def`))
n, err := b.Write([]byte(`def`))
if err != nil {
t.Fatalf("Fatal write: %v", err)
t.Fatalf("write: %v", err)
}
if n != 3 {
t.Errorf("Error write bytes %d; want %d", n, 3)
t.Errorf("write bytes %d; want %d", n, 3)
}

want := `123def789`
got := string(buf.Bytes())
got := string(b.Bytes())
if got != want {
t.Errorf("Error bytes %s; want %s", got, want)
t.Errorf("bytes %s; want %s", got, want)
}
}

func TestTruncate(t *testing.T) {
buf := NewWriteSeekBufferBytes([]byte(`123456789`))
defer buf.Close()
b := NewWriteSeekBufferBytes([]byte(`123456789`))
defer b.Close()

testCases := []struct {
tests := []struct {
n int
want []byte
}{
Expand All @@ -146,16 +153,19 @@ func TestTruncate(t *testing.T) {
n: -5,
want: []byte(`123`),
}, {
n: 100,
n: 10,
want: []byte(`123`),
}, {
n: -100,
want: []byte{},
},
}

for i, testCase := range testCases {
buf.Truncate(testCase.n)
got := buf.Bytes()
if !reflect.DeepEqual(got, testCase.want) {
t.Errorf("Error[%d] truncate bytes %v; want %v", i, got, testCase.want)
for i, test := range tests {
b.Truncate(test.n)
got := b.Bytes()
if !reflect.DeepEqual(got, test.want) {
t.Errorf("tests[%d] truncate bytes %v; want %v", i, got, test.want)
}
}
}