<commit_before>//! This module provides a generic iterator and traits for decomposing
//! primitives and tessellating polygons.

use arrayvec::ArrayVec;
use std::collections::VecDeque;
use std::iter::IntoIterator;
use std::vec;

use math;
use super::primitive::{Line, Polygon, Polygonal, Primitive, Triangle, Quad};

pub struct Decompose<I, P, Q, R>
where
    R: IntoIterator<Item = Q>,
{
    input: I,
    output: VecDeque<Q>,
    f: fn(P) -> R,
}

impl<I, P, Q, R> Decompose<I, P, Q, R>
where
    R: IntoIterator<Item = Q>,
{
    pub(super) fn new(input: I, f: fn(P) -> R) -> Self {
        Decompose {
            input: input,
            output: VecDeque::new(),
            f: f,
        }
    }
}

impl<I, P, R> Decompose<I, P, P, R>
where
    I: Iterator<Item = P>,
    R: IntoIterator<Item = P>,
{
    // TODO: This is questionable, but acts as a replacement for optional
    //       parameters used by the `Into*` traits. In particular,
    //       `into_subdivisions` no longer accepts a parameter `n`, and `remap`
    //       can be used to emulate that behavior. This is especially useful
    //       for larger `n` values, where chaining calls to `subdivide` is not
    //       practical.
    pub fn remap(self, n: usize) -> Decompose<vec::IntoIter<P>, P, P, R> {
        let Decompose { input, output, f } = self;
        Decompose::new(
            output
                .into_iter()
                .rev()
                .chain(remap(n, input, f))
                .collect::<Vec<_>>() // TODO: Only needed to name the iterator.
                .into_iter(),
            f,
        )
    }
}

impl<I, P, Q, R> Iterator for Decompose<I, P, Q, R>
where
    I: Iterator<Item = P>,
    R: IntoIterator<Item = Q>,
{
    type Item = Q;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(primitive) = self.output.pop_front() {
                return Some(primitive);
            }
            if let Some(primitive) = self.input.next() {
                self.output.extend((self.f)(primitive));
            }
            else {
                return None;
            }
        }
    }
}

pub trait Interpolate: math::Interpolate<f32> {}

impl<T> Interpolate for T
where
    T: math::Interpolate<f32>,
{
}

pub trait IntoPoints: Primitive {
    type Output: IntoIterator<Item = Self::Point>;

    fn into_points(self) -> Self::Output;
}

pub trait IntoLines: Primitive {
    type Output: IntoIterator<Item = Line<Self::Point>>;

    fn into_lines(self) -> Self::Output;
}

pub trait IntoTriangles: Polygonal {
    type Output: IntoIterator<Item = Triangle<Self::Point>>;

    fn into_triangles(self) -> Self::Output;
}

pub trait IntoSubdivisions: Polygonal
where
    Self::Point: Clone + Interpolate,
{
    type Output: IntoIterator<Item = Self>;

    fn into_subdivisions(self) -> Self::Output;
}

pub trait IntoTetrahedrons: Polygonal
where
    Self::Point: Clone + Interpolate,
{
    fn into_tetrahedrons(self) -> ArrayVec<[Triangle<Self::Point>; 4]>;
}

impl<T> IntoPoints for Line<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Self::Point; 2]>;

    fn into_points(self) -> Self::Output {
        let Line { a, b } = self;
        ArrayVec::from([a, b])
    }
}

impl<T> IntoPoints for Triangle<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Self::Point; 3]>;

    fn into_points(self) -> Self::Output {
        let Triangle { a, b, c } = self;
        ArrayVec::from([a, b, c])
    }
}

impl<T> IntoPoints for Quad<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Self::Point; 4]>;

    fn into_points(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        ArrayVec::from([a, b, c, d])
    }
}

impl<T> IntoPoints for Polygon<T>
where
    T: Clone,
{
    type Output = Vec<Self::Point>;

    fn into_points(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => triangle.into_points().into_iter().collect(),
            Polygon::Quad(quad) => quad.into_points().into_iter().collect(),
        }
    }
}

impl<T> IntoLines for Line<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Line<Self::Point>; 1]>;

    fn into_lines(self) -> Self::Output {
        ArrayVec::from([self])
    }
}

impl<T> IntoLines for Triangle<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Line<Self::Point>; 3]>;

    fn into_lines(self) -> Self::Output {
        let Triangle { a, b, c } = self;
        ArrayVec::from([
            Line::new(a.clone(), b.clone()),
            Line::new(b, c.clone()),
            Line::new(c, a),
        ])
    }
}

impl<T> IntoLines for Quad<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Line<Self::Point>; 4]>;

    fn into_lines(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        ArrayVec::from([
            Line::new(a.clone(), b.clone()),
            Line::new(b, c.clone()),
            Line::new(c, d.clone()),
            Line::new(d, a),
        ])
    }
}

impl<T> IntoLines for Polygon<T>
where
    T: Clone,
{
    type Output = Vec<Line<Self::Point>>;

    fn into_lines(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => triangle.into_lines().into_iter().collect(),
            Polygon::Quad(quad) => quad.into_lines().into_iter().collect(),
        }
    }
}

impl<T> IntoTriangles for Triangle<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Triangle<Self::Point>; 1]>;

    fn into_triangles(self) -> Self::Output {
        ArrayVec::from([self])
    }
}

impl<T> IntoTriangles for Quad<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Triangle<Self::Point>; 2]>;

    fn into_triangles(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        ArrayVec::from([
            Triangle::new(a.clone(), b, c.clone()),
            Triangle::new(c, d, a),
        ])
    }
}

impl<T> IntoTriangles for Polygon<T>
where
    T: Clone,
{
    type Output = Vec<Triangle<Self::Point>>;

    fn into_triangles(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => triangle.into_triangles().into_iter().collect(),
            Polygon::Quad(quad) => quad.into_triangles().into_iter().collect(),
        }
    }
}

impl<T> IntoSubdivisions for Triangle<T>
where
    T: Clone + Interpolate,
{
    type Output = ArrayVec<[Triangle<Self::Point>; 2]>;

    fn into_subdivisions(self) -> Self::Output {
        let Triangle { a, b, c } = self;
        let ac = a.midpoint(&c);
        ArrayVec::from([
            Triangle::new(b.clone(), ac.clone(), a),
            Triangle::new(c, ac, b),
        ])
    }
}

impl<T> IntoSubdivisions for Quad<T>
where
    T: Clone + Interpolate,
{
    type Output = ArrayVec<[Quad<Self::Point>; 4]>;

    fn into_subdivisions(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        let ab = a.midpoint(&b);
        let bc = b.midpoint(&c);
        let cd = c.midpoint(&d);
        let da = d.midpoint(&a);
        let ac = a.midpoint(&c); // Diagonal.
        ArrayVec::from([
            Quad::new(a, ab.clone(), ac.clone(), da.clone()),
            Quad::new(ab, b, bc.clone(), ac.clone()),
            Quad::new(ac.clone(), bc, c, cd.clone()),
            Quad::new(da, ac, cd, d),
        ])
    }
}

impl<T> IntoTetrahedrons for Quad<T>
where
    T: Clone + Interpolate,
{
    fn into_tetrahedrons(self) -> ArrayVec<[Triangle<Self::Point>; 4]> {
        let Quad { a, b, c, d } = self;
        let ac = a.midpoint(&c); // Diagonal.
        ArrayVec::from([
            Triangle::new(a.clone(), b.clone(), ac.clone()),
            Triangle::new(b, c.clone(), ac.clone()),
            Triangle::new(c, d.clone(), ac.clone()),
            Triangle::new(d, a, ac),
        ])
    }
}

impl<T> IntoSubdivisions for Polygon<T>
where
    T: Clone + Interpolate,
{
    type Output = Vec<Self>;

    fn into_subdivisions(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => {
                triangle
                    .into_subdivisions()
                    .into_iter()
                    .map(|triangle| triangle.into())
                    .collect()
            }
            Polygon::Quad(quad) => {
                quad.into_subdivisions()
                    .into_iter()
                    .map(|quad| quad.into())
                    .collect()
            }
        }
    }
}

pub trait Points<P>: Sized
where
    P: IntoPoints,
{
    fn points(self) -> Decompose<Self, P, P::Point, P::Output>;
}

impl<I, P> Points<P> for I
where
    I: Iterator<Item = P>,
    P: IntoPoints,
    P::Point: Clone,
{
    fn points(self) -> Decompose<Self, P, P::Point, P::Output> {
        Decompose::new(self, P::into_points)
    }
}

pub trait Lines<P>: Sized
where
    P: IntoLines,
{
    fn lines(self) -> Decompose<Self, P, Line<P::Point>, P::Output>;
}

impl<I, P> Lines<P> for I
where
    I: Iterator<Item = P>,
    P: IntoLines,
    P::Point: Clone,
{
    fn lines(self) -> Decompose<Self, P, Line<P::Point>, P::Output> {
        Decompose::new(self, P::into_lines)
    }
}

pub trait Triangulate<P>: Sized
where
    P: IntoTriangles,
{
    fn triangulate(self) -> Decompose<Self, P, Triangle<P::Point>, P::Output>;
}

impl<I, P> Triangulate<P> for I
where
    I: Iterator<Item = P>,
    P: IntoTriangles,
    P::Point: Clone,
{
    fn triangulate(self) -> Decompose<Self, P, Triangle<P::Point>, P::Output> {
        Decompose::new(self, P::into_triangles)
    }
}

pub trait Subdivide<P>: Sized
where
    P: IntoSubdivisions,
    P::Point: Clone + Interpolate,
{
    fn subdivide(self) -> Decompose<Self, P, P, P::Output>;
}

impl<I, P> Subdivide<P> for I
where
    I: Iterator<Item = P>,
    P: IntoSubdivisions,
    P::Point: Clone + Interpolate,
{
    fn subdivide(self) -> Decompose<Self, P, P, P::Output> {
        Decompose::new(self, P::into_subdivisions)
    }
}

pub trait Tetrahedrons<T>: Sized {
    #[allow(type_complexity)]
    fn tetrahedrons(self) -> Decompose<Self, Quad<T>, Triangle<T>, ArrayVec<[Triangle<T>; 4]>>;
}

