-
-
Notifications
You must be signed in to change notification settings - Fork 116
/
gzip.go
74 lines (60 loc) · 1.69 KB
/
gzip.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
package proto
import (
"bytes"
"compress/gzip"
"io"
"golang.org/x/xerrors"
"github.com/gotd/td/bin"
)
// GZIP represents a Packed Object.
//
// Used to replace any other object (or rather, a serialization thereof)
// with its archived (gzipped) representation.
type GZIP struct {
Data []byte
}
// GZIPTypeID is TL type id of GZIP.
const GZIPTypeID = 0x3072cfa1
// Encode implements bin.Encoder.
func (g GZIP) Encode(b *bin.Buffer) error {
b.PutID(GZIPTypeID)
// Writing compressed data to buf.
buf := new(bytes.Buffer)
w := gzip.NewWriter(buf)
if _, err := io.Copy(w, bytes.NewReader(g.Data)); err != nil {
return xerrors.Errorf("compress: %w", err)
}
if err := w.Close(); err != nil {
return xerrors.Errorf("close: %w", err)
}
// Writing compressed data as bytes.
b.PutBytes(buf.Bytes())
return nil
}
// Decode implements bin.Decoder.
func (g *GZIP) Decode(b *bin.Buffer) error {
if err := b.ConsumeID(GZIPTypeID); err != nil {
return err
}
buf, err := b.Bytes()
if err != nil {
return err
}
r, err := gzip.NewReader(bytes.NewReader(buf))
if err != nil {
return xerrors.Errorf("gzip error: %w", err)
}
defer func() { _ = r.Close() }()
// Apply mitigation for reading too much data which can result in OOM.
const maxUncompressedSize = 1024 * 1024 * 10 // 10 mb
// TODO(ernado): fail explicitly if limit is reached
// Currently we just return nil, but it is better than failing with OOM.
if g.Data, err = io.ReadAll(io.LimitReader(r, maxUncompressedSize)); err != nil {
return xerrors.Errorf("decompress: %w", err)
}
if err := r.Close(); err != nil {
// This will verify checksum only if limit is not reached.
return xerrors.Errorf("checksum: %w", err)
}
return nil
}