<commit_before>use hyper;
use serde_json;
use std;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/sectora.toml") }

impl Config {
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Repo {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

impl fmt::Display for SectorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SectorType::Team => write!(f, "T"),
            &SectorType::Repo => write!(f, "R"),
        }
    }
}

impl FromStr for SectorType {
    type Err = std::io::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "T" => Ok(SectorType::Team),
            "R" => Ok(SectorType::Repo),
            _ => Err(std::io::Error::new(std::io::ErrorKind::Other, "unknown sector type")),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team, }
    }
}

impl From<Repo> for Sector {
    fn from(repo: Repo) -> Self {
        Self { id: repo.id,
               name: repo.name,
               sector_type: SectorType::Repo, }
    }
}

impl fmt::Display for Sector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}:{}", self.id, self.name, self.sector_type) }
}

pub enum ParseSectorError {
    Id(std::num::ParseIntError),
    Type(std::io::Error),
}

impl FromStr for Sector {
    type Err = ParseSectorError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(":").collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse().map_err(|e| ParseSectorError::Id(e))?,
                  name: String::from(parts[1]),
                  sector_type: parts[2].parse().map_err(|e| ParseSectorError::Type(e))?, })
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

impl fmt::Display for SectorGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let members_str = self.members.values()
                              .map(|v| v.to_string())
                              .collect::<Vec<_>>()
                              .join(" ");
        write!(f,
               "{}\t{}\t{}\t{}",
               self.sector,
               self.gid.and_then(|i| Some(i.to_string())).unwrap_or(String::new()),
               self.group.clone().unwrap_or(String::new()),
               members_str)
    }
}

pub enum ParseSectorGroupError {
    Sector(ParseSectorError),
    Gid(std::num::ParseIntError),
    Member(std::num::ParseIntError),
}

impl FromStr for SectorGroup {
    type Err = ParseSectorGroupError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split("\t").collect::<Vec<&str>>();
        let sector = parts[0].parse().map_err(|e| ParseSectorGroupError::Sector(e))?;
        let gid: Option<u64> = match parts[1] {
            "" => None,
            s => Some(s.parse::<u64>().map_err(|e| ParseSectorGroupError::Gid(e))?),
        };
        let group: Option<String> = match parts[2] {
            "" => None,
            s => Some(String::from(s)),
        };
        let members = parts[3].split(" ")
                              .map(|s| s.parse::<Member>().map_err(|e| ParseSectorGroupError::Member(e)))
                              .collect::<Result<Vec<Member>, _>>()?
                              .into_iter()
                              .map(|m| (m.login.clone(), m))
                              .collect::<HashMap<_, _>>();
        Ok(Self { sector,
                  gid,
                  group,
                  members, })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

impl fmt::Display for Member {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.id, self.login) }
}

impl FromStr for Member {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(":").collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse()?,
                  login: String::from(parts[1]), })
    }
}

#[allow(dead_code)]
pub struct MemberGid {
    pub member: Member,
    pub gid: u64,
}

impl fmt::Display for MemberGid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}|{}", self.member, self.gid) }
}

impl FromStr for MemberGid {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split("|").collect::<Vec<&str>>();
        Ok(Self { member: parts[0].parse()?,
                  gid: parts[1].parse()?, })
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Rate {
    pub limit: usize,
    pub remaining: usize,
    pub reset: usize,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RateLimit {
    pub rate: Rate,
}

#[derive(Debug)]
pub enum CliError {
    Serde(serde_json::Error),
    Io(std::io::Error),
    Toml(toml::de::Error),
    Hyper(hyper::Error),
}

impl From<serde_json::Error> for CliError {
    fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }
}
impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError { CliError::Io(err) }
}
impl From<toml::de::Error> for CliError {
    fn from(err: toml::de::Error) -> CliError { CliError::Toml(err) }
}
impl From<hyper::Error> for CliError {
    fn from(err: hyper::Error) -> CliError { CliError::Hyper(err) }
}
<commit_msg>Add line end to SectorGroup format<commit_after>use hyper;
use serde_json;
use std;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/sectora.toml") }

impl Config {
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn new(configpath: &std::path::Path) -> Result<Self, CliError> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Repo {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

impl fmt::Display for SectorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &SectorType::Team => write!(f, "T"),
            &SectorType::Repo => write!(f, "R"),
        }
    }
}

impl FromStr for SectorType {
    type Err = std::io::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "T" => Ok(SectorType::Team),
            "R" => Ok(SectorType::Repo),
            _ => Err(std::io::Error::new(std::io::ErrorKind::Other, "unknown sector type")),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team, }
    }
}

impl From<Repo> for Sector {
    fn from(repo: Repo) -> Self {
        Self { id: repo.id,
               name: repo.name,
               sector_type: SectorType::Repo, }
    }
}

impl fmt::Display for Sector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}:{}", self.id, self.name, self.sector_type) }
}

pub enum ParseSectorError {
    Id(std::num::ParseIntError),
    Type(std::io::Error),
}

impl FromStr for Sector {
    type Err = ParseSectorError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(":").collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse().map_err(|e| ParseSectorError::Id(e))?,
                  name: String::from(parts[1]),
                  sector_type: parts[2].parse().map_err(|e| ParseSectorError::Type(e))?, })
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

impl fmt::Display for SectorGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let members_str = self.members.values()
                              .map(|v| v.to_string())
                              .collect::<Vec<_>>()
                              .join(" ");
        write!(f,
               "{}\t{}\t{}\t{}\n",
               self.sector,
               self.gid.and_then(|i| Some(i.to_string())).unwrap_or(String::new()),
               self.group.clone().unwrap_or(String::new()),
               members_str)
    }
}

pub enum ParseSectorGroupError {
    Sector(ParseSectorError),
    Gid(std::num::ParseIntError),
    Member(std::num::ParseIntError),
}

impl FromStr for SectorGroup {
    type Err = ParseSectorGroupError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split("\t").collect::<Vec<&str>>();
        let sector = parts[0].parse().map_err(|e| ParseSectorGroupError::Sector(e))?;
        let gid: Option<u64> = match parts[1] {
            "" => None,
            s => Some(s.parse::<u64>().map_err(|e| ParseSectorGroupError::Gid(e))?),
        };
        let group: Option<String> = match parts[2] {
            "" => None,
            s => Some(String::from(s)),
        };
        let members = parts[3].split(" ")
                              .map(|s| s.parse::<Member>().map_err(|e| ParseSectorGroupError::Member(e)))
                              .collect::<Result<Vec<Member>, _>>()?
                              .into_iter()
                              .map(|m| (m.login.clone(), m))
                              .collect::<HashMap<_, _>>();
        Ok(Self { sector,
                  gid,
                  group,
                  members, })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

impl fmt::Display for Member {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.id, self.login) }
}

impl FromStr for Member {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(":").collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse()?,
                  login: String::from(parts[1]), })
    }
}

#[allow(dead_code)]
pub struct MemberGid {
    pub member: Member,
    pub gid: u64,
}

impl fmt::Display for MemberGid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}|{}", self.member, self.gid) }
}

