<commit_before>use std::hint::unreachable_unchecked;

use crate::bitboard::{BitBoard, EMPTY};
use crate::color::Color;
use crate::file::File;
use crate::square::Square;

use crate::magic::{KINGSIDE_CASTLE_SQUARES, QUEENSIDE_CASTLE_SQUARES};

/// What castle rights does a particular player have?
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Hash)]
pub enum CastleRights {
    NoRights,
    KingSide,
    QueenSide,
    Both,
}

/// How many different types of `CastleRights` are there?
pub const NUM_CASTLE_RIGHTS: usize = 4;

/// Enumerate all castle rights.
pub const ALL_CASTLE_RIGHTS: [CastleRights; NUM_CASTLE_RIGHTS] = [
    CastleRights::NoRights,
    CastleRights::KingSide,
    CastleRights::QueenSide,
    CastleRights::Both,
];

const CASTLES_PER_SQUARE: [[u8; 64]; 2] = [
    [
        2, 0, 0, 0, 3, 0, 0, 1, // 1
        0, 0, 0, 0, 0, 0, 0, 0, // 2
        0, 0, 0, 0, 0, 0, 0, 0, // 3
        0, 0, 0, 0, 0, 0, 0, 0, // 4
        0, 0, 0, 0, 0, 0, 0, 0, // 5
        0, 0, 0, 0, 0, 0, 0, 0, // 6
        0, 0, 0, 0, 0, 0, 0, 0, // 7
        0, 0, 0, 0, 0, 0, 0, 0, // 8
    ],
    [
        0, 0, 0, 0, 0, 0, 0, 0, // 1
        0, 0, 0, 0, 0, 0, 0, 0, // 2
        0, 0, 0, 0, 0, 0, 0, 0, // 3
        0, 0, 0, 0, 0, 0, 0, 0, // 4
        0, 0, 0, 0, 0, 0, 0, 0, // 5
        0, 0, 0, 0, 0, 0, 0, 0, // 6
        0, 0, 0, 0, 0, 0, 0, 0, // 7
        2, 0, 0, 0, 3, 0, 0, 1,
    ],
];

impl CastleRights {
    /// Can I castle kingside?
    pub fn has_kingside(&self) -> bool {
        self.to_index() & 1 == 1
    }

    /// Can I castle queenside?
    pub fn has_queenside(&self) -> bool {
        self.to_index() & 2 == 2
    }

    pub fn square_to_castle_rights(color: Color, sq: Square) -> CastleRights {
        CastleRights::from_index(unsafe {
            *CASTLES_PER_SQUARE
                .get_unchecked(color.to_index())
                .get_unchecked(sq.to_index())
        } as usize)
    }

    /// What squares need to be empty to castle kingside?
    pub fn kingside_squares(&self, color: Color) -> BitBoard {
        unsafe { *KINGSIDE_CASTLE_SQUARES.get_unchecked(color.to_index()) }
    }

    /// What squares need to be empty to castle queenside?
    pub fn queenside_squares(&self, color: Color) -> BitBoard {
        unsafe { *QUEENSIDE_CASTLE_SQUARES.get_unchecked(color.to_index()) }
    }

    /// Remove castle rights, and return a new `CastleRights`.
    pub fn remove(&self, remove: CastleRights) -> CastleRights {
        CastleRights::from_index(self.to_index() & !remove.to_index())
    }

    /// Add some castle rights, and return a new `CastleRights`.
    pub fn add(&self, add: CastleRights) -> CastleRights {
        CastleRights::from_index(self.to_index() | add.to_index())
    }

    /// Convert `CastleRights` to `usize` for table lookups
    pub fn to_index(&self) -> usize {
        *self as usize
    }

    /// Convert `usize` to `CastleRights`.  Panic if invalid number.
    pub fn from_index(i: usize) -> CastleRights {
        match i {
            0 => CastleRights::NoRights,
            1 => CastleRights::KingSide,
            2 => CastleRights::QueenSide,
            3 => CastleRights::Both,
            _ => unsafe { unreachable_unchecked() },
        }
    }

    /// Which rooks can we "guarantee" we haven't moved yet?
    pub fn unmoved_rooks(&self, color: Color) -> BitBoard {
        match *self {
            CastleRights::NoRights => EMPTY,
            CastleRights::KingSide => BitBoard::set(color.to_my_backrank(), File::H),
            CastleRights::QueenSide => BitBoard::set(color.to_my_backrank(), File::A),
            CastleRights::Both => {
                BitBoard::set(color.to_my_backrank(), File::A)
                    ^ BitBoard::set(color.to_my_backrank(), File::H)
            }
        }
    }

    /// Convert the castle rights to an FEN compatible string.
    ///
    /// ```
    /// use chess::{CastleRights, Color};
    ///
    /// assert_eq!(CastleRights::NoRights.to_string(Color::White), "");
    /// assert_eq!(CastleRights::Both.to_string(Color::Black), "kq");
    /// assert_eq!(CastleRights::KingSide.to_string(Color::White), "K");
    /// assert_eq!(CastleRights::QueenSide.to_string(Color::Black), "q");
    /// ```
    pub fn to_string(&self, color: Color) -> String {
        let result = match *self {
            CastleRights::NoRights => "",
            CastleRights::KingSide => "k",
            CastleRights::QueenSide => "q",
            CastleRights::Both => "kq",
        };

        if color == Color::White {
            result.to_uppercase()
        } else {
            result.to_string()
        }
    }

    /// Given a square of a rook, which side is it on?
    /// Note: It is invalid to pass in a non-rook square.  The code may panic.
    pub fn rook_square_to_castle_rights(square: Square) -> CastleRights {
        match square.get_file() {
            File::A => CastleRights::QueenSide,
            File::H => CastleRights::KingSide,
            _ => unsafe { unreachable_unchecked() },
        }
    }
}
<commit_msg>Derived Eq for CastleRights<commit_after>use std::hint::unreachable_unchecked;

use crate::bitboard::{BitBoard, EMPTY};
use crate::color::Color;
use crate::file::File;
use crate::square::Square;

use crate::magic::{KINGSIDE_CASTLE_SQUARES, QUEENSIDE_CASTLE_SQUARES};

/// What castle rights does a particular player have?
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug, Hash)]
pub enum CastleRights {
    NoRights,
    KingSide,
    QueenSide,
    Both,
}

/// How many different types of `CastleRights` are there?
pub const NUM_CASTLE_RIGHTS: usize = 4;

/// Enumerate all castle rights.
pub const ALL_CASTLE_RIGHTS: [CastleRights; NUM_CASTLE_RIGHTS] = [
    CastleRights::NoRights,
    CastleRights::KingSide,
    CastleRights::QueenSide,
    CastleRights::Both,
];

const CASTLES_PER_SQUARE: [[u8; 64]; 2] = [
    [
        2, 0, 0, 0, 3, 0, 0, 1, // 1
        0, 0, 0, 0, 0, 0, 0, 0, // 2
        0, 0, 0, 0, 0, 0, 0, 0, // 3
        0, 0, 0, 0, 0, 0, 0, 0, // 4
        0, 0, 0, 0, 0, 0, 0, 0, // 5
        0, 0, 0, 0, 0, 0, 0, 0, // 6
        0, 0, 0, 0, 0, 0, 0, 0, // 7
        0, 0, 0, 0, 0, 0, 0, 0, // 8
    ],
    [
        0, 0, 0, 0, 0, 0, 0, 0, // 1
        0, 0, 0, 0, 0, 0, 0, 0, // 2
        0, 0, 0, 0, 0, 0, 0, 0, // 3
        0, 0, 0, 0, 0, 0, 0, 0, // 4
        0, 0, 0, 0, 0, 0, 0, 0, // 5
        0, 0, 0, 0, 0, 0, 0, 0, // 6
        0, 0, 0, 0, 0, 0, 0, 0, // 7
        2, 0, 0, 0, 3, 0, 0, 1,
    ],
];

impl CastleRights {
    /// Can I castle kingside?
    pub fn has_kingside(&self) -> bool {
        self.to_index() & 1 == 1
    }

    /// Can I castle queenside?
    pub fn has_queenside(&self) -> bool {
        self.to_index() & 2 == 2
    }

    pub fn square_to_castle_rights(color: Color, sq: Square) -> CastleRights {
        CastleRights::from_index(unsafe {
            *CASTLES_PER_SQUARE
                .get_unchecked(color.to_index())
                .get_unchecked(sq.to_index())
        } as usize)
    }

    /// What squares need to be empty to castle kingside?
    pub fn kingside_squares(&self, color: Color) -> BitBoard {
        unsafe { *KINGSIDE_CASTLE_SQUARES.get_unchecked(color.to_index()) }
    }

    /// What squares need to be empty to castle queenside?
    pub fn queenside_squares(&self, color: Color) -> BitBoard {
        unsafe { *QUEENSIDE_CASTLE_SQUARES.get_unchecked(color.to_index()) }
    }

    /// Remove castle rights, and return a new `CastleRights`.
    pub fn remove(&self, remove: CastleRights) -> CastleRights {
        CastleRights::from_index(self.to_index() & !remove.to_index())
    }

    /// Add some castle rights, and return a new `CastleRights`.
    pub fn add(&self, add: CastleRights) -> CastleRights {
        CastleRights::from_index(self.to_index() | add.to_index())
    }

    /// Convert `CastleRights` to `usize` for table lookups
    pub fn to_index(&self) -> usize {
        *self as usize
    }

    /// Convert `usize` to `CastleRights`.  Panic if invalid number.
    pub fn from_index(i: usize) -> CastleRights {
        match i {
            0 => CastleRights::NoRights,
            1 => CastleRights::KingSide,
            2 => CastleRights::QueenSide,
            3 => CastleRights::Both,
            _ => unsafe { unreachable_unchecked() },
        }
    }

