<commit_before>
use std;
use std::io;
use std::mem;
use std::ptr;
use std::time::Duration;
use std::fs::{File, OpenOptions};
use std::os::unix::io::AsRawFd;
use std::collections::VecDeque;

use futures;

use libc;
use glob::glob;
use super::bindings::bpf;

struct Config {
    buffer_size: usize,
    read_timeout: Option<Duration>,
    write_timeout: Option<Duration>,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            buffer_size: 4096,
            read_timeout: None,
            write_timeout: None,
        }
    }
}

fn pselect(nfds: &File,
           readfds: Option<&mut libc::fd_set>,
           writefds: Option<&mut libc::fd_set>,
           timeout: Option<Duration>)
           -> io::Result<()> {
    let fd = nfds.as_raw_fd();

    let timeout = timeout.map(|d| {
        libc::timespec {
            tv_sec: d.as_secs() as libc::time_t,
            tv_nsec: d.subsec_nanos() as libc::c_long,
        }
    });

    let ret = unsafe {
        libc::pselect(fd + 1,
                      readfds.map(|to| to as *mut libc::fd_set)
                          .unwrap_or(ptr::null_mut()),
                      writefds.map(|to| to as *mut libc::fd_set)
                          .unwrap_or(ptr::null_mut()),
                      ptr::null_mut(),
                      timeout.as_ref()
                          .map(|to| to as *const libc::timespec)
                          .unwrap_or(ptr::null()),
                      ptr::null())
    };

    match ret {
        -1 => Err(io::Error::last_os_error()), // Error occured!
        0 => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")),
        _ => Ok(()),
    }

}

pub struct Tap {
    fd_set: libc::fd_set,
    config: Config,
    file: File,
}

impl Tap {
    pub fn new(interface: &str) -> io::Result<Self> {
        fn open() -> io::Result<File> {
            // On macOS: bpf exposed as /dev/bpf###
            for entry in glob("/dev/bpf*").expect("Failed to read glob pattern") {
                if let Some(file) = entry.ok().and_then(|path| {
                    OpenOptions::new()
                        .read(true)
                        .write(true)
                        .open(&path)
                        .ok()
                }) {
                    return Ok(file);
                }
            }

            Err(io::Error::last_os_error())
        }

        let file = try!(open());
        let config = Config::default();

        {
            let fd = file.as_raw_fd();

            let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
            for (i, c) in interface.bytes().enumerate() {
                iface.ifr_name[i] = c as i8;
            }

            // Set the buffer length
            let buflen = config.buffer_size as libc::c_uint;
            ioctl!(fd, bpf::BIOCSBLEN, &buflen);

            // Set the interface to use
            ioctl!(fd, bpf::BIOCSETIF, &iface);

            let yes: libc::c_uint = 1;

            // Return from read as soon as packets are available - don't wait to fill the buffer
            ioctl!(fd, bpf::BIOCIMMEDIATE, &yes);

            // Get the device type
            let mut dlt: libc::c_uint = 0;
            ioctl!(fd, bpf::BIOCGDLT, &mut dlt);

            match dlt {
                bpf::DLT_NULL => {
                    // Allow packets to be read back after they are written
                    ioctl!(fd, bpf::BIOCSSEESENT, &yes);
                }
                _ => {
                    // Don't fill in source MAC
                    ioctl!(fd, bpf::BIOCSHDRCMPLT, &yes);
                }
            }

            {
                use super::bindings::bpf::*;

                let ethertype_ip = 0x0800;
                let ipproto_tcp = 6;

                // Allow all!
                // let instructions = vec![bpf::BPF_STMT(bpf::BPF_RET + bpf::BPF_K, std::u32::MAX)];

                // Only IPv4/TCP
                let instructions = vec![BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
                                        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ethertype_ip, 0, 3),
                                        BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
                                        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ipproto_tcp, 0, 1),
                                        BPF_STMT(BPF_RET + BPF_K, std::u32::MAX),
                                        BPF_STMT(BPF_RET + BPF_K, 0)];

                let program = bpf_program {
                    bf_len: instructions.len() as u32,
                    bf_insns: instructions.as_ptr(),
                };

                ioctl!(fd, BIOCSETF, &program);
            }

            // Enable nonblocking
            fcntl!(fd, libc::F_SETFL, libc::O_NONBLOCK);
        }

        let mut fd_set: libc::fd_set = unsafe { mem::zeroed() };
        unsafe {
            let fd = file.as_raw_fd();
            let set = &mut fd_set as *mut libc::fd_set;
            libc::FD_ZERO(set);
            libc::FD_SET(fd, set);
        }

        Ok(Tap {
            fd_set: fd_set,
            config: config,
            file: file,
        })
    }

    pub fn stream(&mut self) -> Stream {
        Stream {
            buffer: vec![0u8; self.config.buffer_size],
            packets: VecDeque::new(),
            fd_set: &mut self.fd_set,
            file: &self.file,
            timeout: self.config.read_timeout,
        }
    }

    pub fn sink(&mut self) -> Sink {
        Sink {
            fd_set: &mut self.fd_set,
            file: &self.file,
            timeout: self.config.write_timeout,
        }
    }
}

pub struct Stream<'a> {
    buffer: Vec<u8>,
    packets: VecDeque<(usize, usize)>,
    fd_set: &'a mut libc::fd_set,
    file: &'a File,
    timeout: Option<Duration>,
}

impl<'a> futures::stream::Stream for Stream<'a> {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
        if self.packets.is_empty() {
            let buffer = &mut self.buffer[..];

            try!(pselect(self.file, Some(self.fd_set), None, self.timeout));

            use std::io::Read;
            let buflen = match self.file.read(buffer) {
                Ok(len) if len > 0 => len,
                _ => return Err(io::Error::last_os_error()),
            };

            let mut ptr = buffer.as_mut_ptr();
            let end = unsafe { buffer.as_ptr().offset(buflen as isize) };
            while (ptr as *const u8) < end {
                unsafe {
                    let packet: *const bpf::bpf_hdr = mem::transmute(ptr);

                    let start = ptr as isize + (*packet).bh_hdrlen as isize -
                                buffer.as_ptr() as isize;
                    self.packets.push_back((start as usize, (*packet).bh_caplen as usize));

                    let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                    ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                }
            }
        }

        let buffer = &self.buffer[..];
        Ok(self.packets
            .pop_front()
            .map(move |(start, len)| Vec::from(&buffer[start..start + len]))
            .into())
    }
}

pub struct Sink<'a> {
    fd_set: &'a mut libc::fd_set,
    file: &'a File,
    timeout: Option<Duration>,
}

impl<'a> Sink<'a> {
    // Send `packet` on tapped device (packet should contain data starting @ network layer)
    pub fn send(&mut self, packet: &[u8]) -> io::Result<()> {
        use std::io::Write;

        try!(pselect(self.file, None, Some(self.fd_set), self.timeout));
        try!(self.file.write_all(packet));
        Ok(())
    }
}
<commit_msg>Switch to 'allow all' filter program (by default). (#2)<commit_after>
use std;
use std::io;
use std::mem;
use std::ptr;
use std::time::Duration;
use std::fs::{File, OpenOptions};
use std::os::unix::io::AsRawFd;
use std::collections::VecDeque;

use futures;

use libc;
use glob::glob;
use super::bindings::bpf;

struct Config {
    buffer_size: usize,
    read_timeout: Option<Duration>,
    write_timeout: Option<Duration>,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            buffer_size: 4096,
            read_timeout: None,
            write_timeout: None,
        }
    }
}

fn pselect(nfds: &File,
           readfds: Option<&mut libc::fd_set>,
           writefds: Option<&mut libc::fd_set>,
           timeout: Option<Duration>)
           -> io::Result<()> {
    let fd = nfds.as_raw_fd();

    let timeout = timeout.map(|d| {
        libc::timespec {
            tv_sec: d.as_secs() as libc::time_t,
            tv_nsec: d.subsec_nanos() as libc::c_long,
        }
    });

    let ret = unsafe {
        libc::pselect(fd + 1,
                      readfds.map(|to| to as *mut libc::fd_set)
                          .unwrap_or(ptr::null_mut()),
                      writefds.map(|to| to as *mut libc::fd_set)
                          .unwrap_or(ptr::null_mut()),
                      ptr::null_mut(),
                      timeout.as_ref()
                          .map(|to| to as *const libc::timespec)
                          .unwrap_or(ptr::null()),
                      ptr::null())
    };

    match ret {
        -1 => Err(io::Error::last_os_error()), // Error occured!
        0 => Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")),
        _ => Ok(()),
    }

}

pub struct Tap {
    fd_set: libc::fd_set,
    config: Config,
    file: File,
}

impl Tap {
    pub fn new(interface: &str) -> io::Result<Self> {
        fn open() -> io::Result<File> {
            // On macOS: bpf exposed as /dev/bpf###
            for entry in glob("/dev/bpf*").expect("Failed to read glob pattern") {
                if let Some(file) = entry.ok().and_then(|path| {
                    OpenOptions::new()
                        .read(true)
                        .write(true)
                        .open(&path)
                        .ok()
                }) {
                    return Ok(file);
                }
            }

            Err(io::Error::last_os_error())
        }

        let file = try!(open());
        let config = Config::default();

        {
            let fd = file.as_raw_fd();

            let mut iface: bpf::ifreq = unsafe { mem::zeroed() };
            for (i, c) in interface.bytes().enumerate() {
                iface.ifr_name[i] = c as i8;
            }

            // Set the buffer length
            let buflen = config.buffer_size as libc::c_uint;
            ioctl!(fd, bpf::BIOCSBLEN, &buflen);

            // Set the interface to use
            ioctl!(fd, bpf::BIOCSETIF, &iface);

            let yes: libc::c_uint = 1;

            // Return from read as soon as packets are available - don't wait to fill the buffer
            ioctl!(fd, bpf::BIOCIMMEDIATE, &yes);

            // Get the device type
            let mut dlt: libc::c_uint = 0;
            ioctl!(fd, bpf::BIOCGDLT, &mut dlt);

            match dlt {
                bpf::DLT_NULL => {
                    // Allow packets to be read back after they are written
                    ioctl!(fd, bpf::BIOCSSEESENT, &yes);
                }
                _ => {
                    // Don't fill in source MAC
                    ioctl!(fd, bpf::BIOCSHDRCMPLT, &yes);
                }
            }

            {
                use super::bindings::bpf::*;

                // Allow all!
                let instructions = vec![bpf::BPF_STMT(bpf::BPF_RET + bpf::BPF_K, std::u32::MAX)];

                // Only IPv4/TCP
                // let ethertype_ip = 0x0800;
                // let ipproto_tcp = 6;
                // let instructions = vec![BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
                //                         BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ethertype_ip, 0, 3),
                //                         BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
                //                         BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ipproto_tcp, 0, 1),
                //                         BPF_STMT(BPF_RET + BPF_K, std::u32::MAX),
                //                         BPF_STMT(BPF_RET + BPF_K, 0)];

                ioctl!(fd,
                       BIOCSETF,
                       &bpf_program {
                           bf_len: instructions.len() as u32,
                           bf_insns: instructions.as_ptr(),
                       });
            }

            // Enable nonblocking
            fcntl!(fd, libc::F_SETFL, libc::O_NONBLOCK);
        }

        let mut fd_set: libc::fd_set = unsafe { mem::zeroed() };
        unsafe {
            let fd = file.as_raw_fd();
            let set = &mut fd_set as *mut libc::fd_set;
            libc::FD_ZERO(set);
            libc::FD_SET(fd, set);
        }

        Ok(Tap {
            fd_set: fd_set,
            config: config,
            file: file,
        })
    }

