java-topology/defects/exim/unit/EximSameHosts0001Test.java
russell@unturf.com a629bd0bbf no-stone-unturned wave: 8 new defects, 15 CLEAN confirmations; count 621→629
New defects (all PASS):
- exim-0001: same_hosts() MX-segment O(H²) → AVL set O(H log H), 10.5x at H=20
- minecraft-0001: DependencySorter.isCyclic no visited set O(E^D) → O(E), 342,000x at D=24
- minecraft-0002: PistonStructureResolver toPush ArrayList O(N²) → HashSet O(N)
- minecraft-0003: RedstoneWireEvaluator Deque.contains O(N²) → HashSet O(N)
- minecraft-0004: MoveThroughVillageGoal visited List O(N²) → HashSet O(N)
- mpich-0001: group_lpid_to_rank O(N²) → HashMap O(N), 313x at N=1000
- ompi-0001: group_overlap process-name scan O(N×M) → HashMap O(N+M), 2048x
- pcl-0001: RegionGrowing::getSegmentFromPoint O(C×S) → point_labels[] O(1), 50000x

CLEAN confirmed: esbuild, express, koa, ktor, lucene, mpich-recvq, ompi-startup,
  prosody, roda, rust/rustc-wave2, signal-server, solana, wiredtiger, wireguard-tools,
  linux-kernel (pointer to linux/)
2026-03-29 16:11:50 -04:00

