aboutsummaryrefslogtreecommitdiffhomepage
path: root/pkg/compress.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/compress.go')
-rw-r--r--pkg/compress.go151
1 files changed, 151 insertions, 0 deletions
diff --git a/pkg/compress.go b/pkg/compress.go
new file mode 100644
index 00000000..22f990f1
--- /dev/null
+++ b/pkg/compress.go
@@ -0,0 +1,151 @@
+package pkg
+
+import (
+ "compress/bzip2"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "os"
+
+ "hakurei.app/internal/xz"
+ "hakurei.app/internal/zstd"
+)
+
+const (
+ // Gzip denotes a stream compressed via [gzip].
+ Gzip = iota
+ // Bzip2 denotes a stream compressed via [bzip2].
+ Bzip2
+ // Zstd denotes a stream compressed via [zstd].
+ Zstd
+ // XZ denotes a stream compressed via [xz].
+ XZ
+)
+
+// 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)
+var _ CuresExempt = 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) FileArtifact {
+ 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] prefixed with decompress.
+func (a *decompressArtifactNamed) String() string { return "decompress-" + a.name }
+
+// 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
+ })
+}
+
+// Inputs returns a slice containing the backing file.
+func (a *decompressArtifact) Inputs() []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
+}
+
+// IsExecutable returns false.
+func (*decompressArtifact) IsExecutable() bool { return false }
+
+// 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
+
+ case Zstd:
+ return struct {
+ io.Reader
+ io.Closer
+ }{zstd.NewReader(br), br}, nil
+
+ case XZ:
+ var _dr io.Reader
+ if _dr, err = xz.NewReader(br, 0); err != nil {
+ _ = br.Close()
+ return nil, err
+ }
+ return struct {
+ io.Reader
+ io.Closer
+ }{_dr, br}, nil
+
+ default:
+ return nil, os.ErrInvalid
+ }
+}
+
+// CuresExempt exempts the cheap [KindDecompress] implementation often part of
+// a [FileArtifact] pipeline.
+func (*decompressArtifact) CuresExempt() {}