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

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

@ -1,10 +1,44 @@
export default varint; export default varint;
declare namespace varint { declare namespace varint {
function encodeInt(val: number): Buffer; /**
function encodeString(val: string): Buffer; * Encodes an integer value into a varint byte buffer.
function encodeUShort(val: number): Buffer; * @param val - The integer value to encode.
function concat(chunks: Buffer[]): Buffer; */
function decodeInt(buffer: Buffer, offset: number): number; function encodeInt(val: number): Buffer;
function decodeString(val: Buffer, offset?: number): string;
function decodeLength(val: number): 5 | 7 | 8 | 1 | 2 | 3 | 4 | 6 | 9 | 10; /**
* 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;
} }