<commit_before>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    lexeme: String,
    line: usize,
}

struct Scanner<'a> {
    current: usize,
    current_lexeme: String,
    line: usize,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current: 0,
            current_lexeme: "".into(),
            line: 1, // 1-indexed,
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        self.current += 1;
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.line += 1;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn add_context(&mut self, token: Token) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            line: self.line,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        self.source.reset_peek();
        while self.peek_check(&|c| c != '"') {
            self.advance();
        }
        if !self.advance_if_match('"') {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        while self.peek_check(&is_digit) {
            self.advance();
        }
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            while self.peek_check(&is_digit) {
                self.advance();
            }
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while self.peek_check(&is_alphanumeric) {
            self.advance();
        }
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => {
                return Ok(TokenWithContext {
                    token: Token::Eof,
                    lexeme: "".into(),
                    line: self.line,
                });
            }
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    while self.peek_check(&|c| c != '\n') {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => try!(self.string()),
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, pos {}",
                                   c,
                                   self.line,
                                   self.current - 1));
            }
        };
        Ok(self.add_context(token))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut eof = false;
    while !eof {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            Token::Eof => {
                eof = true;
                tokens.push(token_with_context);
            }
            _ => tokens.push(token_with_context),
        }
    }
    Ok(tokens)
}

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

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements() {
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
                .into())
            .unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].line, 1);
        assert_eq!(tokens[9].line, 2);
    }
}
<commit_msg>Tracking position properly<commit_after>use itertools::{multipeek, MultiPeek};
use std::str;

#[derive(Debug, PartialEq)]
pub enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // The book doesn't have tokens for comments and
    // whitespaces. Introducing them the scanner can
    // deal with them uniformly and in a more
    // functional way.
    Comment,
    Whitespace,
}

#[derive(Debug,Clone,Copy)]
/// Represents a position in the source file.
/// Both line and column are represented by a 1-based
/// index, since this struct exists only to provide
/// context to the user.
struct Position {
    line: usize,
    column: usize,
}

impl Position {
    fn initial() -> Position {
        Position{line:1, column: 1}
    }

    fn increment_column(&mut self) -> () {
        self.column += 1;
    }

    fn increment_line(&mut self) -> () {
        self.line += 1;
        self.column = 1;
    }
}

#[derive(Debug)]
pub struct TokenWithContext {
    token: Token,
    // Takes a copy. Tokens can outlive the file they came from
    lexeme: String,
    /// Position of the first character of the token
    position: Position,
}

struct Scanner<'a> {
    current_position: Position,
    current_lexeme: String,
    source: MultiPeek<str::Chars<'a>>,
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' => true,
        '\r' => true,
        '\t' => true,
        '\n' => true,
        _ => false,
    }
}

impl<'a> Scanner<'a> {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            current_position: Position::initial(),
            current_lexeme: "".into(),
            source: multipeek(source.chars()),
        }
    }

    fn peek_check(&mut self, check: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&c) => check(c),
            None => false,
        }
    }

    fn peek_check2(&mut self, check1: &Fn(char) -> bool, check2: &Fn(char) -> bool) -> bool {
        self.source.reset_peek();
        match self.source.peek() {
            Some(&p1) => {
                match self.source.peek() {
                    Some(&p2) => check1(p1) && check2(p2),
                    None => false,
                }
            }
            None => false,
        }
    }

    fn advance(&mut self) -> Option<char> {
        let next = self.source.next();
        match next {
            Some(c) => {
                self.current_lexeme.push(c);
                if c == '\n' {
                    self.current_position.increment_line();
                }
                else{
                    self.current_position.increment_column();;
                }
            }
            None => (),
        }
        next
    }

    fn advance_if_match(&mut self, expected: char) -> bool {
        if self.peek_check(&|c| c == expected) {
            let _ = self.advance();
            true
        } else {
            false
        }
    }

    fn add_context(&mut self, token: Token, initial_position: Position) -> TokenWithContext {
        let result = TokenWithContext {
            token: token,
            lexeme: self.current_lexeme.clone(),
            position: initial_position,
        };
        self.current_lexeme = "".into();
        result
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.current_position.line;
        self.source.reset_peek();
        while self.peek_check(&|c| c != '"') {
            self.advance();
        }
        if !self.advance_if_match('"') {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        let literal_length = self.current_lexeme.len() - 2;
        // Trims delimiters
        let literal = self.current_lexeme.chars().skip(1).take(literal_length).collect();
        Ok(Token::StringLiteral(literal))
    }

    fn number(&mut self) -> Token {
        while self.peek_check(&is_digit) {
            self.advance();
        }
        if self.peek_check2(&|p1| p1 == '.', &is_digit) {
            self.advance(); // Consume the .
            while self.peek_check(&is_digit) {
                self.advance();
            }
        }
        let value = self.current_lexeme.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while self.peek_check(&is_alphanumeric) {
            self.advance();
        }
        match self.current_lexeme.as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        let initial_position = self.current_position;
        // Check if there is something left to iterate on.
        // Early return if we're done.
        let next_char = match self.advance() {
            Some(c) => c,
            None => {
                return Ok(TokenWithContext {
                    token: Token::Eof,
                    // Not very meaningful. Is Eof needed at all?
                    lexeme: "".into(),
                    position: initial_position
                });
            }
        };
        let token = match next_char {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.advance_if_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.advance_if_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.advance_if_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.advance_if_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.advance_if_match('/') {
                    // Comments go on till the end of the line
                    while self.peek_check(&|c| c != '\n') {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            '"' => try!(self.string()),
            c if is_whitespace(c) => Token::Whitespace,
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, column {}",
                                   c,
                                   self.current_position.line,
                                   self.current_position.column - 1));
            }
        };
        Ok(self.add_context(token, initial_position))
    }
}

pub fn scan(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    let mut eof = false;
    while !eof {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            Token::Eof => {
                eof = true;
                tokens.push(token_with_context);
            }
            _ => tokens.push(token_with_context),
        }
    }
    Ok(tokens)
}

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

    #[test]
    fn single_token() {
        let tokens = scan(&"+".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Plus);
    }

    #[test]
    fn expression() {
        let tokens = scan(&"1+2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn expression_with_whitespaces() {
        let tokens = scan(&"1 + 2".into()).unwrap();
        assert_eq!(tokens[0].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[1].token, Token::Plus);
        assert_eq!(tokens[2].token, Token::NumberLiteral(2.0f64));
        assert_eq!(tokens[3].token, Token::Eof);
    }

    #[test]
    fn assignement_with_comment() {
        let tokens = scan(&"var a = 1.0; // A comment".into()).unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Eof);
    }

    #[test]
    fn multiline_statements() {
        let tokens = scan(&r#"var a = 1.0;
                              var b = "Hello";"#
                .into())
            .unwrap();
        assert_eq!(tokens[0].token, Token::Var);
        assert_eq!(tokens[1].token, Token::Identifier("a".into()));
        assert_eq!(tokens[2].token, Token::Equal);
        assert_eq!(tokens[3].token, Token::NumberLiteral(1.0f64));
        assert_eq!(tokens[4].token, Token::Semicolon);
        assert_eq!(tokens[5].token, Token::Var);
        assert_eq!(tokens[6].token, Token::Identifier("b".into()));
        assert_eq!(tokens[7].token, Token::Equal);
        assert_eq!(tokens[8].token, Token::StringLiteral("Hello".into()));
        assert_eq!(tokens[9].token, Token::Semicolon);
        assert_eq!(tokens[10].token, Token::Eof);
        assert_eq!(tokens[1].position.line, 1);
        assert_eq!(tokens[1].position.column, 5);
        assert_eq!(tokens[9].position.line, 2);
        assert_eq!(tokens[9].position.column, 46); // There are 30 spaces before the text!
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ffi;
use std::str;
use std::ffi::CString;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(name="SDL", kind="framework")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL")]
    extern {}
}

#[cfg(not(target_os="macos"))]
mod others {
    #[link(name="SDL")]
    extern {}
}

pub mod ll {
    #![allow(non_camel_case_types)]

    use libc::{c_int, c_uint, uint32_t};
    use libc::types::os::arch::c95::c_schar;

    pub type SDL_errorcode = c_uint;
    pub const SDL_ENOMEM: SDL_errorcode = 0;
    pub const SDL_EFREAD: SDL_errorcode = 1;
    pub const SDL_EFWRITE: SDL_errorcode = 2;
    pub const SDL_EFSEEK: SDL_errorcode = 3;
    pub const SDL_UNSUPPORTED: SDL_errorcode = 4;
    pub const SDL_LASTERROR: SDL_errorcode = 5;

    pub type SDL_InitFlag = uint32_t;
    pub const SDL_INIT_TIMER: SDL_InitFlag = 0x00000001;
    pub const SDL_INIT_AUDIO: SDL_InitFlag = 0x00000010;
    pub const SDL_INIT_VIDEO: SDL_InitFlag = 0x00000020;
    pub const SDL_INIT_CDROM: SDL_InitFlag = 0x00000100;
    pub const SDL_INIT_JOYSTICK: SDL_InitFlag = 0x00000200;
    pub const SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x00100000;
    pub const SDL_INIT_EVENTTHREAD: SDL_InitFlag = 0x01000000;
    pub const SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000FFFF;

    extern "C" {
        pub fn SDL_ClearError();
        pub fn SDL_Error(code: SDL_errorcode);
        pub fn SDL_SetError(fmt: *const c_schar);
        pub fn SDL_GetError() -> *mut c_schar;
        pub fn SDL_Quit();
        pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
        pub fn SDL_Init(flags: uint32_t) -> c_int;
        pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
        pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
        pub fn SDL_GetTicks() -> uint32_t;
    }
}

#[derive(PartialEq)]
#[repr(C)]
pub struct Rect {
    pub x: i16,
    pub y: i16,
    pub w: u16,
    pub h: u16
}

impl Copy for Rect {}

#[allow(non_snake_case)]
pub fn Rect(x: i16, y: i16, w: u16, h: u16) -> Rect {
    Rect { x: x, y: y, w: w, h: h }
}

