<commit_before>#![allow(non_camel_case_types,dead_code)]

extern crate libc;

use std::io;
use std::time::Duration;

use self::libc::{c_int,c_short};

#[cfg(target_os = "linux")]
type nfds_t = libc::c_ulong;

#[cfg(not(target_os = "linux"))]
type nfds_t = libc::c_uint;

#[derive(Debug)]
#[repr(C)]
struct PollFd {
    fd: c_int,
    events: c_short,
    revents: c_short
}

const POLLIN:   c_short = 0x0001;
const POLLPRI:  c_short = 0x0002;
const POLLOUT:  c_short = 0x0004;

const POLLERR:  c_short = 0x0008;
const POLLHUP:  c_short = 0x0010;
const POLLNVAL: c_short = 0x0020;

pub fn wait_read_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
    wait_fd(fd, POLLIN, timeout)
}

pub fn wait_write_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
    wait_fd(fd, POLLOUT, timeout)
}

fn wait_fd(fd: c_int, events: c_short, timeout: Duration) -> io::Result<()> {
    use self::libc::{EINTR,EPIPE,EIO};

    let mut fds = vec!(PollFd { fd: fd, events: events, revents: 0 });

    let wait = do_poll(&mut fds, timeout);

    if wait < 0 {
        let errno = super::error::errno();

        let kind = match errno {
            EINTR => io::ErrorKind::Interrupted,
            _ => io::ErrorKind::Other
        };

        return Err(io::Error::new(kind, super::error::error_string(errno)));
    }

    if wait == 0 {
        return Err(io::Error::new(io::ErrorKind::TimedOut, "Operation timed out"));
    }

    if fds[0].revents & events != 0 {
        return Ok(());
    }

    if fds[0].revents & (POLLHUP | POLLNVAL) != 0 {
        return Err(io::Error::new(io::ErrorKind::BrokenPipe, super::error::error_string(EPIPE)));
    }

    Err(io::Error::new(io::ErrorKind::Other, super::error::error_string(EIO)))
}

#[cfg(target_os = "linux")]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
    use std::ptr;

    #[repr(C)]
    struct sigset_t;

    extern "C" {
        fn ppoll(fds: *mut PollFd, nfds: nfds_t, timeout_ts: *mut self::libc::timespec, sigmask: *const sigset_t) -> c_int;
    }

    let mut timeout_ts = self::libc::timespec {
        tv_sec: timeout.as_secs() as libc::time_t,
        tv_nsec: timeout.subsec_nanos() as libc::c_long,
    };

    unsafe {
        ppoll((&mut fds[..]).as_mut_ptr(),
              fds.len() as nfds_t,
              &mut timeout_ts,
              ptr::null())
    }
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
    extern "C" {
        fn poll(fds: *mut PollFd, nfds: nfds_t, timeout: c_int) -> c_int;
    }

    let milliseconds = timeout.as_secs() * 1000 + timeout.subsec_nanos() as u64 / 1_000_000;

    unsafe {
        poll((&mut fds[..]).as_mut_ptr(),
             fds.len() as nfds_t,
             milliseconds as c_int)
    }
}
<commit_msg>fixes zero-sized C struct<commit_after>#![allow(non_camel_case_types,dead_code)]

extern crate libc;

use std::io;
use std::time::Duration;

use self::libc::{c_int,c_short};

#[cfg(target_os = "linux")]
type nfds_t = libc::c_ulong;

#[cfg(not(target_os = "linux"))]
type nfds_t = libc::c_uint;

#[derive(Debug)]
#[repr(C)]
struct PollFd {
    fd: c_int,
    events: c_short,
    revents: c_short
}

const POLLIN:   c_short = 0x0001;
const POLLPRI:  c_short = 0x0002;
const POLLOUT:  c_short = 0x0004;

const POLLERR:  c_short = 0x0008;
const POLLHUP:  c_short = 0x0010;
const POLLNVAL: c_short = 0x0020;

pub fn wait_read_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
    wait_fd(fd, POLLIN, timeout)
}

pub fn wait_write_fd(fd: c_int, timeout: Duration) -> io::Result<()> {
    wait_fd(fd, POLLOUT, timeout)
}

fn wait_fd(fd: c_int, events: c_short, timeout: Duration) -> io::Result<()> {
    use self::libc::{EINTR,EPIPE,EIO};

    let mut fds = vec!(PollFd { fd: fd, events: events, revents: 0 });

    let wait = do_poll(&mut fds, timeout);

    if wait < 0 {
        let errno = super::error::errno();

        let kind = match errno {
            EINTR => io::ErrorKind::Interrupted,
            _ => io::ErrorKind::Other
        };

        return Err(io::Error::new(kind, super::error::error_string(errno)));
    }

    if wait == 0 {
        return Err(io::Error::new(io::ErrorKind::TimedOut, "Operation timed out"));
    }

    if fds[0].revents & events != 0 {
        return Ok(());
    }

    if fds[0].revents & (POLLHUP | POLLNVAL) != 0 {
        return Err(io::Error::new(io::ErrorKind::BrokenPipe, super::error::error_string(EPIPE)));
    }

    Err(io::Error::new(io::ErrorKind::Other, super::error::error_string(EIO)))
}

#[cfg(target_os = "linux")]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
    use std::ptr;

    use self::libc::{c_void};

    #[repr(C)]
    struct sigset_t {
        __private: c_void
    }

    extern "C" {
        fn ppoll(fds: *mut PollFd, nfds: nfds_t, timeout_ts: *mut self::libc::timespec, sigmask: *const sigset_t) -> c_int;
    }

    let mut timeout_ts = self::libc::timespec {
        tv_sec: timeout.as_secs() as libc::time_t,
        tv_nsec: timeout.subsec_nanos() as libc::c_long,
    };

    unsafe {
        ppoll((&mut fds[..]).as_mut_ptr(),
              fds.len() as nfds_t,
              &mut timeout_ts,
              ptr::null())
    }
}

