use std::collections::BTreeMap;
use zerocopy::little_endian::{I32, U32};
use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout};
use crate::error::{FieldError, FreezeError};
use crate::field::{Field, Stored};
use crate::topology::{Topology, morton};
use crate::tree::{Coord, TileLevel};
use crate::tree::{LEAF_LOG2, LOWER_DIM_LOG2, LOWER_SPAN_LOG2, UPPER_DIM_LOG2};
#[cfg(target_endian = "big")]
compile_error!("the frozen layout stores values little-endian, as brodo's targets are");
pub(crate) use crate::kind::GRID_MAGIC as MAGIC;
pub(crate) const VERSION: u32 = 2;
pub(crate) const BLOCK: usize = 32;
pub(crate) const UPPER_PLACES: usize = 32 * 32 * 32;
pub(crate) const LOWER_PLACES: usize = 16 * 16 * 16;
pub(crate) const ROOT_TILE: u32 = 0;
pub(crate) const ROOT_NODE: u32 = 1;
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout, Debug, Clone, Copy)]
#[repr(C, align(32))]
pub(crate) struct Header {
pub(crate) magic: U32,
pub(crate) version: U32,
pub(crate) value_type: U32,
pub(crate) value_size: U32,
pub(crate) counts: [U32; 5],
pub(crate) offsets: [U32; 5],
pub(crate) blocks: U32,
pub(crate) background: [U32; 6],
pub(crate) pad: [U32; 3],
}
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout, Debug, Clone, Copy)]
#[repr(C, align(32))]
pub(crate) struct RootEntry {
pub(crate) key: [I32; 3],
pub(crate) kind: U32,
pub(crate) index: U32,
pub(crate) pad: [U32; 3],
}
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout, Debug, Clone, Copy)]
#[repr(C, align(32))]
pub(crate) struct UpperNode {
pub(crate) children: [U32; UPPER_PLACES / 32],
pub(crate) tiles: [U32; UPPER_PLACES / 32],
pub(crate) first_child: U32,
pub(crate) first_tile: U32,
pub(crate) pad: [U32; 6],
}
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout, Debug, Clone, Copy)]
#[repr(C, align(32))]
pub(crate) struct LowerNode {
pub(crate) children: [U32; LOWER_PLACES / 32],
pub(crate) tiles: [U32; LOWER_PLACES / 32],
pub(crate) first_child: U32,
pub(crate) first_tile: U32,
pub(crate) pad: [U32; 6],
}
#[derive(FromBytes, IntoBytes, Immutable, KnownLayout, Debug, Clone, Copy)]
#[repr(C, align(32))]
pub(crate) struct Block([u8; BLOCK]);
pub(crate) fn aligned(bytes: &[u8]) -> Vec<Block> {
let mut out = vec![Block([0; BLOCK]); bytes.len().div_ceil(BLOCK)];
out.as_mut_bytes()[..bytes.len()].copy_from_slice(bytes);
out
}
#[derive(Debug, Clone)]
pub struct FrozenGrid {
blocks: Vec<Block>,
}
impl FrozenGrid {
pub(crate) fn of_blocks(blocks: Vec<Block>) -> Self {
Self { blocks }
}
pub fn as_bytes(&self) -> &[u8] {
self.blocks.as_bytes()
}
pub fn version(&self) -> u32 {
Header::ref_from_prefix(self.as_bytes()).map_or(VERSION, |(h, _)| h.version.get())
}
pub fn view<T: Stored>(
&self,
) -> Result<crate::view::GridView<'_, T>, crate::error::FormatError> {
crate::view::GridView::new(self.as_bytes())
}
}
pub(crate) fn place(local: [u32; 3], bits: u32) -> usize {
let mut p = 0_usize;
for b in 0..bits {
for (axis, c) in local.iter().enumerate() {
p |= (((c >> b) & 1) as usize) << (3 * b as usize + axis);
}
}
p
}
pub(crate) fn local(v: Coord, child_log2: u32, dim_log2: u32) -> [u32; 3] {
v.map(|c| (c.cast_unsigned() >> child_log2) & ((1 << dim_log2) - 1))
}
fn set(mask: &mut [U32], p: usize) {
let w = &mut mask[p / 32];
*w = U32::new(w.get() | (1 << (p % 32)));
}
fn to_u32(n: usize) -> Result<u32, FreezeError> {
u32::try_from(n).map_err(|_| FreezeError::TooLarge)
}
struct Plan {
roots: Vec<RootEntry>,
uppers: Vec<UpperNode>,
lowers: Vec<LowerNode>,
tiles: Vec<u32>,
}
fn origin(v: Coord, level: TileLevel) -> Coord {
level.origin(v)
}
fn plan(t: &Topology) -> Result<Plan, FreezeError> {
let leaves: Vec<Coord> =
t.leaf_numbers().map(|n| t.leaf_origin(n)).collect::<Result<_, _>>()?;
let tiles: Vec<(TileLevel, Coord)> =
t.tile_numbers().map(|n| t.tile(n)).collect::<Result<_, _>>()?;
let by_morton = |set: Vec<Coord>| -> Vec<Coord> {
let mut v = set;
v.dedup();
v.sort_by_cached_key(|o| morton(*o));
v.dedup();
v
};
let below_root = leaves
.iter()
.copied()
.chain(tiles.iter().filter(|(l, _)| *l != TileLevel::Root).map(|(_, o)| *o));
let upper_keys = by_morton(below_root.map(|o| origin(o, TileLevel::Root)).collect());
let lower_src = leaves
.iter()
.copied()
.chain(tiles.iter().filter(|(l, _)| *l == TileLevel::Lower).map(|(_, o)| *o));
let lower_keys = by_morton(lower_src.map(|o| origin(o, TileLevel::Upper)).collect());
let mut uppers: Vec<UpperNode> = upper_keys.iter().map(|_| UpperNode::new_zeroed()).collect();
let mut lowers: Vec<LowerNode> = lower_keys.iter().map(|_| LowerNode::new_zeroed()).collect();
let upper_no: BTreeMap<Coord, usize> =
upper_keys.iter().enumerate().map(|(i, k)| (*k, i)).collect();
let lower_no: BTreeMap<Coord, usize> =
lower_keys.iter().enumerate().map(|(i, k)| (*k, i)).collect();
link_children(&mut uppers, &mut lowers, &leaves, &lower_keys, &upper_no, &lower_no)?;
let roots = roots(&upper_keys, &tiles)?;
let order = tile_order(&tiles, &mut uppers, &mut lowers, &upper_no, &lower_no)?;
Ok(Plan { roots, uppers, lowers, tiles: order })
}
fn link_children(
uppers: &mut [UpperNode],
lowers: &mut [LowerNode],
leaves: &[Coord],
lower_keys: &[Coord],
upper_no: &BTreeMap<Coord, usize>,
lower_no: &BTreeMap<Coord, usize>,
) -> Result<(), FreezeError> {
let mut seen = vec![false; uppers.len()];
for (i, k) in lower_keys.iter().enumerate() {
let u = upper_no[&origin(*k, TileLevel::Root)];
if !seen[u] {
seen[u] = true;
uppers[u].first_child = U32::new(to_u32(i)?);
}
set(
&mut uppers[u].children,
place(local(*k, LOWER_SPAN_LOG2, UPPER_DIM_LOG2), UPPER_DIM_LOG2),
);
}
let mut seen = vec![false; lowers.len()];
for (n, o) in leaves.iter().enumerate() {
let l = lower_no[&origin(*o, TileLevel::Upper)];
if !seen[l] {
seen[l] = true;
lowers[l].first_child = U32::new(to_u32(n)?);
}
set(&mut lowers[l].children, place(local(*o, LEAF_LOG2, LOWER_DIM_LOG2), LOWER_DIM_LOG2));
}
Ok(())
}
fn roots(
upper_keys: &[Coord],
tiles: &[(TileLevel, Coord)],
) -> Result<Vec<RootEntry>, FreezeError> {
let mut by_key: BTreeMap<Coord, (u32, u32)> = BTreeMap::new();
for (i, k) in upper_keys.iter().enumerate() {
by_key.insert(*k, (ROOT_NODE, to_u32(i)?));
}
let root_tiles = tiles.iter().filter(|(l, _)| *l == TileLevel::Root).map(|(_, o)| *o);
for o in root_tiles {
by_key.insert(o, (ROOT_TILE, 0));
}
let mut root_tiles = 0_u32..;
let entry = |(key, (kind, index)): (Coord, (u32, u32)),
tiles: &mut std::ops::RangeFrom<u32>| {
let index = if kind == ROOT_TILE { tiles.next().unwrap_or(index) } else { index };
RootEntry {
key: key.map(I32::new),
kind: U32::new(kind),
index: U32::new(index),
pad: [U32::ZERO; 3],
}
};
Ok(by_key.into_iter().map(|e| entry(e, &mut root_tiles)).collect())
}
fn tile_order(
tiles: &[(TileLevel, Coord)],
uppers: &mut [UpperNode],
lowers: &mut [LowerNode],
upper_no: &BTreeMap<Coord, usize>,
lower_no: &BTreeMap<Coord, usize>,
) -> Result<Vec<u32>, FreezeError> {
let numbered = || tiles.iter().zip(0_u32..);
let mut root: Vec<(Coord, u32)> =
numbered().filter(|((l, _), _)| *l == TileLevel::Root).map(|((_, o), n)| (*o, n)).collect();
root.sort_unstable();
let mut order: Vec<u32> = root.iter().map(|(_, n)| *n).collect();
for level in [TileLevel::Upper, TileLevel::Lower] {
let mut here: Vec<(usize, usize, u32)> = Vec::new();
for ((_, o), n) in numbered().filter(|((l, _), _)| *l == level) {
let (node, p) = match level {
TileLevel::Upper => (
upper_no[&origin(*o, TileLevel::Root)],
place(local(*o, LOWER_SPAN_LOG2, UPPER_DIM_LOG2), UPPER_DIM_LOG2),
),
TileLevel::Lower | TileLevel::Root => (
lower_no[&origin(*o, TileLevel::Upper)],
place(local(*o, LEAF_LOG2, LOWER_DIM_LOG2), LOWER_DIM_LOG2),
),
};
here.push((node, p, n));
}
here.sort_unstable_by_key(|(node, p, _)| (*node, *p));
let mut last = usize::MAX;
for (node, p, n) in here {
if node != last {
last = node;
let first = U32::new(to_u32(order.len())?);
match level {
TileLevel::Upper => uppers[node].first_tile = first,
TileLevel::Lower | TileLevel::Root => lowers[node].first_tile = first,
}
}
match level {
TileLevel::Upper => set(&mut uppers[node].tiles, p),
TileLevel::Lower | TileLevel::Root => set(&mut lowers[node].tiles, p),
}
order.push(n);
}
}
Ok(order)
}
fn blocks(bytes: usize) -> usize {
bytes.div_ceil(BLOCK)
}
pub fn freeze<T: Stored>(t: &Topology, f: &Field<T>) -> Result<FrozenGrid, FreezeError> {
f.belongs_to(t)?;
let plan = plan(t)?;
let leaf_bytes = f.leaves(t)?.as_bytes();
let tile_values: Vec<T> =
plan.tiles.iter().map(|n| f.tile_value_at(*n)).collect::<Result<_, FieldError>>()?;
let sizes = [
plan.roots.as_bytes().len(),
plan.uppers.as_bytes().len(),
plan.lowers.as_bytes().len(),
leaf_bytes.len(),
tile_values.as_bytes().len(),
];
let mut offsets = [0_usize; 5];
let mut at = blocks(size_of::<Header>());
for (o, s) in offsets.iter_mut().zip(sizes) {
*o = at;
at += blocks(s);
}
let mut buf = vec![Block([0; BLOCK]); at];
let bytes = buf.as_mut_bytes();
let header = header::<T>(&plan, f, &offsets, at, [t.leaf_count(), tile_values.len()])?;
bytes[..size_of::<Header>()].copy_from_slice(header.as_bytes());
let sections: [&[u8]; 5] = [
plan.roots.as_bytes(),
plan.uppers.as_bytes(),
plan.lowers.as_bytes(),
leaf_bytes,
tile_values.as_bytes(),
];
for (o, s) in offsets.iter().zip(sections) {
bytes[o * BLOCK..o * BLOCK + s.len()].copy_from_slice(s);
}
Ok(FrozenGrid { blocks: buf })
}
fn header<T: Stored>(
plan: &Plan,
f: &Field<T>,
offsets: &[usize; 5],
total: usize,
[leaves, tiles]: [usize; 2],
) -> Result<Header, FreezeError> {
let counts = [plan.roots.len(), plan.uppers.len(), plan.lowers.len(), leaves, tiles];
let mut background = [U32::ZERO; 6];
background
.as_mut_bytes()
.get_mut(..size_of::<T>())
.ok_or(FreezeError::TooLarge)?
.copy_from_slice(f.background().as_bytes());
Ok(Header {
magic: U32::new(MAGIC),
version: U32::new(VERSION),
value_type: U32::new(T::VALUE_TYPE.code()),
value_size: U32::new(to_u32(size_of::<T>())?),
counts: [
to_u32(counts[0])?,
to_u32(counts[1])?,
to_u32(counts[2])?,
to_u32(counts[3])?,
to_u32(counts[4])?,
]
.map(U32::new),
offsets: [
to_u32(offsets[0])?,
to_u32(offsets[1])?,
to_u32(offsets[2])?,
to_u32(offsets[3])?,
to_u32(offsets[4])?,
]
.map(U32::new),
blocks: U32::new(to_u32(total)?),
background,
pad: [U32::ZERO; 3],
})
}