<commit_before>//! SemanticEngine implementation for [the racer library](https://github.com/phildawes/racer)
//!
use engine::{SemanticEngine, Definition, Context, CursorPosition, Completion, Error};

use racer::core::{Session, FileCache, IndexedSource};

use std::path::Path;
use std::rc::Rc;

use std::sync::Mutex;
use regex::Regex;

pub struct Racer<'a> {
    cache: Mutex<&'a FileCache>
}

impl<'a> Racer<'a> {
    pub fn new() -> Racer<'a> {
        // The unsafe block in the constructor is taking ownership of the Session allocated by this
        // box. It is later freed in the drop impl. Having a Session reference with the same
        // lifetime as Racer solves a concrete self lifetime problem in the SemanticEngine
        // implementation.
        //
        // The following unsafe block should be fine since the memory is known to be valid. The
        // reference will have the same value for the lifetime of the Racer object, so there is no
        // need to worry about stale references wandering around. However, I am not certain that
        // MutexGuard will save us from references escaping a critical section since you normally
        // cannot store references in a Mutex. At this time, that's not a problem, but it would be
        // good to have a static guarantee about not leaking references.
        let cache = Box::new(FileCache::new());
        let cache_ptr = Box::into_raw(cache);
        Racer {
            cache: Mutex::new(unsafe { &*cache_ptr })
        }
    }

    fn build_racer_args<'b, 'c>(&self, ctx: &'b Context, session: &'c Session<'a>)
        -> (Rc<IndexedSource>, usize, &'b Path) {

        let path = ctx.query_path();

        for buffer in &ctx.buffers {
            session.cache_file_contents(buffer.path(), &buffer.contents[..]);
        }

        let file = session.load_file(path);
        let pos = file.coords_to_point(ctx.query_cursor.line,
                                       ctx.query_cursor.col).unwrap();

        (file, pos, path)
    }
}

impl<'a> Drop for Racer<'a> {
    fn drop(&mut self) {
        let cache_ref = *self.cache.lock().unwrap();
        // At this point, nobody has a reference to the session because it is locked behind the
        // mutex and the lock is held here. Nobody has a reference to the mutex because its holder
        // is being dropped. Thus, casting a non mutable reference to a raw *mut pointer to
        // reconstitute the box should be fine.
        let _cache: Box<FileCache> = unsafe { Box::from_raw(::std::mem::transmute(cache_ref)) };
    }
}

use ::Config;
use super::Result;

impl<'a> SemanticEngine for Racer<'a> {
    fn initialize(&self, config: &Config) -> Result<()> {
        if let Some(ref src_path) = config.rust_src_path {
            ::std::env::set_var("RUST_SRC_PATH", &src_path[..]);
        }

        Ok(())
    }

    fn find_definition(&self, ctx: &Context) -> Result<Option<Definition>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner()
        };
        let session = Session::from_path(&cache, ctx.query_path(), ctx.query_path());
        let (file, pos, path) = self.build_racer_args(ctx, &session);

        // TODO catch_panic: apparently this can panic! in a string operation. Something about pos
        // not landing on a character boundary.
        Ok(match ::racer::core::find_definition(&file.code, path, pos, &session) {
            Some(m) => {
                // TODO modify racer Match to return line, col. For now, read the file it found a
                // match in to translate the Match position into line, col.
                let (line, col) = {
                    let file = session.load_file(&m.filepath);
                    try!(file.point_to_coords(m.point).ok_or(Error::Racer))
                };

                let match_type = format!("{:?}", m.mtype);
                Some(Definition {
                    position: CursorPosition {
                        line: line,
                        col: col
                    },
                    dtype: match_type,
                    file_path: m.filepath.to_str().unwrap().to_string(),
                    text: m.matchstr.clone(),
                    text_context: m.contextstr.clone(),
                    docs: m.docs.clone(),
                })
            },
            None => None
        })
    }

    fn list_completions(&self, ctx: &Context) -> Result<Option<Vec<Completion>>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner()
        };
        let session = Session::from_path(&cache, ctx.query_path(), ctx.query_path());
        let (file, pos, path) = self.build_racer_args(ctx, &session);

        let matches = ::racer::core::complete_from_file(&file.code, path, pos, &session);

        let completions = matches.filter_map(|m| {
            let (line, col) = {
                let file = session.load_file(&m.filepath);
                match file.point_to_coords(m.point) {
                    Some((line, col)) => (line, col),
                    None => return None,
                }
            };

            Some(Completion {
                position: CursorPosition {
                    line: line,
                    col: col
                },
                text: m.matchstr,
                context: collapse_whitespace(&m.contextstr),
                kind: format!("{:?}", m.mtype),
                file_path: m.filepath.to_str().unwrap().to_string()
            })
        }).collect::<Vec<_>>();

        if completions.len() != 0 {
            Ok(Some(completions))
        } else {
            Ok(None)
        }
    }
}

pub fn collapse_whitespace(text: &str) -> String {
  Regex::new(r"\s+").unwrap().replace_all(text, " ")
}

unsafe impl<'a> Sync for Racer<'a> {}
unsafe impl<'a> Send for Racer<'a> {}

#[cfg(test)]
mod tests {
    use super::*;
    use engine::{Context, CursorPosition, SemanticEngine, Buffer};

    use ::util::fs::TmpFile;

    #[test]
    #[allow(unused_variables)]
    fn find_definition() {
        // FIXME this is just here for side effects.
        let src2 = TmpFile::with_name("src2.rs", "
            /// myfn docs
            pub fn myfn() {}
            pub fn foo() {}
            ");

        let src = "
            use src2::*;
            mod src2;
            fn main() {
                myfn();
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 5, col: 17 }, "src.rs");
        let racer = Racer::new();
        let def = racer.find_definition(&ctx).unwrap().unwrap();

        assert_eq!(def.text, "myfn");
        assert_eq!(def.docs, "myfn docs");
    }

    #[test]
    #[allow(unused_variables)]
    fn find_completion() {
        let src = "
            mod mymod {
                /// myfn is a thing
                pub fn myfn<T>(reader: T) where T: ::std::io::Read {}
            }

            fn main() {
                mymod::my
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 8, col: 25 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.text, "myfn");
        assert_eq!(completion.position.line, 4);
        assert_eq!(completion.file_path, "src.rs");
    }

    #[test]
    fn find_completion_collapsing_whitespace() {
        let src = "
            struct foo {}

            impl foo {
              fn format( &self )
                -> u32 {
              }
            }

            fn main() {
              let x = foo{};
              x.
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 12, col: 16 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.context, "fn format( &self ) -> u32");
    }

    #[test]
    fn collapse_whitespace_test() {
        assert_eq!(collapse_whitespace("foo  foo"), "foo foo");
        assert_eq!(collapse_whitespace("  "), " ");
        assert_eq!(collapse_whitespace("\n\t  \n"), " ");
        assert_eq!(collapse_whitespace("foo\nbar"), "foo bar");
        assert_eq!(collapse_whitespace("fn foo( &self )\n   -> u32"),
                   "fn foo( &self ) -> u32");
    }
}
<commit_msg>racer: Cleanup unsafe stuff from racer.rs<commit_after>//! SemanticEngine implementation for [the racer library](https://github.com/phildawes/racer)
//!
use engine::{SemanticEngine, Definition, Context, CursorPosition, Completion, Error};

use racer::core::{Session, FileCache, IndexedSource};

use std::path::Path;
use std::sync::Mutex;
use std::rc::Rc;

use regex::Regex;

pub struct Racer {
    cache: Mutex<FileCache>
}

impl Racer {
    pub fn new() -> Racer {
        Racer {
            cache: Mutex::new(FileCache::new()),
        }
    }

    fn build_racer_args<'a, 'b, 'c>(&'a self,
                                    ctx: &'b Context,
                                    session: &'c Session<'a>)
                                    -> (Rc<IndexedSource>, usize, &'b Path)
    {
        let path = ctx.query_path();

        for buffer in &ctx.buffers {
            session.cache_file_contents(buffer.path(), &*buffer.contents)
        }

        let file = session.load_file(path);
        let pos = file.coords_to_point(ctx.query_cursor.line,
                                       ctx.query_cursor.col).unwrap();

        (file, pos, path)
    }
}