    pub fn stream(&mut self) -> Stream {
        Stream {
            buffer: vec![0u8; self.config.buffer_size],
            packets: VecDeque::new(),
            fd_set: &mut self.fd_set,
            file: &self.file,
            timeout: self.config.read_timeout,
        }
    }

    pub fn sink(&mut self) -> Sink {
        Sink {
            fd_set: &mut self.fd_set,
            file: &self.file,
            timeout: self.config.write_timeout,
        }
    }
}

pub struct Stream<'a> {
    buffer: Vec<u8>,
    packets: VecDeque<(usize, usize)>,
    fd_set: &'a mut libc::fd_set,
    file: &'a File,
    timeout: Option<Duration>,
}

impl<'a> futures::stream::Stream for Stream<'a> {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
        if self.packets.is_empty() {
            let buffer = &mut self.buffer[..];

            try!(pselect(self.file, Some(self.fd_set), None, self.timeout));

            use std::io::Read;
            let buflen = match self.file.read(buffer) {
                Ok(len) if len > 0 => len,
                _ => return Err(io::Error::last_os_error()),
            };

            let mut ptr = buffer.as_mut_ptr();
            let end = unsafe { buffer.as_ptr().offset(buflen as isize) };
            while (ptr as *const u8) < end {
                unsafe {
                    let packet: *const bpf::bpf_hdr = mem::transmute(ptr);

                    let start = ptr as isize + (*packet).bh_hdrlen as isize -
                                buffer.as_ptr() as isize;
                    self.packets.push_back((start as usize, (*packet).bh_caplen as usize));

                    let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                    ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                }
            }
        }

        let buffer = &self.buffer[..];
        Ok(self.packets
            .pop_front()
            .map(move |(start, len)| Vec::from(&buffer[start..start + len]))
            .into())
    }
}

pub struct Sink<'a> {
    fd_set: &'a mut libc::fd_set,
    file: &'a File,
    timeout: Option<Duration>,
}

impl<'a> Sink<'a> {
    // Send `packet` on tapped device (packet should contain data starting @ network layer)
    pub fn send(&mut self, packet: &[u8]) -> io::Result<()> {
        use std::io::Write;

        try!(pselect(self.file, None, Some(self.fd_set), self.timeout));
        try!(self.file.write_all(packet));
        Ok(())
    }
}
<|endoftext|>


<commit_before>fn main() {
    loop {
        println!("Hello, world!");
        break;
    }

    let mut number = 3;
    while number != 0 {
        println!("{}", number);
        number = number - 1;
    }

    println!("LIFTOFF!!!");

    let a = [10, 20, 30, 40, 50];
    // both safety & no overhead compare to maintain index of
    // array by while
    for element in a.iter() {
        println!("the value is {}", element);
    }

    // for is most commonly used, replace while:
    for count in (1..4).rev() {
        println!("{}!", count);
    }
    println!("LIFTOFF!!!");

    for n in 1..10 {
        println!("fib {} is {}", n, fib(n));
    }
}

fn fib(n: i32) -> i32 {
    let mut cur = 1;
    let mut nxt = 1;
    let mut res = 0;

    for _ in 0..n {
        res = cur;
        cur = nxt;
        nxt = res + nxt;
    }

    res
}
<commit_msg>Try to impl a functional style version of fib.<commit_after>fn main() {
    loop {
        println!("Hello, world!");
        break;
    }

    let mut number = 3;
    while number != 0 {
        println!("{}", number);
        number = number - 1;
    }

    println!("LIFTOFF!!!");

    let a = [10, 20, 30, 40, 50];
    // both safety & no overhead compare to maintain index of
    // array by while
    for element in a.iter() {
        println!("the value is {}", element);
    }

    // for is most commonly used, replace while:
    for count in (1..4).rev() {
        println!("{}!", count);
    }
    println!("LIFTOFF!!!");

    for n in 1..10 {
        println!("fib {} is {}", n, fib(n));
    }
    let mut f_fib = make_fib();
    for n in 1..10 {
        println!("f_fib {} is {}", n, f_fib(n));
    }
    for n in 1..10 {
        println!("f_fib {} is {}", n, f_fib(n));
    }
}

fn fib(n: i32) -> i32 {
    let mut cur = 1;
    let mut nxt = 1;
    let mut res = 0;

    for _ in 0..n {
        res = cur;
        cur = nxt;
        nxt = res + nxt;
    }

    res
}

// Just a try before know anything about borrow
// help from compiler's hint
fn make_fib() -> Box<FnMut(i32) -> i32> {
    let mut rec = vec![1, 1];
    let mut sz = 2;
    Box::new(move |n| {
        if n <= sz {
            println!("hit!");
            rec[(n - 1) as usize]
        } else {
            println!("miss!");
            for i in sz..n {
                let p2 = rec[(i - 2) as usize];
                let p1 = rec[(i - 1) as usize];
                rec.push(p1 + p2);
                sz = sz + 1;
            }
            rec[(n - 1) as usize]
        }
    })
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Common unit test utility methods

use crate::error::Result;
use arrow::datatypes::{DataType, Field, Schema};
use std::env;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter};
use std::path::Path;
use std::sync::Arc;

/// Get the value of the ARROW_TEST_DATA environment variable
pub fn arrow_testdata_path() -> String {
    env::var("ARROW_TEST_DATA").expect("ARROW_TEST_DATA not defined")
}

/// Generated partitioned copy of a CSV file
pub fn create_partitioned_csv(filename: &str, partitions: usize) -> Result<String> {
    let testdata = arrow_testdata_path();
    let path = format!("{}/csv/{}", testdata, filename);

    let mut dir = env::temp_dir();
    dir.push(&format!("{}-{}", filename, partitions));

    if Path::new(&dir).exists() {
        fs::remove_dir_all(&dir).unwrap();
    }
    fs::create_dir(dir.clone()).unwrap();

    let mut writers = vec![];
    for i in 0..partitions {
        let mut filename = dir.clone();
        filename.push(format!("part{}.csv", i));
        let writer = BufWriter::new(File::create(&filename).unwrap());
        writers.push(writer);
    }

    let f = File::open(&path)?;
    let f = BufReader::new(f);
    let mut i = 0;
    for line in f.lines() {
        let line = line.unwrap();

        if i == 0 {
            // write header to all partitions
            for w in writers.iter_mut() {
                w.write(line.as_bytes()).unwrap();
                w.write(b"\n").unwrap();
            }
        } else {
            // write data line to single partition
            let partition = i % partitions;
            writers[partition].write(line.as_bytes()).unwrap();
            writers[partition].write(b"\n").unwrap();
        }

        i += 1;
    }
    for w in writers.iter_mut() {
        w.flush().unwrap();
    }

    Ok(dir.as_os_str().to_str().unwrap().to_string())
}

/// Get the schema for the aggregate_test_* csv files
pub fn aggr_test_schema() -> Arc<Schema> {
    Arc::new(Schema::new(vec![
        Field::new("c1", DataType::Utf8, false),
        Field::new("c2", DataType::UInt32, false),
        Field::new("c3", DataType::Int8, false),
        Field::new("c4", DataType::Int16, false),
        Field::new("c5", DataType::Int32, false),
        Field::new("c6", DataType::Int64, false),
        Field::new("c7", DataType::UInt8, false),
        Field::new("c8", DataType::UInt16, false),
        Field::new("c9", DataType::UInt32, false),
        Field::new("c10", DataType::UInt64, false),
        Field::new("c11", DataType::Float32, false),
        Field::new("c12", DataType::Float64, false),
        Field::new("c13", DataType::Utf8, false),
    ]))
}
<commit_msg>ARROW-6565: [Rust] [DataFusion] Fix intermittent test failure<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Common unit test utility methods

use crate::error::Result;
use arrow::datatypes::{DataType, Field, Schema};
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter};
use std::sync::Arc;
use tempdir::TempDir;

/// Get the value of the ARROW_TEST_DATA environment variable
pub fn arrow_testdata_path() -> String {
    env::var("ARROW_TEST_DATA").expect("ARROW_TEST_DATA not defined")
}

/// Generated partitioned copy of a CSV file
pub fn create_partitioned_csv(filename: &str, partitions: usize) -> Result<String> {
    let testdata = arrow_testdata_path();
    let path = format!("{}/csv/{}", testdata, filename);

    let tmp_dir = TempDir::new("create_partitioned_csv")?;

    let mut writers = vec![];
    for i in 0..partitions {
        let filename = format!("partition-{}.csv", i);
        let filename = tmp_dir.path().join(&filename);

        let writer = BufWriter::new(File::create(&filename).unwrap());
        writers.push(writer);
    }

    let f = File::open(&path)?;
    let f = BufReader::new(f);
    let mut i = 0;
    for line in f.lines() {
        let line = line.unwrap();

        if i == 0 {
            // write header to all partitions
            for w in writers.iter_mut() {
                w.write(line.as_bytes()).unwrap();
                w.write(b"\n").unwrap();
            }
        } else {
            // write data line to single partition
            let partition = i % partitions;
            writers[partition].write(line.as_bytes()).unwrap();
            writers[partition].write(b"\n").unwrap();
        }

        i += 1;
    }
    for w in writers.iter_mut() {
        w.flush().unwrap();
    }

    Ok(tmp_dir.into_path().to_str().unwrap().to_string())
}

/// Get the schema for the aggregate_test_* csv files
pub fn aggr_test_schema() -> Arc<Schema> {
    Arc::new(Schema::new(vec![
        Field::new("c1", DataType::Utf8, false),
        Field::new("c2", DataType::UInt32, false),
        Field::new("c3", DataType::Int8, false),
        Field::new("c4", DataType::Int16, false),
        Field::new("c5", DataType::Int32, false),
        Field::new("c6", DataType::Int64, false),
        Field::new("c7", DataType::UInt8, false),
        Field::new("c8", DataType::UInt16, false),
        Field::new("c9", DataType::UInt32, false),
        Field::new("c10", DataType::UInt64, false),
        Field::new("c11", DataType::Float32, false),
        Field::new("c12", DataType::Float64, false),
        Field::new("c13", DataType::Utf8, false),
    ]))
}
<|endoftext|>


