<commit_before>//! Provides a simple SAH split based BVH2 that stores types implementing the Boundable trait

use std::f32;
use std::iter::repeat;

use partition::partition;
use geometry::{BBox, Boundable};
use linalg::{Point, Ray, Axis};

/// A standard BVH2 that stores objects that can report their bounds in some space
/// via the `Boundable` trait. The BVH is constructed using a SAH partitioning scheme
pub struct BVH<T> {
    /// Maximum amount of geometry we're willing to put in a leaf
    max_geom: usize,
    /// The geometry stored in this BVH, this will be re-ordered to
    /// fit the BVH construction layout. TODO: We may want to make
    /// the geometry accessible by index
    geometry: Vec<T>,
    /// The flattened tree structure of the BVH
    tree: Vec<FlatNode>,
}

impl<T: Boundable> BVH<T> {
    /// Create a new BVH using a SAH construction algorithm
    pub fn new(max_geom: usize, mut geometry: Vec<T>) -> BVH<T> {
        assert!(!geometry.is_empty());
        let mut flat_tree = Vec::new();
        {
            let mut build_geom = Vec::with_capacity(geometry.len());
            for (i, g) in geometry.iter().enumerate() {
                build_geom.push(GeomInfo::new(g, i));
            }
            // TODO: How to sort the geometry into the flatten tree ordering?
            // we have the indices things should end up in stored in ordered geom
            // but how to use this information in sort_by for example?
            // Should we move things into/out of build_geom instead of borrowing?
            // it knows the index of the items
            let mut total_nodes = 0;
            let mut ordered_geom = Vec::with_capacity(geometry.len());
            let root = Box::new(BVH::build(&mut build_geom[..], &mut ordered_geom, &mut total_nodes,
                                  max_geom));
            root.print_tree(0);
            println!("Ordered geom = {:?}", ordered_geom);
            flat_tree.reserve(total_nodes);
            BVH::<T>::flatten_tree(&root, &mut flat_tree);
            assert_eq!(flat_tree.len(), total_nodes);
            println!("Flattened tree:");
            for n in &flat_tree {
                println!("\t{:?}", n);
            }
        }
        // TODO: does the BVH even need to store max geom after building?
        BVH { max_geom: max_geom, geometry: geometry, tree: flat_tree }
    }
    /// Traverse the BVH and call the function passed on the objects in the leaf nodes
    /// of the BVH, returning the value returned by the function after traversal completes
    pub fn intersect<'a, F, R>(&'a self, ray: &mut Ray, f: F) -> Option<R>
            where F: Fn(&mut Ray, &'a T) -> Option<R> {
        let mut result = None;
		for o in &*self.geometry {
			result = f(ray, o).or(result);
		}
        result
    }
    /// Construct the BVH tree using SAH splitting heuristic to determine split locations
    /// returns the root node of the subtree constructed over the slice of geom info passed
    /// and will increment `total_nodes` by the number of nodes in this subtree
    /// `ordered_geom` will be filled out with the indices of the geometry in the flattened
    /// tree ordering for more efficient access
    fn build(build_info: &mut [GeomInfo<T>], ordered_geom: &mut Vec<usize>,
             total_nodes: &mut usize, max_geom: usize) -> BuildNode {
        println!("Building node {}", total_nodes);
        *total_nodes += 1;
        // Find bounding box for all geometry we're trying to store at this level
        let bounds = build_info.iter().fold(BBox::new(), |b, g| b.box_union(&g.geom.bounds()));
        let ngeom = build_info.len();
        if ngeom == 1 {
            return BVH::build_leaf(build_info, ordered_geom, bounds);
        }
        // Time to build an interior node
        // Start by figuring out which axis we should be splitting on by finding
        // the axis along which there is the most variation in the geometry's centroids
        let centroids = build_info.iter().fold(BBox::new(), |b, g| b.point_union(&g.center));
        let split_axis = centroids.max_extent();
        let mut mid = build_info.len() / 2;
        // If all the geometry's centers are on the same point there's no partitioning that makes
        // sense to do
        if centroids.max[split_axis] == centroids.min[split_axis] {
            if ngeom < max_geom {
                return BVH::build_leaf(&mut build_info[..], ordered_geom, bounds);
            } else {
                let l = Box::new(BVH::build(&mut build_info[..mid], ordered_geom,
                                            total_nodes, max_geom));
                let r = Box::new(BVH::build(&mut build_info[mid..], ordered_geom,
                                            total_nodes, max_geom));
                return BuildNode::interior([l, r], split_axis);
            }
        }
        // If there's only a few objects just use an equal partitioning to split
        // Otherwise do a full SAH based split on the geometry
        if ngeom < 5 {
            // TODO: I'd prefer to use something like nth_element like I do in tray
            // here, but I guess a full sort is kind of meh on 5 elements
            // There shouldn't be NaNs in these positions so just give up if there are
            build_info.sort_by(|a, b| {
                match a.center[split_axis].partial_cmp(&b.center[split_axis]) {
                    Some(o) => o,
                    None => panic!("NaNs in build info centers?!"),
                }
            });
        } else {
            // We only consider binning into 12 buckets
            let mut buckets = [SAHBucket::new(); 12];
            println!("build_info.len() = {}", build_info.len());
            // Place geometry into nearest bucket
            for g in build_info.iter() {
                let b = ((g.center[split_axis] - centroids.min[split_axis])
                    / (centroids.max[split_axis] - centroids.min[split_axis]) * buckets.len() as f32) as usize;
                let b = if b == buckets.len() { b - 1 } else { b };
                println!("building buckets geom idx {} fell into bucket {}", g.geom_idx, b);
                buckets[b].count += 1;
                buckets[b].bounds = buckets[b].bounds.box_union(&g.bounds);
            }
            println!("Buckets: {:?}", buckets);
            // Compute cost of each bucket but the last using the surface area heuristic
            let mut cost = [0.0; 11];
            for (i, c) in cost.iter_mut().enumerate() {
                let left = buckets.iter().take(i).fold(SAHBucket::new(), |mut s, b| {
                    s.bounds = s.bounds.box_union(&b.bounds);
                    s.count += b.count;
                    s
                });
                let right = buckets.iter().skip(i).fold(SAHBucket::new(), |mut s, b| {
                    s.bounds = s.bounds.box_union(&b.bounds);
                    s.count += b.count;
                    s
                });
                *c = 0.125 * (left.count as f32 * left.bounds.surface_area()
                             + right.count as f32 * right.bounds.surface_area()) / bounds.surface_area();
                println!("For i = {}\n\tleft = {:?}\n\tright = {:?}\n\tcost = {}", i, left, right, c);
            }
            let (min_bucket, min_cost) = cost.iter().enumerate().fold((0, f32::INFINITY),
                |(pi, pc), (i, c)| {
                    if *c < pc { (i, *c) } else { (pi, pc) }
                });
            println!("min bucket cost = {}, index = {}", min_cost, min_bucket);
            // If we're forced to split by the amount of geometry or it's cheaper to split, do so
            if ngeom > max_geom || min_cost < ngeom as f32 {
                mid = partition(build_info.iter_mut(),
                    |g| {
                        let b = ((g.center[split_axis] - centroids.min[split_axis])
                                 / (centroids.max[split_axis] - centroids.min[split_axis]) * buckets.len() as f32) as usize;
                        let b = if b == buckets.len() { b - 1 } else { b };
                        println!("partitioning geom idx {} fell into bucket {}", g.geom_idx, b);
                        b <= min_bucket
                    });
                // partition returns the index of the first element in the false group
                mid = mid - 1;
                println!("mid point = {}", mid);
                println!("build info = ");
                for b in build_info.iter() {
                    println!("\tgeom idx = {}", b.geom_idx);
                }
            }
            else {
                return BVH::build_leaf(build_info, ordered_geom, bounds);
            }
        }
        assert!(mid != 0 && mid != build_info.len());
        let l = Box::new(BVH::build(&mut build_info[..mid], ordered_geom,
                                    total_nodes, max_geom));
        let r = Box::new(BVH::build(&mut build_info[mid..], ordered_geom,
                                    total_nodes, max_geom));
        return BuildNode::interior([l, r], split_axis);
    }
    /// Construct a new leaf node containing the passed geometry. Indices will be
    /// added to `ordered_geom` to instruct how the flattened tree should be placed
    /// in memory for the geometry in this leaf node
    fn build_leaf(build_info: &mut [GeomInfo<T>], ordered_geom: &mut Vec<usize>, bounds: BBox)
        -> BuildNode {
        let geom_offset = ordered_geom.len();
        // TODO: Function to append an iterator? Then we don't need this loop and
        // could do like: `ordered_geom.append(build_info.map(|g| g.geom_idx))`
        for g in build_info.iter() {
            ordered_geom.push(g.geom_idx);
        }
        BuildNode::leaf(build_info.len(), geom_offset, bounds)
    }
    /// Flatten the BVH sub-tree pointed too by the node passed into the vec passed
    /// Returns the index that the node was inserted at in the tree
    fn flatten_tree(node: &Box<BuildNode>, tree: &mut Vec<FlatNode>) -> usize {
        let offset = tree.len();
        match node.node {
            BuildNodeData::Interior { children: ref c, split_axis: ref a } => {
                // Push this node on followed by the first child and the second
                tree.push(FlatNode::interior(node.bounds, 0, *a));
                BVH::<T>::flatten_tree(&c[0], tree);
                let second_child = BVH::<T>::flatten_tree(&c[1], tree);
                // This is a little awkward, TODO: maybe better to call resize?
                match &mut tree[offset].node {
                    &mut FlatNodeData::Interior { second_child: ref mut s, axis: _ } => *s = second_child,
                    _ => panic!("Interior node switched to leaf!?"),
                };
            },
            BuildNodeData::Leaf { ngeom: ref n, geom_offset: ref o } => {
                tree.push(FlatNode::leaf(node.bounds, *o, *n));
            },
        }
        offset
    }
}