impl<I, T> Tetrahedrons<T> for I
where
    I: Iterator<Item = Quad<T>>,
    T: Clone + Interpolate,
{
    #[allow(type_complexity)]
    fn tetrahedrons(self) -> Decompose<Self, Quad<T>, Triangle<T>, ArrayVec<[Triangle<T>; 4]>> {
        Decompose::new(self, Quad::into_tetrahedrons)
    }
}

fn remap<I, P, R, F>(n: usize, primitives: I, f: F) -> Vec<P>
where
    I: IntoIterator<Item = P>,
    R: IntoIterator<Item = P>,
    F: Fn(P) -> R,
{
    let mut primitives: Vec<_> = primitives.into_iter().collect();
    for _ in 0..n {
        primitives = primitives.into_iter().flat_map(&f).collect();
    }
    primitives
}
<commit_msg>Name `remap` iterator and remove unnecessary `collect`.<commit_after>//! This module provides a generic iterator and traits for decomposing
//! primitives and tessellating polygons.

use arrayvec::ArrayVec;
use std::collections::{vec_deque, VecDeque};
use std::iter::{Chain, IntoIterator, Rev};
use std::vec;

use math;
use super::primitive::{Line, Polygon, Polygonal, Primitive, Triangle, Quad};

pub struct Decompose<I, P, Q, R>
where
    R: IntoIterator<Item = Q>,
{
    input: I,
    output: VecDeque<Q>,
    f: fn(P) -> R,
}

impl<I, P, Q, R> Decompose<I, P, Q, R>
where
    R: IntoIterator<Item = Q>,
{
    pub(super) fn new(input: I, f: fn(P) -> R) -> Self {
        Decompose {
            input: input,
            output: VecDeque::new(),
            f: f,
        }
    }
}

// Names the iterator fed into the `Decompose` adapter in `remap`.
type Remap<P> = Chain<Rev<vec_deque::IntoIter<P>>, vec::IntoIter<P>>;

impl<I, P, R> Decompose<I, P, P, R>
where
    I: Iterator<Item = P>,
    R: IntoIterator<Item = P>,
{
    // TODO: This is questionable, but acts as a replacement for optional
    //       parameters used by the `Into*` traits. In particular,
    //       `into_subdivisions` no longer accepts a parameter `n`, and `remap`
    //       can be used to emulate that behavior. This is especially useful
    //       for larger `n` values, where chaining calls to `subdivide` is not
    //       practical.
    pub fn remap(self, n: usize) -> Decompose<Remap<P>, P, P, R> {
        let Decompose { input, output, f } = self;
        Decompose::new(output.into_iter().rev().chain(remap(n, input, f)), f)
    }
}

impl<I, P, Q, R> Iterator for Decompose<I, P, Q, R>
where
    I: Iterator<Item = P>,
    R: IntoIterator<Item = Q>,
{
    type Item = Q;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if let Some(primitive) = self.output.pop_front() {
                return Some(primitive);
            }
            if let Some(primitive) = self.input.next() {
                self.output.extend((self.f)(primitive));
            }
            else {
                return None;
            }
        }
    }
}

pub trait Interpolate: math::Interpolate<f32> {}

impl<T> Interpolate for T
where
    T: math::Interpolate<f32>,
{
}

pub trait IntoPoints: Primitive {
    type Output: IntoIterator<Item = Self::Point>;

    fn into_points(self) -> Self::Output;
}

pub trait IntoLines: Primitive {
    type Output: IntoIterator<Item = Line<Self::Point>>;

    fn into_lines(self) -> Self::Output;
}

pub trait IntoTriangles: Polygonal {
    type Output: IntoIterator<Item = Triangle<Self::Point>>;

    fn into_triangles(self) -> Self::Output;
}

pub trait IntoSubdivisions: Polygonal
where
    Self::Point: Clone + Interpolate,
{
    type Output: IntoIterator<Item = Self>;

    fn into_subdivisions(self) -> Self::Output;
}

pub trait IntoTetrahedrons: Polygonal
where
    Self::Point: Clone + Interpolate,
{
    fn into_tetrahedrons(self) -> ArrayVec<[Triangle<Self::Point>; 4]>;
}

impl<T> IntoPoints for Line<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Self::Point; 2]>;

    fn into_points(self) -> Self::Output {
        let Line { a, b } = self;
        ArrayVec::from([a, b])
    }
}

impl<T> IntoPoints for Triangle<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Self::Point; 3]>;

    fn into_points(self) -> Self::Output {
        let Triangle { a, b, c } = self;
        ArrayVec::from([a, b, c])
    }
}

impl<T> IntoPoints for Quad<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Self::Point; 4]>;

    fn into_points(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        ArrayVec::from([a, b, c, d])
    }
}

impl<T> IntoPoints for Polygon<T>
where
    T: Clone,
{
    type Output = Vec<Self::Point>;

    fn into_points(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => triangle.into_points().into_iter().collect(),
            Polygon::Quad(quad) => quad.into_points().into_iter().collect(),
        }
    }
}

impl<T> IntoLines for Line<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Line<Self::Point>; 1]>;

    fn into_lines(self) -> Self::Output {
        ArrayVec::from([self])
    }
}

impl<T> IntoLines for Triangle<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Line<Self::Point>; 3]>;

    fn into_lines(self) -> Self::Output {
        let Triangle { a, b, c } = self;
        ArrayVec::from([
            Line::new(a.clone(), b.clone()),
            Line::new(b, c.clone()),
            Line::new(c, a),
        ])
    }
}

impl<T> IntoLines for Quad<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Line<Self::Point>; 4]>;

    fn into_lines(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        ArrayVec::from([
            Line::new(a.clone(), b.clone()),
            Line::new(b, c.clone()),
            Line::new(c, d.clone()),
            Line::new(d, a),
        ])
    }
}

impl<T> IntoLines for Polygon<T>
where
    T: Clone,
{
    type Output = Vec<Line<Self::Point>>;

    fn into_lines(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => triangle.into_lines().into_iter().collect(),
            Polygon::Quad(quad) => quad.into_lines().into_iter().collect(),
        }
    }
}

impl<T> IntoTriangles for Triangle<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Triangle<Self::Point>; 1]>;

    fn into_triangles(self) -> Self::Output {
        ArrayVec::from([self])
    }
}

impl<T> IntoTriangles for Quad<T>
where
    T: Clone,
{
    type Output = ArrayVec<[Triangle<Self::Point>; 2]>;

    fn into_triangles(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        ArrayVec::from([
            Triangle::new(a.clone(), b, c.clone()),
            Triangle::new(c, d, a),
        ])
    }
}

impl<T> IntoTriangles for Polygon<T>
where
    T: Clone,
{
    type Output = Vec<Triangle<Self::Point>>;

    fn into_triangles(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => triangle.into_triangles().into_iter().collect(),
            Polygon::Quad(quad) => quad.into_triangles().into_iter().collect(),
        }
    }
}

impl<T> IntoSubdivisions for Triangle<T>
where
    T: Clone + Interpolate,
{
    type Output = ArrayVec<[Triangle<Self::Point>; 2]>;

    fn into_subdivisions(self) -> Self::Output {
        let Triangle { a, b, c } = self;
        let ac = a.midpoint(&c);
        ArrayVec::from([
            Triangle::new(b.clone(), ac.clone(), a),
            Triangle::new(c, ac, b),
        ])
    }
}

impl<T> IntoSubdivisions for Quad<T>
where
    T: Clone + Interpolate,
{
    type Output = ArrayVec<[Quad<Self::Point>; 4]>;

    fn into_subdivisions(self) -> Self::Output {
        let Quad { a, b, c, d } = self;
        let ab = a.midpoint(&b);
        let bc = b.midpoint(&c);
        let cd = c.midpoint(&d);
        let da = d.midpoint(&a);
        let ac = a.midpoint(&c); // Diagonal.
        ArrayVec::from([
            Quad::new(a, ab.clone(), ac.clone(), da.clone()),
            Quad::new(ab, b, bc.clone(), ac.clone()),
            Quad::new(ac.clone(), bc, c, cd.clone()),
            Quad::new(da, ac, cd, d),
        ])
    }
}

impl<T> IntoTetrahedrons for Quad<T>
where
    T: Clone + Interpolate,
{
    fn into_tetrahedrons(self) -> ArrayVec<[Triangle<Self::Point>; 4]> {
        let Quad { a, b, c, d } = self;
        let ac = a.midpoint(&c); // Diagonal.
        ArrayVec::from([
            Triangle::new(a.clone(), b.clone(), ac.clone()),
            Triangle::new(b, c.clone(), ac.clone()),
            Triangle::new(c, d.clone(), ac.clone()),
            Triangle::new(d, a, ac),
        ])
    }
}

impl<T> IntoSubdivisions for Polygon<T>
where
    T: Clone + Interpolate,
{
    type Output = Vec<Self>;

    fn into_subdivisions(self) -> Self::Output {
        match self {
            Polygon::Triangle(triangle) => {
                triangle
                    .into_subdivisions()
                    .into_iter()
                    .map(|triangle| triangle.into())
                    .collect()
            }
            Polygon::Quad(quad) => {
                quad.into_subdivisions()
                    .into_iter()
                    .map(|quad| quad.into())
                    .collect()
            }
        }
    }
}

pub trait Points<P>: Sized
where
    P: IntoPoints,
{
    fn points(self) -> Decompose<Self, P, P::Point, P::Output>;
}

impl<I, P> Points<P> for I
where
    I: Iterator<Item = P>,
    P: IntoPoints,
    P::Point: Clone,
{
    fn points(self) -> Decompose<Self, P, P::Point, P::Output> {
        Decompose::new(self, P::into_points)
    }
}

pub trait Lines<P>: Sized
where
    P: IntoLines,
{
    fn lines(self) -> Decompose<Self, P, Line<P::Point>, P::Output>;
}

impl<I, P> Lines<P> for I
where
    I: Iterator<Item = P>,
    P: IntoLines,
    P::Point: Clone,
{
    fn lines(self) -> Decompose<Self, P, Line<P::Point>, P::Output> {
        Decompose::new(self, P::into_lines)
    }
}

pub trait Triangulate<P>: Sized
where
    P: IntoTriangles,
{
    fn triangulate(self) -> Decompose<Self, P, Triangle<P::Point>, P::Output>;
}

impl<I, P> Triangulate<P> for I
where
    I: Iterator<Item = P>,
    P: IntoTriangles,
    P::Point: Clone,
{
    fn triangulate(self) -> Decompose<Self, P, Triangle<P::Point>, P::Output> {
        Decompose::new(self, P::into_triangles)
    }
}

