Coverage Report

Created: 2026-05-16 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/const-oid-0.10.2/src/encoder.rs
Line
Count
Source
1
//! OID encoder with `const` support.
2
3
use crate::{
4
    Arc, Buffer, Error, ObjectIdentifier, Result,
5
    arcs::{ARC_MAX_FIRST, ARC_MAX_SECOND},
6
};
7
8
/// BER/DER encoder.
9
#[derive(Debug)]
10
pub(crate) struct Encoder<const MAX_SIZE: usize> {
11
    /// Current state.
12
    state: State,
13
14
    /// Bytes of the OID being BER-encoded in-progress.
15
    bytes: [u8; MAX_SIZE],
16
17
    /// Current position within the byte buffer.
18
    cursor: usize,
19
}
20
21
/// Current state of the encoder.
22
#[derive(Debug)]
23
enum State {
24
    /// Initial state - no arcs yet encoded.
25
    Initial,
26
27
    /// First arc has been supplied and stored as the wrapped [`Arc`].
28
    FirstArc(Arc),
29
30
    /// Encoding base 128 body of the OID.
31
    Body,
32
}
33
34
impl<const MAX_SIZE: usize> Encoder<MAX_SIZE> {
35
    /// Create a new encoder initialized to an empty default state.
36
0
    pub(crate) const fn new() -> Self {
37
0
        Self {
38
0
            state: State::Initial,
39
0
            bytes: [0u8; MAX_SIZE],
40
0
            cursor: 0,
41
0
        }
42
0
    }
43
44
    /// Extend an existing OID.
45
0
    pub(crate) const fn extend(oid: ObjectIdentifier<MAX_SIZE>) -> Self {
46
0
        Self {
47
0
            state: State::Body,
48
0
            bytes: oid.ber.bytes,
49
0
            cursor: oid.ber.length as usize,
50
0
        }
51
0
    }
52
53
    /// Encode an [`Arc`] as base 128 into the internal buffer.
54
0
    pub(crate) const fn arc(mut self, arc: Arc) -> Result<Self> {
55
0
        match self.state {
56
            State::Initial => {
57
0
                if arc > ARC_MAX_FIRST {
58
0
                    return Err(Error::ArcInvalid { arc });
59
0
                }
60
61
0
                self.state = State::FirstArc(arc);
62
0
                Ok(self)
63
            }
64
0
            State::FirstArc(first_arc) => {
65
0
                if arc > ARC_MAX_SECOND {
66
0
                    return Err(Error::ArcInvalid { arc });
67
0
                }
68
69
0
                self.state = State::Body;
70
0
                self.bytes[0] = checked_add!(
71
0
                    checked_mul!(checked_add!(ARC_MAX_SECOND, 1), first_arc),
72
0
                    arc
73
                ) as u8;
74
0
                self.cursor = 1;
75
0
                Ok(self)
76
            }
77
0
            State::Body => self.encode_base128(arc),
78
        }
79
0
    }
80
81
    /// Finish encoding an OID.
82
0
    pub(crate) const fn finish(self) -> Result<ObjectIdentifier<MAX_SIZE>> {
83
0
        if self.cursor == 0 {
84
0
            return Err(Error::Empty);
85
0
        }
86
87
0
        let ber = Buffer {
88
0
            bytes: self.bytes,
89
0
            length: self.cursor as u8,
90
0
        };
91
92
0
        Ok(ObjectIdentifier { ber })
93
0
    }
94
95
    /// Encode base 128.
96
0
    const fn encode_base128(mut self, arc: Arc) -> Result<Self> {
97
0
        let nbytes = base128_len(arc);
98
0
        let end_pos = checked_add!(self.cursor, nbytes);
99
100
0
        if end_pos > MAX_SIZE {
101
0
            return Err(Error::Length);
102
0
        }
103
104
0
        let mut i = 0;
105
0
        while i < nbytes {
106
            // TODO(tarcieri): use `?` when stable in `const fn`
107
0
            self.bytes[self.cursor] = match base128_byte(arc, i, nbytes) {
108
0
                Ok(byte) => byte,
109
0
                Err(e) => return Err(e),
110
            };
111
0
            self.cursor = checked_add!(self.cursor, 1);
112
0
            i = checked_add!(i, 1);
113
        }
114
115
0
        Ok(self)
116
0
    }
117
}
118
119
/// Compute the length of an arc when encoded in base 128.
120
0
const fn base128_len(arc: Arc) -> usize {
121
0
    match arc {
122
0
        0..=0x7f => 1,              // up to 7 bits
123
0
        0x80..=0x3fff => 2,         // up to 14 bits
124
0
        0x4000..=0x1fffff => 3,     // up to 21 bits
125
0
        0x200000..=0x0fffffff => 4, // up to 28 bits
126
0
        _ => 5,
127
    }
128
0
}
129
130
/// Compute the big endian base 128 encoding of the given [`Arc`] at the given byte.
131
0
const fn base128_byte(arc: Arc, pos: usize, total: usize) -> Result<u8> {
132
0
    debug_assert!(pos < total);
133
0
    let last_byte = checked_add!(pos, 1) == total;
134
0
    let mask = if last_byte { 0 } else { 0b10000000 };
135
0
    let shift = checked_mul!(checked_sub!(checked_sub!(total, pos), 1), 7);
136
0
    Ok(((arc >> shift) & 0b1111111) as u8 | mask)
137
0
}
138
139
#[cfg(test)]
140
#[allow(clippy::unwrap_used)]
141
mod tests {
142
    use super::Encoder;
143
    use hex_literal::hex;
144
145
    /// OID `1.2.840.10045.2.1` encoded as ASN.1 BER/DER
146
    const EXAMPLE_OID_BER: &[u8] = &hex!("2A8648CE3D0201");
147
148
    #[test]
149
    fn base128_byte() {
150
        let example_arc = 0x44332211;
151
        assert_eq!(super::base128_len(example_arc), 5);
152
        assert_eq!(super::base128_byte(example_arc, 0, 5).unwrap(), 0b10000100);
153
        assert_eq!(super::base128_byte(example_arc, 1, 5).unwrap(), 0b10100001);
154
        assert_eq!(super::base128_byte(example_arc, 2, 5).unwrap(), 0b11001100);
155
        assert_eq!(super::base128_byte(example_arc, 3, 5).unwrap(), 0b11000100);
156
        assert_eq!(super::base128_byte(example_arc, 4, 5).unwrap(), 0b10001);
157
    }
158
159
    #[test]
160
    fn encode() {
161
        let encoder = Encoder::<7>::new();
162
        let encoder = encoder.arc(1).unwrap();
163
        let encoder = encoder.arc(2).unwrap();
164
        let encoder = encoder.arc(840).unwrap();
165
        let encoder = encoder.arc(10045).unwrap();
166
        let encoder = encoder.arc(2).unwrap();
167
        let encoder = encoder.arc(1).unwrap();
168
        assert_eq!(&encoder.bytes[..encoder.cursor], EXAMPLE_OID_BER);
169
    }
170
}