<commit_before>//  Copyright 2015 Nathanael Merrill
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use playlist;

use rustty::{
    Terminal,
    Event,
    HasSize,
    CellAccessor,
    Cell,
    Attr,
    Color,
};
use rustty::ui::{
    Painter,
    Dialog,
    Widget,
    Alignable,
    HorizontalAlign,
    VerticalAlign,
};

use std::error::Error;

// Create the dialog that shows the keybindings. Should probably be removed.
fn create_info_dlg(length: usize) -> Dialog {
    let mut optiondlg = Dialog::new(length as usize, 4);

    let inc_label = "space --> play/pause";
    let q_label   = "x     --> exit program";
    let n_label   = "n --> next song";
    let pp_label  = "p --> prev song";

    let inc_pos = optiondlg.window().halign_line(inc_label, HorizontalAlign::Left, 1);
    let q_pos = optiondlg.window().halign_line(q_label, HorizontalAlign::Left, 1);
    let n_pos = optiondlg.window().halign_line(n_label, HorizontalAlign::Middle, 1);
    let pp_pos = optiondlg.window().halign_line(pp_label, HorizontalAlign::Middle, 1);

    optiondlg.window_mut().printline(inc_pos, 1, inc_label);
    optiondlg.window_mut().printline(q_pos, 2, q_label);
    optiondlg.window_mut().printline(n_pos, 1, n_label);
    optiondlg.window_mut().printline(pp_pos, 2, pp_label);
    optiondlg.window_mut().draw_box();
    optiondlg
}

// Tells main what happened to the UI and if the user sent any input.
pub enum UIResult {
    PlayPause,
    Next,
    Previous,
    Exit,
    Error(String),
    NA,
}

pub struct UI {
    term: Terminal,
    optiondlg: Dialog,
    canvas: Widget,
    list_canvas: Widget,
    length: usize,
    height: usize,
    time: (i32, i32),
}

impl UI {
    pub fn new(playlist: &playlist::Playlist) -> UI {
        // Create our terminal, dialog window and main canvasa
        let term = Terminal::new().expect("Failed to make Rustty terminal");
        let length = term.cols();
        let height = term.rows();

        // aligns everything
        let mut optiondlg = create_info_dlg(length);
        let mut canvas = Widget::new(length as usize, 2);
        let mut list_canvas = Widget::new(length as usize,playlist.songs.len());
        optiondlg
            .window_mut()
            .align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 0);
        canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 4);
        list_canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Top, 0);

        UI {term: term,
            optiondlg: optiondlg,
            canvas: canvas,
            list_canvas: list_canvas,
            length: length,
            height: height,
            time: (-1,-1),
        }
    }

    // Updates the User Interface and gets user input.
    pub fn manage_ui(&mut self, playlist: &playlist::Playlist,
                     time: i32, totaltime: i32) -> UIResult {
        //TODO: The rest of this function won't run if there is input
        //this is not really ideal
        while let Some(Event::Key(ch)) = self.term.get_event(0).unwrap() {
            match ch {
                ' ' => return UIResult::PlayPause,
                'p' => return UIResult::Previous,
                'n' => return UIResult::Next,
                'x' => return UIResult::Exit,
                _  => return UIResult::NA,
            }
        }

        if self.time != (time, totaltime) {
            self.time = (time, totaltime);

            if totaltime/60 > 99 {
                return UIResult::Error(String::from(
                    "RFSMP does not support songs longer than 100 minutes"));
            }

            let curr_song = playlist.get_curr_song().unwrap_or("");
            let length_i32 = self.length as i32;
            let mut spaces = (curr_song.len() +
                ((totaltime/60).to_string().len()+3)*2) as i32;
            spaces = length_i32 - spaces - 8;
            spaces = spaces / 2;

            let spaces_l = vec![' '; spaces as usize].into_iter().collect::<String>();
            let spaces_r = match (length_i32-curr_song.len() as i32).wrapping_rem(2) {
                0 => spaces_l.clone(),
                1 => spaces_l.clone() + " ",
                _ => unreachable!(),
            };
            let status_chars: Vec<char> = format!("--{:0>7$}:{:0>2}{}--{}--{}{:0>7$}:{:0>2}--",
                                  time/60, time%60, spaces_l, curr_song, spaces_r,
                                  totaltime/60, totaltime%60,
                                  (totaltime/60).to_string().len())
                                  .chars().collect();

            let (cols, rows) = self.canvas.size();
            let (cols, rows) = (cols as isize, rows as isize);
            let number_of_x;

            if totaltime == 0 {
                number_of_x = 0;
            }
            else {
                number_of_x = (time as f32 / totaltime as f32
                         * length_i32 as f32).round() as i32;
            }

            let mut load_chars = vec!['x'; number_of_x as usize];
            load_chars.append(&mut vec!['-'; (length_i32 - number_of_x) as usize]);

            for x in 0..cols {
                for y in 0..rows {
                    let fep ='*';
                    let mut cell = match self.canvas.get_mut(x as usize, y as usize) {
                        Some(a) => a,
                        None => return UIResult::Error(
                            "Could not draw to screen".to_string()),
                    };
                    match y {
                        0 => cell.set_ch(*status_chars.get(x as usize).unwrap_or_else(|| &fep)),
                        1 => cell.set_ch(*load_chars.get(x as usize).unwrap_or_else(|| &fep)),
                        _ => unreachable!(),
                    };
                }
            }

            let mut a = self.length_checker();
            if a != "" {
                return UIResult::Error(a);
            }
            a = self.second_panel(playlist, curr_song);
            if a != "" {
                return UIResult::Error(a);
            }
            self.canvas.draw_into(&mut self.term);
            self.list_canvas.draw_into(&mut self.term);
            self.optiondlg.window().draw_into(&mut self.term);
            match self.term.swap_buffers() {
                Ok(_) => {},
                Err(e) => return UIResult::Error(
                    String::from("Swap buffers error: ".to_string() + e.description())),
            }
        }
        return UIResult::NA;
    }

    // Draws the list of songs.
    fn second_panel(&mut self, playlist: &playlist::Playlist, curr_song: &str)
                    -> String {
        let cell = Cell::with_style(Color::Black, Color::Red, Attr::Default);
        let cellother = Cell::with_style(Color::Default,
                                         Color::Default,
                                         Attr::Default);
        let (_, rows) = self.list_canvas.size();

        for i in 0..rows {
            let song = match playlist.songs.get(i) {
                Some(a) => a,
                None => return String::from("Failed to look through songs"),
            };
            self.list_canvas
                .printline_with_cell(0, i as usize, song,
                                     match curr_song == song {
                                         true => cell,
                                         false => cellother,
                                     });
        }
        return String::from("");
    }

    // Checks to see if the terminal has changed size.
    fn length_checker(&mut self) -> String {
        let last_pos = (self.length, self.height);
        self.length = self.term.cols();
        self.height =  self.term.rows();
        if last_pos != (self.term.cols(), self.term.rows()){
            match self.term.clear() {
                Ok(_) => {},
                Err(e) => {
                    return "Terminal clear error: ".to_string() + e.description()
                }
            }
            self.optiondlg = create_info_dlg(self.length);
            self.canvas = Widget::new(self.length as usize, 2);
            self.optiondlg
                .window_mut()
                .align(&self.term,
                       HorizontalAlign::Middle,
                       VerticalAlign::Bottom, 0);
            self.canvas.align(&self.term,
                              HorizontalAlign::Middle,
                              VerticalAlign::Bottom, 6);
            self.list_canvas.align(&self.term, HorizontalAlign::Left,
                                   VerticalAlign::Top, 0);
        }
        return "".to_string();
    }
}
<commit_msg>Improve redraw on resize.<commit_after>//  Copyright 2015 Nathanael Merrill
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

use playlist;

use rustty::{
    Terminal,
    Event,
    HasSize,
    CellAccessor,
    Cell,
    Attr,
    Color,
};
use rustty::ui::{
    Painter,
    Dialog,
    Widget,
    Alignable,
    HorizontalAlign,
    VerticalAlign,
};

use std::error::Error;

// Create the dialog that shows the keybindings. Should probably be removed.
fn create_info_dlg(length: usize) -> Dialog {
    let mut optiondlg = Dialog::new(length as usize, 4);

    let inc_label = "space --> play/pause";
    let q_label   = "x     --> exit program";
    let n_label   = "n --> next song";
    let pp_label  = "p --> prev song";

    let inc_pos = optiondlg.window().halign_line(inc_label, HorizontalAlign::Left, 1);
    let q_pos = optiondlg.window().halign_line(q_label, HorizontalAlign::Left, 1);
    let n_pos = optiondlg.window().halign_line(n_label, HorizontalAlign::Middle, 1);
    let pp_pos = optiondlg.window().halign_line(pp_label, HorizontalAlign::Middle, 1);

    optiondlg.window_mut().printline(inc_pos, 1, inc_label);
    optiondlg.window_mut().printline(q_pos, 2, q_label);
    optiondlg.window_mut().printline(n_pos, 1, n_label);
    optiondlg.window_mut().printline(pp_pos, 2, pp_label);
    optiondlg.window_mut().draw_box();
    optiondlg
}

// Tells main what happened to the UI and if the user sent any input.
pub enum UIResult {
    PlayPause,
    Next,
    Previous,
    Exit,
    Error(String),
    NA,
}

