10 Commits
v1.0.2 ... main

Author SHA1 Message Date
71ae225972 Version 1.1.1 2026-03-18 17:33:03 +01:00
3855e478c0 Allow Field to specify its own data 2026-03-18 17:32:53 +01:00
93a642f242 Version 1.1.0 2026-03-15 15:46:25 +01:00
8a641cbc02 Added char type and Segment.isString 2026-03-15 15:46:03 +01:00
b3d5aace89 Use eslint.config.ts 2026-03-12 21:11:26 +01:00
8994fb7b45 Version 1.0.5 2026-03-12 21:06:18 +01:00
a5acb5ed03 Version 1.0.4 2026-03-12 21:02:16 +01:00
2a5e4b1052 Export all the things 2026-03-12 19:52:27 +01:00
c18a544a2e Version 1.0.3 2026-03-12 18:21:34 +01:00
25c07c947a Expose equalBytes and constantTimeEqualBytes 2026-03-12 18:21:21 +01:00
8 changed files with 239 additions and 154 deletions

View File

@@ -11,16 +11,22 @@ repos:
hooks: hooks:
- id: shellcheck - id: shellcheck
- repo: https://github.com/pre-commit/mirrors-eslint - repo: local
rev: v10.0.3 hooks:
- id: prettier
name: prettier
entry: npx prettier --write
language: system
files: "\\.(js|jsx|ts|tsx)$"
- repo: local
hooks: hooks:
- id: eslint - id: eslint
name: eslint
entry: npx eslint --fix
language: system
files: "\\.(js|jsx|ts|tsx)$" files: "\\.(js|jsx|ts|tsx)$"
exclude: node_modules/
# Use stylelint (local) instead of the deprecated scss-lint Ruby gem which
# cannot parse modern Sass `@use` and module syntax. This invokes the
# project's installed `stylelint` via `npx` so the devDependency is used.
- repo: local - repo: local
hooks: hooks:
- id: stylelint - id: stylelint

8
.prettierrc.ts Normal file
View File

@@ -0,0 +1,8 @@
import { type Config } from "prettier";
const config: Config = {
trailingComma: "none",
printWidth: 120
};
export default config;

View File

@@ -1,26 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
rules: {
"@typescript-eslint/ban-ts-comment": [
"error", {
"ts-ignore": "allow-with-description"
}
],
}
},
])

24
eslint.config.ts Normal file
View File

@@ -0,0 +1,24 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from "eslint/config";
export default defineConfig([
globalIgnores(["dist"]),
{
files: ["**/*.{ts,tsx}"],
extends: [js.configs.recommended, tseslint.configs.recommended],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser
},
rules: {
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-ignore": "allow-with-description"
}
]
}
}
]);

View File

