zerodds_corba_giop/
code_set.rs1use alloc::vec::Vec;
25
26use zerodds_cdr::{BufferReader, BufferWriter, Endianness};
27
28use crate::error::{GiopError, GiopResult};
29use crate::service_context::{ServiceContext, ServiceContextList, ServiceContextTag};
30
31pub mod well_known {
33 pub const ISO_8859_1: u32 = 0x0001_0001;
35 pub const UTF_8: u32 = 0x0501_0001;
38 pub const UTF_16: u32 = 0x0001_0109;
40 pub const UCS_2: u32 = 0x0001_0100;
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct CodeSetContext {
47 pub char_data: u32,
49 pub wchar_data: u32,
51}
52
53impl Default for CodeSetContext {
54 fn default() -> Self {
55 Self::default_pair()
56 }
57}
58
59impl CodeSetContext {
60 #[must_use]
62 pub const fn new(char_data: u32, wchar_data: u32) -> Self {
63 Self {
64 char_data,
65 wchar_data,
66 }
67 }
68
69 #[must_use]
71 pub const fn default_pair() -> Self {
72 Self {
73 char_data: well_known::UTF_8,
74 wchar_data: well_known::UTF_16,
75 }
76 }
77
78 pub fn encode_encapsulation(&self, endianness: Endianness) -> GiopResult<Vec<u8>> {
85 let mut w = BufferWriter::new(endianness);
88 w.write_u8(match endianness {
89 Endianness::Big => 0,
90 Endianness::Little => 1,
91 })?;
92 w.write_u32(self.char_data)?;
93 w.write_u32(self.wchar_data)?;
94 Ok(w.into_bytes())
95 }
96
97 pub fn decode_encapsulation(encap: &[u8]) -> GiopResult<Self> {
102 if encap.is_empty() {
103 return Err(GiopError::Malformed(
104 "empty CodeSetContext encapsulation".into(),
105 ));
106 }
107 let endianness = match encap[0] {
108 0 => Endianness::Big,
109 1 => Endianness::Little,
110 other => {
111 return Err(GiopError::Malformed(alloc::format!(
112 "invalid CodeSetContext byte-order octet: {other}"
113 )));
114 }
115 };
116 let mut r = BufferReader::new(encap, endianness);
120 let _bo = r.read_u8()?;
121 let char_data = r.read_u32()?;
122 let wchar_data = r.read_u32()?;
123 Ok(Self {
124 char_data,
125 wchar_data,
126 })
127 }
128
129 pub fn to_service_context(&self, endianness: Endianness) -> GiopResult<ServiceContext> {
134 let data = self.encode_encapsulation(endianness)?;
135 Ok(ServiceContext::new(
136 ServiceContextTag::CodeSets.as_u32(),
137 data,
138 ))
139 }
140
141 pub fn from_service_context_list(list: &ServiceContextList) -> GiopResult<Option<Self>> {
147 let tag = ServiceContextTag::CodeSets.as_u32();
148 match list.0.iter().find(|c| c.context_id == tag) {
149 Some(ctx) => Ok(Some(Self::decode_encapsulation(&ctx.context_data)?)),
150 None => Ok(None),
151 }
152 }
153}
154
155#[cfg(test)]
156#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn well_known_ids_match_osf_registry() {
162 assert_eq!(well_known::ISO_8859_1, 0x0001_0001);
164 assert_eq!(well_known::UTF_8, 0x0501_0001);
165 assert_eq!(well_known::UTF_16, 0x0001_0109);
166 assert_eq!(well_known::UCS_2, 0x0001_0100);
167 }
168
169 #[test]
170 fn encapsulation_wire_layout_be() {
171 let ctx = CodeSetContext::new(well_known::UTF_8, well_known::UTF_16);
172 let encap = ctx.encode_encapsulation(Endianness::Big).unwrap();
173 assert_eq!(encap.len(), 12);
175 assert_eq!(encap[0], 0); assert_eq!(&encap[4..8], &0x0501_0001u32.to_be_bytes());
177 assert_eq!(&encap[8..12], &0x0001_0109u32.to_be_bytes());
178 }
179
180 #[test]
181 fn encapsulation_roundtrip_both_orders() {
182 for e in [Endianness::Big, Endianness::Little] {
183 let ctx = CodeSetContext::new(well_known::ISO_8859_1, well_known::UCS_2);
184 let encap = ctx.encode_encapsulation(e).unwrap();
185 assert_eq!(CodeSetContext::decode_encapsulation(&encap).unwrap(), ctx);
186 }
187 }
188
189 #[test]
190 fn service_context_roundtrip_via_list() {
191 let ctx = CodeSetContext::default_pair();
192 let sc = ctx.to_service_context(Endianness::Little).unwrap();
193 assert_eq!(sc.context_id, 1);
194 let list = ServiceContextList(alloc::vec![sc]);
195 let found = CodeSetContext::from_service_context_list(&list)
196 .unwrap()
197 .expect("CodeSets context present");
198 assert_eq!(found, ctx);
199 }
200
201 #[test]
202 fn absent_context_is_none() {
203 let list = ServiceContextList(alloc::vec![ServiceContext::new(42, alloc::vec![1, 2, 3])]);
204 assert_eq!(
205 CodeSetContext::from_service_context_list(&list).unwrap(),
206 None
207 );
208 }
209
210 #[test]
211 fn invalid_byte_order_octet_rejected() {
212 let bad = alloc::vec![0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
213 assert!(CodeSetContext::decode_encapsulation(&bad).is_err());
214 }
215}