<commit_before>extern crate byteorder;

use std::result::*;
use std::io;
use std::io::{BufReader, ErrorKind, Read, Seek, SeekFrom, Error};
use self::byteorder::{ByteOrder, ReadBytesExt, LittleEndian};

use definitions::*;
use compress::{max_compressed_len};

// The Max Encoded Length of the Max Chunk of 65536 bytes
const MAX_BUFFER_SIZE: usize = 76_490;

pub struct Decompressor<R: Read> {
	inner: BufReader<R>,
	decoded: [u8; MAX_UNCOMPRESSED_CHUNK_LEN as usize],
	buf: [u8; MAX_BUFFER_SIZE + (CHECK_SUM_SIZE as usize)],
	// decoded[i:j] contains decoded bytes that have not yet been passed on.
	i: usize,
	j: usize,
	read_header: bool,
}

impl <R: Read> Decompressor<R> {

	pub fn new(inner: R) -> Decompressor<R> {
		Decompressor {
			inner: BufReader::new(inner),
			decoded: [0; MAX_UNCOMPRESSED_CHUNK_LEN as usize],
			buf: [0; MAX_BUFFER_SIZE + (CHECK_SUM_SIZE as usize)],
			i: 0,
			j: 0,
			read_header: false,
		}
	}
}

impl <R: Read> Read for Decompressor<R> {
	// Implement Read
	// Source (Inner) Buffer into Destination Buffer, returning how many bytes were read.
	fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
		loop {

			if self.i < self.j {
				let mut s = 0;
				for (d, l) in dst.iter_mut().zip(self.decoded.split_at(self.i).1.split_at(self.j).0.iter()) {
			        *d = *l;
			        s += 1;
			    }
			    self.i += s;
			    return Ok(s)
			}

			// Read ChunkType
			if self.inner.read(self.buf.split_at_mut(4).0).unwrap() != 4 {
				return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
			}
			let chunk_type = self.buf[0];

			if !self.read_header {
				if chunk_type != CHUNK_TYPE_STREAM_IDENTIFIER {
					return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
				}
				self.read_header = true;
			}

		}
	}
}

// decompressed_len returns the length of the decoded block.
pub fn decompressed_len(src: &[u8]) -> u64 {
	LittleEndian::read_u64(src)
}

// Decompress reads the decoded form of src into dst and returns the length
// read.
// Returns an error if dst was not large enough to hold the entire decoded
// block.
fn decompress(dst: &mut [u8], src: &mut [u8]) -> io::Result<usize> {
	// TODO: Handle Error
	let dLen = decompressed_len(src) as usize;
	// TODO FIX!!
	// For now, always assume 8 bytes used for src header length
	let mut s: usize = 8;

	if dst.len() < dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: destination buffer is too short"));
    }

    let (mut d, mut offset, mut length): (usize, usize, usize) = (0,0,0);

    while s < src.len() {
    	match src[s] & 0x03 {

			// Parse a Literal Chunk
    		TAG_LITERAL => {

    			let mut x = (src[s] >> 2) as usize;
    			match x {
	    			0...59 => s += 1,
    				60 => {
    					s += 2;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = src[s-1] as usize;
    				},
    				61 => {
    					s += 3;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-2] as usize) | ((src[s-1] as usize) << 8);
    				},
    				62 => {
    					s += 4;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-3] as usize) | ((src[s-2] as usize) << 8) | ((src[s-1] as usize) << 16);
    				},
    				63 => {
    					s += 5;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-4] as usize) | ((src[s-3] as usize) << 8) | ((src[s-2] as usize) << 16) | ((src[s-1] as usize) << 24);
    				},
    				_ => {}
    			}
    			length = x.wrapping_add(1);

    			if length as isize <= 0 {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported literal length"));
    			}
    			if length > dst.len() - d || length > src.len() - s {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    			}

    			// Copy src[s: s+length] to dst[d:0]
    			for (id, is) in dst.split_at_mut(d).1.iter_mut().zip(src.split_at(s).1.split_at(s+length).0.iter()) {
			        *id = *is;
			    }
			    d += length;
			    s += length;
			    continue;
    		},

    		// Parse a Copy1 Chunk
    		TAG_COPY_1 => {
    			s+= 2;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 4 + (src[s-2] as usize)>>2&0x7;
				offset = ((src[s-2] as usize)&0xe0<<3) | (src[s-1] as usize);
    		},

    		// Parse a Copy2 Chunk
    		TAG_COPY_2 => {
    			s+= 3;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 1 + (src[s-3] as usize)>>2;
				offset = (src[s-2] as usize) | ((src[s-1] as usize)<<8);
    		},

    		// Parse a Copy4
    		TAG_COPY_4 => {
		        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported COPY_4 tag"));
    		},

    		_ => unreachable!()
    	};

    	let end = d + length;
    	if offset > d || end > dst.len() {
	        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
	    }
    	while d < end {
    		d += 1;
    		dst[d] = dst[d-offset];
    	}
    }
    if d != dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    }
    Ok(d)
}

<commit_msg>Port Compressed Data Tag Parsing ( A bit icky)<commit_after>extern crate byteorder;
extern crate crc;

use std::result::*;
use std::io;
use std::io::{BufReader, ErrorKind, Read, Seek, SeekFrom, Error};
use self::byteorder::{ByteOrder, ReadBytesExt, LittleEndian};
use self::crc::{crc32, Hasher32};

use definitions::*;
use compress::{max_compressed_len};

// The Max Encoded Length of the Max Chunk of 65536 bytes
const MAX_BUFFER_SIZE: usize = 76_490;

pub struct Decompressor<R: Read> {
	inner: BufReader<R>,
	decoded: [u8; MAX_UNCOMPRESSED_CHUNK_LEN as usize],
	buf: [u8; MAX_BUFFER_SIZE + (CHECK_SUM_SIZE as usize)],
	// decoded[i:j] contains decoded bytes that have not yet been passed on.
	i: usize,
	j: usize,
	read_header: bool,
}

impl <R: Read> Decompressor<R> {

	pub fn new(inner: R) -> Decompressor<R> {
		Decompressor {
			inner: BufReader::new(inner),
			decoded: [0; MAX_UNCOMPRESSED_CHUNK_LEN as usize],
			buf: [0; MAX_BUFFER_SIZE + (CHECK_SUM_SIZE as usize)],
			i: 0,
			j: 0,
			read_header: false,
		}
	}
}

