From 6264f098e34cd232d19dd1cd409eb3064f230f61 Mon Sep 17 00:00:00 2001 From: dishmaker <141624503+dishmaker@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:01:54 +0200 Subject: [PATCH] der: add `AnyCow` --- der/src/asn1/any.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/der/src/asn1/any.rs b/der/src/asn1/any.rs index a522cfe99..eb4d7ea4c 100644 --- a/der/src/asn1/any.rs +++ b/der/src/asn1/any.rs @@ -170,6 +170,151 @@ impl<'a> TryFrom<&'a [u8]> for AnyRef<'a> { } } +/// ASN.1 `ANY`: represents any explicitly tagged ASN.1 value. +/// +/// This type provides the same functionality as [`AnyRef`] but may own the +/// backing data. +/// +/// [`AnyCow`] provides similar functionality to `Cow` type from standard library. +pub enum AnyCow<'a> { + Borrowed(AnyRef<'a>), + + #[cfg(feature = "alloc")] + Owned(Any), +} + +impl<'a> AnyCow<'a> { + /// Create a new [`AnyCow`] from the provided [`Tag`] and DER bytes. + pub const fn new(tag: Tag, bytes: &'a [u8]) -> Result { + match AnyRef::new(tag, bytes) { + Ok(value) => Ok(Self::Borrowed(value)), + Err(err) => Err(err), + } + } + + pub(crate) fn bytes_ref(&self) -> &BytesRef { + match self { + AnyCow::Borrowed(any_ref) => any_ref.value, + + #[cfg(feature = "alloc")] + AnyCow::Owned(any) => any.bytes_ref(), + } + } + + /// Get the raw value for this [`AnyCow`] type as a byte slice. + pub fn value(&'a self) -> &'a [u8] { + self.bytes_ref().as_slice() + } + + /// Returns [`Tag`] and [`Length`] of self. + pub fn header(&self) -> Header { + match self { + AnyCow::Borrowed(any_ref) => any_ref.header(), + + #[cfg(feature = "alloc")] + AnyCow::Owned(any) => any.header(), + } + } + + /// Attempt to decode this [`AnyRef`] type into the inner value. + pub fn decode_as_encoding( + &'a self, + encoding: EncodingRules, + ) -> Result>::Error> + where + T: Choice<'a> + DecodeValue<'a>, + { + match self { + AnyCow::Borrowed(any_ref) => any_ref.decode_as_encoding(encoding), + + #[cfg(feature = "alloc")] + AnyCow::Owned(any) => any.decode_as_encoding(encoding), + } + } + + /// Attempt to make this [`AnyCow`] allocated. + #[cfg(feature = "alloc")] + pub fn into_owned(self) -> AnyCow<'static> { + use crate::referenced::RefToOwned; + match self { + AnyCow::Borrowed(any_ref) => AnyCow::Owned(any_ref.ref_to_owned()), + AnyCow::Owned(any) => AnyCow::Owned(any), + } + } +} + +impl<'a> Choice<'a> for AnyCow<'a> { + fn can_decode(_: Tag) -> bool { + true + } +} + +impl<'a> Decode<'a> for AnyCow<'a> { + type Error = Error; + + fn decode>(reader: &mut R) -> Result, Error> { + let header = Header::decode(reader)?; + Self::decode_value(reader, header) + } +} + +impl<'a> DecodeValue<'a> for AnyCow<'a> { + type Error = Error; + + fn decode_value>(reader: &mut R, header: Header) -> Result { + Ok(Self::Borrowed(AnyRef::decode_value(reader, header)?)) + } +} + +impl EncodeValue for AnyCow<'_> { + fn value_len(&self) -> Result { + match self { + AnyCow::Borrowed(any_ref) => any_ref.value_len(), + + #[cfg(feature = "alloc")] + AnyCow::Owned(any) => any.value_len(), + } + } + + fn encode_value(&self, writer: &mut impl Writer) -> Result<(), Error> { + match self { + AnyCow::Borrowed(any_ref) => any_ref.encode_value(writer), + + #[cfg(feature = "alloc")] + AnyCow::Owned(any) => any.encode_value(writer), + } + } +} + +impl Tagged for AnyCow<'_> { + fn tag(&self) -> Tag { + match self { + AnyCow::Borrowed(any_ref) => any_ref.tag(), + + #[cfg(feature = "alloc")] + AnyCow::Owned(any) => any.tag(), + } + } +} + +impl ValueOrd for AnyCow<'_> { + fn value_cmp(&self, other: &Self) -> Result { + self.bytes_ref().der_cmp(other.bytes_ref()) + } +} + +impl<'a> From<&'a AnyCow<'a>> for &'a BytesRef { + fn from(any: &'a AnyCow<'a>) -> &'a BytesRef { + any.bytes_ref() + } +} + +impl<'a> From> for AnyCow<'a> { + fn from(any_ref: AnyRef<'a>) -> Self { + Self::Borrowed(any_ref) + } +} + #[cfg(feature = "alloc")] pub use self::allocating::Any; @@ -200,6 +345,10 @@ mod allocating { Ok(Self { tag, value }) } + pub(crate) fn bytes_ref(&self) -> &BytesRef { + &self.value + } + /// Allow access to value pub fn value(&self) -> &[u8] { self.value.as_slice() @@ -324,6 +473,12 @@ mod allocating { } } + impl<'a> From for AnyCow<'a> { + fn from(any: Any) -> Self { + Self::Owned(any) + } + } + impl<'a, T> From for Any where T: Into>,