use alloc::vec::Vec;
use crate::modifier::Modifier;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(
clippy::exhaustive_structs,
reason = "a value defined by its fields; there is nothing to add to it"
)]
pub struct FormatMod {
pub fourcc: u32,
pub modifier: Modifier,
}
const BLOB_HEADER_LEN: usize = 24;
const BLOB_MODIFIER_LEN: usize = 24;
struct BlobHeader {
count_formats: u32,
formats_offset: u32,
count_modifiers: u32,
modifiers_offset: u32,
}
fn read_u32(data: &[u8], offset: usize) -> Option<u32> {
let bytes: &[u8; 4] = data.get(offset..offset.checked_add(4)?)?.try_into().ok()?;
Some(u32::from_le_bytes(*bytes))
}
fn read_u64(data: &[u8], offset: usize) -> Option<u64> {
let bytes: &[u8; 8] = data.get(offset..offset.checked_add(8)?)?.try_into().ok()?;
Some(u64::from_le_bytes(*bytes))
}
impl BlobHeader {
fn parse(data: &[u8]) -> Option<Self> {
if data.len() < BLOB_HEADER_LEN {
return None;
}
Some(Self {
count_formats: read_u32(data, 8)?,
formats_offset: read_u32(data, 12)?,
count_modifiers: read_u32(data, 16)?,
modifiers_offset: read_u32(data, 20)?,
})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FormatTable {
pairs: Vec<FormatMod>,
}
impl FormatTable {
#[must_use]
pub const fn new() -> Self {
Self { pairs: Vec::new() }
}
#[must_use]
pub fn from_blob(data: &[u8]) -> Self {
let mut table = Self::new();
let Some(header) = BlobHeader::parse(data) else {
return table;
};
let size = data.len();
let formats_offset = header.formats_offset as usize;
let modifiers_offset = header.modifiers_offset as usize;
if formats_offset > size
|| modifiers_offset > size
|| header.count_formats as usize > (size - formats_offset) / 4
|| header.count_modifiers as usize > (size - modifiers_offset) / BLOB_MODIFIER_LEN
{
return table;
}
for i in 0..header.count_modifiers as usize {
let record = modifiers_offset + i * BLOB_MODIFIER_LEN;
let (Some(formats_bitmask), Some(offset), Some(modifier)) = (
read_u64(data, record),
read_u32(data, record + 8),
read_u64(data, record + 16),
) else {
continue;
};
for bit in 0..64u32 {
if (formats_bitmask >> bit) & 1 == 0 {
continue;
}
let Some(index) = offset.checked_add(bit) else {
continue; };
if index >= header.count_formats {
continue; }
let Some(fourcc) = read_u32(data, formats_offset + index as usize * 4) else {
continue;
};
table.pairs.push(FormatMod {
fourcc,
modifier: Modifier(modifier),
});
}
}
table.build_index();
table
}
pub fn from_pairs<I>(pairs: I) -> Self
where
I: IntoIterator<Item = (u32, Modifier)>,
{
let mut table = Self {
pairs: pairs
.into_iter()
.map(|(fourcc, modifier)| FormatMod { fourcc, modifier })
.collect(),
};
table.build_index();
table
}
fn build_index(&mut self) {
self.pairs.sort_unstable();
self.pairs.dedup();
}
#[must_use]
pub fn supports(&self, fourcc: u32, modifier: Modifier) -> bool {
self.pairs
.binary_search(&FormatMod { fourcc, modifier })
.is_ok()
}
pub fn modifiers_for(&self, fourcc: u32) -> impl Iterator<Item = Modifier> + '_ {
let start = self.pairs.partition_point(|p| p.fourcc < fourcc);
self.pairs[start..]
.iter()
.take_while(move |p| p.fourcc == fourcc)
.map(|p| p.modifier)
}
#[must_use]
pub fn all(&self) -> &[FormatMod] {
&self.pairs
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.pairs.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.pairs.len()
}
}
#[must_use]
pub fn layer_fits_plane(plane_formats: &FormatTable, fourcc: u32, m: Modifier) -> bool {
if plane_formats.supports(fourcc, m) {
return true;
}
if m.is_linear() || m.is_invalid() {
return plane_formats.supports(fourcc, Modifier::LINEAR)
|| plane_formats.supports(fourcc, Modifier::INVALID);
}
if let Some(base) = m.family().base() {
return plane_formats.supports(fourcc, base);
}
false
}