package unit; import java.util.*; /** * thrift-0001: t_cpp_generator is_struct_storage_not_throwing() std::find O(M^2) → O(1) * * Simulates the defective pattern from Apache Thrift's C++ generator: * is_struct_storage_not_throwing() flattens nested struct members using * std::find on a vector for deduplication. * When N nested structs each contribute M fields, total ops = O(N×M×total). * * Fixed: std::unordered_set for O(1) membership test. * * Run: javac -d . ThriftStructMembersAlgorithm.java && java unit.ThriftStructMembersAlgorithm */ public class ThriftStructMembersAlgorithm { // Simulated t_field* — each instance is a unique pointer static class Field { final String name; final boolean isStruct; final List nestedFields; Field(String name) { this.name = name; this.isStruct = false; this.nestedFields = null; } Field(String name, List nested) { this.name = name; this.isStruct = true; this.nestedFields = nested; } } // ---- Result ---- static class Result { final long ops; final boolean noexcept; Result(long ops, boolean noexcept) { this.ops = ops; this.noexcept = noexcept; } } // ---- Defective: std::find on vector — O(M) per dedup check ---- static class DefectiveStructAnalyzer { long opCount = 0; boolean isStructStorageNotThrowing(List topLevelMembers) { List members = new ArrayList<>(topLevelMembers); for (int i = 0; i < members.size(); i++) { Field field = members.get(i); if (field.isStruct && field.nestedFields != null) { for (Field nested : field.nestedFields) { // std::find: O(members.size()) scan boolean found = false; for (Field m : members) { opCount++; if (m == nested) { found = true; break; } } if (!found) { members.add(nested); } } } // Simulate non-struct field checks (constant work) } return true; } } // ---- Fixed: unordered_set O(1) dedup ---- static class FixedStructAnalyzer { long opCount = 0; boolean isStructStorageNotThrowing(List topLevelMembers) { List members = new ArrayList<>(topLevelMembers); Set seen = new IdentityHashMap() {{ for (Field f : members) put(f, Boolean.TRUE); }}.keySet(); // Use IdentityHashMap to simulate pointer-based set (reference equality) Set seenSet = Collections.newSetFromMap(new IdentityHashMap<>()); seenSet.addAll(topLevelMembers); for (int i = 0; i < members.size(); i++) { Field field = members.get(i); if (field.isStruct && field.nestedFields != null) { for (Field nested : field.nestedFields) { opCount++; // O(1) hash lookup if (!seenSet.contains(nested)) { members.add(nested); seenSet.add(nested); } } } } return true; } } // ---- check helper ---- static int passed = 0; static int total = 0; static void check(String name, boolean condition) { total++; if (condition) { passed++; System.out.println(" PASS: " + name); } else { System.out.println(" FAIL: " + name); } } // Build a struct with N fields, each of which is a struct with M nested fields static List buildNestedStruct(int topN, int nestedM, int nestDepth) { if (nestDepth == 0) { List leaves = new ArrayList<>(); for (int i = 0; i < nestedM; i++) leaves.add(new Field("leaf_" + i)); return leaves; } List inner = buildNestedStruct(topN, nestedM, nestDepth - 1); List result = new ArrayList<>(); for (int i = 0; i < topN; i++) { result.add(new Field("struct_" + nestDepth + "_" + i, new ArrayList<>(inner))); } return result; } public static void main(String[] args) { System.out.println("thrift-0001: is_struct_storage_not_throwing std::find O(M^2) -> O(1)"); System.out.println(); // ---- Test 1: Simple flat struct (no nesting) — both same ---- { List members = new ArrayList<>(); for (int i = 0; i < 10; i++) members.add(new Field("field_" + i)); DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer(); FixedStructAnalyzer fast = new FixedStructAnalyzer(); slow.isStructStorageNotThrowing(members); fast.isStructStorageNotThrowing(members); System.out.println("Test 1: flat struct, 10 fields (no nesting)"); System.out.println(" Slow ops: " + slow.opCount + ", Fast ops: " + fast.opCount); check("both handle flat struct (ops >= 0)", slow.opCount >= 0 && fast.opCount >= 0); } // ---- Test 2: Nested struct (5 top-level, each nested 10 fields) ---- { // Build shared nested fields (same pointers = dedup matters) List sharedNested = new ArrayList<>(); for (int i = 0; i < 10; i++) sharedNested.add(new Field("nested_" + i)); List members = new ArrayList<>(); for (int i = 0; i < 5; i++) { members.add(new Field("struct_" + i, sharedNested)); } DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer(); FixedStructAnalyzer fast = new FixedStructAnalyzer(); slow.isStructStorageNotThrowing(members); fast.isStructStorageNotThrowing(members); System.out.println("Test 2: 5 struct fields each with 10 nested fields"); System.out.println(" Slow ops: " + slow.opCount + ", Fast ops: " + fast.opCount); check("slow > fast (vector scan > hash set)", slow.opCount >= fast.opCount); } // ---- Test 3: Large nested struct — O(M^2) regime ---- { // 20 struct fields each with 30 unique nested fields int topN = 20, nestedM = 30; List members = new ArrayList<>(); for (int i = 0; i < topN; i++) { List nested = new ArrayList<>(); for (int j = 0; j < nestedM; j++) nested.add(new Field("n_" + i + "_" + j)); members.add(new Field("s_" + i, nested)); } DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer(); FixedStructAnalyzer fast = new FixedStructAnalyzer(); slow.isStructStorageNotThrowing(members); fast.isStructStorageNotThrowing(members); System.out.println("Test 3: 20 struct fields × 30 nested fields each (M=" + topN*nestedM + ")"); System.out.println(" Slow ops: " + slow.opCount + ", Fast ops: " + fast.opCount); double ratio = (double) slow.opCount / Math.max(fast.opCount, 1); System.out.printf(" Ratio slow/fast: %.1fx%n", ratio); check("slow > 5x fast for M=600", ratio > 5.0); } // ---- Test 4: O(M^2) scaling — doubling members quadruples slow ops ---- { // All unique nested fields → no early termination in std::find int[] sizes = {10, 20}; long[] slowOps = new long[2]; for (int s = 0; s < 2; s++) { int N = sizes[s]; List members = new ArrayList<>(); for (int i = 0; i < N; i++) { List nested = new ArrayList<>(); for (int j = 0; j < N; j++) nested.add(new Field("n_" + i + "_" + j)); members.add(new Field("struct_" + i, nested)); } DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer(); slow.isStructStorageNotThrowing(members); slowOps[s] = slow.opCount; } System.out.println("Test 4: O(M^2) scaling"); System.out.println(" N=10 ops: " + slowOps[0]); System.out.println(" N=20 ops: " + slowOps[1]); double ratio = (double) slowOps[1] / slowOps[0]; System.out.printf(" Scaling: %.2fx (expect >3x for O(M^2))%n", ratio); check("ops grow super-linearly when N doubled (>3x)", ratio > 3.0); } // ---- Test 5: Correctness — same result (noexcept) ---- { List nested = Arrays.asList(new Field("x"), new Field("y")); List members = Arrays.asList( new Field("a", nested), new Field("b", nested), new Field("c") ); DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer(); FixedStructAnalyzer fast = new FixedStructAnalyzer(); boolean slowResult = slow.isStructStorageNotThrowing(members); boolean fastResult = fast.isStructStorageNotThrowing(members); System.out.println("Test 5: correctness — both return same noexcept result"); check("slow and fast return same result", slowResult == fastResult); } System.out.println(); System.out.println(passed + "/" + total + " PASS"); } }