bird-0003/0004 + frrouting-0003/0004: BGP community set ops O(N×M)/O(N²); OpenBGPD CLEAN; count 595→599

This commit is contained in:
russell@unturf.com 2026-03-27 22:30:39 -04:00
parent 3f8c5e6d3e
commit ff7bd27654
11 changed files with 671 additions and 4 deletions

View file

@ -0,0 +1,153 @@
package unit;
import java.util.*;
/**
* Models BIRD routing daemon community set operations.
*
* bird-0003: int_set_union O(N×M) contains check in union loop
* bird-0004: clist_filter O(L×S) contains check in filter loop
*
* SLOW: O(N×M) union / O(L×S) filter linear scan per element
* FAST: O(N+M) union / O(L+S) filter HashSet pre-built from second operand
*
* CWE-407: nest/a-set.c:394 (union), filter/data.c:421 (filter)
*/
public class BirdCommunitySetAlgorithmTest {
// -------------------------------------------------------------------------
// Slow (defective) mirrors int_set_contains in loop
// -------------------------------------------------------------------------
static class SlowCommunityOps {
long scanOps = 0;
boolean contains(int[] arr, int val) {
for (int v : arr) {
scanOps++;
if (v == val) return true;
}
return false;
}
/** bird-0003: O(N×M) union */
int[] union(int[] l1, int[] l2) {
List<Integer> result = new ArrayList<>();
for (int v : l1) result.add(v);
for (int v : l2) {
if (!contains(l1, v)) result.add(v);
}
return result.stream().mapToInt(Integer::intValue).toArray();
}
/** bird-0004: O(L×S) filter — keep entries in l1 that are in filterSet */
int[] filter(int[] l1, int[] filterSet) {
List<Integer> result = new ArrayList<>();
for (int v : l1) {
if (contains(filterSet, v)) result.add(v);
}
return result.stream().mapToInt(Integer::intValue).toArray();
}
long unionOps(int[] l1, int[] l2) { scanOps = 0; union(l1, l2); return scanOps; }
long filterOps(int[] l1, int[] filterSet) { scanOps = 0; filter(l1, filterSet); return scanOps; }
}
// -------------------------------------------------------------------------
// Fast (fixed) HashSet from one operand
// -------------------------------------------------------------------------
static class FastCommunityOps {
long scanOps = 0;
int[] union(int[] l1, int[] l2) {
Set<Integer> seen = new HashSet<>();
for (int v : l1) { seen.add(v); scanOps++; }
List<Integer> result = new ArrayList<>(Arrays.stream(l1).boxed().toList());
for (int v : l2) {
scanOps++;
if (seen.add(v)) result.add(v);
}
return result.stream().mapToInt(Integer::intValue).toArray();
}
int[] filter(int[] l1, int[] filterSet) {
Set<Integer> fs = new HashSet<>();
for (int v : filterSet) { fs.add(v); scanOps++; }
List<Integer> result = new ArrayList<>();
for (int v : l1) {
scanOps++;
if (fs.contains(v)) result.add(v);
}
return result.stream().mapToInt(Integer::intValue).toArray();
}
long unionOps(int[] l1, int[] l2) { scanOps = 0; union(l1, l2); return scanOps; }
long filterOps(int[] l1, int[] filterSet) { scanOps = 0; filter(l1, filterSet); return scanOps; }
}
static int[] range(int from, int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = from + i;
return a;
}
public static void main(String[] args) {
SlowCommunityOps slow = new SlowCommunityOps();
FastCommunityOps fast = new FastCommunityOps();
int passed = 0, total = 0;
System.out.println("=== bird-0003: int_set_union O(N×M) ===");
int[] sizes = {50, 100, 200, 500};
for (int n : sizes) {
int[] l1 = range(0, n);
int[] l2 = range(n / 2, n); // partial overlap
long s = slow.unionOps(l1, l2);
long f = fast.unionOps(l1, l2);
double ratio = (double) s / f;
total++;
boolean ok = s > f && ratio >= 5.0;
System.out.printf("N=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
n, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
System.out.println("=== bird-0004: clist_filter O(L×S) ===");
for (int n : sizes) {
int[] l1 = range(0, n); // route's community list
int[] filterSet = range(n/4, n/2); // keep middle half
long s = slow.filterOps(l1, filterSet);
long f = fast.filterOps(l1, filterSet);
double ratio = (double) s / f;
total++;
boolean ok = s > f && ratio >= 3.0;
System.out.printf("L=%3d S=%3d slow=%8d fast=%5d ratio=%6.1fx %s%n",
n, n/2, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: union contains all unique elements
int[] a = range(0, 100), b = range(50, 100);
int[] slowUnion = slow.union(a, b);
int[] fastUnion = fast.union(a, b);
Arrays.sort(slowUnion); Arrays.sort(fastUnion);
total++;
boolean correct1 = Arrays.equals(slowUnion, fastUnion);
System.out.printf("union correctness: %s%n", correct1 ? "PASS" : "FAIL");
if (correct1) passed++;
// Correctness: filter returns same kept elements
int[] l = range(0, 100), fs = range(20, 60);
int[] slowFilter = slow.filter(l, fs);
int[] fastFilter = fast.filter(l, fs);
Arrays.sort(slowFilter); Arrays.sort(fastFilter);
total++;
boolean correct2 = Arrays.equals(slowFilter, fastFilter);
System.out.printf("filter correctness: %s%n", correct2 ? "PASS" : "FAIL");
if (correct2) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}