Skip to main content

facet_xdr/
parser.rs

1//! XDR parser implementing FormatParser.
2//!
3//! XDR (External Data Representation) is defined in RFC 4506.
4//! Key characteristics:
5//! - Big-endian byte order
6//! - All values are padded to 4-byte boundaries
7//! - Fixed-size integers (4 bytes for i32/u32, 8 bytes for i64/u64)
8//! - No support for i128/u128
9//! - Strings and variable-length data are length-prefixed with 4-byte padding
10//!
11//! XDR is NOT a self-describing format - fields are positional.
12//! This parser uses the `hint_*` methods from `FormatParser` to know what types to expect.
13
14extern crate alloc;
15
16use alloc::{borrow::Cow, string::String, vec::Vec};
17
18use crate::error::{XdrError, codes};
19use facet_format::{
20    ContainerKind, EnumVariantHint, FieldEvidence, FormatParser, ParseEvent, ProbeStream,
21    ScalarTypeHint, ScalarValue,
22};
23
24/// Stored variant metadata for enum parsing.
25#[derive(Debug, Clone)]
26struct VariantMeta {
27    name: String,
28    kind: facet_core::StructKind,
29    field_count: usize,
30}
31
32/// Parser state for tracking nested structures.
33#[derive(Debug, Clone)]
34enum ParserState {
35    /// At the top level or after completing a value.
36    Ready,
37    /// Inside a struct, tracking remaining fields.
38    InStruct { remaining_fields: usize },
39    /// Inside a sequence (variable-length array), tracking remaining elements.
40    InSequence { remaining_elements: u32 },
41    /// Inside a fixed-size array, tracking remaining elements.
42    InArray { remaining_elements: usize },
43    /// Inside an enum variant, tracking parsing progress.
44    InEnum {
45        variant_name: String,
46        variant_kind: facet_core::StructKind,
47        variant_field_count: usize,
48        field_key_emitted: bool,
49        wrapper_start_emitted: bool,
50        wrapper_end_emitted: bool,
51    },
52}
53
54/// XDR parser for deserialization.
55///
56/// XDR is a positional binary format - fields don't have names in the wire format.
57/// This parser relies on `hint_*` methods to know what types to expect.
58pub struct XdrParser<'de> {
59    input: &'de [u8],
60    pos: usize,
61    /// Stack of parser states for nested structures.
62    state_stack: Vec<ParserState>,
63    /// Peeked event (for `peek_event`).
64    peeked: Option<ParseEvent<'de>>,
65    /// Pending struct field count from `hint_struct_fields`.
66    pending_struct_fields: Option<usize>,
67    /// Pending scalar type hint from `hint_scalar_type`.
68    pending_scalar_type: Option<ScalarTypeHint>,
69    /// Pending sequence flag from `hint_sequence`.
70    pending_sequence: bool,
71    /// Pending fixed-size array length from `hint_array`.
72    pending_array: Option<usize>,
73    /// Pending option flag from `hint_option`.
74    pending_option: bool,
75    /// Pending enum variant metadata from `hint_enum`.
76    pending_enum: Option<Vec<VariantMeta>>,
77}
78
79impl<'de> XdrParser<'de> {
80    /// Create a new XDR parser from input bytes.
81    pub const fn new(input: &'de [u8]) -> Self {
82        Self {
83            input,
84            pos: 0,
85            state_stack: Vec::new(),
86            peeked: None,
87            pending_struct_fields: None,
88            pending_scalar_type: None,
89            pending_sequence: false,
90            pending_array: None,
91            pending_option: false,
92            pending_enum: None,
93        }
94    }
95
96    /// Read a u32 in big-endian (XDR standard).
97    fn read_u32(&mut self) -> Result<u32, XdrError> {
98        if self.pos + 4 > self.input.len() {
99            return Err(XdrError::from_code(codes::UNEXPECTED_EOF, self.pos));
100        }
101        let bytes = &self.input[self.pos..self.pos + 4];
102        self.pos += 4;
103        Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
104    }
105
106    /// Read a u64 in big-endian.
107    fn read_u64(&mut self) -> Result<u64, XdrError> {
108        if self.pos + 8 > self.input.len() {
109            return Err(XdrError::from_code(codes::UNEXPECTED_EOF, self.pos));
110        }
111        let bytes = &self.input[self.pos..self.pos + 8];
112        self.pos += 8;
113        Ok(u64::from_be_bytes([
114            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
115        ]))
116    }
117
118    /// Read an i32 in big-endian.
119    fn read_i32(&mut self) -> Result<i32, XdrError> {
120        Ok(self.read_u32()? as i32)
121    }
122
123    /// Read an i64 in big-endian.
124    fn read_i64(&mut self) -> Result<i64, XdrError> {
125        Ok(self.read_u64()? as i64)
126    }
127
128    /// Read an f32 in big-endian.
129    fn read_f32(&mut self) -> Result<f32, XdrError> {
130        let bits = self.read_u32()?;
131        Ok(f32::from_bits(bits))
132    }
133
134    /// Read an f64 in big-endian.
135    fn read_f64(&mut self) -> Result<f64, XdrError> {
136        let bits = self.read_u64()?;
137        Ok(f64::from_bits(bits))
138    }
139
140    /// Read variable-length opaque data (with length prefix and padding).
141    fn read_opaque_var(&mut self) -> Result<&'de [u8], XdrError> {
142        let len = self.read_u32()? as usize;
143        if self.pos + len > self.input.len() {
144            return Err(XdrError::from_code(codes::UNEXPECTED_EOF, self.pos));
145        }
146        let data = &self.input[self.pos..self.pos + len];
147        self.pos += len;
148        // Skip padding to align to 4 bytes
149        let pad = (4 - (len % 4)) % 4;
150        if self.pos + pad > self.input.len() {
151            return Err(XdrError::from_code(codes::UNEXPECTED_EOF, self.pos));
152        }
153        self.pos += pad;
154        Ok(data)
155    }
156
157    /// Read a string (variable-length opaque interpreted as UTF-8).
158    fn read_string(&mut self) -> Result<Cow<'de, str>, XdrError> {
159        let bytes = self.read_opaque_var()?;
160        core::str::from_utf8(bytes)
161            .map(Cow::Borrowed)
162            .map_err(|_| XdrError::from_code(codes::INVALID_UTF8, self.pos))
163    }
164
165    /// Read a boolean (XDR bool is 4 bytes: 0=false, 1=true).
166    fn read_bool(&mut self) -> Result<bool, XdrError> {
167        let val = self.read_u32()?;
168        match val {
169            0 => Ok(false),
170            1 => Ok(true),
171            _ => Err(XdrError::from_code(codes::INVALID_BOOL, self.pos - 4)),
172        }
173    }
174
175    /// Get the current parser state (top of stack or Ready).
176    fn current_state(&self) -> &ParserState {
177        self.state_stack.last().unwrap_or(&ParserState::Ready)
178    }
179
180    /// Generate the next event based on current state and hints.
181    fn generate_next_event(&mut self) -> Result<ParseEvent<'de>, XdrError> {
182        // Check if we have a pending option hint
183        if self.pending_option {
184            self.pending_option = false;
185            let discriminant = self.read_u32()?;
186            match discriminant {
187                0 => return Ok(ParseEvent::Scalar(ScalarValue::Null)),
188                1 => {
189                    // Some(value) - return placeholder, deserializer will call hint for inner
190                    return Ok(ParseEvent::OrderedField);
191                }
192                _ => {
193                    return Err(XdrError::from_code(codes::INVALID_OPTIONAL, self.pos - 4));
194                }
195            }
196        }
197
198        // Check if we have a pending enum hint
199        if let Some(variants) = self.pending_enum.take() {
200            let discriminant = self.read_u32()? as usize;
201
202            if discriminant >= variants.len() {
203                return Err(XdrError::from_code(codes::INVALID_VARIANT, self.pos - 4));
204            }
205            let variant = &variants[discriminant];
206
207            self.state_stack.push(ParserState::InEnum {
208                variant_name: variant.name.clone(),
209                variant_kind: variant.kind,
210                variant_field_count: variant.field_count,
211                field_key_emitted: false,
212                wrapper_start_emitted: false,
213                wrapper_end_emitted: false,
214            });
215            return Ok(ParseEvent::StructStart(ContainerKind::Object));
216        }
217
218        // Check if we have a pending scalar type hint
219        if let Some(hint) = self.pending_scalar_type.take() {
220            return self.parse_scalar_with_hint(hint);
221        }
222
223        // Check if we have a pending sequence hint (variable-length)
224        if self.pending_sequence {
225            self.pending_sequence = false;
226            let count = self.read_u32()?;
227            self.state_stack.push(ParserState::InSequence {
228                remaining_elements: count,
229            });
230            return Ok(ParseEvent::SequenceStart(ContainerKind::Array));
231        }
232
233        // Check if we have a pending fixed-size array hint
234        if let Some(len) = self.pending_array.take() {
235            self.state_stack.push(ParserState::InArray {
236                remaining_elements: len,
237            });
238            return Ok(ParseEvent::SequenceStart(ContainerKind::Array));
239        }
240
241        // Check if we have a pending struct hint
242        if let Some(num_fields) = self.pending_struct_fields.take() {
243            self.state_stack.push(ParserState::InStruct {
244                remaining_fields: num_fields,
245            });
246            return Ok(ParseEvent::StructStart(ContainerKind::Object));
247        }
248
249        // Check current state
250        match self.current_state().clone() {
251            ParserState::Ready => {
252                // At top level without a hint - error
253                Err(XdrError::new(
254                    codes::UNSUPPORTED_TYPE,
255                    self.pos,
256                    "XDR parser needs type hints (use hint_scalar_type, hint_struct_fields, or hint_sequence)",
257                ))
258            }
259            ParserState::InStruct { remaining_fields } => {
260                if remaining_fields == 0 {
261                    self.state_stack.pop();
262                    Ok(ParseEvent::StructEnd)
263                } else {
264                    if let Some(ParserState::InStruct { remaining_fields }) =
265                        self.state_stack.last_mut()
266                    {
267                        *remaining_fields -= 1;
268                    }
269                    Ok(ParseEvent::OrderedField)
270                }
271            }
272            ParserState::InSequence { remaining_elements } => {
273                if remaining_elements == 0 {
274                    self.state_stack.pop();
275                    Ok(ParseEvent::SequenceEnd)
276                } else {
277                    if let Some(ParserState::InSequence { remaining_elements }) =
278                        self.state_stack.last_mut()
279                    {
280                        *remaining_elements -= 1;
281                    }
282                    Ok(ParseEvent::OrderedField)
283                }
284            }
285            ParserState::InArray { remaining_elements } => {
286                if remaining_elements == 0 {
287                    self.state_stack.pop();
288                    Ok(ParseEvent::SequenceEnd)
289                } else {
290                    if let Some(ParserState::InArray { remaining_elements }) =
291                        self.state_stack.last_mut()
292                    {
293                        *remaining_elements -= 1;
294                    }
295                    Ok(ParseEvent::OrderedField)
296                }
297            }
298            ParserState::InEnum {
299                variant_name,
300                variant_kind,
301                variant_field_count,
302                field_key_emitted,
303                wrapper_start_emitted,
304                wrapper_end_emitted,
305            } => {
306                use facet_core::StructKind;
307
308                if !field_key_emitted {
309                    if let Some(ParserState::InEnum {
310                        field_key_emitted, ..
311                    }) = self.state_stack.last_mut()
312                    {
313                        *field_key_emitted = true;
314                    }
315                    Ok(ParseEvent::FieldKey(facet_format::FieldKey::new(
316                        Cow::Owned(variant_name),
317                        facet_format::FieldLocationHint::KeyValue,
318                    )))
319                } else if !wrapper_start_emitted {
320                    match variant_kind {
321                        StructKind::Unit => {
322                            self.state_stack.pop();
323                            Ok(ParseEvent::StructEnd)
324                        }
325                        StructKind::Tuple | StructKind::TupleStruct => {
326                            if variant_field_count == 1 {
327                                // Newtype variant
328                                if let Some(ParserState::InEnum {
329                                    wrapper_start_emitted,
330                                    wrapper_end_emitted,
331                                    ..
332                                }) = self.state_stack.last_mut()
333                                {
334                                    *wrapper_start_emitted = true;
335                                    *wrapper_end_emitted = true;
336                                }
337                                self.generate_next_event()
338                            } else {
339                                if let Some(ParserState::InEnum {
340                                    wrapper_start_emitted,
341                                    ..
342                                }) = self.state_stack.last_mut()
343                                {
344                                    *wrapper_start_emitted = true;
345                                }
346                                Ok(ParseEvent::SequenceStart(ContainerKind::Array))
347                            }
348                        }
349                        StructKind::Struct => {
350                            if let Some(ParserState::InEnum {
351                                wrapper_start_emitted,
352                                ..
353                            }) = self.state_stack.last_mut()
354                            {
355                                *wrapper_start_emitted = true;
356                            }
357                            self.state_stack.push(ParserState::InStruct {
358                                remaining_fields: variant_field_count,
359                            });
360                            Ok(ParseEvent::StructStart(ContainerKind::Object))
361                        }
362                    }
363                } else if !wrapper_end_emitted {
364                    match variant_kind {
365                        StructKind::Unit => unreachable!(),
366                        StructKind::Tuple | StructKind::TupleStruct => {
367                            if variant_field_count > 1 {
368                                if let Some(ParserState::InEnum {
369                                    wrapper_end_emitted,
370                                    ..
371                                }) = self.state_stack.last_mut()
372                                {
373                                    *wrapper_end_emitted = true;
374                                }
375                                Ok(ParseEvent::SequenceEnd)
376                            } else {
377                                self.state_stack.pop();
378                                Ok(ParseEvent::StructEnd)
379                            }
380                        }
381                        StructKind::Struct => {
382                            self.state_stack.pop();
383                            Ok(ParseEvent::StructEnd)
384                        }
385                    }
386                } else {
387                    self.state_stack.pop();
388                    Ok(ParseEvent::StructEnd)
389                }
390            }
391        }
392    }
393
394    /// Parse a scalar value with the given type hint.
395    fn parse_scalar_with_hint(
396        &mut self,
397        hint: ScalarTypeHint,
398    ) -> Result<ParseEvent<'de>, XdrError> {
399        let scalar = match hint {
400            ScalarTypeHint::Bool => {
401                let val = self.read_bool()?;
402                ScalarValue::Bool(val)
403            }
404            // XDR encodes smaller integers as 4 bytes
405            ScalarTypeHint::U8 => {
406                let val = self.read_u32()? as u8;
407                ScalarValue::U64(val as u64)
408            }
409            ScalarTypeHint::U16 => {
410                let val = self.read_u32()? as u16;
411                ScalarValue::U64(val as u64)
412            }
413            ScalarTypeHint::U32 => {
414                let val = self.read_u32()?;
415                ScalarValue::U64(val as u64)
416            }
417            ScalarTypeHint::U64 => {
418                let val = self.read_u64()?;
419                ScalarValue::U64(val)
420            }
421            ScalarTypeHint::U128 => {
422                // XDR doesn't support u128
423                return Err(XdrError::from_code(codes::UNSUPPORTED_TYPE, self.pos));
424            }
425            ScalarTypeHint::Usize => {
426                // Encode usize as u64
427                let val = self.read_u64()?;
428                ScalarValue::U64(val)
429            }
430            ScalarTypeHint::I8 => {
431                let val = self.read_i32()? as i8;
432                ScalarValue::I64(val as i64)
433            }
434            ScalarTypeHint::I16 => {
435                let val = self.read_i32()? as i16;
436                ScalarValue::I64(val as i64)
437            }
438            ScalarTypeHint::I32 => {
439                let val = self.read_i32()?;
440                ScalarValue::I64(val as i64)
441            }
442            ScalarTypeHint::I64 => {
443                let val = self.read_i64()?;
444                ScalarValue::I64(val)
445            }
446            ScalarTypeHint::I128 => {
447                // XDR doesn't support i128
448                return Err(XdrError::from_code(codes::UNSUPPORTED_TYPE, self.pos));
449            }
450            ScalarTypeHint::Isize => {
451                // Encode isize as i64
452                let val = self.read_i64()?;
453                ScalarValue::I64(val)
454            }
455            ScalarTypeHint::F32 => {
456                let val = self.read_f32()?;
457                ScalarValue::F64(val as f64)
458            }
459            ScalarTypeHint::F64 => {
460                let val = self.read_f64()?;
461                ScalarValue::F64(val)
462            }
463            ScalarTypeHint::String => {
464                let val = self.read_string()?;
465                ScalarValue::Str(val)
466            }
467            ScalarTypeHint::Bytes => {
468                let val = self.read_opaque_var()?;
469                ScalarValue::Bytes(Cow::Borrowed(val))
470            }
471            ScalarTypeHint::Char => {
472                // XDR encodes char as u32
473                let val = self.read_u32()?;
474                let c = char::from_u32(val).ok_or_else(|| {
475                    XdrError::new(codes::INVALID_UTF8, self.pos - 4, "invalid char codepoint")
476                })?;
477                ScalarValue::Str(Cow::Owned(c.to_string()))
478            }
479        };
480        Ok(ParseEvent::Scalar(scalar))
481    }
482}
483
484impl<'de> FormatParser<'de> for XdrParser<'de> {
485    type Error = XdrError;
486    type Probe<'a>
487        = XdrProbe
488    where
489        Self: 'a;
490
491    fn next_event(&mut self) -> Result<Option<ParseEvent<'de>>, Self::Error> {
492        if let Some(event) = self.peeked.take() {
493            return Ok(Some(event));
494        }
495        Ok(Some(self.generate_next_event()?))
496    }
497
498    fn peek_event(&mut self) -> Result<Option<ParseEvent<'de>>, Self::Error> {
499        if self.peeked.is_none() {
500            self.peeked = Some(self.generate_next_event()?);
501        }
502        Ok(self.peeked.clone())
503    }
504
505    fn skip_value(&mut self) -> Result<(), Self::Error> {
506        // XDR is not self-describing, so we can't skip arbitrary values
507        Err(XdrError::new(
508            codes::UNSUPPORTED_TYPE,
509            self.pos,
510            "skip_value not supported for XDR (non-self-describing format)",
511        ))
512    }
513
514    fn begin_probe(&mut self) -> Result<Self::Probe<'_>, Self::Error> {
515        // XDR doesn't support probing (positional format)
516        Ok(XdrProbe)
517    }
518
519    fn is_self_describing(&self) -> bool {
520        false
521    }
522
523    fn hint_struct_fields(&mut self, num_fields: usize) {
524        self.pending_struct_fields = Some(num_fields);
525        if matches!(self.peeked, Some(ParseEvent::OrderedField)) {
526            self.peeked = None;
527        }
528    }
529
530    fn hint_scalar_type(&mut self, hint: ScalarTypeHint) {
531        self.pending_scalar_type = Some(hint);
532        if matches!(self.peeked, Some(ParseEvent::OrderedField)) {
533            self.peeked = None;
534        }
535    }
536
537    fn hint_sequence(&mut self) {
538        self.pending_sequence = true;
539        if matches!(self.peeked, Some(ParseEvent::OrderedField)) {
540            self.peeked = None;
541        }
542    }
543
544    fn hint_array(&mut self, len: usize) {
545        self.pending_array = Some(len);
546        if matches!(self.peeked, Some(ParseEvent::OrderedField)) {
547            self.peeked = None;
548        }
549    }
550
551    fn hint_option(&mut self) {
552        self.pending_option = true;
553        if matches!(self.peeked, Some(ParseEvent::OrderedField)) {
554            self.peeked = None;
555        }
556    }
557
558    fn hint_enum(&mut self, variants: &[EnumVariantHint]) {
559        let metas: Vec<VariantMeta> = variants
560            .iter()
561            .map(|v| VariantMeta {
562                name: v.name.to_string(),
563                kind: v.kind,
564                field_count: v.field_count,
565            })
566            .collect();
567        self.pending_enum = Some(metas);
568        if matches!(self.peeked, Some(ParseEvent::OrderedField)) {
569            self.peeked = None;
570        }
571    }
572}
573
574/// Stub probe stream for XdrParser.
575///
576/// XDR doesn't support probing since it's a positional format without field names.
577pub struct XdrProbe;
578
579impl<'de> ProbeStream<'de> for XdrProbe {
580    type Error = XdrError;
581
582    fn next(&mut self) -> Result<Option<FieldEvidence<'de>>, Self::Error> {
583        // XDR doesn't support probing
584        Ok(None)
585    }
586}