<commit_before>use super::{N, P2, V2, RoughEq, THICKNESS, VecLike};
use super::curves::{Curve, FiniteCurve, Segment};
use super::intersect::{Intersect, Intersection, IntersectionResult};
use ordered_float::OrderedFloat;

type ScannerFn<'a> = fn(&mut StartOffsetState, &'a Segment) -> Option<(&'a Segment, N)>;
type ScanIter<'a> =
    ::std::iter::Scan<::std::slice::Iter<'a, Segment>, StartOffsetState, ScannerFn<'a>>;

#[derive(Debug)]
pub enum PathError {
    EmptyPath,
    NotContinuous,
}

#[cfg_attr(feature = "compact_containers", derive(Compact))]
#[derive(Clone)]
pub struct Path {
    pub segments: VecLike<Segment>,
}

impl Path {
    pub fn new_unchecked(segments: VecLike<Segment>) -> Self {
        Path { segments }
    }

    pub fn new(segments: VecLike<Segment>) -> Result<Self, PathError> {
        if segments.is_empty() {
            Result::Err(PathError::EmptyPath)
        } else {
            let continuous = segments.windows(2).all(|seg_pair| {
                seg_pair[0]
                    .end()
                    .rough_eq_by(seg_pair[1].start(), THICKNESS)
            });

            if !continuous {
                Result::Err(PathError::NotContinuous)
            } else {
                Result::Ok(Self::new_unchecked(segments))
            }
        }
    }

    pub fn scan_segments<'a>(
        start_offset: &mut StartOffsetState,
        segment: &'a Segment,
    ) -> Option<(&'a Segment, N)> {
        let pair = (segment, start_offset.0);
        start_offset.0 += segment.length;
        Some(pair)
    }

    pub fn segments_with_start_offsets(&self) -> ScanIter {
        self.segments
            .iter()
            .scan(StartOffsetState(0.0), Self::scan_segments)
    }

    pub fn find_on_segment(&self, distance: N) -> Option<(&Segment, N)> {
        let mut distance_covered = 0.0;
        for segment in self.segments.iter() {
            let new_distance_covered = distance_covered + segment.length();
            if new_distance_covered > distance {
                return Some((segment, distance - distance_covered));
            }
            distance_covered = new_distance_covered;
        }
        None
    }

    pub fn self_intersections(&self) -> Vec<Intersection> {
        self.segments_with_start_offsets()
            .enumerate()
            .flat_map(|(i, (segment_a, offset_a))| {
                self.segments_with_start_offsets()
                    .skip(i + 1)
                    .flat_map(
                        |(segment_b, offset_b)| match (segment_a, segment_b).intersect() {
                            IntersectionResult::Intersecting(intersections) => intersections
                                .into_iter()
                                .filter_map(|intersection| {
                                    if intersection.along_a.rough_eq_by(0.0, THICKNESS)
                                        || intersection
                                            .along_a
                                            .rough_eq_by(segment_a.length(), THICKNESS)
                                        || intersection.along_b.rough_eq_by(0.0, THICKNESS)
                                        || intersection
                                            .along_b
                                            .rough_eq_by(segment_b.length(), THICKNESS)
                                    {
                                        None
                                    } else {
                                        Some(Intersection {
                                            position: intersection.position,
                                            along_a: offset_a + intersection.along_a,
                                            along_b: offset_b + intersection.along_b,
                                        })
                                    }
                                })
                                .collect::<Vec<_>>(),
                            _ => vec![],
                        },
                    )
                    .collect::<Vec<_>>()
            })
            .collect()
    }

    pub fn is_closed(&self) -> bool {
        self.segments
            .last()
            .unwrap()
            .end()
            .rough_eq_by(self.segments.first().unwrap().start(), THICKNESS)
    }

    pub fn concat(&self, other: &Self) -> Result<Self, PathError> {
        // TODO: somehow change this to move self and other into here
        // but then segments would have to return [Segment], possible?
        if self.end().rough_eq_by(other.start(), THICKNESS) {
            Ok(Self::new_unchecked(
                self.segments
                    .iter()
                    .chain(other.segments.iter())
                    .cloned()
                    .collect(),
            ))
        } else {
            Err(PathError::NotContinuous)
        }
    }

    pub fn with_new_start_and_end(&self, new_start: P2, new_end: P2) -> Path {
        if self.segments.len() == 1 {
            let new_segment = if self.segments[0].is_linear() {
                Segment::line(new_start, new_end).expect("New start/end should work A")
            } else {
                Segment::arc_with_direction(new_start, self.segments[0].start_direction(), new_end)
                    .unwrap_or_else(|| {
                        Segment::line(new_start, new_end).expect("New start/end should work B")
                    })
            };

            Path::new_unchecked(Some(new_segment).into_iter().collect())
        } else {
            let first_segment = self.segments.first().unwrap();
            let last_segment = self.segments.last().unwrap();

            let new_last_segment = if last_segment.is_linear() {
                Segment::line(last_segment.start(), new_end).expect("New start/end should work C")
            } else {
                Segment::arc_with_direction(
                    last_segment.start(),
                    last_segment.start_direction(),
                    new_end,
                ).unwrap_or_else(|| {
                    Segment::line(last_segment.start(), new_end)
                        .expect("New start/end should work D")
                })
            };

            let new_first_segment = if first_segment.is_linear() {
                Segment::line(new_start, first_segment.end()).expect("New start/end should work E")
            } else {
                Segment::arc_with_direction(
                    new_start,
                    first_segment.start_direction(),
                    first_segment.end(),
                ).unwrap_or_else(|| {
                    Segment::line(new_start, first_segment.end())
                        .expect("New start/end should work F")
                })
            };

            let segments = Some(new_first_segment)
                .into_iter()
                .chain(self.segments[1..(self.segments.len() - 1)].iter().cloned())
                .chain(Some(new_last_segment))
                .collect();

            Path::new(segments).expect("New start/end should work G")
        }
    }

    pub fn concat_weld(&self, other: &Self, tolerance: N) -> Result<Self, PathError> {
        let distance = (other.start() - self.end()).norm();

        if distance < tolerance {
            let new_end = other.start();

            let distance_at_end = (other.end() - self.start()).norm();
            let other_closes_self = distance_at_end < tolerance;

            let new_start = if other_closes_self {
                other.end()
            } else {
                self.start()
            };

            let new_path = self
                .with_new_start_and_end(new_start, new_end)
                .concat(other)
                .expect("Normal concat should work at this point");

            if other_closes_self {
                assert!(new_path.is_closed());
            }

            Ok(new_path)
        } else {
            Err(PathError::NotContinuous)
        }
    }

    pub fn dash(&self, dash_length: N, gap_length: N) -> Vec<Path> {
        let mut on_dash = true;
        let mut position = 0.0;
        let mut dashes = Vec::new();

        while position < self.length() {
            let old_position = position;
            if on_dash {
                position += dash_length;
                if let Some(dash) = self.subsection(old_position, position) {
                    dashes.push(dash)
                }
            } else {
                position += gap_length;
            }

            on_dash = !on_dash;
        }

        dashes
    }

    pub fn to_svg(&self) -> String {
        self.segments
            .iter()
            .map(Segment::to_svg)
            .collect::<Vec<_>>()
            .join(" ")
    }

    pub fn from_svg(string: &str) -> Result<Self, PathError> {
        let mut tokens = string.split_whitespace();
        let mut position = P2::new(0.0, 0.0);
        let mut first_position = None;
        let mut segments = VecLike::new();

        while let Some(command) = tokens.next() {
            if command == "M" || command == "L" {
                let x: N = tokens
                    .next()
                    .expect("Expected 1st token after M/L")
                    .parse()
                    .expect("Can't parse 1st token after M/L");
                let y: N = tokens
                    .next()
                    .expect("Expected 2nd token after M/L")
                    .parse()
                    .expect("Can't parse 2nd token after M/L");

                let next_position = P2::new(x, y);

                if command == "L" {
                    segments.push(Segment::line(position, next_position).expect("Invalid Segment"));
                }

                position = next_position;
                if first_position.is_none() {
                    first_position = Some(next_position);
                }
            } else if command == "Z" {
                if let Some(closing_segment) = Segment::line(
                    position,
                    first_position.expect("Should have first_position"),
                ) {
                    segments.push(closing_segment);
                }
            }
        }

        Self::new(segments)
    }
}

pub struct StartOffsetState(N);

impl FiniteCurve for Path {
    fn length(&self) -> N {
        self.segments
            .iter()
            .map(|segment| segment.length())
            .fold(0.0, ::std::ops::Add::add)
    }

    fn along(&self, distance: N) -> P2 {
        match self.find_on_segment(distance) {
            Some((segment, distance_on_segment)) => segment.along(distance_on_segment),
            None => {
                if distance < 0.0 {
                    self.segments[0].start
                } else {
                    self.segments.last().unwrap().end
                }
            }
        }
    }

