-
Notifications
You must be signed in to change notification settings - Fork 0
/
compressor.go
189 lines (161 loc) · 4.38 KB
/
compressor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
Package gompress provides both a CLI interface, and a golang library
for working with various compression algorithms. Currently
supported are
brotli
bzip2
deflate
gzip
lz4
lzma2
lzo
snappy
xz
zlib
zstd
*/
package gompress
import (
"errors"
"fmt"
"io"
"os"
)
var (
errNoCompressorDecompressor = errors.New("FileCompressor initialized without CompressorDecompressor")
fs fileSystem = &osFS{}
)
type fileSystem interface {
Create(filename string) (*os.File, error)
Open(filename string) (*os.File, error)
Stat(filename string) (os.FileInfo, error)
}
type osFS struct {
}
func (o osFS) Create(filename string) (*os.File, error) { return os.Create(filename) }
func (o osFS) Open(filename string) (*os.File, error) { return os.Open(filename) }
func (o osFS) Stat(filename string) (os.FileInfo, error) { return os.Stat(filename) }
// Compressor is a type that is capable of compressing
// a data stream provided by an io.Reader, writing the
// output to an io.Writer.
type Compressor interface {
// Compress compresses data from r, and writes to w.
Compress(r io.Reader, w io.Writer) error
}
// Decompressor is a type that is capable of decompressing
// a data stream provided by an io.Reader, writing the
// output to an io.Writer.
type Decompressor interface {
// Decompress decompresses data from r, and writes to w.
Decompress(r io.Reader, w io.Writer) error
}
type CompressorDecompressor interface {
Compressor
Decompressor
}
// Leveller defines the contracts for compression methods
// that make use of levels should implement in order to allow
// for the control of the internal compression algorithms
// compression level.
type Leveller interface {
Level() int
MinLevel() int
MaxLevel() int
SetLevel(level int) error
}
// FileCompressor serves as a wrapper for Compressor/Decompressors,
// providing util functions for working with files.
type FileCompressor struct {
CompressorDecompressor
// Default file extension
Extension string
// The name of the compression method
Method string
}
// CompressionLevel is a helper struct to allow for easy implementation
// of the Leveller interface.
type CompressionLevel struct {
level int
minLevel int
maxLevel int
}
// Level returns the compression level
func (c *CompressionLevel) Level() int {
return c.level
}
// MinLevel returns the minimum supported compression level.
func (c *CompressionLevel) MinLevel() int {
return c.minLevel
}
// MaxLevel returns the maximum supported compression level.
func (c *CompressionLevel) MaxLevel() int {
return c.maxLevel
}
// SetLevel sets the compression level to l if it's within
// the bounds of c.MinLevel() and c.MaxLevel(), returning
// an error otherwise.
func (c *CompressionLevel) SetLevel(l int) error {
if inBound(l, c.minLevel, c.maxLevel) {
c.level = l
return nil
}
return fmt.Errorf("level %v out of range, supported levels: %v - %v", l, c.minLevel, c.maxLevel)
}
// NewFileCompressor returns a new FileCompressor
// instance, ready to compress and decompress files.
func NewFileCompressor(c CompressorDecompressor, method, extension string) *FileCompressor {
fC := &FileCompressor{
CompressorDecompressor: c,
Extension: extension,
Method: method,
}
return fC
}
// CompressFile reads the file from input and writes the
// compressed data to dest, using the Compress method defined
// by the Compressor interface.
func (f *FileCompressor) CompressFile(input string, dest string) error {
if f.CompressorDecompressor == nil {
return errNoCompressorDecompressor
}
if isExists(dest) {
return os.ErrExist
}
r, err := fs.Open(input)
if err != nil {
return err
}
defer r.Close()
w, err := fs.Create(dest)
if err != nil {
return err
}
defer w.Close()
return f.Compress(r, w)
}
// DecompressFile reads the compressed file from input
// and writes the decompressed data to dest, using the
// Decompress method defined by the Decompressor interface.
func (f *FileCompressor) DecompressFile(input string, dest string) error {
if f.CompressorDecompressor == nil {
return errNoCompressorDecompressor
}
if isExists(dest) {
return os.ErrExist
}
r, err := fs.Open(input)
if err != nil {
return err
}
defer r.Close()
w, err := fs.Create(dest)
if err != nil {
return err
}
defer w.Close()
return f.Decompress(r, w)
}
func isExists(file string) bool {
_, err := fs.Stat(file)
return !os.IsNotExist(err)
}