pub trait Subdivide<P>: Sized
where
    P: IntoSubdivisions,
    P::Point: Clone + Interpolate,
{
    fn subdivide(self) -> Decompose<Self, P, P, P::Output>;
}

impl<I, P> Subdivide<P> for I
where
    I: Iterator<Item = P>,
    P: IntoSubdivisions,
    P::Point: Clone + Interpolate,
{
    fn subdivide(self) -> Decompose<Self, P, P, P::Output> {
        Decompose::new(self, P::into_subdivisions)
    }
}

pub trait Tetrahedrons<T>: Sized {
    #[allow(type_complexity)]
    fn tetrahedrons(self) -> Decompose<Self, Quad<T>, Triangle<T>, ArrayVec<[Triangle<T>; 4]>>;
}

impl<I, T> Tetrahedrons<T> for I
where
    I: Iterator<Item = Quad<T>>,
    T: Clone + Interpolate,
{
    #[allow(type_complexity)]
    fn tetrahedrons(self) -> Decompose<Self, Quad<T>, Triangle<T>, ArrayVec<[Triangle<T>; 4]>> {
        Decompose::new(self, Quad::into_tetrahedrons)
    }
}

fn remap<I, P, R, F>(n: usize, primitives: I, f: F) -> Vec<P>
where
    I: IntoIterator<Item = P>,
    R: IntoIterator<Item = P>,
    F: Fn(P) -> R,
{
    let mut primitives: Vec<_> = primitives.into_iter().collect();
    for _ in 0..n {
        primitives = primitives.into_iter().flat_map(&f).collect();
    }
    primitives
}
<|endoftext|>


<commit_before>/* diosix hypervisor code for handling hardware interrupts and software exceptions
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::scheduler;
use super::capsule;
use super::pcore;
use super::hardware;

/* platform-specific code must implement all this */
use platform;
use platform::irq::{IRQContext, IRQType, IRQCause, IRQSeverity, IRQ};
use platform::cpu::PrivilegeMode;
use platform::instructions::{self, EmulationResult};
use platform::syscalls;
use platform::timer;

/* hypervisor_irq_handler
   entry point for hardware interrupts and software exceptions, collectively known as IRQs.
   call down into platform-specific handlers
   => context = platform-specific context of the IRQ, which may be modified depending
   on the IRQ raised.
*/
#[no_mangle]
pub extern "C" fn hypervisor_irq_handler(mut context: IRQContext)
{
    /* if dispatch() returns an IRQ context then we need to handle it here
    at the high level. if it returns None, the platform-specific code handled it.
    note: the platform library should take care of hardware specfic things like
    catching illegal instructions that can be fixed up and handled transparently */
    if let Some(irq) = platform::irq::dispatch(context)
    {
        match irq.irq_type
        {
            IRQType::Exception => exception(irq, &mut context),
            IRQType::Interrupt => interrupt(irq, &mut context),
        };
    }
}

/* handle software exception */
fn exception(irq: IRQ, context: &mut IRQContext)
{
    match (irq.severity, irq.privilege_mode, irq.cause)
    {
        /* catch illegal instructions we may be able to emulate */
        (_, PrivilegeMode::Supervisor, IRQCause::IllegalInstruction) =>
        {
            match instructions::emulate(irq.privilege_mode, context)
            {
                EmulationResult::Success => (), /* nothing more to do, return */
                EmulationResult::Yield =>
                {
                    /* instruction was some kind of sleep or pause operation.
                    try to find something else to run in the meantime */
                    scheduler::run_next(scheduler::SearchMode::CheckOnce);
                },

                /* if we can't handle the instruction,
                kill the capsule and force a context switch.
                TODO: is killing the whole capsule a little extreme? */
                _ => fatal_exception(&irq)
            }
        },

        /* catch environment calls from supervisor mode */
        (_, PrivilegeMode::Supervisor, IRQCause::SupervisorEnvironmentCall) =>
        {
            /* determine what we need to do from the platform code's decoding */
            if let Some(action) = syscalls::handler(context)
            {
                match action
                {
                    syscalls::Action::Terminate => terminate_running_capsule(),
                    syscalls::Action::Restart => restart_running_capsule(),
                    syscalls::Action::TimerIRQAt(target) =>
                    {
                        /* mark this virtual core as awaiting a timer IRQ and
                        schedule a timer interrupt in anticipation */
                        pcore::PhysicalCore::set_virtualcore_timer_target(Some(target));
                        hardware::scheduler_timer_at(target);
                    },

                    /* stash debug output character into capsule's buffer */
                    syscalls::Action::OutputChar(character) => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        if let Err(_e) = capsule::debug_write(capsule_id, character)
                        {
                            hvdebug!("Couldn't buffer debug byte {} from capsule {}: {:?}", character, capsule_id, _e);
                        }
                    },

                    _ => if let Some(c) = pcore::PhysicalCore::get_capsule_id()
                    {
                        hvalert!("Capsule {}: Unhandled syscall: {:x?} at 0x{:x}", c, action, irq.pc);
                    }
                    else
                    {
                        hvdebug!("Unhandled syscall: {:x?} at 0x{:x} in unknown capsule", action, irq.pc);
                    }
                }
            }
        },

        /* catch everything else, halting if fatal */
        (severity, privilege, cause) =>
        {
            /* if an unhandled fatal exception reaches us here from the supervisor or user mode,
            kill the capsule. if the hypervisor can't handle its own fatal exception, give up */
            match privilege
            {
                PrivilegeMode::Supervisor | PrivilegeMode::User => if severity == IRQSeverity::Fatal
                {
                    /* TODO: is it wise to blow away the whole capsule for a user exception?
                    the supervisor should really catch its user-level faults */
                    fatal_exception(&irq);
                },
                PrivilegeMode::Machine =>
                {
                    if severity == IRQSeverity::Fatal
                    {
                        hvalert!("Halting physical CPU core for {:?} at 0x{:x}, stack 0x{:x}", cause, irq.pc, irq.sp);
                        debughousekeeper!(); // flush the debug output
                        loop {}
                    }
                }
            }
        }
    }
}

/* handle hardware interrupt */
fn interrupt(irq: IRQ, _: &mut IRQContext)
{
    match irq.cause
    {
        IRQCause::MachineTimer =>
        {
            /* make a scheduling decision and raise any supervior-level timer IRQs*/
            scheduler::ping();
            check_supervisor_timer_irq();
        },
        _ => hvdebug!("Unhandled hardware interrupt: {:?}", irq.cause)
    }

    /* clear the interrupt condition */
    platform::irq::acknowledge(irq);
}

/* is the virtual core we're about to run awaiting a timer IRQ?
if so, and if its timer target value has been passed, generate a pending timer IRQ */
fn check_supervisor_timer_irq()
{
    if let Some(target) = pcore::PhysicalCore::get_virtualcore_timer_target()
    {
        match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
        {
            (Some(time), Some(freq)) =>
            {
                let current = time.to_exact(freq);
                if current >= target.to_exact(freq)
                {
                    /* create a pending timer IRQ for the supervisor kernel and clear the target */
                    timer::trigger_supervisor_irq();
                    pcore::PhysicalCore::set_virtualcore_timer_target(None);
                }
            },
            (_, _) => ()
        }
    }
}

/* kill the running capsule, alert the user, and then find something else to run */
fn fatal_exception(irq: &IRQ)
{
    hvalert!("Terminating running capsule for {:?} at 0x{:x}, stack 0x{:x}", irq.cause, irq.pc, irq.sp);

    /* terminate the capsule running on this core */
    terminate_running_capsule();

    /* force a context switch to find another virtual core to run */
    scheduler::run_next(scheduler::SearchMode::MustFind);
}

/* terminate the capsule running on this core */
fn terminate_running_capsule()
{
    match capsule::destroy_current()
    {
        Err(e) => hvalert!("Failed to kill running capsule ({:?})", e),
        _ => hvdebug!("Terminated running capsule")
    }
}

/* restart the running capsule, if possible */
fn restart_running_capsule()
{
    match capsule::restart_current()
    {
        Err(e) => hvalert!("Failed to restart running capsule ({:?})", e),
        _ => ()
    }
}<commit_msg>Delegate scheduling decisions to the scheduler Call ping() to make the scheduler take a decision after changes have been made to capsules rather than trying to do the scheduler's job in an IRQ handler. Also catch errors in killing or restarting capsules, or writing debug, and report them back to the syscall caller, if we're in a syscall context<commit_after>/* diosix hypervisor code for handling hardware interrupts and software exceptions
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::scheduler;
use super::capsule;
use super::pcore;
use super::hardware;

/* platform-specific code must implement all this */
use platform;
use platform::irq::{IRQContext, IRQType, IRQCause, IRQSeverity, IRQ};
use platform::cpu::PrivilegeMode;
use platform::instructions::{self, EmulationResult};
use platform::syscalls;
use platform::timer;

/* hypervisor_irq_handler
   entry point for hardware interrupts and software exceptions, collectively known as IRQs.
   call down into platform-specific handlers
   => context = platform-specific context of the IRQ, which may be modified depending
   on the IRQ raised.
*/
#[no_mangle]
pub extern "C" fn hypervisor_irq_handler(mut context: IRQContext)
{
    /* if dispatch() returns an IRQ context then we need to handle it here
    at the high level. if it returns None, the platform-specific code handled it.
    note: the platform library should take care of hardware specfic things like
    catching illegal instructions that can be fixed up and handled transparently */
    if let Some(irq) = platform::irq::dispatch(context)
    {
        match irq.irq_type
        {
            IRQType::Exception => exception(irq, &mut context),
            IRQType::Interrupt => interrupt(irq, &mut context),
        };
    }
}