pub struct UI {
    term: Terminal,
    optiondlg: Dialog,
    canvas: Widget,
    list_canvas: Widget,
    length: usize,
    height: usize,
    time: (i32, i32),
}

impl UI {
    pub fn new(playlist: &playlist::Playlist) -> UI {
        // Create our terminal, dialog window and main canvasa
        let term = Terminal::new().expect("Failed to make Rustty terminal");
        let length = term.cols();
        let height = term.rows();

        // aligns everything
        let mut optiondlg = create_info_dlg(length);
        let mut canvas = Widget::new(length as usize, 2);
        let mut list_canvas = Widget::new(length as usize,playlist.songs.len());
        optiondlg
            .window_mut()
            .align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 0);
        canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 4);
        list_canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Top, 0);

        UI {term: term,
            optiondlg: optiondlg,
            canvas: canvas,
            list_canvas: list_canvas,
            length: length,
            height: height,
            time: (-1,-1),
        }
    }

    // Updates the User Interface and gets user input.
    pub fn manage_ui(&mut self, playlist: &playlist::Playlist,
                     time: i32, totaltime: i32) -> UIResult {
        //TODO: The rest of this function won't run if there is input
        //this is not really ideal
        while let Some(Event::Key(ch)) = self.term.get_event(0).unwrap() {
            match ch {
                ' ' => return UIResult::PlayPause,
                'p' => return UIResult::Previous,
                'n' => return UIResult::Next,
                'x' => return UIResult::Exit,
                _  => return UIResult::NA,
            }
        }

        if self.time != (time, totaltime) ||
            (self.length, self.height) != (self.term.cols(), self.term.rows()){

            let mut a = self.length_checker();
            if a != "" {
                return UIResult::Error(a);
            }

            self.time = (time, totaltime);

            if totaltime/60 > 99 {
                return UIResult::Error(String::from(
                    "RFSMP does not support songs longer than 100 minutes"));
            }

            let curr_song = playlist.get_curr_song().unwrap_or("");
            let length_i32 = self.length as i32;
            let mut spaces = (curr_song.len() +
                ((totaltime/60).to_string().len()+3)*2) as i32;
            spaces = length_i32 - spaces - 8;
            spaces = spaces / 2;

            let spaces_l = vec![' '; spaces as usize].into_iter().collect::<String>();
            let spaces_r = match (length_i32-curr_song.len() as i32).wrapping_rem(2) {
                0 => spaces_l.clone(),
                1 => spaces_l.clone() + " ",
                _ => unreachable!(),
            };
            let status_chars: Vec<char> = format!("--{:0>7$}:{:0>2}{}--{}--{}{:0>7$}:{:0>2}--",
                                  time/60, time%60, spaces_l, curr_song, spaces_r,
                                  totaltime/60, totaltime%60,
                                  (totaltime/60).to_string().len())
                                  .chars().collect();

            let (cols, rows) = self.canvas.size();
            let (cols, rows) = (cols as isize, rows as isize);
            let number_of_x;

            if totaltime == 0 {
                number_of_x = 0;
            }
            else {
                number_of_x = (time as f32 / totaltime as f32
                         * length_i32 as f32).round() as i32;
            }

            let mut load_chars = vec!['x'; number_of_x as usize];
            load_chars.append(&mut vec!['-'; (length_i32 - number_of_x) as usize]);

            for x in 0..cols {
                for y in 0..rows {
                    let fep ='*';
                    let mut cell = match self.canvas.get_mut(x as usize, y as usize) {
                        Some(a) => a,
                        None => return UIResult::Error(
                            "Could not draw to screen".to_string()),
                    };
                    match y {
                        0 => cell.set_ch(*status_chars.get(x as usize).unwrap_or_else(|| &fep)),
                        1 => cell.set_ch(*load_chars.get(x as usize).unwrap_or_else(|| &fep)),
                        _ => unreachable!(),
                    };
                }
            }

            a = self.second_panel(playlist, curr_song);
            if a != "" {
                return UIResult::Error(a);
            }
            self.canvas.draw_into(&mut self.term);
            self.list_canvas.draw_into(&mut self.term);
            self.optiondlg.window().draw_into(&mut self.term);
            match self.term.swap_buffers() {
                Ok(_) => {},
                Err(e) => return UIResult::Error(
                    String::from("Swap buffers error: ".to_string() + e.description())),
            }
        }
        return UIResult::NA;
    }

    // Draws the list of songs.
    fn second_panel(&mut self, playlist: &playlist::Playlist, curr_song: &str)
                    -> String {
        let cell = Cell::with_style(Color::Black, Color::Red, Attr::Default);
        let cellother = Cell::with_style(Color::Default,
                                         Color::Default,
                                         Attr::Default);
        let (_, rows) = self.list_canvas.size();

        for i in 0..rows {
            let song = match playlist.songs.get(i) {
                Some(a) => a,
                None => return String::from("Failed to look through songs"),
            };
            self.list_canvas
                .printline_with_cell(0, i as usize, song,
                                     match curr_song == song {
                                         true => cell,
                                         false => cellother,
                                     });
        }
        return String::from("");
    }

    // Checks to see if the terminal has changed size.
    fn length_checker(&mut self) -> String {
        let last_pos = (self.length, self.height);
        self.length = self.term.cols();
        self.height =  self.term.rows();
        if last_pos != (self.term.cols(), self.term.rows()){
            match self.term.clear() {
                Ok(_) => {},
                Err(e) => {
                    return "Terminal clear error: ".to_string() + e.description()
                }
            }
            self.optiondlg = create_info_dlg(self.length);
            self.canvas = Widget::new(self.length as usize, 2);
            self.optiondlg
                .window_mut()
                .align(&self.term,
                       HorizontalAlign::Middle,
                       VerticalAlign::Bottom, 0);
            self.canvas.align(&self.term,
                              HorizontalAlign::Middle,
                              VerticalAlign::Bottom, 4);
            self.list_canvas.align(&self.term, HorizontalAlign::Left,
                                   VerticalAlign::Top, 0);
        }
        return "".to_string();
    }
}
<|endoftext|>


<commit_before>use std::collections::{VecDeque};
use std::net::{SocketAddr};

use mio::{Handler, EventLoop, Token, EventSet, PollOpt};
use mio::udp::{UdpSocket};

use buffer::{BufferPool, Buffer};
use provider::{self, Provider};

/// Handles events occurring within the event loop.
pub trait Dispatcher: Sized {
    type Timeout;
    type Message: Send;

    /// Process an incoming message from the given address.
    fn incoming<'a>(&mut self, provider: Provider<'a, Self>, message: &[u8], addr: SocketAddr) { }

    /// Process a message sent via the event loop channel.
    fn notify<'a>(&mut self, provider: Provider<'a, Self>, message: Self::Message) { }

    /// Process a timeout that has been triggered.
    fn timeout<'a>(&mut self, provider: Provider<'a, Self>, timeout: Self::Timeout) { }
}

//----------------------------------------------------------------------------//

const UDP_SOCKET_TOKEN: Token = Token(2);

pub struct DispatchHandler<D: Dispatcher> {
    dispatch:    D,
    out_queue:   VecDeque<(Buffer, SocketAddr)>,
    udp_socket:  UdpSocket,
    buffer_pool: BufferPool,
    current_set: EventSet
}

impl<D: Dispatcher> DispatchHandler<D> {
    pub fn new(udp_socket: UdpSocket, buffer_size: usize, dispatch: D, event_loop: &mut EventLoop<DispatchHandler<D>>)
        -> DispatchHandler<D> {
        let buffer_pool = BufferPool::new(buffer_size);
        let out_queue = VecDeque::new();

        event_loop.register(&udp_socket, UDP_SOCKET_TOKEN, EventSet::readable(), PollOpt::level()).unwrap();

        DispatchHandler{ dispatch: dispatch, out_queue: out_queue, udp_socket: udp_socket,
            buffer_pool: buffer_pool, current_set: EventSet::readable() }
    }

    pub fn handle_write(&mut self) {
        match self.out_queue.pop_front() {
            Some((buffer, addr)) => {
                self.udp_socket.send_to(buffer.as_ref(), &addr).unwrap();

                self.buffer_pool.push(buffer);
            },
            None => ()
        };
    }

    pub fn handle_read(&mut self) -> Option<(Buffer, SocketAddr)> {
        let mut buffer = self.buffer_pool.pop();

        if let Ok(Some((bytes, addr))) = self.udp_socket.recv_from(buffer.as_mut()) {
            buffer.set_written(bytes);

            Some((buffer, addr))
        } else {
            None
        }
    }
}

impl<D: Dispatcher> Handler for DispatchHandler<D> {
    type Timeout = D::Timeout;
    type Message = D::Message;

    fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
        if token != UDP_SOCKET_TOKEN {
            return
        }

        if events.is_writable() {
            self.handle_write();
        }

        if events.is_readable() {
            let (buffer, addr) = if let Some((buffer, addr)) = self.handle_read() {
                (buffer, addr)
            } else { return };

            {
                let provider = provider::new(&mut self.buffer_pool, &mut self.out_queue, event_loop);

                self.dispatch.incoming(provider, buffer.as_ref(), addr);
            }

            self.buffer_pool.push(buffer);
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
        let provider = provider::new(&mut self.buffer_pool, &mut self.out_queue, event_loop);

        self.dispatch.notify(provider, msg);
    }

    fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
        let provider = provider::new(&mut self.buffer_pool, &mut self.out_queue, event_loop);

        self.dispatch.timeout(provider, timeout);
    }

    fn tick(&mut self, event_loop: &mut EventLoop<Self>) {
        self.current_set = if !self.out_queue.is_empty() {
            EventSet::readable() | EventSet::writable()
        } else {
            EventSet::readable()
        };

        event_loop.reregister(&self.udp_socket, UDP_SOCKET_TOKEN, self.current_set, PollOpt::level()).unwrap();
    }
}<commit_msg>Add Allow Unused To Dispatcher Methods; Switch Level Triggers To Edge Triggers<commit_after>use std::collections::{VecDeque};
use std::net::{SocketAddr};

use mio::{Handler, EventLoop, Token, EventSet, PollOpt};
use mio::udp::{UdpSocket};

use buffer::{BufferPool, Buffer};
use provider::{self, Provider};

/// Handles events occurring within the event loop.
pub trait Dispatcher: Sized {
    type Timeout;
    type Message: Send;

    /// Process an incoming message from the given address.
    #[allow(unused)]
    fn incoming<'a>(&mut self, provider: Provider<'a, Self>, message: &[u8], addr: SocketAddr) { }

    /// Process a message sent via the event loop channel.
    #[allow(unused)]
    fn notify<'a>(&mut self, provider: Provider<'a, Self>, message: Self::Message) { }

    /// Process a timeout that has been triggered.
    #[allow(unused)]
    fn timeout<'a>(&mut self, provider: Provider<'a, Self>, timeout: Self::Timeout) { }
}

//----------------------------------------------------------------------------//

const UDP_SOCKET_TOKEN: Token = Token(2);

pub struct DispatchHandler<D: Dispatcher> {
    dispatch:    D,
    out_queue:   VecDeque<(Buffer, SocketAddr)>,
    udp_socket:  UdpSocket,
    buffer_pool: BufferPool,
    current_set: EventSet
}

impl<D: Dispatcher> DispatchHandler<D> {
    pub fn new(udp_socket: UdpSocket, buffer_size: usize, dispatch: D, event_loop: &mut EventLoop<DispatchHandler<D>>)
        -> DispatchHandler<D> {
        let buffer_pool = BufferPool::new(buffer_size);
        let out_queue = VecDeque::new();

        event_loop.register(&udp_socket, UDP_SOCKET_TOKEN, EventSet::readable(), PollOpt::edge()).unwrap();

        DispatchHandler{ dispatch: dispatch, out_queue: out_queue, udp_socket: udp_socket,
            buffer_pool: buffer_pool, current_set: EventSet::readable() }
    }

    pub fn handle_write(&mut self) {
        match self.out_queue.pop_front() {
            Some((buffer, addr)) => {
                self.udp_socket.send_to(buffer.as_ref(), &addr).unwrap();

                self.buffer_pool.push(buffer);
            },
            None => ()
        };
    }

    pub fn handle_read(&mut self) -> Option<(Buffer, SocketAddr)> {
        let mut buffer = self.buffer_pool.pop();

        if let Ok(Some((bytes, addr))) = self.udp_socket.recv_from(buffer.as_mut()) {
            buffer.set_written(bytes);

            Some((buffer, addr))
        } else {
            None
        }
    }
}

impl<D: Dispatcher> Handler for DispatchHandler<D> {
    type Timeout = D::Timeout;
    type Message = D::Message;

    fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: EventSet) {
        if token != UDP_SOCKET_TOKEN {
            return
        }

        if events.is_writable() {
            self.handle_write();
        }

        if events.is_readable() {
            let (buffer, addr) = if let Some((buffer, addr)) = self.handle_read() {
                (buffer, addr)
            } else { return };

            {
                let provider = provider::new(&mut self.buffer_pool, &mut self.out_queue, event_loop);

                self.dispatch.incoming(provider, buffer.as_ref(), addr);
            }

            self.buffer_pool.push(buffer);
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
        let provider = provider::new(&mut self.buffer_pool, &mut self.out_queue, event_loop);

        self.dispatch.notify(provider, msg);
    }

    fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
        let provider = provider::new(&mut self.buffer_pool, &mut self.out_queue, event_loop);

        self.dispatch.timeout(provider, timeout);
    }

    fn tick(&mut self, event_loop: &mut EventLoop<Self>) {
        self.current_set = if !self.out_queue.is_empty() {
            EventSet::readable() | EventSet::writable()
        } else {
            EventSet::readable()
        };

        event_loop.reregister(&self.udp_socket, UDP_SOCKET_TOKEN, self.current_set, PollOpt::edge()).unwrap();
    }
}<|endoftext|>


<commit_before>use std::collections::VecDeque;

use eew::Kind;
use eew::EEW;

const DEFAULT_MAX_BLOCK_COUNT: usize = 32;

pub struct EEWBuffer {
	buffer: VecDeque<Vec<EEW>>, // each element of buffer must have at least 1 EEW object
	max_block_count: usize
}

impl EEWBuffer {

	pub fn new() -> EEWBuffer
	{
		EEWBuffer { buffer: VecDeque::new(), max_block_count: DEFAULT_MAX_BLOCK_COUNT }
	}

	pub fn with_capacity(n: usize) -> EEWBuffer
	{
		assert!(n >= 1);
		EEWBuffer { buffer: VecDeque::new(), max_block_count: n }
	}

	fn lookup(&self, eew_id: &str) -> Option<usize>
	{
		return self.buffer.iter().position(|ref block|
			block.first().map(|ref eew| eew.id.as_str()) == Some(eew_id));
	}

	fn extend_block(&mut self, idx: usize, eew: &EEW) -> bool {

		let ref mut block = self.buffer[idx];

		let is_latest = {

			let last_eew = block.last().expect("a block must have at least 1 element");

			if last_eew.number != eew.number {

				last_eew.number < eew.number

			} else {

				let is_cancel = |e: &EEW| {
					match e.kind {
						Kind::Cancel | Kind::DrillCancel => true,
					_ => false
					} };

				!is_cancel(last_eew) && is_cancel(eew)
			}
		};

		if is_latest {
			block.push(eew.clone());
		}

		return is_latest;
	}

	fn create_block(&mut self, eew: &EEW) {

		let block = vec! { eew.clone() };
		self.buffer.push_back(block);

		while self.buffer.len() > self.max_block_count {
			self.buffer.pop_front();
		}
	}

	pub fn append(&mut self, eew: &EEW) -> Option<&[EEW]>
	{
		match self.lookup(&eew.id) {

			Some(idx) => {
				match self.extend_block(idx, eew) {
					true => Some(&self.buffer[idx]),
					false => None
				}
			},
			None => {
				self.create_block(eew);
				Some(&self.buffer.back().expect("a buffer must have at least 1 block"))
			}
		}
	}
}
<commit_msg>EEWBuffer: Split extend_block method<commit_after>use std::collections::VecDeque;

use eew::Kind;
use eew::EEW;

const DEFAULT_MAX_BLOCK_COUNT: usize = 32;

pub struct EEWBuffer {
	buffer: VecDeque<Vec<EEW>>, // each element of buffer must have at least 1 EEW object
	max_block_count: usize
}

impl EEWBuffer {

	pub fn new() -> EEWBuffer
	{
		EEWBuffer { buffer: VecDeque::new(), max_block_count: DEFAULT_MAX_BLOCK_COUNT }
	}

	pub fn with_capacity(n: usize) -> EEWBuffer
	{
		assert!(n >= 1);
		EEWBuffer { buffer: VecDeque::new(), max_block_count: n }
	}

	fn lookup(&self, eew_id: &str) -> Option<usize>
	{
		return self.buffer.iter().position(|ref block|
			block.first().map(|ref eew| eew.id.as_str()) == Some(eew_id));
	}

	fn is_acceptable(&self, idx: usize, eew: &EEW) -> bool {

		let block = &self.buffer[idx];
		let last_eew = block.last().expect("a block must have at least 1 element");

		if last_eew.number != eew.number {
			return last_eew.number < eew.number;
		}

		let is_cancel = |e: &EEW| {
			match e.kind {
				Kind::Cancel | Kind::DrillCancel => true,
				_ => false
			}
		};

		return !is_cancel(last_eew) && is_cancel(eew);
	}

	fn extend_block(&mut self, idx: usize, eew: &EEW) -> bool {

		let to_accept = self.is_acceptable(idx, eew);
		if to_accept {
			self.buffer[idx].push(eew.clone());
		}
		return to_accept;
	}

	fn create_block(&mut self, eew: &EEW) {

		let block = vec! { eew.clone() };
		self.buffer.push_back(block);

		while self.buffer.len() > self.max_block_count {
			self.buffer.pop_front();
		}
	}

	pub fn append(&mut self, eew: &EEW) -> Option<&[EEW]>
	{
		match self.lookup(&eew.id) {

			Some(idx) => {
				match self.extend_block(idx, eew) {
					true => Some(&self.buffer[idx]),
					false => None
				}
			},
			None => {
				self.create_block(eew);
				Some(&self.buffer.back().expect("a buffer must have at least 1 block"))
			}
		}
	}
}
<|endoftext|>


<commit_before>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

const NAME: &'static str = "Alcibiades";
const AUTHOR: &'static str = "Evgeni Pandurski";
const VERSION: &'static str = "0.1";
const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    // The duration of the search in milliseconds.
    pub duration_millis: u64,
}