impl Rect {
    pub fn new(x: i16, y: i16, w: u16, h: u16) -> Rect {
        Rect {
            x: x,
            y: y,
            w: w,
            h: h
        }
    }
}

#[derive(PartialEq, Eq)]
pub enum InitFlag {
     Timer = ll::SDL_INIT_TIMER as isize,
     Audio = ll::SDL_INIT_AUDIO as isize,
     Video = ll::SDL_INIT_VIDEO as isize,
     CDRom = ll::SDL_INIT_CDROM as isize,
     Joystick = ll::SDL_INIT_JOYSTICK as isize,
     NoParachute = ll::SDL_INIT_NOPARACHUTE as isize,
     EventThread = ll::SDL_INIT_EVENTTHREAD as isize,
     Everything = ll::SDL_INIT_EVERYTHING as isize,
}

impl Copy for InitFlag {}

#[derive(PartialEq, Eq)]
pub enum Error {
     NoMem = ll::SDL_ENOMEM as isize,
     Read = ll::SDL_EFREAD as isize,
     Write = ll::SDL_EFWRITE as isize,
     Seek = ll::SDL_EFSEEK as isize,
     Unsupported = ll::SDL_UNSUPPORTED as isize
}

impl Copy for Error {}

pub fn init(flags: &[InitFlag]) -> bool {
    unsafe {
        ll::SDL_Init(flags.iter().fold(0u32, |flags, &flag| {
            flags | flag as ll::SDL_InitFlag
        })) == 0
    }
}

pub fn init_subsystem(flags: &[InitFlag]) -> bool {
    unsafe {
        ll::SDL_InitSubSystem(flags.iter().fold(0u32, |flags, &flag| {
            flags | flag as ll::SDL_InitFlag
        })) == 0
    }
}

pub fn quit_subsystem(flags: &[InitFlag]) {
    let flags = flags.iter().fold(0u32, |flags, &flag| {
        flags | flag as ll::SDL_InitFlag
    });

    unsafe { ll::SDL_QuitSubSystem(flags); }
}

pub fn quit() {
    unsafe { ll::SDL_Quit(); }
}

pub fn was_inited(flags: &[InitFlag]) -> Vec<InitFlag> {
    let flags = flags.iter().fold(0u32, |flags, &flag| {
        flags | flag as ll::SDL_InitFlag
    });
    let bitflags = unsafe { ll::SDL_WasInit(flags) };

    let flags = [InitFlag::Timer,
        InitFlag::Audio,
        InitFlag::Video,
        InitFlag::CDRom,
        InitFlag::Joystick,
        InitFlag::NoParachute,
        InitFlag::EventThread,
        InitFlag::Everything];

    flags.iter().filter_map(|&flag| {
        if bitflags & (flag as ll::SDL_InitFlag) != 0 { Some(flag) }
        else { None }
    }).collect()
}

pub fn get_error() -> String {
    unsafe {
        let cstr = ll::SDL_GetError();

        str::from_utf8(ffi::c_str_to_bytes(mem::transmute_copy(&cstr))).unwrap().to_string()
    }
}

pub fn set_error(err: &str) {
    unsafe { ll::SDL_SetError(CString::from_slice(err.as_bytes()).as_ptr()); }
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode) }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}

pub fn get_ticks() -> usize {
    unsafe { ll::SDL_GetTicks() as usize }
}
<commit_msg>Fixed sdl::get_error to not segfault<commit_after>use std::mem;
use std::ffi;
use std::str;
use std::ffi::CString;

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(mac_framework)]
    #[link(name="SDL", kind="framework")]
    extern {}

    #[cfg(not(mac_framework))]
    #[link(name="SDL")]
    extern {}
}

#[cfg(not(target_os="macos"))]
mod others {
    #[link(name="SDL")]
    extern {}
}

pub mod ll {
    #![allow(non_camel_case_types)]

    use libc::{c_int, c_uint, uint32_t};
    use libc::types::os::arch::c95::c_schar;

    pub type SDL_errorcode = c_uint;
    pub const SDL_ENOMEM: SDL_errorcode = 0;
    pub const SDL_EFREAD: SDL_errorcode = 1;
    pub const SDL_EFWRITE: SDL_errorcode = 2;
    pub const SDL_EFSEEK: SDL_errorcode = 3;
    pub const SDL_UNSUPPORTED: SDL_errorcode = 4;
    pub const SDL_LASTERROR: SDL_errorcode = 5;

    pub type SDL_InitFlag = uint32_t;
    pub const SDL_INIT_TIMER: SDL_InitFlag = 0x00000001;
    pub const SDL_INIT_AUDIO: SDL_InitFlag = 0x00000010;
    pub const SDL_INIT_VIDEO: SDL_InitFlag = 0x00000020;
    pub const SDL_INIT_CDROM: SDL_InitFlag = 0x00000100;
    pub const SDL_INIT_JOYSTICK: SDL_InitFlag = 0x00000200;
    pub const SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x00100000;
    pub const SDL_INIT_EVENTTHREAD: SDL_InitFlag = 0x01000000;
    pub const SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000FFFF;

    extern "C" {
        pub fn SDL_ClearError();
        pub fn SDL_Error(code: SDL_errorcode);
        pub fn SDL_SetError(fmt: *const c_schar);
        pub fn SDL_GetError() -> *mut c_schar;
        pub fn SDL_Quit();
        pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
        pub fn SDL_Init(flags: uint32_t) -> c_int;
        pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
        pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
        pub fn SDL_GetTicks() -> uint32_t;
    }
}

#[derive(PartialEq)]
#[repr(C)]
pub struct Rect {
    pub x: i16,
    pub y: i16,
    pub w: u16,
    pub h: u16
}

impl Copy for Rect {}

#[allow(non_snake_case)]
pub fn Rect(x: i16, y: i16, w: u16, h: u16) -> Rect {
    Rect { x: x, y: y, w: w, h: h }
}

impl Rect {
    pub fn new(x: i16, y: i16, w: u16, h: u16) -> Rect {
        Rect {
            x: x,
            y: y,
            w: w,
            h: h
        }
    }
}

#[derive(PartialEq, Eq)]
pub enum InitFlag {
     Timer = ll::SDL_INIT_TIMER as isize,
     Audio = ll::SDL_INIT_AUDIO as isize,
     Video = ll::SDL_INIT_VIDEO as isize,
     CDRom = ll::SDL_INIT_CDROM as isize,
     Joystick = ll::SDL_INIT_JOYSTICK as isize,
     NoParachute = ll::SDL_INIT_NOPARACHUTE as isize,
     EventThread = ll::SDL_INIT_EVENTTHREAD as isize,
     Everything = ll::SDL_INIT_EVERYTHING as isize,
}

impl Copy for InitFlag {}

#[derive(PartialEq, Eq)]
pub enum Error {
     NoMem = ll::SDL_ENOMEM as isize,
     Read = ll::SDL_EFREAD as isize,
     Write = ll::SDL_EFWRITE as isize,
     Seek = ll::SDL_EFSEEK as isize,
     Unsupported = ll::SDL_UNSUPPORTED as isize
}

impl Copy for Error {}

pub fn init(flags: &[InitFlag]) -> bool {
    unsafe {
        ll::SDL_Init(flags.iter().fold(0u32, |flags, &flag| {
            flags | flag as ll::SDL_InitFlag
        })) == 0
    }
}

pub fn init_subsystem(flags: &[InitFlag]) -> bool {
    unsafe {
        ll::SDL_InitSubSystem(flags.iter().fold(0u32, |flags, &flag| {
            flags | flag as ll::SDL_InitFlag
        })) == 0
    }
}

pub fn quit_subsystem(flags: &[InitFlag]) {
    let flags = flags.iter().fold(0u32, |flags, &flag| {
        flags | flag as ll::SDL_InitFlag
    });

    unsafe { ll::SDL_QuitSubSystem(flags); }
}

pub fn quit() {
    unsafe { ll::SDL_Quit(); }
}

pub fn was_inited(flags: &[InitFlag]) -> Vec<InitFlag> {
    let flags = flags.iter().fold(0u32, |flags, &flag| {
        flags | flag as ll::SDL_InitFlag
    });
    let bitflags = unsafe { ll::SDL_WasInit(flags) };

    let flags = [InitFlag::Timer,
        InitFlag::Audio,
        InitFlag::Video,
        InitFlag::CDRom,
        InitFlag::Joystick,
        InitFlag::NoParachute,
        InitFlag::EventThread,
        InitFlag::Everything];

    flags.iter().filter_map(|&flag| {
        if bitflags & (flag as ll::SDL_InitFlag) != 0 { Some(flag) }
        else { None }
    }).collect()
}

pub fn get_error() -> String {
    unsafe {
        let cstr = ll::SDL_GetError() as *const i8;
        let slice = ffi::c_str_to_bytes(&cstr);

        str::from_utf8(slice).unwrap().to_string()
    }
}

pub fn set_error(err: &str) {
    unsafe { ll::SDL_SetError(CString::from_slice(err.as_bytes()).as_ptr()); }
}

pub fn set_error_from_code(err: Error) {
    unsafe { ll::SDL_Error(err as ll::SDL_errorcode) }
}

pub fn clear_error() {
    unsafe { ll::SDL_ClearError(); }
}

pub fn get_ticks() -> usize {
    unsafe { ll::SDL_GetTicks() as usize }
}
<|endoftext|>


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

use glium::{self, Texture2d, Texture, Program, VertexBuffer};
use glium::{DrawParameters, BlendingFunction, LinearBlendingFactor};
use glium::Surface as SurfaceTrait;
use glium::index::{NoIndices, PrimitiveType};
use graphics::{BACK_END_MAX_VERTEX_COUNT, triangulation};
use window::GliumWindow as Window;

use ui::{BB, Px};
use ui::color::Color;
use ui::text::{FontFace, FontFaces};

pub use glutin::MouseCursor;
pub type Surface = glium::Frame;
pub type DrawTexture = Rc<Texture2d>;

#[derive(Copy, Clone)]
struct PlainVertex {
    pos: [Px; 2]
}
implement_vertex!(PlainVertex, pos);

#[derive(Copy, Clone)]
struct TexturedVertex {
    pos: [Px; 2],
    uv: [Px; 2]
}
implement_vertex!(TexturedVertex, pos, uv);