<commit_before>
//! Various methods for computing with vectors.

use internal::{
    Area,
    Line,
    Matrix2d,
    Polygon,
    Ray,
    Rectangle,
    Vec2d,
    Triangle
};
use modular_index::{previous};

/// Multiplies two matrices.
#[inline(always)]
pub fn multiply(m: Matrix2d, b: Matrix2d) -> Matrix2d {
    [m[0]*b[0]+m[1]*b[3]+m[2]*0.0,  m[0]*b[1]+m[1]*b[4]+m[2]*0.0,  m[0]*b[2]+m[1]*b[5]+m[2]*1.0,
     m[3]*b[0]+m[4]*b[3]+m[5]*0.0,  m[3]*b[1]+m[4]*b[4]+m[5]*0.0,  m[3]*b[2]+m[4]*b[5]+m[5]*1.0]
}

/// Creates a translation matrix.
#[inline(always)]
pub fn translate(x: f64, y: f64) -> Matrix2d {
    [1.0, 0.0, x,
     0.0, 1.0, y]
}

/// Creates a rotation matrix.
#[inline(always)]
pub fn rotate_radians(angle: f64) -> Matrix2d {
    let c = angle.cos();
    let s = angle.sin();
    [c, -s, 0.0,
     s,  c, 0.0]
}

/// Orients x axis to look at point.
///
/// Leaves x axis unchanged if the point to look at is the origin.
#[inline(always)]
pub fn orient(x: f64, y: f64) -> Matrix2d {
    let len = x * x + y * y;
    if len == 0.0 {
        return [1.0, 0.0, 0.0,
                0.0, 1.0, 0.0]
    }

    let len = len.sqrt();
    let c = x / len;
    let s = y / len;
    [c, -s, 0.0,
     s,  c, 0.0]
}

/// Create a scale matrix.
#[inline(always)]
pub fn scale(sx: f64, sy: f64) -> Matrix2d {
    [sx, 0.0, 0.0,
     0.0, sy, 0.0]
}

/// Create a shear matrix.
#[inline(always)]
pub fn shear(sx: f64, sy: f64) -> Matrix2d {
    [1.0, sx, 0.0,
     sy, 1.0, 0.0]
}

/// Create an identity matrix.
#[inline(always)]
pub fn identity() -> Matrix2d {
    [1.0, 0.0, 0.0,
     0.0, 1.0, 0.0]
}

/// Compute the shortest vector from point to ray.
/// A ray stores starting point and directional vector.
#[inline(always)]
pub fn separation(ray: Ray, x: f64, y: f64) -> [f64, ..2] {
    // Get the directional vector.
    let (dir_x, dir_y) = (ray[2], ray[3]);
    // Get displacement vector from point.
    let (dx, dy) = (ray[0] - x, ray[1] - y);
    // Compute the component of position in ray direction.
    let dot = dir_x * x + dir_y * y;
    // The directional vector multiplied with the dot gives us a parallel vector.
    // When we subtract this from the displacement we get a vector normal to the ray.
    // This is the shortest vector from the point to the ray.
    [dx - dot * dir_x, dy - dot * dir_y]
}

/// Returns the least separation out of four.
/// Each seperation can be computed using `separation` function.
/// The separation returned can be used to solve collision of rectangles.
#[inline(always)]
pub fn least_separation_4(
    sep1: Vec2d,
    sep2: Vec2d,
    sep3: Vec2d,
    sep4: Vec2d
) -> Vec2d {
    let dot1 = sep1[0] * sep1[0] + sep1[1] * sep1[1];
    let dot2 = sep2[0] * sep2[0] + sep2[1] * sep2[1];
    let dot3 = sep3[0] * sep3[0] + sep3[1] * sep3[1];
    let dot4 = sep4[0] * sep4[0] + sep4[1] * sep4[1];
    // Search for the smallest dot product.
    if dot1 < dot2 {
        if dot3 < dot4 {
            if dot1 < dot3 { sep1 } else { sep3 }
        } else {
            if dot1 < dot4 { sep1 } else { sep4 }
        }
    } else {
        if dot3 < dot4 {
            if dot2 < dot3 { sep2 } else { sep3 }
        } else {
            if dot2 < dot4 { sep2 } else { sep4 }
        }
    }
}

/// Shrinks a rectangle by a factor on all sides.
#[inline(always)]
pub fn margin_rectangle(rect: Rectangle, m: f64) -> Rectangle {
    let w = rect[2] - 2.0 * m;
    let h = rect[3] - 2.0 * m;
    let (x, w) = if w < 0.0 { (rect[0] + 0.5 * rect[2], 0.0) } else { (rect[0] + m, w) };
    let (y, h) = if h < 0.0 { (rect[1] + 0.5 * rect[3], 0.0) } else { (rect[1] + m, h) };
    [x, y, w, h]
}

/// Computes a relative rectangle using the rectangle as a tile.
#[inline(always)]
pub fn relative_rectangle(rect: Rectangle, x: f64, y: f64) -> Rectangle {
    [rect[0] + x * rect[2], rect[1] + y * rect[3], rect[2], rect[3]]
}

/// Computes modular offset safely for numbers.
#[inline(always)]
pub fn modular_offset<T: Add<T, T> + Rem<T, T>>(
    n: &T,
    i: &T,
    off: &T
) -> T {
    (*i + (*off % *n + *n)) % *n
}

#[test]
fn test_modular_offset() {
    assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &-1.0_f64), 2.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &-1.0_f64), 0.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &-1.0_f64), 1.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &-1.0_f64), 2.0_f64);

    assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &1.0_f64), 1.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &1.0_f64), 2.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &1.0_f64), 0.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &1.0_f64), 1.0_f64);
}

/// Computes the area and centroid of a simple polygon.
///
/// A simple polygon is one that does not intersect itself.
/// Source: http://en.wikipedia.org/wiki/Polygon_area#Simple_polygons
pub fn area_centroid(polygon: Polygon) -> (Area, Vec2d) {
    let n = polygon.len() / 2;
    let mut sum = 0.0_f64;
    let (mut cx, mut cy) = (0.0_f64, 0.0_f64);
    for i in range(0, n) {
        let (qx, qy) = (polygon[i * 2], polygon[i * 2 + 1]);
        let p_i = previous(n, i);
        let (px, py) = (polygon[p_i * 2], polygon[p_i * 2 + 1]);
        let cross = px * qy - qx * py;
        cx += (px + qx) * cross;
        cy += (py + qy) * cross;
        sum += cross;
    }

    let area = 0.5 * sum;
    // 'cx / (6.0 * area)' = 'cx / (3.0 * sum)'
    let centroid = [cx / (3.0 * sum), cy / (3.0 * sum)];
    (area, centroid)
}

/// Computes area of a simple polygon.
///
/// A simple polygon is one that does not intersect itself.
#[inline(always)]
pub fn area(polygon: Polygon) -> f64 {
    let (res, _) = area_centroid(polygon);
    res
}

/// Computes centroid of a simple polygon.
///
/// A simple polygon is one that does not intersect itself.
#[inline(always)]
pub fn centroid(polygon: Polygon) -> Vec2d {
    let (_, res) = area_centroid(polygon);
    res
}

/// Returns a number that tells which side it is relative to a line.
///
/// Computes the cross product of the vector that gives the line
/// with the vector between point and starting point of line.
/// One side of the line has opposite sign of the other.
#[inline(always)]
pub fn line_side(line: Line, x: f64, y: f64) -> f64 {
    let (ax, ay) = (line[0], line[1]);
    let (bx, by) = (line[2], line[3]);
    (bx - ax) * (y - ay) - (by - ay) * (x - ax)
}

/// Returns true if point is inside triangle.
///
/// This is done by computing a `side` number for each edge.
/// If the number is inside if it is on the same side for all edges.
/// Might break for very small triangles.
pub fn inside_triangle(triangle: Triangle, x: f64, y: f64) -> bool {
    let (ax, ay) = (triangle[0], triangle[1]);
    let (bx, by) = (triangle[2], triangle[3]);
    let (cx, cy) = (triangle[4], triangle[5]);

    let ab_side = line_side([ax, ay, bx, by], x, y);
    let bc_side = line_side([bx, by, cx, cy], x, y);
    let ca_side = line_side([cx, cy, ax, ay], x, y);

    let ab_positive = ab_side.is_positive();
    let bc_positive = bc_side.is_positive();
    let ca_positive = ca_side.is_positive();

    ab_positive == bc_positive
    && bc_positive == ca_positive
}

/// Returns true if triangle is clockwise.
///
/// This is done by computing which side the third vertex is relative to
/// the line starting from the first vertex to second vertex.
#[inline(always)]
pub fn triangle_face(
    triangle: Triangle
) -> bool {
    let (ax, ay) = (triangle[0], triangle[1]);
    let (bx, by) = (triangle[2], triangle[3]);
    let (cx, cy) = (triangle[4], triangle[5]);

    let ab_side = line_side([ax, ay, bx, by], cx, cy);

    ab_side.is_negative()
}

#[test]
fn test_triangle() {
    // Triangle counter clock-wise.
    let tri_1 = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0];
    // Triangle clock-wise.
    let tri_2 = [0.0, 0.0, 1.0, 1.0, 1.0, 0.0];
    let (x, y) = (0.5, 0.25);
    assert_eq!(inside_triangle(tri_1, x, y), true);
    assert_eq!(inside_triangle(tri_2, x, y), true);
    assert_eq!(triangle_face(tri_1), false);
    assert_eq!(triangle_face(tri_2), true);
}
<commit_msg>Added `get_scale` to `vecmath`<commit_after>
//! Various methods for computing with vectors.

use internal::{
    Area,
    Line,
    Matrix2d,
    Polygon,
    Ray,
    Rectangle,
    Vec2d,
    Triangle
};
use modular_index::{previous};

/// Multiplies two matrices.
#[inline(always)]
pub fn multiply(m: Matrix2d, b: Matrix2d) -> Matrix2d {
    [m[0]*b[0]+m[1]*b[3]+m[2]*0.0,  m[0]*b[1]+m[1]*b[4]+m[2]*0.0,  m[0]*b[2]+m[1]*b[5]+m[2]*1.0,
     m[3]*b[0]+m[4]*b[3]+m[5]*0.0,  m[3]*b[1]+m[4]*b[4]+m[5]*0.0,  m[3]*b[2]+m[4]*b[5]+m[5]*1.0]
}

/// Creates a translation matrix.
#[inline(always)]
pub fn translate(x: f64, y: f64) -> Matrix2d {
    [1.0, 0.0, x,
     0.0, 1.0, y]
}