    /// Which rooks can we "guarantee" we haven't moved yet?
    pub fn unmoved_rooks(&self, color: Color) -> BitBoard {
        match *self {
            CastleRights::NoRights => EMPTY,
            CastleRights::KingSide => BitBoard::set(color.to_my_backrank(), File::H),
            CastleRights::QueenSide => BitBoard::set(color.to_my_backrank(), File::A),
            CastleRights::Both => {
                BitBoard::set(color.to_my_backrank(), File::A)
                    ^ BitBoard::set(color.to_my_backrank(), File::H)
            }
        }
    }

    /// Convert the castle rights to an FEN compatible string.
    ///
    /// ```
    /// use chess::{CastleRights, Color};
    ///
    /// assert_eq!(CastleRights::NoRights.to_string(Color::White), "");
    /// assert_eq!(CastleRights::Both.to_string(Color::Black), "kq");
    /// assert_eq!(CastleRights::KingSide.to_string(Color::White), "K");
    /// assert_eq!(CastleRights::QueenSide.to_string(Color::Black), "q");
    /// ```
    pub fn to_string(&self, color: Color) -> String {
        let result = match *self {
            CastleRights::NoRights => "",
            CastleRights::KingSide => "k",
            CastleRights::QueenSide => "q",
            CastleRights::Both => "kq",
        };

        if color == Color::White {
            result.to_uppercase()
        } else {
            result.to_string()
        }
    }

    /// Given a square of a rook, which side is it on?
    /// Note: It is invalid to pass in a non-rook square.  The code may panic.
    pub fn rook_square_to_castle_rights(square: Square) -> CastleRights {
        match square.get_file() {
            File::A => CastleRights::QueenSide,
            File::H => CastleRights::KingSide,
            _ => unsafe { unreachable_unchecked() },
        }
    }
}
<|endoftext|>


<commit_before>use scene::{BoxedScene, Scene, SceneChangeEvent, SceneName};
use engine::EngineData;
use text_renderer::TextRenderer;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::surface::Surface;
use std::io::Result;
use std::path::Path;
use uorustlibs::art::{ArtReader, Art};
use uorustlibs::hues::{HueReader, HueGroup, Hue};
use uorustlibs::color::Color as ColorTrait;
use uorustlibs::map::{MapReader, Block};

use std::io::{Error, };
use sdl2::render::{Renderer, Texture, TextureQuery};
use sdl2::rect::Rect;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

const MAX_BLOCKS_WIDTH: usize = 1024 / 8;
const MAX_BLOCKS_HEIGHT: usize = 768 / 8;

pub struct MapScene {
    reader: Result<MapReader>,
    x: u32,
    y: u32,
    texture: Option<Texture>
}

impl MapScene {
    pub fn new<'a>(renderer: &mut Renderer, engine_data: &mut EngineData<'a>) -> BoxedScene<SceneName, EngineData<'a>> {
        let mut scene = Box::new(MapScene {
            reader: MapReader::new(&Path::new("./assets/map0.mul"), 768, 512),
            x: 200,
            y: 200,
            texture: None
        });
        scene.draw_page(renderer, engine_data);
        scene
    }

    pub fn draw_page(&mut self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        let mut blocks = vec![];

        match self.reader {
            Ok(ref mut reader) => {
                for y in 0..MAX_BLOCKS_HEIGHT {
                    for x in 0..MAX_BLOCKS_WIDTH {
                        let block = reader.read_block_from_coordinates(self.x + x as u32, self.y + y as u32);
                        blocks.push(block);
                    }
                }
            },
            _ => ()
        };
        let mut surface = Surface::new(1024, 768, PixelFormatEnum::RGBA8888).unwrap();
        for y in 0..MAX_BLOCKS_HEIGHT {
            for x in 0..MAX_BLOCKS_WIDTH {
                match blocks[x + (y * MAX_BLOCKS_WIDTH)] {
                    Ok(ref block) => {
                        let block_surface = self.draw_block(block);
                        block_surface.blit(None, &mut surface, Some(Rect::new(x as i32 * 8, y as i32* 8, block_surface.width(), block_surface.height())));
                    },
                    _ => ()
                }
            }
        }
        self.texture = Some(renderer.create_texture_from_surface(&surface).unwrap());
    }

    pub fn draw_block(&self, block: &Block) -> Surface {
        let mut surface = Surface::new(8, 8, PixelFormatEnum::RGBA8888).unwrap();
        surface.with_lock_mut(|bitmap| {
            let mut read_idx = 0;

            for y in 0..8 {
                for x in 0..8 {
                    let target = (x + (y * 8));
                    let height = (block.cells[target].altitude as i16 + 128) as u8;
                    bitmap[target * 4] = 255;
                    bitmap[target * 4 + 1] = height;
                    bitmap[target * 4 + 2] = height;
                    bitmap[target * 4 + 3] = height;
                }
            };
        });
        surface
    }
}

impl<'a> Scene<SceneName, EngineData<'a>> for MapScene {
    fn render(&self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        renderer.clear();
        match self.texture {
            Some(ref texture) => {
                renderer.copy(texture, None, None).unwrap();
            },
            None => ()
        };
        renderer.present();
    }

    fn handle_event(&mut self, event: &Event, renderer: &mut Renderer, engine_data: &mut EngineData) -> Option<SceneChangeEvent<SceneName>> {
        match *event {
            Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                Some(SceneChangeEvent::PopScene)
            },
            Event::KeyDown { keycode: Some(Keycode::Left), .. } => {
                if self.x > 0 {
                    self.x -= (MAX_BLOCKS_WIDTH as u32 / 4);
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Right), .. } => {
                self.x += (MAX_BLOCKS_WIDTH as u32 / 4);
                self.draw_page(renderer, engine_data);
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Up), .. } => {
                if self.y > 0 {
                    self.y -= (MAX_BLOCKS_HEIGHT as u32 / 4);
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Down), .. } => {
                self.y += (MAX_BLOCKS_HEIGHT as u32 / 4);
                self.draw_page(renderer, engine_data);
                None
            },
             _ => None
        }
    }
}
<commit_msg>Safer navigating<commit_after>use scene::{BoxedScene, Scene, SceneChangeEvent, SceneName};
use engine::EngineData;
use text_renderer::TextRenderer;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::surface::Surface;
use std::io::Result;
use std::path::Path;
use uorustlibs::art::{ArtReader, Art};
use uorustlibs::hues::{HueReader, HueGroup, Hue};
use uorustlibs::color::Color as ColorTrait;
use uorustlibs::map::{MapReader, Block};

use std::io::{Error, };
use sdl2::render::{Renderer, Texture, TextureQuery};
use sdl2::rect::Rect;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

const MAX_BLOCKS_WIDTH: usize = 1024 / 8;
const MAX_BLOCKS_HEIGHT: usize = 768 / 8;
const STEP_X: usize = MAX_BLOCKS_WIDTH / 4;
const STEP_Y: usize = MAX_BLOCKS_HEIGHT / 4;

pub struct MapScene {
    reader: Result<MapReader>,
    x: u32,
    y: u32,
    texture: Option<Texture>
}

impl MapScene {
    pub fn new<'a>(renderer: &mut Renderer, engine_data: &mut EngineData<'a>) -> BoxedScene<SceneName, EngineData<'a>> {
        let mut scene = Box::new(MapScene {
            reader: MapReader::new(&Path::new("./assets/map0.mul"), 768, 512),
            x: 0,
            y: 0,
            texture: None
        });
        scene.draw_page(renderer, engine_data);
        scene
    }

    pub fn draw_page(&mut self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        let mut blocks = vec![];

        match self.reader {
            Ok(ref mut reader) => {
                for y in 0..MAX_BLOCKS_HEIGHT {
                    for x in 0..MAX_BLOCKS_WIDTH {
                        let block = reader.read_block_from_coordinates(self.x + x as u32, self.y + y as u32);
                        blocks.push(block);
                    }
                }
            },
            _ => ()
        };
        let mut surface = Surface::new(1024, 768, PixelFormatEnum::RGBA8888).unwrap();
        for y in 0..MAX_BLOCKS_HEIGHT {
            for x in 0..MAX_BLOCKS_WIDTH {
                match blocks[x + (y * MAX_BLOCKS_WIDTH)] {
                    Ok(ref block) => {
                        let block_surface = self.draw_block(block);
                        block_surface.blit(None, &mut surface, Some(Rect::new(x as i32 * 8, y as i32* 8, block_surface.width(), block_surface.height())));
                    },
                    _ => ()
                }
            }
        }
        self.texture = Some(renderer.create_texture_from_surface(&surface).unwrap());
    }

    pub fn draw_block(&self, block: &Block) -> Surface {
        let mut surface = Surface::new(8, 8, PixelFormatEnum::RGBA8888).unwrap();
        surface.with_lock_mut(|bitmap| {
            let mut read_idx = 0;

            for y in 0..8 {
                for x in 0..8 {
                    let target = (x + (y * 8));
                    let height = (block.cells[target].altitude as i16 + 128) as u8;
                    bitmap[target * 4] = 255;
                    bitmap[target * 4 + 1] = height;
                    bitmap[target * 4 + 2] = height;
                    bitmap[target * 4 + 3] = height;
                }
            };
        });
        surface
    }
}

impl<'a> Scene<SceneName, EngineData<'a>> for MapScene {
    fn render(&self, renderer: &mut Renderer, engine_data: &mut EngineData) {
        renderer.clear();
        match self.texture {
            Some(ref texture) => {
                renderer.copy(texture, None, None).unwrap();
            },
            None => ()
        };
        renderer.present();
    }