/* handle software exception */
fn exception(irq: IRQ, context: &mut IRQContext)
{
    match (irq.severity, irq.privilege_mode, irq.cause)
    {
        /* catch illegal instructions we may be able to emulate */
        (_, PrivilegeMode::Supervisor, IRQCause::IllegalInstruction) =>
        {
            match instructions::emulate(irq.privilege_mode, context)
            {
                EmulationResult::Success => (), /* nothing more to do, return */
                EmulationResult::Yield =>
                {
                    /* instruction was some kind of sleep or pause operation.
                    try to find something else to run in the meantime */
                    scheduler::ping();
                },

                /* if we can't handle the instruction,
                kill the capsule and force a context switch.
                TODO: is killing the whole capsule a little extreme? */
                _ => fatal_exception(&irq)
            }
        },

        /* catch environment calls from supervisor mode */
        (_, PrivilegeMode::Supervisor, IRQCause::SupervisorEnvironmentCall) =>
        {
            /* determine what we need to do from the platform code's decoding */
            if let Some(action) = syscalls::handler(context)
            {
                match action
                {
                    syscalls::Action::Terminate => if let Err(_e) = capsule::destroy_current()
                    {
                        hvalert!("BUG: Failed to terminate currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is dead */
                        scheduler::ping();
                    },

                    syscalls::Action::Restart => if let Err(_e) = capsule::restart_current()
                    {
                        hvalert!("BUG: Failed to restart currently running capsule ({:?})", _e);
                        syscalls::failed(context, syscalls::ActionResult::Failed);
                    }
                    else
                    {
                        /* find something else to run, this virtual core is being replaced */
                        scheduler::ping();
                    },

                    syscalls::Action::TimerIRQAt(target) =>
                    {
                        /* mark this virtual core as awaiting a timer IRQ and
                        schedule a timer interrupt in anticipation */
                        pcore::PhysicalCore::set_virtualcore_timer_target(Some(target));
                        hardware::scheduler_timer_at(target);
                    },

                    /* stash debug output character into capsule's buffer */
                    syscalls::Action::OutputChar(character) => if let Some(capsule_id) = pcore::PhysicalCore::get_capsule_id()
                    {
                        if let Err(_e) = capsule::debug_write(capsule_id, character)
                        {
                            hvdebug!("Couldn't buffer debug byte {} from capsule {}: {:?}", character, capsule_id, _e);
                            syscalls::failed(context, syscalls::ActionResult::Failed);
                        }
                    },

                    _ => if let Some(c) = pcore::PhysicalCore::get_capsule_id()
                    {
                        hvalert!("Capsule {}: Unhandled syscall: {:x?} at 0x{:x}", c, action, irq.pc);
                    }
                    else
                    {
                        hvdebug!("Unhandled syscall: {:x?} at 0x{:x} in unknown capsule", action, irq.pc);
                    }
                }
            }
        },

        /* catch everything else, halting if fatal */
        (severity, privilege, cause) =>
        {
            /* if an unhandled fatal exception reaches us here from the supervisor or user mode,
            kill the capsule. if the hypervisor can't handle its own fatal exception, give up */
            match privilege
            {
                PrivilegeMode::Supervisor | PrivilegeMode::User => if severity == IRQSeverity::Fatal
                {
                    /* TODO: is it wise to blow away the whole capsule for a user exception?
                    the supervisor should really catch its user-level faults */
                    fatal_exception(&irq);
                },
                PrivilegeMode::Machine =>
                {
                    if severity == IRQSeverity::Fatal
                    {
                        hvalert!("Halting physical CPU core for {:?} at 0x{:x}, stack 0x{:x} integrity {:?}",
                            cause, irq.pc, irq.sp, pcore::PhysicalCore::integrity_check());
                        debughousekeeper!(); // flush the debug output
                        loop {}
                    }
                }
            }
        }
    }
}

/* handle hardware interrupt */
fn interrupt(irq: IRQ, _: &mut IRQContext)
{
    match irq.cause
    {
        IRQCause::MachineTimer =>
        {
            /* make a scheduling decision and raise any supervior-level timer IRQs*/
            scheduler::ping();
            check_supervisor_timer_irq();
        },
        _ => hvdebug!("Unhandled hardware interrupt: {:?}", irq.cause)
    }

    /* clear the interrupt condition */
    platform::irq::acknowledge(irq);
}

/* is the virtual core we're about to run awaiting a timer IRQ?
if so, and if its timer target value has been passed, generate a pending timer IRQ */
fn check_supervisor_timer_irq()
{
    if let Some(target) = pcore::PhysicalCore::get_virtualcore_timer_target()
    {
        match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
        {
            (Some(time), Some(freq)) =>
            {
                let current = time.to_exact(freq);
                if current >= target.to_exact(freq)
                {
                    /* create a pending timer IRQ for the supervisor kernel and clear the target */
                    timer::trigger_supervisor_irq();
                    pcore::PhysicalCore::set_virtualcore_timer_target(None);
                }
            },
            (_, _) => ()
        }
    }
}

/* kill the running capsule, alert the user, and then find something else to run.
   if the capsule is important enough to auto-restart-on-crash, try to revive it */
fn fatal_exception(irq: &IRQ)
{
    hvalert!("Terminating running capsule {} for {:?} at 0x{:x}, stack 0x{:x}",
        match pcore::PhysicalCore::this().get_virtualcore_id()
        {
            Some(id) => format!("{}.{}", id.capsuleid, id.vcoreid),
            None => format!("[unknown!]")
        }, irq.cause, irq.pc, irq.sp);

    let mut terminate = false; // when true, destroy the current capsule
    let mut reschedule = false; // when true, we must find another vcore to run

    match capsule::is_current_autorestart()
    {
        Some(true) =>
        {
            hvalert!("Restarting capsule due to auto-restart-on-crash flag");
            if let Err(err) = capsule::restart_current()
            {
                hvalert!("Can't restart capsule ({:?}), letting it die instead", err);
                terminate = true;
            }
            else
            {
                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        },
        Some(false) => terminate = true,
        None =>
        {
            hvalert!("BUG: fatal_exception() can't find the running capsule to kill");
            return;
        },
    }

    if terminate == true
    {
        match capsule::destroy_current()
        {
            Err(e) => hvalert!("BUG: Failed to kill running capsule ({:?})", e),
            _ =>
            {
                hvdebug!("Terminated running capsule");

                /* the current vcore is no longer running due to restart */
                reschedule = true;
            }
        }
    }

    if reschedule == true
    {
        /* force a context switch to find another virtual core to run
        because this virtual core no long exists */
        scheduler::ping();
    }
}<|endoftext|>


<commit_before>
//! Apply various kinds of transformations mid-stream.

use event;
use template;
use modifier;

/// A stream that can output either one of two kinds of streams depending on an
/// externally supplied criteria.
#[derive(Debug)]
pub struct ApplyEither<S1, S2> {
    stream: OneOf<S1, S2>
}

impl<S1, S2> ApplyEither<S1, S2> {

    pub(crate) fn first(stream: S1) -> ApplyEither<S1, S2> {
        ApplyEither {
            stream: OneOf::First(stream),
        }
    }

    pub(crate) fn second(stream: S2) -> ApplyEither<S1, S2> {
        ApplyEither {
            stream: OneOf::Second(stream),
        }
    }
}

impl<S1, S2> event::ElementStream for ApplyEither<S1, S2>
where
    S1: event::ElementStream,
    S2: event::ElementStream,
{}

impl<S1, S2> event::Stream for ApplyEither<S1, S2>
where
    S1: event::Stream,
    S2: event::Stream,
{
    fn next_event(&mut self) -> event::StreamResult {
        match self.stream {
            OneOf::First(ref mut stream) => stream.next_event(),
            OneOf::Second(ref mut stream) => stream.next_event(),
        }
    }
}

#[derive(Debug)]
enum OneOf<S1, S2> {
    First(S1),
    Second(S2),
}

/// A stream transformation with intermediate template storage.
#[derive(Debug)]
pub struct TemplateApply<S, B> {
    state: modifier::State<TemplateState<S, B>>,
}

impl<S, B> TemplateApply<S, B> {

    pub(crate) fn new(stream: S, builder: B) -> TemplateApply<S, B> {
        TemplateApply {
            state: modifier::State::new(TemplateState::Collect { stream, builder }),
        }
    }
}

impl<S, B> event::Stream for TemplateApply<S, B>
where
    S: event::Stream,
    B: FnOnce(template::Template) -> Result<template::Template, event::StreamError>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| match state {
            TemplateState::Collect { stream, builder } => {
                let template = template::Template::from_stream(stream)?;
                let applied = builder(template)?;
                let stream = applied.to_stream();
                Ok(Some((event::noop(), Some(TemplateState::Emit { stream }))))
            },
            TemplateState::Emit { stream } =>
                modifier::passthrough(stream, |stream| TemplateState::Emit { stream }),
        })
    }
}

#[derive(Debug)]
enum TemplateState<S, B> {
    Collect {
        stream: S,
        builder: B,
    },
    Emit {
        stream: template::TemplateStream,
    },
}

#[cfg(test)]
mod tests {

    test_group!(apply_as_template:
        "with closure" => transform_test!(
            "23",
            "foo",
            |html| html.select("b", |html| html.apply_as_template(|template| template
                .transform(|html| html.select("b", |html| html.replace_contents("foo")))
            )),
        ),
    );

    test_group!(apply:
        "with closure" => transform_test!(
            "23",
            "",
            |html| html.select("b", |html| html.apply(|html| html.remove_contents())),
        ),
    );

    test_group!(apply_if_else:
        "true" => transform_test!(
            "23",
            "99",
            |html| html.select("b", |html| html.apply_if_else(
                true,
                |html| html.replace_contents("99"),
                |html| html.replace_contents("33"),
            )),
        ),
        "false" => transform_test!(
            "23",
            "33",
            |html| html.select("b", |html| html.apply_if_else(
                false,
                |html| html.replace_contents("99"),
                |html| html.replace_contents("33"),
            )),
        ),
    );

    test_group!(apply_if:
        "true" => transform_test!(
            "23",
            "99",
            |html| html.select("b", |html| html
                .apply_if(true, |html| html.replace_contents("99"))
            ),
        ),
        "false" => transform_test!(
            "23",
            "23",
            |html| html.select("b", |html| html
                .apply_if(false, |html| html.replace_contents("99"))
            ),
        ),
    );
}
<commit_msg>element stream tests for apply<commit_after>
//! Apply various kinds of transformations mid-stream.

use event;
use template;
use modifier;

/// A stream that can output either one of two kinds of streams depending on an
/// externally supplied criteria.
#[derive(Debug)]
pub struct ApplyEither<S1, S2> {
    stream: OneOf<S1, S2>
}

impl<S1, S2> ApplyEither<S1, S2> {

    pub(crate) fn first(stream: S1) -> ApplyEither<S1, S2> {
        ApplyEither {
            stream: OneOf::First(stream),
        }
    }

    pub(crate) fn second(stream: S2) -> ApplyEither<S1, S2> {
        ApplyEither {
            stream: OneOf::Second(stream),
        }
    }
}

