Skip to content

Commit

Permalink
Merge pull request #14 from jarxorg/feature/improve-writeseeker
Browse files Browse the repository at this point in the history
Feature/improve writeseeker
  • Loading branch information
mojatter authored Mar 24, 2022
2 parents 0534c40 + d8105f3 commit 2822888
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 86 deletions.
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)
}
}
}

0 comments on commit 2822888

Please sign in to comment.