/// Creates a rotation matrix.
#[inline(always)]
pub fn rotate_radians(angle: f64) -> Matrix2d {
    let c = angle.cos();
    let s = angle.sin();
    [c, -s, 0.0,
     s,  c, 0.0]
}

/// Orients x axis to look at point.
///
/// Leaves x axis unchanged if the point to look at is the origin.
#[inline(always)]
pub fn orient(x: f64, y: f64) -> Matrix2d {
    let len = x * x + y * y;
    if len == 0.0 {
        return [1.0, 0.0, 0.0,
                0.0, 1.0, 0.0]
    }

    let len = len.sqrt();
    let c = x / len;
    let s = y / len;
    [c, -s, 0.0,
     s,  c, 0.0]
}

/// Create a scale matrix.
#[inline(always)]
pub fn scale(sx: f64, sy: f64) -> Matrix2d {
    [sx, 0.0, 0.0,
     0.0, sy, 0.0]
}

/// Create a shear matrix.
#[inline(always)]
pub fn shear(sx: f64, sy: f64) -> Matrix2d {
    [1.0, sx, 0.0,
     sy, 1.0, 0.0]
}

/// Create an identity matrix.
#[inline(always)]
pub fn identity() -> Matrix2d {
    [1.0, 0.0, 0.0,
     0.0, 1.0, 0.0]
}

/// Extract scale information from amtrix.
#[inline(always)]
pub fn get_scale(m: Matrix2d) -> Vec2d {
    [(m[0] * m[0] + m[3] * m[3]).sqrt(), (m[1] * m[1] + m[4] * m[4]).sqrt()]
}

/// Compute the shortest vector from point to ray.
/// A ray stores starting point and directional vector.
#[inline(always)]
pub fn separation(ray: Ray, x: f64, y: f64) -> [f64, ..2] {
    // Get the directional vector.
    let (dir_x, dir_y) = (ray[2], ray[3]);
    // Get displacement vector from point.
    let (dx, dy) = (ray[0] - x, ray[1] - y);
    // Compute the component of position in ray direction.
    let dot = dir_x * x + dir_y * y;
    // The directional vector multiplied with the dot gives us a parallel vector.
    // When we subtract this from the displacement we get a vector normal to the ray.
    // This is the shortest vector from the point to the ray.
    [dx - dot * dir_x, dy - dot * dir_y]
}

/// Returns the least separation out of four.
/// Each seperation can be computed using `separation` function.
/// The separation returned can be used to solve collision of rectangles.
#[inline(always)]
pub fn least_separation_4(
    sep1: Vec2d,
    sep2: Vec2d,
    sep3: Vec2d,
    sep4: Vec2d
) -> Vec2d {
    let dot1 = sep1[0] * sep1[0] + sep1[1] * sep1[1];
    let dot2 = sep2[0] * sep2[0] + sep2[1] * sep2[1];
    let dot3 = sep3[0] * sep3[0] + sep3[1] * sep3[1];
    let dot4 = sep4[0] * sep4[0] + sep4[1] * sep4[1];
    // Search for the smallest dot product.
    if dot1 < dot2 {
        if dot3 < dot4 {
            if dot1 < dot3 { sep1 } else { sep3 }
        } else {
            if dot1 < dot4 { sep1 } else { sep4 }
        }
    } else {
        if dot3 < dot4 {
            if dot2 < dot3 { sep2 } else { sep3 }
        } else {
            if dot2 < dot4 { sep2 } else { sep4 }
        }
    }
}

/// Shrinks a rectangle by a factor on all sides.
#[inline(always)]
pub fn margin_rectangle(rect: Rectangle, m: f64) -> Rectangle {
    let w = rect[2] - 2.0 * m;
    let h = rect[3] - 2.0 * m;
    let (x, w) = if w < 0.0 { (rect[0] + 0.5 * rect[2], 0.0) } else { (rect[0] + m, w) };
    let (y, h) = if h < 0.0 { (rect[1] + 0.5 * rect[3], 0.0) } else { (rect[1] + m, h) };
    [x, y, w, h]
}

/// Computes a relative rectangle using the rectangle as a tile.
#[inline(always)]
pub fn relative_rectangle(rect: Rectangle, x: f64, y: f64) -> Rectangle {
    [rect[0] + x * rect[2], rect[1] + y * rect[3], rect[2], rect[3]]
}

/// Computes modular offset safely for numbers.
#[inline(always)]
pub fn modular_offset<T: Add<T, T> + Rem<T, T>>(
    n: &T,
    i: &T,
    off: &T
) -> T {
    (*i + (*off % *n + *n)) % *n
}

#[test]
fn test_modular_offset() {
    assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &-1.0_f64), 2.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &-1.0_f64), 0.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &-1.0_f64), 1.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &-1.0_f64), 2.0_f64);

    assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &1.0_f64), 1.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &1.0_f64), 2.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &1.0_f64), 0.0_f64);
    assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &1.0_f64), 1.0_f64);
}

/// Computes the area and centroid of a simple polygon.
///
/// A simple polygon is one that does not intersect itself.
/// Source: http://en.wikipedia.org/wiki/Polygon_area#Simple_polygons
pub fn area_centroid(polygon: Polygon) -> (Area, Vec2d) {
    let n = polygon.len() / 2;
    let mut sum = 0.0_f64;
    let (mut cx, mut cy) = (0.0_f64, 0.0_f64);
    for i in range(0, n) {
        let (qx, qy) = (polygon[i * 2], polygon[i * 2 + 1]);
        let p_i = previous(n, i);
        let (px, py) = (polygon[p_i * 2], polygon[p_i * 2 + 1]);
        let cross = px * qy - qx * py;
        cx += (px + qx) * cross;
        cy += (py + qy) * cross;
        sum += cross;
    }

    let area = 0.5 * sum;
    // 'cx / (6.0 * area)' = 'cx / (3.0 * sum)'
    let centroid = [cx / (3.0 * sum), cy / (3.0 * sum)];
    (area, centroid)
}

/// Computes area of a simple polygon.
///
/// A simple polygon is one that does not intersect itself.
#[inline(always)]
pub fn area(polygon: Polygon) -> f64 {
    let (res, _) = area_centroid(polygon);
    res
}

/// Computes centroid of a simple polygon.
///
/// A simple polygon is one that does not intersect itself.
#[inline(always)]
pub fn centroid(polygon: Polygon) -> Vec2d {
    let (_, res) = area_centroid(polygon);
    res
}

/// Returns a number that tells which side it is relative to a line.
///
/// Computes the cross product of the vector that gives the line
/// with the vector between point and starting point of line.
/// One side of the line has opposite sign of the other.
#[inline(always)]
pub fn line_side(line: Line, x: f64, y: f64) -> f64 {
    let (ax, ay) = (line[0], line[1]);
    let (bx, by) = (line[2], line[3]);
    (bx - ax) * (y - ay) - (by - ay) * (x - ax)
}

/// Returns true if point is inside triangle.
///
/// This is done by computing a `side` number for each edge.
/// If the number is inside if it is on the same side for all edges.
/// Might break for very small triangles.
pub fn inside_triangle(triangle: Triangle, x: f64, y: f64) -> bool {
    let (ax, ay) = (triangle[0], triangle[1]);
    let (bx, by) = (triangle[2], triangle[3]);
    let (cx, cy) = (triangle[4], triangle[5]);

    let ab_side = line_side([ax, ay, bx, by], x, y);
    let bc_side = line_side([bx, by, cx, cy], x, y);
    let ca_side = line_side([cx, cy, ax, ay], x, y);

    let ab_positive = ab_side.is_positive();
    let bc_positive = bc_side.is_positive();
    let ca_positive = ca_side.is_positive();

    ab_positive == bc_positive
    && bc_positive == ca_positive
}

/// Returns true if triangle is clockwise.
///
/// This is done by computing which side the third vertex is relative to
/// the line starting from the first vertex to second vertex.
#[inline(always)]
pub fn triangle_face(
    triangle: Triangle
) -> bool {
    let (ax, ay) = (triangle[0], triangle[1]);
    let (bx, by) = (triangle[2], triangle[3]);
    let (cx, cy) = (triangle[4], triangle[5]);

    let ab_side = line_side([ax, ay, bx, by], cx, cy);

    ab_side.is_negative()
}

#[test]
fn test_triangle() {
    // Triangle counter clock-wise.
    let tri_1 = [0.0, 0.0, 1.0, 0.0, 1.0, 1.0];
    // Triangle clock-wise.
    let tri_2 = [0.0, 0.0, 1.0, 1.0, 1.0, 0.0];
    let (x, y) = (0.5, 0.25);
    assert_eq!(inside_triangle(tri_1, x, y), true);
    assert_eq!(inside_triangle(tri_2, x, y), true);
    assert_eq!(triangle_face(tri_1), false);
    assert_eq!(triangle_face(tri_2), true);
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::ffi::CStr;
use gl;

/// Describes a version.
///
/// A version can only be compared to another version if they belong to the same API.
/// For example, both `Version(Gl, 3, 0) >= Version(GlEs, 3, 0)` and `Version(GlEs, 3, 0) >=
/// Version(Gl, 3, 0)` return `false`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Version(pub Api, pub u8, pub u8);

/// Describes an OpenGL-related API.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Api {
    /// Regular OpenGL.
    Gl,
    /// OpenGL embedded system.
    GlEs,
}

impl PartialOrd for Version {
    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
        if self.0 != other.0 {
            return None;
        }

        match self.1.cmp(&other.1) {
            Ordering::Equal => Some(self.2.cmp(&other.2)),
            a => Some(a)
        }
    }
}

/// Obtains the OpenGL version of the current context using the loaded functions.
///
/// # Unsafe
///
/// You must ensure that the functions belong to the current context, otherwise you will get
/// an undefined behavior.
pub unsafe fn get_gl_version(gl: &gl::Gl) -> Version {
    let version = gl.GetString(gl::VERSION) as *const i8;
    let version = String::from_utf8(CStr::from_ptr(version).to_bytes().to_vec()).unwrap();

    let (version, api) = if version.starts_with("OpenGL ES ") {
        (&version[10..], Api::GlEs)
    } else {
        (&version[..], Api::Gl)
    };

    let version = version.split(' ').next().expect("glGetString(GL_VERSION) returned an empty \
                                                    string");

    let mut iter = version.split(move |c: char| c == '.');
    let major = iter.next().unwrap();
    let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

    Version(
        api,
        major.parse().ok().expect("failed to parse GL major version"),
        minor.parse().ok().expect("failed to parse GL minor version"),
    )
}

