readVarInt function
Reads a QUIC Variable-Length Integer from a byte array starting at a given offset.
Returns a VarIntReadResult containing the decoded value and its byte length,
or null if the buffer is too short.
Implementation
VarIntReadResult? readVarInt(Uint8List array, int offset) {
if (offset >= array.length) return null;
final first = array[offset];
final prefix = first >> 6;
// 1-byte encoding (00xxxxxx)
if (prefix == 0) {
return VarIntReadResult(
value: first & 0x3f, // Mask the two prefix bits
byteLength: 1,
);
}
// 2-byte encoding (01xxxxxx)
if (prefix == 0x01) {
if (offset + 1 >= array.length) return null;
// value = (01xxxxxx & 0x3f) << 8 | array[offset + 1]
final value = ((first & 0x3f) << 8) | array[offset + 1];
return VarIntReadResult(value: value, byteLength: 2);
}
// 4-byte encoding (10xxxxxx)
if (prefix == 0x02) {
if (offset + 3 >= array.length) return null;
final value =
((first & 0x3F) << 24) |
(array[offset + 1] << 16) |
(array[offset + 2] << 8) |
array[offset + 3];
// Dart's `int` is 64-bit and handles the result directly.
return VarIntReadResult(value: value, byteLength: 4);
}
// 8-byte encoding (11xxxxxx)
if (prefix == 0x03) {
if (offset + 7 >= array.length) return null;
// In Dart, we can construct the full 62-bit value directly into a 64-bit `int`.
// value = (11xxxxxx & 0x3F) << 56 | B1 << 48 | ... | B7
int value = (first & 0x3F) << 56;
value |= array[offset + 1] << 48;
value |= array[offset + 2] << 40;
value |= array[offset + 3] << 32;
value |= array[offset + 4] << 24;
value |= array[offset + 5] << 16;
value |= array[offset + 6] << 8;
value |= array[offset + 7];
return VarIntReadResult(value: value, byteLength: 8);
}
// Should be unreachable given the 2-bit prefix logic, but included for completeness
return null;
}