impl <R: Read> Read for Decompressor<R> {
	// Implement Read
	// Source (Inner) Buffer into Destination Buffer, returning how many bytes were read.
	fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
		loop {

			if self.i < self.j {
				let mut s = 0;
				for (d, l) in dst.iter_mut().zip(self.decoded.split_at(self.i).1.split_at(self.j).0.iter()) {
			        *d = *l;
			        s += 1;
			    }
			    self.i += s;
			    return Ok(s)
			}

			// Grab Chunk Header
			if self.inner.read(self.buf.split_at_mut(4).0).unwrap() != 4 {
				return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
			}

			// Read Chunk Type
			let chunk_type = self.buf[0];
			if !self.read_header {
				if chunk_type != CHUNK_TYPE_STREAM_IDENTIFIER {
					return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
				}
				self.read_header = true;
			}

			// Read Chunk Length
			let chunk_len = self.buf[1] as usize | ((self.buf[2] as usize) << 8) | ((self.buf[3] as usize) << 16);
			if chunk_len > self.buf.len() {
				return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported input"))
			}

			// The chunk types are specified at
			// https://github.com/google/snappy/blob/master/framing_format.txt
			match chunk_type {

				// Section 4.2. Compressed data (chunk type 0x00).
				CHUNK_TYPE_COMPRESSED_DATA => {
					if chunk_len < (CHECK_SUM_SIZE as usize) {
						return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
					}
					// Setup Buffer Slice with Chunk Length
					let buf: &mut [u8] = self.buf.split_at_mut(chunk_len as usize).0;

					// Read into Buffer
					if self.inner.read(buf).unwrap() != chunk_len {
						return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
					}

					// Read Checksum
					let check_sum = buf[0] as u32 | ((buf[1] as u32) << 8) | ((buf[2] as u32) << 16) | ((buf[3] as u32) << 24);

					// TODO:
					// Figure Out Borrowing and Modifying Buffer Slice instead of Copying.
					// buf = buf.split_at_mut(CHECK_SUM_SIZE as usize).1;
					let mut buf1 = buf.split_at_mut(CHECK_SUM_SIZE as usize).1;

					// Check Decompressed Length
					let n = decompressed_len(buf1.as_ref());
					if n as usize > self.decoded.len() {
						return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
					}

					// Decompress
					try!(decompress(self.decoded.as_mut(), buf1));

					// Check Checksum
					if crc32::checksum_ieee(self.decoded.split_at(n as usize).0) != check_sum {
						return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
					}

					self.i = 0;
					self.j = n as usize;
					continue;
				},
				// Section 4.3. Uncompressed data (chunk type 0x01).
				CHUNK_TYPE_UNCOMPRESSED_DATA => {},
				// Section 4.1. Stream identifier (chunk type 0xff).
				CHUNK_TYPE_STREAM_IDENTIFIER => {},

				// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
				_ => {
					return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported input"))
				}

			}

			unimplemented!()

		}
	}
}

// decompressed_len returns the length of the decoded block.
pub fn decompressed_len(src: &[u8]) -> u64 {
	LittleEndian::read_u64(src)
}

// Decompress reads the decoded form of src into dst and returns the length
// read.
// Returns an error if dst was not large enough to hold the entire decoded
// block.
fn decompress(dst: &mut [u8], src: &mut [u8]) -> io::Result<usize> {
	// TODO: Handle Error
	let dLen = decompressed_len(src) as usize;
	// TODO FIX!!
	// For now, always assume 8 bytes used for src header length
	let mut s: usize = 8;

	if dst.len() < dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: destination buffer is too short"));
    }

    let (mut d, mut offset, mut length): (usize, usize, usize) = (0,0,0);

    while s < src.len() {
    	match src[s] & 0x03 {

			// Parse a Literal Chunk
    		TAG_LITERAL => {

    			let mut x = (src[s] >> 2) as usize;
    			match x {
	    			0...59 => s += 1,
    				60 => {
    					s += 2;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = src[s-1] as usize;
    				},
    				61 => {
    					s += 3;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-2] as usize) | ((src[s-1] as usize) << 8);
    				},
    				62 => {
    					s += 4;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-3] as usize) | ((src[s-2] as usize) << 8) | ((src[s-1] as usize) << 16);
    				},
    				63 => {
    					s += 5;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-4] as usize) | ((src[s-3] as usize) << 8) | ((src[s-2] as usize) << 16) | ((src[s-1] as usize) << 24);
    				},
    				_ => {}
    			}
    			length = x.wrapping_add(1);

    			if length as isize <= 0 {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported literal length"));
    			}
    			if length > dst.len() - d || length > src.len() - s {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    			}

    			// Copy src[s: s+length] to dst[d:0]
    			for (id, is) in dst.split_at_mut(d).1.iter_mut().zip(src.split_at(s).1.split_at(s+length).0.iter()) {
			        *id = *is;
			    }
			    d += length;
			    s += length;
			    continue;
    		},

    		// Parse a Copy1 Chunk
    		TAG_COPY_1 => {
    			s+= 2;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 4 + (src[s-2] as usize)>>2&0x7;
				offset = ((src[s-2] as usize)&0xe0<<3) | (src[s-1] as usize);
    		},

    		// Parse a Copy2 Chunk
    		TAG_COPY_2 => {
    			s+= 3;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 1 + (src[s-3] as usize)>>2;
				offset = (src[s-2] as usize) | ((src[s-1] as usize)<<8);
    		},

    		// Parse a Copy4
    		TAG_COPY_4 => {
		        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported COPY_4 tag"));
    		},

    		_ => unreachable!()
    	};

    	let end = d + length;
    	if offset > d || end > dst.len() {
	        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
	    }
    	while d < end {
    		d += 1;
    		dst[d] = dst[d-offset];
    	}
    }
    if d != dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    }
    Ok(d)
}

<|endoftext|>


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

use std::io;
use rustty::{
    Terminal,
    Event,
    HasSize,
    CellAccessor,
    Cell,
    Attr,
    Color,
};

use rustty::ui::{
    Painter,
    Dialog,
    Widget,
    Alignable,
    HorizontalAlign,
    VerticalAlign
};