impl Default for SearchStatus {
    fn default() -> Self {
        SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
        }
    }
}

enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says so.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

struct Engine<S: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<S::HashTable>,
    position: S::SearchNode,
    searcher: S,
    queue: VecDeque<EngineReply>,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Nodes per second statistics.
    nps_stats: (u64, u64, u64),

    // Helps the engine decide when to show periodic progress reports.
    silent_since: SystemTime,

    // Whether the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // Tells the engine when it must stop thinking and play the best move.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<S> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(S::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<S> {
        let tt = Arc::new(S::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: S::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: S::new(tt),
            queue: VecDeque::new(),
            started_at: SystemTime::now(),
            status: SearchStatus { done: true, ..Default::default() },
            nps_stats: (0, 0, 0),
            silent_since: SystemTime::now(),
            is_pondering: false,
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        S::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = S::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: GoParams) {
        self.terminate();

        // TODO: What should we do with "mate"?
        let GoParams { mut searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        // Validate `searchmoves`.
        let searchmoves = {
            let mut moves = vec![];
            let legal_moves = self.position.legal_moves();
            if !searchmoves.is_empty() {
                searchmoves.sort();
                for m in legal_moves.iter() {
                    if searchmoves.binary_search(&m.notation()).is_ok() {
                        moves.push(*m);
                    }
                }
            };
            if moves.is_empty() {
                legal_moves
            } else {
                moves
            }
        };

        // Start a new search.
        self.tt.new_search();
        self.started_at = SystemTime::now();
        self.status = Default::default();
        self.nps_stats = (self.nps_stats.0, 0, 0);
        self.silent_since = SystemTime::now();
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(min(depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.status.done;

            // Wait for the search thread to do some work, and
            // hopefully update the status. (We must do this even when
            // the search is done -- in that case the next line will
            // just yield the CPU to another process.)
            self.wait_status_update(duration);

            // See if we must play now.
            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    &self.status;
                if !self.is_pondering &&
                   match self.play_when {
                    PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                    PlayWhen::MoveTime(t) => done || duration_millis >= t,
                    PlayWhen::Nodes(n) => done || searched_nodes >= n,
                    PlayWhen::Depth(d) => done || depth >= d,
                    PlayWhen::Never => false,
                } {
                    self.stop();
                }
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> Engine<S> {
    fn queue_progress_report(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
        ]));
        self.silent_since = SystemTime::now();
    }

    fn queue_pv(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, .. } = self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        if !variations.is_empty() {
            self.silent_since = SystemTime::now();
        }
    }

    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If there is no best move, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }

    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    fn process_report(&mut self, report: SearchReport<Vec<Variation>>) {
        let d = self.started_at.elapsed().unwrap();
        let duration_millis = 1000 * d.as_secs() + (d.subsec_nanos() / 1_000_000) as u64;
        self.status = SearchStatus {
            done: report.done,
            depth: report.depth,
            searched_nodes: report.searched_nodes,
            duration_millis: duration_millis,
        };

        // Update `self.nps_stats` each 1000 milliseconds.
        let elapsed_millis = duration_millis - self.nps_stats.2;
        if elapsed_millis >= 1000 {
            let nodes = report.searched_nodes - self.nps_stats.1;
            self.nps_stats = (1000 * nodes / elapsed_millis, report.searched_nodes, duration_millis)
        }

        // Inform the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(&report);
        }

        // Show the primary variations provided with the report (if any).
        self.queue_pv(&report.data);

        // If nothing has happened for a while, show a progress report.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_report();
        }
    }
}

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<S: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<S>>()
}
<commit_msg>beautify<commit_after>//! Facilities for implementing chess engines.

pub mod time_manager;

use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use std::cmp::min;
use std::io;
use uci::*;
use search::*;
use self::time_manager::*;

const NAME: &'static str = "Alcibiades";
const AUTHOR: &'static str = "Evgeni Pandurski";
const VERSION: &'static str = "0.1";
const START_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 1";

struct SearchStatus {
    pub done: bool,
    pub depth: Depth,
    pub searched_nodes: u64,

    // The duration of the search in milliseconds.
    pub duration_millis: u64,
}

impl Default for SearchStatus {
    fn default() -> Self {
        SearchStatus {
            done: false,
            depth: 0,
            searched_nodes: 0,
            duration_millis: 0,
        }
    }
}

enum PlayWhen {
    TimeManagement(TimeManager), // Stop when the time manager says so.
    MoveTime(u64), // Stop after the given number of milliseconds.
    Nodes(u64), // Stop when the given number of nodes has been searched.
    Depth(Depth), // Stop when the given search depth has been completed.
    Never, // An infinite search.
}

struct Engine<S: SearchExecutor<ReportData = Vec<Variation>>> {
    tt: Arc<S::HashTable>,
    position: S::SearchNode,
    searcher: S,
    queue: VecDeque<EngineReply>,

    // The starting time of the current/last search.
    started_at: SystemTime,

    // The status of the current/last search.
    status: SearchStatus,

    // Nodes per second statistics.
    nps_stats: (u64, u64, u64),

    // Helps the engine decide when to show periodic progress reports.
    silent_since: SystemTime,

    // Whether the engine is thinking in pondering mode at the moment.
    is_pondering: bool,

    // Tells the engine when it must stop thinking and play the best move.
    play_when: PlayWhen,

    // Tells the engine if it will be allowed to ponder. This option
    // is needed because the engine might change its time management
    // algorithm when pondering is allowed.
    pondering_is_allowed: bool,
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> UciEngine for Engine<S> {
    fn name() -> String {
        format!("{} {}", NAME, VERSION)
    }

    fn author() -> String {
        AUTHOR.to_string()
    }

    fn options() -> Vec<(String, OptionDescription)> {
        // Add up all suported options.
        let mut options = vec![
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 64 * 1024, default: 16 }),
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ];
        options.extend(S::options());

        // Remove duplicated options.
        options.sort_by(|a, b| a.0.cmp(&b.0));
        let mut options_dedup = vec![];
        let mut prev_name = String::from("");
        for o in options.drain(..) {
            if o.0 == prev_name {
                continue;
            }
            prev_name = o.0.clone();
            options_dedup.push(o);
        }
        options_dedup
    }

    fn new(tt_size_mb: Option<usize>) -> Engine<S> {
        let tt = Arc::new(S::HashTable::new(tt_size_mb));
        Engine {
            tt: tt.clone(),
            position: S::SearchNode::from_history(START_FEN, &mut vec![].into_iter()).ok().unwrap(),
            searcher: S::new(tt),
            queue: VecDeque::new(),
            started_at: SystemTime::now(),
            status: SearchStatus { done: true, ..Default::default() },
            nps_stats: (0, 0, 0),
            silent_since: SystemTime::now(),
            is_pondering: false,
            play_when: PlayWhen::Never,
            pondering_is_allowed: false,
        }
    }

    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            "Ponder" => {
                // TODO: Can we remove this?
                self.pondering_is_allowed = value == "true";
            }
            "Hash" => {
                // We do not support re-sizing of the transposition
                // table once the engine has been started.
                return;
            }
            "Clear Hash" => {
                self.tt.clear();
            }
            _ => (),
        }
        S::set_option(name, value);
    }

    fn new_game(&mut self) {
        self.tt.clear();
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if let Ok(p) = S::SearchNode::from_history(fen, moves) {
            self.position = p;
        }
    }

    fn go(&mut self, params: GoParams) {
        self.terminate();

        // TODO: What should we do with "mate"?
        let GoParams { mut searchmoves,
                       ponder,
                       wtime,
                       btime,
                       winc,
                       binc,
                       movestogo,
                       depth,
                       nodes,
                       movetime,
                       infinite,
                       .. } = params;

        // Validate `searchmoves`.
        let searchmoves = {
            let mut moves = vec![];
            let legal_moves = self.position.legal_moves();
            if !searchmoves.is_empty() {
                searchmoves.sort();
                for m in legal_moves.iter() {
                    if searchmoves.binary_search(&m.notation()).is_ok() {
                        moves.push(*m);
                    }
                }
            };
            if moves.is_empty() {
                legal_moves
            } else {
                moves
            }
        };

        // Start a new search.
        self.tt.new_search();
        self.started_at = SystemTime::now();
        self.status = Default::default();
        self.nps_stats = (self.nps_stats.0, 0, 0);
        self.silent_since = SystemTime::now();
        self.is_pondering = ponder;
        self.play_when = if infinite {
            PlayWhen::Never
        } else if movetime.is_some() {
            PlayWhen::MoveTime(movetime.unwrap())
        } else if nodes.is_some() {
            PlayWhen::Nodes(nodes.unwrap())
        } else if depth.is_some() {
            PlayWhen::Depth(min(depth.unwrap(), DEPTH_MAX as u64) as Depth)
        } else {
            PlayWhen::TimeManagement(TimeManager::new(self.position.board().to_move,
                                                      self.pondering_is_allowed,
                                                      wtime,
                                                      btime,
                                                      winc,
                                                      binc,
                                                      movestogo))
        };
        self.searcher.start_search(SearchParams {
            search_id: 0,
            position: self.position.clone(),
            depth: DEPTH_MAX,
            lower_bound: VALUE_MIN,
            upper_bound: VALUE_MAX,
            searchmoves: searchmoves,
        });
    }

    fn ponder_hit(&mut self) {
        if self.status.done {
            self.queue_best_move();
        } else {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        self.terminate();
        self.queue_best_move();
    }

    fn wait_for_reply(&mut self, duration: Duration) -> Option<EngineReply> {
        if self.queue.is_empty() {
            let done = self.status.done;

            // Wait for the search thread to do some work, and
            // hopefully update the status. (We must do this even when
            // the search is done -- in that case the next line will
            // just yield the CPU to another process.)
            self.wait_status_update(duration);

            // See if we must play now.
            if !done {
                let &SearchStatus { done, depth, searched_nodes, duration_millis, .. } =
                    &self.status;
                if !self.is_pondering &&
                   match self.play_when {
                    PlayWhen::TimeManagement(ref tm) => done || tm.must_play(),
                    PlayWhen::MoveTime(t) => done || duration_millis >= t,
                    PlayWhen::Nodes(n) => done || searched_nodes >= n,
                    PlayWhen::Depth(d) => done || depth >= d,
                    PlayWhen::Never => false,
                } {
                    self.stop();
                }
            }
        }

        self.queue.pop_front()
    }

    fn exit(&mut self) {
        self.terminate();
    }
}