    fn handle_event(&mut self, event: &Event, renderer: &mut Renderer, engine_data: &mut EngineData) -> Option<SceneChangeEvent<SceneName>> {
        match *event {
            Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                Some(SceneChangeEvent::PopScene)
            },
            Event::KeyDown { keycode: Some(Keycode::Left), .. } => {
                if self.x >= STEP_X as u32 {
                    self.x -= STEP_X as u32;
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Right), .. } => {
                self.x += STEP_X as u32;
                self.draw_page(renderer, engine_data);
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Up), .. } => {
                if self.y >= STEP_Y as u32 {
                    self.y -= STEP_Y as u32;
                    self.draw_page(renderer, engine_data);
                }
                None
            },
            Event::KeyDown { keycode: Some(Keycode::Down), .. } => {
                self.y += STEP_Y as u32;
                self.draw_page(renderer, engine_data);
                None
            },
             _ => None
        }
    }
}
<|endoftext|>


<commit_before>use futures::channel::mpsc as async_mpsc;

use gettextrs::gettext;
use gstreamer as gst;
use gtk;
use gtk::prelude::*;
use log::warn;

use std::{
    borrow::ToOwned,
    path::Path,
    rc::Rc,
    string::ToString,
    sync::{Arc, RwLock},
};

use media::{MediaEvent, SplitterPipeline};
use metadata::{get_default_chapter_title, Duration, Format, MediaInfo, Stream, TocVisitor};

use super::{PlaybackPipeline, UIController, UIEventSender, UIFocusContext};

use super::output_base_controller::{
    MediaProcessor, OutputBaseController, OutputControllerImpl, OutputMediaFileInfo,
    ProcessingState, ProcessingType, MEDIA_EVENT_CHANNEL_CAPACITY,
};

pub type SplitController = OutputBaseController<SplitControllerImpl>;

impl SplitController {
    pub fn new(builder: &gtk::Builder, ui_event: UIEventSender) -> Self {
        OutputBaseController::<SplitControllerImpl>::new_base(
            SplitControllerImpl::new(builder),
            builder,
            ui_event,
        )
    }
}

macro_rules! update_list_with_format(
    ($self_:expr, $format:expr, $row:ident, $label:ident) => {
        match SplitterPipeline::check_requirements($format) {
            Ok(_) => if !$self_.is_usable {
                $self_.split_list.select_row(Some(&$self_.$row));
                $self_.is_usable = true;
            },
            Err(err) => {
                warn!("{}", err);
                $self_.$label.set_label(&err);
                $self_.$row.set_sensitive(false);
            }
        }
    };
);

pub struct SplitControllerImpl {
    is_usable: bool,

    src_info: Option<Arc<RwLock<MediaInfo>>>,
    selected_audio: Option<Stream>,

    split_file_info: Option<OutputMediaFileInfo>,
    media_event_sender: Option<async_mpsc::Sender<MediaEvent>>,
    splitter_pipeline: Option<SplitterPipeline>,
    toc_visitor: Option<TocVisitor>,
    idx: usize,
    last_progress: f64,
    current_chapter: Option<gst::TocEntry>,
    current_path: Option<Rc<Path>>,

    split_list: gtk::ListBox,
    split_to_flac_row: gtk::ListBoxRow,
    flac_warning_lbl: gtk::Label,
    split_to_wave_row: gtk::ListBoxRow,
    wave_warning_lbl: gtk::Label,
    split_to_opus_row: gtk::ListBoxRow,
    opus_warning_lbl: gtk::Label,
    split_to_vorbis_row: gtk::ListBoxRow,
    vorbis_warning_lbl: gtk::Label,
    split_to_mp3_row: gtk::ListBoxRow,
    mp3_warning_lbl: gtk::Label,

    split_btn: gtk::Button,
}

impl OutputControllerImpl for SplitControllerImpl {
    const FOCUS_CONTEXT: UIFocusContext = UIFocusContext::SplitPage;
    const BTN_NAME: &'static str = "split-btn";
    const LIST_NAME: &'static str = "split-list-box";
    const PROGRESS_BAR_NAME: &'static str = "split-progress";
}

impl UIController for SplitControllerImpl {
    fn new_media(&mut self, pipeline: &PlaybackPipeline) {
        let info_arc = Arc::clone(&pipeline.info);
        self.src_info = Some(info_arc);
    }

    fn cleanup(&mut self) {
        self.src_info = None;
        self.selected_audio = None;

        self.reset();
    }

    fn streams_changed(&mut self, info: &MediaInfo) {
        self.selected_audio = info.streams.selected_audio().map(ToOwned::to_owned);
        if self.is_usable {
            self.split_btn.set_sensitive(self.selected_audio.is_some());
        }
    }
}

impl SplitControllerImpl {
    pub fn new(builder: &gtk::Builder) -> Self {
        let mut ctrl = SplitControllerImpl {
            is_usable: false,

            src_info: None,
            selected_audio: None,

            split_file_info: None,
            media_event_sender: None,
            splitter_pipeline: None,
            toc_visitor: None,
            idx: 0,
            last_progress: 0f64,
            current_chapter: None,
            current_path: None,

            split_list: builder.get_object(Self::LIST_NAME).unwrap(),
            split_to_flac_row: builder.get_object("flac_split-row").unwrap(),
            flac_warning_lbl: builder.get_object("flac_warning-lbl").unwrap(),
            split_to_wave_row: builder.get_object("wave_split-row").unwrap(),
            wave_warning_lbl: builder.get_object("wave_warning-lbl").unwrap(),
            split_to_opus_row: builder.get_object("opus_split-row").unwrap(),
            opus_warning_lbl: builder.get_object("opus_warning-lbl").unwrap(),
            split_to_vorbis_row: builder.get_object("vorbis_split-row").unwrap(),
            vorbis_warning_lbl: builder.get_object("vorbis_warning-lbl").unwrap(),
            split_to_mp3_row: builder.get_object("mp3_split-row").unwrap(),
            mp3_warning_lbl: builder.get_object("mp3_warning-lbl").unwrap(),

            split_btn: builder.get_object(Self::BTN_NAME).unwrap(),
        };

        update_list_with_format!(ctrl, Format::Flac, split_to_flac_row, flac_warning_lbl);
        update_list_with_format!(ctrl, Format::Wave, split_to_wave_row, wave_warning_lbl);
        update_list_with_format!(ctrl, Format::Opus, split_to_opus_row, opus_warning_lbl);
        update_list_with_format!(
            ctrl,
            Format::Vorbis,
            split_to_vorbis_row,
            vorbis_warning_lbl
        );
        update_list_with_format!(ctrl, Format::MP3, split_to_mp3_row, mp3_warning_lbl);

        ctrl.split_list.set_sensitive(ctrl.is_usable);
        ctrl.split_btn.set_sensitive(ctrl.is_usable);

        ctrl
    }

    fn reset(&mut self) {
        self.split_file_info = None;
        self.media_event_sender = None;
        self.splitter_pipeline = None;
        self.toc_visitor = None;
        self.idx = 0;
        self.last_progress = 0f64;
        self.current_chapter = None;
        self.current_path = None;
    }

    fn get_split_path(&self, chapter: &gst::TocEntry) -> Rc<Path> {
        let mut split_name = String::new();

        let src_info = self.src_info.as_ref().unwrap().read().unwrap();

        // TODO: make format customisable
        let artist = src_info
            .get_media_artist_sortname()
            .or_else(|| src_info.get_media_artist());
        if let Some(artist) = artist {
            split_name += &format!("{} - ", artist);
        }

        let album_title = src_info
            .get_media_title_sortname()
            .or_else(|| src_info.get_media_title());
        if let Some(album_title) = album_title {
            split_name += &format!("{} - ", album_title);
        }

        if self.toc_visitor.is_some() {
            split_name += &format!("{:02}", self.idx);
        }

        let track_title = chapter
            .get_tags()
            .and_then(|tags| {
                tags.get::<gst::tags::Title>()
                    .and_then(|tag| tag.get().map(ToString::to_string))
            })
            .unwrap_or_else(get_default_chapter_title);

        if !split_name.is_empty() {
            split_name += ". ";
        }

        split_name += &track_title;

        let lang = self.selected_audio.as_ref().and_then(|stream| {
            stream
                .tags
                .get_index::<gst::tags::LanguageName>(0)
                .or_else(|| stream.tags.get_index::<gst::tags::LanguageCode>(0))
                .and_then(|value| value.get().map(ToString::to_string))
        });
        if let Some(lang) = lang {
            split_name += &format!(" ({})", lang);
        }

        let split_file_info = self.split_file_info.as_ref().expect(concat!(
            "SplitControllerImpl: split_file_info not defined in `get_split_path()`, ",
            "did you call `init()`?"
        ));

        split_name += &format!(".{}", split_file_info.extension);

        split_file_info.path.with_file_name(split_name).into()
    }
}

impl MediaProcessor for SplitControllerImpl {
    fn init(&mut self) -> ProcessingType {
        self.reset();

        let format = if self.split_to_flac_row.is_selected() {
            Format::Flac
        } else if self.split_to_wave_row.is_selected() {
            Format::Wave
        } else if self.split_to_opus_row.is_selected() {
            Format::Opus
        } else if self.split_to_vorbis_row.is_selected() {
            Format::Vorbis
        } else if self.split_to_mp3_row.is_selected() {
            Format::MP3
        } else {
            unreachable!("`SplitController`: unknown split type");
        };

        // Split button is not sensitive when no audio
        // stream is selected (see `streams_changed`)
        debug_assert!(self.selected_audio.is_some());

        self.toc_visitor = self
            .src_info
            .as_ref()
            .unwrap()
            .read()
            .unwrap()
            .toc
            .as_ref()
            .map(|toc| TocVisitor::new(toc));
        self.idx = 0;

        self.split_file_info = Some({
            let src_info = self.src_info.as_ref().unwrap().read().unwrap();
            OutputMediaFileInfo::new(format, &src_info)
        });

        let (sender, receiver) = async_mpsc::channel(MEDIA_EVENT_CHANNEL_CAPACITY);
        self.media_event_sender = Some(sender);

        ProcessingType::Async(receiver)
    }

