1use std::collections::HashMap;
2
3use indexmap::IndexSet;
4use lutra_bin::Encode;
5use lutra_bin::br::*;
6use lutra_bin::bytes::BufMut;
7use lutra_bin::ir;
8
9pub fn compile_program(value: ir::Program) -> Program {
10 let mut b = ByteCoder {
11 externals: Default::default(),
12 include_defs: false,
13
14 defs: &value.defs,
15 def_map: value.defs.iter().map(|def| (&def.name, &def.ty)).collect(),
16 next_wrapper_id: 0xFF00,
17 };
18
19 let program = Program {
20 main: b.compile_expr(value.main),
21 externals: b.externals.into_iter().collect(),
22 defs: if b.include_defs { value.defs } else { vec![] },
23 };
24 tracing::debug!("br:\n{program:#?}");
25 program
26}
27
28struct ByteCoder<'t> {
29 externals: IndexSet<ExternalSymbol>,
30 defs: &'t [ir::TyDef],
31 def_map: HashMap<&'t ir::Path, &'t ir::Ty>,
32
33 include_defs: bool,
35
36 next_wrapper_id: u32,
38}
39
40impl<'t> ByteCoder<'t> {
41 fn get_ty_mat<'a: 't>(&self, ty: &'a ir::Ty) -> &'t ir::Ty {
42 let mut ty = ty;
43 while let TyKind::Ident(path) = &ty.kind {
44 ty = self.def_map.get(path).unwrap();
45 }
46 ty
47 }
48 fn get_ty_mat_or_std<'a: 't>(&self, ty: &'a ir::Ty) -> &'t ir::Ty {
49 let mut ty = ty;
50 while let TyKind::Ident(path) = &ty.kind {
51 if ir::TyStd::try_new(path).is_some() {
52 return ty;
53 }
54 ty = self.def_map.get(path).unwrap();
55 }
56 ty
57 }
58
59 fn compile_expr(&mut self, expr: ir::Expr) -> Expr {
60 let kind = match expr.kind {
61 ir::ExprKind::Pointer(v) => self.compile_pointer(v, &expr.ty),
62 ir::ExprKind::Literal(v) => ExprKind::Literal(self.compile_literal(v)),
63 ir::ExprKind::Call(v) => ExprKind::Call(Box::new(self.compile_call(*v))),
64 ir::ExprKind::Function(v) => ExprKind::Function(Box::new(self.compile_function(*v))),
65 ir::ExprKind::Tuple(v) => ExprKind::Tuple(Box::new(self.compile_tuple(v))),
66 ir::ExprKind::Array(v) => ExprKind::Array(Box::new(self.compile_array(expr.ty, v))),
67 ir::ExprKind::EnumVariant(v) => {
68 ExprKind::EnumVariant(Box::new(self.compile_enum_variant(expr.ty, *v)))
69 }
70 ir::ExprKind::EnumTag(v) => self.compile_expr(v.subject).kind,
71 ir::ExprKind::EnumUnwrap(v) => return self.compile_enum_unwrap(*v),
72 ir::ExprKind::TupleLookup(v) => return self.compile_tuple_lookup(*v),
73 ir::ExprKind::Binding(v) => ExprKind::Binding(Box::new(self.compile_binding(*v))),
74 ir::ExprKind::Switch(v) => ExprKind::Switch(self.compile_switch(v)),
75 };
76
77 Expr { kind }
78 }
79
80 fn compile_pointer(&mut self, ptr: ir::Pointer, ty: &ir::Ty) -> ExprKind {
81 match ptr {
82 ir::Pointer::External(e_ptr) => {
83 let ty = self.get_ty_mat(ty);
84 self.compile_external_symbol(e_ptr.id, ty)
85 }
86 #[rustfmt::skip]
87 ir::Pointer::Binding(binding_id) => {
88 ExprKind::Pointer(Sid(binding_id).with_tag(SidKind::Var))
89 },
90 ir::Pointer::Parameter(param_ptr) => {
91 let sid = param_ptr.function_id << 8 | param_ptr.param_position as u32;
92
93 ExprKind::Pointer(Sid(sid).with_tag(SidKind::FunctionScope))
94 }
95 }
96 }
97
98 fn compile_literal(&mut self, value: ir::Literal) -> Vec<u8> {
99 match value {
100 ir::Literal::Prim8(v) => v.encode(),
101 ir::Literal::Prim16(v) => v.encode(),
102 ir::Literal::Prim32(v) => v.encode(),
103 ir::Literal::Prim64(v) => v.encode(),
104 ir::Literal::Text(v) => v.encode(),
105 }
106 }
107
108 fn compile_call(&mut self, value: ir::Call) -> Call {
109 Call {
110 function: self.compile_expr(value.function),
111 args: value
112 .args
113 .into_iter()
114 .map(|x| self.compile_expr(x))
115 .collect(),
116 }
117 }
118
119 fn compile_function(&mut self, value: ir::Function) -> Function {
120 Function {
121 symbol_ns: Sid(value.id << 8).with_tag(SidKind::FunctionScope),
122 body: self.compile_expr(value.body),
123 }
124 }
125
126 fn compile_tuple(&mut self, fields: Vec<ir::TupleField>) -> Tuple {
127 let field_layouts = fields
128 .iter()
129 .flat_map(|f| {
130 if f.unpack {
131 let ir::TyKind::Tuple(fields) = &self.get_ty_mat(&f.expr.ty).kind else {
132 panic!();
133 };
134 fields.iter().map(|f| &f.ty).collect::<Vec<_>>()
135 } else {
136 vec![&f.expr.ty]
137 }
138 })
139 .map(|ty| self.compile_ty_layout(ty.layout.clone().unwrap()))
140 .collect();
141
142 let fields = fields
143 .into_iter()
144 .map(|f| {
145 let unpack = if f.unpack {
146 let ir::TyKind::Tuple(fields) = &self.get_ty_mat(&f.expr.ty).kind else {
147 panic!();
148 };
149 fields.len() as u8
150 } else {
151 0
152 };
153 let expr = self.compile_expr(f.expr);
154
155 TupleField { expr, unpack }
156 })
157 .collect();
158 Tuple {
159 fields,
160 field_layouts,
161 }
162 }
163
164 fn compile_array(&mut self, ty: ir::Ty, items: Vec<ir::Expr>) -> Array {
165 let ty_item = self.get_ty_mat(&ty).kind.as_array().unwrap();
166 Array {
167 items: items.into_iter().map(|x| self.compile_expr(x)).collect(),
168 item_layout: self.compile_ty_layout(ty_item.layout.clone().unwrap()),
169 }
170 }
171
172 fn compile_enum_variant(&mut self, ty: Ty, v: ir::EnumVariant) -> EnumVariant {
173 let ty_mat = self.get_ty_mat(&ty);
174 let ir::TyKind::Enum(ty_variants) = &ty_mat.kind else {
175 panic!()
176 };
177 let ty_variant = ty_variants.get(v.tag as usize).unwrap();
178 let head_format = lutra_bin::layout::enum_head_format(ty_variants, &ty.variants_recursive);
179 let variant_format = lutra_bin::layout::enum_variant_format(&head_format, &ty_variant.ty);
180
181 EnumVariant {
182 tag: v.tag.to_le_bytes()[0..head_format.tag_bytes as usize].to_vec(),
183 inner_bytes: head_format.inner_bytes as u8,
184 has_ptr: head_format.has_ptr,
185 padding_bytes: variant_format.padding_bytes,
186 inner: self.compile_expr(v.inner),
187 }
188 }
189
190 fn compile_enum_unwrap(&mut self, v: ir::EnumUnwrap) -> Expr {
191 let ty_mat = self.get_ty_mat(&v.subject.ty);
192 let ir::TyKind::Enum(ty_variants) = &ty_mat.kind else {
193 panic!()
194 };
195
196 let head_format =
197 lutra_bin::layout::enum_head_format(ty_variants, &ty_mat.variants_recursive);
198
199 let mut expr = self.compile_expr(v.subject);
200
201 expr = Expr {
203 kind: ExprKind::Offset(Box::new(Offset {
204 base: expr,
205 offset: head_format.tag_bytes,
206 })),
207 };
208
209 if head_format.has_ptr {
211 expr = Expr {
212 kind: ExprKind::Deref(Box::new(Deref { ptr: expr })),
213 };
214 }
215
216 expr
217 }
218
219 fn compile_tuple_lookup(&mut self, value: ir::TupleLookup) -> Expr {
220 let base_ty = self.get_ty_mat(&value.base.ty);
221 let offset = lutra_bin::layout::tuple_field_offset(base_ty, value.position);
222
223 let kind = ExprKind::Offset(Box::new(Offset {
224 base: self.compile_expr(value.base),
225 offset,
226 }));
227 Expr { kind }
228 }
229
230 fn compile_binding(&mut self, value: ir::Binding) -> Binding {
231 Binding {
232 symbol: Sid(value.id).with_tag(SidKind::Var),
233 expr: self.compile_expr(value.expr),
234 main: self.compile_expr(value.main),
235 }
236 }
237
238 fn compile_switch(&mut self, branches: Vec<ir::SwitchBranch>) -> Vec<SwitchBranch> {
239 branches
240 .into_iter()
241 .map(|b| SwitchBranch {
242 condition: self.compile_expr(b.condition),
243 value: self.compile_expr(b.value),
244 })
245 .collect()
246 }
247
248 fn compile_ty_layout(&self, value: ir::TyLayout) -> TyLayout {
249 TyLayout {
250 head_size: value.head_size,
251 body_ptrs: value.body_ptrs,
252 }
253 }
254
255 fn compile_external_symbol(&mut self, id: String, ty_mat: &ir::Ty) -> ExprKind {
256 let layout_args: Vec<u32> = match id.as_str() {
257 "std::ops::add"
258 | "std::ops::sub"
259 | "std::ops::mul"
260 | "std::ops::div"
261 | "std::ops::mod"
262 | "std::ops::neg"
263 | "std::ops::cmp"
264 | "std::ops::eq"
265 | "std::ops::lt"
266 | "std::ops::lte"
267 | "std::convert::to_int8"
268 | "std::convert::to_int16"
269 | "std::convert::to_int32"
270 | "std::convert::to_int64"
271 | "std::convert::to_uint8"
272 | "std::convert::to_uint16"
273 | "std::convert::to_uint32"
274 | "std::convert::to_uint64"
275 | "std::convert::to_float32"
276 | "std::convert::to_float64"
277 | "std::convert::to_text"
278 | "std::math::abs"
279 | "std::math::pow"
280 | "std::array::sequence" => {
281 let param_ty = as_ty_of_param(ty_mat);
282 let ty_name = self.as_std_ty_suffix(param_ty);
283 return self.make_external(format!("{id}_{ty_name}"), vec![]);
284 }
285
286 "std::array::fold" => {
287 let item_layout = as_layout_of_param_array(ty_mat);
288 vec![
289 item_layout.head_size.div_ceil(8), ]
291 }
292
293 "std::array::min"
294 | "std::array::max"
295 | "std::array::rank"
296 | "std::array::rank_dense"
297 | "std::array::rank_percentile"
298 | "std::array::cume_dist" => {
299 let param_ty = as_ty_of_param(ty_mat);
301 let item_ty = self.get_ty_mat(param_ty).kind.as_array().unwrap();
302 let item_layout = item_ty.layout.as_ref().unwrap();
303 let layout_args = vec![item_layout.head_size.div_ceil(8)];
304
305 let n_params = ty_mat.kind.as_function().unwrap().params.len();
306 let cmp_id = format!("std::ops::cmp_{}", self.as_std_ty_suffix(item_ty));
307 let cmp = self.make_external(cmp_id, vec![]);
308 return self.wrap_external_with_extra_args(id, layout_args, n_params, vec![cmp]);
309 }
310
311 "std::array::sort" => {
312 let item_layout = as_layout_of_param_array(ty_mat);
313
314 let mut layout_args = Vec::with_capacity(1 + 1 + item_layout.body_ptrs.len());
315 layout_args.push(item_layout.head_size.div_ceil(8));
316 layout_args.extend(as_len_and_items(&item_layout.body_ptrs));
317
318 let ty_func = ty_mat.kind.as_function().unwrap();
319 let key_extractor_ty = self.get_ty_mat(&ty_func.params[1]);
320 let key_ty = &key_extractor_ty.kind.as_function().unwrap().body;
321
322 let n_params = ty_func.params.len();
323 let cmp_id = format!("std::ops::cmp_{}", self.as_std_ty_suffix(key_ty));
324 let cmp = self.make_external(cmp_id, vec![]);
325 return self.wrap_external_with_extra_args(id, layout_args, n_params, vec![cmp]);
326 }
327
328 "std::array::sum" | "std::array::mean" | "std::array::rolling_mean" => {
329 let param_ty = as_ty_of_param(ty_mat);
330 let item_ty = self.get_ty_mat(param_ty).kind.as_array().unwrap();
331 let item_layout = item_ty.layout.as_ref().unwrap();
332 let layout_args = vec![item_layout.head_size.div_ceil(8)];
333
334 let ty_name = self.as_std_ty_suffix(item_ty);
335 return self.make_external(format!("{id}_{ty_name}"), layout_args);
336 }
337
338 "std::array::index" => {
339 let item_layout = as_layout_of_param_array(ty_mat);
340
341 let ty_func = ty_mat.kind.as_function().unwrap();
342 let ty_out_variants = self.get_ty_mat(&ty_func.body).kind.as_enum().unwrap();
343 let ty_out_format = lutra_bin::layout::enum_format(
344 ty_out_variants,
345 &ty_func.body.variants_recursive,
346 );
347 let ty_out_format = ty_out_format.encode();
348
349 let mut r = vec![
350 item_layout.head_size.div_ceil(8), ];
352
353 pack_bytes_to_u32(ty_out_format, &mut r);
354 r
355 }
356
357 "std::array::filter"
358 | "std::array::slice"
359 | "std::array::append"
360 | "std::array::loop_until_empty" => {
361 let item_layout = as_layout_of_param_array(ty_mat);
362
363 let mut r = Vec::with_capacity(1 + 1 + item_layout.body_ptrs.len());
364 r.push(item_layout.head_size.div_ceil(8)); r.extend(as_len_and_items(&item_layout.body_ptrs)); r
367 }
368
369 "std::array::lag" | "std::array::lead" => {
370 let item_layout = as_layout_of_param_array(ty_mat);
371
372 let mut r = Vec::with_capacity(1 + 1 + item_layout.body_ptrs.len());
373 r.push(item_layout.head_size.div_ceil(8)); r.extend(as_len_and_items(&item_layout.body_ptrs)); let ty_func = ty_mat.kind.as_function().unwrap();
378 let ty_item = ty_func.body.kind.as_array().unwrap();
379 let default_val = self.construct_default_for_ty(ty_item);
380 pack_bytes_to_u32(default_val, &mut r);
381
382 r
383 }
384
385 "std::array::map" | "std::array::flat_map" | "std::array::scan" => {
386 let input_layout = as_layout_of_param_array(ty_mat);
387 let output_layout = as_layout_of_return_array(ty_mat);
388
389 let mut r = Vec::with_capacity(2 + 1 + output_layout.body_ptrs.len());
390 r.push(input_layout.head_size.div_ceil(8)); r.push(output_layout.head_size.div_ceil(8)); r.extend(as_len_and_items(&output_layout.body_ptrs)); r
394 }
395
396 "std::array::to_columnar" => {
397 let ty_func = ty_mat.kind.as_function().unwrap();
398
399 let input_item = ty_func.params[0].kind.as_array().unwrap();
400 let input_layout = input_item.layout.as_ref().unwrap();
401
402 let mut r = Vec::new();
403 r.push(input_layout.head_size.div_ceil(8)); let input_field_offsets = lutra_bin::layout::tuple_field_offsets(input_item);
406 r.extend(as_len_and_items(&input_field_offsets)); let fields = input_item.kind.as_tuple().unwrap();
410 r.push(fields.len() as u32);
411 for field in fields {
412 let field_layout = field.ty.layout.as_ref().unwrap();
413 r.push(field_layout.head_size.div_ceil(8));
414 }
415
416 for field in fields {
418 let field_layout = field.ty.layout.as_ref().unwrap();
419 r.extend(as_len_and_items(&field_layout.body_ptrs));
420 }
421
422 r
423 }
424 "std::array::from_columnar" => {
425 let ty_func = ty_mat.kind.as_function().unwrap();
426
427 let output_item = ty_func.body.kind.as_array().unwrap();
428 let output_layout = output_item.layout.as_ref().unwrap();
429
430 let mut r = Vec::new();
431 r.push(output_layout.head_size.div_ceil(8)); r.extend(as_len_and_items(&output_layout.body_ptrs)); let fields = output_item.kind.as_tuple().unwrap();
437 r.push(fields.len() as u32);
438 for field in fields {
439 let field_layout = field.ty.layout.as_ref().unwrap();
440 r.push(field_layout.head_size.div_ceil(8));
441 }
442
443 for field in fields {
445 let field_layout = field.ty.layout.as_ref().unwrap();
446 r.extend(as_len_and_items(&field_layout.body_ptrs));
447 }
448
449 r
450 }
451
452 "std::array::zip" => {
453 let ty_func = ty_mat.kind.as_function().unwrap();
454
455 let a_item = self.get_ty_mat(&ty_func.params[0]).kind.as_array().unwrap();
456 let a_layout = a_item.layout.as_ref().unwrap();
457
458 let b_item = self.get_ty_mat(&ty_func.params[1]).kind.as_array().unwrap();
459 let b_layout = b_item.layout.as_ref().unwrap();
460
461 let mut r = Vec::new();
462 r.push(a_layout.head_size.div_ceil(8));
463 r.extend(as_len_and_items(&a_layout.body_ptrs));
464 r.push(b_layout.head_size.div_ceil(8));
465 r.extend(as_len_and_items(&b_layout.body_ptrs));
466 r
467 }
468
469 "std::array::group" => {
470 let ty_func = ty_mat.kind.as_function().unwrap();
471
472 let input_item = self.get_ty_mat(&ty_func.params[0]).kind.as_array().unwrap();
473 let input_layout = input_item.layout.as_ref().unwrap();
474
475 let output_item = self.get_ty_mat(&ty_func.body).kind.as_array().unwrap();
476 let output_layout = output_item.layout.as_ref().unwrap();
477
478 let key = &self.get_ty_mat(output_item).kind.as_tuple().unwrap()[0].ty;
479 let key_layout = key.layout.as_ref().unwrap();
480
481 let mut r = Vec::new();
482 r.push(input_layout.head_size.div_ceil(8)); r.extend(as_len_and_items(&input_layout.body_ptrs)); r.push(output_layout.head_size.div_ceil(8)); r.extend(as_len_and_items(&output_layout.body_ptrs)); let fields = output_item.kind.as_tuple().unwrap();
490 r.push(fields.len() as u32);
491 for field in fields {
492 let field_layout = field.ty.layout.as_ref().unwrap();
493 r.push(field_layout.head_size.div_ceil(8));
494 }
495
496 for field in fields {
498 let field_layout = field.ty.layout.as_ref().unwrap();
499 r.extend(as_len_and_items(&field_layout.body_ptrs));
500 }
501
502 r.push(key_layout.head_size.div_ceil(8)); r
505 }
506
507 "std::fs::read_parquet" => {
508 let ty_func = ty_mat.kind.as_function().unwrap();
509 let ty_data = &ty_func.body;
510
511 self.include_defs = true;
512
513 let mut r = Vec::new();
514 pack_bytes_to_u32(ty_data.encode(), &mut r);
515 r
516 }
517 "std::fs::write_parquet" => {
518 let ty_data = as_ty_of_param(ty_mat);
519
520 self.include_defs = true;
521
522 let mut r = Vec::new();
523 pack_bytes_to_u32(ty_data.encode(), &mut r);
524 r
525 }
526
527 _ => vec![],
528 };
529
530 let (index, _) = self
531 .externals
532 .insert_full(ExternalSymbol { id, layout_args });
533 ExprKind::Pointer(Sid(index as u32).with_tag(SidKind::External))
534 }
535
536 fn as_std_ty_suffix(&self, ty: &ir::Ty) -> String {
538 match &self.get_ty_mat_or_std(ty).kind {
539 ir::TyKind::Ident(path) => path.0.last().unwrap().to_ascii_lowercase(),
540 ir::TyKind::Primitive(ir::TyPrimitive::Prim8) => "uint8".into(),
541 ir::TyKind::Primitive(ir::TyPrimitive::Prim16) => "uint16".into(),
542 ir::TyKind::Primitive(ir::TyPrimitive::Prim32) => "uint32".into(),
543 ir::TyKind::Primitive(ir::TyPrimitive::Prim64) => "uint64".into(),
544 _ => {
545 panic!("std specialization not supported for {}", ir::print_ty(ty));
546 }
547 }
548 }
549
550 fn make_external(&mut self, id: String, layout_args: Vec<u32>) -> ExprKind {
552 let (index, _) = self
553 .externals
554 .insert_full(ExternalSymbol { id, layout_args });
555 ExprKind::Pointer(Sid(index as u32).with_tag(SidKind::External))
556 }
557
558 fn wrap_external_with_extra_args(
571 &mut self,
572 id: String,
573 layout_args: Vec<u32>,
574 n_params: usize,
575 extra_args: Vec<ExprKind>,
576 ) -> ExprKind {
577 let (ext_index, _) = self
579 .externals
580 .insert_full(ExternalSymbol { id, layout_args });
581 let ext_sid = Sid(ext_index as u32).with_tag(SidKind::External);
582
583 let wrapper_id = self.next_wrapper_id;
585 self.next_wrapper_id += 1;
586 let wrapper_ns = Sid(wrapper_id << 8).with_tag(SidKind::FunctionScope);
587
588 let mut args: Vec<Expr> = (0..n_params)
590 .map(|i| {
591 let sid = Sid(wrapper_id << 8 | i as u32).with_tag(SidKind::FunctionScope);
592 Expr {
593 kind: ExprKind::Pointer(sid),
594 }
595 })
596 .collect();
597 args.extend(extra_args.into_iter().map(|kind| Expr { kind }));
598
599 ExprKind::Function(Box::new(Function {
600 symbol_ns: wrapper_ns,
601 body: Expr {
602 kind: ExprKind::Call(Box::new(Call {
603 function: Expr {
604 kind: ExprKind::Pointer(ext_sid),
605 },
606 args,
607 })),
608 },
609 }))
610 }
611
612 fn construct_default_for_ty(&self, ty: &ir::Ty) -> Vec<u8> {
613 self.construct_default_for_ty_re(ty)
614 .encode(ty, self.defs)
615 .unwrap()
616 }
617
618 fn construct_default_for_ty_re(&self, ty: &ir::Ty) -> lutra_bin::Value {
619 match &self.get_ty_mat(ty).kind {
620 ir::TyKind::Primitive(prim) => match prim {
621 ir::TyPrimitive::Prim8 => lutra_bin::Value::Prim8(0),
622 ir::TyPrimitive::Prim16 => lutra_bin::Value::Prim16(0),
623 ir::TyPrimitive::Prim32 => lutra_bin::Value::Prim32(0),
624 ir::TyPrimitive::Prim64 => lutra_bin::Value::Prim64(0),
625 },
626 ir::TyKind::Array(_) => lutra_bin::Value::Array(vec![]),
627 ir::TyKind::Tuple(ty_fields) => lutra_bin::Value::Tuple(
628 ty_fields
629 .iter()
630 .map(|f| self.construct_default_for_ty_re(&f.ty))
631 .collect(),
632 ),
633 ir::TyKind::Enum(ty_enum_variants) => {
634 let variant = ty_enum_variants.iter().next().unwrap();
635 lutra_bin::Value::Enum(0, Box::new(self.construct_default_for_ty_re(&variant.ty)))
636 }
637
638 ir::TyKind::Function(_) => panic!(),
639 ir::TyKind::Ident(_) => unreachable!(),
640 }
641 }
642}
643
644fn as_len_and_items(items: &[u32]) -> impl Iterator<Item = u32> + '_ {
645 Some(items.len() as u32)
646 .into_iter()
647 .chain(items.iter().cloned())
648}
649
650fn as_layout_of_param_array(ty: &Ty) -> &ir::TyLayout {
651 let ty_func = ty.kind.as_function().unwrap();
652 let ty_array = ty_func.params[0].kind.as_array().unwrap();
653
654 ty_array.layout.as_ref().unwrap()
655}
656
657fn as_layout_of_return_array(ty: &Ty) -> &ir::TyLayout {
658 let ty_func = ty.kind.as_function().unwrap();
659 let ty_array = ty_func.body.kind.as_array().unwrap();
660
661 ty_array.layout.as_ref().unwrap()
662}
663
664fn as_ty_of_param(ty: &Ty) -> &ir::Ty {
665 let ty_func = ty.kind.as_function().unwrap();
666 &ty_func.params[0]
667}
668
669fn pack_bytes_to_u32(mut input: Vec<u8>, output: &mut Vec<u32>) {
670 let input_len = input.len();
671
672 if !input.len().is_multiple_of(4) {
674 input.put_bytes(0, 4 - input.len() % 4);
675 }
676
677 output.reserve(2 + input.len() / 4);
679 output.push((input.len() / 4) as u32 + 1);
680 output.push(input_len as u32);
681 for chunk in input.chunks_exact(4) {
682 output.push(u32::from_le_bytes(chunk.try_into().unwrap()));
683 }
684}