feat(core): event queue, Ethernet, switch, ARP, IPv4, ICMP, routing and firewall

First slice of the TypeScript port of the filius-ipad runtime engine,
with 29 tests ported from the Swift suite. Parts not ported yet throw
NotPortedError instead of guessing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-29 19:22:57 +00:00
co-authored by Claude Opus 5.5
parent 7caee33b3d
commit 696334aa5a
29 changed files with 6199 additions and 11 deletions
+48
View File
@@ -0,0 +1,48 @@
// Ported from filius-ipad (GPLv2/GPLv3):
// State/TopologyNetworkRuntimeEngine.swift (parseIPv4, sameSubnet)
// State/TopologyEditorState.swift (TopologyRuntimeManualRoute.parseIPv4)
//
// Two parsers exist on purpose, as in the Swift source: the runtime reads an
// octet the way Swift's `UInt8(String)` does, the route editor the way Java
// FILIUS validates its table cells (see routeTable.ts).
/**
* Swift `UInt8(segment)`: an optional sign, then decimal digits, value 0…255.
* Leading zeros are allowed ("007" is 7), whitespace is not.
*/
function parseSwiftOctet(segment: string): number | undefined {
if (!/^[+-]?[0-9]+$/.test(segment)) return undefined;
const value = Number(segment);
if (!Number.isInteger(value) || value < 0 || value > 255) return undefined;
return Object.is(value, -0) ? 0 : value;
}
/** Dotted quad to an unsigned 32-bit number, or undefined. */
export function parseIPv4(value: string): number | undefined {
const segments = value.split('.');
if (segments.length !== 4) return undefined;
let address = 0;
for (const segment of segments) {
const octet = parseSwiftOctet(segment);
if (octet === undefined) return undefined;
address = ((address << 8) | octet) >>> 0;
}
return address;
}
export function formatIPv4(address: number): string {
return [24, 16, 8, 0].map((shift) => String((address >>> shift) & 0xff)).join('.');
}
export function sameSubnet(lhs: string, rhs: string, mask: string): boolean {
const left = parseIPv4(lhs);
const right = parseIPv4(rhs);
const netmask = parseIPv4(mask);
if (left === undefined || right === undefined || netmask === undefined) return false;
return ((left & netmask) >>> 0) === ((right & netmask) >>> 0);
}
/** `address & mask` as an unsigned number. */
export function maskAddress(address: number, mask: number): number {
return (address & mask) >>> 0;
}