fn create_optiondlg(length: usize) -> Dialog {
    let mut optiondlg = Dialog::new(length as usize, 4);
    let inc_label = "space --> play/pause";
    //let dec_label = "p --> pause song";
    let q_label = "x     --> exit program";
    let n_label = "n --> next song";
    let pp_label = "p --> prev song";
    let inc_pos = optiondlg.window().halign_line(inc_label, HorizontalAlign::Left, 1);
    //let dec_pos = optiondlg.window().halign_line(dec_label, HorizontalAlign::Left, 1);
    let q_pos = optiondlg.window().halign_line(q_label, HorizontalAlign::Left, 1);
    let n_pos = optiondlg.window().halign_line(n_label, HorizontalAlign::Middle, 1);
    let pp_pos = optiondlg.window().halign_line(pp_label, HorizontalAlign::Middle, 1);

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

pub enum UIResult {
    Play,
    Pause,
    Next,
    Exit,
    Error,
    PlayPause,
    Previous,
    NA,
}

pub struct UI {
    stdin: io::Stdin,
    term: Terminal,
    optiondlg: Dialog,
    canvas: Widget,
    list_canvas: Widget,
    length: usize,
    height: usize,
    songs: Vec<String>,
    currentsong: String,
}
//TODO, convert incoming strings from path to cool string
impl UI {
    pub fn new(songs: Vec<String>) -> UI {
        // TODO this is where i should convert the vec<Path> to string
        // by running the method path_to_string on ever single one.
        // (implement path_to_string manually)
        //  but only once nathan implements Paths
        // Create our terminal, dialog window and main canvasa
        let currentsong = "".to_string();
        let mut term = Terminal::new().unwrap();
        let length = term.cols();
        let height = term.rows();
        // aligns everything
        let mut optiondlg = create_optiondlg(length);
        let mut canvas = Widget::new(length as usize, 2);
        let mut list_canvas = Widget::new(length as usize, songs.len());
        optiondlg
            .window_mut()
            .align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 0);
        canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 4);
        list_canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Top, 0);

        UI {stdin: io::stdin(),
        term: term,
        optiondlg: optiondlg,
        canvas: canvas,
        list_canvas: list_canvas,
        length: length,
        height: height,
        songs: songs,
        currentsong: currentsong,
        }
    }

    fn length_checker(&mut self) {
        let last_length = self.length;
        let last_pos = (self.length, self.height);
        self.length = self.term.cols();
        self.height =  self.term.rows();
        match last_pos == (self.term.cols(), self.term.rows()){
            true => {},
            false => {
                self.term.clear();
                self.optiondlg = create_optiondlg(self.length);
                self.canvas = Widget::new(self.length as usize, 2);
                self.optiondlg
                    .window_mut()
                    .align(&self.term,
                           HorizontalAlign::Middle,
                           VerticalAlign::Bottom, 0);
                self.canvas.align(&self.term,
                                  HorizontalAlign::Middle,
                                  VerticalAlign::Bottom, 6);
                self.list_canvas.align(&self.term, HorizontalAlign::Left,
                                       VerticalAlign::Top, 0);
            },
        }

    }
    pub fn manage_ui(&mut self, songname :String,
                     time: i32, totaltime: i32)
        -> UIResult {

            while let Some(Event::Key(ch)) = self.term.get_event(0).unwrap() {
                match ch {
                    ' ' => return UIResult::PlayPause,
                    'p' => return UIResult::Previous,
                    'n' => return UIResult::Next,
                    'x' => return UIResult::Error,
                    _  => return UIResult::NA,
                }
            }
            self.currentsong = songname.clone();
            let length_i32 = self.length as i32;
            let tnum = time.to_string().len() + songname.len() + totaltime.to_string().len();
            let mut num = tnum as i32;
            num = length_i32 - num - 8;
            num = num / 2;
            let mut append = vec![' '; num as usize].into_iter().collect::<String>();
            let append2 = match length_i32.wrapping_rem(2) {
                0 => append.clone(),
                1 => append.clone() + " ",
                _ => unreachable!(),
            };
            let display = format!("--{}{}--{}--{}{}--", time, append,
                                  songname, append2, totaltime);

            let v: Vec<char> = display.chars().collect();
            let (cols, rows) = self.canvas.size();
            let (cols, rows) = (cols as isize, rows as isize);
            let mut num_x = 8.0;
            let mut num_not = 4.0;

            if totaltime == 0 { //TODO this is bad. fix it
                num_x = 0.0;
            }
            else {
                num_x = time as f32 / totaltime as f32;
            }
            num_x = num_x * length_i32 as f32;
            num_not = length_i32 as f32 - num_x as f32;

            let mut va = vec!['x'; num_x.round() as usize];
            let mut ev = vec!['-'; num_not.round() as usize];
            for x in ev {
                va.push(x);
            }
            //v.append(&mut va); unstable
            for i in 0..cols*rows {
                let y = i as isize / cols;
                let x = i as isize % cols;
                let fep ='*';
                let mut cell = self.canvas.get_mut(x as usize, y as usize).unwrap();
                match y {
                    0 => cell.set_ch(*v.get(x as usize).unwrap_or_else(|| &fep)),
                    1 => cell.set_ch(*va.get(x as usize).unwrap_or_else(|| &fep)),
                    _ => cell.set_ch(' '),
                };
            }

            self.length_checker();
            self.second_panel();
            self.canvas.draw_into(&mut self.term);
            self.list_canvas.draw_into(&mut self.term);
            self.optiondlg.window().draw_into(&mut self.term);
            self.term.swap_buffers().unwrap();
            return UIResult::NA;
        }

    fn second_panel(&mut self) {
        let cell = Cell::with_style(Color::Black, Color::Red, Attr::Default);
        let cellother = Cell::with_style(Color::Default, Color::Default, Attr::Default);
        let (cols, rows) = self.list_canvas.size();
        let mut counter = 0;
        let fep = "*".to_string();
        //println!("{}", self.songs.get(counter).unwrap_or_else(|| &fep));

        for i in 0..rows {
            let song = self.songs.get(counter).unwrap_or_else(|| &fep);
            self.list_canvas
                .printline_with_cell(0, counter as usize, song, match &self.currentsong == song {
                    true => cell,
                    false => cellother,
                });
            counter = counter + 1;
        }
    }
}
<commit_msg>Minor formatting changes.<commit_after>//  Copyright 2015 Nathanael Merrill
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

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

