use std::fmt::{Display, Formatter, Result as FmtResult};
use serde::ser::SerializeStruct as _;
use serde::{Serialize, Serializer};
#[cfg(feature = "unstable-v2")]
use crate::codecs::presence_coding::PresenceCoding;
#[cfg(feature = "unstable-v2")]
use crate::wire::Alp;
use crate::wire::StreamMeta;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum RegionKind {
Meta,
DataBlob,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum DecodeHint {
Presence,
Bool,
I32,
U32,
GeometryType,
I64,
#[cfg(feature = "unstable-v2")]
Alp(#[serde(serialize_with = "serialize_alp")] Alp),
U64,
F32,
F64,
Bytes,
#[cfg(feature = "unstable-v2")]
PackedBits,
#[cfg(feature = "unstable-v2")]
PresenceCoded(PresenceCoding),
#[cfg(feature = "unstable-v2")]
Rans,
}
#[cfg(feature = "unstable-v2")]
fn serialize_alp<S: Serializer>(alp: &Alp, s: S) -> Result<S::Ok, S::Error> {
let mut st = s.serialize_struct("Alp", 3)?;
st.serialize_field("e", &alp.scale.e)?;
st.serialize_field("f", &alp.scale.f)?;
st.serialize_field("base", &alp.base)?;
st.end()
}
#[derive(Debug, Clone, Serialize)]
pub struct BitField {
hi: u8,
lo: u8,
#[serde(serialize_with = "serialize_as_number")]
raw: u64,
meaning: String,
}
impl BitField {
pub fn mask(mask: u8, byte: u8, meaning: impl Into<String>) -> Self {
let (hi, lo) = mask_bounds(mask);
Self {
hi,
lo,
raw: u64::from((byte & mask) >> lo),
meaning: meaning.into(),
}
}
#[must_use]
pub fn flag(mask: u8, byte: u8, when_set: &str, when_clear: &str) -> Self {
debug_assert_eq!(mask.count_ones(), 1, "a flag occupies exactly one bit");
let meaning = if byte & mask == 0 {
when_clear
} else {
when_set
};
Self::mask(mask, byte, meaning)
}
#[must_use]
pub fn meaning(&self) -> &str {
&self.meaning
}
}
impl Display for BitField {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
if self.hi == self.lo {
write!(f, "bit {}", self.hi)?;
} else {
write!(f, "bits {}-{}", self.hi, self.lo)?;
}
let width = usize::from(self.hi - self.lo + 1);
write!(f, " = {:0width$b} -> {}", self.raw, self.meaning)
}
}
#[expect(
clippy::cast_possible_truncation,
reason = "a u8 mask spans at most 8 bits"
)]
fn mask_bounds(mask: u8) -> (u8, u8) {
debug_assert!(mask != 0, "a bit field needs at least one bit");
let lo = mask.trailing_zeros() as u8;
let hi = (u8::BITS - 1 - mask.leading_zeros()) as u8;
debug_assert_eq!(
u32::from(hi - lo) + 1,
mask.count_ones(),
"a bit field's mask must be one contiguous run of bits"
);
(hi, lo)
}
#[derive(Debug, Clone, Copy)]
pub struct BlobInfo {
pub meta: StreamMeta,
pub hint: DecodeHint,
}
impl Serialize for BlobInfo {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut st = s.serialize_struct("BlobInfo", 5)?;
st.serialize_field("streamType", &self.meta.stream_type.to_string())?;
st.serialize_field("logical", &self.meta.encoding.logical.to_string())?;
st.serialize_field("physical", &self.meta.encoding.physical.to_string())?;
st.serialize_field("numValues", &self.meta.num_values)?;
st.serialize_field("hint", &self.hint)?;
st.end()
}
}
fn serialize_as_number<T, S>(v: &T, s: S) -> Result<S::Ok, S::Error>
where
T: Copy + TryInto<u32>,
S: Serializer,
{
let n = (*v)
.try_into()
.map_err(|_| serde::ser::Error::custom("offset or count exceeds u32"))?;
s.serialize_u32(n)
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Region {
#[serde(serialize_with = "serialize_as_number")]
pub offset: usize,
#[serde(serialize_with = "serialize_as_number")]
pub len: usize,
#[serde(serialize_with = "serialize_as_number")]
pub depth: usize,
pub label: String,
pub value: Option<String>,
pub bits: Vec<BitField>,
pub kind: RegionKind,
pub container: bool,
pub blob: Option<BlobInfo>,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DumpTree {
#[serde(serialize_with = "serialize_as_number")]
pub buf_len: usize,
pub regions: Vec<Region>,
}
pub const UNANNOTATED: &str = "<unannotated>";
#[must_use]
pub fn filter_layer(tree: &DumpTree, idx: usize) -> Option<DumpTree> {
let layer = tree
.regions
.iter()
.filter(|r| r.depth == 0 && r.container)
.nth(idx)?;
let start = layer.offset;
let end = layer.offset + layer.len;
let regions = tree
.regions
.iter()
.filter(|r| r.offset >= start && r.offset + r.len <= end)
.cloned()
.collect();
Some(DumpTree {
buf_len: tree.buf_len,
regions,
})
}