| 1 | #!/usr/bin/env -S ./vnew run |
| 2 | |
| 3 | import os |
| 4 | import hash.crc64 |
| 5 | |
| 6 | struct TestVector { |
| 7 | name string |
| 8 | data []u8 |
| 9 | } |
| 10 | |
| 11 | const ecma_check_123456789 = u64(0x6c40df5f0b497347) |
| 12 | |
| 13 | fn compile_c_helper(dir string) string { |
| 14 | ref_c := os.join_path(dir, 'crc64_ref.c') |
| 15 | ref_bin := os.join_path(dir, 'crc64_ref') |
| 16 | |
| 17 | // Compile: gcc -std=c99 crc64_ref.c -o crc64_ref |
| 18 | cmd := 'gcc -std=c99 "${ref_c}" -o "${ref_bin}"' |
| 19 | result := os.execute(cmd) |
| 20 | if result.exit_code != 0 { |
| 21 | eprintln('Failed to compile C helper:') |
| 22 | eprintln(result.output) |
| 23 | exit(1) |
| 24 | } |
| 25 | |
| 26 | return ref_bin |
| 27 | } |
| 28 | |
| 29 | fn run_c_checksum(ref_bin string, data []u8) !u64 { |
| 30 | hex_str := data.hex() |
| 31 | cmd := '${ref_bin} checksum ${hex_str}' |
| 32 | result := os.execute(cmd) |
| 33 | if result.exit_code != 0 { |
| 34 | return error('C helper failed: ${result.output}') |
| 35 | } |
| 36 | |
| 37 | result_str := result.output.trim_space() |
| 38 | return u64(result_str.parse_uint(16, 64)!) |
| 39 | } |
| 40 | |
| 41 | fn run_python_checksum(dir string, data []u8) !u64 { |
| 42 | ref_py := os.join_path(dir, 'crc64_ref.py') |
| 43 | hex_str := data.hex() |
| 44 | cmd := 'python3 "${ref_py}" checksum ${hex_str}' |
| 45 | result := os.execute(cmd) |
| 46 | if result.exit_code != 0 { |
| 47 | return error('Python helper failed: ${result.output}') |
| 48 | } |
| 49 | |
| 50 | result_str := result.output.trim_space() |
| 51 | return u64(result_str.parse_uint(16, 64)!) |
| 52 | } |
| 53 | |
| 54 | fn test_vector(name string, data []u8, ref_bin string, dir string) ! { |
| 55 | // V checksum |
| 56 | v_sum := crc64.sum(data) |
| 57 | |
| 58 | // C checksum |
| 59 | c_sum := run_c_checksum(ref_bin, data)! |
| 60 | |
| 61 | // Python checksum |
| 62 | py_sum := run_python_checksum(dir, data)! |
| 63 | |
| 64 | // All must match |
| 65 | if v_sum != c_sum || v_sum != py_sum { |
| 66 | eprintln('FAIL: ${name}') |
| 67 | eprintln(' V: 0x${v_sum:016x}') |
| 68 | eprintln(' C: 0x${c_sum:016x}') |
| 69 | eprintln(' Python: 0x${py_sum:016x}') |
| 70 | return error('checksum mismatch') |
| 71 | } |
| 72 | |
| 73 | if name == 'text_123456789' && v_sum != ecma_check_123456789 { |
| 74 | eprintln('FAIL: ${name}') |
| 75 | eprintln(' V: 0x${v_sum:016x}') |
| 76 | eprintln(' Expected: 0x${ecma_check_123456789:016x}') |
| 77 | return error('unexpected CRC-64-ECMA-182 check value') |
| 78 | } |
| 79 | |
| 80 | println('OK: ${name} => 0x${v_sum:016x}') |
| 81 | } |
| 82 | |
| 83 | fn main() { |
| 84 | dir := os.dir(@FILE) |
| 85 | |
| 86 | println('Compiling C reference helper...') |
| 87 | ref_bin := compile_c_helper(dir) |
| 88 | defer { os.rm(ref_bin) or {} } |
| 89 | |
| 90 | println('Running cross-validation tests...') |
| 91 | mut passed := 0 |
| 92 | mut failed := 0 |
| 93 | |
| 94 | mut vectors := []TestVector{} |
| 95 | vectors << TestVector{'empty', []u8{}} |
| 96 | vectors << TestVector{'single_a', 'a'.bytes()} |
| 97 | vectors << TestVector{'single_null', [u8(0)]} |
| 98 | vectors << TestVector{'text_123456789', '123456789'.bytes()} |
| 99 | vectors << TestVector{'text_hello_world', 'Hello, World!'.bytes()} |
| 100 | vectors << TestVector{'all_zeros_16', []u8{len: 16}} |
| 101 | vectors << TestVector{'all_ones_16', []u8{len: 16, init: 0xFF}} |
| 102 | vectors << TestVector{'repeating_pattern', ('abc'.repeat(50)).bytes()} |
| 103 | |
| 104 | mut all_bytes := []u8{len: 256} |
| 105 | for i in 0 .. 256 { |
| 106 | all_bytes[i] = u8(i) |
| 107 | } |
| 108 | vectors << TestVector{'all_bytes', all_bytes} |
| 109 | vectors << TestVector{'large_payload', ('test data '.repeat(1000)).bytes()} |
| 110 | |
| 111 | for vec in vectors { |
| 112 | test_vector(vec.name, vec.data, ref_bin, dir) or { |
| 113 | eprintln(' Error: ${err}') |
| 114 | failed++ |
| 115 | continue |
| 116 | } |
| 117 | passed++ |
| 118 | } |
| 119 | |
| 120 | println('') |
| 121 | println('=== Results ===') |
| 122 | println('Passed: ${passed}') |
| 123 | println('Failed: ${failed}') |
| 124 | println('Total: ${passed + failed}') |
| 125 | |
| 126 | if failed > 0 { |
| 127 | exit(1) |
| 128 | } |
| 129 | } |
| 130 | |