impl FromStr for MemberGid {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split("|").collect::<Vec<&str>>();
        Ok(Self { member: parts[0].parse()?,
                  gid: parts[1].parse()?, })
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Rate {
    pub limit: usize,
    pub remaining: usize,
    pub reset: usize,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RateLimit {
    pub rate: Rate,
}

#[derive(Debug)]
pub enum CliError {
    Serde(serde_json::Error),
    Io(std::io::Error),
    Toml(toml::de::Error),
    Hyper(hyper::Error),
}

impl From<serde_json::Error> for CliError {
    fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }
}
impl From<std::io::Error> for CliError {
    fn from(err: std::io::Error) -> CliError { CliError::Io(err) }
}
impl From<toml::de::Error> for CliError {
    fn from(err: toml::de::Error) -> CliError { CliError::Toml(err) }
}
impl From<hyper::Error> for CliError {
    fn from(err: hyper::Error) -> CliError { CliError::Hyper(err) }
}
<|endoftext|>


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

use libc::{c_ulong, c_void};
use std::ffi::CString;
use std::fmt;
use std::ops::Deref;
use std::ptr;
use std::slice;

use enums::{Content, Format, Status, SurfaceType};
use ffi;
#[cfg(feature = "use_glib")]
use glib::translate::*;

use device::Device;
use image_surface::ImageSurface;
use rectangle::Rectangle;
use rectangle_int::RectangleInt;

#[derive(Debug)]
pub struct Surface(*mut ffi::cairo_surface_t, bool);

impl Surface {
    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        ffi::cairo_surface_reference(ptr);
        Surface(ptr, false)
    }

    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr, true)
    }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Status> {
        assert!(!ptr.is_null());
        let status = Status::from(ffi::cairo_surface_status(ptr));
        status.to_result(Surface(ptr, false))
    }

    pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t {
        self.0
    }

    pub fn create_similar(
        &self,
        content: Content,
        width: i32,
        height: i32,
    ) -> Result<Surface, Status> {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_similar(
                self.0,
                content.into(),
                width,
                height,
            ))
        }
    }

    pub fn create_for_rectangle(&self, bounds: Rectangle) -> Result<Surface, Status> {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_for_rectangle(
                self.0,
                bounds.x,
                bounds.y,
                bounds.width,
                bounds.height,
            ))
        }
    }

    pub fn get_mime_data(&self, mime_type: &str) -> Option<Vec<u8>> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_get_mime_data(
                self.to_raw_none(),
                mime_type.as_ptr(),
                &data_ptr,
                &mut length,
            );
            if !data_ptr.is_null() && length != 0 {
                Some(slice::from_raw_parts(data_ptr as *const u8, length as usize).to_vec())
            } else {
                None
            }
        }
    }

    pub unsafe fn get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        let mime_type = CString::new(mime_type).unwrap();
        ffi::cairo_surface_get_mime_data(
            self.to_raw_none(),
            mime_type.as_ptr(),
            &data_ptr,
            &mut length,
        );
        if !data_ptr.is_null() && length != 0 {
            Some(slice::from_raw_parts(
                data_ptr as *const u8,
                length as usize,
            ))
        } else {
            None
        }
    }

    pub fn set_mime_data<T: AsRef<[u8]> + 'static>(
        &self,
        mime_type: &str,
        slice: T,
    ) -> Result<(), Status> {
        let b = Box::new(slice);
        let (size, data) = {
            let slice = (*b).as_ref();
            (slice.len(), slice.as_ptr())
        };

        let user_data = Box::into_raw(b);

        unsafe extern "C" fn unbox<T>(data: *mut c_void) {
            let data: Box<T> = Box::from_raw(data as *mut T);
            drop(data);
        }

        let status = unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            Status::from(ffi::cairo_surface_set_mime_data(
                self.to_raw_none(),
                mime_type.as_ptr(),
                data,
                size as c_ulong,
                Some(unbox::<T>),
                user_data as *mut _,
            ))
        };

        status.to_result(())
    }

    pub fn supports_mime_type(&self, mime_type: &str) -> bool {
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_supports_mime_type(self.0, mime_type.as_ptr()).as_bool()
        }
    }

    pub fn get_device(&self) -> Option<Device> {
        unsafe {
            let device = ffi::cairo_surface_get_device(self.to_raw_none());
            if device.is_null() {
                None
            } else {
                Some(Device::from_raw_none(ffi::cairo_surface_get_device(
                    self.to_raw_none(),
                )))
            }
        }
    }

    pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
        unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
    }

    pub fn get_device_offset(&self) -> (f64, f64) {
        let mut x_offset = 0.0f64;
        let mut y_offset = 0.0f64;
        unsafe {
            ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset);
        }
        (x_offset, y_offset)
    }

    #[cfg(any(feature = "v1_14", feature = "dox"))]
    pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
        unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
    }

    #[cfg(any(feature = "v1_14", feature = "dox"))]
    pub fn get_device_scale(&self) -> (f64, f64) {
        let mut x_scale = 0.0f64;
        let mut y_scale = 0.0f64;
        unsafe {
            ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale);
        }
        (x_scale, y_scale)
    }

    pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
        unsafe {
            ffi::cairo_surface_set_fallback_resolution(
                self.to_raw_none(),
                x_pixels_per_inch,
                y_pixels_per_inch,
            )
        }
    }

    pub fn get_fallback_resolution(&self) -> (f64, f64) {
        let mut x_pixels_per_inch = 0.0f64;
        let mut y_pixels_per_inch = 0.0f64;
        unsafe {
            ffi::cairo_surface_get_fallback_resolution(
                self.to_raw_none(),
                &mut x_pixels_per_inch,
                &mut y_pixels_per_inch,
            );
        }
        (x_pixels_per_inch, y_pixels_per_inch)
    }

    pub fn create_similar_image(
        &self,
        format: Format,
        width: i32,
        height: i32,
    ) -> Result<Surface, Status> {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_similar_image(
                self.to_raw_none(),
                format.into(),
                width,
                height,
            ))
        }
    }

    pub fn map_to_image(
        &self,
        extents: Option<RectangleInt>,
    ) -> Result<MappedImageSurface, Status> {
        unsafe {
            ImageSurface::from_raw_full(match extents {
                Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()),
                None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()),
            })
            .map(|s| MappedImageSurface {
                original_surface: self.clone(),
                image_surface: s,
            })
        }
    }

    user_data_methods! {
        ffi::cairo_surface_get_user_data,
        ffi::cairo_surface_set_user_data,
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        Stash(self.to_raw_none(), self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
        unsafe { ffi::cairo_surface_reference(self.to_raw_none()) }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_none(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_borrow(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_full(ptr).unwrap()
    }
}

#[cfg(feature = "use_glib")]
gvalue_impl!(
    Surface,
    ffi::cairo_surface_t,
    ffi::gobject::cairo_gobject_surface_get_type
);

impl Clone for Surface {
    fn clone(&self) -> Surface {
        unsafe { Self::from_raw_none(self.0) }
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        if !self.1 {
            unsafe {
                ffi::cairo_surface_destroy(self.0);
            }
        }
    }
}

impl fmt::Display for Surface {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Surface")
    }
}

impl Surface {
    pub fn flush(&self) {
        unsafe {
            ffi::cairo_surface_flush(self.0);
        }
    }

    pub fn finish(&self) {
        unsafe {
            ffi::cairo_surface_finish(self.0);
        }
    }

    pub fn get_type(&self) -> SurfaceType {
        unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0)) }
    }

    pub fn status(&self) -> Status {
        unsafe { Status::from(ffi::cairo_surface_status(self.0)) }
    }
}

#[derive(Debug)]
pub struct MappedImageSurface {
    original_surface: Surface,
    image_surface: ImageSurface,
}

impl Deref for MappedImageSurface {
    type Target = ImageSurface;

    fn deref(&self) -> &ImageSurface {
        &self.image_surface
    }
}

impl Drop for MappedImageSurface {
    fn drop(&mut self) {
        unsafe {
            ffi::cairo_surface_unmap_image(
                self.original_surface.to_raw_none(),
                self.image_surface.to_raw_none(),
            );
            ffi::cairo_surface_reference(self.image_surface.to_raw_none());
        }
    }
}

impl fmt::Display for MappedImageSurface {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MappedImageSurface")
    }
}

#[cfg(test)]
mod tests {
    use constants::MIME_TYPE_PNG;
    use Format;
    use ImageSurface;

    #[test]
    fn mime_data() {
        let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
        let data = surface.get_mime_data(MIME_TYPE_PNG);
        /* Initially the data for any mime type has to be none */
        assert!(data.is_none());

        assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok());
        let data = surface.get_mime_data(MIME_TYPE_PNG).unwrap();
        assert_eq!(data, &[1u8, 10u8]);
    }
}
<commit_msg>Replace bad code<commit_after>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::{c_ulong, c_void};
use std::ffi::CString;
use std::fmt;
use std::ops::Deref;
use std::ptr;
use std::slice;

use enums::{Content, Format, Status, SurfaceType};
use ffi;
#[cfg(feature = "use_glib")]
use glib::translate::*;

use device::Device;
use image_surface::ImageSurface;
use rectangle::Rectangle;
use rectangle_int::RectangleInt;

#[derive(Debug)]
pub struct Surface(*mut ffi::cairo_surface_t, bool);

