From f77dc7dfe73ee64c2f3d59a60d31c8106d993d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= Date: Fri, 27 Sep 2019 06:35:45 +0200 Subject: [PATCH] Don't import all of pkg/compression in c/image/types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, importing c/image/types for any reason drags in all the compression implementations, which is very undesirable. For now, we don't really want to commit to the compresion.Algorithm API as a public interface where anyone could supply an external implementation; so, the goal is to provide an Algorithm type that has the same public properties (notably a Name() method), but it is usable only within the pkg/compression namespace. To do this, move the Algorithm struct into a pkg/compression/internal subpackage which does not depend on the compression implementations. This still allows defining public methods on the type, but the subpackage can keep the access to creating values of that type and to accessing other members restricted to pkg/compression by only using private fields and providing a constructor and accessors that are callable only from within pkg/compression. Then, make the internal.Algorithm type public as an alias in a new pkg/compression/types subpackage, without exposing anything else from the internal subpackage. The primary pkg/compression package continues to exist the way it used to, only using accessors from the internal subpackage to deal with the internal.Algorithm = compression/types.Algorithm = compression.Algorithm type. Signed-off-by: Miloslav Trmač --- pkg/compression/compression.go | 44 +++++++++--------------- pkg/compression/internal/types.go | 57 +++++++++++++++++++++++++++++++ pkg/compression/types/types.go | 13 +++++++ types/types.go | 4 +-- 4 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 pkg/compression/internal/types.go create mode 100644 pkg/compression/types/types.go diff --git a/pkg/compression/compression.go b/pkg/compression/compression.go index 267868c6ab..f0c4febb39 100644 --- a/pkg/compression/compression.go +++ b/pkg/compression/compression.go @@ -7,6 +7,8 @@ import ( "io" "io/ioutil" + "github.com/containers/image/pkg/compression/internal" + "github.com/containers/image/pkg/compression/types" "github.com/klauspost/pgzip" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -14,36 +16,26 @@ import ( ) // Algorithm is a compression algorithm that can be used for CompressStream. -type Algorithm struct { - name string - prefix []byte - decompressor DecompressorFunc - compressor compressorFunc -} +type Algorithm = types.Algorithm var ( // Gzip compression. - Gzip = Algorithm{"gzip", []byte{0x1F, 0x8B, 0x08}, GzipDecompressor, gzipCompressor} + Gzip = internal.NewAlgorithm("gzip", []byte{0x1F, 0x8B, 0x08}, GzipDecompressor, gzipCompressor) // Bzip2 compression. - Bzip2 = Algorithm{"bzip2", []byte{0x42, 0x5A, 0x68}, Bzip2Decompressor, bzip2Compressor} + Bzip2 = internal.NewAlgorithm("bzip2", []byte{0x42, 0x5A, 0x68}, Bzip2Decompressor, bzip2Compressor) // Xz compression. - Xz = Algorithm{"Xz", []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, XzDecompressor, xzCompressor} + Xz = internal.NewAlgorithm("Xz", []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, XzDecompressor, xzCompressor) // Zstd compression. - Zstd = Algorithm{"zstd", []byte{0x28, 0xb5, 0x2f, 0xfd}, ZstdDecompressor, zstdCompressor} + Zstd = internal.NewAlgorithm("zstd", []byte{0x28, 0xb5, 0x2f, 0xfd}, ZstdDecompressor, zstdCompressor) compressionAlgorithms = map[string]Algorithm{ - Gzip.name: Gzip, - Bzip2.name: Bzip2, - Xz.name: Xz, - Zstd.name: Zstd, + Gzip.Name(): Gzip, + Bzip2.Name(): Bzip2, + Xz.Name(): Xz, + Zstd.Name(): Zstd, } ) -// Name returns the name for the compression algorithm. -func (c Algorithm) Name() string { - return c.name -} - // AlgorithmByName returns the compressor by its name func AlgorithmByName(name string) (Algorithm, error) { algorithm, ok := compressionAlgorithms[name] @@ -55,7 +47,7 @@ func AlgorithmByName(name string) (Algorithm, error) { // DecompressorFunc returns the decompressed stream, given a compressed stream. // The caller must call Close() on the decompressed stream (even if the compressed input stream does not need closing!). -type DecompressorFunc func(io.Reader) (io.ReadCloser, error) +type DecompressorFunc = internal.DecompressorFunc // GzipDecompressor is a DecompressorFunc for the gzip compression algorithm. func GzipDecompressor(r io.Reader) (io.ReadCloser, error) { @@ -76,10 +68,6 @@ func XzDecompressor(r io.Reader) (io.ReadCloser, error) { return ioutil.NopCloser(r), nil } -// compressorFunc writes the compressed stream to the given writer using the specified compression level. -// The caller must call Close() on the stream (even if the input stream does not need closing!). -type compressorFunc func(io.Writer, *int) (io.WriteCloser, error) - // gzipCompressor is a CompressorFunc for the gzip compression algorithm. func gzipCompressor(r io.Writer, level *int) (io.WriteCloser, error) { if level != nil { @@ -100,7 +88,7 @@ func xzCompressor(r io.Writer, level *int) (io.WriteCloser, error) { // CompressStream returns the compressor by its name func CompressStream(dest io.Writer, algo Algorithm, level *int) (io.WriteCloser, error) { - return algo.compressor(dest, level) + return internal.AlgorithmCompressor(algo)(dest, level) } // DetectCompressionFormat returns a DecompressorFunc if the input is recognized as a compressed format, nil otherwise. @@ -118,10 +106,10 @@ func DetectCompressionFormat(input io.Reader) (Algorithm, DecompressorFunc, io.R var retAlgo Algorithm var decompressor DecompressorFunc for _, algo := range compressionAlgorithms { - if bytes.HasPrefix(buffer[:n], algo.prefix) { - logrus.Debugf("Detected compression format %s", algo.name) + if bytes.HasPrefix(buffer[:n], internal.AlgorithmPrefix(algo)) { + logrus.Debugf("Detected compression format %s", algo.Name()) retAlgo = algo - decompressor = algo.decompressor + decompressor = internal.AlgorithmDecompressor(algo) break } } diff --git a/pkg/compression/internal/types.go b/pkg/compression/internal/types.go new file mode 100644 index 0000000000..6092a9517b --- /dev/null +++ b/pkg/compression/internal/types.go @@ -0,0 +1,57 @@ +package internal + +import "io" + +// CompressorFunc writes the compressed stream to the given writer using the specified compression level. +// The caller must call Close() on the stream (even if the input stream does not need closing!). +type CompressorFunc func(io.Writer, *int) (io.WriteCloser, error) + +// DecompressorFunc returns the decompressed stream, given a compressed stream. +// The caller must call Close() on the decompressed stream (even if the compressed input stream does not need closing!). +type DecompressorFunc func(io.Reader) (io.ReadCloser, error) + +// Algorithm is a compression algorithm that can be used for CompressStream. +type Algorithm struct { + name string + prefix []byte + decompressor DecompressorFunc + compressor CompressorFunc +} + +// NewAlgorithm creates an Algorithm instance. +// This function exists so that Algorithm instances can only be created by code that +// is allowed to import this internal subpackage. +func NewAlgorithm(name string, prefix []byte, decompressor DecompressorFunc, compressor CompressorFunc) Algorithm { + return Algorithm{ + name: name, + prefix: prefix, + decompressor: decompressor, + compressor: compressor, + } +} + +// Name returns the name for the compression algorithm. +func (c Algorithm) Name() string { + return c.name +} + +// AlgorithmCompressor returns the compressor field of algo. +// This is a function instead of a public method so that it is only callable from by code +// that is allowed to import this internal subpackage. +func AlgorithmCompressor(algo Algorithm) CompressorFunc { + return algo.compressor +} + +// AlgorithmDecompressor returns the decompressor field of algo. +// This is a function instead of a public method so that it is only callable from by code +// that is allowed to import this internal subpackage. +func AlgorithmDecompressor(algo Algorithm) DecompressorFunc { + return algo.decompressor +} + +// AlgorithmPrefix returns the prefix field of algo. +// This is a function instead of a public method so that it is only callable from by code +// that is allowed to import this internal subpackage. +func AlgorithmPrefix(algo Algorithm) []byte { + return algo.prefix +} diff --git a/pkg/compression/types/types.go b/pkg/compression/types/types.go new file mode 100644 index 0000000000..613342b490 --- /dev/null +++ b/pkg/compression/types/types.go @@ -0,0 +1,13 @@ +package types + +import ( + "github.com/containers/image/pkg/compression/internal" +) + +// DecompressorFunc returns the decompressed stream, given a compressed stream. +// The caller must call Close() on the decompressed stream (even if the compressed input stream does not need closing!). +type DecompressorFunc = internal.DecompressorFunc + +// Algorithm is a compression algorithm provided and supported by pkg/compression. +// It can’t be supplied from the outside. +type Algorithm = internal.Algorithm diff --git a/types/types.go b/types/types.go index 0d2fb7d86b..d9486d5c5a 100644 --- a/types/types.go +++ b/types/types.go @@ -6,8 +6,8 @@ import ( "time" "github.com/containers/image/docker/reference" - "github.com/containers/image/pkg/compression" - "github.com/opencontainers/go-digest" + compression "github.com/containers/image/pkg/compression/types" + digest "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" )