/// Data for flattened BVH nodes
#[derive(Debug)]
enum FlatNodeData {
    /// An interior node is flattened with its first child following it
    /// and the second child at some later index in the tree
    Interior { second_child: usize, axis: Axis },
    /// A leaf node stores information about the offset too its geometry
    /// and the number of geometry references
    Leaf { geom_offset: usize, ngeom: usize },
}

/// Final datastructure that the flattened BVH is stored in
#[derive(Debug)]
struct FlatNode {
    /// Bounding box of this node
    bounds: BBox,
    /// Information about this node, storing interior/leaf node data corresponding
    /// to the type of the node
    node: FlatNodeData,
}

impl FlatNode {
    /// Construct a new flattened interior node
    fn interior(bounds: BBox, second_child: usize, axis: Axis) -> FlatNode {
        FlatNode { bounds: bounds, node: FlatNodeData::Interior { second_child: second_child, axis: axis } }
    }
    /// Construct a new flattened leaf node
    fn leaf(bounds: BBox, geom_offset: usize, ngeom: usize) -> FlatNode {
        FlatNode { bounds: bounds, node: FlatNodeData::Leaf { geom_offset: geom_offset, ngeom: ngeom } }
    }
}

/// Information about the location and bounds of some geometry
struct GeomInfo<'a, T: 'a> {
    geom: &'a T,
    geom_idx: usize,
    center: Point,
    bounds: BBox,
}

impl<'a, T: Boundable> GeomInfo<'a, T> {
    /// Create a new reference to some geometry
    fn new(geom: &'a T, geom_idx: usize) -> GeomInfo<T> {
        let bounds = geom.bounds();
        GeomInfo { geom: geom, geom_idx: geom_idx,
                   center: bounds.lerp(0.5, 0.5, 0.5),
                   bounds: bounds }
    }
}

/// Data needed by a build node during construction
#[derive(Debug)]
enum BuildNodeData {
    /// Interior node of a BVH, stores two child nodes
    Interior {
        /// Left and Right children of the node
        children: [Box<BuildNode>; 2],
        /// Axis that geomtry was partitioned along to split into
        /// the child nodes
        split_axis: Axis,
    },
    /// Leaf node of a BVH, stores geometry
    Leaf {
        /// Number of objects stored in this node
        ngeom: usize,
        /// Offset into the array holding the sorted geometry
        geom_offset: usize,
    },
}

/// Temporary datastructure for constructing the BVH into a tree before
/// flattening it down into a Vec for performance
#[derive(Debug)]
struct BuildNode {
    /// The data stored at this node, either information about an Interior
    /// or Lead node
    node: BuildNodeData,
    /// Bounding box of this node
    bounds: BBox,
}

impl BuildNode {
    fn interior(children: [Box<BuildNode>; 2], split_axis: Axis) -> BuildNode {
        let bounds = children[0].bounds.box_union(&children[1].bounds);
        BuildNode { node: BuildNodeData::Interior { children: children, split_axis: split_axis },
                    bounds: bounds }
    }
    fn leaf(ngeom: usize, geom_offset: usize, bounds: BBox) -> BuildNode {
        BuildNode { node: BuildNodeData::Leaf { ngeom: ngeom, geom_offset: geom_offset }, bounds: bounds }
    }
    fn print_tree(&self, depth: usize) {
        let ident: String = repeat(" ").take(depth).collect();
        println!("{}BuildNode: {{", ident);
        let pad: String = repeat(" ").take(depth + 2).collect();
        println!("{}bounds: {:?}", pad, self.bounds);
        match self.node {
            BuildNodeData::Interior { children: ref c, split_axis: ref a } => {
                println!("{}type: Interior", pad);
                println!("{}split axis: {:?}", pad, a);
                println!("{}left child:", pad);
                c[0].print_tree(depth + 2);
                println!("{}right child:", pad);
                c[1].print_tree(depth + 2);
            },
            BuildNodeData::Leaf { ngeom: ref n, geom_offset: ref o } => {
                println!("{}type: Leaf", pad);
                println!("{}ngeom: {}", pad, n);
                println!("{}geom offset: {}", pad, o);
            },
        }
        println!("{}}}", ident);
    }
}

#[derive(Copy, Clone, Debug)]
struct SAHBucket {
    count: usize,
    bounds: BBox,
}

impl SAHBucket {
    /// Return a SAHBucket with no items and degenerate bounds
    fn new() -> SAHBucket {
        SAHBucket { count: 0, bounds: BBox::new() }
    }
}

<commit_msg>Build and flatten the BVH<commit_after>//! Provides a simple SAH split based BVH2 that stores types implementing the Boundable trait

use std::f32;
use std::iter::repeat;

use partition::partition;
use geometry::{BBox, Boundable};
use linalg::{Point, Ray, Axis};

/// A standard BVH2 that stores objects that can report their bounds in some space
/// via the `Boundable` trait. The BVH is constructed using a SAH partitioning scheme
pub struct BVH<T> {
    /// The geometry stored in this BVH, this will be re-ordered to
    /// fit the BVH construction layout. TODO: We may want to make
    /// the geometry accessible by index
    geometry: Vec<T>,
    /// Indices into `geometry` sorted by the order they're accessed by BVH leaf nodes
    /// TODO: How can we re-sort `geometry to match this ordering?
    ordered_geom: Vec<usize>,
    /// The flattened tree structure of the BVH
    tree: Vec<FlatNode>,
}

