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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
let row_bits = 32
let bit_chars = 2
type segment =
| Fixed of { name : string; bits : int }
| Variable of { label : string }
let rec pp_expr : type a. Buffer.t -> a Types.expr -> unit =
fun buf -> function
| Int n -> Buffer.add_string buf (string_of_int n)
| Int64 n -> Buffer.add_string buf (Int64.to_string n)
| Bool b -> Buffer.add_string buf (string_of_bool b)
| Ref (_, name) -> Buffer.add_string buf name
| Add (a, b) ->
pp_expr buf a;
Buffer.add_string buf " + ";
pp_expr buf b
| Sub (a, b) ->
pp_expr buf a;
Buffer.add_string buf " - ";
pp_expr buf b
| Mul (a, b) ->
pp_expr buf a;
Buffer.add_string buf " * ";
pp_expr buf b
| Eq (a, b) ->
pp_expr buf a;
Buffer.add_string buf " == ";
pp_expr buf b
| Le (a, b) ->
pp_expr buf a;
Buffer.add_string buf " <= ";
pp_expr buf b
| Lt (a, b) ->
pp_expr buf a;
Buffer.add_string buf " < ";
pp_expr buf b
| Ge (a, b) ->
pp_expr buf a;
Buffer.add_string buf " >= ";
pp_expr buf b
| Gt (a, b) ->
pp_expr buf a;
Buffer.add_string buf " > ";
pp_expr buf b
| Ne (a, b) ->
pp_expr buf a;
Buffer.add_string buf " != ";
pp_expr buf b
| And (a, b) ->
pp_expr buf a;
Buffer.add_string buf " && ";
pp_expr buf b
| Or (a, b) ->
pp_expr buf a;
Buffer.add_string buf " || ";
pp_expr buf b
| Not a ->
Buffer.add_string buf "!";
pp_expr buf a
| _ -> Buffer.add_string buf "?"
let string_of_expr (type a) (e : a Types.expr) =
let buf = Buffer.create 32 in
pp_expr buf e;
Buffer.contents buf
let annotate_fixed name typ constraint_ bits =
let base =
if bits <= 8 then name
else
let rec enum_info : type a. a Types.typ -> string option = function
| Enum { cases; _ } ->
let entries = List.map (fun (s, v) -> Fmt.str "%s=%d" s v) cases in
Some (String.concat "," entries)
| Map { inner; _ } -> enum_info inner
| Where { inner; _ } -> enum_info inner
| _ -> None
in
match enum_info typ with
| Some info
when String.length info + String.length name + 3
<= (bits * bit_chars) - 1 ->
Fmt.str "%s {%s}" name info
| _ -> name
in
match constraint_ with
| Some cond ->
let ann = Fmt.str "%s [%s]" base (string_of_expr cond) in
if String.length ann <= (bits * bit_chars) - 1 then ann else base
| None -> base
let variable_annotation : type a. string -> a Types.typ -> string =
fun name typ ->
match typ with
| Types.Byte_array { size } ->
Fmt.str "%s (%s bytes)" name (string_of_expr size)
| Types.Byte_slice { size } ->
Fmt.str "%s (%s bytes)" name (string_of_expr size)
| Byte_array_where { size; _ } ->
Fmt.str "%s (%s bytes, refined)" name (string_of_expr size)
| Array { len; elem; _ } ->
let elem_info =
match Types.field_wire_size elem with
| Some n -> Fmt.str "%d-byte elems" n
| None -> "var elems"
in
Fmt.str "%s (%s x %s)" name (string_of_expr len) elem_info
| Where { inner; cond } ->
let inner_label =
match Types.field_wire_size inner with
| Some n -> Fmt.str "%s (%d)" name (n * 8)
| None -> name
in
Fmt.str "%s [%s]" inner_label (string_of_expr cond)
| _ -> if name = "" then "(variable)" else Fmt.str "%s (variable)" name
let field_segment (Types.Field { field_name; field_typ; constraint_; _ }) =
let name = Option.value field_name ~default:"" in
match field_typ with
| Bits { width; _ } ->
Fixed
{ name = annotate_fixed name field_typ constraint_ width; bits = width }
| _ -> (
match Types.field_wire_size field_typ with
| Some n ->
Fixed
{
name = annotate_fixed name field_typ constraint_ (n * 8);
bits = n * 8;
}
| None ->
let annotation = variable_annotation name field_typ in
let label =
match constraint_ with
| None -> annotation
| Some cond -> Fmt.str "%s [%s]" annotation (string_of_expr cond)
in
Variable { label })
let centre label width =
let len = String.length label in
if len >= width then String.sub label 0 width
else
let pad = width - len in
let left = pad / 2 in
let right = pad - left in
String.make left ' ' ^ label ^ String.make right ' '
let ruler () =
let bytes_line = Buffer.create 80 in
let bits_line = Buffer.create 80 in
Buffer.add_string bytes_line " ";
Buffer.add_string bits_line " ";
for bit = 0 to row_bits - 1 do
if bit > 0 then (
Buffer.add_char bytes_line ' ';
Buffer.add_char bits_line ' ');
if bit mod 8 = 0 then Buffer.add_string bytes_line (string_of_int (bit / 8))
else Buffer.add_char bytes_line ' ';
Buffer.add_string bits_line (string_of_int (bit mod 10))
done;
(Buffer.contents bytes_line, Buffer.contents bits_line)
let sep n =
let buf = Buffer.create ((n * bit_chars) + 2) in
Buffer.add_string buf " +";
for _ = 1 to n do
Buffer.add_char buf '-';
Buffer.add_char buf '+'
done;
Buffer.contents buf
let _full_sep () = sep row_bits
let render_fixed_row fields =
let buf = Buffer.create ((row_bits * bit_chars) + 2) in
Buffer.add_string buf " |";
List.iter
(fun (name, bits) ->
let content_width = (bits * bit_chars) - 1 in
Buffer.add_string buf (centre name content_width);
Buffer.add_char buf '|')
fields;
Buffer.contents buf
let render_variable_row label =
let total_width = (row_bits * bit_chars) - 1 in
let content =
if String.length label >= total_width then String.sub label 0 total_width
else
let padded = " " ^ label in
let pad = total_width - String.length padded in
if pad > 0 then padded ^ String.make pad ' ' else padded
in
Fmt.str " |%s|" content
type row = Fixed_row of (string * int) list | Variable_row of string
let layout segments =
let rows = ref [] in
let cur_row = ref [] in
let cur_bits = ref 0 in
let flush () =
if !cur_row <> [] then (
rows := Fixed_row (List.rev !cur_row) :: !rows;
cur_row := [];
cur_bits := 0)
in
List.iter
(function
| Fixed { name; bits } ->
let remaining = ref bits in
while !remaining > 0 do
let avail = row_bits - !cur_bits in
let take = min avail !remaining in
let label =
if take = bits then name
else if !remaining = bits then name
else ""
in
cur_row := (label, take) :: !cur_row;
cur_bits := !cur_bits + take;
remaining := !remaining - take;
if !cur_bits = row_bits then flush ()
done
| Variable { label } ->
flush ();
rows := Variable_row label :: !rows)
segments;
flush ();
List.rev !rows
let row_bits_used = function
| Fixed_row fields -> List.fold_left (fun acc (_, b) -> acc + b) 0 fields
| Variable_row _ -> row_bits
let render_row buf last_bits row =
let used = row_bits_used row in
Buffer.add_string buf (sep (max !last_bits used));
Buffer.add_char buf '\n';
(match row with
| Fixed_row fields -> Buffer.add_string buf (render_fixed_row fields)
| Variable_row label -> Buffer.add_string buf (render_variable_row label));
Buffer.add_char buf '\n';
last_bits := used
let render_struct (s : Types.struct_) =
let segments = List.map field_segment s.fields in
let rows = layout segments in
if rows = [] then ""
else
let buf = Buffer.create 512 in
let tens, ones = ruler () in
Buffer.add_string buf tens;
Buffer.add_char buf '\n';
Buffer.add_string buf ones;
Buffer.add_char buf '\n';
let last_bits = ref row_bits in
List.iter (render_row buf last_bits) rows;
Buffer.add_string buf (sep !last_bits);
Buffer.add_char buf '\n';
Buffer.contents buf
let of_struct = render_struct
let of_codec t = render_struct (Codec.to_struct t)
let pp_struct ppf s = Fmt.string ppf (render_struct s)
let pp_codec ppf t = pp_struct ppf (Codec.to_struct t)