perf: optimize varint decoding

docs: update type definitions
This commit is contained in:
Timofey Gelazoniya 2025-02-07 08:35:21 +03:00
parent 0b5c5e2938
commit c71236f223
Signed by: zeldon
GPG Key ID: 047886915281DD2A
2 changed files with 79 additions and 53 deletions

View File

@ -66,65 +66,57 @@ const varint = {
},
/**
* Decodes a varint integer value from a byte buffer.
* Decodes a varint integer value from a buffer.
* @param {Buffer} buffer - The byte buffer to decode from.
* @param {number} offset - The offset in the buffer to start decoding from.
* @returns {number}
*/
decodeInt: (buffer, offset) => {
let val = 0;
let count = 0;
while (true) {
const byte = buffer.readUInt8(offset++);
val |= (byte & 0x7f) << (count++ * 7);
if ((byte & 0x80) !== 0x80) {
break;
}
// Fast path for single-byte varints
const firstByte = buffer.readUInt8(offset);
if (firstByte < 0x80) {
return firstByte;
}
return val;
let val = firstByte & 0x7f;
let position = 7;
while (position < 32) {
const byte = buffer.readUInt8(++offset);
val |= (byte & 0x7f) << position;
if ((byte & 0x80) === 0) {
return val;
}
position += 7;
}
throw new Error("VarInt is too big");
},
/**
* Calculates the number of bytes required to decode a varint integer value.
* @param {number} val - The varint integer value.
* @returns {5 | 7 | 8 | 1 | 2 | 3 | 4 | 6 | 9 | 10}
* Calculates how many bytes are needed to encode a number as a VarInt
* VarInts use a variable number of bytes to efficiently encode integers
* Each byte uses 7 bits for the value and 1 bit to indicate if more bytes follow
* VarInts are never longer than 5 bytes
*
* @param {number} val - The number to calculate the VarInt length for
* @returns {1|2|3|4|5} The number of bytes needed to encode the value
*/
decodeLength: (val) => {
// Constants representing the powers of 2 used for comparison
const N1 = Math.pow(2, 7);
const N2 = Math.pow(2, 14);
const N3 = Math.pow(2, 21);
const N4 = Math.pow(2, 28);
const N5 = Math.pow(2, 35);
const N6 = Math.pow(2, 42);
const N7 = Math.pow(2, 49);
const N8 = Math.pow(2, 56);
const N9 = Math.pow(2, 63);
// Using bit shifts to calculate power of 2 thresholds
// 1 << 7 = 2^7 = 128 - Numbers below this fit in 1 byte
// 1 << 14 = 2^14 = 16,384 - Numbers below this fit in 2 bytes
// 1 << 21 = 2^21 = 2,097,152 - Numbers below this fit in 3 bytes
// 1 << 28 = 2^28 = 268,435,456 - Numbers below this fit in 4 bytes
// Any larger number needs 5 bytes (maximum VarInt size)
// Return the number of bytes required based on the value
return val < N1
? 1
: val < N2
? 2
: val < N3
? 3
: val < N4
? 4
: val < N5
? 5
: val < N6
? 6
: val < N7
? 7
: val < N8
? 8
: val < N9
? 9
: 10;
if (val < 1 << 7) return 1;
if (val < 1 << 14) return 2;
if (val < 1 << 21) return 3;
if (val < 1 << 28) return 4;
return 5;
},
};

48
types/lib/varint.d.ts vendored
View File

@ -1,10 +1,44 @@
export default varint;
declare namespace varint {
function encodeInt(val: number): Buffer;
function encodeString(val: string): Buffer;
function encodeUShort(val: number): Buffer;
function concat(chunks: Buffer[]): Buffer;
function decodeInt(buffer: Buffer, offset: number): number;
function decodeString(val: Buffer, offset?: number): string;
function decodeLength(val: number): 5 | 7 | 8 | 1 | 2 | 3 | 4 | 6 | 9 | 10;
/**
* Encodes an integer value into a varint byte buffer.
* @param val - The integer value to encode.
*/
function encodeInt(val: number): Buffer;
/**
* Encodes a string value into a UTF-8 byte buffer.
* @param val - The string value to encode.
*/
function encodeString(val: string): Buffer;
/**
* Encodes an unsigned short value into a byte buffer.
* @param val - The unsigned short value to encode.
*/
function encodeUShort(val: number): Buffer;
/**
* Concatenates multiple byte buffers into a single byte buffer.
* @param chunks - An array of byte buffers to concatenate.
*/
function concat(chunks: Buffer[]): Buffer;
/**
* Decodes a varint integer value from a buffer.
* @param buffer - The byte buffer to decode from.
* @param offset - The offset in the buffer to start decoding from.
*/
function decodeInt(buffer: Buffer, offset: number): number;
/**
* Calculates how many bytes are needed to encode a number as a VarInt.
* VarInts use a variable number of bytes to efficiently encode integers.
* Each byte uses 7 bits for the value and 1 bit to indicate if more bytes follow.
* VarInts are never longer than 5 bytes.
*
* @param val - The number to calculate the VarInt length for.
* @returns The number of bytes needed to encode the value (1-5).
*/
function decodeLength(val: number): 1 | 2 | 3 | 4 | 5;
}