use murk_core::traits::FieldWriter;
use murk_core::{FieldId, FieldMutability};
use crate::descriptor::FieldDescriptor;
use crate::handle::FieldLocation;
use crate::segment::SegmentList;
use crate::sparse::SparseSlab;
pub struct WriteArena<'a> {
per_tick_segments: &'a mut SegmentList,
sparse_segments: &'a mut SegmentList,
sparse_slab: &'a mut SparseSlab,
descriptor: &'a mut FieldDescriptor,
generation: u32,
}
impl<'a> WriteArena<'a> {
pub(crate) fn new(
per_tick_segments: &'a mut SegmentList,
sparse_segments: &'a mut SegmentList,
sparse_slab: &'a mut SparseSlab,
descriptor: &'a mut FieldDescriptor,
generation: u32,
) -> Self {
Self {
per_tick_segments,
sparse_segments,
sparse_slab,
descriptor,
generation,
}
}
fn write_sparse(&mut self, field: FieldId, total_len: u32) -> Option<&mut [f32]> {
let handle = self
.sparse_slab
.alloc(field, total_len, self.generation, self.sparse_segments)
.ok()?;
if let Some(old_handle) = {
self.descriptor
.get(field)
.map(|e| e.handle)
.filter(|h| h.generation() != self.generation)
} {
if let FieldLocation::Sparse {
segment_index: old_seg,
} = old_handle.location()
{
let old_data: Vec<f32> = self
.sparse_segments
.slice(old_seg, old_handle.offset, old_handle.len())?
.to_vec();
if let FieldLocation::Sparse {
segment_index: new_seg,
} = handle.location()
{
let new_data =
self.sparse_segments
.slice_mut(new_seg, handle.offset, handle.len())?;
let copy_len = old_data.len().min(new_data.len());
new_data[..copy_len].copy_from_slice(&old_data[..copy_len]);
}
}
}
self.descriptor.update_handle(field, handle);
if let FieldLocation::Sparse { segment_index } = handle.location() {
self.sparse_segments
.slice_mut(segment_index, handle.offset, handle.len())
} else {
None
}
}
pub fn read(&self, field: FieldId) -> Option<&[f32]> {
let entry = self.descriptor.get(field)?;
let handle = &entry.handle;
match handle.location() {
FieldLocation::PerTick { segment_index } => {
self.per_tick_segments
.slice(segment_index, handle.offset, handle.len())
}
FieldLocation::Sparse { segment_index } => {
self.sparse_segments
.slice(segment_index, handle.offset, handle.len())
}
FieldLocation::Static { .. } => {
None
}
}
}
}
impl FieldWriter for WriteArena<'_> {
fn write(&mut self, field: FieldId) -> Option<&mut [f32]> {
let entry = self.descriptor.get(field)?;
let mutability = entry.meta.mutability;
let total_len = entry.meta.total_len;
let handle = entry.handle;
match mutability {
FieldMutability::PerTick => {
if let FieldLocation::PerTick { segment_index } = handle.location() {
self.per_tick_segments
.slice_mut(segment_index, handle.offset, handle.len())
} else {
None
}
}
FieldMutability::Sparse => {
if handle.generation() == self.generation {
if let FieldLocation::Sparse { segment_index } = handle.location() {
self.sparse_segments
.slice_mut(segment_index, handle.offset, handle.len())
} else {
None
}
} else {
self.write_sparse(field, total_len)
}
}
FieldMutability::Static => {
None
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::descriptor::FieldDescriptor;
use crate::handle::FieldHandle;
use crate::segment::SegmentList;
use crate::sparse::SparseSlab;
use murk_core::{BoundaryBehavior, FieldDef, FieldType};
fn make_defs() -> Vec<(FieldId, FieldDef)> {
vec![
(
FieldId(0),
FieldDef {
name: "temp".into(),
field_type: FieldType::Scalar,
mutability: FieldMutability::PerTick,
units: None,
bounds: None,
boundary_behavior: BoundaryBehavior::Clamp,
},
),
(
FieldId(1),
FieldDef {
name: "resource".into(),
field_type: FieldType::Scalar,
mutability: FieldMutability::Sparse,
units: None,
bounds: None,
boundary_behavior: BoundaryBehavior::Clamp,
},
),
(
FieldId(2),
FieldDef {
name: "terrain".into(),
field_type: FieldType::Scalar,
mutability: FieldMutability::Static,
units: None,
bounds: None,
boundary_behavior: BoundaryBehavior::Clamp,
},
),
]
}
fn setup_write_arena(
cell_count: u32,
generation: u32,
) -> (SegmentList, SegmentList, SparseSlab, FieldDescriptor) {
let defs = make_defs();
let mut desc = FieldDescriptor::from_field_defs(&defs, cell_count).unwrap();
let mut per_tick = SegmentList::new(4096, 4);
let sparse_segs = SegmentList::new(4096, 4);
let slab = SparseSlab::new();
let per_tick_fields: Vec<(FieldId, u32)> = desc
.iter()
.filter(|(_, e)| e.meta.mutability == FieldMutability::PerTick)
.map(|(&id, e)| (id, e.meta.total_len))
.collect();
for (field_id, total_len) in per_tick_fields {
let (seg_idx, offset) = per_tick.alloc(total_len).unwrap();
let handle = FieldHandle::new(
generation,
offset,
total_len,
FieldLocation::PerTick {
segment_index: seg_idx,
},
);
desc.update_handle(field_id, handle);
}
(per_tick, sparse_segs, slab, desc)
}
#[test]
fn write_per_tick_returns_mutable_slice() {
let (mut per_tick, mut sparse_segs, mut slab, mut desc) = setup_write_arena(10, 1);
let mut wa = WriteArena::new(&mut per_tick, &mut sparse_segs, &mut slab, &mut desc, 1);
let data = wa.write(FieldId(0)).unwrap();
assert_eq!(data.len(), 10);
data[0] = 42.0;
assert_eq!(data[0], 42.0);
}
#[test]
fn write_static_returns_none() {
let (mut per_tick, mut sparse_segs, mut slab, mut desc) = setup_write_arena(10, 1);
let mut wa = WriteArena::new(&mut per_tick, &mut sparse_segs, &mut slab, &mut desc, 1);
assert!(wa.write(FieldId(2)).is_none());
}
#[test]
fn write_sparse_allocates_new_copy() {
let (mut per_tick, mut sparse_segs, mut slab, mut desc) = setup_write_arena(10, 1);
let handle = slab.alloc(FieldId(1), 10, 0, &mut sparse_segs).unwrap();
desc.update_handle(FieldId(1), handle);
let mut wa = WriteArena::new(&mut per_tick, &mut sparse_segs, &mut slab, &mut desc, 1);
let data = wa.write(FieldId(1)).unwrap();
assert_eq!(data.len(), 10);
data[0] = 99.0;
}
#[test]
fn write_unknown_field_returns_none() {
let (mut per_tick, mut sparse_segs, mut slab, mut desc) = setup_write_arena(10, 1);
let mut wa = WriteArena::new(&mut per_tick, &mut sparse_segs, &mut slab, &mut desc, 1);
assert!(wa.write(FieldId(99)).is_none());
}
#[test]
fn read_per_tick_returns_data() {
let (mut per_tick, mut sparse_segs, mut slab, mut desc) = setup_write_arena(10, 1);
{
let mut wa = WriteArena::new(&mut per_tick, &mut sparse_segs, &mut slab, &mut desc, 1);
let data = wa.write(FieldId(0)).unwrap();
data[0] = 7.0;
}
let wa = WriteArena::new(&mut per_tick, &mut sparse_segs, &mut slab, &mut desc, 1);
let data = wa.read(FieldId(0)).unwrap();
assert_eq!(data[0], 7.0);
}
}