use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt;
use core::ops::Deref;
#[derive(Clone, Default)]
pub struct Octets<'a>(Cow<'a, [u8]>);
impl<'a> Octets<'a> {
pub const EMPTY: Self = Self(Cow::Borrowed(&[]));
pub const fn borrowed(octets: &'a [u8]) -> Self {
Self(Cow::Borrowed(octets))
}
pub const fn owned(octets: Vec<u8>) -> Octets<'static> {
Octets(Cow::Owned(octets))
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn is_borrowed(&self) -> bool {
matches!(self.0, Cow::Borrowed(_))
}
pub fn as_borrowed(&self) -> Option<&'a [u8]> {
match self.0 {
Cow::Borrowed(octets) => Some(octets),
Cow::Owned(_) => None,
}
}
#[doc(alias("to_owned", "owned", "keep"))]
pub fn into_owned(self) -> Octets<'static> {
Octets(Cow::Owned(self.0.into_owned()))
}
pub fn into_vec(self) -> Vec<u8> {
self.0.into_owned()
}
pub fn to_mut(&mut self) -> &mut Vec<u8> {
self.0.to_mut()
}
}
impl Deref for Octets<'_> {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for Octets<'_> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl<'a> From<&'a [u8]> for Octets<'a> {
fn from(octets: &'a [u8]) -> Self {
Self(Cow::Borrowed(octets))
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for Octets<'a> {
fn from(octets: &'a [u8; N]) -> Self {
Self(Cow::Borrowed(octets))
}
}
impl<'a> From<&'a Vec<u8>> for Octets<'a> {
fn from(octets: &'a Vec<u8>) -> Self {
Self(Cow::Borrowed(octets))
}
}
impl From<Vec<u8>> for Octets<'_> {
fn from(octets: Vec<u8>) -> Self {
Self(Cow::Owned(octets))
}
}
impl<const N: usize> From<[u8; N]> for Octets<'_> {
fn from(octets: [u8; N]) -> Self {
Self(Cow::Owned(octets.to_vec()))
}
}
impl<'a> From<Cow<'a, [u8]>> for Octets<'a> {
fn from(octets: Cow<'a, [u8]>) -> Self {
Self(octets)
}
}
impl<'a> From<Octets<'a>> for Vec<u8> {
fn from(octets: Octets<'a>) -> Self {
octets.into_vec()
}
}
impl PartialEq for Octets<'_> {
fn eq(&self, other: &Self) -> bool {
self.as_slice() == other.as_slice()
}
}
impl Eq for Octets<'_> {}
impl PartialOrd for Octets<'_> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Octets<'_> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl core::hash::Hash for Octets<'_> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_slice().hash(state);
}
}
impl PartialEq<[u8]> for Octets<'_> {
fn eq(&self, other: &[u8]) -> bool {
self.as_slice() == other
}
}
impl PartialEq<&[u8]> for Octets<'_> {
fn eq(&self, other: &&[u8]) -> bool {
self.as_slice() == *other
}
}
impl PartialEq<Vec<u8>> for Octets<'_> {
fn eq(&self, other: &Vec<u8>) -> bool {
self.as_slice() == other.as_slice()
}
}
impl<const N: usize> PartialEq<[u8; N]> for Octets<'_> {
fn eq(&self, other: &[u8; N]) -> bool {
self.as_slice() == other
}
}
impl PartialEq<Octets<'_>> for [u8] {
fn eq(&self, other: &Octets<'_>) -> bool {
self == other.as_slice()
}
}
impl PartialEq<Octets<'_>> for Vec<u8> {
fn eq(&self, other: &Octets<'_>) -> bool {
self.as_slice() == other.as_slice()
}
}
impl crate::AsnType for Octets<'_> {
const NAME: &'static str = "OCTET STRING";
}
impl<'a> crate::Decode<'a> for Octets<'a> {
fn decode(reader: &mut crate::Reader<'a>) -> Result<Self, crate::Error> {
reader.octet_string(0, crate::UNBOUNDED)
}
}
impl crate::Encode for Octets<'_> {
fn encode(&self, writer: &mut crate::Writer<'_>) -> Result<(), crate::Error> {
writer.octet_string(self, 0, crate::UNBOUNDED)
}
}
#[cfg(feature = "inspect")]
impl crate::json::Tree for Octets<'_> {
const NAME: &'static str = "OCTET STRING";
fn to_tree(&self) -> crate::json::Value {
crate::json::octets(self)
}
fn from_tree(
value: &crate::json::Value,
reading: &mut crate::json::Reading,
) -> Result<Self, crate::json::Refused> {
crate::json::read_octets(reading, value)
}
}
impl fmt::Debug for Octets<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write_hex(self.as_slice(), formatter)
}
}
pub(crate) fn write_hex(octets: &[u8], formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for octet in octets {
write!(formatter, "{octet:02x}")?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
use alloc::vec;
#[test]
fn octets_are_borrowed_or_owned_and_compare_by_what_they_hold() {
let received = vec![1u8, 2, 3];
let borrowed = Octets::from(&received);
assert!(borrowed.is_borrowed());
let owned = Octets::from(vec![1u8, 2, 3]);
assert!(!owned.is_borrowed());
assert_eq!(borrowed, owned);
assert_eq!(borrowed, [1, 2, 3]);
assert_eq!(borrowed, vec![1, 2, 3]);
assert_eq!(borrowed, &[1u8, 2, 3][..]);
assert_eq!(received, borrowed);
let (less, more) = (Octets::from([1u8, 2]), Octets::from([1u8, 3]));
assert!(less < more);
assert_eq!(Octets::EMPTY.len(), 0);
assert_eq!(Octets::default(), Octets::EMPTY);
let kept: Octets<'static> = borrowed.clone().into_owned();
assert!(!kept.is_borrowed());
assert_eq!(kept.clone().into_vec(), [1, 2, 3]);
assert_eq!(Vec::from(kept), [1, 2, 3]);
let mut changed = borrowed;
changed.to_mut().push(4);
assert_eq!(changed, [1, 2, 3, 4]);
assert_eq!(format!("{changed:?}"), "01020304");
}
}