Skip to content

Commit 9f59659

Browse files
committed
wrapio returns io.ErrNoProgress when it gets many consecutive empty reads/writes
1 parent 3bf284d commit 9f59659

File tree

2 files changed

+39
-13
lines changed

2 files changed

+39
-13
lines changed

buffer_test.go

+17
Original file line numberDiff line numberDiff line change
@@ -704,3 +704,20 @@ func TestPanicWriteAt(t *testing.T) {
704704
buf.WriteAt(nil, 0)
705705
t.Error("expected a panic!")
706706
}
707+
708+
type badFile struct{}
709+
710+
func (b badFile) Name() string { return "" }
711+
func (b badFile) Stat() (os.FileInfo, error) { return nil, errors.New("unsupported") }
712+
func (b badFile) ReadAt(p []byte, off int64) (int, error) { return 0, nil }
713+
func (b badFile) WriteAt(p []byte, off int64) (int, error) { return len(p), nil }
714+
func (b badFile) Close() error { return nil }
715+
716+
func TestWrapioBreakout(t *testing.T) {
717+
buf := NewFile(10, badFile{})
718+
io.WriteString(buf, "hello world")
719+
if _, err := ioutil.ReadAll(buf); err != io.ErrNoProgress {
720+
t.Error("expected no progress to be made")
721+
t.FailNow()
722+
}
723+
}

wrapio/wrap.go

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package wrapio
22

3-
import (
4-
"fmt"
5-
"io"
6-
)
3+
import "io"
74

85
// DoerAt is a common interface for wrappers WriteAt or ReadAt functions
96
type DoerAt interface {
@@ -98,26 +95,38 @@ func (r *WrapReader) ReadAt(p []byte, off int64) (n int, err error) {
9895
return Wrap(r, p, off, r.wrapAt)
9996
}
10097

98+
// maxConsecutiveEmptyActions determines how many consecutive empty reads/writes can occur before giving up
99+
const maxConsecutiveEmptyActions = 100
100+
101101
// Wrap causes an action on an array of bytes (like read/write) to be done from an offset off,
102102
// wrapping at offset wrapAt.
103103
func Wrap(w DoerAt, p []byte, off int64, wrapAt int64) (n int, err error) {
104-
var m int
104+
var m, fails int
105105

106106
off %= wrapAt
107107

108108
for len(p) > 0 {
109109

110110
if off+int64(len(p)) < wrapAt {
111-
if m, err = w.DoAt(p, off); err != nil && err != io.EOF {
112-
fmt.Println(off, m, len(p), err)
113-
return n + m, err
114-
}
111+
m, err = w.DoAt(p, off)
115112
} else {
116113
space := wrapAt - off
117-
if m, err = w.DoAt(p[:space], off); err != nil && err != io.EOF {
118-
fmt.Println(off, m, len(p[:space]), err)
119-
return n + m, err
120-
}
114+
m, err = w.DoAt(p[:space], off)
115+
}
116+
117+
if err != nil && err != io.EOF {
118+
return n + m, err
119+
}
120+
121+
switch m {
122+
case 0:
123+
fails++
124+
default:
125+
fails = 0
126+
}
127+
128+
if fails > maxConsecutiveEmptyActions {
129+
return n + m, io.ErrNoProgress
121130
}
122131

123132
n += m

0 commit comments

Comments
 (0)