1use alloc::boxed::Box;
25use alloc::string::String;
26use alloc::vec::Vec;
27
28use crate::buffer::{BufferReader, BufferWriter};
29use crate::endianness::Endianness;
30use crate::error::{DecodeError, EncodeError};
31
32#[allow(missing_docs)]
35pub mod tckind {
36 pub const TK_NULL: u32 = 0;
37 pub const TK_VOID: u32 = 1;
38 pub const TK_SHORT: u32 = 2;
39 pub const TK_LONG: u32 = 3;
40 pub const TK_USHORT: u32 = 4;
41 pub const TK_ULONG: u32 = 5;
42 pub const TK_FLOAT: u32 = 6;
43 pub const TK_DOUBLE: u32 = 7;
44 pub const TK_BOOLEAN: u32 = 8;
45 pub const TK_CHAR: u32 = 9;
46 pub const TK_OCTET: u32 = 10;
47 pub const TK_ANY: u32 = 11;
48 pub const TK_TYPECODE: u32 = 12;
49 pub const TK_OBJREF: u32 = 14;
50 pub const TK_STRUCT: u32 = 15;
51 pub const TK_ENUM: u32 = 17;
52 pub const TK_STRING: u32 = 18;
53 pub const TK_SEQUENCE: u32 = 19;
54 pub const TK_ALIAS: u32 = 21;
55 pub const TK_EXCEPT: u32 = 22;
56 pub const TK_LONGLONG: u32 = 23;
57 pub const TK_ULONGLONG: u32 = 24;
58 pub const TK_WCHAR: u32 = 26;
59 pub const TK_WSTRING: u32 = 27;
60 pub const INDIRECTION: u32 = 0xffff_ffff;
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
66pub enum TypeCode {
67 Null,
69 Void,
71 Short,
73 Long,
75 UShort,
77 ULong,
79 LongLong,
81 ULongLong,
83 Float,
85 Double,
87 Boolean,
89 Char,
91 Octet,
93 WChar,
95 Any,
97 TypeCodeTc,
99 String(u32),
101 WString(u32),
103 Sequence {
105 element: Box<TypeCode>,
107 bound: u32,
109 },
110 Struct {
112 repo_id: String,
114 name: String,
116 members: Vec<(String, TypeCode)>,
118 is_except: bool,
120 },
121 Enum {
123 repo_id: String,
125 name: String,
127 members: Vec<String>,
129 },
130 Alias {
132 repo_id: String,
134 name: String,
136 content: Box<TypeCode>,
138 },
139 ObjRef {
141 repo_id: String,
143 name: String,
145 },
146 Recursive {
151 repo_id: String,
153 },
154}
155
156impl TypeCode {
157 #[must_use]
159 pub const fn tckind(&self) -> u32 {
160 use tckind::*;
161 match self {
162 Self::Null => TK_NULL,
163 Self::Void => TK_VOID,
164 Self::Short => TK_SHORT,
165 Self::Long => TK_LONG,
166 Self::UShort => TK_USHORT,
167 Self::ULong => TK_ULONG,
168 Self::LongLong => TK_LONGLONG,
169 Self::ULongLong => TK_ULONGLONG,
170 Self::Float => TK_FLOAT,
171 Self::Double => TK_DOUBLE,
172 Self::Boolean => TK_BOOLEAN,
173 Self::Char => TK_CHAR,
174 Self::Octet => TK_OCTET,
175 Self::WChar => TK_WCHAR,
176 Self::Any => TK_ANY,
177 Self::TypeCodeTc => TK_TYPECODE,
178 Self::String(_) => TK_STRING,
179 Self::WString(_) => TK_WSTRING,
180 Self::Sequence { .. } => TK_SEQUENCE,
181 Self::Struct {
182 is_except: true, ..
183 } => TK_EXCEPT,
184 Self::Struct { .. } => TK_STRUCT,
185 Self::Enum { .. } => TK_ENUM,
186 Self::Alias { .. } => TK_ALIAS,
187 Self::ObjRef { .. } => TK_OBJREF,
188 Self::Recursive { .. } => INDIRECTION,
190 }
191 }
192
193 pub fn encode(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
198 w.write_u32(self.tckind())?;
199 match self {
200 Self::Null
202 | Self::Void
203 | Self::Short
204 | Self::Long
205 | Self::UShort
206 | Self::ULong
207 | Self::LongLong
208 | Self::ULongLong
209 | Self::Float
210 | Self::Double
211 | Self::Boolean
212 | Self::Char
213 | Self::Octet
214 | Self::WChar
215 | Self::Any
216 | Self::TypeCodeTc => Ok(()),
217 Self::String(b) | Self::WString(b) => w.write_u32(*b),
219 Self::Sequence { element, bound } => {
221 let encap = build_encap(w.endianness(), |e| {
222 element.encode(e)?;
223 e.write_u32(*bound)
224 })?;
225 write_encap(w, &encap)
226 }
227 Self::Struct {
228 repo_id,
229 name,
230 members,
231 ..
232 } => {
233 let encap = build_encap(w.endianness(), |e| {
234 e.write_string(repo_id)?;
235 e.write_string(name)?;
236 e.write_u32(u32::try_from(members.len()).map_err(|_| {
237 EncodeError::ValueOutOfRange {
238 message: "TypeCode struct member count exceeds u32",
239 }
240 })?)?;
241 for (mn, mt) in members {
242 e.write_string(mn)?;
243 mt.encode(e)?;
244 }
245 Ok(())
246 })?;
247 write_encap(w, &encap)
248 }
249 Self::Enum {
250 repo_id,
251 name,
252 members,
253 } => {
254 let encap = build_encap(w.endianness(), |e| {
255 e.write_string(repo_id)?;
256 e.write_string(name)?;
257 e.write_u32(u32::try_from(members.len()).map_err(|_| {
258 EncodeError::ValueOutOfRange {
259 message: "TypeCode enum member count exceeds u32",
260 }
261 })?)?;
262 for mn in members {
263 e.write_string(mn)?;
264 }
265 Ok(())
266 })?;
267 write_encap(w, &encap)
268 }
269 Self::Alias {
270 repo_id,
271 name,
272 content,
273 } => {
274 let encap = build_encap(w.endianness(), |e| {
275 e.write_string(repo_id)?;
276 e.write_string(name)?;
277 content.encode(e)
278 })?;
279 write_encap(w, &encap)
280 }
281 Self::ObjRef { repo_id, name } => {
282 let encap = build_encap(w.endianness(), |e| {
283 e.write_string(repo_id)?;
284 e.write_string(name)
285 })?;
286 write_encap(w, &encap)
287 }
288 Self::Recursive { .. } => Err(EncodeError::ValueOutOfRange {
291 message: "TypeCode::Recursive encode (indirection emit) not yet supported",
292 }),
293 }
294 }
295
296 pub fn decode(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
305 let mut cache = TcCache::new();
306 Self::decode_ctx(r, 0, &mut cache)
308 }
309
310 fn decode_ctx(
315 r: &mut BufferReader<'_>,
316 base: usize,
317 cache: &mut TcCache,
318 ) -> Result<Self, DecodeError> {
319 use tckind::*;
320 r.align(4)?;
325 let tckind_pos = base + r.position();
326 let kind = r.read_u32()?;
327 match kind {
328 TK_NULL => Ok(Self::Null),
329 TK_VOID => Ok(Self::Void),
330 TK_SHORT => Ok(Self::Short),
331 TK_LONG => Ok(Self::Long),
332 TK_USHORT => Ok(Self::UShort),
333 TK_ULONG => Ok(Self::ULong),
334 TK_LONGLONG => Ok(Self::LongLong),
335 TK_ULONGLONG => Ok(Self::ULongLong),
336 TK_FLOAT => Ok(Self::Float),
337 TK_DOUBLE => Ok(Self::Double),
338 TK_BOOLEAN => Ok(Self::Boolean),
339 TK_CHAR => Ok(Self::Char),
340 TK_OCTET => Ok(Self::Octet),
341 TK_WCHAR => Ok(Self::WChar),
342 TK_ANY => Ok(Self::Any),
343 TK_TYPECODE => Ok(Self::TypeCodeTc),
344 TK_STRING => Ok(Self::String(r.read_u32()?)),
345 TK_WSTRING => Ok(Self::WString(r.read_u32()?)),
346 INDIRECTION => {
347 let off_field_pos = base + r.position();
350 let offset = r.read_u32()? as i32;
351 if offset >= 0 {
352 return Err(DecodeError::InvalidEnum {
353 kind: "TypeCode indirection: offset must be negative",
354 value: INDIRECTION,
355 });
356 }
357 let target =
358 usize::try_from(off_field_pos as i64 + i64::from(offset)).map_err(|_| {
359 DecodeError::InvalidEnum {
360 kind: "TypeCode indirection: target before stream start",
361 value: INDIRECTION,
362 }
363 })?;
364 match cache.get(&target) {
365 Some(TcCacheEntry::Done(tc)) => Ok(tc.clone()),
366 Some(TcCacheEntry::InProgress(repo_id)) => Ok(Self::Recursive {
367 repo_id: repo_id.clone(),
368 }),
369 None => Err(DecodeError::InvalidEnum {
370 kind: "TypeCode indirection: unresolved target",
371 value: INDIRECTION,
372 }),
373 }
374 }
375 TK_SEQUENCE => decode_encap_ctx(r, base, cache, |e, cb, cache| {
376 let element = Box::new(Self::decode_ctx(e, cb, cache)?);
377 let bound = e.read_u32()?;
378 let tc = Self::Sequence { element, bound };
379 cache.insert(tckind_pos, TcCacheEntry::Done(tc.clone()));
380 Ok(tc)
381 }),
382 TK_STRUCT | TK_EXCEPT => decode_encap_ctx(r, base, cache, |e, cb, cache| {
383 let repo_id = e.read_string()?;
384 let name = e.read_string()?;
385 cache.insert(tckind_pos, TcCacheEntry::InProgress(repo_id.clone()));
388 let count = e.read_u32()? as usize;
389 let mut members = Vec::with_capacity(count.min(256));
390 for _ in 0..count {
391 let mn = e.read_string()?;
392 let mt = Self::decode_ctx(e, cb, cache)?;
393 members.push((mn, mt));
394 }
395 let tc = Self::Struct {
396 repo_id,
397 name,
398 members,
399 is_except: kind == TK_EXCEPT,
400 };
401 cache.insert(tckind_pos, TcCacheEntry::Done(tc.clone()));
402 Ok(tc)
403 }),
404 TK_ENUM => decode_encap_ctx(r, base, cache, |e, _cb, cache| {
405 let repo_id = e.read_string()?;
406 let name = e.read_string()?;
407 let count = e.read_u32()? as usize;
408 let mut members = Vec::with_capacity(count.min(1024));
409 for _ in 0..count {
410 members.push(e.read_string()?);
411 }
412 let tc = Self::Enum {
413 repo_id,
414 name,
415 members,
416 };
417 cache.insert(tckind_pos, TcCacheEntry::Done(tc.clone()));
418 Ok(tc)
419 }),
420 TK_ALIAS => decode_encap_ctx(r, base, cache, |e, cb, cache| {
421 let repo_id = e.read_string()?;
422 let name = e.read_string()?;
423 cache.insert(tckind_pos, TcCacheEntry::InProgress(repo_id.clone()));
424 let content = Box::new(Self::decode_ctx(e, cb, cache)?);
425 let tc = Self::Alias {
426 repo_id,
427 name,
428 content,
429 };
430 cache.insert(tckind_pos, TcCacheEntry::Done(tc.clone()));
431 Ok(tc)
432 }),
433 TK_OBJREF => decode_encap_ctx(r, base, cache, |e, _cb, cache| {
434 let repo_id = e.read_string()?;
435 let name = e.read_string()?;
436 let tc = Self::ObjRef { repo_id, name };
437 cache.insert(tckind_pos, TcCacheEntry::Done(tc.clone()));
438 Ok(tc)
439 }),
440 other => Err(DecodeError::InvalidEnum {
441 kind: "TypeCode TCKind (unsupported)",
442 value: other,
443 }),
444 }
445 }
446}
447
448enum TcCacheEntry {
450 InProgress(String),
453 Done(TypeCode),
455}
456
457type TcCache = alloc::collections::BTreeMap<usize, TcCacheEntry>;
459
460fn build_encap<F>(endianness: Endianness, body: F) -> Result<Vec<u8>, EncodeError>
463where
464 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
465{
466 let mut e = BufferWriter::new(endianness);
467 e.write_u8(match endianness {
468 Endianness::Big => 0,
469 Endianness::Little => 1,
470 })?;
471 body(&mut e)?;
472 Ok(e.into_bytes())
473}
474
475fn write_encap(w: &mut BufferWriter, encap: &[u8]) -> Result<(), EncodeError> {
477 let len = u32::try_from(encap.len()).map_err(|_| EncodeError::ValueOutOfRange {
478 message: "TypeCode encapsulation exceeds u32",
479 })?;
480 w.write_u32(len)?;
481 w.write_bytes(encap)
482}
483
484fn decode_encap_ctx<F>(
490 r: &mut BufferReader<'_>,
491 base: usize,
492 cache: &mut TcCache,
493 body: F,
494) -> Result<TypeCode, DecodeError>
495where
496 F: FnOnce(&mut BufferReader<'_>, usize, &mut TcCache) -> Result<TypeCode, DecodeError>,
497{
498 let offset = r.position();
499 let len = r.read_u32()? as usize;
500 let child_base = base + r.position();
503 let bytes = r.read_bytes(len)?;
504 if bytes.is_empty() {
505 return Err(DecodeError::InvalidString {
506 offset,
507 reason: "empty TypeCode encapsulation",
508 });
509 }
510 let endianness = match bytes[0] {
511 0 => Endianness::Big,
512 1 => Endianness::Little,
513 _ => {
514 return Err(DecodeError::InvalidString {
515 offset,
516 reason: "invalid TypeCode encapsulation byte-order",
517 });
518 }
519 };
520 let mut e = BufferReader::new(bytes, endianness);
523 let _bo = e.read_u8()?;
524 body(&mut e, child_base, cache)
525}
526
527#[cfg(test)]
528#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
529mod tests {
530 use super::*;
531 use alloc::vec;
532
533 fn rt(tc: &TypeCode, e: Endianness) {
534 let mut w = BufferWriter::new(e);
535 tc.encode(&mut w).unwrap();
536 let bytes = w.into_bytes();
537 let mut r = BufferReader::new(&bytes, e);
538 assert_eq!(&TypeCode::decode(&mut r).unwrap(), tc, "{tc:?} / {e:?}");
539 }
540
541 #[test]
542 fn simple_kinds_roundtrip() {
543 for tc in [
544 TypeCode::Null,
545 TypeCode::Long,
546 TypeCode::Double,
547 TypeCode::Boolean,
548 TypeCode::Octet,
549 TypeCode::WChar,
550 TypeCode::Any,
551 TypeCode::String(0),
552 TypeCode::String(255),
553 TypeCode::WString(64),
554 ] {
555 rt(&tc, Endianness::Big);
556 rt(&tc, Endianness::Little);
557 }
558 }
559
560 #[test]
561 fn sequence_of_long_roundtrip() {
562 let tc = TypeCode::Sequence {
563 element: Box::new(TypeCode::Long),
564 bound: 0,
565 };
566 rt(&tc, Endianness::Big);
567 rt(&tc, Endianness::Little);
568 }
569
570 #[test]
577 fn indirection_recursive_struct() {
578 use tckind::{INDIRECTION, TK_STRUCT};
579 let mut eb = BufferWriter::new(Endianness::Big);
580 eb.write_u8(0).unwrap(); eb.write_string("IDL:Node:1.0").unwrap();
582 eb.write_string("Node").unwrap();
583 eb.write_u32(1).unwrap(); eb.write_string("n").unwrap();
585 eb.align(4);
586 let i_pos = eb.position();
587 eb.write_u32(INDIRECTION).unwrap();
588 let offset: i32 = -(12 + i_pos as i32);
589 eb.write_u32(offset as u32).unwrap();
590 let body = eb.into_bytes();
591
592 let mut w = BufferWriter::new(Endianness::Big);
593 w.write_u32(TK_STRUCT).unwrap();
594 w.write_u32(body.len() as u32).unwrap();
595 w.write_bytes(&body).unwrap();
596 let bytes = w.into_bytes();
597
598 let decoded = TypeCode::decode(&mut BufferReader::new(&bytes, Endianness::Big)).unwrap();
599 assert_eq!(
600 decoded,
601 TypeCode::Struct {
602 repo_id: "IDL:Node:1.0".into(),
603 name: "Node".into(),
604 members: vec![(
605 "n".into(),
606 TypeCode::Recursive {
607 repo_id: "IDL:Node:1.0".into()
608 }
609 )],
610 is_except: false,
611 }
612 );
613 }
614
615 #[test]
618 fn indirection_unresolved_target_is_error() {
619 use tckind::{INDIRECTION, TK_STRUCT};
620 let mut eb = BufferWriter::new(Endianness::Big);
623 eb.write_u8(0).unwrap();
624 eb.write_string("IDL:X:1.0").unwrap();
625 eb.write_string("X").unwrap();
626 eb.write_u32(1).unwrap();
627 eb.write_string("m").unwrap();
628 eb.align(4);
629 eb.write_u32(INDIRECTION).unwrap();
630 eb.write_u32((-4_i32) as u32).unwrap(); let body = eb.into_bytes();
632 let mut w = BufferWriter::new(Endianness::Big);
633 w.write_u32(TK_STRUCT).unwrap();
634 w.write_u32(body.len() as u32).unwrap();
635 w.write_bytes(&body).unwrap();
636 let bytes = w.into_bytes();
637 assert!(TypeCode::decode(&mut BufferReader::new(&bytes, Endianness::Big)).is_err());
638 }
639
640 #[test]
646 fn indirection_repeated_typecode() {
647 use tckind::{INDIRECTION, TK_STRUCT};
648 let s = TypeCode::Struct {
649 repo_id: "IDL:S:1.0".into(),
650 name: "S".into(),
651 members: vec![("x".into(), TypeCode::Long)],
652 is_except: false,
653 };
654 let mut eb = BufferWriter::new(Endianness::Big);
655 eb.write_u8(0).unwrap(); eb.write_string("IDL:Outer:1.0").unwrap();
657 eb.write_string("Outer").unwrap();
658 eb.write_u32(2).unwrap(); eb.write_string("a").unwrap();
660 eb.align(4);
661 let s_subpos = eb.position();
662 s.encode(&mut eb).unwrap(); eb.write_string("b").unwrap();
664 eb.align(4);
665 let i_pos = eb.position();
666 eb.write_u32(INDIRECTION).unwrap();
667 let offset: i32 = s_subpos as i32 - (i_pos as i32 + 4);
668 eb.write_u32(offset as u32).unwrap();
669 let body = eb.into_bytes();
670
671 let mut w = BufferWriter::new(Endianness::Big);
672 w.write_u32(TK_STRUCT).unwrap();
673 w.write_u32(body.len() as u32).unwrap();
674 w.write_bytes(&body).unwrap();
675 let bytes = w.into_bytes();
676
677 let decoded = TypeCode::decode(&mut BufferReader::new(&bytes, Endianness::Big)).unwrap();
678 assert_eq!(
679 decoded,
680 TypeCode::Struct {
681 repo_id: "IDL:Outer:1.0".into(),
682 name: "Outer".into(),
683 members: vec![("a".into(), s.clone()), ("b".into(), s.clone())],
684 is_except: false,
685 }
686 );
687 }
688
689 #[test]
691 fn indirection_forward_offset_rejected() {
692 use tckind::INDIRECTION;
693 let mut w = BufferWriter::new(Endianness::Big);
694 w.write_u32(INDIRECTION).unwrap();
695 w.write_u32(4_u32).unwrap(); let bytes = w.into_bytes();
697 assert!(TypeCode::decode(&mut BufferReader::new(&bytes, Endianness::Big)).is_err());
698 }
699
700 #[test]
701 fn struct_roundtrip_both_orders() {
702 let tc = TypeCode::Struct {
703 repo_id: "IDL:Point:1.0".into(),
704 name: "Point".into(),
705 members: vec![
706 ("x".into(), TypeCode::Long),
707 ("y".into(), TypeCode::Long),
708 ("label".into(), TypeCode::String(0)),
709 ],
710 is_except: false,
711 };
712 rt(&tc, Endianness::Big);
713 rt(&tc, Endianness::Little);
714 }
715
716 #[test]
717 fn enum_and_nested_sequence_of_struct() {
718 rt(
719 &TypeCode::Enum {
720 repo_id: "IDL:Color:1.0".into(),
721 name: "Color".into(),
722 members: vec!["RED".into(), "GREEN".into(), "BLUE".into()],
723 },
724 Endianness::Big,
725 );
726 let point = TypeCode::Struct {
728 repo_id: "IDL:Point:1.0".into(),
729 name: "Point".into(),
730 members: vec![("x".into(), TypeCode::Long), ("y".into(), TypeCode::Long)],
731 is_except: false,
732 };
733 let seq = TypeCode::Sequence {
734 element: Box::new(point),
735 bound: 10,
736 };
737 rt(&seq, Endianness::Big);
738 rt(&seq, Endianness::Little);
739 }
740
741 #[test]
742 fn struct_wire_layout_be() {
743 let tc = TypeCode::Struct {
745 repo_id: "X".into(),
746 name: "X".into(),
747 members: vec![],
748 is_except: false,
749 };
750 let mut w = BufferWriter::new(Endianness::Big);
751 tc.encode(&mut w).unwrap();
752 let bytes = w.into_bytes();
753 assert_eq!(&bytes[0..4], &[0, 0, 0, 15], "TCKind tk_struct");
754 let encap_len = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as usize;
756 assert_eq!(bytes.len(), 8 + encap_len);
757 assert_eq!(bytes[8], 0, "encap byte-order = big");
758 }
759}