    fn direction_along(&self, distance: N) -> V2 {
        match self.find_on_segment(distance) {
            Some((segment, distance_on_segment)) => segment.direction_along(distance_on_segment),
            None => {
                if distance < 0.0 {
                    self.segments[0].start_direction()
                } else {
                    self.segments.last().unwrap().end_direction()
                }
            }
        }
    }

    fn start(&self) -> P2 {
        self.segments[0].start()
    }

    fn start_direction(&self) -> V2 {
        self.segments[0].start_direction()
    }

    fn end(&self) -> P2 {
        self.segments.last().unwrap().end()
    }

    fn end_direction(&self) -> V2 {
        self.segments.last().unwrap().end_direction()
    }

    fn reverse(&self) -> Self {
        Self::new_unchecked(self.segments.iter().rev().map(Segment::reverse).collect())
    }

    fn subsection(&self, start: N, end: N) -> Option<Path> {
        if start > end + THICKNESS && self.is_closed() {
            let maybe_first_half = self.subsection(start, self.length());
            let maybe_second_half = self.subsection(0.0, end);

            match (maybe_first_half, maybe_second_half) {
                (Some(first_half), Some(second_half)) => {
                    Some(first_half.concat_weld(&second_half, THICKNESS).unwrap_or_else(|_| {
                        panic!(
                            "Closed path, should always be continous: {:?} subsection {} - {}",
                            self, start, end
                        )
                    }))
                }
                (Some(first_half), None) => Some(first_half),
                (None, Some(second_half)) => Some(second_half),
                _ => None,
            }
        } else {
            let segments = self
                .segments_with_start_offsets()
                .filter_map(|pair: (&Segment, N)| {
                    let (segment, start_offset) = pair;
                    let end_offset = start_offset + segment.length;
                    if start_offset > end || end_offset < start {
                        None
                    } else {
                        segment.subsection(start - start_offset, end - start_offset)
                    }
                })
                .collect();
            Path::new(segments).ok()
        }
    }

    fn shift_orthogonally(&self, shift_to_right: N) -> Option<Path> {
        let segments = self
            .segments
            .iter()
            .filter_map(|segment| segment.shift_orthogonally(shift_to_right))
            .collect::<Vec<_>>();
        let mut glued_segments = VecLike::new();
        let mut window_segments_iter = segments.iter().peekable();
        while let Some(segment) = window_segments_iter.next() {
            glued_segments.push(*segment);
            match window_segments_iter.peek() {
                Some(next_segment) => {
                    if !segment.end().rough_eq_by(next_segment.start(), THICKNESS) {
                        glued_segments.push(Segment::line(segment.end(), next_segment.start())?);
                    }
                }
                None => break,
            }
        }
        if glued_segments.is_empty() {
            None
        } else {
            let was_closed = self.end().rough_eq_by(self.start(), THICKNESS);
            let new_end = glued_segments.last().unwrap().end();
            let new_start = glued_segments[0].start();
            if was_closed && !new_end.rough_eq_by(new_start, THICKNESS) {
                glued_segments.push(Segment::line(new_end, new_start)?);
            }
            Some(Self::new(glued_segments).unwrap())
        }
    }
}

impl Curve for Path {
    fn project_with_tolerance(&self, point: P2, tolerance: N) -> Option<(N, P2)> {
        self.segments_with_start_offsets()
            .filter_map(|pair: (&Segment, N)| {
                let (segment, start_offset) = pair;
                segment
                    .project_with_tolerance(point, tolerance)
                    .map(|(offset, projected_point)| (offset + start_offset, projected_point))
            })
            .min_by_key(|(_offset, projected_point)| OrderedFloat((projected_point - point).norm()))
    }

    fn includes(&self, point: P2) -> bool {
        self.segments.iter().any(|segment| segment.includes(point))
    }

    fn distance_to(&self, point: P2) -> N {
        if let Some((_offset, projected_point)) = self.project(point) {
            (point - projected_point).norm()
        } else {
            *::std::cmp::min(
                OrderedFloat((point - self.start()).norm()),
                OrderedFloat((point - self.end()).norm()),
            )
        }
    }
}

impl<'a> RoughEq for &'a Path {
    fn rough_eq_by(&self, other: &Path, tolerance: N) -> bool {
        self.segments.len() == other.segments.len() && if self.is_closed() && other.is_closed() {
            // TODO: this is strictly too loose
            // and maybe this should be moved to shape instead,
            // since the paths are *not* exactly equal
            self.segments.iter().all(|segment_1| {
                other
                    .segments
                    .iter()
                    .any(|segment_2| segment_1.rough_eq_by(segment_2, tolerance))
            })
        } else {
            self.segments
                .iter()
                .zip(other.segments.iter())
                .all(|(segment_1, segment_2)| segment_1.rough_eq_by(segment_2, tolerance))
        }
    }
}

impl ::std::fmt::Debug for Path {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        write!(f, "Path({:?})", &*self.segments)
    }
}
<commit_msg>Weld subsections of closed paths that cross the end/start point<commit_after>use super::{N, P2, V2, RoughEq, THICKNESS, VecLike};
use super::curves::{Curve, FiniteCurve, Segment};
use super::intersect::{Intersect, Intersection, IntersectionResult};
use ordered_float::OrderedFloat;

