feat(core): RIP after Java's code
Where the Swift template departs from Java's filius.software.rip, Java's code rules: a learned route gets 16 hops at the router's next beacon after it expired (RIPTable.check), not when it expires; messages count from any source port; each router draws its pauses from a random of its own (Java: an unseeded Random per router), which the core seeds from the scenario and the router's id (ripSeed), so that the Java oracle can give it the same. A route of hop count 0 to each own network, the first beacon after a second, then every 25.2 to 35.2 s; a message per interface without the routes through it; a new route or a longer one from the same router sends at once; forwarding by the table alone, fewest hops before longest mask. Messages are read with Java's parser (javaIpCheck, javaParseInt, moved from fls to core for it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,8 @@
|
||||
// both wrong; see isDomainName), the type A, MX or NS is read in any case
|
||||
// (RFC 4343), and a negative TTL is invalid (RFC 2181).
|
||||
|
||||
import type { DnsResourceRecord } from '@mdg-filius/core';
|
||||
import { javaIpCheck, javaParseInt, type DnsResourceRecord } from '@mdg-filius/core';
|
||||
import { javaFileContent } from './javaFileSystem.js';
|
||||
import { javaIpCheck, javaParseInt } from './javaIp.js';
|
||||
import { javaTrim } from './javaString.js';
|
||||
import type { FlsFileTree } from './systemSoftware.js';
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
type EmailServerConfiguration,
|
||||
} from '@mdg-filius/core';
|
||||
import { addressEntry, type JavaEmail, type JavaEmailAccount, type JavaRecipient } from './javaEmail.js';
|
||||
import { javaParseInt } from './javaIp.js';
|
||||
import { javaParseInt } from '@mdg-filius/core';
|
||||
import type { FlsApplication } from './systemSoftware.js';
|
||||
|
||||
type Warn = (message: string) => void;
|
||||
|
||||
@@ -4,7 +4,6 @@ export * from './dnsHosts.js';
|
||||
export * from './importProject.js';
|
||||
export * from './javaEmail.js';
|
||||
export * from './javaFileSystem.js';
|
||||
export * from './javaIp.js';
|
||||
export * from './javaString.js';
|
||||
export * from './javabeans.js';
|
||||
export * from './systemSoftware.js';
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
// What Java FILIUS stores when a file sets an interface's IP address or mask:
|
||||
// NetzwerkInterface.setIp(ip) keeps IP.ipCheck(ip), which is null for text
|
||||
// that is not an address and the canonical dotted quad otherwise.
|
||||
// Java counterpart: filius.software.vermittlungsschicht.IP.inetAton/ipCheck.
|
||||
//
|
||||
// Measured against Java (test/ip-check.cases.txt, tools/java-oracle), since
|
||||
// the rules are wider than they look:
|
||||
// - StringTokenizer(".") skips empty tokens: "10..0.0.1" is 10.0.0.1;
|
||||
// - Integer.parseInt takes a sign and leading zeros: "+010" is 10, "-0" is 0;
|
||||
// - and any Unicode decimal digit in the BMP: Arabic-Indic "١٠" is 10 —
|
||||
// parseInt reads UTF-16 units, so "𝟏" (a surrogate pair) is no digit.
|
||||
|
||||
/**
|
||||
* `Integer.parseInt(text)`: an optional sign, then one or more decimal digits
|
||||
* of any script; undefined where Java throws NumberFormatException.
|
||||
*/
|
||||
export function javaParseInt(text: string): number | undefined {
|
||||
const characters = text.split('');
|
||||
let index = 0;
|
||||
let negative = false;
|
||||
if (characters[0] === '-' || characters[0] === '+') {
|
||||
negative = characters[0] === '-';
|
||||
index = 1;
|
||||
}
|
||||
if (index >= characters.length) return undefined;
|
||||
let value = 0;
|
||||
for (; index < characters.length; index++) {
|
||||
const digit = decimalDigitValue(characters[index]!);
|
||||
if (digit === undefined) return undefined;
|
||||
value = value * 10 + digit;
|
||||
// Past the int range Java throws; the octet check would reject it anyway.
|
||||
if (value > 2 ** 31) return undefined;
|
||||
}
|
||||
if (!negative && value > 2 ** 31 - 1) return undefined;
|
||||
return negative ? -value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* `Character.digit(c, 10)` for one UTF-16 unit: Unicode lays out every
|
||||
* script's digits as a run of ten from zero to nine, so the value is the
|
||||
* distance from the start of the run, modulo ten (in case two runs touch).
|
||||
*/
|
||||
function decimalDigitValue(unit: string): number | undefined {
|
||||
if (!/^\p{Nd}$/u.test(unit)) return undefined;
|
||||
let code = unit.charCodeAt(0);
|
||||
let offset = 0;
|
||||
while (/^\p{Nd}$/u.test(String.fromCharCode(code - 1))) {
|
||||
code -= 1;
|
||||
offset += 1;
|
||||
}
|
||||
return offset % 10;
|
||||
}
|
||||
|
||||
/** `IP.ipCheck(text)`: four octets 0…255 as `a.b.c.d`, or null. */
|
||||
export function javaIpCheck(text: string): string | null {
|
||||
const tokens = text.split('.').filter((token) => token !== '');
|
||||
if (tokens.length !== 4) return null;
|
||||
const octets: number[] = [];
|
||||
for (const token of tokens) {
|
||||
const octet = javaParseInt(token);
|
||||
if (octet === undefined || octet < 0 || octet > 255) return null;
|
||||
octets.push(octet);
|
||||
}
|
||||
return octets.join('.');
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
// mask, upper-cased MAC).
|
||||
|
||||
import { isJavaObject, isUnresolved, listItems, simpleClassName, type JavaObject, type JavaValue } from './javabeans.js';
|
||||
import { javaIpCheck } from './javaIp.js';
|
||||
import { javaIpCheck } from '@mdg-filius/core';
|
||||
import { readSystemSoftware, type FlsSystemSoftware } from './systemSoftware.js';
|
||||
|
||||
export type FlsNodeClass = 'Rechner' | 'Notebook' | 'Switch' | 'Vermittlungsrechner' | 'Gateway' | 'Modem';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { readFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { javaIpCheck, javaParseInt } from '../src/javaIp.js';
|
||||
import { javaIpCheck, javaParseInt } from '@mdg-filius/core';
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const measured = JSON.parse(readFileSync(join(here, 'ip-check.java.json'), 'utf8')) as [string, string | null][];
|
||||
|
||||
Reference in New Issue
Block a user