Version 1.0.4

This commit is contained in:
2026-03-12 21:02:16 +01:00
parent 2a5e4b1052
commit a5acb5ed03
4 changed files with 128 additions and 59 deletions

View File

@@ -15,7 +15,7 @@ export class Reader {
const srcBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
this.buffer = srcBuffer instanceof ArrayBuffer ? srcBuffer : new ArrayBuffer(srcBuffer.byteLength);
} else {
throw new TypeError('Invalid buffer type. Expected ArrayBuffer, Uint8Array, or ArrayBufferView.');
throw new TypeError("Invalid buffer type. Expected ArrayBuffer, Uint8Array, or ArrayBufferView.");
}
this.view = new DataView(this.buffer);
this.offset = 0;
@@ -179,7 +179,7 @@ export class Reader {
while (true) {
this.checkBounds(1);
const byte = this.view.getUint8(this.offset++);
result |= (byte & 0x7F) << shift;
result |= (byte & 0x7f) << shift;
if ((byte & 0x80) === 0) {
break; // Last byte of the varint
}
@@ -205,7 +205,7 @@ export class Reader {
while (true) {
this.checkBounds(1);
const byte = this.view.getUint8(this.offset++);
result |= (byte & 0x7F) << shift;
result |= (byte & 0x7f) << shift;
if ((byte & 0x80) === 0) {
break; // Last byte of the varint
}
@@ -342,32 +342,36 @@ export class Reader {
return new Reader(bytes, littleEndian);
}
public static fromString(str: string, encoding: 'utf8' | 'ascii' | 'hex' | 'base64' | 'rawbase64' | 'urlbase64' | 'rawurlbase64' = 'utf8', littleEndian: boolean = true): Reader {
public static fromString(
str: string,
encoding: "utf8" | "ascii" | "hex" | "base64" | "rawbase64" | "urlbase64" | "rawurlbase64" = "utf8",
littleEndian: boolean = true
): Reader {
let bytes: Uint8Array;
switch (encoding) {
case 'utf8':
case "utf8":
bytes = new TextEncoder().encode(str);
break;
case 'ascii':
bytes = new Uint8Array(str.split('').map(c => c.charCodeAt(0)));
case "ascii":
bytes = new Uint8Array(str.split("").map((c) => c.charCodeAt(0)));
break;
case 'hex':
case "hex":
bytes = new Uint8Array(str.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(str.substr(i * 2, 2), 16);
}
break;
case 'base64':
bytes = Uint8Array.from(atob(str), c => c.charCodeAt(0));
case "base64":
bytes = Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
break;
case 'rawbase64':
bytes = Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
case "rawbase64":
bytes = Uint8Array.from(atob(str.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break;
case 'urlbase64':
bytes = Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
case "urlbase64":
bytes = Uint8Array.from(atob(str.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break;
case 'rawurlbase64':
bytes = Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
case "rawurlbase64":
bytes = Uint8Array.from(atob(str.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break;
}
return new Reader(bytes.slice().buffer, littleEndian);
@@ -387,7 +391,9 @@ export class Reader {
private checkBounds(length: number) {
if (this.offset + length > this.view.byteLength) {
throw new RangeError(`Attempt to read beyond end of buffer: offset=${this.offset}, length=${length}, bufferLength=${this.view.byteLength}`);
throw new RangeError(
`Attempt to read beyond end of buffer: offset=${this.offset}, length=${length}, bufferLength=${this.view.byteLength}`
);
}
}
}
@@ -472,7 +478,7 @@ export class Writer {
*/
public int64(value: number | bigint): void {
this.checkBounds(8);
if (typeof value === 'number') {
if (typeof value === "number") {
value = BigInt(value);
}
this.view.setBigInt64(this.offset, value, this.littleEndian);
@@ -523,7 +529,7 @@ export class Writer {
*/
public uint64(value: number | bigint): void {
this.checkBounds(8);
if (typeof value === 'number') {
if (typeof value === "number") {
value = BigInt(value);
}
this.view.setBigUint64(this.offset, value, this.littleEndian);
@@ -570,7 +576,7 @@ export class Writer {
// Useful for pre-sizing buffers.
let remaining = value >>> 0; // Ensure unsigned
while (remaining >= 0x80) {
this.view.setUint8(this.offset++, (remaining & 0x7F) | 0x80);
this.view.setUint8(this.offset++, (remaining & 0x7f) | 0x80);
remaining >>>= 7;
}
this.view.setUint8(this.offset++, remaining);
@@ -591,7 +597,7 @@ export class Writer {
let remaining = value >>> 0; // Ensure unsigned
const isNegative = value < 0;
while (remaining >= 0x80 || (isNegative && remaining < 0x80)) {
this.view.setUint8(this.offset++, (remaining & 0x7F) | 0x80);
this.view.setUint8(this.offset++, (remaining & 0x7f) | 0x80);
remaining >>>= 7;
}
this.view.setUint8(this.offset++, remaining);
@@ -701,46 +707,60 @@ export class Writer {
* @param encoding The encoding to use for the string conversion.
* @returns The encoded string.
*/
public toString(encoding: 'utf-8' | 'hex' | 'base64' | 'rawbase64' | 'urlbase64' | 'rawurlbase64' = 'utf-8'): string {
public toString(encoding: "utf-8" | "hex" | "base64" | "rawbase64" | "urlbase64" | "rawurlbase64" = "utf-8"): string {
const bytes = this.toBytes();
switch (encoding) {
case 'utf-8':
case "utf-8":
return new TextDecoder().decode(bytes);
case 'hex':
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
case 'base64':
case "hex":
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
case "base64":
return btoa(String.fromCharCode(...bytes));
case 'rawbase64':
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
case 'urlbase64':
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_');
case 'rawurlbase64':
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
case "rawbase64":
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
case "urlbase64":
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, "-")
.replace(/\//g, "_");
case "rawurlbase64":
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
default:
throw new Error(`Unsupported encoding: ${encoding}`);
}
}
public static fromString(value: string, encoding: 'utf-8' | 'hex' | 'base64' | 'rawbase64' | 'urlbase64' | 'rawurlbase64' = 'utf-8', littleEndian: boolean = true): Writer {
public static fromString(
value: string,
encoding: "utf-8" | "hex" | "base64" | "rawbase64" | "urlbase64" | "rawurlbase64" = "utf-8",
littleEndian: boolean = true
): Writer {
let bytes: Uint8Array;
switch (encoding) {
case 'utf-8':
case "utf-8":
bytes = new TextEncoder().encode(value);
break;
case 'hex':
bytes = new Uint8Array(value.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16)));
case "hex":
bytes = new Uint8Array(value.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)));
break;
case 'base64':
bytes = Uint8Array.from(atob(value), c => c.charCodeAt(0));
case "base64":
bytes = Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
break;
case 'rawbase64':
bytes = Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
case "rawbase64":
bytes = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break;
case 'urlbase64':
bytes = Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
case "urlbase64":
bytes = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break;
case 'rawurlbase64':
bytes = Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
case "rawurlbase64":
bytes = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break;
default:
throw new Error(`Unsupported encoding: ${encoding}`);
@@ -764,13 +784,48 @@ export class Writer {
private checkBounds(length: number) {
if (this.offset + length > this.view.byteLength) {
throw new RangeError(`Attempt to write beyond end of buffer: offset=${this.offset}, length=${length}, bufferLength=${this.view.byteLength}`);
throw new RangeError(
`Attempt to write beyond end of buffer: offset=${this.offset}, length=${length}, bufferLength=${this.view.byteLength}`
);
}
}
}
// Exporting types and utilities for external use:
export * from './types';
export type * from './types';
export * from './utils';
export type * from './utils'
export * from "./types";
export {
FieldType,
type Packet,
type Protocol,
type Dissected,
type Segment,
type Field,
type BitField
} from "./types";
export {
I8,
I16,
I32,
U8,
U16,
U32,
F32,
F64,
isBytes,
assertBytes,
equalBytes,
constantTimeEqualBytes,
base64ToBytes,
bytesToBase64,
hexToBytes,
bytesToHex,
decodeBytes,
encodeBytes,
type TypedArray,
type HexEncoding,
type Base64Encoding,
type Base64RawEncoding,
type Base64URLEncoding,
type Base64RawURLEncoding,
type Encoding
} from "./utils";