use std::sync::Arc;
use sim_codec::{
CodecDefaultDecode, CodecRuntime, Decoder, Encoder, Input, LocatedDecoder, LocatedEncoder,
Output, ReadCx, TreeDecoder, TreeEncoder, codec_value, validate_expr_tree,
};
use sim_kernel::{
AbiVersion, DefaultFactory, Dependency, Error, Export, Expr, Lib, LibManifest, LibTarget,
Linker, LocatedExpr, LocatedExprTree, Result, Symbol, Version, WriteCx,
};
use crate::reader::BinaryReader;
use crate::writer::BinaryWriter;
use crate::{BinaryFrame, DecodeLimits, FLAG_NONE, FLAG_ORIGIN, FLAG_TREE_ORIGIN, FrameTables};
pub struct BinaryCodec;
impl Decoder for BinaryCodec {
fn decode(&self, cx: &mut ReadCx<'_>, input: Input) -> Result<Expr> {
let bytes = match input {
Input::Text(text) => text.into_bytes(),
Input::Bytes(bytes) => bytes,
};
decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
.map(|(_, tree)| tree.located().expr)
}
}
impl Encoder for BinaryCodec {
fn encode(&self, _cx: &mut WriteCx<'_>, expr: &Expr) -> Result<Output> {
Ok(Output::Bytes(encode_frame(expr)?.0))
}
}
impl LocatedDecoder for BinaryCodec {
fn decode_located(
&self,
cx: &mut ReadCx<'_>,
input: Input,
_source_id: String,
) -> Result<LocatedExpr> {
let bytes = match input {
Input::Text(text) => text.into_bytes(),
Input::Bytes(bytes) => bytes,
};
decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
.map(|(_, tree)| tree.located())
}
}
impl LocatedEncoder for BinaryCodec {
fn encode_located(&self, cx: &mut WriteCx<'_>, expr: &LocatedExpr) -> Result<Output> {
Ok(Output::Bytes(
encode_located_frame(expr, cx.options.lossless_origin)?.0,
))
}
}
impl TreeDecoder for BinaryCodec {
fn decode_tree(
&self,
cx: &mut ReadCx<'_>,
input: Input,
_source_id: String,
) -> Result<LocatedExprTree> {
let bytes = match input {
Input::Text(text) => text.into_bytes(),
Input::Bytes(bytes) => bytes,
};
decode_located_tree_frame_with_limits(cx.codec, &bytes, DecodeLimits::from(cx.limits))
.map(|(_, tree)| tree)
}
}
impl TreeEncoder for BinaryCodec {
fn encode_tree(&self, cx: &mut WriteCx<'_>, expr: &LocatedExprTree) -> Result<Output> {
validate_expr_tree(cx.codec, expr)?;
Ok(Output::Bytes(
encode_located_tree_frame(expr, cx.options.lossless_origin)?.0,
))
}
}
pub fn encode_frame(expr: &Expr) -> Result<BinaryFrame> {
encode_located_frame(
&LocatedExpr {
expr: expr.clone(),
origin: None,
},
false,
)
}
pub fn encode_located_frame(located: &LocatedExpr, include_origin: bool) -> Result<BinaryFrame> {
let tables = FrameTables::collect(&located.expr);
let mut writer = BinaryWriter::new(tables)?;
writer.flags = if include_origin && located.origin.is_some() {
FLAG_ORIGIN
} else {
FLAG_NONE
};
writer.write_header()?;
writer.write_expr(&located.expr)?;
if writer.flags & FLAG_ORIGIN != 0 {
writer.write_origin(
located
.origin
.as_ref()
.expect("origin flag requires origin payload"),
)?;
}
Ok(BinaryFrame(writer.bytes))
}
pub fn encode_located_tree_frame(
tree: &LocatedExprTree,
include_origin: bool,
) -> Result<BinaryFrame> {
validate_expr_tree(sim_kernel::CodecId(0), tree)?;
let tables = FrameTables::collect(&tree.expr);
let mut writer = BinaryWriter::new(tables)?;
writer.flags = if include_origin {
FLAG_TREE_ORIGIN
} else {
FLAG_NONE
};
writer.write_header()?;
writer.write_expr(&tree.expr)?;
if writer.flags & FLAG_TREE_ORIGIN != 0 {
writer.write_origin_tree(tree)?;
}
Ok(BinaryFrame(writer.bytes))
}
pub fn decode_frame(codec: sim_kernel::CodecId, bytes: &[u8]) -> Result<(FrameTables, Expr)> {
let located = decode_located_frame(codec, bytes)?;
Ok((located.0, located.1.expr))
}
pub fn decode_located_frame(
codec: sim_kernel::CodecId,
bytes: &[u8],
) -> Result<(FrameTables, LocatedExpr)> {
let (tables, tree) = decode_located_tree_frame(codec, bytes)?;
Ok((tables, tree.located()))
}
pub fn decode_located_tree_frame(
codec: sim_kernel::CodecId,
bytes: &[u8],
) -> Result<(FrameTables, LocatedExprTree)> {
decode_located_tree_frame_with_limits(codec, bytes, DecodeLimits::default())
}
pub fn decode_located_tree_frame_with_limits(
codec: sim_kernel::CodecId,
bytes: &[u8],
limits: DecodeLimits,
) -> Result<(FrameTables, LocatedExprTree)> {
let mut reader = BinaryReader::new(codec, bytes, limits)?;
let tables = reader.read_header()?;
let expr = reader.read_expr()?;
let mut tree = if reader.flags & FLAG_TREE_ORIGIN != 0 {
reader.read_origin_tree(expr)?
} else {
LocatedExprTree::from_expr_recursive(expr)
};
if reader.flags & FLAG_ORIGIN != 0 {
tree.origin = Some(reader.read_origin()?);
}
if !reader.is_empty() {
return Err(Error::CodecError {
codec,
message: "trailing bytes after binary payload".to_owned(),
});
}
Ok((tables, tree))
}
pub struct BinaryCodecLib {
symbol: Symbol,
codec_id: sim_kernel::CodecId,
}
impl BinaryCodecLib {
pub fn new(id: sim_kernel::CodecId) -> Self {
Self {
symbol: Symbol::qualified("codec", "binary"),
codec_id: id,
}
}
}
impl Lib for BinaryCodecLib {
fn manifest(&self) -> LibManifest {
LibManifest {
id: self.symbol.clone(),
version: Version(env!("CARGO_PKG_VERSION").to_owned()),
abi: AbiVersion { major: 0, minor: 1 },
target: LibTarget::HostRegistered,
requires: Vec::<Dependency>::new(),
capabilities: Vec::new(),
exports: vec![Export::Codec {
symbol: self.symbol.clone(),
codec_id: Some(self.codec_id),
}],
}
}
fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker) -> Result<()> {
let _factory = DefaultFactory;
let expr_shape =
sim_codec::resolve_expr_shape(linker, &Symbol::qualified("codec", "BinaryFrame"))?;
let options_shape = sim_codec::resolve_options_shape(linker)?;
linker.codec_value(
self.symbol.clone(),
codec_value(CodecRuntime {
id: self.codec_id,
symbol: self.symbol.clone(),
decoder: Some(Arc::new(BinaryCodec)),
located_decoder: Some(Arc::new(BinaryCodec)),
tree_decoder: Some(Arc::new(BinaryCodec)),
encoder: Some(Arc::new(BinaryCodec)),
located_encoder: Some(Arc::new(BinaryCodec)),
tree_encoder: Some(Arc::new(BinaryCodec)),
expr_shape,
options_shape,
default_decode: CodecDefaultDecode::Datum,
}),
)?;
Ok(())
}
}