impl<S1, S2> event::ElementStream for ApplyEither<S1, S2>
where
    S1: event::ElementStream,
    S2: event::ElementStream,
{}

impl<S1, S2> event::Stream for ApplyEither<S1, S2>
where
    S1: event::Stream,
    S2: event::Stream,
{
    fn next_event(&mut self) -> event::StreamResult {
        match self.stream {
            OneOf::First(ref mut stream) => stream.next_event(),
            OneOf::Second(ref mut stream) => stream.next_event(),
        }
    }
}

#[derive(Debug)]
enum OneOf<S1, S2> {
    First(S1),
    Second(S2),
}

/// A stream transformation with intermediate template storage.
#[derive(Debug)]
pub struct TemplateApply<S, B> {
    state: modifier::State<TemplateState<S, B>>,
}

impl<S, B> TemplateApply<S, B> {

    pub(crate) fn new(stream: S, builder: B) -> TemplateApply<S, B> {
        TemplateApply {
            state: modifier::State::new(TemplateState::Collect { stream, builder }),
        }
    }
}

impl<S, B> event::Stream for TemplateApply<S, B>
where
    S: event::Stream,
    B: FnOnce(template::Template) -> Result<template::Template, event::StreamError>,
{
    fn next_event(&mut self) -> event::StreamResult {
        self.state.step(|state| match state {
            TemplateState::Collect { stream, builder } => {
                let template = template::Template::from_stream(stream)?;
                let applied = builder(template)?;
                let stream = applied.to_stream();
                Ok(Some((event::noop(), Some(TemplateState::Emit { stream }))))
            },
            TemplateState::Emit { stream } =>
                modifier::passthrough(stream, |stream| TemplateState::Emit { stream }),
        })
    }
}

#[derive(Debug)]
enum TemplateState<S, B> {
    Collect {
        stream: S,
        builder: B,
    },
    Emit {
        stream: template::TemplateStream,
    },
}

#[cfg(test)]
mod tests {

    test_group!(apply_as_template:
        "with closure" => transform_test!(
            "23",
            "foo",
            |html| html.select("b", |html| html.apply_as_template(|template| template
                .transform(|html| html.select("b", |html| html.replace_contents("foo")))
            )),
        ),
    );

    test_group!(apply:
        "with closure" => transform_test!(
            "23",
            "",
            |html| html.select("b", |html| html.apply(|html| html.remove_contents())),
        ),
        "ensure element streams" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("link", |html| html
                .apply(|html| html.into_boxed_element())
                .into_boxed_element()
            ),
        ),
    );

    test_group!(apply_if_else:
        "true" => transform_test!(
            "23",
            "99",
            |html| html.select("b", |html| html.apply_if_else(
                true,
                |html| html.replace_contents("99"),
                |html| html.replace_contents("33"),
            )),
        ),
        "false" => transform_test!(
            "23",
            "33",
            |html| html.select("b", |html| html.apply_if_else(
                false,
                |html| html.replace_contents("99"),
                |html| html.replace_contents("33"),
            )),
        ),
        "element stream if both are element streams" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("link", |html| html
                .apply_if_else(
                    true,
                    |html| html.into_boxed_element(),
                    |html| html.into_boxed_element(),
                )
                .into_boxed_element()
            ),
        ),
    );

    test_group!(apply_if:
        "true" => transform_test!(
            "23",
            "99",
            |html| html.select("b", |html| html
                .apply_if(true, |html| html.replace_contents("99"))
            ),
        ),
        "false" => transform_test!(
            "23",
            "23",
            |html| html.select("b", |html| html
                .apply_if(false, |html| html.replace_contents("99"))
            ),
        ),
        "element stream if both are element streams" => transform_test!(
            "<link>",
            "<link>",
            |html| html.select("link", |html| html
                .apply_if(
                    true,
                    |html| html.into_boxed_element(),
                )
                .into_boxed_element()
            ),
        ),
    );
}
<|endoftext|>


<commit_before>// This function is unstable yet, tests ignored

use index::btree::node::read_node;
use index::btree::node::read_unchecked;
use index::btree::node::write_node;
use index::btree::node::write_targeted;
use index::btree::node::EmptyNode;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::node::NodeWriteGuard;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Slice;
use std::fmt::Debug;
use std::mem;
use index::btree::external::ExtNode;

pub enum RemoveSearchResult {
    External,
    RightNode(NodeCellRef),
    Internal(NodeCellRef),
}

pub enum SubNodeStatus {
    ExtNodeEmpty,
    InNodeEmpty,
    Relocate(usize, usize),
    Merge(usize, usize),
    Ok,
}

pub struct RebalancingNodes<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub left_guard: NodeWriteGuard<KS, PS>,
    pub left_ref: NodeCellRef,
    pub right_guard: NodeWriteGuard<KS, PS>,
    pub right_right_guard: NodeWriteGuard<KS, PS>, // for external pointer modification
    pub parent: NodeWriteGuard<KS, PS>,
    pub parent_pos: usize,
}

pub struct RemoveResult<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub rebalancing: Option<RebalancingNodes<KS, PS>>,
    pub removed: bool,
}

pub fn with_innode_removing<F, KS, PS>(
    key: &EntryKey,
    mut rebalancing: RebalancingNodes<KS, PS>,
    parent: &NodeCellRef,
    parent_parent: &NodeCellRef,
    removed: bool,
    level: usize,
    func: F,
) -> RemoveResult<KS, PS>
where
    F: Fn(&mut RebalancingNodes<KS, PS>),
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let parent_half_full = rebalancing.parent.is_half_full();
    let mut parent_right_guard = write_node(&rebalancing.parent.innode_mut().right);
    let mut parent_remove_result = RemoveResult {
        rebalancing: None,
        removed,
    };

    let parent_parent_guard;
    let parent_parent_remove_pos;
    let parent_right_right_guard;
    if level == 0 {
        parent_parent_guard = write_node(parent_parent);
        parent_right_right_guard = None;
        parent_parent_remove_pos = 0;
    } else {
        let pre_locked_parent_right_right_guard = parent_right_guard
            .right_ref_mut_no_empty()
            .map(|r| write_node(r));
        let pp_guard = write_targeted(write_node(parent_parent), key);
        parent_parent_guard = pp_guard;
        parent_parent_remove_pos = parent_parent_guard.search(key);
        let parent_right_half_full = parent_right_guard.is_half_full();
        parent_right_right_guard = if parent_parent_guard.len() > parent_parent_remove_pos + 1
            && (!parent_half_full || !parent_right_half_full)
        {
            // indicates whether the upper parent level need to relocated
            pre_locked_parent_right_right_guard
        } else {
            None
        };
    }

    func(&mut rebalancing);
    parent_remove_result.rebalancing =
        parent_right_right_guard.map(|parent_right_right_guard_stripped| RebalancingNodes {
            left_guard: rebalancing.parent,
            left_ref: parent.clone(),
            right_right_guard: parent_right_right_guard_stripped,
            right_guard: parent_right_guard,
            parent: parent_parent_guard,
            parent_pos: parent_parent_remove_pos,
        });
    parent_remove_result
}

