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::PrimText => {
61            let v = value.expect_text()?;
62            Ok(ValueHeadPtr::Offset(v.encode_head(buf)))
63        }
64
65        TyClass::Tuple(ty_fields) => {
66            let fields = value.expect_tuple()?;
67
68            let mut head_ptrs = vec::Vec::with_capacity(fields.len());
69            for (f, f_ty) in fields.iter().zip(ty_fields) {
70                head_ptrs.push(encode_head(buf, f, &f_ty.ty, ctx)?);
71            }
72
73            Ok(ValueHeadPtr::Tuple(head_ptrs))
74        }
75        TyClass::Array(_ty_items) => {
76            let items = value.expect_array()?;
77
78            let offset_ptr = ReversePointer::new(buf);
79            buf.put_u32_le(items.len() as u32);
80            Ok(ValueHeadPtr::Offset(offset_ptr))
81        }
82        TyClass::Enum(ty_variants) => {
83            let (tag, inner) = value.expect_enum()?;
84
85            let (head, variant, tag, variant_ty) =
86                enum_params_encode(tag, ty_variants, &ty.variants_recursive)?;
87
88            let tag_bytes = &(tag as u64).to_le_bytes()[0..head.tag_bytes as usize];
89            buf.put_slice(tag_bytes);
90
91            let r = if head.has_ptr {
92                if variant.is_unit {
93                    // this is unit variant, no need to encode head
94                    ValueHeadPtr::None
95                } else {
96                    let offset = ReversePointer::new(buf);
97
98                    ValueHeadPtr::Offset(offset)
99                }
100            } else {
101                encode_head(buf, inner, variant_ty, ctx)?
102            };
103
104            if variant.padding_bytes > 0 {
105                buf.put_bytes(0, variant.padding_bytes as usize);
106            }
107            Ok(r)
108        }
109    }
110}
111
112enum ValueHeadPtr {
113    None,
114    Offset(ReversePointer),
115    Tuple(vec::Vec<ValueHeadPtr>),
116}
117
118fn encode_body<'t>(
119    w: &mut BytesMut,
120    value: &Value,
121    head_ptr: ValueHeadPtr,
122    ty: &'t ir::Ty,
123    ctx: &mut Context<'t>,
124) -> Result<()> {
125    let ty = ctx.get_mat_ty(ty);
126
127    match TyClass::of_ty(ty)? {
128        TyClass::Prim8 | TyClass::Prim16 | TyClass::Prim32 | TyClass::Prim64 => {}
129        TyClass::PrimText => {
130            let v = value.expect_text()?;
131
132            let ValueHeadPtr::Offset(offset_ptr) = head_ptr else {
133                return Err(Error::Bug);
134            };
135
136            v.encode_body(offset_ptr, w);
137        }
138        TyClass::Tuple(ty_fields) => {
139            let fields = value.expect_tuple()?;
140
141            let ValueHeadPtr::Tuple(tuple_ptrs) = head_ptr else {
142                return Err(Error::Bug);
143            };
144
145            for ((f, h), f_ty) in fields.iter().zip(tuple_ptrs.into_iter()).zip(ty_fields) {
146                encode_body(w, f, h, &f_ty.ty, ctx)?
147            }
148        }
149        TyClass::Array(items_ty) => {
150            let items = value.expect_array()?;
151
152            let ValueHeadPtr::Offset(offset_ptr) = head_ptr else {
153                return Err(Error::Bug);
154            };
155            offset_ptr.write_cur_len(w);
156
157            let mut head_ptrs = vec::Vec::with_capacity(items.len());
158            for i in items {
159                head_ptrs.push(encode_head(w, i, items_ty, ctx)?);
160            }
161
162            for (i, h) in items.iter().zip(head_ptrs.into_iter()) {
163                encode_body(w, i, h, items_ty, ctx)?;
164            }
165        }
166        TyClass::Enum(variants) => {
167            let (tag, inner) = value.expect_enum()?;
168
169            let (head_format, _, _, variant_ty) =
170                enum_params_encode(tag, variants, &ty.variants_recursive)?;
171
172            if head_format.has_ptr {
173                match head_ptr {
174                    ValueHeadPtr::None => {
175                        // unit variant, done
176                    }
177                    ValueHeadPtr::Offset(offset_ptr) => {
178                        offset_ptr.write_cur_len(w);
179
180                        let head_ptr = encode_head(w, inner, variant_ty, ctx)?;
181                        encode_body(w, inner, head_ptr, variant_ty, ctx)?;
182                    }
183                    ValueHeadPtr::Tuple(_) => return Err(Error::Bug),
184                }
185            } else {
186                encode_body(w, inner, head_ptr, variant_ty, ctx)?;
187            }
188        }
189    }
190
191    Ok(())
192}
193
194fn enum_params_encode<'a>(
195    tag: usize,
196    variants: &'a [ir::TyEnumVariant],
197    variants_recursive: &[u16],
198) -> Result<(EnumHeadFormat, EnumVariantFormat, usize, &'a ir::Ty)> {
199    let head_format = layout::enum_head_format(variants, variants_recursive);
200
201    let variant = variants.get(tag).ok_or(Error::InvalidData)?;
202
203    let variant_format = layout::enum_variant_format(&head_format, &variant.ty);
204    Ok((head_format, variant_format, tag, &variant.ty))
205}
206
207pub(super) struct Context<'t> {
208    ty_defs: Map<&'t ir::Path, &'t ir::Ty>,
209}
210
211impl<'t> Context<'t> {
212    pub(super) fn new(ty_defs: &'t [ir::TyDef]) -> Self {
213        Context {
214            ty_defs: ty_defs.iter().map(|def| (&def.name, &def.ty)).collect(),
215        }
216    }
217
218    pub(super) fn get_mat_ty(&self, ty: &'t ir::Ty) -> &'t ir::Ty {
219        if let ir::TyKind::Ident(ident) = &ty.kind {
220            self.ty_defs.get(ident).unwrap()
221        } else {
222            ty
223        }
224    }
225}