use crate::stack::Stack;
use std::fmt;
use std::os::raw::c_void;
extern "C" {
fn make_fcontext(sp: *mut c_void, size: usize, f: ContextFn) -> &'static c_void;
fn jump_fcontext(to: &'static c_void, p: usize) -> Transfer;
}
pub type ContextFn = extern "C" fn(t: Transfer);
#[repr(C)]
pub struct Context(&'static c_void);
impl Context {
#[inline(always)]
pub unsafe fn new(stack: &Stack, f: ContextFn) -> Context {
Context(make_fcontext(stack.top(), stack.len(), f))
}
#[inline(always)]
pub unsafe fn resume(&self, data: usize) -> Transfer {
jump_fcontext(self.0, data)
}
}
impl fmt::Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Context({:p})", self.0)
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Transfer {
pub context: Context,
pub data: usize,
}
impl Transfer {
#[inline(always)]
pub fn new(context: Context, data: usize) -> Transfer {
Transfer { context, data }
}
}
#[cfg(test)]
mod tests {
use std::mem;
use std::os::raw::c_void;
use super::*;
use crate::stack::ProtectedFixedSizeStack;
#[test]
fn type_sizes() {
assert_eq!(mem::size_of::<Context>(), mem::size_of::<usize>());
assert_eq!(mem::size_of::<Context>(), mem::size_of::<*const c_void>());
}
#[test]
fn number_generator() {
extern "C" fn context_function(mut t: Transfer) {
for i in 0usize.. {
assert_eq!(t.data, i);
t = unsafe { t.context.resume(i) };
}
unreachable!();
}
let stack = ProtectedFixedSizeStack::default();
let mut t = Transfer::new(unsafe { Context::new(&stack, context_function) }, 0);
for i in 0..10usize {
t = unsafe { t.context.resume(i) };
assert_eq!(t.data, i);
if t.data == 9 {
break;
}
}
}
}