1use std::sync::Arc;
7
8use sim_codec::{
9 CodecDefaultDecode, CodecRuntime, Decoder, Encoder, Input, LocatedDecoder, LocatedEncoder,
10 Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
11};
12use sim_kernel::{
13 AbiVersion, DefaultFactory, Dependency, Error, Export, Expr, Lib, LibManifest, LibTarget,
14 Linker, LocatedExpr, LocatedExprTree, Result, Symbol, Version, WriteCx,
15};
16
17use crate::cookbook::{BinaryRoundtripReport, roundtrip_report_symbol};
18use crate::reader::BinaryReader;
19use crate::writer::BinaryWriter;
20use crate::{BinaryFrame, DecodeLimits, FLAG_NONE, FLAG_ORIGIN, FLAG_TREE_ORIGIN, FrameTables};
21
22pub struct BinaryCodec;
32
33impl Decoder for BinaryCodec {
34 fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
35 let bytes = match input {
36 Input::Text(text) => text.into_bytes(),
37 Input::Bytes(bytes) => bytes,
38 };
39 decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
40 .map(|(_, tree)| tree.located().expr)
41 }
42}
43
44impl Encoder for BinaryCodec {
45 fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
46 Ok(Output::Bytes(encode_frame(expr)?.0))
47 }
48}
49
50impl LocatedDecoder for BinaryCodec {
51 fn decode_located(
52 &self,
53 cx: &mut ReadCx<'_>,
54 input: Input,
55 _source_id: String,
56 ) -> Result<LocatedExpr> {
57 let bytes = match input {
58 Input::Text(text) => text.into_bytes(),
59 Input::Bytes(bytes) => bytes,
60 };
61 decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
62 .map(|(_, tree)| tree.located())
63 }
64}
65
66impl LocatedEncoder for BinaryCodec {
67 fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
68 Ok(Output::Bytes(
69 encode_located_frame(expr, cx.options.lossless_origin)?.0,
70 ))
71 }
72}
73
74impl TreeDecoder for BinaryCodec {
75 fn decode_tree(
76 &self,
77 cx: &mut ReadCx<'_>,
78 input: Input,
79 _source_id: String,
80 ) -> Result<LocatedExprTree> {
81 let bytes = match input {
82 Input::Text(text) => text.into_bytes(),
83 Input::Bytes(bytes) => bytes,
84 };
85 decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
86 .map(|(_, tree)| tree)
87 }
88}
89
90impl TreeEncoder for BinaryCodec {
91 fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
92 validate_expr_tree(cx.codec, expr)?;
93 Ok(Output::Bytes(
94 encode_located_tree_frame(expr, cx.options.lossless_origin)?.0,
95 ))
96 }
97}
98
99pub fn encode_frame(expr: &Expr) -> Result<BinaryFrame> {
101 encode_located_frame(
102 &LocatedExpr {
103 expr: expr.clone(),
104 origin: None,
105 },
106 false,
107 )
108}
109
110pub fn encode_located_frame(located: &LocatedExpr, include_origin: bool) -> Result<BinaryFrame> {
115 let tables = FrameTables::collect(&located.expr);
116 let mut writer = BinaryWriter::new(tables)?;
117 writer.flags = if include_origin && located.origin.is_some() {
118 FLAG_ORIGIN
119 } else {
120 FLAG_NONE
121 };
122 writer.write_header()?;
123 writer.write_expr(&located.expr)?;
124 if writer.flags & FLAG_ORIGIN != 0 {
125 writer.write_origin(
126 located
127 .origin
128 .as_ref()
129 .expect("origin flag requires origin payload"),
130 )?;
131 }
132 Ok(BinaryFrame(writer.bytes))
133}
134
135pub fn encode_located_tree_frame(
141 tree: &LocatedExprTree,
142 include_origin: bool,
143) -> Result<BinaryFrame> {
144 validate_expr_tree(sim_kernel::CodecId(0), tree)?;
145 let tables = FrameTables::collect(&tree.expr);
146 let mut writer = BinaryWriter::new(tables)?;
147 writer.flags = if include_origin {
148 FLAG_TREE_ORIGIN
149 } else {
150 FLAG_NONE
151 };
152 writer.write_header()?;
153 writer.write_expr(&tree.expr)?;
154 if writer.flags & FLAG_TREE_ORIGIN != 0 {
155 writer.write_origin_tree(tree)?;
156 }
157 Ok(BinaryFrame(writer.bytes))
158}
159
160pub fn decode_frame(codec: sim_kernel::CodecId, bytes: &[u8]) -> Result<(FrameTables, Expr)> {
165 let located = decode_located_frame(codec, bytes)?;
166 Ok((located.0, located.1.expr))
167}
168
169pub fn decode_located_frame(
174 codec: sim_kernel::CodecId,
175 bytes: &[u8],
176) -> Result<(FrameTables, LocatedExpr)> {
177 let (tables, tree) = decode_located_tree_frame(codec, bytes)?;
178 Ok((tables, tree.located()))
179}
180
181pub fn decode_located_tree_frame(
188 codec: sim_kernel::CodecId,
189 bytes: &[u8],
190) -> Result<(FrameTables, LocatedExprTree)> {
191 decode_located_tree_frame_with_limits(codec, bytes, DecodeLimits::default())
192}
193
194pub fn decode_located_tree_frame_with_limits(
201 codec: sim_kernel::CodecId,
202 bytes: &[u8],
203 limits: DecodeLimits,
204) -> Result<(FrameTables, LocatedExprTree)> {
205 let mut reader = BinaryReader::new(codec, bytes, limits)?;
206 let tables = reader.read_header()?;
207 let expr = reader.read_expr()?;
208 let mut tree = if reader.flags & FLAG_TREE_ORIGIN != 0 {
209 reader.read_origin_tree(expr)?
210 } else {
211 LocatedExprTree::from_expr_recursive(expr)
212 };
213 if reader.flags & FLAG_ORIGIN != 0 {
214 tree.origin = Some(reader.read_origin()?);
215 }
216 if !reader.is_empty() {
217 return Err(Error::CodecError {
218 codec,
219 message: "trailing bytes after binary payload".to_owned(),
220 });
221 }
222 Ok((tables, tree))
223}
224
225pub struct BinaryCodecLib {
231 symbol: Symbol,
232 codec_id: sim_kernel::CodecId,
233}
234
235impl BinaryCodecLib {
236 pub fn new(id: sim_kernel::CodecId) -> Self {
239 Self {
240 symbol: Symbol::qualified("codec", "binary"),
241 codec_id: id,
242 }
243 }
244}
245
246impl Lib for BinaryCodecLib {
247 fn manifest(&self) -> LibManifest {
248 LibManifest {
249 id: self.symbol.clone(),
250 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
251 abi: AbiVersion { major: 0, minor: 1 },
252 target: LibTarget::HostRegistered,
253 requires: Vec::<Dependency>::new(),
254 capabilities: Vec::new(),
255 exports: vec![
256 Export::Codec {
257 symbol: self.symbol.clone(),
258 codec_id: Some(self.codec_id),
259 },
260 Export::Function {
261 symbol: roundtrip_report_symbol(),
262 function_id: None,
263 },
264 ],
265 }
266 }
267
268 fn load(&self, cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
269 let _factory = DefaultFactory;
270 let expr_shape =
271 sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "BinaryFrame"))?;
272 let options_shape = sim_codec::resolve_options_shape(linker)?;
273
274 linker.codec_value(
275 self.symbol.clone(),
276 codec_value(CodecRuntime {
277 id: self.codec_id,
278 symbol: self.symbol.clone(),
279 decoder: Some(Arc::new(BinaryCodec)),
280 located_decoder: Some(Arc::new(BinaryCodec)),
281 tree_decoder: Some(Arc::new(BinaryCodec)),
282 encoder: Some(Arc::new(BinaryCodec)),
283 located_encoder: Some(Arc::new(BinaryCodec)),
284 tree_encoder: Some(Arc::new(BinaryCodec)),
285 expr_shape,
286 options_shape,
287 default_decode: CodecDefaultDecode::Datum,
288 }),
289 )?;
290 linker.function_value(
291 roundtrip_report_symbol(),
292 cx.factory().opaque(Arc::new(BinaryRoundtripReport))?,
293 )?;
294 Ok(())
295 }
296}