impl<T: Boundable> BVH<T> {
    /// Create a new BVH using a SAH construction algorithm
    pub fn new(max_geom: usize, mut geometry: Vec<T>) -> BVH<T> {
        assert!(!geometry.is_empty());
        let mut flat_tree = Vec::new();
        let mut ordered_geom = Vec::with_capacity(geometry.len());
        {
            let mut build_geom = Vec::with_capacity(geometry.len());
            for (i, g) in geometry.iter().enumerate() {
                build_geom.push(GeomInfo::new(g, i));
            }
            // TODO: How to sort the geometry into the flatten tree ordering?
            // we have the indices things should end up in stored in ordered geom
            // but how to use this information in sort_by for example?
            // Should we move things into/out of build_geom instead of borrowing?
            // it knows the index of the items
            let mut total_nodes = 0;
            let root = Box::new(BVH::build(&mut build_geom[..], &mut ordered_geom, &mut total_nodes,
                                  max_geom));
            root.print_tree(0);
            println!("Ordered geom = {:?}", ordered_geom);
            flat_tree.reserve(total_nodes);
            BVH::<T>::flatten_tree(&root, &mut flat_tree);
            assert_eq!(flat_tree.len(), total_nodes);
            assert_eq!(ordered_geom.len(), geometry.len());
            println!("Flattened tree:");
            for n in &flat_tree {
                println!("\t{:?}", n);
            }
            // TODO: I'm not sure if there's a better way that we can re-sort the geometry by the
            // indices in ordered geom
        }
        BVH { geometry: geometry, ordered_geom: ordered_geom, tree: flat_tree }
    }
    /// Traverse the BVH and call the function passed on the objects in the leaf nodes
    /// of the BVH, returning the value returned by the function after traversal completes
    pub fn intersect<'a, F, R>(&'a self, ray: &mut Ray, f: F) -> Option<R>
            where F: Fn(&mut Ray, &'a T) -> Option<R> {
        let mut result = None;
		for o in &*self.geometry {
			result = f(ray, o).or(result);
		}
        result
    }
    /// Construct the BVH tree using SAH splitting heuristic to determine split locations
    /// returns the root node of the subtree constructed over the slice of geom info passed
    /// and will increment `total_nodes` by the number of nodes in this subtree
    /// `ordered_geom` will be filled out with the indices of the geometry in the flattened
    /// tree ordering for more efficient access
    fn build(build_info: &mut [GeomInfo<T>], ordered_geom: &mut Vec<usize>,
             total_nodes: &mut usize, max_geom: usize) -> BuildNode {
        println!("Building node {}", total_nodes);
        *total_nodes += 1;
        // Find bounding box for all geometry we're trying to store at this level
        let bounds = build_info.iter().fold(BBox::new(), |b, g| b.box_union(&g.geom.bounds()));
        let ngeom = build_info.len();
        if ngeom == 1 {
            return BVH::build_leaf(build_info, ordered_geom, bounds);
        }
        // Time to build an interior node
        // Start by figuring out which axis we should be splitting on by finding
        // the axis along which there is the most variation in the geometry's centroids
        let centroids = build_info.iter().fold(BBox::new(), |b, g| b.point_union(&g.center));
        let split_axis = centroids.max_extent();
        let mut mid = build_info.len() / 2;
        // If all the geometry's centers are on the same point there's no partitioning that makes
        // sense to do
        if centroids.max[split_axis] == centroids.min[split_axis] {
            if ngeom < max_geom {
                return BVH::build_leaf(&mut build_info[..], ordered_geom, bounds);
            } else {
                let l = Box::new(BVH::build(&mut build_info[..mid], ordered_geom,
                                            total_nodes, max_geom));
                let r = Box::new(BVH::build(&mut build_info[mid..], ordered_geom,
                                            total_nodes, max_geom));
                return BuildNode::interior([l, r], split_axis);
            }
        }
        // If there's only a few objects just use an equal partitioning to split
        // Otherwise do a full SAH based split on the geometry
        if ngeom < 5 {
            // TODO: I'd prefer to use something like nth_element like I do in tray
            // here, but I guess a full sort is kind of meh on 5 elements
            // There shouldn't be NaNs in these positions so just give up if there are
            build_info.sort_by(|a, b| {
                match a.center[split_axis].partial_cmp(&b.center[split_axis]) {
                    Some(o) => o,
                    None => panic!("NaNs in build info centers?!"),
                }
            });
        } else {
            // We only consider binning into 12 buckets
            let mut buckets = [SAHBucket::new(); 12];
            println!("build_info.len() = {}", build_info.len());
            // Place geometry into nearest bucket
            for g in build_info.iter() {
                let b = ((g.center[split_axis] - centroids.min[split_axis])
                    / (centroids.max[split_axis] - centroids.min[split_axis]) * buckets.len() as f32) as usize;
                let b = if b == buckets.len() { b - 1 } else { b };
                println!("building buckets geom idx {} fell into bucket {}", g.geom_idx, b);
                buckets[b].count += 1;
                buckets[b].bounds = buckets[b].bounds.box_union(&g.bounds);
            }
            println!("Buckets: {:?}", buckets);
            // Compute cost of each bucket but the last using the surface area heuristic
            let mut cost = [0.0; 11];
            for (i, c) in cost.iter_mut().enumerate() {
                let left = buckets.iter().take(i).fold(SAHBucket::new(), |mut s, b| {
                    s.bounds = s.bounds.box_union(&b.bounds);
                    s.count += b.count;
                    s
                });
                let right = buckets.iter().skip(i).fold(SAHBucket::new(), |mut s, b| {
                    s.bounds = s.bounds.box_union(&b.bounds);
                    s.count += b.count;
                    s
                });
                *c = 0.125 * (left.count as f32 * left.bounds.surface_area()
                             + right.count as f32 * right.bounds.surface_area()) / bounds.surface_area();
                println!("For i = {}\n\tleft = {:?}\n\tright = {:?}\n\tcost = {}", i, left, right, c);
            }
            let (min_bucket, min_cost) = cost.iter().enumerate().fold((0, f32::INFINITY),
                |(pi, pc), (i, c)| {
                    if *c < pc { (i, *c) } else { (pi, pc) }
                });
            println!("min bucket cost = {}, index = {}", min_cost, min_bucket);
            // If we're forced to split by the amount of geometry or it's cheaper to split, do so
            if ngeom > max_geom || min_cost < ngeom as f32 {
                mid = partition(build_info.iter_mut(),
                    |g| {
                        let b = ((g.center[split_axis] - centroids.min[split_axis])
                                 / (centroids.max[split_axis] - centroids.min[split_axis]) * buckets.len() as f32) as usize;
                        let b = if b == buckets.len() { b - 1 } else { b };
                        println!("partitioning geom idx {} fell into bucket {}", g.geom_idx, b);
                        b <= min_bucket
                    });
                // partition returns the index of the first element in the false group
                mid = mid - 1;
                println!("mid point = {}", mid);
                println!("build info = ");
                for b in build_info.iter() {
                    println!("\tgeom idx = {}", b.geom_idx);
                }
            }
            else {
                return BVH::build_leaf(build_info, ordered_geom, bounds);
            }
        }
        assert!(mid != 0 && mid != build_info.len());
        let l = Box::new(BVH::build(&mut build_info[..mid], ordered_geom,
                                    total_nodes, max_geom));
        let r = Box::new(BVH::build(&mut build_info[mid..], ordered_geom,
                                    total_nodes, max_geom));
        return BuildNode::interior([l, r], split_axis);
    }
    /// Construct a new leaf node containing the passed geometry. Indices will be
    /// added to `ordered_geom` to instruct how the flattened tree should be placed
    /// in memory for the geometry in this leaf node
    fn build_leaf(build_info: &mut [GeomInfo<T>], ordered_geom: &mut Vec<usize>, bounds: BBox)
        -> BuildNode {
        let geom_offset = ordered_geom.len();
        // TODO: Function to append an iterator? Then we don't need this loop and
        // could do like: `ordered_geom.append(build_info.map(|g| g.geom_idx))`
        for g in build_info.iter() {
            ordered_geom.push(g.geom_idx);
        }
        BuildNode::leaf(build_info.len(), geom_offset, bounds)
    }
    /// Flatten the BVH sub-tree pointed too by the node passed into the vec passed
    /// Returns the index that the node was inserted at in the tree
    fn flatten_tree(node: &Box<BuildNode>, tree: &mut Vec<FlatNode>) -> usize {
        let offset = tree.len();
        match node.node {
            BuildNodeData::Interior { children: ref c, split_axis: ref a } => {
                // Push this node on followed by the first child and the second
                tree.push(FlatNode::interior(node.bounds, 0, *a));
                BVH::<T>::flatten_tree(&c[0], tree);
                let second_child = BVH::<T>::flatten_tree(&c[1], tree);
                // This is a little awkward, TODO: maybe better to call resize?
                match &mut tree[offset].node {
                    &mut FlatNodeData::Interior { second_child: ref mut s, axis: _ } => *s = second_child,
                    _ => panic!("Interior node switched to leaf!?"),
                };
            },
            BuildNodeData::Leaf { ngeom: ref n, geom_offset: ref o } => {
                tree.push(FlatNode::leaf(node.bounds, *o, *n));
            },
        }
        offset
    }
}

/// Data for flattened BVH nodes
#[derive(Debug)]
enum FlatNodeData {
    /// An interior node is flattened with its first child following it
    /// and the second child at some later index in the tree
    Interior { second_child: usize, axis: Axis },
    /// A leaf node stores information about the offset too its geometry
    /// and the number of geometry references
    Leaf { geom_offset: usize, ngeom: usize },
}

/// Final datastructure that the flattened BVH is stored in
#[derive(Debug)]
struct FlatNode {
    /// Bounding box of this node
    bounds: BBox,
    /// Information about this node, storing interior/leaf node data corresponding
    /// to the type of the node
    node: FlatNodeData,
}

impl FlatNode {
    /// Construct a new flattened interior node
    fn interior(bounds: BBox, second_child: usize, axis: Axis) -> FlatNode {
        FlatNode { bounds: bounds, node: FlatNodeData::Interior { second_child: second_child, axis: axis } }
    }
    /// Construct a new flattened leaf node
    fn leaf(bounds: BBox, geom_offset: usize, ngeom: usize) -> FlatNode {
        FlatNode { bounds: bounds, node: FlatNodeData::Leaf { geom_offset: geom_offset, ngeom: ngeom } }
    }
}

/// Information about the location and bounds of some geometry
struct GeomInfo<'a, T: 'a> {
    geom: &'a T,
    geom_idx: usize,
    center: Point,
    bounds: BBox,
}

impl<'a, T: Boundable> GeomInfo<'a, T> {
    /// Create a new reference to some geometry
    fn new(geom: &'a T, geom_idx: usize) -> GeomInfo<T> {
        let bounds = geom.bounds();
        GeomInfo { geom: geom, geom_idx: geom_idx,
                   center: bounds.lerp(0.5, 0.5, 0.5),
                   bounds: bounds }
    }
}

/// Data needed by a build node during construction
#[derive(Debug)]
enum BuildNodeData {
    /// Interior node of a BVH, stores two child nodes
    Interior {
        /// Left and Right children of the node
        children: [Box<BuildNode>; 2],
        /// Axis that geomtry was partitioned along to split into
        /// the child nodes
        split_axis: Axis,
    },
    /// Leaf node of a BVH, stores geometry
    Leaf {
        /// Number of objects stored in this node
        ngeom: usize,
        /// Offset into the array holding the sorted geometry
        geom_offset: usize,
    },
}

/// Temporary datastructure for constructing the BVH into a tree before
/// flattening it down into a Vec for performance
#[derive(Debug)]
struct BuildNode {
    /// The data stored at this node, either information about an Interior
    /// or Lead node
    node: BuildNodeData,
    /// Bounding box of this node
    bounds: BBox,
}

impl BuildNode {
    fn interior(children: [Box<BuildNode>; 2], split_axis: Axis) -> BuildNode {
        let bounds = children[0].bounds.box_union(&children[1].bounds);
        BuildNode { node: BuildNodeData::Interior { children: children, split_axis: split_axis },
                    bounds: bounds }
    }
    fn leaf(ngeom: usize, geom_offset: usize, bounds: BBox) -> BuildNode {
        BuildNode { node: BuildNodeData::Leaf { ngeom: ngeom, geom_offset: geom_offset }, bounds: bounds }
    }
    fn print_tree(&self, depth: usize) {
        let ident: String = repeat(" ").take(depth).collect();
        println!("{}BuildNode: {{", ident);
        let pad: String = repeat(" ").take(depth + 2).collect();
        println!("{}bounds: {:?}", pad, self.bounds);
        match self.node {
            BuildNodeData::Interior { children: ref c, split_axis: ref a } => {
                println!("{}type: Interior", pad);
                println!("{}split axis: {:?}", pad, a);
                println!("{}left child:", pad);
                c[0].print_tree(depth + 2);
                println!("{}right child:", pad);
                c[1].print_tree(depth + 2);
            },
            BuildNodeData::Leaf { ngeom: ref n, geom_offset: ref o } => {
                println!("{}type: Leaf", pad);
                println!("{}ngeom: {}", pad, n);
                println!("{}geom offset: {}", pad, o);
            },
        }
        println!("{}}}", ident);
    }
}

#[derive(Copy, Clone, Debug)]
struct SAHBucket {
    count: usize,
    bounds: BBox,
}