@@ -1,7 +1,7 @@
{ {
"name": "@hamradio/packet", "name": "@hamradio/packet",
"type": "module", "type": "module",
"version": "1.0.2", "version": "1.1.1",
"description": "Low level packet parsing library (for radio protocols)", "description": "Low level packet parsing library (for radio protocols)",
"keywords": [ "keywords": [
"HAM radio", "HAM radio",
@@ -13,12 +13,12 @@
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.maze.io/ham/packet.js" "url": "https://git.maze.io/ham/packet.ts"
}, },
"license": "MIT", "license": "MIT",
"author": "Wijnand Modderman-Lenstra", "author": "Wijnand Modderman-Lenstra",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.mjs", "module": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"files": [ "files": [
"dist" "dist"
@@ -26,7 +26,7 @@
"exports": { "exports": {
".": { ".": {
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"import": "./dist/index.mjs", "import": "./dist/index.js",
"require": "./dist/index.js" "require": "./dist/index.js"
} }
}, },
@@ -40,12 +40,13 @@
"lint": "eslint .", "lint": "eslint .",
"prepare": "npm run build" "prepare": "npm run build"
}, },
"dependencies": {},
"devDependencies": { "devDependencies": {
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@vitest/coverage-v8": "^4.0.18", "@vitest/coverage-v8": "^4.0.18",
"eslint": "^10.0.3", "eslint": "^10.0.3",
"globals": "^17.4.0", "globals": "^17.4.0",
"jiti": "^2.6.1",
"prettier": "3.8.1",
"tsup": "^8.5.1", "tsup": "^8.5.1",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.57.0", "typescript-eslint": "^8.57.0",

View File

@@ -1,28 +1,30 @@
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from "vitest";
import { Reader, Writer } from '.'; import { Reader, Writer } from ".";
describe('Reader/Writer round-trip', () => { describe("Reader/Writer round-trip", () => {
it('writes then reads a variety of types correctly', () => { it("writes then reads a variety of types correctly", () => {
const writer = new Writer(1024); const writer = new Writer(1024);
// values to write // values to write
const bBool = true; const bBool = true;
const bChar = "A";
const bInt8 = -42; const bInt8 = -42;
const bUint8 = 200; const bUint8 = 200;
const bUint16 = 0xBEEF; const bUint16 = 0xbeef;
const bUint32 = 0xDEADBEEF >>> 0; const bUint32 = 0xdeadbeef >>> 0;
const bUint64 = 0x1122334455667788n; const bUint64 = 0x1122334455667788n;
const bFloat32 = 3.1415927; const bFloat32 = 3.1415927;
const bFloat64 = 1.23456789e5; const bFloat64 = 1.23456789e5;
const bCString = 'hello'; const bCString = "hello";
const bUtf8 = 'π≈3.14'; const bUtf8 = "π≈3.14";
const words = [0x1234, 0x5678, 0x9ABC]; const words = [0x1234, 0x5678, 0x9abc];
const dwords = [0x11223344 >>> 0, 0x55667788 >>> 0]; const dwords = [0x11223344 >>> 0, 0x55667788 >>> 0];
const qwords = [0x8000000000000000n, 0xAABBCCDDEEFF0011n]; const qwords = [0x8000000000000000n, 0xaabbccddeeff0011n];
const rawBytes = new Uint8Array([1, 2, 3, 4]); const rawBytes = new Uint8Array([1, 2, 3, 4]);
// write sequence // write sequence
writer.bool(bBool); writer.bool(bBool);
writer.char(bChar);
writer.int8(bInt8); writer.int8(bInt8);
writer.uint8(bUint8); writer.uint8(bUint8);
writer.uint16(bUint16); writer.uint16(bUint16);
@@ -45,6 +47,7 @@ describe('Reader/Writer round-trip', () => {
// read back in same order // read back in same order
expect(reader.bool()).toBe(bBool); expect(reader.bool()).toBe(bBool);
expect(reader.char()).toBe(bChar);
expect(reader.int8()).toBe(bInt8); expect(reader.int8()).toBe(bInt8);
expect(reader.uint8()).toBe(bUint8); expect(reader.uint8()).toBe(bUint8);
expect(reader.uint16()).toBe(bUint16); expect(reader.uint16()).toBe(bUint16);

View File

@@ -15,7 +15,7 @@ export class Reader {
const srcBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); const srcBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
this.buffer = srcBuffer instanceof ArrayBuffer ? srcBuffer : new ArrayBuffer(srcBuffer.byteLength); this.buffer = srcBuffer instanceof ArrayBuffer ? srcBuffer : new ArrayBuffer(srcBuffer.byteLength);
} else { } 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.view = new DataView(this.buffer);
this.offset = 0; this.offset = 0;
@@ -35,6 +35,17 @@ export class Reader {
return value !== 0; return value !== 0;
} }
/**
* Read an 8-bit character from the buffer at the current offset, and advance the offset by 1 byte.
* @returns A string containing the character read from the buffer.
*/
public char(): string {
this.checkBounds(1);
const value = this.view.getUint8(this.offset);
this.offset += 1;
return String.fromCharCode(value);
}
/** /**
* Read an 8-bit signed integer from the buffer at the current offset, and advance the offset by 1 byte. * Read an 8-bit signed integer from the buffer at the current offset, and advance the offset by 1 byte.
* *
@@ -179,7 +190,7 @@ export class Reader {
while (true) { while (true) {
this.checkBounds(1); this.checkBounds(1);
const byte = this.view.getUint8(this.offset++); const byte = this.view.getUint8(this.offset++);
result |= (byte & 0x7F) << shift; result |= (byte & 0x7f) << shift;
if ((byte & 0x80) === 0) { if ((byte & 0x80) === 0) {
break; // Last byte of the varint break; // Last byte of the varint
} }
@@ -205,7 +216,7 @@ export class Reader {
while (true) { while (true) {
this.checkBounds(1); this.checkBounds(1);
const byte = this.view.getUint8(this.offset++); const byte = this.view.getUint8(this.offset++);
result |= (byte & 0x7F) << shift; result |= (byte & 0x7f) << shift;
if ((byte & 0x80) === 0) { if ((byte & 0x80) === 0) {
break; // Last byte of the varint break; // Last byte of the varint
} }
@@ -342,32 +353,36 @@ export class Reader {
return new Reader(bytes, littleEndian); 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; let bytes: Uint8Array;
switch (encoding) { switch (encoding) {
case 'utf8': case "utf8":
bytes = new TextEncoder().encode(str); bytes = new TextEncoder().encode(str);
break; break;
case 'ascii': case "ascii":
bytes = new Uint8Array(str.split('').map(c => c.charCodeAt(0))); bytes = new Uint8Array(str.split("").map((c) => c.charCodeAt(0)));
break; break;
case 'hex': case "hex":
bytes = new Uint8Array(str.length / 2); bytes = new Uint8Array(str.length / 2);
for (let i = 0; i < bytes.length; i++) { for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(str.substr(i * 2, 2), 16); bytes[i] = parseInt(str.substr(i * 2, 2), 16);
} }
break; break;
case 'base64': case "base64":
bytes = Uint8Array.from(atob(str), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
break; break;
case 'rawbase64': case "rawbase64":
bytes = Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(str.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break; break;
case 'urlbase64': case "urlbase64":
bytes = Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(str.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break; break;
case 'rawurlbase64': case "rawurlbase64":
bytes = Uint8Array.from(atob(str.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(str.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break; break;
} }
return new Reader(bytes.slice().buffer, littleEndian); return new Reader(bytes.slice().buffer, littleEndian);
@@ -387,7 +402,9 @@ export class Reader {
private checkBounds(length: number) { private checkBounds(length: number) {
if (this.offset + length > this.view.byteLength) { 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}`
);
} }
} }
} }
@@ -428,6 +445,27 @@ export class Writer {
this.offset += 1; this.offset += 1;
} }
/**
* Write an 8-bit character to the buffer at the current offset, and advance the offset by 1 byte.
*
* @param value The character or its ASCII code to write.
*/
public char(value: string | number): void {
if (typeof value === "string") {
if (value.length !== 1) {
throw new Error("Input must be a single character");
}
this.checkBounds(1);
this.view.setUint8(this.offset, value.charCodeAt(0));
} else if (typeof value === "number") {
this.checkBounds(1);
this.view.setUint8(this.offset, value);
} else {
throw new Error("Input must be a string or number");
}
this.offset += 1;
}
/** /**
* Write an 8-bit signed integer to the buffer at the current offset, and advance the offset by * Write an 8-bit signed integer to the buffer at the current offset, and advance the offset by
* 1 byte. * 1 byte.
@@ -472,7 +510,7 @@ export class Writer {
*/ */
public int64(value: number | bigint): void { public int64(value: number | bigint): void {
this.checkBounds(8); this.checkBounds(8);
if (typeof value === 'number') { if (typeof value === "number") {
value = BigInt(value); value = BigInt(value);
} }
this.view.setBigInt64(this.offset, value, this.littleEndian); this.view.setBigInt64(this.offset, value, this.littleEndian);
@@ -523,7 +561,7 @@ export class Writer {
*/ */
public uint64(value: number | bigint): void { public uint64(value: number | bigint): void {
this.checkBounds(8); this.checkBounds(8);
if (typeof value === 'number') { if (typeof value === "number") {
value = BigInt(value); value = BigInt(value);
} }
this.view.setBigUint64(this.offset, value, this.littleEndian); this.view.setBigUint64(this.offset, value, this.littleEndian);
@@ -570,7 +608,7 @@ export class Writer {
// Useful for pre-sizing buffers. // Useful for pre-sizing buffers.
let remaining = value >>> 0; // Ensure unsigned let remaining = value >>> 0; // Ensure unsigned
while (remaining >= 0x80) { while (remaining >= 0x80) {
this.view.setUint8(this.offset++, (remaining & 0x7F) | 0x80); this.view.setUint8(this.offset++, (remaining & 0x7f) | 0x80);
remaining >>>= 7; remaining >>>= 7;
} }
this.view.setUint8(this.offset++, remaining); this.view.setUint8(this.offset++, remaining);
@@ -591,7 +629,7 @@ export class Writer {
let remaining = value >>> 0; // Ensure unsigned let remaining = value >>> 0; // Ensure unsigned
const isNegative = value < 0; const isNegative = value < 0;
while (remaining >= 0x80 || (isNegative && remaining < 0x80)) { while (remaining >= 0x80 || (isNegative && remaining < 0x80)) {
this.view.setUint8(this.offset++, (remaining & 0x7F) | 0x80); this.view.setUint8(this.offset++, (remaining & 0x7f) | 0x80);
remaining >>>= 7; remaining >>>= 7;
} }
this.view.setUint8(this.offset++, remaining); this.view.setUint8(this.offset++, remaining);
@@ -701,46 +739,60 @@ export class Writer {
* @param encoding The encoding to use for the string conversion. * @param encoding The encoding to use for the string conversion.
* @returns The encoded string. * @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(); const bytes = this.toBytes();
switch (encoding) { switch (encoding) {
case 'utf-8': case "utf-8":
return new TextDecoder().decode(bytes); return new TextDecoder().decode(bytes);
case 'hex': case "hex":
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); return Array.from(bytes)
case 'base64': .map((b) => b.toString(16).padStart(2, "0"))
.join("");
case "base64":
return btoa(String.fromCharCode(...bytes)); return btoa(String.fromCharCode(...bytes));
case 'rawbase64': case "rawbase64":
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); return btoa(String.fromCharCode(...bytes))
case 'urlbase64': .replace(/\+/g, "-")
return btoa(String.fromCharCode(...bytes)).replace(/\+/g, '-').replace(/\//g, '_'); .replace(/\//g, "_")
case 'rawurlbase64': .replace(/=+$/, "");
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: default:
throw new Error(`Unsupported encoding: ${encoding}`); 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; let bytes: Uint8Array;
switch (encoding) { switch (encoding) {
case 'utf-8': case "utf-8":
bytes = new TextEncoder().encode(value); bytes = new TextEncoder().encode(value);
break; break;
case 'hex': case "hex":
bytes = new Uint8Array(value.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))); bytes = new Uint8Array(value.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)));
break; break;
case 'base64': case "base64":
bytes = Uint8Array.from(atob(value), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(value), (c) => c.charCodeAt(0));
break; break;
case 'rawbase64': case "rawbase64":
bytes = Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break; break;
case 'urlbase64': case "urlbase64":
bytes = Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break; break;
case 'rawurlbase64': case "rawurlbase64":
bytes = Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); bytes = Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0));
break; break;
default: default:
throw new Error(`Unsupported encoding: ${encoding}`); throw new Error(`Unsupported encoding: ${encoding}`);
@@ -764,32 +816,48 @@ export class Writer {
private checkBounds(length: number) { private checkBounds(length: number) {
if (this.offset + length > this.view.byteLength) { 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}`
);
} }
} }
} }
export { FieldType } from './types'; // Exporting types and utilities for external use:
export * from "./types";
export {
FieldType,
type Packet,
type Protocol,
type Dissected,
type Segment,
type Field,
type BitField
} from "./types";
export { export {
isBytes,
assertBytes,
bytesToHex,
hexToBytes,
bytesToBase64,
base64ToBytes,
U8,
U16,
U32,
I8, I8,
I16, I16,
I32, I32,
U8,
U16,
U32,
F32, F32,
F64, F64,
isBytes,
assertBytes,
equalBytes,
constantTimeEqualBytes,
base64ToBytes,
bytesToBase64,
hexToBytes,
bytesToHex,
decodeBytes,
encodeBytes,
type TypedArray, type TypedArray,
type Encoding,
type HexEncoding, type HexEncoding,
type Base64Encoding, type Base64Encoding,
type Base64RawEncoding, type Base64RawEncoding,
type Base64URLEncoding, type Base64URLEncoding,
type Base64RawURLEncoding, type Base64RawURLEncoding,
} from './utils'; type Encoding
} from "./utils";

View File

@@ -1,9 +1,7 @@
/** /**
* Type definitions for the packet parsing library. * Type definitions for the packet parsing library.
*/ */
/** /**
* Enumeration of supported field types for dissecting packets. * Enumeration of supported field types for dissecting packets.
* *
@@ -13,51 +11,52 @@
*/ */
export const FieldType = { export const FieldType = {
// Boolean types // Boolean types
BOOL: 'BOOL', // Boolean value (stored as a byte, 0 for false, non-zero for true) BOOL: "BOOL", // Boolean value (stored as a byte, 0 for false, non-zero for true)
// Number types // Number types
BITS: 'BITS', // 1-bit values stored in a number (1 bit per value, packed into bytes) BITS: "BITS", // 1-bit values stored in a number (1 bit per value, packed into bytes)
INT8: 'INT8', // 8-bit signed integer (1 byte) CHAR: "CHAR", // 8-bit character (1 byte)
INT16_LE: 'INT16_LE', // 16-bit signed integer (little-endian) INT8: "INT8", // 8-bit signed integer (1 byte)
INT16_BE: 'INT16_BE', // 16-bit signed integer (big-endian) INT16_LE: "INT16_LE", // 16-bit signed integer (little-endian)
INT32_LE: 'INT32_LE', // 32-bit signed integer (little-endian) INT16_BE: "INT16_BE", // 16-bit signed integer (big-endian)
INT32_BE: 'INT32_BE', // 32-bit signed integer (big-endian) INT32_LE: "INT32_LE", // 32-bit signed integer (little-endian)
INT64_LE: 'INT64_LE', // 64-bit signed integer (little-endian) INT32_BE: "INT32_BE", // 32-bit signed integer (big-endian)
INT64_BE: 'INT64_BE', // 64-bit signed integer (big-endian) INT64_LE: "INT64_LE", // 64-bit signed integer (little-endian)
UINT8: 'UINT8', // 8-bit unsigned integer INT64_BE: "INT64_BE", // 64-bit signed integer (big-endian)
UINT16_LE: 'UINT16_LE', // 16-bit unsigned integer (little-endian) UINT8: "UINT8", // 8-bit unsigned integer
UINT16_BE: 'UINT16_BE', // 16-bit unsigned integer (big-endian) UINT16_LE: "UINT16_LE", // 16-bit unsigned integer (little-endian)
UINT32_LE: 'UINT32_LE', // 32-bit unsigned integer (little-endian) UINT16_BE: "UINT16_BE", // 16-bit unsigned integer (big-endian)
UINT32_BE: 'UINT32_BE', // 32-bit unsigned integer (big-endian) UINT32_LE: "UINT32_LE", // 32-bit unsigned integer (little-endian)
UINT64_LE: 'UINT64_LE', // 64-bit unsigned integer (little-endian) UINT32_BE: "UINT32_BE", // 32-bit unsigned integer (big-endian)
UINT64_BE: 'UINT64_BE', // 64-bit unsigned integer (big-endian) UINT64_LE: "UINT64_LE", // 64-bit unsigned integer (little-endian)
FLOAT32_LE: 'FLOAT32_LE', // 32-bit IEEE floating point (little-endian) UINT64_BE: "UINT64_BE", // 64-bit unsigned integer (big-endian)
FLOAT32_BE: 'FLOAT32_BE', // 32-bit IEEE floating point (big-endian) FLOAT32_LE: "FLOAT32_LE", // 32-bit IEEE floating point (little-endian)
FLOAT64_LE: 'FLOAT64_LE', // 64-bit IEEE floating point (little-endian) FLOAT32_BE: "FLOAT32_BE", // 32-bit IEEE floating point (big-endian)
FLOAT64_BE: 'FLOAT64_BE', // 64-bit IEEE floating point (big-endian) FLOAT64_LE: "FLOAT64_LE", // 64-bit IEEE floating point (little-endian)
VARINT: 'VARINT', // Variable-length integer (unsigned, LEB128 encoding) FLOAT64_BE: "FLOAT64_BE", // 64-bit IEEE floating point (big-endian)
VARSINT: 'VARSINT', // Variable-length integer (signed, LEB128 encoding) VARINT: "VARINT", // Variable-length integer (unsigned, LEB128 encoding)
VARSINT: "VARSINT", // Variable-length integer (signed, LEB128 encoding)
// Date/time types (stored as integer timestamps) // Date/time types (stored as integer timestamps)
DATE32_LE: 'DATE32_LE', // 32-bit integer date (e.g., seconds since epoch) little-endian DATE32_LE: "DATE32_LE", // 32-bit integer date (e.g., seconds since epoch) little-endian
DATE32_BE: 'DATE32_BE', // 32-bit integer date big-endian DATE32_BE: "DATE32_BE", // 32-bit integer date big-endian
DATE64_LE: 'DATE64_LE', // 64-bit integer date (e.g., ms since epoch) little-endian DATE64_LE: "DATE64_LE", // 64-bit integer date (e.g., ms since epoch) little-endian
DATE64_BE: 'DATE64_BE', // 64-bit integer date big-endian DATE64_BE: "DATE64_BE", // 64-bit integer date big-endian
// Array buffer types // Array buffer types
BYTES: 'BYTES', // 8-bits per value array (Uint8Array) BYTES: "BYTES", // 8-bits per value array (Uint8Array)
C_STRING: 'C_STRING', // Null-terminated string (C-style) (Uint8Array) C_STRING: "C_STRING", // Null-terminated string (C-style) (Uint8Array)
UTF8_STRING: 'UTF8_STRING', // UTF-8 encoded string (Uint8Array) UTF8_STRING: "UTF8_STRING", // UTF-8 encoded string (Uint8Array)
WORDS: 'WORDS', // 16-bits per value array (Uint16Array) WORDS: "WORDS", // 16-bits per value array (Uint16Array)
DWORDS: 'DWORDS', // 32-bits per value array (Uint32Array) DWORDS: "DWORDS", // 32-bits per value array (Uint32Array)
QWORDS: 'QWORDS', // 64-bits per value array (BigUint64Array) QWORDS: "QWORDS", // 64-bits per value array (BigUint64Array)
// Aliases // Aliases
FLAG: 'BOOL', // alternate name for boolean/flag fields FLAG: "BOOL", // alternate name for boolean/flag fields
STRING: 'UTF8_STRING', // alias for UTF8 encoded strings STRING: "UTF8_STRING" // alias for UTF8 encoded strings
} as const; } as const;
export type FieldType = typeof FieldType[keyof typeof FieldType]; export type FieldType = (typeof FieldType)[keyof typeof FieldType];
/** /**
* Interface for a packet, which can be dissected into segments and fields. This is a placeholder * Interface for a packet, which can be dissected into segments and fields. This is a placeholder
@@ -110,6 +109,7 @@ export type Dissected = Segment[];
export interface Segment { export interface Segment {
name: string; name: string;
data?: ArrayBuffer; // Optional raw data for the segment (if needed for parsing / serialization) data?: ArrayBuffer; // Optional raw data for the segment (if needed for parsing / serialization)
isString?: boolean; // Optional flag indicating if the segment represents a string (for special handling)
fields: Field[]; fields: Field[];
} }
@@ -120,6 +120,7 @@ export interface Segment {
export interface Field { export interface Field {
type: FieldType; type: FieldType;
name: string; name: string;
data?: ArrayBuffer; // Optional raw data for the field (if needed for parsing / serialization)
value?: unknown; // Optional value for the field (used for serialization or as a default value) value?: unknown; // Optional value for the field (used for serialization or as a default value)
bits?: BitField[]; // Optional array of bit field definitions (for BITS type) bits?: BitField[]; // Optional array of bit field definitions (for BITS type)
length?: number; // Optional length for array types (e.g., BYTES, WORDS) length?: number; // Optional length for array types (e.g., BYTES, WORDS)