1pub struct XdrEncoder {
14 buf: Vec<u8>,
15}
16
17impl XdrEncoder {
18 pub fn new(capacity: usize) -> Self {
20 Self { buf: Vec::with_capacity(capacity) }
21 }
22
23 pub fn encode_u32(&mut self, v: u32) {
25 self.buf.extend_from_slice(&v.to_be_bytes());
26 }
27
28 pub fn encode_i32(&mut self, v: i32) {
30 self.buf.extend_from_slice(&v.to_be_bytes());
31 }
32
33 pub fn encode_u64(&mut self, v: u64) {
35 self.buf.extend_from_slice(&v.to_be_bytes());
36 }
37
38 pub fn encode_i64(&mut self, v: i64) {
40 self.buf.extend_from_slice(&v.to_be_bytes());
41 }
42
43 pub fn encode_bool(&mut self, v: bool) {
45 self.encode_u32(if v { 1 } else { 0 });
46 }
47
48 pub fn encode_opaque(&mut self, data: &[u8]) {
50 self.encode_u32(data.len() as u32);
51 self.buf.extend_from_slice(data);
52 let pad = (4 - (data.len() % 4)) % 4;
54 for _ in 0..pad {
55 self.buf.push(0);
56 }
57 }
58
59 pub fn encode_opaque_fixed(&mut self, data: &[u8]) {
61 self.buf.extend_from_slice(data);
62 let pad = (4 - (data.len() % 4)) % 4;
63 for _ in 0..pad {
64 self.buf.push(0);
65 }
66 }
67
68 pub fn encode_string(&mut self, s: &str) {
70 self.encode_opaque(s.as_bytes());
71 }
72
73 pub fn encode_raw(&mut self, data: &[u8]) {
75 self.buf.extend_from_slice(data);
76 }
77
78 pub fn into_bytes(self) -> Vec<u8> {
80 self.buf
81 }
82
83 pub fn len(&self) -> usize {
85 self.buf.len()
86 }
87
88 pub fn is_empty(&self) -> bool {
90 self.buf.is_empty()
91 }
92
93 pub fn as_bytes(&self) -> &[u8] {
95 &self.buf
96 }
97}
98
99pub struct XdrDecoder<'a> {
101 data: &'a [u8],
102 pos: usize,
103}
104
105impl<'a> XdrDecoder<'a> {
106 pub fn new(data: &'a [u8]) -> Self {
108 Self { data, pos: 0 }
109 }
110
111 pub fn decode_u32(&mut self) -> Result<u32, XdrError> {
113 if self.pos + 4 > self.data.len() {
114 return Err(XdrError::Truncated);
115 }
116 let v = u32::from_be_bytes([
117 self.data[self.pos], self.data[self.pos + 1],
118 self.data[self.pos + 2], self.data[self.pos + 3],
119 ]);
120 self.pos += 4;
121 Ok(v)
122 }
123
124 pub fn decode_i32(&mut self) -> Result<i32, XdrError> {
126 Ok(self.decode_u32()? as i32)
127 }
128
129 pub fn decode_u64(&mut self) -> Result<u64, XdrError> {
131 if self.pos + 8 > self.data.len() {
132 return Err(XdrError::Truncated);
133 }
134 let v = u64::from_be_bytes([
135 self.data[self.pos], self.data[self.pos + 1],
136 self.data[self.pos + 2], self.data[self.pos + 3],
137 self.data[self.pos + 4], self.data[self.pos + 5],
138 self.data[self.pos + 6], self.data[self.pos + 7],
139 ]);
140 self.pos += 8;
141 Ok(v)
142 }
143
144 pub fn decode_opaque(&mut self) -> Result<&'a [u8], XdrError> {
146 let len = self.decode_u32()? as usize;
147 if self.pos + len > self.data.len() {
148 return Err(XdrError::Truncated);
149 }
150 let data = &self.data[self.pos..self.pos + len];
151 let padded = len + ((4 - (len % 4)) % 4);
152 self.pos += padded;
153 Ok(data)
154 }
155
156 pub fn decode_opaque_fixed(&mut self, len: usize) -> Result<&'a [u8], XdrError> {
158 if self.pos + len > self.data.len() {
159 return Err(XdrError::Truncated);
160 }
161 let data = &self.data[self.pos..self.pos + len];
162 let padded = len + ((4 - (len % 4)) % 4);
163 self.pos += padded;
164 Ok(data)
165 }
166
167 pub fn skip(&mut self, n: usize) -> Result<(), XdrError> {
169 let padded = n + ((4 - (n % 4)) % 4);
170 if self.pos + padded > self.data.len() {
171 return Err(XdrError::Truncated);
172 }
173 self.pos += padded;
174 Ok(())
175 }
176
177 pub fn skip_raw(&mut self, n: usize) -> Result<(), XdrError> {
179 if self.pos + n > self.data.len() {
180 return Err(XdrError::Truncated);
181 }
182 self.pos += n;
183 Ok(())
184 }
185
186 pub fn remaining(&self) -> usize {
188 self.data.len().saturating_sub(self.pos)
189 }
190
191 pub fn position(&self) -> usize {
193 self.pos
194 }
195}
196
197#[derive(Debug, Clone)]
199pub enum XdrError {
200 Truncated,
202 InvalidLength,
204}
205
206impl std::fmt::Display for XdrError {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 match self {
209 Self::Truncated => write!(f, "XDR: unexpected end of data"),
210 Self::InvalidLength => write!(f, "XDR: invalid opaque length"),
211 }
212 }
213}
214
215impl std::error::Error for XdrError {}
216
217#[cfg(test)]
218mod tests {
219 #![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
220 use super::*;
221
222 #[test]
223 fn test_u32_roundtrip() {
224 let mut enc = XdrEncoder::new(64);
225 enc.encode_u32(0);
226 enc.encode_u32(1);
227 enc.encode_u32(0xDEADBEEF);
228 enc.encode_u32(u32::MAX);
229
230 let bytes = enc.into_bytes();
231 let mut dec = XdrDecoder::new(&bytes);
232 assert_eq!(dec.decode_u32().unwrap(), 0);
233 assert_eq!(dec.decode_u32().unwrap(), 1);
234 assert_eq!(dec.decode_u32().unwrap(), 0xDEADBEEF);
235 assert_eq!(dec.decode_u32().unwrap(), u32::MAX);
236 assert_eq!(dec.remaining(), 0);
237 }
238
239 #[test]
240 fn test_u64_roundtrip() {
241 let mut enc = XdrEncoder::new(64);
242 enc.encode_u64(0x0102030405060708);
243 let bytes = enc.into_bytes();
244 let mut dec = XdrDecoder::new(&bytes);
245 assert_eq!(dec.decode_u64().unwrap(), 0x0102030405060708);
246 }
247
248 #[test]
249 fn test_opaque_roundtrip() {
250 let mut enc = XdrEncoder::new(64);
251 enc.encode_opaque(b"hello"); enc.encode_opaque(b"test"); enc.encode_opaque(b"x"); enc.encode_opaque(b""); let bytes = enc.into_bytes();
257 assert_eq!(bytes.len(), 4+8 + 4+4 + 4+4 + 4); let mut dec = XdrDecoder::new(&bytes);
260 assert_eq!(dec.decode_opaque().unwrap(), b"hello");
261 assert_eq!(dec.decode_opaque().unwrap(), b"test");
262 assert_eq!(dec.decode_opaque().unwrap(), b"x");
263 assert_eq!(dec.decode_opaque().unwrap(), b"");
264 assert_eq!(dec.remaining(), 0);
265 }
266
267 #[test]
268 fn test_string_roundtrip() {
269 let mut enc = XdrEncoder::new(64);
270 enc.encode_string("foxing");
271 let bytes = enc.into_bytes();
272 let mut dec = XdrDecoder::new(&bytes);
273 let s = dec.decode_opaque().unwrap();
274 assert_eq!(std::str::from_utf8(s).unwrap(), "foxing");
275 }
276
277 #[test]
278 fn test_bool_encode() {
279 let mut enc = XdrEncoder::new(16);
280 enc.encode_bool(true);
281 enc.encode_bool(false);
282 let bytes = enc.into_bytes();
283 assert_eq!(&bytes, &[0, 0, 0, 1, 0, 0, 0, 0]);
284 }
285
286 #[test]
287 fn test_decoder_truncated() {
288 let data = [0u8; 3]; let mut dec = XdrDecoder::new(&data);
290 assert!(matches!(dec.decode_u32(), Err(XdrError::Truncated)));
291 }
292
293 #[test]
294 fn test_padding_alignment() {
295 let mut enc = XdrEncoder::new(16);
297 enc.encode_opaque(&[0xFF]);
298 let bytes = enc.into_bytes();
299 assert_eq!(bytes.len(), 8);
300 assert_eq!(&bytes, &[0, 0, 0, 1, 0xFF, 0, 0, 0]);
301 }
302}