Skip to content

Commit 4ca2510

Browse files
Xmisterklauspost
authored andcommitted
io.Seeker interface implementation (#10)
Support io.Seeker
1 parent 7f90b27 commit 4ca2510

File tree

3 files changed

+259
-9
lines changed

3 files changed

+259
-9
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ os:
77
- osx
88

99
go:
10-
- 1.6.x
11-
- 1.7.x
1210
- 1.8.x
1311
- 1.9.x
12+
- 1.10.x
13+
- 1.11.x
1414
- master
1515

1616
script:

reader.go

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,23 @@ import (
1818
"io"
1919
)
2020

21+
type seekable struct {
22+
*reader
23+
}
24+
25+
type ReadSeekCloser interface {
26+
io.ReadCloser
27+
io.Seeker
28+
}
29+
2130
type reader struct {
2231
in io.Reader // Input reader
2332
closer io.Closer // Optional closer
2433
ready chan *buffer // Buffers ready to be handed to the reader
2534
reuse chan *buffer // Buffers to reuse for input reading
2635
exit chan struct{} // Closes when finished
2736
buffers int // Number of buffers
37+
size int // Size of each buffer
2838
err error // If an error has occurred it is here
2939
cur *buffer // Current buffer being served
3040
exited chan struct{} // Channel is closed been the async reader shuts down
@@ -38,6 +48,8 @@ type reader struct {
3848
//
3949
// The input can be read from the returned reader.
4050
// When done use Close() to release the buffers.
51+
// If a reader supporting the io.Seeker is given,
52+
// the returned reader will also support it.
4153
func NewReader(rd io.Reader) io.ReadCloser {
4254
if rd == nil {
4355
return nil
@@ -61,6 +73,8 @@ func NewReader(rd io.Reader) io.ReadCloser {
6173
// The input can be read from the returned reader.
6274
// When done use Close() to release the buffers,
6375
// which will also close the supplied closer.
76+
// If a reader supporting the io.Seeker is given,
77+
// the returned reader will also support it.
6478
func NewReadCloser(rd io.ReadCloser) io.ReadCloser {
6579
if rd == nil {
6680
return nil
@@ -75,10 +89,39 @@ func NewReadCloser(rd io.ReadCloser) io.ReadCloser {
7589
return ret
7690
}
7791

92+
// New returns a reader that will asynchronously read from
93+
// the supplied reader into 4 buffers of 1MB each.
94+
//
95+
// It will start reading from the input at once, maybe even before this
96+
// function has returned.
97+
//
98+
// The input can be read and seeked from the returned reader.
99+
// When done use Close() to release the buffers.
100+
func NewReadSeeker(rd io.ReadSeeker) ReadSeekCloser {
101+
//Not checking for result as the input interface guarantees it's seekable
102+
res, _ := NewReader(rd).(ReadSeekCloser)
103+
return res
104+
}
105+
106+
// New returns a reader that will asynchronously read from
107+
// the supplied reader into 4 buffers of 1MB each.
108+
//
109+
// It will start reading from the input at once, maybe even before this
110+
// function has returned.
111+
//
112+
// The input can be read and seeked from the returned reader.
113+
// When done use Close() to release the buffers,
114+
// which will also close the supplied closer.
115+
func NewReadSeekCloser(rd ReadSeekCloser) ReadSeekCloser {
116+
//Not checking for result as the input interface guarantees it's seekable
117+
res, _ := NewReadCloser(rd).(ReadSeekCloser)
118+
return res
119+
}
120+
78121
// NewReaderSize returns a reader with a custom number of buffers and size.
79122
// buffers is the number of queued buffers and size is the size of each
80123
// buffer in bytes.
81-
func NewReaderSize(rd io.Reader, buffers, size int) (io.ReadCloser, error) {
124+
func NewReaderSize(rd io.Reader, buffers, size int) (res io.ReadCloser, err error) {
82125
if size <= 0 {
83126
return nil, fmt.Errorf("buffer size too small")
84127
}
@@ -89,14 +132,19 @@ func NewReaderSize(rd io.Reader, buffers, size int) (io.ReadCloser, error) {
89132
return nil, fmt.Errorf("nil input reader supplied")
90133
}
91134
a := &reader{}
135+
if _, ok := rd.(io.Seeker); ok {
136+
res = &seekable{a}
137+
} else {
138+
res = a
139+
}
92140
a.init(rd, buffers, size)
93-
return a, nil
141+
return
94142
}
95143

96144
// NewReadCloserSize returns a reader with a custom number of buffers and size.
97145
// buffers is the number of queued buffers and size is the size of each
98146
// buffer in bytes.
99-
func NewReadCloserSize(rc io.ReadCloser, buffers, size int) (io.ReadCloser, error) {
147+
func NewReadCloserSize(rc io.ReadCloser, buffers, size int) (res io.ReadCloser, err error) {
100148
if size <= 0 {
101149
return nil, fmt.Errorf("buffer size too small")
102150
}
@@ -107,8 +155,39 @@ func NewReadCloserSize(rc io.ReadCloser, buffers, size int) (io.ReadCloser, erro
107155
return nil, fmt.Errorf("nil input reader supplied")
108156
}
109157
a := &reader{closer: rc}
158+
if _, ok := rc.(io.Seeker); ok {
159+
res = &seekable{a}
160+
} else {
161+
res = a
162+
}
110163
a.init(rc, buffers, size)
111-
return a, nil
164+
return
165+
}
166+
167+
// NewReadSeekerSize returns a reader with a custom number of buffers and size.
168+
// buffers is the number of queued buffers and size is the size of each
169+
// buffer in bytes.
170+
func NewReadSeekerSize(rd io.ReadSeeker, buffers, size int) (res ReadSeekCloser, err error) {
171+
reader, err := NewReaderSize(rd, buffers, size)
172+
if err != nil {
173+
return nil, err
174+
}
175+
//Not checking for result as the input interface guarantees it's seekable
176+
res, _ = reader.(ReadSeekCloser)
177+
return
178+
}
179+
180+
// NewReadSeekCloserSize returns a reader with a custom number of buffers and size.
181+
// buffers is the number of queued buffers and size is the size of each
182+
// buffer in bytes.
183+
func NewReadSeekCloserSize(rd ReadSeekCloser, buffers, size int) (res ReadSeekCloser, err error) {
184+
reader, err := NewReadCloserSize(rd, buffers, size)
185+
if err != nil {
186+
return nil, err
187+
}
188+
//Not checking for result as the input interface guarantees it's seekable
189+
res, _ = reader.(ReadSeekCloser)
190+
return
112191
}
113192

114193
// initialize the reader
@@ -119,7 +198,9 @@ func (a *reader) init(rd io.Reader, buffers, size int) {
119198
a.exit = make(chan struct{}, 0)
120199
a.exited = make(chan struct{}, 0)
121200
a.buffers = buffers
201+
a.size = size
122202
a.cur = nil
203+
a.err = nil
123204

124205
// Create buffers
125206
for i := 0; i < buffers; i++ {
@@ -130,13 +211,13 @@ func (a *reader) init(rd io.Reader, buffers, size int) {
130211
go func() {
131212
// Ensure that when we exit this is signalled.
132213
defer close(a.exited)
214+
defer close(a.ready)
133215
for {
134216
select {
135217
case b := <-a.reuse:
136218
err := b.read(a.in)
137219
a.ready <- b
138220
if err != nil {
139-
close(a.ready)
140221
return
141222
}
142223
case <-a.exit:
@@ -183,6 +264,34 @@ func (a *reader) Read(p []byte) (n int, err error) {
183264
return n, nil
184265
}
185266

267+
func (a *seekable) Seek(offset int64, whence int) (res int64, err error) {
268+
//Not checking the result as seekable receiver guarantees it to be assertable
269+
seeker, _ := a.in.(io.Seeker)
270+
//Make sure the async routine is closed
271+
select {
272+
case <-a.exited:
273+
case a.exit <- struct{}{}:
274+
<-a.exited
275+
}
276+
if whence == io.SeekCurrent {
277+
//If need to seek based on current position, take into consideration the bytes we read but the consumer
278+
//doesn't know about
279+
err = nil
280+
for a.cur != nil {
281+
if err = a.fill(); err == nil && a.cur != nil {
282+
offset -= int64(len(a.cur.buffer()))
283+
a.cur.offset = len(a.cur.buf)
284+
}
285+
}
286+
}
287+
//Seek the actual Seeker
288+
if res, err = seeker.Seek(offset, whence); err == nil {
289+
//If the seek was successful, reinitalize ourselves (with the new position).
290+
a.init(a.in, a.buffers, a.size)
291+
}
292+
return
293+
}
294+
186295
// WriteTo writes data to w until there's no more data to write or when an error occurs.
187296
// The return value n is the number of bytes written.
188297
// Any error encountered during the write is also returned.

reader_test.go

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ import (
55
"bytes"
66
"errors"
77
"fmt"
8+
"github.com/klauspost/readahead"
89
"io"
910
"io/ioutil"
1011
"strings"
1112
"sync"
1213
"testing"
1314
"testing/iotest"
14-
15-
"github.com/klauspost/readahead"
1615
)
1716

1817
func TestReader(t *testing.T) {
@@ -65,6 +64,148 @@ func TestReader(t *testing.T) {
6564
}
6665
}
6766

67+
type SeekerBuffer struct {
68+
*bytes.Buffer
69+
pos int64
70+
}
71+
72+
func (s *SeekerBuffer) Read(p []byte) (n int, err error) {
73+
n, err = bytes.NewReader(s.Bytes()[s.pos:]).Read(p)
74+
if n > 0 {
75+
s.pos += int64(n)
76+
}
77+
return
78+
}
79+
80+
func (s *SeekerBuffer) Seek(offset int64, whence int) (res int64, err error) {
81+
if offset > int64(s.Len()) {
82+
err = fmt.Errorf("wrong offset")
83+
return
84+
}
85+
switch whence {
86+
case io.SeekStart:
87+
res = offset
88+
case io.SeekCurrent:
89+
res = s.pos + offset
90+
case io.SeekEnd:
91+
res = int64(s.Len()) + offset
92+
}
93+
s.pos = res
94+
return
95+
}
96+
97+
func TestSeeker(t *testing.T) {
98+
testBytes := []byte("Testbuffer")
99+
newControl := func(i int) io.Reader {
100+
buf := bytes.NewBuffer(testBytes)
101+
for j := 0; j < i*100-1; j++ {
102+
buf.Write(testBytes)
103+
}
104+
return buf
105+
}
106+
for i := 1; i <= 100; i++ {
107+
length := len(testBytes) * i * 100
108+
buf := &SeekerBuffer{
109+
Buffer: bytes.NewBuffer(testBytes),
110+
}
111+
for j := 0; j < i*100-1; j++ {
112+
buf.Write(testBytes)
113+
}
114+
control := newControl(i)
115+
ar, err := readahead.NewReadSeekerSize(buf, i, 11*i)
116+
if _, ok := control.(io.Seeker); ok {
117+
t.Fatal("created reader implements seeking without underlying reader support")
118+
}
119+
if err != nil {
120+
t.Fatal("error when creating:", err)
121+
}
122+
dstSize := 3 * i
123+
dst := make([]byte, dstSize)
124+
controlDst := make([]byte, dstSize)
125+
control.Read(controlDst)
126+
n, err := ar.Read(dst)
127+
if err != nil {
128+
t.Fatal("error when reading:", err)
129+
}
130+
if n != dstSize {
131+
t.Fatal("unexpected length, expected ", dstSize, ", got ", n)
132+
}
133+
if string(dst) != string(controlDst) {
134+
t.Fatal("seeker and control reader mismatch")
135+
}
136+
137+
pos, err := ar.Seek(1, io.SeekStart)
138+
if err != nil {
139+
t.Fatal("error when seeking:", err)
140+
}
141+
if pos != 1 {
142+
t.Fatal("unexpected position, expected 1, got ", pos)
143+
}
144+
control = newControl(i)
145+
control.Read(make([]byte, 1)) //Emulate seeking to offset 1 from beginning
146+
control.Read(controlDst)
147+
n, err = ar.Read(dst)
148+
if err != nil {
149+
t.Fatal("error when reading:", err)
150+
}
151+
if n != dstSize {
152+
t.Fatal("unexpected length, expected ", dstSize, ", got ", n)
153+
}
154+
if string(dst) != string(controlDst) {
155+
t.Fatal("seeker and control reader mismatch")
156+
}
157+
158+
pos, err = ar.Seek(int64(i), io.SeekCurrent)
159+
if err != nil {
160+
t.Fatal("error when seeking:", err)
161+
}
162+
if pos != int64(dstSize+i+1) {
163+
t.Fatal("unexpected position, expected ", dstSize+i, ", got ", pos)
164+
}
165+
control.Read(make([]byte, int64(i))) //Emulate seeking to offset 1 from current pos
166+
control.Read(controlDst)
167+
n, err = ar.Read(dst)
168+
if err != nil {
169+
t.Fatal("error when reading:", err)
170+
}
171+
if n != dstSize {
172+
t.Fatal("unexpected length, expected ", dstSize, ", got ", n)
173+
}
174+
if string(dst) != string(controlDst) {
175+
t.Fatal("seeker and control reader mismatch")
176+
}
177+
178+
control = newControl(i)
179+
pos, err = ar.Seek(-1, io.SeekEnd)
180+
if err != nil {
181+
t.Fatal("error when seeking:", err)
182+
}
183+
if pos != int64(length-1) {
184+
t.Fatal("unexpected position, expected ", length-1, ", got ", pos)
185+
}
186+
control.Read(make([]byte, length-1)) //Emulate seeking to offset -1 from the end
187+
control.Read(controlDst)
188+
n, err = ar.Read(dst)
189+
if err != nil {
190+
t.Fatal("error when reading:", err)
191+
}
192+
if n != 1 {
193+
t.Fatal("unexpected length, expected 1, got ", n)
194+
}
195+
if string(dst[:n]) != string(controlDst[:n]) {
196+
t.Fatal("seeker and control reader mismatch")
197+
}
198+
199+
n, err = ar.Read(dst)
200+
if err != io.EOF {
201+
t.Fatal("expected io.EOF, got", err)
202+
}
203+
if n != 0 {
204+
t.Fatal("unexpected length, expected 0, got ", n)
205+
}
206+
}
207+
}
208+
68209
type testCloser struct {
69210
io.Reader
70211
closed int

0 commit comments

Comments
 (0)