impl SAHBucket {
    /// Return a SAHBucket with no items and degenerate bounds
    fn new() -> SAHBucket {
        SAHBucket { count: 0, bounds: BBox::new() }
    }
}

<|endoftext|>


<commit_before>use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::event_enum;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{de, Serialize};
use serde_json::value::RawValue as RawJsonValue;

use crate::{from_raw_json_value, EventDeHelper};

event_enum! {
    /// Any global account data event.
    enum GlobalAccountData {
        "m.direct",
        "m.ignored_user_list",
        "m.push_rules",
    }

    /// Any room account data event.
    enum RoomAccountData {
        "m.fully_read",
        "m.tag",
    }

    /// Any ephemeral room event.
    enum EphemeralRoom {
        "m.receipt",
        "m.typing",
    }

    /// Any message event.
    enum Message {
        "m.call.answer",
        "m.call.invite",
        "m.call.hangup",
        "m.call.candidates",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.ready",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.start",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.cancel",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.accept",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.key",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.mac",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.done",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.reaction",
        "m.room.encrypted",
        "m.room.message",
        "m.room.message.feedback",
        "m.room.redaction",
        "m.sticker",
    }

    /// Any state event.
    enum State {
        "m.policy.rule.room",
        "m.policy.rule.server",
        "m.policy.rule.user",
        "m.room.aliases",
        "m.room.avatar",
        "m.room.canonical_alias",
        "m.room.create",
        "m.room.encryption",
        "m.room.guest_access",
        "m.room.history_visibility",
        "m.room.join_rules",
        "m.room.member",
        "m.room.name",
        "m.room.pinned_events",
        "m.room.power_levels",
        "m.room.server_acl",
        "m.room.third_party_invite",
        "m.room.tombstone",
        "m.room.topic",
    }

    /// Any to-device event.
    enum ToDevice {
        "m.dummy",
        "m.room_key",
        "m.room_key_request",
        "m.forwarded_room_key",
        "m.key.verification.request",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.ready",
        "m.key.verification.start",
        "m.key.verification.cancel",
        "m.key.verification.accept",
        "m.key.verification.key",
        "m.key.verification.mac",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.done",
        "m.room.encrypted",
    }
}

/// Declares an item with a doc attribute computed by some macro expression.
/// This allows documentation to be dynamically generated based on input.
/// Necessary to work around <https://github.com/rust-lang/rust/issues/52607>.
macro_rules! doc_concat {
    ( $( #[doc = $doc:expr] $( $thing:tt )* )* ) => ( $( #[doc = $doc] $( $thing )* )* );
}

macro_rules! room_ev_accessor {
    ($field:ident: $ty:ty) => {
        doc_concat! {
            #[doc = concat!("Returns this event's `", stringify!($field), "` field.")]
            pub fn $field(&self) -> $ty {
                match self {
                    Self::Message(ev) => ev.$field(),
                    Self::State(ev) => ev.$field(),
                    Self::RedactedMessage(ev) => ev.$field(),
                    Self::RedactedState(ev) => ev.$field(),
                }
            }
        }
    };
}

/// Any room event.
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum AnyRoomEvent {
    /// Any message event.
    Message(AnyMessageEvent),

    /// Any state event.
    State(AnyStateEvent),

    /// Any message event that has been redacted.
    RedactedMessage(AnyRedactedMessageEvent),

    /// Any state event that has been redacted.
    RedactedState(AnyRedactedStateEvent),
}

impl AnyRoomEvent {
    room_ev_accessor!(origin_server_ts: &MilliSecondsSinceUnixEpoch);
    room_ev_accessor!(room_id: &RoomId);
    room_ev_accessor!(event_id: &EventId);
    room_ev_accessor!(sender: &UserId);
}

/// Any sync room event (room event without a `room_id`, as returned in `/sync` responses)
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum AnySyncRoomEvent {
    /// Any sync message event
    Message(AnySyncMessageEvent),

    /// Any sync state event
    State(AnySyncStateEvent),

    /// Any sync message event that has been redacted.
    RedactedMessage(AnyRedactedSyncMessageEvent),

    /// Any sync state event that has been redacted.
    RedactedState(AnyRedactedSyncStateEvent),
}

impl AnySyncRoomEvent {
    room_ev_accessor!(origin_server_ts: &MilliSecondsSinceUnixEpoch);
    room_ev_accessor!(event_id: &EventId);
    room_ev_accessor!(sender: &UserId);
}

impl<'de> de::Deserialize<'de> for AnyRoomEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
        let EventDeHelper { state_key, unsigned, .. } = from_raw_json_value(&json)?;

        if state_key.is_some() {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnyRoomEvent::RedactedState(from_raw_json_value(&json)?)
                }
                _ => AnyRoomEvent::State(from_raw_json_value(&json)?),
            })
        } else {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnyRoomEvent::RedactedMessage(from_raw_json_value(&json)?)
                }
                _ => AnyRoomEvent::Message(from_raw_json_value(&json)?),
            })
        }
    }
}

impl<'de> de::Deserialize<'de> for AnySyncRoomEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
        let EventDeHelper { state_key, unsigned, .. } = from_raw_json_value(&json)?;

        if state_key.is_some() {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnySyncRoomEvent::RedactedState(from_raw_json_value(&json)?)
                }
                _ => AnySyncRoomEvent::State(from_raw_json_value(&json)?),
            })
        } else {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnySyncRoomEvent::RedactedMessage(from_raw_json_value(&json)?)
                }
                _ => AnySyncRoomEvent::Message(from_raw_json_value(&json)?),
            })
        }
    }
}
<commit_msg>events: Add AnySyncRoomEvent::into_full_event<commit_after>use ruma_common::MilliSecondsSinceUnixEpoch;
use ruma_events_macros::event_enum;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{de, Serialize};
use serde_json::value::RawValue as RawJsonValue;

use crate::{from_raw_json_value, EventDeHelper};

event_enum! {
    /// Any global account data event.
    enum GlobalAccountData {
        "m.direct",
        "m.ignored_user_list",
        "m.push_rules",
    }

    /// Any room account data event.
    enum RoomAccountData {
        "m.fully_read",
        "m.tag",
    }

    /// Any ephemeral room event.
    enum EphemeralRoom {
        "m.receipt",
        "m.typing",
    }

    /// Any message event.
    enum Message {
        "m.call.answer",
        "m.call.invite",
        "m.call.hangup",
        "m.call.candidates",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.ready",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.start",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.cancel",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.accept",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.key",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.mac",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.done",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.reaction",
        "m.room.encrypted",
        "m.room.message",
        "m.room.message.feedback",
        "m.room.redaction",
        "m.sticker",
    }

    /// Any state event.
    enum State {
        "m.policy.rule.room",
        "m.policy.rule.server",
        "m.policy.rule.user",
        "m.room.aliases",
        "m.room.avatar",
        "m.room.canonical_alias",
        "m.room.create",
        "m.room.encryption",
        "m.room.guest_access",
        "m.room.history_visibility",
        "m.room.join_rules",
        "m.room.member",
        "m.room.name",
        "m.room.pinned_events",
        "m.room.power_levels",
        "m.room.server_acl",
        "m.room.third_party_invite",
        "m.room.tombstone",
        "m.room.topic",
    }

    /// Any to-device event.
    enum ToDevice {
        "m.dummy",
        "m.room_key",
        "m.room_key_request",
        "m.forwarded_room_key",
        "m.key.verification.request",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.ready",
        "m.key.verification.start",
        "m.key.verification.cancel",
        "m.key.verification.accept",
        "m.key.verification.key",
        "m.key.verification.mac",
        #[cfg(feature = "unstable-pre-spec")]
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
        "m.key.verification.done",
        "m.room.encrypted",
    }
}

/// Declares an item with a doc attribute computed by some macro expression.
/// This allows documentation to be dynamically generated based on input.
/// Necessary to work around <https://github.com/rust-lang/rust/issues/52607>.
macro_rules! doc_concat {
    ( $( #[doc = $doc:expr] $( $thing:tt )* )* ) => ( $( #[doc = $doc] $( $thing )* )* );
}

macro_rules! room_ev_accessor {
    ($field:ident: $ty:ty) => {
        doc_concat! {
            #[doc = concat!("Returns this event's `", stringify!($field), "` field.")]
            pub fn $field(&self) -> $ty {
                match self {
                    Self::Message(ev) => ev.$field(),
                    Self::State(ev) => ev.$field(),
                    Self::RedactedMessage(ev) => ev.$field(),
                    Self::RedactedState(ev) => ev.$field(),
                }
            }
        }
    };
}

/// Any room event.
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum AnyRoomEvent {
    /// Any message event.
    Message(AnyMessageEvent),

    /// Any state event.
    State(AnyStateEvent),

    /// Any message event that has been redacted.
    RedactedMessage(AnyRedactedMessageEvent),

    /// Any state event that has been redacted.
    RedactedState(AnyRedactedStateEvent),
}

impl AnyRoomEvent {
    room_ev_accessor!(origin_server_ts: &MilliSecondsSinceUnixEpoch);
    room_ev_accessor!(room_id: &RoomId);
    room_ev_accessor!(event_id: &EventId);
    room_ev_accessor!(sender: &UserId);
}

/// Any sync room event (room event without a `room_id`, as returned in `/sync` responses)
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, Serialize)]
#[serde(untagged)]
pub enum AnySyncRoomEvent {
    /// Any sync message event
    Message(AnySyncMessageEvent),

    /// Any sync state event
    State(AnySyncStateEvent),

    /// Any sync message event that has been redacted.
    RedactedMessage(AnyRedactedSyncMessageEvent),

