github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/quicvarint/varint.go (about)

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