pub struct System {
    next_plain_buffer: u32,
    plain_buffer1: VertexBuffer<PlainVertex>,
    plain_buffer2: VertexBuffer<PlainVertex>,
    next_textured_buffer: u32,
    textured_buffer1: VertexBuffer<TexturedVertex>,
    textured_buffer2: VertexBuffer<TexturedVertex>,
    shader_texture: Program,
    shader_color: Program,
    fonts: FontFaces
}

impl System {
    pub fn new(window: &Window, fonts: FontFaces) -> System {
        use std::iter::repeat;

        // FIXME: create empty buffers when glium supports them
        let plain_data = repeat(PlainVertex { pos: [0.0, 0.0] })
                                .take(BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();
        let textured_data = repeat(TexturedVertex { pos: [0.0, 0.0], uv: [0.0, 0.0] })
                                .take(BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();

        System {
            next_plain_buffer: 0,
            plain_buffer1: VertexBuffer::dynamic(window, plain_data.clone()),
            plain_buffer2: VertexBuffer::dynamic(window, plain_data),
            next_textured_buffer: 0,
            textured_buffer1: VertexBuffer::dynamic(window, textured_data.clone()),
            textured_buffer2: VertexBuffer::dynamic(window, textured_data),
            shader_texture: Program::from_source(window, "
                #version 120
                uniform sampler2D s_texture;
                uniform vec4 color;
                attribute vec2 pos;
                attribute vec2 uv;
                varying vec2 v_uv;
                void main() {
                    v_uv = uv;
                    gl_Position = vec4(pos, 0.0, 1.0);
                }","
                #version 120
                uniform sampler2D s_texture;
                uniform vec4 color;
                varying vec2 v_uv;
                void main() {
                    gl_FragColor = texture2D(s_texture, v_uv) * color;
                }", None).unwrap(),
            shader_color: Program::from_source(window, "
                #version 120
                uniform vec4 color;
                attribute vec2 pos;
                void main() {
                    gl_Position = vec4(pos, 0.0, 1.0);
                }","
                #version 120
                uniform vec4 color;
                void main() {
                    gl_FragColor = color;
                }",
                None).unwrap(),
            fonts: fonts
        }
    }
}

pub struct DrawCx<'a> {
    system: &'a mut System,
    surface: Surface,
    cursor: MouseCursor,
    transform: [[f64; 3]; 2],
    overlay_requested: bool,
    overlay_drawing: bool,
    inside_overlay: bool
}

#[cfg(not(windows))]
fn gamma_pre_correct([r, g, b, a]: Color) -> Color {
    fn ch(x: f32) -> f32 { ((x + 0.055) / 1.055).powf(2.4) }
    [ch(r), ch(g), ch(b), a]
}

#[cfg(windows)]
fn gamma_pre_correct(color: Color) -> Color {
    color
}

impl<'a> DrawCx<'a> {
    pub fn new(system: &'a mut System, surface: Surface) -> DrawCx<'a> {
        let (w, h) = surface.get_dimensions();
        let (w, h) = (w as f64, h as f64);

        DrawCx {
            system: system,
            surface: surface,
            cursor: MouseCursor::Default,
            transform: [
                [2.0 / w, 0.0, -1.0],
                [0.0,  -2.0 / h, 1.0]
            ],
            overlay_requested: false,
            overlay_drawing: false,
            inside_overlay: false
        }
    }

    pub fn fonts(&mut self) -> &mut FontFaces {
        &mut self.system.fonts
    }

    pub fn dimensions(&mut self) -> [Px; 2] {
        let (w, h) = self.surface.get_dimensions();
        [w as Px, h as Px]
    }

    pub fn draw<T: Draw>(&mut self, x: &T) {
        x.draw(self);
        if self.overlay_requested {
            self.overlay_drawing = true;
            x.draw(self);
            self.overlay_drawing = false;
            self.overlay_requested = false;
        }
    }

    pub fn draw_overlay<F, T>(&mut self, f: F) -> T where F: FnOnce(&mut DrawCx) -> T {
        assert!(!self.inside_overlay);
        self.inside_overlay = true;
        let r = f(self);
        self.inside_overlay = false;
        self.overlay_requested = true;
        r
    }

    pub fn with_surface<F, T>(&mut self, f: F) -> Option<T> where F: FnOnce(&mut Self) -> T {
        if self.inside_overlay == self.overlay_drawing {
            Some(f(self))
        } else {
            None
        }
    }

    pub fn get_cursor(&self) -> MouseCursor {
        self.cursor
    }

    pub fn cursor(&mut self, cursor: MouseCursor) {
        self.with_surface(|this| this.cursor = cursor);
    }

    pub fn clear(&mut self, color: Color) {
        let [r, g, b, a] = gamma_pre_correct(color);
        self.surface.clear_color(r, g, b, a);
    }

    // TODO make DrawCx linear to ensure this method gets called.
    pub fn finish(self) {
        self.surface.finish();
    }

    fn tri_list<F>(&mut self, color: Color, mut f: F)
        where F: FnMut(&mut FnMut(&[Px])) {
        f(&mut |vertices: &[Px]| {
            let slice = match self.system.next_plain_buffer {
                0 => {
                    self.system.next_plain_buffer = 1;
                    self.system.plain_buffer1.slice(0..vertices.len() / 2).unwrap()
                },
                1 => {
                    self.system.next_plain_buffer = 0;
                    self.system.plain_buffer2.slice(0..vertices.len() / 2).unwrap()
                },
                _ => unreachable!()
            };

            slice.write({
                (0..vertices.len() / 2)
                    .map(|i| PlainVertex {
                        pos: [vertices[2 * i], vertices[2 * i + 1]],
                    })
                    .collect::<Vec<_>>()
            });

            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_color,
                &uniform! { color: color },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }

    fn tri_list_uv<F>(&mut self, color: Color, texture: &Texture2d, mut f: F)
        where F: FnMut(&mut FnMut(&[Px], &[Px])) {
        use std::cmp::min;

        f(&mut |vertices: &[Px], texture_coords: &[Px]| {
            let len = min(vertices.len(), texture_coords.len()) / 2;

            let slice = match self.system.next_textured_buffer {
                0 => {
                    self.system.next_textured_buffer = 1;
                    self.system.textured_buffer1.slice(0..len).unwrap()
                },
                1 => {
                    self.system.next_textured_buffer = 0;
                    self.system.textured_buffer2.slice(0..len).unwrap()
                },
                _ => unreachable!()
            };

            slice.write({
                (0..len)
                    .map(|i| TexturedVertex {
                        pos: [vertices[2 * i], vertices[2 * i + 1]],
                        // FIXME: The `1.0 - ...` is because of a wrong convention
                        uv: [texture_coords[2 * i], 1.0 - texture_coords[2 * i + 1]],
                    })
                    .collect::<Vec<_>>()
            });

            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_texture,
                &uniform! {
                    color: color,
                    s_texture: texture
                },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }

    pub fn fill(&mut self, bb: BB<Px>, color: Color/*, corner_radius: Px*/) {
        let corner_radius: Px = 0.0;
        let transform = self.transform;
        self.with_surface(|this| {
            let rectangle = [bb.x1 as f64, bb.y1 as f64,
                             (bb.x2 - bb.x1)  as f64, (bb.y2 - bb.y1)  as f64];
            let color = gamma_pre_correct(color);
            let resolution = 1; // ???

            if corner_radius == 0.0 {
                this.tri_list(color, |f| {
                    f(&triangulation::rect_tri_list_xy(transform, rectangle));
                });
            } else {
                this.tri_list(color, |f| {
                    triangulation::with_round_rectangle_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        corner_radius as f64,
                        |vertices| f(vertices)
                    );
                });
            }
        });
    }

    pub fn border(&mut self, bb: BB<Px>, color: Color, border_size: Px, corner_radius: Px) {
        let transform = self.transform;
        self.with_surface(|this| {
            let rectangle = [bb.x1 as f64, bb.y1 as f64,
                             (bb.x2 - bb.x1)  as f64, (bb.y2 - bb.y1)  as f64];
            let color = gamma_pre_correct(color);
            let resolution = 1; // ???

            if corner_radius == 0.0 {
                this.tri_list(color, |f| {
                    f(&triangulation::rect_border_tri_list_xy(transform, rectangle, border_size as f64));
                });
            } else {
                this.tri_list(color, |f| {
                    triangulation::with_round_rectangle_border_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        corner_radius as f64,
                        border_size as f64,
                        |vertices| f(vertices)
                    );
                });
            }
        });
    }

    pub fn text<F: FontFace>(&mut self, font: F, [x, y]: [Px; 2], color: Color, text: &str) {
        self.with_surface(|this| {
            let transform = this.transform;
            let (mut x, y) = (x, y + this.fonts().metrics(font).baseline);

            // TODO use graphemes and maybe harfbuzz.
            for ch in text.chars() {
                let glyph = this.fonts().glyph(font, ch).clone();
                let texture = &glyph.texture;
                let w = texture.get_width();
                let h = texture.get_height().unwrap();

                let [dx, dy] = glyph.offset;
                let rectangle = [(x + dx) as f64, (y + dy) as f64, w as f64, h as f64];
                this.tri_list_uv(color, texture, |f| {
                    f(&triangulation::rect_tri_list_xy(transform, rectangle),
                      &[0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
                        1.0, 0.0, 1.0, 1.0, 0.0, 1.0]);
                });
                x += glyph.advance;
            }
        });
    }
}

pub trait Draw {
    fn draw(&self, cx: &mut DrawCx);
}

impl<A, B> Draw for (A, B) where
        A: Draw,
        B: Draw {
    fn draw(&self, cx: &mut DrawCx) {
        self.0.draw(cx);
        self.1.draw(cx);
    }
}
<commit_msg>ui::draw: backport removal of double buffering.<commit_after>use std::default::Default;
use std::rc::Rc;

use glium::{self, Texture2d, Texture, Program, VertexBuffer};
use glium::{DrawParameters, BlendingFunction, LinearBlendingFactor};
use glium::Surface as SurfaceTrait;
use glium::index::{NoIndices, PrimitiveType};
use graphics::{BACK_END_MAX_VERTEX_COUNT, triangulation};
use window::GliumWindow as Window;

use ui::{BB, Px};
use ui::color::Color;
use ui::text::{FontFace, FontFaces};

pub use glutin::MouseCursor;
pub type Surface = glium::Frame;
pub type DrawTexture = Rc<Texture2d>;

#[derive(Copy, Clone)]
struct PlainVertex {
    pos: [Px; 2]
}
implement_vertex!(PlainVertex, pos);

#[derive(Copy, Clone)]
struct TexturedVertex {
    pos: [Px; 2],
    uv: [Px; 2]
}
implement_vertex!(TexturedVertex, pos, uv);

pub struct System {
    plain_buffer: VertexBuffer<PlainVertex>,
    textured_buffer: VertexBuffer<TexturedVertex>,
    shader_texture: Program,
    shader_color: Program,
    fonts: FontFaces
}

impl System {
    pub fn new(window: &Window, fonts: FontFaces) -> System {
        use std::iter::repeat;

        // FIXME: create empty buffers when glium supports them
        let plain_data = repeat(PlainVertex { pos: [0.0, 0.0] })
                                .take(BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();
        let textured_data = repeat(TexturedVertex { pos: [0.0, 0.0], uv: [0.0, 0.0] })
                                .take(BACK_END_MAX_VERTEX_COUNT).collect::<Vec<_>>();

        System {
            plain_buffer: VertexBuffer::dynamic(window, plain_data),
            textured_buffer: VertexBuffer::dynamic(window, textured_data),
            shader_texture: Program::from_source(window, "
                #version 120
                uniform sampler2D s_texture;
                uniform vec4 color;
                attribute vec2 pos;
                attribute vec2 uv;
                varying vec2 v_uv;
                void main() {
                    v_uv = uv;
                    gl_Position = vec4(pos, 0.0, 1.0);
                }","
                #version 120
                uniform sampler2D s_texture;
                uniform vec4 color;
                varying vec2 v_uv;
                void main() {
                    gl_FragColor = texture2D(s_texture, v_uv) * color;
                }", None).unwrap(),
            shader_color: Program::from_source(window, "
                #version 120
                uniform vec4 color;
                attribute vec2 pos;
                void main() {
                    gl_Position = vec4(pos, 0.0, 1.0);
                }","
                #version 120
                uniform vec4 color;
                void main() {
                    gl_FragColor = color;
                }",
                None).unwrap(),
            fonts: fonts
        }
    }
}

pub struct DrawCx<'a> {
    system: &'a mut System,
    surface: Surface,
    cursor: MouseCursor,
    transform: [[f64; 3]; 2],
    overlay_requested: bool,
    overlay_drawing: bool,
    inside_overlay: bool
}

#[cfg(not(windows))]
fn gamma_pre_correct([r, g, b, a]: Color) -> Color {
    fn ch(x: f32) -> f32 { ((x + 0.055) / 1.055).powf(2.4) }
    [ch(r), ch(g), ch(b), a]
}

#[cfg(windows)]
fn gamma_pre_correct(color: Color) -> Color {
    color
}

impl<'a> DrawCx<'a> {
    pub fn new(system: &'a mut System, surface: Surface) -> DrawCx<'a> {
        let (w, h) = surface.get_dimensions();
        let (w, h) = (w as f64, h as f64);

        DrawCx {
            system: system,
            surface: surface,
            cursor: MouseCursor::Default,
            transform: [
                [2.0 / w, 0.0, -1.0],
                [0.0,  -2.0 / h, 1.0]
            ],
            overlay_requested: false,
            overlay_drawing: false,
            inside_overlay: false
        }
    }

    pub fn fonts(&mut self) -> &mut FontFaces {
        &mut self.system.fonts
    }

    pub fn dimensions(&mut self) -> [Px; 2] {
        let (w, h) = self.surface.get_dimensions();
        [w as Px, h as Px]
    }

    pub fn draw<T: Draw>(&mut self, x: &T) {
        x.draw(self);
        if self.overlay_requested {
            self.overlay_drawing = true;
            x.draw(self);
            self.overlay_drawing = false;
            self.overlay_requested = false;
        }
    }

    pub fn draw_overlay<F, T>(&mut self, f: F) -> T where F: FnOnce(&mut DrawCx) -> T {
        assert!(!self.inside_overlay);
        self.inside_overlay = true;
        let r = f(self);
        self.inside_overlay = false;
        self.overlay_requested = true;
        r
    }

    pub fn with_surface<F, T>(&mut self, f: F) -> Option<T> where F: FnOnce(&mut Self) -> T {
        if self.inside_overlay == self.overlay_drawing {
            Some(f(self))
        } else {
            None
        }
    }

    pub fn get_cursor(&self) -> MouseCursor {
        self.cursor
    }

    pub fn cursor(&mut self, cursor: MouseCursor) {
        self.with_surface(|this| this.cursor = cursor);
    }

    pub fn clear(&mut self, color: Color) {
        let [r, g, b, a] = gamma_pre_correct(color);
        self.surface.clear_color(r, g, b, a);
    }

    // TODO make DrawCx linear to ensure this method gets called.
    pub fn finish(self) {
        self.surface.finish();
    }

    fn tri_list<F>(&mut self, color: Color, mut f: F)
        where F: FnMut(&mut FnMut(&[Px])) {
        f(&mut |vertices: &[Px]| {
            let slice = self.system.plain_buffer.slice(0..vertices.len() / 2).unwrap();

            slice.write({
                (0..vertices.len() / 2)
                    .map(|i| PlainVertex {
                        pos: [vertices[2 * i], vertices[2 * i + 1]],
                    })
                    .collect::<Vec<_>>()
            });

            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_color,
                &uniform! { color: color },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }

    fn tri_list_uv<F>(&mut self, color: Color, texture: &Texture2d, mut f: F)
        where F: FnMut(&mut FnMut(&[Px], &[Px])) {
        use std::cmp::min;

        f(&mut |vertices: &[Px], texture_coords: &[Px]| {
            let len = min(vertices.len(), texture_coords.len()) / 2;

            let slice = self.system.textured_buffer.slice(0..len).unwrap();

            slice.write({
                (0..len)
                    .map(|i| TexturedVertex {
                        pos: [vertices[2 * i], vertices[2 * i + 1]],
                        // FIXME: The `1.0 - ...` is because of a wrong convention
                        uv: [texture_coords[2 * i], 1.0 - texture_coords[2 * i + 1]],
                    })
                    .collect::<Vec<_>>()
            });

            self.surface.draw(
                slice,
                &NoIndices(PrimitiveType::TrianglesList),
                &self.system.shader_texture,
                &uniform! {
                    color: color,
                    s_texture: texture
                },
                &DrawParameters {
                    blending_function: Some(BlendingFunction::Addition {
                        source: LinearBlendingFactor::SourceAlpha,
                        destination: LinearBlendingFactor::OneMinusSourceAlpha,
                    }),
                    .. Default::default()
                },
            )
            .ok()
            .expect("failed to draw triangle list");
        })
    }

    pub fn fill(&mut self, bb: BB<Px>, color: Color/*, corner_radius: Px*/) {
        let corner_radius: Px = 0.0;
        let transform = self.transform;
        self.with_surface(|this| {
            let rectangle = [bb.x1 as f64, bb.y1 as f64,
                             (bb.x2 - bb.x1)  as f64, (bb.y2 - bb.y1)  as f64];
            let color = gamma_pre_correct(color);
            let resolution = 1; // ???

            if corner_radius == 0.0 {
                this.tri_list(color, |f| {
                    f(&triangulation::rect_tri_list_xy(transform, rectangle));
                });
            } else {
                this.tri_list(color, |f| {
                    triangulation::with_round_rectangle_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        corner_radius as f64,
                        |vertices| f(vertices)
                    );
                });
            }
        });
    }

    pub fn border(&mut self, bb: BB<Px>, color: Color, border_size: Px, corner_radius: Px) {
        let transform = self.transform;
        self.with_surface(|this| {
            let rectangle = [bb.x1 as f64, bb.y1 as f64,
                             (bb.x2 - bb.x1)  as f64, (bb.y2 - bb.y1)  as f64];
            let color = gamma_pre_correct(color);
            let resolution = 1; // ???

            if corner_radius == 0.0 {
                this.tri_list(color, |f| {
                    f(&triangulation::rect_border_tri_list_xy(transform, rectangle, border_size as f64));
                });
            } else {
                this.tri_list(color, |f| {
                    triangulation::with_round_rectangle_border_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        corner_radius as f64,
                        border_size as f64,
                        |vertices| f(vertices)
                    );
                });
            }
        });
    }

    pub fn text<F: FontFace>(&mut self, font: F, [x, y]: [Px; 2], color: Color, text: &str) {
        self.with_surface(|this| {
            let transform = this.transform;
            let (mut x, y) = (x, y + this.fonts().metrics(font).baseline);

            // TODO use graphemes and maybe harfbuzz.
            for ch in text.chars() {
                let glyph = this.fonts().glyph(font, ch).clone();
                let texture = &glyph.texture;
                let w = texture.get_width();
                let h = texture.get_height().unwrap();

                let [dx, dy] = glyph.offset;
                let rectangle = [(x + dx) as f64, (y + dy) as f64, w as f64, h as f64];
                this.tri_list_uv(color, texture, |f| {
                    f(&triangulation::rect_tri_list_xy(transform, rectangle),
                      &[0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
                        1.0, 0.0, 1.0, 1.0, 0.0, 1.0]);
                });
                x += glyph.advance;
            }
        });
    }
}

pub trait Draw {
    fn draw(&self, cx: &mut DrawCx);
}

impl<A, B> Draw for (A, B) where
        A: Draw,
        B: Draw {
    fn draw(&self, cx: &mut DrawCx) {
        self.0.draw(cx);
        self.1.draw(cx);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! `Variant` binding and helper traits.
//!
//! [`Variant`](struct.Variant.html) is an immutable dynamically-typed generic
//! container. Its type and value are defined at construction and never change.
//!
//! `Variant` types are described by [`VariantType`](../struct.VariantType.html)
//! "type strings".
//!
//! Although `GVariant` supports arbitrarily complex types, this binding is
//! currently limited to the basic ones: `bool`, `u8`, `i16`, `u16`, `i32`,
//! `u32`, `i64`, `u64`, `f64` and `&str`/`String`.
//!
//! # Examples
//!
//! ```
//! use glib::prelude::*; // or `use gtk::prelude::*;`
//! use glib::Variant;
//!
//! // Using the `ToVariant` trait.
//! let num = 10.to_variant();
//!
//! // `is` tests the type of the value.
//! assert!(num.is::<i32>());
//!
//! // `get` tries to extract the value.
//! assert_eq!(num.get::<i32>(), Some(10));
//! assert_eq!(num.get::<u32>(), None);
//!
//! // `Variant` implements `From`
//! let hello = Variant::from("Hello!");
//!
//! // `get_str` tries to borrow a string slice.
//! assert_eq!(hello.get_str(), Some("Hello!"));
//! assert_eq!(num.get_str(), None);
//! ```

use bytes::Bytes;
use glib_sys;
use gobject_sys;
use gstring::GString;
use std::borrow::Cow;
use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::slice;
use std::str;
use translate::*;
use value;
use StaticType;
use Type;
use Value;
use VariantTy;

glib_wrapper! {
    /// A generic immutable value capable of carrying various types.
    ///
    /// See the [module documentation](index.html) for more details.
    pub struct Variant(Shared<glib_sys::GVariant>);

    match fn {
        ref => |ptr| glib_sys::g_variant_ref_sink(ptr),
        unref => |ptr| glib_sys::g_variant_unref(ptr),
    }
}

impl StaticType for Variant {
    fn static_type() -> Type {
        Type::Variant
    }
}

#[doc(hidden)]
impl<'a> value::FromValueOptional<'a> for Variant {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        from_glib_full(gobject_sys::g_value_dup_variant(
            ToGlibPtr::to_glib_none(value).0,
        ))
    }
}

#[doc(hidden)]
impl value::SetValue for Variant {
    unsafe fn set_value(value: &mut Value, this: &Self) {
        gobject_sys::g_value_set_variant(
            ToGlibPtrMut::to_glib_none_mut(value).0,
            ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(this).0,
        )
    }
}

#[doc(hidden)]
impl value::SetValueOptional for Variant {
    unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
        gobject_sys::g_value_set_variant(
            ToGlibPtrMut::to_glib_none_mut(value).0,
            ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(&this).0,
        )
    }
}

impl Variant {
    /// Returns the type of the value.
    pub fn type_(&self) -> &VariantTy {
        unsafe { VariantTy::from_ptr(glib_sys::g_variant_get_type(self.to_glib_none().0)) }
    }

    /// Returns `true` if the type of the value corresponds to `T`.
    #[inline]
    pub fn is<T: StaticVariantType>(&self) -> bool {
        self.type_() == T::static_variant_type()
    }

    /// Tries to extract a value of type `T`.
    ///
    /// Returns `Some` if `T` matches the variant's type.
    #[inline]
    pub fn get<T: FromVariant>(&self) -> Option<T> {
        T::from_variant(self)
    }

    /// Tries to extract a `&str`.
    ///
    /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type
    /// strings).
    pub fn get_str(&self) -> Option<&str> {
        unsafe {
            match self.type_().to_str() {
                "s" | "o" | "g" => {
                    let mut len = 0;
                    let ptr = glib_sys::g_variant_get_string(self.to_glib_none().0, &mut len);
                    let ret = str::from_utf8_unchecked(slice::from_raw_parts(
                        ptr as *const u8,
                        len as usize,
                    ));
                    Some(ret)
                }
                _ => None,
            }
        }
    }

    /// Constructs a new serialised-mode GVariant instance.
    pub fn new_from_bytes<T: StaticVariantType>(bytes: &Bytes) -> Self {
        unsafe {
            from_glib_none(glib_sys::g_variant_new_from_bytes(
                T::static_variant_type().as_ptr() as *const _,
                bytes.to_glib_none().0,
                false.to_glib(),
            ))
        }
    }

    /// Same as `new_from_bytes`, except checks on the passed data are skipped, which makes it a
    /// potentially unsafe function. You should not use this function on data from external sources.
    pub unsafe fn new_from_bytes_trusted<T: StaticVariantType>(bytes: &Bytes) -> Self {
        from_glib_none(glib_sys::g_variant_new_from_bytes(
            T::static_variant_type().as_ptr() as *const _,
            bytes.to_glib_none().0,
            true.to_glib(),
        ))
    }

    /// Returns the serialised form of a GVariant instance.
    pub fn get_data_as_bytes(&self) -> Bytes {
        unsafe { from_glib_full(glib_sys::g_variant_get_data_as_bytes(self.to_glib_none().0)) }
    }
}

unsafe impl Send for Variant {}
unsafe impl Sync for Variant {}

impl fmt::Debug for Variant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Variant")
            .field("ptr", &self.to_glib_none().0)
            .field("type", &self.type_())
            .field("value", &self.to_string())
            .finish()
    }
}

impl fmt::Display for Variant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let serialized: GString = unsafe {
            from_glib_full(glib_sys::g_variant_print(
                self.to_glib_none().0,
                false.to_glib(),
            ))
        };
        f.write_str(&serialized)
    }
}

impl PartialEq for Variant {
    fn eq(&self, other: &Self) -> bool {
        unsafe {
            from_glib(glib_sys::g_variant_equal(
                self.to_glib_none().0 as *const _,
                other.to_glib_none().0 as *const _,
            ))
        }
    }
}

impl Eq for Variant {}

impl PartialOrd for Variant {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        unsafe {
            if glib_sys::g_variant_classify(self.to_glib_none().0)
                != glib_sys::g_variant_classify(other.to_glib_none().0)
            {
                return None;
            }

            if glib_sys::g_variant_is_container(self.to_glib_none().0) != glib_sys::GFALSE {
                return None;
            }

            let res = glib_sys::g_variant_compare(
                self.to_glib_none().0 as *const _,
                other.to_glib_none().0 as *const _,
            );

            Some(res.cmp(&0))
        }
    }
}

impl Hash for Variant {
    fn hash<H: Hasher>(&self, state: &mut H) {
        unsafe { state.write_u32(glib_sys::g_variant_hash(self.to_glib_none().0 as *const _)) }
    }
}

/// Converts to `Variant`.
pub trait ToVariant {
    /// Returns a `Variant` clone of `self`.
    fn to_variant(&self) -> Variant;
}

/// Extracts a value.
pub trait FromVariant: Sized + StaticVariantType {
    /// Tries to extract a value.
    ///
    /// Returns `Some` if the variant's type matches `Self`.
    fn from_variant(variant: &Variant) -> Option<Self>;
}

/// Returns `VariantType` of `Self`.
pub trait StaticVariantType {
    /// Returns the `VariantType` corresponding to `Self`.
    fn static_variant_type() -> Cow<'static, VariantTy>;
}

impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T {
    fn to_variant(&self) -> Variant {
        <T as ToVariant>::to_variant(self)
    }
}

impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        <T as StaticVariantType>::static_variant_type()
    }
}