type ScannerFn<'a> = fn(&mut StartOffsetState, &'a Segment) -> Option<(&'a Segment, N)>;
type ScanIter<'a> =
    ::std::iter::Scan<::std::slice::Iter<'a, Segment>, StartOffsetState, ScannerFn<'a>>;

#[derive(Debug)]
pub enum PathError {
    EmptyPath,
    NotContinuous,
}

#[cfg_attr(feature = "compact_containers", derive(Compact))]
#[derive(Clone)]
pub struct Path {
    pub segments: VecLike<Segment>,
}

impl Path {
    pub fn new_unchecked(segments: VecLike<Segment>) -> Self {
        Path { segments }
    }

    pub fn new(segments: VecLike<Segment>) -> Result<Self, PathError> {
        if segments.is_empty() {
            Result::Err(PathError::EmptyPath)
        } else {
            let continuous = segments.windows(2).all(|seg_pair| {
                seg_pair[0]
                    .end()
                    .rough_eq_by(seg_pair[1].start(), THICKNESS)
            });

            if !continuous {
                Result::Err(PathError::NotContinuous)
            } else {
                Result::Ok(Self::new_unchecked(segments))
            }
        }
    }

    pub fn scan_segments<'a>(
        start_offset: &mut StartOffsetState,
        segment: &'a Segment,
    ) -> Option<(&'a Segment, N)> {
        let pair = (segment, start_offset.0);
        start_offset.0 += segment.length;
        Some(pair)
    }

    pub fn segments_with_start_offsets(&self) -> ScanIter {
        self.segments
            .iter()
            .scan(StartOffsetState(0.0), Self::scan_segments)
    }

    pub fn find_on_segment(&self, distance: N) -> Option<(&Segment, N)> {
        let mut distance_covered = 0.0;
        for segment in self.segments.iter() {
            let new_distance_covered = distance_covered + segment.length();
            if new_distance_covered > distance {
                return Some((segment, distance - distance_covered));
            }
            distance_covered = new_distance_covered;
        }
        None
    }

    pub fn self_intersections(&self) -> Vec<Intersection> {
        self.segments_with_start_offsets()
            .enumerate()
            .flat_map(|(i, (segment_a, offset_a))| {
                self.segments_with_start_offsets()
                    .skip(i + 1)
                    .flat_map(
                        |(segment_b, offset_b)| match (segment_a, segment_b).intersect() {
                            IntersectionResult::Intersecting(intersections) => intersections
                                .into_iter()
                                .filter_map(|intersection| {
                                    if intersection.along_a.rough_eq_by(0.0, THICKNESS)
                                        || intersection
                                            .along_a
                                            .rough_eq_by(segment_a.length(), THICKNESS)
                                        || intersection.along_b.rough_eq_by(0.0, THICKNESS)
                                        || intersection
                                            .along_b
                                            .rough_eq_by(segment_b.length(), THICKNESS)
                                    {
                                        None
                                    } else {
                                        Some(Intersection {
                                            position: intersection.position,
                                            along_a: offset_a + intersection.along_a,
                                            along_b: offset_b + intersection.along_b,
                                        })
                                    }
                                })
                                .collect::<Vec<_>>(),
                            _ => vec![],
                        },
                    )
                    .collect::<Vec<_>>()
            })
            .collect()
    }

    pub fn is_closed(&self) -> bool {
        self.segments
            .last()
            .unwrap()
            .end()
            .rough_eq_by(self.segments.first().unwrap().start(), THICKNESS)
    }

    pub fn concat(&self, other: &Self) -> Result<Self, PathError> {
        // TODO: somehow change this to move self and other into here
        // but then segments would have to return [Segment], possible?
        if self.end().rough_eq_by(other.start(), THICKNESS) {
            Ok(Self::new_unchecked(
                self.segments
                    .iter()
                    .chain(other.segments.iter())
                    .cloned()
                    .collect(),
            ))
        } else {
            Err(PathError::NotContinuous)
        }
    }

    pub fn with_new_start_and_end(&self, new_start: P2, new_end: P2) -> Path {
        if self.segments.len() == 1 {
            let new_segment = if self.segments[0].is_linear() {
                Segment::line(new_start, new_end).expect("New start/end should work A")
            } else {
                Segment::arc_with_direction(new_start, self.segments[0].start_direction(), new_end)
                    .unwrap_or_else(|| {
                        Segment::line(new_start, new_end).expect("New start/end should work B")
                    })
            };

            Path::new_unchecked(Some(new_segment).into_iter().collect())
        } else {
            let first_segment = self.segments.first().unwrap();
            let last_segment = self.segments.last().unwrap();

            let new_last_segment = if last_segment.is_linear() {
                Segment::line(last_segment.start(), new_end).expect("New start/end should work C")
            } else {
                Segment::arc_with_direction(
                    last_segment.start(),
                    last_segment.start_direction(),
                    new_end,
                ).unwrap_or_else(|| {
                    Segment::line(last_segment.start(), new_end)
                        .expect("New start/end should work D")
                })
            };

            let new_first_segment = if first_segment.is_linear() {
                Segment::line(new_start, first_segment.end()).expect("New start/end should work E")
            } else {
                Segment::arc_with_direction(
                    new_start,
                    first_segment.start_direction(),
                    first_segment.end(),
                ).unwrap_or_else(|| {
                    Segment::line(new_start, first_segment.end())
                        .expect("New start/end should work F")
                })
            };

            let segments = Some(new_first_segment)
                .into_iter()
                .chain(self.segments[1..(self.segments.len() - 1)].iter().cloned())
                .chain(Some(new_last_segment))
                .collect();

            Path::new(segments).expect("New start/end should work G")
        }
    }

    pub fn concat_weld(&self, other: &Self, tolerance: N) -> Result<Self, PathError> {
        let distance = (other.start() - self.end()).norm();

        if distance < tolerance {
            let new_end = other.start();

            let distance_at_end = (other.end() - self.start()).norm();
            let other_closes_self = distance_at_end < tolerance;

            let new_start = if other_closes_self {
                other.end()
            } else {
                self.start()
            };

            let new_path = self
                .with_new_start_and_end(new_start, new_end)
                .concat(other)
                .expect("Normal concat should work at this point");

            if other_closes_self {
                assert!(new_path.is_closed());
            }

            Ok(new_path)
        } else {
            Err(PathError::NotContinuous)
        }
    }

    pub fn dash(&self, dash_length: N, gap_length: N) -> Vec<Path> {
        let mut on_dash = true;
        let mut position = 0.0;
        let mut dashes = Vec::new();

        while position < self.length() {
            let old_position = position;
            if on_dash {
                position += dash_length;
                if let Some(dash) = self.subsection(old_position, position) {
                    dashes.push(dash)
                }
            } else {
                position += gap_length;
            }

            on_dash = !on_dash;
        }

        dashes
    }

    pub fn to_svg(&self) -> String {
        self.segments
            .iter()
            .map(Segment::to_svg)
            .collect::<Vec<_>>()
            .join(" ")
    }

    pub fn from_svg(string: &str) -> Result<Self, PathError> {
        let mut tokens = string.split_whitespace();
        let mut position = P2::new(0.0, 0.0);
        let mut first_position = None;
        let mut segments = VecLike::new();

        while let Some(command) = tokens.next() {
            if command == "M" || command == "L" {
                let x: N = tokens
                    .next()
                    .expect("Expected 1st token after M/L")
                    .parse()
                    .expect("Can't parse 1st token after M/L");
                let y: N = tokens
                    .next()
                    .expect("Expected 2nd token after M/L")
                    .parse()
                    .expect("Can't parse 2nd token after M/L");

                let next_position = P2::new(x, y);

                if command == "L" {
                    segments.push(Segment::line(position, next_position).expect("Invalid Segment"));
                }

                position = next_position;
                if first_position.is_none() {
                    first_position = Some(next_position);
                }
            } else if command == "Z" {
                if let Some(closing_segment) = Segment::line(
                    position,
                    first_position.expect("Should have first_position"),
                ) {
                    segments.push(closing_segment);
                }
            }
        }

        Self::new(segments)
    }
}

pub struct StartOffsetState(N);

impl FiniteCurve for Path {
    fn length(&self) -> N {
        self.segments
            .iter()
            .map(|segment| segment.length())
            .fold(0.0, ::std::ops::Add::add)
    }

    fn along(&self, distance: N) -> P2 {
        match self.find_on_segment(distance) {
            Some((segment, distance_on_segment)) => segment.along(distance_on_segment),
            None => {
                if distance < 0.0 {
                    self.segments[0].start
                } else {
                    self.segments.last().unwrap().end
                }
            }
        }
    }

    fn direction_along(&self, distance: N) -> V2 {
        match self.find_on_segment(distance) {
            Some((segment, distance_on_segment)) => segment.direction_along(distance_on_segment),
            None => {
                if distance < 0.0 {
                    self.segments[0].start_direction()
                } else {
                    self.segments.last().unwrap().end_direction()
                }
            }
        }
    }

    fn start(&self) -> P2 {
        self.segments[0].start()
    }

    fn start_direction(&self) -> V2 {
        self.segments[0].start_direction()
    }

    fn end(&self) -> P2 {
        self.segments.last().unwrap().end()
    }

    fn end_direction(&self) -> V2 {
        self.segments.last().unwrap().end_direction()
    }

    fn reverse(&self) -> Self {
        Self::new_unchecked(self.segments.iter().rev().map(Segment::reverse).collect())
    }

    fn subsection(&self, start: N, end: N) -> Option<Path> {
        if start > end + THICKNESS && self.is_closed() {
            let maybe_first_half = self.subsection(start, self.length());
            let maybe_second_half = self.subsection(0.0, end);

            match (maybe_first_half, maybe_second_half) {
                (Some(first_half), Some(second_half)) => {
                    Some(first_half.concat_weld(&second_half, THICKNESS * 2.0).unwrap_or_else(|_| {
                        panic!(
                            "Closed path, should always be continous: {:?} subsection {} - {}, first: {:?} second: {:?}",
                            self, start, end, first_half, second_half
                        )
                    }))
                }
                (Some(first_half), None) => Some(first_half),
                (None, Some(second_half)) => Some(second_half),
                _ => None,
            }
        } else {
            let segments = self
                .segments_with_start_offsets()
                .filter_map(|pair: (&Segment, N)| {
                    let (segment, start_offset) = pair;
                    let end_offset = start_offset + segment.length;
                    if start_offset > end || end_offset < start {
                        None
                    } else {
                        segment.subsection(start - start_offset, end - start_offset)
                    }
                })
                .collect();
            Path::new(segments).ok()
        }
    }

    fn shift_orthogonally(&self, shift_to_right: N) -> Option<Path> {
        let segments = self
            .segments
            .iter()
            .filter_map(|segment| segment.shift_orthogonally(shift_to_right))
            .collect::<Vec<_>>();
        let mut glued_segments = VecLike::new();
        let mut window_segments_iter = segments.iter().peekable();
        while let Some(segment) = window_segments_iter.next() {
            glued_segments.push(*segment);
            match window_segments_iter.peek() {
                Some(next_segment) => {
                    if !segment.end().rough_eq_by(next_segment.start(), THICKNESS) {
                        glued_segments.push(Segment::line(segment.end(), next_segment.start())?);
                    }
                }
                None => break,
            }
        }
        if glued_segments.is_empty() {
            None
        } else {
            let was_closed = self.end().rough_eq_by(self.start(), THICKNESS);
            let new_end = glued_segments.last().unwrap().end();
            let new_start = glued_segments[0].start();
            if was_closed && !new_end.rough_eq_by(new_start, THICKNESS) {
                glued_segments.push(Segment::line(new_end, new_start)?);
            }
            Some(Self::new(glued_segments).unwrap())
        }
    }
}

impl Curve for Path {
    fn project_with_tolerance(&self, point: P2, tolerance: N) -> Option<(N, P2)> {
        self.segments_with_start_offsets()
            .filter_map(|pair: (&Segment, N)| {
                let (segment, start_offset) = pair;
                segment
                    .project_with_tolerance(point, tolerance)
                    .map(|(offset, projected_point)| (offset + start_offset, projected_point))
            })
            .min_by_key(|(_offset, projected_point)| OrderedFloat((projected_point - point).norm()))
    }

    fn includes(&self, point: P2) -> bool {
        self.segments.iter().any(|segment| segment.includes(point))
    }

    fn distance_to(&self, point: P2) -> N {
        if let Some((_offset, projected_point)) = self.project(point) {
            (point - projected_point).norm()
        } else {
            *::std::cmp::min(
                OrderedFloat((point - self.start()).norm()),
                OrderedFloat((point - self.end()).norm()),
            )
        }
    }
}

