Skip to content

Commit d40f154

Browse files
committed
quic: varint encoding and decoding
Functions to encode and decode QUIC variable-length integers (RFC 9000, Section 16), as well as a few other common operations. For golang/go#58547 Change-Id: I2a738e8798b8013a7b13d7c1e1385bf846c6c2cd Reviewed-on: https://go-review.googlesource.com/c/net/+/478258 Run-TryBot: Damien Neil <dneil@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Jonathan Amsterdam <jba@google.com>
1 parent 10d9069 commit d40f154

File tree

2 files changed

+368
-0
lines changed

2 files changed

+368
-0
lines changed

internal/quic/wire.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package quic
6+
7+
import "encoding/binary"
8+
9+
const maxVarintSize = 8
10+
11+
// consumeVarint parses a variable-length integer, reporting its length.
12+
// It returns a negative length upon an error.
13+
//
14+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-16
15+
func consumeVarint(b []byte) (v uint64, n int) {
16+
if len(b) < 1 {
17+
return 0, -1
18+
}
19+
b0 := b[0] & 0x3f
20+
switch b[0] >> 6 {
21+
case 0:
22+
return uint64(b0), 1
23+
case 1:
24+
if len(b) < 2 {
25+
return 0, -1
26+
}
27+
return uint64(b0)<<8 | uint64(b[1]), 2
28+
case 2:
29+
if len(b) < 4 {
30+
return 0, -1
31+
}
32+
return uint64(b0)<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3]), 4
33+
case 3:
34+
if len(b) < 8 {
35+
return 0, -1
36+
}
37+
return uint64(b0)<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7]), 8
38+
}
39+
return 0, -1
40+
}
41+
42+
// consumeVarint64 parses a variable-length integer as an int64.
43+
func consumeVarintInt64(b []byte) (v int64, n int) {
44+
u, n := consumeVarint(b)
45+
// QUIC varints are 62-bits large, so this conversion can never overflow.
46+
return int64(u), n
47+
}
48+
49+
// appendVarint appends a variable-length integer to b.
50+
//
51+
// https://www.rfc-editor.org/rfc/rfc9000.html#section-16
52+
func appendVarint(b []byte, v uint64) []byte {
53+
switch {
54+
case v <= 63:
55+
return append(b, byte(v))
56+
case v <= 16383:
57+
return append(b, (1<<6)|byte(v>>8), byte(v))
58+
case v <= 1073741823:
59+
return append(b, (2<<6)|byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
60+
case v <= 4611686018427387903:
61+
return append(b, (3<<6)|byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
62+
default:
63+
panic("varint too large")
64+
}
65+
}
66+
67+
// sizeVarint returns the size of the variable-length integer encoding of f.
68+
func sizeVarint(v uint64) int {
69+
switch {
70+
case v <= 63:
71+
return 1
72+
case v <= 16383:
73+
return 2
74+
case v <= 1073741823:
75+
return 4
76+
case v <= 4611686018427387903:
77+
return 8
78+
default:
79+
panic("varint too large")
80+
}
81+
}
82+
83+
// consumeUint32 parses a 32-bit fixed-length, big-endian integer, reporting its length.
84+
// It returns a negative length upon an error.
85+
func consumeUint32(b []byte) (uint32, int) {
86+
if len(b) < 4 {
87+
return 0, -1
88+
}
89+
return binary.BigEndian.Uint32(b), 4
90+
}
91+
92+
// consumeUint64 parses a 64-bit fixed-length, big-endian integer, reporting its length.
93+
// It returns a negative length upon an error.
94+
func consumeUint64(b []byte) (uint64, int) {
95+
if len(b) < 8 {
96+
return 0, -1
97+
}
98+
return binary.BigEndian.Uint64(b), 8
99+
}
100+
101+
// consumeUint8Bytes parses a sequence of bytes prefixed with an 8-bit length,
102+
// reporting the total number of bytes consumed.
103+
// It returns a negative length upon an error.
104+
func consumeUint8Bytes(b []byte) ([]byte, int) {
105+
if len(b) < 1 {
106+
return nil, -1
107+
}
108+
size := int(b[0])
109+
const n = 1
110+
if size > len(b[n:]) {
111+
return nil, -1
112+
}
113+
return b[n:][:size], size + n
114+
}
115+
116+
// appendUint8Bytes appends a sequence of bytes prefixed by an 8-bit length.
117+
func appendUint8Bytes(b, v []byte) []byte {
118+
if len(v) > 0xff {
119+
panic("uint8-prefixed bytes too large")
120+
}
121+
b = append(b, uint8(len(v)))
122+
b = append(b, v...)
123+
return b
124+
}
125+
126+
// consumeVarintBytes parses a sequence of bytes preceded by a variable-length integer length,
127+
// reporting the total number of bytes consumed.
128+
// It returns a negative length upon an error.
129+
func consumeVarintBytes(b []byte) ([]byte, int) {
130+
size, n := consumeVarint(b)
131+
if n < 0 {
132+
return nil, -1
133+
}
134+
if size > uint64(len(b[n:])) {
135+
return nil, -1
136+
}
137+
return b[n:][:size], int(size) + n
138+
}
139+
140+
// appendVarintBytes appends a sequence of bytes prefixed by a variable-length integer length.
141+
func appendVarintBytes(b, v []byte) []byte {
142+
b = appendVarint(b, uint64(len(v)))
143+
b = append(b, v...)
144+
return b
145+
}

internal/quic/wire_test.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package quic
6+
7+
import (
8+
"bytes"
9+
"testing"
10+
)
11+
12+
func TestConsumeVarint(t *testing.T) {
13+
for _, test := range []struct {
14+
b []byte
15+
want uint64
16+
wantLen int
17+
}{
18+
{[]byte{0x00}, 0, 1},
19+
{[]byte{0x3f}, 63, 1},
20+
{[]byte{0x40, 0x00}, 0, 2},
21+
{[]byte{0x7f, 0xff}, 16383, 2},
22+
{[]byte{0x80, 0x00, 0x00, 0x00}, 0, 4},
23+
{[]byte{0xbf, 0xff, 0xff, 0xff}, 1073741823, 4},
24+
{[]byte{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0, 8},
25+
{[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 4611686018427387903, 8},
26+
// Example cases from https://www.rfc-editor.org/rfc/rfc9000.html#section-a.1
27+
{[]byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}, 151288809941952652, 8},
28+
{[]byte{0x9d, 0x7f, 0x3e, 0x7d}, 494878333, 4},
29+
{[]byte{0x7b, 0xbd}, 15293, 2},
30+
{[]byte{0x25}, 37, 1},
31+
{[]byte{0x40, 0x25}, 37, 2},
32+
} {
33+
got, gotLen := consumeVarint(test.b)
34+
if got != test.want || gotLen != test.wantLen {
35+
t.Errorf("consumeVarint(%x) = %v, %v; want %v, %v", test.b, got, gotLen, test.want, test.wantLen)
36+
}
37+
// Extra data in the buffer is ignored.
38+
b := append(test.b, 0)
39+
got, gotLen = consumeVarint(b)
40+
if got != test.want || gotLen != test.wantLen {
41+
t.Errorf("consumeVarint(%x) = %v, %v; want %v, %v", b, got, gotLen, test.want, test.wantLen)
42+
}
43+
// Short buffer results in an error.
44+
for i := 1; i <= len(test.b); i++ {
45+
b = test.b[:len(test.b)-i]
46+
got, gotLen = consumeVarint(b)
47+
if got != 0 || gotLen >= 0 {
48+
t.Errorf("consumeVarint(%x) = %v, %v; want 0, -1", b, got, gotLen)
49+
}
50+
}
51+
}
52+
}
53+
54+
func TestAppendVarint(t *testing.T) {
55+
for _, test := range []struct {
56+
v uint64
57+
want []byte
58+
}{
59+
{0, []byte{0x00}},
60+
{63, []byte{0x3f}},
61+
{16383, []byte{0x7f, 0xff}},
62+
{1073741823, []byte{0xbf, 0xff, 0xff, 0xff}},
63+
{4611686018427387903, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
64+
// Example cases from https://www.rfc-editor.org/rfc/rfc9000.html#section-a.1
65+
{151288809941952652, []byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}},
66+
{494878333, []byte{0x9d, 0x7f, 0x3e, 0x7d}},
67+
{15293, []byte{0x7b, 0xbd}},
68+
{37, []byte{0x25}},
69+
} {
70+
got := appendVarint([]byte{}, test.v)
71+
if !bytes.Equal(got, test.want) {
72+
t.Errorf("AppendVarint(nil, %v) = %x, want %x", test.v, got, test.want)
73+
}
74+
if gotLen, wantLen := sizeVarint(test.v), len(got); gotLen != wantLen {
75+
t.Errorf("SizeVarint(%v) = %v, want %v", test.v, gotLen, wantLen)
76+
}
77+
}
78+
}
79+
80+
func TestConsumeUint32(t *testing.T) {
81+
for _, test := range []struct {
82+
b []byte
83+
want uint32
84+
wantLen int
85+
}{
86+
{[]byte{0x01, 0x02, 0x03, 0x04}, 0x01020304, 4},
87+
{[]byte{0x01, 0x02, 0x03}, 0, -1},
88+
} {
89+
if got, n := consumeUint32(test.b); got != test.want || n != test.wantLen {
90+
t.Errorf("consumeUint32(%x) = %v, %v; want %v, %v", test.b, got, n, test.want, test.wantLen)
91+
}
92+
}
93+
}
94+
95+
func TestConsumeUint64(t *testing.T) {
96+
for _, test := range []struct {
97+
b []byte
98+
want uint64
99+
wantLen int
100+
}{
101+
{[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 0x0102030405060708, 8},
102+
{[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 0, -1},
103+
} {
104+
if got, n := consumeUint64(test.b); got != test.want || n != test.wantLen {
105+
t.Errorf("consumeUint32(%x) = %v, %v; want %v, %v", test.b, got, n, test.want, test.wantLen)
106+
}
107+
}
108+
}
109+
110+
func TestConsumeVarintBytes(t *testing.T) {
111+
for _, test := range []struct {
112+
b []byte
113+
want []byte
114+
wantLen int
115+
}{
116+
{[]byte{0x00}, []byte{}, 1},
117+
{[]byte{0x40, 0x00}, []byte{}, 2},
118+
{[]byte{0x04, 0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, 5},
119+
{[]byte{0x40, 0x04, 0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, 6},
120+
} {
121+
got, gotLen := consumeVarintBytes(test.b)
122+
if !bytes.Equal(got, test.want) || gotLen != test.wantLen {
123+
t.Errorf("consumeVarintBytes(%x) = {%x}, %v; want {%x}, %v", test.b, got, gotLen, test.want, test.wantLen)
124+
}
125+
// Extra data in the buffer is ignored.
126+
b := append(test.b, 0)
127+
got, gotLen = consumeVarintBytes(b)
128+
if !bytes.Equal(got, test.want) || gotLen != test.wantLen {
129+
t.Errorf("consumeVarintBytes(%x) = {%x}, %v; want {%x}, %v", b, got, gotLen, test.want, test.wantLen)
130+
}
131+
// Short buffer results in an error.
132+
for i := 1; i <= len(test.b); i++ {
133+
b = test.b[:len(test.b)-i]
134+
got, gotLen := consumeVarintBytes(b)
135+
if len(got) > 0 || gotLen > 0 {
136+
t.Errorf("consumeVarintBytes(%x) = {%x}, %v; want {}, -1", b, got, gotLen)
137+
}
138+
}
139+
140+
}
141+
}
142+
143+
func TestConsumeVarintBytesErrors(t *testing.T) {
144+
for _, b := range [][]byte{
145+
{0x01},
146+
{0x40, 0x01},
147+
} {
148+
got, gotLen := consumeVarintBytes(b)
149+
if len(got) > 0 || gotLen > 0 {
150+
t.Errorf("consumeVarintBytes(%x) = {%x}, %v; want {}, -1", b, got, gotLen)
151+
}
152+
}
153+
}
154+
155+
func TestConsumeUint8Bytes(t *testing.T) {
156+
for _, test := range []struct {
157+
b []byte
158+
want []byte
159+
wantLen int
160+
}{
161+
{[]byte{0x00}, []byte{}, 1},
162+
{[]byte{0x01, 0x00}, []byte{0x00}, 2},
163+
{[]byte{0x04, 0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, 5},
164+
} {
165+
got, gotLen := consumeUint8Bytes(test.b)
166+
if !bytes.Equal(got, test.want) || gotLen != test.wantLen {
167+
t.Errorf("consumeUint8Bytes(%x) = {%x}, %v; want {%x}, %v", test.b, got, gotLen, test.want, test.wantLen)
168+
}
169+
// Extra data in the buffer is ignored.
170+
b := append(test.b, 0)
171+
got, gotLen = consumeUint8Bytes(b)
172+
if !bytes.Equal(got, test.want) || gotLen != test.wantLen {
173+
t.Errorf("consumeUint8Bytes(%x) = {%x}, %v; want {%x}, %v", b, got, gotLen, test.want, test.wantLen)
174+
}
175+
// Short buffer results in an error.
176+
for i := 1; i <= len(test.b); i++ {
177+
b = test.b[:len(test.b)-i]
178+
got, gotLen := consumeUint8Bytes(b)
179+
if len(got) > 0 || gotLen > 0 {
180+
t.Errorf("consumeUint8Bytes(%x) = {%x}, %v; want {}, -1", b, got, gotLen)
181+
}
182+
}
183+
184+
}
185+
}
186+
187+
func TestConsumeUint8BytesErrors(t *testing.T) {
188+
for _, b := range [][]byte{
189+
{0x01},
190+
{0x04, 0x01, 0x02, 0x03},
191+
} {
192+
got, gotLen := consumeUint8Bytes(b)
193+
if len(got) > 0 || gotLen > 0 {
194+
t.Errorf("consumeUint8Bytes(%x) = {%x}, %v; want {}, -1", b, got, gotLen)
195+
}
196+
}
197+
}
198+
199+
func TestAppendUint8Bytes(t *testing.T) {
200+
var got []byte
201+
got = appendUint8Bytes(got, []byte{})
202+
got = appendUint8Bytes(got, []byte{0xaa, 0xbb})
203+
want := []byte{
204+
0x00,
205+
0x02, 0xaa, 0xbb,
206+
}
207+
if !bytes.Equal(got, want) {
208+
t.Errorf("appendUint8Bytes {}, {aabb} = {%x}; want {%x}", got, want)
209+
}
210+
}
211+
212+
func TestAppendVarintBytes(t *testing.T) {
213+
var got []byte
214+
got = appendVarintBytes(got, []byte{})
215+
got = appendVarintBytes(got, []byte{0xaa, 0xbb})
216+
want := []byte{
217+
0x00,
218+
0x02, 0xaa, 0xbb,
219+
}
220+
if !bytes.Equal(got, want) {
221+
t.Errorf("appendVarintBytes {}, {aabb} = {%x}; want {%x}", got, want)
222+
}
223+
}

0 commit comments

Comments
 (0)