macro_rules! impl_numeric {
    ($name:ty, $type_str:expr, $new_fn:ident, $get_fn:ident) => {
        impl StaticVariantType for $name {
            fn static_variant_type() -> Cow<'static, VariantTy> {
                unsafe { VariantTy::from_str_unchecked($type_str).into() }
            }
        }

        impl ToVariant for $name {
            fn to_variant(&self) -> Variant {
                unsafe { from_glib_none(glib_sys::$new_fn(*self)) }
            }
        }

        impl FromVariant for $name {
            fn from_variant(variant: &Variant) -> Option<Self> {
                unsafe {
                    if variant.is::<Self>() {
                        Some(glib_sys::$get_fn(variant.to_glib_none().0))
                    } else {
                        None
                    }
                }
            }
        }
    };
}

impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte);
impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16);
impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16);
impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32);
impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32);
impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64);
impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64);
impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double);

impl StaticVariantType for bool {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        unsafe { VariantTy::from_str_unchecked("b").into() }
    }
}

impl ToVariant for bool {
    fn to_variant(&self) -> Variant {
        unsafe { from_glib_none(glib_sys::g_variant_new_boolean(self.to_glib())) }
    }
}

impl FromVariant for bool {
    fn from_variant(variant: &Variant) -> Option<Self> {
        unsafe {
            if variant.is::<Self>() {
                Some(from_glib(glib_sys::g_variant_get_boolean(
                    variant.to_glib_none().0,
                )))
            } else {
                None
            }
        }
    }
}

