rasn/types/strings/
octet.rs

1use crate::prelude::*;
2
3use alloc::vec::Vec;
4
5#[cfg(all(feature = "arc-slice", feature = "bytes"))]
6compile_error!("features `arc-slice` and `bytes` conflict, choose one");
7
8#[cfg(all(not(feature = "arc-slice"), feature = "bytes"))]
9type BytesImpl = bytes::Bytes;
10
11#[cfg(all(feature = "arc-slice", not(feature = "bytes")))]
12type BytesImpl = arc_slice::ArcBytes<arc_slice::layout::ArcLayout<true, true>>;
13
14/// The `OCTET STRING` type.
15///
16/// This type represents a contiguous sequence of bytes. It is the ASN.1
17/// equivalent to `Vec<u8>`. Rasn uses a specialised type to allow for `Vec<T>`
18/// to represent `SEQUENCE OF`, and to provide optimisations specific to bytes.
19///
20/// This type should be considered cheaply clonable.
21///
22/// ```
23/// use rasn::types::OctetString;
24///
25/// let os = OctetString::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
26///
27/// for byte in &*os {
28///    println!("{byte:0x}");
29/// }
30/// ```
31/// ### Feature Flags
32/// You can enable an alternative container implementation `arc-slice`, this
33/// should provide roughly 7–10% performance increase, but is newer and less
34/// well tested than the default `bytes` implementation.
35#[derive(Debug, Default, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
36pub struct OctetString(BytesImpl);
37
38impl OctetString {
39    /// Creates a new [OctetString] from a static slice.
40    ///
41    /// The returned [OctetString] will point directly to the static slice.
42    /// There is no allocating or copying.
43    pub const fn from_static(value: &'static [u8]) -> Self {
44        Self(BytesImpl::from_static(value))
45    }
46
47    /// Creates a new [OctetString] from a slice by copying it.
48    pub fn from_slice(value: &[u8]) -> Self {
49        Self::from(value)
50    }
51}
52
53impl AsRef<[u8]> for OctetString {
54    fn as_ref(&self) -> &[u8] {
55        &self.0
56    }
57}
58
59impl<const N: usize> From<[u8; N]> for OctetString {
60    fn from(value: [u8; N]) -> Self {
61        cfg_if::cfg_if! {
62            if #[cfg(feature = "arc-slice")] {
63                Self(value.into())
64            } else {
65                Self(bytes::Bytes::copy_from_slice(&value))
66            }
67        }
68    }
69}
70
71impl From<Vec<u8>> for OctetString {
72    fn from(value: Vec<u8>) -> Self {
73        Self(value.into())
74    }
75}
76
77impl From<&[u8]> for OctetString {
78    fn from(value: &[u8]) -> Self {
79        cfg_if::cfg_if! {
80            if #[cfg(feature = "arc-slice")] {
81                Self(value.into())
82            } else {
83                Self(bytes::Bytes::copy_from_slice(value))
84            }
85        }
86    }
87}
88
89impl core::ops::Deref for OctetString {
90    type Target = [u8];
91
92    fn deref(&self) -> &Self::Target {
93        &self.0
94    }
95}
96
97impl PartialEq<[u8]> for OctetString {
98    fn eq(&self, value: &[u8]) -> bool {
99        self.0 == value
100    }
101}
102
103impl PartialEq<&[u8]> for OctetString {
104    fn eq(&self, value: &&[u8]) -> bool {
105        self.0 == *value
106    }
107}
108
109impl PartialEq<Vec<u8>> for OctetString {
110    fn eq(&self, value: &Vec<u8>) -> bool {
111        self.0 == *value
112    }
113}
114
115/// An `OCTET STRING` which has a fixed size range. This type uses const
116/// generics to be able to place the octet string on the stack rather than the
117/// heap.
118#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
119pub struct FixedOctetString<const N: usize>([u8; N]);
120
121impl<const N: usize> FixedOctetString<N> {
122    /// Creates a new octet string from a given array.
123    #[must_use]
124    pub fn new(value: [u8; N]) -> Self {
125        Self(value)
126    }
127}
128
129impl<const N: usize> From<[u8; N]> for FixedOctetString<N> {
130    fn from(value: [u8; N]) -> Self {
131        Self::new(value)
132    }
133}
134
135impl<const N: usize> TryFrom<Vec<u8>> for FixedOctetString<N> {
136    type Error = <[u8; N] as TryFrom<Vec<u8>>>::Error;
137
138    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
139        value.try_into().map(Self)
140    }
141}
142
143impl<const N: usize> TryFrom<&[u8]> for FixedOctetString<N> {
144    type Error = <[u8; N] as TryFrom<&'static [u8]>>::Error;
145
146    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
147        value.try_into().map(Self)
148    }
149}
150
151impl<const N: usize> TryFrom<OctetString> for FixedOctetString<N> {
152    type Error = <[u8; N] as TryFrom<&'static [u8]>>::Error;
153
154    fn try_from(value: OctetString) -> Result<Self, Self::Error> {
155        (&*value).try_into().map(Self)
156    }
157}
158
159impl<const N: usize> core::ops::Deref for FixedOctetString<N> {
160    type Target = [u8; N];
161
162    fn deref(&self) -> &Self::Target {
163        &self.0
164    }
165}
166
167impl<const N: usize> core::ops::DerefMut for FixedOctetString<N> {
168    fn deref_mut(&mut self) -> &mut Self::Target {
169        &mut self.0
170    }
171}
172
173impl<const N: usize> AsnType for FixedOctetString<N> {
174    const TAG: Tag = Tag::OCTET_STRING;
175    const CONSTRAINTS: Constraints = constraints!(size_constraint!(N));
176    const IDENTIFIER: Identifier = Identifier::OCTET_STRING;
177}
178
179impl<const N: usize> Decode for FixedOctetString<N> {
180    fn decode_with_tag_and_constraints<D: Decoder>(
181        decoder: &mut D,
182        tag: Tag,
183        constraints: Constraints,
184    ) -> Result<Self, D::Error> {
185        let codec = decoder.codec();
186        let bytes = decoder.decode_octet_string::<alloc::borrow::Cow<[u8]>>(tag, constraints)?;
187        let len = bytes.len();
188        match bytes {
189            alloc::borrow::Cow::Borrowed(slice) => Self::try_from(slice).map_err(|_| {
190                D::Error::from(crate::error::DecodeError::fixed_string_conversion_failed(
191                    tag,
192                    bytes.len(),
193                    N,
194                    codec,
195                ))
196            }),
197            alloc::borrow::Cow::Owned(vec) => Self::try_from(vec).map_err(|_| {
198                D::Error::from(crate::error::DecodeError::fixed_string_conversion_failed(
199                    tag, len, N, codec,
200                ))
201            }),
202        }
203    }
204}
205
206impl<const N: usize> Encode for FixedOctetString<N> {
207    fn encode_with_tag_and_constraints<'b, E: Encoder<'b>>(
208        &self,
209        encoder: &mut E,
210        tag: Tag,
211        constraints: Constraints,
212        identifier: Identifier,
213    ) -> Result<(), E::Error> {
214        encoder
215            .encode_octet_string(tag, constraints, &self.0, identifier)
216            .map(drop)
217    }
218}