    fn next(&mut self) -> Result<ProcessingState, String> {
        let chapter = match self.toc_visitor.as_mut() {
            Some(toc_visitor) => match toc_visitor.next_chapter() {
                Some(chapter) => {
                    self.idx += 1;
                    chapter
                }
                None => {
                    self.split_file_info = None;
                    return Ok(ProcessingState::AllComplete(gettext(
                        "Media split succesfully",
                    )));
                }
            },
            None => {
                // No chapter defined => build a fake chapter corresponding to the whole file
                self.idx += 1;

                let src_info = self.src_info.as_ref().unwrap().read().unwrap();
                let mut toc_entry = gst::TocEntry::new(gst::TocEntryType::Chapter, &"".to_owned());
                toc_entry
                    .get_mut()
                    .unwrap()
                    .set_start_stop_times(0, src_info.duration.as_i64());

                let mut tag_list = gst::TagList::new();
                tag_list.get_mut().unwrap().add::<gst::tags::Title>(
                    &src_info.path.file_stem().unwrap().to_str().unwrap(),
                    gst::TagMergeMode::Replace,
                );
                toc_entry.get_mut().unwrap().set_tags(tag_list);

                toc_entry
            }
        };

        self.current_chapter = Some(
            self.src_info
                .as_ref()
                .unwrap()
                .read()
                .unwrap()
                .get_chapter_with_track_tags(&chapter, self.idx),
        );

        let split_path = self.get_split_path(&chapter);
        self.current_path = Some(Rc::clone(&split_path));

        Ok(ProcessingState::WouldOutputTo(split_path))
    }

    fn process(&mut self, output_path: &Path) -> Result<ProcessingState, String> {
        let res = {
            let src_info = self.src_info.as_ref().unwrap().read().unwrap();
            let split_file_info = self.split_file_info.as_ref().expect(
                "SplitControllerImpl: split_file_info not defined in `next()`, did you call `init()`?"
            );
            SplitterPipeline::try_new(
                &src_info.path,
                output_path,
                &self.selected_audio.as_ref().unwrap().id,
                split_file_info.format,
                self.current_chapter.take().expect(concat!(
                    "SplitControllerImpl: no current_chapter ",
                    "in `process_current_chapter()`",
                )),
                self.media_event_sender
                    .as_ref()
                    .expect(concat!(
                        "SplitControllerImpl: no media_event_sender in `process_current_chapter()` ",
                        "did you call `init()`?",
                    ))
                    .clone(),
            )
        };

        self.splitter_pipeline = Some(res.map_err(|err| {
            self.reset();
            gettext("Failed to prepare for split. {}").replacen("{}", &err, 1)
        })?);

        Ok(ProcessingState::PendingAsyncMediaEvent)
    }

    fn cancel(&mut self) {
        if let Some(pipeline) = self.splitter_pipeline.as_mut() {
            pipeline.cancel();

            if let Some(current_path) = self.current_path.take() {
                if std::fs::remove_file(&current_path).is_err() {
                    if let Some(printable_path) = current_path.to_str() {
                        warn!("Failed to remove canceled split file {}", printable_path);
                    }
                }
            }
        }

        self.reset();
    }

    fn handle_media_event(&mut self, event: MediaEvent) -> Result<ProcessingState, String> {
        match event {
            MediaEvent::Eos => {
                self.current_chapter = None;
                self.current_path = None;
                self.splitter_pipeline = None;
                Ok(ProcessingState::DoneWithCurrent)
            }
            MediaEvent::FailedToExport(err) => {
                self.reset();
                Err(gettext("Failed to split media. {}").replacen("{}", &err, 1))
            }
            other => unimplemented!("SplitController: can't handle media event {:?}", other),
        }
    }

    fn report_progress(&mut self) -> f64 {
        let duration = self.src_info.as_ref().unwrap().read().unwrap().duration;
        if duration > Duration::default() {
            // With some formats, we can't retrieve a proper ts between 2 files
            // so, just report known last progress in this case
            if let Some(ts) = self
                .splitter_pipeline
                .as_ref()
                .and_then(SplitterPipeline::get_current_ts)
            {
                self.last_progress = ts.as_f64() / duration.as_f64()
            }

            self.last_progress
        } else {
            0f64
        }
    }
}
<commit_msg>Inifinite loop splitting a chapterless media<commit_after>use futures::channel::mpsc as async_mpsc;

use gettextrs::gettext;
use gstreamer as gst;
use gtk;
use gtk::prelude::*;
use log::warn;

use std::{
    borrow::ToOwned,
    path::Path,
    rc::Rc,
    string::ToString,
    sync::{Arc, RwLock},
};

use media::{MediaEvent, SplitterPipeline};
use metadata::{get_default_chapter_title, Duration, Format, MediaInfo, Stream, TocVisitor};

use super::{PlaybackPipeline, UIController, UIEventSender, UIFocusContext};

use super::output_base_controller::{
    MediaProcessor, OutputBaseController, OutputControllerImpl, OutputMediaFileInfo,
    ProcessingState, ProcessingType, MEDIA_EVENT_CHANNEL_CAPACITY,
};

pub type SplitController = OutputBaseController<SplitControllerImpl>;

impl SplitController {
    pub fn new(builder: &gtk::Builder, ui_event: UIEventSender) -> Self {
        OutputBaseController::<SplitControllerImpl>::new_base(
            SplitControllerImpl::new(builder),
            builder,
            ui_event,
        )
    }
}

macro_rules! update_list_with_format(
    ($self_:expr, $format:expr, $row:ident, $label:ident) => {
        match SplitterPipeline::check_requirements($format) {
            Ok(_) => if !$self_.is_usable {
                $self_.split_list.select_row(Some(&$self_.$row));
                $self_.is_usable = true;
            },
            Err(err) => {
                warn!("{}", err);
                $self_.$label.set_label(&err);
                $self_.$row.set_sensitive(false);
            }
        }
    };
);

pub struct SplitControllerImpl {
    is_usable: bool,

    src_info: Option<Arc<RwLock<MediaInfo>>>,
    selected_audio: Option<Stream>,

    split_file_info: Option<OutputMediaFileInfo>,
    media_event_sender: Option<async_mpsc::Sender<MediaEvent>>,
    splitter_pipeline: Option<SplitterPipeline>,
    toc_visitor: Option<TocVisitor>,
    idx: usize,
    last_progress: f64,
    current_chapter: Option<gst::TocEntry>,
    current_path: Option<Rc<Path>>,

    split_list: gtk::ListBox,
    split_to_flac_row: gtk::ListBoxRow,
    flac_warning_lbl: gtk::Label,
    split_to_wave_row: gtk::ListBoxRow,
    wave_warning_lbl: gtk::Label,
    split_to_opus_row: gtk::ListBoxRow,
    opus_warning_lbl: gtk::Label,
    split_to_vorbis_row: gtk::ListBoxRow,
    vorbis_warning_lbl: gtk::Label,
    split_to_mp3_row: gtk::ListBoxRow,
    mp3_warning_lbl: gtk::Label,

    split_btn: gtk::Button,
}

impl OutputControllerImpl for SplitControllerImpl {
    const FOCUS_CONTEXT: UIFocusContext = UIFocusContext::SplitPage;
    const BTN_NAME: &'static str = "split-btn";
    const LIST_NAME: &'static str = "split-list-box";
    const PROGRESS_BAR_NAME: &'static str = "split-progress";
}

impl UIController for SplitControllerImpl {
    fn new_media(&mut self, pipeline: &PlaybackPipeline) {
        let info_arc = Arc::clone(&pipeline.info);
        self.src_info = Some(info_arc);
    }

    fn cleanup(&mut self) {
        self.src_info = None;
        self.selected_audio = None;

        self.reset();
    }

    fn streams_changed(&mut self, info: &MediaInfo) {
        self.selected_audio = info.streams.selected_audio().map(ToOwned::to_owned);
        if self.is_usable {
            self.split_btn.set_sensitive(self.selected_audio.is_some());
        }
    }
}

impl SplitControllerImpl {
    pub fn new(builder: &gtk::Builder) -> Self {
        let mut ctrl = SplitControllerImpl {
            is_usable: false,

            src_info: None,
            selected_audio: None,

            split_file_info: None,
            media_event_sender: None,
            splitter_pipeline: None,
            toc_visitor: None,
            idx: 0,
            last_progress: 0f64,
            current_chapter: None,
            current_path: None,

            split_list: builder.get_object(Self::LIST_NAME).unwrap(),
            split_to_flac_row: builder.get_object("flac_split-row").unwrap(),
            flac_warning_lbl: builder.get_object("flac_warning-lbl").unwrap(),
            split_to_wave_row: builder.get_object("wave_split-row").unwrap(),
            wave_warning_lbl: builder.get_object("wave_warning-lbl").unwrap(),
            split_to_opus_row: builder.get_object("opus_split-row").unwrap(),
            opus_warning_lbl: builder.get_object("opus_warning-lbl").unwrap(),
            split_to_vorbis_row: builder.get_object("vorbis_split-row").unwrap(),
            vorbis_warning_lbl: builder.get_object("vorbis_warning-lbl").unwrap(),
            split_to_mp3_row: builder.get_object("mp3_split-row").unwrap(),
            mp3_warning_lbl: builder.get_object("mp3_warning-lbl").unwrap(),

            split_btn: builder.get_object(Self::BTN_NAME).unwrap(),
        };

        update_list_with_format!(ctrl, Format::Flac, split_to_flac_row, flac_warning_lbl);
        update_list_with_format!(ctrl, Format::Wave, split_to_wave_row, wave_warning_lbl);
        update_list_with_format!(ctrl, Format::Opus, split_to_opus_row, opus_warning_lbl);
        update_list_with_format!(
            ctrl,
            Format::Vorbis,
            split_to_vorbis_row,
            vorbis_warning_lbl
        );
        update_list_with_format!(ctrl, Format::MP3, split_to_mp3_row, mp3_warning_lbl);

        ctrl.split_list.set_sensitive(ctrl.is_usable);
        ctrl.split_btn.set_sensitive(ctrl.is_usable);

        ctrl
    }

