1use alloc::format;
4use alloc::string::String;
5use alloc::vec::Vec;
6use core::fmt;
7
8use crate::{Error, ErrorKind};
9
10#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
25pub struct ObjectIdentifier(Vec<u32>);
26
27impl ObjectIdentifier {
28 pub fn new(arcs: Vec<u32>) -> Result<Self, Error> {
36 let argument = |what: String| Error::new(ErrorKind::Argument, what);
37 match arcs[..] {
38 [] | [_] => Err(argument(format!(
39 "an object identifier has two arcs at least, not {}",
40 arcs.len()
41 ))),
42 [first, ..] if first > 2 => Err(argument(format!(
43 "the first arc of an object identifier is 0, 1 or 2, not {first}"
44 ))),
45 [first, second, ..] if first < 2 && second >= 40 => Err(argument(format!(
46 "the second arc of an object identifier is below 40 under {first}, not {second}"
47 ))),
48 [2, second, ..] if second > u32::MAX - 80 => Err(argument(format!(
49 "the second arc of an object identifier, {second}, is more than this crate holds"
50 ))),
51 _ => Ok(Self(arcs)),
52 }
53 }
54
55 pub fn arcs(&self) -> &[u32] {
57 &self.0
58 }
59
60 pub(crate) fn to_contents(&self) -> Vec<u8> {
65 let mut contents = Vec::with_capacity(self.0.len() + 2);
66 let first = u64::from(self.0[0]) * 40 + u64::from(self.0[1]);
67 let rest = self.0[2..].iter().map(|arc| u64::from(*arc));
68 for number in core::iter::once(first).chain(rest) {
69 let septets = (64 - number.leading_zeros() as usize).div_ceil(7).max(1);
70 for septet in (0..septets).rev() {
71 let more = if septet > 0 { 0x80 } else { 0x00 };
72 contents.push((number >> (septet * 7)) as u8 & 0x7f | more);
73 }
74 }
75 contents
76 }
77
78 pub(crate) fn from_contents(contents: &[u8]) -> Result<Self, Error> {
80 let mut arcs = Vec::new();
81 let mut number: u64 = 0;
82 let mut started = false;
83 for octet in contents {
84 started = true;
87 number = number << 7 | u64::from(octet & 0x7f);
88 if number > u64::from(u32::MAX) {
89 return Err(Error::decode(
90 "an arc of an object identifier is more than 32 bits",
91 ));
92 }
93 if octet & 0x80 == 0 {
94 match arcs.is_empty() {
95 true => {
96 let first = (number / 40).min(2);
97 arcs.push(first as u32);
98 arcs.push((number - first * 40) as u32);
99 }
100 false => arcs.push(number as u32),
101 }
102 (number, started) = (0, false);
103 }
104 }
105 if started || arcs.is_empty() {
106 return Err(Error::decode(
107 "the octets of an object identifier end before its last arc does",
108 ));
109 }
110 Ok(Self(arcs))
111 }
112}
113
114impl fmt::Display for ObjectIdentifier {
116 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
117 for (at, arc) in self.0.iter().enumerate() {
118 if at > 0 {
119 formatter.write_str(".")?;
120 }
121 write!(formatter, "{arc}")?;
122 }
123 Ok(())
124 }
125}
126
127impl core::str::FromStr for ObjectIdentifier {
130 type Err = Error;
131
132 fn from_str(text: &str) -> Result<Self, Error> {
133 let arcs: Result<Vec<u32>, _> = text.split('.').map(str::parse).collect();
134 match arcs {
135 Ok(arcs) => Self::new(arcs),
136 Err(_) => Err(Error::new(
137 ErrorKind::Argument,
138 format!(
139 "{text:?} is no object identifier: its arcs are written in decimal, with a \
140 full stop between them"
141 ),
142 )),
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use alloc::string::ToString;
151 use alloc::vec;
152
153 #[test]
154 fn an_object_identifier_is_its_arcs_and_the_contents_octets_of_ber() {
155 let example = ObjectIdentifier::new(vec![2, 100, 3]).unwrap();
157 assert_eq!(example.to_contents(), [0x81, 0x34, 0x03]);
158 assert_eq!(
159 ObjectIdentifier::from_contents(&[0x81, 0x34, 0x03]).unwrap(),
160 example
161 );
162 assert_eq!(example.to_string(), "2.100.3");
163 assert_eq!("2.100.3".parse::<ObjectIdentifier>().unwrap(), example);
164
165 for arcs in [
166 vec![0, 0],
167 vec![0, 39],
168 vec![1, 39, 0],
169 vec![2, 0],
170 vec![2, 999, u32::MAX, 0, 127, 128, 16383, 16384],
171 vec![0, 4, 0, 1989, 0],
172 ] {
173 let identifier = ObjectIdentifier::new(arcs.clone()).unwrap();
174 let contents = identifier.to_contents();
175 let read = ObjectIdentifier::from_contents(&contents).unwrap();
176 assert_eq!(read.arcs(), arcs);
177 }
178
179 for arcs in [vec![], vec![1], vec![3, 0], vec![0, 40], vec![1, 40]] {
180 let error = ObjectIdentifier::new(arcs).unwrap_err();
181 assert_eq!(error.kind(), ErrorKind::Argument);
182 }
183 assert!("1.2.x".parse::<ObjectIdentifier>().is_err());
184 assert!("".parse::<ObjectIdentifier>().is_err());
185
186 let padded = ObjectIdentifier::from_contents(&[0x2a, 0x80, 0x01]).unwrap();
188 assert_eq!(padded.arcs(), [1, 2, 1]);
189 for contents in [&[][..], &[0x81], &[0x2a, 0x90, 0x80, 0x80, 0x80, 0x00]] {
191 let error = ObjectIdentifier::from_contents(contents).unwrap_err();
192 assert_eq!(error.kind(), ErrorKind::Decode, "{contents:02x?}");
193 }
194 }
195}