// FIXME: These impls are sort of a lie, since we could theoretically hold a
// `Rc<IndexedSource>` for too long.
//
// They're needed by the http middleware, and our usage turns out to be fine,
// since we're only holding Rc's while we hold the cache lock, but something
// more elegant (like making racer use `Arc`'s) should be done instead.
unsafe impl Sync for Racer {}
unsafe impl Send for Racer {}

use ::Config;
use super::Result;

impl SemanticEngine for Racer {
    fn initialize(&self, config: &Config) -> Result<()> {
        if let Some(ref src_path) = config.rust_src_path {
            ::std::env::set_var("RUST_SRC_PATH", src_path);
        }

        Ok(())
    }

    fn find_definition(&self, ctx: &Context) -> Result<Option<Definition>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let session = Session::from_path(&cache, ctx.query_path(), ctx.query_path());
        let (file, pos, path) = self.build_racer_args(ctx, &session);

        // TODO catch_panic: apparently this can panic! in a string operation. Something about pos
        // not landing on a character boundary.
        Ok(match ::racer::core::find_definition(&file.code, path, pos, &session) {
            Some(m) => {
                // TODO modify racer Match to return line, col. For now, read the file it found a
                // match in to translate the Match position into line, col.
                let (line, col) = {
                    let file = session.load_file(&m.filepath);
                    try!(file.point_to_coords(m.point).ok_or(Error::Racer))
                };

                let match_type = format!("{:?}", m.mtype);
                Some(Definition {
                    position: CursorPosition {
                        line: line,
                        col: col
                    },
                    dtype: match_type,
                    file_path: m.filepath.to_str().unwrap().to_string(),
                    text: m.matchstr.clone(),
                    text_context: m.contextstr.clone(),
                    docs: m.docs.clone(),
                })
            },
            None => None
        })
    }

    fn list_completions(&self, ctx: &Context) -> Result<Option<Vec<Completion>>> {
        let cache = match self.cache.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner()
        };

        let session = Session::from_path(&cache, ctx.query_path(), ctx.query_path());
        let (file, pos, path) = self.build_racer_args(ctx, &session);

        let matches = ::racer::core::complete_from_file(&file.code, path, pos, &session);

        let completions = matches.filter_map(|m| {
            let (line, col) = {
                let file = session.load_file(&m.filepath);
                match file.point_to_coords(m.point) {
                    Some((line, col)) => (line, col),
                    None => return None,
                }
            };

            Some(Completion {
                position: CursorPosition {
                    line: line,
                    col: col
                },
                text: m.matchstr,
                context: collapse_whitespace(&m.contextstr),
                kind: format!("{:?}", m.mtype),
                file_path: m.filepath.to_str().unwrap().to_string()
            })
        }).collect::<Vec<_>>();

        if completions.len() != 0 {
            Ok(Some(completions))
        } else {
            Ok(None)
        }
    }
}

pub fn collapse_whitespace(text: &str) -> String {
  Regex::new(r"\s+").unwrap().replace_all(text, " ")
}

#[cfg(test)]
mod tests {
    use super::*;
    use engine::{Context, CursorPosition, SemanticEngine, Buffer};

    use ::util::fs::TmpFile;

    #[test]
    #[allow(unused_variables)]
    fn find_definition() {
        // FIXME this is just here for side effects.
        let src2 = TmpFile::with_name("src2.rs", "
            /// myfn docs
            pub fn myfn() {}
            pub fn foo() {}
            ");

        let src = "
            use src2::*;
            mod src2;
            fn main() {
                myfn();
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 5, col: 17 }, "src.rs");
        let racer = Racer::new();
        let def = racer.find_definition(&ctx).unwrap().unwrap();

        assert_eq!(def.text, "myfn");
        assert_eq!(def.docs, "myfn docs");
    }

    #[test]
    #[allow(unused_variables)]
    fn find_completion() {
        let src = "
            mod mymod {
                /// myfn is a thing
                pub fn myfn<T>(reader: T) where T: ::std::io::Read {}
            }

            fn main() {
                mymod::my
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 8, col: 25 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.text, "myfn");
        assert_eq!(completion.position.line, 4);
        assert_eq!(completion.file_path, "src.rs");
    }