/// Given an API version, this function returns the GLSL version that the implementation is
/// required to support.
///
/// # Panic
///
/// Panics if the version is invalid or is not supposed to support a GLSL version.
pub fn get_supported_glsl_version(gl_version: &Version) -> Version {
    match gl_version.0 {
        Api::Gl => {
            // since OpenGL 3.3: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 3) {
                return *gl_version;
            }

            // match to detect invalid versions
            match *gl_version {
                Version(a, 2, 0) => Version(a, 1, 1),
                Version(a, 2, 1) => Version(a, 1, 2),
                Version(a, 3, 0) => Version(a, 1, 3),
                Version(a, 3, 1) => Version(a, 1, 4),
                Version(a, 3, 2) => Version(a, 1, 5),
                _ => panic!("no corresponding glsl version exists")
            }
        },
        Api::GlEs => {
            // since OpenGL ES 3.0: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 0) {
                return *gl_version;
            }

            // only other valid GLES version is 2.0
            if *gl_version == Version(gl_version.0, 2, 0){
                return Version(Api::GlEs, 1, 0);
            } else {
                panic!("no corresponding glsl version exists")
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Version, Api, get_supported_glsl_version};

    macro_rules! assert_versions {
        ( $api:path, $gl_major:expr, $gl_minor:expr => $glsl_major:expr, $glsl_minor:expr) => {
            assert_eq!(
                get_supported_glsl_version(&Version($api, $gl_major, $gl_minor)),
                Version($api, $glsl_major, $glsl_minor)
                    );
        }
    }

    #[test]
    fn valid_gl_versions() {
        // irregular versions
        assert_versions!(Api::Gl, 2, 0 => 1, 1);
        assert_versions!(Api::Gl, 2, 1 => 1, 2);
        assert_versions!(Api::Gl, 3, 0 => 1, 3);
        assert_versions!(Api::Gl, 3, 1 => 1, 4);
        assert_versions!(Api::Gl, 3, 2 => 1, 5);

        // test a few regular versions
        assert_versions!(Api::Gl, 3, 3 => 3, 3);
        assert_versions!(Api::Gl, 4, 0 => 4, 0);
        assert_versions!(Api::Gl, 4, 5 => 4, 5);
    }

    #[test]
    fn valid_gles_versions() {
        // only irregular version
        assert_versions!(Api::GlEs, 2, 0 => 1, 0);

        // some regular versions
        assert_versions!(Api::GlEs, 3, 0 => 3, 0);
        assert_versions!(Api::GlEs, 3, 1 => 3, 1);
    }

    #[test]
    #[should_panic]
    fn invalid_gl_version() {
        get_supported_glsl_version(&Version(Api::Gl, 1, 0));
    }

    #[test]
    #[should_panic]
    fn invalid_gles_version() {
        get_supported_glsl_version(&Version(Api::GlEs, 1, 0));
    }
}
<commit_msg>Correctly parse OpenGL ES 1.x version strings in order to avoid panics<commit_after>use std::cmp::Ordering;
use std::ffi::CStr;
use gl;

/// Describes a version.
///
/// A version can only be compared to another version if they belong to the same API.
/// For example, both `Version(Gl, 3, 0) >= Version(GlEs, 3, 0)` and `Version(GlEs, 3, 0) >=
/// Version(Gl, 3, 0)` return `false`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Version(pub Api, pub u8, pub u8);

/// Describes an OpenGL-related API.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Api {
    /// Regular OpenGL.
    Gl,
    /// OpenGL embedded system.
    GlEs,
}

impl PartialOrd for Version {
    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
        if self.0 != other.0 {
            return None;
        }

        match self.1.cmp(&other.1) {
            Ordering::Equal => Some(self.2.cmp(&other.2)),
            a => Some(a)
        }
    }
}

/// Obtains the OpenGL version of the current context using the loaded functions.
///
/// # Unsafe
///
/// You must ensure that the functions belong to the current context, otherwise you will get
/// an undefined behavior.
pub unsafe fn get_gl_version(gl: &gl::Gl) -> Version {
    let version = gl.GetString(gl::VERSION) as *const i8;
    let version = String::from_utf8(CStr::from_ptr(version).to_bytes().to_vec()).unwrap();

    let (version, api) = if version.starts_with("OpenGL ES ") {
        (&version[10..], Api::GlEs)
    } else if version.starts_with("OpenGL ES-") {
        (&version[13..], Api::GlEs)
    } else {
        (&version[..], Api::Gl)
    };

    let version = version.split(' ').next().expect("glGetString(GL_VERSION) returned an empty \
                                                    string");

    let mut iter = version.split(move |c: char| c == '.');
    let major = iter.next().unwrap();
    let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

    Version(
        api,
        major.parse().ok().expect("failed to parse GL major version"),
        minor.parse().ok().expect("failed to parse GL minor version"),
    )
}

/// Given an API version, this function returns the GLSL version that the implementation is
/// required to support.
///
/// # Panic
///
/// Panics if the version is invalid or is not supposed to support a GLSL version.
pub fn get_supported_glsl_version(gl_version: &Version) -> Version {
    match gl_version.0 {
        Api::Gl => {
            // since OpenGL 3.3: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 3) {
                return *gl_version;
            }

            // match to detect invalid versions
            match *gl_version {
                Version(a, 2, 0) => Version(a, 1, 1),
                Version(a, 2, 1) => Version(a, 1, 2),
                Version(a, 3, 0) => Version(a, 1, 3),
                Version(a, 3, 1) => Version(a, 1, 4),
                Version(a, 3, 2) => Version(a, 1, 5),
                _ => panic!("no corresponding glsl version exists")
            }
        },
        Api::GlEs => {
            // since OpenGL ES 3.0: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 0) {
                return *gl_version;
            }

            // only other valid GLES version is 2.0
            if *gl_version == Version(gl_version.0, 2, 0){
                return Version(Api::GlEs, 1, 0);
            } else {
                panic!("no corresponding glsl version exists")
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Version, Api, get_supported_glsl_version};

    macro_rules! assert_versions {
        ( $api:path, $gl_major:expr, $gl_minor:expr => $glsl_major:expr, $glsl_minor:expr) => {
            assert_eq!(
                get_supported_glsl_version(&Version($api, $gl_major, $gl_minor)),
                Version($api, $glsl_major, $glsl_minor)
                    );
        }
    }

    #[test]
    fn valid_gl_versions() {
        // irregular versions
        assert_versions!(Api::Gl, 2, 0 => 1, 1);
        assert_versions!(Api::Gl, 2, 1 => 1, 2);
        assert_versions!(Api::Gl, 3, 0 => 1, 3);
        assert_versions!(Api::Gl, 3, 1 => 1, 4);
        assert_versions!(Api::Gl, 3, 2 => 1, 5);

        // test a few regular versions
        assert_versions!(Api::Gl, 3, 3 => 3, 3);
        assert_versions!(Api::Gl, 4, 0 => 4, 0);
        assert_versions!(Api::Gl, 4, 5 => 4, 5);
    }

    #[test]
    fn valid_gles_versions() {
        // only irregular version
        assert_versions!(Api::GlEs, 2, 0 => 1, 0);

        // some regular versions
        assert_versions!(Api::GlEs, 3, 0 => 3, 0);
        assert_versions!(Api::GlEs, 3, 1 => 3, 1);
    }

    #[test]
    #[should_panic]
    fn invalid_gl_version() {
        get_supported_glsl_version(&Version(Api::Gl, 1, 0));
    }

    #[test]
    #[should_panic]
    fn invalid_gles_version() {
        get_supported_glsl_version(&Version(Api::GlEs, 1, 0));
    }
}
<|endoftext|>


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

use super::{ReadAt, WriteAt};

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

extern crate winapi;
use self::winapi::shared::minwindef::{BOOL, DWORD, LPVOID};
use self::winapi::um::fileapi::ReadFile;
use self::winapi::um::minwinbase::{OVERLAPPED_u, OVERLAPPED_u_s, OVERLAPPED};
use self::winapi::um::winnt::HANDLE;

fn result(e: BOOL) -> Result<()> {
    if e == 0 {
        Err(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(),
    }
}

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

impl WriteAt for File {
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> 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) -> Result<()> {
        Write::flush(self)
    }
}
<commit_msg>fix windows.rs out of sync<commit_after>use std::fs::File;
use std::io;
use std::io::Write;
use std::mem;
use std::ptr;

use super::{ReadAt, WriteAt};

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

extern crate winapi;
use self::winapi::shared::minwindef::{BOOL, DWORD, LPVOID};
use self::winapi::um::fileapi::ReadFile;
use self::winapi::um::minwinbase::{OVERLAPPED_u, OVERLAPPED_u_s, OVERLAPPED};
use self::winapi::um::winnt::HANDLE;

fn result(e: BOOL) -> 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(),
    }
}

impl ReadAt for File {
    fn read_at(&self, pos: u64, buf: &mut [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_mut_ptr() as LPVOID,
                buf.len() as DWORD,
                &mut bytes,
                &mut ov,
            )
        })?;
        Ok(bytes 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|>


<commit_before>/* io::mod.rs */

use core::option::Some;
use super::drivers;
use kernel::sgash;

mod font;

/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/BBABEGGE.html */
pub static UART0: *mut u32 = 0x101f1000 as *mut u32;

pub static UART0_IMSC: *mut u32 = (0x101f1000 + 0x038) as *mut u32;
pub static VIC_INTENABLE: *mut u32 = (0x10140000 + 0x010) as *mut u32;

pub static mut CURSOR_X: u32 = 0;
pub static mut CURSOR_Y: u32 = 0;
pub static CURSOR_HEIGHT: u32 = 16;
pub static CURSOR_WIDTH: u32 = 8;
pub static mut CURSOR_COLOR: u32 = 0x000000FF;
pub static mut FG_COLOR: u32 = 0x00FFFFFF;
pub static mut BG_COLOR: u32 = 0x00000000;
pub static mut CURSOR_BUFFER: [u32, ..8*16] = [0x00FF0000, ..8*16];
pub static mut SAVE_X: u32 = 0;
pub static mut SAVE_Y: u32 = 0;
pub static START_ADDR: u32 = 1024*1024;
pub static mut SCREEN_WIDTH: u32 = 0;
pub static mut SCREEN_HEIGHT: u32 = 0;

pub unsafe fn init(width: u32, height: u32)
{
    SCREEN_WIDTH = width;
    SCREEN_HEIGHT= height;
    sgash::init();

    /* For the following magic values, see
     * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACHEDGD.html
     */

    // 800x600
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    if (SCREEN_WIDTH == 800 && SCREEN_HEIGHT == 600)
    {
    	ws(0x10000010, 0x2CAC);
    	ws(0x10120000, 0x1313A4C4);
    	ws(0x10120004, 0x0505F657);
    	ws(0x10120008, 0x071F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);
    }

    // 640x480
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    else if (SCREEN_WIDTH == 640 && SCREEN_HEIGHT == 480)
    {
	ws(0x10000010, 0x2C77);
	ws(0x10120000, 0x3F1F3F9C);
	ws(0x10120004, 0x090B61DF);
	ws(0x10120008, 0x067F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);

    }
    set_bg(0x222C38);
    set_fg(0xFAFCFF);
    set_cursor_color(0xFAFCFF);
    fill_bg();
    sgash::drawstr(&"sgash > ");
    draw_cursor();
}

pub unsafe fn write_char(c: char, address: *mut u32) {
    *address = c as u32;
}

pub unsafe fn scrollup()
{
    let mut i = CURSOR_HEIGHT*SCREEN_WIDTH;
    while i < (SCREEN_WIDTH*SCREEN_HEIGHT)
    {
	*((START_ADDR + ((i-16*SCREEN_WIDTH)*4)) as *mut u32) = *((START_ADDR+(i*4)) as *u32);
	i += 1;
    }
    i = 4*(SCREEN_WIDTH*SCREEN_HEIGHT - CURSOR_HEIGHT*SCREEN_WIDTH);
    while i < 4*SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR + (i as u32)) as *mut u32) = BG_COLOR;
	i += 4;
    }
    CURSOR_X = 0x0u32;
    CURSOR_Y -= CURSOR_HEIGHT;
}
pub unsafe fn draw_char(c: char)
{
    if CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
	scrollup();
    }
    let font_offset = (c as u8) - 0x20;
    let map = font::bitmaps[font_offset];

    let mut i = -1;
    let mut j = 0;
    let mut addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + 1 + SCREEN_WIDTH*CURSOR_Y);
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH - i + SCREEN_WIDTH*(CURSOR_Y + j));
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + SCREEN_WIDTH*CURSOR_Y) - 4*i + 4*SCREEN_WIDTH*j
	    if ((map[j] >> 4*i) & 1) == 1
	    {
		*(addr as *mut u32) = FG_COLOR;
	    }
	    else
	    {
		*(addr as *mut u32) = BG_COLOR;
	    }

	    addr-= 4;
	    i += 1;
	}
	addr += 4*(i);
	addr += 4*SCREEN_WIDTH;
	i = 0;
	j += 1;
    }
}