impl StaticVariantType for String {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        unsafe { VariantTy::from_str_unchecked("s").into() }
    }
}

impl ToVariant for String {
    fn to_variant(&self) -> Variant {
        self[..].to_variant()
    }
}

impl FromVariant for String {
    fn from_variant(variant: &Variant) -> Option<Self> {
        variant.get_str().map(String::from)
    }
}

impl StaticVariantType for str {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        unsafe { VariantTy::from_str_unchecked("s").into() }
    }
}

impl ToVariant for str {
    fn to_variant(&self) -> Variant {
        unsafe { from_glib_none(glib_sys::g_variant_new_take_string(self.to_glib_full())) }
    }
}

impl<T: ToVariant> From<T> for Variant {
    fn from(value: T) -> Variant {
        value.to_variant()
    }
}

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

    macro_rules! unsigned {
        ($name:ident, $ty:ident) => {
            #[test]
            fn $name() {
                let mut n = $ty::max_value();
                while n > 0 {
                    let v = Variant::from(n);
                    assert_eq!(v.get(), Some(n));
                    n /= 2;
                }
            }
        };
    }

    macro_rules! signed {
        ($name:ident, $ty:ident) => {
            #[test]
            fn $name() {
                let mut n = $ty::max_value();
                while n > 0 {
                    let v = Variant::from(n);
                    assert_eq!(v.get(), Some(n));
                    let v = Variant::from(-n);
                    assert_eq!(v.get(), Some(-n));
                    n /= 2;
                }
            }
        };
    }

    unsigned!(test_u8, u8);
    unsigned!(test_u16, u16);
    unsigned!(test_u32, u32);
    unsigned!(test_u64, u64);
    signed!(test_i16, i16);
    signed!(test_i32, i32);
    signed!(test_i64, i64);

    #[test]
    fn test_str() {
        let s = "this is a test";
        let v = Variant::from(s);
        assert_eq!(v.get_str(), Some(s));
    }

    #[test]
    fn test_string() {
        let s = String::from("this is a test");
        let v = Variant::from(s.clone());
        assert_eq!(v.get(), Some(s.clone()));
    }

    #[test]
    fn test_eq() {
        let v1 = Variant::from("this is a test");
        let v2 = Variant::from("this is a test");
        let v3 = Variant::from("test");
        assert_eq!(v1, v2);
        assert!(v1 != v3);
    }

    #[test]
    fn test_hash() {
        let v1 = Variant::from("this is a test");
        let v2 = Variant::from("this is a test");
        let v3 = Variant::from("test");
        let mut set = HashSet::new();
        set.insert(v1);
        assert!(set.contains(&v2));
        assert!(!set.contains(&v3));
    }
}
<commit_msg>variant: Mention VariantDict support<commit_after>// Copyright 2013-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! `Variant` binding and helper traits.
//!
//! [`Variant`](struct.Variant.html) is an immutable dynamically-typed generic
//! container. Its type and value are defined at construction and never change.
//!
//! `Variant` types are described by [`VariantType`](../struct.VariantType.html)
//! "type strings".
//!
//! Although `GVariant` supports arbitrarily complex types, this binding is
//! currently limited to the basic ones: `bool`, `u8`, `i16`, `u16`, `i32`,
//! `u32`, `i64`, `u64`, `f64`, `&str`/`String`, and [`VariantDict`](../struct.VariantDict.html).
//!
//! # Examples
//!
//! ```
//! use glib::prelude::*; // or `use gtk::prelude::*;`
//! use glib::Variant;
//!
//! // Using the `ToVariant` trait.
//! let num = 10.to_variant();
//!
//! // `is` tests the type of the value.
//! assert!(num.is::<i32>());
//!
//! // `get` tries to extract the value.
//! assert_eq!(num.get::<i32>(), Some(10));
//! assert_eq!(num.get::<u32>(), None);
//!
//! // `Variant` implements `From`
//! let hello = Variant::from("Hello!");
//!
//! // `get_str` tries to borrow a string slice.
//! assert_eq!(hello.get_str(), Some("Hello!"));
//! assert_eq!(num.get_str(), None);
//! ```