    #[test]
    fn find_completion_collapsing_whitespace() {
        let src = "
            struct foo {}

            impl foo {
              fn format( &self )
                -> u32 {
              }
            }

            fn main() {
              let x = foo{};
              x.
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(buffers, CursorPosition { line: 12, col: 16 }, "src.rs");
        let racer = Racer::new();

        let completions = racer.list_completions(&ctx).unwrap().unwrap();
        assert_eq!(completions.len(), 1);

        let completion = &completions[0];
        assert_eq!(completion.context, "fn format( &self ) -> u32");
    }

    #[test]
    fn collapse_whitespace_test() {
        assert_eq!(collapse_whitespace("foo  foo"), "foo foo");
        assert_eq!(collapse_whitespace("  "), " ");
        assert_eq!(collapse_whitespace("\n\t  \n"), " ");
        assert_eq!(collapse_whitespace("foo\nbar"), "foo bar");
        assert_eq!(collapse_whitespace("fn foo( &self )\n   -> u32"),
                   "fn foo( &self ) -> u32");
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::ReprConsumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

/// Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
/// Assumes Von Nuemann's neighborhood.

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport
}

impl Default for Stage {
    fn default() -> Self { Stage::Collision }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down
}

impl Direction {

    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32)
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {

    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Transport,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) && neighbor.particle(&opposite);

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    }
                    else {
                        let particle = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, particle);
                    }
                },
                // Rebound
                None => {
                    if self.particle(&direction) {
                        let opposite = direction.opposite();
                        new.set_particle(&opposite, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Collision,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();

                    if neighbor.particle(&opposite) {
                        new.set_particle(&opposite, neighbor.particle(&opposite));
                    }
                },
                None => {
                    if self.particle(&direction) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {

        match *direction {
            Direction::Up => self.particles[0],
            Direction::Left => self.particles[1],
            Direction::Right => self.particles[2],
            Direction::Down => self.particles[3]
        }
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {

        match *direction {
            Direction::Up => { self.particles[0] = exists },
            Direction::Left => { self.particles[1] = exists },
            Direction::Right => { self.particles[2] = exists },
            Direction::Down => { self.particles[3] = exists }
        }
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {

    pub fn new() -> Self {
        HPPRulesTestConsumer { }
    }

    fn particles_count<C: Cell>(&self,
                                 cells: &Vec<C>,
                                 direction: &Direction) -> i32 {

        cells.iter()
             .map(|c| to_cell(c))
             .filter(|c: &HPP| c.particle(direction) == true)
             .count() as i32
    }

    fn find_cell<C: Cell>(&self,
                          cells: &Vec<C>,
                          x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
                         .find(|c| c.coord().x() == x && c.coord().y() == y)
                         .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {
        println!("Collision");

        let left_particle_count =
            self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count =
            self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count =
            self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count =
            self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right =
            self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up =
            self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down =
            self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {
        println!("Transport");

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right), true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0 .. 3 {
            print!("|");

            for x in 0 .. 3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle =
                    directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl ReprConsumer for HPPRulesTestConsumer {

    fn consume<G: Grid>(&mut self, grid: &G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count =
        grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(&Direction::Right) == true)
            .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction
}

impl HPPSpreadTestConsumer {

    pub fn new() -> Self {
        HPPSpreadTestConsumer { cur_y: 0, cur_direction: Direction::Down }
    }

    fn particles_count<C: Cell>(&self,
                                 cells: &Vec<C>,
                                 direction: &Direction) -> i32 {

        cells.iter()
             .map(|c| to_cell(c))
             .filter(|c: &HPP| c.particle(direction) == true)
             .count() as i32
    }

    fn find_cell<C: Cell>(&self,
                          cells: &Vec<C>,
                          x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
                         .find(|c| c.coord().x() == x && c.coord().y() == y)
                         .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0 .. 5 {
            print!("|");

            for x in 0 .. 5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle =
                    directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {
        println!("Collision");
        println!("{} {:?}", self.cur_y, self.cur_direction);

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count =
            self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        }
        else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {
        println!("Transport");
        println!("{} {:?}", self.cur_y, self.cur_direction);

        let particles_count =
            self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(
            grid.cells().iter()
                        .map(|c| to_cell(c))
                        .filter(|c: &HPP| c.coord().y() == self.cur_y)
                        .all(|c| c.particle(&self.cur_direction) == true)
        );

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl ReprConsumer for HPPSpreadTestConsumer {

    fn consume<G: Grid>(&mut self, grid: &G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0 .. 5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0)
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}<commit_msg>Removed prints.<commit_after>#![cfg(test)]
use traits::Cell;
use traits::Coord;
use traits::Grid;
use traits::Engine;
use traits::ReprConsumer;
use engine::Sequential;
use grid::nhood::VonNeumannNhood;
use grid::square::SquareGrid;

/// Implementation of [HPP model](https://en.wikipedia.org/wiki/HPP_model).
/// Assumes Von Nuemann's neighborhood.

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
enum Stage {
    Collision,
    Transport
}

impl Default for Stage {
    fn default() -> Self { Stage::Collision }
}

#[derive(Clone, Copy, Debug, PartialEq)]
enum Direction {
    Up,
    Left,
    Right,
    Down
}

impl Direction {

    fn opposite(&self) -> Self {

        match *self {
            Direction::Up => Direction::Down,
            Direction::Left => Direction::Right,
            Direction::Right => Direction::Left,
            Direction::Down => Direction::Up
        }
    }

    fn perpendicular(&self) -> Self {

        match *self {
            Direction::Up => Direction::Left,
            Direction::Left => Direction::Down,
            Direction::Right => Direction::Up,
            Direction::Down => Direction::Right
        }
    }
}

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
struct HPP {
    particles: [bool; 4],
    stage: Stage,
    coord: (i32, i32)
}

impl Cell for HPP {
    type Coord = (i32, i32);

    fn step<'a, I>(&'a self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>> {

        match self.stage {
            Stage::Collision => self.collision(neighbors),
            Stage::Transport => self.transport(neighbors),
        }
    }

    fn with_coord<C: Coord>(coord: C) -> Self {
        HPP {
            stage: Stage::Collision,
            coord: (coord.x(), coord.y()),
            ..Default::default()
        }
    }

    fn coord(&self) -> &Self::Coord {
        &self.coord
    }

    fn set_coord<C: Coord>(&mut self, coord: &C) {
        self.coord = (coord.x(), coord.y());
    }
}

impl HPP {

    fn collision<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Transport,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {

                Some(neighbor) => {
                    let opposite = direction.opposite();
                    let head_on = self.particle(&direction) && neighbor.particle(&opposite);

                    if head_on {
                        new.set_particle(&direction.perpendicular(), self.particle(&direction));
                    }
                    else {
                        let particle = new.particle(&direction) || self.particle(&direction);
                        new.set_particle(&direction, particle);
                    }
                },
                // Rebound
                None => {
                    if self.particle(&direction) {
                        let opposite = direction.opposite();
                        new.set_particle(&opposite, true);
                    }
                }
            }
        }

        new
    }

    fn transport<'a, I>(&self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>> {

        let mut new = HPP {
            stage: Stage::Collision,
            ..Default::default()
        };

        for (neighbor, direction) in neighbors.zip(self.directions().iter()) {

            match neighbor {
                Some(neighbor) => {
                    let opposite = direction.opposite();

                    if neighbor.particle(&opposite) {
                        new.set_particle(&opposite, neighbor.particle(&opposite));
                    }
                },
                None => {
                    if self.particle(&direction) {
                        new.set_particle(&direction, true);
                    }
                }
            }
        }

        new
    }

    pub fn particle(&self, direction: &Direction) -> bool {

        match *direction {
            Direction::Up => self.particles[0],
            Direction::Left => self.particles[1],
            Direction::Right => self.particles[2],
            Direction::Down => self.particles[3]
        }
    }

    fn set_particle(&mut self, direction: &Direction, exists: bool) {

        match *direction {
            Direction::Up => { self.particles[0] = exists },
            Direction::Left => { self.particles[1] = exists },
            Direction::Right => { self.particles[2] = exists },
            Direction::Down => { self.particles[3] = exists }
        }
    }

    #[inline]
    pub fn directions(&self) -> [Direction; 4] {
        [Direction::Up, Direction::Left, Direction::Right, Direction::Down]
    }
}

#[derive(Debug)]
struct HPPRulesTestConsumer;

use test_helpers::to_cell;

impl HPPRulesTestConsumer {

    pub fn new() -> Self {
        HPPRulesTestConsumer { }
    }

    fn particles_count<C: Cell>(&self,
                                 cells: &Vec<C>,
                                 direction: &Direction) -> i32 {

        cells.iter()
             .map(|c| to_cell(c))
             .filter(|c: &HPP| c.particle(direction) == true)
             .count() as i32
    }

    fn find_cell<C: Cell>(&self,
                          cells: &Vec<C>,
                          x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
                         .find(|c| c.coord().x() == x && c.coord().y() == y)
                         .unwrap();

        to_cell(found)
    }

    fn test_collision<G: Grid>(&self, grid: &G) {

        let left_particle_count =
            self.particles_count(&grid.cells(), &Direction::Left);
        assert_eq!(left_particle_count, 0);

        let right_particle_count =
            self.particles_count(&grid.cells(), &Direction::Right);
        assert_eq!(right_particle_count, 2);

        let up_particles_count =
            self.particles_count(&grid.cells(), &Direction::Up);
        assert_eq!(up_particles_count, 1);

        let down_particles_count =
            self.particles_count(&grid.cells(), &Direction::Down);
        assert_eq!(down_particles_count, 1);

        let rebound_to_right =
            self.find_cell(&grid.cells(), 0, 1);
        assert_eq!(rebound_to_right.particle(&Direction::Right), true);

        let head_on_up =
            self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(head_on_up.particle(&Direction::Up), true);

        let head_on_down =
            self.find_cell(&grid.cells(), 2, 0);
        assert_eq!(head_on_down.particle(&Direction::Down), true);
    }

    fn test_transport<G: Grid>(&self, grid: &G) {

        let simple_move_to_right = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(simple_move_to_right.particle(&Direction::Right), true);

        let move_to_right_after_rebound = self.find_cell(&grid.cells(), 1, 2);
        assert_eq!(move_to_right_after_rebound.particle(&Direction::Right), true);

        let move_to_down_after_head_on = self.find_cell(&grid.cells(), 2, 1);
        assert_eq!(move_to_down_after_head_on.particle(&Direction::Down), true);

        let fixed_to_up_after_head_on = self.find_cell(&grid.cells(), 1, 0);
        assert_eq!(fixed_to_up_after_head_on.particle(&Direction::Up), true);
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0 .. 3 {
            print!("|");

            for x in 0 .. 3 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle =
                    directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }
}

impl ReprConsumer for HPPRulesTestConsumer {

    fn consume<G: Grid>(&mut self, grid: &G) {
        assert_eq!(grid.cells().len(), 9);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_rules() {
    // initial      collision    transport
    // | * > < |    | * ^ v |    | * ^ * |
    // | < * * | => | > * * | => | * > v |
    // | > * * |    | > * * |    | * > * |
    // 3x3 grid with 4 particles. There should be
    // one rebound, head-on collision and simple move.

    let left_particle = [false, true, false, false];
    let right_particle = [false, false, true, false];

    let cells = vec![
        HPP { stage: Stage::Collision, particles: right_particle, coord: (1, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (2, 0) },
        HPP { stage: Stage::Collision, particles: left_particle, coord: (0, 1) },
        HPP { stage: Stage::Collision, particles: right_particle, coord: (0, 2) },
    ];

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(3, 3, nhood);
    grid.set_cells(cells);

    let right_particles_count =
        grid.cells()
            .iter()
            .map(|c| to_cell(c))
            .filter(|c: &HPP| c.particle(&Direction::Right) == true)
            .count();
    assert_eq!(right_particles_count, 2);

    let consumer = HPPRulesTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    engine.run_times(2);
}

struct HPPSpreadTestConsumer {
    cur_y: i32,
    cur_direction: Direction
}

impl HPPSpreadTestConsumer {

    pub fn new() -> Self {
        HPPSpreadTestConsumer { cur_y: 0, cur_direction: Direction::Down }
    }

    fn particles_count<C: Cell>(&self,
                                 cells: &Vec<C>,
                                 direction: &Direction) -> i32 {

        cells.iter()
             .map(|c| to_cell(c))
             .filter(|c: &HPP| c.particle(direction) == true)
             .count() as i32
    }

    fn find_cell<C: Cell>(&self,
                          cells: &Vec<C>,
                          x: i32, y: i32) -> HPP {

        assert!(cells.iter().any(|c| c.coord().x() == x && c.coord().y() == y));

        let found = cells.iter()
                         .find(|c| c.coord().x() == x && c.coord().y() == y)
                         .unwrap();

        to_cell(found)
    }

    fn pretty_print<G: Grid>(&self, grid: &G) {
        println!("");

        for y in 0 .. 5 {
            print!("|");

            for x in 0 .. 5 {
                let cell = self.find_cell(grid.cells(), x, y);
                let directions = cell.directions();
                let maybe_particle =
                    directions.iter().find(|d| cell.particle(d));

                let to_print = match maybe_particle {
                    Some(&Direction::Up) => " ^ |",
                    Some(&Direction::Left) => " < |",
                    Some(&Direction::Right) => " > |",
                    Some(&Direction::Down) => " v |",
                    None => " * |",
                };
                print!("{}", to_print);
            }

            println!("");
        }
    }

    fn test_collision<G: Grid>(&mut self, grid: &G) {

        if self.cur_y == 0 {
            self.cur_direction = Direction::Down;
        }

        let particles_count =
            self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        if self.cur_direction == Direction::Down {
            self.cur_y += 1;
        }
        else {
            self.cur_y -= 1;
        }
    }

    fn test_transport<G: Grid>(&mut self, grid: &G) {

        let particles_count =
            self.particles_count(&grid.cells(), &self.cur_direction);
        assert_eq!(particles_count, 5);

        assert!(
            grid.cells().iter()
                        .map(|c| to_cell(c))
                        .filter(|c: &HPP| c.coord().y() == self.cur_y)
                        .all(|c| c.particle(&self.cur_direction) == true)
        );

        if self.cur_y == 4 {
            self.cur_direction = Direction::Up;
        }
    }
}

impl ReprConsumer for HPPSpreadTestConsumer {

    fn consume<G: Grid>(&mut self, grid: &G) {
        assert_eq!(grid.cells().len(), 25);

        self.pretty_print(grid);

        // We are testing previous state.
        let first: HPP = to_cell(&grid.cells()[0]);

        match first.stage {
            Stage::Collision => self.test_transport(grid),
            Stage::Transport => self.test_collision(grid),
        }
    }
}

#[test]
fn test_spread() {
    // initial          later
    // | v v v v v |    | * * * * * |
    // | * * * * * |    | v v v v v |
    // | * * * * * | => | * * * * * | => (so on)
    // | * * * * * |    | * * * * * |
    // | * * * * * |    | * * * * * |
    // 5x5 grid with 5 particles. Particles should move
    // to lower border then rebound, then move to upper and so on.

    let down_particle = [false, false, false, true];

    let mut cells = Vec::new();
    for x in 0 .. 5 {
        cells.push(HPP {
            stage: Stage::Collision,
            particles: down_particle,
            coord: (x, 0)
        });
    }

    let nhood = VonNeumannNhood::new();
    let mut grid: SquareGrid<HPP, _> = SquareGrid::new(5, 5, nhood);
    grid.set_cells(cells);

    let consumer = HPPSpreadTestConsumer::new();
    let mut engine = Sequential::new(grid, consumer);
    // 2 phases * 10 full cycles = 20 times.
    engine.run_times(20);
}<|endoftext|>


<commit_before>// library ~ (core/bundler file)

// Copyright (C) ~ Alex Lyon <arcterus@mail.com>
// Copyright (C) ~ Roy Ivy III <rivy.dev@gmail.com>; MIT license

// * feature-gated external crates (re-shared as public internal modules)
#[cfg(feature = "libc")]
pub extern crate libc;
#[cfg(feature = "winapi")]
pub extern crate winapi;

//## internal modules

mod features; // feature-gated code modules
mod macros; // crate macros (macro_rules-type; exported to `crate::...`)
mod mods; // core cross-platform modules
mod parser; // string parsing modules

pub use uucore_procs::*;

// * cross-platform modules
pub use crate::mods::backup_control;
pub use crate::mods::display;
pub use crate::mods::error;
pub use crate::mods::os;
pub use crate::mods::panic;
pub use crate::mods::quoting_style;
pub use crate::mods::ranges;
pub use crate::mods::version_cmp;

// * string parsing modules
pub use crate::parser::parse_glob;
pub use crate::parser::parse_size;
pub use crate::parser::parse_time;

// * feature-gated modules
#[cfg(feature = "encoding")]
pub use crate::features::encoding;
#[cfg(feature = "fs")]
pub use crate::features::fs;
#[cfg(feature = "fsext")]
pub use crate::features::fsext;
#[cfg(feature = "lines")]
pub use crate::features::lines;
#[cfg(feature = "memo")]
pub use crate::features::memo;
#[cfg(feature = "ringbuffer")]
pub use crate::features::ringbuffer;

// * (platform-specific) feature-gated modules
// ** non-windows (i.e. Unix + Fuchsia)
#[cfg(all(not(windows), feature = "mode"))]
pub use crate::features::mode;
// ** unix-only
#[cfg(all(unix, feature = "entries"))]
pub use crate::features::entries;
#[cfg(all(unix, feature = "perms"))]
pub use crate::features::perms;
#[cfg(all(unix, feature = "pipes"))]
pub use crate::features::pipes;
#[cfg(all(unix, feature = "process"))]
pub use crate::features::process;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
pub use crate::features::signals;
#[cfg(all(
    unix,
    not(target_os = "android"),
    not(target_os = "fuchsia"),
    not(target_os = "redox"),
    not(target_env = "musl"),
    feature = "utmpx"
))]
pub use crate::features::utmpx;
// ** windows-only
#[cfg(all(windows, feature = "wide"))]
pub use crate::features::wide;

//## core functions

use std::ffi::OsString;
use std::sync::atomic::Ordering;

use once_cell::sync::Lazy;

use crate::display::Quotable;

#[macro_export]
macro_rules! bin {
    ($util:ident) => {
        pub fn main() {
            use std::io::Write;
            uucore::panic::mute_sigpipe_panic(); // suppress extraneous error output for SIGPIPE failures/panics
            let code = $util::uumain(uucore::args_os()); // execute utility code
            std::io::stdout().flush().expect("could not flush stdout"); // (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
            std::process::exit(code);
        }
    };
}

/// Generate the usage string for clap.
///
/// This function replaces all occurrences of `{}` with the execution phrase
/// and leaks the result to return a `&'static str`. It does **not** support
/// more advanced formatting features such as `{0}`.
pub fn format_usage(s: &str) -> &'static str {
    &*Box::leak(s.replace("{}", crate::execution_phrase()).into_boxed_str())
}

pub fn get_utility_is_second_arg() -> bool {
    crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst)
}

pub fn set_utility_is_second_arg() {
    crate::macros::UTILITY_IS_SECOND_ARG.store(true, Ordering::SeqCst);
}

// args_os() can be expensive to call, it copies all of argv before iterating.
// So if we want only the first arg or so it's overkill. We cache it.
static ARGV: Lazy<Vec<OsString>> = Lazy::new(|| wild::args_os().collect());

static UTIL_NAME: Lazy<String> = Lazy::new(|| {
    if get_utility_is_second_arg() {
        &ARGV[1]
    } else {
        &ARGV[0]
    }
    .to_string_lossy()
    .into_owned()
});

/// Derive the utility name.
pub fn util_name() -> &'static str {
    &UTIL_NAME
}

static EXECUTION_PHRASE: Lazy<String> = Lazy::new(|| {
    if get_utility_is_second_arg() {
        ARGV.iter()
            .take(2)
            .map(|os_str| os_str.to_string_lossy().into_owned())
            .collect::<Vec<_>>()
            .join(" ")
    } else {
        ARGV[0].to_string_lossy().into_owned()
    }
});

/// Derive the complete execution phrase for "usage".
pub fn execution_phrase() -> &'static str {
    &EXECUTION_PHRASE
}

pub enum InvalidEncodingHandling {
    Ignore,
    ConvertLossy,
    Panic,
}

#[must_use]
pub enum ConversionResult {
    Complete(Vec<String>),
    Lossy(Vec<String>),
}

impl ConversionResult {
    pub fn accept_any(self) -> Vec<String> {
        match self {
            Self::Complete(result) | Self::Lossy(result) => result,
        }
    }

    pub fn expect_lossy(self, msg: &str) -> Vec<String> {
        match self {
            Self::Lossy(result) => result,
            Self::Complete(_) => {
                panic!("{}", msg);
            }
        }
    }

    pub fn expect_complete(self, msg: &str) -> Vec<String> {
        match self {
            Self::Complete(result) => result,
            Self::Lossy(_) => {
                panic!("{}", msg);
            }
        }
    }
}

pub trait Args: Iterator<Item = OsString> + Sized {
    /// Converts each iterator item to a String and collects these into a vector
    /// On invalid encoding, the result will depend on the argument. This method allows to either drop entries with illegal encoding
    /// completely (```InvalidEncodingHandling::Ignore```), convert them using lossy-conversion (```InvalidEncodingHandling::ConvertLossy```)
    /// which will result in strange strings or can chosen to panic (```InvalidEncodingHandling::Panic```).
    /// # Arguments
    /// * `handling` - This switch allows to switch the behavior, when invalid encoding is encountered
    /// # Panics
    /// * Occurs, when invalid encoding is encountered and handling is set to ```InvalidEncodingHandling::Panic```
    fn collect_str(self, handling: InvalidEncodingHandling) -> ConversionResult {
        let mut full_conversion = true;
        let result_vector: Vec<String> = self
            .map(|s| match s.into_string() {
                Ok(string) => Ok(string),
                Err(s_ret) => {
                    full_conversion = false;
                    eprintln!(
                        "Input with broken encoding occurred! (s = {}) ",
                        s_ret.quote()
                    );
                    match handling {
                        InvalidEncodingHandling::Ignore => Err(String::new()),
                        InvalidEncodingHandling::ConvertLossy => {
                            Err(s_ret.to_string_lossy().into_owned())
                        }
                        InvalidEncodingHandling::Panic => {
                            panic!("Broken encoding found but caller cannot handle it")
                        }
                    }
                }
            })
            .filter(|s| match handling {
                InvalidEncodingHandling::Ignore => s.is_ok(),
                _ => true,
            })
            .map(|s| match s {
                Ok(v) => v,
                Err(e) => e,
            })
            .collect();

        if full_conversion {
            ConversionResult::Complete(result_vector)
        } else {
            ConversionResult::Lossy(result_vector)
        }
    }

    /// convenience function for a more slim interface
    fn collect_str_lossy(self) -> ConversionResult {
        self.collect_str(InvalidEncodingHandling::ConvertLossy)
    }
}

impl<T: Iterator<Item = OsString> + Sized> Args for T {}

pub fn args_os() -> impl Iterator<Item = OsString> {
    ARGV.iter().cloned()
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::OsStr;

    fn make_os_vec(os_str: &OsStr) -> Vec<OsString> {
        vec![
            OsString::from("test"),
            OsString::from("สวัสดี"), // spell-checker:disable-line
            os_str.to_os_string(),
        ]
    }

    fn collect_os_str(vec: Vec<OsString>, handling: InvalidEncodingHandling) -> ConversionResult {
        vec.into_iter().collect_str(handling)
    }

    #[cfg(any(unix, target_os = "redox"))]
    fn test_invalid_utf8_args_lossy(os_str: &OsStr) {
        //assert our string is invalid utf8
        assert!(os_str.to_os_string().into_string().is_err());
        let test_vec = make_os_vec(os_str);
        let collected_to_str =
            collect_os_str(test_vec.clone(), InvalidEncodingHandling::ConvertLossy)
                .expect_lossy("Lossy conversion expected in this test: bad encoding entries should be converted as good as possible");
        //conservation of length - when accepting lossy conversion no arguments may be dropped
        assert_eq!(collected_to_str.len(), test_vec.len());
        //first indices identical
        for index in 0..2 {
            assert_eq!(
                collected_to_str.get(index).unwrap(),
                test_vec.get(index).unwrap().to_str().unwrap()
            );
        }
        //lossy conversion for string with illegal encoding is done
        assert_eq!(
            *collected_to_str.get(2).unwrap(),
            os_str.to_os_string().to_string_lossy()
        );
    }

    #[cfg(any(unix, target_os = "redox"))]
    fn test_invalid_utf8_args_ignore(os_str: &OsStr) {
        //assert our string is invalid utf8
        assert!(os_str.to_os_string().into_string().is_err());
        let test_vec = make_os_vec(os_str);
        let collected_to_str = collect_os_str(test_vec.clone(), InvalidEncodingHandling::Ignore)
            .expect_lossy(
                "Lossy conversion expected in this test: bad encoding entries should be filtered",
            );
        //assert that the broken entry is filtered out
        assert_eq!(collected_to_str.len(), test_vec.len() - 1);
        //assert that the unbroken indices are converted as expected
        for index in 0..2 {
            assert_eq!(
                collected_to_str.get(index).unwrap(),
                test_vec.get(index).unwrap().to_str().unwrap()
            );
        }
    }

    #[test]
    fn valid_utf8_encoding_args() {
        //create a vector containing only correct encoding
        let test_vec = make_os_vec(&OsString::from("test2"));
        //expect complete conversion without losses, even when lossy conversion is accepted
        let _ = collect_os_str(test_vec, InvalidEncodingHandling::ConvertLossy)
            .expect_complete("Lossy conversion not expected in this test");
    }

    #[cfg(any(unix, target_os = "redox"))]
    #[test]
    fn invalid_utf8_args_unix() {
        use std::os::unix::ffi::OsStrExt;

        let source = [0x66, 0x6f, 0x80, 0x6f];
        let os_str = OsStr::from_bytes(&source[..]);
        test_invalid_utf8_args_lossy(os_str);
        test_invalid_utf8_args_ignore(os_str);
    }
}
<commit_msg>uucore: remove panic encoding handling<commit_after>// library ~ (core/bundler file)

// Copyright (C) ~ Alex Lyon <arcterus@mail.com>
// Copyright (C) ~ Roy Ivy III <rivy.dev@gmail.com>; MIT license

// * feature-gated external crates (re-shared as public internal modules)
#[cfg(feature = "libc")]
pub extern crate libc;
#[cfg(feature = "winapi")]
pub extern crate winapi;

//## internal modules

mod features; // feature-gated code modules
mod macros; // crate macros (macro_rules-type; exported to `crate::...`)
mod mods; // core cross-platform modules
mod parser; // string parsing modules

pub use uucore_procs::*;

// * cross-platform modules
pub use crate::mods::backup_control;
pub use crate::mods::display;
pub use crate::mods::error;
pub use crate::mods::os;
pub use crate::mods::panic;
pub use crate::mods::quoting_style;
pub use crate::mods::ranges;
pub use crate::mods::version_cmp;

// * string parsing modules
pub use crate::parser::parse_glob;
pub use crate::parser::parse_size;
pub use crate::parser::parse_time;

// * feature-gated modules
#[cfg(feature = "encoding")]
pub use crate::features::encoding;
#[cfg(feature = "fs")]
pub use crate::features::fs;
#[cfg(feature = "fsext")]
pub use crate::features::fsext;
#[cfg(feature = "lines")]
pub use crate::features::lines;
#[cfg(feature = "memo")]
pub use crate::features::memo;
#[cfg(feature = "ringbuffer")]
pub use crate::features::ringbuffer;

// * (platform-specific) feature-gated modules
// ** non-windows (i.e. Unix + Fuchsia)
#[cfg(all(not(windows), feature = "mode"))]
pub use crate::features::mode;
// ** unix-only
#[cfg(all(unix, feature = "entries"))]
pub use crate::features::entries;
#[cfg(all(unix, feature = "perms"))]
pub use crate::features::perms;
#[cfg(all(unix, feature = "pipes"))]
pub use crate::features::pipes;
#[cfg(all(unix, feature = "process"))]
pub use crate::features::process;
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
pub use crate::features::signals;
#[cfg(all(
    unix,
    not(target_os = "android"),
    not(target_os = "fuchsia"),
    not(target_os = "redox"),
    not(target_env = "musl"),
    feature = "utmpx"
))]
pub use crate::features::utmpx;
// ** windows-only
#[cfg(all(windows, feature = "wide"))]
pub use crate::features::wide;

//## core functions

use std::ffi::OsString;
use std::sync::atomic::Ordering;

use once_cell::sync::Lazy;

use crate::display::Quotable;

#[macro_export]
macro_rules! bin {
    ($util:ident) => {
        pub fn main() {
            use std::io::Write;
            uucore::panic::mute_sigpipe_panic(); // suppress extraneous error output for SIGPIPE failures/panics
            let code = $util::uumain(uucore::args_os()); // execute utility code
            std::io::stdout().flush().expect("could not flush stdout"); // (defensively) flush stdout for utility prior to exit; see <https://github.com/rust-lang/rust/issues/23818>
            std::process::exit(code);
        }
    };
}

/// Generate the usage string for clap.
///
/// This function replaces all occurrences of `{}` with the execution phrase
/// and leaks the result to return a `&'static str`. It does **not** support
/// more advanced formatting features such as `{0}`.
pub fn format_usage(s: &str) -> &'static str {
    &*Box::leak(s.replace("{}", crate::execution_phrase()).into_boxed_str())
}

pub fn get_utility_is_second_arg() -> bool {
    crate::macros::UTILITY_IS_SECOND_ARG.load(Ordering::SeqCst)
}

pub fn set_utility_is_second_arg() {
    crate::macros::UTILITY_IS_SECOND_ARG.store(true, Ordering::SeqCst);
}

// args_os() can be expensive to call, it copies all of argv before iterating.
// So if we want only the first arg or so it's overkill. We cache it.
static ARGV: Lazy<Vec<OsString>> = Lazy::new(|| wild::args_os().collect());

static UTIL_NAME: Lazy<String> = Lazy::new(|| {
    if get_utility_is_second_arg() {
        &ARGV[1]
    } else {
        &ARGV[0]
    }
    .to_string_lossy()
    .into_owned()
});

/// Derive the utility name.
pub fn util_name() -> &'static str {
    &UTIL_NAME
}

static EXECUTION_PHRASE: Lazy<String> = Lazy::new(|| {
    if get_utility_is_second_arg() {
        ARGV.iter()
            .take(2)
            .map(|os_str| os_str.to_string_lossy().into_owned())
            .collect::<Vec<_>>()
            .join(" ")
    } else {
        ARGV[0].to_string_lossy().into_owned()
    }
});

/// Derive the complete execution phrase for "usage".
pub fn execution_phrase() -> &'static str {
    &EXECUTION_PHRASE
}

pub enum InvalidEncodingHandling {
    Ignore,
    ConvertLossy,
}

#[must_use]
pub enum ConversionResult {
    Complete(Vec<String>),
    Lossy(Vec<String>),
}

impl ConversionResult {
    pub fn accept_any(self) -> Vec<String> {
        match self {
            Self::Complete(result) | Self::Lossy(result) => result,
        }
    }

    pub fn expect_lossy(self, msg: &str) -> Vec<String> {
        match self {
            Self::Lossy(result) => result,
            Self::Complete(_) => {
                panic!("{}", msg);
            }
        }
    }

    pub fn expect_complete(self, msg: &str) -> Vec<String> {
        match self {
            Self::Complete(result) => result,
            Self::Lossy(_) => {
                panic!("{}", msg);
            }
        }
    }
}

pub trait Args: Iterator<Item = OsString> + Sized {
    /// Converts each iterator item to a String and collects these into a vector
    /// On invalid encoding, the result will depend on the argument. This method allows to either drop entries with illegal encoding
    /// completely (```InvalidEncodingHandling::Ignore```), convert them using lossy-conversion (```InvalidEncodingHandling::ConvertLossy```)
    /// which will result in strange strings or can chosen to panic.
    /// # Arguments
    /// * `handling` - This switch allows to switch the behavior, when invalid encoding is encountered
    fn collect_str(self, handling: InvalidEncodingHandling) -> ConversionResult {
        let mut full_conversion = true;
        let result_vector: Vec<String> = self
            .map(|s| match s.into_string() {
                Ok(string) => Ok(string),
                Err(s_ret) => {
                    full_conversion = false;
                    eprintln!(
                        "Input with broken encoding occurred! (s = {}) ",
                        s_ret.quote()
                    );
                    match handling {
                        InvalidEncodingHandling::Ignore => Err(String::new()),
                        InvalidEncodingHandling::ConvertLossy => {
                            Err(s_ret.to_string_lossy().into_owned())
                        }
                    }
                }
            })
            .filter(|s| match handling {
                InvalidEncodingHandling::Ignore => s.is_ok(),
                _ => true,
            })
            .map(|s| match s {
                Ok(v) => v,
                Err(e) => e,
            })
            .collect();

        if full_conversion {
            ConversionResult::Complete(result_vector)
        } else {
            ConversionResult::Lossy(result_vector)
        }
    }

    /// convenience function for a more slim interface
    fn collect_str_lossy(self) -> ConversionResult {
        self.collect_str(InvalidEncodingHandling::ConvertLossy)
    }
}

impl<T: Iterator<Item = OsString> + Sized> Args for T {}

pub fn args_os() -> impl Iterator<Item = OsString> {
    ARGV.iter().cloned()
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::OsStr;

    fn make_os_vec(os_str: &OsStr) -> Vec<OsString> {
        vec![
            OsString::from("test"),
            OsString::from("สวัสดี"), // spell-checker:disable-line
            os_str.to_os_string(),
        ]
    }

    fn collect_os_str(vec: Vec<OsString>, handling: InvalidEncodingHandling) -> ConversionResult {
        vec.into_iter().collect_str(handling)
    }

    #[cfg(any(unix, target_os = "redox"))]
    fn test_invalid_utf8_args_lossy(os_str: &OsStr) {
        //assert our string is invalid utf8
        assert!(os_str.to_os_string().into_string().is_err());
        let test_vec = make_os_vec(os_str);
        let collected_to_str =
            collect_os_str(test_vec.clone(), InvalidEncodingHandling::ConvertLossy)
                .expect_lossy("Lossy conversion expected in this test: bad encoding entries should be converted as good as possible");
        //conservation of length - when accepting lossy conversion no arguments may be dropped
        assert_eq!(collected_to_str.len(), test_vec.len());
        //first indices identical
        for index in 0..2 {
            assert_eq!(
                collected_to_str.get(index).unwrap(),
                test_vec.get(index).unwrap().to_str().unwrap()
            );
        }
        //lossy conversion for string with illegal encoding is done
        assert_eq!(
            *collected_to_str.get(2).unwrap(),
            os_str.to_os_string().to_string_lossy()
        );
    }

    #[cfg(any(unix, target_os = "redox"))]
    fn test_invalid_utf8_args_ignore(os_str: &OsStr) {
        //assert our string is invalid utf8
        assert!(os_str.to_os_string().into_string().is_err());
        let test_vec = make_os_vec(os_str);
        let collected_to_str = collect_os_str(test_vec.clone(), InvalidEncodingHandling::Ignore)
            .expect_lossy(
                "Lossy conversion expected in this test: bad encoding entries should be filtered",
            );
        //assert that the broken entry is filtered out
        assert_eq!(collected_to_str.len(), test_vec.len() - 1);
        //assert that the unbroken indices are converted as expected
        for index in 0..2 {
            assert_eq!(
                collected_to_str.get(index).unwrap(),
                test_vec.get(index).unwrap().to_str().unwrap()
            );
        }
    }

    #[test]
    fn valid_utf8_encoding_args() {
        //create a vector containing only correct encoding
        let test_vec = make_os_vec(&OsString::from("test2"));
        //expect complete conversion without losses, even when lossy conversion is accepted
        let _ = collect_os_str(test_vec, InvalidEncodingHandling::ConvertLossy)
            .expect_complete("Lossy conversion not expected in this test");
    }

    #[cfg(any(unix, target_os = "redox"))]
    #[test]
    fn invalid_utf8_args_unix() {
        use std::os::unix::ffi::OsStrExt;

        let source = [0x66, 0x6f, 0x80, 0x6f];
        let os_str = OsStr::from_bytes(&source[..]);
        test_invalid_utf8_args_lossy(os_str);
        test_invalid_utf8_args_ignore(os_str);
    }
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/decoders/u64_decoder.rs                             *
 *                                                        *
 * hprose u64 decoder for Rust.                           *
 *                                                        *
 * LastModified: Sep 25, 2016                             *
 * Author: Chen Fei <cf@hprose.com>                       *
 *                                                        *
\**********************************************************/

use io::{Reader, Decoder, DecoderError, ParserError};
use io::tags::*;
use io::reader::cast_error;
use io::util::utf8_slice_to_str;

use std::{result, str};

type Result = result::Result<u64, DecoderError>;

pub fn u64_decode(r: &mut Reader, tag: u8) -> Result {
    match tag {
        b'0' | TAG_NULL | TAG_EMPTY | TAG_FALSE => Ok(0),
        b'1' | TAG_TRUE => Ok(1),
        b'2' => Ok(2),
        b'3' => Ok(3),
        b'4' => Ok(4),
        b'5' => Ok(5),
        b'6' => Ok(6),
        b'7' => Ok(7),
        b'8' => Ok(8),
        b'9' => Ok(9),
        TAG_INTEGER | TAG_LONG => read_u64(r),
        TAG_DOUBLE => read_f64_as_u64(r),
        TAG_UTF8_CHAR => read_utf8_char_as_u64(r),
        TAG_STRING => read_string_as_u64(r),
        TAG_DATE => read_datetime_as_u64(r),
        TAG_TIME => read_time_as_u64(r),
        TAG_REF => r.read_ref(),
        _ => Err(cast_error(tag, "u64"))
    }
}

fn read_u64(r: &mut Reader) -> Result {
    r.byte_reader.read_u64_with_tag(TAG_SEMICOLON).map_err(|e| DecoderError::ParserError(e))
}

fn read_f64_as_u64(r: &mut Reader) -> Result {
    r.byte_reader.read_f64().map(|f| f as u64).map_err(|e| DecoderError::ParserError(e))
}

fn read_utf8_char_as_u64(r: &mut Reader) -> Result {
    r.byte_reader
        .read_utf8_slice(1)
        .and_then(|s| utf8_slice_to_str(s).parse::<u64>().map_err(|e| ParserError::ParseIntError(e)))
        .map_err(|e| DecoderError::ParserError(e))
}

fn read_string_as_u64(r: &mut Reader) -> Result {
    r
        .read_string_without_tag()
        .and_then(|s| s.parse::<u64>().map_err(|e| ParserError::ParseIntError(e)).map_err(|e| DecoderError::ParserError(e)))
}

fn read_datetime_as_u64(r: &mut Reader) -> Result {
    r.read_datetime_without_tag()
        .map(|ref tm| {
            let ts = tm.to_timespec();
            ts.sec as u64 * 1_000_000_000 + (ts.nsec as u64)
        })
}

fn read_time_as_u64(r: &mut Reader) -> Result {
    r.read_time_without_tag()
        .map(|ref tm| {
            let ts = tm.to_timespec();
            ts.sec * 1_000_000_000 + (ts.nsec as i64)
        })
}
<commit_msg>Fix u64 read time.<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/decoders/u64_decoder.rs                             *
 *                                                        *
 * hprose u64 decoder for Rust.                           *
 *                                                        *
 * LastModified: Sep 25, 2016                             *
 * Author: Chen Fei <cf@hprose.com>                       *
 *                                                        *
\**********************************************************/

use io::{Reader, Decoder, DecoderError, ParserError};
use io::tags::*;
use io::reader::cast_error;
use io::util::utf8_slice_to_str;

use std::{result, str};

type Result = result::Result<u64, DecoderError>;

pub fn u64_decode(r: &mut Reader, tag: u8) -> Result {
    match tag {
        b'0' | TAG_NULL | TAG_EMPTY | TAG_FALSE => Ok(0),
        b'1' | TAG_TRUE => Ok(1),
        b'2' => Ok(2),
        b'3' => Ok(3),
        b'4' => Ok(4),
        b'5' => Ok(5),
        b'6' => Ok(6),
        b'7' => Ok(7),
        b'8' => Ok(8),
        b'9' => Ok(9),
        TAG_INTEGER | TAG_LONG => read_u64(r),
        TAG_DOUBLE => read_f64_as_u64(r),
        TAG_UTF8_CHAR => read_utf8_char_as_u64(r),
        TAG_STRING => read_string_as_u64(r),
        TAG_DATE => read_datetime_as_u64(r),
        TAG_TIME => read_time_as_u64(r),
        TAG_REF => r.read_ref(),
        _ => Err(cast_error(tag, "u64"))
    }
}

fn read_u64(r: &mut Reader) -> Result {
    r.byte_reader.read_u64_with_tag(TAG_SEMICOLON).map_err(|e| DecoderError::ParserError(e))
}

fn read_f64_as_u64(r: &mut Reader) -> Result {
    r.byte_reader.read_f64().map(|f| f as u64).map_err(|e| DecoderError::ParserError(e))
}

fn read_utf8_char_as_u64(r: &mut Reader) -> Result {
    r.byte_reader
        .read_utf8_slice(1)
        .and_then(|s| utf8_slice_to_str(s).parse::<u64>().map_err(|e| ParserError::ParseIntError(e)))
        .map_err(|e| DecoderError::ParserError(e))
}

fn read_string_as_u64(r: &mut Reader) -> Result {
    r
        .read_string_without_tag()
        .and_then(|s| s.parse::<u64>().map_err(|e| ParserError::ParseIntError(e)).map_err(|e| DecoderError::ParserError(e)))
}

fn read_datetime_as_u64(r: &mut Reader) -> Result {
    r.read_datetime_without_tag()
        .map(|ref tm| {
            let ts = tm.to_timespec();
            ts.sec as u64 * 1_000_000_000 + (ts.nsec as u64)
        })
}

fn read_time_as_u64(r: &mut Reader) -> Result {
    r.read_time_without_tag()
        .map(|ref tm| {
            let ts = tm.to_timespec();
            ts.sec as u64 * 1_000_000_000 + (ts.nsec as u64)
        })
}
<|endoftext|>


<commit_before>use crate::chip::*;
use crate::database::*;
use crate::wires::*;
use crate::bels::*;

use std::collections::{BTreeSet, BTreeMap};
use std::fmt;

pub struct SitePin {
    pub tile_wire: String,
    pub site_wire: String,
    pub dir: PinDir,
}

impl fmt::Debug for SitePin {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {} {}",
            &self.tile_wire,
            match self.dir {
                PinDir::INPUT => "-->",
                PinDir::OUTPUT => "<--",
                PinDir::INOUT => "<->",
            },
            &self.site_wire
        )
    }
}

