-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathbitfield-tests.scm
executable file
·200 lines (176 loc) · 6.61 KB
/
bitfield-tests.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
;;; Warning: nasal demons.
;;;
;;; Will output differences between GCC's behavior and our behavior, but not in
;;; a very nice format. Zero output is good. The C code and Scheme procedure
;;; we generate are fairly straightforward so read the code to understand.
(define-module (bytestructures bitfield-tests))
(export run-bitfield-tests)
(use-modules (srfi srfi-1)
(srfi srfi-9)
(ice-9 rdelim)
(bytestructures r6 bytevectors)
(bytestructures guile))
(define-record-type <struct>
(make-struct name fields)
struct?
(name struct-name)
(fields struct-fields))
(define-record-type <field>
(make-field name int-size bit-size signed? value)
struct?
(name field-name)
(int-size field-int-size)
(bit-size field-bit-size)
(signed? field-signed?)
(value field-value))
(define *keep-files* (make-parameter #f))
(define (run-bitfield-tests count random-seed-string keep-files)
(set! *random-state* (seed->random-state random-seed-string))
(parameterize ((*keep-files* keep-files))
(test-structs (generate-structs count))))
(define (generate-structs n)
(remove-bad-structs (map random-struct (iota n))))
(define (remove-bad-structs structs)
(filter (lambda (struct)
(find (lambda (field)
(not (zero? (field-bit-size field))))
(struct-fields struct)))
structs))
(define (random-struct i)
(let ((field-count (+ 1 (random 50))))
(make-struct (format #f "s~a" i)
(map random-field (iota field-count)))))
(define (random-field i)
(let* ((name (format #f "f~a" i))
(int-size (* 8 (expt 2 (random 4))))
(bit-size (random (+ 1 int-size)))
(signed? (= 0 (random 2)))
(value (random (expt 2 bit-size)))
(value (if (and signed? (> value (+ -1 (expt 2 (- bit-size 1)))))
(- value (expt 2 bit-size))
value)))
(make-field name int-size bit-size signed? value)))
(define (test-structs structs)
(let* ((c-code (c-code-for-structs structs))
(c-output (get-c-output c-code))
(scm-code (scm-code-for-structs structs))
(scm-output (get-scm-output scm-code)))
(diff-outputs c-output scm-output)))
(define (c-code-for-structs structs)
(string-concatenate
(append
(list "#include <stdio.h>\n"
"#include <stdint.h>\n"
"#include <strings.h>\n"
"int main(){\n")
(map c-code-for-struct structs)
(list "return 0;}"))))
(define (c-code-for-struct struct)
(let ((name (struct-name struct))
(fields (struct-fields struct)))
(string-concatenate
(append
(list (format #f "struct ~a {\n" name))
(map c-decl-for-field fields)
(list "};\n"
(format #f "{ struct ~a foo;\n" name)
(format #f " bzero((void*)&foo, sizeof(foo));\n"))
(map c-assignment-for-field fields)
(list (format #f " printf(\"struct ~a:\\n\");\n" name)
" uint8_t *ptr = (void*)&foo;\n"
" for (int i = 0; i < sizeof(foo); ++i) {\n"
" printf(\"%d \", *(ptr+i));\n"
" }\n"
" printf(\"\\n\");\n"
"}\n")))))
(define (c-decl-for-field field)
(let ((name (field-name field))
(int-size (field-int-size field))
(bit-size (field-bit-size field))
(signed? (field-signed? field)))
(format #f " ~aint~a_t ~a:~a;\n"
(if signed? "" "u")
int-size
(if (zero? bit-size) "" name)
bit-size)))
(define (c-assignment-for-field field)
(let ((name (field-name field))
(bit-size (field-bit-size field))
(signed? (field-signed? field))
(value (field-value field)))
(if (zero? bit-size)
""
(format #f " foo.~a = ~a~a;\n" name value (if signed? "" "u")))))
(define (get-c-output code)
(let* ((port (mkstemp! (string-copy "/tmp/bitfield-XXXXXX")))
(file (port-filename port))
(exe-port (mkstemp! (string-copy "/tmp/bitfield-compiled-XXXXXX")))
(exe-file (port-filename exe-port))
(output-port (mkstemp! (string-copy "/tmp/bitfield-output-XXXXXX")))
(output-file (port-filename output-port)))
(close-port exe-port)
(close-port output-port)
(display code port)
(force-output port)
(unless (zero? (system* "gcc" "-x" "c" "-std=c11" file "-o" exe-file))
(error "gcc failed"))
(unless (zero? (system (format #f "~a > ~a" exe-file output-file)))
(error "exe failed"))
(let ((out (read-string (open output-file O_RDONLY))))
(unless (*keep-files*)
(for-each delete-file (list file exe-file output-file)))
out)))
(define (scm-code-for-structs structs)
(lambda ()
(string-concatenate
(map scm-code-for-struct structs))))
(define (scm-code-for-struct struct)
(let* ((name (struct-name struct))
(fields (struct-fields struct))
(descriptor (struct->descriptor struct))
(values (map field-value (filter-nonzero-fields fields)))
(bs (bytestructure descriptor (list->vector values))))
(string-concatenate
(append
(list (format #f "struct ~a:\n" name))
(let ((bv (bytestructure-bytevector bs)))
(map (lambda (i)
(format #f "~a " (bytevector-u8-ref bv i)))
(iota (bytevector-length bv))))
(list "\n")))))
(define (struct->descriptor struct)
(let ((fields (struct-fields struct)))
(bs:struct (map field->struct-descriptor-field fields))))
(define (field->struct-descriptor-field field)
(let ((name (field-name field))
(int-size (field-int-size field))
(bit-size (field-bit-size field))
(signed? (field-signed? field)))
(list name
(module-ref (resolve-module
'(bytestructures bitfield-tests))
(string->symbol
(format #f "~aint~a"
(if signed? "" "u")
int-size)))
bit-size)))
(define (filter-nonzero-fields fields)
(filter (lambda (field)
(not (zero? (field-bit-size field))))
fields))
(define (get-scm-output code)
(code))
(define (diff-outputs o1 o2)
(let* ((p1 (mkstemp! (string-copy "/tmp/bitfield-out1-XXXXXX")))
(f1 (port-filename p1))
(p2 (mkstemp! (string-copy "/tmp/bitfield-out2-XXXXXX")))
(f2 (port-filename p2)))
(display o1 p1)
(display o2 p2)
(flush-all-ports)
(close-port p1)
(close-port p2)
(let ((retval (system* "diff" "-y" "--suppress-common" f1 f2)))
(unless (*keep-files*)
(for-each delete-file (list f1 f2)))
retval)))