use crate::error::Result;
use crate::records::*;
use std::io::Write;
pub struct Encoder<W: Write> {
writer: W,
}
impl<W: Write> Encoder<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
pub fn encode(&mut self, record: &Record) -> Result<()> {
match record {
Record::SerializationHeader(rec) => {
self.write_u8(RecordType::SerializedStreamHeader as u8)?;
self.write_serialization_header(rec)?;
}
Record::BinaryLibrary(rec) => {
self.write_u8(RecordType::BinaryLibrary as u8)?;
self.write_binary_library(rec)?;
}
Record::ClassWithMembersAndTypes(rec) => {
self.write_u8(RecordType::ClassWithMembersAndTypes as u8)?;
self.write_class_with_members_and_types(rec)?;
}
Record::SystemClassWithMembersAndTypes(rec) => {
self.write_u8(RecordType::SystemClassWithMembersAndTypes as u8)?;
self.write_system_class_with_members_and_types(rec)?;
}
Record::SystemClassWithMembers(rec) => {
self.write_u8(RecordType::SystemClassWithMembers as u8)?;
self.write_system_class_with_members(rec)?;
}
Record::ClassWithMembers(rec) => {
self.write_u8(RecordType::ClassWithMembers as u8)?;
self.write_class_with_members(rec)?;
}
Record::ClassWithId(rec) => {
self.write_u8(RecordType::ClassWithId as u8)?;
self.write_class_with_id(rec)?;
}
Record::BinaryObjectString { object_id, value } => {
self.write_u8(RecordType::BinaryObjectString as u8)?;
self.write_i32(*object_id)?;
self.write_length_prefixed_string(value)?;
}
Record::BinaryArray(rec) => {
self.write_u8(RecordType::BinaryArray as u8)?;
self.write_binary_array(rec)?;
}
Record::ArraySingleObject(rec) => {
self.write_u8(RecordType::ArraySingleObject as u8)?;
self.write_i32(rec.object_id)?;
self.write_i32(rec.length)?;
for val in &rec.element_values {
self.write_object_value(val)?;
}
}
Record::ArraySinglePrimitive(rec) => {
self.write_u8(RecordType::ArraySinglePrimitive as u8)?;
self.write_i32(rec.object_id)?;
self.write_i32(rec.length)?;
self.write_u8(rec.primitive_type_enum as u8)?;
for val in &rec.element_values {
self.write_primitive_value(val)?;
}
}
Record::ArraySingleString(rec) => {
self.write_u8(RecordType::ArraySingleString as u8)?;
self.write_i32(rec.object_id)?;
self.write_i32(rec.length)?;
for val in &rec.element_values {
self.write_object_value(val)?;
}
}
Record::MemberPrimitiveTyped {
primitive_type_enum,
value,
} => {
self.write_u8(RecordType::MemberPrimitiveTyped as u8)?;
self.write_u8(*primitive_type_enum as u8)?;
self.write_primitive_value(value)?;
}
Record::MemberReference { id_ref } => {
self.write_u8(RecordType::MemberReference as u8)?;
self.write_i32(*id_ref)?;
}
Record::ObjectNull => {
self.write_u8(RecordType::ObjectNull as u8)?;
}
Record::ObjectNullMultiple(rec) => {
self.write_u8(RecordType::ObjectNullMultiple as u8)?;
self.write_i32(rec.null_count)?;
}
Record::ObjectNullMultiple256(rec) => {
self.write_u8(RecordType::ObjectNullMultiple256 as u8)?;
self.write_u8(rec.null_count)?;
}
Record::MessageEnd => {
self.write_u8(RecordType::MessageEnd as u8)?;
}
}
Ok(())
}
fn write_i32(&mut self, val: i32) -> Result<()> {
self.writer.write_all(&val.to_le_bytes())?;
Ok(())
}
fn write_u8(&mut self, val: u8) -> Result<()> {
self.writer.write_all(&[val])?;
Ok(())
}
fn write_serialization_header(&mut self, rec: &SerializationHeader) -> Result<()> {
self.write_i32(rec.root_id)?;
self.write_i32(rec.header_id)?;
self.write_i32(rec.major_version)?;
self.write_i32(rec.minor_version)?;
Ok(())
}
fn write_binary_library(&mut self, rec: &BinaryLibrary) -> Result<()> {
self.write_i32(rec.library_id)?;
self.write_length_prefixed_string(&rec.library_name)?;
Ok(())
}
fn write_length_prefixed_string(&mut self, s: &str) -> Result<()> {
let bytes = s.as_bytes();
self.write_variable_length_int(bytes.len() as i32)?;
self.writer.write_all(bytes)?;
Ok(())
}
fn write_variable_length_int(&mut self, mut value: i32) -> Result<()> {
loop {
let mut b = (value & 0x7F) as u8;
value >>= 7;
if value > 0 {
b |= 0x80;
self.write_u8(b)?;
} else {
self.write_u8(b)?;
break;
}
}
Ok(())
}
fn write_class_info(&mut self, info: &ClassInfo) -> Result<()> {
self.write_i32(info.object_id)?;
self.write_length_prefixed_string(&info.name)?;
self.write_i32(info.member_count)?;
for name in &info.member_names {
self.write_length_prefixed_string(name)?;
}
Ok(())
}
fn write_member_type_info(&mut self, info: &MemberTypeInfo) -> Result<()> {
for bt in &info.binary_type_enums {
self.write_u8(*bt as u8)?;
}
for info in &info.additional_infos {
match info {
AdditionalTypeInfo::Primitive(pt) => self.write_u8(*pt as u8)?,
AdditionalTypeInfo::SystemClass(s) => self.write_length_prefixed_string(s)?,
AdditionalTypeInfo::Class(c) => {
self.write_length_prefixed_string(&c.type_name)?;
self.write_i32(c.library_id)?;
}
AdditionalTypeInfo::None => {}
}
}
Ok(())
}
fn write_class_with_members_and_types(&mut self, rec: &ClassWithMembersAndTypes) -> Result<()> {
self.write_class_info(&rec.class_info)?;
self.write_member_type_info(&rec.member_type_info)?;
self.write_i32(rec.library_id)?;
for val in &rec.member_values {
self.write_object_value(val)?;
}
Ok(())
}
fn write_system_class_with_members_and_types(
&mut self,
rec: &SystemClassWithMembersAndTypes,
) -> Result<()> {
self.write_class_info(&rec.class_info)?;
self.write_member_type_info(&rec.member_type_info)?;
for val in &rec.member_values {
self.write_object_value(val)?;
}
Ok(())
}
fn write_system_class_with_members(&mut self, rec: &SystemClassWithMembers) -> Result<()> {
self.write_class_info(&rec.class_info)?;
for val in &rec.member_values {
self.write_object_value(val)?;
}
Ok(())
}
fn write_class_with_members(&mut self, rec: &ClassWithMembers) -> Result<()> {
self.write_class_info(&rec.class_info)?;
self.write_i32(rec.library_id)?;
for val in &rec.member_values {
self.write_object_value(val)?;
}
Ok(())
}
fn write_class_with_id(&mut self, rec: &ClassWithId) -> Result<()> {
self.write_i32(rec.object_id)?;
self.write_i32(rec.metadata_id)?;
for val in &rec.member_values {
self.write_object_value(val)?;
}
Ok(())
}
fn write_binary_array(&mut self, rec: &BinaryArray) -> Result<()> {
self.write_i32(rec.object_id)?;
self.write_u8(rec.binary_array_type_enum)?;
self.write_i32(rec.rank)?;
for len in &rec.lengths {
self.write_i32(*len)?;
}
if let Some(bounds) = &rec.lower_bounds {
for bound in bounds {
self.write_i32(*bound)?;
}
}
self.write_u8(rec.type_enum as u8)?;
match &rec.additional_type_info {
AdditionalTypeInfo::Primitive(pt) => self.write_u8(*pt as u8)?,
AdditionalTypeInfo::SystemClass(s) => self.write_length_prefixed_string(s)?,
AdditionalTypeInfo::Class(c) => {
self.write_length_prefixed_string(&c.type_name)?;
self.write_i32(c.library_id)?;
}
AdditionalTypeInfo::None => {}
}
for val in &rec.element_values {
self.write_object_value(val)?;
}
Ok(())
}
fn write_primitive_value(&mut self, val: &PrimitiveValue) -> Result<()> {
match val {
PrimitiveValue::Boolean(b) => self.write_u8(if *b { 1 } else { 0 })?,
PrimitiveValue::Byte(b) => self.write_u8(*b)?,
PrimitiveValue::Char(c) => self.write_u8(*c as u8)?,
PrimitiveValue::Int16(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::Int32(v) => self.write_i32(*v)?,
PrimitiveValue::Int64(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::SByte(v) => self.write_u8(*v as u8)?,
PrimitiveValue::Single(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::Double(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::TimeSpan(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::DateTime(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::UInt16(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::UInt32(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::UInt64(v) => self.writer.write_all(&v.to_le_bytes())?,
PrimitiveValue::String(s) => self.write_length_prefixed_string(s)?,
PrimitiveValue::Decimal(s) => {
let bytes = hex::decode(s).map_err(|e| {
crate::error::Error::Custom(format!("Invalid hex for Decimal: {}", e))
})?;
if bytes.len() != 16 {
return Err(crate::error::Error::Custom(format!(
"Decimal must be 16 bytes, got {}",
bytes.len()
)));
}
self.writer.write_all(&bytes)?;
}
PrimitiveValue::Null => {} }
Ok(())
}
fn write_object_value(&mut self, val: &ObjectValue) -> Result<()> {
match val {
ObjectValue::Primitive(p) => {
if let PrimitiveValue::Null = p {
self.write_u8(RecordType::ObjectNull as u8)?;
} else {
self.write_primitive_value(p)?;
}
}
ObjectValue::Record(r) => {
self.encode(r)?;
}
}
Ok(())
}
}