pub struct SitePip {
    pub src_wire: String,
    pub dst_wire: String,
}

pub struct Site {
    pub name: String,
    pub pins: Vec<SitePin>,
    pub pips: Vec<SitePip>,
}

// To speed up site routing; where fixed connections connect multiple site wires together, merge them into one
// this structure stores the mapping in both directions
struct SiteWireMap {
    pub wire2root: BTreeMap<String, String>,
    pub root2wires: BTreeMap<String, BTreeSet<String>>,
}

impl SiteWireMap {
    pub fn lookup_wire(&self, name: &str) -> String {
        self.wire2root.get(name).unwrap_or(&name.to_string()).to_string()
    }
    pub fn add_alias(&mut self, root: &str, wire: &str) {
        self.wire2root.entry(wire.to_string()).or_insert(root.to_string());
        self.root2wires.entry(root.to_string()).or_insert(BTreeSet::new()).insert(wire.to_string());
        if let Some(wires) = self.root2wires.get(wire).cloned() {
            for w in wires {
                *self.wire2root.get_mut(&w).unwrap() = root.to_string();
                self.root2wires.get_mut(root).unwrap().insert(w.to_string());
            }
            self.root2wires.remove(wire);
        }
    }
}

// determine which wire has priority when flattening connections
fn compare_wire(a: &str, b: &str) -> bool {
    // prioritise shorter wires first, then fall back to a lexicographical compare
    (a.len(), a) < (b.len(), b)
}

