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

//! Implementation of `std::os` functionality for Windows

#![allow(bad_style)]

use prelude::v1::*;
use os::windows::*;

use error::Error as StdError;
use ffi::{OsString, OsStr, AsOsStr};
use fmt;
use iter::Range;
use libc::types::os::arch::extra::LPWCH;
use libc::{self, c_int, c_void};
use mem;
use old_io::{IoError, IoResult};
use ptr;
use slice;
use sys::c;
use sys::fs::FileDesc;
use sys::handle::Handle as RawHandle;

use libc::funcs::extra::kernel32::{
    GetEnvironmentStringsW,
    FreeEnvironmentStringsW
};

pub fn errno() -> i32 {
    unsafe { libc::GetLastError() as i32 }
}

/// Get a detailed string description for the given error number
pub fn error_string(errnum: i32) -> String {
    use libc::types::os::arch::extra::DWORD;
    use libc::types::os::arch::extra::LPWSTR;
    use libc::types::os::arch::extra::LPVOID;
    use libc::types::os::arch::extra::WCHAR;

    #[link_name = "kernel32"]
    extern "system" {
        fn FormatMessageW(flags: DWORD,
                          lpSrc: LPVOID,
                          msgId: DWORD,
                          langId: DWORD,
                          buf: LPWSTR,
                          nsize: DWORD,
                          args: *const c_void)
                          -> DWORD;
    }

    static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
    static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;

    // This value is calculated from the macro
    // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
    let langId = 0x0800 as DWORD;

    let mut buf = [0 as WCHAR; 2048];

    unsafe {
        let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
                                 FORMAT_MESSAGE_IGNORE_INSERTS,
                                 ptr::null_mut(),
                                 errnum as DWORD,
                                 langId,
                                 buf.as_mut_ptr(),
                                 buf.len() as DWORD,
                                 ptr::null());
        if res == 0 {
            // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
            let fm_err = errno();
            return format!("OS Error {} (FormatMessageW() returned error {})",
                           errnum, fm_err);
        }

        let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
        let msg = String::from_utf16(&buf[..b]);
        match msg {
            Ok(msg) => msg,
            Err(..) => format!("OS Error {} (FormatMessageW() returned \
                                invalid UTF-16)", errnum),
        }
    }
}

pub struct Env {
    base: LPWCH,
    cur: LPWCH,
}

impl Iterator for Env {
    type Item = (OsString, OsString);

    fn next(&mut self) -> Option<(OsString, OsString)> {
        unsafe {
            if *self.cur == 0 { return None }
            let p = &*self.cur;
            let mut len = 0;
            while *(p as *const _).offset(len) != 0 {
                len += 1;
            }
            let p = p as *const u16;
            let s = slice::from_raw_parts(p, len as usize);
            self.cur = self.cur.offset(len + 1);

            let (k, v) = match s.iter().position(|&b| b == '=' as u16) {
                Some(n) => (&s[..n], &s[n+1..]),
                None => (s, &[][]),
            };
            Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v)))
        }
    }
}

impl Drop for Env {
    fn drop(&mut self) {
        unsafe { FreeEnvironmentStringsW(self.base); }
    }
}

pub fn env() -> Env {
    unsafe {
        let ch = GetEnvironmentStringsW();
        if ch as usize == 0 {
            panic!("failure getting env string from OS: {}",
                   IoError::last_error());
        }
        Env { base: ch, cur: ch }
    }
}

pub struct SplitPaths<'a> {
    data: EncodeWide<'a>,
    must_yield: bool,
}

pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
    SplitPaths {
        data: unparsed.encode_wide(),
        must_yield: true,
    }
}

impl<'a> Iterator for SplitPaths<'a> {
    type Item = Path;
    fn next(&mut self) -> Option<Path> {
        // On Windows, the PATH environment variable is semicolon separated.
        // Double quotes are used as a way of introducing literal semicolons
        // (since c:\some;dir is a valid Windows path). Double quotes are not
        // themselves permitted in path names, so there is no way to escape a
        // double quote.  Quoted regions can appear in arbitrary locations, so
        //
        //   c:\foo;c:\som"e;di"r;c:\bar
        //
        // Should parse as [c:\foo, c:\some;dir, c:\bar].
        //
        // (The above is based on testing; there is no clear reference available
        // for the grammar.)

        let must_yield = self.must_yield;
        self.must_yield = false;

        let mut in_progress = Vec::new();
        let mut in_quote = false;
        for b in self.data.by_ref() {
            if b == '"' as u16 {
                in_quote = !in_quote;
            } else if b == ';' as u16 && !in_quote {
                self.must_yield = true;
                break
            } else {
                in_progress.push(b)
            }
        }

        if !must_yield && in_progress.is_empty() {
            None
        } else {
            Some(super::os2path(&in_progress[]))
        }
    }
}

#[derive(Debug)]
pub struct JoinPathsError;

pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
    where I: Iterator<Item=T>, T: AsOsStr
{
    let mut joined = Vec::new();
    let sep = b';' as u16;

    for (i, path) in paths.enumerate() {
        let path = path.as_os_str();
        if i > 0 { joined.push(sep) }
        let v = path.encode_wide().collect::<Vec<u16>>();
        if v.contains(&(b'"' as u16)) {
            return Err(JoinPathsError)
        } else if v.contains(&sep) {
            joined.push(b'"' as u16);
            joined.push_all(&v[]);
            joined.push(b'"' as u16);
        } else {
            joined.push_all(&v[]);
        }
    }

    Ok(OsStringExt::from_wide(&joined[]))
}

impl fmt::Display for JoinPathsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "path segment contains `\"`".fmt(f)
    }
}

impl StdError for JoinPathsError {
    fn description(&self) -> &str { "failed to join paths" }
}

pub fn current_exe() -> IoResult<Path> {
    super::fill_utf16_buf(|buf, sz| unsafe {
        libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
    }, super::os2path)
}

pub fn getcwd() -> IoResult<Path> {
    super::fill_utf16_buf(|buf, sz| unsafe {
        libc::GetCurrentDirectoryW(sz, buf)
    }, super::os2path)
}

pub fn chdir(p: &Path) -> IoResult<()> {
    let mut p = p.as_os_str().encode_wide().collect::<Vec<_>>();
    p.push(0);

    unsafe {
        match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
            true => Ok(()),
            false => Err(IoError::last_error()),
        }
    }
}

pub fn getenv(k: &OsStr) -> Option<OsString> {
    let k = super::to_utf16_os(k);
    super::fill_utf16_buf(|buf, sz| unsafe {
        libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
    }, |buf| {
        OsStringExt::from_wide(buf)
    }).ok()
}

pub fn setenv(k: &OsStr, v: &OsStr) {
    let k = super::to_utf16_os(k);
    let v = super::to_utf16_os(v);

    unsafe {
        if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
            panic!("failed to set env: {}", IoError::last_error());
        }
    }
}

pub fn unsetenv(n: &OsStr) {
    let v = super::to_utf16_os(n);
    unsafe {
        if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
            panic!("failed to unset env: {}", IoError::last_error());
        }
    }
}

pub struct Args {
    range: Range<isize>,
    cur: *mut *mut u16,
}

impl Iterator for Args {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        self.range.next().map(|i| unsafe {
            let ptr = *self.cur.offset(i);
            let mut len = 0;
            while *ptr.offset(len) != 0 { len += 1; }

            // Push it onto the list.
            let ptr = ptr as *const u16;
            let buf = slice::from_raw_parts(ptr, len as usize);
            OsStringExt::from_wide(buf)
        })
    }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}

impl ExactSizeIterator for Args {
    fn len(&self) -> usize { self.range.len() }
}

impl Drop for Args {
    fn drop(&mut self) {
        unsafe { c::LocalFree(self.cur as *mut c_void); }
    }
}

pub fn args() -> Args {
    unsafe {
        let mut nArgs: c_int = 0;
        let lpCmdLine = c::GetCommandLineW();
        let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);

        Args { cur: szArgList, range: range(0, nArgs as isize) }
    }
}

pub fn page_size() -> usize {
    unsafe {
        let mut info = mem::zeroed();
        libc::GetSystemInfo(&mut info);
        return info.dwPageSize as usize;
    }
}

pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
    // Windows pipes work subtly differently than unix pipes, and their
    // inheritance has to be handled in a different way that I do not
    // fully understand. Here we explicitly make the pipe non-inheritable,
    // which means to pass it to a subprocess they need to be duplicated
    // first, as in std::run.
    let mut fds = [0; 2];
    match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
    (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
        0 => {
            assert!(fds[0] != -1 && fds[0] != 0);
            assert!(fds[1] != -1 && fds[1] != 0);
            Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
        }
        _ => Err(IoError::last_error()),
    }
}

pub fn temp_dir() -> Path {
    super::fill_utf16_buf(|buf, sz| unsafe {
        c::GetTempPathW(sz, buf)
    }, super::os2path).unwrap()
}

