Skip to content

compress/gzip: allow multi-stream io.Reader reuse #30234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compress/gzip/gunzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ func NewReader(r io.Reader) (*Reader, error) {
// Reset discards the Reader z's state and makes it equivalent to the
// result of its original state from NewReader, but reading from r instead.
// This permits reusing a Reader rather than allocating a new one.
// If r is nil, Reader will reuse the existing io.Reader.
func (z *Reader) Reset(r io.Reader) error {
if r == nil {
r = z.r
}
*z = Reader{
decompressor: z.decompressor,
multistream: true,
Expand Down
55 changes: 55 additions & 0 deletions src/compress/gzip/gunzip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,61 @@ Found:
}
}

func TestMultistreamFalseReuse(t *testing.T) {
// Find concatenation test.
var tt gunzipTest
for _, tt = range gunzipTests {
if strings.HasSuffix(tt.desc, " x2") {
goto Found
}
}
t.Fatal("cannot find hello.txt x2 in gunzip tests")

Found:
fr, err := ioutil.TempFile("","TestMultistreamFalseReuse")
if err != nil {
t.Fatalf("TempFile failed: %v", err)
}
defer os.Remove(fr.Name())
defer fr.Close()
_, err = fr.Write(tt.gzip)
if err != nil {
t.Fatalf("TempFile Write failed: %v", err)
}
_, err = fr.Seek(0, io.SeekStart)
if err != nil {
t.Fatalf("TempFile Seek failed: %v", err)
}

var r Reader
if err := r.Reset(fr); err != nil {
t.Fatalf("first reset: %v", err)
}

// Expect two streams with "hello world\n", then real EOF.
const hello = "hello world\n"

r.Multistream(false)
data, err := ioutil.ReadAll(&r)
if string(data) != hello || err != nil {
t.Fatalf("first stream = %q, %v, want %q, %v", string(data), err, hello, nil)
}

// r.Reset(fr) would fail here due to bufio read-ahead
if err := r.Reset(nil); err != nil {
t.Fatalf("second reset: %v", err)
}
r.Multistream(false)
data, err = ioutil.ReadAll(&r)
if string(data) != hello || err != nil {
t.Fatalf("second stream = %q, %v, want %q, %v", string(data), err, hello, nil)
}

if err := r.Reset(nil); err != io.EOF {
t.Fatalf("third reset: err=%v, want io.EOF", err)
}
}

func TestNilStream(t *testing.T) {
// Go liberally interprets RFC 1952 section 2.2 to mean that a gzip file
// consist of zero or more members. Thus, we test that a nil stream is okay.
Expand Down