fn flatten_wires(tile: &Tile, tiledata: &TileBitsDatabase) -> SiteWireMap {
    let mut m = SiteWireMap {
        wire2root: BTreeMap::new(),
        root2wires: BTreeMap::new(),
    };
    for (dst_wire, conns) in tiledata.conns.iter() {
        if !is_site_wire(tile, dst_wire) {
            continue;
        }
        for c in conns {
            let looked_up_src = m.lookup_wire(&c.from_wire);
            let looked_up_dst = m.lookup_wire(dst_wire);
            if compare_wire(&looked_up_src, &looked_up_dst) && is_site_wire(tile, &c.from_wire) {
                m.add_alias(&looked_up_src, &looked_up_dst);
            } else {
                m.add_alias(&looked_up_dst, &looked_up_src);
            }
        }
    }
    return m;
}

pub fn build_sites(_chip: &Chip, tile: &Tile, tiledata: &TileBitsDatabase) {
    let flat_wires = flatten_wires(tile, tiledata);
    let mut pins = Vec::new();
    let mut found_pins = BTreeSet::new();
    for (dst_wire, conns) in tiledata.conns.iter() {
        for conn in conns.iter() {
            if is_site_wire(tile, dst_wire) && !is_site_wire(tile, &conn.from_wire) {
                // from tile into site
                let site_dst_wire = flat_wires.lookup_wire(dst_wire);
                if found_pins.contains(&(conn.from_wire.to_string(), site_dst_wire.to_string())) {
                    continue;
                }
                found_pins.insert((conn.from_wire.to_string(), site_dst_wire.to_string()));
                pins.push(SitePin {
                    tile_wire: conn.from_wire.clone(),
                    site_wire: site_dst_wire,
                    dir: PinDir::INPUT,
                });
            } else if !is_site_wire(tile, dst_wire) && is_site_wire(tile, &conn.from_wire) {
                // from site into tile
                let site_src_wire = flat_wires.lookup_wire(&conn.from_wire);
                if found_pins.contains(&(site_src_wire.to_string(), dst_wire.to_string())) {
                    continue;
                }
                found_pins.insert((site_src_wire.to_string(), dst_wire.to_string()));
                pins.push(SitePin {
                    tile_wire: dst_wire.to_string(),
                    site_wire: site_src_wire,
                    dir: PinDir::OUTPUT,
                });
            }
        }
    }
    // For the F outputs where have a MUX/LUT choice; use the FF DI mux because it's not independently controllable from the FF mux
    for i in 0..4 {
            pins.push(SitePin {
                tile_wire: format!("F{}", 2*i),
                site_wire: flat_wires.lookup_wire(&format!("JDI0_SLICE{}", &"ABCD"[i..i+1])),
                dir: PinDir::OUTPUT,
            });
    }
    for pin in pins.iter() {
        println!("{:?}", pin);
    }
}
<commit_msg>Infer site routing bels<commit_after>use crate::chip::*;
use crate::database::*;
use crate::wires::*;
use crate::bels::*;

