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 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 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 }
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}