From 653a1076066896aa8fac4f18d796716e748075b7 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sat, 14 Nov 2020 16:08:18 +0700 Subject: [PATCH] implement a function to encode a varint using a fixed length --- internal/utils/varint.go | 28 +++++ internal/utils/varint_test.go | 196 ++++++++++++++++++++++------------ 2 files changed, 153 insertions(+), 71 deletions(-) diff --git a/internal/utils/varint.go b/internal/utils/varint.go index d3ba9d19..c10f062a 100644 --- a/internal/utils/varint.go +++ b/internal/utils/varint.go @@ -83,6 +83,34 @@ func WriteVarInt(b *bytes.Buffer, i uint64) { } } +// WriteVarIntWithLen writes a number in the QUIC varint format, with the desired length. +func WriteVarIntWithLen(b *bytes.Buffer, i uint64, length protocol.ByteCount) { + if length != 1 && length != 2 && length != 4 && length != 8 { + panic("invalid varint length") + } + l := VarIntLen(i) + if l == length { + WriteVarInt(b, i) + return + } + if l > length { + panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length)) + } + if length == 2 { + b.WriteByte(0b01000000) + } else if length == 4 { + b.WriteByte(0b10000000) + } else if length == 8 { + b.WriteByte(0b11000000) + } + for j := protocol.ByteCount(1); j < length-l; j++ { + b.WriteByte(0) + } + for j := protocol.ByteCount(0); j < l; j++ { + b.WriteByte(uint8(i >> (8 * (l - 1 - j)))) + } +} + // VarIntLen determines the number of bytes that will be needed to write a number func VarIntLen(i uint64) protocol.ByteCount { if i <= maxVarInt1 { diff --git a/internal/utils/varint_test.go b/internal/utils/varint_test.go index b49774f0..48eb2c02 100644 --- a/internal/utils/varint_test.go +++ b/internal/utils/varint_test.go @@ -10,7 +10,7 @@ import ( var _ = Describe("Varint encoding / decoding", func() { Context("decoding", func() { It("reads a 1 byte number", func() { - b := bytes.NewReader([]byte{25}) // 00011001 + b := bytes.NewReader([]byte{0b00011001}) val, err := ReadVarInt(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(25))) @@ -18,7 +18,7 @@ var _ = Describe("Varint encoding / decoding", func() { }) It("reads a number that is encoded too long", func() { - b := bytes.NewReader([]byte{0x40, 0x25}) // first byte: 01000000 + b := bytes.NewReader([]byte{0b01000000, 0x25}) val, err := ReadVarInt(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(37))) @@ -26,7 +26,7 @@ var _ = Describe("Varint encoding / decoding", func() { }) It("reads a 2 byte number", func() { - b := bytes.NewReader([]byte{0x7b, 0xbd}) // first byte: 01111011 + b := bytes.NewReader([]byte{0b01111011, 0xbd}) val, err := ReadVarInt(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(15293))) @@ -34,7 +34,7 @@ var _ = Describe("Varint encoding / decoding", func() { }) It("reads a 4 byte number", func() { - b := bytes.NewReader([]byte{0x9d, 0x7f, 0x3e, 0x7d}) // first byte: 10011011 + b := bytes.NewReader([]byte{0b10011101, 0x7f, 0x3e, 0x7d}) val, err := ReadVarInt(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(494878333))) @@ -42,7 +42,7 @@ var _ = Describe("Varint encoding / decoding", func() { }) It("reads an 8 byte number", func() { - b := bytes.NewReader([]byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}) // first byte: 10000010 + b := bytes.NewReader([]byte{0b11000010, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}) val, err := ReadVarInt(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(151288809941952652))) @@ -51,81 +51,135 @@ var _ = Describe("Varint encoding / decoding", func() { }) Context("encoding", func() { - It("writes a 1 byte number", func() { - b := &bytes.Buffer{} - WriteVarInt(b, 37) - Expect(b.Bytes()).To(Equal([]byte{0x25})) + Context("with minimal length", func() { + It("writes a 1 byte number", func() { + b := &bytes.Buffer{} + WriteVarInt(b, 37) + Expect(b.Bytes()).To(Equal([]byte{0x25})) + }) + + It("writes the maximum 1 byte number in 1 byte", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt1) + Expect(b.Bytes()).To(Equal([]byte{0b00111111})) + }) + + It("writes the minimum 2 byte number in 2 bytes", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt1+1) + Expect(b.Bytes()).To(Equal([]byte{0x40, maxVarInt1 + 1})) + }) + + It("writes a 2 byte number", func() { + b := &bytes.Buffer{} + WriteVarInt(b, 15293) + Expect(b.Bytes()).To(Equal([]byte{0b01000000 ^ 0x3b, 0xbd})) + }) + + It("writes the maximum 2 byte number in 2 bytes", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt2) + Expect(b.Bytes()).To(Equal([]byte{0b01111111, 0xff})) + }) + + It("writes the minimum 4 byte number in 4 bytes", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt2+1) + Expect(b.Len()).To(Equal(4)) + num, err := ReadVarInt(b) + Expect(err).ToNot(HaveOccurred()) + Expect(num).To(Equal(uint64(maxVarInt2 + 1))) + }) + + It("writes a 4 byte number", func() { + b := &bytes.Buffer{} + WriteVarInt(b, 494878333) + Expect(b.Bytes()).To(Equal([]byte{0b10000000 ^ 0x1d, 0x7f, 0x3e, 0x7d})) + }) + + It("writes the maximum 4 byte number in 4 bytes", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt4) + Expect(b.Bytes()).To(Equal([]byte{0b10111111, 0xff, 0xff, 0xff})) + }) + + It("writes the minimum 8 byte number in 8 bytes", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt4+1) + Expect(b.Len()).To(Equal(8)) + num, err := ReadVarInt(b) + Expect(err).ToNot(HaveOccurred()) + Expect(num).To(Equal(uint64(maxVarInt4 + 1))) + }) + + It("writes an 8 byte number", func() { + b := &bytes.Buffer{} + WriteVarInt(b, 151288809941952652) + Expect(b.Bytes()).To(Equal([]byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c})) + }) + + It("writes the maximum 8 byte number in 8 bytes", func() { + b := &bytes.Buffer{} + WriteVarInt(b, maxVarInt8) + Expect(b.Bytes()).To(Equal([]byte{0xff /* 11111111 */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) + }) + + It("panics when given a too large number (> 62 bit)", func() { + Expect(func() { WriteVarInt(&bytes.Buffer{}, maxVarInt8+1) }).Should(Panic()) + }) }) - It("writes the maximum 1 byte number in 1 byte", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt1) - Expect(b.Bytes()).To(Equal([]byte{0x3f /* 00111111 */})) - }) + Context("with fixed length", func() { + It("panics when given an invalid length", func() { + Expect(func() { WriteVarIntWithLen(&bytes.Buffer{}, 25, 3) }).Should(Panic()) + }) - It("writes the minimum 2 byte number in 2 bytes", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt1+1) - Expect(b.Bytes()).To(Equal([]byte{0x40, maxVarInt1 + 1})) - }) + It("panics when given a too short length", func() { + Expect(func() { WriteVarIntWithLen(&bytes.Buffer{}, maxVarInt1+1, 1) }).Should(Panic()) + Expect(func() { WriteVarIntWithLen(&bytes.Buffer{}, maxVarInt2+1, 2) }).Should(Panic()) + Expect(func() { WriteVarIntWithLen(&bytes.Buffer{}, maxVarInt4+1, 4) }).Should(Panic()) + }) - It("writes a 2 byte number", func() { - b := &bytes.Buffer{} - WriteVarInt(b, 15293) - Expect(b.Bytes()).To(Equal([]byte{0x7b, 0xbd})) - }) + It("writes a 1-byte number in minimal encoding", func() { + b := &bytes.Buffer{} + WriteVarIntWithLen(b, 37, 1) + Expect(b.Bytes()).To(Equal([]byte{0x25})) + }) - It("writes the maximum 2 byte number in 2 bytes", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt2) - Expect(b.Bytes()).To(Equal([]byte{0x7f /* 01111111 */, 0xff})) - }) + It("writes a 1-byte number in 2 bytes", func() { + b := &bytes.Buffer{} + WriteVarIntWithLen(b, 37, 2) + Expect(b.Bytes()).To(Equal([]byte{0b01000000, 0x25})) + Expect(ReadVarInt(b)).To(BeEquivalentTo(37)) + }) - It("writes the minimum 4 byte number in 4 bytes", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt2+1) - Expect(b.Len()).To(Equal(4)) - num, err := ReadVarInt(b) - Expect(err).ToNot(HaveOccurred()) - Expect(num).To(Equal(uint64(maxVarInt2 + 1))) - }) + It("writes a 1-byte number in 4 bytes", func() { + b := &bytes.Buffer{} + WriteVarIntWithLen(b, 37, 4) + Expect(b.Bytes()).To(Equal([]byte{0b10000000, 0, 0, 0x25})) + Expect(ReadVarInt(b)).To(BeEquivalentTo(37)) + }) - It("writes a 4 byte number", func() { - b := &bytes.Buffer{} - WriteVarInt(b, 494878333) - Expect(b.Bytes()).To(Equal([]byte{0x9d, 0x7f, 0x3e, 0x7d})) - }) + It("writes a 1-byte number in 8 bytes", func() { + b := &bytes.Buffer{} + WriteVarIntWithLen(b, 37, 8) + Expect(b.Bytes()).To(Equal([]byte{0b11000000, 0, 0, 0, 0, 0, 0, 0x25})) + Expect(ReadVarInt(b)).To(BeEquivalentTo(37)) + }) - It("writes the maximum 4 byte number in 4 bytes", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt4) - Expect(b.Bytes()).To(Equal([]byte{0xbf /* 10111111 */, 0xff, 0xff, 0xff})) - }) + It("writes a 2-byte number in 4 bytes", func() { + b := &bytes.Buffer{} + WriteVarIntWithLen(b, 15293, 4) + Expect(b.Bytes()).To(Equal([]byte{0b10000000, 0, 0x3b, 0xbd})) + Expect(ReadVarInt(b)).To(BeEquivalentTo(15293)) + }) - It("writes the minimum 8 byte number in 8 bytes", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt4+1) - Expect(b.Len()).To(Equal(8)) - num, err := ReadVarInt(b) - Expect(err).ToNot(HaveOccurred()) - Expect(num).To(Equal(uint64(maxVarInt4 + 1))) - }) - - It("writes an 8 byte number", func() { - b := &bytes.Buffer{} - WriteVarInt(b, 151288809941952652) - Expect(b.Bytes()).To(Equal([]byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c})) - }) - - It("writes the maximum 8 byte number in 8 bytes", func() { - b := &bytes.Buffer{} - WriteVarInt(b, maxVarInt8) - Expect(b.Bytes()).To(Equal([]byte{0xff /* 11111111 */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) - }) - - It("panics when given a too large number (> 62 bit)", func() { - b := &bytes.Buffer{} - Expect(func() { WriteVarInt(b, maxVarInt8+1) }).Should(Panic()) + It("write a 4-byte number in 8 bytes", func() { + b := &bytes.Buffer{} + WriteVarIntWithLen(b, 494878333, 8) + Expect(b.Bytes()).To(Equal([]byte{0b11000000, 0, 0, 0, 0x1d, 0x7f, 0x3e, 0x7d})) + Expect(ReadVarInt(b)).To(BeEquivalentTo(494878333)) + }) }) })