use std::collections::{BTreeSet, BTreeMap};
use std::fmt;

pub struct SitePin {
    pub tile_wire: String,
    pub site_wire: String,
    pub dir: PinDir,
}

impl fmt::Debug for SitePin {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {} {}",
            &self.tile_wire,
            match self.dir {
                PinDir::INPUT => "-->",
                PinDir::OUTPUT => "<--",
                PinDir::INOUT => "<->",
            },
            &self.site_wire
        )
    }
}

pub struct SiteRoutingBel {
    pub src_wires: Vec<String>,
    pub dst_wire: String,
}

impl fmt::Debug for SiteRoutingBel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} <-- {{", &self.dst_wire)?;
        let mut first = true;
        for src in self.src_wires.iter() {
            if !first { write!(f, ", ")?; }
            write!(f, "{}", src)?;
            first = false;
        }
        write!(f, "}}")?;
        Ok(())
    }
}

pub struct Site {
    pub name: String,
    pub pins: Vec<SitePin>,
    pub rbels: Vec<SiteRoutingBel>,
}

// To speed up site routing; where fixed connections connect multiple site wires together, merge them into one
// this structure stores the mapping in both directions
struct SiteWireMap {
    pub wire2root: BTreeMap<String, String>,
    pub root2wires: BTreeMap<String, BTreeSet<String>>,
}