impl Surface {
    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        ffi::cairo_surface_reference(ptr);
        Surface(ptr, false)
    }

    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr, true)
    }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Result<Surface, Status> {
        assert!(!ptr.is_null());
        let status = Status::from(ffi::cairo_surface_status(ptr));
        status.to_result(Surface(ptr, false))
    }

    pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t {
        self.0
    }

    pub fn create_similar(
        &self,
        content: Content,
        width: i32,
        height: i32,
    ) -> Result<Surface, Status> {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_similar(
                self.0,
                content.into(),
                width,
                height,
            ))
        }
    }

    pub fn create_for_rectangle(&self, bounds: Rectangle) -> Result<Surface, Status> {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_for_rectangle(
                self.0,
                bounds.x,
                bounds.y,
                bounds.width,
                bounds.height,
            ))
        }
    }

    pub fn get_mime_data(&self, mime_type: &str) -> Option<Vec<u8>> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_get_mime_data(
                self.to_raw_none(),
                mime_type.as_ptr(),
                &data_ptr,
                &mut length,
            );
            if !data_ptr.is_null() && length != 0 {
                Some(slice::from_raw_parts(data_ptr as *const u8, length as usize).to_vec())
            } else {
                None
            }
        }
    }

    pub unsafe fn get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        let mime_type = CString::new(mime_type).unwrap();
        ffi::cairo_surface_get_mime_data(
            self.to_raw_none(),
            mime_type.as_ptr(),
            &data_ptr,
            &mut length,
        );
        if !data_ptr.is_null() && length != 0 {
            Some(slice::from_raw_parts(
                data_ptr as *const u8,
                length as usize,
            ))
        } else {
            None
        }
    }

    pub fn set_mime_data<T: AsRef<[u8]> + 'static>(
        &self,
        mime_type: &str,
        slice: T,
    ) -> Result<(), Status> {
        let b = Box::new(slice);
        let (size, data) = {
            let slice = (*b).as_ref();
            (slice.len(), slice.as_ptr())
        };

        let user_data = Box::into_raw(b);

        unsafe extern "C" fn unbox<T>(data: *mut c_void) {
            let data: Box<T> = Box::from_raw(data as *mut T);
            drop(data);
        }

        let status = unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            Status::from(ffi::cairo_surface_set_mime_data(
                self.to_raw_none(),
                mime_type.as_ptr(),
                data,
                size as c_ulong,
                Some(unbox::<T>),
                user_data as *mut _,
            ))
        };

        status.to_result(())
    }

    pub fn supports_mime_type(&self, mime_type: &str) -> bool {
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_supports_mime_type(self.0, mime_type.as_ptr()).as_bool()
        }
    }

    pub fn get_device(&self) -> Option<Device> {
        unsafe {
            let device = ffi::cairo_surface_get_device(self.to_raw_none());
            if device.is_null() {
                None
            } else {
                Some(Device::from_raw_none(device))
            }
        }
    }

    pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
        unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
    }

    pub fn get_device_offset(&self) -> (f64, f64) {
        let mut x_offset = 0.0f64;
        let mut y_offset = 0.0f64;
        unsafe {
            ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset);
        }
        (x_offset, y_offset)
    }

    #[cfg(any(feature = "v1_14", feature = "dox"))]
    pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
        unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
    }

    #[cfg(any(feature = "v1_14", feature = "dox"))]
    pub fn get_device_scale(&self) -> (f64, f64) {
        let mut x_scale = 0.0f64;
        let mut y_scale = 0.0f64;
        unsafe {
            ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale);
        }
        (x_scale, y_scale)
    }

    pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
        unsafe {
            ffi::cairo_surface_set_fallback_resolution(
                self.to_raw_none(),
                x_pixels_per_inch,
                y_pixels_per_inch,
            )
        }
    }

    pub fn get_fallback_resolution(&self) -> (f64, f64) {
        let mut x_pixels_per_inch = 0.0f64;
        let mut y_pixels_per_inch = 0.0f64;
        unsafe {
            ffi::cairo_surface_get_fallback_resolution(
                self.to_raw_none(),
                &mut x_pixels_per_inch,
                &mut y_pixels_per_inch,
            );
        }
        (x_pixels_per_inch, y_pixels_per_inch)
    }

    pub fn create_similar_image(
        &self,
        format: Format,
        width: i32,
        height: i32,
    ) -> Result<Surface, Status> {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_similar_image(
                self.to_raw_none(),
                format.into(),
                width,
                height,
            ))
        }
    }

    pub fn map_to_image(
        &self,
        extents: Option<RectangleInt>,
    ) -> Result<MappedImageSurface, Status> {
        unsafe {
            ImageSurface::from_raw_full(match extents {
                Some(ref e) => ffi::cairo_surface_map_to_image(self.to_raw_none(), e.to_raw_none()),
                None => ffi::cairo_surface_map_to_image(self.to_raw_none(), std::ptr::null()),
            })
            .map(|s| MappedImageSurface {
                original_surface: self.clone(),
                image_surface: s,
            })
        }
    }

    user_data_methods! {
        ffi::cairo_surface_get_user_data,
        ffi::cairo_surface_set_user_data,
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        Stash(self.to_raw_none(), self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
        unsafe { ffi::cairo_surface_reference(self.to_raw_none()) }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_none(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_borrow(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_full(ptr).unwrap()
    }
}

#[cfg(feature = "use_glib")]
gvalue_impl!(
    Surface,
    ffi::cairo_surface_t,
    ffi::gobject::cairo_gobject_surface_get_type
);

impl Clone for Surface {
    fn clone(&self) -> Surface {
        unsafe { Self::from_raw_none(self.0) }
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        if !self.1 {
            unsafe {
                ffi::cairo_surface_destroy(self.0);
            }
        }
    }
}

impl fmt::Display for Surface {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Surface")
    }
}

impl Surface {
    pub fn flush(&self) {
        unsafe {
            ffi::cairo_surface_flush(self.0);
        }
    }

    pub fn finish(&self) {
        unsafe {
            ffi::cairo_surface_finish(self.0);
        }
    }

    pub fn get_type(&self) -> SurfaceType {
        unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.0)) }
    }

    pub fn status(&self) -> Status {
        unsafe { Status::from(ffi::cairo_surface_status(self.0)) }
    }
}

#[derive(Debug)]
pub struct MappedImageSurface {
    original_surface: Surface,
    image_surface: ImageSurface,
}

impl Deref for MappedImageSurface {
    type Target = ImageSurface;

    fn deref(&self) -> &ImageSurface {
        &self.image_surface
    }
}

impl Drop for MappedImageSurface {
    fn drop(&mut self) {
        unsafe {
            ffi::cairo_surface_unmap_image(
                self.original_surface.to_raw_none(),
                self.image_surface.to_raw_none(),
            );
            ffi::cairo_surface_reference(self.image_surface.to_raw_none());
        }
    }
}

impl fmt::Display for MappedImageSurface {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MappedImageSurface")
    }
}

#[cfg(test)]
mod tests {
    use constants::MIME_TYPE_PNG;
    use Format;
    use ImageSurface;

