use alloc::vec::Vec;
use serde::ser::Error as _;
use super::{Error, Integer, Value};
#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum KeyOrder {
#[default]
Bytewise,
LengthFirst,
}
impl Value {
#[inline]
pub fn canonicalize(&mut self) -> Result<(), Error> {
self.canonicalize_with(KeyOrder::Bytewise)
}
pub fn canonicalize_with(&mut self, order: KeyOrder) -> Result<(), Error> {
match self {
Value::Float(x) if x.is_nan() => *x = f64::NAN,
Value::Array(items) => {
for item in items {
item.canonicalize_with(order)?;
}
}
Value::Tag(tag @ (2 | 3), inner) => {
inner.canonicalize_with(order)?;
let tag = *tag;
let reduced = match inner.as_mut() {
Value::Bytes(bytes) => {
let first = bytes.iter().position(|&b| b != 0).unwrap_or(bytes.len());
bytes.drain(..first);
if bytes.len() <= 8 {
let mut buffer = [0u8; 8];
buffer[8 - bytes.len()..].copy_from_slice(bytes);
let raw = u64::from_be_bytes(buffer);
Some(match tag {
2 => Integer::from(raw),
_ => Integer::try_from(-1i128 - i128::from(raw))
.expect("-1 - u64 is always in range"),
})
} else {
None
}
}
_ => None,
};
if let Some(int) = reduced {
*self = Value::Integer(int);
}
}
Value::Tag(.., inner) => inner.canonicalize_with(order)?,
Value::Map(entries) => {
let mut keyed = Vec::with_capacity(entries.len());
for (mut key, mut val) in core::mem::take(entries) {
key.canonicalize_with(order)?;
val.canonicalize_with(order)?;
let encoded = crate::ser::to_vec(&key).map_err(Error::custom)?;
keyed.push((encoded, key, val));
}
keyed.sort_by(|a, b| match order {
KeyOrder::Bytewise => a.0.cmp(&b.0),
KeyOrder::LengthFirst => a.0.len().cmp(&b.0.len()).then_with(|| a.0.cmp(&b.0)),
});
for window in keyed.windows(2) {
if window[0].0 == window[1].0 {
return Err(Error::custom("duplicate map key"));
}
}
entries.extend(keyed.into_iter().map(|(_, key, val)| (key, val)));
}
_ => {}
}
Ok(())
}
}