    fn reset(&mut self) {
        self.split_file_info = None;
        self.media_event_sender = None;
        self.splitter_pipeline = None;
        self.toc_visitor = None;
        self.idx = 0;
        self.last_progress = 0f64;
        self.current_chapter = None;
        self.current_path = None;
    }

    fn get_split_path(&self, chapter: &gst::TocEntry) -> Rc<Path> {
        let mut split_name = String::new();

        let src_info = self.src_info.as_ref().unwrap().read().unwrap();

        // TODO: make format customisable
        let artist = src_info
            .get_media_artist_sortname()
            .or_else(|| src_info.get_media_artist());
        if let Some(artist) = artist {
            split_name += &format!("{} - ", artist);
        }

        let album_title = src_info
            .get_media_title_sortname()
            .or_else(|| src_info.get_media_title());
        if let Some(album_title) = album_title {
            split_name += &format!("{} - ", album_title);
        }

        if self.toc_visitor.is_some() {
            split_name += &format!("{:02}", self.idx);
        }

        let track_title = chapter
            .get_tags()
            .and_then(|tags| {
                tags.get::<gst::tags::Title>()
                    .and_then(|tag| tag.get().map(ToString::to_string))
            })
            .unwrap_or_else(get_default_chapter_title);

        if !split_name.is_empty() {
            split_name += ". ";
        }

        split_name += &track_title;

        let lang = self.selected_audio.as_ref().and_then(|stream| {
            stream
                .tags
                .get_index::<gst::tags::LanguageName>(0)
                .or_else(|| stream.tags.get_index::<gst::tags::LanguageCode>(0))
                .and_then(|value| value.get().map(ToString::to_string))
        });
        if let Some(lang) = lang {
            split_name += &format!(" ({})", lang);
        }

        let split_file_info = self.split_file_info.as_ref().expect(concat!(
            "SplitControllerImpl: split_file_info not defined in `get_split_path()`, ",
            "did you call `init()`?"
        ));

        split_name += &format!(".{}", split_file_info.extension);

        split_file_info.path.with_file_name(split_name).into()
    }
}

impl MediaProcessor for SplitControllerImpl {
    fn init(&mut self) -> ProcessingType {
        self.reset();

        let format = if self.split_to_flac_row.is_selected() {
            Format::Flac
        } else if self.split_to_wave_row.is_selected() {
            Format::Wave
        } else if self.split_to_opus_row.is_selected() {
            Format::Opus
        } else if self.split_to_vorbis_row.is_selected() {
            Format::Vorbis
        } else if self.split_to_mp3_row.is_selected() {
            Format::MP3
        } else {
            unreachable!("`SplitController`: unknown split type");
        };

        // Split button is not sensitive when no audio
        // stream is selected (see `streams_changed`)
        debug_assert!(self.selected_audio.is_some());

        self.toc_visitor = self
            .src_info
            .as_ref()
            .unwrap()
            .read()
            .unwrap()
            .toc
            .as_ref()
            .map(|toc| TocVisitor::new(toc));
        self.idx = 0;

        self.split_file_info = Some({
            let src_info = self.src_info.as_ref().unwrap().read().unwrap();
            OutputMediaFileInfo::new(format, &src_info)
        });

        let (sender, receiver) = async_mpsc::channel(MEDIA_EVENT_CHANNEL_CAPACITY);
        self.media_event_sender = Some(sender);

        ProcessingType::Async(receiver)
    }

    fn next(&mut self) -> Result<ProcessingState, String> {
        let chapter = match self.toc_visitor.as_mut() {
            Some(toc_visitor) => toc_visitor.next_chapter().map(|chapter| {
                self.idx += 1;
                chapter
            }),
            None => {
                // No chapter defined => build a fake chapter corresponding to the whole file
                if self.idx == 0 {
                    self.idx += 1;

                    let src_info = self.src_info.as_ref().unwrap().read().unwrap();
                    let mut toc_entry =
                        gst::TocEntry::new(gst::TocEntryType::Chapter, &"".to_owned());
                    toc_entry
                        .get_mut()
                        .unwrap()
                        .set_start_stop_times(0, src_info.duration.as_i64());

                    let mut tag_list = gst::TagList::new();
                    tag_list.get_mut().unwrap().add::<gst::tags::Title>(
                        &src_info.path.file_stem().unwrap().to_str().unwrap(),
                        gst::TagMergeMode::Replace,
                    );
                    toc_entry.get_mut().unwrap().set_tags(tag_list);

                    Some(toc_entry)
                } else {
                    None
                }
            }
        };

        if chapter.is_none() {
            self.split_file_info = None;

            return Ok(ProcessingState::AllComplete(gettext(
                "Media split succesfully",
            )));
        }

        let chapter = chapter.as_ref().unwrap();

        self.current_chapter = Some(
            self.src_info
                .as_ref()
                .unwrap()
                .read()
                .unwrap()
                .get_chapter_with_track_tags(chapter, self.idx),
        );

        let split_path = self.get_split_path(chapter);
        self.current_path = Some(Rc::clone(&split_path));

        Ok(ProcessingState::WouldOutputTo(split_path))
    }

    fn process(&mut self, output_path: &Path) -> Result<ProcessingState, String> {
        let res = {
            let src_info = self.src_info.as_ref().unwrap().read().unwrap();
            let split_file_info = self.split_file_info.as_ref().expect(
                "SplitControllerImpl: split_file_info not defined in `next()`, did you call `init()`?"
            );
            SplitterPipeline::try_new(
                &src_info.path,
                output_path,
                &self.selected_audio.as_ref().unwrap().id,
                split_file_info.format,
                self.current_chapter.take().expect(concat!(
                    "SplitControllerImpl: no current_chapter ",
                    "in `process_current_chapter()`",
                )),
                self.media_event_sender
                    .as_ref()
                    .expect(concat!(
                        "SplitControllerImpl: no media_event_sender in `process_current_chapter()` ",
                        "did you call `init()`?",
                    ))
                    .clone(),
            )
        };

        self.splitter_pipeline = Some(res.map_err(|err| {
            self.reset();
            gettext("Failed to prepare for split. {}").replacen("{}", &err, 1)
        })?);

        Ok(ProcessingState::PendingAsyncMediaEvent)
    }

    fn cancel(&mut self) {
        if let Some(pipeline) = self.splitter_pipeline.as_mut() {
            pipeline.cancel();

            if let Some(current_path) = self.current_path.take() {
                if std::fs::remove_file(&current_path).is_err() {
                    if let Some(printable_path) = current_path.to_str() {
                        warn!("Failed to remove canceled split file {}", printable_path);
                    }
                }
            }
        }

        self.reset();
    }

    fn handle_media_event(&mut self, event: MediaEvent) -> Result<ProcessingState, String> {
        match event {
            MediaEvent::Eos => {
                self.current_chapter = None;
                self.current_path = None;
                self.splitter_pipeline = None;
                Ok(ProcessingState::DoneWithCurrent)
            }
            MediaEvent::FailedToExport(err) => {
                self.reset();
                Err(gettext("Failed to split media. {}").replacen("{}", &err, 1))
            }
            other => unimplemented!("SplitController: can't handle media event {:?}", other),
        }
    }

    fn report_progress(&mut self) -> f64 {
        let duration = self.src_info.as_ref().unwrap().read().unwrap().duration;
        if duration > Duration::default() {
            // With some formats, we can't retrieve a proper ts between 2 files
            // so, just report known last progress in this case
            if let Some(ts) = self
                .splitter_pipeline
                .as_ref()
                .and_then(SplitterPipeline::get_current_ts)
            {
                self.last_progress = ts.as_f64() / duration.as_f64()
            }

            self.last_progress
        } else {
            0f64
        }
    }
}
<|endoftext|>


<commit_before>use core::mem;
use syscall::IntRegisters;

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct ScratchRegisters {
    pub r11: usize,
    pub r10: usize,
    pub r9: usize,
    pub r8: usize,
    pub rsi: usize,
    pub rdi: usize,
    pub rdx: usize,
    pub rcx: usize,
    pub rax: usize,
}