impl<S: SearchExecutor<ReportData = Vec<Variation>>> Engine<S> {
    fn queue_progress_report(&mut self) {
        let SearchStatus { ref depth, ref searched_nodes, .. } = self.status;
        self.queue.push_back(EngineReply::Info(vec![
            InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
            InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
            InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
        ]));
        self.silent_since = SystemTime::now();
    }

    fn queue_pv(&mut self, variations: &Vec<Variation>) {
        let SearchStatus { ref depth, ref searched_nodes, ref duration_millis, .. } = self.status;
        for (i, &Variation { ref moves, value, bound }) in variations.iter().enumerate() {
            let bound_suffix = match bound {
                BOUND_EXACT => "",
                BOUND_UPPER => " upperbound",
                BOUND_LOWER => " lowerbound",
                _ => panic!("unexpected bound type"),
            };
            let mut moves_string = String::new();
            for m in moves {
                moves_string.push_str(&m.notation());
                moves_string.push(' ');
            }
            self.queue.push_back(EngineReply::Info(vec![
                InfoItem { info_type: "depth".to_string(), data: format!("{}", depth) },
                InfoItem { info_type: "multipv".to_string(), data: format!("{}", i + 1) },
                InfoItem { info_type: "score".to_string(), data: format!("cp {}{}", value, bound_suffix) },
                InfoItem { info_type: "time".to_string(), data: format!("{}", duration_millis) },
                InfoItem { info_type: "nodes".to_string(), data: format!("{}", searched_nodes) },
                InfoItem { info_type: "nps".to_string(), data: format!("{}", self.nps_stats.0) },
                InfoItem { info_type: "pv".to_string(), data: moves_string },
            ]));
        }
        if !variations.is_empty() {
            self.silent_since = SystemTime::now();
        }
    }

    fn queue_best_move(&mut self) {
        let pv = extract_pv(self.tt.deref(), &self.position, 2);
        let best_move = if let Some(m) = pv.moves.get(0) {
            m.notation()
        } else {
            // If there is no best move, pick the first legal move.
            self.position.legal_moves().get(0).map_or("0000".to_string(), |m| m.notation())
        };
        self.queue.push_back(EngineReply::BestMove {
            best_move: best_move,
            ponder_move: pv.moves.get(1).map(|m| m.notation()),
        });
        self.silent_since = SystemTime::now();
    }

    fn terminate(&mut self) {
        self.searcher.terminate_search();
        while !self.status.done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    fn process_report(&mut self, report: SearchReport<Vec<Variation>>) {
        let duration_millis = {
            let d = self.started_at.elapsed().unwrap();
            1000 * d.as_secs() + (d.subsec_nanos() / 1_000_000) as u64
        };
        self.status = SearchStatus {
            done: report.done,
            depth: report.depth,
            searched_nodes: report.searched_nodes,
            duration_millis: duration_millis,
        };

        // Update `self.nps_stats` each 1000 milliseconds.
        let elapsed_millis = duration_millis - self.nps_stats.2;
        if elapsed_millis >= 1000 {
            let nodes = report.searched_nodes - self.nps_stats.1;
            self.nps_stats = (1000 * nodes / elapsed_millis, report.searched_nodes, duration_millis)
        }

        // Inform the time manager.
        if let PlayWhen::TimeManagement(ref mut tm) = self.play_when {
            tm.update_status(&report);
        }

        // Show the primary variations provided with the report (if any).
        self.queue_pv(&report.data);

        // If nothing has happened for a while, show a progress report.
        if self.silent_since.elapsed().unwrap().as_secs() > 10 {
            self.queue_progress_report();
        }
    }
}

/// Serves UCI commands until a "quit" command is received.
///
/// The current thread will block until the UCI session is closed.
///
/// Returns `Err` if the handshake was unsuccessful, or if an IO error
/// occurred.
pub fn run_server<S: SearchExecutor<ReportData = Vec<Variation>>>() -> io::Result<()> {
    run_engine::<Engine<S>>()
}
<|endoftext|>


<commit_before>//! Interfaces to various spaced repetition systems.

use handlebars::Handlebars;
use rustc_serialize::json::{ToJson, Json};
use std::collections::BTreeMap;
use std::convert::From;
use std::ffi::OsStr;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::str::from_utf8;

use err::Result;
use srt::{Subtitle, SubtitleFile};
use video::Video;

/// Information about media file and associated subtitles that the user
/// wants to export.
pub struct ExportRequest {
    /// The video file from which to extract images and audio clips.
    pub video: Video,

    /// The foreign-language subtitles.
    pub foreign_subtitles: SubtitleFile,

    /// The optional native-language subtitles, if available.  Highly
    /// recommended for beginners, and surprisingly harmless even for
    /// advanced learners who are otherwise trying to maintain immersion.
    pub native_subtitles: Option<SubtitleFile>,
}

/// Information about a subtitle for use by our Handlebars HTML template.
/// This needs to implement `ToJson`, because that's what Handlebars wants
/// as input.
#[derive(RustcEncodable)]
struct SubtitleInfo {
    index: usize,
    image_path: String,
    audio_path: String,
    foreign_text: String,
    native_text: Option<String>,
}

impl ToJson for SubtitleInfo {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("index".to_string(), self.index.to_json());
        m.insert("image_path".to_string(), self.image_path.to_json());
        m.insert("audio_path".to_string(), self.audio_path.to_json());
        m.insert("foreign_text".to_string(), self.foreign_text.to_json());
        if let &Some(ref s) = &self.native_text {
            m.insert("native_text".to_string(), s.to_json());
        }
        m.to_json()
    }
}

/// Export-related information for use by our Handlebars HTML template.
#[derive(RustcEncodable)]
struct ExportInfo {
    filename: String,
    subtitles: Vec<SubtitleInfo>,
}

impl ToJson for ExportInfo {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("filename".to_string(), self.filename.to_json());
        m.insert("subtitles".to_string(), self.subtitles.to_json());
        m.to_json()
    }
}

// Write a chunk of data to a file.
fn write_all(path: &Path, data: &[u8]) -> Result<()> {
    let mut f = try!(fs::File::create(path));
    try!(f.write_all(data));
    Ok(())
}

/// Export flashcards and associated media files.
pub fn export(request: &ExportRequest) -> Result<()> {
    // Construct a path `dir` which we'll use to store our output files.
    // This is much uglier than it ought to be because paths are not
    // necessarily valid Unicode strings on all OSes, so we need to jump
    // through extra hoops.
    let mut dir_name = request.video.file_stem().to_owned();
    let suffix: PathBuf = From::from("_review");
    dir_name.push(&suffix);
    let dir = Path::new("./").with_file_name(&dir_name);
    try!(fs::create_dir_all(&dir));

    // Start preparing information we'll pass to our HTML template.
    let path_str = |os_str: &OsStr| { os_str.to_string_lossy().into_owned() };
    let mut bindings = ExportInfo {
        filename: path_str(request.video.file_name()),
        subtitles: vec!(),
    };

    // TODO: Offer some way to specify which subs.
    let subs: Vec<&Subtitle> = request.foreign_subtitles.subtitles.iter()
        .take(6).collect();
    for sub in subs {
        println!("Subtitle #{}: Extracting audio and video", sub.index);

        let image_path = dir.join(format!("sub{}.jpg", sub.index));
        try!(request.video.extract_image(sub.midpoint(), &image_path));

        let audio_path = dir.join(format!("sub{}.mp3", sub.index));
        try!(request.video.extract_audio(sub.begin, sub.end - sub.begin,
                                         &audio_path));

        bindings.subtitles.push(SubtitleInfo {
            index: sub.index,
            image_path: path_str(image_path.file_name().unwrap()),
            audio_path: path_str(audio_path.file_name().unwrap()),
            foreign_text: sub.lines.join(" "),
            native_text: None,
        });
    }

    // Write out our support files.
    try!(write_all(&dir.join("style.css"), &include_bytes!("style.css")[..]));
    try!(write_all(&dir.join("play.svg"), &include_bytes!("play.svg")[..]));

    // Render and write out our HTML.
    let template = try!(from_utf8(include_bytes!("review.html.hbs")));
    let mut handlebars = Handlebars::new();
    try!(handlebars.register_template_string("review", template.to_owned()));
    let html = try!(handlebars.render("review", &bindings));
    try!(write_all(&dir.join("index.html"), html.as_bytes()));

    Ok(())
}
<commit_msg>Name media files after the input video name<commit_after>//! Interfaces to various spaced repetition systems.