use bytes::Bytes;
use glib_sys;
use gobject_sys;
use gstring::GString;
use std::borrow::Cow;
use std::cmp::{Eq, Ordering, PartialEq, PartialOrd};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::slice;
use std::str;
use translate::*;
use value;
use StaticType;
use Type;
use Value;
use VariantTy;

glib_wrapper! {
    /// A generic immutable value capable of carrying various types.
    ///
    /// See the [module documentation](index.html) for more details.
    pub struct Variant(Shared<glib_sys::GVariant>);

    match fn {
        ref => |ptr| glib_sys::g_variant_ref_sink(ptr),
        unref => |ptr| glib_sys::g_variant_unref(ptr),
    }
}

impl StaticType for Variant {
    fn static_type() -> Type {
        Type::Variant
    }
}

#[doc(hidden)]
impl<'a> value::FromValueOptional<'a> for Variant {
    unsafe fn from_value_optional(value: &Value) -> Option<Self> {
        from_glib_full(gobject_sys::g_value_dup_variant(
            ToGlibPtr::to_glib_none(value).0,
        ))
    }
}

#[doc(hidden)]
impl value::SetValue for Variant {
    unsafe fn set_value(value: &mut Value, this: &Self) {
        gobject_sys::g_value_set_variant(
            ToGlibPtrMut::to_glib_none_mut(value).0,
            ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(this).0,
        )
    }
}

#[doc(hidden)]
impl value::SetValueOptional for Variant {
    unsafe fn set_value_optional(value: &mut Value, this: Option<&Self>) {
        gobject_sys::g_value_set_variant(
            ToGlibPtrMut::to_glib_none_mut(value).0,
            ToGlibPtr::<*mut glib_sys::GVariant>::to_glib_none(&this).0,
        )
    }
}

impl Variant {
    /// Returns the type of the value.
    pub fn type_(&self) -> &VariantTy {
        unsafe { VariantTy::from_ptr(glib_sys::g_variant_get_type(self.to_glib_none().0)) }
    }

    /// Returns `true` if the type of the value corresponds to `T`.
    #[inline]
    pub fn is<T: StaticVariantType>(&self) -> bool {
        self.type_() == T::static_variant_type()
    }

    /// Tries to extract a value of type `T`.
    ///
    /// Returns `Some` if `T` matches the variant's type.
    #[inline]
    pub fn get<T: FromVariant>(&self) -> Option<T> {
        T::from_variant(self)
    }

    /// Tries to extract a `&str`.
    ///
    /// Returns `Some` if the variant has a string type (`s`, `o` or `g` type
    /// strings).
    pub fn get_str(&self) -> Option<&str> {
        unsafe {
            match self.type_().to_str() {
                "s" | "o" | "g" => {
                    let mut len = 0;
                    let ptr = glib_sys::g_variant_get_string(self.to_glib_none().0, &mut len);
                    let ret = str::from_utf8_unchecked(slice::from_raw_parts(
                        ptr as *const u8,
                        len as usize,
                    ));
                    Some(ret)
                }
                _ => None,
            }
        }
    }

    /// Constructs a new serialised-mode GVariant instance.
    pub fn new_from_bytes<T: StaticVariantType>(bytes: &Bytes) -> Self {
        unsafe {
            from_glib_none(glib_sys::g_variant_new_from_bytes(
                T::static_variant_type().as_ptr() as *const _,
                bytes.to_glib_none().0,
                false.to_glib(),
            ))
        }
    }

    /// Same as `new_from_bytes`, except checks on the passed data are skipped, which makes it a
    /// potentially unsafe function. You should not use this function on data from external sources.
    pub unsafe fn new_from_bytes_trusted<T: StaticVariantType>(bytes: &Bytes) -> Self {
        from_glib_none(glib_sys::g_variant_new_from_bytes(
            T::static_variant_type().as_ptr() as *const _,
            bytes.to_glib_none().0,
            true.to_glib(),
        ))
    }

    /// Returns the serialised form of a GVariant instance.
    pub fn get_data_as_bytes(&self) -> Bytes {
        unsafe { from_glib_full(glib_sys::g_variant_get_data_as_bytes(self.to_glib_none().0)) }
    }
}

unsafe impl Send for Variant {}
unsafe impl Sync for Variant {}

impl fmt::Debug for Variant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Variant")
            .field("ptr", &self.to_glib_none().0)
            .field("type", &self.type_())
            .field("value", &self.to_string())
            .finish()
    }
}

impl fmt::Display for Variant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let serialized: GString = unsafe {
            from_glib_full(glib_sys::g_variant_print(
                self.to_glib_none().0,
                false.to_glib(),
            ))
        };
        f.write_str(&serialized)
    }
}

impl PartialEq for Variant {
    fn eq(&self, other: &Self) -> bool {
        unsafe {
            from_glib(glib_sys::g_variant_equal(
                self.to_glib_none().0 as *const _,
                other.to_glib_none().0 as *const _,
            ))
        }
    }
}

impl Eq for Variant {}

impl PartialOrd for Variant {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        unsafe {
            if glib_sys::g_variant_classify(self.to_glib_none().0)
                != glib_sys::g_variant_classify(other.to_glib_none().0)
            {
                return None;
            }

            if glib_sys::g_variant_is_container(self.to_glib_none().0) != glib_sys::GFALSE {
                return None;
            }

            let res = glib_sys::g_variant_compare(
                self.to_glib_none().0 as *const _,
                other.to_glib_none().0 as *const _,
            );

            Some(res.cmp(&0))
        }
    }
}

impl Hash for Variant {
    fn hash<H: Hasher>(&self, state: &mut H) {
        unsafe { state.write_u32(glib_sys::g_variant_hash(self.to_glib_none().0 as *const _)) }
    }
}

/// Converts to `Variant`.
pub trait ToVariant {
    /// Returns a `Variant` clone of `self`.
    fn to_variant(&self) -> Variant;
}

/// Extracts a value.
pub trait FromVariant: Sized + StaticVariantType {
    /// Tries to extract a value.
    ///
    /// Returns `Some` if the variant's type matches `Self`.
    fn from_variant(variant: &Variant) -> Option<Self>;
}

/// Returns `VariantType` of `Self`.
pub trait StaticVariantType {
    /// Returns the `VariantType` corresponding to `Self`.
    fn static_variant_type() -> Cow<'static, VariantTy>;
}

impl<'a, T: ?Sized + ToVariant> ToVariant for &'a T {
    fn to_variant(&self) -> Variant {
        <T as ToVariant>::to_variant(self)
    }
}

impl<'a, T: ?Sized + StaticVariantType> StaticVariantType for &'a T {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        <T as StaticVariantType>::static_variant_type()
    }
}

macro_rules! impl_numeric {
    ($name:ty, $type_str:expr, $new_fn:ident, $get_fn:ident) => {
        impl StaticVariantType for $name {
            fn static_variant_type() -> Cow<'static, VariantTy> {
                unsafe { VariantTy::from_str_unchecked($type_str).into() }
            }
        }

        impl ToVariant for $name {
            fn to_variant(&self) -> Variant {
                unsafe { from_glib_none(glib_sys::$new_fn(*self)) }
            }
        }

        impl FromVariant for $name {
            fn from_variant(variant: &Variant) -> Option<Self> {
                unsafe {
                    if variant.is::<Self>() {
                        Some(glib_sys::$get_fn(variant.to_glib_none().0))
                    } else {
                        None
                    }
                }
            }
        }
    };
}

