<commit_before>use petgraph::{Graph, Undirected};
use rand;
use rand::distributions::{IndependentSample, Range};

pub type GridIndexType = u16;
pub type GridGraphNodeIndex = ::petgraph::graph::NodeIndex<GridIndexType>;

pub enum GridDirection {
    North,
    South,
    East,
    West,
}

struct AdjacentCells {
    north: Option<GridGraphNodeIndex>,
    south: Option<GridGraphNodeIndex>,
    east: Option<GridGraphNodeIndex>,
    west: Option<GridGraphNodeIndex>,
}
impl AdjacentCells {
    fn new() -> Self {
        AdjacentCells {
            north: None,
            south: None,
            east: None,
            west: None,
        }
    }
}

#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
struct GridCoordinate {
    pub x: isize,
    pub y: isize,
}

pub struct SquareGrid {
    /// For mapping a 2D gridcoordinate (converted to a 1D index) to a graph node index.
    grid_coordinate_to_node_index_lookup: Vec<GridGraphNodeIndex>,
    graph: Graph<AdjacentCells, (), Undirected, GridIndexType>,
    dimension_size: GridIndexType,
}

impl SquareGrid {
    pub fn new(dimension_size: GridIndexType) -> SquareGrid {

        let cells_count = dimension_size * dimension_size;
        let nodes_count_hint = cells_count as usize;
        let edges_count_hint = 4 * cells_count as usize - 4 * dimension_size as usize; // Probably overkill, but don't want any capacity panics

        let mut grid = SquareGrid {
            grid_coordinate_to_node_index_lookup: Vec::with_capacity(nodes_count_hint),
            graph: Graph::with_capacity(nodes_count_hint, edges_count_hint),
            dimension_size: dimension_size,
        };

        // Although it does seem to, there is no guarantee that the index values are monotonically increasing when calling graph.add_node
        // So we maintain our own symbol table mapping gridcoordinates converted to 1d (the implicit Vec indices) to the NodeIndexes created
        // by adding a node to the graph.
        for _ in 0..cells_count {
            let node_index = grid.graph.add_node(AdjacentCells::new());
            grid.grid_coordinate_to_node_index_lookup.push(node_index);
        }

        for (index, node_index) in grid.grid_coordinate_to_node_index_lookup.iter().enumerate() {

            let coord = to_grid_coordinate(index, dimension_size);

            // Limit the lifetime of the find_neighbour_nodeindex closure so we don't get a grid
            // borrow conflict when mutating the adjacent_cells
            let (north_index, south_index, east_index, west_index) = {
                let find_neighbour_nodeindex = |dir: GridDirection| -> Option<GridGraphNodeIndex> {
                    let neighbour_coord = offset_coordinate(&coord, dir);
                    if is_valid_coordinate(&neighbour_coord, dimension_size) {
                        let node_indices_index = to_1d_index(&neighbour_coord, dimension_size);
                        let node_index =
                            grid.grid_coordinate_to_node_index_lookup[node_indices_index as usize];
                        Some(node_index)
                    } else {
                        None
                    }
                };
                (find_neighbour_nodeindex(GridDirection::North),
                 find_neighbour_nodeindex(GridDirection::South),
                 find_neighbour_nodeindex(GridDirection::East),
                 find_neighbour_nodeindex(GridDirection::West))
            };

            let adjacent_cells = grid.graph
                                     .node_weight_mut(node_index.clone())
                                     .unwrap();
            adjacent_cells.north = north_index;
            adjacent_cells.south = south_index;
            adjacent_cells.east = east_index;
            adjacent_cells.west = west_index;
        }

        grid
    }

    pub fn random_cell(&self) -> GridGraphNodeIndex {
        let random_pos = Range::new(0, self.grid_coordinate_to_node_index_lookup.len());
        let mut rng = rand::thread_rng();
        let index = random_pos.ind_sample(&mut rng);
        self.grid_coordinate_to_node_index_lookup[index]
    }

    pub fn link(&mut self, a: GridGraphNodeIndex, b: GridGraphNodeIndex) {
        if a != b {
            let _ = self.graph.update_edge(a, b, ());
        }
    }

    pub fn unlink(&mut self, a: GridGraphNodeIndex, b: GridGraphNodeIndex) {

        if let Some(edge_index) = self.graph.find_edge(a, b) {
            // This will invalidate the last edge index in the graph, which is fine as we
            // are not storing them for any reason.
            self.graph.remove_edge(edge_index);
        }
    }

    /// Cell nodes that are linked to a particular node by a passage.
    pub fn links(&self, node_index: GridGraphNodeIndex) -> Vec<GridGraphNodeIndex> {
        self.graph
            .edges(node_index)
            .map(|index_edge_data_pair| index_edge_data_pair.0.clone())
            .collect()
    }

    /// Cell nodes that are to the North, South, East or West of a particular node, but not
    /// necessarily linked by a passage.
    pub fn neighbours(&self, node_index: GridGraphNodeIndex) -> Vec<GridGraphNodeIndex> {

        if let Some(adj) = self.graph.node_weight(node_index) {
            vec![adj.north, adj.south, adj.east, adj.west]
                .iter()
                .filter(|&maybe_adj| maybe_adj.is_some())
                .map(|&opt| opt.unwrap().clone())
                .collect()
        } else {
            vec![]
        }
    }
}

fn offset_coordinate(coord: &GridCoordinate, dir: GridDirection) -> GridCoordinate {
    let (x, y) = (coord.x, coord.y);
    match dir {
        GridDirection::North => GridCoordinate { x: x, y: y - 1 },
        GridDirection::South => GridCoordinate { x: x, y: y + 1 },
        GridDirection::East => GridCoordinate { x: x + 1, y: y },
        GridDirection::West => GridCoordinate { x: x - 1, y: y },
    }
}

fn is_valid_coordinate(coord: &GridCoordinate, dimension_size: GridIndexType) -> bool {
    if coord.x < 0 || coord.y < 0 {
        return false;
    }
    let (x, y) = (coord.x, coord.y);
    if x >= dimension_size as isize || y >= dimension_size as isize {
        return false;
    }
    true
}

fn to_1d_index(coord: &GridCoordinate, dimension_size: GridIndexType) -> GridIndexType {
    ((coord.y * dimension_size as isize) + coord.x) as GridIndexType
}

fn to_grid_coordinate(one_dimensional_index: usize,
                      dimension_size: GridIndexType)
                      -> GridCoordinate {
    let y = one_dimensional_index / dimension_size as usize;
    let x = one_dimensional_index - (y * dimension_size as usize);
    GridCoordinate {
        x: x as isize,
        y: y as isize,
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use itertools::Itertools; // a trait

    #[test]
    fn neighbour_cells() {
        let g = SquareGrid::new(10);

        let check_expected_neighbours = |node_index, vec_expected_neighbour_indices: Vec<usize>| {
            let node_indices = g.neighbours(GridGraphNodeIndex::new(node_index))
                                .into_iter()
                                .sorted();
            let expected_indices =
                vec_expected_neighbour_indices.into_iter()
                                              .sorted()
                                              .into_iter()
                                              .map(|index: usize| GridGraphNodeIndex::new(index))
                                              .collect::<Vec<GridGraphNodeIndex>>();
            assert_eq!(node_indices, expected_indices);
        };

        // corners
        check_expected_neighbours(0, vec![1, 10]);
        check_expected_neighbours(9, vec![8, 19]);
        check_expected_neighbours(90, vec![80, 91]);
        check_expected_neighbours(99, vec![89, 98]);

        // side element examples
        check_expected_neighbours(10, vec![0, 11, 20]);
        check_expected_neighbours(1, vec![0, 2, 11]);
        check_expected_neighbours(80, vec![70, 81, 90]);
        check_expected_neighbours(89, vec![79, 88, 99]);
        check_expected_neighbours(19, vec![9, 18, 29]);
        check_expected_neighbours(8, vec![7, 9, 18]);

        // Some place with 4 neighbours inside the grid
        check_expected_neighbours(12, vec![2, 11, 13, 22]);
    }

    #[test]
    fn random_cell() {
        let g = SquareGrid::new(4);
        let cells_count = 4 * 4;
        for _ in 0..1000 {
            let grid_index = g.random_cell();
            assert!(grid_index.index() >= 0 && grid_index.index() < cells_count);
        }
    }

    #[test]
    fn linking_cells() {
        let mut g = SquareGrid::new(4);
        let a = GridGraphNodeIndex::new(0);
        let b = GridGraphNodeIndex::new(1);
        let c = GridGraphNodeIndex::new(2);

        // I'd rather use a closure, but it needs to borrow the graph immutably until
        // it goes out of scope
        // Passing the graph in allows me to use a plain function:
        // let links_sorted = |n: GridGraphNodeIndex, g: &SquareGrid| -> Vec<GridGraphNodeIndex> {
        //     g.links(n).into_iter().sorted()
        // };
        // but it's uglier in that I need to explicitly pass in the SquareGrid ref all the time
        macro_rules! links_sorted {
            ($x:expr) => (g.links($x).into_iter().sorted())
        }

        // a, b and c start with no links
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);

        g.link(a, b);
        // a - b linked bi-directionally
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a]);

        g.link(b, c);
        // a - b still linked bi-directionally after linking b - c
        // b linked to a & c bi-directionally
        // c linked to b bi-directionally
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // add the same link - ensuring we can't add parallel links
        g.link(a, b);

        // a - b still linked bi-directionally after updating exist link
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);

        // a - b unlinked
        // b still linked to c bi-directionally
        g.unlink(a, b);
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // a, b and c start all unlinked again
        g.unlink(b, c);
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);

        // Deny cycle - self links
        g.link(a, a);
        assert_eq!(links_sorted!(a), vec![]);
    }
}
<commit_msg>is_neighbour grid method (to test). filter_map vs map + filter on iterators.<commit_after>use petgraph::{Graph, Undirected};
use rand;
use rand::distributions::{IndependentSample, Range};

pub type GridIndexType = u16;
pub type GridGraphNodeIndex = ::petgraph::graph::NodeIndex<GridIndexType>;

pub enum GridDirection {
    North,
    South,
    East,
    West,
}

struct AdjacentCells {
    north: Option<GridGraphNodeIndex>,
    south: Option<GridGraphNodeIndex>,
    east: Option<GridGraphNodeIndex>,
    west: Option<GridGraphNodeIndex>,
}
impl AdjacentCells {
    fn new() -> Self {
        AdjacentCells {
            north: None,
            south: None,
            east: None,
            west: None,
        }
    }
}

#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
struct GridCoordinate {
    pub x: isize,
    pub y: isize,
}

pub struct SquareGrid {
    /// For mapping a 2D gridcoordinate (converted to a 1D index) to a graph node index.
    grid_coordinate_to_node_index_lookup: Vec<GridGraphNodeIndex>,
    graph: Graph<AdjacentCells, (), Undirected, GridIndexType>,
    dimension_size: GridIndexType,
}

impl SquareGrid {
    pub fn new(dimension_size: GridIndexType) -> SquareGrid {

        let cells_count = dimension_size * dimension_size;
        let nodes_count_hint = cells_count as usize;
        let edges_count_hint = 4 * cells_count as usize - 4 * dimension_size as usize; // Probably overkill, but don't want any capacity panics

        let mut grid = SquareGrid {
            grid_coordinate_to_node_index_lookup: Vec::with_capacity(nodes_count_hint),
            graph: Graph::with_capacity(nodes_count_hint, edges_count_hint),
            dimension_size: dimension_size,
        };

        // Although it does seem to, there is no guarantee that the index values are monotonically increasing when calling graph.add_node
        // So we maintain our own symbol table mapping gridcoordinates converted to 1d (the implicit Vec indices) to the NodeIndexes created
        // by adding a node to the graph.
        for _ in 0..cells_count {
            let node_index = grid.graph.add_node(AdjacentCells::new());
            grid.grid_coordinate_to_node_index_lookup.push(node_index);
        }

        for (index, node_index) in grid.grid_coordinate_to_node_index_lookup.iter().enumerate() {

            let coord = to_grid_coordinate(index, dimension_size);

            // Limit the lifetime of the find_neighbour_nodeindex closure so we don't get a grid
            // borrow conflict when mutating the adjacent_cells
            let (north_index, south_index, east_index, west_index) = {
                let find_neighbour_nodeindex = |dir: GridDirection| -> Option<GridGraphNodeIndex> {
                    let neighbour_coord = offset_coordinate(&coord, dir);
                    if is_valid_coordinate(&neighbour_coord, dimension_size) {
                        let node_indices_index = to_1d_index(&neighbour_coord, dimension_size);
                        let node_index =
                            grid.grid_coordinate_to_node_index_lookup[node_indices_index as usize];
                        Some(node_index)
                    } else {
                        None
                    }
                };
                (find_neighbour_nodeindex(GridDirection::North),
                 find_neighbour_nodeindex(GridDirection::South),
                 find_neighbour_nodeindex(GridDirection::East),
                 find_neighbour_nodeindex(GridDirection::West))
            };

            let adjacent_cells = grid.graph
                                     .node_weight_mut(node_index.clone())
                                     .unwrap();
            adjacent_cells.north = north_index;
            adjacent_cells.south = south_index;
            adjacent_cells.east = east_index;
            adjacent_cells.west = west_index;
        }

        grid
    }

    pub fn random_cell(&self) -> GridGraphNodeIndex {
        let random_pos = Range::new(0, self.grid_coordinate_to_node_index_lookup.len());
        let mut rng = rand::thread_rng();
        let index = random_pos.ind_sample(&mut rng);
        self.grid_coordinate_to_node_index_lookup[index]
    }

    /// Link two cells
    /// Todo - only allow links between adjacent cells? If `b` not in `g.neighbours(a)`.
    /// neighbours is a wasteful computation? It filters the None adjacents, that's all.
    /// It is so small maybe it could move return an array instead of allocating a vector on the heap
    /// then I'd have no crazy ideas about passing a pre-allocated output container.
    ///
    /// Panics if a cell does not exist.
    pub fn link(&mut self, a: GridGraphNodeIndex, b: GridGraphNodeIndex) {
        if a != b {
            let _ = self.graph.update_edge(a, b, ());
        }
    }

    /// Unlink two cells, if a link exists between them.
    pub fn unlink(&mut self, a: GridGraphNodeIndex, b: GridGraphNodeIndex) {

        if let Some(edge_index) = self.graph.find_edge(a, b) {
            // This will invalidate the last edge index in the graph, which is fine as we
            // are not storing them for any reason.
            self.graph.remove_edge(edge_index);
        }
    }

    /// Cell nodes that are linked to a particular node by a passage.
    pub fn links(&self, node_index: GridGraphNodeIndex) -> Vec<GridGraphNodeIndex> {
        self.graph
            .edges(node_index)
            .map(|index_edge_data_pair| index_edge_data_pair.0.clone())
            .collect()
    }

    /// Cell nodes that are to the North, South, East or West of a particular node, but not
    /// necessarily linked by a passage.
    pub fn neighbours(&self, node_index: GridGraphNodeIndex) -> Vec<GridGraphNodeIndex> {

        if let Some(adj) = self.graph.node_weight(node_index) {
            vec![adj.north, adj.south, adj.east, adj.west]
                .iter()
                .filter_map(|&maybe_adj| maybe_adj.clone())
                .collect()
        } else {
            vec![]
        }
    }

    fn is_neighbour(&self, a: GridGraphNodeIndex, b: GridGraphNodeIndex) -> bool {
        macro_rules! index_node_match {
            ($opt_grid_index:expr) => (if let Some(node_index) = $opt_grid_index {
                                           if node_index == b {
                                               return true;
                                           }
                                       })
        }
        if let Some(adjacents_of_a) = self.graph.node_weight(a) {
            index_node_match!(adjacents_of_a.north);
            index_node_match!(adjacents_of_a.south);
            index_node_match!(adjacents_of_a.east);
            index_node_match!(adjacents_of_a.west);
        }
        false
    }
}

fn offset_coordinate(coord: &GridCoordinate, dir: GridDirection) -> GridCoordinate {
    let (x, y) = (coord.x, coord.y);
    match dir {
        GridDirection::North => GridCoordinate { x: x, y: y - 1 },
        GridDirection::South => GridCoordinate { x: x, y: y + 1 },
        GridDirection::East => GridCoordinate { x: x + 1, y: y },
        GridDirection::West => GridCoordinate { x: x - 1, y: y },
    }
}

fn is_valid_coordinate(coord: &GridCoordinate, dimension_size: GridIndexType) -> bool {
    if coord.x < 0 || coord.y < 0 {
        return false;
    }
    let (x, y) = (coord.x, coord.y);
    if x >= dimension_size as isize || y >= dimension_size as isize {
        return false;
    }
    true
}

fn to_1d_index(coord: &GridCoordinate, dimension_size: GridIndexType) -> GridIndexType {
    ((coord.y * dimension_size as isize) + coord.x) as GridIndexType
}

fn to_grid_coordinate(one_dimensional_index: usize,
                      dimension_size: GridIndexType)
                      -> GridCoordinate {
    let y = one_dimensional_index / dimension_size as usize;
    let x = one_dimensional_index - (y * dimension_size as usize);
    GridCoordinate {
        x: x as isize,
        y: y as isize,
    }
}

#[cfg(test)]
mod test {

    use super::*;
    use itertools::Itertools; // a trait

    #[test]
    fn neighbour_cells() {
        let g = SquareGrid::new(10);

        let check_expected_neighbours = |node_index, vec_expected_neighbour_indices: Vec<usize>| {
            let node_indices = g.neighbours(GridGraphNodeIndex::new(node_index))
                                .into_iter()
                                .sorted();
            let expected_indices =
                vec_expected_neighbour_indices.into_iter()
                                              .sorted()
                                              .into_iter()
                                              .map(|index: usize| GridGraphNodeIndex::new(index))
                                              .collect::<Vec<GridGraphNodeIndex>>();
            assert_eq!(node_indices, expected_indices);
        };

        // corners
        check_expected_neighbours(0, vec![1, 10]);
        check_expected_neighbours(9, vec![8, 19]);
        check_expected_neighbours(90, vec![80, 91]);
        check_expected_neighbours(99, vec![89, 98]);

        // side element examples
        check_expected_neighbours(10, vec![0, 11, 20]);
        check_expected_neighbours(1, vec![0, 2, 11]);
        check_expected_neighbours(80, vec![70, 81, 90]);
        check_expected_neighbours(89, vec![79, 88, 99]);
        check_expected_neighbours(19, vec![9, 18, 29]);
        check_expected_neighbours(8, vec![7, 9, 18]);

        // Some place with 4 neighbours inside the grid
        check_expected_neighbours(12, vec![2, 11, 13, 22]);
    }

    #[test]
    fn random_cell() {
        let g = SquareGrid::new(4);
        let cells_count = 4 * 4;
        for _ in 0..1000 {
            let grid_index = g.random_cell();
            assert!(grid_index.index() >= 0 && grid_index.index() < cells_count);
        }
    }

    #[test]
    fn linking_cells() {
        let mut g = SquareGrid::new(4);
        let a = GridGraphNodeIndex::new(0);
        let b = GridGraphNodeIndex::new(1);
        let c = GridGraphNodeIndex::new(2);

        // I'd rather use a closure, but it needs to borrow the graph immutably until
        // it goes out of scope
        // Passing the graph in allows me to use a plain function:
        // let links_sorted = |n: GridGraphNodeIndex, g: &SquareGrid| -> Vec<GridGraphNodeIndex> {
        //     g.links(n).into_iter().sorted()
        // };
        // but it's uglier in that I need to explicitly pass in the SquareGrid ref all the time
        macro_rules! links_sorted {
            ($x:expr) => (g.links($x).into_iter().sorted())
        }

        // a, b and c start with no links
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);

        g.link(a, b);
        // a - b linked bi-directionally
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a]);

        g.link(b, c);
        // a - b still linked bi-directionally after linking b - c
        // b linked to a & c bi-directionally
        // c linked to b bi-directionally
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // add the same link - ensuring we can't add parallel links
        g.link(a, b);

        // a - b still linked bi-directionally after updating exist link
        assert_eq!(links_sorted!(a), vec![b]);
        assert_eq!(links_sorted!(b), vec![a, c]);

        // a - b unlinked
        // b still linked to c bi-directionally
        g.unlink(a, b);
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![c]);
        assert_eq!(links_sorted!(c), vec![b]);

        // a, b and c start all unlinked again
        g.unlink(b, c);
        assert_eq!(links_sorted!(a), vec![]);
        assert_eq!(links_sorted!(b), vec![]);
        assert_eq!(links_sorted!(c), vec![]);

        // Deny cycle - self links
        g.link(a, a);
        assert_eq!(links_sorted!(a), vec![]);
    }
}
<|endoftext|>


<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

// extension: .lib
// format: old-style

use std::fmt;
use std::result;
use std::str::FromStr;
use std::path::PathBuf;

// get from parent
use Result;
use util::read_file;
use parse_split_quote_aware;
use schematic;
use str_error;

/// a Kicad symbolic file
#[derive(Debug,Default)]
pub struct SymbolLib {
    /// the symbols
    pub symbols: Vec<Symbol>,
}

// DEF name reference unused text_offset draw_pinnumber draw_pinname unit_count units_locked option_flag
/// a symbol
#[derive(Debug,Clone)]
pub struct Symbol {
    /// name
    pub name: String,
    /// reference
    pub reference: String,
    /// text offset
    pub text_offset: f64,
    /// draw pinnumber
    pub draw_pinnumber: bool,
    /// draw pinname
    pub draw_pinname: bool,
    /// unit count
    pub unit_count: i64,
    /// is the unit locked
    pub unit_locked: bool,
    /// is it a power symbol
    pub is_power: bool,
    /// fields
    pub fields: Vec<Field>,
    /// aliases
    pub aliases: Vec<String>,
    /// draw
    pub draw: Vec<Draw>,
}
// F n “text” posx posy dimension orientation visibility hjustify vjustify/italic/bold “name”
// F0 "#PWR" 0 0 30 H I C CNN

/// a field
#[derive(Debug,Clone)]
pub struct Field {
    /// field number
    pub i: i64,
    /// value
    pub value: String,
    /// X coordinate
    pub x: f64,
    /// Y coordinate
    pub y: f64,
    /// dimension
    pub dimension: i64,
    /// orientation
    pub orientation: schematic::Orientation,
    /// if the field is visible
    pub visible: bool,
    /// horizontal justification
    pub hjustify: schematic::Justify,
    /// vertical justification
    pub vjustify: schematic::Justify,
    /// italic
    pub italic: bool,
    /// bold
    pub bold: bool,
    /// name of the field
    pub name: String,
}

/// a drawing
#[derive(Debug,Clone)]
pub enum Draw {
    /// a non-parsed drawing part
    Other(String),
}

impl SymbolLib {
    /// find a symbol in a symbol lib
    pub fn find<F>(&self, filter: F) -> Option<&Symbol>
        where F: Fn(&Symbol) -> bool
    {
        for symbol in &self.symbols {
            if filter(symbol) {
                return Some(symbol);
            }
        }
        None
    }
}

impl fmt::Display for SymbolLib {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        writeln!(f, "EESchema-LIBRARY Version 2.3")?;
        writeln!(f, "#encoding utf-8")?;
        writeln!(f, "#")?;
        for i in &self.symbols {
            write!(f, "{}", i)?
        }
        writeln!(f, "#End Library")
    }
}

impl Symbol {
    /// create a new symbol
    pub fn new(name: String, reference: String) -> Symbol {
        Symbol {
            name: name,
            reference: reference,
            text_offset: 0.0,
            draw_pinnumber: false,
            draw_pinname: false,
            unit_count: 1,
            unit_locked: false,
            is_power: false,
            fields: vec![],
            aliases: vec![],
            draw: vec![],
        }
    }

    /// set the name of the symbol
    pub fn set_name(&mut self, name: &str) {
        if char_at(&self.name, 0) == '~' {
            self.name = format!("~{}", name)
        } else {
            self.name = name.to_string()
        }
        let mut field = &mut self.fields[1];
        field.value = name.to_string()
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        writeln!(f, "# {}", &&self.fields[1].value)?;
        writeln!(f, "#")?;
        writeln!(f, "DEF {} {} 0 {} {} {} {} {} {}",
                     self.name, self.reference, self.text_offset,
                     if self.draw_pinnumber { "Y" } else { "N" },
                     if self.draw_pinname { "Y" } else { "N" },
                     self.unit_count,
                     if self.unit_locked { "L" } else { "F" },
                     if self.is_power { "P" } else { "N" },
                     )
            ?;
        for field in &self.fields {
            writeln!(f, "{}", field)?
        }
        if !self.aliases.is_empty() {
            write!(f, "ALIAS")?;
            for alias in &self.aliases {
                write!(f, " ")?;
                write!(f, "{}", alias)?;
            }
            writeln!(f, "")?;
        }
        writeln!(f, "DRAW")?;
        for draw in &self.draw {
            writeln!(f, "{}", draw)?
        }
        writeln!(f, "ENDDRAW")?;
        writeln!(f, "ENDDEF")?;
        writeln!(f, "#")
    }
}

impl Default for Field {
    fn default() -> Field {
        Field {
            i: 0,
            value: String::from(""),
            x: 0.0,
            y: 0.0,
            dimension: 0,
            orientation: schematic::Orientation::Horizontal,
            visible: false,
            hjustify: schematic::Justify::Center,
            vjustify: schematic::Justify::Center,
            italic: false,
            bold: false,
            name: String::from(""),
        }
    }
}

impl fmt::Display for Field {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(f, "F{} \"{}\" {} {} {} {} {} {} {}{}{}",
               self.i, self.value, self.x, self.y, self.dimension,
               self.orientation,
               if self.visible { "V" } else { "I" },
               self.hjustify, self.vjustify,
               if self.italic { "I" } else { "N" },
               if self.bold { "I" } else { "N" },
                    )
            ?;
        if self.i > 3 {
            write!(f, " \"{}\"", self.name)?
        };
        Ok(())
    }
}
impl fmt::Display for Draw {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        match *self {
            Draw::Other(ref s) => write!(f, "{}", s),
        }
    }
}

macro_rules! assume_line {
    ($s:expr, $exp:expr) => (
        if $s.eof() {
            return str_error("end of file reached".to_string())
        }
        if $s.here() != $exp {
            return str_error(format!("expected '{}', got '{}'", $exp, $s.here()))
        }
        $s.i += 1;
    )
}

#[derive(Debug)]
struct ParseState {
    i: usize,
    v: Vec<String>,
}

impl ParseState {
    fn new(v2: Vec<&str>) -> ParseState {
        ParseState {
            i: 0,
            v: v2.iter().map(|x| String::from(*x)).collect(),
        }
    }

    fn here(&self) -> String {
        (self.v[self.i]).clone()
    }

    fn next(&mut self) {
        self.i += 1;
    }

    fn eof(&self) -> bool {
        self.i >= self.v.len()
    }
}

fn assume_string(e: &'static str, s: &str) -> Result<()> {
    if *e != *s {
        return str_error(format!("expecting: {}, actually: {}", e, s));
    }
    Ok(())
}

fn i64_from_string(p: &ParseState, s: &str) -> Result<i64> {
    match i64::from_str(s) {
        Ok(i) => Ok(i),
        _ => str_error(format!("int parse error in {}; line: {}", s, p.here())),
    }
}

fn f64_from_string(p: &ParseState, s: &str) -> Result<f64> {
    match f64::from_str(s) {
        Ok(i) => Ok(i),
        _ => str_error(format!("float parse error in {}; line: {}", s, p.here())),
    }
}

fn bool_from_string(s: &str, t: &'static str, f: &'static str) -> Result<bool> {
    if &s[..] == t {
        return Ok(true);
    }
    if &s[..] == f {
        return Ok(false);
    }
    str_error(format!("unknown boolean {}, expected {} or {}", s, t, f))
}

fn char_at(s: &str, p: usize) -> char {
    let v: Vec<char> = s.chars().collect();
    v[..][p]
}

fn parse_symbol(p: &mut ParseState) -> Result<Symbol> {
    p.next(); // skip line like # name
    assume_line!(p, "#");
    let s = p.here();
    let v = &parse_split_quote_aware(&s);
    if v.len() != 10 {
        return str_error(format!("unexpected elements in {}", s));
    }
    assume_string("DEF", &v[0])?;
    let mut s = Symbol::new(v[1].clone(), v[2].clone());
    s.text_offset = f64_from_string(p, &v[4])?;
    s.draw_pinnumber = bool_from_string(&v[5], "Y", "N")?;
    s.draw_pinname = bool_from_string(&v[6], "Y", "N")?;
    s.unit_count = i64_from_string(p, &v[7])?;
    s.unit_locked = bool_from_string(&v[8], "L", "F")?;
    s.is_power = bool_from_string(&v[9], "P", "N")?;
    p.next();
    loop {
        let s2 = p.here();
        if char_at(&s2, 0) == 'F' {
            let f = parse_field(p, &s2)?;
            s.fields.push(f);
            p.next();
        } else {
            break;
        }
    }
    if &p.here() == "$FPLIST" {
        p.next();
        // skip FPLIST for now
        while !p.eof() {
            if &p.here() == "$ENDFPLIST" {
                p.next();
                break;
            }
            p.next()
        }
    }
    if p.here().starts_with("ALIAS") {
        let v = parse_split_quote_aware(&p.here());
        for alias in v.into_iter().skip(1) {
            s.aliases.push(alias)
        }
        p.next();
    }
    // TODO draw
    assume_line!(p, "DRAW");
    while !p.eof() {
        let s2 = p.here();
        if &s2 == "ENDDRAW" {
            p.next();
            break;
        }
        s.draw.push(Draw::Other(s2.clone()));
        p.next()
    }
    assume_line!(p, "ENDDEF");
    assume_line!(p, "#");
    Ok(s)
}

fn bool_from<T: PartialEq + fmt::Display>(i: T, t: T, f: T) -> Result<bool> {
    if i == t {
        return Ok(true);
    }
    if i == f {
        return Ok(false);
    }
    str_error(format!("unknown boolean {}, expected {} or {}", i, t, f))
}

// F0 "L" 0 50 40 H V C CNN
fn parse_field(p: &mut ParseState, line: &str) -> Result<Field> {
    let mut f = Field::default();
    let v = &parse_split_quote_aware(line);
    if v.len() != 9 && v.len() != 10 {
        return str_error(format!("unexpected elements in {}", line));
    }
    f.i = i64_from_string(p, &String::from(&v[0][1..]))?;
    let name = if v.len() == 10 {
        v[9].clone()
    } else {
        match f.i {
            0 => String::from("Reference"),
            1 => String::from("Value"),
            2 => String::from("Footprint"),
            3 => String::from("UserDocLink"),
            _ => return str_error("expecting name for componentfield > 3".to_string()),
        }
    };
    f.value = v[1].clone();
    f.x = f64_from_string(p, &v[2])?;
    f.y = f64_from_string(p, &v[3])?;
    f.dimension = i64_from_string(p, &v[4])?;
    f.orientation = schematic::Orientation::new(char_at(&v[5], 0))?;
    f.visible = bool_from_string(&v[6], "V", "I")?;
    f.hjustify = schematic::Justify::new(char_at(&v[7], 0))?;
    f.vjustify = schematic::Justify::new(char_at(&v[8], 0))?;
    f.italic = bool_from(char_at(&v[8], 1), 'I', 'N')?;
    f.bold = bool_from(char_at(&v[8], 2), 'B', 'N')?;
    f.name = name;
    Ok(f)
}

fn parse(s: &str) -> Result<SymbolLib> {
    let mut lib = SymbolLib::default();
    let v: Vec<&str> = s.lines().collect();
    let p = &mut ParseState::new(v);
    assume_line!(p, "EESchema-LIBRARY Version 2.3");
    assume_line!(p, "#encoding utf-8");
    assume_line!(p, "#");
    while !p.eof() {
        // println!("here: {}", &p.here());
        if &p.here() == "#End Library" {
            break;
        }
        let s = parse_symbol(p)?;
        // println!("new symbol: {}", &s);
        lib.symbols.push(s)
    }
    Ok(lib)
}

/// parse a &str to a symbol lib
pub fn parse_str(s: &str) -> Result<SymbolLib> {
    parse(s)
}

/// parse a file to a symbol lib
pub fn parse_file(filename: &PathBuf) -> Result<SymbolLib> {
    let name = filename.to_str().unwrap();
    let s = read_file(name)?;
    parse(&s[..])
}
<commit_msg>introduce Pin datastructure<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

// extension: .lib
// format: old-style

use std::fmt;
use std::result;
use std::str::FromStr;
use std::path::PathBuf;

// get from parent
use Result;
use util::read_file;
use parse_split_quote_aware;
use schematic;
use str_error;

/// a Kicad symbolic file
#[derive(Debug,Default)]
pub struct SymbolLib {
    /// the symbols
    pub symbols: Vec<Symbol>,
}

// DEF name reference unused text_offset draw_pinnumber draw_pinname unit_count units_locked option_flag
/// a symbol
#[derive(Debug,Clone)]
pub struct Symbol {
    /// name
    pub name: String,
    /// reference
    pub reference: String,
    /// text offset
    pub text_offset: f64,
    /// draw pinnumber
    pub draw_pinnumber: bool,
    /// draw pinname
    pub draw_pinname: bool,
    /// unit count
    pub unit_count: i64,
    /// is the unit locked
    pub unit_locked: bool,
    /// is it a power symbol
    pub is_power: bool,
    /// fields
    pub fields: Vec<Field>,
    /// aliases
    pub aliases: Vec<String>,
    /// draw
    pub draw: Vec<Draw>,
}
// F n “text” posx posy dimension orientation visibility hjustify vjustify/italic/bold “name”
// F0 "#PWR" 0 0 30 H I C CNN

/// a field
#[derive(Debug,Clone)]
pub struct Field {
    /// field number
    pub i: i64,
    /// value
    pub value: String,
    /// X coordinate
    pub x: f64,
    /// Y coordinate
    pub y: f64,
    /// dimension
    pub dimension: i64,
    /// orientation
    pub orientation: schematic::Orientation,
    /// if the field is visible
    pub visible: bool,
    /// horizontal justification
    pub hjustify: schematic::Justify,
    /// vertical justification
    pub vjustify: schematic::Justify,
    /// italic
    pub italic: bool,
    /// bold
    pub bold: bool,
    /// name of the field
    pub name: String,
}

/// a drawing
#[derive(Debug,Clone)]
pub enum Draw {
    /// a pin
    Pin(Pin),
    /// a non-parsed drawing part
    Other(String),
}

// U (up) D (down) R (right) L (left).
/// pin orientation
#[derive(Debug,Clone)]
pub enum PinOrientation {
    /// up
    Up,    // U
    /// down
    Down,  // D
    /// left
    Left,  // L
    /// right
    Right, // R
}

/// pin type
#[derive(Debug,Clone)]
pub enum PinType {
    /// input
    Input, // I
    /// output
    Output, // O
    /// bidi
    Bidi, // B
    /// tristate
    Tristate, // T
    /// passive
    Passive, // P
    /// unspecified
    Unspecified, // U
    /// power output
    PowerInput, // W
    /// power input
    PowerOutput, // w
    /// open collector
    OpenCollector, // C
    /// open emitter
    OpenEmitter, // E
    /// not connected
    NotConnected, // N
}

/// pin shape
#[derive(Debug,Clone)]
pub enum PinShape {
    /// line
    Line, // None (default)
    /// inverted
    Inverted, // I
    /// clock
    Clock, // C
    /// inverted clock
    InvertedClock, // CI
    /// input low
    InputLow, // L
    /// clock low
    ClockLow, // CL
    /// output low
    OutputLow, // V
    /// falling edge clock
    FallingEdgeClock, // F
    /// non logic
    NonLogic, // X
}

// X name number posx posy length orientation Snum Snom unit convert Etype [shape].
// X P1 1 -200 200 150 R 50 50 1 1 P
// X +3.3V 1 0 0 0 U 30 30 0 0 W N
/// draw a pin
#[derive(Debug,Clone)]
pub struct Pin {
    /// name of the pin
    pub name:String,
    /// number of the pin, which doesn't have to be an actual number
    pub number:String,
    /// x position of the pin
    pub x:i64,
    /// y position of the pin
    pub y:i64,
    /// length of the pin
    pub len:i64,
    /// orientation of the pin
    pub orientation:PinOrientation,
    /// pin number text size
    pub num_size:i64,
    /// pin name text size
    pub name_size:i64,
    /// unit ??
    pub unit: i64,
    /// convert ??
    pub convert: i64,
    /// pin type
    pub pin_type: PinType,
    /// pin visible
    pub pin_visible: bool,
    /// pin shape
    pub pin_shape: PinShape,

}

impl SymbolLib {
    /// find a symbol in a symbol lib
    pub fn find<F>(&self, filter: F) -> Option<&Symbol>
        where F: Fn(&Symbol) -> bool
    {
        for symbol in &self.symbols {
            if filter(symbol) {
                return Some(symbol);
            }
        }
        None
    }
}

impl fmt::Display for SymbolLib {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        writeln!(f, "EESchema-LIBRARY Version 2.3")?;
        writeln!(f, "#encoding utf-8")?;
        writeln!(f, "#")?;
        for i in &self.symbols {
            write!(f, "{}", i)?
        }
        writeln!(f, "#End Library")
    }
}

impl Symbol {
    /// create a new symbol
    pub fn new(name: String, reference: String) -> Symbol {
        Symbol {
            name: name,
            reference: reference,
            text_offset: 0.0,
            draw_pinnumber: false,
            draw_pinname: false,
            unit_count: 1,
            unit_locked: false,
            is_power: false,
            fields: vec![],
            aliases: vec![],
            draw: vec![],
        }
    }

    /// set the name of the symbol
    pub fn set_name(&mut self, name: &str) {
        if char_at(&self.name, 0) == '~' {
            self.name = format!("~{}", name)
        } else {
            self.name = name.to_string()
        }
        let mut field = &mut self.fields[1];
        field.value = name.to_string()
    }
}

impl fmt::Display for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        writeln!(f, "# {}", &&self.fields[1].value)?;
        writeln!(f, "#")?;
        writeln!(f, "DEF {} {} 0 {} {} {} {} {} {}",
                     self.name, self.reference, self.text_offset,
                     if self.draw_pinnumber { "Y" } else { "N" },
                     if self.draw_pinname { "Y" } else { "N" },
                     self.unit_count,
                     if self.unit_locked { "L" } else { "F" },
                     if self.is_power { "P" } else { "N" },
                     )
            ?;
        for field in &self.fields {
            writeln!(f, "{}", field)?
        }
        if !self.aliases.is_empty() {
            write!(f, "ALIAS")?;
            for alias in &self.aliases {
                write!(f, " ")?;
                write!(f, "{}", alias)?;
            }
            writeln!(f, "")?;
        }
        writeln!(f, "DRAW")?;
        for draw in &self.draw {
            writeln!(f, "{}", draw)?
        }
        writeln!(f, "ENDDRAW")?;
        writeln!(f, "ENDDEF")?;
        writeln!(f, "#")
    }
}

impl Default for Field {
    fn default() -> Field {
        Field {
            i: 0,
            value: String::from(""),
            x: 0.0,
            y: 0.0,
            dimension: 0,
            orientation: schematic::Orientation::Horizontal,
            visible: false,
            hjustify: schematic::Justify::Center,
            vjustify: schematic::Justify::Center,
            italic: false,
            bold: false,
            name: String::from(""),
        }
    }
}

impl fmt::Display for Field {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(f, "F{} \"{}\" {} {} {} {} {} {} {}{}{}",
               self.i, self.value, self.x, self.y, self.dimension,
               self.orientation,
               if self.visible { "V" } else { "I" },
               self.hjustify, self.vjustify,
               if self.italic { "I" } else { "N" },
               if self.bold { "I" } else { "N" },
                    )
            ?;
        if self.i > 3 {
            write!(f, " \"{}\"", self.name)?
        };
        Ok(())
    }
}
impl fmt::Display for Draw {
    fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        match *self {
            Draw::Other(ref s) => write!(f, "{}", s),
            Draw::Pin(ref p) => {
                unimplemented!();
            }
        }
    }
}

macro_rules! assume_line {
    ($s:expr, $exp:expr) => (
        if $s.eof() {
            return str_error("end of file reached".to_string())
        }
        if $s.here() != $exp {
            return str_error(format!("expected '{}', got '{}'", $exp, $s.here()))
        }
        $s.i += 1;
    )
}

#[derive(Debug)]
struct ParseState {
    i: usize,
    v: Vec<String>,
}

impl ParseState {
    fn new(v2: Vec<&str>) -> ParseState {
        ParseState {
            i: 0,
            v: v2.iter().map(|x| String::from(*x)).collect(),
        }
    }

    fn here(&self) -> String {
        (self.v[self.i]).clone()
    }

    fn next(&mut self) {
        self.i += 1;
    }

    fn eof(&self) -> bool {
        self.i >= self.v.len()
    }
}

fn assume_string(e: &'static str, s: &str) -> Result<()> {
    if *e != *s {
        return str_error(format!("expecting: {}, actually: {}", e, s));
    }
    Ok(())
}

fn i64_from_string(p: &ParseState, s: &str) -> Result<i64> {
    match i64::from_str(s) {
        Ok(i) => Ok(i),
        _ => str_error(format!("int parse error in {}; line: {}", s, p.here())),
    }
}

fn f64_from_string(p: &ParseState, s: &str) -> Result<f64> {
    match f64::from_str(s) {
        Ok(i) => Ok(i),
        _ => str_error(format!("float parse error in {}; line: {}", s, p.here())),
    }
}

fn bool_from_string(s: &str, t: &'static str, f: &'static str) -> Result<bool> {
    if &s[..] == t {
        return Ok(true);
    }
    if &s[..] == f {
        return Ok(false);
    }
    str_error(format!("unknown boolean {}, expected {} or {}", s, t, f))
}

fn char_at(s: &str, p: usize) -> char {
    let v: Vec<char> = s.chars().collect();
    v[..][p]
}

fn parse_symbol(p: &mut ParseState) -> Result<Symbol> {
    p.next(); // skip line like # name
    assume_line!(p, "#");
    let s = p.here();
    let v = &parse_split_quote_aware(&s);
    if v.len() != 10 {
        return str_error(format!("unexpected elements in {}", s));
    }
    assume_string("DEF", &v[0])?;
    let mut s = Symbol::new(v[1].clone(), v[2].clone());
    s.text_offset = f64_from_string(p, &v[4])?;
    s.draw_pinnumber = bool_from_string(&v[5], "Y", "N")?;
    s.draw_pinname = bool_from_string(&v[6], "Y", "N")?;
    s.unit_count = i64_from_string(p, &v[7])?;
    s.unit_locked = bool_from_string(&v[8], "L", "F")?;
    s.is_power = bool_from_string(&v[9], "P", "N")?;
    p.next();
    loop {
        let s2 = p.here();
        if char_at(&s2, 0) == 'F' {
            let f = parse_field(p, &s2)?;
            s.fields.push(f);
            p.next();
        } else {
            break;
        }
    }
    if &p.here() == "$FPLIST" {
        p.next();
        // skip FPLIST for now
        while !p.eof() {
            if &p.here() == "$ENDFPLIST" {
                p.next();
                break;
            }
            p.next()
        }
    }
    if p.here().starts_with("ALIAS") {
        let v = parse_split_quote_aware(&p.here());
        for alias in v.into_iter().skip(1) {
            s.aliases.push(alias)
        }
        p.next();
    }
    // TODO draw
    assume_line!(p, "DRAW");
    while !p.eof() {
        let s2 = p.here();
        if &s2 == "ENDDRAW" {
            p.next();
            break;
        }
        s.draw.push(Draw::Other(s2.clone()));
        p.next()
    }
    assume_line!(p, "ENDDEF");
    assume_line!(p, "#");
    Ok(s)
}

fn bool_from<T: PartialEq + fmt::Display>(i: T, t: T, f: T) -> Result<bool> {
    if i == t {
        return Ok(true);
    }
    if i == f {
        return Ok(false);
    }
    str_error(format!("unknown boolean {}, expected {} or {}", i, t, f))
}

// F0 "L" 0 50 40 H V C CNN
fn parse_field(p: &mut ParseState, line: &str) -> Result<Field> {
    let mut f = Field::default();
    let v = &parse_split_quote_aware(line);
    if v.len() != 9 && v.len() != 10 {
        return str_error(format!("unexpected elements in {}", line));
    }
    f.i = i64_from_string(p, &String::from(&v[0][1..]))?;
    let name = if v.len() == 10 {
        v[9].clone()
    } else {
        match f.i {
            0 => String::from("Reference"),
            1 => String::from("Value"),
            2 => String::from("Footprint"),
            3 => String::from("UserDocLink"),
            _ => return str_error("expecting name for componentfield > 3".to_string()),
        }
    };
    f.value = v[1].clone();
    f.x = f64_from_string(p, &v[2])?;
    f.y = f64_from_string(p, &v[3])?;
    f.dimension = i64_from_string(p, &v[4])?;
    f.orientation = schematic::Orientation::new(char_at(&v[5], 0))?;
    f.visible = bool_from_string(&v[6], "V", "I")?;
    f.hjustify = schematic::Justify::new(char_at(&v[7], 0))?;
    f.vjustify = schematic::Justify::new(char_at(&v[8], 0))?;
    f.italic = bool_from(char_at(&v[8], 1), 'I', 'N')?;
    f.bold = bool_from(char_at(&v[8], 2), 'B', 'N')?;
    f.name = name;
    Ok(f)
}

fn parse(s: &str) -> Result<SymbolLib> {
    let mut lib = SymbolLib::default();
    let v: Vec<&str> = s.lines().collect();
    let p = &mut ParseState::new(v);
    assume_line!(p, "EESchema-LIBRARY Version 2.3");
    assume_line!(p, "#encoding utf-8");
    assume_line!(p, "#");
    while !p.eof() {
        // println!("here: {}", &p.here());
        if &p.here() == "#End Library" {
            break;
        }
        let s = parse_symbol(p)?;
        // println!("new symbol: {}", &s);
        lib.symbols.push(s)
    }
    Ok(lib)
}

/// parse a &str to a symbol lib
pub fn parse_str(s: &str) -> Result<SymbolLib> {
    parse(s)
}

/// parse a file to a symbol lib
pub fn parse_file(filename: &PathBuf) -> Result<SymbolLib> {
    let name = filename.to_str().unwrap();
    let s = read_file(name)?;
    parse(&s[..])
}
<|endoftext|>


<commit_before>use {Errno, Error, Result};
use libc;
use void::Void;
use std::mem::drop;

#[repr(i32)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RebootMode {
    Halt = libc::RB_HALT_SYSTEM,
    kexec = libc::RB_KEXEC,
    PowerOff = libc::RB_POWER_OFF,
    Restart = libc::RB_AUTOBOOT,
    // we do not support Restart2,
    Suspend = libc::RB_SW_SUSPEND,
}

pub fn reboot(how: RebootMode) -> Result<Void> {
    unsafe {
        libc::reboot(how as libc::c_int)
    };
    Err(Error::Sys(Errno::last()))
}

/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
    let cmd = if enable {
        libc::RB_ENABLE_CAD
    } else {
        libc::RB_DISABLE_CAD
    };
    let res = unsafe {
        libc::reboot(cmd)
    };
    Errno::result(res).map(drop)
}
<commit_msg>Manually match on RebootMode::*<commit_after>use {Errno, Error, Result};
use libc;
use void::Void;
use std::mem::drop;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RebootMode {
    Halt,
    kexec,
    PowerOff,
    Restart,
    // we do not support Restart2,
    Suspend,
}

pub fn reboot(how: RebootMode) -> Result<Void> {
    let cmd = match how {
        RebootMode::Halt => libc::RB_HALT_SYSTEM,
        RebootMode::kexec => libc::RB_KEXEC,
        RebootMode::PowerOff => libc::RB_POWER_OFF,
        RebootMode::Restart => libc::RB_AUTOBOOT,
        // we do not support Restart2,
        RebootMode::Suspend => libc::RB_SW_SUSPEND,
    };
    unsafe {
        libc::reboot(cmd)
    };
    Err(Error::Sys(Errno::last()))
}

/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
///
/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
pub fn set_cad_enabled(enable: bool) -> Result<()> {
    let cmd = if enable {
        libc::RB_ENABLE_CAD
    } else {
        libc::RB_DISABLE_CAD
    };
    let res = unsafe {
        libc::reboot(cmd)
    };
    Errno::result(res).map(drop)
}
<|endoftext|>


<commit_before>use crate::prelude::*;

/// A `Brush` defines the fill pattern of shapes.
/// The syntax allows to express fill patterns in several ways:
///
/// * solid colors
/// * colors with alpha channel
/// * gradients of colors
/// * gradients with directions
/// * gradients with angles
///
/// The string declaration of a `Brush` is composed combining the following
/// syntax elements:
///
/// 1. The `color name`
/// 2. The `gradient` string
///    * the gradient type (linear, repeating-linear)
///    * gradient attributes (direction-identifier, angles, color names )
///
/// ## Examples
/// Here are some implementations with declarations of colors, degrees, orientations and directions.
///
/// ```text
/// .foreground("white")
/// .background("black")
/// .background("linear-gradient(0deg, #4b6cb7, #182848)")
/// .background("repeating-linear-gradient(0.25turn, rgba(255, 255, 0, 0.6), dodgerblue, deepskyblue)")
/// .background("linear-gradient(-90deg, hsv(201, 94%, 80.5%), steelblue)")
/// .background("linear-gradient(to top right, white, skyblue 60%, lightskyblue 80%, yellow 83%, yellow)")
/// ```
/// Read on to see how the syntax is composed.
///
/// ## Definition of a color name
/// With the given implementation you can choose between three methods
/// to define a color.
///
/// A. `color codes`
///
/// You can define the value of a color with a symbol "#" followed
/// by letters or numbers. These numbers are in hexadecimal numeral system.
/// The short variant will use 3 numbers , the long variant will use 6
/// numbers.
/// For example `#f00` will give you red. If you write `#0000ff`, you will
/// get blue.
/// To include an alpha channel, the short variant takes 4 numbers.
/// If you need a yellow with 50.2% opaque, you use `#ff08`.
/// In the long form you need 8 numbers. `#0000ff80` represents 50.2% opaque
/// (non-premultiplied) blue.
///
/// B. `color function`
///
/// Currently the unique available functions that interpret a color are
/// distincted with the keywords `rgb`, `hsv`, `hsb`, `hsl`. There are
/// `alpha variants` as well. `hsb` is an alias to `hsv`.
/// Alpha variants are coded with the keywords `rgba`, `abgr`  or `argb`.
/// Here is an example to define a color via the function method:
/// `hsl(197, 71%, 73%)` will provide you a pretty skyblue color.
/// For `rgb` and `rgba` the range of the values are 0-255.
/// Any other keyword will use floating point integers to define the color
/// value. `hsva(0.0-360.0, 0.0-1.0, 0.0-1.0, 0.0-1.0)` is such an example.
/// In addition you can choose to use percent values (`%` sign) for the given
/// parameters.
/// When appending the `%` sign to the range parameters of the `rgb` function
/// call, the values are mapped to 0.0-100.0 (percent) or 0.0-1.0 (min/max).
/// For all other keywords (`hsv`, `hsb`, `hsl`) you are not allowed to append
/// the percent sign to the first parameter. If you append `%` to the following
/// parameters, OrbTk will interpret the values in a range between `0.0-100.0`.
///
/// C. `color name`
///
/// **WIP: The given implementation is using (utils/colors.txt). This has to be adopted!!!**
///
/// OrbTK maintains color names as constants [`utils::const_colors`]. It enables
/// you, to directly choose their string value inside the code.
/// Example color names are:
///
///  * COLOR_WHITE
///  * COLOR_RED
///  * COLOR_OLIVE
///  * COLOR_LINK_WATER
///  * COLOR_SLATE_GRAY
///
/// ## Definition of a gradient
/// The syntax of a gradient definition is structured as follows:
///
/// * Optional parameters are inside brackets (`[]`).
/// * Within braces (`{}`) you define the appropriate parameter value.
/// * The pipe (`|`) is offering mutual exclusive variants
///   e.g: degrees(deg), radians(rad) or turns(turn).
/// * Three points (`...`) refer to multiple stops.
///   They are respected when a gradient is rendered.
///
/// To understand gradient directions, imagine a line or vector that
/// starts at a given point inside the entity and points to an
/// imaginary target point within the same entity. Gradients will be
/// rendered along the choosen direction to reach its target
/// poing. Supported directions are:
///
///  * "to bottom"
///  * "to bottom left"
///  * "to bottom right"
///  * "to left"
///  * "to right"
///  * "to top
///  * "to top left"
///  * "to top right"
///
/// Displacement points tell the gradient algorithm to add
/// (`positive`) or or substract (`negative`) the given pixel numbers
/// from the original starting point.
///
/// Lets look at some examples. The first one shows the
/// structure of an angled gradient
///
/// ```text
/// [repeating-]linear-gradient({Gradient-angle}{deg|rad|turn}, ...) [{X Displacement}px {Y Displacement}px], {Color} [{Stop  position}{%|px}]
/// ```
///
/// The next example shows the structure of a gradient that will be
/// rendered in a given direction
///
/// ```text
/// [repeating-]linear-gradient({direction-identifier}, {initial color-name}, {terminating color-name}
/// ```
///
//#[cfg(feature = "nightly")]
//#[doc(include = "../colors.md")]

#[derive(Clone, PartialEq, Debug)]
pub enum Brush {
    /// Paints an area with a solid color.
    SolidColor(Color),

    /// Paints an area with a gradient.
    Gradient(Gradient),
}

impl Brush {
    pub fn is_transparent(&self) -> bool {
        match self {
            Brush::SolidColor(color) => color.a() == 0,
            _ => false,
        }
    }
}

impl From<Brush> for Color {
    fn from(b: Brush) -> Color {
        match b {
            Brush::SolidColor(color) => color,
            _ => Color::rgb(0, 0, 0),
        }
    }
}

impl From<Brush> for Gradient {
    fn from(b: Brush) -> Gradient {
        match b {
            Brush::Gradient(g) => g,
            _ => Gradient::default(),
        }
    }
}

impl Default for Brush {
    fn default() -> Self {
        Brush::SolidColor(Color::rgba(0, 0, 0, 0))
    }
}

impl From<Color> for Brush {
    fn from(c: Color) -> Brush {
        Brush::SolidColor(c)
    }
}

impl From<Gradient> for Brush {
    fn from(g: Gradient) -> Brush {
        Brush::Gradient(g)
    }
}

impl From<&str> for Brush {
    fn from(s: &str) -> Brush {
        Expression::from(s).brush().unwrap_or_default()
    }
}

impl From<String> for Brush {
    fn from(s: String) -> Brush {
        Self::from(&s[..])
    }
}

impl From<Value> for Brush {
    fn from(v: Value) -> Self {
        let value = v.get::<String>();
        Brush::from(value)
    }
}

#[cfg(test)]
mod tests {
    //  use crate::prelude::*;
    // todo: tbd after brush struct is finished
}
<commit_msg>utils: remove dead reference<commit_after>use crate::prelude::*;

/// A `Brush` defines the fill pattern of shapes.
/// The syntax allows to express fill patterns in several ways:
///
/// * solid colors
/// * colors with alpha channel
/// * gradients of colors
/// * gradients with directions
/// * gradients with angles
///
/// The string declaration of a `Brush` is composed combining the following
/// syntax elements:
///
/// 1. The `color name`
/// 2. The `gradient` string
///    * the gradient type (linear, repeating-linear)
///    * gradient attributes (direction-identifier, angles, color names )
///
/// ## Examples
/// Here are some implementations with declarations of colors, degrees, orientations and directions.
///
/// ```text
/// .foreground("white")
/// .background("black")
/// .background("linear-gradient(0deg, #4b6cb7, #182848)")
/// .background("repeating-linear-gradient(0.25turn, rgba(255, 255, 0, 0.6), dodgerblue, deepskyblue)")
/// .background("linear-gradient(-90deg, hsv(201, 94%, 80.5%), steelblue)")
/// .background("linear-gradient(to top right, white, skyblue 60%, lightskyblue 80%, yellow 83%, yellow)")
/// ```
/// Read on to see how the syntax is composed.
///
/// ## Definition of a color name
/// With the given implementation you can choose between three methods
/// to define a color.
///
/// A. `color codes`
///
/// You can define the value of a color with a symbol "#" followed
/// by letters or numbers. These numbers are in hexadecimal numeral system.
/// The short variant will use 3 numbers , the long variant will use 6
/// numbers.
/// For example `#f00` will give you red. If you write `#0000ff`, you will
/// get blue.
/// To include an alpha channel, the short variant takes 4 numbers.
/// If you need a yellow with 50.2% opaque, you use `#ff08`.
/// In the long form you need 8 numbers. `#0000ff80` represents 50.2% opaque
/// (non-premultiplied) blue.
///
/// B. `color function`
///
/// Currently the unique available functions that interpret a color are
/// distincted with the keywords `rgb`, `hsv`, `hsb`, `hsl`. There are
/// `alpha variants` as well. `hsb` is an alias to `hsv`.
/// Alpha variants are coded with the keywords `rgba`, `abgr`  or `argb`.
/// Here is an example to define a color via the function method:
/// `hsl(197, 71%, 73%)` will provide you a pretty skyblue color.
/// For `rgb` and `rgba` the range of the values are 0-255.
/// Any other keyword will use floating point integers to define the color
/// value. `hsva(0.0-360.0, 0.0-1.0, 0.0-1.0, 0.0-1.0)` is such an example.
/// In addition you can choose to use percent values (`%` sign) for the given
/// parameters.
/// When appending the `%` sign to the range parameters of the `rgb` function
/// call, the values are mapped to 0.0-100.0 (percent) or 0.0-1.0 (min/max).
/// For all other keywords (`hsv`, `hsb`, `hsl`) you are not allowed to append
/// the percent sign to the first parameter. If you append `%` to the following
/// parameters, OrbTk will interpret the values in a range between `0.0-100.0`.
///
/// C. `color name`
///
/// **WIP: The given implementation is using (utils/colors.txt). This has to be adopted!!!**
///
/// OrbTK maintains a list of color names as constants. You may
/// directly choose their string value inside the code.
///
/// Example color names are:
///
///  * COLOR_WHITE
///  * COLOR_RED
///  * COLOR_OLIVE
///  * COLOR_LINK_WATER
///  * COLOR_SLATE_GRAY
///
/// ## Definition of a gradient
/// The syntax of a gradient definition is structured as follows:
///
/// * Optional parameters are inside brackets (`[]`).
/// * Within braces (`{}`) you define the appropriate parameter value.
/// * The pipe (`|`) is offering mutual exclusive variants
///   e.g: degrees(deg), radians(rad) or turns(turn).
/// * Three points (`...`) refer to multiple stops.
///   They are respected when a gradient is rendered.
///
/// To understand gradient directions, imagine a line or vector that
/// starts at a given point inside the entity and points to an
/// imaginary target point within the same entity. Gradients will be
/// rendered along the choosen direction to reach its target
/// poing. Supported directions are:
///
///  * "to bottom"
///  * "to bottom left"
///  * "to bottom right"
///  * "to left"
///  * "to right"
///  * "to top
///  * "to top left"
///  * "to top right"
///
/// Displacement points tell the gradient algorithm to add
/// (`positive`) or or substract (`negative`) the given pixel numbers
/// from the original starting point.
///
/// Lets look at some examples. The first one shows the
/// structure of an angled gradient
///
/// ```text
/// [repeating-]linear-gradient({Gradient-angle}{deg|rad|turn}, ...) [{X Displacement}px {Y Displacement}px], {Color} [{Stop  position}{%|px}]
/// ```
///
/// The next example shows the structure of a gradient that will be
/// rendered in a given direction
///
/// ```text
/// [repeating-]linear-gradient({direction-identifier}, {initial color-name}, {terminating color-name}
/// ```
///
//#[cfg(feature = "nightly")]
//#[doc(include = "../colors.md")]

#[derive(Clone, PartialEq, Debug)]
pub enum Brush {
    /// Paints an area with a solid color.
    SolidColor(Color),

    /// Paints an area with a gradient.
    Gradient(Gradient),
}

impl Brush {
    pub fn is_transparent(&self) -> bool {
        match self {
            Brush::SolidColor(color) => color.a() == 0,
            _ => false,
        }
    }
}

impl From<Brush> for Color {
    fn from(b: Brush) -> Color {
        match b {
            Brush::SolidColor(color) => color,
            _ => Color::rgb(0, 0, 0),
        }
    }
}

impl From<Brush> for Gradient {
    fn from(b: Brush) -> Gradient {
        match b {
            Brush::Gradient(g) => g,
            _ => Gradient::default(),
        }
    }
}

impl Default for Brush {
    fn default() -> Self {
        Brush::SolidColor(Color::rgba(0, 0, 0, 0))
    }
}

impl From<Color> for Brush {
    fn from(c: Color) -> Brush {
        Brush::SolidColor(c)
    }
}

impl From<Gradient> for Brush {
    fn from(g: Gradient) -> Brush {
        Brush::Gradient(g)
    }
}

impl From<&str> for Brush {
    fn from(s: &str) -> Brush {
        Expression::from(s).brush().unwrap_or_default()
    }
}

impl From<String> for Brush {
    fn from(s: String) -> Brush {
        Self::from(&s[..])
    }
}

impl From<Value> for Brush {
    fn from(v: Value) -> Self {
        let value = v.get::<String>();
        Brush::from(value)
    }
}

#[cfg(test)]
mod tests {
    //  use crate::prelude::*;
    // todo: tbd after brush struct is finished
}
<|endoftext|>


<commit_before>use crate::*;
use std::env;
use std::path;

#[test]
pub fn context_build_tests() {
    // TODO: More tests!
    let confs = vec! [
        conf::Conf::default()
            .window_mode(conf::WindowMode::default()
            .dimensions(800.0, 600.0)),
        conf::Conf::default()
            .window_mode(conf::WindowMode::default()
            .dimensions(400.0, 400.0)),
    ];
    for conf in confs.into_iter() {
        let mut cb = ContextBuilder::new("ggez_unit_tests", "ggez").conf(conf);
        if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
            let mut path = path::PathBuf::from(manifest_dir);
            path.push("resources");
            cb = cb.add_resource_path(path);
        }
        let (c, _e) = cb.clone().build().unwrap();
        let (w, h) = graphics::drawable_size(&c);
        assert_eq!(w, cb.conf.window_mode.width.into());
        assert_eq!(h, cb.conf.window_mode.height.into());
    }
}
<commit_msg>Continuing with conf test stuff<commit_after>use crate::*;
use std::env;
use std::path;

#[test]
pub fn context_build_tests() {
    // TODO: More tests!
    let confs = vec! [
        conf::Conf::default()
            .window_mode(conf::WindowMode::default()
            .dimensions(800.0, 600.0)),
        conf::Conf::default()
            .window_mode(conf::WindowMode::default()
            .dimensions(400.0, 400.0)),
        conf::Conf::default()
            .window_mode(conf::WindowMode::default()
            .resizable(false)),
    ];
    for conf in confs.into_iter() {
        let mut cb = ContextBuilder::new("ggez_unit_tests", "ggez").conf(conf);
        if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
            let mut path = path::PathBuf::from(manifest_dir);
            path.push("resources");
            cb = cb.add_resource_path(path);
        }
        let (c, _e) = cb.clone().build().unwrap();
        let (w, h) = graphics::drawable_size(&c);
        assert_eq!(w, cb.conf.window_mode.width.into());
        assert_eq!(h, cb.conf.window_mode.height.into());
        // Can't really test whether or not the window is resizable?
    }
}
<|endoftext|>


<commit_before>//! A simple adaptive threadpool that returns a oneshot future.

use std::sync::Arc;

use crate::{OneShot, Result};

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "ios",
    )
))]
mod queue {
    use std::{
        cell::RefCell,
        collections::VecDeque,
        sync::{
            atomic::{AtomicBool, AtomicUsize, Ordering},
            Once,
        },
        time::{Duration, Instant},
    };

    use parking_lot::{Condvar, Mutex};

    use crate::{debug_delay, Lazy, OneShot};

    thread_local! {
        static WORKER: RefCell<bool> = RefCell::new(false);
    }

    fn is_worker() -> bool {
        WORKER.with(|w| *w.borrow())
    }

    pub(super) static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static IO_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static TRUNCATE_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);

    type Work = Box<dyn FnOnce() + Send + 'static>;

    pub(super) fn spawn_to<F, R>(work: F, queue: &'static Queue) -> OneShot<R>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static + Sized,
    {
        static START_THREADS: Once = Once::new();

        START_THREADS.call_once(|| {
            std::thread::Builder::new()
                .name("sled-io-thread".into())
                .spawn(|| IO_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical IO thread");

            std::thread::Builder::new()
                .name("sled-blocking-thread".into())
                .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical blocking thread");

            std::thread::Builder::new()
                .name("sled-snapshot-thread".into())
                .spawn(|| SNAPSHOT_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical snapshot thread");

            std::thread::Builder::new()
                .name("sled-truncate-thread".into())
                .spawn(|| TRUNCATE_QUEUE.perform_work(false, false))
                .expect("failed to spawn critical truncation thread");
        });

        let (promise_filler, promise) = OneShot::pair();
        let task = move || {
            promise_filler.fill((work)());
        };

        if is_worker() {
            task();
        } else {
            queue.send(Box::new(task));
        }

        promise
    }

    #[derive(Default)]
    pub(super) struct Queue {
        cv: Condvar,
        mu: Mutex<VecDeque<Work>>,
        temporary_threads: AtomicUsize,
        spawning: AtomicBool,
    }

    #[allow(unsafe_code)]
    unsafe impl Send for Queue {}

    impl Queue {
        fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
            let mut queue = self.mu.lock();

            let cutoff = Instant::now() + duration;

            while queue.is_empty() {
                let res = self.cv.wait_until(&mut queue, cutoff);
                if res.timed_out() {
                    break;
                }
            }

            queue.pop_front().map(|w| (w, queue.len()))
        }

        fn send(&self, work: Work) -> usize {
            let mut queue = self.mu.lock();
            queue.push_back(work);

            let len = queue.len();

            // having held the mutex makes this linearized
            // with the notify below.
            drop(queue);

            self.cv.notify_all();

            len
        }

        fn perform_work(&'static self, elastic: bool, temporary: bool) {
            const MAX_TEMPORARY_THREADS: usize = 16;

            WORKER.with(|w| *w.borrow_mut() = true);

            self.spawning.store(false, Ordering::SeqCst);

            let wait_limit = Duration::from_millis(100);

            let mut unemployed_loops = 0;
            while !temporary || unemployed_loops < 50 {
                // take on a bit of GC labor
                let guard = crate::pin();
                guard.flush();
                drop(guard);

                debug_delay();
                let task_opt = self.recv_timeout(wait_limit);

                if let Some((task, outstanding_work)) = task_opt {
                    // spin up some help if we're falling behind

                    let temporary_threads =
                        self.temporary_threads.load(Ordering::Acquire);

                    if elastic
                        && outstanding_work > 5
                        && temporary_threads < MAX_TEMPORARY_THREADS
                        && self
                            .spawning
                            .compare_exchange(
                                false,
                                true,
                                Ordering::SeqCst,
                                Ordering::SeqCst,
                            )
                            .is_ok()
                    {
                        self.temporary_threads.fetch_add(1, Ordering::SeqCst);
                        let spawn_res = std::thread::Builder::new()
                            .name("sled-temporary-thread".into())
                            .spawn(move || self.perform_work(false, true));
                        if let Err(e) = spawn_res {
                            log::error!(
                                "failed to spin-up temporary work thread: {:?}",
                                e
                            );
                            self.temporary_threads
                                .fetch_sub(1, Ordering::SeqCst);
                        }
                    }

                    // execute the work sent to this thread
                    (task)();

                    unemployed_loops = 0;
                } else {
                    unemployed_loops += 1;
                }
            }

            assert!(temporary);
            self.temporary_threads.fetch_sub(1, Ordering::SeqCst);
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> OneShot<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &queue::BLOCKING_QUEUE)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "ios",
    ))
))]
mod queue {
    /// This is the polyfill that just executes things synchronously.
    use crate::{OneShot, Result};

    pub(super) fn spawn_to<F, R>(work: F, _: &()) -> OneShot<R>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        // Polyfill for platforms other than those we explicitly trust to
        // perform threaded work on. Just execute a task without involving threads.
        let (promise_filler, promise) = OneShot::pair();
        promise_filler.fill((work)());
        promise
    }

    pub(super) const IO_QUEUE: () = ();
    pub(super) const BLOCKING_QUEUE: () = ();
    pub(super) const SNAPSHOT_QUEUE: () = ();
    pub(super) const TRUNCATE_QUEUE: () = ();
}

use queue::spawn_to;

pub fn truncate(config: crate::RunningConfig, at: u64) -> OneShot<Result<()>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &queue::TRUNCATE_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(pc: crate::pagecache::PageCache) -> OneShot<()> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &queue::SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> OneShot<()> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &queue::IO_QUEUE,
    )
}
<commit_msg>Add comment documenting the correctness issue of cyclical threadpool blocking<commit_after>//! A simple adaptive threadpool that returns a oneshot future.

use std::sync::Arc;

use crate::{OneShot, Result};

#[cfg(all(
    not(miri),
    any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "ios",
    )
))]
mod queue {
    use std::{
        cell::RefCell,
        collections::VecDeque,
        sync::{
            atomic::{AtomicBool, AtomicUsize, Ordering},
            Once,
        },
        time::{Duration, Instant},
    };

    use parking_lot::{Condvar, Mutex};

    use crate::{debug_delay, Lazy, OneShot};

    thread_local! {
        static WORKER: RefCell<bool> = RefCell::new(false);
    }

    fn is_worker() -> bool {
        WORKER.with(|w| *w.borrow())
    }

    pub(super) static BLOCKING_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static IO_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static SNAPSHOT_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);
    pub(super) static TRUNCATE_QUEUE: Lazy<Queue, fn() -> Queue> =
        Lazy::new(Default::default);

    type Work = Box<dyn FnOnce() + Send + 'static>;

    pub(super) fn spawn_to<F, R>(work: F, queue: &'static Queue) -> OneShot<R>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static + Sized,
    {
        static START_THREADS: Once = Once::new();

        START_THREADS.call_once(|| {
            std::thread::Builder::new()
                .name("sled-io-thread".into())
                .spawn(|| IO_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical IO thread");

            std::thread::Builder::new()
                .name("sled-blocking-thread".into())
                .spawn(|| BLOCKING_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical blocking thread");

            std::thread::Builder::new()
                .name("sled-snapshot-thread".into())
                .spawn(|| SNAPSHOT_QUEUE.perform_work(true, false))
                .expect("failed to spawn critical snapshot thread");

            std::thread::Builder::new()
                .name("sled-truncate-thread".into())
                .spawn(|| TRUNCATE_QUEUE.perform_work(false, false))
                .expect("failed to spawn critical truncation thread");
        });

        let (promise_filler, promise) = OneShot::pair();
        let task = move || {
            promise_filler.fill((work)());
        };

        if is_worker() {
            // NB this could prevent deadlocks because
            // if a threadpool thread spawns work into
            // the threadpool's queue, which it later
            // blocks on the completion of, it would be
            // possible for threadpool threads to block
            // forever on the completion of work that
            // exists in the queue but will never be
            // scheduled.
            task();
        } else {
            queue.send(Box::new(task));
        }

        promise
    }

    #[derive(Default)]
    pub(super) struct Queue {
        cv: Condvar,
        mu: Mutex<VecDeque<Work>>,
        temporary_threads: AtomicUsize,
        spawning: AtomicBool,
    }

    #[allow(unsafe_code)]
    unsafe impl Send for Queue {}

    impl Queue {
        fn recv_timeout(&self, duration: Duration) -> Option<(Work, usize)> {
            let mut queue = self.mu.lock();

            let cutoff = Instant::now() + duration;

            while queue.is_empty() {
                let res = self.cv.wait_until(&mut queue, cutoff);
                if res.timed_out() {
                    break;
                }
            }

            queue.pop_front().map(|w| (w, queue.len()))
        }

        fn send(&self, work: Work) -> usize {
            let mut queue = self.mu.lock();
            queue.push_back(work);

            let len = queue.len();

            // having held the mutex makes this linearized
            // with the notify below.
            drop(queue);

            self.cv.notify_all();

            len
        }

        fn perform_work(&'static self, elastic: bool, temporary: bool) {
            const MAX_TEMPORARY_THREADS: usize = 16;

            WORKER.with(|w| *w.borrow_mut() = true);

            self.spawning.store(false, Ordering::SeqCst);

            let wait_limit = Duration::from_millis(100);

            let mut unemployed_loops = 0;
            while !temporary || unemployed_loops < 50 {
                // take on a bit of GC labor
                let guard = crate::pin();
                guard.flush();
                drop(guard);

                debug_delay();
                let task_opt = self.recv_timeout(wait_limit);

                if let Some((task, outstanding_work)) = task_opt {
                    // spin up some help if we're falling behind

                    let temporary_threads =
                        self.temporary_threads.load(Ordering::Acquire);

                    if elastic
                        && outstanding_work > 5
                        && temporary_threads < MAX_TEMPORARY_THREADS
                        && self
                            .spawning
                            .compare_exchange(
                                false,
                                true,
                                Ordering::SeqCst,
                                Ordering::SeqCst,
                            )
                            .is_ok()
                    {
                        self.temporary_threads.fetch_add(1, Ordering::SeqCst);
                        let spawn_res = std::thread::Builder::new()
                            .name("sled-temporary-thread".into())
                            .spawn(move || self.perform_work(false, true));
                        if let Err(e) = spawn_res {
                            log::error!(
                                "failed to spin-up temporary work thread: {:?}",
                                e
                            );
                            self.temporary_threads
                                .fetch_sub(1, Ordering::SeqCst);
                        }
                    }

                    // execute the work sent to this thread
                    (task)();

                    unemployed_loops = 0;
                } else {
                    unemployed_loops += 1;
                }
            }

            assert!(temporary);
            self.temporary_threads.fetch_sub(1, Ordering::SeqCst);
        }
    }
}

/// Spawn a function on the threadpool.
pub fn spawn<F, R>(work: F) -> OneShot<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static + Sized,
{
    spawn_to(work, &queue::BLOCKING_QUEUE)
}

#[cfg(any(
    miri,
    not(any(
        windows,
        target_os = "linux",
        target_os = "macos",
        target_os = "dragonfly",
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "ios",
    ))
))]
mod queue {
    /// This is the polyfill that just executes things synchronously.
    use crate::{OneShot, Result};

    pub(super) fn spawn_to<F, R>(work: F, _: &()) -> OneShot<R>
    where
        F: FnOnce() -> R + Send + 'static,
        R: Send + 'static,
    {
        // Polyfill for platforms other than those we explicitly trust to
        // perform threaded work on. Just execute a task without involving threads.
        let (promise_filler, promise) = OneShot::pair();
        promise_filler.fill((work)());
        promise
    }

    pub(super) const IO_QUEUE: () = ();
    pub(super) const BLOCKING_QUEUE: () = ();
    pub(super) const SNAPSHOT_QUEUE: () = ();
    pub(super) const TRUNCATE_QUEUE: () = ();
}

use queue::spawn_to;

pub fn truncate(config: crate::RunningConfig, at: u64) -> OneShot<Result<()>> {
    spawn_to(
        move || {
            log::debug!("truncating file to length {}", at);
            config
                .file
                .set_len(at)
                .and_then(|_| config.file.sync_all())
                .map_err(|e| e.into())
        },
        &queue::TRUNCATE_QUEUE,
    )
}

pub fn take_fuzzy_snapshot(pc: crate::pagecache::PageCache) -> OneShot<()> {
    spawn_to(
        move || {
            if let Err(e) = pc.take_fuzzy_snapshot() {
                log::error!("failed to write snapshot: {:?}", e);
            }
        },
        &queue::SNAPSHOT_QUEUE,
    )
}

pub(crate) fn write_to_log(
    iobuf: Arc<crate::pagecache::iobuf::IoBuf>,
    iobufs: Arc<crate::pagecache::iobuf::IoBufs>,
) -> OneShot<()> {
    spawn_to(
        move || {
            let lsn = iobuf.lsn;
            if let Err(e) = iobufs.write_to_log(iobuf) {
                log::error!(
                    "hit error while writing iobuf with lsn {}: {:?}",
                    lsn,
                    e
                );

                // store error before notifying so that waiting threads will see
                // it
                iobufs.config.set_global_error(e);

                let intervals = iobufs.intervals.lock();

                // having held the mutex makes this linearized
                // with the notify below.
                drop(intervals);

                let _notified = iobufs.interval_updated.notify_all();
            }
        },
        &queue::IO_QUEUE,
    )
}
<|endoftext|>