use handlebars::Handlebars;
use rustc_serialize::json::{ToJson, Json};
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::str::from_utf8;

use err::Result;
use srt::{Subtitle, SubtitleFile};
use video::Video;

/// Information about media file and associated subtitles that the user
/// wants to export.
pub struct ExportRequest {
    /// The video file from which to extract images and audio clips.
    pub video: Video,

    /// The foreign-language subtitles.
    pub foreign_subtitles: SubtitleFile,

    /// The optional native-language subtitles, if available.  Highly
    /// recommended for beginners, and surprisingly harmless even for
    /// advanced learners who are otherwise trying to maintain immersion.
    pub native_subtitles: Option<SubtitleFile>,
}

/// Information about a subtitle for use by our Handlebars HTML template.
/// This needs to implement `ToJson`, because that's what Handlebars wants
/// as input.
#[derive(RustcEncodable)]
struct SubtitleInfo {
    index: usize,
    image_path: String,
    audio_path: String,
    foreign_text: String,
    native_text: Option<String>,
}

impl ToJson for SubtitleInfo {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("index".to_string(), self.index.to_json());
        m.insert("image_path".to_string(), self.image_path.to_json());
        m.insert("audio_path".to_string(), self.audio_path.to_json());
        m.insert("foreign_text".to_string(), self.foreign_text.to_json());
        if let &Some(ref s) = &self.native_text {
            m.insert("native_text".to_string(), s.to_json());
        }
        m.to_json()
    }
}

/// Export-related information for use by our Handlebars HTML template.
#[derive(RustcEncodable)]
struct ExportInfo {
    filename: String,
    subtitles: Vec<SubtitleInfo>,
}

impl ToJson for ExportInfo {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("filename".to_string(), self.filename.to_json());
        m.insert("subtitles".to_string(), self.subtitles.to_json());
        m.to_json()
    }
}

// Write a chunk of data to a file.
fn write_all(path: &Path, data: &[u8]) -> Result<()> {
    let mut f = try!(fs::File::create(path));
    try!(f.write_all(data));
    Ok(())
}

/// Export flashcards and associated media files.
pub fn export(request: &ExportRequest) -> Result<()> {
    // Convert an OsStr to a String, losing any non-Unicode data, because we're
    // not going to create _new_ files with non-Unicode names.
    let path_str = |os_str: &OsStr| { os_str.to_string_lossy().into_owned() };
    let stem = path_str(request.video.file_stem());

    // Construct a path `dir` which we'll use to store our output files.
    // This is much uglier than it ought to be because paths are not
    // necessarily valid Unicode strings on all OSes, so we need to jump
    // through extra hoops.
    let dir = Path::new("./").join(format!("{}_review", &stem));
    try!(fs::create_dir_all(&dir));
    let media_path = |index: usize, ext: &str| -> PathBuf {
        dir.join(format!("{}_{:03}.{}", stem, index, ext))
    };

    // Start preparing information we'll pass to our HTML template.
    let mut bindings = ExportInfo {
        filename: path_str(request.video.file_name()),
        subtitles: vec!(),
    };

    // TODO: Offer some way to specify which subs.
    let subs: Vec<&Subtitle> = request.foreign_subtitles.subtitles.iter()
        .take(6).collect();
    for sub in subs {
        println!("Subtitle #{}: Extracting audio and video", sub.index);

        let image_path = media_path(sub.index, "jpg");
        try!(request.video.extract_image(sub.midpoint(), &image_path));

        let audio_path = media_path(sub.index, "mp3");
        try!(request.video.extract_audio(sub.begin, sub.end - sub.begin,
                                         &audio_path));

        bindings.subtitles.push(SubtitleInfo {
            index: sub.index,
            image_path: path_str(image_path.file_name().unwrap()),
            audio_path: path_str(audio_path.file_name().unwrap()),
            foreign_text: sub.lines.join(" "),
            native_text: None,
        });
    }

    // Write out our support files.
    try!(write_all(&dir.join("style.css"), &include_bytes!("style.css")[..]));
    try!(write_all(&dir.join("play.svg"), &include_bytes!("play.svg")[..]));

    // Render and write out our HTML.
    let template = try!(from_utf8(include_bytes!("review.html.hbs")));
    let mut handlebars = Handlebars::new();
    try!(handlebars.register_template_string("review", template.to_owned()));
    let html = try!(handlebars.render("review", &bindings));
    try!(write_all(&dir.join("index.html"), html.as_bytes()));

    Ok(())
}
<|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.

//! This module contains types and functions to support formatting specific line ranges.

use std::{cmp, iter, path, str};
use std::collections::HashMap;

use serde_json as json;

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)]
pub struct Range {
    lo: usize,
    hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
                (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(
                cmp::min(self.lo, other.lo),
                cmp::max(self.hi, other.hi),
            ))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<HashMap<String, Vec<Range>>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(ranges: &mut HashMap<String, Vec<Range>>) {
    for ranges in ranges.values_mut() {
        ranges.sort();
        let mut result = vec![];
        {
            let mut iter = ranges.into_iter().peekable();
            while let Some(next) = iter.next() {
                let mut next = next.clone();
                while let Some(&&mut peek) = iter.peek() {
                    if let Some(merged) = next.merge(peek) {
                        iter.next().unwrap();
                        next = merged;
                    } else {
                        break;
                    }
                }
                result.push(next)
            }
        }
        *ranges = result;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    fn from_ranges(mut ranges: HashMap<String, Vec<Range>>) -> FileLines {
        normalize_ranges(&mut ranges);
        FileLines(Some(ranges))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(|m| m.keys()))
    }

    /// Returns true if `self` includes all lines in all files. Otherwise runs `f` on all ranges in
    /// the designated file (if any) and returns true if `f` ever does.
    fn file_range_matches<F>(&self, file_name: &str, f: F) -> bool
    where
        F: FnMut(&Range) -> bool,
    {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) {
            Some(ranges) => ranges.iter().any(f),
            None => false,
        }
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        self.file_range_matches(range.file_name(), |r| r.contains(Range::from(range)))
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        self.file_range_matches(range.file_name(), |r| r.intersects(Range::from(range)))
    }

    /// Returns true if `line` from `file_name` is in `self`.
    pub fn contains_line(&self, file_name: &str, line: usize) -> bool {
        self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
    }

