use std::os::raw::c_void;
use yog_abi::{YogBytes, YogReply, YogStatus, YogStr, status};
use yog_data::Diagnostic;
use yog_event::Message;
use yog_event::mirror::{Declare, Key, Keys, Provide, Release, Write};
use yog_mirror::{Bits, ColumnRef, ColumnWriter, KeySpaceRef};
use yog_sync::Arc;
pub use yog_mirror::{Cell, FieldSpec, Kind, MirrorSpec, Rate};
use crate::registry::{Registry, guard};
mod host;
mod typed;
pub use host::{Blocks, Entities, Players};
pub use typed::{Mirror, MirrorWriter, Mirrored, Row, RowWriter};
pub use yog_event::mirror::BlockScope;
pub use yog_mirror::host as host_tables;
const WORD_BITS: u32 = u64::BITS;
#[derive(Copy, Clone)]
struct Field {
column: ColumnRef,
depth: u8,
}
pub struct Declared {
id: u32,
space: KeySpaceRef,
rows: ColumnRef,
fields: Vec<Option<Field>>,
}
fn no_runtime() -> Diagnostic {
Diagnostic::error(
yog_ids::mirror_problem::FAILED,
"no one answers mirrors here",
)
}
impl Declared {
pub fn new(spec: &MirrorSpec, wanted: u64, rate: Rate) -> Result<Declared, Diagnostic> {
Self::scoped(spec, wanted, rate, None)
}
pub fn scoped(
spec: &MirrorSpec,
wanted: u64,
rate: Rate,
scope: Option<Vec<u8>>,
) -> Result<Declared, Diagnostic> {
let request = Declare {
spec: spec.clone(),
wanted,
rate,
scope: scope.map(|bytes| yog_event::mirror::Scope { bytes }),
};
let reply = crate::call(&request).ok_or_else(no_runtime)?;
let d = match (reply.declaration, reply.problem) {
(Some(d), _) => d,
(None, problem) => return Err(problem.unwrap_or_else(no_runtime)),
};
let (space, rows) = unsafe {
(
KeySpaceRef::new(d.space as *const _),
ColumnRef::new(d.rows as *const _),
)
};
let fields = d
.fields
.iter()
.map(|f| {
let f = (*f)?;
let column = unsafe { ColumnRef::new(f.column as *const _) }?;
Some(Field {
column,
depth: f.depth,
})
})
.collect();
match (space, rows) {
(Some(space), Some(rows)) => Ok(Declared {
id: d.id,
space,
rows,
fields,
}),
_ => Err(no_runtime()),
}
}
pub fn rows(&self) -> impl Iterator<Item = u32> + '_ {
let words = self.space.rows().div_ceil(WORD_BITS);
(0..words).flat_map(move |w| {
let both = self.rows.word(w) & self.space.live_word(w);
Bits(both).map(move |b| w * WORD_BITS + b)
})
}
pub fn row_of(&self, key: u64) -> Option<u32> {
self.space.row_of(key)
}
pub fn key_of(&self, row: u32) -> Option<u64> {
self.space.key_of(row)
}
#[inline]
pub fn get<T: Cell>(&self, field: usize, row: u32) -> Option<T> {
let f = (*self.fields.get(field)?)?;
let (_, row) = self.space.up(row, f.depth)?;
f.column.get(row)
}
}
impl Drop for Declared {
fn drop(&mut self) {
crate::call(&Release { id: self.id });
}
}
pub struct Writer {
mirror: String,
space_name: String,
scopes: Vec<Vec<u8>>,
fields: u64,
space: Option<KeySpaceRef>,
rows: Option<ColumnWriter>,
columns: Vec<Option<ColumnWriter>>,
}
impl Writer {
fn open(w: Write) -> Writer {
let open = |c: u64| unsafe { ColumnRef::new(c as *const _) }.map(|c| c.write());
let due = (0..u64::BITS as usize).filter(|i| w.fields >> i & 1 == 1);
let mut columns: Vec<Option<ColumnWriter>> = Vec::new();
for (i, c) in due.zip(&w.columns) {
columns.resize_with(columns.len().max(i + 1), || None);
columns[i] = open(*c);
}
Writer {
mirror: w.mirror,
space_name: w.space_name,
scopes: w.scopes.into_iter().map(|s| s.bytes).collect(),
fields: w.fields,
space: unsafe { KeySpaceRef::new(w.space as *const _) },
rows: open(w.rows),
columns,
}
}
pub fn mirror(&self) -> &str {
&self.mirror
}
pub fn scopes(&self) -> &[Vec<u8>] {
&self.scopes
}
pub fn keep_keys(&mut self, live: &[(u64, Option<u64>)]) {
let Some(space) = self.space else { return };
let wanted: std::collections::HashSet<u64> = live.iter().map(|(k, _)| *k).collect();
let take: Vec<u64> = space
.live()
.map(|(_, k)| k)
.filter(|k| !wanted.contains(k))
.collect();
let put: Vec<Key> = live
.iter()
.filter(|(k, _)| space.row_of(*k).is_none())
.map(|&(key, parent)| Key { key, parent })
.collect();
if put.is_empty() && take.is_empty() {
return;
}
crate::call(&Keys {
space: self.space_name.clone(),
put,
take,
});
}
#[inline]
pub fn wants(&self, field: usize) -> bool {
self.fields >> field & 1 == 1
}
pub fn row(&mut self, key: u64) -> Option<u32> {
let row = self.space?.row_of(key)?;
if let Some(rows) = &mut self.rows {
rows.set_bit(row, true);
}
Some(row)
}
pub fn leave(&mut self, row: u32) {
if let Some(rows) = &mut self.rows {
rows.set_bit(row, false);
}
}
#[inline]
pub fn set<T: Cell>(&mut self, field: usize, row: u32, value: impl FnOnce() -> T) {
if let Some(Some(w)) = self.columns.get_mut(field) {
w.set(row, value());
}
}
}
unsafe extern "C" fn on_write<F: Fn(&mut Writer) + Send + Sync>(
ud: *mut c_void,
payload: YogBytes,
_reply: *const YogReply,
) -> YogStatus {
let f = unsafe { &*(ud as *const F) };
let Some(w) = Write::from_bytes(unsafe { payload.as_slice() }) else {
return status::BAD_PAYLOAD;
};
let mut writer = Writer::open(w);
guard((), || f(&mut writer));
status::OK
}
impl Registry {
pub fn provide_mirror(
&mut self,
spec: &MirrorSpec,
write: impl Fn(&mut Writer) + Send + Sync + 'static,
) -> bool {
self.providing_mirror(spec, false, Arc::new(write))
}
pub(crate) fn providing_mirror<F: Fn(&mut Writer) + Send + Sync + 'static>(
&mut self,
spec: &MirrorSpec,
family: bool,
write: Arc<F>,
) -> bool {
let bus = unsafe { &*self.api().bus };
let ud = Arc::into_raw(write).cast_mut().cast::<c_void>();
let st = unsafe {
let at = (bus.resolve)(bus.ctx, YogStr::from_str(&spec.name));
(bus.provide)(bus.ctx, at, ud, on_write::<F>)
};
if st != status::OK {
crate::error!("{}: someone provides it already", spec.name);
return false;
}
let problem = self.call(&Provide {
spec: spec.clone(),
host: false,
family,
});
match problem.flatten() {
Some(p) => {
crate::error!("{}: {p}", spec.name);
false
}
None => true,
}
}
}
#[cfg(test)]
mod tests;