impl<'a> RoughEq for &'a Path {
    fn rough_eq_by(&self, other: &Path, tolerance: N) -> bool {
        self.segments.len() == other.segments.len() && if self.is_closed() && other.is_closed() {
            // TODO: this is strictly too loose
            // and maybe this should be moved to shape instead,
            // since the paths are *not* exactly equal
            self.segments.iter().all(|segment_1| {
                other
                    .segments
                    .iter()
                    .any(|segment_2| segment_1.rough_eq_by(segment_2, tolerance))
            })
        } else {
            self.segments
                .iter()
                .zip(other.segments.iter())
                .all(|(segment_1, segment_2)| segment_1.rough_eq_by(segment_2, tolerance))
        }
    }
}

impl ::std::fmt::Debug for Path {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
        write!(f, "Path({:?})", &*self.segments)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std;
use std::vec::Vec;
use any_pointer;
use private::capability::ClientHook;
use private::units::*;
use private::arena::{BuilderArena, ReaderArena, SegmentBuilder, SegmentReader, NumWords, ZeroedWords};
use private::layout;
use traits::{FromPointerReader, FromPointerBuilder, SetPointerBuilder};
use Word;

#[derive(Copy)]
pub struct ReaderOptions {
    pub traversal_limit_in_words : u64,
    pub nesting_limit : i32,

    /// If true, malformed messages trigger task failure.
    /// If false, malformed messages fall back to default values.
    pub fail_fast : bool,
}

pub const DEFAULT_READER_OPTIONS : ReaderOptions =
    ReaderOptions { traversal_limit_in_words : 8 * 1024 * 1024, nesting_limit : 64,
                    fail_fast : true };

impl ReaderOptions {
    pub fn new() -> ReaderOptions { DEFAULT_READER_OPTIONS }

    pub fn nesting_limit<'a>(&'a mut self, value : i32) -> &'a mut ReaderOptions {
        self.nesting_limit = value;
        return self;
    }

    pub fn traversal_limit_in_words<'a>(&'a mut self, value : u64) -> &'a mut ReaderOptions {
        self.traversal_limit_in_words = value;
        return self;
    }

    pub fn fail_fast<'a>(&'a mut self, value : bool) -> &'a mut ReaderOptions {
        self.fail_fast = value;
        return self;
    }
}

type SegmentId = u32;

pub trait MessageReader {
    fn get_segment(&self, id : usize) -> &[Word];
    fn arena(&self) -> &ReaderArena;
    fn mut_arena(&mut self) -> &mut ReaderArena;
    fn get_options(&self) -> &ReaderOptions;

    fn get_root_internal(&self) -> any_pointer::Reader {
        unsafe {
            let segment : *const SegmentReader = &self.arena().segment0;

            let pointer_reader = layout::PointerReader::get_root(
                segment, (*segment).get_start_ptr(), self.get_options().nesting_limit);

            any_pointer::Reader::new(pointer_reader)
        }
    }

    fn get_root<'a, T : FromPointerReader<'a>>(&'a self) -> T {
        self.get_root_internal().get_as()
    }

    fn init_cap_table(&mut self, cap_table : Vec<Option<Box<ClientHook+Send>>>) {
        self.mut_arena().init_cap_table(cap_table);
    }
}

pub struct SegmentArrayMessageReader<'a> {
    segments : &'a [ &'a [Word]],
    options : ReaderOptions,
    arena : Box<ReaderArena>
}

impl <'a> MessageReader for SegmentArrayMessageReader<'a> {
    fn get_segment<'b>(&'b self, id : usize) -> &'b [Word] {
        self.segments[id]
    }

    fn arena<'b>(&'b self) -> &'b ReaderArena { &*self.arena }
    fn mut_arena<'b>(&'b mut self) -> &'b mut ReaderArena { &mut *self.arena }

    fn get_options<'b>(&'b self) -> &'b ReaderOptions {
        return &self.options;
    }
}

impl <'a> SegmentArrayMessageReader<'a> {

    pub fn new<'b>(segments : &'b [&'b [Word]], options : ReaderOptions) -> SegmentArrayMessageReader<'b> {
        assert!(segments.len() > 0);
        SegmentArrayMessageReader {
            segments : segments,
            arena : ReaderArena::new(segments, options),
            options : options
        }
    }
}

#[derive(Copy)]
pub enum AllocationStrategy {
    FixedSize,
    GrowHeuristically
}

pub const SUGGESTED_FIRST_SEGMENT_WORDS : u32 = 1024;
pub const SUGGESTED_ALLOCATION_STRATEGY : AllocationStrategy = AllocationStrategy::GrowHeuristically;

#[derive(Copy)]
pub struct BuilderOptions {
    pub first_segment_words : u32,
    pub allocation_strategy : AllocationStrategy,

    /// If true, malformed messages trigger task failure.
    /// If false, malformed messages fall back to default values.
    pub fail_fast : bool,
}

impl BuilderOptions {
    pub fn new() -> BuilderOptions {
        BuilderOptions {first_segment_words : SUGGESTED_FIRST_SEGMENT_WORDS,
                        allocation_strategy : AllocationStrategy::GrowHeuristically,
                        fail_fast : true }
    }

    pub fn first_segment_words<'a>(&'a mut self, value : u32) -> &'a mut BuilderOptions {
        self.first_segment_words = value;
        return self;
    }

    pub fn allocation_strategy<'a>(&'a mut self, value : AllocationStrategy) -> &'a mut BuilderOptions {
        self.allocation_strategy = value;
        return self;
    }

    pub fn fail_fast<'a>(&'a mut self, value : bool) -> &'a mut BuilderOptions {
        self.fail_fast = value;
        return self;
    }
}

pub trait MessageBuilder {
    fn mut_arena(&mut self) -> &mut BuilderArena;
    fn arena(&self) -> &BuilderArena;

    // XXX is there a way to make this private?
    fn get_root_internal<'a>(&mut self) -> any_pointer::Builder<'a> {
        let root_segment = &mut self.mut_arena().segment0 as *mut SegmentBuilder;

        if self.arena().segment0.current_size() == 0 {
            match self.mut_arena().segment0.allocate(WORDS_PER_POINTER as u32) {
                None => {panic!("could not allocate root pointer") }
                Some(location) => {
                    assert!(location == self.arena().segment0.get_ptr_unchecked(0),
                            "First allocated word of new segment was not at offset 0");

                    any_pointer::Builder::new(layout::PointerBuilder::get_root(root_segment, location))

                }
            }
        } else {
            any_pointer::Builder::new(
                layout::PointerBuilder::get_root(root_segment,
                                                 self.arena().segment0.get_ptr_unchecked(0)))
        }

    }

    fn init_root<'a, T : FromPointerBuilder<'a>>(&'a mut self) -> T {
        self.get_root_internal().init_as()
    }

    fn get_root<'a, T : FromPointerBuilder<'a>>(&'a mut self) -> T {
        self.get_root_internal().get_as()
    }

    fn set_root<To, From : SetPointerBuilder<To>>(&mut self, value : From) {
        self.get_root_internal().set_as(value);
    }

    fn get_segments_for_output<T, U : FnMut(&[&[Word]]) -> T>(&self, cont : U) -> T {
        self.arena().get_segments_for_output(cont)
    }

    fn get_cap_table<'a>(&'a self) -> &'a [Option<Box<ClientHook+Send>>] {
        self.arena().get_cap_table()
    }
}

pub struct MallocMessageBuilder {
    arena : Box<BuilderArena>,
}

impl Drop for MallocMessageBuilder {
    fn drop(&mut self) { }
}

unsafe impl Send for MallocMessageBuilder {}

impl MallocMessageBuilder {

    pub fn new(options : BuilderOptions) -> MallocMessageBuilder {
        let arena = BuilderArena::new(options.allocation_strategy,
                                      NumWords(options.first_segment_words),
                                      options.fail_fast);

        MallocMessageBuilder { arena : arena }
    }

    pub fn new_default() -> MallocMessageBuilder {
        MallocMessageBuilder::new(BuilderOptions::new())
    }

}

impl MessageBuilder for MallocMessageBuilder {
    fn mut_arena(&mut self) -> &mut BuilderArena {
        &mut *self.arena
    }
    fn arena(&self) -> &BuilderArena {
        & *self.arena
    }
}

pub struct ScratchSpaceMallocMessageBuilder<'a> {
    arena : Box<BuilderArena>,
    scratch_space : &'a mut [Word],
}

// TODO: figure out why rust thinks this is unsafe.
#[unsafe_destructor]
impl <'a> Drop for ScratchSpaceMallocMessageBuilder<'a> {
    fn drop(&mut self) {
        let ptr = self.scratch_space.as_mut_ptr();
        self.get_segments_for_output(|segments| {
                unsafe {
                    std::ptr::zero_memory(ptr, segments[0].len());
                }
            });
    }
}