    /// Returns true if any of the lines between `lo` and `hi` from `file_name` are in `self`.
    pub fn intersects_range(&self, file_name: &str, lo: usize, hi: usize) -> bool {
        self.file_range_matches(file_name, |r| r.intersects(Range::new(lo, hi)))
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Option<String> {
    if s == "stdin" {
        return Some(s.to_string());
    }

    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()),
        _ => None,
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = json::from_str(s).map_err(|e| e.to_string())?;
        let mut m = HashMap::new();
        for js in v.into_iter() {
            let (s, r) = JsonSpan::into_tuple(js)?;
            m.entry(s).or_insert(vec![]).push(r);
        }
        Ok(FileLines::from_ranges(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, Deserialize)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = canonicalize_path_string(&self.file)
            .ok_or_else(|| format!("Can't canonicalize {}", &self.file))?;
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl<'de> ::serde::de::Deserialize<'de> for FileLines {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
    where
        D: ::serde::de::Deserializer<'de>,
    {
        panic!(
            "FileLines cannot be deserialized from a project rustfmt.toml file: please \
             specify it via the `--file-lines` option instead"
        );
    }
}

// We also want to avoid attempting to serialize a FileLines to toml. The
// `Config` struct should ensure this impl is never reached.
impl ::serde::ser::Serialize for FileLines {
    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
    where
        S: ::serde::ser::Serializer,
    {
        unreachable!("FileLines cannot be serialized. This is a rustfmt bug.");
    }
}

#[cfg(test)]
mod test {
    use super::Range;

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(4, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(5, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(6, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 7).merge(Range::new(4, 5))
        );
    }
}
<commit_msg>Public file-lines constructor<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.

//! This module contains types and functions to support formatting specific line ranges.

use std::{cmp, iter, path, str};
use std::collections::HashMap;

use serde_json as json;

use codemap::LineRange;

/// A range that is inclusive of both ends.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Deserialize)]
pub struct Range {
    lo: usize,
    hi: usize,
}

impl<'a> From<&'a LineRange> for Range {
    fn from(range: &'a LineRange) -> Range {
        Range::new(range.lo, range.hi)
    }
}

impl Range {
    pub fn new(lo: usize, hi: usize) -> Range {
        Range { lo: lo, hi: hi }
    }

    fn is_empty(self) -> bool {
        self.lo > self.hi
    }

    fn contains(self, other: Range) -> bool {
        if other.is_empty() {
            true
        } else {
            !self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
        }
    }

    fn intersects(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            (self.lo <= other.hi && other.hi <= self.hi) ||
                (other.lo <= self.hi && self.hi <= other.hi)
        }
    }

    fn adjacent_to(self, other: Range) -> bool {
        if self.is_empty() || other.is_empty() {
            false
        } else {
            self.hi + 1 == other.lo || other.hi + 1 == self.lo
        }
    }

    /// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
    /// intersect; returns `None` otherwise.
    fn merge(self, other: Range) -> Option<Range> {
        if self.adjacent_to(other) || self.intersects(other) {
            Some(Range::new(
                cmp::min(self.lo, other.lo),
                cmp::max(self.hi, other.hi),
            ))
        } else {
            None
        }
    }
}

/// A set of lines in files.
///
/// It is represented as a multimap keyed on file names, with values a collection of
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
/// lines in all files.
#[derive(Clone, Debug, Default)]
pub struct FileLines(Option<HashMap<String, Vec<Range>>>);

/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
/// and ordered by their start point.
fn normalize_ranges(ranges: &mut HashMap<String, Vec<Range>>) {
    for ranges in ranges.values_mut() {
        ranges.sort();
        let mut result = vec![];
        {
            let mut iter = ranges.into_iter().peekable();
            while let Some(next) = iter.next() {
                let mut next = next.clone();
                while let Some(&&mut peek) = iter.peek() {
                    if let Some(merged) = next.merge(peek) {
                        iter.next().unwrap();
                        next = merged;
                    } else {
                        break;
                    }
                }
                result.push(next)
            }
        }
        *ranges = result;
    }
}

impl FileLines {
    /// Creates a `FileLines` that contains all lines in all files.
    pub fn all() -> FileLines {
        FileLines(None)
    }

    pub fn from_ranges(mut ranges: HashMap<String, Vec<Range>>) -> FileLines {
        normalize_ranges(&mut ranges);
        FileLines(Some(ranges))
    }

    /// Returns an iterator over the files contained in `self`.
    pub fn files(&self) -> Files {
        Files(self.0.as_ref().map(|m| m.keys()))
    }

    /// Returns true if `self` includes all lines in all files. Otherwise runs `f` on all ranges in
    /// the designated file (if any) and returns true if `f` ever does.
    fn file_range_matches<F>(&self, file_name: &str, f: F) -> bool
    where
        F: FnMut(&Range) -> bool,
    {
        let map = match self.0 {
            // `None` means "all lines in all files".
            None => return true,
            Some(ref map) => map,
        };

        match canonicalize_path_string(file_name).and_then(|file| map.get(&file)) {
            Some(ranges) => ranges.iter().any(f),
            None => false,
        }
    }

    /// Returns true if `range` is fully contained in `self`.
    pub fn contains(&self, range: &LineRange) -> bool {
        self.file_range_matches(range.file_name(), |r| r.contains(Range::from(range)))
    }

    /// Returns true if any lines in `range` are in `self`.
    pub fn intersects(&self, range: &LineRange) -> bool {
        self.file_range_matches(range.file_name(), |r| r.intersects(Range::from(range)))
    }

    /// Returns true if `line` from `file_name` is in `self`.
    pub fn contains_line(&self, file_name: &str, line: usize) -> bool {
        self.file_range_matches(file_name, |r| r.lo <= line && r.hi >= line)
    }

    /// Returns true if any of the lines between `lo` and `hi` from `file_name` are in `self`.
    pub fn intersects_range(&self, file_name: &str, lo: usize, hi: usize) -> bool {
        self.file_range_matches(file_name, |r| r.intersects(Range::new(lo, hi)))
    }
}

/// FileLines files iterator.
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);

impl<'a> iter::Iterator for Files<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.as_mut().and_then(Iterator::next)
    }
}

fn canonicalize_path_string(s: &str) -> Option<String> {
    if s == "stdin" {
        return Some(s.to_string());
    }

    match path::PathBuf::from(s).canonicalize() {
        Ok(canonicalized) => canonicalized.to_str().map(|s| s.to_string()),
        _ => None,
    }
}

// This impl is needed for `Config::override_value` to work for use in tests.
impl str::FromStr for FileLines {
    type Err = String;

    fn from_str(s: &str) -> Result<FileLines, String> {
        let v: Vec<JsonSpan> = json::from_str(s).map_err(|e| e.to_string())?;
        let mut m = HashMap::new();
        for js in v.into_iter() {
            let (s, r) = JsonSpan::into_tuple(js)?;
            m.entry(s).or_insert(vec![]).push(r);
        }
        Ok(FileLines::from_ranges(m))
    }
}

// For JSON decoding.
#[derive(Clone, Debug, Deserialize)]
struct JsonSpan {
    file: String,
    range: (usize, usize),
}

impl JsonSpan {
    fn into_tuple(self) -> Result<(String, Range), String> {
        let (lo, hi) = self.range;
        let canonical = canonicalize_path_string(&self.file)
            .ok_or_else(|| format!("Can't canonicalize {}", &self.file))?;
        Ok((canonical, Range::new(lo, hi)))
    }
}

// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
// for `FileLines`, so it will just panic instead.
impl<'de> ::serde::de::Deserialize<'de> for FileLines {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
    where
        D: ::serde::de::Deserializer<'de>,
    {
        panic!(
            "FileLines cannot be deserialized from a project rustfmt.toml file: please \
             specify it via the `--file-lines` option instead"
        );
    }
}

// We also want to avoid attempting to serialize a FileLines to toml. The
// `Config` struct should ensure this impl is never reached.
impl ::serde::ser::Serialize for FileLines {
    fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
    where
        S: ::serde::ser::Serializer,
    {
        unreachable!("FileLines cannot be serialized. This is a rustfmt bug.");
    }
}

#[cfg(test)]
mod test {
    use super::Range;

    #[test]
    fn test_range_intersects() {
        assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
        assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
        assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
        assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
        assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
    }

    #[test]
    fn test_range_adjacent_to() {
        assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
        assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
        assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
        assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
    }

    #[test]
    fn test_range_contains() {
        assert!(Range::new(1, 2).contains(Range::new(1, 1)));
        assert!(Range::new(1, 2).contains(Range::new(2, 2)));
        assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
        assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
    }

    #[test]
    fn test_range_merge() {
        assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
        assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(4, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(5, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 5).merge(Range::new(6, 7))
        );
        assert_eq!(
            Some(Range::new(3, 7)),
            Range::new(3, 7).merge(Range::new(4, 5))
        );
    }
}
<|endoftext|>


<commit_before>use image::GrayAlphaImage;
use rand::Rng;
use itertools::Itertools;

use utils::*;
use codec::Codec;

pub struct GrayAlphaCodec;

impl Codec for GrayAlphaCodec
{
    type Input = GrayAlphaImage;
    type Mode = GrayAlphaMode;

    fn encode<R: Rng>(
        source: &mut GrayAlphaImage,
        payload: &[u8],
        mode: GrayAlphaMode,
        mut rng: R)
    {
        // pixels per bytes
        let ppb = match mode
        {
            GrayAlphaMode::Alpha => 8,
            GrayAlphaMode::All => 4,
        };

        for (pixels, byte) in source.pixels_mut()
            .chunks(ppb).into_iter()
            .map(|ch| ch.collect::<Vec<_>>())
            .zip(payload.iter().map(|x| *x))
        {
            if pixels.len() != ppb
            {
                return;
            }

            match mode
            {
                GrayAlphaMode::Alpha =>
                {
                    for (i, px) in pixels.into_iter()
                        .enumerate()
                    {
                        fix_u8(&mut px.data[1],
                               get_bit(byte, i as u8), &mut rng);
                    }
                },
                GrayAlphaMode::All =>
                {
                    for (i, px) in pixels.into_iter()
                        .enumerate()
                    {
                        fix_u8(&mut px.data[0],
                               get_bit(byte, i as u8 * 2), &mut rng);
                        fix_u8(&mut px.data[1],
                               get_bit(byte, i as u8 * 2 + 1), &mut rng);
                    }
                },
            }
        }
    }

    fn decode(
        source: &GrayAlphaImage,
        payload: &mut [u8],
        len: usize,
        mode: GrayAlphaMode)
    {
        unimplemented!()
    }

    fn estimate(
        source: &GrayAlphaImage,
        mode: GrayAlphaMode) -> Option<usize>
    {
        Some(match mode
        {
            GrayAlphaMode::Alpha =>
                source.height() as usize * source.width() as usize / 8,
            GrayAlphaMode::All =>
                source.height() as usize * source.width() as usize / 4,
        })
    }
}

#[derive(Copy, Clone)]
pub enum GrayAlphaMode
{
    Alpha,
    All,
}

use std::default::Default;

impl Default for GrayAlphaMode
{
    fn default() -> GrayAlphaMode
    {
        GrayAlphaMode::Alpha
    }
}

use std::str::FromStr;

impl FromStr for GrayAlphaMode
{
    type Err = ();

    fn from_str(s: &str) -> Result<GrayAlphaMode, ()>
    {
        if s == "alpha"
        {
            Ok(GrayAlphaMode::Alpha)
        }
        else if s == "all"
        {
            Ok(GrayAlphaMode::All)
        }
        else
        {
            Err(())
        }
    }
}
<commit_msg>Implemented gray alpha decoding<commit_after>use image::GrayAlphaImage;
use rand::Rng;
use itertools::Itertools;

use utils::*;
use codec::Codec;

pub struct GrayAlphaCodec;

impl Codec for GrayAlphaCodec
{
    type Input = GrayAlphaImage;
    type Mode = GrayAlphaMode;

