Coverage Report

Created: 2026-07-10 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.9.6/src/arcs.rs
Line
Count
Source
1
//! Arcs are integer values which exist within an OID's hierarchy.
2
3
use crate::{Error, ObjectIdentifier, Result};
4
use core::mem;
5
6
/// Type alias used to represent an "arc" (i.e. integer identifier value).
7
///
8
/// X.660 does not define a maximum size of an arc.
9
///
10
/// The current representation is `u32`, which has been selected as being
11
/// sufficient to cover the current PKCS/PKIX use cases this library has been
12
/// used in conjunction with.
13
///
14
/// Future versions may potentially make it larger if a sufficiently important
15
/// use case is discovered.
16
pub type Arc = u32;
17
18
/// Maximum value of the first arc in an OID.
19
pub(crate) const ARC_MAX_FIRST: Arc = 2;
20
21
/// Maximum value of the second arc in an OID.
22
pub(crate) const ARC_MAX_SECOND: Arc = 39;
23
24
/// Maximum number of bytes supported in an arc.
25
const ARC_MAX_BYTES: usize = mem::size_of::<Arc>();
26
27
/// Maximum value of the last byte in an arc.
28
const ARC_MAX_LAST_OCTET: u8 = 0b11110000; // Max bytes of leading 1-bits
29
30
/// [`Iterator`] over [`Arc`] values (a.k.a. nodes) in an [`ObjectIdentifier`].
31
///
32
/// This iterates over all arcs in an OID, including the root.
33
pub struct Arcs<'a> {
34
    /// OID we're iterating over
35
    oid: &'a ObjectIdentifier,
36
37
    /// Current position within the serialized DER bytes of this OID
38
    cursor: Option<usize>,
39
}
40
41
impl<'a> Arcs<'a> {
42
    /// Create a new iterator over the arcs of this OID
43
0
    pub(crate) fn new(oid: &'a ObjectIdentifier) -> Self {
44
0
        Self { oid, cursor: None }
45
0
    }
46
47
    /// Try to parse the next arc in this OID.
48
    ///
49
    /// This method is fallible so it can be used as a first pass to determine
50
    /// that the arcs in the OID are well-formed.
51
0
    pub(crate) fn try_next(&mut self) -> Result<Option<Arc>> {
52
0
        match self.cursor {
53
            // Indicates we're on the root OID
54
            None => {
55
0
                let root = RootArcs::try_from(self.oid.as_bytes()[0])?;
56
0
                self.cursor = Some(0);
57
0
                Ok(Some(root.first_arc()))
58
            }
59
            Some(0) => {
60
0
                let root = RootArcs::try_from(self.oid.as_bytes()[0])?;
61
0
                self.cursor = Some(1);
62
0
                Ok(Some(root.second_arc()))
63
            }
64
0
            Some(offset) => {
65
0
                let mut result = 0;
66
0
                let mut arc_bytes = 0;
67
68
                loop {
69
0
                    let len = checked_add!(offset, arc_bytes);
70
71
0
                    match self.oid.as_bytes().get(len).cloned() {
72
                        // The arithmetic below includes advance checks
73
                        // against `ARC_MAX_BYTES` and `ARC_MAX_LAST_OCTET`
74
                        // which ensure the operations will not overflow.
75
                        #[allow(clippy::integer_arithmetic)]
76
0
                        Some(byte) => {
77
0
                            arc_bytes = checked_add!(arc_bytes, 1);
78
79
0
                            if (arc_bytes > ARC_MAX_BYTES) && (byte & ARC_MAX_LAST_OCTET != 0) {
80
0
                                return Err(Error::ArcTooBig);
81
0
                            }
82
83
0
                            result = result << 7 | (byte & 0b1111111) as Arc;
84
85
0
                            if byte & 0b10000000 == 0 {
86
0
                                self.cursor = Some(checked_add!(offset, arc_bytes));
87
0
                                return Ok(Some(result));
88
0
                            }
89
                        }
90
                        None => {
91
0
                            if arc_bytes == 0 {
92
0
                                return Ok(None);
93
                            } else {
94
0
                                return Err(Error::Base128);
95
                            }
96
                        }
97
                    }
98
                }
99
            }
100
        }
101
0
    }
102
}
103
104
impl<'a> Iterator for Arcs<'a> {
105
    type Item = Arc;
106
107
0
    fn next(&mut self) -> Option<Arc> {
108
        // ObjectIdentifier constructors should ensure the OID is well-formed
109
0
        self.try_next().expect("OID malformed")
110
0
    }
111
}
112
113
/// Byte containing the first and second arcs of an OID.
114
///
115
/// This is represented this way in order to reduce the overall size of the
116
/// [`ObjectIdentifier`] struct.
117
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
118
struct RootArcs(u8);
119
120
impl RootArcs {
121
    /// Create [`RootArcs`] from the first and second arc values represented
122
    /// as `Arc` integers.
123
0
    pub(crate) const fn new(first_arc: Arc, second_arc: Arc) -> Result<Self> {
124
0
        if first_arc > ARC_MAX_FIRST {
125
0
            return Err(Error::ArcInvalid { arc: first_arc });
126
0
        }
127
128
0
        if second_arc > ARC_MAX_SECOND {
129
0
            return Err(Error::ArcInvalid { arc: second_arc });
130
0
        }
131
132
        // The checks above ensure this operation will not overflow
133
        #[allow(clippy::integer_arithmetic)]
134
0
        let byte = (first_arc * (ARC_MAX_SECOND + 1)) as u8 + second_arc as u8;
135
136
0
        Ok(Self(byte))
137
0
    }
138
139
    /// Get the value of the first arc
140
    #[allow(clippy::integer_arithmetic)]
141
0
    pub(crate) const fn first_arc(self) -> Arc {
142
0
        self.0 as Arc / (ARC_MAX_SECOND + 1)
143
0
    }
144
145
    /// Get the value of the second arc
146
    #[allow(clippy::integer_arithmetic)]
147
0
    pub(crate) const fn second_arc(self) -> Arc {
148
0
        self.0 as Arc % (ARC_MAX_SECOND + 1)
149
0
    }
150
}
151
152
impl TryFrom<u8> for RootArcs {
153
    type Error = Error;
154
155
    // Ensured not to overflow by constructor invariants
156
    #[allow(clippy::integer_arithmetic)]
157
0
    fn try_from(octet: u8) -> Result<Self> {
158
0
        let first = octet as Arc / (ARC_MAX_SECOND + 1);
159
0
        let second = octet as Arc % (ARC_MAX_SECOND + 1);
160
0
        let result = Self::new(first, second)?;
161
0
        debug_assert_eq!(octet, result.0);
162
0
        Ok(result)
163
0
    }
164
}
165
166
impl From<RootArcs> for u8 {
167
0
    fn from(root_arcs: RootArcs) -> u8 {
168
0
        root_arcs.0
169
0
    }
170
}