Skip to main content

libvirt_xdr/
opaque.rs

1//! Fixed-length opaque serialization helpers.
2//!
3//! XDR fixed-length opaque data (like UUID) doesn't have a length prefix,
4//! just the raw bytes with padding to 4-byte alignment.
5//!
6//! This module provides `FixedOpaque16` type that correctly handles
7//! XDR serialization for 16-byte fixed opaque data (UUID).
8
9use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
10
11/// Wrapper type for 16-byte fixed-length opaque data (UUID).
12///
13/// In XDR, fixed-length opaque data is serialized as raw bytes without
14/// a length prefix, padded to 4-byte alignment.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16pub struct FixedOpaque16(pub [u8; 16]);
17
18impl FixedOpaque16 {
19    /// Create a new FixedOpaque16 from a byte array.
20    pub fn new(data: [u8; 16]) -> Self {
21        Self(data)
22    }
23
24    /// Get the inner byte array.
25    pub fn as_bytes(&self) -> &[u8; 16] {
26        &self.0
27    }
28
29    /// Format as UUID string (lowercase hex with dashes).
30    pub fn to_uuid_string(&self) -> String {
31        format!(
32            "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
33            self.0[0], self.0[1], self.0[2], self.0[3],
34            self.0[4], self.0[5],
35            self.0[6], self.0[7],
36            self.0[8], self.0[9],
37            self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15]
38        )
39    }
40}
41
42impl std::fmt::Display for FixedOpaque16 {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        write!(f, "{}", self.to_uuid_string())
45    }
46}
47
48impl Serialize for FixedOpaque16 {
49    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
50    where
51        S: Serializer,
52    {
53        // Use newtype_struct to signal we want raw bytes without length prefix
54        // The inner type is just the byte slice wrapped in a helper
55        serializer.serialize_newtype_struct("FixedOpaque16", &FixedOpaqueBytes(&self.0))
56    }
57}
58
59/// Helper to serialize raw bytes for fixed opaque.
60struct FixedOpaqueBytes<'a>(&'a [u8]);
61
62impl Serialize for FixedOpaqueBytes<'_> {
63    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
64    where
65        S: Serializer,
66    {
67        serializer.serialize_bytes(self.0)
68    }
69}
70
71impl<'de> Deserialize<'de> for FixedOpaque16 {
72    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73    where
74        D: Deserializer<'de>,
75    {
76        struct FixedOpaque16Visitor;
77
78        impl<'de> de::Visitor<'de> for FixedOpaque16Visitor {
79            type Value = FixedOpaque16;
80
81            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
82                formatter.write_str("16 bytes of opaque data")
83            }
84
85            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
86                if v.len() >= 16 {
87                    let mut arr = [0u8; 16];
88                    arr.copy_from_slice(&v[..16]);
89                    Ok(FixedOpaque16(arr))
90                } else {
91                    Err(E::invalid_length(v.len(), &self))
92                }
93            }
94
95            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
96            where
97                A: de::SeqAccess<'de>,
98            {
99                let mut arr = [0u8; 16];
100                for i in 0..16 {
101                    arr[i] = seq
102                        .next_element()?
103                        .ok_or_else(|| de::Error::invalid_length(i, &self))?;
104                }
105                Ok(FixedOpaque16(arr))
106            }
107        }
108
109        // Use newtype_struct to signal we want raw bytes without length prefix
110        deserializer.deserialize_newtype_struct("FixedOpaque16", FixedOpaque16Visitor)
111    }
112}