pub unsafe fn backup()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    CURSOR_BUFFER[i + j*8] = *(addr as *mut u32);
	    i += 1;
	}
	i = 0;
	j += 1;
    }
    SAVE_X = CURSOR_X;
    SAVE_Y = CURSOR_Y;
}

pub unsafe fn restore()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(SAVE_X + i + SCREEN_WIDTH*(SAVE_Y + j));
	    *(addr as *mut u32) = CURSOR_BUFFER[i + j*8];
	    i += 1;
	}
	i = 0;
	j += 1;
    }
}

pub unsafe fn draw_cursor()
{

    let mut i = 0;
    let mut j = 0;

    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    *(addr as *mut u32) = CURSOR_COLOR;
	    i += 1;
	}
	i = 0;
	j += 1;
    }

}

pub unsafe fn paint(color: u32)
{
    let mut i = 0;
    while i < SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR as u32 + i*4) as *mut u32) = color;
	i+=1;
    }
}

pub unsafe fn fill_bg()
{
    paint(BG_COLOR);
}

pub unsafe fn read(addr: u32)	->	u32
{
    *(addr as *mut u32)
}

pub unsafe fn ws(addr: u32, value: u32)
{
    *(addr as *mut u32) = *(addr as *mut u32) | value;
}

pub unsafe fn wh(addr: u32, value: u32)
{
    *(addr as *mut u32) = value;
}

pub unsafe fn set_fg(color: u32)
{
    FG_COLOR = color;
}

pub unsafe fn set_bg(color: u32)
{
    BG_COLOR = color;
}

pub unsafe fn set_cursor_color(color: u32)
{
    CURSOR_COLOR = color;
}
<commit_msg>EVEN FASTER CHARACTER DRAWING.<commit_after>/* io::mod.rs */

use core::option::Some;
use super::drivers;
use kernel::sgash;

mod font;

/* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/BBABEGGE.html */
pub static UART0: *mut u32 = 0x101f1000 as *mut u32;

pub static UART0_IMSC: *mut u32 = (0x101f1000 + 0x038) as *mut u32;
pub static VIC_INTENABLE: *mut u32 = (0x10140000 + 0x010) as *mut u32;

pub static mut CURSOR_X: u32 = 0;
pub static mut CURSOR_Y: u32 = 0;
pub static CURSOR_HEIGHT: u32 = 16;
pub static CURSOR_WIDTH: u32 = 8;
pub static mut CURSOR_COLOR: u32 = 0x000000FF;
pub static mut FG_COLOR: u32 = 0x00FFFFFF;
pub static mut BG_COLOR: u32 = 0x00000000;
pub static mut CURSOR_BUFFER: [u32, ..8*16] = [0x00FF0000, ..8*16];
pub static mut SAVE_X: u32 = 0;
pub static mut SAVE_Y: u32 = 0;
pub static START_ADDR: u32 = 1024*1024;
pub static mut SCREEN_WIDTH: u32 = 0;
pub static mut SCREEN_HEIGHT: u32 = 0;

pub unsafe fn init(width: u32, height: u32)
{
    SCREEN_WIDTH = width;
    SCREEN_HEIGHT= height;
    sgash::init();

    /* For the following magic values, see
     * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACHEDGD.html
     */

    // 800x600
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    if (SCREEN_WIDTH == 800 && SCREEN_HEIGHT == 600)
    {
    	ws(0x10000010, 0x2CAC);
    	ws(0x10120000, 0x1313A4C4);
    	ws(0x10120004, 0x0505F657);
    	ws(0x10120008, 0x071F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);
    }

    // 640x480
    // See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0225d/CACCCFBF.html
    else if (SCREEN_WIDTH == 640 && SCREEN_HEIGHT == 480)
    {
	ws(0x10000010, 0x2C77);
	ws(0x10120000, 0x3F1F3F9C);
	ws(0x10120004, 0x090B61DF);
	ws(0x10120008, 0x067F1800);

	/* See http://forum.osdev.org/viewtopic.php?p=195000 */
	ws(0x10120010, START_ADDR);

	/* See http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0161e/I911024.html */
	ws(0x10120018, 0x82B);

    }
    set_bg(0x222C38);
    set_fg(0xFAFCFF);
    set_cursor_color(0xFAFCFF);
    fill_bg();
    sgash::drawstr(&"sgash > ");
    draw_cursor();
}

pub unsafe fn write_char(c: char, address: *mut u32) {
    *address = c as u32;
}

pub unsafe fn scrollup()
{
    let mut i = CURSOR_HEIGHT*SCREEN_WIDTH;
    while i < (SCREEN_WIDTH*SCREEN_HEIGHT)
    {
	*((START_ADDR + ((i-16*SCREEN_WIDTH)*4)) as *mut u32) = *((START_ADDR+(i*4)) as *u32);
	i += 1;
    }
    i = 4*(SCREEN_WIDTH*SCREEN_HEIGHT - CURSOR_HEIGHT*SCREEN_WIDTH);
    while i < 4*SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR + (i as u32)) as *mut u32) = BG_COLOR;
	i += 4;
    }
    CURSOR_X = 0x0u32;
    CURSOR_Y -= CURSOR_HEIGHT;
}
pub unsafe fn draw_char(c: char)
{
    if CURSOR_X+(SCREEN_WIDTH*CURSOR_Y) >= SCREEN_WIDTH*SCREEN_HEIGHT
    {
	scrollup();
    }
    let font_offset = (c as u8) - 0x20;
    let map = font::bitmaps[font_offset];

    let mut i = -1;
    let mut j = 0;
    let mut addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + 1 + SCREEN_WIDTH*CURSOR_Y);
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH - i + SCREEN_WIDTH*(CURSOR_Y + j));
	    //let addr = START_ADDR + 4*(CURSOR_X + CURSOR_WIDTH + SCREEN_WIDTH*CURSOR_Y) - 4*i + 4*SCREEN_WIDTH*j
	    if ((map[j] >> 4*i) & 1) == 1
	    {
		*(addr as *mut u32) = FG_COLOR;
	    }
	    else
	    {
		*(addr as *mut u32) = BG_COLOR;
	    }

	    addr-= 4;
	    i += 1;
	}
	addr += 4*(i+SCREEN_WIDTH);
	i = 0;
	j += 1;
    }
}

pub unsafe fn backup()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    CURSOR_BUFFER[i + j*8] = *(addr as *mut u32);
	    i += 1;
	}
	i = 0;
	j += 1;
    }
    SAVE_X = CURSOR_X;
    SAVE_Y = CURSOR_Y;
}

pub unsafe fn restore()
{
    let mut i = 0;
    let mut j = 0;
    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(SAVE_X + i + SCREEN_WIDTH*(SAVE_Y + j));
	    *(addr as *mut u32) = CURSOR_BUFFER[i + j*8];
	    i += 1;
	}
	i = 0;
	j += 1;
    }
}

pub unsafe fn draw_cursor()
{

    let mut i = 0;
    let mut j = 0;

    while j < CURSOR_HEIGHT
    {
	while i < CURSOR_WIDTH
	{
	    let addr = START_ADDR + 4*(CURSOR_X + i + SCREEN_WIDTH*(CURSOR_Y + j));
	    *(addr as *mut u32) = CURSOR_COLOR;
	    i += 1;
	}
	i = 0;
	j += 1;
    }

}

pub unsafe fn paint(color: u32)
{
    let mut i = 0;
    while i < SCREEN_WIDTH*SCREEN_HEIGHT
    {
	*((START_ADDR as u32 + i*4) as *mut u32) = color;
	i+=1;
    }
}

pub unsafe fn fill_bg()
{
    paint(BG_COLOR);
}

pub unsafe fn read(addr: u32)	->	u32
{
    *(addr as *mut u32)
}

pub unsafe fn ws(addr: u32, value: u32)
{
    *(addr as *mut u32) = *(addr as *mut u32) | value;
}

pub unsafe fn wh(addr: u32, value: u32)
{
    *(addr as *mut u32) = value;
}

pub unsafe fn set_fg(color: u32)
{
    FG_COLOR = color;
}

pub unsafe fn set_bg(color: u32)
{
    BG_COLOR = color;
}

pub unsafe fn set_cursor_color(color: u32)
{
    CURSOR_COLOR = color;
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::{RefCell};

type Link<T> = Option<Rc<RefCell<Node<T>>>>;

pub struct DoubleLink<T> {
    head: Link<T>,
    tail: Link<T>,
}

pub struct Node<T> {
    prev: Link<T>,
    next: Link<T>,
    elem: T,
}

impl<T> Node<T> {
    fn new(elem: T) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Node {
            prev: None,
            next: None,
            elem: elem,
        }))
    }
}

impl<T> DoubleLink<T> {
    fn new() -> Self {
        DoubleLink {
            head: None,
            tail: None,
        }
    }