    /// Any sync state event that has been redacted.
    RedactedState(AnyRedactedSyncStateEvent),
}

impl AnySyncRoomEvent {
    room_ev_accessor!(origin_server_ts: &MilliSecondsSinceUnixEpoch);
    room_ev_accessor!(event_id: &EventId);
    room_ev_accessor!(sender: &UserId);

    /// Converts `self` to an `AnyRoomEvent` by adding the given a room ID.
    pub fn into_full_event(self, room_id: RoomId) -> AnyRoomEvent {
        match self {
            Self::Message(ev) => AnyRoomEvent::Message(ev.into_full_event(room_id)),
            Self::State(ev) => AnyRoomEvent::State(ev.into_full_event(room_id)),
            Self::RedactedMessage(ev) => AnyRoomEvent::RedactedMessage(ev.into_full_event(room_id)),
            Self::RedactedState(ev) => AnyRoomEvent::RedactedState(ev.into_full_event(room_id)),
        }
    }
}

impl<'de> de::Deserialize<'de> for AnyRoomEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
        let EventDeHelper { state_key, unsigned, .. } = from_raw_json_value(&json)?;

        if state_key.is_some() {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnyRoomEvent::RedactedState(from_raw_json_value(&json)?)
                }
                _ => AnyRoomEvent::State(from_raw_json_value(&json)?),
            })
        } else {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnyRoomEvent::RedactedMessage(from_raw_json_value(&json)?)
                }
                _ => AnyRoomEvent::Message(from_raw_json_value(&json)?),
            })
        }
    }
}

impl<'de> de::Deserialize<'de> for AnySyncRoomEvent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
        let EventDeHelper { state_key, unsigned, .. } = from_raw_json_value(&json)?;

        if state_key.is_some() {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnySyncRoomEvent::RedactedState(from_raw_json_value(&json)?)
                }
                _ => AnySyncRoomEvent::State(from_raw_json_value(&json)?),
            })
        } else {
            Ok(match unsigned {
                Some(unsigned) if unsigned.redacted_because.is_some() => {
                    AnySyncRoomEvent::RedactedMessage(from_raw_json_value(&json)?)
                }
                _ => AnySyncRoomEvent::Message(from_raw_json_value(&json)?),
            })
        }
    }
}
<|endoftext|>


<commit_before>//! This pass replaces a drop of a type that does not need dropping, with a goto

use crate::transform::{MirPass, MirSource};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;

pub struct RemoveUnneededDrops;

impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        trace!("Running RemoveUnneededDrops on {:?}", source);
        let mut opt_finder = RemoveUnneededDropsOptimizationFinder {
            tcx,
            body,
            optimizations: vec![],
            def_id: source.def_id().expect_local(),
        };
        opt_finder.visit_body(body);
        for (loc, target) in opt_finder.optimizations {
            let terminator = body.basic_blocks_mut()[loc.block].terminator_mut();
            debug!("SUCCESS: replacing `drop` with goto({:?})", target);
            terminator.kind = TerminatorKind::Goto { target };
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match terminator.kind {
            TerminatorKind::Drop { place, target, .. } => {
                let ty = place.ty(self.body, self.tcx);
                let needs_drop = ty.ty.needs_drop(self.tcx, self.tcx.param_env(self.def_id));
                if !needs_drop {
                    self.optimizations.push((location, target));
                }
            }
            _ => {}
        }
        self.super_terminator(terminator, location);
    }
}
pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    body: &'a Body<'tcx>,
    optimizations: Vec<(Location, BasicBlock)>,
    def_id: LocalDefId,
}
<commit_msg>The optimization should also apply for DropAndReplace<commit_after>//! This pass replaces a drop of a type that does not need dropping, with a goto

use crate::transform::{MirPass, MirSource};
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;

pub struct RemoveUnneededDrops;

impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        trace!("Running RemoveUnneededDrops on {:?}", source);
        let mut opt_finder = RemoveUnneededDropsOptimizationFinder {
            tcx,
            body,
            optimizations: vec![],
            def_id: source.def_id().expect_local(),
        };
        opt_finder.visit_body(body);
        for (loc, target) in opt_finder.optimizations {
            let terminator = body.basic_blocks_mut()[loc.block].terminator_mut();
            debug!("SUCCESS: replacing `drop` with goto({:?})", target);
            terminator.kind = TerminatorKind::Goto { target };
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match terminator.kind {
            TerminatorKind::Drop { place, target, .. }
            | TerminatorKind::DropAndReplace { place, target, .. } => {
                let ty = place.ty(self.body, self.tcx);
                let needs_drop = ty.ty.needs_drop(self.tcx, self.tcx.param_env(self.def_id));
                if !needs_drop {
                    self.optimizations.push((location, target));
                }
            }
            _ => {}
        }
        self.super_terminator(terminator, location);
    }
}
pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> {
    tcx: TyCtxt<'tcx>,
    body: &'a Body<'tcx>,
    optimizations: Vec<(Location, BasicBlock)>,
    def_id: LocalDefId,
}
<|endoftext|>


<commit_before>// TODO:
//      - Simplify using instruction iterators
//      - Make movement mode
//      - Record modifiers

mod editor;
pub use self::editor::*;

mod mode;
pub use self::mode::*;

mod movement;
pub use self::movement::*;

mod cursor;
pub use self::cursor::*;

mod insert;
pub use self::insert::*;

mod exec;
pub use self::exec::*;

use redox::*;

pub fn main() {
    let mut window = Window::new((rand() % 400 + 50) as isize,
								 (rand() % 300 + 50) as isize,
								 576,
								 400,
								 &"Sodium");

    let mut editor = Editor::new();

    editor.iter = Some({
        window.clone().filter_map(|x| {
            match x.to_option() {
                EventOption::Key(k) if k.pressed => {

                    Some(k.character)
                }
                _ => None,
            }
        })
    });
    window.set([255, 255, 255, 255]);

}

pub fn redraw() {
    /*
                    // Redraw window
                    window.set([255, 255, 255, 255]);

                    for (y, row) in editor.text.iter().enumerate() {
                        for (x, c) in row.iter().enumerate() {
                            window.char(8 * (y - editor.scroll_y) as isize, 16 * (x - editor.scroll_x) as isize, *c, [128, 128, 128, 255]);
                            if editor.cursor().x == x && editor.cursor().y == y {
                                window.char(8 * (y - editor.scroll_y) as isize, 16 * (x - editor.scroll_x) as isize, '_', [128, 128, 128, 255]);
                            }
                        }
                    }
    */
}
<commit_msg>Fix compile errors<commit_after>// TODO:
//      - Simplify using instruction iterators
//      - Make movement mode
//      - Record modifiers

mod editor;
pub use self::editor::*;

mod mode;
pub use self::mode::*;

mod movement;
pub use self::movement::*;

mod cursor;
pub use self::cursor::*;

mod insert;
pub use self::insert::*;

mod exec;
pub use self::exec::*;

use redox::*;

pub fn main() {
    let mut window = Window::new((rand() % 400 + 50) as isize,
								 (rand() % 300 + 50) as isize,
								 576,
								 400,
								 &"Sodium");

    let mut editor = Editor::new();

    editor.iter = Some({
        window.clone().filter_map(|x| {
            match x.to_option() {
                EventOption::Key(k) if k.pressed => {

                    Some(k.character)
                }
                _ => None,
            }
        })
    });
//    window.set([255, 255, 255, 255]);

}

pub fn redraw() {
    /*
                    // Redraw window
                    window.set([255, 255, 255, 255]);

                    for (y, row) in editor.text.iter().enumerate() {
                        for (x, c) in row.iter().enumerate() {
                            window.char(8 * (y - editor.scroll_y) as isize, 16 * (x - editor.scroll_x) as isize, *c, [128, 128, 128, 255]);
                            if editor.cursor().x == x && editor.cursor().y == y {
                                window.char(8 * (y - editor.scroll_y) as isize, 16 * (x - editor.scroll_x) as isize, '_', [128, 128, 128, 255]);
                            }
                        }
                    }
    */
}
<|endoftext|>


<commit_before>//! # generator stack
//!
//!

use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io;
use std::os::raw::c_void;
use std::ptr;

#[cfg(all(unix, target_arch = "x86_64"))]
#[path = "unix.rs"]
pub mod sys;

#[cfg(all(windows, target_arch = "x86_64"))]
#[path = "windows.rs"]
pub mod sys;

/// Error type returned by stack allocation methods.
#[derive(Debug)]
pub enum StackError {
    /// Contains the maximum amount of memory allowed to be allocated as stack space.
    ExceedsMaximumSize(usize),

    /// Returned if some kind of I/O error happens during allocation.
    IoError(io::Error),
}

impl Display for StackError {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        match *self {
            StackError::ExceedsMaximumSize(size) => write!(
                fmt,
                "Requested more than max size of {} bytes for a stack",
                size
            ),
            StackError::IoError(ref e) => e.fmt(fmt),
        }
    }
}

impl Error for StackError {
    fn description(&self) -> &str {
        match *self {
            StackError::ExceedsMaximumSize(_) => "exceeds maximum stack size",
            StackError::IoError(ref e) => e.description(),
        }
    }
    fn cause(&self) -> Option<&dyn Error> {
        match *self {
            StackError::ExceedsMaximumSize(_) => None,
            StackError::IoError(ref e) => Some(e),
        }
    }
}

/// Represents any kind of stack memory.
///
/// `FixedSizeStack` as well as `ProtectedFixedSizeStack`
/// can be used to allocate actual stack space.
#[derive(Debug)]
pub struct SysStack {
    top: *mut c_void,
    bottom: *mut c_void,
}