#[cfg(not(target_os = "linux"))]
#[inline]
fn do_poll(fds: &mut Vec<PollFd>, timeout: Duration) -> c_int {
    extern "C" {
        fn poll(fds: *mut PollFd, nfds: nfds_t, timeout: c_int) -> c_int;
    }

    let milliseconds = timeout.as_secs() * 1000 + timeout.subsec_nanos() as u64 / 1_000_000;

    unsafe {
        poll((&mut fds[..]).as_mut_ptr(),
             fds.len() as nfds_t,
             milliseconds as c_int)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Gomez Guillaume
//
// 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 std::fs::{OpenOptions, remove_file};
use std::io::{BufRead, BufReader, Write};
use std::collections::HashMap;
use std::ops::Deref;
use strip;
use utils::{join, loop_over_files};

use stripper_interface::{
    TypeStruct,
    EventType,
    Type,
    MOD_COMMENT,
    FILE_COMMENT,
    FILE,
};
use types::OUTPUT_COMMENT_FILE;

fn get_corresponding_type(elements: &[(Option<TypeStruct>, Vec<String>)],
                          to_find: &Option<TypeStruct>,
                          mut line: usize,
                          decal: &mut usize,
                          original_content: &mut Vec<String>) -> Option<usize> {
    let mut pos = 0;

    while pos < elements.len() {
        if match (&elements[pos].0, to_find) {
            (&Some(ref a), &Some(ref b)) => {
                let ret = a == b;

                // to detect variants
                if !ret && b.ty == Type::Unknown && b.parent.is_some() && a.parent.is_some() && a.parent == b.parent {
                    if match b.parent {
                        Some(ref p) => p.ty == Type::Struct || p.ty == Type::Enum || p.ty == Type::Use,
                        None => false,
                    } {
                        let mut tmp = b.clone();

                        tmp.ty = Type::Variant;
                        a == &tmp
                    } else {
                        false
                    }
                } else {
                    ret
                }
            },
            _ => false,
        } {
            let mut file_comment = false;

            if elements[pos].1.len() > 0 && elements[pos].1[0].starts_with(FILE_COMMENT) {
                line += 1;
                file_comment = true;
            } else {
                while line > 0 && (line + *decal) > 0 &&
                      original_content[line + *decal - 1].trim().starts_with("#") {
                    line -= 1;
                }
            }
            for comment in &elements[pos].1 {
                if file_comment {
                    original_content.insert(line + *decal, comment[FILE_COMMENT.len()..].to_owned());
                } else {
                    original_content.insert(line + *decal, comment.clone());
                }
                *decal += 1;
            }
            return Some(pos);
        }
        pos += 1;
    }
    None
}

pub fn regenerate_comments(path: &str, infos: &mut HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    if !infos.contains_key(path) {
        return;
    }
    match strip::build_event_list(path) {
        Ok(mut parse_result) => {
            let mut elements = infos.get_mut(path).unwrap();
            let mut position = 0;
            let mut decal = 0;

            // first, we need to put back file comment
            for entry in elements.iter() {
                if entry.0.is_none() {
                    let mut it = 0;

                    while it < parse_result.original_content.len() {
                        if parse_result.original_content[it].starts_with("/") &&
                           it + 1 < parse_result.original_content.len() &&
                           parse_result.original_content[it + 1].len() < 1 {
                            it += 2;
                            break;
                        }
                        it += 1;
                    }
                    if it < parse_result.original_content.len() {
                        for line in &entry.1 {
                            parse_result.original_content.insert(it, line.clone());
                            decal += 1;
                            it += 1;
                        }
                    }
                    break;
                }
                position += 1;
            }
            if position < elements.len() {
                elements.remove(position);
            }
            let mut waiting_type = None;
            let mut current = None;
            let mut it = 0;

            while it < parse_result.event_list.len() {
                match parse_result.event_list[it].event {
                    EventType::Type(ref t) => {
                        if t.ty != Type::Unknown {
                            waiting_type = Some(t.clone());
                            let tmp = strip::add_to_type_scope(&current, &waiting_type);

                            match get_corresponding_type(&elements, &tmp,
                                                         parse_result.event_list[it].line,
                                                         &mut decal,
                                                         &mut parse_result.original_content) {
                                Some(l) => { elements.remove(l); },
                                None => {}
                            };
                        } else {
                            match current {
                                Some(ref c) => {
                                    if c.ty == Type::Struct || c.ty == Type::Enum ||
                                       c.ty == Type::Mod {
                                        let tmp = Some(t.clone());
                                        let cc = strip::add_to_type_scope(&current, &tmp);

                                        match get_corresponding_type(&elements, &cc,
                                                                     parse_result.event_list[it].line,
                                                                     &mut decal,
                                                                     &mut parse_result.original_content) {
                                            Some(l) => { elements.remove(l); },
                                            None => {}
                                        }
                                    }
                                }
                                None => {}
                            }
                        }
                    }
                    EventType::InScope => {
                        current = strip::add_to_type_scope(&current, &waiting_type);
                        waiting_type = None;
                        match get_corresponding_type(&elements, &current,
                                                     parse_result.event_list[it].line,
                                                     &mut decal,
                                                     &mut parse_result.original_content) {
                            Some(l) => { elements.remove(l); },
                            None => {}
                        };
                    }
                    EventType::OutScope => {
                        current = strip::type_out_scope(&current);
                        waiting_type = None;
                    }
                    _ => {}
                }
                it += 1;
            }
            rewrite_file(path, &parse_result.original_content);
        }
        Err(e) => {
            println!("Error on file '{}': {}", path, e);
        }
    }
}

fn rewrite_file(path: &str, o_content: &[String]) {
    match OpenOptions::new().write(true).create(true).truncate(true).open(path) {
        Ok(mut f) => {
            write!(f, "{}", o_content.join("\n")).unwrap();
        }
        Err(e) => {
            println!("Cannot open '{}': {}", path, e);
        }
    }
}

fn parse_mod_line(line: &str) -> Option<TypeStruct> {
    let line = line.replace(MOD_COMMENT, "");
    let parts : Vec<&str> = line.split("§").collect();
    let mut current = None;

    for part in parts {
        let elems : Vec<&str> = part.split(" ").filter(|x| x.len() > 0).collect();

        current = strip::add_to_type_scope(&current.clone(),
                                           &Some(TypeStruct::new(Type::from(elems[0]),
                                                                 elems[elems.len() - 1])));
    }
    current
}

fn save_remainings(infos: &HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    let mut remainings = 0;

    for (_, content) in infos {
        if content.len() > 0 {
            remainings += 1;
        }
    }
    if remainings < 1 {
        remove_file(OUTPUT_COMMENT_FILE);
        return;
    }
    match OpenOptions::new().write(true).create(true).truncate(true).open(OUTPUT_COMMENT_FILE) {
        Ok(mut out_file) => {
            println!("Some comments couldn't have been regenerated to the files. Saving them back to '{}'.",
                     OUTPUT_COMMENT_FILE);
            for (key, content) in infos {
                if content.len() < 1 {
                    continue;
                }
                writeln!(out_file, "{}{}", FILE_COMMENT, key);
                for line in content {
                    match line.0 {
                        Some(ref d) => {
                            writeln!(out_file, "{}{}\n{}", MOD_COMMENT, d, join(&line.1, "\n"));
                        }
                        None => {}
                    }
                }
            }
            println!("Not implemented yet.");
        }
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    }
}

pub fn regenerate_doc_comments(directory: &str, verbose: bool) {
    // we start by storing files info
    let f = match OpenOptions::new().read(true).open(OUTPUT_COMMENT_FILE) {
        Ok(f) => f,
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    };
    let reader = BufReader::new(f);
    let lines = reader.lines().map(|line| line.unwrap());
    let mut infos = parse_cmts(lines);
    loop_over_files(directory, &mut infos, &regenerate_comments, &vec!(), verbose);
    save_remainings(&infos);
    // TODO: rewrite comments.cmts with remaining infos in regenerate_comments
}

pub fn parse_cmts<S, I>(mut lines: I) -> HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>
where S: Deref<Target = str>,
      I: Iterator<Item = S> {
    enum State {
        Initial,
        File {
            file: String,
            infos: Vec<(Option<TypeStruct>, Vec<String>)>,
            ty: Option<TypeStruct>,
            comments: Vec<String>,
        }
    }

    let mut ret = HashMap::new();
    let mut state = State::Initial;

    while let Some(line) = lines.next() {
        state = match state {
            State::Initial => {
                if line.starts_with(FILE) {
                    State::File {
                        file: line[FILE.len()..].to_owned(),
                        infos: vec![],
                        ty: None,
                        comments: vec![],
                    }
                } else {
                    panic!("Unrecognized format");
                }
            }
            State::File { mut file, mut infos, mut ty, mut comments } => {
                if line.starts_with(FILE) {
                    if !comments.is_empty() {
                        infos.push((ty.take(), comments));
                        comments = vec![];
                    }
                    if !infos.is_empty() {
                        ret.insert(file, infos);
                        file = line[FILE.len()..].to_owned();
                        infos = vec![];
                    }
                } else if line.starts_with(FILE_COMMENT) {
                    if let Some(ty) = ty.take() {
                        if !comments.is_empty() {
                            infos.push((Some(ty), comments));
                            comments = vec![];
                        }
                    } else {
                        comments.push(line[FILE_COMMENT.len()..].to_owned());
                    }
                } else if line.starts_with(MOD_COMMENT) {
                    if !comments.is_empty() {
                        infos.push((ty, comments));
                        comments = vec![];
                    }
                    ty = parse_mod_line(&line[..]);
                } else {
                    if ty.is_some() {
                        comments.push(line[..].to_owned());
                    } else {
                        panic!("Orphan comment");
                    }
                }
                State::File {
                    file: file,
                    infos: infos,
                    ty: ty,
                    comments: comments,
                }
            }
        }
    }

    if let State::File { file, mut infos, ty, comments } = state {
        if !comments.is_empty() {
            infos.push((ty, comments));
        }
        if !infos.is_empty() {
            ret.insert(file, infos);
        }
    }

    ret
}
<commit_msg>Silence warnings<commit_after>// Copyright 2015 Gomez Guillaume
//
// 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 std::fs::{OpenOptions, remove_file};
use std::io::{BufRead, BufReader, Write};
use std::collections::HashMap;
use std::ops::Deref;
use strip;
use utils::{join, loop_over_files};

use stripper_interface::{
    TypeStruct,
    EventType,
    Type,
    MOD_COMMENT,
    FILE_COMMENT,
    FILE,
};
use types::OUTPUT_COMMENT_FILE;

fn get_corresponding_type(elements: &[(Option<TypeStruct>, Vec<String>)],
                          to_find: &Option<TypeStruct>,
                          mut line: usize,
                          decal: &mut usize,
                          original_content: &mut Vec<String>) -> Option<usize> {
    let mut pos = 0;

    while pos < elements.len() {
        if match (&elements[pos].0, to_find) {
            (&Some(ref a), &Some(ref b)) => {
                let ret = a == b;

                // to detect variants
                if !ret && b.ty == Type::Unknown && b.parent.is_some() && a.parent.is_some() && a.parent == b.parent {
                    if match b.parent {
                        Some(ref p) => p.ty == Type::Struct || p.ty == Type::Enum || p.ty == Type::Use,
                        None => false,
                    } {
                        let mut tmp = b.clone();

                        tmp.ty = Type::Variant;
                        a == &tmp
                    } else {
                        false
                    }
                } else {
                    ret
                }
            },
            _ => false,
        } {
            let mut file_comment = false;

            if elements[pos].1.len() > 0 && elements[pos].1[0].starts_with(FILE_COMMENT) {
                line += 1;
                file_comment = true;
            } else {
                while line > 0 && (line + *decal) > 0 &&
                      original_content[line + *decal - 1].trim().starts_with("#") {
                    line -= 1;
                }
            }
            for comment in &elements[pos].1 {
                if file_comment {
                    original_content.insert(line + *decal, comment[FILE_COMMENT.len()..].to_owned());
                } else {
                    original_content.insert(line + *decal, comment.clone());
                }
                *decal += 1;
            }
            return Some(pos);
        }
        pos += 1;
    }
    None
}

pub fn regenerate_comments(path: &str, infos: &mut HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    if !infos.contains_key(path) {
        return;
    }
    match strip::build_event_list(path) {
        Ok(mut parse_result) => {
            let mut elements = infos.get_mut(path).unwrap();
            let mut position = 0;
            let mut decal = 0;

            // first, we need to put back file comment
            for entry in elements.iter() {
                if entry.0.is_none() {
                    let mut it = 0;

                    while it < parse_result.original_content.len() {
                        if parse_result.original_content[it].starts_with("/") &&
                           it + 1 < parse_result.original_content.len() &&
                           parse_result.original_content[it + 1].len() < 1 {
                            it += 2;
                            break;
                        }
                        it += 1;
                    }
                    if it < parse_result.original_content.len() {
                        for line in &entry.1 {
                            parse_result.original_content.insert(it, line.clone());
                            decal += 1;
                            it += 1;
                        }
                    }
                    break;
                }
                position += 1;
            }
            if position < elements.len() {
                elements.remove(position);
            }
            let mut waiting_type = None;
            let mut current = None;
            let mut it = 0;

            while it < parse_result.event_list.len() {
                match parse_result.event_list[it].event {
                    EventType::Type(ref t) => {
                        if t.ty != Type::Unknown {
                            waiting_type = Some(t.clone());
                            let tmp = strip::add_to_type_scope(&current, &waiting_type);

                            match get_corresponding_type(&elements, &tmp,
                                                         parse_result.event_list[it].line,
                                                         &mut decal,
                                                         &mut parse_result.original_content) {
                                Some(l) => { elements.remove(l); },
                                None => {}
                            };
                        } else {
                            match current {
                                Some(ref c) => {
                                    if c.ty == Type::Struct || c.ty == Type::Enum ||
                                       c.ty == Type::Mod {
                                        let tmp = Some(t.clone());
                                        let cc = strip::add_to_type_scope(&current, &tmp);

                                        match get_corresponding_type(&elements, &cc,
                                                                     parse_result.event_list[it].line,
                                                                     &mut decal,
                                                                     &mut parse_result.original_content) {
                                            Some(l) => { elements.remove(l); },
                                            None => {}
                                        }
                                    }
                                }
                                None => {}
                            }
                        }
                    }
                    EventType::InScope => {
                        current = strip::add_to_type_scope(&current, &waiting_type);
                        waiting_type = None;
                        match get_corresponding_type(&elements, &current,
                                                     parse_result.event_list[it].line,
                                                     &mut decal,
                                                     &mut parse_result.original_content) {
                            Some(l) => { elements.remove(l); },
                            None => {}
                        };
                    }
                    EventType::OutScope => {
                        current = strip::type_out_scope(&current);
                        waiting_type = None;
                    }
                    _ => {}
                }
                it += 1;
            }
            rewrite_file(path, &parse_result.original_content);
        }
        Err(e) => {
            println!("Error on file '{}': {}", path, e);
        }
    }
}

fn rewrite_file(path: &str, o_content: &[String]) {
    match OpenOptions::new().write(true).create(true).truncate(true).open(path) {
        Ok(mut f) => {
            write!(f, "{}", o_content.join("\n")).unwrap();
        }
        Err(e) => {
            println!("Cannot open '{}': {}", path, e);
        }
    }
}

fn parse_mod_line(line: &str) -> Option<TypeStruct> {
    let line = line.replace(MOD_COMMENT, "");
    let parts : Vec<&str> = line.split("§").collect();
    let mut current = None;

    for part in parts {
        let elems : Vec<&str> = part.split(" ").filter(|x| x.len() > 0).collect();

        current = strip::add_to_type_scope(&current.clone(),
                                           &Some(TypeStruct::new(Type::from(elems[0]),
                                                                 elems[elems.len() - 1])));
    }
    current
}

fn save_remainings(infos: &HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>) {
    let mut remainings = 0;

    for (_, content) in infos {
        if content.len() > 0 {
            remainings += 1;
        }
    }
    if remainings < 1 {
        let _ = remove_file(OUTPUT_COMMENT_FILE);
        return;
    }
    match OpenOptions::new().write(true).create(true).truncate(true).open(OUTPUT_COMMENT_FILE) {
        Ok(mut out_file) => {
            println!("Some comments couldn't have been regenerated to the files. Saving them back to '{}'.",
                     OUTPUT_COMMENT_FILE);
            for (key, content) in infos {
                if content.len() < 1 {
                    continue;
                }
                let _ = writeln!(out_file, "{}{}", FILE_COMMENT, key);
                for line in content {
                    match line.0 {
                        Some(ref d) => {
                            let _ = writeln!(out_file, "{}{}\n{}", MOD_COMMENT, d, join(&line.1, "\n"));
                        }
                        None => {}
                    }
                }
            }
            println!("Not implemented yet.");
        }
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    }
}

pub fn regenerate_doc_comments(directory: &str, verbose: bool) {
    // we start by storing files info
    let f = match OpenOptions::new().read(true).open(OUTPUT_COMMENT_FILE) {
        Ok(f) => f,
        Err(e) => {
            println!("An error occured while trying to open '{}': {}", OUTPUT_COMMENT_FILE, e);
            return;
        }
    };
    let reader = BufReader::new(f);
    let lines = reader.lines().map(|line| line.unwrap());
    let mut infos = parse_cmts(lines);
    loop_over_files(directory, &mut infos, &regenerate_comments, &vec!(), verbose);
    save_remainings(&infos);
    // TODO: rewrite comments.cmts with remaining infos in regenerate_comments
}

pub fn parse_cmts<S, I>(mut lines: I) -> HashMap<String, Vec<(Option<TypeStruct>, Vec<String>)>>
where S: Deref<Target = str>,
      I: Iterator<Item = S> {
    enum State {
        Initial,
        File {
            file: String,
            infos: Vec<(Option<TypeStruct>, Vec<String>)>,
            ty: Option<TypeStruct>,
            comments: Vec<String>,
        }
    }

    let mut ret = HashMap::new();
    let mut state = State::Initial;

    while let Some(line) = lines.next() {
        state = match state {
            State::Initial => {
                if line.starts_with(FILE) {
                    State::File {
                        file: line[FILE.len()..].to_owned(),
                        infos: vec![],
                        ty: None,
                        comments: vec![],
                    }
                } else {
                    panic!("Unrecognized format");
                }
            }
            State::File { mut file, mut infos, mut ty, mut comments } => {
                if line.starts_with(FILE) {
                    if !comments.is_empty() {
                        infos.push((ty.take(), comments));
                        comments = vec![];
                    }
                    if !infos.is_empty() {
                        ret.insert(file, infos);
                        file = line[FILE.len()..].to_owned();
                        infos = vec![];
                    }
                } else if line.starts_with(FILE_COMMENT) {
                    if let Some(ty) = ty.take() {
                        if !comments.is_empty() {
                            infos.push((Some(ty), comments));
                            comments = vec![];
                        }
                    } else {
                        comments.push(line[FILE_COMMENT.len()..].to_owned());
                    }
                } else if line.starts_with(MOD_COMMENT) {
                    if !comments.is_empty() {
                        infos.push((ty, comments));
                        comments = vec![];
                    }
                    ty = parse_mod_line(&line[..]);
                } else {
                    if ty.is_some() {
                        comments.push(line[..].to_owned());
                    } else {
                        panic!("Orphan comment");
                    }
                }
                State::File {
                    file: file,
                    infos: infos,
                    ty: ty,
                    comments: comments,
                }
            }
        }
    }

    if let State::File { file, mut infos, ty, comments } = state {
        if !comments.is_empty() {
            infos.push((ty, comments));
        }
        if !infos.is_empty() {
            ret.insert(file, infos);
        }
    }

    ret
}
<|endoftext|>


<commit_before>mod renderable;
mod draw;
mod color;
pub mod screen_scrape;

use cairo::{self, ImageSurface};
use gdk_pixbuf::Pixbuf;
pub use self::renderable::Renderable;
pub use self::draw::{Drawable, DrawErr, BaseDraw};
pub use self::color::Color;

/// Using a Pixbuf buffer, loads the data into a Cairo surface.
pub fn load_surface_from_pixbuf(pixbuf: Pixbuf) -> ImageSurface {
    let width = pixbuf.get_width();
    let height = pixbuf.get_height();
    let channels = pixbuf.get_n_channels();
    let pix_stride = pixbuf.get_rowstride() as usize;
    // NOTE This is safe because we aren't modifying the bytes, but there's no immutable view
    let pixels = unsafe { pixbuf.get_pixels() };
    let format = if channels == 3 {cairo::Format::Rgb24} else { cairo::Format::ARgb32};
    let mut surface = ImageSurface::create(format, width, height)
        .expect("Could not create image of that size");
    let cairo_stride = surface.get_stride() as usize;
    {
        let mut cairo_data = surface.get_data().unwrap();
        let mut pix_pixels_index = 0;
        let mut cairo_pixels_index = 0;
        for _ in 0..height {
            let mut pix_pixels_index2 = pix_pixels_index;
            let mut cairo_pixels_index2 = cairo_pixels_index;
            for _ in 0..width {
                if channels == 3 {
                    let r = pixels[pix_pixels_index2];
                    let g = pixels[pix_pixels_index2 + 1];
                    let b = pixels[pix_pixels_index2 + 2];
                    cairo_data[cairo_pixels_index2] = b;
                    cairo_data[cairo_pixels_index2 + 1] = g;
                    cairo_data[cairo_pixels_index2 + 2] = r;
                    pix_pixels_index2 += 3;
                    // NOTE Four because of the alpha value we ignore
                    cairo_pixels_index2 += 4;
                } else {
                    let mut r = pixels[pix_pixels_index];
                    let mut g = pixels[pix_pixels_index + 1];
                    let mut b = pixels[pix_pixels_index + 2];
                    let a = pixels[pix_pixels_index + 3];
                    let alpha = a as f64 / 255.0;
                    r *= alpha as u8;
                    g *= alpha as u8;
                    b *= alpha as u8;
                    cairo_data[cairo_pixels_index] = b;
                    cairo_data[cairo_pixels_index + 1] = g;
                    cairo_data[cairo_pixels_index + 2] = r;
                    cairo_data[cairo_pixels_index + 3] = a;
                    pix_pixels_index += 4;
                    cairo_pixels_index += 4;
                }
            }
            pix_pixels_index += pix_stride;
            cairo_pixels_index += cairo_stride;
        }
    }
    surface
}
<commit_msg>Fixed logic bug in 4 channel load_image<commit_after>mod renderable;
mod draw;
mod color;
pub mod screen_scrape;

use cairo::{self, ImageSurface};
use gdk_pixbuf::Pixbuf;
pub use self::renderable::Renderable;
pub use self::draw::{Drawable, DrawErr, BaseDraw};
pub use self::color::Color;

/// Using a Pixbuf buffer, loads the data into a Cairo surface.
pub fn load_surface_from_pixbuf(pixbuf: Pixbuf) -> ImageSurface {
    let width = pixbuf.get_width();
    let height = pixbuf.get_height();
    let channels = pixbuf.get_n_channels();
    let pix_stride = pixbuf.get_rowstride() as usize;
    // NOTE This is safe because we aren't modifying the bytes, but there's no immutable view
    let pixels = unsafe { pixbuf.get_pixels() };
    let format = if channels == 3 {cairo::Format::Rgb24} else { cairo::Format::ARgb32};
    let mut surface = ImageSurface::create(format, width, height)
        .expect("Could not create image of that size");
    let cairo_stride = surface.get_stride() as usize;
    {
        let mut cairo_data = surface.get_data().unwrap();
        let mut pix_pixels_index = 0;
        let mut cairo_pixels_index = 0;
        for _ in 0..height {
            let mut pix_pixels_index2 = pix_pixels_index;
            let mut cairo_pixels_index2 = cairo_pixels_index;
            for _ in 0..width {
                if channels == 3 {
                    let r = pixels[pix_pixels_index2];
                    let g = pixels[pix_pixels_index2 + 1];
                    let b = pixels[pix_pixels_index2 + 2];
                    cairo_data[cairo_pixels_index2] = b;
                    cairo_data[cairo_pixels_index2 + 1] = g;
                    cairo_data[cairo_pixels_index2 + 2] = r;
                    pix_pixels_index2 += 3;
                    // NOTE Four because of the alpha value we ignore
                    cairo_pixels_index2 += 4;
                } else {
                    let mut r = pixels[pix_pixels_index];
                    let mut g = pixels[pix_pixels_index + 1];
                    let mut b = pixels[pix_pixels_index + 2];
                    let a = pixels[pix_pixels_index + 3];
                    let alpha = a as f64 / 255.0;
                    r *= alpha as u8;
                    g *= alpha as u8;
                    b *= alpha as u8;
                    cairo_data[cairo_pixels_index] = b;
                    cairo_data[cairo_pixels_index + 1] = g;
                    cairo_data[cairo_pixels_index + 2] = r;
                    cairo_data[cairo_pixels_index + 3] = a;
                    pix_pixels_index2 += 4;
                    cairo_pixels_index2 += 4;
                }
            }
            pix_pixels_index += pix_stride;
            cairo_pixels_index += cairo_stride;
        }
    }
    surface
}
<|endoftext|>


<commit_before>use std::ops::Deref;

use cgmath::Point2;

use crate::bsdf::BSDF;
use crate::color::Color;
use crate::float::*;
use crate::obj_load;
use crate::texture::Texture;

mod diffuse;
mod glossy;
mod specular;

use self::diffuse::*;
use self::glossy::*;
use self::specular::*;

/// Scattering model over the whole surface
pub trait ScatteringT {
    /// Get the local scattering functions
    fn local(&self, tex_coords: Point2<Float>) -> BSDF;
    /// The texture to use for preview rendering
    fn preview_texture(&self) -> &Texture;
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum Scattering {
    DR(DiffuseReflection),
    GB(GlossyBlend),
    GR(GlossyReflection),
    GT(GlossyTransmission),
    SR(SpecularReflection),
    ST(SpecularTransmission),
}

fn diffuse_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.diffuse_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.diffuse_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn specular_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.specular_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.specular_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn transmissive_texture(obj_mat: &obj_load::Material) -> Texture {
    let color = Color::from(
        obj_mat
            .transmission_color
            .expect("No transmission color for transmissive material"),
    );
    // MTL spec states that transmissive color defines light that is able to pass through
    // but some scenes seem to interpret it as a filter that removes light
    let color = Color::white() - color;
    Texture::from_color(color)
}

impl Scattering {
    pub fn from_obj(obj_mat: &obj_load::Material) -> Self {
        use self::Scattering::*;

        let diffuse = diffuse_texture(obj_mat);
        let specular = specular_texture(obj_mat);
        match obj_mat.illumination_model {
            Some(2) => {
                let exponent = obj_mat.specular_exponent.unwrap().to_float();
                if diffuse.is_black() {
                    GR(GlossyReflection::new(specular, exponent))
                } else if specular.is_black() {
                    DR(DiffuseReflection::new(diffuse))
                } else {
                    GB(GlossyBlend::new(diffuse, specular, exponent))
                }
            }
            Some(5) => {
                let texture = specular_texture(obj_mat);
                SR(SpecularReflection::new(texture))
            }
            Some(4) | Some(6) | Some(7) => {
                let transmissive = transmissive_texture(obj_mat);
                let exponent = obj_mat.specular_exponent.unwrap().to_float();
                let eta = obj_mat
                    .index_of_refraction
                    .expect("No index of refraction for translucent material")
                    .to_float();
                if (eta - 1.0).abs() < crate::consts::EPSILON {
                    // Glossy does not handle eta = 1 properly
                    // and the distribution would be the same anyways
                    ST(SpecularTransmission::new(specular, transmissive, eta))
                } else {
                    GT(GlossyTransmission::new(
                        specular,
                        transmissive,
                        exponent,
                        eta,
                    ))
                }
            }
            Some(i) => {
                if i > 10 {
                    println!("Illumination model {} is not defined in the mtl spec!", i);
                } else {
                    println!("Unimplemented illumination model {}!", i);
                }
                println!("Defaulting to diffuse reflection.");
                DR(DiffuseReflection::new(diffuse))
            }
            None => DR(DiffuseReflection::new(diffuse)),
        }
    }
}

impl Deref for Scattering {
    type Target = dyn ScatteringT;

    fn deref(&self) -> &Self::Target {
        use self::Scattering::*;
        match self {
            DR(inner) => inner,
            GB(inner) => inner,
            GR(inner) => inner,
            GT(inner) => inner,
            SR(inner) => inner,
            ST(inner) => inner,
        }
    }
}
<commit_msg>Don't crash if there is no specular exponent and no specular color<commit_after>use std::ops::Deref;

use cgmath::Point2;

use crate::bsdf::BSDF;
use crate::color::Color;
use crate::float::*;
use crate::obj_load;
use crate::texture::Texture;

mod diffuse;
mod glossy;
mod specular;

use self::diffuse::*;
use self::glossy::*;
use self::specular::*;

/// Scattering model over the whole surface
pub trait ScatteringT {
    /// Get the local scattering functions
    fn local(&self, tex_coords: Point2<Float>) -> BSDF;
    /// The texture to use for preview rendering
    fn preview_texture(&self) -> &Texture;
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum Scattering {
    DR(DiffuseReflection),
    GB(GlossyBlend),
    GR(GlossyReflection),
    GT(GlossyTransmission),
    SR(SpecularReflection),
    ST(SpecularTransmission),
}

fn diffuse_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.diffuse_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.diffuse_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn specular_texture(obj_mat: &obj_load::Material) -> Texture {
    match &obj_mat.specular_texture {
        Some(path) => Texture::from_image_path(path),
        None => {
            let color = Color::from(obj_mat.specular_color.unwrap_or([0.0, 0.0, 0.0]));
            Texture::from_color(color)
        }
    }
}

fn transmissive_texture(obj_mat: &obj_load::Material) -> Texture {
    let color = Color::from(
        obj_mat
            .transmission_color
            .expect("No transmission color for transmissive material"),
    );
    // MTL spec states that transmissive color defines light that is able to pass through
    // but some scenes seem to interpret it as a filter that removes light
    let color = Color::white() - color;
    Texture::from_color(color)
}

impl Scattering {
    pub fn from_obj(obj_mat: &obj_load::Material) -> Self {
        use self::Scattering::*;

        let diffuse = diffuse_texture(obj_mat);
        let specular = specular_texture(obj_mat);
        match obj_mat.illumination_model {
            Some(2) => {
                let exponent = obj_mat.specular_exponent.map(|e| e.to_float());
                if diffuse.is_black() {
                    GR(GlossyReflection::new(specular, exponent.unwrap()))
                } else if specular.is_black() {
                    DR(DiffuseReflection::new(diffuse))
                } else {
                    GB(GlossyBlend::new(diffuse, specular, exponent.unwrap()))
                }
            }
            Some(5) => {
                let texture = specular_texture(obj_mat);
                SR(SpecularReflection::new(texture))
            }
            Some(4) | Some(6) | Some(7) => {
                let transmissive = transmissive_texture(obj_mat);
                let eta = obj_mat
                    .index_of_refraction
                    .expect("No index of refraction for translucent material")
                    .to_float();
                if (eta - 1.0).abs() < crate::consts::EPSILON {
                    // Glossy does not handle eta = 1 properly
                    // and the distribution would be the same anyways
                    ST(SpecularTransmission::new(specular, transmissive, eta))
                } else {
                    let exponent = obj_mat.specular_exponent.unwrap().to_float();
                    GT(GlossyTransmission::new(
                        specular,
                        transmissive,
                        exponent,
                        eta,
                    ))
                }
            }
            Some(i) => {
                if i > 10 {
                    println!("Illumination model {} is not defined in the mtl spec!", i);
                } else {
                    println!("Unimplemented illumination model {}!", i);
                }
                println!("Defaulting to diffuse reflection.");
                DR(DiffuseReflection::new(diffuse))
            }
            None => DR(DiffuseReflection::new(diffuse)),
        }
    }
}

impl Deref for Scattering {
    type Target = dyn ScatteringT;

    fn deref(&self) -> &Self::Target {
        use self::Scattering::*;
        match self {
            DR(inner) => inner,
            GB(inner) => inner,
            GR(inner) => inner,
            GT(inner) => inner,
            SR(inner) => inner,
            ST(inner) => inner,
        }
    }
}
<|endoftext|>


<commit_before>use std::ptr;

use get_error;
use SdlResult;
use surface;
use video;

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_uint, c_void, uint8_t, uint32_t};
    use surface::ll::SDL_Surface;
    use video::ll::SDL_Window;

    pub type SDL_bool = c_int;
    pub type SDL_Cursor = c_void;

    pub type SDL_SystemCursor = c_uint;
    pub const SDL_SYSTEM_CURSOR_ARROW: SDL_SystemCursor = 0;
    pub const SDL_SYSTEM_CURSOR_IBEAM: SDL_SystemCursor = 1;
    pub const SDL_SYSTEM_CURSOR_WAIT: SDL_SystemCursor = 2;
    pub const SDL_SYSTEM_CURSOR_CROSSHAIR: SDL_SystemCursor = 3;
    pub const SDL_SYSTEM_CURSOR_WAITARROW: SDL_SystemCursor = 4;
    pub const SDL_SYSTEM_CURSOR_SIZENWSE: SDL_SystemCursor = 5;
    pub const SDL_SYSTEM_CURSOR_SIZENESW: SDL_SystemCursor = 6;
    pub const SDL_SYSTEM_CURSOR_SIZEWE: SDL_SystemCursor = 7;
    pub const SDL_SYSTEM_CURSOR_SIZENS: SDL_SystemCursor = 8;
    pub const SDL_SYSTEM_CURSOR_SIZEALL: SDL_SystemCursor = 9;
    pub const SDL_SYSTEM_CURSOR_NO: SDL_SystemCursor = 10;
    pub const SDL_SYSTEM_CURSOR_HAND: SDL_SystemCursor = 11;
    pub const SDL_NUM_SYSTEM_CURSORS: SDL_SystemCursor = 12;

    pub type SDL_MouseState = c_int;
    pub const SDL_DISABLE: SDL_MouseState = 0;
    pub const SDL_ENABLE: SDL_MouseState = 1;
    pub const SDL_QUERY: SDL_MouseState = -1;

    extern "C" {
        pub fn SDL_GetMouseFocus() -> *const SDL_Window;
        pub fn SDL_GetMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_GetRelativeMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_WarpMouseInWindow(window: *const SDL_Window, x: c_int, y: c_int);
        pub fn SDL_SetRelativeMouseMode(enabled: SDL_bool) -> c_int;
        pub fn SDL_GetRelativeMouseMode() -> SDL_bool;
        pub fn SDL_CreateCursor(data: *const uint8_t, mask: *const uint8_t, w: c_int,
                                      h: c_int, hot_x: c_int, hot_y: c_int) ->
                  *const SDL_Cursor;
        pub fn SDL_CreateColorCursor(surface: *const SDL_Surface, hot_x: c_int,
                                           hot_y: c_int) -> *const SDL_Cursor;
        pub fn SDL_CreateSystemCursor(id: SDL_SystemCursor) -> *const SDL_Cursor;
        pub fn SDL_SetCursor(cursor: *const SDL_Cursor);
        pub fn SDL_GetCursor() -> *const SDL_Cursor;
        pub fn SDL_GetDefaultCursor() -> *const SDL_Cursor;
        pub fn SDL_FreeCursor(cursor: *const SDL_Cursor);
        pub fn SDL_ShowCursor(toggle: SDL_MouseState) -> SDL_MouseState;
    }
}

#[deriving(PartialEq)]
#[repr(u32)]
pub enum SystemCursor {
    ArrowCursor = ll::SDL_SYSTEM_CURSOR_ARROW,
    IBeamCursor = ll::SDL_SYSTEM_CURSOR_IBEAM,
    WaitCursor = ll::SDL_SYSTEM_CURSOR_WAIT,
    CrosshairCursor = ll::SDL_SYSTEM_CURSOR_CROSSHAIR,
    WaitArrowCursor = ll::SDL_SYSTEM_CURSOR_WAITARROW,
    SizeNWSECursor = ll::SDL_SYSTEM_CURSOR_SIZENWSE,
    SizeNESWCursor = ll::SDL_SYSTEM_CURSOR_SIZENESW,
    SizeWECursor = ll::SDL_SYSTEM_CURSOR_SIZEWE,
    SizeNSCursor = ll::SDL_SYSTEM_CURSOR_SIZENS,
    SizeAllCursor = ll::SDL_SYSTEM_CURSOR_SIZEALL,
    NoCursor = ll::SDL_SYSTEM_CURSOR_NO,
    HandCursor = ll::SDL_SYSTEM_CURSOR_HAND,
}

#[deriving(PartialEq)] #[allow(raw_pointer_deriving)]
pub struct Cursor {
    raw: *const ll::SDL_Cursor,
    owned: bool
}

impl Drop for Cursor {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ll::SDL_FreeCursor(self.raw);
            }
        }
    }
}

