// 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; }