forked from wal-g/wal-g
-
Notifications
You must be signed in to change notification settings - Fork 0
/
decompress.go
150 lines (126 loc) · 3.28 KB
/
decompress.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
package walg
import (
"encoding/binary"
"github.com/pierrec/lz4"
"github.com/pkg/errors"
"github.com/rasky/go-lzo"
"io"
"regexp"
)
// RaskyReader handles cases when the Rasky lzo package crashes.
// Occurs if byte size is too small (1-5).
type RaskyReader struct {
R io.Reader
}
// Read ensures all bytes are get read for Rasky package.
func (r *RaskyReader) Read(p []byte) (int, error) {
return io.ReadFull(r.R, p)
}
// Uncompressed is used to log compression ratio.
var Uncompressed uint32
// Compressed is used to log compression ratio.
var Compressed uint32
// CheckType grabs the file extension from PATH.
func CheckType(path string) string {
re := regexp.MustCompile(`\.([^\.]+)$`)
f := re.FindString(path)
if f != "" {
return f[1:]
}
return ""
}
// DecompressLzo decompresses an .lzo file. Returns the first error
// encountered.
func DecompressLzo(d io.Writer, s io.Reader) error {
skip := 33
sk := make([]byte, skip)
n, err := io.ReadFull(s, sk)
if n != len(sk) {
return errors.New("DecompressLzo: did not fill skip")
}
if err != nil {
return errors.Wrap(err, "DecompressLzo: read failed")
}
var fileNameLen uint8
binary.Read(s, binary.BigEndian, &fileNameLen)
fileName := make([]byte, fileNameLen)
n, err = io.ReadFull(s, fileName)
if n != len(fileName) {
return errors.New("DecompressLzo: did not fill filename")
}
if err != nil {
return errors.Wrap(err, "DecompressLzo: read failed")
}
fileComment := make([]byte, 4)
n, err = io.ReadFull(s, fileComment)
if n != len(fileComment) {
return errors.New("DecompressLzo: did not fill fileComment")
}
if err != nil {
return errors.Wrap(err, "DecompressLzo: read failed")
}
var uncom uint32
var com uint32
var check uint32
for {
err := binary.Read(s, binary.BigEndian, &uncom)
if uncom == 0 {
break
}
if err != nil {
return errors.Wrap(err, "DecompressLzo: read failed")
}
err = binary.Read(s, binary.BigEndian, &com)
if err != nil {
return errors.Wrap(err, "DecompressLzo: read failed")
}
Uncompressed += uncom
Compressed += com
err = binary.Read(s, binary.BigEndian, &check)
if err != nil {
return errors.Wrap(err, "DecompressLzo: read failed")
}
if uncom <= com {
n, err := io.CopyN(d, s, int64(com))
if n != int64(com) {
return errors.New("DecompressLzo: copy failed")
}
if err != nil {
return errors.Wrap(err, "DecompressLzo: copy failed")
}
} else {
ras := &RaskyReader{
R: s,
}
out, err := lzo.Decompress1X(ras, int(com), int(uncom))
if err != nil {
return errors.Wrap(err, "DecompressLzo: decompress lzo failed")
}
if len(out) != int(uncom) {
return errors.New("DecompressLzo: out bytes do not equal uncompressed")
}
n, err = d.Write(out)
if n != len(out) {
return errors.New("DecompressLzo: write to pipe failed")
}
if err != nil {
return errors.Wrap(err, "DecompressLzo: write to pipe failed")
}
}
}
return nil
}
// DecompressLz4 decompresses a .lz4 file. Returns an error upon failure.
func DecompressLz4(d io.Writer, s io.Reader) (int64, error) {
lz := lz4.NewReader(s)
n, err := lz.WriteTo(d)
if err != nil {
return n, errors.Wrap(err, "DecompressLz4: lz4 write failed")
}
return n, nil
}
// Compose io.ReadCloser from two parts
type ReadCascadeClose struct {
io.Reader
io.Closer
}