    #[test]
    fn mime_data() {
        let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
        let data = surface.get_mime_data(MIME_TYPE_PNG);
        /* Initially the data for any mime type has to be none */
        assert!(data.is_none());

        assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok());
        let data = surface.get_mime_data(MIME_TYPE_PNG).unwrap();
        assert_eq!(data, &[1u8, 10u8]);
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::Image2d;
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        unsafe {
            let mut id : GLuint =  0;

            gl::GenTextures(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Texture {
                gl_id: id,
                phantom: PhantomData
            }
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

gl_enum! {
    pub gl_enum Tx2dImageTarget {
        Texture2d as TEXTURE_2D_TARGET = gl::TEXTURE_2D
    }
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

gl_enum! {
    pub gl_enum TextureBindingTarget {
        Texture2d as TEXTURE_2D = gl::TEXTURE_2D,
        TextureCubeMap as TEXTURE_CUBE_MAP = gl::TEXTURE_CUBE_MAP
    }
}

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    pub gl_enum TextureWrapMode {
        ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,
        MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,
        Repeat as REPEAT = gl::REPEAT
    }
}

unsafe fn _tex_parameter_iv(target: TextureBindingTarget,
                            pname: GLenum,
                            params: *const GLint)
{
    gl::TexParameteriv(target.gl_enum(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target` or `pname` is not an accepted defined value, or `params` should have defined a symbolic constant and does not",
        _ => "Unknown error"
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn set_min_filter<F: Into<TextureMipmapFilter>>(&mut self, filter: F) {
        let gl_int = filter.into().gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MIN_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_mag_filter(&mut self, filter: TextureFilter) {
        let gl_int = filter.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MAG_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_s(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_S,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_t(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_T,
                              &gl_int as *const GLint);
        }
    }

    fn image_2d<I>(&mut self,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   level: u32,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            let ptr = mem::transmute(img.textel_bytes().as_ptr());
            gl::TexImage2D(target.gl_enum(),
                           level as GLint,
                           img.format().textel_format.gl_enum() as GLint,
                           img.width() as i32,
                           img.height() as i32,
                           0,
                           img.format().textel_format.gl_enum(),
                           img.format().textel_type.gl_enum(),
                           ptr);
            dbg_gl_error! {
                GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
                GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
                GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
                _ => "Unknown error"
            }
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut Texture2d)
        -> Texture2dBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<commit_msg>Move raw `glTexImage2D` call to `_tex_image_2d` function<commit_after>use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::{Image2d, TextelFormat, ImageFormat};
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        unsafe {
            let mut id : GLuint =  0;

            gl::GenTextures(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Texture {
                gl_id: id,
                phantom: PhantomData
            }
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

gl_enum! {
    pub gl_enum Tx2dImageTarget {
        Texture2d as TEXTURE_2D_TARGET = gl::TEXTURE_2D
    }
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

gl_enum! {
    pub gl_enum TextureBindingTarget {
        Texture2d as TEXTURE_2D = gl::TEXTURE_2D,
        TextureCubeMap as TEXTURE_CUBE_MAP = gl::TEXTURE_CUBE_MAP
    }
}

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    pub gl_enum TextureWrapMode {
        ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,
        MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,
        Repeat as REPEAT = gl::REPEAT
    }
}

unsafe fn _tex_parameter_iv(target: TextureBindingTarget,
                            pname: GLenum,
                            params: *const GLint)
{
    gl::TexParameteriv(target.gl_enum(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target` or `pname` is not an accepted defined value, or `params` should have defined a symbolic constant and does not",
        _ => "Unknown error"
    }
}

unsafe fn _tex_image_2d<T: ImageTargetType>(target: T,
                                            level: u32,
                                            internal_format: TextelFormat,
                                            width: u32,
                                            height: u32,
                                            border: u32,
                                            format: ImageFormat,
                                            image_ptr: *const u8) {
    debug_assert!(internal_format == format.textel_format);
    gl::TexImage2D(target.gl_enum(),
                   level as GLint,
                   internal_format.gl_enum() as GLint,
                   width as GLint,
                   height as GLint,
                   border as GLint,
                   format.textel_format.gl_enum(),
                   format.textel_type.gl_enum(),
                   image_ptr as *const GLvoid);
    dbg_gl_error! {
        GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
        GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
        GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
        _ => "Unknown error"
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn set_min_filter<F: Into<TextureMipmapFilter>>(&mut self, filter: F) {
        let gl_int = filter.into().gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MIN_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_mag_filter(&mut self, filter: TextureFilter) {
        let gl_int = filter.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MAG_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_s(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_S,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_t(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_T,
                              &gl_int as *const GLint);
        }
    }

    fn image_2d<I>(&mut self,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   level: u32,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          img.format().textel_format,
                          img.width() as u32,
                          img.height() as u32,
                          0,
                          img.format(),
                          img.textel_bytes().as_ptr());
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut Texture2d)
        -> Texture2dBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<|endoftext|>


<commit_before>extern crate walkdir;
extern crate itertools;
extern crate cargo_erlangapp;

use cargo_erlangapp::{Target, target_filenames};
use std::ffi::{OsStr};
use std::{env, fs, io};
use std::path::{Path};
use walkdir::WalkDir;
use itertools::Itertools;

#[cfg(unix)]
const TEST_DIR: &'static str = "tests/testdir";
#[cfg(unix)]
const APP_DIR: &'static str = "tests/testdir/testapp";
#[cfg(unix)]
const APP_SRC: &'static str = "tests/testapp";

#[cfg(windows)]
const TEST_DIR: &'static str = "tests\\testdir";
#[cfg(windows)]
const APP_DIR: &'static str = "tests\\testdir\\testapp";
#[cfg(windows)]
const APP_SRC: &'static str = "tests\\testapp";

#[test]
fn do_test() {
    test_init();

    invoke_with_args(&["cargo-erlangapp", "build" ]);
    check_build();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    invoke_with_args(&["cargo-erlangapp", "build", "--release" ]);
    check_build();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    invoke_with_args(&["cargo-erlangapp", "build", "--target=x86_64-unknown-linux-gnu" ]);
    check_build();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    invoke_with_args(&["cargo-erlangapp", "test" ]);
    check_clean();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    test_cleanup();
}

fn invoke_with_args(args: &[&str]) {
    let mut appdir = env::current_dir().unwrap();
    appdir.push(TEST_DIR);
    appdir.push("testapp");
    cargo_erlangapp::invoke_with_args(args.into_iter().cloned(), &appdir)
}

fn check_build() {
    check_artifact("bonjourdylib", &Target::Dylib("bonjourdylib".into())).unwrap();
    check_artifact("helloexe", &Target::Bin("helloexe".into())).unwrap();
}

fn check_clean() {
    check_artifact("bonjourdylib", &Target::Dylib("bonjourdylib".into())).unwrap_err();
    check_artifact("helloexe", &Target::Bin("helloexe".into())).unwrap_err();
}

fn check_artifact(cratename: &str, target: &Target) -> Result<String,String> {
    let (dstname, _srcname) = target_filenames(target);
    let targetpath = Path::new(APP_DIR).join("priv").join("crates").join(cratename).join(dstname);
    file_must_exist(&targetpath)
}

fn file_must_exist<S: AsRef<OsStr> + ?Sized>(s: &S) -> Result<String,String> {
    match fs::metadata(s.as_ref())
        .map(|m| m.is_file()).unwrap_or(false) {
        true => Ok(format!("{:?} exists!", s.as_ref())),
        false => Err(format!("{:?} does not exist!", s.as_ref())),
    }
}

fn test_init() {
    test_cleanup();
    copy_all(APP_SRC, TEST_DIR).unwrap();
}

fn test_cleanup() {
    let testdir = Path::new("tests").join("testdir");
    if fs::metadata(&testdir).map(|m| m.is_dir()).unwrap_or(false) {
        fs::remove_dir_all(&testdir).unwrap();
    }
}

fn copy_all<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
    // calculate how many path elements to chop off entry when forming to path
    let chop_cnt = from.as_ref().components().count() - 1;
    for entry in WalkDir::new(from).follow_links(false) {
        let entry = try!(entry);
        let filetype = entry.file_type();
        let compi = entry.path().components().dropping(chop_cnt);
        let to_path = to.as_ref().join(compi.as_path());
        //let to_path = to.as_ref().join(entry.path());
        if filetype.is_dir() {
            try!(fs::create_dir_all(to_path));
        } else if filetype.is_file() {
            try!(fs::copy(entry.path(), to_path));
        }
    }
    Ok(())
}

//testapp_src
//drwxr-xr-x 3 goertzen goertzen 4 Jul 18 09:34 bonjourdylib
//drwxr-xr-x 3 goertzen goertzen 4 Jul 18 09:34 helloexe
//drwxr-xr-x 3 goertzen goertzen 4 Jul 18 09:37 holalib

<commit_msg>disable non-portable test<commit_after>extern crate walkdir;
extern crate itertools;
extern crate cargo_erlangapp;

use cargo_erlangapp::{Target, target_filenames};
use std::ffi::{OsStr};
use std::{env, fs, io};
use std::path::{Path};
use walkdir::WalkDir;
use itertools::Itertools;

#[cfg(unix)]
const TEST_DIR: &'static str = "tests/testdir";
#[cfg(unix)]
const APP_DIR: &'static str = "tests/testdir/testapp";
#[cfg(unix)]
const APP_SRC: &'static str = "tests/testapp";

#[cfg(windows)]
const TEST_DIR: &'static str = "tests\\testdir";
#[cfg(windows)]
const APP_DIR: &'static str = "tests\\testdir\\testapp";
#[cfg(windows)]
const APP_SRC: &'static str = "tests\\testapp";

#[test]
fn do_test() {
    test_init();

    invoke_with_args(&["cargo-erlangapp", "build" ]);
    check_build();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    invoke_with_args(&["cargo-erlangapp", "build", "--release" ]);
    check_build();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    // this test is not portable
    //    invoke_with_args(&["cargo-erlangapp", "build", "--target=x86_64-unknown-linux-gnu" ]);
    //    check_build();
    //    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    //    check_clean();

    invoke_with_args(&["cargo-erlangapp", "test" ]);
    check_clean();
    invoke_with_args(&["cargo-erlangapp", "clean" ]);
    check_clean();

    test_cleanup();
}

fn invoke_with_args(args: &[&str]) {
    let mut appdir = env::current_dir().unwrap();
    appdir.push(TEST_DIR);
    appdir.push("testapp");
    cargo_erlangapp::invoke_with_args(args.into_iter().cloned(), &appdir)
}

fn check_build() {
    check_artifact("bonjourdylib", &Target::Dylib("bonjourdylib".into())).unwrap();
    check_artifact("helloexe", &Target::Bin("helloexe".into())).unwrap();
}

fn check_clean() {
    check_artifact("bonjourdylib", &Target::Dylib("bonjourdylib".into())).unwrap_err();
    check_artifact("helloexe", &Target::Bin("helloexe".into())).unwrap_err();
}

fn check_artifact(cratename: &str, target: &Target) -> Result<String,String> {
    let (dstname, _srcname) = target_filenames(target);
    let targetpath = Path::new(APP_DIR).join("priv").join("crates").join(cratename).join(dstname);
    file_must_exist(&targetpath)
}

fn file_must_exist<S: AsRef<OsStr> + ?Sized>(s: &S) -> Result<String,String> {
    match fs::metadata(s.as_ref())
        .map(|m| m.is_file()).unwrap_or(false) {
        true => Ok(format!("{:?} exists!", s.as_ref())),
        false => Err(format!("{:?} does not exist!", s.as_ref())),
    }
}

fn test_init() {
    test_cleanup();
    copy_all(APP_SRC, TEST_DIR).unwrap();
}

fn test_cleanup() {
    let testdir = Path::new("tests").join("testdir");
    if fs::metadata(&testdir).map(|m| m.is_dir()).unwrap_or(false) {
        fs::remove_dir_all(&testdir).unwrap();
    }
}

fn copy_all<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
    // calculate how many path elements to chop off entry when forming to path
    let chop_cnt = from.as_ref().components().count() - 1;
    for entry in WalkDir::new(from).follow_links(false) {
        let entry = try!(entry);
        let filetype = entry.file_type();
        let compi = entry.path().components().dropping(chop_cnt);
        let to_path = to.as_ref().join(compi.as_path());
        //let to_path = to.as_ref().join(entry.path());
        if filetype.is_dir() {
            try!(fs::create_dir_all(to_path));
        } else if filetype.is_file() {
            try!(fs::copy(entry.path(), to_path));
        }
    }
    Ok(())
}

//testapp_src
//drwxr-xr-x 3 goertzen goertzen 4 Jul 18 09:34 bonjourdylib
//drwxr-xr-x 3 goertzen goertzen 4 Jul 18 09:34 helloexe
//drwxr-xr-x 3 goertzen goertzen 4 Jul 18 09:37 holalib

<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// arch/amd64/memory/virt.rs
//! Virtual address space management
use core::prelude::*;
use super::{PAddr,VAddr};
use PAGE_SIZE;

static MASK_VBITS : usize = 0x0000FFFF_FFFFFFFF;

#[derive(PartialEq,Debug)]
enum PTEPos
{
	Absent,
	Page512G,
	Page1G,
	Page2M,
	Page4K,
}

struct PTE
{
	pos: PTEPos,
	data: *mut u64
}

unsafe fn get_entry(level: u8, index: usize, force_allocate: bool) -> PTE
{
	use arch::memory::addresses::FRACTAL_BASE;
	let index = if index < (1<<(4*9)) { index as isize } else { panic!("{} index OOR {}", module_path!(), index) };

	let pt_page = (FRACTAL_BASE & MASK_VBITS) / PAGE_SIZE;
	let tab_pt = FRACTAL_BASE as *mut u64;
	let tab_pd = tab_pt.offset( pt_page as isize );
	let tab_pdp = tab_pd.offset( (pt_page >> (9)) as isize );
	let tab_pml4 = tab_pdp.offset( (pt_page >> (9+9)) as isize );
	//log_debug!("tab_pt = {}, tab_pd = {}, tab_pdp = {}, tab_pml4 = {}",
	//	tab_pt, tab_pd, tab_pdp, tab_pml4);

	//log_trace!("get_entry(level={}, index={:#x})", level, index);
	// NOTE: Does no checks on presence
	match level
	{
	0 => {
		assert!(index < 512*512*512*512);
		PTE::new(PTEPos::Page4K, tab_pt.offset(index))
		}
	1 => {
		assert!(index < 512*512*512);
		let rv = PTE::new(PTEPos::Page2M, tab_pd.offset(index));
		if !rv.is_present() && force_allocate {
			let ptr = tab_pt.offset(index * 512) as *mut ();
			//log_debug!("Allocating for {:?} (PD Ent {})", ptr, index);
			::memory::phys::allocate( ptr );
		}
		rv
		},
	2 => {
		assert!(index < 512*512);
		let rv = PTE::new(PTEPos::Page1G, tab_pdp.offset(index));
		if !rv.is_present() && force_allocate {
			let ptr = tab_pd.offset(index * 512) as *mut ();
			//log_debug!("Allocating for {:?} (PDPT Ent {})", ptr, index);
			::memory::phys::allocate( ptr );
		}
		rv
		},
	3 => {
		assert!(index < 512);
		let rv = PTE::new(PTEPos::Page512G, tab_pml4.offset(index));
		if !rv.is_present() && force_allocate {
			let ptr = tab_pdp.offset(index * 512) as *mut ();
			//log_debug!("Allocating for {:?} (PML4 Ent {})", ptr, index);
			::memory::phys::allocate( ptr );
		}
		rv
		},
	_ => panic!("Passed invalid number to get_entry, {} > 3", level)
	}
}
unsafe fn get_page_ent(addr: usize, from_temp: bool, allocate: bool, large_ok: bool) -> PTE
{
	assert!( from_temp == false );
	let pagenum = (addr & MASK_VBITS) / PAGE_SIZE;
	//log_trace!("get_page_ent(addr={:#x}, from_temp={}, allocate={}), pagenum={:#x}", addr, from_temp, allocate, pagenum);

	let ent = get_entry(3, pagenum >> (9*3), allocate);
	// 1. Walk down page tables from PML4
	if !ent.is_present() {
		//log_trace!("get_page_ent(addr={:#x}, ...) PML4 Ent {} absent", addr, pagenum >> (9*3));
		return PTE::null();
	}

	let ent = get_entry(2, pagenum >> (9*2), allocate);
	if !ent.is_present() {
		//log_trace!("get_page_ent(addr={:#x}, ...) PDPT Ent {} absent", addr, pagenum >> (9*2));
		return PTE::null();
	}
	if ent.is_large() {
		panic!("TODO: Support large pages (1GiB)");
	}

	let ent = get_entry(1, pagenum >> (9*1), allocate);
	if !ent.is_present() {
		//log_trace!("get_page_ent(addr={:#x}, ...) PD Ent {} absent", addr, pagenum >> (9*1));
		return PTE::null();
	}
	if ent.is_large() {
		//log_trace!("Large page covering {:#x}", addr);
		if large_ok {
			return ent;
		}
		else {
			PTE::null();
		}
	}

	return get_entry(0, pagenum, allocate)
}

/// Returns Some(addr) if the passed physical address is in a fixed allocation range (i.e. kernel's identity range)
pub fn fixed_alloc(addr: PAddr, page_count: usize) -> Option<VAddr>
{
	const FOURMEG: PAddr = (::arch::memory::addresses::IDENT_END - ::arch::memory::addresses::IDENT_START) as PAddr;
	if addr < FOURMEG && (FOURMEG - addr >> 10) as usize > page_count
	{
		Some( ::arch::memory::addresses::IDENT_START + addr as usize )
	}
	else
	{
		None
	}
}

pub fn is_fixed_alloc(addr: *const (), page_count: usize) -> bool
{
	use arch::memory::addresses::{IDENT_START,IDENT_END};

	let vaddr = addr as usize;
	if IDENT_START <= vaddr && vaddr < IDENT_END {
		let space = IDENT_END - vaddr;
		assert!(space >> 12 >= page_count);
		true
	}
	else {
		false
	}
}

/// Returns true if the passed address is "valid" (allocated, or delay allocated)
pub fn is_reserved<T>(addr: *const T) -> bool
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, false, true);
		return !pte.is_null() && pte.is_reserved();
	}
}
/// Returns the physical address for the provided pointer
pub fn get_phys<T>(addr: *const T) -> PAddr
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, false, true);
		pte.addr()
	}
}
/// Maps a physical frame to a page, with the provided protection mode
pub fn map(addr: *mut (), phys: PAddr, prot: ::memory::virt::ProtectionMode)
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, true, false);
		assert!( !pte.is_null(), "Failed to obtain ent for {:p}", addr );
		pte.set( phys, prot );
		asm!("invlpg $0" : : "m" (addr) : "memory");
	}
}
/// Removes a mapping
pub fn unmap(addr: *mut ())
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, false, false);
		pte.set( 0, ::memory::virt::ProtectionMode::Unmapped );

		asm!("invlpg $0" : : "m" (addr) : "memory");
	}
}