impl <'a> ScratchSpaceMallocMessageBuilder<'a> {

    pub fn new<'b>(scratch_space : &'b mut [Word], options : BuilderOptions)
               -> ScratchSpaceMallocMessageBuilder<'b> {
        let arena = BuilderArena::new(options.allocation_strategy, ZeroedWords(scratch_space),
                                      options.fail_fast);

        ScratchSpaceMallocMessageBuilder { arena : arena, scratch_space : scratch_space }
    }

    pub fn new_default<'b>(scratch_space : &'b mut [Word]) -> ScratchSpaceMallocMessageBuilder<'b> {
        ScratchSpaceMallocMessageBuilder::new(scratch_space, BuilderOptions::new())
    }

}

impl <'b> MessageBuilder for ScratchSpaceMallocMessageBuilder<'b> {
    fn mut_arena(&mut self) -> &mut BuilderArena {
        &mut *self.arena
    }
    fn arena(&self) -> &BuilderArena {
        & *self.arena
    }
}
<commit_msg>clean up imports<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use any_pointer;
use private::capability::ClientHook;
use private::units::*;
use private::arena::{BuilderArena, ReaderArena, SegmentBuilder, SegmentReader, NumWords, ZeroedWords};
use private::layout;
use traits::{FromPointerReader, FromPointerBuilder, SetPointerBuilder};
use Word;

#[derive(Copy)]
pub struct ReaderOptions {
    pub traversal_limit_in_words : u64,
    pub nesting_limit : i32,

    /// If true, malformed messages trigger task failure.
    /// If false, malformed messages fall back to default values.
    pub fail_fast : bool,
}

pub const DEFAULT_READER_OPTIONS : ReaderOptions =
    ReaderOptions { traversal_limit_in_words : 8 * 1024 * 1024, nesting_limit : 64,
                    fail_fast : true };

impl ReaderOptions {
    pub fn new() -> ReaderOptions { DEFAULT_READER_OPTIONS }

    pub fn nesting_limit<'a>(&'a mut self, value : i32) -> &'a mut ReaderOptions {
        self.nesting_limit = value;
        return self;
    }

    pub fn traversal_limit_in_words<'a>(&'a mut self, value : u64) -> &'a mut ReaderOptions {
        self.traversal_limit_in_words = value;
        return self;
    }

    pub fn fail_fast<'a>(&'a mut self, value : bool) -> &'a mut ReaderOptions {
        self.fail_fast = value;
        return self;
    }
}

type SegmentId = u32;

pub trait MessageReader {
    fn get_segment(&self, id : usize) -> &[Word];
    fn arena(&self) -> &ReaderArena;
    fn mut_arena(&mut self) -> &mut ReaderArena;
    fn get_options(&self) -> &ReaderOptions;

    fn get_root_internal(&self) -> any_pointer::Reader {
        unsafe {
            let segment : *const SegmentReader = &self.arena().segment0;

            let pointer_reader = layout::PointerReader::get_root(
                segment, (*segment).get_start_ptr(), self.get_options().nesting_limit);

            any_pointer::Reader::new(pointer_reader)
        }
    }

    fn get_root<'a, T : FromPointerReader<'a>>(&'a self) -> T {
        self.get_root_internal().get_as()
    }

    fn init_cap_table(&mut self, cap_table : Vec<Option<Box<ClientHook+Send>>>) {
        self.mut_arena().init_cap_table(cap_table);
    }
}

pub struct SegmentArrayMessageReader<'a> {
    segments : &'a [ &'a [Word]],
    options : ReaderOptions,
    arena : Box<ReaderArena>
}

impl <'a> MessageReader for SegmentArrayMessageReader<'a> {
    fn get_segment<'b>(&'b self, id : usize) -> &'b [Word] {
        self.segments[id]
    }

    fn arena<'b>(&'b self) -> &'b ReaderArena { &*self.arena }
    fn mut_arena<'b>(&'b mut self) -> &'b mut ReaderArena { &mut *self.arena }

    fn get_options<'b>(&'b self) -> &'b ReaderOptions {
        return &self.options;
    }
}

impl <'a> SegmentArrayMessageReader<'a> {

    pub fn new<'b>(segments : &'b [&'b [Word]], options : ReaderOptions) -> SegmentArrayMessageReader<'b> {
        assert!(segments.len() > 0);
        SegmentArrayMessageReader {
            segments : segments,
            arena : ReaderArena::new(segments, options),
            options : options
        }
    }
}

#[derive(Copy)]
pub enum AllocationStrategy {
    FixedSize,
    GrowHeuristically
}

pub const SUGGESTED_FIRST_SEGMENT_WORDS : u32 = 1024;
pub const SUGGESTED_ALLOCATION_STRATEGY : AllocationStrategy = AllocationStrategy::GrowHeuristically;

#[derive(Copy)]
pub struct BuilderOptions {
    pub first_segment_words : u32,
    pub allocation_strategy : AllocationStrategy,

    /// If true, malformed messages trigger task failure.
    /// If false, malformed messages fall back to default values.
    pub fail_fast : bool,
}

impl BuilderOptions {
    pub fn new() -> BuilderOptions {
        BuilderOptions {first_segment_words : SUGGESTED_FIRST_SEGMENT_WORDS,
                        allocation_strategy : AllocationStrategy::GrowHeuristically,
                        fail_fast : true }
    }

    pub fn first_segment_words<'a>(&'a mut self, value : u32) -> &'a mut BuilderOptions {
        self.first_segment_words = value;
        return self;
    }

    pub fn allocation_strategy<'a>(&'a mut self, value : AllocationStrategy) -> &'a mut BuilderOptions {
        self.allocation_strategy = value;
        return self;
    }

    pub fn fail_fast<'a>(&'a mut self, value : bool) -> &'a mut BuilderOptions {
        self.fail_fast = value;
        return self;
    }
}

pub trait MessageBuilder {
    fn mut_arena(&mut self) -> &mut BuilderArena;
    fn arena(&self) -> &BuilderArena;

    // XXX is there a way to make this private?
    fn get_root_internal<'a>(&mut self) -> any_pointer::Builder<'a> {
        let root_segment = &mut self.mut_arena().segment0 as *mut SegmentBuilder;

        if self.arena().segment0.current_size() == 0 {
            match self.mut_arena().segment0.allocate(WORDS_PER_POINTER as u32) {
                None => {panic!("could not allocate root pointer") }
                Some(location) => {
                    assert!(location == self.arena().segment0.get_ptr_unchecked(0),
                            "First allocated word of new segment was not at offset 0");

                    any_pointer::Builder::new(layout::PointerBuilder::get_root(root_segment, location))

                }
            }
        } else {
            any_pointer::Builder::new(
                layout::PointerBuilder::get_root(root_segment,
                                                 self.arena().segment0.get_ptr_unchecked(0)))
        }

    }

    fn init_root<'a, T : FromPointerBuilder<'a>>(&'a mut self) -> T {
        self.get_root_internal().init_as()
    }

    fn get_root<'a, T : FromPointerBuilder<'a>>(&'a mut self) -> T {
        self.get_root_internal().get_as()
    }

    fn set_root<To, From : SetPointerBuilder<To>>(&mut self, value : From) {
        self.get_root_internal().set_as(value);
    }

    fn get_segments_for_output<T, U : FnMut(&[&[Word]]) -> T>(&self, cont : U) -> T {
        self.arena().get_segments_for_output(cont)
    }

    fn get_cap_table<'a>(&'a self) -> &'a [Option<Box<ClientHook+Send>>] {
        self.arena().get_cap_table()
    }
}

pub struct MallocMessageBuilder {
    arena : Box<BuilderArena>,
}

impl Drop for MallocMessageBuilder {
    fn drop(&mut self) { }
}

unsafe impl Send for MallocMessageBuilder {}

impl MallocMessageBuilder {

    pub fn new(options : BuilderOptions) -> MallocMessageBuilder {
        let arena = BuilderArena::new(options.allocation_strategy,
                                      NumWords(options.first_segment_words),
                                      options.fail_fast);

        MallocMessageBuilder { arena : arena }
    }

    pub fn new_default() -> MallocMessageBuilder {
        MallocMessageBuilder::new(BuilderOptions::new())
    }

}

impl MessageBuilder for MallocMessageBuilder {
    fn mut_arena(&mut self) -> &mut BuilderArena {
        &mut *self.arena
    }
    fn arena(&self) -> &BuilderArena {
        & *self.arena
    }
}

pub struct ScratchSpaceMallocMessageBuilder<'a> {
    arena : Box<BuilderArena>,
    scratch_space : &'a mut [Word],
}

// TODO: figure out why rust thinks this is unsafe.
#[unsafe_destructor]
impl <'a> Drop for ScratchSpaceMallocMessageBuilder<'a> {
    fn drop(&mut self) {
        let ptr = self.scratch_space.as_mut_ptr();
        self.get_segments_for_output(|segments| {
                unsafe {
                    ::std::ptr::zero_memory(ptr, segments[0].len());
                }
            });
    }
}

