use super::uploads::Upload;
use core::ffi::c_void;
use core::mem::size_of;
use windows::core::{IUnknown, Interface};
const UPLOAD: u32 = u32::MAX;
const NULL: u64 = u64::MAX;
struct Held([usize; 64]);
impl Default for Held {
fn default() -> Held {
Held([0; 64])
}
}
#[derive(Default)]
pub struct Tape {
cells: Vec<u64>,
owners: Vec<IUnknown>,
held: Held,
uploads: Vec<Upload>,
last: Option<u32>,
}
unsafe impl Send for Tape {}
unsafe impl Sync for Tape {}
impl Tape {
pub fn sized_like(previous: &Tape) -> Tape {
Tape {
cells: Vec::with_capacity(previous.cells.len()),
owners: Vec::with_capacity(previous.owners.len()),
..Tape::default()
}
}
pub fn last(&self) -> Option<u32> {
self.last
}
pub(crate) fn tag(&mut self, tag: u32) {
self.cells.push(u64::from(tag));
self.last = Some(tag);
}
unsafe fn bytes<T: Copy>(&mut self, p: *const T, count: usize) {
let bytes = count * size_of::<T>();
let at = self.cells.len();
self.cells.resize(at + bytes.div_ceil(8), 0);
unsafe {
core::ptr::copy_nonoverlapping(p.cast::<u8>(), self.cells[at..].as_mut_ptr().cast::<u8>(), bytes)
};
}
pub(crate) fn put<T: Copy>(&mut self, value: T) {
unsafe { self.bytes(&value, 1) };
}
pub(crate) unsafe fn array<T: Copy>(&mut self, p: *const T, count: u32) {
if p.is_null() {
self.cells.push(NULL);
} else {
self.cells.push(u64::from(count));
unsafe { self.bytes(p, count as usize) };
}
}
pub(crate) unsafe fn object(&mut self, p: *mut c_void) {
self.cells.push(p as u64);
let slot = &mut self.held.0[(p as usize >> 4) % 64];
if *slot == p as usize {
return;
}
if let Some(o) = unsafe { IUnknown::from_raw_borrowed(&p) } {
self.owners.push(o.clone());
*slot = p as usize;
}
}
pub(crate) unsafe fn objects(&mut self, p: *const *mut c_void, count: u32) {
if p.is_null() {
self.cells.push(NULL);
return;
}
self.cells.push(u64::from(count));
let objects = unsafe { core::slice::from_raw_parts(p, count as usize) };
for &o in objects {
unsafe { self.object(o) };
}
}
pub(super) fn upload(&mut self, upload: Upload) {
self.cells.push(u64::from(UPLOAD));
self.cells.push(self.uploads.len() as u64);
self.uploads.push(upload);
self.last = Some(UPLOAD);
}
pub fn append(&mut self, other: &Tape) {
debug_assert!(other.uploads.is_empty(), "appending a tape with uploads");
self.cells.extend_from_slice(&other.cells);
self.owners.extend(other.owners.iter().cloned());
self.held = Held::default();
self.last = other.last.or(self.last);
}
pub unsafe fn replay(&self, context: *mut c_void) -> windows::core::Result<()> {
let mut r = Reader {
cells: &self.cells,
at: 0,
};
while r.at < self.cells.len() {
let tag: u32 = r.get();
if tag == UPLOAD {
let index: u64 = r.get();
unsafe { self.uploads[index as usize].replay(context) }?;
} else {
unsafe { crate::vtables::recorded_context::call(tag, &mut r, context) }?;
}
}
Ok(())
}
}
pub struct Reader<'a> {
cells: &'a [u64],
at: usize,
}
impl Reader<'_> {
fn take(&mut self, bytes: usize) -> *const u64 {
let p = self.cells[self.at..].as_ptr();
self.at += bytes.div_ceil(8);
p
}
pub(crate) fn get<T: Copy>(&mut self) -> T {
unsafe { self.take(size_of::<T>()).cast::<T>().read() }
}
pub(crate) fn array<T: Copy>(&mut self) -> *const T {
match self.get::<u64>() {
NULL => core::ptr::null(),
count => self.take(count as usize * size_of::<T>()).cast(),
}
}
pub(crate) fn object(&mut self) -> *mut c_void {
self.get::<u64>() as *mut c_void
}
pub(crate) fn objects(&mut self) -> *const *mut c_void {
match self.get::<u64>() {
NULL => core::ptr::null(),
count => self.take(count as usize * 8).cast(),
}
}
}