fn create_optiondlg(length: usize) -> Dialog {
    let mut optiondlg = Dialog::new(length as usize, 4);

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

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

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

pub enum UIResult {
    Play,
    Pause,
    Next,
    Exit,
    Error,
    PlayPause,
    Previous,
    NA,
}

pub struct UI {
    stdin: io::Stdin,
    term: Terminal,
    optiondlg: Dialog,
    canvas: Widget,
    list_canvas: Widget,
    length: usize,
    height: usize,
    songs: Vec<String>,
    currentsong: String,
}
//TODO, convert incoming strings from path to cool string
impl UI {
    pub fn new(songs: Vec<String>) -> UI {
        // TODO this is where i should convert the vec<Path> to string
        // by running the method path_to_string on ever single one.
        // (implement path_to_string manually)
        //  but only once nathan implements Paths
        // Create our terminal, dialog window and main canvasa
        let currentsong = "".to_string();
        let mut term = Terminal::new().unwrap();
        let length = term.cols();
        let height = term.rows();
        // aligns everything
        let mut optiondlg = create_optiondlg(length);
        let mut canvas = Widget::new(length as usize, 2);
        let mut list_canvas = Widget::new(length as usize, songs.len());
        optiondlg
            .window_mut()
            .align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 0);
        canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Bottom, 4);
        list_canvas.align(&term, HorizontalAlign::Middle, VerticalAlign::Top, 0);

        UI {stdin: io::stdin(),
            term: term,
            optiondlg: optiondlg,
            canvas: canvas,
            list_canvas: list_canvas,
            length: length,
            height: height,
            songs: songs,
            currentsong: currentsong,
        }
    }

    fn length_checker(&mut self) {
        let last_length = self.length;
        let last_pos = (self.length, self.height);
        self.length = self.term.cols();
        self.height =  self.term.rows();
        match last_pos == (self.term.cols(), self.term.rows()){
            true => {},
            false => {
                self.term.clear();
                self.optiondlg = create_optiondlg(self.length);
                self.canvas = Widget::new(self.length as usize, 2);
                self.optiondlg
                    .window_mut()
                    .align(&self.term,
                           HorizontalAlign::Middle,
                           VerticalAlign::Bottom, 0);
                self.canvas.align(&self.term,
                                  HorizontalAlign::Middle,
                                  VerticalAlign::Bottom, 6);
                self.list_canvas.align(&self.term, HorizontalAlign::Left,
                                       VerticalAlign::Top, 0);
            },
        }

    }
    pub fn manage_ui(&mut self, songname :String,
                     time: i32, totaltime: i32) -> UIResult {
        //TODO: The rest of this function won't run if there is input
        //this is not really ideal
        while let Some(Event::Key(ch)) = self.term.get_event(0).unwrap() {
            match ch {
                ' ' => return UIResult::PlayPause,
                'p' => return UIResult::Previous,
                'n' => return UIResult::Next,
                'x' => return UIResult::Error,
                _  => return UIResult::NA,
            }
        }
        self.currentsong = songname.clone();
        let length_i32 = self.length as i32;
        let tnum = time.to_string().len() + songname.len() + totaltime.to_string().len();
        let mut num = tnum as i32;
        num = length_i32 - num - 8;
        num = num / 2;
        let mut append = vec![' '; num as usize].into_iter().collect::<String>();
        let append2 = match length_i32.wrapping_rem(2) {
            0 => append.clone(),
            1 => append.clone() + " ",
            _ => unreachable!(),
        };
        let display = format!("--{}{}--{}--{}{}--", time, append,
                              songname, append2, totaltime);

        let v: Vec<char> = display.chars().collect();
        let (cols, rows) = self.canvas.size();
        let (cols, rows) = (cols as isize, rows as isize);
        let mut num_x = 8.0;
        let mut num_not = 4.0;

        if totaltime == 0 { //TODO this is bad. fix it
            num_x = 0.0;
        }
        else {
            num_x = time as f32 / totaltime as f32;
        }
        num_x = num_x * length_i32 as f32;
        num_not = length_i32 as f32 - num_x as f32;

        let mut va = vec!['x'; num_x.round() as usize];
        let mut ev = vec!['-'; num_not.round() as usize];
        for x in ev {
            va.push(x);
        }
        //v.append(&mut va); unstable
        for i in 0..cols*rows {
            let y = i as isize / cols;
            let x = i as isize % cols;
            let fep ='*';
            let mut cell = self.canvas.get_mut(x as usize, y as usize).unwrap();
            match y {
                0 => cell.set_ch(*v.get(x as usize).unwrap_or_else(|| &fep)),
                1 => cell.set_ch(*va.get(x as usize).unwrap_or_else(|| &fep)),
                _ => cell.set_ch(' '),
            };
        }

        self.length_checker();
        self.second_panel();
        self.canvas.draw_into(&mut self.term);
        self.list_canvas.draw_into(&mut self.term);
        self.optiondlg.window().draw_into(&mut self.term);
        self.term.swap_buffers().unwrap();
        return UIResult::NA;
    }

    fn second_panel(&mut self) {
        let cell = Cell::with_style(Color::Black, Color::Red, Attr::Default);
        let cellother = Cell::with_style(Color::Default, Color::Default, Attr::Default);
        let (cols, rows) = self.list_canvas.size();
        let mut counter = 0;
        let fep = "*".to_string();
        //println!("{}", self.songs.get(counter).unwrap_or_else(|| &fep));

        for i in 0..rows {
            let song = self.songs.get(counter).unwrap_or_else(|| &fep);
            self.list_canvas
                .printline_with_cell(0, counter as usize, song, match &self.currentsong == song {
                    true => cell,
                    false => cellother,
                });
            counter = counter + 1;
        }
    }
}
<|endoftext|>


<commit_before>//! Module for working with derivatives.
//!
//! This module has functions for estimating and evaluating
//! derivatives of functions with one or more variables.

use super::func::*;

const EPSILON: f64 = 10.0e-8;

/// Return a `function` estimating the `n`th derivative of `f`.
///
/// TODO
pub fn nth_derivative(n: u64, f: &Function) -> Function {

    let f_copy = f.clone();
    let deriv: Function = func!(
        move |x: f64| {
            (f_copy(x + EPSILON) - f_copy(x)) / EPSILON
    });

    if n != 1 {
        return nth_derivative(n - 1, &deriv);
    }

    deriv
}

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

#[test]
    fn t_nth_derivative() {
        let f: Function = func!(|x: f64| x * x);

        let deriv = nth_derivative(1, &f.clone());

        let deriv_2 = nth_derivative(2, &f.clone());
    }
}
<commit_msg>improve nth_derivative()<commit_after>//! Module for working with derivatives.
//!
//! This module has functions for estimating and evaluating
//! derivatives of functions with one or more variables.

pub use super::func::*;

/// The value used for `h` in derivative estimates.
///
/// This value is chosen so as to offer the best accuracy,
/// it is a compromise between the increase in accuracy caused
/// by having an `h` closer to zero, and the decrease decrease
/// in accuracy caused by floating point imprecision with very
/// small values.
pub const EPSILON: f64 = 10.0e-7;

/// Return a `Function` estimating the `n`th derivative of `f`.
///
/// This function will return a `Function` that estimates the
/// `n`th derivative of `f` using the limit definition of the
/// derivative:
///
/// ```text
///                     f(x + h) - f(x)
/// f'(x) = lim         ---------------
///         h -> 0             h
/// ```
///
/// Where `h` is equal to `EPSILON`. See the documentation for
/// `EPSILON` for more information.
///
/// This function will use recursion to provide derivatives for `n > 1`.
///
/// It is important to note that the inaccuracy of the derivative
/// estimates compound each other, the higher `n` is, the less precise
/// the resulting function will be!
///
/// If `n = 0`, then a `clone()` of `f` is returned.
///
/// Examples
///
/// ```
/// #[macro_use] extern crate reikna;
/// # fn main() {
/// use reikna::derivative::*;
///
/// let f = func![|x| x * x];
///
/// let first_deriv = nth_derivative(1, &f);
/// let second_deriv = nth_derivative(2, &f);
///
/// println!("f(5)   = {}", f(5.0));
/// println!("f'(5)  = {}", first_deriv(5.0));
/// println!("f''(5) = {}", second_deriv(5.0));
/// # }
///
/// ```
///
/// Outputs:
///
/// ``` text
/// f(5)   = 25
/// f'(5)  = 10.00000100148668
/// f''(5) = 2.000177801164682
/// ```
pub fn nth_derivative(n: u64, f: &Function) -> Function {

    let f_copy = f.clone();
    let deriv: Function = func!(
        move |x: f64| {
            (f_copy(x + EPSILON) - f_copy(x)) / EPSILON
    });

    match n {
        0 => f.clone(),
        1 => deriv,
        _ => nth_derivative(n - 1, & deriv),
    }
}

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

#[test]
    fn t_nth_derivative() {
        let f = func!(|x: f64| x * x);
        assert_eq!(f(2.0), 4.0);
    }
}
<|endoftext|>