impl SysStack {
    /// Creates a (non-owning) representation of some stack memory.
    ///
    /// It is unsafe because it is your responsibility to make sure that `top` and `bottom` are valid
    /// addresses.
    #[inline]
    pub unsafe fn new(top: *mut c_void, bottom: *mut c_void) -> SysStack {
        debug_assert!(top >= bottom);

        SysStack { top, bottom }
    }

    /// Returns the top of the stack from which on it grows downwards towards bottom().
    #[inline]
    pub fn top(&self) -> *mut c_void {
        self.top
    }

    /// Returns the bottom of the stack and thus it's end.
    #[inline]
    pub fn bottom(&self) -> *mut c_void {
        self.bottom
    }

    /// Returns the size of the stack between top() and bottom().
    #[inline]
    pub fn len(&self) -> usize {
        self.top as usize - self.bottom as usize
    }

    /// Returns the minimal stack size allowed by the current platform.
    #[inline]
    pub fn min_size() -> usize {
        sys::min_stack_size()
    }

    /// Allocates a new stack of `size`.
    fn allocate(mut size: usize, protected: bool) -> Result<SysStack, StackError> {
        let page_size = sys::page_size();
        let min_stack_size = sys::min_stack_size();
        let max_stack_size = sys::max_stack_size();
        let add_shift = if protected { 1 } else { 0 };
        let add = page_size << add_shift;

        if size < min_stack_size {
            size = min_stack_size;
        }

        size = (size - 1) & !(page_size.overflowing_sub(1).0);

        if let Some(size) = size.checked_add(add) {
            if size <= max_stack_size {
                let mut ret = unsafe { sys::allocate_stack(size) };

                if protected {
                    if let Ok(stack) = ret {
                        ret = unsafe { sys::protect_stack(&stack) };
                    }
                }

                return ret.map_err(StackError::IoError);
            }
        }

        Err(StackError::ExceedsMaximumSize(max_stack_size - add))
    }
}

unsafe impl Send for SysStack {}

/// generator stack
pub struct Stack {
    buf: SysStack,
}

impl Stack {
    pub fn empty() -> Stack {
        Stack {
            buf: SysStack {
                top: ptr::null_mut(),
                bottom: ptr::null_mut(),
            },
        }
    }

    /// Allocate a new stack of `size`. If size = 0, this is a `dummy_stack`
    pub fn new(size: usize) -> Stack {
        let track = (size & 1) != 0;
        let mut bytes = size * std::mem::size_of::<usize>();
        // the minimal size
        let min_size = SysStack::min_size();

        if bytes < min_size {
            bytes = min_size;
        }

        let buf = SysStack::allocate(bytes, true).expect("failed to alloc sys stack");

        let stk = Stack { buf };

        // if size is not even we do the full foot print test
        let count = if track {
            stk.size()
        } else {
            // we only check the last few words
            8
        };

        unsafe {
            let buf = stk.buf.bottom() as *mut usize;
            ptr::write_bytes(buf, 0xEE, count);
        }
        stk
    }

    /// get used stack size
    pub fn get_used_size(&self) -> usize {
        let mut offset: usize = 0;
        unsafe {
            let mut magic: usize = 0xEE;
            ptr::write_bytes(&mut magic, 0xEE, 1);
            let mut ptr = self.buf.bottom() as *mut usize;
            while *ptr == magic {
                offset += 1;
                ptr = ptr.offset(1);
            }
        }
        let cap = self.size();
        cap - offset
    }

    /// get the stack cap
    #[inline]
    pub fn size(&self) -> usize {
        self.buf.len() / std::mem::size_of::<usize>()
    }

    /// Point to the high end of the allocated stack
    pub fn end(&self) -> *mut usize {
        self.buf.top() as *mut _
    }

    /// Point to the low end of the allocated stack
    #[allow(dead_code)]
    pub fn begin(&self) -> *mut usize {
        self.buf.bottom() as *mut _
    }
}

impl Drop for Stack {
    fn drop(&mut self) {
        if self.buf.len() == 0 {
            return;
        }
        let page_size = sys::page_size();
        let guard = (self.buf.bottom() as usize - page_size) as *mut c_void;
        let size_with_guard = self.buf.len() + page_size;
        unsafe {
            sys::deallocate_stack(guard, size_with_guard);
        }
    }
}
<commit_msg>remove deprecated Error::description, replace deprecated cause with source<commit_after>//! # generator stack
//!
//!

use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io;
use std::os::raw::c_void;
use std::ptr;

#[cfg(all(unix, target_arch = "x86_64"))]
#[path = "unix.rs"]
pub mod sys;

#[cfg(all(windows, target_arch = "x86_64"))]
#[path = "windows.rs"]
pub mod sys;

/// Error type returned by stack allocation methods.
#[derive(Debug)]
pub enum StackError {
    /// Contains the maximum amount of memory allowed to be allocated as stack space.
    ExceedsMaximumSize(usize),

    /// Returned if some kind of I/O error happens during allocation.
    IoError(io::Error),
}

impl Display for StackError {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        match *self {
            StackError::ExceedsMaximumSize(size) => write!(
                fmt,
                "Requested more than max size of {} bytes for a stack",
                size
            ),
            StackError::IoError(ref e) => e.fmt(fmt),
        }
    }
}

impl Error for StackError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match *self {
            StackError::ExceedsMaximumSize(_) => None,
            StackError::IoError(ref e) => Some(e),
        }
    }
}

/// Represents any kind of stack memory.
///
/// `FixedSizeStack` as well as `ProtectedFixedSizeStack`
/// can be used to allocate actual stack space.
#[derive(Debug)]
pub struct SysStack {
    top: *mut c_void,
    bottom: *mut c_void,
}

impl SysStack {
    /// Creates a (non-owning) representation of some stack memory.
    ///
    /// It is unsafe because it is your responsibility to make sure that `top` and `bottom` are valid
    /// addresses.
    #[inline]
    pub unsafe fn new(top: *mut c_void, bottom: *mut c_void) -> SysStack {
        debug_assert!(top >= bottom);

        SysStack { top, bottom }
    }

    /// Returns the top of the stack from which on it grows downwards towards bottom().
    #[inline]
    pub fn top(&self) -> *mut c_void {
        self.top
    }

    /// Returns the bottom of the stack and thus it's end.
    #[inline]
    pub fn bottom(&self) -> *mut c_void {
        self.bottom
    }

    /// Returns the size of the stack between top() and bottom().
    #[inline]
    pub fn len(&self) -> usize {
        self.top as usize - self.bottom as usize
    }

    /// Returns the minimal stack size allowed by the current platform.
    #[inline]
    pub fn min_size() -> usize {
        sys::min_stack_size()
    }

    /// Allocates a new stack of `size`.
    fn allocate(mut size: usize, protected: bool) -> Result<SysStack, StackError> {
        let page_size = sys::page_size();
        let min_stack_size = sys::min_stack_size();
        let max_stack_size = sys::max_stack_size();
        let add_shift = if protected { 1 } else { 0 };
        let add = page_size << add_shift;

        if size < min_stack_size {
            size = min_stack_size;
        }

        size = (size - 1) & !(page_size.overflowing_sub(1).0);

        if let Some(size) = size.checked_add(add) {
            if size <= max_stack_size {
                let mut ret = unsafe { sys::allocate_stack(size) };

                if protected {
                    if let Ok(stack) = ret {
                        ret = unsafe { sys::protect_stack(&stack) };
                    }
                }

                return ret.map_err(StackError::IoError);
            }
        }

        Err(StackError::ExceedsMaximumSize(max_stack_size - add))
    }
}

unsafe impl Send for SysStack {}

/// generator stack
pub struct Stack {
    buf: SysStack,
}

impl Stack {
    pub fn empty() -> Stack {
        Stack {
            buf: SysStack {
                top: ptr::null_mut(),
                bottom: ptr::null_mut(),
            },
        }
    }

    /// Allocate a new stack of `size`. If size = 0, this is a `dummy_stack`
    pub fn new(size: usize) -> Stack {
        let track = (size & 1) != 0;
        let mut bytes = size * std::mem::size_of::<usize>();
        // the minimal size
        let min_size = SysStack::min_size();

        if bytes < min_size {
            bytes = min_size;
        }

        let buf = SysStack::allocate(bytes, true).expect("failed to alloc sys stack");

        let stk = Stack { buf };

        // if size is not even we do the full foot print test
        let count = if track {
            stk.size()
        } else {
            // we only check the last few words
            8
        };

        unsafe {
            let buf = stk.buf.bottom() as *mut usize;
            ptr::write_bytes(buf, 0xEE, count);
        }
        stk
    }

    /// get used stack size
    pub fn get_used_size(&self) -> usize {
        let mut offset: usize = 0;
        unsafe {
            let mut magic: usize = 0xEE;
            ptr::write_bytes(&mut magic, 0xEE, 1);
            let mut ptr = self.buf.bottom() as *mut usize;
            while *ptr == magic {
                offset += 1;
                ptr = ptr.offset(1);
            }
        }
        let cap = self.size();
        cap - offset
    }

    /// get the stack cap
    #[inline]
    pub fn size(&self) -> usize {
        self.buf.len() / std::mem::size_of::<usize>()
    }

    /// Point to the high end of the allocated stack
    pub fn end(&self) -> *mut usize {
        self.buf.top() as *mut _
    }

    /// Point to the low end of the allocated stack
    #[allow(dead_code)]
    pub fn begin(&self) -> *mut usize {
        self.buf.bottom() as *mut _
    }
}