static PF_PRESENT : u64 = 0x001;
static PF_LARGE   : u64 = 0x080;

impl PTE
{
	pub fn new(pos: PTEPos, ptr: *mut u64) -> PTE
	{
		PTE { pos: pos, data: ptr }
	}
	pub fn null() -> PTE {
		PTE { pos: PTEPos::Absent, data: ::core::ptr::null_mut() }
	}

	pub fn is_null(&self) -> bool { self.pos == PTEPos::Absent }
	pub unsafe fn is_reserved(&self) -> bool { !self.is_null() && *self.data != 0 }
	pub unsafe fn is_present(&self) -> bool { !self.is_null() && *self.data & 1 != 0 }
	pub unsafe fn is_large(&self) -> bool { *self.data & (PF_PRESENT | PF_LARGE) == PF_LARGE|PF_PRESENT }

	pub unsafe fn addr(&self) -> PAddr { *self.data & 0x7FFFFFFF_FFFFF000 }
	//pub unsafe fn set_addr(&self, paddr: PAddr) {
	//	assert!(!self.is_null());
	//	*self.data = (*self.data & !0x7FFFFFFF_FFFFF000) | paddr;
	//}

	pub unsafe fn set(&self, paddr: PAddr, prot: ::memory::virt::ProtectionMode) {
		assert!(!self.is_null());
		let flags: u64 = match prot
			{
			::memory::virt::ProtectionMode::Unmapped => 0,
			::memory::virt::ProtectionMode::KernelRO => (1<<63)|1,
			::memory::virt::ProtectionMode::KernelRW => (1<<63)|2|1,
			::memory::virt::ProtectionMode::KernelRX => 1,
			::memory::virt::ProtectionMode::UserRO => (1<<63)|4|1,
			::memory::virt::ProtectionMode::UserRW => (1<<63)|4|2|1,
			::memory::virt::ProtectionMode::UserRX => 4|1,
			};
		*self.data = (paddr & 0x7FFFFFFF_FFFFF000) | flags;
	}
}

