import java.util.*; /** * Unit test for dosbox-x-0003: jtbs/dbox vector> O(N^2) membership * in Mouse_GetSelected nested row/col loops. * * Models the DOSBox-X defect where std::find is called on a vector of (row,col) * pairs inside nested loops over all screen positions. For a full DBCS screen * (80 cols x 25 rows), jtbs can hold ~2000 entries; each position calls find * making the total O(rows * cols * N_entries) = O(N^2). * * The fix encodes (row,col) as a single int key and stores in a HashSet, * making each membership test O(1). */ public class DosboxXJtbsDboxTest { // --- Defect: vector> with linear find --- static List buildVectorList(int screenCols, int screenRows) { List list = new ArrayList<>(); // Simulate: every other column is a double-byte char (DBCS), producing (row,col) pairs for (int row = 0; row < screenRows; row++) { for (int col = 0; col < screenCols; col += 2) { list.add(new long[]{row, col}); } } return list; } static boolean vectorContains(List list, int row, int col) { for (long[] pair : list) { if (pair[0] == row && pair[1] == col) return true; } return false; } static int countHitsVector(List jtbs, int r1, int r2, int c1, int c2) { int hits = 0; for (int i = r1; i <= r2; i++) { for (int j = c1; j <= c2; j++) { if (vectorContains(jtbs, i, j)) hits++; } } return hits; } // --- Fix: unordered_set encoded as (row<<16)|col --- static int encodePos(int row, int col) { return ((row & 0xFFFF) << 16) | (col & 0xFFFF); } static Set buildHashSet(int screenCols, int screenRows) { Set set = new HashSet<>(); for (int row = 0; row < screenRows; row++) { for (int col = 0; col < screenCols; col += 2) { set.add(encodePos(row, col)); } } return set; } static int countHitsHashSet(Set jtbs, int r1, int r2, int c1, int c2) { int hits = 0; for (int i = r1; i <= r2; i++) { for (int j = c1; j <= c2; j++) { if (jtbs.contains(encodePos(i, j))) hits++; } } return hits; } public static void main(String[] args) { // Small correctness test int smallCols = 10, smallRows = 5; List vec = buildVectorList(smallCols, smallRows); Set set = buildHashSet(smallCols, smallRows); int vecHits = countHitsVector(vec, 0, smallRows - 1, 0, smallCols - 1); int setHits = countHitsHashSet(set, 0, smallRows - 1, 0, smallCols - 1); assert vecHits == setHits : "Correctness failed: vec=" + vecHits + " set=" + setHits; System.out.println("Correctness OK: " + vecHits + " hits in " + smallCols + "x" + smallRows + " grid"); // Performance test: 80x25 full DBCS screen int cols = 80, rows = 25; List bigVec = buildVectorList(cols, rows); Set bigSet = buildHashSet(cols, rows); // Warmup for (int i = 0; i < 3; i++) { countHitsVector(bigVec, 0, rows - 1, 0, cols - 1); countHitsHashSet(bigSet, 0, rows - 1, 0, cols - 1); } int reps = 200; long t0 = System.nanoTime(); int totalVec = 0; for (int i = 0; i < reps; i++) totalVec += countHitsVector(bigVec, 0, rows - 1, 0, cols - 1); long vecNs = System.nanoTime() - t0; long t1 = System.nanoTime(); int totalSet = 0; for (int i = 0; i < reps; i++) totalSet += countHitsHashSet(bigSet, 0, rows - 1, 0, cols - 1); long setNs = System.nanoTime() - t1; assert totalVec == totalSet : "Result mismatch"; double ratio = (double) vecNs / setNs; System.out.printf("80x25 DBCS text selection (%d reps):%n", reps); System.out.printf(" vector linear scan: %,d ns%n", vecNs); System.out.printf(" unordered_set O(1) lookup: %,d ns%n", setNs); System.out.printf(" speedup: %.1fx%n", ratio); assert ratio > 3.0 : "Expected >3x speedup, got " + ratio; System.out.println("PASS"); } }