impl <'a> ScratchSpaceMallocMessageBuilder<'a> {

    pub fn new<'b>(scratch_space : &'b mut [Word], options : BuilderOptions)
               -> ScratchSpaceMallocMessageBuilder<'b> {
        let arena = BuilderArena::new(options.allocation_strategy, ZeroedWords(scratch_space),
                                      options.fail_fast);

        ScratchSpaceMallocMessageBuilder { arena : arena, scratch_space : scratch_space }
    }

    pub fn new_default<'b>(scratch_space : &'b mut [Word]) -> ScratchSpaceMallocMessageBuilder<'b> {
        ScratchSpaceMallocMessageBuilder::new(scratch_space, BuilderOptions::new())
    }

}

impl <'b> MessageBuilder for ScratchSpaceMallocMessageBuilder<'b> {
    fn mut_arena(&mut self) -> &mut BuilderArena {
        &mut *self.arena
    }
    fn arena(&self) -> &BuilderArena {
        & *self.arena
    }
}
<|endoftext|>


<commit_before>use std::path::{PathBuf, Path};
use std::fs;
use std::error::Error;
use std::fmt;

#[cfg(feature = "cache")]
use time::{self, Timespec};
#[cfg(feature = "cache")]
use std::time::Duration;

use iron::prelude::*;
use iron::{Handler, status};
#[cfg(feature = "cache")]
use iron::modifier::Modifier;
use iron::modifiers::Redirect;
use mount::OriginalUrl;
use requested_path::RequestedPath;

/// The static file-serving `Handler`.
///
/// This handler serves files from a single filesystem path, which may be absolute or relative.
/// Incoming requests are mapped onto the filesystem by appending their URL path to the handler's
/// root path. If the filesystem path corresponds to a regular file, the handler will attempt to
/// serve it. Otherwise, if the path corresponds to a directory containing an `index.html`,
/// the handler will attempt to serve that instead.
///
/// ## Errors
///
/// If the path doesn't match any real object in the filesystem, the handler will return
/// a Response with `status::NotFound`. If an IO error occurs whilst attempting to serve
/// a file, `FileError(IoError)` will be returned.
#[derive(Clone)]
pub struct Static {
    /// The path this handler is serving files from.
    pub root: PathBuf,
    #[cfg(feature = "cache")]
    cache: Option<Cache>,
}

impl Static {
    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(feature = "cache")]
    pub fn new<P: AsRef<Path>>(root: P) -> Static {
        Static {
            root: root.as_ref().to_path_buf(),
            cache: None
        }
    }

    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(not(feature = "cache"))]
    pub fn new<P: AsRef<Path>>(root: P) -> Static {
        Static {
            root: root.as_ref().to_path_buf(),
        }
    }

    /// Specify the response's `cache-control` header with a given duration. Internally, this is
    /// a helper function to set a `Cache` on an instance of `Static`.
    ///
    /// ## Example
    ///
    /// ```ignore
    /// let cached_static_handler = Static::new(path).cache(Duration::from_secs(30*24*60*60));
    /// ```
    #[cfg(feature = "cache")]
    pub fn cache(self, duration: Duration) -> Static {
        self.set(Cache::new(duration))
    }

    #[cfg(feature = "cache")]
    fn try_cache<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        match self.cache {
            None => Ok(Response::with((status::Ok, path.as_ref()))),
            Some(ref cache) => cache.handle(req, path.as_ref()),
        }
    }
}

impl Handler for Static {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        use std::io;

        let requested_path = RequestedPath::new(&self.root, req);

        let metadata = match fs::metadata(&requested_path.path) {
            Ok(meta) => meta,
            Err(e) => {
                let status = match e.kind() {
                    io::ErrorKind::NotFound => status::NotFound,
                    io::ErrorKind::PermissionDenied => status::Forbidden,
                    _ => status::InternalServerError,
                };

                return Err(IronError::new(e, status))
            },
        };

        // If the URL ends in a slash, serve the file directly.
        // Otherwise, redirect to the directory equivalent of the URL.
        if requested_path.should_redirect(&metadata, req) {
            // Perform an HTTP 301 Redirect.
            let mut redirect_path = match req.extensions.get::<OriginalUrl>() {
                None => &req.url,
                Some(original_url) => original_url,
            }.clone();

            // Append the trailing slash
            //
            // rust-url automatically turns an empty string in the last
            // slot in the path into a trailing slash.
            redirect_path.path.push("".to_string());

            return Ok(Response::with((status::MovedPermanently,
                                      format!("Redirecting to {}", redirect_path),
                                      Redirect(redirect_path))));
        }

        match requested_path.get_file(&metadata) {
            // If no file is found, return a 404 response.
            None => Err(IronError::new(NoFile, status::NotFound)),
            // Won't panic because we know the file exists from get_file.
            #[cfg(feature = "cache")]
            Some(path) => self.try_cache(req, path),
            #[cfg(not(feature = "cache"))]
            Some(path) => {
                let path: &Path = &path;
                Ok(Response::with((status::Ok, path)))
            },
        }
    }
}

impl Set for Static {}

/// A modifier for `Static` to specify a response's `cache-control`.
#[cfg(feature = "cache")]
#[derive(Clone)]
pub struct Cache {
    /// The length of time the file should be cached for.
    pub duration: Duration,
}

#[cfg(feature = "cache")]
impl Cache {
    /// Create a new instance of `Cache` with a given duration.
    pub fn new(duration: Duration) -> Cache {
        Cache { duration: duration }
    }

    fn handle<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        use iron::headers::{IfModifiedSince, HttpDate};

        let path = path.as_ref();

        let last_modified_time = match fs::metadata(path) {
            Err(error) => return Err(IronError::new(error, status::InternalServerError)),
            Ok(metadata) => {
                use filetime::FileTime;

                let time = FileTime::from_last_modification_time(&metadata);
                Timespec::new(time.seconds() as i64, time.nanoseconds() as i32)
            },
        };

        let if_modified_since = match req.headers.get::<IfModifiedSince>().cloned() {
            None => return Ok(self.response_with_cache(path, last_modified_time)),
            Some(IfModifiedSince(HttpDate(time))) => time.to_timespec(),
        };

        if last_modified_time <= if_modified_since {
            Ok(Response::with(status::NotModified))
        } else {
            Ok(self.response_with_cache(path, last_modified_time))
        }
    }

    fn response_with_cache<P: AsRef<Path>>(&self, path: P, modified: Timespec) -> Response {
        use iron::headers::{CacheControl, LastModified, CacheDirective, HttpDate};

        let mut response = Response::with((status::Ok, path.as_ref()));
        let seconds = self.duration.as_secs() as u32;
        let cache = vec![CacheDirective::Public, CacheDirective::MaxAge(seconds)];
        response.headers.set(CacheControl(cache));
        response.headers.set(LastModified(HttpDate(time::at(modified))));
        response
    }
}

#[cfg(feature = "cache")]
impl Modifier<Static> for Cache {
    fn modify(self, static_handler: &mut Static) {
        static_handler.cache = Some(self);
    }
}

/// Thrown if no file is found. It is always accompanied by a NotFound response.
#[derive(Debug)]
pub struct NoFile;

impl Error for NoFile {
    fn description(&self) -> &str { "File not found" }
}

impl fmt::Display for NoFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
<commit_msg>Use Into<PathBuf> instead of AsRef<Path> where appropriate.<commit_after>use std::path::{PathBuf, Path};
use std::fs;
use std::error::Error;
use std::fmt;

#[cfg(feature = "cache")]
use time::{self, Timespec};
#[cfg(feature = "cache")]
use std::time::Duration;

use iron::prelude::*;
use iron::{Handler, status};
#[cfg(feature = "cache")]
use iron::modifier::Modifier;
use iron::modifiers::Redirect;
use mount::OriginalUrl;
use requested_path::RequestedPath;

/// The static file-serving `Handler`.
///
/// This handler serves files from a single filesystem path, which may be absolute or relative.
/// Incoming requests are mapped onto the filesystem by appending their URL path to the handler's
/// root path. If the filesystem path corresponds to a regular file, the handler will attempt to
/// serve it. Otherwise, if the path corresponds to a directory containing an `index.html`,
/// the handler will attempt to serve that instead.
///
/// ## Errors
///
/// If the path doesn't match any real object in the filesystem, the handler will return
/// a Response with `status::NotFound`. If an IO error occurs whilst attempting to serve
/// a file, `FileError(IoError)` will be returned.
#[derive(Clone)]
pub struct Static {
    /// The path this handler is serving files from.
    pub root: PathBuf,
    #[cfg(feature = "cache")]
    cache: Option<Cache>,
}

