diff options
Diffstat (limited to 'vendor/ciborium-ll/src/hdr.rs')
-rw-r--r-- | vendor/ciborium-ll/src/hdr.rs | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/vendor/ciborium-ll/src/hdr.rs b/vendor/ciborium-ll/src/hdr.rs new file mode 100644 index 000000000..dec178815 --- /dev/null +++ b/vendor/ciborium-ll/src/hdr.rs @@ -0,0 +1,163 @@ +use super::*; + +use half::f16; + +/// A semantic representation of a CBOR item header +/// +/// This structure represents the valid values of a CBOR item header and is +/// used extensively when serializing or deserializing CBOR items. Note well +/// that this structure **DOES NOT** represent the body (i.e. suffix) of the +/// CBOR item. You must parse the body yourself based on the contents of the +/// `Header`. However, utility functions are provided for this (see: +/// `Decoder::bytes()` and `Decoder::text()`). +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Header { + /// A positive integer + Positive(u64), + + /// A negative integer + /// + /// Note well that this value has all bits inverted from a normal signed + /// integer. For example, to convert the `u64` to a `i128` you would do + /// this: `neg as i128 ^ !0`. + Negative(u64), + + /// A floating point value + Float(f64), + + /// A "simple" value + Simple(u8), + + /// A tag + Tag(u64), + + /// The "break" value + /// + /// This value is used to terminate indefinite length arrays and maps, + /// as well as segmented byte or text items. + Break, + + /// A bytes item + /// + /// The value contained in this variant indicates the length of the bytes + /// which follow or, if `None`, segmented bytes input. + /// + /// A best practice is to call `Decoder::bytes()` immediately after + /// first pulling a bytes item header since this utility function + /// encapsulates all the logic needed to handle segmentation. + Bytes(Option<usize>), + + /// A text item + /// + /// The value contained in this variant indicates the length of the text + /// which follows (in bytes) or, if `None`, segmented text input. + /// + /// A best practice is to call `Decoder::text()` immediately after + /// first pulling a text item header since this utility function + /// encapsulates all the logic needed to handle segmentation. + Text(Option<usize>), + + /// An array item + /// + /// The value contained in this variant indicates the length of the array + /// which follows (in items) or, if `None`, an indefinite length array + /// terminated by a "break" value. + Array(Option<usize>), + + /// An map item + /// + /// The value contained in this variant indicates the length of the map + /// which follows (in item pairs) or, if `None`, an indefinite length map + /// terminated by a "break" value. + Map(Option<usize>), +} + +impl TryFrom<Title> for Header { + type Error = InvalidError; + + fn try_from(title: Title) -> Result<Self, Self::Error> { + let opt = |minor| { + Some(match minor { + Minor::This(x) => x.into(), + Minor::Next1(x) => u8::from_be_bytes(x).into(), + Minor::Next2(x) => u16::from_be_bytes(x).into(), + Minor::Next4(x) => u32::from_be_bytes(x).into(), + Minor::Next8(x) => u64::from_be_bytes(x), + Minor::More => return None, + }) + }; + + let int = |m| opt(m).ok_or(InvalidError(())); + + let len = |m| { + opt(m) + .map(usize::try_from) + .transpose() + .or(Err(InvalidError(()))) + }; + + Ok(match title { + Title(Major::Positive, minor) => Self::Positive(int(minor)?), + Title(Major::Negative, minor) => Self::Negative(int(minor)?), + Title(Major::Bytes, minor) => Self::Bytes(len(minor)?), + Title(Major::Text, minor) => Self::Text(len(minor)?), + Title(Major::Array, minor) => Self::Array(len(minor)?), + Title(Major::Map, minor) => Self::Map(len(minor)?), + Title(Major::Tag, minor) => Self::Tag(int(minor)?), + + Title(Major::Other, Minor::More) => Self::Break, + Title(Major::Other, Minor::This(x)) => Self::Simple(x), + Title(Major::Other, Minor::Next1(x)) => Self::Simple(x[0]), + Title(Major::Other, Minor::Next2(x)) => Self::Float(f16::from_be_bytes(x).into()), + Title(Major::Other, Minor::Next4(x)) => Self::Float(f32::from_be_bytes(x).into()), + Title(Major::Other, Minor::Next8(x)) => Self::Float(f64::from_be_bytes(x)), + }) + } +} + +impl From<Header> for Title { + fn from(header: Header) -> Self { + let int = |i: u64| match i { + x if x <= 23 => Minor::This(i as u8), + x if x <= core::u8::MAX as u64 => Minor::Next1([i as u8]), + x if x <= core::u16::MAX as u64 => Minor::Next2((i as u16).to_be_bytes()), + x if x <= core::u32::MAX as u64 => Minor::Next4((i as u32).to_be_bytes()), + x => Minor::Next8(x.to_be_bytes()), + }; + + let len = |l: Option<usize>| l.map(|x| int(x as u64)).unwrap_or(Minor::More); + + match header { + Header::Positive(x) => Title(Major::Positive, int(x)), + Header::Negative(x) => Title(Major::Negative, int(x)), + Header::Bytes(x) => Title(Major::Bytes, len(x)), + Header::Text(x) => Title(Major::Text, len(x)), + Header::Array(x) => Title(Major::Array, len(x)), + Header::Map(x) => Title(Major::Map, len(x)), + Header::Tag(x) => Title(Major::Tag, int(x)), + + Header::Break => Title(Major::Other, Minor::More), + + Header::Simple(x) => match x { + x @ 0..=23 => Title(Major::Other, Minor::This(x)), + x => Title(Major::Other, Minor::Next1([x])), + }, + + Header::Float(n64) => { + let n16 = f16::from_f64(n64); + let n32 = n64 as f32; + + Title( + Major::Other, + if f64::from(n16).to_bits() == n64.to_bits() { + Minor::Next2(n16.to_be_bytes()) + } else if f64::from(n32).to_bits() == n64.to_bits() { + Minor::Next4(n32.to_be_bytes()) + } else { + Minor::Next8(n64.to_be_bytes()) + }, + ) + } + } + } +} |