impl ::core::fmt::Debug for PTE
{
	fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
		unsafe { write!(f, "PTE({:?}, *{:?}={:#x})", self.pos, self.data, *self.data) }
	}
}

// vim: ft=rust

<commit_msg>Kernel/amd64 - Fix incorrect invlpg argument<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// arch/amd64/memory/virt.rs
//! Virtual address space management
use core::prelude::*;
use super::{PAddr,VAddr};
use PAGE_SIZE;

static MASK_VBITS : usize = 0x0000FFFF_FFFFFFFF;

#[derive(PartialEq,Debug)]
enum PTEPos
{
	Absent,
	Page512G,
	Page1G,
	Page2M,
	Page4K,
}

struct PTE
{
	pos: PTEPos,
	data: *mut u64
}

unsafe fn get_entry(level: u8, index: usize, force_allocate: bool) -> PTE
{
	use arch::memory::addresses::FRACTAL_BASE;
	let index = if index < (1<<(4*9)) { index as isize } else { panic!("{} index OOR {}", module_path!(), index) };

	let pt_page = (FRACTAL_BASE & MASK_VBITS) / PAGE_SIZE;
	let tab_pt = FRACTAL_BASE as *mut u64;
	let tab_pd = tab_pt.offset( pt_page as isize );
	let tab_pdp = tab_pd.offset( (pt_page >> (9)) as isize );
	let tab_pml4 = tab_pdp.offset( (pt_page >> (9+9)) as isize );
	//log_debug!("tab_pt = {}, tab_pd = {}, tab_pdp = {}, tab_pml4 = {}",
	//	tab_pt, tab_pd, tab_pdp, tab_pml4);

	//log_trace!("get_entry(level={}, index={:#x})", level, index);
	// NOTE: Does no checks on presence
	match level
	{
	0 => {
		assert!(index < 512*512*512*512);
		PTE::new(PTEPos::Page4K, tab_pt.offset(index))
		}
	1 => {
		assert!(index < 512*512*512);
		let rv = PTE::new(PTEPos::Page2M, tab_pd.offset(index));
		if !rv.is_present() && force_allocate {
			let ptr = tab_pt.offset(index * 512) as *mut ();
			//log_debug!("Allocating for {:?} (PD Ent {})", ptr, index);
			::memory::phys::allocate( ptr );
		}
		rv
		},
	2 => {
		assert!(index < 512*512);
		let rv = PTE::new(PTEPos::Page1G, tab_pdp.offset(index));
		if !rv.is_present() && force_allocate {
			let ptr = tab_pd.offset(index * 512) as *mut ();
			//log_debug!("Allocating for {:?} (PDPT Ent {})", ptr, index);
			::memory::phys::allocate( ptr );
		}
		rv
		},
	3 => {
		assert!(index < 512);
		let rv = PTE::new(PTEPos::Page512G, tab_pml4.offset(index));
		if !rv.is_present() && force_allocate {
			let ptr = tab_pdp.offset(index * 512) as *mut ();
			//log_debug!("Allocating for {:?} (PML4 Ent {})", ptr, index);
			::memory::phys::allocate( ptr );
		}
		rv
		},
	_ => panic!("Passed invalid number to get_entry, {} > 3", level)
	}
}
unsafe fn get_page_ent(addr: usize, from_temp: bool, allocate: bool, large_ok: bool) -> PTE
{
	assert!( from_temp == false );
	let pagenum = (addr & MASK_VBITS) / PAGE_SIZE;
	//log_trace!("get_page_ent(addr={:#x}, from_temp={}, allocate={}), pagenum={:#x}", addr, from_temp, allocate, pagenum);

	let ent = get_entry(3, pagenum >> (9*3), allocate);
	// 1. Walk down page tables from PML4
	if !ent.is_present() {
		//log_trace!("get_page_ent(addr={:#x}, ...) PML4 Ent {} absent", addr, pagenum >> (9*3));
		return PTE::null();
	}

	let ent = get_entry(2, pagenum >> (9*2), allocate);
	if !ent.is_present() {
		//log_trace!("get_page_ent(addr={:#x}, ...) PDPT Ent {} absent", addr, pagenum >> (9*2));
		return PTE::null();
	}
	if ent.is_large() {
		panic!("TODO: Support large pages (1GiB)");
	}

	let ent = get_entry(1, pagenum >> (9*1), allocate);
	if !ent.is_present() {
		//log_trace!("get_page_ent(addr={:#x}, ...) PD Ent {} absent", addr, pagenum >> (9*1));
		return PTE::null();
	}
	if ent.is_large() {
		//log_trace!("Large page covering {:#x}", addr);
		if large_ok {
			return ent;
		}
		else {
			PTE::null();
		}
	}

	return get_entry(0, pagenum, allocate)
}

/// Returns Some(addr) if the passed physical address is in a fixed allocation range (i.e. kernel's identity range)
pub fn fixed_alloc(addr: PAddr, page_count: usize) -> Option<VAddr>
{
	const FOURMEG: PAddr = (::arch::memory::addresses::IDENT_END - ::arch::memory::addresses::IDENT_START) as PAddr;
	if addr < FOURMEG && (FOURMEG - addr >> 10) as usize > page_count
	{
		Some( ::arch::memory::addresses::IDENT_START + addr as usize )
	}
	else
	{
		None
	}
}

pub fn is_fixed_alloc(addr: *const (), page_count: usize) -> bool
{
	use arch::memory::addresses::{IDENT_START,IDENT_END};

	let vaddr = addr as usize;
	if IDENT_START <= vaddr && vaddr < IDENT_END {
		let space = IDENT_END - vaddr;
		assert!(space >> 12 >= page_count);
		true
	}
	else {
		false
	}
}

/// Returns true if the passed address is "valid" (allocated, or delay allocated)
pub fn is_reserved<T>(addr: *const T) -> bool
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, false, true);
		return !pte.is_null() && pte.is_reserved();
	}
}
/// Returns the physical address for the provided pointer
pub fn get_phys<T>(addr: *const T) -> PAddr
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, false, true);
		pte.addr()
	}
}
/// Maps a physical frame to a page, with the provided protection mode
pub fn map(addr: *mut (), phys: PAddr, prot: ::memory::virt::ProtectionMode)
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, true, false);
		assert!( !pte.is_null(), "Failed to obtain ent for {:p}", addr );
		pte.set( phys, prot );
		asm!("invlpg ($0)" : : "r" (addr) : "memory" : "volatile");
	}
}
/// Removes a mapping
pub fn unmap(addr: *mut ())
{
	unsafe {
		let pte = get_page_ent(addr as usize, false, false, false);
		pte.set( 0, ::memory::virt::ProtectionMode::Unmapped );

		asm!("invlpg ($0)" : : "r" (addr) : "memory" : "volatile");
	}
}

static PF_PRESENT : u64 = 0x001;
static PF_LARGE   : u64 = 0x080;

impl PTE
{
	pub fn new(pos: PTEPos, ptr: *mut u64) -> PTE
	{
		PTE { pos: pos, data: ptr }
	}
	pub fn null() -> PTE {
		PTE { pos: PTEPos::Absent, data: ::core::ptr::null_mut() }
	}