pub fn remove_from_node<KS, PS>(
    tree: &BPlusTree<KS, PS>,
    node_ref: &mut NodeCellRef,
    key: &mut EntryKey,
    parent: &NodeCellRef,
    level: usize,
) -> RemoveResult<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("Removing {:?} from node, level {}", key, level);
    let search = mut_search::<KS, PS>(node_ref, key);
    match search {
        MutSearchResult::Internal(mut sub_node) => {
            let mut node_remove_res =
                remove_from_node(tree, &mut sub_node, key, node_ref, level + 1);
            let removed = node_remove_res.removed;
            if let Some(mut rebalancing) = node_remove_res.rebalancing {
                debug!(
                    "Need to rebalance sub nodes, key {:?}, level {}",
                    key, level
                );
                if rebalancing.left_guard.is_empty() && !rebalancing.right_guard.is_empty() {
                    debug!(
                        "Remove {:?} sub level left node is empty, level {}",
                        key, level
                    );
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // Remove the empty node that have a right node with the same parent
                            // Because we cannot lock from left to right, we have to move the content
                            // of the right node to the left and remove the right node instead so the
                            // left right pointers can be modified
                            if !rebalancing.left_guard.is_empty()
                                || (level == 0
                                    && read_unchecked::<KS, PS>(&(*tree.root.read())).first_key()
                                        != rebalancing.left_guard.first_key())
                            {
                                return;
                            }

                            let left_node = &mut *rebalancing.left_guard;
                            let right_node = &mut *rebalancing.right_guard;
                            // swap the content of left and right, then delete right
                            // this procedure will prevent locking left node for changing right reference
                            let left_left_ref = left_node.left_ref_mut().map(|r| r.clone());
                            let right_right_ref =
                                right_node.right_ref_mut_no_empty().map(|r| r.clone());
                            let left_ref = rebalancing.left_ref.clone();
                            // swap the empty node with the right node. In this case left node holds
                            // content of the right node but pointers need to be corrected.
                            mem::swap(left_node, right_node);
                            *right_node = NodeData::Empty(box EmptyNode {
                                left: Some(left_ref.clone()),
                                right: left_ref.clone(),
                            });
                            left_node
                                .left_ref_mut()
                                .map(|r| *r = left_left_ref.unwrap());
                            left_node
                                .right_ref_mut_no_empty()
                                .map(|r| *r = right_right_ref.unwrap());
                            rebalancing
                                .right_right_guard
                                .left_ref_mut()
                                .map(|r| *r = left_ref);
                            // remove the left ptr
                            rebalancing.parent.remove(rebalancing.parent_pos);
                            // point the right ptr to the replaced sub node
                            debug!(
                                "Removed sub level empty node, living node len {}, have {:?}",
                                left_node.len(),
                                left_node.keys()
                            );
                            rebalancing.parent.innode_mut().ptrs.as_slice()
                                [rebalancing.parent_pos + 1] = sub_node.clone();
                        },
                    ))
                } else if rebalancing.right_guard.is_empty() {
                    debug!(
                        "Remove {:?} sub level right node is empty, level {}",
                        key, level
                    );
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // There is a right empty node that can be deleted directly without hassle
                            let node_to_remove = &mut rebalancing.right_guard;
                            let owned_left_ref = rebalancing.left_ref.clone();
                            rebalancing.left_guard.right_ref_mut_no_empty().map(|r| {
                                *r = node_to_remove.right_ref_mut_no_empty().unwrap().clone()
                            });
                            rebalancing
                                .right_right_guard
                                .left_ref_mut()
                                .map(|r| *r = owned_left_ref);
                            rebalancing.parent.remove(rebalancing.parent_pos + 1);
                        },
                    ))
                } else if rebalancing.left_guard.cannot_merge()
                    || rebalancing.right_guard.cannot_merge()
                {
                    if rebalancing.right_guard.len() < KS::slice_len() / 3
                        && rebalancing.right_guard.len() < rebalancing.left_guard.len()
                        && rebalancing.left_guard.len() > KS::slice_len() / 2
                    {
                        // Relocate the nodes with the same parent for balance.
                        // For OLFIT, items can only be located from left to right.
                        debug!(
                            "Remove {:?} sub level need relocation, level {}",
                            key, level
                        );
                        let mut left_node = &mut *rebalancing.left_guard;
                        let mut right_node = &mut *rebalancing.right_guard;
                        let left_pos = rebalancing.parent_pos;
                        let right_pos = left_pos + 1;
                        rebalancing
                            .parent
                            .innode_mut()
                            .relocate_children(left_pos, right_pos, left_node, right_node);
                    }
                    None
                } else if rebalancing.left_guard.len() + rebalancing.right_guard.len() + 1
                    <= KS::slice_len()
                {
                    // Nodes with the same parent can merge
                    debug!(
                        "Remove {:?} sub level need to be merged, level {}",
                        key, level
                    );
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            let left_node = &mut *rebalancing.left_guard;
                            let right_node = &mut *rebalancing.right_guard;
                            let right_node_next = &mut *rebalancing.right_right_guard;
                            let left_pos = rebalancing.parent_pos;
                            let right_pos = left_pos + 1;
                            if left_node.is_empty() || right_node.is_empty() {
                                return;
                            }
                            rebalancing.parent.innode_mut().merge_children(
                                left_pos,
                                right_pos,
                                left_node,
                                right_node,
                                right_node_next,
                            );
                        },
                    ))
                // None
                } else if rebalancing.parent.len() == 1
                    && rebalancing.left_guard.is_empty()
                    && rebalancing.right_guard.is_empty()
                {
                    // this node and its children is empty, should be removed
                    debug!("Cleaning up empty level nodes, level {}", level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            rebalancing.parent.remove(0);
                            rebalancing.parent.innode_mut().ptrs.as_slice()[0] = Default::default();
                        },
                    ))
                } else {
                    None
                }
            } else {
                None
            }
            .unwrap_or_else(|| RemoveResult {
                rebalancing: None,
                removed,
            })
        }
        MutSearchResult::External => {
            let node_guard: NodeWriteGuard<KS, PS> = write_node(node_ref);
            let mut target_guard = write_targeted(node_guard, key);
            let target_guard_ref = target_guard.node_ref().clone();
            let mut remove_result = RemoveResult {
                rebalancing: None,
                removed: false,
            };
            {
                let is_left_half_full = target_guard.is_half_full();
                let mut node = target_guard.extnode_mut();
                let pos = node.search(key);
                let mut right_guard = write_node(&node.next);
                let _right_node_cannot_rebalance =
                    right_guard.is_none() || !right_guard.is_empty_node();
                if !right_guard.is_none() && !is_left_half_full || !right_guard.is_half_full() {
                    let right_right_guard =
                        write_node(&right_guard.right_ref_mut_no_empty().unwrap());
                    let parent_guard = write_node(parent);
                    let parent_target_guard = write_targeted(parent_guard, key);
                    let parent_pos = parent_target_guard.search(key);
                    // Check if the right node is innode and its parent is the same as the left one
                    // because we have to lock from left to right, there is no way to lock backwards
                    // if the empty non half-full node is at the right most of its parent
                    // So left over imbalanced such nodes will be expected and they can be eliminate
                    // by their left node remove operations.
                    if parent_pos < parent_target_guard.len() - 1 {
                        debug!("removing innode have a rebalance requirement");
                        remove_result.rebalancing = Some(RebalancingNodes {
                            left_ref: target_guard_ref,
                            left_guard: Default::default(),
                            parent: parent_target_guard,
                            parent_pos,
                            right_right_guard,
                            right_guard,
                        });
                    } else {
                        debug!("removing innode have a rebalance requirement but left-right node does not with the same parent");
                    }
                }
                if pos >= node.len {
                    debug!(
                        "Removing pos overflows external node, pos {}, len {}, expecting key {:?}, current keys {:?}, right keys {:?}",
                        pos, node.len, key, node.keys, read_unchecked::<KS, PS>(&node.next).keys()
                    );
                    remove_result.removed = false;
                }
                if &node.keys.as_slice()[pos] == key {
                    debug!("Removing key {:?} at {}, keys {:?}", key, pos, &node.keys);
                    node.remove_at(pos);
                    remove_result.removed = true;
                } else {
                    debug!(
                        "Search check failed for remove at pos {}, expecting {:?}, actual {:?}, have {:?}",
                        pos, key, &node.keys.as_slice_immute()[pos], &node.keys
                    );
                    remove_result.removed = false;
                }
            }
            if let Some(ref mut rebalance) = &mut remove_result.rebalancing {
                rebalance.left_guard = target_guard;
            }
            remove_result
        }
    }
}

// scatter the node and its references to ensure garbage collection
pub fn scatter_nodes<KS, PS>(node_ref: &NodeCellRef) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut footprint = 0;
    if node_ref.is_default() {
        return footprint;
    }
    let mut node = write_node::<KS, PS>(node_ref);
    loop {
        match &*node {
            &NodeData::None => return footprint,
            _ => {}
        }
        let right = node.right_ref().unwrap().clone();
        footprint += node.len();
        *node = NodeData::None;
        ExtNode::make_deleted(node.node_ref());
        node = write_node::<KS, PS>(&right);
    }
}
<commit_msg>fix compile issue<commit_after>// This function is unstable yet, tests ignored

use index::btree::node::read_node;
use index::btree::node::read_unchecked;
use index::btree::node::write_node;
use index::btree::node::write_targeted;
use index::btree::node::EmptyNode;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::node::NodeWriteGuard;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::BPlusTree;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Slice;
use std::fmt::Debug;
use std::mem;
use index::btree::external::ExtNode;

pub enum RemoveSearchResult {
    External,
    RightNode(NodeCellRef),
    Internal(NodeCellRef),
}

pub enum SubNodeStatus {
    ExtNodeEmpty,
    InNodeEmpty,
    Relocate(usize, usize),
    Merge(usize, usize),
    Ok,
}

pub struct RebalancingNodes<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub left_guard: NodeWriteGuard<KS, PS>,
    pub left_ref: NodeCellRef,
    pub right_guard: NodeWriteGuard<KS, PS>,
    pub right_right_guard: NodeWriteGuard<KS, PS>, // for external pointer modification
    pub parent: NodeWriteGuard<KS, PS>,
    pub parent_pos: usize,
}

pub struct RemoveResult<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub rebalancing: Option<RebalancingNodes<KS, PS>>,
    pub removed: bool,
}

pub fn with_innode_removing<F, KS, PS>(
    key: &EntryKey,
    mut rebalancing: RebalancingNodes<KS, PS>,
    parent: &NodeCellRef,
    parent_parent: &NodeCellRef,
    removed: bool,
    level: usize,
    func: F,
) -> RemoveResult<KS, PS>
where
    F: Fn(&mut RebalancingNodes<KS, PS>),
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let parent_half_full = rebalancing.parent.is_half_full();
    let mut parent_right_guard = write_node(&rebalancing.parent.innode_mut().right);
    let mut parent_remove_result = RemoveResult {
        rebalancing: None,
        removed,
    };

    let parent_parent_guard;
    let parent_parent_remove_pos;
    let parent_right_right_guard;
    if level == 0 {
        parent_parent_guard = write_node(parent_parent);
        parent_right_right_guard = None;
        parent_parent_remove_pos = 0;
    } else {
        let pre_locked_parent_right_right_guard = parent_right_guard
            .right_ref_mut_no_empty()
            .map(|r| write_node(r));
        let pp_guard = write_targeted(write_node(parent_parent), key);
        parent_parent_guard = pp_guard;
        parent_parent_remove_pos = parent_parent_guard.search(key);
        let parent_right_half_full = parent_right_guard.is_half_full();
        parent_right_right_guard = if parent_parent_guard.len() > parent_parent_remove_pos + 1
            && (!parent_half_full || !parent_right_half_full)
        {
            // indicates whether the upper parent level need to relocated
            pre_locked_parent_right_right_guard
        } else {
            None
        };
    }

    func(&mut rebalancing);
    parent_remove_result.rebalancing =
        parent_right_right_guard.map(|parent_right_right_guard_stripped| RebalancingNodes {
            left_guard: rebalancing.parent,
            left_ref: parent.clone(),
            right_right_guard: parent_right_right_guard_stripped,
            right_guard: parent_right_guard,
            parent: parent_parent_guard,
            parent_pos: parent_parent_remove_pos,
        });
    parent_remove_result
}

