use alloc::string::String;
use alloc::vec::Vec;
use zerodds_cdr::{BufferReader, BufferWriter, DecodeError, EncodeError};
use crate::type_identifier::TypeIdentifier;
use super::flags::{StructMemberFlag, UnionMemberFlag};
pub type MemberId = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
pub struct NameHash(pub [u8; 4]);
impl NameHash {
#[must_use]
pub fn from_name(name: &str) -> Self {
let digest = zerodds_foundation::md5(name.as_bytes());
let out: [u8; 4] = [digest[0], digest[1], digest[2], digest[3]];
Self(out)
}
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
w.write_bytes(&self.0)
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let bytes = r.read_bytes(4)?;
let Ok(out): Result<[u8; 4], _> = bytes.try_into() else {
return Err(DecodeError::UnexpectedEof {
needed: 4,
offset: 0,
});
};
Ok(Self(out))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommonStructMember {
pub member_id: MemberId,
pub member_flags: StructMemberFlag,
pub member_type_id: TypeIdentifier,
}
impl CommonStructMember {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
w.write_u32(self.member_id)?;
w.write_u16(self.member_flags.0)?;
self.member_type_id.encode_into(w)
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let member_id = r.read_u32()?;
let member_flags = StructMemberFlag(r.read_u16()?);
let member_type_id = TypeIdentifier::decode_from(r)?;
Ok(Self {
member_id,
member_flags,
member_type_id,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommonUnionMember {
pub member_id: MemberId,
pub member_flags: UnionMemberFlag,
pub type_id: TypeIdentifier,
pub label_seq: alloc::vec::Vec<i32>,
}
impl CommonUnionMember {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
w.write_u32(self.member_id)?;
w.write_u16(self.member_flags.0)?;
self.type_id.encode_into(w)?;
let len =
u32::try_from(self.label_seq.len()).map_err(|_| EncodeError::ValueOutOfRange {
message: "union label sequence length exceeds u32::MAX",
})?;
w.write_u32(len)?;
for l in &self.label_seq {
w.write_u32(*l as u32)?;
}
Ok(())
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let member_id = r.read_u32()?;
let member_flags = UnionMemberFlag(r.read_u16()?);
let type_id = TypeIdentifier::decode_from(r)?;
let len = r.read_u32()? as usize;
let cap = safe_capacity(len, 4, r.remaining());
let mut label_seq = alloc::vec::Vec::with_capacity(cap);
for _ in 0..len {
label_seq.push(r.read_u32()? as i32);
}
Ok(Self {
member_id,
member_flags,
type_id,
label_seq,
})
}
}
pub type QualifiedTypeName = String;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VerbatimPlacement {
Before,
After,
BeginFile,
EndFile,
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AppliedVerbatimAnnotation {
pub placement: VerbatimPlacement,
pub language: String,
pub text: String,
}
impl AppliedVerbatimAnnotation {
fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
let placement_str = match &self.placement {
VerbatimPlacement::Before => "BEFORE_DECLARATION",
VerbatimPlacement::After => "AFTER_DECLARATION",
VerbatimPlacement::BeginFile => "BEGIN_FILE",
VerbatimPlacement::EndFile => "END_FILE",
VerbatimPlacement::Other(s) => s.as_str(),
};
w.write_string(placement_str)?;
w.write_string(&self.language)?;
w.write_string(&self.text)
}
fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let placement_str = r.read_string()?;
let placement = match placement_str.as_str() {
"BEFORE_DECLARATION" => VerbatimPlacement::Before,
"AFTER_DECLARATION" => VerbatimPlacement::After,
"BEGIN_FILE" => VerbatimPlacement::BeginFile,
"END_FILE" => VerbatimPlacement::EndFile,
_ => VerbatimPlacement::Other(placement_str),
};
let language = r.read_string()?;
let text = r.read_string()?;
Ok(Self {
placement,
language,
text,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AppliedBuiltinTypeAnnotations {
pub verbatim: Option<AppliedVerbatimAnnotation>,
}
impl AppliedBuiltinTypeAnnotations {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
match &self.verbatim {
None => w.write_u32(0),
Some(v) => {
w.write_u32(1)?;
v.encode_into(w)
}
}
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let len = r.read_u32()?;
let verbatim = if len == 0 {
None
} else {
Some(AppliedVerbatimAnnotation::decode_from(r)?)
};
for _ in 1..len {
let _ = AppliedVerbatimAnnotation::decode_from(r)?;
}
Ok(Self { verbatim })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AppliedAnnotationParameter {
pub paramname_hash: NameHash,
pub value: Vec<u8>,
}
impl AppliedAnnotationParameter {
fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
self.paramname_hash.encode_into(w)?;
let len = u32::try_from(self.value.len()).map_err(|_| EncodeError::ValueOutOfRange {
message: "annotation parameter value exceeds u32::MAX bytes",
})?;
w.write_u32(len)?;
w.write_bytes(&self.value)
}
fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let paramname_hash = NameHash::decode_from(r)?;
let len = r.read_u32()? as usize;
let value = r.read_bytes(len)?.to_vec();
Ok(Self {
paramname_hash,
value,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AppliedAnnotation {
pub annotation_typeid: TypeIdentifier,
pub param_seq: Vec<AppliedAnnotationParameter>,
}
impl AppliedAnnotation {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
self.annotation_typeid.encode_into(w)?;
encode_seq(w, &self.param_seq, |w, p| p.encode_into(w))
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let annotation_typeid = TypeIdentifier::decode_from(r)?;
let param_seq = decode_seq(r, AppliedAnnotationParameter::decode_from)?;
Ok(Self {
annotation_typeid,
param_seq,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct OptionalAppliedAnnotationSeq(pub Option<Vec<AppliedAnnotation>>);
impl OptionalAppliedAnnotationSeq {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
match &self.0 {
None => w.write_u32(0),
Some(seq) => {
w.write_u32(1)?;
encode_seq(w, seq, |w, a| a.encode_into(w))
}
}
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let len = r.read_u32()?;
if len == 0 {
return Ok(Self(None));
}
let seq = decode_seq(r, AppliedAnnotation::decode_from)?;
for _ in 1..len {
let _ = decode_seq(r, AppliedAnnotation::decode_from)?;
}
Ok(Self(Some(seq)))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompleteTypeDetail {
pub ann_builtin: AppliedBuiltinTypeAnnotations,
pub ann_custom: OptionalAppliedAnnotationSeq,
pub type_name: QualifiedTypeName,
}
impl CompleteTypeDetail {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
self.ann_builtin.encode_into(w)?;
self.ann_custom.encode_into(w)?;
w.write_string(&self.type_name)
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let ann_builtin = AppliedBuiltinTypeAnnotations::decode_from(r)?;
let ann_custom = OptionalAppliedAnnotationSeq::decode_from(r)?;
let type_name = r.read_string()?;
Ok(Self {
ann_builtin,
ann_custom,
type_name,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AppliedBuiltinMemberAnnotations {
pub unit: Option<String>,
pub min: Option<Vec<u8>>,
pub max: Option<Vec<u8>>,
pub hash_id: Option<String>,
pub default_value: Option<String>,
}
impl AppliedBuiltinMemberAnnotations {
fn write_opt_string(w: &mut BufferWriter, s: &Option<String>) -> Result<(), EncodeError> {
match s {
None => w.write_u32(0),
Some(v) => {
w.write_u32(1)?;
w.write_string(v)
}
}
}
fn read_opt_string(r: &mut BufferReader<'_>) -> Result<Option<String>, DecodeError> {
let len = r.read_u32()?;
if len == 0 {
return Ok(None);
}
if len != 1 {
return Err(DecodeError::LengthExceeded {
announced: len as usize,
remaining: 1,
offset: 0,
});
}
let out = r.read_string()?;
Ok(Some(out))
}
fn write_opt_bytes(w: &mut BufferWriter, b: &Option<Vec<u8>>) -> Result<(), EncodeError> {
match b {
None => w.write_u32(0),
Some(v) => {
w.write_u32(1)?;
let len = u32::try_from(v.len()).map_err(|_| EncodeError::ValueOutOfRange {
message: "annotation value exceeds u32::MAX",
})?;
w.write_u32(len)?;
w.write_bytes(v)
}
}
}
fn read_opt_bytes(r: &mut BufferReader<'_>) -> Result<Option<Vec<u8>>, DecodeError> {
let len = r.read_u32()?;
if len == 0 {
return Ok(None);
}
if len != 1 {
return Err(DecodeError::LengthExceeded {
announced: len as usize,
remaining: 1,
offset: 0,
});
}
let inner_len = r.read_u32()? as usize;
let out = r.read_bytes(inner_len)?.to_vec();
Ok(Some(out))
}
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
Self::write_opt_string(w, &self.unit)?;
Self::write_opt_bytes(w, &self.min)?;
Self::write_opt_bytes(w, &self.max)?;
Self::write_opt_string(w, &self.hash_id)?;
Self::write_opt_string(w, &self.default_value)
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let unit = Self::read_opt_string(r)?;
let min = Self::read_opt_bytes(r)?;
let max = Self::read_opt_bytes(r)?;
let hash_id = Self::read_opt_string(r)?;
let default_value = if r.remaining() >= 4 {
Self::read_opt_string(r).ok().flatten()
} else {
None
};
Ok(Self {
unit,
min,
max,
hash_id,
default_value,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompleteMemberDetail {
pub name: String,
pub ann_builtin: AppliedBuiltinMemberAnnotations,
pub ann_custom: OptionalAppliedAnnotationSeq,
}
impl CompleteMemberDetail {
pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
w.write_string(&self.name)?;
self.ann_builtin.encode_into(w)?;
self.ann_custom.encode_into(w)
}
pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
let name = r.read_string()?;
let ann_builtin = AppliedBuiltinMemberAnnotations::decode_from(r)?;
let ann_custom = OptionalAppliedAnnotationSeq::decode_from(r)?;
Ok(Self {
name,
ann_builtin,
ann_custom,
})
}
}
pub(crate) fn encode_seq<T, F>(
w: &mut BufferWriter,
items: &[T],
mut f: F,
) -> Result<(), EncodeError>
where
F: FnMut(&mut BufferWriter, &T) -> Result<(), EncodeError>,
{
let len = u32::try_from(items.len()).map_err(|_| EncodeError::ValueOutOfRange {
message: "sequence length exceeds u32::MAX",
})?;
w.write_u32(len)?;
for it in items {
f(w, it)?;
}
Ok(())
}
pub const DECODE_PREALLOC_CAP: usize = 4096;
#[must_use]
pub(crate) fn safe_capacity(len: usize, min_elem_size: usize, remaining_bytes: usize) -> usize {
let by_bytes = if min_elem_size == 0 {
DECODE_PREALLOC_CAP
} else {
remaining_bytes.saturating_div(min_elem_size)
};
len.min(by_bytes).min(DECODE_PREALLOC_CAP)
}
pub(crate) fn decode_seq<T, F>(
r: &mut BufferReader<'_>,
mut f: F,
) -> Result<alloc::vec::Vec<T>, DecodeError>
where
F: FnMut(&mut BufferReader<'_>) -> Result<T, DecodeError>,
{
let len = r.read_u32()? as usize;
let cap = safe_capacity(len, 1, r.remaining());
let mut out = alloc::vec::Vec::with_capacity(cap);
for _ in 0..len {
out.push(f(r)?);
}
Ok(out)
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod safe_capacity_tests {
use super::*;
#[test]
fn safe_capacity_clamps_by_remaining_bytes() {
assert_eq!(safe_capacity(1_000_000_000, 4, 100), 25);
}
#[test]
fn safe_capacity_caps_at_prealloc_cap() {
let cap = safe_capacity(usize::MAX, 1, usize::MAX);
assert_eq!(cap, DECODE_PREALLOC_CAP);
}
#[test]
fn safe_capacity_returns_len_when_small() {
assert_eq!(safe_capacity(10, 4, 1000), 10);
}
#[test]
fn safe_capacity_handles_zero_elem_size() {
assert_eq!(safe_capacity(usize::MAX, 0, 100), DECODE_PREALLOC_CAP);
}
#[test]
fn decode_seq_truncates_preallocation_for_large_lengths() {
let mut bytes = alloc::vec::Vec::new();
bytes.extend_from_slice(&u32::MAX.to_le_bytes());
let mut r = BufferReader::new(&bytes, zerodds_cdr::Endianness::Little);
let res: Result<alloc::vec::Vec<u8>, _> = decode_seq(&mut r, |rr| rr.read_u8());
assert!(res.is_err());
}
fn roundtrip_verbatim(placement: VerbatimPlacement) {
let a = AppliedVerbatimAnnotation {
placement: placement.clone(),
language: alloc::string::String::from("c++"),
text: alloc::string::String::from("// example"),
};
let mut w = BufferWriter::new(zerodds_cdr::Endianness::Little);
a.encode_into(&mut w).unwrap();
let bytes = w.into_bytes();
let mut r = BufferReader::new(&bytes, zerodds_cdr::Endianness::Little);
let decoded = AppliedVerbatimAnnotation::decode_from(&mut r).unwrap();
assert_eq!(decoded, a);
}
#[test]
fn verbatim_placement_roundtrip_before() {
roundtrip_verbatim(VerbatimPlacement::Before);
}
#[test]
fn verbatim_placement_roundtrip_after() {
roundtrip_verbatim(VerbatimPlacement::After);
}
#[test]
fn verbatim_placement_roundtrip_begin_file() {
roundtrip_verbatim(VerbatimPlacement::BeginFile);
}
#[test]
fn verbatim_placement_roundtrip_end_file() {
roundtrip_verbatim(VerbatimPlacement::EndFile);
}
#[test]
fn verbatim_placement_roundtrip_other_forward_compat() {
roundtrip_verbatim(VerbatimPlacement::Other(alloc::string::String::from(
"CUSTOM_PLACEMENT",
)));
}
}