Skip to main content

lutra_bin/value/
decode.rs

1use crate::value::TyClass;
2use crate::{boxed, vec};
3
4use bytes::Buf;
5
6use super::Value;
7use super::encode::Context;
8
9use crate::ir;
10use crate::layout;
11use crate::{ArrayReader, Decode, Result};
12
13impl Value {
14    /// Convert .ld binary encoding into Lutra [Value].
15    pub fn decode(buf: &[u8], ty: &ir::Ty, ty_defs: &[ir::TyDef]) -> Result<Value> {
16        let mut ctx = Context::new(ty_defs);
17
18        let mut buf = buf;
19        decode_inner(&mut buf, ty, &mut ctx)
20    }
21}
22
23fn decode_inner<'t>(
24    r: &mut (impl bytes::Buf + Clone),
25    ty: &'t ir::Ty,
26    ctx: &mut Context<'t>,
27) -> Result<Value> {
28    let ty = ctx.get_mat_ty(ty);
29
30    Ok(match TyClass::of_ty(ty)? {
31        TyClass::Prim8 => Value::Prim8(decode::<u8>(r)?),
32        TyClass::Prim16 => Value::Prim16(decode::<u16>(r)?),
33        TyClass::Prim32 => Value::Prim32(decode::<u32>(r)?),
34        TyClass::Prim64 => Value::Prim64(decode::<u64>(r)?),
35
36        TyClass::Tuple(fields) => {
37            let mut res = vec::Vec::with_capacity(fields.len());
38            for field in fields {
39                res.push(decode_inner(r, &field.ty, ctx)?);
40            }
41            Value::Tuple(res)
42        }
43        TyClass::Array(item_ty) => {
44            let mut body = r.clone();
45
46            let (offset, len) = ArrayReader::<&[u8]>::read_head(r.chunk());
47            r.advance(8);
48
49            body.advance(offset);
50
51            let mut buf = vec::Vec::with_capacity(len);
52            for _ in 0..len {
53                buf.push(decode_inner(&mut body, item_ty, ctx)?);
54            }
55
56            Value::Array(buf)
57        }
58
59        TyClass::Enum(variants) => {
60            let head = layout::enum_head_format(variants, &ty.variants_recursive);
61
62            let mut tag_bytes = vec![0; 8];
63            r.copy_to_slice(&mut tag_bytes[0..head.tag_bytes as usize]);
64            tag_bytes.resize(8, 0);
65            let tag = u64::from_le_bytes(tag_bytes.try_into().unwrap()) as usize;
66
67            let variant = variants.get(tag).unwrap();
68
69            let variant_format = layout::enum_variant_format(&head, &variant.ty);
70
71            let inner = if head.has_ptr {
72                if variant_format.is_unit {
73                    // unit variant
74                    Value::Tuple(vec![])
75                } else {
76                    let mut body = r.clone();
77                    body.advance(r.get_u32_le() as usize);
78                    decode_inner(&mut body, &variant.ty, ctx)?
79                }
80            } else {
81                decode_inner(r, &variant.ty, ctx)?
82            };
83
84            r.advance(variant_format.padding_bytes as usize);
85            Value::Enum(tag, boxed::Box::new(inner))
86        }
87    })
88}
89
90fn decode<D: Decode + Sized>(reader: &mut impl bytes::Buf) -> Result<D> {
91    let res = D::decode(reader.chunk());
92    reader.advance(D::head_size().div_ceil(8));
93    res
94}