pub fn remove_from_node<KS, PS>(
    tree: &BPlusTree<KS, PS>,
    node_ref: &mut NodeCellRef,
    key: &mut EntryKey,
    parent: &NodeCellRef,
    level: usize,
) -> RemoveResult<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("Removing {:?} from node, level {}", key, level);
    let search = mut_search::<KS, PS>(node_ref, key);
    match search {
        MutSearchResult::Internal(mut sub_node) => {
            let mut node_remove_res =
                remove_from_node(tree, &mut sub_node, key, node_ref, level + 1);
            let removed = node_remove_res.removed;
            if let Some(mut rebalancing) = node_remove_res.rebalancing {
                debug!(
                    "Need to rebalance sub nodes, key {:?}, level {}",
                    key, level
                );
                if rebalancing.left_guard.is_empty() && !rebalancing.right_guard.is_empty() {
                    debug!(
                        "Remove {:?} sub level left node is empty, level {}",
                        key, level
                    );
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // Remove the empty node that have a right node with the same parent
                            // Because we cannot lock from left to right, we have to move the content
                            // of the right node to the left and remove the right node instead so the
                            // left right pointers can be modified
                            if !rebalancing.left_guard.is_empty()
                                || (level == 0
                                    && read_unchecked::<KS, PS>(&(*tree.root.read())).first_key()
                                        != rebalancing.left_guard.first_key())
                            {
                                return;
                            }

                            let left_node = &mut *rebalancing.left_guard;
                            let right_node = &mut *rebalancing.right_guard;
                            // swap the content of left and right, then delete right
                            // this procedure will prevent locking left node for changing right reference
                            let left_left_ref = left_node.left_ref_mut().map(|r| r.clone());
                            let right_right_ref =
                                right_node.right_ref_mut_no_empty().map(|r| r.clone());
                            let left_ref = rebalancing.left_ref.clone();
                            // swap the empty node with the right node. In this case left node holds
                            // content of the right node but pointers need to be corrected.
                            mem::swap(left_node, right_node);
                            *right_node = NodeData::Empty(box EmptyNode {
                                left: Some(left_ref.clone()),
                                right: left_ref.clone(),
                            });
                            left_node
                                .left_ref_mut()
                                .map(|r| *r = left_left_ref.unwrap());
                            left_node
                                .right_ref_mut_no_empty()
                                .map(|r| *r = right_right_ref.unwrap());
                            rebalancing
                                .right_right_guard
                                .left_ref_mut()
                                .map(|r| *r = left_ref);
                            // remove the left ptr
                            rebalancing.parent.remove(rebalancing.parent_pos);
                            // point the right ptr to the replaced sub node
                            debug!(
                                "Removed sub level empty node, living node len {}, have {:?}",
                                left_node.len(),
                                left_node.keys()
                            );
                            rebalancing.parent.innode_mut().ptrs.as_slice()
                                [rebalancing.parent_pos + 1] = sub_node.clone();
                        },
                    ))
                } else if rebalancing.right_guard.is_empty() {
                    debug!(
                        "Remove {:?} sub level right node is empty, level {}",
                        key, level
                    );
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            // There is a right empty node that can be deleted directly without hassle
                            let node_to_remove = &mut rebalancing.right_guard;
                            let owned_left_ref = rebalancing.left_ref.clone();
                            rebalancing.left_guard.right_ref_mut_no_empty().map(|r| {
                                *r = node_to_remove.right_ref_mut_no_empty().unwrap().clone()
                            });
                            rebalancing
                                .right_right_guard
                                .left_ref_mut()
                                .map(|r| *r = owned_left_ref);
                            rebalancing.parent.remove(rebalancing.parent_pos + 1);
                        },
                    ))
                } else if rebalancing.left_guard.cannot_merge()
                    || rebalancing.right_guard.cannot_merge()
                {
                    if rebalancing.right_guard.len() < KS::slice_len() / 3
                        && rebalancing.right_guard.len() < rebalancing.left_guard.len()
                        && rebalancing.left_guard.len() > KS::slice_len() / 2
                    {
                        // Relocate the nodes with the same parent for balance.
                        // For OLFIT, items can only be located from left to right.
                        debug!(
                            "Remove {:?} sub level need relocation, level {}",
                            key, level
                        );
                        let mut left_node = &mut *rebalancing.left_guard;
                        let mut right_node = &mut *rebalancing.right_guard;
                        let left_pos = rebalancing.parent_pos;
                        let right_pos = left_pos + 1;
                        rebalancing
                            .parent
                            .innode_mut()
                            .relocate_children(left_pos, right_pos, left_node, right_node);
                    }
                    None
                } else if rebalancing.left_guard.len() + rebalancing.right_guard.len() + 1
                    <= KS::slice_len()
                {
                    // Nodes with the same parent can merge
                    debug!(
                        "Remove {:?} sub level need to be merged, level {}",
                        key, level
                    );
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            let left_node = &mut *rebalancing.left_guard;
                            let right_node = &mut *rebalancing.right_guard;
                            let right_node_next = &mut *rebalancing.right_right_guard;
                            let left_pos = rebalancing.parent_pos;
                            let right_pos = left_pos + 1;
                            if left_node.is_empty() || right_node.is_empty() {
                                return;
                            }
                            rebalancing.parent.innode_mut().merge_children(
                                left_pos,
                                right_pos,
                                left_node,
                                right_node,
                                right_node_next,
                            );
                        },
                    ))
                // None
                } else if rebalancing.parent.len() == 1
                    && rebalancing.left_guard.is_empty()
                    && rebalancing.right_guard.is_empty()
                {
                    // this node and its children is empty, should be removed
                    debug!("Cleaning up empty level nodes, level {}", level);
                    Some(with_innode_removing(
                        key,
                        rebalancing,
                        &sub_node,
                        parent,
                        removed,
                        level,
                        |rebalancing| {
                            rebalancing.parent.remove(0);
                            rebalancing.parent.innode_mut().ptrs.as_slice()[0] = Default::default();
                        },
                    ))
                } else {
                    None
                }
            } else {
                None
            }
            .unwrap_or_else(|| RemoveResult {
                rebalancing: None,
                removed,
            })
        }
        MutSearchResult::External => {
            let node_guard: NodeWriteGuard<KS, PS> = write_node(node_ref);
            let mut target_guard = write_targeted(node_guard, key);
            let target_guard_ref = target_guard.node_ref().clone();
            let mut remove_result = RemoveResult {
                rebalancing: None,
                removed: false,
            };
            {
                let is_left_half_full = target_guard.is_half_full();
                let mut node = target_guard.extnode_mut();
                let pos = node.search(key);
                let mut right_guard = write_node(&node.next);
                let _right_node_cannot_rebalance =
                    right_guard.is_none() || !right_guard.is_empty_node();
                if !right_guard.is_none() && !is_left_half_full || !right_guard.is_half_full() {
                    let right_right_guard =
                        write_node(&right_guard.right_ref_mut_no_empty().unwrap());
                    let parent_guard = write_node(parent);
                    let parent_target_guard = write_targeted(parent_guard, key);
                    let parent_pos = parent_target_guard.search(key);
                    // Check if the right node is innode and its parent is the same as the left one
                    // because we have to lock from left to right, there is no way to lock backwards
                    // if the empty non half-full node is at the right most of its parent
                    // So left over imbalanced such nodes will be expected and they can be eliminate
                    // by their left node remove operations.
                    if parent_pos < parent_target_guard.len() - 1 {
                        debug!("removing innode have a rebalance requirement");
                        remove_result.rebalancing = Some(RebalancingNodes {
                            left_ref: target_guard_ref,
                            left_guard: Default::default(),
                            parent: parent_target_guard,
                            parent_pos,
                            right_right_guard,
                            right_guard,
                        });
                    } else {
                        debug!("removing innode have a rebalance requirement but left-right node does not with the same parent");
                    }
                }
                if pos >= node.len {
                    debug!(
                        "Removing pos overflows external node, pos {}, len {}, expecting key {:?}, current keys {:?}, right keys {:?}",
                        pos, node.len, key, node.keys, read_unchecked::<KS, PS>(&node.next).keys()
                    );
                    remove_result.removed = false;
                }
                if &node.keys.as_slice()[pos] == key {
                    debug!("Removing key {:?} at {}, keys {:?}", key, pos, &node.keys);
                    node.remove_at(pos);
                    remove_result.removed = true;
                } else {
                    debug!(
                        "Search check failed for remove at pos {}, expecting {:?}, actual {:?}, have {:?}",
                        pos, key, &node.keys.as_slice_immute()[pos], &node.keys
                    );
                    remove_result.removed = false;
                }
            }
            if let Some(ref mut rebalance) = &mut remove_result.rebalancing {
                rebalance.left_guard = target_guard;
            }
            remove_result
        }
    }
}

// scatter the node and its references to ensure garbage collection
pub fn scatter_nodes<KS, PS>(node_ref: &NodeCellRef) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut footprint = 0;
    if node_ref.is_default() {
        return footprint;
    }
    let mut node = write_node::<KS, PS>(node_ref);
    loop {
        match &*node {
            &NodeData::None => return footprint,
            _ => {}
        }
        let right = node.right_ref().unwrap().clone();
        footprint += node.len();
        *node = NodeData::None;
        ExtNode::<KS, PS>::make_deleted(node.node_ref());
        node = write_node::<KS, PS>(&right);
    }
}
<|endoftext|>


<commit_before>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types, Var};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::quote;
use syn::{Error, Result};

pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
    let ref mut errors = Vec::new();

    for ty in types {
        match ty {
            Type::Ident(ident) => check_type_ident(errors, types, ident),
            Type::RustBox(ptr) => check_type_box(errors, types, ptr),
            Type::UniquePtr(ptr) => check_type_unique_ptr(errors, types, ptr),
            Type::Ref(ty) => check_type_ref(errors, ty),
            Type::Fn(_) => errors.push(unimplemented_fn_type(ty)),
            _ => {}
        }
    }

    for api in apis {
        match api {
            Api::Struct(strct) => check_api_struct(errors, types, strct),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(errors, types, efn),
            _ => {}
        }
    }

    for api in apis {
        if let Api::CxxFunction(efn) = api {
            errors.extend(check_mut_return_restriction(efn).err());
        }
        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
            errors.extend(check_multiple_arg_lifetimes(efn).err());
        }
    }

    ident::check_all(apis, errors);

    let mut iter = errors.drain(..);
    let mut all_errors = match iter.next() {
        Some(err) => err,
        None => return Ok(()),
    };
    for err in iter {
        all_errors.combine(err);
    }
    Err(all_errors)
}

fn check_type_ident(errors: &mut Vec<Error>, types: &Types, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !types.structs.contains_key(ident)
        && !types.cxx.contains(ident)
        && !types.rust.contains(ident)
    {
        errors.push(unsupported_type(ident));
    }
}

fn check_type_box(errors: &mut Vec<Error>, types: &Types, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if types.cxx.contains(ident) {
            errors.push(unsupported_cxx_type_in_box(ptr));
        }
        if Atom::from(ident).is_none() {
            return;
        }
    }
    errors.push(unsupported_box_target(ptr));
}

fn check_type_unique_ptr(errors: &mut Vec<Error>, types: &Types, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if types.rust.contains(ident) {
            errors.push(unsupported_rust_type_in_unique_ptr(ptr));
        }
        match Atom::from(ident) {
            None | Some(CxxString) => return,
            _ => {}
        }
    }
    errors.push(unsupported_unique_ptr_target(ptr));
}

fn check_type_ref(errors: &mut Vec<Error>, ty: &Ref) {
    if let Type::Void(_) = ty.inner {
        errors.push(unsupported_reference_type(ty));
    }
}

