use yog_data::Diagnostic;
use yog_ids::{mirror_problem, sep};
use super::{
Column, ColumnId, MAX_FIELDS, Mirror, Place, Store, Write, fact_space, problem, unknown_space,
};
use crate::buffer::Buffer;
use crate::keys::{BIT_WORD, bit_words};
use crate::spec::{FieldSpec, MirrorSpec, Rate};
impl Store {
pub(super) fn ensure_mirror(&mut self, spec: &MirrorSpec) -> Result<(), Diagnostic> {
if spec.fields.len() > MAX_FIELDS {
let n = spec.fields.len();
return Err(problem(
mirror_problem::TOO_MANY_FIELDS,
format_args!("{} has {n} fields, {MAX_FIELDS} at most", spec.name),
));
}
let space = self.ensure_space(&spec.space)?;
let places = spec
.fields
.iter()
.map(|f| self.place(spec, f))
.collect::<Result<Vec<_>, _>>()?;
if let Some(known) = self.mirrors.get_mut(&spec.name) {
return merge(known, spec, places);
}
let rows = Buffer::new(BIT_WORD, bit_words(self.space_of(space).keys.capacity()));
let mirror = Mirror {
name: spec.name.as_str().into(),
spec: spec.clone(),
space,
columns: vec![None; places.len()],
places,
rows,
provider: None,
writes: Vec::new(),
declarations: 0,
touched: false,
scopes: Vec::new(),
};
self.mirrors.insert(spec.name.clone(), mirror);
Ok(())
}
fn place(&self, mirror: &MirrorSpec, field: &FieldSpec) -> Result<Place, Diagnostic> {
let fact = field
.fact
.clone()
.unwrap_or_else(|| format!("{}{}{}", mirror.name, sep::PATH, field.name));
let space_name = fact_space(&fact).unwrap_or_else(|| mirror.space.clone());
if self.is_family(&space_name) && Self::member_of(&space_name, &mirror.space) {
let space = self
.space_id(&mirror.space)
.ok_or_else(|| unknown_space(&mirror.space))?;
return Ok(Place {
space,
fact,
depth: 0,
});
}
let space = self
.space_id(&space_name)
.ok_or_else(|| unknown_space(&space_name))?;
let mut at = Some(
self.space_id(&mirror.space)
.ok_or_else(|| unknown_space(&mirror.space))?,
);
let mut depth = 0u8;
while let Some(s) = at {
if s == space {
return Ok(Place { space, fact, depth });
}
at = self.space_of(s).parent;
depth += 1;
}
Err(problem(
mirror_problem::FOREIGN_FACT,
format_args!(
"{fact} is of {space_name}, which {} isn't part of",
mirror.space
),
))
}
pub(super) fn ensure_column(
&mut self,
place: &Place,
field: &FieldSpec,
) -> Result<ColumnId, Diagnostic> {
let key = (place.space, place.fact.clone());
if let Some(&id) = self.by_fact.get(&key) {
let c = self.columns[id as usize].as_ref();
if c.is_some_and(|c| c.kind != field.kind || c.count != field.count) {
return Err(problem(
mirror_problem::WRONG_SHAPE,
format_args!(
"{} is kept in another shape than field {}'s",
place.fact, field.name
),
));
}
return Ok(id);
}
let width = field.width().ok_or_else(|| {
problem(
mirror_problem::FAILED,
format_args!("field {} has no kind {}", field.name, field.kind),
)
})?;
let column = Column {
buf: Buffer::new(width, self.space_of(place.space).keys.capacity()),
space: place.space,
fact: place.fact.clone(),
kind: field.kind,
count: field.count,
demand: Vec::new(),
writer: None,
};
let id = match self.columns.iter().position(Option::is_none) {
Some(free) => {
self.columns[free] = Some(column);
free
}
None => {
self.columns.push(Some(column));
self.columns.len() - 1
}
} as ColumnId;
self.by_fact.insert(key, id);
Ok(id)
}
pub(super) fn drop_column(&mut self, id: ColumnId) {
if let Some(c) = self.columns.get_mut(id as usize).and_then(Option::take) {
self.by_fact.remove(&(c.space, c.fact));
}
}
pub(super) fn forget_unused(&mut self, name: &str) {
if self
.mirrors
.get(name)
.is_some_and(|m| m.declarations == 0 && m.provider.is_none())
{
self.mirrors.remove(name);
}
}
pub(super) fn relink(&mut self) {
for m in self.mirrors.values_mut() {
for (col, place) in m.columns.iter_mut().zip(&m.places) {
*col = self
.by_fact
.get(&(place.space, place.fact.clone()))
.copied();
}
}
for (id, c) in self.columns.iter_mut().enumerate() {
let Some(c) = c else { continue };
c.writer = self
.mirrors
.values()
.filter(|m| m.columns.contains(&Some(id as ColumnId)))
.filter_map(|m| m.provider)
.min_by_key(|p| (!p.host, p.order))
.map(|p| p.writer);
}
self.in_use = vec![false; self.spaces.len()];
let read: Vec<_> = self
.mirrors
.values()
.filter(|m| m.declarations > 0)
.map(|m| m.space)
.collect();
for space in read {
let mut at = Some(space);
while let Some(s) = at {
self.in_use[s.0 as usize] = true;
at = self.space_of(s).parent;
}
}
for m in self.mirrors.values_mut() {
let writer = m.provider.map(|p| p.writer);
let writes = m
.columns
.iter()
.enumerate()
.filter_map(|(i, col)| {
let c = self.columns[(*col)? as usize].as_ref()?;
(writer.is_some() && c.writer == writer).then(|| write_of(i, c))
})
.map(|mut w| {
if let Some(old) = m.writes.iter().find(|o| o.bit == w.bit) {
w.next = old.next;
}
w
})
.collect();
m.writes = writes;
}
}
}
fn write_of(i: usize, c: &Column) -> Write {
let period = c
.demand
.iter()
.filter_map(|(_, r)| match r {
Rate::Every(n) => Some((*n).max(1)),
Rate::Changes => None,
})
.min()
.unwrap_or(0);
Write {
bit: 1 << i,
column: std::ptr::from_ref(c.buf.raw()) as u64,
period,
next: 0,
on_change: c.demand.iter().any(|(_, r)| *r == Rate::Changes),
}
}
fn merge(known: &mut Mirror, spec: &MirrorSpec, places: Vec<Place>) -> Result<(), Diagnostic> {
let (old, new) = (&known.spec.fields, &spec.fields);
let extends = |a: &[FieldSpec], b: &[FieldSpec]| a.len() <= b.len() && b[..a.len()] == *a;
if known.spec.space != spec.space || !(extends(old, new) || extends(new, old)) {
return Err(problem(
mirror_problem::TAKEN,
format_args!("{} is declared otherwise already", spec.name),
));
}
if new.len() > old.len() {
known.spec = spec.clone();
known.columns.resize(places.len(), None);
known.places = places;
}
Ok(())
}