github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/quicvarint/varint_test.go (about) 1 package quicvarint 2 3 import ( 4 "bytes" 5 "io" 6 "testing" 7 8 "golang.org/x/exp/rand" 9 10 . "github.com/onsi/ginkgo/v2" 11 . "github.com/onsi/gomega" 12 ) 13 14 var _ = Describe("Varint encoding / decoding", func() { 15 Context("limits", func() { 16 Specify("Min == 0", func() { 17 Expect(Min).To(Equal(0)) 18 }) 19 20 Specify("Max == 2^62-1", func() { 21 Expect(uint64(Max)).To(Equal(uint64(1<<62 - 1))) 22 }) 23 }) 24 25 Context("reading", func() { 26 It("reads a 1 byte number", func() { 27 b := bytes.NewReader([]byte{0b00011001}) 28 val, err := Read(b) 29 Expect(err).ToNot(HaveOccurred()) 30 Expect(val).To(Equal(uint64(25))) 31 Expect(b.Len()).To(BeZero()) 32 }) 33 34 It("reads a number that is encoded too long", func() { 35 b := bytes.NewReader([]byte{0b01000000, 0x25}) 36 val, err := Read(b) 37 Expect(err).ToNot(HaveOccurred()) 38 Expect(val).To(Equal(uint64(37))) 39 Expect(b.Len()).To(BeZero()) 40 }) 41 42 It("reads a 2 byte number", func() { 43 b := bytes.NewReader([]byte{0b01111011, 0xbd}) 44 val, err := Read(b) 45 Expect(err).ToNot(HaveOccurred()) 46 Expect(val).To(Equal(uint64(15293))) 47 Expect(b.Len()).To(BeZero()) 48 }) 49 50 It("reads a 4 byte number", func() { 51 b := bytes.NewReader([]byte{0b10011101, 0x7f, 0x3e, 0x7d}) 52 val, err := Read(b) 53 Expect(err).ToNot(HaveOccurred()) 54 Expect(val).To(Equal(uint64(494878333))) 55 Expect(b.Len()).To(BeZero()) 56 }) 57 58 It("reads an 8 byte number", func() { 59 b := bytes.NewReader([]byte{0b11000010, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}) 60 val, err := Read(b) 61 Expect(err).ToNot(HaveOccurred()) 62 Expect(val).To(Equal(uint64(151288809941952652))) 63 Expect(b.Len()).To(BeZero()) 64 }) 65 }) 66 67 Context("parsing", func() { 68 It("fails on an empty slice", func() { 69 _, _, err := Parse([]byte{}) 70 Expect(err).To(Equal(io.EOF)) 71 }) 72 73 It("parses a 1 byte number", func() { 74 b := []byte{0b00011001} 75 val, n, err := Parse(b) 76 Expect(err).ToNot(HaveOccurred()) 77 Expect(val).To(Equal(uint64(25))) 78 Expect(n).To(Equal(1)) 79 }) 80 81 It("parses a number that is encoded too long", func() { 82 b := []byte{0b01000000, 0x25} 83 val, n, err := Parse(b) 84 Expect(err).ToNot(HaveOccurred()) 85 Expect(val).To(Equal(uint64(37))) 86 Expect(n).To(Equal(2)) 87 }) 88 89 It("parses a 2 byte number", func() { 90 b := []byte{0b01111011, 0xbd} 91 val, n, err := Parse(b) 92 Expect(err).ToNot(HaveOccurred()) 93 Expect(val).To(Equal(uint64(15293))) 94 Expect(n).To(Equal(2)) 95 }) 96 97 It("parses a 4 byte number", func() { 98 b := []byte{0b10011101, 0x7f, 0x3e, 0x7d} 99 val, n, err := Parse(b) 100 Expect(err).ToNot(HaveOccurred()) 101 Expect(val).To(Equal(uint64(494878333))) 102 Expect(n).To(Equal(4)) 103 }) 104 105 It("parses an 8 byte number", func() { 106 b := []byte{0b11000010, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c} 107 val, n, err := Parse(b) 108 Expect(err).ToNot(HaveOccurred()) 109 Expect(val).To(Equal(uint64(151288809941952652))) 110 Expect(n).To(Equal(8)) 111 }) 112 113 It("fails if the slice is too short", func() { 114 b := Append(nil, maxVarInt2*10) 115 _, _, err := Parse(b[:3]) 116 Expect(err).To(Equal(io.ErrUnexpectedEOF)) 117 }) 118 }) 119 120 Context("encoding", func() { 121 Context("with minimal length", func() { 122 It("writes a 1 byte number", func() { 123 Expect(Append(nil, 37)).To(Equal([]byte{0x25})) 124 }) 125 126 It("writes the maximum 1 byte number in 1 byte", func() { 127 Expect(Append(nil, maxVarInt1)).To(Equal([]byte{0b00111111})) 128 }) 129 130 It("writes the minimum 2 byte number in 2 bytes", func() { 131 Expect(Append(nil, maxVarInt1+1)).To(Equal([]byte{0x40, maxVarInt1 + 1})) 132 }) 133 134 It("writes a 2 byte number", func() { 135 Expect(Append(nil, 15293)).To(Equal([]byte{0b01000000 ^ 0x3b, 0xbd})) 136 }) 137 138 It("writes the maximum 2 byte number in 2 bytes", func() { 139 Expect(Append(nil, maxVarInt2)).To(Equal([]byte{0b01111111, 0xff})) 140 }) 141 142 It("writes the minimum 4 byte number in 4 bytes", func() { 143 b := Append(nil, maxVarInt2+1) 144 Expect(b).To(HaveLen(4)) 145 num, err := Read(bytes.NewReader(b)) 146 Expect(err).ToNot(HaveOccurred()) 147 Expect(num).To(Equal(uint64(maxVarInt2 + 1))) 148 }) 149 150 It("writes a 4 byte number", func() { 151 Expect(Append(nil, 494878333)).To(Equal([]byte{0b10000000 ^ 0x1d, 0x7f, 0x3e, 0x7d})) 152 }) 153 154 It("writes the maximum 4 byte number in 4 bytes", func() { 155 Expect(Append(nil, maxVarInt4)).To(Equal([]byte{0b10111111, 0xff, 0xff, 0xff})) 156 }) 157 158 It("writes the minimum 8 byte number in 8 bytes", func() { 159 b := Append(nil, maxVarInt4+1) 160 Expect(b).To(HaveLen(8)) 161 num, err := Read(bytes.NewReader(b)) 162 Expect(err).ToNot(HaveOccurred()) 163 Expect(num).To(Equal(uint64(maxVarInt4 + 1))) 164 }) 165 166 It("writes an 8 byte number", func() { 167 Expect(Append(nil, 151288809941952652)).To(Equal([]byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c})) 168 }) 169 170 It("writes the maximum 8 byte number in 8 bytes", func() { 171 Expect(Append(nil, maxVarInt8)).To(Equal([]byte{0xff /* 11111111 */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) 172 }) 173 174 It("panics when given a too large number (> 62 bit)", func() { 175 Expect(func() { Append(nil, maxVarInt8+1) }).Should(Panic()) 176 }) 177 }) 178 179 Context("with fixed length", func() { 180 It("panics when given an invalid length", func() { 181 Expect(func() { AppendWithLen(nil, 25, 3) }).Should(Panic()) 182 }) 183 184 It("panics when given a too short length", func() { 185 Expect(func() { AppendWithLen(nil, maxVarInt1+1, 1) }).Should(Panic()) 186 Expect(func() { AppendWithLen(nil, maxVarInt2+1, 2) }).Should(Panic()) 187 Expect(func() { AppendWithLen(nil, maxVarInt4+1, 4) }).Should(Panic()) 188 }) 189 190 It("writes a 1-byte number in minimal encoding", func() { 191 Expect(AppendWithLen(nil, 37, 1)).To(Equal([]byte{0x25})) 192 }) 193 194 It("writes a 1-byte number in 2 bytes", func() { 195 b := AppendWithLen(nil, 37, 2) 196 Expect(b).To(Equal([]byte{0b01000000, 0x25})) 197 Expect(Read(bytes.NewReader(b))).To(BeEquivalentTo(37)) 198 }) 199 200 It("writes a 1-byte number in 4 bytes", func() { 201 b := AppendWithLen(nil, 37, 4) 202 Expect(b).To(Equal([]byte{0b10000000, 0, 0, 0x25})) 203 Expect(Read(bytes.NewReader(b))).To(BeEquivalentTo(37)) 204 }) 205 206 It("writes a 1-byte number in 8 bytes", func() { 207 b := AppendWithLen(nil, 37, 8) 208 Expect(b).To(Equal([]byte{0b11000000, 0, 0, 0, 0, 0, 0, 0x25})) 209 Expect(Read(bytes.NewReader(b))).To(BeEquivalentTo(37)) 210 }) 211 212 It("writes a 2-byte number in 4 bytes", func() { 213 b := AppendWithLen(nil, 15293, 4) 214 Expect(b).To(Equal([]byte{0b10000000, 0, 0x3b, 0xbd})) 215 Expect(Read(bytes.NewReader(b))).To(BeEquivalentTo(15293)) 216 }) 217 218 It("write a 4-byte number in 8 bytes", func() { 219 b := AppendWithLen(nil, 494878333, 8) 220 Expect(b).To(Equal([]byte{0b11000000, 0, 0, 0, 0x1d, 0x7f, 0x3e, 0x7d})) 221 Expect(Read(bytes.NewReader(b))).To(BeEquivalentTo(494878333)) 222 }) 223 }) 224 }) 225 226 Context("determining the length needed for encoding", func() { 227 It("for numbers that need 1 byte", func() { 228 Expect(Len(0)).To(BeEquivalentTo(1)) 229 Expect(Len(maxVarInt1)).To(BeEquivalentTo(1)) 230 }) 231 232 It("for numbers that need 2 bytes", func() { 233 Expect(Len(maxVarInt1 + 1)).To(BeEquivalentTo(2)) 234 Expect(Len(maxVarInt2)).To(BeEquivalentTo(2)) 235 }) 236 237 It("for numbers that need 4 bytes", func() { 238 Expect(Len(maxVarInt2 + 1)).To(BeEquivalentTo(4)) 239 Expect(Len(maxVarInt4)).To(BeEquivalentTo(4)) 240 }) 241 242 It("for numbers that need 8 bytes", func() { 243 Expect(Len(maxVarInt4 + 1)).To(BeEquivalentTo(8)) 244 Expect(Len(maxVarInt8)).To(BeEquivalentTo(8)) 245 }) 246 247 It("panics when given a too large number (> 62 bit)", func() { 248 Expect(func() { Len(maxVarInt8 + 1) }).Should(Panic()) 249 }) 250 }) 251 }) 252 253 type benchmarkValue struct { 254 b []byte 255 v uint64 256 } 257 258 func randomValues(num int, maxValue uint64) []benchmarkValue { 259 r := rand.New(rand.NewSource(1)) 260 261 bv := make([]benchmarkValue, num) 262 for i := 0; i < num; i++ { 263 v := r.Uint64() % maxValue 264 bv[i].v = v 265 bv[i].b = Append([]byte{}, v) 266 } 267 return bv 268 } 269 270 func BenchmarkRead(b *testing.B) { 271 b.Run("1-byte", func(b *testing.B) { benchmarkRead(b, randomValues(min(b.N, 1024), maxVarInt1)) }) 272 b.Run("2-byte", func(b *testing.B) { benchmarkRead(b, randomValues(min(b.N, 1024), maxVarInt2)) }) 273 b.Run("4-byte", func(b *testing.B) { benchmarkRead(b, randomValues(min(b.N, 1024), maxVarInt4)) }) 274 b.Run("8-byte", func(b *testing.B) { benchmarkRead(b, randomValues(min(b.N, 1024), maxVarInt8)) }) 275 } 276 277 func benchmarkRead(b *testing.B, inputs []benchmarkValue) { 278 r := bytes.NewReader([]byte{}) 279 b.ResetTimer() 280 for i := 0; i < b.N; i++ { 281 index := i % len(inputs) 282 r.Reset(inputs[index].b) 283 val, err := Read(r) 284 if err != nil { 285 b.Fatal(err) 286 } 287 if val != inputs[index].v { 288 b.Fatalf("expected %d, got %d", inputs[index].v, val) 289 } 290 } 291 } 292 293 func BenchmarkParse(b *testing.B) { 294 b.Run("1-byte", func(b *testing.B) { benchmarkParse(b, randomValues(min(b.N, 1024), maxVarInt1)) }) 295 b.Run("2-byte", func(b *testing.B) { benchmarkParse(b, randomValues(min(b.N, 1024), maxVarInt2)) }) 296 b.Run("4-byte", func(b *testing.B) { benchmarkParse(b, randomValues(min(b.N, 1024), maxVarInt4)) }) 297 b.Run("8-byte", func(b *testing.B) { benchmarkParse(b, randomValues(min(b.N, 1024), maxVarInt8)) }) 298 } 299 300 func benchmarkParse(b *testing.B, inputs []benchmarkValue) { 301 b.ResetTimer() 302 for i := 0; i < b.N; i++ { 303 index := i % 1024 304 val, n, err := Parse(inputs[index].b) 305 if err != nil { 306 b.Fatal(err) 307 } 308 if n != len(inputs[index].b) { 309 b.Fatalf("expected to consume %d bytes, consumed %d", len(inputs[i].b), n) 310 } 311 if val != inputs[index].v { 312 b.Fatalf("expected %d, got %d", inputs[index].v, val) 313 } 314 } 315 }