impl SiteWireMap {
    pub fn lookup_wire(&self, name: &str) -> String {
        self.wire2root.get(name).unwrap_or(&name.to_string()).to_string()
    }
    pub fn add_alias(&mut self, root: &str, wire: &str) {
        self.wire2root.entry(wire.to_string()).or_insert(root.to_string());
        self.root2wires.entry(root.to_string()).or_insert(BTreeSet::new()).insert(wire.to_string());
        if let Some(wires) = self.root2wires.get(wire).cloned() {
            for w in wires {
                *self.wire2root.get_mut(&w).unwrap() = root.to_string();
                self.root2wires.get_mut(root).unwrap().insert(w.to_string());
            }
            self.root2wires.remove(wire);
        }
    }
}

// determine which wire has priority when flattening connections
fn compare_wire(a: &str, b: &str) -> bool {
    // prioritise shorter wires first, then fall back to a lexicographical compare
    (a.len(), a) < (b.len(), b)
}

fn flatten_wires(tile: &Tile, tiledata: &TileBitsDatabase) -> SiteWireMap {
    let mut m = SiteWireMap {
        wire2root: BTreeMap::new(),
        root2wires: BTreeMap::new(),
    };
    for (dst_wire, conns) in tiledata.conns.iter() {
        if !is_site_wire(tile, dst_wire) {
            continue;
        }
        for c in conns {
            let looked_up_src = m.lookup_wire(&c.from_wire);
            let looked_up_dst = m.lookup_wire(dst_wire);
            if compare_wire(&looked_up_src, &looked_up_dst) && is_site_wire(tile, &c.from_wire) {
                m.add_alias(&looked_up_src, &looked_up_dst);
            } else {
                m.add_alias(&looked_up_dst, &looked_up_src);
            }
        }
    }
    return m;
}