pub fn home_dir() -> Option<Path> {
    getenv("HOME".as_os_str()).or_else(|| {
        getenv("USERPROFILE".as_os_str())
    }).map(|os| {
        // FIXME: OsString => Path
        Path::new(os.to_str().unwrap())
    }).or_else(|| unsafe {
        let me = c::GetCurrentProcess();
        let mut token = ptr::null_mut();
        if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
            return None
        }
        let _handle = RawHandle::new(token);
        super::fill_utf16_buf(|buf, mut sz| {
            match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
                0 if libc::GetLastError() != 0 => 0,
                0 => sz,
                n => n as libc::DWORD,
            }
        }, super::os2path).ok()
    })
}
<commit_msg>fix windows<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of `std::os` functionality for Windows

#![allow(bad_style)]

use prelude::v1::*;
use os::windows::*;

use error::Error as StdError;
use ffi::{OsString, OsStr, AsOsStr};
use fmt;
use ops::Range;
use libc::types::os::arch::extra::LPWCH;
use libc::{self, c_int, c_void};
use mem;
use old_io::{IoError, IoResult};
use ptr;
use slice;
use sys::c;
use sys::fs::FileDesc;
use sys::handle::Handle as RawHandle;

use libc::funcs::extra::kernel32::{
    GetEnvironmentStringsW,
    FreeEnvironmentStringsW
};

pub fn errno() -> i32 {
    unsafe { libc::GetLastError() as i32 }
}

/// Get a detailed string description for the given error number
pub fn error_string(errnum: i32) -> String {
    use libc::types::os::arch::extra::DWORD;
    use libc::types::os::arch::extra::LPWSTR;
    use libc::types::os::arch::extra::LPVOID;
    use libc::types::os::arch::extra::WCHAR;

    #[link_name = "kernel32"]
    extern "system" {
        fn FormatMessageW(flags: DWORD,
                          lpSrc: LPVOID,
                          msgId: DWORD,
                          langId: DWORD,
                          buf: LPWSTR,
                          nsize: DWORD,
                          args: *const c_void)
                          -> DWORD;
    }

    static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
    static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;

    // This value is calculated from the macro
    // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
    let langId = 0x0800 as DWORD;

    let mut buf = [0 as WCHAR; 2048];

    unsafe {
        let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
                                 FORMAT_MESSAGE_IGNORE_INSERTS,
                                 ptr::null_mut(),
                                 errnum as DWORD,
                                 langId,
                                 buf.as_mut_ptr(),
                                 buf.len() as DWORD,
                                 ptr::null());
        if res == 0 {
            // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
            let fm_err = errno();
            return format!("OS Error {} (FormatMessageW() returned error {})",
                           errnum, fm_err);
        }

        let b = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
        let msg = String::from_utf16(&buf[..b]);
        match msg {
            Ok(msg) => msg,
            Err(..) => format!("OS Error {} (FormatMessageW() returned \
                                invalid UTF-16)", errnum),
        }
    }
}

pub struct Env {
    base: LPWCH,
    cur: LPWCH,
}

impl Iterator for Env {
    type Item = (OsString, OsString);

    fn next(&mut self) -> Option<(OsString, OsString)> {
        unsafe {
            if *self.cur == 0 { return None }
            let p = &*self.cur;
            let mut len = 0;
            while *(p as *const _).offset(len) != 0 {
                len += 1;
            }
            let p = p as *const u16;
            let s = slice::from_raw_parts(p, len as usize);
            self.cur = self.cur.offset(len + 1);

            let (k, v) = match s.iter().position(|&b| b == '=' as u16) {
                Some(n) => (&s[..n], &s[n+1..]),
                None => (s, &[][]),
            };
            Some((OsStringExt::from_wide(k), OsStringExt::from_wide(v)))
        }
    }
}

impl Drop for Env {
    fn drop(&mut self) {
        unsafe { FreeEnvironmentStringsW(self.base); }
    }
}

pub fn env() -> Env {
    unsafe {
        let ch = GetEnvironmentStringsW();
        if ch as usize == 0 {
            panic!("failure getting env string from OS: {}",
                   IoError::last_error());
        }
        Env { base: ch, cur: ch }
    }
}

pub struct SplitPaths<'a> {
    data: EncodeWide<'a>,
    must_yield: bool,
}

pub fn split_paths(unparsed: &OsStr) -> SplitPaths {
    SplitPaths {
        data: unparsed.encode_wide(),
        must_yield: true,
    }
}

impl<'a> Iterator for SplitPaths<'a> {
    type Item = Path;
    fn next(&mut self) -> Option<Path> {
        // On Windows, the PATH environment variable is semicolon separated.
        // Double quotes are used as a way of introducing literal semicolons
        // (since c:\some;dir is a valid Windows path). Double quotes are not
        // themselves permitted in path names, so there is no way to escape a
        // double quote.  Quoted regions can appear in arbitrary locations, so
        //
        //   c:\foo;c:\som"e;di"r;c:\bar
        //
        // Should parse as [c:\foo, c:\some;dir, c:\bar].
        //
        // (The above is based on testing; there is no clear reference available
        // for the grammar.)

        let must_yield = self.must_yield;
        self.must_yield = false;

        let mut in_progress = Vec::new();
        let mut in_quote = false;
        for b in self.data.by_ref() {
            if b == '"' as u16 {
                in_quote = !in_quote;
            } else if b == ';' as u16 && !in_quote {
                self.must_yield = true;
                break
            } else {
                in_progress.push(b)
            }
        }

        if !must_yield && in_progress.is_empty() {
            None
        } else {
            Some(super::os2path(&in_progress[]))
        }
    }
}

#[derive(Debug)]
pub struct JoinPathsError;

pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError>
    where I: Iterator<Item=T>, T: AsOsStr
{
    let mut joined = Vec::new();
    let sep = b';' as u16;

    for (i, path) in paths.enumerate() {
        let path = path.as_os_str();
        if i > 0 { joined.push(sep) }
        let v = path.encode_wide().collect::<Vec<u16>>();
        if v.contains(&(b'"' as u16)) {
            return Err(JoinPathsError)
        } else if v.contains(&sep) {
            joined.push(b'"' as u16);
            joined.push_all(&v[]);
            joined.push(b'"' as u16);
        } else {
            joined.push_all(&v[]);
        }
    }

    Ok(OsStringExt::from_wide(&joined[]))
}

impl fmt::Display for JoinPathsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        "path segment contains `\"`".fmt(f)
    }
}

impl StdError for JoinPathsError {
    fn description(&self) -> &str { "failed to join paths" }
}

pub fn current_exe() -> IoResult<Path> {
    super::fill_utf16_buf(|buf, sz| unsafe {
        libc::GetModuleFileNameW(ptr::null_mut(), buf, sz)
    }, super::os2path)
}

pub fn getcwd() -> IoResult<Path> {
    super::fill_utf16_buf(|buf, sz| unsafe {
        libc::GetCurrentDirectoryW(sz, buf)
    }, super::os2path)
}

pub fn chdir(p: &Path) -> IoResult<()> {
    let mut p = p.as_os_str().encode_wide().collect::<Vec<_>>();
    p.push(0);

    unsafe {
        match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
            true => Ok(()),
            false => Err(IoError::last_error()),
        }
    }
}

pub fn getenv(k: &OsStr) -> Option<OsString> {
    let k = super::to_utf16_os(k);
    super::fill_utf16_buf(|buf, sz| unsafe {
        libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
    }, |buf| {
        OsStringExt::from_wide(buf)
    }).ok()
}

pub fn setenv(k: &OsStr, v: &OsStr) {
    let k = super::to_utf16_os(k);
    let v = super::to_utf16_os(v);

    unsafe {
        if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
            panic!("failed to set env: {}", IoError::last_error());
        }
    }
}

pub fn unsetenv(n: &OsStr) {
    let v = super::to_utf16_os(n);
    unsafe {
        if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
            panic!("failed to unset env: {}", IoError::last_error());
        }
    }
}

pub struct Args {
    range: Range<isize>,
    cur: *mut *mut u16,
}

impl Iterator for Args {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        self.range.next().map(|i| unsafe {
            let ptr = *self.cur.offset(i);
            let mut len = 0;
            while *ptr.offset(len) != 0 { len += 1; }

            // Push it onto the list.
            let ptr = ptr as *const u16;
            let buf = slice::from_raw_parts(ptr, len as usize);
            OsStringExt::from_wide(buf)
        })
    }
    fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}

impl ExactSizeIterator for Args {
    fn len(&self) -> usize { self.range.len() }
}

impl Drop for Args {
    fn drop(&mut self) {
        unsafe { c::LocalFree(self.cur as *mut c_void); }
    }
}

pub fn args() -> Args {
    unsafe {
        let mut nArgs: c_int = 0;
        let lpCmdLine = c::GetCommandLineW();
        let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs);

        Args { cur: szArgList, range: 0..(nArgs as isize) }
    }
}

