extern crate alloc;
use alloc::vec::Vec;
pub use zerodds_cdr::{KEY_HASH_LEN, PlainCdr2BeKeyHolder, compute_key_hash};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Extensibility {
Final = 0,
Appendable = 1,
Mutable = 2,
}
pub type ExtensibilityKind = Extensibility;
pub trait DdsType: Sized {
const TYPE_NAME: &'static str;
const EXTENSIBILITY: Extensibility = Extensibility::Final;
const HAS_KEY: bool = false;
const IS_KEYED: bool = Self::HAS_KEY;
const KEY_HOLDER_MAX_SIZE: Option<usize> = None;
const IS_NESTED: bool = false;
const TYPE_IDENTIFIER: zerodds_types::TypeIdentifier = zerodds_types::TypeIdentifier::None;
fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError>;
fn encode_be(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
self.encode(out)
}
fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError>;
fn encode_key_holder_be(&self, _holder: &mut PlainCdr2BeKeyHolder) {
}
#[must_use]
fn field_value(&self, _path: &str) -> Option<zerodds_sql_filter::Value> {
None
}
#[must_use]
fn compute_key_hash(&self) -> Option<[u8; KEY_HASH_LEN]> {
if !Self::HAS_KEY {
return None;
}
let mut holder = PlainCdr2BeKeyHolder::new();
self.encode_key_holder_be(&mut holder);
let max = Self::KEY_HOLDER_MAX_SIZE.unwrap_or(usize::MAX);
Some(compute_key_hash(holder.as_bytes(), max))
}
#[must_use]
fn key_hash(&self) -> Option<[u8; KEY_HASH_LEN]> {
self.compute_key_hash()
}
}
pub struct DdsTypeRow<'a, T: DdsType> {
pub sample: &'a T,
}
impl<'a, T: DdsType> DdsTypeRow<'a, T> {
#[must_use]
pub fn new(sample: &'a T) -> Self {
Self { sample }
}
}
impl<T: DdsType> zerodds_sql_filter::RowAccess for DdsTypeRow<'_, T> {
fn get(&self, path: &str) -> Option<zerodds_sql_filter::Value> {
self.sample.field_value(path)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum EncodeError {
Invalid {
what: &'static str,
},
}
impl core::fmt::Display for EncodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Invalid { what } => write!(f, "encode error: {what}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for EncodeError {}
impl From<zerodds_cdr::EncodeError> for EncodeError {
fn from(e: zerodds_cdr::EncodeError) -> Self {
let _ = e;
Self::Invalid {
what: "zerodds_cdr encode error",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DecodeError {
Invalid {
what: &'static str,
},
}
impl core::fmt::Display for DecodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Invalid { what } => write!(f, "decode error: {what}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
impl From<zerodds_cdr::DecodeError> for DecodeError {
fn from(e: zerodds_cdr::DecodeError) -> Self {
let _ = e;
Self::Invalid {
what: "zerodds_cdr decode error",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct DdsAny {
pub type_name: alloc::string::String,
pub payload: Vec<u8>,
}
impl DdsAny {
pub fn pack<T: DdsType>(value: &T) -> Result<Self, EncodeError> {
let mut payload = Vec::new();
value.encode(&mut payload)?;
Ok(Self {
type_name: alloc::string::String::from(T::TYPE_NAME),
payload,
})
}
pub fn unpack<T: DdsType>(&self) -> Result<T, DecodeError> {
if self.type_name != T::TYPE_NAME {
return Err(DecodeError::Invalid {
what: "DdsAny: type-name mismatch",
});
}
T::decode(&self.payload)
}
}
impl zerodds_cdr::CdrEncode for DdsAny {
fn encode(
&self,
w: &mut zerodds_cdr::BufferWriter,
) -> core::result::Result<(), zerodds_cdr::EncodeError> {
w.write_string(&self.type_name)?;
let payload_len = u32::try_from(self.payload.len()).map_err(|_| {
zerodds_cdr::EncodeError::ValueOutOfRange {
message: "DdsAny: payload > u32::MAX",
}
})?;
w.write_u32(payload_len)?;
w.write_bytes(&self.payload)?;
Ok(())
}
}
impl zerodds_cdr::CdrDecode for DdsAny {
fn decode(
r: &mut zerodds_cdr::BufferReader<'_>,
) -> core::result::Result<Self, zerodds_cdr::DecodeError> {
let type_name = r.read_string()?;
let payload_len = r.read_u32()? as usize;
let payload = r.read_bytes(payload_len)?.to_vec();
Ok(Self { type_name, payload })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RawBytes {
pub data: Vec<u8>,
}
impl RawBytes {
#[must_use]
pub fn new(data: Vec<u8>) -> Self {
Self { data }
}
}
impl DdsType for RawBytes {
const TYPE_NAME: &'static str = "zerodds::RawBytes";
fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
out.extend_from_slice(&self.data);
Ok(())
}
fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
Ok(Self {
data: bytes.to_vec(),
})
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn raw_bytes_roundtrip() {
let orig = RawBytes::new(vec![1, 2, 3, 4, 5]);
let mut buf = Vec::new();
orig.encode(&mut buf).unwrap();
let back = RawBytes::decode(&buf).unwrap();
assert_eq!(back, orig);
}
#[test]
fn raw_bytes_type_name_is_namespaced() {
assert_eq!(RawBytes::TYPE_NAME, "zerodds::RawBytes");
}
struct SmallKeyed {
id: u32,
}
impl DdsType for SmallKeyed {
const TYPE_NAME: &'static str = "test::SmallKeyed";
const HAS_KEY: bool = true;
const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(4);
fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
out.extend_from_slice(&self.id.to_le_bytes());
Ok(())
}
fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
if bytes.len() < 4 {
return Err(DecodeError::Invalid {
what: "truncated SmallKeyed",
});
}
let id = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
Ok(Self { id })
}
fn encode_key_holder_be(&self, holder: &mut PlainCdr2BeKeyHolder) {
holder.write_u32(self.id);
}
}
#[test]
fn small_keyed_produces_zero_padded_keyhash() {
let s = SmallKeyed { id: 0x1122_3344 };
let key = s.compute_key_hash().expect("keyed");
assert_eq!(&key[0..4], &[0x11, 0x22, 0x33, 0x44]);
assert_eq!(&key[4..16], &[0u8; 12]);
}
#[test]
fn non_keyed_returns_none_for_keyhash() {
let r = RawBytes::new(vec![1, 2, 3]);
assert_eq!(r.compute_key_hash(), None);
}
#[test]
fn keyed_two_instances_have_distinct_hashes() {
let a = SmallKeyed { id: 1 };
let b = SmallKeyed { id: 2 };
assert_ne!(a.compute_key_hash(), b.compute_key_hash());
}
struct LargeKeyed {
topic: alloc::string::String,
}
impl DdsType for LargeKeyed {
const TYPE_NAME: &'static str = "test::LargeKeyed";
const HAS_KEY: bool = true;
const KEY_HOLDER_MAX_SIZE: Option<usize> = None;
fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
out.extend_from_slice(self.topic.as_bytes());
Ok(())
}
fn decode(_bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
Err(DecodeError::Invalid {
what: "test fixture",
})
}
fn encode_key_holder_be(&self, holder: &mut PlainCdr2BeKeyHolder) {
holder.write_string(&self.topic);
}
}
#[test]
fn large_keyed_produces_md5_hashed_keyhash() {
let s = LargeKeyed {
topic: alloc::string::String::from("hello"),
};
let key = s.compute_key_hash().expect("keyed");
assert_ne!(key, [0u8; 16]);
let key2 = s.compute_key_hash().expect("keyed");
assert_eq!(key, key2);
}
#[test]
fn spec_aligned_aliases_match_implementation_names() {
assert_eq!(
<RawBytes as DdsType>::IS_KEYED,
<RawBytes as DdsType>::HAS_KEY
);
fn is_keyed<T: DdsType>() -> bool {
T::IS_KEYED
}
assert!(is_keyed::<SmallKeyed>());
let s = SmallKeyed { id: 0xABCD };
assert_eq!(s.key_hash(), s.compute_key_hash());
}
#[test]
fn extensibility_default_is_final() {
assert_eq!(<RawBytes as DdsType>::EXTENSIBILITY, Extensibility::Final);
let _: ExtensibilityKind = Extensibility::Mutable;
}
#[test]
fn encode_be_default_delegates_to_encode() {
let r = RawBytes::new(vec![1, 2, 3]);
let mut le = Vec::new();
let mut be = Vec::new();
r.encode(&mut le).unwrap();
r.encode_be(&mut be).unwrap();
assert_eq!(le, be);
}
#[test]
fn keyed_member_order_matters() {
struct A {
x: u32,
y: u32,
}
impl DdsType for A {
const TYPE_NAME: &'static str = "test::A";
const HAS_KEY: bool = true;
const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(8);
fn encode(&self, _out: &mut Vec<u8>) -> Result<(), EncodeError> {
Ok(())
}
fn decode(_b: &[u8]) -> Result<Self, DecodeError> {
Err(DecodeError::Invalid { what: "stub" })
}
fn encode_key_holder_be(&self, holder: &mut PlainCdr2BeKeyHolder) {
holder.write_u32(self.x);
holder.write_u32(self.y);
}
}
struct B {
x: u32,
y: u32,
}
impl DdsType for B {
const TYPE_NAME: &'static str = "test::B";
const HAS_KEY: bool = true;
const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(8);
fn encode(&self, _out: &mut Vec<u8>) -> Result<(), EncodeError> {
Ok(())
}
fn decode(_b: &[u8]) -> Result<Self, DecodeError> {
Err(DecodeError::Invalid { what: "stub" })
}
fn encode_key_holder_be(&self, holder: &mut PlainCdr2BeKeyHolder) {
holder.write_u32(self.y);
holder.write_u32(self.x);
}
}
let a = A { x: 1, y: 2 };
let b = B { x: 1, y: 2 };
assert_ne!(a.compute_key_hash(), b.compute_key_hash());
}
}