package quicvarint import ( "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 len := 1 << ((firstByte & 0xc0) >> 6) b1 := firstByte & (0xff - 0xc0) if len == 1 { return uint64(b1), nil } b2, err := r.ReadByte() if err != nil { return 0, err } if len == 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 len == 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 } // Write writes i in the QUIC varint format to w. func Write(w Writer, i uint64) { if i <= maxVarInt1 { w.WriteByte(uint8(i)) } else if i <= maxVarInt2 { w.Write([]byte{uint8(i>>8) | 0x40, uint8(i)}) } else if i <= maxVarInt4 { w.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}) } else if i <= maxVarInt8 { w.Write([]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), }) } else { panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) } } // WriteWithLen writes i in the QUIC varint format with the desired length to w. func WriteWithLen(w Writer, i uint64, length protocol.ByteCount) { if length != 1 && length != 2 && length != 4 && length != 8 { panic("invalid varint length") } l := Len(i) if l == length { Write(w, i) return } if l > length { panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length)) } if length == 2 { w.WriteByte(0b01000000) } else if length == 4 { w.WriteByte(0b10000000) } else if length == 8 { w.WriteByte(0b11000000) } for j := protocol.ByteCount(1); j < length-l; j++ { w.WriteByte(0) } for j := protocol.ByteCount(0); j < l; j++ { w.WriteByte(uint8(i >> (8 * (l - 1 - j)))) } } // Len determines the number of bytes that will be needed to write the number i. func Len(i uint64) protocol.ByteCount { 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}) }