pub fn page_size() -> usize {
    unsafe {
        let mut info = mem::zeroed();
        libc::GetSystemInfo(&mut info);
        return info.dwPageSize as usize;
    }
}

pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
    // Windows pipes work subtly differently than unix pipes, and their
    // inheritance has to be handled in a different way that I do not
    // fully understand. Here we explicitly make the pipe non-inheritable,
    // which means to pass it to a subprocess they need to be duplicated
    // first, as in std::run.
    let mut fds = [0; 2];
    match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
    (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
        0 => {
            assert!(fds[0] != -1 && fds[0] != 0);
            assert!(fds[1] != -1 && fds[1] != 0);
            Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
        }
        _ => Err(IoError::last_error()),
    }
}

pub fn temp_dir() -> Path {
    super::fill_utf16_buf(|buf, sz| unsafe {
        c::GetTempPathW(sz, buf)
    }, super::os2path).unwrap()
}

pub fn home_dir() -> Option<Path> {
    getenv("HOME".as_os_str()).or_else(|| {
        getenv("USERPROFILE".as_os_str())
    }).map(|os| {
        // FIXME: OsString => Path
        Path::new(os.to_str().unwrap())
    }).or_else(|| unsafe {
        let me = c::GetCurrentProcess();
        let mut token = ptr::null_mut();
        if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
            return None
        }
        let _handle = RawHandle::new(token);
        super::fill_utf16_buf(|buf, mut sz| {
            match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
                0 if libc::GetLastError() != 0 => 0,
                0 => sz,
                n => n as libc::DWORD,
            }
        }, super::os2path).ok()
    })
}
<|endoftext|>


<commit_before>mod pick;

use std::ptr;
#[allow(unused_imports)]
use pleco::{BitMove,Board,ScoringMove,ScoringMoveList};
use pleco::board::movegen::{PseudoLegal,MoveGen};
use pleco::helper::prelude::piece_value;
use pleco::core::mono_traits::*;

use self::pick::*;

// TODO: use Generators once stabilized.

pub struct MovePicker {
    pick: Pick,
    board: *const Board,
    moves: ScoringMoveList,
    depth: u16,
    ttm: BitMove,
    killers: [BitMove; 2],
    cm: BitMove,

    cur_ptr: *mut ScoringMove,
    end_ptr: *mut ScoringMove,
    end_bad_captures: *mut ScoringMove,
}

impl MovePicker {
    pub fn main_search(board: &Board, depth: u16, mut ttm: BitMove,
                       killers: &[BitMove; 2], counter_move: BitMove) -> Self {
        assert!(!board.in_check());

        if ttm == BitMove::null() || !board.pseudo_legal_move(ttm) {
            ttm = BitMove::null();
        }

        let mut moves = ScoringMoveList::default();
        let first: *mut ScoringMove = moves.as_mut_ptr();

        MovePicker {
            pick: Pick::MainSearch,
            board: &* board,
            moves,
            depth,
            ttm,
            killers: (*killers).clone(),
            cm: counter_move,
            cur_ptr: first,
            end_ptr: first,
            end_bad_captures: first,
        }
    }

    fn score_captures(&mut self, board: &Board) {
        let mut ptr = self.cur_ptr;
        unsafe {
            while ptr < self.end_ptr {
                let mov: BitMove = (*ptr).bit_move;
                let piece_moved = board.moved_piece(mov);
                let piece_cap = board.captured_piece(mov).unwrap();
                (*ptr).score = piece_value(piece_cap,false) as i16
                    - piece_value(piece_moved,false) as i16;
                ptr = ptr.add(1);
            }
        }
    }

    fn pick_best(&self, begin: *mut ScoringMove, end: *mut ScoringMove) -> ScoringMove {
        unsafe {
            let mut best_score = begin;
            let mut cur = begin.add(1);
            while cur < end {
                if (*cur).score > (*best_score).score {
                    best_score = cur;
                }
                cur = cur.add(1);
            }
            ptr::swap(begin, best_score);
            *begin
        }
    }

    pub fn next(&mut self, board: &Board, skip_quiets: bool) -> BitMove {
        let mut mov: ScoringMove = ScoringMove::null();
        match self.pick {
            Pick::MainSearch => {
                self.pick.incr();
                return self.ttm;
            },
            Pick::CapturesInit => {
                unsafe {
                    self.end_ptr = MoveGen::extend_from_ptr::<PseudoLegal,CapturesGenType, ScoringMoveList>
                        (board, self.cur_ptr);
                }
                self.score_captures(board);
                self.pick.incr();
                self.next(board, skip_quiets)
            },
            Pick::GoodCaptures => {
                while self.cur_ptr < self.end_ptr {
                    mov = self.pick_best(self.cur_ptr, self.end_ptr);
                    unsafe {self.cur_ptr = self.cur_ptr.add(1);}
                    if mov.bit_move != self.ttm && mov.score > -20 {
                        return mov.bit_move;
                    }
                    unsafe {
                        *self.end_bad_captures = mov;
                        self.end_bad_captures = self.end_bad_captures.add(1);
                    }
                }
                self.pick.incr();
                self.next(board, skip_quiets)
            },
            Pick::KillerOne | Pick::KillerTwo => {
                mov.bit_move = self.killers[self.pick as usize - Pick::KillerOne as usize];
                self.pick.incr();
                if mov.bit_move != BitMove::null()
                    && mov.bit_move != self.ttm
                    && board.pseudo_legal_move(mov.bit_move)
                    && !mov.bit_move.is_capture() {
                    return mov.bit_move;
                }
                self.next(board, skip_quiets)
            },
            Pick::CounterMove => {
                self.pick.incr();
                if self.cm != BitMove::null()
                    && self.cm != self.ttm
                    && self.cm != self.killers[0]
                    && self.cm != self.killers[1]
                    && board.pseudo_legal_move(self.cm)
                    && !self.cm.is_capture() {
                    return self.cm;
                }
                self.next(board, skip_quiets)
            },
            Pick::QuietInit => {
                unsafe {
                    self.cur_ptr = self.end_bad_captures;
                    self.end_ptr = MoveGen::extend_from_ptr::<PseudoLegal,CapturesGenType, ScoringMoveList>(
                        board, self.cur_ptr);
                    // TODO: Need to score the captures
                }
                return BitMove::null();
            }
            _ => BitMove::null()
        }
    }
}