impl Static {
    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(feature = "cache")]
    pub fn new<P: Into<PathBuf>>(root: P) -> Static {
        Static {
            root: root.into(),
            cache: None
        }
    }

    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(not(feature = "cache"))]
    pub fn new<P: Into<PathBuf>>(root: P) -> Static {
        Static {
            root: root.into(),
        }
    }

    /// Specify the response's `cache-control` header with a given duration. Internally, this is
    /// a helper function to set a `Cache` on an instance of `Static`.
    ///
    /// ## Example
    ///
    /// ```ignore
    /// let cached_static_handler = Static::new(path).cache(Duration::from_secs(30*24*60*60));
    /// ```
    #[cfg(feature = "cache")]
    pub fn cache(self, duration: Duration) -> Static {
        self.set(Cache::new(duration))
    }

    #[cfg(feature = "cache")]
    fn try_cache<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        match self.cache {
            None => Ok(Response::with((status::Ok, path.as_ref()))),
            Some(ref cache) => cache.handle(req, path.as_ref()),
        }
    }
}

impl Handler for Static {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        use std::io;

        let requested_path = RequestedPath::new(&self.root, req);

        let metadata = match fs::metadata(&requested_path.path) {
            Ok(meta) => meta,
            Err(e) => {
                let status = match e.kind() {
                    io::ErrorKind::NotFound => status::NotFound,
                    io::ErrorKind::PermissionDenied => status::Forbidden,
                    _ => status::InternalServerError,
                };

                return Err(IronError::new(e, status))
            },
        };

        // If the URL ends in a slash, serve the file directly.
        // Otherwise, redirect to the directory equivalent of the URL.
        if requested_path.should_redirect(&metadata, req) {
            // Perform an HTTP 301 Redirect.
            let mut redirect_path = match req.extensions.get::<OriginalUrl>() {
                None => &req.url,
                Some(original_url) => original_url,
            }.clone();

            // Append the trailing slash
            //
            // rust-url automatically turns an empty string in the last
            // slot in the path into a trailing slash.
            redirect_path.path.push("".to_string());

            return Ok(Response::with((status::MovedPermanently,
                                      format!("Redirecting to {}", redirect_path),
                                      Redirect(redirect_path))));
        }

        match requested_path.get_file(&metadata) {
            // If no file is found, return a 404 response.
            None => Err(IronError::new(NoFile, status::NotFound)),
            // Won't panic because we know the file exists from get_file.
            #[cfg(feature = "cache")]
            Some(path) => self.try_cache(req, path),
            #[cfg(not(feature = "cache"))]
            Some(path) => {
                let path: &Path = &path;
                Ok(Response::with((status::Ok, path)))
            },
        }
    }
}

impl Set for Static {}

/// A modifier for `Static` to specify a response's `cache-control`.
#[cfg(feature = "cache")]
#[derive(Clone)]
pub struct Cache {
    /// The length of time the file should be cached for.
    pub duration: Duration,
}

#[cfg(feature = "cache")]
impl Cache {
    /// Create a new instance of `Cache` with a given duration.
    pub fn new(duration: Duration) -> Cache {
        Cache { duration: duration }
    }

    fn handle<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        use iron::headers::{IfModifiedSince, HttpDate};

        let path = path.as_ref();

        let last_modified_time = match fs::metadata(path) {
            Err(error) => return Err(IronError::new(error, status::InternalServerError)),
            Ok(metadata) => {
                use filetime::FileTime;

                let time = FileTime::from_last_modification_time(&metadata);
                Timespec::new(time.seconds() as i64, time.nanoseconds() as i32)
            },
        };

        let if_modified_since = match req.headers.get::<IfModifiedSince>().cloned() {
            None => return Ok(self.response_with_cache(path, last_modified_time)),
            Some(IfModifiedSince(HttpDate(time))) => time.to_timespec(),
        };

        if last_modified_time <= if_modified_since {
            Ok(Response::with(status::NotModified))
        } else {
            Ok(self.response_with_cache(path, last_modified_time))
        }
    }

    fn response_with_cache<P: AsRef<Path>>(&self, path: P, modified: Timespec) -> Response {
        use iron::headers::{CacheControl, LastModified, CacheDirective, HttpDate};

        let mut response = Response::with((status::Ok, path.as_ref()));
        let seconds = self.duration.as_secs() as u32;
        let cache = vec![CacheDirective::Public, CacheDirective::MaxAge(seconds)];
        response.headers.set(CacheControl(cache));
        response.headers.set(LastModified(HttpDate(time::at(modified))));
        response
    }
}

#[cfg(feature = "cache")]
impl Modifier<Static> for Cache {
    fn modify(self, static_handler: &mut Static) {
        static_handler.cache = Some(self);
    }
}

/// Thrown if no file is found. It is always accompanied by a NotFound response.
#[derive(Debug)]
pub struct NoFile;

impl Error for NoFile {
    fn description(&self) -> &str { "File not found" }
}

impl fmt::Display for NoFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
<|endoftext|>


<commit_before>// Integration with Musashi
extern crate libc;

// Register enum copied from Musashi's m68k_register_t enum
#[repr(C)]
#[derive(Copy, Clone)]
pub enum Register {
	/* Real registers */
	D0,		/* Data registers */
	D1,
	D2,
	D3,
	D4,
	D5,
	D6,
	D7,
	A0,		/* Address registers */
	A1,
	A2,
	A3,
	A4,
	A5,
	A6,
	A7,
	PC,		/* Program Counter */
	SR,		/* Status Register */
	SP,		/* The current Stack Pointer (located in A7) */
	USP,		/* User Stack Pointer */
	ISP,		/* Interrupt Stack Pointer */
	MSP,		/* Master Stack Pointer */
	SFC,		/* Source Function Code */
	DFC,		/* Destination Function Code */
	VBR,		/* Vector Base Register */
	CACR,		/* Cache Control Register */
	CAAR,		/* Cache Address Register */

	/* Assumed registers */
	/* These are cheat registers which emulate the 1-longword prefetch
	 * present in the 68000 and 68010.
	 */
	PrefAddr,	/* Last prefetch address */
	PrefData,	/* Last prefetch data */

	/* Convenience registers */
	PPC,		/* Previous value in the program counter */
	IR,			/* Instruction register */
	CpuType	/* Type of CPU being run */
}

#[repr(C)]
#[derive(Copy, Clone)]
enum CpuType
{
	Invalid,
	M68000,
	M68010,
	M68EC020,
	M68020,
	M68030,		/* Supported by disassembler ONLY */
	M68040		/* Supported by disassembler ONLY */
}

#[link(name = "musashi", kind = "static")]
extern {
	fn m68k_init();
	fn m68k_set_cpu_type(cputype: CpuType);
	fn m68k_pulse_reset();
	fn m68k_execute(num_cycles: i32) -> i32;
	fn m68k_get_reg(context: *mut libc::c_void, regnum: Register) -> u32;
	fn m68k_set_reg(regnum: Register, value: u32);
}

static mut musashi_memory:  [u8; 1024] = [0u8; 1024];

