-
Notifications
You must be signed in to change notification settings - Fork 309
/
frameenc.go
137 lines (126 loc) · 3.33 KB
/
frameenc.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
// Copyright 2019+ Klaus Post. All rights reserved.
// License information can be found in the LICENSE file.
// Based on work by Yann Collet, released under BSD License.
package zstd
import (
"encoding/binary"
"fmt"
"io"
"math"
"math/bits"
)
type frameHeader struct {
ContentSize uint64
WindowSize uint32
SingleSegment bool
Checksum bool
DictID uint32
}
const maxHeaderSize = 14
func (f frameHeader) appendTo(dst []byte) ([]byte, error) {
dst = append(dst, frameMagic...)
var fhd uint8
if f.Checksum {
fhd |= 1 << 2
}
if f.SingleSegment {
fhd |= 1 << 5
}
var dictIDContent []byte
if f.DictID > 0 {
var tmp [4]byte
if f.DictID < 256 {
fhd |= 1
tmp[0] = uint8(f.DictID)
dictIDContent = tmp[:1]
} else if f.DictID < 1<<16 {
fhd |= 2
binary.LittleEndian.PutUint16(tmp[:2], uint16(f.DictID))
dictIDContent = tmp[:2]
} else {
fhd |= 3
binary.LittleEndian.PutUint32(tmp[:4], f.DictID)
dictIDContent = tmp[:4]
}
}
var fcs uint8
if f.ContentSize >= 256 {
fcs++
}
if f.ContentSize >= 65536+256 {
fcs++
}
if f.ContentSize >= 0xffffffff {
fcs++
}
fhd |= fcs << 6
dst = append(dst, fhd)
if !f.SingleSegment {
const winLogMin = 10
windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3
dst = append(dst, uint8(windowLog))
}
if f.DictID > 0 {
dst = append(dst, dictIDContent...)
}
switch fcs {
case 0:
if f.SingleSegment {
dst = append(dst, uint8(f.ContentSize))
}
// Unless SingleSegment is set, framessizes < 256 are nto stored.
case 1:
f.ContentSize -= 256
dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8))
case 2:
dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24))
case 3:
dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24),
uint8(f.ContentSize>>32), uint8(f.ContentSize>>40), uint8(f.ContentSize>>48), uint8(f.ContentSize>>56))
default:
panic("invalid fcs")
}
return dst, nil
}
const skippableFrameHeader = 4 + 4
// calcSkippableFrame will return a total size to be added for written
// to be divisible by multiple.
// The value will always be > skippableFrameHeader.
// The function will panic if written < 0 or wantMultiple <= 0.
func calcSkippableFrame(written, wantMultiple int64) int {
if wantMultiple <= 0 {
panic("wantMultiple <= 0")
}
if written < 0 {
panic("written < 0")
}
leftOver := written % wantMultiple
if leftOver == 0 {
return 0
}
toAdd := wantMultiple - leftOver
for toAdd < skippableFrameHeader {
toAdd += wantMultiple
}
return int(toAdd)
}
// skippableFrame will add a skippable frame with a total size of bytes.
// total should be >= skippableFrameHeader and < math.MaxUint32.
func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) {
if total == 0 {
return dst, nil
}
if total < skippableFrameHeader {
return dst, fmt.Errorf("requested skippable frame (%d) < 8", total)
}
if int64(total) > math.MaxUint32 {
return dst, fmt.Errorf("requested skippable frame (%d) > max uint32", total)
}
dst = append(dst, 0x50, 0x2a, 0x4d, 0x18)
f := uint32(total - skippableFrameHeader)
dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24))
start := len(dst)
dst = append(dst, make([]byte, f)...)
_, err := io.ReadFull(r, dst[start:])
return dst, err
}