der/asn1/
bmp_string.rs
1use crate::{
4 BytesOwned, DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag,
5 Writer,
6};
7use alloc::{boxed::Box, vec::Vec};
8use core::{fmt, str::FromStr};
9
10#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
15pub struct BmpString {
16 bytes: BytesOwned,
17}
18
19impl BmpString {
20 pub fn from_ucs2(bytes: impl Into<Box<[u8]>>) -> Result<Self> {
22 let bytes = bytes.into();
23
24 if bytes.len() % 2 != 0 {
25 return Err(Tag::BmpString.length_error());
26 }
27
28 let ret = Self {
29 bytes: bytes.try_into()?,
30 };
31
32 for maybe_char in char::decode_utf16(ret.codepoints()) {
33 match maybe_char {
34 Ok(c) if (c as u64) < u64::from(u16::MAX) => (),
36 _ => return Err(Tag::BmpString.value_error()),
38 }
39 }
40
41 Ok(ret)
42 }
43
44 pub fn from_utf8(utf8: &str) -> Result<Self> {
46 let capacity = utf8
47 .len()
48 .checked_mul(2)
49 .ok_or_else(|| Tag::BmpString.length_error())?;
50
51 let mut bytes = Vec::with_capacity(capacity);
52
53 for code_point in utf8.encode_utf16() {
54 bytes.extend(code_point.to_be_bytes());
55 }
56
57 Self::from_ucs2(bytes)
58 }
59
60 pub fn as_bytes(&self) -> &[u8] {
62 self.bytes.as_ref()
63 }
64
65 #[inline]
67 pub fn into_bytes(self) -> Box<[u8]> {
68 self.bytes.into()
69 }
70
71 pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
73 char::decode_utf16(self.codepoints())
74 .map(|maybe_char| maybe_char.expect("unpaired surrogates checked in constructor"))
75 }
76
77 pub fn codepoints(&self) -> impl Iterator<Item = u16> + '_ {
79 self.as_bytes()
81 .chunks_exact(2)
82 .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
83 }
84}
85
86impl AsRef<[u8]> for BmpString {
87 fn as_ref(&self) -> &[u8] {
88 self.as_bytes()
89 }
90}
91
92impl<'a> DecodeValue<'a> for BmpString {
93 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
94 Self::from_ucs2(reader.read_vec(header.length)?)
95 }
96}
97
98impl EncodeValue for BmpString {
99 fn value_len(&self) -> Result<Length> {
100 Ok(self.bytes.len())
101 }
102
103 fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
104 writer.write(self.as_bytes())
105 }
106}
107
108impl FixedTag for BmpString {
109 const TAG: Tag = Tag::BmpString;
110}
111
112impl FromStr for BmpString {
113 type Err = Error;
114
115 fn from_str(s: &str) -> Result<Self> {
116 Self::from_utf8(s)
117 }
118}
119
120impl fmt::Debug for BmpString {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(f, "BmpString(\"{}\")", self)
123 }
124}
125
126impl fmt::Display for BmpString {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 for c in self.chars() {
129 write!(f, "{}", c)?;
130 }
131 Ok(())
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::BmpString;
138 use crate::{Decode, Encode};
139 use alloc::string::ToString;
140 use hex_literal::hex;
141
142 const EXAMPLE_BYTES: &[u8] = &hex!(
143 "1e 26 00 43 00 65 00 72 00 74"
144 " 00 69 00 66 00 69 00 63"
145 " 00 61 00 74 00 65 00 54"
146 " 00 65 00 6d 00 70 00 6c"
147 " 00 61 00 74 00 65"
148 );
149
150 const EXAMPLE_UTF8: &str = "CertificateTemplate";
151
152 #[test]
153 fn decode() {
154 let bmp_string = BmpString::from_der(EXAMPLE_BYTES).unwrap();
155 assert_eq!(bmp_string.to_string(), EXAMPLE_UTF8);
156 }
157
158 #[test]
159 fn encode() {
160 let bmp_string = BmpString::from_utf8(EXAMPLE_UTF8).unwrap();
161 let encoded = bmp_string.to_der().unwrap();
162 assert_eq!(encoded, EXAMPLE_BYTES);
163 }
164}