v / vlib / compress / lz / interop / lz_interop.v
172 lines · 151 sloc · 5.01 KB · 6cafb40142fca520cc21eb213dab2bc08b545e8d
Raw
1module main
2
3import compress.lz
4import os
5
6const default_data_size = 512 * 1024
7
8fn main() {
9 mut data_size := default_data_size
10 if os.args.len > 1 {
11 parsed := int(os.args[1].i32())
12 if parsed > 0 {
13 data_size = parsed
14 }
15 }
16 data := `z`.repeat(data_size).bytes()
17
18 println('LZ interop input: ${data.len} bytes')
19
20 for format in [lz.Format.lz77, .lz78, .lzw, .lz4, .lzss, .lzma, .lzma2, .lzjb] {
21 validate_v_roundtrip(data, format) or {
22 eprintln('V validation failed for ${format}: ${err.msg()}')
23 exit(1)
24 }
25 println('V roundtrip (${format}): OK')
26 }
27
28 tmp_dir := os.join_path(os.temp_dir(), 'v_lz_interop')
29 os.mkdir_all(tmp_dir) or {
30 eprintln('Could not create temp directory ${tmp_dir}: ${err.msg()}')
31 exit(1)
32 }
33 defer {
34 os.rmdir_all(tmp_dir) or {
35 eprintln('Could not remove temp directory ${tmp_dir}: ${err.msg()}')
36 }
37 }
38 input_path := os.join_path(tmp_dir, 'input.bin')
39 os.write_file_array(input_path, data) or {
40 eprintln('Could not write input file ${input_path}: ${err.msg()}')
41 exit(1)
42 }
43 mut c_bin := ''
44 if bin := compile_c_runner() {
45 c_bin = bin
46 } else {
47 eprintln('Skipping C benchmark: ${err.msg()}')
48 }
49 python_ok := has_python3()
50 if !python_ok {
51 eprintln('Skipping Python benchmark: python3 is not available')
52 }
53
54 if c_bin.len > 0 {
55 cross_validate_v_c(c_bin, data, input_path, tmp_dir) or {
56 eprintln('Cross-validation V<->C failed: ${err.msg()}')
57 exit(1)
58 }
59 println('Cross-validation: V<->C compress/decompress OK')
60 } else {
61 println('Cross-validation: skipped V<->C (requires C compiler)')
62 }
63
64 if python_ok {
65 cross_validate_v_python(data, input_path, tmp_dir) or {
66 eprintln('Cross-validation V<->Python failed: ${err.msg()}')
67 exit(1)
68 }
69 println('Cross-validation: V<->Python compress/decompress OK')
70 } else {
71 println('Cross-validation: skipped V<->Python (requires python3)')
72 }
73}
74
75fn validate_v_roundtrip(data []u8, format lz.Format) ! {
76 encoded := lz.compress(data, format)!
77 decoded := lz.decompress(encoded, format)!
78 if decoded != data {
79 return error('roundtrip mismatch for ${format}')
80 }
81}
82
83fn compile_c_runner() !string {
84 cc := choose_cc()
85 if cc.len == 0 {
86 return error('no C compiler found (tried cc, gcc, and clang)')
87 }
88 bin_path := os.join_path(os.temp_dir(), 'lz77_ref_bench')
89 c_src := os.join_path(@DIR, 'lz77_ref.c')
90 compile_cmd := '${cc} -O3 -std=c99 ${os.quoted_path(c_src)} -o ${os.quoted_path(bin_path)}'
91 compile_res := os.execute(compile_cmd)
92 if compile_res.exit_code != 0 {
93 return error('C compile failed: ${compile_res.output.trim_space()}')
94 }
95 return bin_path
96}
97
98fn choose_cc() string {
99 for cc in ['cc', 'gcc', 'clang'] {
100 if os.execute('${cc} --version').exit_code == 0 {
101 return cc
102 }
103 }
104 return ''
105}
106
107fn has_python3() bool {
108 return os.execute('python3 --version').exit_code == 0
109}
110
111fn cross_validate_v_c(c_bin string, original []u8, input_path string, tmp_dir string) ! {
112 v_encoded := os.join_path(tmp_dir, 'v_encoded.bin')
113 c_decoded := os.join_path(tmp_dir, 'c_decoded.bin')
114 c_encoded := os.join_path(tmp_dir, 'c_encoded.bin')
115
116 v_stream := lz.compress_lz77(original)!
117 os.write_file_array(v_encoded, v_stream)!
118
119 mut res :=
120 os.execute('${os.quoted_path(c_bin)} decompress ${os.quoted_path(v_encoded)} ${os.quoted_path(c_decoded)}')
121 if res.exit_code != 0 {
122 return error('C decompress(V output) failed: ${res.output.trim_space()}')
123 }
124 validate_equal_files(input_path, c_decoded, 'V->C')!
125
126 res =
127 os.execute('${os.quoted_path(c_bin)} compress ${os.quoted_path(input_path)} ${os.quoted_path(c_encoded)}')
128 if res.exit_code != 0 {
129 return error('C compress failed: ${res.output.trim_space()}')
130 }
131 c_encoded_data := os.read_bytes(c_encoded)!
132 v_decoded := lz.decompress_lz77(c_encoded_data)!
133 if v_decoded != original {
134 return error('C->V output mismatch')
135 }
136}
137
138fn cross_validate_v_python(original []u8, input_path string, tmp_dir string) ! {
139 v_encoded := os.join_path(tmp_dir, 'v_encoded_for_py.bin')
140 py_decoded := os.join_path(tmp_dir, 'py_decoded.bin')
141 py_encoded := os.join_path(tmp_dir, 'py_encoded.bin')
142 py_script := os.join_path(@DIR, 'lz77_ref.py')
143
144 v_stream := lz.compress_lz77(original)!
145 os.write_file_array(v_encoded, v_stream)!
146
147 mut res :=
148 os.execute('python3 ${os.quoted_path(py_script)} decompress ${os.quoted_path(v_encoded)} ${os.quoted_path(py_decoded)}')
149 if res.exit_code != 0 {
150 return error('Python decompress(V output) failed: ${res.output.trim_space()}')
151 }
152 validate_equal_files(input_path, py_decoded, 'V->Python')!
153
154 res =
155 os.execute('python3 ${os.quoted_path(py_script)} compress ${os.quoted_path(input_path)} ${os.quoted_path(py_encoded)}')
156 if res.exit_code != 0 {
157 return error('Python compress failed: ${res.output.trim_space()}')
158 }
159 py_encoded_data := os.read_bytes(py_encoded)!
160 v_decoded := lz.decompress_lz77(py_encoded_data)!
161 if v_decoded != original {
162 return error('Python->V output mismatch')
163 }
164}
165
166fn validate_equal_files(expected_path string, actual_path string, tag string) ! {
167 expected := os.read_bytes(expected_path)!
168 actual := os.read_bytes(actual_path)!
169 if expected != actual {
170 return error('${tag} output mismatch')
171 }
172}
173