	pub fn is_null(&self) -> bool { self.pos == PTEPos::Absent }
	pub unsafe fn is_reserved(&self) -> bool { !self.is_null() && *self.data != 0 }
	pub unsafe fn is_present(&self) -> bool { !self.is_null() && *self.data & 1 != 0 }
	pub unsafe fn is_large(&self) -> bool { *self.data & (PF_PRESENT | PF_LARGE) == PF_LARGE|PF_PRESENT }

	pub unsafe fn addr(&self) -> PAddr { *self.data & 0x7FFFFFFF_FFFFF000 }
	//pub unsafe fn set_addr(&self, paddr: PAddr) {
	//	assert!(!self.is_null());
	//	*self.data = (*self.data & !0x7FFFFFFF_FFFFF000) | paddr;
	//}

	pub unsafe fn set(&self, paddr: PAddr, prot: ::memory::virt::ProtectionMode) {
		assert!(!self.is_null());
		let flags: u64 = match prot
			{
			::memory::virt::ProtectionMode::Unmapped => 0,
			::memory::virt::ProtectionMode::KernelRO => (1<<63)|1,
			::memory::virt::ProtectionMode::KernelRW => (1<<63)|2|1,
			::memory::virt::ProtectionMode::KernelRX => 1,
			::memory::virt::ProtectionMode::UserRO => (1<<63)|4|1,
			::memory::virt::ProtectionMode::UserRW => (1<<63)|4|2|1,
			::memory::virt::ProtectionMode::UserRX => 4|1,
			};
		*self.data = (paddr & 0x7FFFFFFF_FFFFF000) | flags;
	}
}

impl ::core::fmt::Debug for PTE
{
	fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
		unsafe { write!(f, "PTE({:?}, *{:?}={:#x})", self.pos, self.data, *self.data) }
	}
}

// vim: ft=rust

<|endoftext|>


<commit_before>//! A splittable pseudorandom generator based on the [Chaskey
//! MAC](http://mouha.be/chaskey/).  **This is not intended to be a
//! cryptographically secure PRNG.**
//!
//! Like `SipRng`, this is broadly modeled after Claessen and Pałka's
//! splittable PRNGs, but with a different choice of cryptographic
//! primitive.
//!
//! ## References
//!
//! * Mouha, Nicky, Bart Mennik, Anthony Van Herrewege, Dai Watanabe,
//!   Bart Preneet and Ingrid Verbauwhede.  2014.  ["Chaskey: An
//!   Efficient MAC Algorithm for 32-bit
//!   Microcontrollers."](https://eprint.iacr.org/2014/386.pdf)
//!   Cryptology ePrint Archive, Report 2014/386.

//! * Claessen, Koen and Michał H. Pałka.  2013.  ["Splittable
//!   Pseudorandom Number Generators using Cryptographic
//!   Hashing."](http://publications.lib.chalmers.se/records/fulltext/183348/local_183348.pdf)
//!   *Haskell '13: Proceedings of the 2013 ACM SIGPLAN symposium on
//!   Haskell*, pp. 47-58.

use rand::{Rand, Rng, SeedableRng};
use super::{SplitRng, SplitPrf};
use std::mem;
use std::u32;

/// A splittable pseudorandom generator based on Chaskey.
pub struct ChaskeyRng {
      v: [u32; 4],
     k1: [u32; 4],
    ctr: u64
}

/// A PRF taken off a `ChaskeyRng`.
pub struct ChaskeyPrf(ChaskeyRng);

macro_rules! round {
    ($v0: expr, $v1: expr, $v2: expr, $v3: expr) => {
        $v0 = $v0.wrapping_add($v1); $v2 = $v2.wrapping_add($v3);
        $v1 = $v1.rotate_left(5);    $v3 = $v3.rotate_left(8);
        $v1 ^= $v0;                  $v3 ^= $v2;
        $v0 = $v0.rotate_left(16);

        $v2 = $v2.wrapping_add($v1); $v0 = $v0.wrapping_add($v3);
        $v1 = $v1.rotate_left(7);    $v3 = $v3.rotate_left(13);
        $v1 ^= $v2;                  $v3 ^= $v0;
        $v2 = $v2.rotate_left(16);
    }
}

macro_rules! permute {
    ($v0: expr, $v1: expr, $v2: expr, $v3: expr) => {
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
    }
}

macro_rules! times_two {
    ($k: expr) => {
        [$k[0].wrapping_shl(1)
         ^ if $k[3] < 0x8000_0000 { 0 } else { 0x87 },
         $k[1].wrapping_shl(1) ^ 0,
         $k[2].wrapping_shl(1) ^ 0,
         $k[3].wrapping_shl(1) ^ 0]
    }
}

#[inline]
fn lsb32(n: u64) -> u32 {
    const MASK: u64 = 0xffff_ffff;
    (n & MASK) as u32
}

#[inline]
fn msb32(n: u64) -> u32 {
    lsb32(n.wrapping_shr(32))
}

impl ChaskeyRng {
    pub fn new(k: [u32; 4]) -> ChaskeyRng {
        ChaskeyRng {
              v: k,
             k1: times_two!(k),
            ctr: 0
        }
    }

    fn clone(&self) -> ChaskeyRng {
        ChaskeyRng {
              v: self.v,
             k1: self.k1,
            ctr: self.ctr
        }
    }

    #[inline]
    fn advance(&mut self) -> [u32; 4] {
        let result = {
            let (mut v0, mut v1, mut v2, mut v3) =
                (self.v[0] ^ self.k1[0],
                 self.v[1] ^ self.k1[1],
                 self.v[2] ^ self.k1[2] ^ lsb32(self.ctr),
                 self.v[3] ^ self.k1[3] ^ msb32(self.ctr));
            permute!(v0, v1, v2, v3);
            [v0 ^ self.k1[0],
             v1 ^ self.k1[1],
             v2 ^ self.k1[2],
             v3 ^ self.k1[3]]
        };
        self.ctr = self.ctr.wrapping_add(1);

        result
    }

    #[inline]
    fn descend(&mut self, i: u32) {
        self.v = [self.v[0] ^ u32::MAX,
                  self.v[1] ^ i,
                  self.v[2] ^ lsb32(self.ctr),
                  self.v[3] ^ msb32(self.ctr)];
        permute!(self.v[0], self.v[1], self.v[2], self.v[3]);
        self.ctr = 0;
    }

}

impl SplitPrf<ChaskeyRng> for ChaskeyPrf {
    fn call(&self, i: u32) -> ChaskeyRng {
        let mut r = self.0.clone();
        r.descend(i);
        r
    }
}

impl SplitRng for ChaskeyRng {
    type Prf = ChaskeyPrf;

    fn split(&mut self) -> Self {
        let mut child = self.clone();
        self.descend(0);
        child.descend(1);
        child
    }

    fn splitn(&mut self) -> ChaskeyPrf {
        let child = self.split();
        ChaskeyPrf(child)
    }

}

impl Rng for ChaskeyRng {
    #[inline]
    fn next_u64(&mut self) -> u64 {
        let block = self.advance();
        (block[0] as u64).wrapping_shl(32) | (block[1] as u64)
    }

    #[inline]
    fn next_u32(&mut self) -> u32 {
        self.advance()[0]
    }

    #[inline]
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        for chunk in dest.chunks_mut(16) {
            let block = unsafe {
                mem::transmute::<[u32; 4], [u8; 16]>(self.advance())
            };
            for i in 0..chunk.len() {
                chunk[i] = block[i];
            }
        }
    }
}

impl SeedableRng<[u32; 4]> for ChaskeyRng {

    fn reseed(&mut self, seed: [u32; 4]) {
        self.v = seed;
        self.k1 = times_two!(seed);
        self.ctr = 0;
    }

    fn from_seed(seed: [u32; 4]) -> ChaskeyRng {
        ChaskeyRng::new(seed)
    }
}

impl Rand for ChaskeyRng {
    fn rand<R: Rng>(other: &mut R) -> ChaskeyRng {
        ChaskeyRng::new(other.gen::<[u32; 4]>())
    }
}

#[cfg(test)]
mod tests {
    use rand::Rng;
    use rand::os::OsRng;
    use chaskeyrng::ChaskeyRng;

    fn gen_siprng() -> ChaskeyRng {
        let mut osrng = OsRng::new().ok().expect("Could not create OsRng");
        osrng.gen()
    }

    #[test]
    fn test_split_rand_independence() {
        ::tests::test_split_rand_independence(&mut gen_siprng());
    }

    #[test]
    fn test_split_rand_closure() {
        ::tests::test_split_rand_closure(&mut gen_siprng());
    }

