1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use crate::{DynTagged, Error, Result, Tag};
#[cfg(feature = "std")]
use crate::{SerializeResult, ToDer};
use core::ops;

/// BER Object Length
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Length {
    /// Definite form (X.690 8.1.3.3)
    Definite(usize),
    /// Indefinite form (X.690 8.1.3.6)
    Indefinite,
}

impl Length {
    /// Return true if length is definite and equal to 0
    #[inline]
    pub fn is_null(&self) -> bool {
        *self == Length::Definite(0)
    }

    /// Get length of primitive object
    #[inline]
    pub fn definite(&self) -> Result<usize> {
        match self {
            Length::Definite(sz) => Ok(*sz),
            Length::Indefinite => Err(Error::IndefiniteLengthUnexpected),
        }
    }

    /// Return true if length is definite
    #[inline]
    pub const fn is_definite(&self) -> bool {
        matches!(self, Length::Definite(_))
    }

    /// Return error if length is not definite
    #[inline]
    pub const fn assert_definite(&self) -> Result<()> {
        match self {
            Length::Definite(_) => Ok(()),
            Length::Indefinite => Err(Error::IndefiniteLengthUnexpected),
        }
    }
}

impl From<usize> for Length {
    fn from(l: usize) -> Self {
        Length::Definite(l)
    }
}

impl ops::Add<Length> for Length {
    type Output = Self;

    fn add(self, rhs: Length) -> Self::Output {
        match self {
            Length::Indefinite => self,
            Length::Definite(lhs) => match rhs {
                Length::Indefinite => rhs,
                Length::Definite(rhs) => Length::Definite(lhs + rhs),
            },
        }
    }
}

impl ops::Add<usize> for Length {
    type Output = Self;

    fn add(self, rhs: usize) -> Self::Output {
        match self {
            Length::Definite(lhs) => Length::Definite(lhs + rhs),
            Length::Indefinite => self,
        }
    }
}

impl ops::AddAssign<usize> for Length {
    fn add_assign(&mut self, rhs: usize) {
        match self {
            Length::Definite(ref mut lhs) => *lhs += rhs,
            Length::Indefinite => (),
        }
    }
}

impl DynTagged for Length {
    fn tag(&self) -> Tag {
        Tag(0)
    }
}

#[cfg(feature = "std")]
impl ToDer for Length {
    fn to_der_len(&self) -> Result<usize> {
        match self {
            Length::Indefinite => Ok(1),
            Length::Definite(l) => match l {
                0..=0x7f => Ok(1),
                0x80..=0xff => Ok(2),
                0x100..=0xffff => Ok(3),
                0x1_0000..=0xffff_ffff => Ok(4),
                _ => Err(Error::InvalidLength),
            },
        }
    }

    fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        match *self {
            Length::Indefinite => {
                let sz = writer.write(&[0b1000_0000])?;
                Ok(sz)
            }
            Length::Definite(l) => {
                if l <= 127 {
                    // Short form
                    let sz = writer.write(&[l as u8])?;
                    Ok(sz)
                } else {
                    // Long form
                    let b = l.to_be_bytes();
                    // skip leading zeroes
                    // we do not have to test for length, l cannot be 0
                    let mut idx = 0;
                    while b[idx] == 0 {
                        idx += 1;
                    }
                    let b = &b[idx..];
                    // first byte: 0x80 + length of length
                    let b0 = 0x80 | (b.len() as u8);
                    let sz = writer.write(&[b0])?;
                    let sz = sz + writer.write(b)?;
                    Ok(sz)
                }
            }
        }
    }

    fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        Ok(0)
    }
}

#[cfg(test)]
mod tests {
    use crate::*;

    /// Generic and coverage tests
    #[test]
    fn methods_length() {
        let l = Length::from(2);
        assert_eq!(l.definite(), Ok(2));
        assert!(l.assert_definite().is_ok());

        let l = Length::Indefinite;
        assert!(l.definite().is_err());
        assert!(l.assert_definite().is_err());

        let l = Length::from(2);
        assert_eq!(l + 2, Length::from(4));
        assert_eq!(l + Length::Indefinite, Length::Indefinite);

        let l = Length::Indefinite;
        assert_eq!(l + 2, Length::Indefinite);

        let l = Length::from(2);
        assert_eq!(l + Length::from(2), Length::from(4));

        let l = Length::Indefinite;
        assert_eq!(l + Length::from(2), Length::Indefinite);

        let mut l = Length::from(2);
        l += 2;
        assert_eq!(l.definite(), Ok(4));

        let mut l = Length::Indefinite;
        l += 2;
        assert_eq!(l, Length::Indefinite);
    }
}