mod array;
mod bytes;
mod default_eq_ord_hash;
mod float;
mod index;
mod int;
mod map;
mod simple_value;
mod string;
use std::{
cmp,
collections::BTreeMap,
fmt,
hash::{Hash, Hasher},
io,
ops::{Index, IndexMut},
time::{Duration, SystemTime},
};
use crate::{
ArgLength, Array, CtrlByte, DataType, DateTime, EpochTime, Error, Float, IntegerBytes, Major, Map, Result,
SimpleValue, Tag, util::u128_from_slice,
};
const OOM_MITIGATION: usize = 100_000_000; const LENGTH_LIMIT: u64 = 1_000_000_000; const RECURSION_LIMIT: u16 = 200;
#[derive(Clone)]
pub enum Value {
SimpleValue(SimpleValue),
Unsigned(u64),
Negative(u64),
Float(Float),
ByteString(Vec<u8>),
TextString(String),
Array(Vec<Value>),
Map(BTreeMap<Value, Value>),
Tag(u64, Box<Value>),
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SimpleValue(sv) => match *sv {
SimpleValue::FALSE => f.write_str("false"),
SimpleValue::TRUE => f.write_str("true"),
SimpleValue::NULL => f.write_str("null"),
other => write!(f, "simple({})", other.0),
},
Self::Unsigned(n) => write!(f, "{n}"),
Self::Negative(n) => write!(f, "{actual}", actual = -i128::from(*n) - 1),
Self::Float(float) => {
let value = float.to_f64();
if value.is_nan() {
use crate::float::Inner;
match float.0 {
Inner::F16(0x7e00) => f.write_str("NaN"), Inner::F16(bits) => write!(f, "float'{bits:04x}'"),
Inner::F32(bits) => write!(f, "float'{bits:08x}'"),
Inner::F64(bits) => write!(f, "float'{bits:016x}'"),
}
} else if value.is_infinite() {
if value.is_sign_positive() {
f.write_str("Infinity")
} else {
f.write_str("-Infinity")
}
} else {
let s = format!("{value}");
f.write_str(&s)?;
if !s.contains('.') && !s.contains('e') && !s.contains('E') {
f.write_str(".0")?; }
Ok(())
}
}
Self::ByteString(bytes) => {
f.write_str("h'")?;
for b in bytes {
write!(f, "{b:02x}")?;
}
f.write_str("'")
}
Self::TextString(s) => {
f.write_str("\"")?;
for c in s.chars() {
match c {
'"' => f.write_str("\\\"")?,
'\\' => f.write_str("\\\\")?,
'\u{08}' => f.write_str("\\b")?,
'\u{0C}' => f.write_str("\\f")?,
'\n' => f.write_str("\\n")?,
'\r' => f.write_str("\\r")?,
'\t' => f.write_str("\\t")?,
c if c.is_control() => write!(f, "\\u{:04x}", c as u32)?,
c => write!(f, "{c}")?,
}
}
f.write_str("\"")
}
Self::Array(items) => {
let mut list = f.debug_list();
for item in items {
list.entry(item);
}
list.finish()
}
Self::Map(map) => {
let mut m = f.debug_map();
for (key, value) in map {
m.entry(key, value);
}
m.finish()
}
Self::Tag(tag, content) => {
if self.data_type().is_integer() {
if let Ok(n) = self.to_u128() {
return write!(f, "{n}");
}
if let Ok(n) = self.to_i128() {
return write!(f, "{n}");
}
}
if f.alternate() {
write!(f, "{tag}({content:#?})")
} else {
write!(f, "{tag}({content:?})")
}
}
}
}
}
impl Value {
pub fn take(&mut self) -> Self {
std::mem::take(self)
}
pub fn replace(&mut self, value: Self) -> Self {
std::mem::replace(self, value)
}
#[must_use]
pub fn encode(&self) -> Vec<u8> {
let len = self.cbor_len();
let mut bytes = Vec::with_capacity(len);
self.write_to(&mut bytes).unwrap();
debug_assert_eq!(bytes.len(), len);
bytes
}
#[must_use]
pub fn encode_hex(&self) -> String {
let len2 = self.cbor_len() * 2;
let mut hex = Vec::with_capacity(len2);
self.write_hex_to(&mut hex).unwrap();
debug_assert_eq!(hex.len(), len2);
String::from_utf8(hex).unwrap()
}
pub fn decode(bytes: impl AsRef<[u8]>) -> crate::Result<Self> {
let mut bytes = bytes.as_ref();
Self::read_from(&mut bytes).map_err(|error| match error {
crate::IoError::Io(_io_error) => unreachable!(),
crate::IoError::Data(error) => error,
})
}
pub fn decode_hex(hex: impl AsRef<[u8]>) -> Result<Self> {
let mut bytes = hex.as_ref();
Self::read_hex_from(&mut bytes).map_err(|error| match error {
crate::IoError::Io(_io_error) => unreachable!(),
crate::IoError::Data(error) => error,
})
}
pub fn read_from(mut reader: impl io::Read) -> crate::IoResult<Self> {
Self::read_from_inner(&mut reader, RECURSION_LIMIT, OOM_MITIGATION)
}
fn read_from_inner(
reader: &mut impl io::Read,
recursion_limit: u16,
oom_mitigation: usize,
) -> crate::IoResult<Self> {
let ctrl_byte = {
let mut buf = [0];
reader.read_exact(&mut buf)?;
buf[0]
};
let is_float = matches!(ctrl_byte, CtrlByte::F16 | CtrlByte::F32 | CtrlByte::F64);
let major = ctrl_byte >> 5;
let info = ctrl_byte & 0x1f;
let argument = {
let mut buf = [0; 8];
if info < ArgLength::U8 {
buf[7] = info;
} else {
match info {
ArgLength::U8 => reader.read_exact(&mut buf[7..])?,
ArgLength::U16 => reader.read_exact(&mut buf[6..])?,
ArgLength::U32 => reader.read_exact(&mut buf[4..])?,
ArgLength::U64 => reader.read_exact(&mut buf)?,
_ => return Error::Malformed.into(),
}
}
u64::from_be_bytes(buf)
};
if !is_float {
let non_deterministic = match info {
ArgLength::U8 => argument < u64::from(ArgLength::U8),
ArgLength::U16 => argument <= u64::from(u8::MAX),
ArgLength::U32 => argument <= u64::from(u16::MAX),
ArgLength::U64 => argument <= u64::from(u32::MAX),
_ => false,
};
if non_deterministic {
return Error::NonDeterministic.into();
}
}
let this = match major {
Major::UNSIGNED => Self::Unsigned(argument),
Major::NEGATIVE => Self::Negative(argument),
Major::BYTE_STRING => Self::ByteString(read_vec(reader, argument)?),
Major::TEXT_STRING => {
let bytes = read_vec(reader, argument)?;
let string = String::from_utf8(bytes)?;
Self::TextString(string)
}
Major::ARRAY => {
if argument > LENGTH_LIMIT {
return Error::LengthTooLarge.into();
}
let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
return Error::LengthTooLarge.into();
};
let request: usize = argument.try_into().or(Err(Error::LengthTooLarge))?;
let granted = request.min(oom_mitigation / size_of::<Self>());
let oom_mitigation = oom_mitigation - granted * size_of::<Self>();
let mut vec = Vec::with_capacity(granted);
for _ in 0..argument {
vec.push(Self::read_from_inner(reader, recursion_limit, oom_mitigation)?);
}
Self::Array(vec)
}
Major::MAP => {
if argument > LENGTH_LIMIT {
return Error::LengthTooLarge.into();
}
let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
return Error::LengthTooLarge.into();
};
let mut map = BTreeMap::new();
let mut prev = None;
for _ in 0..argument {
let key = Self::read_from_inner(reader, recursion_limit, oom_mitigation)?;
let value = Self::read_from_inner(reader, recursion_limit, oom_mitigation)?;
if let Some((prev_key, prev_value)) = prev.take() {
if prev_key >= key {
return Error::NonDeterministic.into();
}
map.insert(prev_key, prev_value);
}
prev = Some((key, value));
}
if let Some((key, value)) = prev.take() {
map.insert(key, value);
}
Self::Map(map)
}
Major::TAG => {
let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
return Error::LengthTooLarge.into();
};
let content = Box::new(Self::read_from_inner(reader, recursion_limit, oom_mitigation)?);
if matches!(argument, Tag::POS_BIG_INT | Tag::NEG_BIG_INT)
&& let Ok(bigint) = content.as_bytes()
{
let valid = bigint.len() >= 8 && bigint[0] != 0;
if !valid {
return Error::NonDeterministic.into();
}
}
Self::Tag(argument, content)
}
Major::SIMPLE_VALUE => match info {
0..ArgLength::U8 => Self::SimpleValue(SimpleValue(argument as u8)),
ArgLength::U8 if argument >= 32 => Self::SimpleValue(SimpleValue(argument as u8)),
ArgLength::U16 => Self::Float(Float::from_u16(argument as u16)),
ArgLength::U32 => Self::Float(Float::from_u32(argument as u32)?),
ArgLength::U64 => Self::Float(Float::from_u64(argument)?),
_ => return Error::Malformed.into(),
},
_ => unreachable!(),
};
Ok(this)
}
pub fn read_hex_from(reader: impl io::Read) -> crate::IoResult<Self> {
struct HexReader<R>(R, Option<Error>);
impl<R: io::Read> io::Read for HexReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
fn nibble(char: u8) -> Option<u8> {
match char {
b'0'..=b'9' => Some(char - b'0'),
b'a'..=b'f' => Some(char - b'a' + 10),
b'A'..=b'F' => Some(char - b'A' + 10),
_ => None,
}
}
for byte in buf.iter_mut() {
let mut hex = [0; 2];
self.0.read_exact(&mut hex)?;
if let Some(n0) = nibble(hex[0])
&& let Some(n1) = nibble(hex[1])
{
*byte = n0 << 4 | n1
} else {
self.1 = Some(Error::InvalidHex);
return Err(io::Error::other("invalid hex character"));
}
}
Ok(buf.len())
}
}
let mut hex_reader = HexReader(reader, None);
let result = Self::read_from_inner(&mut hex_reader, RECURSION_LIMIT, OOM_MITIGATION);
if let Some(error) = hex_reader.1 {
error.into()
} else {
result
}
}
pub fn write_to(&self, mut writer: impl io::Write) -> crate::IoResult<()> {
self.write_to_inner(&mut writer)
}
fn write_to_inner(&self, writer: &mut impl io::Write) -> crate::IoResult<()> {
let major = self.cbor_major();
let (info, argument) = self.cbor_argument();
let ctrl_byte = major << 5 | info;
writer.write_all(&[ctrl_byte])?;
let buf = argument.to_be_bytes();
match info {
ArgLength::U8 => writer.write_all(&buf[7..])?,
ArgLength::U16 => writer.write_all(&buf[6..])?,
ArgLength::U32 => writer.write_all(&buf[4..])?,
ArgLength::U64 => writer.write_all(&buf)?,
_ => (), }
match self {
Value::ByteString(bytes) => writer.write_all(bytes)?,
Value::TextString(string) => writer.write_all(string.as_bytes())?,
Value::Tag(_number, content) => content.write_to_inner(writer)?,
Value::Array(values) => {
for value in values {
value.write_to_inner(writer)?;
}
}
Value::Map(map) => {
for (key, value) in map {
key.write_to_inner(writer)?;
value.write_to_inner(writer)?;
}
}
_ => (),
}
Ok(())
}
pub fn write_hex_to(&self, writer: impl io::Write) -> crate::IoResult<()> {
struct HexWriter<W>(W);
impl<W: io::Write> io::Write for HexWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
for &byte in buf {
write!(self.0, "{byte:02x}")?;
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
self.write_to_inner(&mut HexWriter(writer))
}
fn cbor_major(&self) -> u8 {
match self {
Value::Unsigned(_) => Major::UNSIGNED,
Value::Negative(_) => Major::NEGATIVE,
Value::ByteString(_) => Major::BYTE_STRING,
Value::TextString(_) => Major::TEXT_STRING,
Value::Array(_) => Major::ARRAY,
Value::Map(_) => Major::MAP,
Value::Tag(_, _) => Major::TAG,
Value::SimpleValue(_) => Major::SIMPLE_VALUE,
Value::Float(_) => Major::SIMPLE_VALUE,
}
}
fn cbor_argument(&self) -> (u8, u64) {
fn arg(value: u64) -> (u8, u64) {
if value < u64::from(ArgLength::U8) {
(value as u8, value)
} else {
let info = match value {
0x00..=0xFF => ArgLength::U8,
0x100..=0xFFFF => ArgLength::U16,
0x10000..=0xFFFF_FFFF => ArgLength::U32,
_ => ArgLength::U64,
};
(info, value)
}
}
match self {
Value::Unsigned(value) => arg(*value),
Value::Negative(value) => arg(*value),
Value::ByteString(vec) => arg(vec.len().try_into().unwrap()),
Value::TextString(str) => arg(str.len().try_into().unwrap()),
Value::Array(vec) => arg(vec.len().try_into().unwrap()),
Value::Map(map) => arg(map.len().try_into().unwrap()),
Value::Tag(number, _) => arg(*number),
Value::SimpleValue(value) => arg(value.0.into()),
Value::Float(float) => float.cbor_argument(),
}
}
fn cbor_len(&self) -> usize {
let (info, _) = self.cbor_argument();
let header_len = match info {
0..ArgLength::U8 => 1,
ArgLength::U8 => 2,
ArgLength::U16 => 3,
ArgLength::U32 => 5,
ArgLength::U64 => 9,
_ => unreachable!(),
};
let data_len = match self {
Self::ByteString(bytes) => bytes.len(),
Self::TextString(text) => text.len(),
Self::Array(vec) => vec.iter().map(Self::cbor_len).sum(),
Self::Map(map) => map.iter().map(|(k, v)| k.cbor_len() + v.cbor_len()).sum(),
Self::Tag(_, content) => content.cbor_len(),
_ => 0,
};
header_len + data_len
}
#[must_use]
pub const fn null() -> Self {
Self::SimpleValue(SimpleValue::NULL)
}
pub fn simple_value(value: impl TryInto<SimpleValue>) -> Self {
match value.try_into() {
Ok(sv) => Self::SimpleValue(sv),
Err(_) => panic!("Invalid simple value"),
}
}
pub fn date_time(value: impl TryInto<DateTime>) -> Self {
match value.try_into() {
Ok(dt) => dt.into(),
Err(_) => panic!("Invalid date/time"),
}
}
pub fn epoch_time(value: impl TryInto<EpochTime>) -> Self {
match value.try_into() {
Ok(et) => et.into(),
Err(_) => panic!("Invalid epoch time"),
}
}
pub fn float(value: impl Into<Float>) -> Self {
Self::Float(value.into())
}
pub fn array(array: impl Into<Array>) -> Self {
Self::Array(array.into().0)
}
pub fn map(map: impl Into<Map>) -> Self {
Self::Map(map.into().0)
}
pub fn tag(number: u64, content: impl Into<Value>) -> Self {
Self::Tag(number, Box::new(content.into()))
}
#[must_use]
pub const fn data_type(&self) -> DataType {
match self {
Self::SimpleValue(sv) => sv.data_type(),
Self::Unsigned(_) | Self::Negative(_) => DataType::Int,
Self::Float(float) => float.data_type(),
Self::TextString(_) => DataType::Text,
Self::ByteString(_) => DataType::Bytes,
Self::Array(_) => DataType::Array,
Self::Map(_) => DataType::Map,
Self::Tag(Tag::DATE_TIME, content) if content.data_type().is_text() => DataType::DateTime,
Self::Tag(Tag::EPOCH_TIME, content) if content.data_type().is_numeric() => DataType::EpochTime,
Self::Tag(Tag::POS_BIG_INT | Tag::NEG_BIG_INT, content) if content.data_type().is_bytes() => {
DataType::BigInt
}
Self::Tag(_, _) => DataType::Tag,
}
}
pub(crate) const fn is_bytes(&self) -> bool {
self.data_type().is_bytes()
}
pub const fn to_bool(&self) -> Result<bool> {
match self {
Self::SimpleValue(sv) => sv.to_bool(),
Self::Tag(_number, content) => content.untagged().to_bool(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn to_simple_value(&self) -> Result<u8> {
match self {
Self::SimpleValue(sv) => Ok(sv.0),
Self::Tag(_number, content) => content.untagged().to_simple_value(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
fn to_uint<T>(&self) -> Result<T>
where
T: TryFrom<u64> + TryFrom<u128>,
{
match self {
Self::Unsigned(x) => T::try_from(*x).or(Err(Error::Overflow)),
Self::Negative(_) => Err(Error::NegativeUnsigned),
Self::Tag(Tag::POS_BIG_INT, content) if content.is_bytes() => {
T::try_from(u128_from_slice(self.as_bytes()?)?).or(Err(Error::Overflow))
}
Self::Tag(Tag::NEG_BIG_INT, content) if content.is_bytes() => Err(Error::NegativeUnsigned),
Self::Tag(_other_number, content) => content.peeled().to_uint(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_u8(&self) -> Result<u8> {
self.to_uint()
}
pub fn to_u16(&self) -> Result<u16> {
self.to_uint()
}
pub fn to_u32(&self) -> Result<u32> {
self.to_uint()
}
pub fn to_u64(&self) -> Result<u64> {
self.to_uint()
}
pub fn to_u128(&self) -> Result<u128> {
self.to_uint()
}
pub fn to_usize(&self) -> Result<usize> {
self.to_uint()
}
#[allow(dead_code)]
pub(crate) fn as_integer_bytes(&self) -> Result<IntegerBytes<'_>> {
match self {
Self::Unsigned(x) => Ok(IntegerBytes::UnsignedOwned(x.to_be_bytes())),
Self::Negative(x) => Ok(IntegerBytes::NegativeOwned(x.to_be_bytes())),
Self::Tag(Tag::POS_BIG_INT, content) if content.is_bytes() => {
Ok(IntegerBytes::UnsignedBorrowed(content.as_bytes()?))
}
Self::Tag(Tag::NEG_BIG_INT, content) if content.is_bytes() => {
Ok(IntegerBytes::NegativeBorrowed(content.as_bytes()?))
}
Self::Tag(_other_number, content) => content.peeled().as_integer_bytes(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
fn to_sint<T>(&self) -> Result<T>
where
T: TryFrom<u64> + TryFrom<u128> + std::ops::Not<Output = T>,
{
match self {
Self::Unsigned(x) => T::try_from(*x).or(Err(Error::Overflow)),
Self::Negative(x) => T::try_from(*x).map(T::not).or(Err(Error::Overflow)),
Self::Tag(Tag::POS_BIG_INT, content) if content.is_bytes() => {
T::try_from(u128_from_slice(self.as_bytes()?)?).or(Err(Error::Overflow))
}
Self::Tag(Tag::NEG_BIG_INT, content) if content.is_bytes() => {
T::try_from(u128_from_slice(self.as_bytes()?)?)
.map(T::not)
.or(Err(Error::Overflow))
}
Self::Tag(_other_number, content) => content.peeled().to_sint(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_i8(&self) -> Result<i8> {
self.to_sint()
}
pub fn to_i16(&self) -> Result<i16> {
self.to_sint()
}
pub fn to_i32(&self) -> Result<i32> {
self.to_sint()
}
pub fn to_i64(&self) -> Result<i64> {
self.to_sint()
}
pub fn to_i128(&self) -> Result<i128> {
self.to_sint()
}
pub fn to_isize(&self) -> Result<isize> {
self.to_sint()
}
pub fn to_f32(&self) -> Result<f32> {
match self {
Self::Float(float) => float.to_f32(),
Self::Tag(_number, content) => content.untagged().to_f32(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_f64(&self) -> Result<f64> {
match self {
Self::Float(float) => Ok(float.to_f64()),
Self::Tag(_number, content) => content.untagged().to_f64(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn to_system_time(&self) -> Result<SystemTime> {
if let Ok(s) = self.as_str() {
Ok(s.parse::<crate::iso3339::Timestamp>()?.try_into()?)
} else if let Ok(f) = self.to_f64() {
if f.is_finite() && (0.0..=253402300799.0).contains(&f) {
Ok(SystemTime::UNIX_EPOCH + Duration::from_secs_f64(f))
} else {
Err(Error::InvalidValue)
}
} else {
match self.to_u64() {
Ok(secs) if secs <= 253402300799 => Ok(SystemTime::UNIX_EPOCH + Duration::from_secs(secs)),
Ok(_) | Err(Error::NegativeUnsigned) => Err(Error::InvalidValue),
Err(error) => Err(error),
}
}
}
pub fn as_bytes(&self) -> Result<&[u8]> {
match self {
Self::ByteString(vec) => Ok(vec.as_slice()),
Self::Tag(_number, content) => content.untagged().as_bytes(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_bytes_mut(&mut self) -> Result<&mut Vec<u8>> {
match self {
Self::ByteString(vec) => Ok(vec),
Self::Tag(_number, content) => content.untagged_mut().as_bytes_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_bytes(self) -> Result<Vec<u8>> {
match self {
Self::ByteString(vec) => Ok(vec),
Self::Tag(_number, content) => content.into_untagged().into_bytes(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_str(&self) -> Result<&str> {
match self {
Self::TextString(s) => Ok(s.as_str()),
Self::Tag(_number, content) => content.untagged().as_str(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_string_mut(&mut self) -> Result<&mut String> {
match self {
Self::TextString(s) => Ok(s),
Self::Tag(_number, content) => content.untagged_mut().as_string_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_string(self) -> Result<String> {
match self {
Self::TextString(s) => Ok(s),
Self::Tag(_number, content) => content.into_untagged().into_string(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_array(&self) -> Result<&[Value]> {
match self {
Self::Array(v) => Ok(v.as_slice()),
Self::Tag(_number, content) => content.untagged().as_array(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_array_mut(&mut self) -> Result<&mut Vec<Value>> {
match self {
Self::Array(v) => Ok(v),
Self::Tag(_number, content) => content.untagged_mut().as_array_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_array(self) -> Result<Vec<Value>> {
match self {
Self::Array(v) => Ok(v),
Self::Tag(_number, content) => content.into_untagged().into_array(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_map(&self) -> Result<&BTreeMap<Value, Value>> {
match self {
Self::Map(m) => Ok(m),
Self::Tag(_number, content) => content.untagged().as_map(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn as_map_mut(&mut self) -> Result<&mut BTreeMap<Value, Value>> {
match self {
Self::Map(m) => Ok(m),
Self::Tag(_number, content) => content.untagged_mut().as_map_mut(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_map(self) -> Result<BTreeMap<Value, Value>> {
match self {
Self::Map(m) => Ok(m),
Self::Tag(_number, content) => content.into_untagged().into_map(),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn get(&self, index: impl Into<Value>) -> Option<&Value> {
let key = index.into();
match self.untagged() {
Value::Array(arr) => key.to_usize().ok().and_then(|i| arr.get(i)),
Value::Map(map) => map.get(&key),
_ => None,
}
}
pub fn get_mut(&mut self, index: impl Into<Value>) -> Option<&mut Value> {
let key = index.into();
match self.untagged_mut() {
Value::Array(arr) => key.to_usize().ok().and_then(|i| arr.get_mut(i)),
Value::Map(map) => map.get_mut(&key),
_ => None,
}
}
pub const fn tag_number(&self) -> Result<u64> {
match self {
Self::Tag(number, _content) => Ok(*number),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn tag_content(&self) -> Result<&Self> {
match self {
Self::Tag(_tag, content) => Ok(content),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub const fn tag_content_mut(&mut self) -> Result<&mut Self> {
match self {
Self::Tag(_, value) => Ok(value),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_tag(&self) -> Result<(u64, &Value)> {
match self {
Self::Tag(number, content) => Ok((*number, content)),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn as_tag_mut(&mut self) -> Result<(u64, &mut Value)> {
match self {
Self::Tag(number, content) => Ok((*number, content)),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn into_tag(self) -> Result<(u64, Value)> {
match self {
Self::Tag(number, content) => Ok((number, *content)),
_ => Err(Error::IncompatibleType(self.data_type())),
}
}
pub fn remove_tag(&mut self) -> Option<u64> {
let mut result = None;
if let Self::Tag(number, content) = self {
result = Some(*number);
*self = std::mem::take(content);
}
result
}
pub fn remove_all_tags(&mut self) -> Vec<u64> {
let mut tags = Vec::new();
while let Self::Tag(number, content) = self {
tags.push(*number);
*self = std::mem::take(content);
}
tags
}
#[must_use]
pub(crate) const fn peeled(&self) -> &Self {
let mut result = self;
while let Self::Tag(_, content) = result
&& content.data_type().is_tag()
{
result = content;
}
result
}
#[must_use]
pub const fn untagged(&self) -> &Self {
let mut result = self;
while let Self::Tag(_, content) = result {
result = content;
}
result
}
pub const fn untagged_mut(&mut self) -> &mut Self {
let mut result = self;
while let Self::Tag(_, content) = result {
result = content;
}
result
}
#[must_use]
pub fn into_untagged(mut self) -> Self {
while let Self::Tag(_number, content) = self {
self = *content;
}
self
}
}
fn read_vec(reader: &mut impl io::Read, len: u64) -> crate::IoResult<Vec<u8>> {
use io::Read;
if len > LENGTH_LIMIT {
return Error::LengthTooLarge.into();
}
let len_usize = usize::try_from(len).or(Err(Error::LengthTooLarge))?;
let mut buf = Vec::with_capacity(len_usize.min(OOM_MITIGATION)); let bytes_read = reader.take(len).read_to_end(&mut buf)?;
if bytes_read == len_usize {
Ok(buf)
} else {
Error::UnexpectedEof.into()
}
}