forked from quic-go/quic-go
174 lines
4.0 KiB
Go
174 lines
4.0 KiB
Go
package quicvarint
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// taken from the QUIC draft
|
|
const (
|
|
// Min is the minimum value allowed for a QUIC varint.
|
|
Min = 0
|
|
|
|
// Max is the maximum allowed value for a QUIC varint (2^62-1).
|
|
Max = maxVarInt8
|
|
|
|
maxVarInt1 = 63
|
|
maxVarInt2 = 16383
|
|
maxVarInt4 = 1073741823
|
|
maxVarInt8 = 4611686018427387903
|
|
)
|
|
|
|
// Read reads a number in the QUIC varint format from r.
|
|
func Read(r io.ByteReader) (uint64, error) {
|
|
firstByte, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
// the first two bits of the first byte encode the length
|
|
l := 1 << ((firstByte & 0xc0) >> 6)
|
|
b1 := firstByte & (0xff - 0xc0)
|
|
if l == 1 {
|
|
return uint64(b1), nil
|
|
}
|
|
b2, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if l == 2 {
|
|
return uint64(b2) + uint64(b1)<<8, nil
|
|
}
|
|
b3, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
b4, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if l == 4 {
|
|
return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil
|
|
}
|
|
b5, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
b6, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
b7, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
b8, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil
|
|
}
|
|
|
|
// Parse reads a number in the QUIC varint format.
|
|
// It returns the number of bytes consumed.
|
|
func Parse(b []byte) (uint64 /* value */, int /* bytes consumed */, error) {
|
|
if len(b) == 0 {
|
|
return 0, 0, io.EOF
|
|
}
|
|
|
|
first := b[0]
|
|
switch first >> 6 {
|
|
case 0: // 1-byte encoding: 00xxxxxx
|
|
return uint64(first & 0b00111111), 1, nil
|
|
case 1: // 2-byte encoding: 01xxxxxx
|
|
if len(b) < 2 {
|
|
return 0, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return uint64(b[1]) | uint64(first&0b00111111)<<8, 2, nil
|
|
case 2: // 4-byte encoding: 10xxxxxx
|
|
if len(b) < 4 {
|
|
return 0, 0, io.ErrUnexpectedEOF
|
|
}
|
|
return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(first&0b00111111)<<24, 4, nil
|
|
case 3: // 8-byte encoding: 00xxxxxx
|
|
if len(b) < 8 {
|
|
return 0, 0, io.ErrUnexpectedEOF
|
|
}
|
|
// binary.BigEndian.Uint64 only reads the first 8 bytes. Passing the full slice avoids slicing overhead.
|
|
return binary.BigEndian.Uint64(b) & 0x3fffffffffffffff, 8, nil
|
|
}
|
|
|
|
panic("unreachable")
|
|
}
|
|
|
|
// Append appends i in the QUIC varint format.
|
|
func Append(b []byte, i uint64) []byte {
|
|
if i <= maxVarInt1 {
|
|
return append(b, uint8(i))
|
|
}
|
|
if i <= maxVarInt2 {
|
|
return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...)
|
|
}
|
|
if i <= maxVarInt4 {
|
|
return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...)
|
|
}
|
|
if i <= maxVarInt8 {
|
|
return append(b, []byte{
|
|
uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
|
|
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
|
|
}...)
|
|
}
|
|
panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
|
|
}
|
|
|
|
// AppendWithLen append i in the QUIC varint format with the desired length.
|
|
func AppendWithLen(b []byte, i uint64, length int) []byte {
|
|
if length != 1 && length != 2 && length != 4 && length != 8 {
|
|
panic("invalid varint length")
|
|
}
|
|
l := Len(i)
|
|
if l == length {
|
|
return Append(b, i)
|
|
}
|
|
if l > length {
|
|
panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length))
|
|
}
|
|
switch length {
|
|
case 2:
|
|
b = append(b, 0b01000000)
|
|
case 4:
|
|
b = append(b, 0b10000000)
|
|
case 8:
|
|
b = append(b, 0b11000000)
|
|
}
|
|
for range length - l - 1 {
|
|
b = append(b, 0)
|
|
}
|
|
for j := range l {
|
|
b = append(b, uint8(i>>(8*(l-1-j))))
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Len determines the number of bytes that will be needed to write the number i.
|
|
func Len(i uint64) int {
|
|
if i <= maxVarInt1 {
|
|
return 1
|
|
}
|
|
if i <= maxVarInt2 {
|
|
return 2
|
|
}
|
|
if i <= maxVarInt4 {
|
|
return 4
|
|
}
|
|
if i <= maxVarInt8 {
|
|
return 8
|
|
}
|
|
// Don't use a fmt.Sprintf here to format the error message.
|
|
// The function would then exceed the inlining budget.
|
|
panic(struct {
|
|
message string
|
|
num uint64
|
|
}{"value doesn't fit into 62 bits: ", i})
|
|
}
|