<commit_before>use std::default::Default;

use docopt::Docopt;

pub fn parse() -> Args {
    Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit())
}

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: dora [options] <file>
       dora (--version | --help)

Options:
    -h, --help  Shows this text
    --version   Shows version
    --emit-ast  Emits AST to stdout
";

#[derive(Debug, RustcDecodable)]
pub struct Args {
    pub arg_file: String,
    pub flag_emit_ast: bool,
    pub flag_version: bool
}

impl Default for Args {
    fn default() -> Args {
        Args {
            arg_file: "".into(),
            flag_emit_ast: false,
            flag_version: false
        }
    }
}
<commit_msg>driver: added --emit-asm option<commit_after>use std::default::Default;

use docopt::Docopt;

pub fn parse() -> Args {
    Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit())
}

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: dora [options] <file>
       dora (--version | --help)

Options:
    -h, --help  Shows this text
    --version   Shows version
    --emit-ast  Emits AST to stdout
    --emit-asm  Emits ASM code to stdout
";

#[derive(Debug, RustcDecodable)]
pub struct Args {
    pub arg_file: String,
    pub flag_emit_ast: bool,
    pub flag_emit_asm: bool,
    pub flag_version: bool
}

impl Default for Args {
    fn default() -> Args {
        Args {
            arg_file: "".into(),
            flag_emit_ast: false,
            flag_emit_asm: false,
            flag_version: false
        }
    }
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time > t,
                TimeManagement::MoveTimeHint(t) => self.searched_time > t,
                TimeManagement::Nodes(n) => self.searched_nodes > n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            // let rep_type = match report {
            //     search::Report::Progress { searched_nodes, .. } => format!("progress {}", searched_nodes),
            //     search::Report::Done { .. } => "done".to_string(),
            // };
            // self.replies.push(EngineReply::Info(vec![
            //     ("string".to_string(), rep_type),
            // ]));
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            // We are done with the old depth, so we
                            // extract the PV, the value, and the
                            // score from the transposition table.
                            let mut prev_move = None;
                            let mut p = self.position.clone();
                            let mut value = -20000;
                            let mut bound = BOUND_LOWER;
                            let mut pv = Vec::new();
                            while let Some(entry) = self.tt.probe(p.hash()) {
                                if pv.len() < self.curr_depth as usize {
                                    if let Some(m) = prev_move {
                                        pv.push(m);
                                    }
                                    value = if pv.len() & 1 == 0 {
                                        entry.value()
                                    } else {
                                        -entry.value()
                                    };
                                    bound = entry.bound();
                                    if bound == BOUND_EXACT {
                                        if let Some(m) = p.try_move_digest(entry.move16()) {
                                            if p.do_move(m) {
                                                prev_move = Some(m);
                                                continue;
                                            }
                                        }
                                    }
                                }
                                break;
                            }

                            // Send the extracted info to the GUI.
                            let score_suffix = match bound {
                                BOUND_EXACT => "",
                                BOUND_UPPER => " upperbound",
                                BOUND_LOWER => " lowerbound",
                                _ => panic!("unexpected bound type"),
                            };
                            let nps = if self.searched_time == 0 {
                                0
                            } else {
                                1000 * searched_nodes / self.searched_time
                            };
                            let mut pv_string = String::new();
                            for m in pv {
                                pv_string.push(' ');
                                pv_string.push_str(&m.notation());
                            }
                            self.replies.push(EngineReply::Info(vec![
                                ("depth".to_string(), format!("{}", self.curr_depth)),
                                ("score".to_string(), format!("cp {}{}", value, score_suffix)),
                                ("time".to_string(), format!("{}", self.searched_time)),
                                ("nodes".to_string(), format!("{}", searched_nodes)),
                                ("nps".to_string(), format!("{}", nps)),
                                ("pv".to_string(), pv_string),
                            ]));

                            // Update the old depth value.
                            self.curr_depth = depth;
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        self.stop()
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>removed commented code<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time > t,
                TimeManagement::MoveTimeHint(t) => self.searched_time > t,
                TimeManagement::Nodes(n) => self.searched_nodes > n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            // We are done with the old depth, so we
                            // extract the PV, the value, and the
                            // score from the transposition table.
                            let mut prev_move = None;
                            let mut p = self.position.clone();
                            let mut value = -20000;
                            let mut bound = BOUND_LOWER;
                            let mut pv = Vec::new();
                            while let Some(entry) = self.tt.probe(p.hash()) {
                                if pv.len() < self.curr_depth as usize {
                                    if let Some(m) = prev_move {
                                        pv.push(m);
                                    }
                                    value = if pv.len() & 1 == 0 {
                                        entry.value()
                                    } else {
                                        -entry.value()
                                    };
                                    bound = entry.bound();
                                    if bound == BOUND_EXACT {
                                        if let Some(m) = p.try_move_digest(entry.move16()) {
                                            if p.do_move(m) {
                                                prev_move = Some(m);
                                                continue;
                                            }
                                        }
                                    }
                                }
                                break;
                            }

                            // Send the extracted info to the GUI.
                            let score_suffix = match bound {
                                BOUND_EXACT => "",
                                BOUND_UPPER => " upperbound",
                                BOUND_LOWER => " lowerbound",
                                _ => panic!("unexpected bound type"),
                            };
                            let nps = if self.searched_time == 0 {
                                0
                            } else {
                                1000 * searched_nodes / self.searched_time
                            };
                            let mut pv_string = String::new();
                            for m in pv {
                                pv_string.push(' ');
                                pv_string.push_str(&m.notation());
                            }
                            self.replies.push(EngineReply::Info(vec![
                                ("depth".to_string(), format!("{}", self.curr_depth)),
                                ("score".to_string(), format!("cp {}{}", value, score_suffix)),
                                ("time".to_string(), format!("{}", self.searched_time)),
                                ("nodes".to_string(), format!("{}", searched_nodes)),
                                ("nps".to_string(), format!("{}", nps)),
                                ("pv".to_string(), pv_string),
                            ]));

                            // Update the old depth value.
                            self.curr_depth = depth;
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        self.stop()
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

use crate::raw_fair_mutex::RawFairMutex;
use lock_api;

/// A always fair mutual exclusion primitive useful for protecting shared data
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can also be statically initialized or created via a `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// This mutex is always fair.
///
/// # Differences from the standard library `FairMutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of space, whereas the standard library boxes the
///   `FairMutex` due to platform limitations.
/// - Can be statically constructed (requires the `const_fn` nightly feature).
/// - Does not require any drop glue when dropped.
/// - Inline fast path for the uncontended case.
/// - Efficient handling of micro-contention using adaptive spinning.
/// - Allows raw locking & unlocking without a guard.
/// - Supports eventual fairness so that the mutex is fair on average.
/// - Optionally allows making the mutex fair by calling `FairMutexGuard::unlock_fair`.
///
/// # Examples
///
/// ```
/// use parking_lot::FairMutex;
/// use std::sync::{Arc, mpsc::channel};
/// use std::thread;
///
/// const N: usize = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
/// // let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data inside
/// // the Arc is protected with a mutex.
/// let data = Arc::new(FairMutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..10 {
///     let (data, tx) = (Arc::clone(&data), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its
/// `Deref` and `DerefMut` implementations.
pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;

/// An RAII mutex guard returned by `FairMutexGuard::map`, which can point to a
/// subfield of the protected data.
///
/// The main difference between `MappedFairMutexGuard` and `FairMutexGuard` is that the
/// former doesn't support temporarily unlocking and re-locking, since that
/// could introduce soundness issues if the locked object is modified by another
/// thread.
pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;

#[cfg(test)]
mod tests {
    use crate::FairMutex;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::thread;

