package pkg import ( "compress/bzip2" "compress/gzip" "fmt" "io" "os" ) const ( // Gzip denotes a stream compressed via [gzip]. Gzip = iota // Bzip2 denotes a stream compressed via [bzip2]. Bzip2 ) // A decompressArtifact is a [FileArtifact] decompressing a backing // [FileArtifact] stream. type decompressArtifact struct { // Caller-supplied backing stream. f Artifact // Compression on top of the stream. compress uint32 } var _ FileArtifact = new(decompressArtifact) // decompressArtifactNamed embeds decompressArtifact for a [fmt.Stringer] stream. type decompressArtifactNamed struct { decompressArtifact // Copied from decompressArtifact.f. name string } var _ fmt.Stringer = new(decompressArtifactNamed) // NewDecompress returns a [FileArtifact] decompressing the supplied [Artifact]. func NewDecompress(a Artifact, compress uint32) Artifact { da := decompressArtifact{a, compress} if s, ok := a.(fmt.Stringer); ok { if name := s.String(); name != "" { return &decompressArtifactNamed{da, name} } } return &da } // String returns the name of the underlying [Artifact] suffixed with decompress. func (a *decompressArtifactNamed) String() string { return a.name + "-decompress" } // Kind returns the hardcoded [Kind] constant. func (a *decompressArtifact) Kind() Kind { return KindDecompress } // Params writes value of compression enum. func (a *decompressArtifact) Params(ctx *IContext) { ctx.WriteUint32(a.compress) } func init() { register(KindDecompress, func(r *IRReader) Artifact { a := NewDecompress(r.Next(), r.ReadUint32()) if _, ok := r.Finalise(); ok { panic(ErrUnexpectedChecksum) } return a }) } // Dependencies returns a slice containing the backing file. func (a *decompressArtifact) Dependencies() []Artifact { return []Artifact{a.f} } // IsExclusive returns false: decompressor is fully sequential. func (a *decompressArtifact) IsExclusive() bool { return false } // compoundCloser is an [io.ReadCloser] with an additional [io.Closer] attached. type compoundCloser struct { io.ReadCloser c io.Closer } // Close closes [io.ReadCloser] and the additional [io.Closer]. It returns the // non-nil error returned by the underlying [io.ReadCloser], otherwise it // returns the error returned by the additional [io.Closer]. func (c compoundCloser) Close() error { err := c.ReadCloser.Close() if _err := c.c.Close(); err == nil { err = _err } return err } // Cure returns a decompressor [io.ReadCloser]. func (a *decompressArtifact) Cure(r *RContext) (io.ReadCloser, error) { sr, err := r.Open(a.f) if err != nil { return nil, err } br := r.cache.getReaderRC(sr) var dr io.ReadCloser switch a.compress { case Gzip: if dr, err = gzip.NewReader(br); err != nil { _ = br.Close() return nil, err } return compoundCloser{dr, br}, nil case Bzip2: return struct { io.Reader io.Closer }{bzip2.NewReader(br), br}, nil default: return nil, os.ErrInvalid } }