All checks were successful
Test / Create distribution (push) Successful in 2m56s
Test / Sandbox (push) Successful in 6m55s
Test / Hakurei (push) Successful in 9m46s
Test / ShareFS (push) Successful in 10m21s
Test / Sandbox (race detector) (push) Successful in 10m44s
Test / Hakurei (race detector) (push) Successful in 14m34s
Test / Flake checks (push) Successful in 3m14s
The tarArtifact predates FileArtifact pipelining. This migrates decompression and buffering into a standalone artifact implementation. Signed-off-by: Ophestra <cat@gensokyo.uk>
120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
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
|
|
}
|
|
}
|