1use crate::base128;
2use crate::parser::{ParseError, ParseErrorKind, ParseResult};
3use crate::writer::{WriteBuf, WriteResult};
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
6pub enum TagClass {
7 Universal = 0b00,
8 Application = 0b01,
9 ContextSpecific = 0b10,
10 Private = 0b11,
11}
12
13#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
14pub struct Tag {
15 value: u32,
16 constructed: bool,
17 class: TagClass,
18}
19
20pub(crate) const CONSTRUCTED: u8 = 0x20;
21
22impl Tag {
23 pub fn from_bytes(mut data: &[u8]) -> ParseResult<(Tag, &[u8])> {
26 let tag = match data.first() {
27 Some(&b) => b,
28 None => return Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
29 };
30 data = &data[1..];
31 let mut value = u32::from(tag & 0x1f);
32 let constructed = tag & CONSTRUCTED == CONSTRUCTED;
33
34 let tag_class_bits = tag >> 6;
35 let class = if tag_class_bits == TagClass::Universal as u8 {
36 TagClass::Universal
37 } else if tag_class_bits == TagClass::Application as u8 {
38 TagClass::Application
39 } else if tag_class_bits == TagClass::ContextSpecific as u8 {
40 TagClass::ContextSpecific
41 } else {
42 assert!(tag_class_bits == TagClass::Private as u8);
43 TagClass::Private
44 };
45
46 if value == 0x1f {
48 let large_value;
49 (large_value, data) = base128::read_base128_int(data).map_err(|e| {
50 if matches!(e.kind(), ParseErrorKind::ShortData { .. }) {
51 e
52 } else {
53 ParseError::new(ParseErrorKind::InvalidTag)
54 }
55 })?;
56 value = large_value
57 .try_into()
58 .map_err(|_| ParseError::new(ParseErrorKind::InvalidTag))?;
59 if value < 0x1f {
61 return Err(ParseError::new(ParseErrorKind::InvalidTag));
62 }
63 }
64
65 Ok((
66 Tag {
67 value,
68 constructed,
69 class,
70 },
71 data,
72 ))
73 }
74
75 pub(crate) const fn new(tag: u32, class: TagClass, constructed: bool) -> Tag {
76 Tag {
77 value: tag,
78 constructed,
79 class,
80 }
81 }
82
83 #[doc(hidden)]
86 pub const fn primitive(tag: u32) -> Tag {
87 Tag::new(tag, TagClass::Universal, false)
88 }
89
90 pub(crate) const fn constructed(tag: u32) -> Tag {
91 Tag::new(tag, TagClass::Universal, true)
92 }
93
94 pub fn as_u8(self) -> Option<u8> {
98 if self.value >= 0x1f {
99 return None;
100 }
101 Some(
102 ((self.class as u8) << 6)
103 | if self.constructed { CONSTRUCTED } else { 0 }
104 | (self.value as u8),
105 )
106 }
107
108 pub(crate) fn write_to(self, dest: &mut WriteBuf) -> WriteResult {
111 let mut b = ((self.class as u8) << 6) | if self.constructed { CONSTRUCTED } else { 0 };
112 if self.value >= 0x1f {
113 b |= 0x1f;
114 dest.push_byte(b)?;
115 let len = base128::base128_length(self.value.into());
116 let orig_len = dest.len();
117 for _ in 0..len {
118 dest.push_byte(0)?;
119 }
120 base128::write_base128_int(&mut dest.as_mut_slice()[orig_len..], self.value.into());
121 } else {
122 b |= self.value as u8;
123 dest.push_byte(b)?;
124 }
125
126 Ok(())
127 }
128
129 pub const fn is_constructed(self) -> bool {
130 self.constructed
131 }
132
133 pub fn class(self) -> TagClass {
134 self.class
135 }
136
137 pub fn value(self) -> u32 {
138 self.value
139 }
140
141 pub(crate) fn encoded_length(self) -> usize {
143 if self.value >= 0x1f {
144 1 + crate::base128::base128_length(self.value.into())
146 } else {
147 1
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::{Tag, TagClass, CONSTRUCTED};
156
157 #[test]
158 fn test_constructed() {
159 for i in 0..31 {
160 let tag = Tag::constructed(u32::from(i));
161 assert_eq!(tag.as_u8(), Some(CONSTRUCTED | i));
162 assert!(tag.is_constructed());
163 }
164 }
165
166 #[test]
167 fn test_as_u8() {
168 for (t, expected) in &[
169 (Tag::new(5, TagClass::Application, true), Some(0x65)),
170 (Tag::new(5, TagClass::Universal, false), Some(0x05)),
171 (Tag::new(0x1f, TagClass::Universal, false), None),
172 ] {
173 assert_eq!(&t.as_u8(), expected);
174 }
175 }
176
177 #[test]
178 fn test_class() {
179 assert_eq!(
180 Tag::new(5, TagClass::Application, true).class(),
181 TagClass::Application
182 );
183 }
184
185 #[test]
186 fn test_value() {
187 assert_eq!(Tag::new(5, TagClass::Application, true).value(), 5);
188 }
189
190 #[test]
191 fn test_write_to() {
192 for (t, expected) in [
193 (Tag::new(5, TagClass::Application, true), b"\x65" as &[u8]),
194 (Tag::new(5, TagClass::Universal, false), b"\x05"),
195 (Tag::new(0x1f, TagClass::Universal, false), b"\x1f\x1f"),
196 ] {
197 let data = vec![];
198 let mut buf = super::WriteBuf::new(data);
199 t.write_to(&mut buf).unwrap();
200 assert_eq!(buf.as_slice(), expected);
201 }
202 }
203
204 #[test]
205 fn test_write_to_roundtrip() {
206 for tag in [
207 Tag::new(5, TagClass::Application, true),
208 Tag::new(5, TagClass::Universal, false),
209 Tag::new(0x1f, TagClass::Universal, false),
210 Tag::new(0, TagClass::ContextSpecific, true),
211 Tag::new(127, TagClass::Private, false),
212 ] {
213 let data = vec![];
214 let mut buf = super::WriteBuf::new(data);
215 tag.write_to(&mut buf).unwrap();
216 let (parsed, rest) = Tag::from_bytes(buf.as_slice()).unwrap();
217 assert_eq!(parsed, tag);
218 assert!(rest.is_empty());
219 }
220 }
221}