impl_numeric!(u8, "y", g_variant_new_byte, g_variant_get_byte);
impl_numeric!(i16, "n", g_variant_new_int16, g_variant_get_int16);
impl_numeric!(u16, "q", g_variant_new_uint16, g_variant_get_uint16);
impl_numeric!(i32, "i", g_variant_new_int32, g_variant_get_int32);
impl_numeric!(u32, "u", g_variant_new_uint32, g_variant_get_uint32);
impl_numeric!(i64, "x", g_variant_new_int64, g_variant_get_int64);
impl_numeric!(u64, "t", g_variant_new_uint64, g_variant_get_uint64);
impl_numeric!(f64, "d", g_variant_new_double, g_variant_get_double);

impl StaticVariantType for bool {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        unsafe { VariantTy::from_str_unchecked("b").into() }
    }
}

impl ToVariant for bool {
    fn to_variant(&self) -> Variant {
        unsafe { from_glib_none(glib_sys::g_variant_new_boolean(self.to_glib())) }
    }
}

impl FromVariant for bool {
    fn from_variant(variant: &Variant) -> Option<Self> {
        unsafe {
            if variant.is::<Self>() {
                Some(from_glib(glib_sys::g_variant_get_boolean(
                    variant.to_glib_none().0,
                )))
            } else {
                None
            }
        }
    }
}

impl StaticVariantType for String {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        unsafe { VariantTy::from_str_unchecked("s").into() }
    }
}

impl ToVariant for String {
    fn to_variant(&self) -> Variant {
        self[..].to_variant()
    }
}

impl FromVariant for String {
    fn from_variant(variant: &Variant) -> Option<Self> {
        variant.get_str().map(String::from)
    }
}

impl StaticVariantType for str {
    fn static_variant_type() -> Cow<'static, VariantTy> {
        unsafe { VariantTy::from_str_unchecked("s").into() }
    }
}

impl ToVariant for str {
    fn to_variant(&self) -> Variant {
        unsafe { from_glib_none(glib_sys::g_variant_new_take_string(self.to_glib_full())) }
    }
}

impl<T: ToVariant> From<T> for Variant {
    fn from(value: T) -> Variant {
        value.to_variant()
    }
}

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

    macro_rules! unsigned {
        ($name:ident, $ty:ident) => {
            #[test]
            fn $name() {
                let mut n = $ty::max_value();
                while n > 0 {
                    let v = Variant::from(n);
                    assert_eq!(v.get(), Some(n));
                    n /= 2;
                }
            }
        };
    }

    macro_rules! signed {
        ($name:ident, $ty:ident) => {
            #[test]
            fn $name() {
                let mut n = $ty::max_value();
                while n > 0 {
                    let v = Variant::from(n);
                    assert_eq!(v.get(), Some(n));
                    let v = Variant::from(-n);
                    assert_eq!(v.get(), Some(-n));
                    n /= 2;
                }
            }
        };
    }

    unsigned!(test_u8, u8);
    unsigned!(test_u16, u16);
    unsigned!(test_u32, u32);
    unsigned!(test_u64, u64);
    signed!(test_i16, i16);
    signed!(test_i32, i32);
    signed!(test_i64, i64);

    #[test]
    fn test_str() {
        let s = "this is a test";
        let v = Variant::from(s);
        assert_eq!(v.get_str(), Some(s));
    }

    #[test]
    fn test_string() {
        let s = String::from("this is a test");
        let v = Variant::from(s.clone());
        assert_eq!(v.get(), Some(s.clone()));
    }

    #[test]
    fn test_eq() {
        let v1 = Variant::from("this is a test");
        let v2 = Variant::from("this is a test");
        let v3 = Variant::from("test");
        assert_eq!(v1, v2);
        assert!(v1 != v3);
    }

    #[test]
    fn test_hash() {
        let v1 = Variant::from("this is a test");
        let v2 = Variant::from("this is a test");
        let v3 = Variant::from("test");
        let mut set = HashSet::new();
        set.insert(v1);
        assert!(set.contains(&v2));
        assert!(!set.contains(&v3));
    }
}
<|endoftext|>


<commit_before>// Filebuffer -- Fast and simple file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! This mod contains the platform-specific implementations for Windows based on the winapi crate.

use std::fs;
use std::io;
use std::os;
use std::os::windows::io::AsRawHandle;
use std::ptr;

extern crate kernel32;
extern crate winapi;

pub struct PlatformData {
    // On Windows, the file must be kept open for the lifetime of the mapping.
    #[allow(dead_code)] // The field is not dead, the destructor is effectful.
    file: fs::File,
    mapping_handle: winapi::winnt::HANDLE,
}

impl Drop for PlatformData {
    fn drop (&mut self) {
        if self.mapping_handle != ptr::null_mut() {
            let success = unsafe { kernel32::CloseHandle(self.mapping_handle) };
            assert!(success != 0);
        }
    }
}

pub fn map_file(file: fs::File) -> io::Result<(*const u8, usize, PlatformData)> {
    let file_handle = file.as_raw_handle();
    let length = try!(file.metadata()).len();

    if length > usize::max_value() as u64 {
        return Err(io::Error::new(io::ErrorKind::Other, "file is larger than address space"));
    }

    let mut platform_data = PlatformData {
        file: file,
        mapping_handle: ptr::null_mut(),
    };

    // Don't try to map anything if the file is empty.
    if length == 0 {
        return Ok((ptr::null(), 0, platform_data));
    }

    // Memory-mapping a file on Windows is a two-step process: first we create a file mapping
    // object, and then we create a view of that mapping in the virtual address space.
    platform_data.mapping_handle = unsafe {
        kernel32::CreateFileMappingW(
            file_handle,
            ptr::null_mut(),              // Use default security policy.
            winapi::winnt::PAGE_READONLY, // The memory will be read-only.
            0, 0,                         // The mapping size is the size of the file.
            ptr::null_mut()               // The mapping does not have a name.
        )
    };

    if platform_data.mapping_handle == ptr::null_mut() {
        return Err(io::Error::last_os_error());
    }

    let result = unsafe {
        kernel32::MapViewOfFile(
            platform_data.mapping_handle,
            winapi::memoryapi::FILE_MAP_READ, // The memory mapping will be read-only.
            0, 0,                             // Start offset of the mapping is 0.
            length                            // Map the entire file.
        )
    };

    if result == ptr::null_mut() {
        Err(io::Error::last_os_error())
    } else {
        Ok((result as *const u8, length as usize, platform_data))
    }
}

pub fn unmap_file(buffer: *const u8, _length: usize) {
    let success = unsafe { kernel32::UnmapViewOfFile(buffer as *mut os::raw::c_void) };
    assert!(success != 0);
}

/// See also `unix::get_resident`.
pub fn get_resident(_buffer: *const u8, _length: usize, residency: &mut [bool]) {
    // As far as I am aware, Windows does not expose a way to query whether pages are resident.
    // There is no equivalent of `mincore()`. The closest thing is `VirtualQuery()`, but the
    // `state` value in the `MEMORY_BASIC_INFORMATION` struct that it fills does not indicate
    // whether the page is resident.

    // Lie and pretend everything is resident.
    for x in residency {
        *x = true;
    }
}

/// See also `unix::prefetch`.
pub fn prefetch(buffer: *const u8, length: usize) {
    let mut entry = winapi::memoryapi::WIN32_MEMORY_RANGE_ENTRY {
        VirtualAddress: buffer as *mut os::raw::c_void,
        NumberOfBytes: length as u64,
    };

    unsafe {
        let current_process_handle = kernel32::GetCurrentProcess();
        kernel32::PrefetchVirtualMemory(
            current_process_handle, // Prefetch for the current process.
            1, &mut entry,          // An array of length 1 that contains `entry`.
            0                       // Reserved flag that must be 0.
        );
    }

    // The return value of `PrefetchVirtualMemory` is ignored. MSDN says the function may fail if
    // the system is under memory pressure. (It is not entirely clear whether "fail" means
    // "returns a nonzero value", but I assume it does.)
}

pub fn get_page_size() -> usize {
    // Fill the `SYSTEM_INFO` struct with zeroes. It will be filled by
    // `GetSystemInfo` later but Rust requires it to be initialized.
    let mut sysinfo = winapi::sysinfoapi::SYSTEM_INFO {
        wProcessorArchitecture: 0,
        wReserved: 0,
        dwPageSize: 0,
        lpMinimumApplicationAddress: ptr::null_mut(),
        lpMaximumApplicationAddress: ptr::null_mut(),
        dwActiveProcessorMask: 0,
        dwNumberOfProcessors: 0,
        dwProcessorType: 0,
        dwAllocationGranularity: 0,
        wProcessorLevel: 0,
        wProcessorRevision: 0
    };
    unsafe { kernel32::GetSystemInfo(&mut sysinfo); }
    sysinfo.dwPageSize as usize
}
<commit_msg>Use correct size type on 32-bit Windows<commit_after>// Filebuffer -- Fast and simple file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! This mod contains the platform-specific implementations for Windows based on the winapi crate.

use std::fs;
use std::io;
use std::os;
use std::os::windows::io::AsRawHandle;
use std::ptr;

extern crate kernel32;
extern crate winapi;

pub struct PlatformData {
    // On Windows, the file must be kept open for the lifetime of the mapping.
    #[allow(dead_code)] // The field is not dead, the destructor is effectful.
    file: fs::File,
    mapping_handle: winapi::winnt::HANDLE,
}

impl Drop for PlatformData {
    fn drop (&mut self) {
        if self.mapping_handle != ptr::null_mut() {
            let success = unsafe { kernel32::CloseHandle(self.mapping_handle) };
            assert!(success != 0);
        }
    }
}

