package gzip import ( "compress/gzip" "io" "github.com/rqlite/rqlite/v8/progress" ) // Decompressor is a wrapper around a gzip.Reader that reads from an io.Reader // and decompresses the data. type Decompressor struct { cr *progress.CountingReader gzr *gzip.Reader nRx int64 nTx int64 } // NewDecompressor returns an instantied Decompressor that reads from r and // decompresses the data using gzip. func NewDecompressor(r io.Reader) *Decompressor { return &Decompressor{ cr: progress.NewCountingReader(r), } } // Read reads decompressed data. func (c *Decompressor) Read(p []byte) (nn int, err error) { defer func() { c.nTx += int64(nn) }() if c.cr == nil { return 0, io.EOF } if c.gzr == nil { var err error c.gzr, err = gzip.NewReader(c.cr) if err != nil { return 0, err } // Setting Multistream to false means the gzip reader will // return io.EOF when it reaches the end of the gzip stream. // Otherwise the reader hangs. This seems to be needed only // for the gzip over a stream. c.gzr.Multistream(false) } n, err := c.gzr.Read(p) c.nRx += int64(n) if err == io.EOF { if err := c.gzr.Close(); err != nil { return 0, err } c.cr = nil // Signal no more re-use of the underlying reader. c.gzr = nil } return n, err }