fn check_api_struct(errors: &mut Vec<Error>, types: &Types, strct: &Struct) {
    if strct.fields.is_empty() {
        errors.push(struct_empty(strct));
    }
    for field in &strct.fields {
        if is_unsized(&field.ty, types) {
            errors.push(field_by_value(field, types));
        }
    }
}

fn check_api_fn(errors: &mut Vec<Error>, types: &Types, efn: &ExternFn) {
    for arg in &efn.args {
        if is_unsized(&arg.ty, types) {
            errors.push(argument_by_value(arg, types));
        }
    }
    if let Some(ty) = &efn.ret {
        if is_unsized(ty, types) {
            errors.push(return_by_value(ty, types));
        }
    }
}

fn check_mut_return_restriction(efn: &ExternFn) -> Result<()> {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return Ok(()),
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return Ok(());
            }
        }
    }

    Err(Error::new_spanned(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    ))
}

fn check_multiple_arg_lifetimes(efn: &ExternFn) -> Result<()> {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return Ok(()),
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if reference_args == 1 {
        Ok(())
    } else {
        Err(Error::new_spanned(
            efn,
            "functions that return a reference must take exactly one input reference",
        ))
    }
}

fn is_unsized(ty: &Type, types: &Types) -> bool {
    let ident = match ty {
        Type::Ident(ident) => ident,
        Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString || types.cxx.contains(ident) || types.rust.contains(ident)
}

fn describe(ty: &Type, types: &Types) -> String {
    match ty {
        Type::Ident(ident) => {
            if types.structs.contains_key(ident) {
                "struct".to_owned()
            } else if types.cxx.contains(ident) {
                "C++ type".to_owned()
            } else if types.rust.contains(ident) {
                "opaque Rust type".to_owned()
            } else if Atom::from(ident) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}

fn unsupported_type(ident: &Ident) -> Error {
    Error::new(ident.span(), "unsupported type")
}

fn unsupported_reference_type(ty: &Ref) -> Error {
    Error::new_spanned(ty, "unsupported reference type")
}

fn unsupported_cxx_type_in_box(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, error::BOX_CXX_TYPE.msg)
}

fn unsupported_box_target(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, "unsupported target type of Box")
}

fn unsupported_rust_type_in_unique_ptr(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, "unique_ptr of a Rust type is not supported yet")
}

fn unsupported_unique_ptr_target(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, "unsupported unique_ptr target type")
}

fn struct_empty(strct: &Struct) -> Error {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    let span = quote!(#struct_token #brace_token);
    Error::new_spanned(span, "structs without any fields are not supported")
}

fn field_by_value(field: &Var, types: &Types) -> Error {
    let desc = describe(&field.ty, types);
    let message = format!("using {} by value is not supported", desc);
    Error::new_spanned(field, message)
}

fn argument_by_value(arg: &Var, types: &Types) -> Error {
    let desc = describe(&arg.ty, types);
    let message = format!("passing {} by value is not supported", desc);
    Error::new_spanned(arg, message)
}

fn return_by_value(ty: &Type, types: &Types) -> Error {
    let desc = describe(ty, types);
    let message = format!("returning {} by value is not supported", desc);
    Error::new_spanned(ty, message)
}

fn unimplemented_fn_type(ty: &Type) -> Error {
    Error::new_spanned(ty, "function pointer support is not implemented yet")
}
<commit_msg>Collect typecheck context into a struct<commit_after>use crate::syntax::atom::Atom::{self, *};
use crate::syntax::{error, ident, Api, ExternFn, Ref, Struct, Ty1, Type, Types, Var};
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
use quote::quote;
use syn::{Error, Result};

struct Check<'a> {
    apis: &'a [Api],
    types: &'a Types<'a>,
    errors: &'a mut Vec<Error>,
}

pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
    let mut errors = Vec::new();
    let mut cx = Check {
        apis,
        types,
        errors: &mut errors,
    };
    do_typecheck(&mut cx);
    combine_errors(errors)
}

fn do_typecheck(cx: &mut Check) {
    for ty in cx.types {
        match ty {
            Type::Ident(ident) => check_type_ident(cx, ident),
            Type::RustBox(ptr) => check_type_box(cx, ptr),
            Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
            Type::Ref(ty) => check_type_ref(cx, ty),
            Type::Fn(_) => cx.errors.push(unimplemented_fn_type(ty)),
            _ => {}
        }
    }

    for api in cx.apis {
        match api {
            Api::Struct(strct) => check_api_struct(cx, strct),
            Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
            _ => {}
        }
    }

    for api in cx.apis {
        if let Api::CxxFunction(efn) = api {
            check_mut_return_restriction(cx, efn);
        }
        if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
            check_multiple_arg_lifetimes(cx, efn);
        }
    }

    ident::check_all(cx.apis, cx.errors);
}

fn check_type_ident(cx: &mut Check, ident: &Ident) {
    if Atom::from(ident).is_none()
        && !cx.types.structs.contains_key(ident)
        && !cx.types.cxx.contains(ident)
        && !cx.types.rust.contains(ident)
    {
        cx.errors.push(unsupported_type(ident));
    }
}

fn check_type_box(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.cxx.contains(ident) {
            cx.errors.push(unsupported_cxx_type_in_box(ptr));
        }
        if Atom::from(ident).is_none() {
            return;
        }
    }
    cx.errors.push(unsupported_box_target(ptr));
}

fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
    if let Type::Ident(ident) = &ptr.inner {
        if cx.types.rust.contains(ident) {
            cx.errors.push(unsupported_rust_type_in_unique_ptr(ptr));
        }
        match Atom::from(ident) {
            None | Some(CxxString) => return,
            _ => {}
        }
    }
    cx.errors.push(unsupported_unique_ptr_target(ptr));
}

fn check_type_ref(cx: &mut Check, ty: &Ref) {
    if let Type::Void(_) = ty.inner {
        cx.errors.push(unsupported_reference_type(ty));
    }
}

fn check_api_struct(cx: &mut Check, strct: &Struct) {
    if strct.fields.is_empty() {
        cx.errors.push(struct_empty(strct));
    }
    for field in &strct.fields {
        if is_unsized(cx, &field.ty) {
            cx.errors.push(field_by_value(field, cx.types));
        }
    }
}

fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
    for arg in &efn.args {
        if is_unsized(cx, &arg.ty) {
            cx.errors.push(argument_by_value(arg, cx.types));
        }
    }
    if let Some(ty) = &efn.ret {
        if is_unsized(cx, ty) {
            cx.errors.push(return_by_value(ty, cx.types));
        }
    }
}

fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(ty)) if ty.mutability.is_some() => {}
        _ => return,
    }

    for arg in &efn.args {
        if let Type::Ref(ty) = &arg.ty {
            if ty.mutability.is_some() {
                return;
            }
        }
    }

    cx.errors.push(Error::new_spanned(
        efn,
        "&mut return type is not allowed unless there is a &mut argument",
    ));
}

fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) {
    match &efn.ret {
        Some(Type::Ref(_)) => {}
        _ => return,
    }

    let mut reference_args = 0;
    for arg in &efn.args {
        if let Type::Ref(_) = &arg.ty {
            reference_args += 1;
        }
    }

    if reference_args != 1 {
        cx.errors.push(Error::new_spanned(
            efn,
            "functions that return a reference must take exactly one input reference",
        ));
    }
}

fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
    let ident = match ty {
        Type::Ident(ident) => ident,
        Type::Void(_) => return true,
        _ => return false,
    };
    ident == CxxString || cx.types.cxx.contains(ident) || cx.types.rust.contains(ident)
}

fn combine_errors(errors: Vec<Error>) -> Result<()> {
    let mut iter = errors.into_iter();
    let mut all_errors = match iter.next() {
        Some(err) => err,
        None => return Ok(()),
    };
    for err in iter {
        all_errors.combine(err);
    }
    Err(all_errors)
}

fn describe(ty: &Type, types: &Types) -> String {
    match ty {
        Type::Ident(ident) => {
            if types.structs.contains_key(ident) {
                "struct".to_owned()
            } else if types.cxx.contains(ident) {
                "C++ type".to_owned()
            } else if types.rust.contains(ident) {
                "opaque Rust type".to_owned()
            } else if Atom::from(ident) == Some(CxxString) {
                "C++ string".to_owned()
            } else {
                ident.to_string()
            }
        }
        Type::RustBox(_) => "Box".to_owned(),
        Type::UniquePtr(_) => "unique_ptr".to_owned(),
        Type::Ref(_) => "reference".to_owned(),
        Type::Str(_) => "&str".to_owned(),
        Type::Fn(_) => "function pointer".to_owned(),
        Type::Void(_) => "()".to_owned(),
    }
}

fn unsupported_type(ident: &Ident) -> Error {
    Error::new(ident.span(), "unsupported type")
}

fn unsupported_reference_type(ty: &Ref) -> Error {
    Error::new_spanned(ty, "unsupported reference type")
}

fn unsupported_cxx_type_in_box(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, error::BOX_CXX_TYPE.msg)
}

fn unsupported_box_target(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, "unsupported target type of Box")
}

fn unsupported_rust_type_in_unique_ptr(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, "unique_ptr of a Rust type is not supported yet")
}

fn unsupported_unique_ptr_target(unique_ptr: &Ty1) -> Error {
    Error::new_spanned(unique_ptr, "unsupported unique_ptr target type")
}

fn struct_empty(strct: &Struct) -> Error {
    let struct_token = strct.struct_token;
    let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
    brace_token.set_span(strct.brace_token.span);
    let span = quote!(#struct_token #brace_token);
    Error::new_spanned(span, "structs without any fields are not supported")
}

fn field_by_value(field: &Var, types: &Types) -> Error {
    let desc = describe(&field.ty, types);
    let message = format!("using {} by value is not supported", desc);
    Error::new_spanned(field, message)
}

fn argument_by_value(arg: &Var, types: &Types) -> Error {
    let desc = describe(&arg.ty, types);
    let message = format!("passing {} by value is not supported", desc);
    Error::new_spanned(arg, message)
}

fn return_by_value(ty: &Type, types: &Types) -> Error {
    let desc = describe(ty, types);
    let message = format!("returning {} by value is not supported", desc);
    Error::new_spanned(ty, message)
}

fn unimplemented_fn_type(ty: &Type) -> Error {
    Error::new_spanned(ty, "function pointer support is not implemented yet")
}
<|endoftext|>