pub fn build_sites(_chip: &Chip, tile: &Tile, tiledata: &TileBitsDatabase) {
    let flat_wires = flatten_wires(tile, tiledata);
    let mut pins = Vec::new();
    let mut found_pins = BTreeSet::new();
    for (dst_wire, conns) in tiledata.conns.iter() {
        for conn in conns.iter() {
            if is_site_wire(tile, dst_wire) && !is_site_wire(tile, &conn.from_wire) {
                // from tile into site
                let site_dst_wire = flat_wires.lookup_wire(dst_wire);
                if found_pins.contains(&(conn.from_wire.to_string(), site_dst_wire.to_string())) {
                    continue;
                }
                found_pins.insert((conn.from_wire.to_string(), site_dst_wire.to_string()));
                pins.push(SitePin {
                    tile_wire: conn.from_wire.clone(),
                    site_wire: site_dst_wire,
                    dir: PinDir::INPUT,
                });
            } else if !is_site_wire(tile, dst_wire) && is_site_wire(tile, &conn.from_wire) {
                // from site into tile
                let site_src_wire = flat_wires.lookup_wire(&conn.from_wire);
                if found_pins.contains(&(site_src_wire.to_string(), dst_wire.to_string())) {
                    continue;
                }
                found_pins.insert((site_src_wire.to_string(), dst_wire.to_string()));
                pins.push(SitePin {
                    tile_wire: dst_wire.to_string(),
                    site_wire: site_src_wire,
                    dir: PinDir::OUTPUT,
                });
            }
        }
    }
    // For the F outputs where have a MUX/LUT choice; use the FF DI mux because it's not independently controllable from the FF mux
    for i in 0..4 {
            pins.push(SitePin {
                tile_wire: format!("F{}", 2*i),
                site_wire: flat_wires.lookup_wire(&format!("JDI0_SLICE{}", &"ABCD"[i..i+1])),
                dir: PinDir::OUTPUT,
            });
    }
    println!("Pins: ");
    for pin in pins.iter() {
        println!("    {:?}", pin);
    }
    let mut rbels = Vec::new();
    // Convert pips to routing bels
    for (dst_wire, pips) in tiledata.pips.iter() {
        if !is_site_wire(tile, dst_wire) {
            continue;
        }
        let site_dst_wire = flat_wires.lookup_wire(dst_wire);
        let mapped_src_wires = pips.iter().map(|p| &p.from_wire).filter(|w| is_site_wire(tile, w)).map(|w| flat_wires.lookup_wire(w)).collect();
        rbels.push(SiteRoutingBel {
            dst_wire: site_dst_wire,
            src_wires: mapped_src_wires,
        });
    }
    println!("Routing bels: ");
    for rbel in rbels.iter() {
        println!("    {:?}", rbel);
    }
}
<|endoftext|>