// ReadData reads n bytes from the input stream, but avoids allocating
// all n bytes if n is large. This avoids crashing the program by
// allocating all n bytes in cases where n is incorrect.
+//
+// The error is io.EOF only if no bytes were read.
+// If an io.EOF happens after reading some but not all the bytes,
+// ReadData returns io.ErrUnexpectedEOF.
func ReadData(r io.Reader, n uint64) ([]byte, error) {
if int64(n) < 0 || n != uint64(int(n)) {
// n is too large to fit in int, so we can't allocate
}
_, err := io.ReadFull(r, buf1[:next])
if err != nil {
+ if len(buf) > 0 && err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
return nil, err
}
buf = append(buf, buf1[:next]...)
t.Error("large read succeeded unexpectedly")
}
})
+
+ t.Run("small-EOF", func(t *testing.T) {
+ _, err := ReadData(bytes.NewReader(nil), chunk-1)
+ if err != io.EOF {
+ t.Errorf("ReadData = %v, want io.EOF", err)
+ }
+ })
+
+ t.Run("large-EOF", func(t *testing.T) {
+ _, err := ReadData(bytes.NewReader(nil), chunk+1)
+ if err != io.EOF {
+ t.Errorf("ReadData = %v, want io.EOF", err)
+ }
+ })
+
+ t.Run("large-UnexpectedEOF", func(t *testing.T) {
+ _, err := ReadData(bytes.NewReader(make([]byte, chunk)), chunk+1)
+ if err != io.ErrUnexpectedEOF {
+ t.Errorf("ReadData = %v, want io.ErrUnexpectedEOF", err)
+ }
+ })
}
func TestReadDataAt(t *testing.T) {