    fn push_head(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.head.take() {
            Some(prev_head) => {
                // set prev head's prev prop to new node
                prev_head.borrow_mut().prev = Some(new_node.clone());
                // set new_node's next prop to prev head
                new_node.borrow_mut().next = Some(prev_head);
                // set self's head to new node
                self.head = Some(new_node);
            },
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn push_tail(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.tail.take() {
            Some(prev_tail) => {
                prev_tail.borrow_mut().next = Some(new_node.clone());
                new_node.borrow_mut().prev = Some(prev_tail);
                self.tail = Some(new_node);
            },
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn pop_head(&mut self) -> Option<T> {
        self.head.take().map(|old_head| {
            match old_head.borrow_mut().next.take() {
                Some(new_head) => {
                    // remove the reference to the previous head.
                    new_head.borrow_mut().prev.take();
                    self.head = Some(new_head);
                },
                None => {
                    // If we've consumed the end of the list, we need to update the tail property. head has already been set to none via the 'take' call earlier.
                    self.tail.take();
                }
            };
            Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
        })
    }
}

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

    #[test]
    fn basics() {
        let mut list = DoubleLink::new();

        // Check empty list behaves right
        assert_eq!(list.pop_head(), None);

        // // Populate list
        list.push_head(1);
        list.push_head(2);
        list.push_head(3);

        // // Check normal removal
        assert_eq!(list.pop_head(), Some(3));
        assert_eq!(list.pop_head(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push_head(4);
        list.push_head(5);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(5));
        assert_eq!(list.pop_head(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop_head(), Some(1));
        assert_eq!(list.pop_head(), None);
    }
}
<commit_msg>adds pop tail and tests<commit_after>use std::rc::Rc;
use std::cell::{RefCell};

type Link<T> = Option<Rc<RefCell<Node<T>>>>;

pub struct DoubleLink<T> {
    head: Link<T>,
    tail: Link<T>,
}

pub struct Node<T> {
    prev: Link<T>,
    next: Link<T>,
    elem: T,
}

impl<T> Node<T> {
    fn new(elem: T) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(Node {
            prev: None,
            next: None,
            elem: elem,
        }))
    }
}

impl<T> DoubleLink<T> {
    fn new() -> Self {
        DoubleLink {
            head: None,
            tail: None,
        }
    }

    fn push_head(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.head.take() {
            Some(prev_head) => {
                // set prev head's prev prop to new node
                prev_head.borrow_mut().prev = Some(new_node.clone());
                // set new_node's next prop to prev head
                new_node.borrow_mut().next = Some(prev_head);
                // set self's head to new node
                self.head = Some(new_node);
            },
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn push_tail(&mut self, elem: T) -> &mut Self {
        let new_node = Node::new(elem);
        match self.tail.take() {
            Some(prev_tail) => {
                prev_tail.borrow_mut().next = Some(new_node.clone());
                new_node.borrow_mut().prev = Some(prev_tail);
                self.tail = Some(new_node);
            },
            None => {
                self.head = Some(new_node.clone());
                self.tail = Some(new_node);
            }
        };
        self
    }

    fn pop_head(&mut self) -> Option<T> {
        self.head.take().map(|old_head| {
            match old_head.borrow_mut().next.take() {
                Some(new_head) => {
                    // remove the reference to the previous head.
                    new_head.borrow_mut().prev.take();
                    self.head = Some(new_head);
                },
                None => {
                    // If we've consumed the end of the list, we need to update the tail property. head has already been set to none via the 'take' call earlier.
                    self.tail.take();
                }
            };
            Rc::try_unwrap(old_head).ok().unwrap().into_inner().elem
        })
    }

    fn pop_tail(&mut self) -> Option<T> {
        self.tail.take().map(|old_tail| {
            match old_tail.borrow_mut().prev.take() {
                Some(new_tail) => {
                    new_tail.borrow_mut().next.take();
                    self.tail = Some(new_tail);
                },
                None => {
                    self.head.take();
                }
            };
            Rc::try_unwrap(old_tail).ok().unwrap().into_inner().elem
        })
    }
}

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

    #[test]
    fn basics() {
        let mut list = DoubleLink::new();

        // Check empty list behaves right
        assert_eq!(list.pop_head(), None);

        // Populate list
        list.push_head(1);
        list.push_head(2);
        list.push_head(3);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(3));
        assert_eq!(list.pop_head(), Some(2));

        // Push some more just to make sure nothing's corrupted
        list.push_head(4);
        list.push_head(5);

        // Check normal removal
        assert_eq!(list.pop_head(), Some(5));
        assert_eq!(list.pop_head(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop_head(), Some(1));
        assert_eq!(list.pop_head(), None);
    }

    #[test]
    fn backwards() {
        let mut list = DoubleLink::new();

        // Populate list
        list.push_tail(1);
        list.push_tail(2);
        list.push_tail(3);

        // check backwards removal
        assert_eq!(list.pop_tail(), Some(3));
        assert_eq!(list.pop_tail(), Some(2));

        // push after pops
        list.push_tail(4).push_tail(5);
        assert_eq!(list.pop_tail(), Some(5));
        assert_eq!(list.pop_tail(), Some(4));

        assert_eq!(list.pop_tail(), Some(1));
        assert_eq!(list.pop_tail(), None);
    }
}
<|endoftext|>


<commit_before>extern crate orbtk;

use orbtk::{Button, Point, Rect, TextBox, Window};
use orbtk::callback::Click;
use orbtk::place::Place;

use std::env;
use std::fs::File;
use std::io::Read;

fn main(){
    let path_option = env::args().nth(1);

    let title = if let Some(ref path) = path_option {
        format!("{} - Editor", path)
    } else {
        format!("Editor")
    };

    let mut window = Window::new(Rect::new(100, 100, 576, 420), &title);

    let text_box = TextBox::new()
        .position(0, 16)
        .size(576, 404)
        .place(&mut window);

    if let Some(ref path) = path_option {
        match File::open(path) {
            Ok(mut file) => {
                let mut text = String::new();
                match file.read_to_string(&mut text) {
                    Ok(_) => text_box.text.set(text),
                    Err(err) => println!("Failed to read {}: {}", path, err)
                }
            },
            Err(err) => println!("Failed to open {}: {}", path, err)
        }
    }

    Button::new()
        .position(0, 0)
        .size(32, 16)
        .text("Save")
        .on_click(move |_button: &Button, _point: Point| {
            println!("{}", text_box.text.get());
        })
        .place(&mut window);

    window.exec();
}
<commit_msg>Make save button work<commit_after>extern crate orbtk;

use orbtk::{Button, Point, Rect, TextBox, Window};
use orbtk::callback::Click;
use orbtk::place::Place;

use std::env;
use std::fs::File;
use std::io::{Read, Write};

fn main(){
    let path_option = env::args().nth(1);

    let title = if let Some(ref path) = path_option {
        format!("{} - Editor", path)
    } else {
        format!("Editor")
    };

    let mut window = Window::new(Rect::new(100, 100, 576, 420), &title);

    let text_box = TextBox::new()
        .position(0, 16)
        .size(576, 404)
        .place(&mut window);

    if let Some(ref path) = path_option {
        match File::open(path) {
            Ok(mut file) => {
                let mut text = String::new();
                match file.read_to_string(&mut text) {
                    Ok(_) => text_box.text.set(text),
                    Err(err) => println!("Failed to read {}: {}", path, err)
                }
            },
            Err(err) => println!("Failed to open {}: {}", path, err)
        }
    }

    Button::new()
        .position(0, 0)
        .size(32, 16)
        .text("Save")
        .on_click(move |_button: &Button, _point: Point| {
            if let Some(ref path) = path_option {
                match File::create(path) {
                    Ok(mut file) => {
                        let text = text_box.text.get();
                        match file.write(&mut text.as_bytes()) {
                            Ok(_) => match file.set_len(text.len()) {
                                Ok(_) => println!("Successfully saved {}", path),
                                Err(err) => println!("Failed to truncate {}: {}", path, err)
                            },
                            Err(err) => println!("Failed to write {}: {}", path, err)
                        }
                    },
                    Err(err) => println!("Failed to open {}: {}", path, err)
                }
            } else {
                println!("Need to create file!");
            }
        })
        .place(&mut window);

    window.exec();
}
<|endoftext|>


<commit_before>use std::fmt::{self, Debug, Formatter};

use url::Url;

use core::source::{Source, SourceId};
use core::GitReference;
use core::{Dependency, Package, PackageId, Summary};
use util::Config;
use util::errors::CargoResult;
use util::hex::short_hash;
use sources::PathSource;
use sources::git::utils::{GitRemote, GitRevision};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'cfg> {
    remote: GitRemote,
    reference: GitReference,
    source_id: SourceId,
    path_source: Option<PathSource<'cfg>>,
    rev: Option<GitRevision>,
    ident: String,
    config: &'cfg Config,
}

impl<'cfg> GitSource<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config) -> CargoResult<GitSource<'cfg>> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let remote = GitRemote::new(source_id.url());
        let ident = ident(source_id.url())?;

        let reference = match source_id.precise() {
            Some(s) => GitReference::Rev(s.to_string()),
            None => source_id.git_reference().unwrap().clone(),
        };

        let source = GitSource {
            remote,
            reference,
            source_id: source_id.clone(),
            path_source: None,
            rev: None,
            ident,
            config,
        };

        Ok(source)
    }

    pub fn url(&self) -> &Url {
        self.remote.url()
    }

    pub fn read_packages(&mut self) -> CargoResult<Vec<Package>> {
        if self.path_source.is_none() {
            self.update()?;
        }
        self.path_source.as_mut().unwrap().read_packages()
    }
}

fn ident(url: &Url) -> CargoResult<String> {
    let url = canonicalize_url(url)?;
    let ident = url.path_segments()
        .and_then(|mut s| s.next_back())
        .unwrap_or("");

    let ident = if ident == "" { "_empty" } else { ident };

    Ok(format!("{}-{}", ident, short_hash(&url)))
}

// Some hacks and heuristics for making equivalent URLs hash the same
pub fn canonicalize_url(url: &Url) -> CargoResult<Url> {
    let mut url = url.clone();

    // cannot-be-a-base-urls are not supported
    // eg. github.com:rust-lang-nursery/rustfmt.git
    if url.cannot_be_a_base() {
        bail!(
            "invalid url `{}`: cannot-be-a-base-URLs are not supported",
            url
        )
    }

    // Strip a trailing slash
    if url.path().ends_with('/') {
        url.path_segments_mut().unwrap().pop_if_empty();
    }

    // HACKHACK: For GitHub URL's specifically just lowercase
    // everything.  GitHub treats both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)
    if url.host_str() == Some("github.com") {
        url.set_scheme("https").unwrap();
        let path = url.path().to_lowercase();
        url.set_path(&path);
    }

    // Repos generally can be accessed with or w/o '.git'
    let needs_chopping = url.path().ends_with(".git");
    if needs_chopping {
        let last = {
            let last = url.path_segments().unwrap().next_back().unwrap();
            last[..last.len() - 4].to_owned()
        };
        url.path_segments_mut().unwrap().pop().push(&last);
    }

    Ok(url)
}

impl<'cfg> Debug for GitSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "git repo at {}", self.remote.url())?;

