use crate::objects::{self, FieldSlot};
use std::sync::{Mutex, OnceLock};
pub const WIN_BIT: u64 = 1 << 62;
#[derive(Clone, Copy)]
enum Root {
Mem(usize),
Field(u64, FieldSlot),
}
#[derive(Clone, Copy)]
struct Win {
root: Root,
two_state: bool,
}
#[derive(Default)]
struct Wins {
slots: Vec<Option<Win>>,
free: Vec<u32>,
}
fn table() -> &'static Mutex<Wins> {
static T: OnceLock<Mutex<Wins>> = OnceLock::new();
T.get_or_init(|| Mutex::new(Wins::default()))
}
pub fn is_window(id: u64) -> bool {
id & WIN_BIT != 0
}
fn push(w: Win) -> u64 {
let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
let k = match t.free.pop() {
Some(k) => {
t.slots[k as usize] = Some(w);
k
}
None => {
t.slots.push(Some(w));
(t.slots.len() - 1) as u32
}
};
WIN_BIT | (u64::from(k) + 1)
}
fn lookup(id: u64) -> Option<Win> {
let t = table().lock().unwrap_or_else(|e| e.into_inner());
let k = (id & !WIN_BIT).checked_sub(1)? as usize;
t.slots.get(k).copied().flatten()
}
pub unsafe fn open_mem(addr: usize, two_state: bool) -> u64 {
push(Win {
root: Root::Mem(addr),
two_state,
})
}
pub fn open_field(h: u64, slot: FieldSlot, two_state: bool) -> u64 {
push(Win {
root: Root::Field(h, slot),
two_state,
})
}
pub fn close(id: u64) {
let mut t = table().lock().unwrap_or_else(|e| e.into_inner());
if let Some(k) = (id & !WIN_BIT).checked_sub(1).map(|k| k as usize)
&& t.slots.get_mut(k).and_then(Option::take).is_some()
{
t.free.push(k as u32);
}
}
fn bit_at(words: &[u64], i: usize) -> bool {
words.get(i / 64).is_some_and(|w| w >> (i % 64) & 1 != 0)
}
fn set_bit(words: &mut [u64], i: usize, v: bool) {
if let Some(w) = words.get_mut(i / 64) {
if v {
*w |= 1 << (i % 64);
} else {
*w &= !(1 << (i % 64));
}
}
}
fn image_bits(win: Win, big_w: u32) -> usize {
if win.two_state {
big_w as usize
} else {
2 * big_w as usize
}
}
fn load(win: Win, big_w: u32) -> Option<Vec<u64>> {
let bits = image_bits(win, big_w);
match win.root {
Root::Mem(addr) => {
let mut words = vec![0u64; bits.div_ceil(64).max(1)];
let bytes = unsafe { std::slice::from_raw_parts(addr as *const u8, bits.div_ceil(8)) };
for (i, b) in bytes.iter().enumerate() {
words[i / 8] |= u64::from(*b) << (8 * (i % 8));
}
Some(words)
}
Root::Field(h, slot) => {
let mut words = vec![0u64; slot.words.max(1)];
objects::field_get(h, slot, &mut words).then_some(words)
}
}
}
fn store(win: Win, big_w: u32, words: &[u64]) {
let bits = image_bits(win, big_w);
match win.root {
Root::Mem(addr) => {
let bytes =
unsafe { std::slice::from_raw_parts_mut(addr as *mut u8, bits.div_ceil(8)) };
for (i, b) in bytes.iter_mut().enumerate() {
*b = (words[i / 8] >> (8 * (i % 8))) as u8;
}
}
Root::Field(h, slot) => {
objects::field_set(h, slot, words);
}
}
}
pub fn get_part(id: u64, lo: u64, w: u32, big_w: u32, out: &mut [u64]) -> bool {
let Some(win) = lookup(id) else { return false };
let Some(img) = load(win, big_w) else {
return false;
};
out.fill(0);
for k in 0..w as usize {
let i = lo as usize + k;
if i < big_w as usize {
set_bit(out, k, bit_at(&img, i));
if !win.two_state {
set_bit(out, w as usize + k, bit_at(&img, big_w as usize + i));
}
}
}
true
}
pub fn put_part(id: u64, lo: u64, w: u32, big_w: u32, val: &[u64]) {
let Some(win) = lookup(id) else { return };
let Some(mut img) = load(win, big_w) else {
return;
};
for k in 0..w as usize {
let i = lo as usize + k;
if i < big_w as usize {
set_bit(&mut img, i, bit_at(val, k));
if !win.two_state {
set_bit(&mut img, big_w as usize + i, bit_at(val, w as usize + k));
}
}
}
store(win, big_w, &img);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_window_reads_and_writes_only_its_bits() {
let mut img: u32 = 0xABCD;
let id = unsafe { open_mem(&mut img as *mut u32 as usize, false) };
assert!(is_window(id));
let mut out = [0u64; 1];
assert!(get_part(id, 4, 8, 16, &mut out));
assert_eq!(out[0], 0xBC);
put_part(id, 4, 8, 16, &[0x5A]);
close(id);
assert_eq!(img, 0xA5AD);
assert!(!get_part(id, 0, 8, 16, &mut out));
}
}