// callbacks from Musashi
#[no_mangle]
pub extern fn cpu_read_byte(address: u32) -> u32 {
	println!("read byte {:08x}", address);
	let address = address as usize;
	unsafe {
		musashi_memory[address] as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_word(address: u32) -> u32 {
	println!("read word {:08x}", address);
	let address = address as usize;
	unsafe {
		((musashi_memory[address+0] as u32) << 8
		|(musashi_memory[address+1] as u32) << 0) as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_long(address: u32) -> u32 {
	println!("read long {:08x}", address);
	let address = address as usize;
	unsafe {
		((musashi_memory[address+0] as u32) << 24
		|(musashi_memory[address+1] as u32) << 16
		|(musashi_memory[address+2] as u32) <<  8
		|(musashi_memory[address+3] as u32) <<  0) as u32
	}
}

#[no_mangle]
pub extern fn cpu_write_byte(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_word(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff00 >> 8) as u8;
		musashi_memory[address+1] = (value & 0x00ff >> 0) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_long(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff000000 >> 24) as u8;
		musashi_memory[address+1] = (value & 0x00ff0000 >> 16) as u8;
		musashi_memory[address+2] = (value & 0x0000ff00 >>  8) as u8;
		musashi_memory[address+3] = (value & 0x000000ff >>  0) as u8;
	}
}

#[no_mangle]
pub extern fn cpu_pulse_reset() {panic!("pr")}
#[no_mangle]
pub extern fn cpu_long_branch() {println!("lb")}
#[no_mangle]
pub extern fn cpu_set_fc(fc: u32) {println!("set_fc {}", fc)}
#[no_mangle]
pub extern fn cpu_irq_ack(level: i32) -> i32 {panic!("ia")}
#[no_mangle]
pub extern fn cpu_instr_callback() {println!("ic")}

use std::ptr;

pub fn experimental_communication() {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(Register::D0, 123);
		println!("D0: {}", m68k_get_reg(ptr::null_mut(), Register::D0));
	}
}

pub fn roundtrip_register(reg: Register, value: u32) -> u32 {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(reg, value);
		m68k_get_reg(ptr::null_mut(), reg)
	}
}

use cpu::Core;

pub fn roundtrip_core(core: &mut Core) {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		let regs = [Register::D0, Register::D1, Register::D2, Register::D3, Register::D4, Register::D5, Register::D6, Register::D7, Register::A0, Register::A1, Register::A2, Register::A3, Register::A4, Register::A5, Register::A6, Register::A7];
		for (i, &reg) in regs.iter().enumerate() {m68k_set_reg(reg, core.dar[i]);}
		m68k_set_reg(Register::PC, core.pc);
		m68k_set_reg(Register::SP, core.sp);
		m68k_set_reg(Register::SR, core.status_register());

		m68k_execute(1);

		for (i, &reg) in regs.iter().enumerate() {
			core.dar[i] = m68k_get_reg(ptr::null_mut(), reg);
		}
		core.pc = m68k_get_reg(ptr::null_mut(), Register::PC);
		core.sp = m68k_get_reg(ptr::null_mut(), Register::SP);
		core.sr_to_flags(m68k_get_reg(ptr::null_mut(), Register::SR));
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use super::musashi_memory;
	use cpu::Core;

	#[test]
	fn roundtrip_d0() {
		assert_eq!(256, roundtrip_register(Register::D0, 256));
	}

	#[test]
	fn roundtrip_abcd_rr() {
		let mut cpu = Core::new_mem(0x40, &[0xc3, 0x00]);
		unsafe {
			let offset = cpu.pc as usize;
			for (i,b) in cpu.mem.iter().enumerate() {
				musashi_memory[i] = *b;
			}
		}
		cpu.dar[0] = 0x16;
		cpu.dar[1] = 0x26;
		roundtrip_core(&mut cpu);

		// 16 + 26 is 42
		assert_eq!(0x42, cpu.dar[1]);
	}
}<commit_msg>Keep track of Musashi RAM operations<commit_after>// Integration with Musashi
extern crate libc;

// Register enum copied from Musashi's m68k_register_t enum
#[repr(C)]
#[derive(Copy, Clone)]
pub enum Register {
	/* Real registers */
	D0,		/* Data registers */
	D1,
	D2,
	D3,
	D4,
	D5,
	D6,
	D7,
	A0,		/* Address registers */
	A1,
	A2,
	A3,
	A4,
	A5,
	A6,
	A7,
	PC,		/* Program Counter */
	SR,		/* Status Register */
	SP,		/* The current Stack Pointer (located in A7) */
	USP,		/* User Stack Pointer */
	ISP,		/* Interrupt Stack Pointer */
	MSP,		/* Master Stack Pointer */
	SFC,		/* Source Function Code */
	DFC,		/* Destination Function Code */
	VBR,		/* Vector Base Register */
	CACR,		/* Cache Control Register */
	CAAR,		/* Cache Address Register */

	/* Assumed registers */
	/* These are cheat registers which emulate the 1-longword prefetch
	 * present in the 68000 and 68010.
	 */
	PrefAddr,	/* Last prefetch address */
	PrefData,	/* Last prefetch data */

	/* Convenience registers */
	PPC,		/* Previous value in the program counter */
	IR,			/* Instruction register */
	CpuType	/* Type of CPU being run */
}

#[repr(C)]
#[derive(Copy, Clone)]
enum CpuType
{
	Invalid,
	M68000,
	M68010,
	M68EC020,
	M68020,
	M68030,		/* Supported by disassembler ONLY */
	M68040		/* Supported by disassembler ONLY */
}

#[link(name = "musashi", kind = "static")]
extern {
	fn m68k_init();
	fn m68k_set_cpu_type(cputype: CpuType);
	fn m68k_pulse_reset();
	fn m68k_execute(num_cycles: i32) -> i32;
	fn m68k_get_reg(context: *mut libc::c_void, regnum: Register) -> u32;
	fn m68k_set_reg(regnum: Register, value: u32);
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Operation {
	None,
    ReadByte(u32),
    ReadWord(u32),
    ReadLong(u32),
    WriteByte(u32, u32),
    WriteWord(u32, u32),
    WriteLong(u32, u32),
}
static mut musashi_memory:  [u8; 1024] = [0u8; 1024];
// as statics are not allowed to have destructors, allocate a
// big enough array to hold the small number of operations
// expected from executing a very limited number of opcodes
static mut musashi_opcount: usize = 0;
static mut musashi_ops: [Operation; 128] = [Operation::None; 128];

unsafe fn register_op(op: Operation) {
	if(musashi_opcount < musashi_ops.len()) {
		musashi_ops[musashi_opcount] = op;
		musashi_opcount += 1;
	}
}
// callbacks from Musashi
#[no_mangle]
pub extern fn cpu_read_byte(address: u32) -> u32 {
	let op = Operation::ReadByte(address);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address] as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_word(address: u32) -> u32 {
	let op = Operation::ReadWord(address);
	let address = address as usize;
	unsafe {
		register_op(op);
		((musashi_memory[address+0] as u32) << 8
		|(musashi_memory[address+1] as u32) << 0) as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_long(address: u32) -> u32 {
	let op = Operation::ReadLong(address);
	let address = address as usize;
	unsafe {
		register_op(op);
		((musashi_memory[address+0] as u32) << 24
		|(musashi_memory[address+1] as u32) << 16
		|(musashi_memory[address+2] as u32) <<  8
		|(musashi_memory[address+3] as u32) <<  0) as u32
	}
}

#[no_mangle]
pub extern fn cpu_write_byte(address: u32, value: u32) {
	let op = Operation::WriteByte(address, value);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address+0] = (value & 0xff) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_word(address: u32, value: u32) {
	let op = Operation::WriteWord(address, value);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address+0] = (value & 0xff00 >> 8) as u8;
		musashi_memory[address+1] = (value & 0x00ff >> 0) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_long(address: u32, value: u32) {
	let op = Operation::WriteLong(address, value);
	let address = address as usize;
	unsafe {
		register_op(op);
		musashi_memory[address+0] = (value & 0xff000000 >> 24) as u8;
		musashi_memory[address+1] = (value & 0x00ff0000 >> 16) as u8;
		musashi_memory[address+2] = (value & 0x0000ff00 >>  8) as u8;
		musashi_memory[address+3] = (value & 0x000000ff >>  0) as u8;
	}
}

#[no_mangle]
pub extern fn cpu_pulse_reset() {panic!("pr")}
#[no_mangle]
pub extern fn cpu_long_branch() {println!("lb")}
#[no_mangle]
pub extern fn cpu_set_fc(fc: u32) {println!("set_fc {}", fc)}
#[no_mangle]
pub extern fn cpu_irq_ack(level: i32) -> i32 {panic!("ia")}
#[no_mangle]
pub extern fn cpu_instr_callback() {println!("ic")}

use std::ptr;

pub fn experimental_communication() {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(Register::D0, 123);
		println!("D0: {}", m68k_get_reg(ptr::null_mut(), Register::D0));
	}
}

pub fn roundtrip_register(reg: Register, value: u32) -> u32 {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(reg, value);
		m68k_get_reg(ptr::null_mut(), reg)
	}
}

use cpu::Core;

pub fn roundtrip_core(core: &mut Core) {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		let regs = [Register::D0, Register::D1, Register::D2, Register::D3, Register::D4, Register::D5, Register::D6, Register::D7, Register::A0, Register::A1, Register::A2, Register::A3, Register::A4, Register::A5, Register::A6, Register::A7];
		for (i, &reg) in regs.iter().enumerate() {m68k_set_reg(reg, core.dar[i]);}
		m68k_set_reg(Register::PC, core.pc);
		m68k_set_reg(Register::SP, core.sp);
		m68k_set_reg(Register::SR, core.status_register());

		m68k_execute(1);

		for (i, &reg) in regs.iter().enumerate() {
			core.dar[i] = m68k_get_reg(ptr::null_mut(), reg);
		}
		core.pc = m68k_get_reg(ptr::null_mut(), Register::PC);
		core.sp = m68k_get_reg(ptr::null_mut(), Register::SP);
		core.sr_to_flags(m68k_get_reg(ptr::null_mut(), Register::SR));
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use super::musashi_memory;
	use cpu::Core;

	#[test]
	fn roundtrip_d0() {
		assert_eq!(256, roundtrip_register(Register::D0, 256));
	}

	#[test]
	fn roundtrip_abcd_rr() {
		let mut cpu = Core::new_mem(0x40, &[0xc3, 0x00]);
		unsafe {
			let offset = cpu.pc as usize;
			for (i,b) in cpu.mem.iter().enumerate() {
				musashi_memory[i] = *b;
			}
		}
		cpu.dar[0] = 0x16;
		cpu.dar[1] = 0x26;
		roundtrip_core(&mut cpu);

		// 16 + 26 is 42
		assert_eq!(0x42, cpu.dar[1]);
	}
}<|endoftext|>