Skip to main content

lutra_bin/value/
encode.rs

1use crate::vec;
2
3#[cfg(not(feature = "std"))]
4use alloc::collections::BTreeMap as Map;
5#[cfg(feature = "std")]
6use std::collections::HashMap as Map;
7
8use bytes::{BufMut, BytesMut};
9
10use super::{TyClass, Value};
11use crate::encode::ReversePointer;
12use crate::ir;
13use crate::layout::{self, EnumHeadFormat, EnumVariantFormat};
14use crate::{Encode, Error, Result};
15
16impl Value {
17    /// Convert a Lutra [Value] to .ld binary encoding.
18    pub fn encode(&self, ty: &ir::Ty, ty_defs: &[ir::TyDef]) -> Result<vec::Vec<u8>> {
19        let mut buf = BytesMut::new();
20
21        let mut ctx = Context::new(ty_defs);
22
23        let head_ptr = encode_head(&mut buf, self, ty, &mut ctx)?;
24        encode_body(&mut buf, self, head_ptr, ty, &mut ctx)?;
25        Ok(buf.to_vec())
26    }
27}
28
29fn encode_head<'t>(
30    buf: &mut BytesMut,
31    value: &Value,
32    ty: &'t ir::Ty,
33    ctx: &mut Context<'t>,
34) -> Result<ValueHeadPtr> {
35    let ty = ctx.get_mat_ty(ty);
36
37    match TyClass::of_ty(ty)? {
38        TyClass::Prim8 => {
39            let v = value.expect_prim8()?;
40            v.encode_head(buf);
41            Ok(ValueHeadPtr::None)
42        }
43        TyClass::Prim16 => {
44            let v = value.expect_prim16()?;
45            v.encode_head(buf);
46            Ok(ValueHeadPtr::None)
47        }
48        TyClass::Prim32 => {
49            let v = value.expect_prim32()?;
50            v.encode_head(buf);
51            Ok(ValueHeadPtr::None)
52        }
53
54        TyClass::Prim64 => {
55            let v = value.expect_prim64()?;
56            v.encode_head(buf);
57            Ok(ValueHeadPtr::None)
58        }
59
60        TyClass::Tuple(ty_fields) => {
61            let fields = value.expect_tuple()?;
62
63            let mut head_ptrs = vec::Vec::with_capacity(fields.len());
64            for (f, f_ty) in fields.iter().zip(ty_fields) {
65                head_ptrs.push(encode_head(buf, f, &f_ty.ty, ctx)?);
66            }
67
68            Ok(ValueHeadPtr::Tuple(head_ptrs))
69        }
70        TyClass::Array(_ty_items) => {
71            let items = value.expect_array()?;
72
73            let offset_ptr = ReversePointer::new(buf);
74            buf.put_u32_le(items.len() as u32);
75            Ok(ValueHeadPtr::Offset(offset_ptr))
76        }
77        TyClass::Enum(ty_variants) => {
78            let (tag, inner) = value.expect_enum()?;
79
80            let (head, variant, tag, variant_ty) =
81                enum_params_encode(tag, ty_variants, &ty.variants_recursive)?;
82
83            let tag_bytes = &(tag as u64).to_le_bytes()[0..head.tag_bytes as usize];
84            buf.put_slice(tag_bytes);
85
86            let r = if head.has_ptr {
87                if variant.is_unit {
88                    // this is unit variant, no need to encode head
89                    ValueHeadPtr::None
90                } else {
91                    let offset = ReversePointer::new(buf);
92
93                    ValueHeadPtr::Offset(offset)
94                }
95            } else {
96                encode_head(buf, inner, variant_ty, ctx)?
97            };
98
99            if variant.padding_bytes > 0 {
100                buf.put_bytes(0, variant.padding_bytes as usize);
101            }
102            Ok(r)
103        }
104    }
105}
106
107enum ValueHeadPtr {
108    None,
109    Offset(ReversePointer),
110    Tuple(vec::Vec<ValueHeadPtr>),
111}
112
113fn encode_body<'t>(
114    w: &mut BytesMut,
115    value: &Value,
116    head_ptr: ValueHeadPtr,
117    ty: &'t ir::Ty,
118    ctx: &mut Context<'t>,
119) -> Result<()> {
120    let ty = ctx.get_mat_ty(ty);
121
122    match TyClass::of_ty(ty)? {
123        TyClass::Prim8 | TyClass::Prim16 | TyClass::Prim32 | TyClass::Prim64 => {}
124        TyClass::Tuple(ty_fields) => {
125            let fields = value.expect_tuple()?;
126
127            let ValueHeadPtr::Tuple(tuple_ptrs) = head_ptr else {
128                return Err(Error::Bug);
129            };
130
131            for ((f, h), f_ty) in fields.iter().zip(tuple_ptrs).zip(ty_fields) {
132                encode_body(w, f, h, &f_ty.ty, ctx)?
133            }
134        }
135        TyClass::Array(items_ty) => {
136            let items = value.expect_array()?;
137
138            let ValueHeadPtr::Offset(offset_ptr) = head_ptr else {
139                return Err(Error::Bug);
140            };
141            offset_ptr.write_cur_len(w);
142
143            let mut head_ptrs = vec::Vec::with_capacity(items.len());
144            for i in items {
145                head_ptrs.push(encode_head(w, i, items_ty, ctx)?);
146            }
147
148            for (i, h) in items.iter().zip(head_ptrs) {
149                encode_body(w, i, h, items_ty, ctx)?;
150            }
151        }
152        TyClass::Enum(variants) => {
153            let (tag, inner) = value.expect_enum()?;
154
155            let (head_format, _, _, variant_ty) =
156                enum_params_encode(tag, variants, &ty.variants_recursive)?;
157
158            if head_format.has_ptr {
159                match head_ptr {
160                    ValueHeadPtr::None => {
161                        // unit variant, done
162                    }
163                    ValueHeadPtr::Offset(offset_ptr) => {
164                        offset_ptr.write_cur_len(w);
165
166                        let head_ptr = encode_head(w, inner, variant_ty, ctx)?;
167                        encode_body(w, inner, head_ptr, variant_ty, ctx)?;
168                    }
169                    ValueHeadPtr::Tuple(_) => return Err(Error::Bug),
170                }
171            } else {
172                encode_body(w, inner, head_ptr, variant_ty, ctx)?;
173            }
174        }
175    }
176
177    Ok(())
178}
179
180fn enum_params_encode<'a>(
181    tag: usize,
182    variants: &'a [ir::TyEnumVariant],
183    variants_recursive: &[u16],
184) -> Result<(EnumHeadFormat, EnumVariantFormat, usize, &'a ir::Ty)> {
185    let head_format = layout::enum_head_format(variants, variants_recursive);
186
187    let variant = variants.get(tag).ok_or(Error::InvalidData)?;
188
189    let variant_format = layout::enum_variant_format(&head_format, &variant.ty);
190    Ok((head_format, variant_format, tag, &variant.ty))
191}
192
193pub(super) struct Context<'t> {
194    ty_defs: Map<&'t ir::Path, &'t ir::Ty>,
195}
196
197impl<'t> Context<'t> {
198    pub(super) fn new(ty_defs: &'t [ir::TyDef]) -> Self {
199        Context {
200            ty_defs: ty_defs.iter().map(|def| (&def.name, &def.ty)).collect(),
201        }
202    }
203
204    pub(super) fn get_mat_ty(&self, mut ty: &'t ir::Ty) -> &'t ir::Ty {
205        while let ir::TyKind::Ident(ident) = &ty.kind {
206            ty = self.ty_defs.get(ident).unwrap();
207        }
208        ty
209    }
210}