impl Drop for Stack {
    fn drop(&mut self) {
        if self.buf.len() == 0 {
            return;
        }
        let page_size = sys::page_size();
        let guard = (self.buf.bottom() as usize - page_size) as *mut c_void;
        let size_with_guard = self.buf.len() + page_size;
        unsafe {
            sys::deallocate_stack(guard, size_with_guard);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 James R. Garrison
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;
extern crate num;

use self::libc::{c_char, c_double, c_float, c_int};

#[link(name = "arpack")]
extern {

    // single precision symmetric

    fn ssaupd_(ido: *mut c_int,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_float,
               resid: *mut c_float,
               ncv: *const c_int,
               v: *mut c_float,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_float,
               workl: *mut c_float,
               lworkl: *const c_int,
               info: *mut c_int);

    fn sseupd_(rvec: *const c_int,
               howmny: *const c_char,
               select: *mut c_int,
               d: *mut c_float,
               z: *mut c_float,
               ldz: *const c_int,
               sigma: *const c_float,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_float,
               resid: *mut c_float,
               ncv: *const c_int,
               v: *mut c_float,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_float,
               workl: *mut c_float,
               lworkl: *const c_int,
               ierr: *mut c_int);

    // double precision symmetric

    fn dsaupd_(ido: *mut c_int,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_double,
               resid: *mut c_double,
               ncv: *const c_int,
               v: *mut c_double,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_double,
               workl: *mut c_double,
               lworkl: *const c_int,
               info: *mut c_int);

    fn dseupd_(rvec: *const c_int,
               howmny: *const c_char,
               select: *mut c_int,
               d: *mut c_double,
               z: *mut c_double,
               ldz: *const c_int,
               sigma: *const c_double,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_double,
               resid: *mut c_double,
               ncv: *const c_int,
               v: *mut c_double,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_double,
               workl: *mut c_double,
               lworkl: *const c_int,
               ierr: *mut c_int);

}

pub enum Which {
    LargestMagnitude,
    SmallestMagnitude,
    LargestAlgebraic,
    SmallestAlgebraic,
    BothEnds,
}

fn which_to_str(which: Which) -> &'static str {
    match which {
        LargestMagnitude => "LM",
        SmallestMagnitude => "SM",
        LargestAlgebraic => "LA",
        SmallestAlgebraic => "SA",
        BothEnds => "BE",
    }
}

// note: evals may potentially be modified even if there is an error.
fn dsaupd(n: c_int,
          nev: c_int,
          mv_multiply: |slice: &[c_double]| -> Vec<c_double>,
          evals: &mut Vec<c_double>,
          evecs: Option<&mut Vec<c_double>>) {
    assert!(nev > 0);
    assert!(nev < n);

    let mut ido: c_int = 0;
    let bmat = "I".to_c_str(); // not configurable, for now
    let which = which_to_str(SmallestAlgebraic).to_c_str();
    let tol: c_double = 0.0;
    let mut resid = Vec::from_elem(n as uint, 0.0 as c_double);
    let ncv = {
        let possible_ncv: c_int = 4 * nev;
        if possible_ncv > n {
            n
        } else {
            possible_ncv
        }
    };
    let ldv: c_int = n;
    let mut v = Vec::from_elem((ldv * ncv) as uint, 0.0 as c_double);
    let mut iparam = vec![1 as c_int, 0, 3 * n, 0, 0, 0, 1, 0, 0, 0, 0];
    // fixme: may wish to tune iparam[2] differently.
    let mut ipntr = Vec::from_elem(11, 0 as c_int);
    let mut workd = Vec::from_elem(3 * n as uint, 0.0 as c_double);
    let lworkl: c_int = ncv * (ncv + 8);
    let mut workl = Vec::from_elem(lworkl as uint, 0.0 as c_double);
    let mut info: c_int = 0;

    loop {
        unsafe {
            dsaupd_(&mut ido, bmat.as_ptr(), &n, which.as_ptr(), &nev, &tol,
                    resid.as_mut_ptr(), &ncv, v.as_mut_ptr(), &ldv,
                    iparam.as_mut_ptr(), ipntr.as_mut_ptr(), workd.as_mut_ptr(),
                    workl.as_mut_ptr(), &lworkl, &mut info);
        }

        if ido != 1 && ido != -1 {
            break;
        }

        // fixme: seems overkill
        let output = {
            let in_slice = workd.slice((ipntr[0] - 1) as uint,
                                       (ipntr[0] - 1 + n) as uint);
            mv_multiply(in_slice)
        };
        let out_slice = workd.mut_slice((ipntr[1] - 1) as uint,
                                        (ipntr[1] - 1 + n) as uint);
        out_slice.clone_from_slice(output.as_slice());
    }

    if info < 0 {
        // fixme: return error using enum
        return;
    }

    let rvec: c_int = match evecs {
        Some(ref evecs) => 1,
        None => 0
    };
    let mut select = Vec::from_elem(ncv as uint, 0 as c_int);
    evals.clear(); // called ``d`` in arpack
    evals.grow(nev as uint, &0.0);
    let sigma: c_double = 0.0;
    let mut ierr: c_int = 0;

    let howmny = "A".to_c_str();
    unsafe {
        dseupd_(&rvec, howmny.as_ptr(), select.as_mut_ptr(), evals.as_mut_ptr(),
                v.as_mut_ptr(), &ldv, &sigma, bmat.as_ptr(),
	        &n, which.as_ptr(), &nev, &tol, resid.as_mut_ptr(), &ncv, v.as_mut_ptr(), &ldv,
	        iparam.as_mut_ptr(), ipntr.as_mut_ptr(), workd.as_mut_ptr(), workl.as_mut_ptr(), &lworkl, &mut ierr);
    }

    if ierr != 0 {
        // fixme: return error code
        return;
    }

    match evecs {
        Some(evecs) => {
            // copy the vector.  fixme: seems overkill.
            evecs.clear();
            evecs.grow((nev * n) as uint, &0.0);
            evecs.as_mut_slice().clone_from_slice(v.as_slice());
        },
        None => ()
    };
}

#[cfg(test)]
mod tests {

    extern crate libc;
    extern crate test;

    use super::dsaupd;
    use self::test::Bencher;
    use self::libc::c_double;

    #[test]
    fn test_dsaupd() {
        let mut evals: Vec<c_double> = Vec::new();
        let mut evecs: Vec<c_double> = Vec::new();
        dsaupd(8, 2, |slice: &[c_double]| {
            // FIXME: this is probably more involved than necessary
            slice.to_vec().move_iter().map(|i| 2.0 * i).collect()
        }, &mut evals, Some(&mut evecs));
    }

    #[bench]
    fn benchmark_dsaupd(b: &mut Bencher) {
        b.iter(|| test_dsaupd());
    }

}
<commit_msg>grow() method was changed<commit_after>// Copyright 2014 James R. Garrison
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate libc;
extern crate num;

use self::libc::{c_char, c_double, c_float, c_int};

#[link(name = "arpack")]
extern {

    // single precision symmetric

    fn ssaupd_(ido: *mut c_int,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_float,
               resid: *mut c_float,
               ncv: *const c_int,
               v: *mut c_float,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_float,
               workl: *mut c_float,
               lworkl: *const c_int,
               info: *mut c_int);

    fn sseupd_(rvec: *const c_int,
               howmny: *const c_char,
               select: *mut c_int,
               d: *mut c_float,
               z: *mut c_float,
               ldz: *const c_int,
               sigma: *const c_float,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_float,
               resid: *mut c_float,
               ncv: *const c_int,
               v: *mut c_float,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_float,
               workl: *mut c_float,
               lworkl: *const c_int,
               ierr: *mut c_int);

    // double precision symmetric

    fn dsaupd_(ido: *mut c_int,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_double,
               resid: *mut c_double,
               ncv: *const c_int,
               v: *mut c_double,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_double,
               workl: *mut c_double,
               lworkl: *const c_int,
               info: *mut c_int);

    fn dseupd_(rvec: *const c_int,
               howmny: *const c_char,
               select: *mut c_int,
               d: *mut c_double,
               z: *mut c_double,
               ldz: *const c_int,
               sigma: *const c_double,
               bmat: *const c_char,
               n: *const c_int,
               which: *const c_char,
               nev: *const c_int,
               tol: *const c_double,
               resid: *mut c_double,
               ncv: *const c_int,
               v: *mut c_double,
               ldv: *const c_int,
               iparam: *mut c_int,
               ipntr: *mut c_int,
               workd: *mut c_double,
               workl: *mut c_double,
               lworkl: *const c_int,
               ierr: *mut c_int);

}

pub enum Which {
    LargestMagnitude,
    SmallestMagnitude,
    LargestAlgebraic,
    SmallestAlgebraic,
    BothEnds,
}

fn which_to_str(which: Which) -> &'static str {
    match which {
        LargestMagnitude => "LM",
        SmallestMagnitude => "SM",
        LargestAlgebraic => "LA",
        SmallestAlgebraic => "SA",
        BothEnds => "BE",
    }
}

// note: evals may potentially be modified even if there is an error.
fn dsaupd(n: c_int,
          nev: c_int,
          mv_multiply: |slice: &[c_double]| -> Vec<c_double>,
          evals: &mut Vec<c_double>,
          evecs: Option<&mut Vec<c_double>>) {
    assert!(nev > 0);
    assert!(nev < n);

    let mut ido: c_int = 0;
    let bmat = "I".to_c_str(); // not configurable, for now
    let which = which_to_str(SmallestAlgebraic).to_c_str();
    let tol: c_double = 0.0;
    let mut resid = Vec::from_elem(n as uint, 0.0 as c_double);
    let ncv = {
        let possible_ncv: c_int = 4 * nev;
        if possible_ncv > n {
            n
        } else {
            possible_ncv
        }
    };
    let ldv: c_int = n;
    let mut v = Vec::from_elem((ldv * ncv) as uint, 0.0 as c_double);
    let mut iparam = vec![1 as c_int, 0, 3 * n, 0, 0, 0, 1, 0, 0, 0, 0];
    // fixme: may wish to tune iparam[2] differently.
    let mut ipntr = Vec::from_elem(11, 0 as c_int);
    let mut workd = Vec::from_elem(3 * n as uint, 0.0 as c_double);
    let lworkl: c_int = ncv * (ncv + 8);
    let mut workl = Vec::from_elem(lworkl as uint, 0.0 as c_double);
    let mut info: c_int = 0;

    loop {
        unsafe {
            dsaupd_(&mut ido, bmat.as_ptr(), &n, which.as_ptr(), &nev, &tol,
                    resid.as_mut_ptr(), &ncv, v.as_mut_ptr(), &ldv,
                    iparam.as_mut_ptr(), ipntr.as_mut_ptr(), workd.as_mut_ptr(),
                    workl.as_mut_ptr(), &lworkl, &mut info);
        }

        if ido != 1 && ido != -1 {
            break;
        }

        // fixme: seems overkill
        let output = {
            let in_slice = workd.slice((ipntr[0] - 1) as uint,
                                       (ipntr[0] - 1 + n) as uint);
            mv_multiply(in_slice)
        };
        let out_slice = workd.mut_slice((ipntr[1] - 1) as uint,
                                        (ipntr[1] - 1 + n) as uint);
        out_slice.clone_from_slice(output.as_slice());
    }

    if info < 0 {
        // fixme: return error using enum
        return;
    }

    let rvec: c_int = match evecs {
        Some(ref evecs) => 1,
        None => 0
    };
    let mut select = Vec::from_elem(ncv as uint, 0 as c_int);
    evals.clear(); // called ``d`` in arpack
    evals.grow(nev as uint, 0.0);
    let sigma: c_double = 0.0;
    let mut ierr: c_int = 0;

    let howmny = "A".to_c_str();
    unsafe {
        dseupd_(&rvec, howmny.as_ptr(), select.as_mut_ptr(), evals.as_mut_ptr(),
                v.as_mut_ptr(), &ldv, &sigma, bmat.as_ptr(),
	        &n, which.as_ptr(), &nev, &tol, resid.as_mut_ptr(), &ncv, v.as_mut_ptr(), &ldv,
	        iparam.as_mut_ptr(), ipntr.as_mut_ptr(), workd.as_mut_ptr(), workl.as_mut_ptr(), &lworkl, &mut ierr);
    }

    if ierr != 0 {
        // fixme: return error code
        return;
    }

    match evecs {
        Some(evecs) => {
            // copy the vector.  fixme: seems overkill.
            evecs.clear();
            evecs.grow((nev * n) as uint, 0.0);
            evecs.as_mut_slice().clone_from_slice(v.as_slice());
        },
        None => ()
    };
}

#[cfg(test)]
mod tests {