        match self.reference.pretty_ref() {
            Some(s) => write!(f, " ({})", s),
            None => Ok(()),
        }
    }
}

impl<'cfg> Source for GitSource<'cfg> {
    fn source_id(&self) -> &SourceId {
        &self.source_id
    }

    fn supports_checksums(&self) -> bool {
        false
    }

    fn requires_precise(&self) -> bool {
        true
    }

    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        let src = self.path_source
            .as_mut()
            .expect("BUG: update() must be called before query()");
        Source::query(src, dep, f)
    }

    fn update(&mut self) -> CargoResult<()> {
        let lock =
            self.config
                .git_path()
                .open_rw(".cargo-lock-git", self.config, "the git checkouts")?;

        let db_path = lock.parent().join("db").join(&self.ident);

        if self.config.cli_unstable().offline && !db_path.exists() {
            bail!(
                "can't checkout from '{}': you are in the offline mode (-Z offline)",
                self.remote.url()
            );
        }

        // Resolve our reference to an actual revision, and check if the
        // database already has that revision. If it does, we just load a
        // database pinned at that revision, and if we don't we issue an update
        // to try to find the revision.
        let actual_rev = self.remote.rev_for(&db_path, &self.reference);
        let should_update = actual_rev.is_err() || self.source_id.precise().is_none();

        let (db, actual_rev) = if should_update && !self.config.cli_unstable().offline {
            self.config.shell().status(
                "Updating",
                format!("git repository `{}`", self.remote.url()),
            )?;

            trace!("updating git source `{:?}`", self.remote);

            self.remote
                .checkout(&db_path, &self.reference, self.config)?
        } else {
            (self.remote.db_at(&db_path)?, actual_rev.unwrap())
        };

        // Don’t use the full hash,
        // to contribute less to reaching the path length limit on Windows:
        // https://github.com/servo/servo/pull/14397
        let short_id = db.to_short_id(actual_rev.clone()).unwrap();

        let checkout_path = lock.parent()
            .join("checkouts")
            .join(&self.ident)
            .join(short_id.as_str());

        // Copy the database to the checkout location. After this we could drop
        // the lock on the database as we no longer needed it, but we leave it
        // in scope so the destructors here won't tamper with too much.
        // Checkout is immutable, so we don't need to protect it with a lock once
        // it is created.
        db.copy_to(actual_rev.clone(), &checkout_path, self.config)?;

        let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
        let path_source = PathSource::new_recursive(&checkout_path, &source_id, self.config);

        self.path_source = Some(path_source);
        self.rev = Some(actual_rev);
        self.path_source.as_mut().unwrap().update()
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
        trace!(
            "getting packages for package id `{}` from `{:?}`",
            id,
            self.remote
        );
        self.path_source
            .as_mut()
            .expect("BUG: update() must be called before get()")
            .download(id)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        Ok(self.rev.as_ref().unwrap().to_string())
    }
}

#[cfg(test)]
mod test {
    use url::Url;
    use super::ident;
    use util::ToUrl;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&url("https://github.com/carlhuda/cargo")).unwrap();
        assert!(ident.starts_with("cargo-"));
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&url("https://github.com")).unwrap();
        assert!(ident.starts_with("_empty-"));
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston/")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/pistondevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston.git")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_different_protocols() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("git://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_cannot_be_a_base_urls() {
        assert!(ident(&url("github.com:PistonDevelopers/piston")).is_err());
        assert!(ident(&url("google.com:PistonDevelopers/piston")).is_err());
    }

    fn url(s: &str) -> Url {
        s.to_url().unwrap()
    }
}
<commit_msg>Re-simplify GitSource::query<commit_after>use std::fmt::{self, Debug, Formatter};

use url::Url;

use core::source::{Source, SourceId};
use core::GitReference;
use core::{Dependency, Package, PackageId, Summary};
use util::Config;
use util::errors::CargoResult;
use util::hex::short_hash;
use sources::PathSource;
use sources::git::utils::{GitRemote, GitRevision};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'cfg> {
    remote: GitRemote,
    reference: GitReference,
    source_id: SourceId,
    path_source: Option<PathSource<'cfg>>,
    rev: Option<GitRevision>,
    ident: String,
    config: &'cfg Config,
}

impl<'cfg> GitSource<'cfg> {
    pub fn new(source_id: &SourceId, config: &'cfg Config) -> CargoResult<GitSource<'cfg>> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let remote = GitRemote::new(source_id.url());
        let ident = ident(source_id.url())?;

        let reference = match source_id.precise() {
            Some(s) => GitReference::Rev(s.to_string()),
            None => source_id.git_reference().unwrap().clone(),
        };

        let source = GitSource {
            remote,
            reference,
            source_id: source_id.clone(),
            path_source: None,
            rev: None,
            ident,
            config,
        };

        Ok(source)
    }

    pub fn url(&self) -> &Url {
        self.remote.url()
    }

    pub fn read_packages(&mut self) -> CargoResult<Vec<Package>> {
        if self.path_source.is_none() {
            self.update()?;
        }
        self.path_source.as_mut().unwrap().read_packages()
    }
}

fn ident(url: &Url) -> CargoResult<String> {
    let url = canonicalize_url(url)?;
    let ident = url.path_segments()
        .and_then(|mut s| s.next_back())
        .unwrap_or("");

    let ident = if ident == "" { "_empty" } else { ident };

    Ok(format!("{}-{}", ident, short_hash(&url)))
}

// Some hacks and heuristics for making equivalent URLs hash the same
pub fn canonicalize_url(url: &Url) -> CargoResult<Url> {
    let mut url = url.clone();

    // cannot-be-a-base-urls are not supported
    // eg. github.com:rust-lang-nursery/rustfmt.git
    if url.cannot_be_a_base() {
        bail!(
            "invalid url `{}`: cannot-be-a-base-URLs are not supported",
            url
        )
    }

    // Strip a trailing slash
    if url.path().ends_with('/') {
        url.path_segments_mut().unwrap().pop_if_empty();
    }

    // HACKHACK: For GitHub URL's specifically just lowercase
    // everything.  GitHub treats both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)
    if url.host_str() == Some("github.com") {
        url.set_scheme("https").unwrap();
        let path = url.path().to_lowercase();
        url.set_path(&path);
    }

    // Repos generally can be accessed with or w/o '.git'
    let needs_chopping = url.path().ends_with(".git");
    if needs_chopping {
        let last = {
            let last = url.path_segments().unwrap().next_back().unwrap();
            last[..last.len() - 4].to_owned()
        };
        url.path_segments_mut().unwrap().pop().push(&last);
    }

    Ok(url)
}

impl<'cfg> Debug for GitSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "git repo at {}", self.remote.url())?;

        match self.reference.pretty_ref() {
            Some(s) => write!(f, " ({})", s),
            None => Ok(()),
        }
    }
}

impl<'cfg> Source for GitSource<'cfg> {
    fn source_id(&self) -> &SourceId {
        &self.source_id
    }

    fn supports_checksums(&self) -> bool {
        false
    }

    fn requires_precise(&self) -> bool {
        true
    }

    fn query(&mut self, dep: &Dependency, f: &mut FnMut(Summary)) -> CargoResult<()> {
        let src = self.path_source
            .as_mut()
            .expect("BUG: update() must be called before query()");
        src.query(dep, f)
    }

    fn update(&mut self) -> CargoResult<()> {
        let lock =
            self.config
                .git_path()
                .open_rw(".cargo-lock-git", self.config, "the git checkouts")?;

        let db_path = lock.parent().join("db").join(&self.ident);

        if self.config.cli_unstable().offline && !db_path.exists() {
            bail!(
                "can't checkout from '{}': you are in the offline mode (-Z offline)",
                self.remote.url()
            );
        }

        // Resolve our reference to an actual revision, and check if the
        // database already has that revision. If it does, we just load a
        // database pinned at that revision, and if we don't we issue an update
        // to try to find the revision.
        let actual_rev = self.remote.rev_for(&db_path, &self.reference);
        let should_update = actual_rev.is_err() || self.source_id.precise().is_none();

        let (db, actual_rev) = if should_update && !self.config.cli_unstable().offline {
            self.config.shell().status(
                "Updating",
                format!("git repository `{}`", self.remote.url()),
            )?;

            trace!("updating git source `{:?}`", self.remote);

            self.remote
                .checkout(&db_path, &self.reference, self.config)?
        } else {
            (self.remote.db_at(&db_path)?, actual_rev.unwrap())
        };

        // Don’t use the full hash,
        // to contribute less to reaching the path length limit on Windows:
        // https://github.com/servo/servo/pull/14397
        let short_id = db.to_short_id(actual_rev.clone()).unwrap();

        let checkout_path = lock.parent()
            .join("checkouts")
            .join(&self.ident)
            .join(short_id.as_str());

        // Copy the database to the checkout location. After this we could drop
        // the lock on the database as we no longer needed it, but we leave it
        // in scope so the destructors here won't tamper with too much.
        // Checkout is immutable, so we don't need to protect it with a lock once
        // it is created.
        db.copy_to(actual_rev.clone(), &checkout_path, self.config)?;

        let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
        let path_source = PathSource::new_recursive(&checkout_path, &source_id, self.config);

        self.path_source = Some(path_source);
        self.rev = Some(actual_rev);
        self.path_source.as_mut().unwrap().update()
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
        trace!(
            "getting packages for package id `{}` from `{:?}`",
            id,
            self.remote
        );
        self.path_source
            .as_mut()
            .expect("BUG: update() must be called before get()")
            .download(id)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        Ok(self.rev.as_ref().unwrap().to_string())
    }
}

#[cfg(test)]
mod test {
    use url::Url;
    use super::ident;
    use util::ToUrl;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&url("https://github.com/carlhuda/cargo")).unwrap();
        assert!(ident.starts_with("cargo-"));
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&url("https://github.com")).unwrap();
        assert!(ident.starts_with("_empty-"));
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston/")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/pistondevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("https://github.com/PistonDevelopers/piston.git")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_different_protocols() {
        let ident1 = ident(&url("https://github.com/PistonDevelopers/piston")).unwrap();
        let ident2 = ident(&url("git://github.com/PistonDevelopers/piston")).unwrap();
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_cannot_be_a_base_urls() {
        assert!(ident(&url("github.com:PistonDevelopers/piston")).is_err());
        assert!(ident(&url("google.com:PistonDevelopers/piston")).is_err());
    }

    fn url(s: &str) -> Url {
        s.to_url().unwrap()
    }
}
<|endoftext|>