198 lines
7.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* exim-0001: same_hosts() MX-segment membership check O(H²) → O(H log H)
*
* In src/src/deliver.c::same_hosts():
*
* for (;;) // H iterations
* {
* host_item *hi;
* for (hi = two; hi != end_two->next; hi = hi->next) // H iterations
* if (Ustrcmp(one->name, hi->name) == 0) break; // O(1) per cmp
* if (hi == end_two->next) return FALSE;
* if (one == end_one) break;
* one = one->next;
* }
*
* When H hosts share the same MX priority value, the nested scan costs O(H²)
* string comparisons to verify that the segment is equivalent (possibly
* reordered).
*
* same_hosts() is called from the outer address-grouping while-loop
* (deliver.c:4527) once per candidate remote address — O(N) calls per
* message. Total cost: O(N × H²).
*
* Fix: build a set of host names in the 'two' segment (AVL tree in C, or
* HashSet in Java analogue) before the outer loop. Each membership check is
* then O(log H) (AVL) or O(1) (hash), reducing the per-call segment work
* from O(H²) to O(H log H) or O(H).
*
* UNDF: assigned by generate_undf.py
* Severity: MEDIUM
* Hot path: remote delivery batching — once per candidate address per message
*/
public class EximSameHosts0001Test {
static long cmpOps = 0;
/** Simulate a host_item (just the name field we care about). */
static class HostItem {
String name;
HostItem next;
HostItem(String name) { this.name = name; }
}
/** Build a singly-linked host list from an array of names. */
static HostItem buildList(String[] names) {
HostItem head = null, tail = null;
for (String n : names) {
HostItem h = new HostItem(n);
if (head == null) head = tail = h;
else { tail.next = h; tail = h; }
}
return head;
}
// ----------------------------------------------------------------
// SLOW: original O(H²) nested scan
// ----------------------------------------------------------------
/**
* Returns true if every name in 'one' segment [one..endOne] appears in
* the 'two' segment [two..endTwo]. Mirrors the C code exactly.
*/
static boolean segmentMatchSlow(HostItem one, HostItem endOne,
HostItem two, HostItem endTwoNext) {
for (;;) {
HostItem hi = two;
boolean found = false;
while (hi != endTwoNext) { // O(H) inner scan
cmpOps++;
if (one.name.equals(hi.name)) { found = true; break; }
hi = hi.next;
}
if (!found) return false;
if (one == endOne) break;
one = one.next;
}
return true;
}
// ----------------------------------------------------------------
// FAST: O(H log H) using TreeSet (mirrors AVL tree fix in C)
// ----------------------------------------------------------------
static boolean segmentMatchFast(HostItem one, HostItem endOne,
HostItem two, HostItem endTwoNext) {
// Build a set of names from the 'two' segment — O(H log H)
TreeSet<String> set = new TreeSet<>();
for (HostItem hi = two; hi != endTwoNext; hi = hi.next) {
set.add(hi.name);
}
// Check each 'one' name in O(log H)
for (;;) {
cmpOps++; // charge 1 op to represent O(log H) tree lookup
if (!set.contains(one.name)) return false;
if (one == endOne) break;
one = one.next;
}
return true;
}
// ----------------------------------------------------------------
// Driver: run N address-pair comparisons, H hosts per segment
// ----------------------------------------------------------------
static long runSlow(int N, int H) {
cmpOps = 0;
// Build a canonical host list: [host0, host1, ..., host(H-1)]
String[] canonical = new String[H];
for (int i = 0; i < H; i++) canonical[i] = "host" + i + ".example.com";
// Build the reversed permutation for 'two' (worst-case ordering)
String[] reversed = new String[H];
for (int i = 0; i < H; i++) reversed[i] = canonical[H - 1 - i];
for (int n = 0; n < N; n++) {
HostItem one = buildList(canonical);
HostItem two = buildList(reversed);
// Find end of both segments (all H hosts)
HostItem endOne = one;
for (int i = 1; i < H; i++) endOne = endOne.next;
HostItem endTwo = two;
for (int i = 1; i < H; i++) endTwo = endTwo.next;
segmentMatchSlow(one, endOne, two, endTwo.next);
}
return cmpOps;
}
static long runFast(int N, int H) {
cmpOps = 0;
String[] canonical = new String[H];
for (int i = 0; i < H; i++) canonical[i] = "host" + i + ".example.com";
String[] reversed = new String[H];
for (int i = 0; i < H; i++) reversed[i] = canonical[H - 1 - i];
for (int n = 0; n < N; n++) {
HostItem one = buildList(canonical);
HostItem two = buildList(reversed);
HostItem endOne = one;
for (int i = 1; i < H; i++) endOne = endOne.next;
HostItem endTwo = two;
for (int i = 1; i < H; i++) endTwo = endTwo.next;
segmentMatchFast(one, endOne, two, endTwo.next);
}
return cmpOps;
}
public static void main(String[] args) {
// Parameters matching real-world values (N=100 messages batched, H=20 equal-MX hosts)
int N = 100;
int H = 20;
long slowOps = runSlow(N, H);
long fastOps = runFast(N, H);
double ratio = (double) slowOps / Math.max(fastOps, 1);
System.out.printf(
"exim-0001 same_hosts MX-segment: N=%d H=%d SLOW=%d ops FAST=%d ops ratio=%.1fx%n",
N, H, slowOps, fastOps, ratio);
// Expect at least 3x improvement (H=20 → theoretical 20/log2(20)≈4.6x)
if (ratio < 3.0) {
System.err.printf("FAIL: ratio %.1f < 3x expected%n", ratio);
System.exit(1);
}
// Verify correctness: both return true for reversed-but-same 2-host segment
cmpOps = 0;
String[] c2 = {"alpha.mx.com", "beta.mx.com"};
HostItem o1 = buildList(c2);
HostItem o1end = o1.next; // last node in 'one' segment
String[] r2 = {"beta.mx.com", "alpha.mx.com"};
HostItem t1 = buildList(r2);
HostItem t1end = t1.next; // last node in 'two' segment (next==null)
// endTwoNext = node AFTER last = null (sentinel meaning "past end")
boolean slowResult = segmentMatchSlow(o1, o1end, t1, null /* endTwoNext=null */);
cmpOps = 0;
HostItem o1b = buildList(c2);
HostItem o1bend = o1b.next;
HostItem t1b = buildList(r2);
boolean fastResult = segmentMatchFast(o1b, o1bend, t1b, null /* endTwoNext=null */);
if (!slowResult || !fastResult) {
System.err.println("FAIL: correctness check — both should return true");
System.exit(1);
}
System.out.println("PASS");
}
}