wave16: dart-0001/2/3 + octave-0002 + php-0003/4 + cassandra-0005 + erlang-0003 + chef-0001 — 542/240
This commit is contained in:
parent
31850d5ef6
commit
d816d3c74c
18 changed files with 1902 additions and 6 deletions
178
defects/erlang/unit/ErlangAlgorithm.java
Normal file
178
defects/erlang/unit/ErlangAlgorithm.java
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
package unit;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Erlang/OTP CWE-407 unit tests — standalone, no JUnit.
|
||||
*
|
||||
* erlang-0001 digraph.erl one_path() — lists:member on growing visited-list
|
||||
* lib/stdlib/src/digraph.erl (patch: erlang-0001-one-path-sets.patch)
|
||||
*
|
||||
* erlang-0003 code_server.erl merge_path1() — lists:member(P, Acc) in recursive loop
|
||||
* lib/kernel/src/code_server.erl lines 600-609
|
||||
* Acc grows as each unique path is prepended; lists:member is O(|Acc|)
|
||||
* per call → O(N²) total for N unique paths.
|
||||
* Fix: track seen paths in a HashSet alongside the accumulator list.
|
||||
*/
|
||||
public class ErlangAlgorithm {
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// erlang-0001: digraph one_path — lists:member on visited list
|
||||
//
|
||||
// In DFS graph path-finding, each node visited checks membership against
|
||||
// the accumulated visited list (Xs in the Erlang code).
|
||||
// Slow: ArrayList.contains() — O(depth) per node
|
||||
// Fast: HashSet.contains() — O(1) per node
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Simulate one_path DFS traversal with a linear visited-list (Erlang original).
|
||||
* Graph: a long linear chain 0→1→2→...→N-1
|
||||
* Returns total comparison operations performed.
|
||||
*/
|
||||
static long onePathSlowOps(int n) {
|
||||
// visited list, mirrors Erlang's Xs accumulator (a plain list)
|
||||
List<Integer> visited = new ArrayList<>(n);
|
||||
long ops = 0;
|
||||
|
||||
// Simulate DFS: at each step, check if next node is already visited
|
||||
for (int node = 0; node < n; node++) {
|
||||
// lists:member(node, visited) — linear scan
|
||||
boolean found = false;
|
||||
for (int i = 0; i < visited.size(); i++) {
|
||||
ops++;
|
||||
if (visited.get(i).equals(node)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
visited.add(node);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate one_path DFS traversal with a HashSet visited-set (patched).
|
||||
* Returns total comparison operations performed.
|
||||
*/
|
||||
static long onePathFastOps(int n) {
|
||||
Set<Integer> visited = new HashSet<>(n);
|
||||
long ops = 0;
|
||||
|
||||
for (int node = 0; node < n; node++) {
|
||||
ops++; // O(1) hash lookup — count as single op
|
||||
if (!visited.contains(node)) {
|
||||
visited.add(node);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// erlang-0003: code_server merge_path1 — lists:member(P, Acc) per unique path
|
||||
//
|
||||
// merge_path1/3 recursively processes a list of N directory paths.
|
||||
// For each new path P, it checks whether P is already in Acc (the output
|
||||
// accumulator) using lists:member/2, which is O(|Acc|).
|
||||
// Since Acc grows by 1 for each unique P, total cost = 1+2+...+N = O(N²).
|
||||
//
|
||||
// This runs at VM startup (add_loader_path) and on every code:add_paths/1 call.
|
||||
// Large Elixir/OTP deployments routinely have N=500-2000 library directories.
|
||||
//
|
||||
// File: lib/kernel/src/code_server.erl lines 600-609
|
||||
//
|
||||
// Slow: ArrayList.contains() — O(|Acc|) per path → O(N²) total
|
||||
// Fast: HashSet.contains() — O(1) per path → O(N) total
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Simulate merge_path1 with the original List accumulator.
|
||||
* Input: N unique directory paths.
|
||||
* Returns total comparison operations performed.
|
||||
*/
|
||||
static long mergePathSlowOps(int n) {
|
||||
// Acc: the accumulator list (paths seen so far, prepended)
|
||||
List<String> acc = new ArrayList<>(n);
|
||||
long ops = 0;
|
||||
|
||||
// Simulate the IPath list (empty — all paths are new)
|
||||
// merge_path1([P|Path], IPath, Acc) → member check on Acc
|
||||
for (int i = 0; i < n; i++) {
|
||||
String path = "/usr/lib/erlang/lib/module-" + i + "/ebin";
|
||||
|
||||
// lists:member(P, Acc) — O(|Acc|) linear scan
|
||||
boolean found = false;
|
||||
for (int j = 0; j < acc.size(); j++) {
|
||||
ops++;
|
||||
if (acc.get(j).equals(path)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// IPath1 = exclude(P, IPath) — for empty IPath this is a no-op
|
||||
acc.add(path); // [P|Acc] — prepend (modelled as add at end)
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate merge_path1 with the patched HashSet seen-set alongside the accumulator.
|
||||
* Returns total comparison operations performed.
|
||||
*/
|
||||
static long mergePathFastOps(int n) {
|
||||
List<String> acc = new ArrayList<>(n);
|
||||
Set<String> seen = new HashSet<>(n);
|
||||
long ops = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
String path = "/usr/lib/erlang/lib/module-" + i + "/ebin";
|
||||
ops++; // sets:is_element — O(1)
|
||||
if (!seen.contains(path)) {
|
||||
seen.add(path);
|
||||
acc.add(path);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test runner
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
|
||||
// erlang-0001: digraph one_path
|
||||
int[] graphSizes = {100, 300, 500, 1000};
|
||||
for (int n : graphSizes) {
|
||||
long slow = onePathSlowOps(n);
|
||||
long fast = onePathFastOps(n);
|
||||
boolean pass = slow >= fast * 5;
|
||||
System.out.printf(
|
||||
"erlang-0001 N=%-5d slow=%8d fast=%8d ratio=%.1fx %s%n",
|
||||
n, slow, fast, (double) slow / fast, pass ? "PASS" : "FAIL");
|
||||
if (pass) passed++; else failed++;
|
||||
}
|
||||
|
||||
// erlang-0003: code_server merge_path1
|
||||
// N represents number of unique library directories (typical: 200-2000)
|
||||
int[] pathCounts = {200, 500, 1000, 2000};
|
||||
for (int n : pathCounts) {
|
||||
long slow = mergePathSlowOps(n);
|
||||
long fast = mergePathFastOps(n);
|
||||
// Expected ratio: ~N/2 (triangular sum / N = N/2)
|
||||
boolean pass = slow >= fast * 5;
|
||||
System.out.printf(
|
||||
"erlang-0003 N=%-5d slow=%8d fast=%8d ratio=%.1fx %s%n",
|
||||
n, slow, fast, (double) slow / fast, pass ? "PASS" : "FAIL");
|
||||
if (pass) passed++; else failed++;
|
||||
}
|
||||
|
||||
System.out.printf("%nTotal: %d/%d PASS%n", passed, passed + failed);
|
||||
if (failed > 0) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue