Skip to content

Instantly share code, notes, and snippets.

@noncombatant
Last active June 17, 2020 01:20
Show Gist options
  • Save noncombatant/2b84000ce6bf0b7c8bfd906d7dc11a21 to your computer and use it in GitHub Desktop.
Save noncombatant/2b84000ce6bf0b7c8bfd906d7dc11a21 to your computer and use it in GitHub Desktop.
MaybeGzipReader
package maybegzip
import (
"compress/gzip"
"io"
"os"
)
// Provides an io.Reader that might or might not refer to a gzipped file. If it
// is a gzipped file, reading from the io.Reader transparently decompresses the
// stream, and IsGzipped will be set to true.
//
// TODO: Consider supporting (transparently) all the compression formats that
// the Go standard library supports. The name would change to
// MaybeCompressedReader, IsGzipped would become an enum, et c.
type MaybeGzipReader struct {
// This Reader might refer to the original ReadSeeker passed to New (or the
// os.File created by Open), or it might refer to a gzip.Reader wrapping it.
// Check IsGzipped to find out. Either way, reading from it yields an
// uncompressed stream.
io.Reader
// Indicates whether or not Reader is a gzip.Reader.
IsGzipped bool
// If IsGzipped, this refers to the io.Closer that the gzip.Reader is
// wrapping. We use this to implement Close.
underlyingCloser io.Closer
}
func New(r io.ReadSeeker) (*MaybeGzipReader, error) {
gzr, e := gzip.NewReader(r)
if e != nil {
if e != gzip.ErrHeader && e != gzip.ErrChecksum {
return nil, e
}
_, e = r.Seek(0, io.SeekStart)
if e != nil {
return nil, e
}
// This type coercion (see also in Close) is nasty and may cause run-time
// panics. TODO: Find some clean way to fix that; create a ReadSeekCloser
// interface and make r be of that type?
return &MaybeGzipReader{Reader: r, IsGzipped: false, underlyingCloser: r.(io.Closer)}, nil
}
return &MaybeGzipReader{Reader: gzr, IsGzipped: true, underlyingCloser: r.(io.Closer)}, nil
}
func Open(pathname string) (*MaybeGzipReader, error) {
file, e := os.Open(pathname)
if e != nil {
return nil, e
}
return New(file)
}
func (r *MaybeGzipReader) Close() error {
e := r.Reader.(io.Closer).Close()
if e != nil {
return e
}
if r.IsGzipped {
e = r.underlyingCloser.Close()
if e != nil {
return e
}
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment