<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::Arc;
use std::thread::{self, JoinHandle};

use model::ttable::{Entry, EvalType, TTable};
use model::{Board, Move, Outcome};

const INFINITY: i16 = 0x7FFF;
const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;

pub enum AI {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> AI {
        AI::Idle
    }

    pub fn is_idle(&self) -> bool {
        match self {
            AI::Idle => true,
            AI::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let AI::Thinking { stop_signal, .. } = self {
            stop_signal.store(true, Ordering::Relaxed);
            *self = AI::Idle;
        }
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        match self {
            AI::Idle => None,
            AI::Thinking { move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => {
                    *self = AI::Idle;
                    Some(mv)
                }
                Err(Empty) => None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_ai = mem::replace(self, AI::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let handle = thread::spawn(move || {
            if let AI::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_ai
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // Only take positions after the last irreversible move
            let mut board_list: Vec<_> = board_list
                .into_iter()
                .rev()
                .take_while(|b| b.vitals() == board.vitals())
                .collect();
            board_list.reverse();

            // 2-ply iterative deepening
            let mut moves: Vec<(Move, i16)> = board
                .generate_moves()
                .into_iter()
                .map(|mv| {
                    let mut new_board = board;
                    new_board.apply_move(&mv);

                    let score =
                        -alphabeta_negamax(&new_board, &mut board_list, NEG_INFINITY, INFINITY, 1);
                    (mv, score)
                })
                .collect();

            moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));

            let mut max_score = NEG_INFINITY;
            let mut best_move = None;
            for (mv, _) in moves {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }

                let mut new_board = board;
                new_board.apply_move(&mv);

                let score = -alphabeta_negamax(
                    &new_board,
                    &mut board_list,
                    NEG_INFINITY,
                    -max_score,
                    depth - 1,
                );
                if score > max_score {
                    max_score = score;
                    best_move = Some(mv);
                }
            }

            match best_move {
                Some(mv) => move_sender.send(mv).expect("AI failed to send Move"),
                None => panic!("AI failed to find a move"),
            }
        });

        *self = AI::Thinking {
            move_recv,
            stop_signal,
            handle,
        }
    }
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
) -> i16 {
    let set_ttable = |eval_type, score| {
        let zobrist = board.zobrist();
        TTable::set(
            zobrist,
            Entry {
                eval_type,
                depth,
                score,
                zobrist,
            },
        );
    };

    match board.outcome() {
        Outcome::Draw => {
            // This only works because the draw Outcome does not consider draw by repetition
            set_ttable(EvalType::Exact, DRAW);
            return DRAW;
        }
        Outcome::Win(color) => {
            assert_ne!(color, board.turn());
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - depth as i16;
            set_ttable(EvalType::Exact, score);
            return score;
        }
        Outcome::InProgress => {}
    }

    if let Some(entry) = TTable::get(board.zobrist()) {
        if board_list.len() >= 8 {
            if board_list.iter().filter(|&&b| b == *board).count() >= 2 {
                return DRAW;
            }
        }

        if entry.zobrist == board.zobrist() && entry.depth >= depth {
            match entry.eval_type {
                EvalType::Exact => {
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    if depth == 0 {
        evaluate(board)
    } else {
        let mut best_score = NEG_INFINITY;

        let moves = board.generate_moves();
        for mv in moves {
            let mut new_board = *board;
            new_board.apply_move(&mv);

            board_list.push(*board);
            let score = -alphabeta_negamax(&new_board, &mut board_list, -beta, -alpha, depth - 1);
            board_list.pop();

            best_score = cmp::max(score, best_score);

            if score >= beta {
                set_ttable(EvalType::Beta, score);
                return beta;
            } else if score > alpha {
                alpha = score;
            }
        }
        set_ttable(EvalType::Exact, best_score);
        alpha
    }
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn() {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<commit_msg>Fix possible lossy conversion<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::Arc;
use std::thread::{self, JoinHandle};

use model::ttable::{Entry, EvalType, TTable};
use model::{Board, Move, Outcome};

const INFINITY: i16 = 0x7FFF;
const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;

pub enum AI {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> AI {
        AI::Idle
    }

    pub fn is_idle(&self) -> bool {
        match self {
            AI::Idle => true,
            AI::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let AI::Thinking { stop_signal, .. } = self {
            stop_signal.store(true, Ordering::Relaxed);
            *self = AI::Idle;
        }
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        match self {
            AI::Idle => None,
            AI::Thinking { move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => {
                    *self = AI::Idle;
                    Some(mv)
                }
                Err(Empty) => None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_ai = mem::replace(self, AI::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let handle = thread::spawn(move || {
            if let AI::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_ai
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // Only take positions after the last irreversible move
            let mut board_list: Vec<_> = board_list
                .into_iter()
                .rev()
                .take_while(|b| b.vitals() == board.vitals())
                .collect();
            board_list.reverse();

            // 2-ply iterative deepening
            let mut moves: Vec<(Move, i16)> = board
                .generate_moves()
                .into_iter()
                .map(|mv| {
                    let mut new_board = board;
                    new_board.apply_move(&mv);

                    let score =
                        -alphabeta_negamax(&new_board, &mut board_list, NEG_INFINITY, INFINITY, 1);
                    (mv, score)
                })
                .collect();

            moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));

            let mut max_score = NEG_INFINITY;
            let mut best_move = None;
            for (mv, _) in moves {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }

                let mut new_board = board;
                new_board.apply_move(&mv);

                let score = -alphabeta_negamax(
                    &new_board,
                    &mut board_list,
                    NEG_INFINITY,
                    -max_score,
                    depth - 1,
                );
                if score > max_score {
                    max_score = score;
                    best_move = Some(mv);
                }
            }

            match best_move {
                Some(mv) => move_sender.send(mv).expect("AI failed to send Move"),
                None => panic!("AI failed to find a move"),
            }
        });

        *self = AI::Thinking {
            move_recv,
            stop_signal,
            handle,
        }
    }
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
) -> i16 {
    let set_ttable = |eval_type, score| {
        let zobrist = board.zobrist();
        TTable::set(
            zobrist,
            Entry {
                eval_type,
                depth,
                score,
                zobrist,
            },
        );
    };

    match board.outcome() {
        Outcome::Draw => {
            // This only works because the draw Outcome does not consider draw by repetition
            set_ttable(EvalType::Exact, DRAW);
            return DRAW;
        }
        Outcome::Win(color) => {
            assert_ne!(color, board.turn());
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(EvalType::Exact, score);
            return score;
        }
        Outcome::InProgress => {}
    }

    if let Some(entry) = TTable::get(board.zobrist()) {
        if board_list.len() >= 8 {
            if board_list.iter().filter(|&&b| b == *board).count() >= 2 {
                return DRAW;
            }
        }

        if entry.zobrist == board.zobrist() && entry.depth >= depth {
            match entry.eval_type {
                EvalType::Exact => {
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    if depth == 0 {
        evaluate(board)
    } else {
        let mut best_score = NEG_INFINITY;

        let moves = board.generate_moves();
        for mv in moves {
            let mut new_board = *board;
            new_board.apply_move(&mv);

            board_list.push(*board);
            let score = -alphabeta_negamax(&new_board, &mut board_list, -beta, -alpha, depth - 1);
            board_list.pop();

            best_score = cmp::max(score, best_score);

            if score >= beta {
                set_ttable(EvalType::Beta, score);
                return beta;
            } else if score > alpha {
                alpha = score;
            }
        }
        set_ttable(EvalType::Exact, best_score);
        alpha
    }
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn() {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<|endoftext|>


<commit_before>use time::precise_time_ns;
use rand;

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Id {
	time: u64,
	random: u64,
}

impl Id {
    pub fn new() -> Id {
        Id {
        	time: precise_time_ns(),
        	random: rand::random(),
        }
    }
}

<commit_msg>Fix indent<commit_after>use time::precise_time_ns;
use rand;

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct Id {
    time: u64,
    random: u64,
}

impl Id {
    pub fn new() -> Id {
        Id {
            time: precise_time_ns(),
            random: rand::random(),
        }
    }
}

<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Logical Volumes

use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeMap;

use parser::{
    LvmTextMap,
    TextMapOps,
    Entry,
    status_from_textmap,
};
use Device;
use PV;

/// A Logical Volume that is created from a Volume Group.
#[derive(Debug)]
pub struct LV {
    /// The name.
    pub name: String,
    /// The UUID.
    pub id: String,
    /// The status.
    pub status: Vec<String>,
    /// Flags.
    pub flags: Vec<String>,
    /// Created by this host.
    pub creation_host: String,
    /// Created at this Unix time.
    pub creation_time: i64,
    /// A list of the segments comprising the LV.
    pub segments: Vec<Box<segment::Segment>>,
    /// The major/minor number of the LV.
    pub device: Option<Device>,
}

impl LV {
    /// The total number of extents used by this logical volume.
    pub fn used_extents(&self) -> u64 {
        self.segments
            .iter()
            .map(|x| x.extent_count())
            .sum()
    }
}

impl PartialEq for LV {
    fn eq(&self, other: &LV) -> bool {
        self.name == other.name
    }
}

pub fn used_areas(lv: &LV) -> Vec<(Device, u64, u64)> {
    let mut v = Vec::new();
    for seg in &lv.segments {
        v.extend(seg.used_areas())
    }
    v
}

/// Construct an LV from an LvmTextMap.
pub fn from_textmap(name: &str, map: &LvmTextMap, pvs: &BTreeMap<String, PV>) -> Result<LV> {
    let err = || Error::new(Other, "lv textmap parsing error");

    let id = try!(map.string_from_textmap("id").ok_or(err()));
    let creation_host = try!(map.string_from_textmap("creation_host")
                             .ok_or(err()));
    let creation_time = try!(map.i64_from_textmap("creation_time")
                             .ok_or(err()));
    let segment_count = try!(map.i64_from_textmap("segment_count")
                             .ok_or(err()));

    let segments: Vec<_> = (0..segment_count)
        .map(|num| {
            let name = format!("segment{}", num+1);
            let seg_dict = try!(map.textmap_from_textmap(&name).ok_or(err()));
            segment::from_textmap(seg_dict, pvs)
        })
        .filter_map(|seg| seg.ok())
        .collect();

    let status = try!(status_from_textmap(map));

    let flags: Vec<_> = try!(map.list_from_textmap("flags").ok_or(err()))
        .iter()
        .filter_map(|item| match item {
            &Entry::String(ref x) => Some(x.clone()),
            _ => {None},
        })
        .collect();

    Ok(LV {
        name: name.to_string(),
        id: id.to_string(),
        status: status,
        flags: flags,
        creation_host: creation_host.to_string(),
        creation_time: creation_time,
        segments: segments,
        device: None,
    })
}

pub fn to_textmap(lv: &LV, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap {
    let mut map = LvmTextMap::new();

    map.insert("id".to_string(), Entry::String(lv.id.clone()));

    map.insert("status".to_string(),
               Entry::List(
                   Box::new(
                       lv.status
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("flags".to_string(),
               Entry::List(
                   Box::new(
                       lv.flags
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("creation_host".to_string(),
               Entry::String(lv.creation_host.clone()));
    map.insert("creation_time".to_string(),
               Entry::Number(lv.creation_time as i64));

    map.insert("segment_count".to_string(),
               Entry::Number(lv.segments.len() as i64));

    for (i, seg) in lv.segments.iter().enumerate() {
        map.insert(format!("segment{}", i+1),
                   Entry::TextMap(
                       Box::new(seg.to_textmap(dev_to_idx))));
    }

    map
}

pub mod segment {
    use std::io::Result;
    use std::io::Error;
    use std::io::ErrorKind::Other;
    use std::collections::BTreeMap;
    use std::fmt;

    use parser::{
        LvmTextMap,
        TextMapOps,
        Entry,
    };
    use PV;
    use VG;
    use Device;

    /// Used to treat segment types polymorphically
    pub trait Segment : fmt::Debug {
        /// Convert this segment to an LvmTextMap.
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap;
        /// Returns the first extent of the segment.
        fn start_extent(&self) -> u64;
        /// Returns how many extents are in the segment.
        fn extent_count(&self) -> u64;
        /// Returns which PVs the segment depends on.
        fn pv_dependencies(&self) -> Vec<Device>;
        /// Returns areas that make up the segment.
        fn used_areas(&self) -> Vec<(Device, u64, u64)>;
        /// Returns the name of the DM target that handles this segment.
        fn dm_type(&self) -> &'static str;
        /// Generates the parameters to send to DM for this segment.
        fn dm_params(&self, vg: &VG) -> String;
    }

    /// A Logical Volume Segment.
    #[derive(Debug, PartialEq, Clone)]
    pub struct StripedSegment {
        /// The first extent within the LV this segment comprises.
        pub start_extent: u64,
        /// How many extents this segment comprises
        pub extent_count: u64,
        /// Hoy many 512-byte sectors per stripe
        pub stripe_size: Option<u64>,
        /// Stripes contain the Device and the starting PV extent.
        pub stripes: Vec<(Device, u64)>,
    }

    pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                        -> Result<Box<Segment>> {
        match map.string_from_textmap("type") {
            Some("striped") => StripedSegment::from_textmap(map, pvs),
            _ => unimplemented!(),
        }
    }

    impl StripedSegment {
        pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                            -> Result<Box<Segment>> {
            let err = || Error::new(Other, "segment textmap parsing error");

            let stripe_list = try!(map.list_from_textmap("stripes").ok_or(err()));

            let mut stripes: Vec<_> = Vec::new();
            for slc in stripe_list.chunks(2) {
                let name = match &slc[0] {
                    &Entry::String(ref x) => {
                        let pv = try!(pvs.get(x).ok_or(err()));
                        pv.device
                    },
                    _ => return Err(err())
                };
                let val = match slc[1] {
                    Entry::Number(x) => x,
                    _ => return Err(err())
                };
                stripes.push((name, val as u64));
            }

            Ok(Box::new(StripedSegment {
                start_extent: try!(
                    map.i64_from_textmap("start_extent").ok_or(err())) as u64,
                extent_count: try!(
                    map.i64_from_textmap("extent_count").ok_or(err())) as u64,
                stripes: stripes,
                stripe_size: map.i64_from_textmap("start_extent").map(|x| x as u64),
            }))
        }
    }

    impl Segment for StripedSegment {
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>)
                          -> LvmTextMap {
            let mut map = LvmTextMap::new();

            map.insert("start_extent".to_string(),
                       Entry::Number(self.start_extent as i64));
            map.insert("extent_count".to_string(),
                       Entry::Number(self.extent_count as i64));
            map.insert("type".to_string(),
                       Entry::String("striped".to_string()));
            map.insert("stripe_count".to_string(),
                       Entry::Number(self.stripes.len() as i64));
            if let Some(stripe_size) = self.stripe_size {
                map.insert("stripe_size".to_string(),
                           Entry::Number(stripe_size as i64));
            }

            map.insert("stripes".to_string(),
                       Entry::List(
                           Box::new(
                               self.stripes
                                   .iter()
                                   .map(|&(k, v)| {
                                       let name = format!(
                                           "pv{}", dev_to_idx.get(&k).unwrap());
                                       vec![
                                           Entry::String(name),
                                           Entry::Number(v as i64)
                                               ]
                                           .into_iter()
                                   })
                                   .flat_map(|x| x)
                                   .collect())));
            map
        }

        fn start_extent(&self) -> u64 {
            self.start_extent
        }

        fn extent_count(&self) -> u64 {
            self.extent_count
        }

        fn pv_dependencies(&self) -> Vec<Device> {
            self.stripes.iter()
                .map(|&(dev, _)| dev)
                .collect()
        }

        // returns (device, start_extent, length)
        fn used_areas(&self) -> Vec<(Device, u64, u64)> {
            self.stripes.iter()
                .map(|&(dev, ext)| (dev, ext, self.extent_count))
                .collect()
        }

        fn dm_type(&self) -> &'static str {
            if self.stripes.len() == 1 {
                "linear"
            } else {
                "striped"
            }
        }

        fn dm_params(&self, vg: &VG) -> String {
            if self.stripes.len() == 1 {
                let (dev, start_ext) = self.stripes[0];
                let pv = vg.pv_get(dev).unwrap();
                format!("{}:{} {}", dev.major, dev.minor,
                        (start_ext * vg.extent_size()) + pv.pe_start)
            } else {
                let stripes: Vec<_> = self.stripes.iter()
                    .map(|&(dev, start_ext)| {
                        let pv = vg.pv_get(dev).unwrap();
                        format!("{}:{} {}", dev.major, dev.minor,
                                (start_ext * vg.extent_size()) + pv.pe_start)
                            })
                    .collect();

                format!("{} {} {}",
                        self.stripes.len(),
                        self.stripe_size.unwrap(),
                        stripes.join(" "))
            }
        }
    }
}
<commit_msg>LV: Move from_textmap code up and don't derive Clone for StripedSegment<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Logical Volumes

use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeMap;

use parser::{
    LvmTextMap,
    TextMapOps,
    Entry,
    status_from_textmap,
};
use Device;
use PV;

/// A Logical Volume that is created from a Volume Group.
#[derive(Debug)]
pub struct LV {
    /// The name.
    pub name: String,
    /// The UUID.
    pub id: String,
    /// The status.
    pub status: Vec<String>,
    /// Flags.
    pub flags: Vec<String>,
    /// Created by this host.
    pub creation_host: String,
    /// Created at this Unix time.
    pub creation_time: i64,
    /// A list of the segments comprising the LV.
    pub segments: Vec<Box<segment::Segment>>,
    /// The major/minor number of the LV.
    pub device: Option<Device>,
}

impl LV {
    /// The total number of extents used by this logical volume.
    pub fn used_extents(&self) -> u64 {
        self.segments
            .iter()
            .map(|x| x.extent_count())
            .sum()
    }
}

impl PartialEq for LV {
    fn eq(&self, other: &LV) -> bool {
        self.name == other.name
    }
}

pub fn used_areas(lv: &LV) -> Vec<(Device, u64, u64)> {
    let mut v = Vec::new();
    for seg in &lv.segments {
        v.extend(seg.used_areas())
    }
    v
}

/// Construct an LV from an LvmTextMap.
pub fn from_textmap(name: &str, map: &LvmTextMap, pvs: &BTreeMap<String, PV>) -> Result<LV> {
    let err = || Error::new(Other, "lv textmap parsing error");

    let id = try!(map.string_from_textmap("id").ok_or(err()));
    let creation_host = try!(map.string_from_textmap("creation_host")
                             .ok_or(err()));
    let creation_time = try!(map.i64_from_textmap("creation_time")
                             .ok_or(err()));
    let segment_count = try!(map.i64_from_textmap("segment_count")
                             .ok_or(err()));

    let segments: Vec<_> = (0..segment_count)
        .map(|num| {
            let name = format!("segment{}", num+1);
            let seg_dict = try!(map.textmap_from_textmap(&name).ok_or(err()));
            segment::from_textmap(seg_dict, pvs)
        })
        .filter_map(|seg| seg.ok())
        .collect();

    let status = try!(status_from_textmap(map));

    let flags: Vec<_> = try!(map.list_from_textmap("flags").ok_or(err()))
        .iter()
        .filter_map(|item| match item {
            &Entry::String(ref x) => Some(x.clone()),
            _ => {None},
        })
        .collect();

    Ok(LV {
        name: name.to_string(),
        id: id.to_string(),
        status: status,
        flags: flags,
        creation_host: creation_host.to_string(),
        creation_time: creation_time,
        segments: segments,
        device: None,
    })
}

pub fn to_textmap(lv: &LV, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap {
    let mut map = LvmTextMap::new();

    map.insert("id".to_string(), Entry::String(lv.id.clone()));

    map.insert("status".to_string(),
               Entry::List(
                   Box::new(
                       lv.status
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("flags".to_string(),
               Entry::List(
                   Box::new(
                       lv.flags
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("creation_host".to_string(),
               Entry::String(lv.creation_host.clone()));
    map.insert("creation_time".to_string(),
               Entry::Number(lv.creation_time as i64));

    map.insert("segment_count".to_string(),
               Entry::Number(lv.segments.len() as i64));

    for (i, seg) in lv.segments.iter().enumerate() {
        map.insert(format!("segment{}", i+1),
                   Entry::TextMap(
                       Box::new(seg.to_textmap(dev_to_idx))));
    }

    map
}

pub mod segment {
    use std::io::Result;
    use std::io::Error;
    use std::io::ErrorKind::Other;
    use std::collections::BTreeMap;
    use std::fmt;

    use parser::{
        LvmTextMap,
        TextMapOps,
        Entry,
    };
    use PV;
    use VG;
    use Device;

    /// Used to treat segment types polymorphically
    pub trait Segment : fmt::Debug {
        /// Convert this segment to an LvmTextMap.
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap;
        /// Returns the first extent of the segment.
        fn start_extent(&self) -> u64;
        /// Returns how many extents are in the segment.
        fn extent_count(&self) -> u64;
        /// Returns which PVs the segment depends on.
        fn pv_dependencies(&self) -> Vec<Device>;
        /// Returns areas that make up the segment.
        fn used_areas(&self) -> Vec<(Device, u64, u64)>;
        /// Returns the name of the DM target that handles this segment.
        fn dm_type(&self) -> &'static str;
        /// Generates the parameters to send to DM for this segment.
        fn dm_params(&self, vg: &VG) -> String;
    }

    pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                        -> Result<Box<Segment>> {
        match map.string_from_textmap("type") {
            Some("striped") => StripedSegment::from_textmap(map, pvs),
            _ => unimplemented!(),
        }
    }

    /// A striped Logical Volume Segment.
    #[derive(Debug, PartialEq)]
    pub struct StripedSegment {
        /// The first extent within the LV this segment comprises.
        pub start_extent: u64,
        /// How many extents this segment comprises
        pub extent_count: u64,
        /// Hoy many 512-byte sectors per stripe
        pub stripe_size: Option<u64>,
        /// Stripes contain the Device and the starting PV extent.
        pub stripes: Vec<(Device, u64)>,
    }

    impl StripedSegment {
        pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                            -> Result<Box<Segment>> {
            let err = || Error::new(Other, "segment textmap parsing error");

            let stripe_list = try!(map.list_from_textmap("stripes").ok_or(err()));

            let mut stripes: Vec<_> = Vec::new();
            for slc in stripe_list.chunks(2) {
                let name = match &slc[0] {
                    &Entry::String(ref x) => {
                        let pv = try!(pvs.get(x).ok_or(err()));
                        pv.device
                    },
                    _ => return Err(err())
                };
                let val = match slc[1] {
                    Entry::Number(x) => x,
                    _ => return Err(err())
                };
                stripes.push((name, val as u64));
            }

            Ok(Box::new(StripedSegment {
                start_extent: try!(
                    map.i64_from_textmap("start_extent").ok_or(err())) as u64,
                extent_count: try!(
                    map.i64_from_textmap("extent_count").ok_or(err())) as u64,
                stripes: stripes,
                stripe_size: map.i64_from_textmap("start_extent").map(|x| x as u64),
            }))
        }
    }

    impl Segment for StripedSegment {
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>)
                          -> LvmTextMap {
            let mut map = LvmTextMap::new();

            map.insert("start_extent".to_string(),
                       Entry::Number(self.start_extent as i64));
            map.insert("extent_count".to_string(),
                       Entry::Number(self.extent_count as i64));
            map.insert("type".to_string(),
                       Entry::String("striped".to_string()));
            map.insert("stripe_count".to_string(),
                       Entry::Number(self.stripes.len() as i64));
            if let Some(stripe_size) = self.stripe_size {
                map.insert("stripe_size".to_string(),
                           Entry::Number(stripe_size as i64));
            }

            map.insert("stripes".to_string(),
                       Entry::List(
                           Box::new(
                               self.stripes
                                   .iter()
                                   .map(|&(k, v)| {
                                       let name = format!(
                                           "pv{}", dev_to_idx.get(&k).unwrap());
                                       vec![
                                           Entry::String(name),
                                           Entry::Number(v as i64)
                                               ]
                                           .into_iter()
                                   })
                                   .flat_map(|x| x)
                                   .collect())));
            map
        }

        fn start_extent(&self) -> u64 {
            self.start_extent
        }

        fn extent_count(&self) -> u64 {
            self.extent_count
        }

        fn pv_dependencies(&self) -> Vec<Device> {
            self.stripes.iter()
                .map(|&(dev, _)| dev)
                .collect()
        }

        // returns (device, start_extent, length)
        fn used_areas(&self) -> Vec<(Device, u64, u64)> {
            self.stripes.iter()
                .map(|&(dev, ext)| (dev, ext, self.extent_count))
                .collect()
        }

        fn dm_type(&self) -> &'static str {
            if self.stripes.len() == 1 {
                "linear"
            } else {
                "striped"
            }
        }

        fn dm_params(&self, vg: &VG) -> String {
            if self.stripes.len() == 1 {
                let (dev, start_ext) = self.stripes[0];
                let pv = vg.pv_get(dev).unwrap();
                format!("{}:{} {}", dev.major, dev.minor,
                        (start_ext * vg.extent_size()) + pv.pe_start)
            } else {
                let stripes: Vec<_> = self.stripes.iter()
                    .map(|&(dev, start_ext)| {
                        let pv = vg.pv_get(dev).unwrap();
                        format!("{}:{} {}", dev.major, dev.minor,
                                (start_ext * vg.extent_size()) + pv.pe_start)
                            })
                    .collect();

                format!("{} {} {}",
                        self.stripes.len(),
                        self.stripe_size.unwrap(),
                        stripes.join(" "))
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate time;
extern crate timely;
extern crate differential_dataflow;

use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::progress::timestamp::RootTimestamp;

use rand::{Rng, SeedableRng, StdRng};

use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::operators::join::JoinUnsigned;
use differential_dataflow::operators::group::GroupUnsigned;
use differential_dataflow::collection::LeastUpperBound;

type Node = u32;
type Edge = (Node, Node);

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: u32 = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: u32 = std::env::args().nth(3).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().nth(4).unwrap() == "inspect";

    // define a new computational scope, in which to run BFS
    timely::execute_from_args(std::env::args().skip(5), move |computation| {
        let start = time::precise_time_s();

        // define BFS dataflow; return handles to roots and edges inputs
        let (mut graph, probe) = computation.scoped(|scope| {

            let roots = vec![(0,1)].into_iter().to_stream(scope);
            let (edge_input, graph) = scope.new_input();
            let mut result = bfs(&Collection::new(graph), &Collection::new(roots));

            if !inspect {
                result = result.filter(|_| false);
            }

            let probe = result.map(|(_,l)| l)
                              .consolidate_by(|&x| x)
                              .inspect(|x| println!("\t{:?}", x)).probe();

            (edge_input, probe.0)
        });

        let seed: &[_] = &[1, 2, 3, 4];
        let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
        let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions

        println!("performing BFS on {} nodes, {} edges:", nodes, edges);

        if computation.index() == 0 {
            // trickle edges in to dataflow
            for _ in 0..(edges/1000) {
                for _ in 0..1000 {
                    graph.send(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), 1));
                }
                computation.step();
            }
        }

        println!("loaded; elapsed: {}s", time::precise_time_s() - start);

        graph.advance_to(1);
        while probe.le(&RootTimestamp::new(0)) { computation.step(); }

        println!("stable; elapsed: {}s", time::precise_time_s() - start);

        if batch > 0 {
            let mut changes = Vec::new();
            for wave in 0.. {
                if computation.index() == 0 {
                    for _ in 0..batch {
                        changes.push(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), 1));
                        changes.push(((rng2.gen_range(0, nodes), rng2.gen_range(0, nodes)),-1));
                    }
                }

                let start = time::precise_time_s();
                let round = *graph.epoch();
                if computation.index() == 0 {
                    while let Some(change) = changes.pop() {
                        graph.send(change);
                    }
                }
                graph.advance_to(round + 1);

                while probe.le(&RootTimestamp::new(round)) { computation.step(); }

                if computation.index() == 0 {
                    println!("wave {}: avg {}", wave, (time::precise_time_s() - start) / (batch as f64));
                }
            }
        }
    });
}

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn bfs<G: Scope>(edges: &Collection<G, Edge>, roots: &Collection<G, Node>) -> Collection<G, (Node, u32)>
where G::Timestamp: LeastUpperBound {

    // initialize roots as reaching themselves at distance 0
    let nodes = roots.map(|x| (x, 0));

    // repeatedly update minimal distances each node can be reached from each root
    nodes.iterate(|inner| {

        let edges = edges.enter(&inner.scope());
        let nodes = nodes.enter(&inner.scope());

        inner.join_map_u(&edges, |_k,l,d| (*d, l+1))
             .concat(&nodes)
             .group_u(|_, s, t| t.push((*s.peek().unwrap().0, 1)))
     })
}
<commit_msg>added experimental shared state version; not better yet<commit_after>extern crate rand;
extern crate time;
extern crate timely;
extern crate timely_sort;
extern crate differential_dataflow;
extern crate vec_map;

use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::progress::timestamp::RootTimestamp;
use timely_sort::Unsigned;

use rand::{Rng, SeedableRng, StdRng};
use vec_map::VecMap;

use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::operators::join::JoinUnsigned;
use differential_dataflow::operators::join::JoinArranged;
use differential_dataflow::operators::group::GroupUnsigned;
use differential_dataflow::operators::group::GroupArranged;
use differential_dataflow::collection::LeastUpperBound;

use differential_dataflow::operators::iterate::Variable;

type Node = u32;
type Edge = (Node, Node);

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: u32 = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: u32 = std::env::args().nth(3).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().nth(4).unwrap() == "inspect";

    // define a new computational scope, in which to run BFS
    timely::execute_from_args(std::env::args().skip(5), move |computation| {
        let start = time::precise_time_s();

        // define BFS dataflow; return handles to roots and edges inputs
        let (mut graph, probe) = computation.scoped(|scope| {

            let roots = vec![(0,1)].into_iter().to_stream(scope);
            let (edge_input, graph) = scope.new_input();
            let mut result = bfs(&Collection::new(graph), &Collection::new(roots));

            if !inspect {
                result = result.filter(|_| false);
            }

            let probe = result.map(|(_,l)| l)
                              .consolidate_by(|&x| x)
                              .inspect(|x| println!("\t{:?}", x)).probe();

            (edge_input, probe.0)
        });

        let seed: &[_] = &[1, 2, 3, 4];
        let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
        let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions

        println!("performing BFS on {} nodes, {} edges:", nodes, edges);

        if computation.index() == 0 {
            // trickle edges in to dataflow
            for _ in 0..(edges/1000) {
                for _ in 0..1000 {
                    graph.send(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), 1));
                }
                computation.step();
            }
        }

        println!("loaded; elapsed: {}s", time::precise_time_s() - start);

        graph.advance_to(1);
        while probe.le(&RootTimestamp::new(0)) { computation.step(); }

        println!("stable; elapsed: {}s", time::precise_time_s() - start);

        if batch > 0 {
            let mut changes = Vec::new();
            for wave in 0.. {
                if computation.index() == 0 {
                    for _ in 0..batch {
                        changes.push(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), 1));
                        changes.push(((rng2.gen_range(0, nodes), rng2.gen_range(0, nodes)),-1));
                    }
                }

                let start = time::precise_time_s();
                let round = *graph.epoch();
                if computation.index() == 0 {
                    while let Some(change) = changes.pop() {
                        graph.send(change);
                    }
                }
                graph.advance_to(round + 1);

                while probe.le(&RootTimestamp::new(round)) { computation.step(); }

                if computation.index() == 0 {
                    println!("wave {}: avg {}", wave, (time::precise_time_s() - start) / (batch as f64));
                }
            }
        }
    });
}

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn bfs<G: Scope>(edges: &Collection<G, Edge>, roots: &Collection<G, Node>) -> Collection<G, (Node, u32)>
where G::Timestamp: LeastUpperBound {

    // initialize roots as reaching themselves at distance 0
    let nodes = roots.map(|x| (x, 0));

    // repeatedly update minimal distances each node can be reached from each root
    nodes.iterate(|inner| {

        let edges = edges.enter(&inner.scope());
        let nodes = nodes.enter(&inner.scope());

        inner.join_map_u(&edges, |_k,l,d| (*d, l+1))
             .concat(&nodes)
             .group_u(|_, s, t| t.push((*s.peek().unwrap().0, 1)))
     })
}

// Experimental implementation using arrangement to share the output of group_u with the input to join_map_u.

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn bfs2<G: Scope>(edges: &Collection<G, Edge>, roots: &Collection<G, Node>) -> Collection<G, (Node, u32)>
where G::Timestamp: LeastUpperBound {

    // initialize roots as reaching themselves at distance 0
    let nodes = roots.map(|x| (x, 0));

    // repeatedly update minimal distances each node can be reached from each root
    nodes.scope().scoped(|scope| {

        let edges = edges.enter(scope);
        let nodes2 = nodes.enter(scope);

        let variable = Variable::from(nodes.enter(scope));

        let arranged = variable.concat(&nodes2)
                               .arrange_by_key(|k| k.as_u64(), |x| (VecMap::new(), x))
                               .group(|k| k.as_u64(), |x| (VecMap::new(), x), |_, s, t| t.push((*s.peek().unwrap().0, 1)));

        let result = arranged.as_collection().leave();

        variable.set(&arranged.join(&edges.arrange_by_key(|k| k.as_u64(), |x| (VecMap::new(), x)), |_k,l,d| (*d, l+1)));

        result
    })
}
<|endoftext|>


<commit_before>use std::{mem, slice, cmp, ptr};
use allocator::*;

use super::entry_location::*;

/// If the entry is an alias, contents_size is the number
/// of segments in the alias. If it's an entry, contents_size
/// represents the number of bytes in the entry.
#[derive(Clone, PartialEq, Debug)]
#[repr(C)]
pub struct ByteStringEntry {
    entry_type: MemType,
    contents_size: usize,
    // contents_size bytes of data
}

// Lexicographically compare the entries pointed to by e1 and e2
pub fn cmp(e1: &EntryLocation, e2: &EntryLocation, p: &Pool) -> cmp::Ordering {
    get_iter(e1, p).cmp(get_iter(e2, p))
}

/// Decrement the ref count for the given byte string
/// Follow aliases to release all their byte strings
pub fn release_byte_string(entry: &EntryLocation, pool: &Pool) {
    match get_entry_header(entry, pool).entry_type {
        MemType::Entry => {
            pool.release(entry.page_index);
        },
        MemType::Deleted => {},
        MemType::Alias => {
            for e in get_aliased_entries(entry, pool) {
                release_byte_string(e, pool);
            }
            // Once we've followed and release all the aliased
            // strings, release this alias.
            pool.release(entry.page_index);
        },
        _ => {},
    }
}

/// Get the header for the entry pointed to by the given location
fn get_entry_header<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a mut ByteStringEntry {
    pool[entry.page_index].transmute_segment_mut::<ByteStringEntry>(entry.offset)
}

/// Returns a slice of the entries which are aliased by the given entry
pub fn get_aliased_entries<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a[EntryLocation] {
    let header = get_entry_header(entry, pool);
    assert_eq!(MemType::Alias, header.entry_type);

    let start = entry.offset + mem::size_of::<ByteStringEntry>();
    let start_ptr: *const u8 = &pool[entry.page_index][start];

    unsafe {
        let start_ptr: *const EntryLocation = mem::transmute(start_ptr);
        slice::from_raw_parts(start_ptr, header.contents_size)
    }
}

/// Allocate a new byte string in the given page and populate it with the
/// given contents. Returns Ok if the operation succeeded and Err if the
/// page does not have capacity for the value.
pub fn alloc_with_contents(page_index: usize, contents: &[u8], pool: &Pool) -> Result<EntryLocation, &'static str> {
    let page = &pool[page_index];
    let offset = next_free_offset(page);
    let header_size = mem::size_of::<ByteStringEntry>();

    // TODO add support for aliasing memory
    if mem::size_of::<Page>() - offset < header_size + contents.len() {
        Err("Not enough room")
    } else {
        let mut header = page.transmute_segment_mut::<ByteStringEntry>(offset);
        header.entry_type = MemType::Entry;
        header.contents_size = contents.len();

        unsafe {
            let to = &mut page.borrow_mut()[offset+header_size] as *mut u8;
            let from = contents.as_ptr();
            ptr::copy_nonoverlapping(from, to, contents.len());
        }
        Ok(EntryLocation {
            page_index: page_index,
            offset: offset,
        })
    }
}

// Treat the given page as a set of nodes, return the remaining
// free space in the page.
pub fn free_space_entry_page(page: &Page) -> usize {
    mem::size_of::<Page>() - next_free_offset(page)
}

// Find the index of the start of free space
pub fn next_free_offset(page: &Page) -> usize {
    let mut offset = 0usize;
    loop {
        let header = page.transmute_segment::<ByteStringEntry>(offset);
        match header.entry_type {
            MemType::Entry | MemType::Alias | MemType::Deleted => {
                offset += mem::size_of::<ByteStringEntry>() + header.contents_size;
            },
            _ => break,
        }
    }
    offset
}

/// Treates the entry location as a ByteStringEntry
/// Panics if not given the correct entry
pub fn get_slice<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a[u8] {
    let header = get_entry_header(entry, pool);
    assert_eq!(MemType::Entry, header.entry_type);

    let start = entry.offset + mem::size_of::<ByteStringEntry>();
    &pool[entry.page_index][start..start+header.contents_size]
}

/// Treates the entry location as a ByteStringEntry
/// Panics if not given the correct entry
pub fn get_slice_mut<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a mut [u8] {
    let header = get_entry_header(entry, pool);
    assert_eq!(MemType::Entry, header.entry_type);

    let start = entry.offset + mem::size_of::<ByteStringEntry>();

    let page: &mut Page = pool[entry.page_index].borrow_mut();
    &mut page[start..start+header.contents_size]
}

/// Get the iter over the memory represented by the bytestring
pub fn get_iter<'a>(entry: &'a EntryLocation, pool: &'a Pool) -> ByteStringIter<'a> {
    let page = &pool[entry.page_index];
    let header = get_entry_header(entry, pool);
    match header.entry_type {
        MemType::Entry => {
            ByteStringIter {
                entry: entry,
                pool: pool,

                is_aliased: false,
                current_page: page,
                current_entry: entry,
                current_string: header,
                alias_index: 0,
                byte_index: 0,
            }
        },
        MemType::Alias => {
            let first_entry = &get_aliased_entries(entry, pool)[0];
            ByteStringIter {
                entry: entry,
                pool: pool,

                is_aliased: true,
                current_page: page,
                current_entry: first_entry,
                current_string: get_entry_header(first_entry, pool),
                alias_index: 0,
                byte_index: 0,
            }
        },
        MemType::Deleted => {
            panic!("get_iter called on a deleted entry");
        },
        _ => {
            panic!("get_iter called on a Node instead of an entry");
        },
    }
}

/// ITERATION
/// Provides byte string iters

pub struct ByteStringIter<'a> {
    entry: &'a EntryLocation,
    pool: &'a Pool,

    is_aliased: bool,
    current_page: &'a Page,
    current_entry: &'a EntryLocation,
    current_string: &'a ByteStringEntry,
    alias_index: usize,
    byte_index: usize,
}

impl <'a> Iterator for ByteStringIter<'a> {
    type Item = &'a u8;

    fn next(&mut self) -> Option<&'a u8> {
        let current_byte = &self.current_page
            [self.current_entry.offset+mem::size_of::<ByteStringEntry>()+self.byte_index];

        // Increment our count, and roll over if necessary
        self.byte_index += 1;
        if self.byte_index >= self.current_string.contents_size {
            if self.is_aliased {
                self.alias_index += 1;
                let entries = get_aliased_entries(self.entry, self.pool);
                if self.alias_index >= entries.len() {
                    return None
                }
                self.current_entry = &entries[self.alias_index];
                self.current_string = self.pool[self.current_entry.page_index]
                    .transmute_segment(self.current_entry.offset);
                self.byte_index = 0;
                self.current_page = &self.pool[self.current_entry.page_index];
            } else {
                return None
            }
        }
        Some(current_byte)
    }
}

#[test]
fn test_alloc_with_contents() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let page = &pool[page_index];

    for i in 0..10 {
        let remaining_space = mem::size_of::<Page>() - 20*i;

        assert_eq!(remaining_space, free_space_entry_page(page));
        assert_eq!(20*i, next_free_offset(page));

        let loc = alloc_with_contents(page_index, &[1u8, 2u8, 3u8, 4u8][..], &pool).unwrap();
        let entry = get_entry_header(&loc, &pool);

        assert_eq!(20*i, loc.offset);
        assert_eq!(page_index, loc.page_index);
        assert_eq!(4, entry.contents_size);

        assert_eq!([1u8, 2u8, 3u8, 4u8][..], *get_slice(&loc, &pool));
    }

    assert_eq!(3896, free_space_entry_page(page));

    // Test error case
    assert_eq!(Err(""), alloc_with_contents(page_index, &[42u8; 4097][..], &pool));
}

#[test]
fn test_free_space_entry_page() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let page = &pool[page_index];
    let mut loc = EntryLocation {
        page_index: page_index,
        offset: 0,
    };

    for i in 0..10 {
        let remaining_space = mem::size_of::<Page>() - 36*i;
        assert_eq!(remaining_space, free_space_entry_page(page));
        assert_eq!(36*i, next_free_offset(page));

        let mut entry = get_entry_header(&loc, &pool);
        entry.entry_type = MemType::Entry;
        entry.contents_size = 20;

        loc.offset += 36;
    }

    assert_eq!(3736, free_space_entry_page(page));
}

#[test]
fn get_aliased_string() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let p: &Page = &pool[page_index];

    let alias_loc = EntryLocation {
        page_index: page_index,
        offset: 0,
    };

    let mut alias = get_entry_header(&alias_loc, &pool);
    alias.entry_type = MemType::Alias;
    alias.contents_size = 2;

    let loc_size = mem::size_of::<EntryLocation>();
    let header_size = mem::size_of::<EntryLocation>();

    let mut entry1_loc = p.transmute_segment_mut::<EntryLocation>(header_size);
    entry1_loc.page_index = page_index;
    entry1_loc.offset = header_size+2*loc_size;
    let mut entry1 = get_entry_header(entry1_loc, &pool);
    entry1.entry_type = MemType::Entry;
    entry1.contents_size = 10;

    let mut entry2_loc = p.transmute_segment_mut::<EntryLocation>(header_size+loc_size);
    entry2_loc.page_index = page_index;
    entry2_loc.offset = 2*header_size+2*loc_size+10;
    let mut entry2 = get_entry_header(entry2_loc, &pool);
    entry2.entry_type = MemType::Entry;
    entry2.contents_size = 10;

    // Init the data as a slice
    for iv in get_slice_mut(&entry1_loc, &pool).iter_mut().enumerate() {
        *iv.1 = iv.0 as u8;
    }
    for iv in get_slice_mut(&entry2_loc, &pool).iter_mut().enumerate() {
        *iv.1 = (iv.0 as u8) + 10u8;
    }

    // check the data via iterator
    get_iter(&alias_loc, &pool).zip((0u8..)).fold(true, |b, actual_exp| {
        assert_eq!(*actual_exp.0, actual_exp.1);
        b && *actual_exp.0 == actual_exp.1
    });
}

#[test]
fn test_get_slice_mut_and_iter() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let loc = EntryLocation {
        page_index: page_index,
        offset: 0,
    };

    let mut entry = get_entry_header(&loc, &pool);
    entry.entry_type = MemType::Entry;
    entry.contents_size = 10;

    // Init the data as a slice
    for iv in get_slice_mut(&loc, &pool).iter_mut().enumerate() {
        *iv.1 = iv.0 as u8;
    }

    // check the data via iterator
    get_iter(&loc, &pool).zip((0u8..)).fold(true, |b, actual_exp| {
        assert_eq!(*actual_exp.0, actual_exp.1);
        b && *actual_exp.0 == actual_exp.1
    });
}
<commit_msg>starting to add multi-alloc for aliasing<commit_after>use std::{mem, slice, cmp, ptr};
use allocator::*;

use super::entry_location::*;

/// If the entry is an alias, contents_size is the number
/// of segments in the alias. If it's an entry, contents_size
/// represents the number of bytes in the entry.
#[derive(Clone, PartialEq, Debug)]
#[repr(C)]
pub struct ByteStringEntry {
    entry_type: MemType,
    contents_size: usize,
    // contents_size bytes of data
}

// Lexicographically compare the entries pointed to by e1 and e2
pub fn cmp(e1: &EntryLocation, e2: &EntryLocation, p: &Pool) -> cmp::Ordering {
    get_iter(e1, p).cmp(get_iter(e2, p))
}

/// Decrement the ref count for the given byte string
/// Follow aliases to release all their byte strings
pub fn release_byte_string(entry: &EntryLocation, pool: &Pool) {
    match get_entry_header(entry, pool).entry_type {
        MemType::Entry => {
            pool.release(entry.page_index);
        },
        MemType::Deleted => {},
        MemType::Alias => {
            for e in get_aliased_entries(entry, pool) {
                release_byte_string(e, pool);
            }
            // Once we've followed and release all the aliased
            // strings, release this alias.
            pool.release(entry.page_index);
        },
        _ => {},
    }
}

/// Get the header for the entry pointed to by the given location
fn get_entry_header<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a mut ByteStringEntry {
    pool[entry.page_index].transmute_segment_mut::<ByteStringEntry>(entry.offset)
}

/// Returns a slice of the entries which are aliased by the given entry
pub fn get_aliased_entries<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a[EntryLocation] {
    let header = get_entry_header(entry, pool);
    assert_eq!(MemType::Alias, header.entry_type);

    let start = entry.offset + mem::size_of::<ByteStringEntry>();
    let start_ptr: *const u8 = &pool[entry.page_index][start];

    unsafe {
        let start_ptr: *const EntryLocation = mem::transmute(start_ptr);
        slice::from_raw_parts(start_ptr, header.contents_size)
    }
}

#[inline]
fn calc_num_chunks(size: usize) -> usize {
    let spill = if size % mem::size_of::<Page>() == 0 {0} else {1};
    size / mem::size_of::<Page>() + spill
}

/// Allocate a new byte string and populate it with the
/// given contents. Returns Ok if the operation succeeded and Err if the
/// allocation failed
pub fn alloc_with_contents(page_hint: usize, contents: &[u8], pool: &Pool) -> Result<EntryLocation, &'static str> {
    let page = &pool[page_hint];
    let offset = next_free_offset(page);
    let header_size = mem::size_of::<ByteStringEntry>();
    let free_space = mem::size_of::<Page>() - offset;

    let required_space = header_size + contents.len();

    // Check to see if we can append to the given page.
    if mem::size_of::<Page>() - offset >= header_size + contents.len() {
        append_to_with_contents(page_hint, contents, pool)
    } else if required_space > mem::size_of::<Page>() {
        let num_chunks = calc_num_chunks(contents.len());

        let alias_header = alias_alloc_with_contents(contents, pool);

        if free_space >= header_size { // if we can, at least put the alias in

        }

        Ok(EntryLocation {
            page_index: page_hint,
            offset: offset,
        })
    } else {

    }
}

/// Allocate a new byte string in the given page and populate it with the
/// given contents. Returns Ok if the operation succeeded and Err if the
/// page does not have capacity for the value.
pub fn append_to_with_contents(page_index: usize, contents: &[u8], pool: &Pool) -> Result<EntryLocation, &'static str> {
    let page = &pool[page_index];
    let offset = next_free_offset(page);
    let header_size = mem::size_of::<ByteStringEntry>();

    // TODO add support for aliasing memory
    if mem::size_of::<Page>() - offset < header_size + contents.len() {
        Err("Not enough room")
    } else {
        let mut header = page.transmute_segment_mut::<ByteStringEntry>(offset);
        header.entry_type = MemType::Entry;
        header.contents_size = contents.len();

        unsafe {
            let to = &mut page.borrow_mut()[offset+header_size] as *mut u8;
            let from = contents.as_ptr();
            ptr::copy_nonoverlapping(from, to, contents.len());
        }
        Ok(EntryLocation {
            page_index: page_index,
            offset: offset,
        })
    }
}

// Treat the given page as a set of nodes, return the remaining
// free space in the page.
pub fn free_space_entry_page(page: &Page) -> usize {
    mem::size_of::<Page>() - next_free_offset(page)
}

// Find the index of the start of free space
pub fn next_free_offset(page: &Page) -> usize {
    let mut offset = 0usize;
    loop {
        let header = page.transmute_segment::<ByteStringEntry>(offset);
        match header.entry_type {
            MemType::Entry | MemType::Alias | MemType::Deleted => {
                offset += mem::size_of::<ByteStringEntry>() + header.contents_size;
            },
            _ => break,
        }
    }
    offset
}

/// Treates the entry location as a ByteStringEntry
/// Panics if not given the correct entry
pub fn get_slice<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a[u8] {
    let header = get_entry_header(entry, pool);
    assert_eq!(MemType::Entry, header.entry_type);

    let start = entry.offset + mem::size_of::<ByteStringEntry>();
    &pool[entry.page_index][start..start+header.contents_size]
}

/// Treates the entry location as a ByteStringEntry
/// Panics if not given the correct entry
pub fn get_slice_mut<'a>(entry: &EntryLocation, pool: &'a Pool) -> &'a mut [u8] {
    let header = get_entry_header(entry, pool);
    assert_eq!(MemType::Entry, header.entry_type);

    let start = entry.offset + mem::size_of::<ByteStringEntry>();

    let page: &mut Page = pool[entry.page_index].borrow_mut();
    &mut page[start..start+header.contents_size]
}

/// Get the iter over the memory represented by the bytestring
pub fn get_iter<'a>(entry: &'a EntryLocation, pool: &'a Pool) -> ByteStringIter<'a> {
    let page = &pool[entry.page_index];
    let header = get_entry_header(entry, pool);
    match header.entry_type {
        MemType::Entry => {
            ByteStringIter {
                entry: entry,
                pool: pool,

                is_aliased: false,
                current_page: page,
                current_entry: entry,
                current_string: header,
                alias_index: 0,
                byte_index: 0,
            }
        },
        MemType::Alias => {
            let first_entry = &get_aliased_entries(entry, pool)[0];
            ByteStringIter {
                entry: entry,
                pool: pool,

                is_aliased: true,
                current_page: page,
                current_entry: first_entry,
                current_string: get_entry_header(first_entry, pool),
                alias_index: 0,
                byte_index: 0,
            }
        },
        MemType::Deleted => {
            panic!("get_iter called on a deleted entry");
        },
        _ => {
            panic!("get_iter called on a Node instead of an entry");
        },
    }
}

/// ITERATION
/// Provides byte string iters

pub struct ByteStringIter<'a> {
    entry: &'a EntryLocation,
    pool: &'a Pool,

    is_aliased: bool,
    current_page: &'a Page,
    current_entry: &'a EntryLocation,
    current_string: &'a ByteStringEntry,
    alias_index: usize,
    byte_index: usize,
}

impl <'a> Iterator for ByteStringIter<'a> {
    type Item = &'a u8;

    fn next(&mut self) -> Option<&'a u8> {
        let current_byte = &self.current_page
            [self.current_entry.offset+mem::size_of::<ByteStringEntry>()+self.byte_index];

        // Increment our count, and roll over if necessary
        self.byte_index += 1;
        if self.byte_index >= self.current_string.contents_size {
            if self.is_aliased {
                self.alias_index += 1;
                let entries = get_aliased_entries(self.entry, self.pool);
                if self.alias_index >= entries.len() {
                    return None
                }
                self.current_entry = &entries[self.alias_index];
                self.current_string = self.pool[self.current_entry.page_index]
                    .transmute_segment(self.current_entry.offset);
                self.byte_index = 0;
                self.current_page = &self.pool[self.current_entry.page_index];
            } else {
                return None
            }
        }
        Some(current_byte)
    }
}

#[test]
fn test_append_to_with_contents() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let page = &pool[page_index];

    for i in 0..10 {
        let remaining_space = mem::size_of::<Page>() - 20*i;

        assert_eq!(remaining_space, free_space_entry_page(page));
        assert_eq!(20*i, next_free_offset(page));

        let loc = alloc_with_contents(page_index, &[1u8, 2u8, 3u8, 4u8][..], &pool).unwrap();
        let entry = get_entry_header(&loc, &pool);

        assert_eq!(20*i, loc.offset);
        assert_eq!(page_index, loc.page_index);
        assert_eq!(4, entry.contents_size);

        assert_eq!([1u8, 2u8, 3u8, 4u8][..], *get_slice(&loc, &pool));
    }

    assert_eq!(3896, free_space_entry_page(page));

    // Test error case
    assert_eq!(Err("Not enough room"),
        alloc_with_contents(page_index, &[42u8; 4097][..], &pool));
}

#[test]
fn test_append_to_with_contents() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let page = &pool[page_index];

    for i in 0..10 {
        let remaining_space = mem::size_of::<Page>() - 20*i;

        assert_eq!(remaining_space, free_space_entry_page(page));
        assert_eq!(20*i, next_free_offset(page));

        let loc = alloc_with_contents(page_index, &[1u8, 2u8, 3u8, 4u8][..], &pool).unwrap();
        let entry = get_entry_header(&loc, &pool);

        assert_eq!(20*i, loc.offset);
        assert_eq!(page_index, loc.page_index);
        assert_eq!(4, entry.contents_size);

        assert_eq!([1u8, 2u8, 3u8, 4u8][..], *get_slice(&loc, &pool));
    }

    assert_eq!(3896, free_space_entry_page(page));

    // Test error case
    assert_eq!(Err("Not enough room"),
        alloc_with_contents(page_index, &[42u8; 4097][..], &pool));
}

#[test]
fn test_free_space_entry_page() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let page = &pool[page_index];
    let mut loc = EntryLocation {
        page_index: page_index,
        offset: 0,
    };

    for i in 0..10 {
        let remaining_space = mem::size_of::<Page>() - 36*i;
        assert_eq!(remaining_space, free_space_entry_page(page));
        assert_eq!(36*i, next_free_offset(page));

        let mut entry = get_entry_header(&loc, &pool);
        entry.entry_type = MemType::Entry;
        entry.contents_size = 20;

        loc.offset += 36;
    }

    assert_eq!(3736, free_space_entry_page(page));
}

#[test]
fn get_aliased_string() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let p: &Page = &pool[page_index];

    let alias_loc = EntryLocation {
        page_index: page_index,
        offset: 0,
    };

    let mut alias = get_entry_header(&alias_loc, &pool);
    alias.entry_type = MemType::Alias;
    alias.contents_size = 2;

    let loc_size = mem::size_of::<EntryLocation>();
    let header_size = mem::size_of::<EntryLocation>();

    let mut entry1_loc = p.transmute_segment_mut::<EntryLocation>(header_size);
    entry1_loc.page_index = page_index;
    entry1_loc.offset = header_size+2*loc_size;
    let mut entry1 = get_entry_header(entry1_loc, &pool);
    entry1.entry_type = MemType::Entry;
    entry1.contents_size = 10;

    let mut entry2_loc = p.transmute_segment_mut::<EntryLocation>(header_size+loc_size);
    entry2_loc.page_index = page_index;
    entry2_loc.offset = 2*header_size+2*loc_size+10;
    let mut entry2 = get_entry_header(entry2_loc, &pool);
    entry2.entry_type = MemType::Entry;
    entry2.contents_size = 10;

    // Init the data as a slice
    for iv in get_slice_mut(&entry1_loc, &pool).iter_mut().enumerate() {
        *iv.1 = iv.0 as u8;
    }
    for iv in get_slice_mut(&entry2_loc, &pool).iter_mut().enumerate() {
        *iv.1 = (iv.0 as u8) + 10u8;
    }

    // check the data via iterator
    get_iter(&alias_loc, &pool).zip((0u8..)).fold(true, |b, actual_exp| {
        assert_eq!(*actual_exp.0, actual_exp.1);
        b && *actual_exp.0 == actual_exp.1
    });
}

#[test]
fn test_get_slice_mut_and_iter() {
    let mut buf = [0u8; 0x1100];
    let pool = Pool::new(&mut buf);

    let page_index = pool.alloc().unwrap();
    let loc = EntryLocation {
        page_index: page_index,
        offset: 0,
    };

    let mut entry = get_entry_header(&loc, &pool);
    entry.entry_type = MemType::Entry;
    entry.contents_size = 10;

    // Init the data as a slice
    for iv in get_slice_mut(&loc, &pool).iter_mut().enumerate() {
        *iv.1 = iv.0 as u8;
    }

    // check the data via iterator
    get_iter(&loc, &pool).zip((0u8..)).fold(true, |b, actual_exp| {
        assert_eq!(*actual_exp.0, actual_exp.1);
        b && *actual_exp.0 == actual_exp.1
    });
}
<|endoftext|>


<commit_before>use ncollide::world::CollisionGroups;

// internal reserved static group id
const STATIC_GROUP_ID: usize = 29;

/// Groups of collision used to filter which object collide with which other one.
/// nphysics use a specific group for its own purposes (i.e. the group of static objects).
/// The `group 29` is reserved and you cannot use it.
#[derive(Clone, Debug)]
pub struct RigidBodyCollisionGroups {
    collision_groups: CollisionGroups
}

impl RigidBodyCollisionGroups {
    // FIXME better documentation
    /// Creates a new `RigidBodyCollisionGroups` that enables collisions with everything except
    /// self-collision.
    #[inline]
    pub fn new_dynamic() -> RigidBodyCollisionGroups {

        let mut groups = CollisionGroups::new();
        groups.modify_whitelist(STATIC_GROUP_ID, false);

        RigidBodyCollisionGroups{
            collision_groups: groups
        }
    }

    /// Creates a new `RigidBodyCollisionGroups` that enables collisions with everything except
    /// self-collision.
    #[inline]
    pub fn new_static() -> RigidBodyCollisionGroups {

        let mut groups = CollisionGroups::new();
        groups.modify_membership(STATIC_GROUP_ID, true);
        groups.modify_blacklist(STATIC_GROUP_ID, true);

        RigidBodyCollisionGroups{
            collision_groups: groups
        }
    }

    /// Return the internal `CollisionGroups`
    #[inline]
    pub fn as_collision_groups(&self) -> &CollisionGroups {
        &self.collision_groups
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_membership(&mut self, group_id: usize, add: bool) {
        assert!(group_id != STATIC_GROUP_ID, "The group {} is reserved.", STATIC_GROUP_ID);
        self.collision_groups.modify_membership(group_id, add);
    }

    /// Adds or removes the given group from this entity whitelist.
    #[inline]
    pub fn modify_whitelist(&mut self, group_id: usize, add: bool) {
        assert!(group_id != STATIC_GROUP_ID, "The group {} is reserved.", STATIC_GROUP_ID);
        self.collision_groups.modify_whitelist(group_id, add);
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_blacklist(&mut self, group_id: usize, add: bool) {
        assert!(group_id != STATIC_GROUP_ID, "The group {} is reserved.", STATIC_GROUP_ID);
        self.collision_groups.modify_blacklist(group_id, add);
    }

    /// Enables self collision detection.
    #[inline]
    pub fn enable_self_collision(&mut self) {
        self.collision_groups.enable_self_collision();
    }

    /// Disables self collision detection.
    #[inline]
    pub fn disable_self_collision(&mut self) {
        self.collision_groups.disable_self_collision();
    }

    /// Tests if this entity is part of the given group.
    #[inline]
    pub fn is_member_of(&self, group_id: usize) -> bool {
        self.collision_groups.is_member_of(group_id)
    }

    /// Tests if the given group is whitelisted.
    #[inline]
    pub fn is_group_whitelisted(&self, group_id: usize) -> bool {
        self.collision_groups.is_group_whitelisted(group_id)
    }

    /// Tests if the given group is blacklisted.
    #[inline]
    pub fn is_group_blacklisted(&self, group_id: usize) -> bool {
        self.collision_groups.is_group_blacklisted(group_id)
    }

    /// Tests whether collisions with a given group is possible.
    ///
    /// Collision is possible if `group_id` is whitelisted but not blacklisted.
    #[inline]
    pub fn can_collide_with(&self, group_id: usize) -> bool {
        self.collision_groups.can_collide_with(group_id)
    }

    /// Tests whether two collision groups have at least one group in common.
    #[inline]
    pub fn can_collide_with_groups(&self, other: &CollisionGroups) -> bool {
        self.collision_groups.can_collide_with_groups(other)
    }

    /// Tests whether self-collision is enabled.
    #[inline]
    pub fn can_collide_with_self(&self) -> bool {
        self.collision_groups.can_collide_with_self()
    }
}
<commit_msg>[BUG FIX] STATIC_GROUP_ID correctly implemented<commit_after>use ncollide::world::CollisionGroups;

// internal reserved static group id
const STATIC_GROUP_ID: usize = 29;

/// Groups of collision used to filter which object collide with which other one.
/// nphysics use a specific group for its own purposes (i.e. the group of static objects).
/// The `group 29` is reserved and you cannot use it.
#[derive(Clone, Debug)]
pub struct RigidBodyCollisionGroups {
    collision_groups: CollisionGroups
}

impl RigidBodyCollisionGroups {
    /// Creates a new `RigidBodyCollisionGroups` that enables collisions with everything except
    /// self-collision.
    #[inline]
    pub fn new_dynamic() -> RigidBodyCollisionGroups {

        // all memberships + all whitelists activated
        let mut groups = CollisionGroups::new();

        groups.modify_membership(STATIC_GROUP_ID, false);
        groups.modify_whitelist(STATIC_GROUP_ID, false);

        RigidBodyCollisionGroups{
            collision_groups: groups
        }
    }

    /// Creates a new `RigidBodyCollisionGroups` that enables collisions with everything except
    /// self-collision.
    #[inline]
    pub fn new_static() -> RigidBodyCollisionGroups {

        // all memberships + all whitelists activated
        let mut groups = CollisionGroups::new();

        groups.modify_whitelist(STATIC_GROUP_ID, false);
        groups.modify_blacklist(STATIC_GROUP_ID, true);

        RigidBodyCollisionGroups{
            collision_groups: groups
        }
    }

    /// Return the internal `CollisionGroups`
    #[inline]
    pub fn as_collision_groups(&self) -> &CollisionGroups {
        &self.collision_groups
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_membership(&mut self, group_id: usize, add: bool) {
        assert!(group_id != STATIC_GROUP_ID, "The group {} is reserved.", STATIC_GROUP_ID);
        self.collision_groups.modify_membership(group_id, add);
    }

    /// Adds or removes the given group from this entity whitelist.
    #[inline]
    pub fn modify_whitelist(&mut self, group_id: usize, add: bool) {
        assert!(group_id != STATIC_GROUP_ID, "The group {} is reserved.", STATIC_GROUP_ID);
        self.collision_groups.modify_whitelist(group_id, add);
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_blacklist(&mut self, group_id: usize, add: bool) {
        assert!(group_id != STATIC_GROUP_ID, "The group {} is reserved.", STATIC_GROUP_ID);
        self.collision_groups.modify_blacklist(group_id, add);
    }

    /// Enables self collision detection.
    #[inline]
    pub fn enable_self_collision(&mut self) {
        self.collision_groups.enable_self_collision();
    }

    /// Disables self collision detection.
    #[inline]
    pub fn disable_self_collision(&mut self) {
        self.collision_groups.disable_self_collision();
    }

    /// Tests if this entity is part of the given group.
    #[inline]
    pub fn is_member_of(&self, group_id: usize) -> bool {
        self.collision_groups.is_member_of(group_id)
    }

    /// Tests if the given group is whitelisted.
    #[inline]
    pub fn is_group_whitelisted(&self, group_id: usize) -> bool {
        self.collision_groups.is_group_whitelisted(group_id)
    }

    /// Tests if the given group is blacklisted.
    #[inline]
    pub fn is_group_blacklisted(&self, group_id: usize) -> bool {
        self.collision_groups.is_group_blacklisted(group_id)
    }

    /// Tests whether collisions with a given group is possible.
    ///
    /// Collision is possible if `group_id` is whitelisted but not blacklisted.
    #[inline]
    pub fn can_collide_with(&self, group_id: usize) -> bool {
        self.collision_groups.can_collide_with(group_id)
    }

    /// Tests whether two collision groups have at least one group in common.
    #[inline]
    pub fn can_collide_with_groups(&self, other: &CollisionGroups) -> bool {
        self.collision_groups.can_collide_with_groups(other)
    }

    /// Tests whether self-collision is enabled.
    #[inline]
    pub fn can_collide_with_self(&self) -> bool {
        self.collision_groups.can_collide_with_self()
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::fs::File;
use std::io::Write;
use api_parser::*;
use std::collections::HashMap;

static HEADER: &'static [u8] = b"use rust_ffi::*;\n\n";

/*
pub struct Ui {
    pu: *const PU
}

impl Ui {
    pub fn new(pu: *const PU) -> Ui { Ui { pu: pu } }
#\n";
*/

fn generate_struct(f: &mut File, structs: &Vec<Struct>) -> io::Result<()> {
    for sdef in structs {
        f.write_fmt(format_args!("pub struct {} {{\n", sdef.name))?;

        if sdef.is_pod() {
            for entry in &sdef.entries {
                match *entry {
                    StructEntry::Var(ref var) => f.write_fmt(format_args!("    pub {}: {},\n", var.name, var.vtype))?,
                    _ => (),
                }
            }
        } else {
            // Assume for non-pod that we only use the FFI interface to do stuff.
            f.write_fmt(format_args!("    obj: const* PU{},\n", sdef.name))?;
        }

        f.write_all(b"}\n\n")?;
    }

    Ok(())
}

fn generate_func_impl(f: &mut File, func: &Function) -> io::Result<()> {
    f.write_fmt(format_args!("    pub fn {}(", func.name))?;

    func.write_func_def(f, |_, arg| {
        if arg.vtype == "String" {
            (arg.name.clone(), "&str".to_owned())
        } else if arg.vtype == "self" {
            ("&self".to_owned(), "".to_owned())
        } else if arg.primitive {
            (arg.name.to_owned(), arg.vtype.clone())
        } else {
            (arg.name.clone(), format!("&{}", arg.vtype.to_owned()))
        }
    })?;

    f.write_all(b") {\n")?;

    // Handle strings (as they need to use CString before call down to the C code

    let mut str_in_count = 0;
    let mut name_remap = HashMap::with_capacity(func.function_args.len());

    for arg in &func.function_args {
        if arg.vtype == "String" {
            f.write_fmt(format_args!("        let str_in_{} = CString::new({}).unwrap();\n", str_in_count, arg.name))?;
            name_remap.insert(arg.name.to_owned(), format!("str_in_{}.get_ptr()", str_in_count));
            str_in_count += 1;
        } else {
            name_remap.insert(arg.name.to_owned(), arg.name.to_owned());
        }
    }

    f.write_all(b"        unsafe {\n")?;
    f.write_fmt(format_args!("            ((*self.obj).{})(", func.name))?;

    func.write_func_def(f, |index, arg| {
        if index == 0 {
            ("(*self.obj).privd))".to_owned(), "".to_owned())
        } else if !arg.primitive {
            (format!("{}", name_remap.get(&arg.name.to_owned()).unwrap()), "".to_owned())
        } else {
            (arg.name.to_owned(), "".to_owned())
        }
    })?;

    f.write_all(b");\n")?;

    f.write_all(b"        }\n")?;
    f.write_all(b"    }\n\n")?;

    Ok(())
}

fn generate_impl(f: &mut File, api_def: &ApiDef) -> io::Result<()> {
    for sdef in api_def.entries.iter().filter(|s| !s.is_pod()) {
        f.write_fmt(format_args!("impl {} {{\n", sdef.name))?;

        let funcs = api_def.collect_functions(&sdef);

        for func in &funcs {
            if func.callback {
            } else {
                generate_func_impl(f, func)?;
            }
        }

        f.write_all(b"}\n\n")?;
    }

    Ok(())
}

pub fn generate_rust_bindigs(filename: &str, api_def: &ApiDef) -> io::Result<()> {
    let mut f = File::create(filename)?;

    f.write_all(HEADER)?;

    generate_struct(&mut f, &api_def.entries)?;
    generate_impl(&mut f, &api_def)?;

    Ok(())
}
<commit_msg>Generate UI bindings<commit_after>use std::io;
use std::fs::File;
use std::io::Write;
use api_parser::*;
use std::collections::HashMap;
use heck::SnakeCase;

static HEADER: &'static [u8] = b"use rust_ffi::*;\n\n";

static UI_HEADER: &'static [u8] = b"pub struct Ui {
    pu: *const PU
}

impl Ui {
    pub fn new(pu: *const PU) -> Ui { Ui { pu: pu } }
\n";

///
///
///
fn generate_struct(f: &mut File, structs: &Vec<Struct>) -> io::Result<()> {
    for sdef in structs {
        f.write_fmt(format_args!("pub struct {} {{\n", sdef.name))?;

        if sdef.is_pod() {
            for entry in &sdef.entries {
                match *entry {
                    StructEntry::Var(ref var) => f.write_fmt(format_args!("    pub {}: {},\n", var.name, var.vtype))?,
                    _ => (),
                }
            }
        } else {
            // Assume for non-pod that we only use the FFI interface to do stuff.
            f.write_fmt(format_args!("    obj: const* PU{},\n", sdef.name))?;
        }

        f.write_all(b"}\n\n")?;
    }

    Ok(())
}

fn generate_func_impl(f: &mut File, func: &Function) -> io::Result<()> {
    f.write_fmt(format_args!("    pub fn {}(", func.name))?;

    func.write_func_def(f, |_, arg| {
        if arg.vtype == "String" {
            (arg.name.clone(), "&str".to_owned())
        } else if arg.vtype == "self" {
            ("&self".to_owned(), "".to_owned())
        } else if arg.primitive {
            (arg.name.to_owned(), arg.vtype.clone())
        } else {
            (arg.name.clone(), format!("&{}", arg.vtype.to_owned()))
        }
    })?;

    f.write_all(b") {\n")?;

    // Handle strings (as they need to use CString before call down to the C code

    let mut str_in_count = 0;
    let mut name_remap = HashMap::with_capacity(func.function_args.len());

    for arg in &func.function_args {
        if arg.vtype == "String" {
            f.write_fmt(format_args!("        let str_in_{} = CString::new({}).unwrap();\n", str_in_count, arg.name))?;
            name_remap.insert(arg.name.to_owned(), format!("str_in_{}.get_ptr()", str_in_count));
            str_in_count += 1;
        } else {
            name_remap.insert(arg.name.to_owned(), arg.name.to_owned());
        }
    }

    f.write_all(b"        unsafe {\n")?;
    f.write_fmt(format_args!("            ((*self.obj).{})(", func.name))?;

    func.write_func_def(f, |index, arg| {
        if index == 0 {
            ("(*self.obj).privd))".to_owned(), "".to_owned())
        } else if !arg.primitive {
            (format!("{}", name_remap.get(&arg.name.to_owned()).unwrap()), "".to_owned())
        } else {
            (arg.name.to_owned(), "".to_owned())
        }
    })?;

    f.write_all(b");\n")?;

    f.write_all(b"        }\n")?;
    f.write_all(b"    }\n\n")?;

    Ok(())
}

fn generate_impl(f: &mut File, api_def: &ApiDef) -> io::Result<()> {
    for sdef in api_def.entries.iter().filter(|s| !s.is_pod()) {
        f.write_fmt(format_args!("impl {} {{\n", sdef.name))?;

        let funcs = api_def.collect_functions(&sdef);

        for func in &funcs {
            if func.callback {
            } else {
                generate_func_impl(f, func)?;
            }
        }

        f.write_all(b"}\n\n")?;
    }

    Ok(())
}

fn generate_ui_impl(f: &mut File, api_def: &ApiDef) -> io::Result<()> {
    f.write_all(UI_HEADER)?;

    for sdef in api_def.entries.iter().filter(|s| !s.is_pod()) {
        let snake_name = sdef.name.to_snake_case();

        f.write_fmt(format_args!("    pub fn create_{}(&self) -> {} {{\n", snake_name, sdef.name))?;
        f.write_fmt(format_args!("        {} {{ obj: (*self.obj)(create_{})() }}\n", sdef.name, snake_name))?;
        f.write_all(b"    }\n\n")?;
    }

    f.write_all(b"}\n")?;

    Ok(())
}

pub fn generate_rust_bindigs(filename: &str, api_def: &ApiDef) -> io::Result<()> {
    let mut f = File::create(filename)?;

    f.write_all(HEADER)?;

    generate_struct(&mut f, &api_def.entries)?;
    generate_impl(&mut f, &api_def)?;
    generate_ui_impl(&mut f, &api_def)?;

    Ok(())
}
<|endoftext|>