pub fn map_file(file: fs::File) -> io::Result<(*const u8, usize, PlatformData)> {
    let file_handle = file.as_raw_handle();
    let length = try!(file.metadata()).len();

    if length > usize::max_value() as u64 {
        return Err(io::Error::new(io::ErrorKind::Other, "file is larger than address space"));
    }

    let mut platform_data = PlatformData {
        file: file,
        mapping_handle: ptr::null_mut(),
    };

    // Don't try to map anything if the file is empty.
    if length == 0 {
        return Ok((ptr::null(), 0, platform_data));
    }

    // Memory-mapping a file on Windows is a two-step process: first we create a file mapping
    // object, and then we create a view of that mapping in the virtual address space.
    platform_data.mapping_handle = unsafe {
        kernel32::CreateFileMappingW(
            file_handle,
            ptr::null_mut(),              // Use default security policy.
            winapi::winnt::PAGE_READONLY, // The memory will be read-only.
            0, 0,                         // The mapping size is the size of the file.
            ptr::null_mut()               // The mapping does not have a name.
        )
    };

    if platform_data.mapping_handle == ptr::null_mut() {
        return Err(io::Error::last_os_error());
    }

    let result = unsafe {
        kernel32::MapViewOfFile(
            platform_data.mapping_handle,
            winapi::memoryapi::FILE_MAP_READ, // The memory mapping will be read-only.
            0, 0,                             // Start offset of the mapping is 0.
            length                            // Map the entire file.
        )
    };

    if result == ptr::null_mut() {
        Err(io::Error::last_os_error())
    } else {
        Ok((result as *const u8, length as usize, platform_data))
    }
}

pub fn unmap_file(buffer: *const u8, _length: usize) {
    let success = unsafe { kernel32::UnmapViewOfFile(buffer as *mut os::raw::c_void) };
    assert!(success != 0);
}

/// See also `unix::get_resident`.
pub fn get_resident(_buffer: *const u8, _length: usize, residency: &mut [bool]) {
    // As far as I am aware, Windows does not expose a way to query whether pages are resident.
    // There is no equivalent of `mincore()`. The closest thing is `VirtualQuery()`, but the
    // `state` value in the `MEMORY_BASIC_INFORMATION` struct that it fills does not indicate
    // whether the page is resident.

    // Lie and pretend everything is resident.
    for x in residency {
        *x = true;
    }
}

/// See also `unix::prefetch`.
pub fn prefetch(buffer: *const u8, length: usize) {
    let mut entry = winapi::memoryapi::WIN32_MEMORY_RANGE_ENTRY {
        VirtualAddress: buffer as *mut os::raw::c_void,
        NumberOfBytes: length as winapi::basetsd::SIZE_T,
    };

    unsafe {
        let current_process_handle = kernel32::GetCurrentProcess();
        kernel32::PrefetchVirtualMemory(
            current_process_handle, // Prefetch for the current process.
            1, &mut entry,          // An array of length 1 that contains `entry`.
            0                       // Reserved flag that must be 0.
        );
    }

    // The return value of `PrefetchVirtualMemory` is ignored. MSDN says the function may fail if
    // the system is under memory pressure. (It is not entirely clear whether "fail" means
    // "returns a nonzero value", but I assume it does.)
}

pub fn get_page_size() -> usize {
    // Fill the `SYSTEM_INFO` struct with zeroes. It will be filled by
    // `GetSystemInfo` later but Rust requires it to be initialized.
    let mut sysinfo = winapi::sysinfoapi::SYSTEM_INFO {
        wProcessorArchitecture: 0,
        wReserved: 0,
        dwPageSize: 0,
        lpMinimumApplicationAddress: ptr::null_mut(),
        lpMaximumApplicationAddress: ptr::null_mut(),
        dwActiveProcessorMask: 0,
        dwNumberOfProcessors: 0,
        dwProcessorType: 0,
        dwAllocationGranularity: 0,
        wProcessorLevel: 0,
        wProcessorRevision: 0
    };
    unsafe { kernel32::GetSystemInfo(&mut sysinfo); }
    sysinfo.dwPageSize as usize
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io;
use std::io::Write;
use std::mem;
use std::ptr;
use std::cmp::min;

use super::{ReadAt, WriteAt};

use std::os::windows::io::AsRawHandle;

extern crate winapi;
use self::winapi::shared::basetsd::SIZE_T;
use self::winapi::shared::minwindef::{BOOL, DWORD, LPVOID};
use self::winapi::um::fileapi::ReadFile;
use self::winapi::um::handleapi::CloseHandle;
use self::winapi::um::minwinbase::{OVERLAPPED_u, OVERLAPPED_u_s, OVERLAPPED};
use self::winapi::um::sysinfoapi::GetSystemInfo;
use self::winapi::um::winnt::{HANDLE, PAGE_READONLY};
use self::winapi::um::memoryapi::{CreateFileMappingW, MapViewOfFile, UnmapViewOfFile, FILE_MAP_READ};

fn result(e: BOOL) -> io::Result<()> {
    if e == 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

fn overlapped(pos: u64) -> OVERLAPPED {
    OVERLAPPED {
        Internal: 0,
        InternalHigh: 0,
        u: unsafe {
            let mut u: OVERLAPPED_u = mem::zeroed();
            *u.s_mut() = OVERLAPPED_u_s {
                Offset: pos as u32,
                OffsetHigh: (pos >> 32) as u32,
            };
            u
        },
        hEvent: ptr::null_mut(),
    }
}

fn allocation_granularity() -> u64 {
    unsafe {
        let mut info = mem::zeroed();
        GetSystemInfo(&mut info);
        info.dwAllocationGranularity as u64
    }
}

impl ReadAt for File {
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }

        let file_len = self.metadata()?.len();
        if (usize::max_value() as u64) < file_len {
            return Err(io::Error::new(io::ErrorKind::InvalidData, "file length overflows usize"));
        }

        let len = min(file_len, pos + buf.len() as u64) - pos;

        unsafe {
            let alignment = pos % allocation_granularity();
            let aligned_pos = pos - alignment;
            let aligned_len = len + alignment;

            let mapping = CreateFileMappingW(
                self.as_raw_handle() as HANDLE,
                ptr::null_mut(),
                PAGE_READONLY,
                0,
                0,
                ptr::null(),
            );

            if mapping.is_null() {
                return Err(io::Error::last_os_error());
            }

            let aligned_ptr = MapViewOfFile(
                mapping,
                FILE_MAP_READ,
                (aligned_pos >> 32) as DWORD,
                (aligned_pos & 0xffff_ffff) as DWORD,
                aligned_len as SIZE_T,
            );

            CloseHandle(mapping);

            if aligned_ptr.is_null() {
                return Err(io::Error::last_os_error());
            }

            let ptr = (aligned_ptr as *const u8).offset(alignment as isize);
            ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), len as usize);

            if 0 == UnmapViewOfFile(aligned_ptr) {
                return Err(io::Error::last_os_error());
            }
        }

        Ok(buf.len())
    }
}

impl WriteAt for File {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        let mut bytes: DWORD = 0;
        let mut ov = overlapped(pos);
        result(unsafe {
            ReadFile(
                self.as_raw_handle() as HANDLE,
                buf.as_ptr() as LPVOID,
                buf.len() as DWORD,
                &mut bytes,
                &mut ov,
            )
        })?;
        Ok(bytes as usize)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(self)
    }
}
<commit_msg>fix return of read_at on windows<commit_after>use std::fs::File;
use std::io;
use std::io::Write;
use std::mem;
use std::ptr;
use std::cmp::min;

use super::{ReadAt, WriteAt};

use std::os::windows::io::AsRawHandle;

extern crate winapi;
use self::winapi::shared::basetsd::SIZE_T;
use self::winapi::shared::minwindef::{BOOL, DWORD, LPVOID};
use self::winapi::um::fileapi::ReadFile;
use self::winapi::um::handleapi::CloseHandle;
use self::winapi::um::minwinbase::{OVERLAPPED_u, OVERLAPPED_u_s, OVERLAPPED};
use self::winapi::um::sysinfoapi::GetSystemInfo;
use self::winapi::um::winnt::{HANDLE, PAGE_READONLY};
use self::winapi::um::memoryapi::{CreateFileMappingW, MapViewOfFile, UnmapViewOfFile, FILE_MAP_READ};

fn result(e: BOOL) -> io::Result<()> {
    if e == 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

fn overlapped(pos: u64) -> OVERLAPPED {
    OVERLAPPED {
        Internal: 0,
        InternalHigh: 0,
        u: unsafe {
            let mut u: OVERLAPPED_u = mem::zeroed();
            *u.s_mut() = OVERLAPPED_u_s {
                Offset: pos as u32,
                OffsetHigh: (pos >> 32) as u32,
            };
            u
        },
        hEvent: ptr::null_mut(),
    }
}

fn allocation_granularity() -> u64 {
    unsafe {
        let mut info = mem::zeroed();
        GetSystemInfo(&mut info);
        info.dwAllocationGranularity as u64
    }
}

impl ReadAt for File {
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize> {
        if buf.is_empty() {
            return Ok(0);
        }

        let file_len = self.metadata()?.len();
        if (usize::max_value() as u64) < file_len {
            return Err(io::Error::new(io::ErrorKind::InvalidData, "file length overflows usize"));
        }

        let len = min(file_len, pos + buf.len() as u64) - pos;

        unsafe {
            let alignment = pos % allocation_granularity();
            let aligned_pos = pos - alignment;
            let aligned_len = len + alignment;

            let mapping = CreateFileMappingW(
                self.as_raw_handle() as HANDLE,
                ptr::null_mut(),
                PAGE_READONLY,
                0,
                0,
                ptr::null(),
            );

            if mapping.is_null() {
                return Err(io::Error::last_os_error());
            }

            let aligned_ptr = MapViewOfFile(
                mapping,
                FILE_MAP_READ,
                (aligned_pos >> 32) as DWORD,
                (aligned_pos & 0xffff_ffff) as DWORD,
                aligned_len as SIZE_T,
            );

            CloseHandle(mapping);

            if aligned_ptr.is_null() {
                return Err(io::Error::last_os_error());
            }

            let ptr = (aligned_ptr as *const u8).offset(alignment as isize);
            ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), len as usize);

            if 0 == UnmapViewOfFile(aligned_ptr) {
                return Err(io::Error::last_os_error());
            }
        }

        Ok(len as usize)
    }
}

impl WriteAt for File {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize> {
        let mut bytes: DWORD = 0;
        let mut ov = overlapped(pos);
        result(unsafe {
            ReadFile(
                self.as_raw_handle() as HANDLE,
                buf.as_ptr() as LPVOID,
                buf.len() as DWORD,
                &mut bytes,
                &mut ov,
            )
        })?;
        Ok(bytes as usize)
    }

    fn flush(&mut self) -> io::Result<()> {
        Write::flush(self)
    }
}
<|endoftext|>