From c264c87335ff4b3111d43f830dbe37eac1509f2e Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 22 Jan 2015 14:15:47 -0800 Subject: [PATCH] bytes, strings: add Reader.Size methods As noted on recently on golang-nuts, there's currently no way to know the total size of a strings.Reader or bytes.Reader when using ReadAt on them. Most callers resort to wrapping it in an io.SectionReader to retain that information. The SizeReaderAt abstraction (an io.ReaderAt with a Size() int64 method) has proven useful as a way of expressing a concurrency-safe read-only number of bytes. As one example, see http://talks.golang.org/2013/oscon-dl.slide#49 and the rest of that presentation for its use in dl.google.com. SizeReaderAt is also used in the open source google-api-go-client, and within Google's internal codebase, where it exists in a public package created in 2013 with the package comment: "These may migrate to the standard library after we have enough experience with their feel." I'm still as happy with the SizeReaderAt abstraction and its composabilty as I was in 2013, so I'd like to make these two Readers also be SizeReaderAts. Fixes #9667 Change-Id: Ie6f145ada419dd116280472d8c029f046d5edf70 Reviewed-on: https://go-review.googlesource.com/3199 Reviewed-by: Andrew Gerrand Reviewed-by: Russ Cox Reviewed-by: Rob Pike --- src/bytes/reader.go | 6 ++++++ src/bytes/reader_test.go | 12 ++++++++++++ src/strings/reader.go | 6 ++++++ src/strings/reader_test.go | 13 +++++++++++++ 4 files changed, 37 insertions(+) diff --git a/src/bytes/reader.go b/src/bytes/reader.go index d2d40fa7ca..b89d1548f1 100644 --- a/src/bytes/reader.go +++ b/src/bytes/reader.go @@ -29,6 +29,12 @@ func (r *Reader) Len() int { return int(int64(len(r.s)) - r.i) } +// Size returns the original length of the underlying byte slice. +// Size is the number of bytes available for reading via ReadAt. +// The returned value is always the same and is not affected by calls +// to any other method. +func (r *Reader) Size() int64 { return int64(len(r.s)) } + func (r *Reader) Read(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil diff --git a/src/bytes/reader_test.go b/src/bytes/reader_test.go index d3dce53499..b929a28260 100644 --- a/src/bytes/reader_test.go +++ b/src/bytes/reader_test.go @@ -244,3 +244,15 @@ func TestReaderCopyNothing(t *testing.T) { t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut) } } + +// tests that Len is affected by reads, but Size is not. +func TestReaderLenSize(t *testing.T) { + r := NewReader([]byte("abc")) + io.CopyN(ioutil.Discard, r, 1) + if r.Len() != 2 { + t.Errorf("Len = %d; want 2", r.Len()) + } + if r.Size() != 3 { + t.Errorf("Size = %d; want 3", r.Size()) + } +} diff --git a/src/strings/reader.go b/src/strings/reader.go index 82df974398..7a872fbcb0 100644 --- a/src/strings/reader.go +++ b/src/strings/reader.go @@ -28,6 +28,12 @@ func (r *Reader) Len() int { return int(int64(len(r.s)) - r.i) } +// Size returns the original length of the underlying string. +// Size is the number of bytes available for reading via ReadAt. +// The returned value is always the same and is not affected by calls +// to any other method. +func (r *Reader) Size() int64 { return int64(len(r.s)) } + func (r *Reader) Read(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil diff --git a/src/strings/reader_test.go b/src/strings/reader_test.go index bee90eb258..5003a37be4 100644 --- a/src/strings/reader_test.go +++ b/src/strings/reader_test.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "io" + "io/ioutil" "os" "strings" "sync" @@ -157,3 +158,15 @@ func TestWriteTo(t *testing.T) { } } } + +// tests that Len is affected by reads, but Size is not. +func TestReaderLenSize(t *testing.T) { + r := strings.NewReader("abc") + io.CopyN(ioutil.Discard, r, 1) + if r.Len() != 2 { + t.Errorf("Len = %d; want 2", r.Len()) + } + if r.Size() != 3 { + t.Errorf("Size = %d; want 3", r.Size()) + } +} -- 2.48.1