]> Cypherpunks repositories - gostls13.git/commitdiff
bytes, strings: add Reader.Size methods
authorBrad Fitzpatrick <bradfitz@golang.org>
Thu, 22 Jan 2015 22:15:47 +0000 (14:15 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 6 Apr 2015 08:53:47 +0000 (08:53 +0000)
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 <adg@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
src/bytes/reader.go
src/bytes/reader_test.go
src/strings/reader.go
src/strings/reader_test.go

index d2d40fa7ca1470faf3186ee731fe824ce329b2f4..b89d1548f1b78ea16e5fa1bdb25e4228a5c684c9 100644 (file)
@@ -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
index d3dce53499eda631ea145a49f69113895c3a7d89..b929a2826096d501966925b00244c3af79dee42a 100644 (file)
@@ -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())
+       }
+}
index 82df974398c57c04352bcd0d7bc10500624b5484..7a872fbcb088732de99fbec36dcaf88a5e715136 100644 (file)
@@ -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
index bee90eb2585faf11f77fb80c85dfaccc74e606d0..5003a37be4817b1b6701c2fa1e2e729087023c60 100644 (file)
@@ -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())
+       }
+}