    #[cfg(feature = "serde")]
    use bincode::{deserialize, serialize};

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    #[test]
    fn smoke() {
        let m = FairMutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        const J: u32 = 1000;
        const K: u32 = 3;

        let m = Arc::new(FairMutex::new(0));

        fn inc(m: &FairMutex<u32>) {
            for _ in 0..J {
                *m.lock() += 1;
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(*m.lock(), J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = FairMutex::new(());
        *m.try_lock().unwrap() = ();
    }

    #[test]
    fn test_into_inner() {
        let m = FairMutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = FairMutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = FairMutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = Arc::new(FairMutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || {
            struct Unwinder {
                i: Arc<FairMutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &FairMutex<[i32]> = &FairMutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_sync() {
        fn sync<T: Sync>(_: T) {}

        let mutex = FairMutex::new(());
        sync(mutex.lock());
    }

    #[test]
    fn test_mutex_debug() {
        let mutex = FairMutex::new(vec![0u8, 10]);

        assert_eq!(format!("{:?}", mutex), "FairMutex { data: [0, 10] }");
        let _lock = mutex.lock();
        assert_eq!(format!("{:?}", mutex), "FairMutex { data: <locked> }");
    }

    #[cfg(feature = "serde")]
    #[test]
    fn test_serde() {
        let contents: Vec<u8> = vec![0, 1, 2];
        let mutex = FairMutex::new(contents.clone());

        let serialized = serialize(&mutex).unwrap();
        let deserialized: FairMutex<Vec<u8>> = deserialize(&serialized).unwrap();

        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
        assert_eq!(contents, *(deserialized.lock()));
    }
}
<commit_msg>Fix FairMutex debug<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

use crate::raw_fair_mutex::RawFairMutex;
use lock_api;

/// A always fair mutual exclusion primitive useful for protecting shared data
///
/// This mutex will block threads waiting for the lock to become available. The
/// mutex can also be statically initialized or created via a `new`
/// constructor. Each mutex has a type parameter which represents the data that
/// it is protecting. The data can only be accessed through the RAII guards
/// returned from `lock` and `try_lock`, which guarantees that the data is only
/// ever accessed when the mutex is locked.
///
/// This mutex is always fair.
///
/// # Differences from the standard library `FairMutex`
///
/// - No poisoning, the lock is released normally on panic.
/// - Only requires 1 byte of space, whereas the standard library boxes the
///   `FairMutex` due to platform limitations.
/// - Can be statically constructed (requires the `const_fn` nightly feature).
/// - Does not require any drop glue when dropped.
/// - Inline fast path for the uncontended case.
/// - Efficient handling of micro-contention using adaptive spinning.
/// - Allows raw locking & unlocking without a guard.
/// - Supports eventual fairness so that the mutex is fair on average.
/// - Optionally allows making the mutex fair by calling `FairMutexGuard::unlock_fair`.
///
/// # Examples
///
/// ```
/// use parking_lot::FairMutex;
/// use std::sync::{Arc, mpsc::channel};
/// use std::thread;
///
/// const N: usize = 10;
///
/// // Spawn a few threads to increment a shared variable (non-atomically), and
/// // let the main thread know once all increments are done.
/// //
/// // Here we're using an Arc to share memory among threads, and the data inside
/// // the Arc is protected with a mutex.
/// let data = Arc::new(FairMutex::new(0));
///
/// let (tx, rx) = channel();
/// for _ in 0..10 {
///     let (data, tx) = (Arc::clone(&data), tx.clone());
///     thread::spawn(move || {
///         // The shared state can only be accessed once the lock is held.
///         // Our non-atomic increment is safe because we're the only thread
///         // which can access the shared state when the lock is held.
///         let mut data = data.lock();
///         *data += 1;
///         if *data == N {
///             tx.send(()).unwrap();
///         }
///         // the lock is unlocked here when `data` goes out of scope.
///     });
/// }
///
/// rx.recv().unwrap();
/// ```
pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
///
/// The data protected by the mutex can be accessed through this guard via its
/// `Deref` and `DerefMut` implementations.
pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;

/// An RAII mutex guard returned by `FairMutexGuard::map`, which can point to a
/// subfield of the protected data.
///
/// The main difference between `MappedFairMutexGuard` and `FairMutexGuard` is that the
/// former doesn't support temporarily unlocking and re-locking, since that
/// could introduce soundness issues if the locked object is modified by another
/// thread.
pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;

#[cfg(test)]
mod tests {
    use crate::FairMutex;
    use std::sync::atomic::{AtomicUsize, Ordering};
    use std::sync::mpsc::channel;
    use std::sync::Arc;
    use std::thread;

    #[cfg(feature = "serde")]
    use bincode::{deserialize, serialize};

    #[derive(Eq, PartialEq, Debug)]
    struct NonCopy(i32);

    #[test]
    fn smoke() {
        let m = FairMutex::new(());
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn lots_and_lots() {
        const J: u32 = 1000;
        const K: u32 = 3;

        let m = Arc::new(FairMutex::new(0));

        fn inc(m: &FairMutex<u32>) {
            for _ in 0..J {
                *m.lock() += 1;
            }
        }

        let (tx, rx) = channel();
        for _ in 0..K {
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
            let tx2 = tx.clone();
            let m2 = m.clone();
            thread::spawn(move || {
                inc(&m2);
                tx2.send(()).unwrap();
            });
        }

        drop(tx);
        for _ in 0..2 * K {
            rx.recv().unwrap();
        }
        assert_eq!(*m.lock(), J * K * 2);
    }

    #[test]
    fn try_lock() {
        let m = FairMutex::new(());
        *m.try_lock().unwrap() = ();
    }

    #[test]
    fn test_into_inner() {
        let m = FairMutex::new(NonCopy(10));
        assert_eq!(m.into_inner(), NonCopy(10));
    }

    #[test]
    fn test_into_inner_drop() {
        struct Foo(Arc<AtomicUsize>);
        impl Drop for Foo {
            fn drop(&mut self) {
                self.0.fetch_add(1, Ordering::SeqCst);
            }
        }
        let num_drops = Arc::new(AtomicUsize::new(0));
        let m = FairMutex::new(Foo(num_drops.clone()));
        assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        {
            let _inner = m.into_inner();
            assert_eq!(num_drops.load(Ordering::SeqCst), 0);
        }
        assert_eq!(num_drops.load(Ordering::SeqCst), 1);
    }

    #[test]
    fn test_get_mut() {
        let mut m = FairMutex::new(NonCopy(10));
        *m.get_mut() = NonCopy(20);
        assert_eq!(m.into_inner(), NonCopy(20));
    }

    #[test]
    fn test_mutex_arc_nested() {
        // Tests nested mutexes and access
        // to underlying data.
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = Arc::new(FairMutex::new(arc));
        let (tx, rx) = channel();
        let _t = thread::spawn(move || {
            let lock = arc2.lock();
            let lock2 = lock.lock();
            assert_eq!(*lock2, 1);
            tx.send(()).unwrap();
        });
        rx.recv().unwrap();
    }

    #[test]
    fn test_mutex_arc_access_in_unwind() {
        let arc = Arc::new(FairMutex::new(1));
        let arc2 = arc.clone();
        let _ = thread::spawn(move || {
            struct Unwinder {
                i: Arc<FairMutex<i32>>,
            }
            impl Drop for Unwinder {
                fn drop(&mut self) {
                    *self.i.lock() += 1;
                }
            }
            let _u = Unwinder { i: arc2 };
            panic!();
        })
        .join();
        let lock = arc.lock();
        assert_eq!(*lock, 2);
    }

    #[test]
    fn test_mutex_unsized() {
        let mutex: &FairMutex<[i32]> = &FairMutex::new([1, 2, 3]);
        {
            let b = &mut *mutex.lock();
            b[0] = 4;
            b[2] = 5;
        }
        let comp: &[i32] = &[4, 2, 5];
        assert_eq!(&*mutex.lock(), comp);
    }

    #[test]
    fn test_mutexguard_sync() {
        fn sync<T: Sync>(_: T) {}

        let mutex = FairMutex::new(());
        sync(mutex.lock());
    }

    #[test]
    fn test_mutex_debug() {
        let mutex = FairMutex::new(vec![0u8, 10]);

        assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }");
        let _lock = mutex.lock();
        assert_eq!(format!("{:?}", mutex), "Mutex { data: <locked> }");
    }

    #[cfg(feature = "serde")]
    #[test]
    fn test_serde() {
        let contents: Vec<u8> = vec![0, 1, 2];
        let mutex = FairMutex::new(contents.clone());

        let serialized = serialize(&mutex).unwrap();
        let deserialized: FairMutex<Vec<u8>> = deserialize(&serialized).unwrap();

        assert_eq!(*(mutex.lock()), *(deserialized.lock()));
        assert_eq!(contents, *(deserialized.lock()));
    }
}
<|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" />
<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>

<% data.new_style_struct("Border", inherited=False,
                   additional_methods=[Method("border_" + side + "_has_nonzero_width",
                                              "bool") for side in ["top", "right", "bottom", "left"]]) %>
<%
    def maybe_logical_spec(side, kind):
        if side[1]: # if it is logical
            return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
        else:
            return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
%>
% for side in ALL_SIDES:
    <%
        side_name = side[0]
        is_logical = side[1]
    %>
    ${helpers.predefined_type(
        "border-%s-color" % side_name, "Color",
        "computed_value::T::currentcolor()",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
        spec=maybe_logical_spec(side, "color"),
        animation_value_type="AnimatedColor",
        logical=is_logical,
        logical_group="border-color",
        allow_quirks=not is_logical,
        flags="APPLIES_TO_FIRST_LETTER",
        ignored_when_colors_disabled=True,
    )}

    ${helpers.predefined_type(
        "border-%s-style" % side_name, "BorderStyle",
        "specified::BorderStyle::None",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
        spec=maybe_logical_spec(side, "style"),
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="discrete" if not is_logical else "none",
        logical=is_logical,
        logical_group="border-style",
        needs_context=False,
    )}

    ${helpers.predefined_type(
        "border-%s-width" % side_name,
        "BorderSideWidth",
        "::values::computed::NonNegativeLength::new(3.)",
        computed_type="::values::computed::NonNegativeLength",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
        spec=maybe_logical_spec(side, "width"),
        animation_value_type="NonNegativeLength",
        logical=is_logical,
        logical_group="border-width",
        flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
        allow_quirks=not is_logical,
        servo_restyle_damage="reflow rebuild_and_reflow_inline"
    )}
% endfor

${helpers.gecko_keyword_conversion(
    Keyword('border-style',
    "none solid double dotted dashed hidden groove ridge inset outset"),
    type="::values::specified::BorderStyle",
)}

// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
    ${helpers.predefined_type(
        "border-" + corner + "-radius",
        "BorderCornerRadius",
        "computed::BorderCornerRadius::zero()",
        "parse",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
        boxed=True,
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="BorderCornerRadius",
    )}
% endfor

${helpers.single_keyword(
    "box-decoration-break",
    "slice clone",
    gecko_enum_prefix="StyleBoxDecorationBreak",
    gecko_pref="layout.css.box-decoration-break.enabled",
    spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
    products="gecko",
    animation_value_type="discrete",
)}

${helpers.single_keyword(
    "-moz-float-edge",
    "content-box margin-box",
    gecko_ffi_name="mFloatEdge",
    gecko_enum_prefix="StyleFloatEdge",
    products="gecko",
    spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
    animation_value_type="discrete",
)}

${helpers.predefined_type(
    "border-image-source",
    "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
    vector=False,
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-outset",
    "LengthOrNumberRect",
    parse_method="parse_non_negative",
    initial_value="computed::LengthOrNumberRect::all(computed::LengthOrNumber::zero())",
    initial_specified_value="specified::LengthOrNumberRect::all(specified::LengthOrNumber::zero())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-repeat",
    "BorderImageRepeat",
    "computed::BorderImageRepeat::stretch()",
    initial_specified_value="specified::BorderImageRepeat::stretch()",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
    flags="APPLIES_TO_FIRST_LETTER",
)}

${helpers.predefined_type(
    "border-image-width",
    "BorderImageWidth",
    initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
    initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-slice",
    "BorderImageSlice",
    initial_value="computed::NumberOrPercentage::Percentage(computed::Percentage(1.)).into()",
    initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage::new(1.)).into()",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

#[cfg(feature = "gecko")]
impl ::values::computed::BorderImageWidth {
    pub fn to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides) {
        use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
        use gecko::values::GeckoStyleCoordConvertible;
        use values::generics::border::BorderImageSideWidth;

        % for i in range(0, 4):
        match self.${i} {
            BorderImageSideWidth::Auto => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Auto)
            },
            BorderImageSideWidth::Length(l) => {
                l.to_gecko_style_coord(&mut sides.data_at_mut(${i}))
            },
            BorderImageSideWidth::Number(n) => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Factor(n))
            },
        }
        % endfor
    }

    pub fn from_gecko_rect(
        sides: &::gecko_bindings::structs::nsStyleSides,
    ) -> Option<::values::computed::BorderImageWidth> {
        use gecko_bindings::structs::nsStyleUnit::{eStyleUnit_Factor, eStyleUnit_Auto};
        use gecko_bindings::sugar::ns_style_coord::CoordData;
        use gecko::values::GeckoStyleCoordConvertible;
        use values::computed::{LengthOrPercentage, Number};
        use values::generics::border::BorderImageSideWidth;

        Some(
            ::values::computed::BorderImageWidth::new(
                % for i in range(0, 4):
                match sides.data_at(${i}).unit() {
                    eStyleUnit_Auto => {
                        BorderImageSideWidth::Auto
                    },
                    eStyleUnit_Factor => {
                        BorderImageSideWidth::Number(
                            Number::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to Number"))
                    },
                    _ => {
                        BorderImageSideWidth::Length(
                            LengthOrPercentage::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to LengthOrPercentager"))
                    },
                },
                % endfor
            )
        )
    }
}
<commit_msg>style: followup test fix to the followup build fix.<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" />
<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>

<% data.new_style_struct("Border", inherited=False,
                   additional_methods=[Method("border_" + side + "_has_nonzero_width",
                                              "bool") for side in ["top", "right", "bottom", "left"]]) %>
<%
    def maybe_logical_spec(side, kind):
        if side[1]: # if it is logical
            return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
        else:
            return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
%>
% for side in ALL_SIDES:
    <%
        side_name = side[0]
        is_logical = side[1]
    %>
    ${helpers.predefined_type(
        "border-%s-color" % side_name, "Color",
        "computed_value::T::currentcolor()",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"),
        spec=maybe_logical_spec(side, "color"),
        animation_value_type="AnimatedColor",
        logical=is_logical,
        logical_group="border-color",
        allow_quirks=not is_logical,
        flags="APPLIES_TO_FIRST_LETTER",
        ignored_when_colors_disabled=True,
    )}

    ${helpers.predefined_type(
        "border-%s-style" % side_name, "BorderStyle",
        "specified::BorderStyle::None",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
        spec=maybe_logical_spec(side, "style"),
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="discrete" if not is_logical else "none",
        logical=is_logical,
        logical_group="border-style",
        needs_context=False,
    )}

    ${helpers.predefined_type(
        "border-%s-width" % side_name,
        "BorderSideWidth",
        "::values::computed::NonNegativeLength::new(3.)",
        computed_type="::values::computed::NonNegativeLength",
        alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-width"),
        spec=maybe_logical_spec(side, "width"),
        animation_value_type="NonNegativeLength",
        logical=is_logical,
        logical_group="border-width",
        flags="APPLIES_TO_FIRST_LETTER GETCS_NEEDS_LAYOUT_FLUSH",
        allow_quirks=not is_logical,
        servo_restyle_damage="reflow rebuild_and_reflow_inline"
    )}
% endfor

${helpers.gecko_keyword_conversion(
    Keyword('border-style',
    "none solid double dotted dashed hidden groove ridge inset outset"),
    type="::values::specified::BorderStyle",
)}

// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthOrPercentage>
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
    ${helpers.predefined_type(
        "border-" + corner + "-radius",
        "BorderCornerRadius",
        "computed::BorderCornerRadius::zero()",
        "parse",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
        boxed=True,
        flags="APPLIES_TO_FIRST_LETTER",
        animation_value_type="BorderCornerRadius",
    )}
% endfor

${helpers.single_keyword(
    "box-decoration-break",
    "slice clone",
    gecko_enum_prefix="StyleBoxDecorationBreak",
    gecko_pref="layout.css.box-decoration-break.enabled",
    spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
    products="gecko",
    animation_value_type="discrete",
)}

${helpers.single_keyword(
    "-moz-float-edge",
    "content-box margin-box",
    gecko_ffi_name="mFloatEdge",
    gecko_enum_prefix="StyleFloatEdge",
    products="gecko",
    spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
    animation_value_type="discrete",
)}

${helpers.predefined_type(
    "border-image-source",
    "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
    vector=False,
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
)}

${helpers.predefined_type(
    "border-image-outset",
    "LengthOrNumberRect",
    parse_method="parse_non_negative",
    initial_value="computed::LengthOrNumberRect::all(computed::LengthOrNumber::zero())",
    initial_specified_value="specified::LengthOrNumberRect::all(specified::LengthOrNumber::zero())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-repeat",
    "BorderImageRepeat",
    "computed::BorderImageRepeat::stretch()",
    initial_specified_value="specified::BorderImageRepeat::stretch()",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
    flags="APPLIES_TO_FIRST_LETTER",
)}

${helpers.predefined_type(
    "border-image-width",
    "BorderImageWidth",
    initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
    initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

${helpers.predefined_type(
    "border-image-slice",
    "BorderImageSlice",
    initial_value="computed::NumberOrPercentage::Percentage(computed::Percentage(1.)).into()",
    initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage::new(1.)).into()",
    spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
    animation_value_type="discrete",
    flags="APPLIES_TO_FIRST_LETTER",
    boxed=True,
)}

#[cfg(feature = "gecko")]
impl ::values::computed::BorderImageWidth {
    pub fn to_gecko_rect(&self, sides: &mut ::gecko_bindings::structs::nsStyleSides) {
        use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
        use gecko::values::GeckoStyleCoordConvertible;
        use values::generics::border::BorderImageSideWidth;

        % for i in range(0, 4):
        match self.${i} {
            BorderImageSideWidth::Auto => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Auto)
            },
            BorderImageSideWidth::Length(l) => {
                l.to_gecko_style_coord(&mut sides.data_at_mut(${i}))
            },
            BorderImageSideWidth::Number(n) => {
                sides.data_at_mut(${i}).set_value(CoordDataValue::Factor(n))
            },
        }
        % endfor
    }

    pub fn from_gecko_rect(
        sides: &::gecko_bindings::structs::nsStyleSides,
    ) -> Option<::values::computed::BorderImageWidth> {
        use gecko_bindings::structs::nsStyleUnit::{eStyleUnit_Factor, eStyleUnit_Auto};
        use gecko_bindings::sugar::ns_style_coord::CoordData;
        use gecko::values::GeckoStyleCoordConvertible;
        use values::computed::{LengthOrPercentage, Number};
        use values::generics::border::BorderImageSideWidth;

        Some(
            ::values::computed::BorderImageWidth::new(
                % for i in range(0, 4):
                match sides.data_at(${i}).unit() {
                    eStyleUnit_Auto => {
                        BorderImageSideWidth::Auto
                    },
                    eStyleUnit_Factor => {
                        BorderImageSideWidth::Number(
                            Number::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to Number"))
                    },
                    _ => {
                        BorderImageSideWidth::Length(
                            LengthOrPercentage::from_gecko_style_coord(&sides.data_at(${i}))
                                .expect("sides[${i}] could not convert to LengthOrPercentager"))
                    },
                },
                % endfor
            )
        )
    }
}
<|endoftext|>