use crate::schema::attr::AttrType;
use crate::{AttrId, KindId};
#[derive(Clone, Copy, Debug)]
pub struct Slot {
pub offset: u32,
pub ty: AttrType,
}
#[derive(Clone, Debug)]
pub struct KindLayout {
pub region_start: u32,
pub region_len: u32,
pub slots: Vec<Slot>,
pub attr_index: ahash::AHashMap<AttrId, usize>,
}
#[derive(Clone, Debug)]
pub struct SlotLayout {
pub kinds: Vec<KindLayout>,
pub total_bytes: u32,
}
impl SlotLayout {
#[must_use]
pub fn resolve(&self, kind: KindId, attr: AttrId) -> Option<Slot> {
let kl = self.kinds.get(usize::from(kind.0))?;
let &local = kl.attr_index.get(&attr)?;
let slot = kl.slots[local];
Some(Slot {
offset: kl.region_start + slot.offset,
ty: slot.ty,
})
}
}
const fn align_up(off: u32, align: u32) -> u32 {
(off + align - 1) & !(align - 1)
}
pub(crate) struct SlotLayoutBuilder {
kinds: Vec<KindLayout>,
cursor: u32,
}
impl SlotLayoutBuilder {
pub const fn new() -> Self {
Self {
kinds: Vec::new(),
cursor: 0,
}
}
pub fn push_kind(&mut self, attrs: &[(AttrId, AttrType)]) {
let mut local_slots = Vec::with_capacity(attrs.len());
let mut attr_index = ahash::AHashMap::with_capacity(attrs.len());
let mut local_off: u32 = 0;
for (i, &(aid, ty)) in attrs.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
{
local_off = align_up(local_off, ty.slot_align() as u32);
}
local_slots.push(Slot {
offset: local_off,
ty,
});
attr_index.insert(aid, i);
#[allow(clippy::cast_possible_truncation)]
{
local_off += ty.slot_width() as u32;
}
}
self.cursor = align_up(self.cursor, 8);
let region_start = self.cursor;
let region_len = local_off;
self.cursor += region_len;
self.kinds.push(KindLayout {
region_start,
region_len,
slots: local_slots,
attr_index,
});
}
#[must_use]
pub fn build(self) -> SlotLayout {
SlotLayout {
kinds: self.kinds,
total_bytes: self.cursor,
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn aligns_within_and_across_kinds() {
let mut b = SlotLayoutBuilder::new();
b.push_kind(&[(AttrId(0), AttrType::F32), (AttrId(1), AttrType::Int)]);
b.push_kind(&[(AttrId(2), AttrType::Int), (AttrId(3), AttrType::F32)]);
let layout = b.build();
assert_eq!(layout.kinds[0].region_start, 0);
assert_eq!(layout.kinds[0].region_len, 16);
assert_eq!(layout.kinds[0].slots[0].offset, 0);
assert_eq!(layout.kinds[0].slots[1].offset, 8);
assert_eq!(layout.kinds[1].region_start, 16);
assert_eq!(layout.kinds[1].slots[0].offset, 0);
assert_eq!(layout.kinds[1].slots[1].offset, 8);
assert_eq!(layout.total_bytes, 28);
}
#[test]
fn resolve_picks_correct_slot() {
let mut b = SlotLayoutBuilder::new();
b.push_kind(&[(AttrId(0), AttrType::F32), (AttrId(1), AttrType::Int)]);
let layout = b.build();
let slot = layout.resolve(KindId(0), AttrId(1)).unwrap();
assert_eq!(slot.offset, 8);
assert_eq!(slot.ty, AttrType::Int);
}
#[test]
fn resolve_returns_none_for_unknown() {
let b = SlotLayoutBuilder::new();
let layout = b.build();
assert!(layout.resolve(KindId(0), AttrId(0)).is_none());
}
}