/
zwrap.go
81 lines (72 loc) · 2.14 KB
/
zwrap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Package zwrap takes a file pointer and optionally wraps it so upon
// calling Close, the decompressor will be closed, followed by the
// underlying file.
// I benchmarked with and without buffering in Wrap(). I could not measure
// any difference.
package zwrap
import (
"compress/gzip"
"errors"
"io"
)
type FpGzip struct { // This is what we return.
fp io.ReadCloser
zrdr *gzip.Reader
}
// Close closes the compressor, then the underlying backing readCloser.
// It should work if the source is a file or an http stream.
func (fc *FpGzip) Close() error {
if fc.zrdr == nil {
return fc.fp.Close()
}
var s string
if e := fc.zrdr.Close(); e != nil { // Close decompressor
s = e.Error()
}
if e := fc.fp.Close(); e != nil { // and backing file
s = s + " " + e.Error()
}
if s == "" {
return nil
}
return errors.New(s)
}
// Read makes sure we read from the compressed stream and
// not the underlying file stream.
func (fc *FpGzip) Read(p []byte) (int, error) {
if fc.zrdr != nil {
return fc.zrdr.Read(p)
}
return fc.fp.Read(p)
}
// Wrap takes a source like a file pointer or http stream and wraps it
// so the correct Close and Read will be called. Although we use the
// name fp, it should be happy if it is fed an http stream.
func Wrap(fp io.ReadCloser) (*FpGzip, error) {
var fpz FpGzip
var err error
fpz.fp = fp
fpz.zrdr, err = gzip.NewReader(fpz.fp) // No need to check error.
return &fpz, err // Just pass it back
}
// ReadSeekCloser does not seem to be in the standard library
type ReadSeekCloser interface {
io.Reader
io.Seeker
io.Closer
}
// WrapMaybe will decide if the underlying stream is compressed
// and wrap the file pointer if necessary.
// You do lose something. If you pass in something which can seek,
// you get back a ReadCloser which cannot seek. This is the price
// one pays for reading from a compressed reader.
func WrapMaybe(fpIn ReadSeekCloser) (*FpGzip, error) {
if out, err := Wrap(fpIn); err == nil {
return out, nil // It was compressed. Return compressed reader.
}
_, err := fpIn.Seek(0, io.SeekStart)
r := &FpGzip{
fp: fpIn, // Leave the zrdr implicitly nil
}
return r, err
}