fn partial_insertion_sort(begin: *mut ScoringMove, end: *mut ScoringMove, limit: i32) {
    unsafe {
        let mut sorted_end: *mut ScoringMove = begin;
        let mut p: *mut ScoringMove = begin.add(1);
        while p < end {
            if (*p).score as i32 >= limit {
                let tmp: ScoringMove = *p;
                sorted_end = sorted_end.add(1);
                *p = *sorted_end;
                let mut q = sorted_end;
                while q != begin && *(q.sub(1)) < tmp {
                    *q = *(q.sub(1));
                    q = q.sub(1);
                }
                *q = tmp;
            }
            p = p.add(1);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand;

    use std::i16::{MAX,MIN};

    #[test]
    fn mp_partial_insertion_sort() {
        let mut moves = ScoringMoveList::default();
        moves.push_score(BitMove::null(), 34);
        moves.push_score(BitMove::null(), 20);
        moves.push_score(BitMove::null(), -5);
        moves.push_score(BitMove::null(), 50);
        moves.push_score(BitMove::null(), 3);
        moves.push_score(BitMove::null(), 0);
        moves.push_score(BitMove::null(), 9);
        moves.push_score(BitMove::null(), -1);
        moves.push_score(BitMove::null(), 2);

        let len = moves.len();
        let begin = moves.get_mut(0).unwrap() as *mut ScoringMove;
        let end = unsafe {
            begin.add(len)
        };

        partial_insertion_sort(begin, end, 4);
    }

    #[test]
    fn mp_partial_insertion_sort_rand() {
        for _x in 0..10 {
            partial_insertion_t();
        }
    }

    fn partial_insertion_t() {
        let min = 10;
        let max = 200;
        let num = (rand::random::<u16>() % (max - min)) + min;

        let mut moves = ScoringMoveList::default();

        for _x in 0..num {
            let rand_score = rand::random::<i16>();
            moves.push_score(BitMove::null(),rand_score);
        }

        let limit: i16 = rand::random::<i16>()
            .max(MIN + 10)
            .min(MAX - 10);

        let len = moves.len();
        let begin = moves.get_mut(0).unwrap() as *mut ScoringMove;
        let end = unsafe {
            begin.add(len)
        };

        partial_insertion_sort(begin, end, limit as i32);

        let mut unsorted_idx = 0;

        while unsorted_idx < len {
            if moves[unsorted_idx].score < limit {
                break;
            }
            unsorted_idx += 1;
        }

        for x in 0..unsorted_idx {
            assert!(moves[x].score >= limit);
        }

        for x in unsorted_idx..len {
            assert!(moves[x].score < limit);
        }
    }
}
<commit_msg>Basic MovePicker down. Methods for Main Search, Evasions, QSearch, and ProbCut in place. Need to test it though.<commit_after>mod pick;

use std::ptr;
use std::mem;

#[allow(unused_imports)]
use pleco::{BitMove,Board,ScoringMove,ScoringMoveList,SQ};
use pleco::board::movegen::{PseudoLegal,MoveGen};
use pleco::helper::prelude::piece_value;
use pleco::core::mono_traits::*;

use self::pick::*;

// TODO: use Generators once stabilized.

pub struct MovePicker {
    pick: Pick,
    board: *const Board,
    moves: ScoringMoveList,
    depth: i16,
    ttm: BitMove,
    killers: [BitMove; 2],
    cm: BitMove,
    recapture_sq: SQ,
    threshold: i32,
    cur_ptr: *mut ScoringMove,
    end_ptr: *mut ScoringMove,
    end_bad_captures: *mut ScoringMove,
}

impl MovePicker {
    /// MovePicker constructor for the main search
    pub fn main_search(board: &Board, depth: i16, mut ttm: BitMove,
                       killers: &[BitMove; 2], counter_move: BitMove) -> Self {

        let mut pick = if board.in_check() {Pick::EvasionSearch} else {Pick::MainSearch};

        if ttm == BitMove::null() || !board.pseudo_legal_move(ttm) {
            ttm = BitMove::null();
            pick.incr();
        }

        let mut moves = ScoringMoveList::default();
        let first: *mut ScoringMove = moves.as_mut_ptr();

        MovePicker {
            pick,
            board: &* board,
            moves,
            depth,
            ttm,
            killers: (*killers).clone(),
            cm: counter_move,
            recapture_sq: unsafe {mem::uninitialized()},
            threshold: unsafe {mem::uninitialized()},
            cur_ptr: first,
            end_ptr: first,
            end_bad_captures: first,
        }
    }

    /// MovePicker constructor for quiescence search
    pub fn qsearch(board: &Board, depth: i16, ttm: BitMove, recapture_sq: SQ) -> Self {
        assert!(depth <= 0);
        let mut moves = ScoringMoveList::default();
        let first: *mut ScoringMove = moves.as_mut_ptr();
        let mut mp_qs = MovePicker {
            pick: unsafe { mem::uninitialized() },
            board: &*board,
            moves,
            depth,
            ttm,
            killers: unsafe { mem::uninitialized() },
            cm: unsafe { mem::uninitialized() },
            recapture_sq: unsafe { mem::uninitialized() },
            threshold: unsafe {mem::uninitialized()},
            cur_ptr: first,
            end_ptr: first,
            end_bad_captures: first,
        };

        if board.in_check() {
            mp_qs.pick = Pick::EvasionSearch;
        } else if depth > -5 {
            mp_qs.pick = Pick::QSearch;
        } else {
            mp_qs.pick = Pick::QSearchRecaptures;
            mp_qs.recapture_sq = recapture_sq;
            return mp_qs;
        }

        if ttm == BitMove::null() || !board.pseudo_legal_move(ttm) {
            mp_qs.ttm = BitMove::null();
            mp_qs.pick.incr()
        }

        mp_qs
    }

    /// MovePicker constructor for ProbCut: we generate captures with SEE higher
    /// than or equal to the given threshold.
    pub fn probcut_search(board: &Board, threshold: i32, mut ttm: BitMove, recapture_sq: SQ) -> Self {
        assert!(board.in_check());
        let mut moves = ScoringMoveList::default();
        let first: *mut ScoringMove = moves.as_mut_ptr();

        let mut pick = Pick::ProbCutSearch;

        ttm = if ttm != BitMove::null()
            && board.pseudo_legal_move(ttm)
            && ttm.is_capture() {
            ttm
        } else {
            pick.incr();
            BitMove::null()
        };

        MovePicker {
            pick,
            board: &*board,
            moves,
            depth: unsafe { mem::uninitialized() },
            ttm,
            killers: unsafe { mem::uninitialized() },
            cm: unsafe { mem::uninitialized() },
            recapture_sq: recapture_sq,
            threshold,
            cur_ptr: first,
            end_ptr: first,
            end_bad_captures: first,
        }
    }

    fn score_captures(&mut self, board: &Board) {
        let mut ptr = self.cur_ptr;
        unsafe {
            while ptr < self.end_ptr {
                let mov: BitMove = (*ptr).bit_move;
                let piece_moved = board.moved_piece(mov);
                let piece_cap = board.captured_piece(mov).unwrap();
                (*ptr).score = piece_value(piece_cap,false) as i16
                    - piece_value(piece_moved,false) as i16;
                ptr = ptr.add(1);
            }
        }
    }

    fn score_evasions(&mut self, board: &Board) {
        let mut ptr = self.cur_ptr;
        unsafe {
            while ptr < self.end_ptr {
                let mov: BitMove = (*ptr).bit_move;
                if mov.is_capture() {
                    let piece_moved = board.moved_piece(mov);
                    let piece_cap = board.captured_piece(mov).unwrap();
                    (*ptr).score = piece_value(piece_cap,false) as i16
                        - piece_value(piece_moved,false) as i16;
                }
                ptr = ptr.add(1);
            }
        }
    }

    fn pick_best(&self, begin: *mut ScoringMove, end: *mut ScoringMove) -> ScoringMove {
        unsafe {
            let mut best_score = begin;
            let mut cur = begin.add(1);
            while cur < end {
                if (*cur).score > (*best_score).score {
                    best_score = cur;
                }
                cur = cur.add(1);
            }
            ptr::swap(begin, best_score);
            *begin
        }
    }

    pub fn next(&mut self, board: &Board, skip_quiets: bool) -> BitMove {
        let mut mov: ScoringMove = ScoringMove::null();
        match self.pick {
            Pick::MainSearch | Pick::EvasionSearch | Pick::QSearch | Pick::ProbCutSearch => {
                self.pick.incr();
                return self.ttm;
            },
            Pick::CapturesInit | Pick::ProbCutCapturesInit | Pick::QSearchInit | Pick::QSearchRecaptures => {
                unsafe {
                    self.end_bad_captures = self.moves.as_mut_ptr();
                    self.cur_ptr = self.end_bad_captures;
                    self.end_ptr = MoveGen::extend_from_ptr::<PseudoLegal,CapturesGenType, ScoringMoveList>
                        (board, self.cur_ptr);
                }
                self.score_captures(board);
                self.pick.incr();
                return self.next(board, skip_quiets);
            },
            Pick::GoodCaptures => {
                while self.cur_ptr < self.end_ptr {
                    mov = self.pick_best(self.cur_ptr, self.end_ptr);
                    unsafe {self.cur_ptr = self.cur_ptr.add(1);}
                    if mov.bit_move != self.ttm && mov.score > -20 {
                        return mov.bit_move;
                    }
                    unsafe {
                        *self.end_bad_captures = mov;
                        self.end_bad_captures = self.end_bad_captures.add(1);
                    }
                }
                self.pick.incr();
                return self.next(board, skip_quiets);
            },
            Pick::KillerOne | Pick::KillerTwo => {
                mov.bit_move = self.killers[self.pick as usize - Pick::KillerOne as usize];
                self.pick.incr();
                if mov.bit_move != BitMove::null()
                    && mov.bit_move != self.ttm
                    && board.pseudo_legal_move(mov.bit_move)
                    && !mov.bit_move.is_capture() {
                    return mov.bit_move;
                }
                return self.next(board, skip_quiets);
            },
            Pick::CounterMove => {
                self.pick.incr();
                if self.cm != BitMove::null()
                    && self.cm != self.ttm
                    && self.cm != self.killers[0]
                    && self.cm != self.killers[1]
                    && board.pseudo_legal_move(self.cm)
                    && !self.cm.is_capture() {
                    return self.cm;
                }
                return self.next(board, skip_quiets);
            },
            Pick::QuietInit => {
                unsafe {
                    self.cur_ptr = self.end_bad_captures;
                    self.end_ptr = MoveGen::extend_from_ptr::<PseudoLegal,CapturesGenType, ScoringMoveList>(
                        board, self.cur_ptr);
                    // TODO: Need to score the captures
                }
                self.pick.incr();
                return self.next(board, skip_quiets);
            },
            Pick::QuietMoves => {
                if !skip_quiets {
                    while self.cur_ptr < self.end_ptr {
                        unsafe {
                            mov = *self.cur_ptr;
                            self.cur_ptr = self.cur_ptr.add(1);
                            if mov.bit_move != self.ttm
                                && mov.bit_move != self.killers[0]
                                && mov.bit_move != self.killers[1]
                                && mov.bit_move != self.cm {
                                return mov.bit_move;
                            }
                        }
                    }
                }
                self.pick.incr();
                self.cur_ptr = self.moves.as_mut_ptr();
                return self.next(board, skip_quiets);
            },
            Pick::BadCaptures => {
                if self.cur_ptr < self.end_bad_captures {
                    unsafe  {
                        mov = *self.cur_ptr;
                        self.cur_ptr = self.cur_ptr.add(1);
                    }
                    return mov.bit_move
                }
            },
            Pick::EvasionsInit => {
                unsafe {
                    self.cur_ptr = self.moves.as_mut_ptr();
                    self.end_ptr = MoveGen::extend_from_ptr::<PseudoLegal,EvasionsGenType,ScoringMoveList>
                        (board, self.cur_ptr);
                }
                self.score_evasions(board);
                self.pick.incr();
                return self.next(board, skip_quiets);
            },
            Pick::AllEvasions => {
                while self.cur_ptr < self.end_ptr {
                    mov = self.pick_best(self.cur_ptr, self.end_ptr);
                    unsafe {self.cur_ptr = self.cur_ptr.add(1);}
                    if mov.bit_move != self.ttm {
                        return mov.bit_move;
                    }
                }
            },
            Pick::ProbCutCaptures => {
                while self.cur_ptr < self.end_ptr {
                    mov = self.pick_best(self.cur_ptr, self.end_ptr);
                    unsafe {self.cur_ptr = self.cur_ptr.add(1);}
                    if mov.bit_move != self.ttm {
                        return mov.bit_move;
                    }
                }
            },
            Pick::QCaptures => {
                while self.cur_ptr < self.end_ptr {
                    mov = self.pick_best(self.cur_ptr, self.end_ptr);
                    unsafe {self.cur_ptr = self.cur_ptr.add(1);}
                    if mov.bit_move != self.ttm {
                        return mov.bit_move;
                    }
                }

                if self.depth > -1 {
                    unsafe {
                        self.cur_ptr = self.moves.as_mut_ptr();
                        self.end_ptr = MoveGen::extend_from_ptr::<PseudoLegal,QuietChecksGenType,ScoringMoveList>
                            (board, self.cur_ptr);
                    }
                    self.pick.incr();
                    return self.next(board, skip_quiets);
                }
            },
            Pick::QChecks => {
                while self.cur_ptr < self.end_ptr {
                    unsafe {
                        mov = *self.cur_ptr;
                        self.cur_ptr = self.cur_ptr.add(1);
                    }
                    if mov.bit_move != self.ttm {
                        return mov.bit_move;
                    }
                }

            },
            Pick::QRecaptures => {
                while self.cur_ptr < self.end_ptr {
                    unsafe {
                        mov = *self.cur_ptr;
                        self.cur_ptr = self.cur_ptr.add(1);
                        if mov.bit_move.get_dest() == self.recapture_sq {
                            return mov.bit_move;
                        }
                    }
                }
            }
        }
        BitMove::null()
    }
}

fn partial_insertion_sort(begin: *mut ScoringMove, end: *mut ScoringMove, limit: i32) {
    unsafe {
        let mut sorted_end: *mut ScoringMove = begin;
        let mut p: *mut ScoringMove = begin.add(1);
        while p < end {
            if (*p).score as i32 >= limit {
                let tmp: ScoringMove = *p;
                sorted_end = sorted_end.add(1);
                *p = *sorted_end;
                let mut q = sorted_end;
                while q != begin && *(q.sub(1)) < tmp {
                    *q = *(q.sub(1));
                    q = q.sub(1);
                }
                *q = tmp;
            }
            p = p.add(1);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand;

    use std::i16::{MAX,MIN};

    #[test]
    fn mp_partial_insertion_sort() {
        let mut moves = ScoringMoveList::default();
        moves.push_score(BitMove::null(), 34);
        moves.push_score(BitMove::null(), 20);
        moves.push_score(BitMove::null(), -5);
        moves.push_score(BitMove::null(), 50);
        moves.push_score(BitMove::null(), 3);
        moves.push_score(BitMove::null(), 0);
        moves.push_score(BitMove::null(), 9);
        moves.push_score(BitMove::null(), -1);
        moves.push_score(BitMove::null(), 2);

        let len = moves.len();
        let begin = moves.get_mut(0).unwrap() as *mut ScoringMove;
        let end = unsafe {
            begin.add(len)
        };

        partial_insertion_sort(begin, end, 4);
    }

    #[test]
    fn mp_partial_insertion_sort_rand() {
        for _x in 0..10 {
            partial_insertion_t();
        }
    }

    fn partial_insertion_t() {
        let min = 10;
        let max = 200;
        let num = (rand::random::<u16>() % (max - min)) + min;

        let mut moves = ScoringMoveList::default();

        for _x in 0..num {
            let rand_score = rand::random::<i16>();
            moves.push_score(BitMove::null(),rand_score);
        }

        let limit: i16 = rand::random::<i16>()
            .max(MIN + 10)
            .min(MAX - 10);

        let len = moves.len();
        let begin = moves.get_mut(0).unwrap() as *mut ScoringMove;
        let end = unsafe {
            begin.add(len)
        };

        partial_insertion_sort(begin, end, limit as i32);

        let mut unsorted_idx = 0;

        while unsorted_idx < len {
            if moves[unsorted_idx].score < limit {
                break;
            }
            unsorted_idx += 1;
        }

        for x in 0..unsorted_idx {
            assert!(moves[x].score >= limit);
        }

        for x in unsorted_idx..len {
            assert!(moves[x].score < limit);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Carl-Erwin Griffith
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the
// Software without restriction, including without
// limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice
// shall be included in all copies or substantial portions
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER

extern crate mapped_file;
extern crate rand;

use rand::Rng;
use rand::distributions::{IndependentSample, Range};

use mapped_file::mapped_file::MappedFile;

#[cfg(test)]
use std::rc::Rc;

#[cfg(test)]
use std::cell::RefCell;

#[cfg(test)]
fn wcl(file: &Rc<RefCell<MappedFile>>) {
    println!("------------ WCL {} @ {}", file!(), line!());

    let read_size = 32 * 1024;

    let mut prev_byte = 0;
    let mut nr_lines = 0;
    let mut it = MappedFile::iter_from(&file, 0);
    let mut vec = Vec::with_capacity(read_size);
    loop {
        println!("wc loop");

        println!("try read {} bytes", read_size);

        let n = MappedFile::read(&mut it, read_size, &mut vec);

        println!("read ok");

        for i in &vec {
            match *i as char {
                '\r' => {
                    nr_lines += 1;
                }

                '\n' => {
                    if prev_byte != '\r' as u8 {
                        nr_lines += 1;
                    }
                }

                _ => {}
            }
            prev_byte = *i as u8;
        }

        if n < read_size {
            break;
        }

        vec.clear();
    }

    if file.borrow().size() > 0 {
        nr_lines += 1;
    }
    println!("nr_lines {}", nr_lines)
}

///////////////////////////////////////////////////////////////////////////////////////////////////

fn test_insert(
    test_num: u32,
    file_size: usize,
    page_size: usize,
    insert_size: usize,
    nr_insert: usize,
) {
    use std::fs::File;
    use std::io::prelude::*;

    let filename = "/tmp/playground_insert_test";
    let mut file = File::create(filename).unwrap();

    // prepare file content
    println!("-- generating test file");
    let mut slc = Vec::with_capacity(file_size);
    for i in 0..file_size {
        if (i % (1024 * 1024 * 256)) == 0 {
            println!("-- @ bytes {}", i);
        }

        let val = if i % 100 == 0 {
            '\n' as u8
        } else {
            (('0' as i32) + (i as i32 % 10)) as u8
        };
        slc.push(val);
    }
    file.write_all(slc.as_slice()).unwrap();
    file.sync_all().unwrap();
    drop(slc);

    println!("-- mapping the test file");
    let file = match MappedFile::new(filename, page_size) {
        Some(file) => file,
        None => panic!("cannot map file"),
    };

    println!("-- testing insert");

    let mut insert_data = Vec::with_capacity(insert_size);
    for i in 0..insert_size {
        if i % 100 == 0 {
            insert_data.push('\n' as u8);
        } else {
            let val = (('0' as i32) + (i as i32 % 10)) as u8;
            insert_data.push(val);
        }
    }

    println!("-- insert data size = {} bytes", insert_data.len());
    if false {
        for off in 0..nr_insert {
            // let fsz = file.as_ref().borrow().size();
            // println!("fsz = {}", fsz);
            let mut it = MappedFile::iter_from(&file, off as u64);
            MappedFile::insert(&mut it, &insert_data.as_slice());
        }
    }

    println!("-- generate random offsets");

    let mut indexes = Vec::new();
    let between = Range::new(0 as u64, (file_size + 1) as u64);
    let mut rng = rand::thread_rng();

    for _ in 0..nr_insert {
        let a = between.ind_sample(&mut rng);
        //        println!("random index {}", a);
        indexes.push(a);
    }

    println!("-- insert {} times", indexes.len());
    for off in &indexes {
        let mut it = MappedFile::iter_from(&file, *off as u64);
        MappedFile::insert(&mut it, &insert_data.as_slice());
    }

    return;

    println!("-- print data {{");
    let mut count: u64 = 0;
    for i in MappedFile::iter(&file) {
        print!("{}", *i as char);
        count += 1;
    }
    println!("}}");
    println!(" count = {}", count);

    //     println!("-- testing insert 2");
    //      MappedFile::insert(&mut it0, &[1, 2, 3]);

    use std::io;

    if !true {
        println!("Hit [Enter] to stop");
        let mut stop = String::new();
        io::stdin().read_line(&mut stop).expect("something");
    }
}

fn test_remove(test_num: u32, file_size: usize, page_size: usize, nr_remove: usize, offset: u64) {
    use std::fs::File;
    use std::io::prelude::*;

    let filename = "/tmp/playground_insert_test";
    let mut file = File::create(filename).unwrap();

    // prepare file content
    println!("-- generating test file");
    let mut slc = Vec::with_capacity(file_size);
    for i in 0..file_size {
        if (i % (1024 * 1024 * 256)) == 0 {
            println!("-- @ bytes {}", i);
        }

        let val = if i % 100 == 0 {
            '\n' as u8
        } else {
            (('0' as i32) + (i as i32 % 10)) as u8
        };
        slc.push(val);
    }
    file.write_all(slc.as_slice()).unwrap();
    file.sync_all().unwrap();
    drop(slc);

    println!("-- mapping the test file");
    let file = match MappedFile::new(filename, page_size) {
        Some(file) => file,
        None => panic!("cannot map file"),
    };

    println!(
        "-- testing remove {} @ {} from {}",
        nr_remove, offset, file_size
    );
    let mut it = MappedFile::iter_from(&file, offset);
    MappedFile::remove(&mut it, nr_remove);

    println!("-- file.size() {}", file.as_ref().borrow().size());

    return;
    println!("-- print data {{");
    let mut count: u64 = 0;
    for i in MappedFile::iter(&file) {
        print!("{}", *i as char);
        count += 1;
    }
    println!("}}");
    println!(" count = {}", count);

    use std::io;

    if !true {
        println!("Hit [Enter] to stop");
        let mut stop = String::new();
        io::stdin().read_line(&mut stop).expect("something");
    }
}

fn main() {
    let file_size = 1024 * 1024 * 1024 * 2;
    let page_size = 4096;
    let nr_remove = file_size - 4096 * 2;
    let offset = page_size as u64;
    test_remove(0, file_size, page_size, nr_remove, offset);

    return;
    test_remove(0, 1024 * 1024 * 1024 * 1, 4096, 4096 * 2, 0);

    return;
    test_remove(0, 4096 * 4, 4096, 4096 * 2, 0);

    for i in 0..4096 + 1 {
        test_remove(0, 4096, 4096, i, 0);
    }

    let max_file_size: usize = 1024 * 1024 * 100;
    let max_page_size = 4096 * 2;
    let max_insert_size = 4;
    let max_nr_insert = 10;

    let mut test_num = 0;
    let mut file_size = 0;

    while file_size < max_file_size {
        let mut page_size = 4096;
        while page_size < max_page_size {
            let mut insert_size = 1;
            while insert_size < max_insert_size {
                let mut nr_insert = 1;
                while nr_insert < max_nr_insert {
                    println!("------------------------------------");
                    println!("test_num {}", test_num);
                    println!("file_size {}", file_size);
                    println!("page_size {}", page_size);
                    println!("insert_size {}", insert_size);

                    test_insert(test_num, file_size, page_size, insert_size, nr_insert);

                    test_num += 1;
                    nr_insert += 1;
                }
                insert_size += 1;
            }
            page_size += 4096;
        }
        file_size += 1024 * 1024;
    }
}
<commit_msg>remove old file<commit_after><|endoftext|>


<commit_before>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::fmt::{self, Display, Formatter};

use byteorder::{BigEndian, WriteBytesExt};
use kvproto::metapb::Region;

use crate::store::{CasualMessage, CasualRouter};
use engine_rocks::RocksSnapshot;
use engine_traits::Snapshot;
use engine_traits::CF_RAFT;
use tikv_util::worker::Runnable;

use super::metrics::*;
use crate::store::metrics::*;

/// Consistency checking task.
pub enum Task<S> {
    ComputeHash { index: u64, region: Region, snap: S },
}

impl<S> Task<S>
where
    S: Snapshot,
{
    pub fn compute_hash(region: Region, index: u64, snap: S) -> Task<S> {
        Task::ComputeHash {
            region,
            index,
            snap,
        }
    }
}

impl<S> Display for Task<S>
where
    S: Snapshot,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match *self {
            Task::ComputeHash {
                ref region, index, ..
            } => write!(f, "Compute Hash Task for {:?} at {}", region, index),
        }
    }
}

pub struct Runner<C: CasualRouter<RocksSnapshot>> {
    router: C,
}

impl<C: CasualRouter<RocksSnapshot>> Runner<C> {
    pub fn new(router: C) -> Runner<C> {
        Runner { router }
    }

    /// Computes the hash of the Region.
    fn compute_hash<S>(&mut self, region: Region, index: u64, snap: S)
    where
        S: Snapshot,
    {
        let region_id = region.get_id();
        info!(
            "computing hash";
            "region_id" => region_id,
            "index" => index,
        );
        REGION_HASH_COUNTER.compute.all.inc();

        let timer = REGION_HASH_HISTOGRAM.start_coarse_timer();
        let mut digest = crc32fast::Hasher::new();
        let mut cf_names = snap.cf_names();
        cf_names.sort();

        // Computes the hash from all the keys and values in the range of the Region.
        let start_key = keys::enc_start_key(&region);
        let end_key = keys::enc_end_key(&region);
        for cf in cf_names {
            let res = snap.scan_cf(cf, &start_key, &end_key, false, |k, v| {
                digest.update(k);
                digest.update(v);
                Ok(true)
            });
            if let Err(e) = res {
                REGION_HASH_COUNTER.compute.failed.inc();
                error!(
                    "failed to calculate hash";
                    "region_id" => region_id,
                    "err" => %e,
                );
                return;
            }
        }

        // Computes the hash from the Region state too.
        let region_state_key = keys::region_state_key(region_id);
        digest.update(&region_state_key);
        match snap.get_value_cf(CF_RAFT, &region_state_key) {
            Err(e) => {
                REGION_HASH_COUNTER.compute.failed.inc();
                error!(
                    "failed to get region state";
                    "region_id" => region_id,
                    "err" => %e,
                );
                return;
            }
            Ok(Some(v)) => digest.update(&v),
            Ok(None) => {}
        }
        let sum = digest.finalize();
        timer.observe_duration();

        let mut checksum = Vec::with_capacity(4);
        checksum.write_u32::<BigEndian>(sum).unwrap();
        let msg = CasualMessage::ComputeHashResult {
            index,
            hash: checksum,
        };
        if let Err(e) = self.router.send(region_id, msg) {
            warn!(
                "failed to send hash compute result";
                "region_id" => region_id,
                "err" => %e,
            );
        }
    }
}

impl<C, S> Runnable<Task<S>> for Runner<C>
where
    C: CasualRouter<RocksSnapshot>,
    S: Snapshot,
{
    fn run(&mut self, task: Task<S>) {
        match task {
            Task::ComputeHash {
                region,
                index,
                snap,
            } => self.compute_hash::<S>(region, index, snap),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use byteorder::{BigEndian, WriteBytesExt};
    use engine_rocks::util::new_engine;
    use engine_rocks::RocksSnapshot;
    use engine_traits::{KvEngine, SyncMutable, CF_DEFAULT, CF_RAFT};
    use kvproto::metapb::*;
    use std::sync::mpsc;
    use std::time::Duration;
    use tempfile::Builder;
    use tikv_util::worker::Runnable;

    #[test]
    fn test_consistency_check() {
        let path = Builder::new().prefix("tikv-store-test").tempdir().unwrap();
        let db = new_engine(
            path.path().to_str().unwrap(),
            None,
            &[CF_DEFAULT, CF_RAFT],
            None,
        )
        .unwrap();

        let mut region = Region::default();
        region.mut_peers().push(Peer::default());

        let (tx, rx) = mpsc::sync_channel(100);
        let mut runner = Runner::new(tx);
        let mut digest = crc32fast::Hasher::new();
        let kvs = vec![(b"k1", b"v1"), (b"k2", b"v2")];
        for (k, v) in kvs {
            let key = keys::data_key(k);
            db.put(&key, v).unwrap();
            // hash should contain all kvs
            digest.update(&key);
            digest.update(v);
        }

        // hash should also contains region state key.
        digest.update(&keys::region_state_key(region.get_id()));
        let sum = digest.finalize();
        runner.run(Task::<RocksSnapshot>::ComputeHash {
            index: 10,
            region: region.clone(),
            snap: db.snapshot(),
        });
        let mut checksum_bytes = vec![];
        checksum_bytes.write_u32::<BigEndian>(sum).unwrap();

        let res = rx.recv_timeout(Duration::from_secs(3)).unwrap();
        match res {
            (region_id, CasualMessage::ComputeHashResult { index, hash }) => {
                assert_eq!(region_id, region.get_id());
                assert_eq!(index, 10);
                assert_eq!(hash, checksum_bytes);
            }
            e => panic!("unexpected {:?}", e),
        }
    }
}
<commit_msg>raftstore: use generic consistency check runner (#8257)<commit_after>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::fmt::{self, Display, Formatter};
use std::marker::PhantomData;

use byteorder::{BigEndian, WriteBytesExt};
use kvproto::metapb::Region;

use crate::store::{CasualMessage, CasualRouter};
use engine_traits::Snapshot;
use engine_traits::CF_RAFT;
use tikv_util::worker::Runnable;

use super::metrics::*;
use crate::store::metrics::*;

/// Consistency checking task.
pub enum Task<S> {
    ComputeHash { index: u64, region: Region, snap: S },
}

impl<S> Task<S>
where
    S: Snapshot,
{
    pub fn compute_hash(region: Region, index: u64, snap: S) -> Task<S> {
        Task::ComputeHash {
            region,
            index,
            snap,
        }
    }
}

impl<S> Display for Task<S>
where
    S: Snapshot,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match *self {
            Task::ComputeHash {
                ref region, index, ..
            } => write!(f, "Compute Hash Task for {:?} at {}", region, index),
        }
    }
}

pub struct Runner<S: Snapshot, C: CasualRouter<S>> {
    _s: PhantomData<S>,
    router: C,
}

impl<S: Snapshot, C: CasualRouter<S>> Runner<S, C> {
    pub fn new(router: C) -> Runner<S, C> {
        Runner {
            _s: Default::default(),
            router,
        }
    }

    /// Computes the hash of the Region.
    fn compute_hash(&mut self, region: Region, index: u64, snap: S) {
        let region_id = region.get_id();
        info!(
            "computing hash";
            "region_id" => region_id,
            "index" => index,
        );
        REGION_HASH_COUNTER.compute.all.inc();

        let timer = REGION_HASH_HISTOGRAM.start_coarse_timer();
        let mut digest = crc32fast::Hasher::new();
        let mut cf_names = snap.cf_names();
        cf_names.sort();

        // Computes the hash from all the keys and values in the range of the Region.
        let start_key = keys::enc_start_key(&region);
        let end_key = keys::enc_end_key(&region);
        for cf in cf_names {
            let res = snap.scan_cf(cf, &start_key, &end_key, false, |k, v| {
                digest.update(k);
                digest.update(v);
                Ok(true)
            });
            if let Err(e) = res {
                REGION_HASH_COUNTER.compute.failed.inc();
                error!(
                    "failed to calculate hash";
                    "region_id" => region_id,
                    "err" => %e,
                );
                return;
            }
        }

        // Computes the hash from the Region state too.
        let region_state_key = keys::region_state_key(region_id);
        digest.update(&region_state_key);
        match snap.get_value_cf(CF_RAFT, &region_state_key) {
            Err(e) => {
                REGION_HASH_COUNTER.compute.failed.inc();
                error!(
                    "failed to get region state";
                    "region_id" => region_id,
                    "err" => %e,
                );
                return;
            }
            Ok(Some(v)) => digest.update(&v),
            Ok(None) => {}
        }
        let sum = digest.finalize();
        timer.observe_duration();

        let mut checksum = Vec::with_capacity(4);
        checksum.write_u32::<BigEndian>(sum).unwrap();
        let msg = CasualMessage::ComputeHashResult {
            index,
            hash: checksum,
        };
        if let Err(e) = self.router.send(region_id, msg) {
            warn!(
                "failed to send hash compute result";
                "region_id" => region_id,
                "err" => %e,
            );
        }
    }
}

impl<C, S> Runnable<Task<S>> for Runner<S, C>
where
    S: Snapshot,
    C: CasualRouter<S>,
{
    fn run(&mut self, task: Task<S>) {
        match task {
            Task::ComputeHash {
                region,
                index,
                snap,
            } => self.compute_hash(region, index, snap),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use byteorder::{BigEndian, WriteBytesExt};
    use engine_rocks::util::new_engine;
    use engine_rocks::RocksSnapshot;
    use engine_traits::{KvEngine, SyncMutable, CF_DEFAULT, CF_RAFT};
    use kvproto::metapb::*;
    use std::sync::mpsc;
    use std::time::Duration;
    use tempfile::Builder;
    use tikv_util::worker::Runnable;

    #[test]
    fn test_consistency_check() {
        let path = Builder::new().prefix("tikv-store-test").tempdir().unwrap();
        let db = new_engine(
            path.path().to_str().unwrap(),
            None,
            &[CF_DEFAULT, CF_RAFT],
            None,
        )
        .unwrap();

        let mut region = Region::default();
        region.mut_peers().push(Peer::default());

        let (tx, rx) = mpsc::sync_channel(100);
        let mut runner = Runner::new(tx);
        let mut digest = crc32fast::Hasher::new();
        let kvs = vec![(b"k1", b"v1"), (b"k2", b"v2")];
        for (k, v) in kvs {
            let key = keys::data_key(k);
            db.put(&key, v).unwrap();
            // hash should contain all kvs
            digest.update(&key);
            digest.update(v);
        }

        // hash should also contains region state key.
        digest.update(&keys::region_state_key(region.get_id()));
        let sum = digest.finalize();
        runner.run(Task::<RocksSnapshot>::ComputeHash {
            index: 10,
            region: region.clone(),
            snap: db.snapshot(),
        });
        let mut checksum_bytes = vec![];
        checksum_bytes.write_u32::<BigEndian>(sum).unwrap();

        let res = rx.recv_timeout(Duration::from_secs(3)).unwrap();
        match res {
            (region_id, CasualMessage::ComputeHashResult { index, hash }) => {
                assert_eq!(region_id, region.get_id());
                assert_eq!(index, 10);
                assert_eq!(hash, checksum_bytes);
            }
            e => panic!("unexpected {:?}", e),
        }
    }
}
<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>

// TODO: collapse. Well, do tables first.
${helpers.single_keyword("visibility",
                         "visible hidden",
                         extra_gecko_values="collapse",
                         gecko_ffi_name="mVisible",
                         animation_type="normal",
                         spec="https://drafts.csswg.org/css-box/#propdef-visibility")}

// CSS Writing Modes Level 3
// https://drafts.csswg.org/css-writing-modes-3
${helpers.single_keyword("writing-mode",
                         "horizontal-tb vertical-rl vertical-lr",
                         experimental=True,
                         need_clone=True,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode")}

${helpers.single_keyword("direction", "ltr rtl", need_clone=True, animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction")}

<%helpers:single_keyword_computed
    name="text-orientation"
    values="mixed upright sideways"
    extra_specified="sideways-right"
    products="gecko"
    need_clone="True"
    animation_type="none"
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation"
>
    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "mixed upright sideways".split():
                    SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-sideways-right
                SpecifiedValue::sideways_right => computed_value::T::sideways,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "mixed upright sideways".split():
                    computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
</%helpers:single_keyword_computed>

// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword("color-adjust",
                         "economy exact", products="gecko",
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-color/#propdef-color-adjust")}

<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES" } %>
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
${helpers.single_keyword("image-rendering",
                         "auto crisp-edges",
                         extra_gecko_values="optimizespeed optimizequality",
                         extra_servo_values="pixelated",
                         custom_consts=image_rendering_custom_consts,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-images/#propdef-image-rendering")}

// Image Orientation
<%helpers:longhand name="image-orientation"
                   products="None"
                   animation_type="none"
    spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
      /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation">
    use std::fmt;
    use style_traits::ToCss;
    use values::specified::Angle;

    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    use std::f32::consts::PI;
    use values::CSSFloat;
    const TWO_PI: CSSFloat = 2.0 * PI;

    #[derive(Clone, PartialEq, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub angle: Option<Angle>,
        pub flipped: bool
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if let Some(angle) = self.angle {
                try!(angle.to_css(dest));
                if self.flipped {
                    dest.write_str(" flipped")
                } else {
                    Ok(())
                }
            } else {
                if self.flipped {
                    dest.write_str("flipped")
                } else {
                    dest.write_str("from-image")
                }
            }
        }
    }

    pub mod computed_value {
        use values::computed::Angle;

        #[derive(Clone, PartialEq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum T {
            FromImage,
            AngleWithFlipped(Angle, bool),
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
    }

    // According to CSS Content Module Level 3:
    // The computed value of the property is calculated by rounding the specified angle
    // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
    #[inline]
    fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
        let radians = angle.radians();
        let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
        let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
        let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
        computed::Angle::from_radians(normalized_radians)
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, context: &Context) -> computed_value::T {
            if let Some(ref angle) = self.angle {
                let angle = angle.to_computed_value(context);
                let normalized_angle = normalize_angle(&angle);
                computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
            } else {
                if self.flipped {
                    computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
                } else {
                    computed_value::T::FromImage
                }
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> Self {
            match *computed {
                computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
                computed_value::T::AngleWithFlipped(ref angle, flipped) => {
                    SpecifiedValue {
                        angle: Some(Angle::from_computed_value(angle)),
                        flipped: flipped,
                    }
                }
            }
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                computed_value::T::FromImage => dest.write_str("from-image"),
                computed_value::T::AngleWithFlipped(angle, flipped) => {
                    try!(angle.to_css(dest));
                    if flipped {
                        try!(dest.write_str(" flipped"));
                    }
                    Ok(())
                },
            }
        }
    }

    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if input.try(|input| input.expect_ident_matching("from-image")).is_ok() {
            // Handle from-image
            Ok(SpecifiedValue { angle: None, flipped: false })
        } else {
            // Handle <angle> | <angle>? flip
            let angle = input.try(|input| Angle::parse(context, input)).ok();
            let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
            let explicit_angle = if angle.is_none() && !flipped {
                Some(Angle::zero())
            } else {
                angle
            };

            Ok(SpecifiedValue { angle: explicit_angle, flipped: flipped })
        }
    }
</%helpers:longhand>

// Used in the bottom-up flow construction traversal to avoid constructing flows for
// descendants of nodes with `display: none`.
<%helpers:longhand name="-servo-under-display-none"
                   derived_from="display"
                   products="servo"
                   animation_type="none"
                   spec="Nonstandard (internal layout use only)">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;
    use values::HasViewportPercentage;

    no_viewport_percentage!(SpecifiedValue);

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
    pub struct SpecifiedValue(pub bool);

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }
    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub fn get_initial_value() -> computed_value::T {
        SpecifiedValue(false)
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
            Ok(()) // Internal property
        }
    }

    #[inline]
    pub fn derive_from_display(context: &mut Context) {
        use super::display::computed_value::T as Display;

        if context.style().get_box().clone_display() == Display::none {
            context.mutate_style().mutate_inheritedbox()
                                  .set__servo_under_display_none(SpecifiedValue(true));
        }
    }
</%helpers:longhand>
<commit_msg>Make stylo recognize -moz-crisp-edges keyword.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>

// TODO: collapse. Well, do tables first.
${helpers.single_keyword("visibility",
                         "visible hidden",
                         extra_gecko_values="collapse",
                         gecko_ffi_name="mVisible",
                         animation_type="normal",
                         spec="https://drafts.csswg.org/css-box/#propdef-visibility")}

// CSS Writing Modes Level 3
// https://drafts.csswg.org/css-writing-modes-3
${helpers.single_keyword("writing-mode",
                         "horizontal-tb vertical-rl vertical-lr",
                         experimental=True,
                         need_clone=True,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode")}

${helpers.single_keyword("direction", "ltr rtl", need_clone=True, animation_type="none",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction")}

<%helpers:single_keyword_computed
    name="text-orientation"
    values="mixed upright sideways"
    extra_specified="sideways-right"
    products="gecko"
    need_clone="True"
    animation_type="none"
    spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation"
>
    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _: &Context) -> computed_value::T {
            match *self {
                % for value in "mixed upright sideways".split():
                    SpecifiedValue::${value} => computed_value::T::${value},
                % endfor
                // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-sideways-right
                SpecifiedValue::sideways_right => computed_value::T::sideways,
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> SpecifiedValue {
            match *computed {
                % for value in "mixed upright sideways".split():
                    computed_value::T::${value} => SpecifiedValue::${value},
                % endfor
            }
        }
    }
</%helpers:single_keyword_computed>

// CSS Color Module Level 4
// https://drafts.csswg.org/css-color/
${helpers.single_keyword("color-adjust",
                         "economy exact", products="gecko",
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-color/#propdef-color-adjust")}

<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES",
                                     "-moz-crisp-edges": "CRISPEDGES" } %>
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
${helpers.single_keyword("image-rendering",
                         "auto",
                         extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
                         extra_servo_values="pixelated crisp-edges",
                         custom_consts=image_rendering_custom_consts,
                         animation_type="none",
                         spec="https://drafts.csswg.org/css-images/#propdef-image-rendering")}

// Image Orientation
<%helpers:longhand name="image-orientation"
                   products="None"
                   animation_type="none"
    spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
      /// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation">
    use std::fmt;
    use style_traits::ToCss;
    use values::specified::Angle;

    use values::HasViewportPercentage;
    no_viewport_percentage!(SpecifiedValue);

    use std::f32::consts::PI;
    use values::CSSFloat;
    const TWO_PI: CSSFloat = 2.0 * PI;

    #[derive(Clone, PartialEq, Copy, Debug)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub struct SpecifiedValue {
        pub angle: Option<Angle>,
        pub flipped: bool
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if let Some(angle) = self.angle {
                try!(angle.to_css(dest));
                if self.flipped {
                    dest.write_str(" flipped")
                } else {
                    Ok(())
                }
            } else {
                if self.flipped {
                    dest.write_str("flipped")
                } else {
                    dest.write_str("from-image")
                }
            }
        }
    }

    pub mod computed_value {
        use values::computed::Angle;

        #[derive(Clone, PartialEq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum T {
            FromImage,
            AngleWithFlipped(Angle, bool),
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T::AngleWithFlipped(computed::Angle::zero(), false)
    }

    // According to CSS Content Module Level 3:
    // The computed value of the property is calculated by rounding the specified angle
    // to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
    #[inline]
    fn normalize_angle(angle: &computed::Angle) -> computed::Angle {
        let radians = angle.radians();
        let rounded_quarter_turns = (4.0 * radians / TWO_PI).round();
        let normalized_quarter_turns = (rounded_quarter_turns % 4.0 + 4.0) % 4.0;
        let normalized_radians = normalized_quarter_turns/4.0 * TWO_PI;
        computed::Angle::from_radians(normalized_radians)
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, context: &Context) -> computed_value::T {
            if let Some(ref angle) = self.angle {
                let angle = angle.to_computed_value(context);
                let normalized_angle = normalize_angle(&angle);
                computed_value::T::AngleWithFlipped(normalized_angle, self.flipped)
            } else {
                if self.flipped {
                    computed_value::T::AngleWithFlipped(computed::Angle::zero(), true)
                } else {
                    computed_value::T::FromImage
                }
            }
        }

        #[inline]
        fn from_computed_value(computed: &computed_value::T) -> Self {
            match *computed {
                computed_value::T::FromImage => SpecifiedValue { angle: None, flipped: false },
                computed_value::T::AngleWithFlipped(ref angle, flipped) => {
                    SpecifiedValue {
                        angle: Some(Angle::from_computed_value(angle)),
                        flipped: flipped,
                    }
                }
            }
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self {
                computed_value::T::FromImage => dest.write_str("from-image"),
                computed_value::T::AngleWithFlipped(angle, flipped) => {
                    try!(angle.to_css(dest));
                    if flipped {
                        try!(dest.write_str(" flipped"));
                    }
                    Ok(())
                },
            }
        }
    }

    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if input.try(|input| input.expect_ident_matching("from-image")).is_ok() {
            // Handle from-image
            Ok(SpecifiedValue { angle: None, flipped: false })
        } else {
            // Handle <angle> | <angle>? flip
            let angle = input.try(|input| Angle::parse(context, input)).ok();
            let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
            let explicit_angle = if angle.is_none() && !flipped {
                Some(Angle::zero())
            } else {
                angle
            };

            Ok(SpecifiedValue { angle: explicit_angle, flipped: flipped })
        }
    }
</%helpers:longhand>

// Used in the bottom-up flow construction traversal to avoid constructing flows for
// descendants of nodes with `display: none`.
<%helpers:longhand name="-servo-under-display-none"
                   derived_from="display"
                   products="servo"
                   animation_type="none"
                   spec="Nonstandard (internal layout use only)">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;
    use values::HasViewportPercentage;

    no_viewport_percentage!(SpecifiedValue);

    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
    pub struct SpecifiedValue(pub bool);

    pub mod computed_value {
        pub type T = super::SpecifiedValue;
    }
    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub fn get_initial_value() -> computed_value::T {
        SpecifiedValue(false)
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write {
            Ok(()) // Internal property
        }
    }

    #[inline]
    pub fn derive_from_display(context: &mut Context) {
        use super::display::computed_value::T as Display;

        if context.style().get_box().clone_display() == Display::none {
            context.mutate_style().mutate_inheritedbox()
                                  .set__servo_under_display_none(SpecifiedValue(true));
        }
    }
</%helpers:longhand>
<|endoftext|>