    extern crate libc;
    extern crate test;

    use super::dsaupd;
    use self::test::Bencher;
    use self::libc::c_double;

    #[test]
    fn test_dsaupd() {
        let mut evals: Vec<c_double> = Vec::new();
        let mut evecs: Vec<c_double> = Vec::new();
        dsaupd(8, 2, |slice: &[c_double]| {
            // FIXME: this is probably more involved than necessary
            slice.to_vec().move_iter().map(|i| 2.0 * i).collect()
        }, &mut evals, Some(&mut evecs));
    }

    #[bench]
    fn benchmark_dsaupd(b: &mut Bencher) {
        b.iter(|| test_dsaupd());
    }

}
<|endoftext|>


<commit_before>// Copyright (c) 2019, Ben Boeckel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of this project nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::iter;

use crate::keytypes::User;
use crate::{Keyring, SpecialKeyring};

use super::utils;
use super::utils::kernel::*;
use super::utils::keys::*;

#[test]
fn empty_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<EmptyKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn unsupported_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<UnsupportedKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::ENODEV));
}

#[test]
fn invalid_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<InvalidKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::EPERM));
}

#[test]
fn maxlen_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<MaxLenKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::ENODEV));
}

#[test]
fn overlong_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<OverlongKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn keyring_with_payload() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring
        .add_key::<KeyringShadow, _, _>("", "payload")
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn max_user_description() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    // Subtract one because the NUL is added in the kernel API.
    let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE - 1).collect();
    let res = keyring.add_key::<User, _, _>(maxdesc.as_ref(), "payload".as_bytes());
    // If the user's quota is smaller than this, it's an error.
    if KEY_INFO.maxbytes < *PAGE_SIZE {
        assert_eq!(res.unwrap_err(), errno::Errno(libc::EDQUOT));
    } else {
        let key = res.unwrap();
        assert_eq!(key.description().unwrap().description, maxdesc);
        key.invalidate().unwrap();
    }
}

#[test]
fn overlong_user_description() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    // On MIPS with < 3.19, there is a bug where this is allowed. 3.19 was released in Feb 2015,
    // so this is being ignored here.
    let toolarge: String = iter::repeat('a').take(*PAGE_SIZE).collect();
    let err = keyring
        .add_key::<User, _, _>(toolarge.as_ref(), "payload".as_bytes())
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn invalid_keyring() {
    let mut keyring = utils::invalid_keyring();
    let err = keyring
        .add_key::<User, _, _>("invalid_keyring", "payload".as_bytes())
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn add_key_to_non_keyring() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Session).unwrap();
    let expected = "stuff".as_bytes();
    let key = keyring
        .add_key::<User, _, _>("add_key_to_non_keyring", expected)
        .unwrap();

    let mut not_a_keyring = unsafe { Keyring::new(key.serial()) };
    let err = not_a_keyring
        .add_key::<User, _, _>("add_key_to_non_keyring", expected)
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::ENOTDIR));

    keyring.unlink_key(&key).unwrap();
}

#[test]
fn add_key_to_session() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Session).unwrap();
    let expected = "stuff".as_bytes();
    let mut key = keyring
        .add_key::<User, _, _>("add_key_to_session", expected)
        .unwrap();
    let payload = key.read().unwrap();
    assert_eq!(payload, expected);
    let new_expected = "lizard".as_bytes();
    key.update(new_expected).unwrap();
    let new_payload = key.read().unwrap();
    assert_eq!(new_payload, new_expected);
    keyring.unlink_key(&key).unwrap();
}
<commit_msg>tests/add: whitespace nit<commit_after>// Copyright (c) 2019, Ben Boeckel
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of this project nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::iter;

use crate::keytypes::User;
use crate::{Keyring, SpecialKeyring};

use super::utils;
use super::utils::kernel::*;
use super::utils::keys::*;

#[test]
fn empty_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<EmptyKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn unsupported_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<UnsupportedKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::ENODEV));
}

#[test]
fn invalid_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<InvalidKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::EPERM));
}

#[test]
fn maxlen_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<MaxLenKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::ENODEV));
}

#[test]
fn overlong_key_type() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring.add_key::<OverlongKey, _, _>("", ()).unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn keyring_with_payload() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    let err = keyring
        .add_key::<KeyringShadow, _, _>("", "payload")
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn max_user_description() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    // Subtract one because the NUL is added in the kernel API.
    let maxdesc: String = iter::repeat('a').take(*PAGE_SIZE - 1).collect();
    let res = keyring.add_key::<User, _, _>(maxdesc.as_ref(), "payload".as_bytes());
    // If the user's quota is smaller than this, it's an error.
    if KEY_INFO.maxbytes < *PAGE_SIZE {
        assert_eq!(res.unwrap_err(), errno::Errno(libc::EDQUOT));
    } else {
        let key = res.unwrap();
        assert_eq!(key.description().unwrap().description, maxdesc);
        key.invalidate().unwrap();
    }
}

#[test]
fn overlong_user_description() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Process).unwrap();
    // On MIPS with < 3.19, there is a bug where this is allowed. 3.19 was released in Feb 2015,
    // so this is being ignored here.
    let toolarge: String = iter::repeat('a').take(*PAGE_SIZE).collect();
    let err = keyring
        .add_key::<User, _, _>(toolarge.as_ref(), "payload".as_bytes())
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn invalid_keyring() {
    let mut keyring = utils::invalid_keyring();
    let err = keyring
        .add_key::<User, _, _>("invalid_keyring", "payload".as_bytes())
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::EINVAL));
}

#[test]
fn add_key_to_non_keyring() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Session).unwrap();
    let expected = "stuff".as_bytes();
    let key = keyring
        .add_key::<User, _, _>("add_key_to_non_keyring", expected)
        .unwrap();

    let mut not_a_keyring = unsafe { Keyring::new(key.serial()) };
    let err = not_a_keyring
        .add_key::<User, _, _>("add_key_to_non_keyring", expected)
        .unwrap_err();
    assert_eq!(err, errno::Errno(libc::ENOTDIR));

    keyring.unlink_key(&key).unwrap();
}

#[test]
fn add_key_to_session() {
    let mut keyring = Keyring::attach_or_create(SpecialKeyring::Session).unwrap();
    let expected = "stuff".as_bytes();
    let mut key = keyring
        .add_key::<User, _, _>("add_key_to_session", expected)
        .unwrap();
    let payload = key.read().unwrap();
    assert_eq!(payload, expected);

    let new_expected = "lizard".as_bytes();
    key.update(new_expected).unwrap();
    let new_payload = key.read().unwrap();
    assert_eq!(new_payload, new_expected);
    keyring.unlink_key(&key).unwrap();
}
<|endoftext|>