-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
truncated.go
207 lines (189 loc) · 5.71 KB
/
truncated.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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package tlv
import (
"encoding/binary"
"errors"
"io"
)
// ErrTUintNotMinimal signals that decoding a truncated uint failed because the
// value was not minimally encoded.
var ErrTUintNotMinimal = errors.New("truncated uint not minimally encoded")
// numLeadingZeroBytes16 computes the number of leading zeros for a uint16.
func numLeadingZeroBytes16(v uint16) uint64 {
switch {
case v == 0:
return 2
case v&0xff00 == 0:
return 1
default:
return 0
}
}
// SizeTUint16 returns the number of bytes remaining in a uint16 after
// truncating the leading zeros.
func SizeTUint16(v uint16) uint64 {
return 2 - numLeadingZeroBytes16(v)
}
// ETUint16 is an Encoder for truncated uint16 values, where leading zeros will
// be omitted. An error is returned if val is not a *uint16.
func ETUint16(w io.Writer, val interface{}, buf *[8]byte) error {
if t, ok := val.(*uint16); ok {
binary.BigEndian.PutUint16(buf[:2], *t)
numZeros := numLeadingZeroBytes16(*t)
_, err := w.Write(buf[numZeros:2])
return err
}
return NewTypeForEncodingErr(val, "uint16")
}
// ETUint16T is an Encoder for truncated uint16 values, where leading zeros will
// be omitted. An error is returned if val is not a *uint16.
func ETUint16T(w io.Writer, val uint16, buf *[8]byte) error {
binary.BigEndian.PutUint16(buf[:2], val)
numZeros := numLeadingZeroBytes16(val)
_, err := w.Write(buf[numZeros:2])
return err
}
// DTUint16 is an Decoder for truncated uint16 values, where leading zeros will
// be resurrected. An error is returned if val is not a *uint16.
func DTUint16(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
if t, ok := val.(*uint16); ok && l <= 2 {
_, err := io.ReadFull(r, buf[2-l:2])
if err != nil {
return err
}
zero(buf[:2-l])
*t = binary.BigEndian.Uint16(buf[:2])
if 2-numLeadingZeroBytes16(*t) != l {
return ErrTUintNotMinimal
}
return nil
}
return NewTypeForDecodingErr(val, "uint16", l, 2)
}
// numLeadingZeroBytes32 computes the number of leading zeros for a uint32.
func numLeadingZeroBytes32(v uint32) uint64 {
switch {
case v == 0:
return 4
case v&0xffffff00 == 0:
return 3
case v&0xffff0000 == 0:
return 2
case v&0xff000000 == 0:
return 1
default:
return 0
}
}
// SizeTUint32 returns the number of bytes remaining in a uint32 after
// truncating the leading zeros.
func SizeTUint32(v uint32) uint64 {
return 4 - numLeadingZeroBytes32(v)
}
// ETUint32 is an Encoder for truncated uint32 values, where leading zeros will
// be omitted. An error is returned if val is not a *uint32.
func ETUint32(w io.Writer, val interface{}, buf *[8]byte) error {
if t, ok := val.(*uint32); ok {
binary.BigEndian.PutUint32(buf[:4], *t)
numZeros := numLeadingZeroBytes32(*t)
_, err := w.Write(buf[numZeros:4])
return err
}
return NewTypeForEncodingErr(val, "uint32")
}
// ETUint32T is an Encoder for truncated uint32 values, where leading zeros will
// be omitted. An error is returned if val is not a *uint32.
func ETUint32T(w io.Writer, val uint32, buf *[8]byte) error {
binary.BigEndian.PutUint32(buf[:4], val)
numZeros := numLeadingZeroBytes32(val)
_, err := w.Write(buf[numZeros:4])
return err
}
// DTUint32 is an Decoder for truncated uint32 values, where leading zeros will
// be resurrected. An error is returned if val is not a *uint32.
func DTUint32(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
if t, ok := val.(*uint32); ok && l <= 4 {
_, err := io.ReadFull(r, buf[4-l:4])
if err != nil {
return err
}
zero(buf[:4-l])
*t = binary.BigEndian.Uint32(buf[:4])
if 4-numLeadingZeroBytes32(*t) != l {
return ErrTUintNotMinimal
}
return nil
}
return NewTypeForDecodingErr(val, "uint32", l, 4)
}
// numLeadingZeroBytes64 computes the number of leading zeros for a uint64.
//
// TODO(conner): optimize using unrolled binary search
func numLeadingZeroBytes64(v uint64) uint64 {
switch {
case v == 0:
return 8
case v&0xffffffffffffff00 == 0:
return 7
case v&0xffffffffffff0000 == 0:
return 6
case v&0xffffffffff000000 == 0:
return 5
case v&0xffffffff00000000 == 0:
return 4
case v&0xffffff0000000000 == 0:
return 3
case v&0xffff000000000000 == 0:
return 2
case v&0xff00000000000000 == 0:
return 1
default:
return 0
}
}
// SizeTUint64 returns the number of bytes remaining in a uint64 after
// truncating the leading zeros.
func SizeTUint64(v uint64) uint64 {
return 8 - numLeadingZeroBytes64(v)
}
// ETUint64 is an Encoder for truncated uint64 values, where leading zeros will
// be omitted. An error is returned if val is not a *uint64.
func ETUint64(w io.Writer, val interface{}, buf *[8]byte) error {
if t, ok := val.(*uint64); ok {
binary.BigEndian.PutUint64(buf[:], *t)
numZeros := numLeadingZeroBytes64(*t)
_, err := w.Write(buf[numZeros:])
return err
}
return NewTypeForEncodingErr(val, "uint64")
}
// ETUint64T is an Encoder for truncated uint64 values, where leading zeros will
// be omitted. An error is returned if val is not a *uint64.
func ETUint64T(w io.Writer, val uint64, buf *[8]byte) error {
binary.BigEndian.PutUint64(buf[:], val)
numZeros := numLeadingZeroBytes64(val)
_, err := w.Write(buf[numZeros:])
return err
}
// DTUint64 is an Decoder for truncated uint64 values, where leading zeros will
// be resurrected. An error is returned if val is not a *uint64.
func DTUint64(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
if t, ok := val.(*uint64); ok && l <= 8 {
_, err := io.ReadFull(r, buf[8-l:])
if err != nil {
return err
}
zero(buf[:8-l])
*t = binary.BigEndian.Uint64(buf[:])
if 8-numLeadingZeroBytes64(*t) != l {
return ErrTUintNotMinimal
}
return nil
}
return NewTypeForDecodingErr(val, "uint64", l, 8)
}
// zero clears the passed byte slice.
func zero(b []byte) {
for i := range b {
b[i] = 0x00
}
}