    #[test]
    fn test_split_rand_split() {
        ::tests::test_split_rand_split(&mut gen_siprng());
    }

    fn gen_seed() -> [u32; 4] {
        let mut osrng = OsRng::new().ok().expect("Could not create OsRng");
        osrng.gen()
    }

    #[test]
    fn test_rng_rand_seeded() {
        let seed = gen_seed();
        ::tests::test_rng_rand_seeded::<ChaskeyRng, [u32; 4]>(seed);
    }

    #[test]
    fn test_rng_seeded() {
        let seed = gen_seed();
        ::tests::test_rng_seeded::<ChaskeyRng, [u32; 4]>(seed);
    }

    #[test]
    fn test_rng_reseed() {
        let seed = gen_seed();
        ::tests::test_rng_reseed::<ChaskeyRng, [u32; 4]>(seed);
    }
}
<commit_msg>Speed up `ChaskeyRng` by caching output blocks.<commit_after>//! A splittable pseudorandom generator based on the [Chaskey
//! MAC](http://mouha.be/chaskey/).  **This is not intended to be a
//! cryptographically secure PRNG.**
//!
//! Like `SipRng`, this is broadly modeled after Claessen and Pałka's
//! splittable PRNGs, but with a different choice of cryptographic
//! primitive.
//!
//! ## References
//!
//! * Mouha, Nicky, Bart Mennik, Anthony Van Herrewege, Dai Watanabe,
//!   Bart Preneet and Ingrid Verbauwhede.  2014.  ["Chaskey: An
//!   Efficient MAC Algorithm for 32-bit
//!   Microcontrollers."](https://eprint.iacr.org/2014/386.pdf)
//!   Cryptology ePrint Archive, Report 2014/386.

//! * Claessen, Koen and Michał H. Pałka.  2013.  ["Splittable
//!   Pseudorandom Number Generators using Cryptographic
//!   Hashing."](http://publications.lib.chalmers.se/records/fulltext/183348/local_183348.pdf)
//!   *Haskell '13: Proceedings of the 2013 ACM SIGPLAN symposium on
//!   Haskell*, pp. 47-58.

use rand::{Rand, Rng, SeedableRng};
use super::{SplitRng, SplitPrf};
use std::u32;

/// A splittable pseudorandom generator based on Chaskey.
pub struct ChaskeyRng {
    // The state of the splittable RNG, properly speaking.
      v: [u32; 4],
     k1: [u32; 4],
    ctr: u64,

    // We buffer the 128-bit raw outputs of the RNG to speed it up a bit.
    buf: [u32; 4],
    i: usize
}

/// A PRF taken off a `ChaskeyRng`.
pub struct ChaskeyPrf(ChaskeyRng);

macro_rules! round {
    ($v0: expr, $v1: expr, $v2: expr, $v3: expr) => {
        $v0 = $v0.wrapping_add($v1); $v2 = $v2.wrapping_add($v3);
        $v1 = $v1.rotate_left(5);    $v3 = $v3.rotate_left(8);
        $v1 ^= $v0;                  $v3 ^= $v2;
        $v0 = $v0.rotate_left(16);

        $v2 = $v2.wrapping_add($v1); $v0 = $v0.wrapping_add($v3);
        $v1 = $v1.rotate_left(7);    $v3 = $v3.rotate_left(13);
        $v1 ^= $v2;                  $v3 ^= $v0;
        $v2 = $v2.rotate_left(16);
    }
}

macro_rules! permute {
    ($v0: expr, $v1: expr, $v2: expr, $v3: expr) => {
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
        round!($v0, $v1, $v2, $v3);
    }
}

macro_rules! times_two {
    ($k: expr) => {
        [$k[0].wrapping_shl(1)
         ^ if $k[3] < 0x8000_0000 { 0 } else { 0x87 },
         $k[1].wrapping_shl(1) ^ 0,
         $k[2].wrapping_shl(1) ^ 0,
         $k[3].wrapping_shl(1) ^ 0]
    }
}

#[inline]
fn lsb32(n: u64) -> u32 {
    const MASK: u64 = 0xffff_ffff;
    (n & MASK) as u32
}

#[inline]
fn msb32(n: u64) -> u32 {
    lsb32(n.wrapping_shr(32))
}

impl ChaskeyRng {
    pub fn new(seed: [u32; 4]) -> ChaskeyRng {
        let mut result = ChaskeyRng {
              v: [0u32; 4],
             k1: [0u32; 4],
            ctr: 0,
            buf: [0u32; 4],
              i: 0
        };
        result.reseed(seed);
        result
    }

    fn reseed(&mut self, seed: [u32; 4]) {
        self.v = seed;
        self.k1 = times_two!(seed);
        self.ctr = 0;
        self.advance();
    }

    fn clone(&self) -> ChaskeyRng {
        ChaskeyRng {
              v: self.v,
             k1: self.k1,
            ctr: self.ctr,
            buf: self.buf,
              i: self.i
        }
    }

    #[inline]
    fn advance(&mut self) {
        self.buf[0] = self.v[0] ^ self.k1[0];
        self.buf[1] = self.v[0] ^ self.k1[1];
        self.buf[2] = self.v[0] ^ self.k1[2] ^ lsb32(self.ctr);
        self.buf[3] = self.v[0] ^ self.k1[3] ^ msb32(self.ctr);
        permute!(self.buf[0], self.buf[1], self.buf[2], self.buf[3]);
        self.buf[0] ^= self.k1[0];
        self.buf[1] ^= self.k1[1];
        self.buf[2] ^= self.k1[2];
        self.buf[3] ^= self.k1[3];
        self.ctr = self.ctr.wrapping_add(1);
        self.i = 0;
    }

    #[inline]
    fn descend(&mut self, i: u32) {
        self.v[0] ^= u32::MAX;
        self.v[1] ^= i;
        self.v[2] ^= lsb32(self.ctr);
        self.v[3] ^= msb32(self.ctr);
        permute!(self.v[0], self.v[1], self.v[2], self.v[3]);
        self.ctr = 0;
    }

}

impl SplitPrf<ChaskeyRng> for ChaskeyPrf {
    fn call(&self, i: u32) -> ChaskeyRng {
        let mut r = self.0.clone();
        r.descend(i);
        r
    }
}

impl SplitRng for ChaskeyRng {
    type Prf = ChaskeyPrf;

    fn split(&mut self) -> Self {
        let mut child = self.clone();
        self.descend(0);
        child.descend(1);
        child
    }

    fn splitn(&mut self) -> ChaskeyPrf {
        ChaskeyPrf(self.split())
    }

}

impl Rng for ChaskeyRng {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        if self.i >= 4 {
            self.advance();
        }
        let result = self.buf[self.i];
        self.i += 1;
        result
    }
}

impl SeedableRng<[u32; 4]> for ChaskeyRng {

    fn reseed(&mut self, seed: [u32; 4]) {
        ChaskeyRng::reseed(self, seed);
    }

    fn from_seed(seed: [u32; 4]) -> ChaskeyRng {
        ChaskeyRng::new(seed)
    }
}

impl Rand for ChaskeyRng {
    fn rand<R: Rng>(other: &mut R) -> ChaskeyRng {
        ChaskeyRng::new(other.gen::<[u32; 4]>())
    }
}

#[cfg(test)]
mod tests {
    use rand::Rng;
    use rand::os::OsRng;
    use chaskeyrng::ChaskeyRng;

    fn gen_siprng() -> ChaskeyRng {
        let mut osrng = OsRng::new().ok().expect("Could not create OsRng");
        osrng.gen()
    }

    #[test]
    fn test_split_rand_independence() {
        ::tests::test_split_rand_independence(&mut gen_siprng());
    }

    #[test]
    fn test_split_rand_closure() {
        ::tests::test_split_rand_closure(&mut gen_siprng());
    }

    #[test]
    fn test_split_rand_split() {
        ::tests::test_split_rand_split(&mut gen_siprng());
    }

    fn gen_seed() -> [u32; 4] {
        let mut osrng = OsRng::new().ok().expect("Could not create OsRng");
        osrng.gen()
    }

    #[test]
    fn test_rng_rand_seeded() {
        let seed = gen_seed();
        ::tests::test_rng_rand_seeded::<ChaskeyRng, [u32; 4]>(seed);
    }

    #[test]
    fn test_rng_seeded() {
        let seed = gen_seed();
        ::tests::test_rng_seeded::<ChaskeyRng, [u32; 4]>(seed);
    }

    #[test]
    fn test_rng_reseed() {
        let seed = gen_seed();
        ::tests::test_rng_reseed::<ChaskeyRng, [u32; 4]>(seed);
    }
}
<|endoftext|>