github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/quicvarint/varint.go (about) 1 package quicvarint 2 3 import ( 4 "fmt" 5 "io" 6 ) 7 8 // taken from the QUIC draft 9 const ( 10 // Min is the minimum value allowed for a QUIC varint. 11 Min = 0 12 13 // Max is the maximum allowed value for a QUIC varint (2^62-1). 14 Max = maxVarInt8 15 16 maxVarInt1 = 63 17 maxVarInt2 = 16383 18 maxVarInt4 = 1073741823 19 maxVarInt8 = 4611686018427387903 20 ) 21 22 // Read reads a number in the QUIC varint format from r. 23 func Read(r io.ByteReader) (uint64, error) { 24 firstByte, err := r.ReadByte() 25 if err != nil { 26 return 0, err 27 } 28 // the first two bits of the first byte encode the length 29 l := 1 << ((firstByte & 0xc0) >> 6) 30 b1 := firstByte & (0xff - 0xc0) 31 if l == 1 { 32 return uint64(b1), nil 33 } 34 b2, err := r.ReadByte() 35 if err != nil { 36 return 0, err 37 } 38 if l == 2 { 39 return uint64(b2) + uint64(b1)<<8, nil 40 } 41 b3, err := r.ReadByte() 42 if err != nil { 43 return 0, err 44 } 45 b4, err := r.ReadByte() 46 if err != nil { 47 return 0, err 48 } 49 if l == 4 { 50 return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil 51 } 52 b5, err := r.ReadByte() 53 if err != nil { 54 return 0, err 55 } 56 b6, err := r.ReadByte() 57 if err != nil { 58 return 0, err 59 } 60 b7, err := r.ReadByte() 61 if err != nil { 62 return 0, err 63 } 64 b8, err := r.ReadByte() 65 if err != nil { 66 return 0, err 67 } 68 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 69 } 70 71 // Parse reads a number in the QUIC varint format. 72 // It returns the number of bytes consumed. 73 func Parse(b []byte) (uint64 /* value */, int /* bytes consumed */, error) { 74 if len(b) == 0 { 75 return 0, 0, io.EOF 76 } 77 firstByte := b[0] 78 // the first two bits of the first byte encode the length 79 l := 1 << ((firstByte & 0xc0) >> 6) 80 if len(b) < l { 81 return 0, 0, io.ErrUnexpectedEOF 82 } 83 b0 := firstByte & (0xff - 0xc0) 84 if l == 1 { 85 return uint64(b0), 1, nil 86 } 87 if l == 2 { 88 return uint64(b[1]) + uint64(b0)<<8, 2, nil 89 } 90 if l == 4 { 91 return uint64(b[3]) + uint64(b[2])<<8 + uint64(b[1])<<16 + uint64(b0)<<24, 4, nil 92 } 93 return uint64(b[7]) + uint64(b[6])<<8 + uint64(b[5])<<16 + uint64(b[4])<<24 + uint64(b[3])<<32 + uint64(b[2])<<40 + uint64(b[1])<<48 + uint64(b0)<<56, 8, nil 94 } 95 96 // Append appends i in the QUIC varint format. 97 func Append(b []byte, i uint64) []byte { 98 if i <= maxVarInt1 { 99 return append(b, uint8(i)) 100 } 101 if i <= maxVarInt2 { 102 return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...) 103 } 104 if i <= maxVarInt4 { 105 return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...) 106 } 107 if i <= maxVarInt8 { 108 return append(b, []byte{ 109 uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32), 110 uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i), 111 }...) 112 } 113 panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) 114 } 115 116 // AppendWithLen append i in the QUIC varint format with the desired length. 117 func AppendWithLen(b []byte, i uint64, length int) []byte { 118 if length != 1 && length != 2 && length != 4 && length != 8 { 119 panic("invalid varint length") 120 } 121 l := Len(i) 122 if l == length { 123 return Append(b, i) 124 } 125 if l > length { 126 panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length)) 127 } 128 if length == 2 { 129 b = append(b, 0b01000000) 130 } else if length == 4 { 131 b = append(b, 0b10000000) 132 } else if length == 8 { 133 b = append(b, 0b11000000) 134 } 135 for j := 1; j < length-l; j++ { 136 b = append(b, 0) 137 } 138 for j := 0; j < l; j++ { 139 b = append(b, uint8(i>>(8*(l-1-j)))) 140 } 141 return b 142 } 143 144 // Len determines the number of bytes that will be needed to write the number i. 145 func Len(i uint64) int { 146 if i <= maxVarInt1 { 147 return 1 148 } 149 if i <= maxVarInt2 { 150 return 2 151 } 152 if i <= maxVarInt4 { 153 return 4 154 } 155 if i <= maxVarInt8 { 156 return 8 157 } 158 // Don't use a fmt.Sprintf here to format the error message. 159 // The function would then exceed the inlining budget. 160 panic(struct { 161 message string 162 num uint64 163 }{"value doesn't fit into 62 bits: ", i}) 164 }