    fn encode<R: Rng>(
        source: &mut GrayAlphaImage,
        payload: &[u8],
        mode: GrayAlphaMode,
        mut rng: R)
    {
        // pixels per bytes
        let ppb = match mode
        {
            GrayAlphaMode::Alpha => 8,
            GrayAlphaMode::All => 4,
        };

        for (pixels, byte) in source.pixels_mut()
            .chunks(ppb).into_iter()
            .map(|ch| ch.collect::<Vec<_>>())
            .zip(payload.iter().map(|x| *x))
        {
            if pixels.len() != ppb
            {
                return;
            }

            match mode
            {
                GrayAlphaMode::Alpha =>
                {
                    for (i, px) in pixels.into_iter()
                        .enumerate()
                    {
                        fix_u8(&mut px.data[1],
                               get_bit(byte, i as u8), &mut rng);
                    }
                },
                GrayAlphaMode::All =>
                {
                    for (i, px) in pixels.into_iter()
                        .enumerate()
                    {
                        fix_u8(&mut px.data[0],
                               get_bit(byte, i as u8 * 2), &mut rng);
                        fix_u8(&mut px.data[1],
                               get_bit(byte, i as u8 * 2 + 1), &mut rng);
                    }
                },
            }
        }
    }

    fn decode(
        source: &GrayAlphaImage,
        payload: &mut [u8],
        len: usize,
        mode: GrayAlphaMode)
    {
        assert!(len <= payload.len());

        // pixels per bytes
        let ppb = match mode
        {
            GrayAlphaMode::Alpha => 8,
            GrayAlphaMode::All => 4,
        };

        for (index, pixels) in source.pixels()
            .chunks(ppb).into_iter()
            .map(|ch| ch.collect::<Vec<_>>())
            .enumerate()
            .take(len)
        {
            if pixels.len() != ppb
            {
                return;
            }

            match mode
            {
                GrayAlphaMode::Alpha =>
                {
                    let mut byte = 0u8;

                    for (i, px) in pixels.into_iter()
                        .enumerate()
                    {
                        byte = set_bit(byte,
                            i as u8, px.data[1] % 2 == 1);
                    }

                    payload[index] = byte;
                },
                GrayAlphaMode::All =>
                {
                    let mut byte = 0u8;

                    for (i, px) in pixels.into_iter()
                        .enumerate()
                    {
                        byte = set_bit(byte,
                            i as u8 * 2, px.data[0] % 2 == 1);
                        byte = set_bit(byte,
                            i as u8* 2 + 1, px.data[1] % 2 == 1);
                    }

                    payload[index] = byte;
                },
            }
        }
    }

    fn estimate(
        source: &GrayAlphaImage,
        mode: GrayAlphaMode) -> Option<usize>
    {
        Some(match mode
        {
            GrayAlphaMode::Alpha =>
                source.height() as usize * source.width() as usize / 8,
            GrayAlphaMode::All =>
                source.height() as usize * source.width() as usize / 4,
        })
    }
}

#[derive(Copy, Clone)]
pub enum GrayAlphaMode
{
    Alpha,
    All,
}

use std::default::Default;

impl Default for GrayAlphaMode
{
    fn default() -> GrayAlphaMode
    {
        GrayAlphaMode::Alpha
    }
}

use std::str::FromStr;

impl FromStr for GrayAlphaMode
{
    type Err = ();

    fn from_str(s: &str) -> Result<GrayAlphaMode, ()>
    {
        if s == "alpha"
        {
            Ok(GrayAlphaMode::Alpha)
        }
        else if s == "all"
        {
            Ok(GrayAlphaMode::All)
        }
        else
        {
            Err(())
        }
    }
}
<|endoftext|>


<commit_before>#[llvm_versions(9.0..=latest)]
use llvm_sys::core::{LLVMGetIntrinsicDeclaration, LLVMIntrinsicIsOverloaded, LLVMLookupIntrinsicID};
use llvm_sys::prelude::LLVMTypeRef;

use crate::module::Module;
use crate::types::{AsTypeRef, BasicTypeEnum, FunctionType};
use crate::values::FunctionValue;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Intrinsic {
    id: u32,
}

#[llvm_versions(9.0..=latest)]
impl Intrinsic {
    // SAFETY: the id is a valid LLVM intrinsic ID
    pub(crate) unsafe fn new(id: u32) -> Self {
        Self {
            id
        }
    }

    pub fn find(name: &str) -> Option<Self> {
        let id = unsafe {
            LLVMLookupIntrinsicID(name.as_ptr() as *const ::libc::c_char, name.len())
        };

        if id == 0 {
            return None;
        }

        return Some(unsafe {
            Intrinsic::new(id)
        });
    }

    pub fn is_overloaded(&self) -> bool {
        unsafe {
            LLVMIntrinsicIsOverloaded(self.id) != 0
        }
    }

    pub fn get_declaration<'ctx>(&self, module: &Module<'ctx>, param_types: &[BasicTypeEnum]) -> Option<FunctionValue<'ctx>> {
        let mut param_types: Vec<LLVMTypeRef> = param_types.iter()
            .map(|val| val.as_type_ref())
            .collect();

        // param_types should be empty for non-overloaded intrinsics (I think?)
        // for overloaded intrinsics they determine the overload used

        if self.is_overloaded() && param_types.is_empty() {
            // LLVM crashes otherwise
            return None;
        }

        let res = unsafe {
            FunctionValue::new(
                LLVMGetIntrinsicDeclaration(
                    module.module.get(),
                    self.id,
                    param_types.as_mut_ptr(),
                    param_types.len()
                )
            )
        };

        res
    }
}<commit_msg>Add some docs to intrinsics API<commit_after>#[llvm_versions(9.0..=latest)]
use llvm_sys::core::{LLVMGetIntrinsicDeclaration, LLVMIntrinsicIsOverloaded, LLVMLookupIntrinsicID};
use llvm_sys::prelude::LLVMTypeRef;

use crate::module::Module;
use crate::types::{AsTypeRef, BasicTypeEnum, FunctionType};
use crate::values::FunctionValue;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Intrinsic {
    id: u32,
}

/// A wrapper around LLVM intrinsic id
///
/// To call it you would need to create a declaration inside a module using [`Self::get_declaration()`].
#[llvm_versions(9.0..=latest)]
impl Intrinsic {
    /// Create an Intrinsic object from raw LLVM intrinsic id
    ///
    /// SAFETY: the id is a valid LLVM intrinsic ID
    pub(crate) unsafe fn new(id: u32) -> Self {
        Self {
            id
        }
    }

    /// Find llvm intrinsic id from name
    ///
    /// # Example
    /// ```no_run
    /// use inkwell::intrinsics::Intrinsic;
    ///
    /// let trap_intrinsic = Intrinsic::find("llvm.trap").unwrap();
    ///
    /// let context = Context::create();
    /// let module = context.create_module("trap");
    /// let builder = context.create_builder();
    /// let fn_type = struct_type.fn_type(&[], false);
    /// let fn_value = module.add_function("trap", fn_type, None);
    /// let entry = context.append_basic_block(fn_value, "entry");
    ///
    /// let trap_function = trap_intrinsic.get_declaration(module, &[]).unwrap();
    ///
    /// builder.position_at_end(entry);
    /// builder.build(trap_function, &[], "trap_call");
    /// ```
    pub fn find(name: &str) -> Option<Self> {
        let id = unsafe {
            LLVMLookupIntrinsicID(name.as_ptr() as *const ::libc::c_char, name.len())
        };

        if id == 0 {
            return None;
        }

        return Some(unsafe {
            Intrinsic::new(id)
        });
    }

    /// Check if specified intrinsic is overloaded
    ///
    /// Overloaded intrinsics need some argument types to be specified to declare them
    pub fn is_overloaded(&self) -> bool {
        unsafe {
            LLVMIntrinsicIsOverloaded(self.id) != 0
        }
    }

    /// Create or insert the declaration of an intrinsic.
    ///
    /// For overloaded intrinsics, parameter types must be provided to uniquely identify an overload.
    ///
    /// # Example
    /// ```no_run
    /// use inkwell::intrinsics::Intrinsic;
    ///
    /// let trap_intrinsic = Intrinsic::find("llvm.trap").unwrap();
    ///
    /// let context = Context::create();
    /// let module = context.create_module("trap");
    /// let builder = context.create_builder();
    /// let fn_type = struct_type.fn_type(&[], false);
    /// let fn_value = module.add_function("trap", fn_type, None);
    /// let entry = context.append_basic_block(fn_value, "entry");
    ///
    /// let trap_function = trap_intrinsic.get_declaration(module, &[]).unwrap();
    ///
    /// builder.position_at_end(entry);
    /// builder.build(trap_function, &[], "trap_call");
    /// ```
    pub fn get_declaration<'ctx>(&self, module: &Module<'ctx>, param_types: &[BasicTypeEnum]) -> Option<FunctionValue<'ctx>> {
        let mut param_types: Vec<LLVMTypeRef> = param_types.iter()
            .map(|val| val.as_type_ref())
            .collect();

        // param_types should be empty for non-overloaded intrinsics (I think?)
        // for overloaded intrinsics they determine the overload used

        if self.is_overloaded() && param_types.is_empty() {
            // LLVM crashes otherwise
            return None;
        }

        let res = unsafe {
            FunctionValue::new(
                LLVMGetIntrinsicDeclaration(
                    module.module.get(),
                    self.id,
                    param_types.as_mut_ptr(),
                    param_types.len()
                )
            )
        };

        res
    }
}<|endoftext|>