impl Cursor {
    pub fn new(data: &[u8], mask: &[u8], width: int, height: int, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateCursor(data.as_ptr(),
                                           mask.as_ptr(),
                                           width as i32, height as i32,
                                           hot_x as i32, hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    // TODO: figure out how to pass Surface in here correctly
    pub fn from_surface(surface: &surface::Surface, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateColorCursor(surface.raw(), hot_x as i32,
                                                hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn from_system(cursor: SystemCursor) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateSystemCursor(cursor as u32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn set(&self) {
        unsafe { ll::SDL_SetCursor(self.raw); }
    }
}

#[deriving(PartialEq)]
pub enum Mouse {
    LeftMouse,
    MiddleMouse,
    RightMouse,
    X1Mouse,
    X2Mouse,
    UnknownMouse(u8)
}

bitflags! {
    flags MouseState: u32 {
        const LEFTMOUSESTATE = 0x01,
        const MIDDLEMOUSESTATE = 0x02,
        const RIGHTMOUSESTATE = 0x04,
        const X1MOUSESTATE = 0x08,
        const X2MOUSESTATE = 0x10
    }
}

pub fn wrap_mouse(bitflags: u8) -> Mouse {
    match bitflags {
        1 => Mouse::LeftMouse,
        2 => Mouse::MiddleMouse,
        3 => Mouse::RightMouse,
        4 => Mouse::X1Mouse,
        5 => Mouse::X2Mouse,
        _ => Mouse::UnknownMouse(bitflags)
    }
}

pub fn get_mouse_focus() -> Option<video::Window> {
    let raw = unsafe { ll::SDL_GetMouseFocus() };
    if raw == ptr::null() {
        None
    } else {
        unsafe { Some(video::Window::from_ll(raw, false)) }
    }
}

pub fn get_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn get_relative_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetRelativeMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn warp_mouse_in_window(window: &video::Window, x: i32, y: i32) {
    unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
}

pub fn set_relative_mouse_mode(on: bool) {
    unsafe { ll::SDL_SetRelativeMouseMode(on as i32); }
}

pub fn get_relative_mouse_mode() -> bool {
    unsafe { ll::SDL_GetRelativeMouseMode() == 1 }
}

pub fn get_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn get_default_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetDefaultCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn is_cursor_showing() -> bool {
    unsafe { ll::SDL_ShowCursor(ll::SDL_QUERY) == 1 }
}

pub fn show_cursor(show: bool) {
    unsafe { ll::SDL_ShowCursor(show as i32); }
}
<commit_msg>Remove "Cursor" suffix from SystemCursor enum variants.<commit_after>use std::ptr;

use get_error;
use SdlResult;
use surface;
use video;

#[allow(non_camel_case_types)]
pub mod ll {
    use libc::{c_int, c_uint, c_void, uint8_t, uint32_t};
    use surface::ll::SDL_Surface;
    use video::ll::SDL_Window;

    pub type SDL_bool = c_int;
    pub type SDL_Cursor = c_void;

    pub type SDL_SystemCursor = c_uint;
    pub const SDL_SYSTEM_CURSOR_ARROW: SDL_SystemCursor = 0;
    pub const SDL_SYSTEM_CURSOR_IBEAM: SDL_SystemCursor = 1;
    pub const SDL_SYSTEM_CURSOR_WAIT: SDL_SystemCursor = 2;
    pub const SDL_SYSTEM_CURSOR_CROSSHAIR: SDL_SystemCursor = 3;
    pub const SDL_SYSTEM_CURSOR_WAITARROW: SDL_SystemCursor = 4;
    pub const SDL_SYSTEM_CURSOR_SIZENWSE: SDL_SystemCursor = 5;
    pub const SDL_SYSTEM_CURSOR_SIZENESW: SDL_SystemCursor = 6;
    pub const SDL_SYSTEM_CURSOR_SIZEWE: SDL_SystemCursor = 7;
    pub const SDL_SYSTEM_CURSOR_SIZENS: SDL_SystemCursor = 8;
    pub const SDL_SYSTEM_CURSOR_SIZEALL: SDL_SystemCursor = 9;
    pub const SDL_SYSTEM_CURSOR_NO: SDL_SystemCursor = 10;
    pub const SDL_SYSTEM_CURSOR_HAND: SDL_SystemCursor = 11;
    pub const SDL_NUM_SYSTEM_CURSORS: SDL_SystemCursor = 12;

    pub type SDL_MouseState = c_int;
    pub const SDL_DISABLE: SDL_MouseState = 0;
    pub const SDL_ENABLE: SDL_MouseState = 1;
    pub const SDL_QUERY: SDL_MouseState = -1;

    extern "C" {
        pub fn SDL_GetMouseFocus() -> *const SDL_Window;
        pub fn SDL_GetMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_GetRelativeMouseState(x: *const c_int, y: *const c_int) -> uint32_t;
        pub fn SDL_WarpMouseInWindow(window: *const SDL_Window, x: c_int, y: c_int);
        pub fn SDL_SetRelativeMouseMode(enabled: SDL_bool) -> c_int;
        pub fn SDL_GetRelativeMouseMode() -> SDL_bool;
        pub fn SDL_CreateCursor(data: *const uint8_t, mask: *const uint8_t, w: c_int,
                                      h: c_int, hot_x: c_int, hot_y: c_int) ->
                  *const SDL_Cursor;
        pub fn SDL_CreateColorCursor(surface: *const SDL_Surface, hot_x: c_int,
                                           hot_y: c_int) -> *const SDL_Cursor;
        pub fn SDL_CreateSystemCursor(id: SDL_SystemCursor) -> *const SDL_Cursor;
        pub fn SDL_SetCursor(cursor: *const SDL_Cursor);
        pub fn SDL_GetCursor() -> *const SDL_Cursor;
        pub fn SDL_GetDefaultCursor() -> *const SDL_Cursor;
        pub fn SDL_FreeCursor(cursor: *const SDL_Cursor);
        pub fn SDL_ShowCursor(toggle: SDL_MouseState) -> SDL_MouseState;
    }
}

#[deriving(PartialEq)]
#[repr(u32)]
pub enum SystemCursor {
    Arrow = ll::SDL_SYSTEM_CURSOR_ARROW,
    IBeam = ll::SDL_SYSTEM_CURSOR_IBEAM,
    Wait = ll::SDL_SYSTEM_CURSOR_WAIT,
    Crosshair = ll::SDL_SYSTEM_CURSOR_CROSSHAIR,
    WaitArrow = ll::SDL_SYSTEM_CURSOR_WAITARROW,
    SizeNWSE = ll::SDL_SYSTEM_CURSOR_SIZENWSE,
    SizeNESW = ll::SDL_SYSTEM_CURSOR_SIZENESW,
    SizeWE = ll::SDL_SYSTEM_CURSOR_SIZEWE,
    SizeNS = ll::SDL_SYSTEM_CURSOR_SIZENS,
    SizeAll = ll::SDL_SYSTEM_CURSOR_SIZEALL,
    No = ll::SDL_SYSTEM_CURSOR_NO,
    Hand = ll::SDL_SYSTEM_CURSOR_HAND,
}

#[deriving(PartialEq)] #[allow(raw_pointer_deriving)]
pub struct Cursor {
    raw: *const ll::SDL_Cursor,
    owned: bool
}

impl Drop for Cursor {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ll::SDL_FreeCursor(self.raw);
            }
        }
    }
}

impl Cursor {
    pub fn new(data: &[u8], mask: &[u8], width: int, height: int, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateCursor(data.as_ptr(),
                                           mask.as_ptr(),
                                           width as i32, height as i32,
                                           hot_x as i32, hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    // TODO: figure out how to pass Surface in here correctly
    pub fn from_surface(surface: &surface::Surface, hot_x: int, hot_y: int) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateColorCursor(surface.raw(), hot_x as i32,
                                                hot_y as i32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn from_system(cursor: SystemCursor) -> SdlResult<Cursor> {
        unsafe {
            let raw = ll::SDL_CreateSystemCursor(cursor as u32);

            if raw == ptr::null() {
                Err(get_error())
            } else {
                Ok(Cursor{ raw: raw, owned: true })
            }
        }
    }

    pub fn set(&self) {
        unsafe { ll::SDL_SetCursor(self.raw); }
    }
}

#[deriving(PartialEq)]
pub enum Mouse {
    LeftMouse,
    MiddleMouse,
    RightMouse,
    X1Mouse,
    X2Mouse,
    UnknownMouse(u8)
}

bitflags! {
    flags MouseState: u32 {
        const LEFTMOUSESTATE = 0x01,
        const MIDDLEMOUSESTATE = 0x02,
        const RIGHTMOUSESTATE = 0x04,
        const X1MOUSESTATE = 0x08,
        const X2MOUSESTATE = 0x10
    }
}

pub fn wrap_mouse(bitflags: u8) -> Mouse {
    match bitflags {
        1 => Mouse::LeftMouse,
        2 => Mouse::MiddleMouse,
        3 => Mouse::RightMouse,
        4 => Mouse::X1Mouse,
        5 => Mouse::X2Mouse,
        _ => Mouse::UnknownMouse(bitflags)
    }
}

pub fn get_mouse_focus() -> Option<video::Window> {
    let raw = unsafe { ll::SDL_GetMouseFocus() };
    if raw == ptr::null() {
        None
    } else {
        unsafe { Some(video::Window::from_ll(raw, false)) }
    }
}

pub fn get_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn get_relative_mouse_state() -> (MouseState, int, int) {
    let x = 0;
    let y = 0;
    unsafe {
        let raw = ll::SDL_GetRelativeMouseState(&x, &y);
        return (MouseState::from_bits(raw).unwrap(), x as int, y as int);
    }
}

pub fn warp_mouse_in_window(window: &video::Window, x: i32, y: i32) {
    unsafe { ll::SDL_WarpMouseInWindow(window.raw(), x, y); }
}

pub fn set_relative_mouse_mode(on: bool) {
    unsafe { ll::SDL_SetRelativeMouseMode(on as i32); }
}

pub fn get_relative_mouse_mode() -> bool {
    unsafe { ll::SDL_GetRelativeMouseMode() == 1 }
}

pub fn get_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn get_default_cursor() -> Option<Cursor> {
    let raw = unsafe { ll::SDL_GetDefaultCursor() };

    if raw == ptr::null() {
        None
    } else {
        Some(Cursor { raw: raw, owned: false })
    }
}

pub fn is_cursor_showing() -> bool {
    unsafe { ll::SDL_ShowCursor(ll::SDL_QUERY) == 1 }
}

pub fn show_cursor(show: bool) {
    unsafe { ll::SDL_ShowCursor(show as i32); }
}
<|endoftext|>


<commit_before>//! Implements the searching of the game tree -- iterative deepening,
//! aspiration windows, multi-PV.
//!
//! Iterative deepening works as follows: the program starts with a
//! one ply search, then increments the search depth and does another
//! search. This process is repeated until the time allocated for the
//! search is exhausted or the maximum search depth is reached. In
//! case of an unfinished search, the program can always fall back to
//! the move selected in the last iteration of the search.
//!
//! Aspiration windows are a way to reduce the search space in the
//! search. The way it works is that we get the value from the last
//! search iteration, calculate a window around it, and use this as
//! alpha-beta bounds for the next search. Because the window is
//! narrower, more beta cutoffs are achieved, and the search takes a
//! shorter time. The drawback is that if the true score is outside
//! this window, then a costly re-search must be made. But then most
//! probably the re-search will be much faster, because many positions
//! will be remembered from the transposition table.
//!
//! In multi-PV mode the engine calculates and sends to the GUI
//! several principal variations (PV), each one starting with a
//! different first move. This mode makes the search slower, but it is
//! very useful for chess analysis.

pub mod alpha_beta;
pub mod threading;

use std::cmp::{min, max};
use std::time::Duration;
use std::thread;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use basetypes::*;
use moves::*;
use tt::*;
use position::{Position, START_POSITION_FEN};
use self::threading::*;

/// The maximum search depth in half-moves.
pub const MAX_DEPTH: u8 = 63; // Should be less than 127.

/// Parameters describing a new search.
#[derive(Clone)]
pub struct SearchParams {
    /// A number identifying the new search.
    pub search_id: usize,

    /// The root position for the new search.
    pub position: Position,

    /// The requested search depth.
    pub depth: u8,

    /// The lower bound for the new search.
    pub lower_bound: Value,

    /// The upper bound for the new search.
    pub upper_bound: Value,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    pub value: Value,

    /// Restricts the analysis of the root position to the supplied
    /// list of moves only. No restrictions if the supplied list is
    /// empty.
    pub searchmoves: Vec<Move>,

    /// Specifies how many best lines of play to calculate. (The first
    /// move in each line will be different.)
    pub variation_count: usize,
}

/// A progress report from a search.
#[derive(Clone)]
pub struct Report {
    /// The ID assigned to search.
    pub search_id: usize,

    /// The number of positions searched so far.
    pub searched_nodes: NodeCount,

    /// The search depth completed so far.
    pub depth: u8,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    pub value: Value,

    /// The best moves found so far (sorted by descending strength),
    /// or an empty list if not available.
    pub best_moves: Vec<Move>,

    /// `true` if the search is done, `false` otherwise.
    pub done: bool,
}

/// The `SearchExecutor` trait is used to execute consecutive searches
/// in different starting positions.
pub trait SearchExecutor {
    /// Creates a new instance.
    fn new(tt: Arc<Tt>) -> Self;

    /// Starts a new search.
    ///
    /// After calling `start_search`, `try_recv_report` must be called
    /// periodically until the returned report indicates that the
    /// search is done. A new search must not be started until the
    /// previous search is done.
    fn start_search(&mut self, params: SearchParams);

    /// Attempts to return a search progress report without blocking.
    fn try_recv_report(&mut self) -> Result<Report, TryRecvError>;

    /// Waits until a search progress report is available, timing out
    /// after a specified duration or earlier.
    fn wait_report(&self, duration: Duration);

    /// Requests the termination of the current search.
    ///
    /// After calling `terminate`, `try_recv_report` must continue to
    /// be called periodically until the returned report indicates
    /// that the search is done.
    fn terminate_search(&mut self);
}

/// Executes alpha-beta searches.
///
/// `SimpleSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
struct SimpleSearcher {
    thread_join_handle: Option<thread::JoinHandle<()>>,
    thread_commands: Sender<Command>,
    thread_reports: Receiver<Report>,
    has_reports_condition: Arc<(Mutex<bool>, Condvar)>,
}

impl SearchExecutor for SimpleSearcher {
    fn new(tt: Arc<Tt>) -> SimpleSearcher {
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let has_reports_condition = Arc::new((Mutex::new(false), Condvar::new()));
        SimpleSearcher {
            thread_commands: commands_tx,
            thread_reports: reports_rx,
            has_reports_condition: has_reports_condition.clone(),
            thread_join_handle: Some(thread::spawn(move || {
                serve_simple(tt, commands_rx, reports_tx, has_reports_condition);
            })),
        }
    }

    #[allow(unused_variables)]
    fn start_search(&mut self, params: SearchParams) {
        debug_assert!(params.depth <= MAX_DEPTH);
        debug_assert!(params.lower_bound < params.upper_bound);
        debug_assert!(params.lower_bound != VALUE_UNKNOWN);
        debug_assert!(params.searchmoves.is_empty());
        debug_assert_eq!(params.variation_count, 1);
        self.thread_commands.send(Command::Start(params)).unwrap();
    }

    fn try_recv_report(&mut self) -> Result<Report, TryRecvError> {
        let result = self.thread_reports.try_recv();
        if result.is_err() {
            *self.has_reports_condition.0.lock().unwrap() = false;
        }
        result
    }

    fn wait_report(&self, duration: Duration) {
        let &(ref has_reports, ref condition) = &*self.has_reports_condition;
        let has_reports = has_reports.lock().unwrap();
        if !*has_reports {
            condition.wait_timeout(has_reports, duration).unwrap();
        }
    }

    fn terminate_search(&mut self) {
        self.thread_commands.send(Command::Terminate).unwrap();
    }
}

impl Drop for SimpleSearcher {
    fn drop(&mut self) {
        self.thread_commands.send(Command::Exit).unwrap();
        self.thread_join_handle.take().unwrap().join().unwrap();
    }
}

/// Executes multi-PV searches.
///
/// `MultipvSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
pub struct MultipvSearcher;

/// Executes multi-PV searches with aspiration windows.
///
/// `AspirationSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
pub struct AspirationSearcher {
    params: SearchParams,

    /// `true` if the current search has been terminated.
    search_is_terminated: bool,

    /// The number of positions analyzed during previous (failed) searches.
    previously_searched_nodes: NodeCount,

    /// The evaluation of the root position so far.
    value: Value,

    /// The aspiration window will be widened by this value if the
    /// search fails. (We use `isize` to avoid overflows.)
    delta: isize,

    /// The lower bound of the aspiration window.
    alpha: Value,

    /// The upper bound of the aspiration window.
    beta: Value,

    /// The real work will be handed over to `SimpleSearcher`.
    searcher: SimpleSearcher, // TODO: should be `MultipvSearcher`
}

impl AspirationSearcher {
    /// A helper method. It tells `self.searcher` to run a new search.
    fn start_aspirated_search(&mut self) {
        self.searcher.start_search(SearchParams {
            search_id: 0,
            lower_bound: self.alpha,
            upper_bound: self.beta,
            value: self.value,
            searchmoves: vec![], // TODO: should be `self.searchmoves.clone(),`
            variation_count: 1, // TODO: should be  `self.variation_count,`
            ..self.params.clone()
        });
    }

    /// A helper method. It multiplies `self.delta` by a constant.
    fn increase_delta(&mut self) {
        self.delta += 3 * self.delta / 8;
        if self.delta > 1500 {
            self.delta = 1_000_000;
        }
    }

    /// A helper method. It widens the aspiration window if necessary.
    fn widen_aspiration_window(&mut self) -> bool {
        let SearchParams { lower_bound, upper_bound, .. } = self.params;
        let v = self.value as isize;
        if lower_bound < self.alpha && lower_bound < self.value && self.value <= self.alpha {
            // Set smaller `self.alpha`.
            self.alpha = max(v - self.delta, lower_bound as isize) as Value;
            self.increase_delta();
            return true;
        } else if self.beta < upper_bound && self.beta <= self.value && self.value < upper_bound {
            // Set bigger `self.beta`.
            self.beta = min(v + self.delta, upper_bound as isize) as Value;
            self.increase_delta();
            return true;
        }
        false
    }
}

impl SearchExecutor for AspirationSearcher {
    fn new(tt: Arc<Tt>) -> AspirationSearcher {
        AspirationSearcher {
            params: SearchParams {
                search_id: 0,
                position: Position::from_fen(START_POSITION_FEN).ok().unwrap(),
                depth: 0,
                lower_bound: VALUE_MIN,
                upper_bound: VALUE_MAX,
                value: VALUE_UNKNOWN,
                searchmoves: vec![],
                variation_count: 1,
            },
            search_is_terminated: false,
            previously_searched_nodes: 0,
            value: VALUE_UNKNOWN,
            delta: 1_000_000,
            alpha: VALUE_MIN,
            beta: VALUE_MAX,
            searcher: SimpleSearcher::new(tt),
        }
    }

    fn start_search(&mut self, params: SearchParams) {
        debug_assert!(params.depth <= MAX_DEPTH);
        debug_assert!(params.lower_bound < params.upper_bound);
        debug_assert!(params.lower_bound != VALUE_UNKNOWN);
        self.params = params;
        self.search_is_terminated = false;
        self.previously_searched_nodes = 0;
        self.value = self.params.value;

        // The half-width of the initial aspiration window.
        self.delta = 17; // TODO: make this `16`?

        // Set the initial aspiration window (`self.alpha`, `self.beta`).
        let SearchParams { value, lower_bound, upper_bound, .. } = self.params;
        let (a, b) = if value == VALUE_UNKNOWN || value < lower_bound || value > upper_bound {
            (lower_bound, upper_bound)
        } else {
            let v = value as isize;
            (max(v - self.delta, lower_bound as isize) as Value,
             min(v + self.delta, upper_bound as isize) as Value)
        };
        self.alpha = a;
        self.beta = b;

        self.start_aspirated_search();
    }

    fn try_recv_report(&mut self) -> Result<Report, TryRecvError> {
        let Report { searched_nodes, depth, value, best_moves, done, .. } =
            try!(self.searcher.try_recv_report());
        self.value = value;
        let searched_nodes = self.previously_searched_nodes + searched_nodes;
        let depth = if done && !self.search_is_terminated {
            self.previously_searched_nodes = searched_nodes;
            if self.widen_aspiration_window() {
                // Start a re-search.
                self.start_aspirated_search();
                return Err(TryRecvError::Empty);
            }
            depth
        } else {
            0
        };

        return Ok(Report {
            search_id: self.params.search_id,
            searched_nodes: searched_nodes,
            depth: depth,
            value: self.value,
            best_moves: best_moves,
            done: done,
        });
    }

    fn wait_report(&self, duration: Duration) {
        self.searcher.wait_report(duration);
    }

    fn terminate_search(&mut self) {
        self.search_is_terminated = true;
        self.searcher.terminate_search();
    }
}

/// Executes multi-PV searches with aspiration windows and iterative
/// deepening.
///
/// `DeepeningSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
pub struct DeepeningSearcher {
    params: SearchParams,

    /// `true` if the current search has been terminated.
    search_is_terminated: bool,

    /// The number of positions analyzed during previous (shallower)
    /// searches.
    previously_searched_nodes: NodeCount,

    /// The evaluation of the root position so far.
    value: Value,

    /// The depth of the currently executing search.
    depth: u8,

    /// The real work will be handed over to `AspirationSearcher`.
    searcher: AspirationSearcher,
}

impl DeepeningSearcher {
    /// A helper method. It tells `self.searcher` to run a new search.
    fn start_deeper_search(&mut self) {
        self.depth += 1;
        let value = if self.depth < 5 {
            VALUE_UNKNOWN
        } else {
            self.value
        };
        self.searcher.start_search(SearchParams {
            search_id: 0,
            depth: self.depth,
            value: value,
            ..self.params.clone()
        });
    }
}

impl SearchExecutor for DeepeningSearcher {
    fn new(tt: Arc<Tt>) -> DeepeningSearcher {
        DeepeningSearcher {
            params: SearchParams {
                search_id: 0,
                position: Position::from_fen(START_POSITION_FEN).ok().unwrap(),
                depth: 0,
                lower_bound: VALUE_MIN,
                upper_bound: VALUE_MAX,
                value: VALUE_UNKNOWN,
                searchmoves: vec![],
                variation_count: 1,
            },
            search_is_terminated: false,
            previously_searched_nodes: 0,
            value: VALUE_UNKNOWN,
            depth: 0,
            searcher: AspirationSearcher::new(tt),
        }
    }

    fn start_search(&mut self, params: SearchParams) {
        debug_assert!(params.depth <= MAX_DEPTH);
        debug_assert!(params.lower_bound < params.upper_bound);
        debug_assert!(params.lower_bound != VALUE_UNKNOWN);
        self.params = params;
        self.search_is_terminated = false;
        self.previously_searched_nodes = 0;
        self.value = self.params.value;
        self.depth = 0;

        self.start_deeper_search();
    }

    fn try_recv_report(&mut self) -> Result<Report, TryRecvError> {
        let Report { searched_nodes, depth, value, best_moves, done, .. } =
            try!(self.searcher.try_recv_report());
        if value != VALUE_UNKNOWN {
            self.value = value;
        }
        let searched_nodes = self.previously_searched_nodes + searched_nodes;
        let depth = if done && !self.search_is_terminated {
            debug_assert_eq!(depth, self.depth);
            self.previously_searched_nodes = searched_nodes;
            if depth < self.params.depth {
                self.start_deeper_search();
                return Err(TryRecvError::Empty);
            }
            depth
        } else {
            self.depth - 1
        };

        return Ok(Report {
            search_id: self.params.search_id,
            searched_nodes: searched_nodes,
            depth: depth,
            value: self.value,
            best_moves: best_moves,
            done: done,
        });
    }

    fn wait_report(&self, duration: Duration) {
        self.searcher.wait_report(duration);
    }

    fn terminate_search(&mut self) {
        self.search_is_terminated = true;
        self.searcher.terminate_search();
    }
}
<commit_msg>beautify<commit_after>//! Implements the searching of the game tree -- iterative deepening,
//! aspiration windows, multi-PV.
//!
//! Iterative deepening works as follows: the program starts with a
//! one ply search, then increments the search depth and does another
//! search. This process is repeated until the time allocated for the
//! search is exhausted or the maximum search depth is reached. In
//! case of an unfinished search, the program can always fall back to
//! the move selected in the last iteration of the search.
//!
//! Aspiration windows are a way to reduce the search space in the
//! search. The way it works is that we get the value from the last
//! search iteration, calculate a window around it, and use this as
//! alpha-beta bounds for the next search. Because the window is
//! narrower, more beta cutoffs are achieved, and the search takes a
//! shorter time. The drawback is that if the true score is outside
//! this window, then a costly re-search must be made. But then most
//! probably the re-search will be much faster, because many positions
//! will be remembered from the transposition table.
//!
//! In multi-PV mode the engine calculates and sends to the GUI
//! several principal variations (PV), each one starting with a
//! different first move. This mode makes the search slower, but it is
//! very useful for chess analysis.

pub mod alpha_beta;
pub mod threading;

use std::cmp::{min, max};
use std::time::Duration;
use std::thread;
use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use basetypes::*;
use moves::*;
use tt::*;
use position::{Position, START_POSITION_FEN};
use self::threading::*;

/// The maximum search depth in half-moves.
pub const MAX_DEPTH: u8 = 63; // Should be less than 127.

/// Parameters describing a new search.
#[derive(Clone)]
pub struct SearchParams {
    /// A number identifying the new search.
    pub search_id: usize,

    /// The root position for the new search.
    pub position: Position,

    /// The requested search depth.
    pub depth: u8,

    /// The lower bound for the new search.
    pub lower_bound: Value,

    /// The upper bound for the new search.
    pub upper_bound: Value,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    pub value: Value,

    /// Restricts the analysis of the root position to the supplied
    /// list of moves only. No restrictions if the supplied list is
    /// empty.
    pub searchmoves: Vec<Move>,

    /// Specifies how many best lines of play to calculate. (The first
    /// move in each line will be different.)
    pub variation_count: usize,
}

/// A progress report from a search.
#[derive(Clone)]
pub struct Report {
    /// The ID assigned to search.
    pub search_id: usize,

    /// The number of positions searched so far.
    pub searched_nodes: NodeCount,

    /// The search depth completed so far.
    pub depth: u8,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    pub value: Value,

    /// The best moves found so far (sorted by descending strength),
    /// or an empty list if not available.
    pub best_moves: Vec<Move>,

    /// `true` if the search is done, `false` otherwise.
    pub done: bool,
}

/// The `SearchExecutor` trait is used to execute consecutive searches
/// in different starting positions.
pub trait SearchExecutor {
    /// Creates a new instance.
    fn new(tt: Arc<Tt>) -> Self;

    /// Starts a new search.
    ///
    /// After calling `start_search`, `try_recv_report` must be called
    /// periodically until the returned report indicates that the
    /// search is done. A new search must not be started until the
    /// previous search is done.
    fn start_search(&mut self, params: SearchParams);

    /// Attempts to return a search progress report without blocking.
    fn try_recv_report(&mut self) -> Result<Report, TryRecvError>;

    /// Waits until a search progress report is available, timing out
    /// after a specified duration or earlier.
    fn wait_report(&self, duration: Duration);

    /// Requests the termination of the current search.
    ///
    /// After calling `terminate`, `try_recv_report` must continue to
    /// be called periodically until the returned report indicates
    /// that the search is done.
    fn terminate_search(&mut self);
}

/// Executes alpha-beta searches.
///
/// `SimpleSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
struct SimpleSearcher {
    thread_join_handle: Option<thread::JoinHandle<()>>,
    thread_commands: Sender<Command>,
    thread_reports: Receiver<Report>,
    has_reports_condition: Arc<(Mutex<bool>, Condvar)>,
}

impl SearchExecutor for SimpleSearcher {
    fn new(tt: Arc<Tt>) -> SimpleSearcher {
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let has_reports_condition = Arc::new((Mutex::new(false), Condvar::new()));
        SimpleSearcher {
            thread_commands: commands_tx,
            thread_reports: reports_rx,
            has_reports_condition: has_reports_condition.clone(),
            thread_join_handle: Some(thread::spawn(move || {
                serve_simple(tt, commands_rx, reports_tx, has_reports_condition);
            })),
        }
    }

    #[allow(unused_variables)]
    fn start_search(&mut self, params: SearchParams) {
        debug_assert!(params.depth <= MAX_DEPTH);
        debug_assert!(params.lower_bound < params.upper_bound);
        debug_assert!(params.lower_bound != VALUE_UNKNOWN);
        debug_assert!(params.searchmoves.is_empty());
        debug_assert_eq!(params.variation_count, 1);
        self.thread_commands.send(Command::Start(params)).unwrap();
    }

    fn try_recv_report(&mut self) -> Result<Report, TryRecvError> {
        let result = self.thread_reports.try_recv();
        if result.is_err() {
            *self.has_reports_condition.0.lock().unwrap() = false;
        }
        result
    }

    fn wait_report(&self, duration: Duration) {
        let &(ref has_reports, ref condition) = &*self.has_reports_condition;
        let has_reports = has_reports.lock().unwrap();
        if !*has_reports {
            condition.wait_timeout(has_reports, duration).unwrap();
        }
    }

    fn terminate_search(&mut self) {
        self.thread_commands.send(Command::Terminate).unwrap();
    }
}

impl Drop for SimpleSearcher {
    fn drop(&mut self) {
        self.thread_commands.send(Command::Exit).unwrap();
        self.thread_join_handle.take().unwrap().join().unwrap();
    }
}

/// Executes multi-PV searches.
///
/// `MultipvSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
pub struct MultipvSearcher;

/// Executes multi-PV searches with aspiration windows.
///
/// `AspirationSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
pub struct AspirationSearcher {
    params: SearchParams,

    /// `true` if the current search has been terminated.
    search_is_terminated: bool,

    /// The number of positions analyzed during previous failed searches.
    previously_searched_nodes: NodeCount,

    /// The evaluation of the root position so far.
    value: Value,

    /// The aspiration window will be widened by this value if the
    /// search fails. (We use `isize` to avoid overflows.)
    delta: isize,

    /// The lower bound of the aspiration window.
    alpha: Value,

    /// The upper bound of the aspiration window.
    beta: Value,

    /// The real work will be handed over to `SimpleSearcher`.
    searcher: SimpleSearcher, // TODO: should be `MultipvSearcher`
}

impl AspirationSearcher {
    /// A helper method. It tells `self.searcher` to run a new search.
    fn start_aspirated_search(&mut self) {
        self.searcher.start_search(SearchParams {
            search_id: 0,
            lower_bound: self.alpha,
            upper_bound: self.beta,
            value: self.value,
            searchmoves: vec![], // TODO: should be `self.searchmoves.clone(),`
            variation_count: 1, // TODO: should be  `self.variation_count,`
            ..self.params.clone()
        });
    }

    /// A helper method. It multiplies `self.delta` by a constant.
    fn increase_delta(&mut self) {
        self.delta += 3 * self.delta / 8;
        if self.delta > 1500 {
            self.delta = 1_000_000;
        }
    }

    /// A helper method. It widens the aspiration window if necessary.
    fn widen_aspiration_window(&mut self) -> bool {
        let SearchParams { lower_bound, upper_bound, .. } = self.params;
        if lower_bound < self.alpha && lower_bound < self.value && self.value <= self.alpha {
            // Set smaller `self.alpha`.
            self.alpha = max(self.value as isize - self.delta, lower_bound as isize) as Value;
            self.increase_delta();
            return true;
        } else if self.beta < upper_bound && self.beta <= self.value && self.value < upper_bound {
            // Set bigger `self.beta`.
            self.beta = min(self.value as isize + self.delta, upper_bound as isize) as Value;
            self.increase_delta();
            return true;
        }
        false
    }
}

impl SearchExecutor for AspirationSearcher {
    fn new(tt: Arc<Tt>) -> AspirationSearcher {
        AspirationSearcher {
            params: SearchParams {
                search_id: 0,
                position: Position::from_fen(START_POSITION_FEN).ok().unwrap(),
                depth: 0,
                lower_bound: VALUE_MIN,
                upper_bound: VALUE_MAX,
                value: VALUE_UNKNOWN,
                searchmoves: vec![],
                variation_count: 1,
            },
            search_is_terminated: false,
            previously_searched_nodes: 0,
            value: VALUE_UNKNOWN,
            delta: 0,
            alpha: VALUE_MIN,
            beta: VALUE_MAX,
            searcher: SimpleSearcher::new(tt),
        }
    }

    fn start_search(&mut self, params: SearchParams) {
        debug_assert!(params.depth <= MAX_DEPTH);
        debug_assert!(params.lower_bound < params.upper_bound);
        debug_assert!(params.lower_bound != VALUE_UNKNOWN);
        self.params = params;
        self.search_is_terminated = false;
        self.previously_searched_nodes = 0;
        self.value = self.params.value;

        // The half-width of the initial aspiration window.
        self.delta = 17; // TODO: make this `16`?

        // Set the initial aspiration window (`self.alpha`, `self.beta`).
        let SearchParams { value, lower_bound, upper_bound, .. } = self.params;
        let (a, b) = if value == VALUE_UNKNOWN || value < lower_bound || value > upper_bound {
            (lower_bound, upper_bound)
        } else {
            let v = value as isize;
            (max(v - self.delta, lower_bound as isize) as Value,
             min(v + self.delta, upper_bound as isize) as Value)
        };
        self.alpha = a;
        self.beta = b;

        self.start_aspirated_search();
    }

    fn try_recv_report(&mut self) -> Result<Report, TryRecvError> {
        let Report { searched_nodes, depth, value, best_moves, done, .. } =
            try!(self.searcher.try_recv_report());
        self.value = value;
        let searched_nodes = self.previously_searched_nodes + searched_nodes;
        let depth = if done && !self.search_is_terminated {
            self.previously_searched_nodes = searched_nodes;
            if self.widen_aspiration_window() {
                // Start a re-search.
                self.start_aspirated_search();
                return Err(TryRecvError::Empty);
            }
            depth
        } else {
            0
        };

        return Ok(Report {
            search_id: self.params.search_id,
            searched_nodes: searched_nodes,
            depth: depth,
            value: self.value,
            best_moves: best_moves,
            done: done,
        });
    }

    fn wait_report(&self, duration: Duration) {
        self.searcher.wait_report(duration);
    }

    fn terminate_search(&mut self) {
        self.search_is_terminated = true;
        self.searcher.terminate_search();
    }
}

/// Executes multi-PV searches with aspiration windows and iterative
/// deepening.
///
/// `DeepeningSearcher::new` will spawn a separate thread to do the
/// computational heavy lifting.
pub struct DeepeningSearcher {
    params: SearchParams,

    /// `true` if the current search has been terminated.
    search_is_terminated: bool,

    /// The number of positions analyzed during previous (shallower)
    /// searches.
    previously_searched_nodes: NodeCount,

    /// The evaluation of the root position so far.
    value: Value,

    /// The depth of the currently executing search.
    depth: u8,

    /// The real work will be handed over to `AspirationSearcher`.
    searcher: AspirationSearcher,
}

impl DeepeningSearcher {
    /// A helper method. It tells `self.searcher` to run a new search.
    fn start_deeper_search(&mut self) {
        self.depth += 1;
        let value = if self.depth < 5 {
            VALUE_UNKNOWN
        } else {
            self.value
        };
        self.searcher.start_search(SearchParams {
            search_id: 0,
            depth: self.depth,
            value: value,
            ..self.params.clone()
        });
    }
}

impl SearchExecutor for DeepeningSearcher {
    fn new(tt: Arc<Tt>) -> DeepeningSearcher {
        DeepeningSearcher {
            params: SearchParams {
                search_id: 0,
                position: Position::from_fen(START_POSITION_FEN).ok().unwrap(),
                depth: 0,
                lower_bound: VALUE_MIN,
                upper_bound: VALUE_MAX,
                value: VALUE_UNKNOWN,
                searchmoves: vec![],
                variation_count: 1,
            },
            search_is_terminated: false,
            previously_searched_nodes: 0,
            value: VALUE_UNKNOWN,
            depth: 0,
            searcher: AspirationSearcher::new(tt),
        }
    }

    fn start_search(&mut self, params: SearchParams) {
        debug_assert!(params.depth <= MAX_DEPTH);
        debug_assert!(params.lower_bound < params.upper_bound);
        debug_assert!(params.lower_bound != VALUE_UNKNOWN);
        self.params = params;
        self.search_is_terminated = false;
        self.previously_searched_nodes = 0;
        self.value = self.params.value;
        self.depth = 0;

        self.start_deeper_search();
    }

    fn try_recv_report(&mut self) -> Result<Report, TryRecvError> {
        let Report { searched_nodes, depth, value, best_moves, done, .. } =
            try!(self.searcher.try_recv_report());
        if value != VALUE_UNKNOWN {
            self.value = value;
        }
        let searched_nodes = self.previously_searched_nodes + searched_nodes;
        let depth = if done && !self.search_is_terminated {
            debug_assert_eq!(depth, self.depth);
            self.previously_searched_nodes = searched_nodes;
            if depth < self.params.depth {
                self.start_deeper_search();
                return Err(TryRecvError::Empty);
            }
            depth
        } else {
            self.depth - 1
        };

        return Ok(Report {
            search_id: self.params.search_id,
            searched_nodes: searched_nodes,
            depth: depth,
            value: self.value,
            best_moves: best_moves,
            done: done,
        });
    }

    fn wait_report(&self, duration: Duration) {
        self.searcher.wait_report(duration);
    }

    fn terminate_search(&mut self) {
        self.search_is_terminated = true;
        self.searcher.terminate_search();
    }
}
<|endoftext|>