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  }