/
convert.go
63 lines (52 loc) · 1.43 KB
/
convert.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
package multigz
import (
"bytes"
"errors"
"io"
"github.com/klauspost/compress/gzip"
)
type ConvertMode int
const (
ConvertNormal ConvertMode = iota
ConvertRsyncable
)
var (
errInvalidConvertMode = errors.New("invalid convert mode specified")
)
// Convert a whole gzip file into a multi-gzip file. mode can be used to
// select between using a normal writer, or the rsync-friendly writer.
func Convert(w io.Writer, r io.Reader, mode ConvertMode) error {
// We want to match the same algorithm originally used, to preserve
// the rsyncable effect. The gzip library doesn't expose this data in the
// headers, so we parse it. We don't do additional checks here, as if the
// header is broken, gzip.NewReader will error out just afterwards.
var gzhead [10]byte
if _, err := io.ReadFull(r, gzhead[:]); err != nil {
return err
}
comprlevel := gzip.DefaultCompression
if gzhead[8] == 0x2 {
comprlevel = gzip.BestCompression
} else if gzhead[8] == 0x4 {
comprlevel = gzip.BestSpeed
}
var oz io.WriteCloser
switch mode {
case ConvertNormal:
oz, _ = NewWriterLevel(w, comprlevel, DefaultBlockSize)
case ConvertRsyncable:
oz, _ = NewWriterLevelRsyncable(w, comprlevel)
default:
return errInvalidConvertMode
}
defer oz.Close()
fz, err := gzip.NewReader(io.MultiReader(bytes.NewReader(gzhead[:]), r))
if err != nil {
return nil
}
defer fz.Close()
if _, err = io.Copy(oz, fz); err != nil {
return err
}
return nil
}