impl ScratchRegisters {
    pub fn dump(&self) {
        println!("RAX:   {:>016X}", { self.rax });
        println!("RCX:   {:>016X}", { self.rcx });
        println!("RDX:   {:>016X}", { self.rdx });
        println!("RDI:   {:>016X}", { self.rdi });
        println!("RSI:   {:>016X}", { self.rsi });
        println!("R8:    {:>016X}", { self.r8 });
        println!("R9:    {:>016X}", { self.r9 });
        println!("R10:   {:>016X}", { self.r10 });
        println!("R11:   {:>016X}", { self.r11 });
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct PreservedRegisters {
    pub r15: usize,
    pub r14: usize,
    pub r13: usize,
    pub r12: usize,
    pub rbp: usize,
    pub rbx: usize,
}

impl PreservedRegisters {
    pub fn dump(&self) {
        println!("RBX:   {:>016X}", { self.rbx });
        println!("RBP:   {:>016X}", { self.rbp });
        println!("R12:   {:>016X}", { self.r12 });
        println!("R13:   {:>016X}", { self.r13 });
        println!("R14:   {:>016X}", { self.r14 });
        println!("R15:   {:>016X}", { self.r15 });
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct IretRegisters {
    pub rip: usize,
    pub cs: usize,
    pub rflags: usize,
    // Will only be present if interrupt is raised from another
    // privilege ring
    pub rsp: usize,
    pub ss: usize
}

impl IretRegisters {
    pub fn dump(&self) {
        println!("RFLAG: {:>016X}", { self.rflags });
        println!("CS:    {:>016X}", { self.cs });
        println!("RIP:   {:>016X}", { self.rip });
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct InterruptStack {
    pub fs: usize,
    pub preserved: PreservedRegisters,
    pub scratch: ScratchRegisters,
    pub iret: IretRegisters,
}

impl InterruptStack {
    pub fn dump(&self) {
        self.iret.dump();
        self.scratch.dump();
        self.preserved.dump();
        println!("FS:    {:>016X}", { self.fs });
    }
    /// Saves all registers to a struct used by the proc:
    /// scheme to read/write registers.
    pub fn save(&self, all: &mut IntRegisters) {
        all.fs = self.fs;

        all.r15 = self.preserved.r15;
        all.r14 = self.preserved.r14;
        all.r13 = self.preserved.r13;
        all.r12 = self.preserved.r12;
        all.rbp = self.preserved.rbp;
        all.rbx = self.preserved.rbx;
        all.r11 = self.scratch.r11;
        all.r10 = self.scratch.r10;
        all.r9 = self.scratch.r9;
        all.r8 = self.scratch.r8;
        all.rsi = self.scratch.rsi;
        all.rdi = self.scratch.rdi;
        all.rdx = self.scratch.rdx;
        all.rcx = self.scratch.rcx;
        all.rax = self.scratch.rax;
        all.rip = self.iret.rip;
        all.cs = self.iret.cs;
        all.rflags = self.iret.rflags;

        // Set rsp and ss:

        const CPL_MASK: usize = 0b11;

        let cs: usize;
        unsafe {
            asm!("mov $0, cs" : "=r"(cs) ::: "intel");
        }

        if self.iret.cs & CPL_MASK == cs & CPL_MASK {
            // Privilege ring didn't change, so neither did the stack
            all.rsp = self as *const Self as usize // rsp after Self was pushed to the stack
                + mem::size_of::<Self>() // disregard Self
                - mem::size_of::<usize>() * 2; // well, almost: rsp and ss need to be excluded as they aren't present
            unsafe {
                asm!("mov $0, ss" : "=r"(all.ss) ::: "intel");
            }
        } else {
            all.rsp = self.iret.rsp;
            all.ss = self.iret.ss;
        }
    }
    /// Loads all registers from a struct used by the proc:
    /// scheme to read/write registers.
    pub fn load(&mut self, all: &IntRegisters) {
        // TODO: Which of these should be allowed to change?

        // self.fs = all.fs;
        self.preserved.r15 = all.r15;
        self.preserved.r14 = all.r14;
        self.preserved.r13 = all.r13;
        self.preserved.r12 = all.r12;
        self.preserved.rbp = all.rbp;
        self.preserved.rbx = all.rbx;
        self.scratch.r11 = all.r11;
        self.scratch.r10 = all.r10;
        self.scratch.r9 = all.r9;
        self.scratch.r8 = all.r8;
        self.scratch.rsi = all.rsi;
        self.scratch.rdi = all.rdi;
        self.scratch.rdx = all.rdx;
        self.scratch.rcx = all.rcx;
        self.scratch.rax = all.rax;
        self.iret.rip = all.rip;

        // These should probably be restricted
        // self.iret.cs = all.cs;
        // self.iret.rflags = all.eflags;
    }
    /// Enables the "Trap Flag" in the FLAGS register, causing the CPU
    /// to send a Debug exception after the next instruction. This is
    /// used for singlestep in the proc: scheme.
    pub fn set_singlestep(&mut self, enabled: bool) {
        if enabled {
            self.iret.rflags |= 1 << 8;
        } else {
            self.iret.rflags &= !(1 << 8);
        }
    }
    /// Checks if the trap flag is enabled, see `set_singlestep`
    pub fn is_singlestep(&self) -> bool {
        self.iret.rflags & 1 << 8 == 1 << 8
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct InterruptErrorStack {
    pub code: usize,
    pub inner: InterruptStack,
}

impl InterruptErrorStack {
    pub fn dump(&self) {
        println!("CODE:  {:>016X}", { self.code });
        self.inner.dump();
    }
}

#[macro_export]
macro_rules! intel_asm {
    ($($strings:expr,)+) => {
        global_asm!(concat!(
            ".intel_syntax noprefix\n",
            $($strings),+,
            ".att_syntax prefix\n",
        ));
    };
}
#[macro_export]
macro_rules! function {
    ($name:expr => { $($body:expr,)+ }) => {
        intel_asm!(
            ".global ", $name, "\n",
            $name, ":\n",
            $($body,)+
        );
    }
}

#[macro_export]
macro_rules! push_scratch {
    () => { "
        // Push scratch registers
        push rcx
        push rdx
        push rdi
        push rsi
        push r8
        push r9
        push r10
        push r11
    " };
}
#[macro_export]
macro_rules! push_preserved {
    () => { "
        // Push preserved registers
        push rbx
        push rbp
        push r12
        push r13
        push r14
        push r15
    " };
}
#[macro_export]
macro_rules! push_fs {
    () => { "
        // Push fs
        push fs

        // Load kernel tls
        mov rax, 0x18
        mov fs, ax // can't load value directly into `fs`
    " };
}

#[macro_export]
macro_rules! pop_scratch {
    () => { "
        // Pop scratch registers
        pop r11
        pop r10
        pop r9
        pop r8
        pop rsi
        pop rdi
        pop rdx
        pop rcx
    " };
}
#[macro_export]
macro_rules! pop_preserved {
    () => { "
        // Pop preserved registers
        pop r15
        pop r14
        pop r13
        pop r12
        pop rbp
        pop rbx
    " };
}
#[macro_export]
macro_rules! pop_fs {
    () => { "
        // Pop fs
        pop fs
    " };
}

#[macro_export]
macro_rules! interrupt_stack {
    ($name:ident, |$stack:ident| $code:block) => {
        paste::item! {
            #[no_mangle]
            unsafe extern "C" fn [<__interrupt_ $name>]($stack: *mut $crate::arch::x86_64::interrupt::InterruptStack) {
                #[no_mangle]
                unsafe extern "C" fn [<__interrupt_inner_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::InterruptStack) {
                    $code
                }

                $crate::arch::x86_64::pti::map();
                [<__interrupt_inner_ $name>](&mut *$stack);
                $crate::arch::x86_64::pti::unmap();
            }

            function!(stringify!($name) => {
                // Backup all userspace registers to stack
                "push rax\n",
                push_scratch!(),
                push_preserved!(),
                push_fs!(),

                // Call inner function with pointer to stack
                "mov rdi, rsp\n",
                "call __interrupt_", stringify!($name), "\n",

                // Restore all userspace registers
                pop_fs!(),
                pop_preserved!(),
                pop_scratch!(),

                "iretq\n",
            });

            extern "C" {
                pub fn $name();
            }
        }
    };
}

#[macro_export]
macro_rules! interrupt {
    ($name:ident, || $code:block) => {
        paste::item! {
            #[no_mangle]
            unsafe extern "C" fn [<__interrupt_ $name>]() {
                #[no_mangle]
                unsafe extern "C" fn [<__interrupt_inner_ $name>]() {
                    $code
                }

                $crate::arch::x86_64::pti::map();
                [<__interrupt_inner_ $name>]();
                $crate::arch::x86_64::pti::unmap();
            }

            function!(stringify!($name) => {
                // Backup all userspace registers to stack
                "push rax\n",
                push_scratch!(),
                push_fs!(),

                // Call inner function with pointer to stack
                "call __interrupt_", stringify!($name), "\n",

                // Restore all userspace registers
                pop_fs!(),
                pop_scratch!(),

                "iretq\n",
            });

            extern "C" {
                pub fn $name();
            }
        }
    };
}

#[macro_export]
macro_rules! interrupt_error {
    ($name:ident, |$stack:ident| $code:block) => {
        paste::item! {
            #[no_mangle]
            unsafe extern "C" fn [<__interrupt_ $name>]($stack: *mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) {
                #[no_mangle]
                unsafe extern "C" fn [<__interrupt_inner_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) {
                    $code
                }

                $crate::arch::x86_64::pti::map();
                [<__interrupt_inner_ $name>](&mut *$stack);
                $crate::arch::x86_64::pti::unmap();
            }

            function!(stringify!($name) => {
                // Move rax into code's place, put code in last instead (to be
                // compatible with InterruptStack)
                "xchg [rsp], rax\n",

                // Push all userspace registers
                push_scratch!(),
                push_preserved!(),
                push_fs!(),

                // Put code in, it's now in rax
                "push rax\n",

                // Call inner function with pointer to stack
                "mov rdi, rsp\n",
                "call __interrupt_", stringify!($name), "\n",

                // Pop code
                "add rsp, 8\n",

                // Restore all userspace registers
                pop_fs!(),
                pop_preserved!(),
                pop_scratch!(),

                "iretq\n",
            });

            extern "C" {
                pub fn $name();
            }
        }
    };
}
<commit_msg>fixup! WIP: Rewrite interrupts as global assembly<commit_after>use core::mem;
use syscall::IntRegisters;

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct ScratchRegisters {
    pub r11: usize,
    pub r10: usize,
    pub r9: usize,
    pub r8: usize,
    pub rsi: usize,
    pub rdi: usize,
    pub rdx: usize,
    pub rcx: usize,
    pub rax: usize,
}

impl ScratchRegisters {
    pub fn dump(&self) {
        println!("RAX:   {:>016X}", { self.rax });
        println!("RCX:   {:>016X}", { self.rcx });
        println!("RDX:   {:>016X}", { self.rdx });
        println!("RDI:   {:>016X}", { self.rdi });
        println!("RSI:   {:>016X}", { self.rsi });
        println!("R8:    {:>016X}", { self.r8 });
        println!("R9:    {:>016X}", { self.r9 });
        println!("R10:   {:>016X}", { self.r10 });
        println!("R11:   {:>016X}", { self.r11 });
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct PreservedRegisters {
    pub r15: usize,
    pub r14: usize,
    pub r13: usize,
    pub r12: usize,
    pub rbp: usize,
    pub rbx: usize,
}

impl PreservedRegisters {
    pub fn dump(&self) {
        println!("RBX:   {:>016X}", { self.rbx });
        println!("RBP:   {:>016X}", { self.rbp });
        println!("R12:   {:>016X}", { self.r12 });
        println!("R13:   {:>016X}", { self.r13 });
        println!("R14:   {:>016X}", { self.r14 });
        println!("R15:   {:>016X}", { self.r15 });
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct IretRegisters {
    pub rip: usize,
    pub cs: usize,
    pub rflags: usize,
    // Will only be present if interrupt is raised from another
    // privilege ring
    pub rsp: usize,
    pub ss: usize
}

impl IretRegisters {
    pub fn dump(&self) {
        println!("RFLAG: {:>016X}", { self.rflags });
        println!("CS:    {:>016X}", { self.cs });
        println!("RIP:   {:>016X}", { self.rip });
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct InterruptStack {
    pub fs: usize,
    pub preserved: PreservedRegisters,
    pub scratch: ScratchRegisters,
    pub iret: IretRegisters,
}

impl InterruptStack {
    pub fn dump(&self) {
        self.iret.dump();
        self.scratch.dump();
        self.preserved.dump();
        println!("FS:    {:>016X}", { self.fs });
    }
    /// Saves all registers to a struct used by the proc:
    /// scheme to read/write registers.
    pub fn save(&self, all: &mut IntRegisters) {
        all.fs = self.fs;

        all.r15 = self.preserved.r15;
        all.r14 = self.preserved.r14;
        all.r13 = self.preserved.r13;
        all.r12 = self.preserved.r12;
        all.rbp = self.preserved.rbp;
        all.rbx = self.preserved.rbx;
        all.r11 = self.scratch.r11;
        all.r10 = self.scratch.r10;
        all.r9 = self.scratch.r9;
        all.r8 = self.scratch.r8;
        all.rsi = self.scratch.rsi;
        all.rdi = self.scratch.rdi;
        all.rdx = self.scratch.rdx;
        all.rcx = self.scratch.rcx;
        all.rax = self.scratch.rax;
        all.rip = self.iret.rip;
        all.cs = self.iret.cs;
        all.rflags = self.iret.rflags;

        // Set rsp and ss:

        const CPL_MASK: usize = 0b11;

        let cs: usize;
        unsafe {
            asm!("mov $0, cs" : "=r"(cs) ::: "intel");
        }

        if self.iret.cs & CPL_MASK == cs & CPL_MASK {
            // Privilege ring didn't change, so neither did the stack
            all.rsp = self as *const Self as usize // rsp after Self was pushed to the stack
                + mem::size_of::<Self>() // disregard Self
                - mem::size_of::<usize>() * 2; // well, almost: rsp and ss need to be excluded as they aren't present
            unsafe {
                asm!("mov $0, ss" : "=r"(all.ss) ::: "intel");
            }
        } else {
            all.rsp = self.iret.rsp;
            all.ss = self.iret.ss;
        }
    }
    /// Loads all registers from a struct used by the proc:
    /// scheme to read/write registers.
    pub fn load(&mut self, all: &IntRegisters) {
        // TODO: Which of these should be allowed to change?

        // self.fs = all.fs;
        self.preserved.r15 = all.r15;
        self.preserved.r14 = all.r14;
        self.preserved.r13 = all.r13;
        self.preserved.r12 = all.r12;
        self.preserved.rbp = all.rbp;
        self.preserved.rbx = all.rbx;
        self.scratch.r11 = all.r11;
        self.scratch.r10 = all.r10;
        self.scratch.r9 = all.r9;
        self.scratch.r8 = all.r8;
        self.scratch.rsi = all.rsi;
        self.scratch.rdi = all.rdi;
        self.scratch.rdx = all.rdx;
        self.scratch.rcx = all.rcx;
        self.scratch.rax = all.rax;
        self.iret.rip = all.rip;

        // These should probably be restricted
        // self.iret.cs = all.cs;
        // self.iret.rflags = all.eflags;
    }
    /// Enables the "Trap Flag" in the FLAGS register, causing the CPU
    /// to send a Debug exception after the next instruction. This is
    /// used for singlestep in the proc: scheme.
    pub fn set_singlestep(&mut self, enabled: bool) {
        if enabled {
            self.iret.rflags |= 1 << 8;
        } else {
            self.iret.rflags &= !(1 << 8);
        }
    }
    /// Checks if the trap flag is enabled, see `set_singlestep`
    pub fn is_singlestep(&self) -> bool {
        self.iret.rflags & 1 << 8 == 1 << 8
    }
}

#[allow(dead_code)]
#[derive(Default)]
#[repr(packed)]
pub struct InterruptErrorStack {
    pub code: usize,
    pub inner: InterruptStack,
}

impl InterruptErrorStack {
    pub fn dump(&self) {
        println!("CODE:  {:>016X}", { self.code });
        self.inner.dump();
    }
}

#[macro_export]
macro_rules! intel_asm {
    ($($strings:expr,)+) => {
        global_asm!(concat!(
            ".intel_syntax noprefix\n",
            $($strings),+,
            ".att_syntax prefix\n",
        ));
    };
}
#[macro_export]
macro_rules! function {
    ($name:expr => { $($body:expr,)+ }) => {
        intel_asm!(
            ".global ", $name, "\n",
            $name, ":\n",
            $($body,)+
        );
    }
}

#[macro_export]
macro_rules! push_scratch {
    () => { "
        // Push scratch registers
        push rcx
        push rdx
        push rdi
        push rsi
        push r8
        push r9
        push r10
        push r11
    " };
}
#[macro_export]
macro_rules! push_preserved {
    () => { "
        // Push preserved registers
        push rbx
        push rbp
        push r12
        push r13
        push r14
        push r15
    " };
}
#[macro_export]
macro_rules! push_fs {
    () => { "
        // Push fs
        push fs

        // Load kernel tls
        mov rax, 0x18
        mov fs, ax // can't load value directly into `fs`
    " };
}

#[macro_export]
macro_rules! pop_scratch {
    () => { "
        // Pop scratch registers
        pop r11
        pop r10
        pop r9
        pop r8
        pop rsi
        pop rdi
        pop rdx
        pop rcx
        pop rax
    " };
}
#[macro_export]
macro_rules! pop_preserved {
    () => { "
        // Pop preserved registers
        pop r15
        pop r14
        pop r13
        pop r12
        pop rbp
        pop rbx
    " };
}
#[macro_export]
macro_rules! pop_fs {
    () => { "
        // Pop fs
        pop fs
    " };
}

#[macro_export]
macro_rules! interrupt_stack {
    ($name:ident, |$stack:ident| $code:block) => {
        paste::item! {
            #[no_mangle]
            unsafe extern "C" fn [<__interrupt_ $name>]($stack: *mut $crate::arch::x86_64::interrupt::InterruptStack) {
                unsafe fn [<__interrupt_inner_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::InterruptStack) {
                    $code
                }

                $crate::arch::x86_64::pti::map();
                [<__interrupt_inner_ $name>](&mut *$stack);
                $crate::arch::x86_64::pti::unmap();
            }

            function!(stringify!($name) => {
                // Backup all userspace registers to stack
                "push rax\n",
                push_scratch!(),
                push_preserved!(),
                push_fs!(),

                // Call inner function with pointer to stack
                "mov rdi, rsp\n",
                "call __interrupt_", stringify!($name), "\n",

                // Restore all userspace registers
                pop_fs!(),
                pop_preserved!(),
                pop_scratch!(),

                "iretq\n",
            });

            extern "C" {
                pub fn $name();
            }
        }
    };
}

#[macro_export]
macro_rules! interrupt {
    ($name:ident, || $code:block) => {
        paste::item! {
            #[no_mangle]
            unsafe extern "C" fn [<__interrupt_ $name>]() {
                unsafe fn [<__interrupt_inner_ $name>]() {
                    $code
                }

                $crate::arch::x86_64::pti::map();
                [<__interrupt_inner_ $name>]();
                $crate::arch::x86_64::pti::unmap();
            }

            function!(stringify!($name) => {
                // Backup all userspace registers to stack
                "push rax\n",
                push_scratch!(),
                push_fs!(),

                // Call inner function with pointer to stack
                "call __interrupt_", stringify!($name), "\n",

                // Restore all userspace registers
                pop_fs!(),
                pop_scratch!(),

                "iretq\n",
            });

            extern "C" {
                pub fn $name();
            }
        }
    };
}

#[macro_export]
macro_rules! interrupt_error {
    ($name:ident, |$stack:ident| $code:block) => {
        paste::item! {
            #[no_mangle]
            unsafe extern "C" fn [<__interrupt_ $name>]($stack: *mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) {
                unsafe fn [<__interrupt_inner_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) {
                    $code
                }

                $crate::arch::x86_64::pti::map();
                [<__interrupt_inner_ $name>](&mut *$stack);
                $crate::arch::x86_64::pti::unmap();
            }

            function!(stringify!($name) => {
                // Move rax into code's place, put code in last instead (to be
                // compatible with InterruptStack)
                "xchg [rsp], rax\n",

                // Push all userspace registers
                push_scratch!(),
                push_preserved!(),
                push_fs!(),

                // Put code in, it's now in rax
                "push rax\n",

                // Call inner function with pointer to stack
                "mov rdi, rsp\n",
                "call __interrupt_", stringify!($name), "\n",

                // Pop code
                "add rsp, 8\n",

                // Restore all userspace registers
                pop_fs!(),
                pop_preserved!(),
                pop_scratch!(),

                "iretq\n",
            });

            extern "C" {
                pub fn $name();
            }
        }
    };
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -O -C no-prepopulate-passes

#![crate_type = "lib"]

// CHECK-LABEL: @test
#[no_mangle]
pub fn test() {
    let a = 0;
    &a; // keep variable in an alloca

// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_a]])

    {
        let b = &Some(a);
        &b; // keep variable in an alloca

// CHECK: [[S_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_b]])

// CHECK: [[S__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S__5]])

// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_b]])

// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E__5]])
    }

    let c = 1;
    &c; // keep variable in an alloca

// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S_c]])

// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_c]])

// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_a]])
}
<commit_msg>Relax a codegen test to be compatible with LLVM 5.0<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// compile-flags: -O -C no-prepopulate-passes

#![crate_type = "lib"]

// CHECK-LABEL: @test
#[no_mangle]
pub fn test() {
    let a = 0;
    &a; // keep variable in an alloca

// CHECK: [[S_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_a]])

    {
        let b = &Some(a);
        &b; // keep variable in an alloca

// CHECK: [[S_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_b]])

// CHECK: [[S__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S__5]])

// CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option<i32>"** %b to i8*
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_b]])

// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option<i32>"* %_5 to i8*
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E__5]])
    }

    let c = 1;
    &c; // keep variable in an alloca

// CHECK: [[S_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, i8* [[S_c]])

// CHECK: [[E_c:%[0-9]+]] = bitcast i32* %c to i8*
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_c]])

// CHECK: [[E_a:%[0-9]+]] = bitcast i32* %a to i8*
// CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, i8* [[E_a]])
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use context;
use dispatcher::request::{InternalRequest, Request, RequestHandler};
use dispatcher::response::ResponseHandler;
use dispatcher::Response;
use context::event::EventHandler;
use message::{PatternId};
use reactor;

pub struct MessageEventHandler {
    handlers: HashMap<PatternId, Vec<Rc<RefCell<Box<context::event::EventHandler<InternalRequest>>>>>>,
    keyless_handlers: Vec<Rc<RefCell<Box<context::event::EventHandler<InternalRequest>>>>>,
    response_handler: Rc<RefCell<Box<ResponseHandler<Response>>>>,
}

impl MessageEventHandler {
    pub fn new(response_handler: Rc<RefCell<Box<ResponseHandler<Response>>>>) -> MessageEventHandler {
        MessageEventHandler{
            handlers: HashMap::new(),
            keyless_handlers: Vec::new(),
            response_handler: response_handler
        }
    }

    pub fn register_handler(&mut self, handler: Rc<RefCell<Box<context::event::EventHandler<InternalRequest>>>>) {
        let cloned_handler = handler.clone();
        if handler.borrow().handlers().is_empty() {
            self.keyless_handlers.push(cloned_handler);
        } else {
            for key in cloned_handler.borrow().handlers() {
                println!("key: {:?}", key);
                let handlers = self.handlers.entry(key.clone()).or_insert(Vec::new());
                handlers.push(cloned_handler.clone());
            }
        }
    }
}

impl reactor::EventHandler<InternalRequest> for MessageEventHandler {
    type Handler = RequestHandler;
    fn handle_event(&mut self, event: InternalRequest) {
        println!("map: {:?}", self.handlers.len());
        if let Request::Message(event) = event {
            println!("message event");
            if let Some(name) = event.name() {
                println!("message has a name");
                if let Some(handlers) = self.handlers.get_mut(name) {
                    for i in handlers.iter_mut() {
                        if let Some(result) = i.borrow_mut().handle_event(Request::Message(event.clone())) {
                            for i in result.into_iter() {
                                self.response_handler.borrow_mut().handle_response(i.into());
                            }
                        }
                    }
                }
            } else {
                println!("uuid: {:?}", event.uuid());
                if let Some(handlers) = self.handlers.get_mut(event.uuid()) {
                    for i in handlers.iter_mut() {
                        if let Some(result) = i.borrow_mut().handle_event(Request::Message(event.clone())) {
                            for i in result.into_iter() {
                                self.response_handler.borrow_mut().handle_response(i.into());
                            }
                        }
                    }
                } else {
                    println!("no handler found for this message");
                }
            }

            for i in self.keyless_handlers.iter_mut() {
                i.borrow_mut().handle_event(Request::Message(event.clone()));
            }
        } else {
            unreachable!("MessageEventHandler should only handle Message events");
        }
    }
    fn handler(&self) -> Self::Handler {
        RequestHandler::Message
    }
}
<commit_msg>MessageEventHandler: add a unit test<commit_after>use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use context;
use dispatcher::request::{InternalRequest, Request, RequestHandler};
use dispatcher::response::ResponseHandler;
use dispatcher::Response;
use context::event::EventHandler;
use message::{PatternId};
use reactor;

pub struct MessageEventHandler {
    handlers: HashMap<PatternId, Vec<Rc<RefCell<Box<context::event::EventHandler<InternalRequest>>>>>>,
    keyless_handlers: Vec<Rc<RefCell<Box<context::event::EventHandler<InternalRequest>>>>>,
    response_handler: Rc<RefCell<Box<ResponseHandler<Response>>>>,
}

impl MessageEventHandler {
    pub fn new(response_handler: Rc<RefCell<Box<ResponseHandler<Response>>>>) -> MessageEventHandler {
        MessageEventHandler{
            handlers: HashMap::new(),
            keyless_handlers: Vec::new(),
            response_handler: response_handler
        }
    }

    pub fn register_handler(&mut self, handler: Rc<RefCell<Box<context::event::EventHandler<InternalRequest>>>>) {
        let cloned_handler = handler.clone();
        if handler.borrow().handlers().is_empty() {
            self.keyless_handlers.push(cloned_handler);
        } else {
            for key in cloned_handler.borrow().handlers() {
                println!("key: {:?}", key);
                let handlers = self.handlers.entry(key.clone()).or_insert(Vec::new());
                handlers.push(cloned_handler.clone());
            }
        }
    }
}

impl reactor::EventHandler<InternalRequest> for MessageEventHandler {
    type Handler = RequestHandler;
    fn handle_event(&mut self, event: InternalRequest) {
        println!("map: {:?}", self.handlers.len());
        if let Request::Message(event) = event {
            println!("message event");
            if let Some(name) = event.name() {
                println!("message has a name");
                if let Some(handlers) = self.handlers.get_mut(name) {
                    for i in handlers.iter_mut() {
                        if let Some(result) = i.borrow_mut().handle_event(Request::Message(event.clone())) {
                            for i in result.into_iter() {
                                self.response_handler.borrow_mut().handle_response(i.into());
                            }
                        }
                    }
                }
            } else {
                println!("uuid: {:?}", event.uuid());
                if let Some(handlers) = self.handlers.get_mut(event.uuid()) {
                    for i in handlers.iter_mut() {
                        if let Some(result) = i.borrow_mut().handle_event(Request::Message(event.clone())) {
                            for i in result.into_iter() {
                                self.response_handler.borrow_mut().handle_response(i.into());
                            }
                        }
                    }
                } else {
                    println!("no handler found for this message");
                }
            }

            for i in self.keyless_handlers.iter_mut() {
                i.borrow_mut().handle_event(Request::Message(event.clone()));
            }
        } else {
            unreachable!("MessageEventHandler should only handle Message events");
        }
    }
    fn handler(&self) -> Self::Handler {
        RequestHandler::Message
    }
}

#[cfg(test)]
mod test {
    use uuid::Uuid;
    use std::cell::RefCell;
    use std::rc::Rc;

    use action::ExecResult;
    use context;
    use dispatcher::request::{InternalRequest, Request};
    use dispatcher::response::ResponseHandler;
    use dispatcher::Response;
    use message::{Builder, PatternId};
    use reactor::EventHandler;

    use super::MessageEventHandler;

    struct DummyResponseHandler(Rc<RefCell<i32>>);

    impl ResponseHandler<Response> for DummyResponseHandler {
        fn handle_response(&mut self, _: Response) {
            *self.0.borrow_mut() += 1;
        }
    }

    struct DummyEventHandler {
        counter: Rc<RefCell<i32>>,
        ids: Vec<PatternId>
    }

    impl context::event::EventHandler<InternalRequest> for DummyEventHandler {
        fn handle_event(&mut self, _: InternalRequest) -> Option<Vec<ExecResult>> {
            *self.counter.borrow_mut() += 1;
            None
        }
        fn handlers(&self) -> &[PatternId] {
            &self.ids
        }
    }

    #[test]
    fn test_given_message_event_handler_when_it_receives_a_message_then_it_calls_the_right_handler() {
        let uuid1 = "1b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
        let uuid2 = "2b47ba91-d867-4a8c-9553-a5dfd6ea1274".to_string();
        let response_handler_counter = Rc::new(RefCell::new(0));
        let response_handler: Box<ResponseHandler<Response>> = Box::new(DummyResponseHandler(response_handler_counter.clone()));
        let response_handler = Rc::new(RefCell::new(response_handler));
        let ids_1 = vec![ PatternId::Uuid(Uuid::parse_str(&uuid1).unwrap()) ];
        let ids_2 = vec![ PatternId::Uuid(Uuid::parse_str(&uuid2).unwrap()) ];
        let event_handler_counter_1 = Rc::new(RefCell::new(0));
        let event_handler_counter_2 = Rc::new(RefCell::new(0));
        let event_handler_1: Box<context::event::EventHandler<InternalRequest>> = Box::new(DummyEventHandler{counter: event_handler_counter_1.clone(), ids: ids_1});
        let event_handler_2: Box<context::event::EventHandler<InternalRequest>> = Box::new(DummyEventHandler{counter: event_handler_counter_2.clone(), ids: ids_2});
        let event_handler_1 = Rc::new(RefCell::new(event_handler_1));
        let event_handler_2 = Rc::new(RefCell::new(event_handler_2));
        let mut message_event_handler = MessageEventHandler::new(response_handler.clone());
        message_event_handler.register_handler(event_handler_1);
        message_event_handler.register_handler(event_handler_2);
        message_event_handler.handle_event(Request::Message(Rc::new(Builder::new(&uuid1).build())));
        assert_eq!(1, *event_handler_counter_1.borrow());
        message_event_handler.handle_event(Request::Message(Rc::new(Builder::new(&uuid1).build())));
        assert_eq!(2, *event_handler_counter_1.borrow());
        assert_eq!(0, *event_handler_counter_2.borrow());
        assert_eq!(0, *response_handler_counter.borrow());
    }
}
<|endoftext|>