ciborium_ll/hdr.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 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())
},
)
}
}
}
}