/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.10.2/src/arcs.rs
Line | Count | Source |
1 | | //! Arcs are integer values which exist within an OID's hierarchy. |
2 | | |
3 | | use crate::{Error, Result}; |
4 | | |
5 | | #[cfg(doc)] |
6 | | use crate::ObjectIdentifier; |
7 | | |
8 | | /// Type alias used to represent an "arc", i.e. integer identifier value, where an OID comprises a |
9 | | /// sequence of arcs. |
10 | | /// |
11 | | /// X.660 does not define a maximum size of an arc. We instead follow Mozilla* conventions for |
12 | | /// maximum values of an arc, with a maximum value of 2^32-1 (4294967295), a.k.a. [`u32::MAX`] |
13 | | /// with [`Arc`] being a type alias for [`u32`]. |
14 | | /// |
15 | | /// Note that this means we deliberately do *NOT* support UUIDs used as OIDs. |
16 | | /// |
17 | | /// *NOTE: please see this study for a survey of how various OID libraries handle maximum arcs: |
18 | | /// <https://misc.daniel-marschall.de/asn.1/oid_facts.html> |
19 | | pub type Arc = u32; |
20 | | |
21 | | /// Maximum value of the first arc in an OID. |
22 | | pub(crate) const ARC_MAX_FIRST: Arc = 2; |
23 | | |
24 | | /// Maximum value of the second arc in an OID. |
25 | | pub(crate) const ARC_MAX_SECOND: Arc = 39; |
26 | | |
27 | | /// Maximum number of bytes supported in an arc. |
28 | | /// |
29 | | /// Note that OIDs are base 128 encoded (with continuation bits), so we must consider how many bytes |
30 | | /// are required when each byte can only represent 7-bits of the input. |
31 | | const ARC_MAX_BYTES: usize = (Arc::BITS as usize).div_ceil(7); |
32 | | |
33 | | /// Maximum value of the last byte in an arc. |
34 | | const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits |
35 | | |
36 | | /// [`Iterator`] over [`Arc`] values (a.k.a. nodes) in an [`ObjectIdentifier`]. |
37 | | /// |
38 | | /// This iterates over all arcs in an OID, including the root. |
39 | | pub struct Arcs<'a> { |
40 | | /// OID bytes we're iterating over. |
41 | | bytes: &'a [u8], |
42 | | |
43 | | /// Current position within the serialized BER bytes of this OID. |
44 | | cursor: Option<usize>, |
45 | | } |
46 | | |
47 | | impl<'a> Arcs<'a> { |
48 | | /// Create a new iterator over an OID encoded as BER bytes. |
49 | 0 | pub(crate) fn new(bytes: &'a [u8]) -> Self { |
50 | 0 | Self { |
51 | 0 | bytes, |
52 | 0 | cursor: None, |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | /// Try to parse the next arc in this OID. |
57 | | /// |
58 | | /// This method is fallible so it can be used as a first pass to determine |
59 | | /// that the arcs in the OID are well-formed. |
60 | 0 | pub(crate) fn try_next(&mut self) -> Result<Option<Arc>> { |
61 | 0 | match self.cursor { |
62 | | // Indicates we're on the root arc |
63 | | None => { |
64 | 0 | let root_byte = *self.bytes.first().ok_or(Error::Empty)?; |
65 | 0 | let root = RootArcs::try_from(root_byte)?; |
66 | 0 | self.cursor = Some(0); |
67 | 0 | Ok(Some(root.first_arc())) |
68 | | } |
69 | | Some(0) => { |
70 | 0 | let root = RootArcs::try_from(self.bytes[0])?; |
71 | 0 | self.cursor = Some(1); |
72 | 0 | Ok(Some(root.second_arc())) |
73 | | } |
74 | 0 | Some(offset) => { |
75 | 0 | let mut result = 0; |
76 | 0 | let mut arc_bytes = 0; |
77 | | |
78 | | loop { |
79 | 0 | let len = checked_add!(offset, arc_bytes); |
80 | | |
81 | 0 | match self.bytes.get(len).cloned() { |
82 | | // The arithmetic below includes advance checks |
83 | | // against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET` |
84 | | // which ensure the operations will not overflow. |
85 | | #[allow(clippy::arithmetic_side_effects)] |
86 | 0 | Some(byte) => { |
87 | 0 | arc_bytes = checked_add!(arc_bytes, 1); |
88 | | |
89 | 0 | if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) { |
90 | 0 | return Err(Error::ArcTooBig); |
91 | 0 | } |
92 | | |
93 | 0 | result = (result << 7) | (byte & 0b1111111) as Arc; |
94 | | |
95 | 0 | if byte & 0b10000000 == 0 { |
96 | 0 | self.cursor = Some(checked_add!(offset, arc_bytes)); |
97 | 0 | return Ok(Some(result)); |
98 | 0 | } |
99 | | } |
100 | | None => { |
101 | 0 | if arc_bytes == 0 { |
102 | 0 | return Ok(None); |
103 | | } else { |
104 | 0 | return Err(Error::Base128); |
105 | | } |
106 | | } |
107 | | } |
108 | | } |
109 | | } |
110 | | } |
111 | 0 | } |
112 | | } |
113 | | |
114 | | impl Iterator for Arcs<'_> { |
115 | | type Item = Arc; |
116 | | |
117 | 0 | fn next(&mut self) -> Option<Arc> { |
118 | | // ObjectIdentifier constructors should ensure the OID is well-formed |
119 | 0 | self.try_next().expect("OID malformed") |
120 | 0 | } |
121 | | } |
122 | | |
123 | | /// Byte containing the first and second arcs of an OID. |
124 | | /// |
125 | | /// This is represented this way in order to reduce the overall size of the |
126 | | /// [`ObjectIdentifier`] struct. |
127 | | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
128 | | struct RootArcs(u8); |
129 | | |
130 | | impl RootArcs { |
131 | | /// Create [`RootArcs`] from the first and second arc values represented |
132 | | /// as `Arc` integers. |
133 | 0 | pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> { |
134 | 0 | if first_arc > ARC_MAX_FIRST { |
135 | 0 | return Err(Error::ArcInvalid { arc: first_arc }); |
136 | 0 | } |
137 | | |
138 | 0 | if second_arc > ARC_MAX_SECOND { |
139 | 0 | return Err(Error::ArcInvalid { arc: second_arc }); |
140 | 0 | } |
141 | | |
142 | | // The checks above ensure this operation will not overflow |
143 | | #[allow(clippy::arithmetic_side_effects)] |
144 | 0 | let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8; |
145 | | |
146 | 0 | Ok(Self(byte)) |
147 | 0 | } |
148 | | |
149 | | /// Get the value of the first arc |
150 | | #[allow(clippy::arithmetic_side_effects)] |
151 | 0 | pub(crate) const fn first_arc(self) -> Arc { |
152 | 0 | self.0 as Arc / (ARC_MAX_SECOND + 1) |
153 | 0 | } |
154 | | |
155 | | /// Get the value of the second arc |
156 | | #[allow(clippy::arithmetic_side_effects)] |
157 | 0 | pub(crate) const fn second_arc(self) -> Arc { |
158 | 0 | self.0 as Arc % (ARC_MAX_SECOND + 1) |
159 | 0 | } |
160 | | } |
161 | | |
162 | | impl TryFrom<u8> for RootArcs { |
163 | | type Error = Error; |
164 | | |
165 | | // Ensured not to overflow by constructor invariants |
166 | | #[allow(clippy::arithmetic_side_effects)] |
167 | 0 | fn try_from(octet: u8) -> Result<Self> { |
168 | 0 | let first = octet as Arc / (ARC_MAX_SECOND + 1); |
169 | 0 | let second = octet as Arc % (ARC_MAX_SECOND + 1); |
170 | 0 | let result = Self::new(first, second)?; |
171 | 0 | debug_assert_eq!(octet, result.0); |
172 | 0 | Ok(result) |
173 | 0 | } |
174 | | } |
175 | | |
176 | | impl From<RootArcs> for u8 { |
177 | 0 | fn from(root_arcs: RootArcs) -> u8 { |
178 | 0 | root_arcs.0 |
179 | 0 | } |
180 | | } |