<commit_before>//! Topic name

use std::io::{self, Read, Write};
use std::ops::Deref;

use lazy_static::lazy_static;
use regex::Regex;

use crate::{Decodable, Encodable};

const TOPIC_NAME_VALIDATE_REGEX: &str = r"^[^#+]+$";

lazy_static! {
    static ref TOPIC_NAME_VALIDATOR: Regex = Regex::new(TOPIC_NAME_VALIDATE_REGEX).unwrap();
}

#[inline]
fn is_invalid_topic_name(topic_name: &str) -> bool {
    topic_name.is_empty() || topic_name.as_bytes().len() > 65535 || !TOPIC_NAME_VALIDATOR.is_match(&topic_name)
}

/// Topic name
///
/// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106
#[derive(Debug, Eq, PartialEq, Clone, Hash, Ord, PartialOrd)]
pub struct TopicName(String);

impl TopicName {
    /// Creates a new topic name from string
    /// Return error if the string is not a valid topic name
    pub fn new<S: Into<String>>(topic_name: S) -> Result<TopicName, TopicNameError> {
        let topic_name = topic_name.into();
        if is_invalid_topic_name(&topic_name) {
            Err(TopicNameError(topic_name))
        } else {
            Ok(TopicName(topic_name))
        }
    }

    /// Creates a new topic name from string without validation
    ///
    /// # Safety
    ///
    /// Topic names' syntax is defined in [MQTT specification](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106).
    /// Creating a name from raw string may cause errors
    pub unsafe fn new_unchecked(topic_name: String) -> TopicName {
        TopicName(topic_name)
    }
}

impl From<TopicName> for String {
    fn from(topic_name: TopicName) -> String {
        topic_name.0
    }
}

impl Deref for TopicName {
    type Target = TopicNameRef;

    fn deref(&self) -> &TopicNameRef {
        unsafe { TopicNameRef::new_unchecked(&self.0) }
    }
}

impl Encodable for TopicName {
    fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
        (&self.0[..]).encode(writer)
    }

    fn encoded_length(&self) -> u32 {
        (&self.0[..]).encoded_length()
    }
}

impl Decodable for TopicName {
    type Error = TopicNameDecodeError;
    type Cond = ();

    fn decode_with<R: Read>(reader: &mut R, _rest: ()) -> Result<TopicName, TopicNameDecodeError> {
        let topic_name = String::decode(reader)?;
        Ok(TopicName::new(topic_name)?)
    }
}

#[derive(Debug, thiserror::Error)]
#[error("invalid topic filter ({0})")]
pub struct TopicNameError(pub String);

/// Errors while parsing topic names
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub enum TopicNameDecodeError {
    IoError(#[from] io::Error),
    InvalidTopicName(#[from] TopicNameError),
}

/// Reference to a topic name
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct TopicNameRef(str);

impl TopicNameRef {
    /// Creates a new topic name from string
    /// Return error if the string is not a valid topic name
    pub fn new<S: AsRef<str> + ?Sized>(topic_name: &S) -> Result<&TopicNameRef, TopicNameError> {
        let topic_name = topic_name.as_ref();
        if is_invalid_topic_name(&topic_name) {
            Err(TopicNameError(topic_name.to_owned()))
        } else {
            Ok(unsafe { &*(topic_name as *const str as *const TopicNameRef) })
        }
    }

    /// Creates a new topic name from string without validation
    ///
    /// # Safety
    ///
    /// Topic names' syntax is defined in [MQTT specification](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106).
    /// Creating a name from raw string may cause errors
    pub unsafe fn new_unchecked<S: AsRef<str> + ?Sized>(topic_name: &S) -> &TopicNameRef {
        let topic_name = topic_name.as_ref();
        &*(topic_name as *const str as *const TopicNameRef)
    }

    /// Check if this topic name is only for server.
    ///
    /// Topic names that beginning with a '$' character are reserved for servers
    pub fn is_server_specific(&self) -> bool {
        self.0.starts_with('$')
    }
}

impl Deref for TopicNameRef {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

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

    #[test]
    fn topic_name_sys() {
        let topic_name = "$SYS".to_owned();
        TopicName::new(topic_name).unwrap();

        let topic_name = "$SYS/broker/connection/test.cosm-energy/state".to_owned();
        TopicName::new(topic_name).unwrap();
    }

    #[test]
    fn topic_name_slash() {
        TopicName::new("/").unwrap();
    }

    #[test]
    fn topic_name_basic() {
        TopicName::new("/finance").unwrap();
        TopicName::new("/finance//def").unwrap();
    }
}
<commit_msg>TopicName conv operations to TopicNameRef<commit_after>//! Topic name

use std::{
    borrow::{Borrow, BorrowMut},
    io::{self, Read, Write},
    ops::{Deref, DerefMut},
};

use lazy_static::lazy_static;
use regex::Regex;

use crate::{Decodable, Encodable};

const TOPIC_NAME_VALIDATE_REGEX: &str = r"^[^#+]+$";

lazy_static! {
    static ref TOPIC_NAME_VALIDATOR: Regex = Regex::new(TOPIC_NAME_VALIDATE_REGEX).unwrap();
}

#[inline]
fn is_invalid_topic_name(topic_name: &str) -> bool {
    topic_name.is_empty() || topic_name.as_bytes().len() > 65535 || !TOPIC_NAME_VALIDATOR.is_match(&topic_name)
}

/// Topic name
///
/// http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106
#[derive(Debug, Eq, PartialEq, Clone, Hash, Ord, PartialOrd)]
pub struct TopicName(String);

impl TopicName {
    /// Creates a new topic name from string
    /// Return error if the string is not a valid topic name
    pub fn new<S: Into<String>>(topic_name: S) -> Result<TopicName, TopicNameError> {
        let topic_name = topic_name.into();
        if is_invalid_topic_name(&topic_name) {
            Err(TopicNameError(topic_name))
        } else {
            Ok(TopicName(topic_name))
        }
    }

    /// Creates a new topic name from string without validation
    ///
    /// # Safety
    ///
    /// Topic names' syntax is defined in [MQTT specification](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106).
    /// Creating a name from raw string may cause errors
    pub unsafe fn new_unchecked(topic_name: String) -> TopicName {
        TopicName(topic_name)
    }
}

impl From<TopicName> for String {
    fn from(topic_name: TopicName) -> String {
        topic_name.0
    }
}

impl Deref for TopicName {
    type Target = TopicNameRef;

    fn deref(&self) -> &TopicNameRef {
        unsafe { TopicNameRef::new_unchecked(&self.0) }
    }
}

impl DerefMut for TopicName {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { TopicNameRef::new_mut_unchecked(&mut self.0) }
    }
}

impl Borrow<TopicNameRef> for TopicName {
    fn borrow(&self) -> &TopicNameRef {
        Deref::deref(self)
    }
}

impl BorrowMut<TopicNameRef> for TopicName {
    fn borrow_mut(&mut self) -> &mut TopicNameRef {
        DerefMut::deref_mut(self)
    }
}

impl Encodable for TopicName {
    fn encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
        (&self.0[..]).encode(writer)
    }

    fn encoded_length(&self) -> u32 {
        (&self.0[..]).encoded_length()
    }
}

impl Decodable for TopicName {
    type Error = TopicNameDecodeError;
    type Cond = ();

    fn decode_with<R: Read>(reader: &mut R, _rest: ()) -> Result<TopicName, TopicNameDecodeError> {
        let topic_name = String::decode(reader)?;
        Ok(TopicName::new(topic_name)?)
    }
}

#[derive(Debug, thiserror::Error)]
#[error("invalid topic filter ({0})")]
pub struct TopicNameError(pub String);

/// Errors while parsing topic names
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub enum TopicNameDecodeError {
    IoError(#[from] io::Error),
    InvalidTopicName(#[from] TopicNameError),
}

/// Reference to a topic name
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(transparent)]
pub struct TopicNameRef(str);

impl TopicNameRef {
    /// Creates a new topic name from string
    /// Return error if the string is not a valid topic name
    pub fn new<S: AsRef<str> + ?Sized>(topic_name: &S) -> Result<&TopicNameRef, TopicNameError> {
        let topic_name = topic_name.as_ref();
        if is_invalid_topic_name(topic_name) {
            Err(TopicNameError(topic_name.to_owned()))
        } else {
            Ok(unsafe { &*(topic_name as *const str as *const TopicNameRef) })
        }
    }

    /// Creates a new topic name from string
    /// Return error if the string is not a valid topic name
    pub fn new_mut<S: AsMut<str> + ?Sized>(topic_name: &mut S) -> Result<&mut TopicNameRef, TopicNameError> {
        let topic_name = topic_name.as_mut();
        if is_invalid_topic_name(topic_name) {
            Err(TopicNameError(topic_name.to_owned()))
        } else {
            Ok(unsafe { &mut *(topic_name as *mut str as *mut TopicNameRef) })
        }
    }

    /// Creates a new topic name from string without validation
    ///
    /// # Safety
    ///
    /// Topic names' syntax is defined in [MQTT specification](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106).
    /// Creating a name from raw string may cause errors
    pub unsafe fn new_unchecked<S: AsRef<str> + ?Sized>(topic_name: &S) -> &TopicNameRef {
        let topic_name = topic_name.as_ref();
        &*(topic_name as *const str as *const TopicNameRef)
    }

    /// Creates a new topic name from string without validation
    ///
    /// # Safety
    ///
    /// Topic names' syntax is defined in [MQTT specification](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718106).
    /// Creating a name from raw string may cause errors
    pub unsafe fn new_mut_unchecked<S: AsMut<str> + ?Sized>(topic_name: &mut S) -> &mut TopicNameRef {
        let topic_name = topic_name.as_mut();
        &mut *(topic_name as *mut str as *mut TopicNameRef)
    }

    /// Check if this topic name is only for server.
    ///
    /// Topic names that beginning with a '$' character are reserved for servers
    pub fn is_server_specific(&self) -> bool {
        self.0.starts_with('$')
    }
}

impl Deref for TopicNameRef {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

impl ToOwned for TopicNameRef {
    type Owned = TopicName;

    fn to_owned(&self) -> Self::Owned {
        TopicName(self.0.to_owned())
    }
}

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

    #[test]
    fn topic_name_sys() {
        let topic_name = "$SYS".to_owned();
        TopicName::new(topic_name).unwrap();

        let topic_name = "$SYS/broker/connection/test.cosm-energy/state".to_owned();
        TopicName::new(topic_name).unwrap();
    }

    #[test]
    fn topic_name_slash() {
        TopicName::new("/").unwrap();
    }

    #[test]
    fn topic_name_basic() {
        TopicName::new("/finance").unwrap();
        TopicName::new("/finance//def").unwrap();
    }
}
<|endoftext|>


<commit_before>#![allow(unused_imports)]
use rustc_serialize::json;

#[test]
fn reply_keyboard_markup() {
    use ReplyKeyboardMarkup as RKM;

    // Test default values
    assert_eq!(RKM::default(), RKM {
        keyboard: Vec::new(),
        resize_keyboard: None,
        one_time_keyboard: None,
        selective: None,
    });

    // Test encoding
    let x = RKM::default();
    assert_eq!(json::encode(&x).unwrap(), "{\"keyboard\":[]}".to_string());

    let x = RKM { resize_keyboard: Some(true), ..Default::default() };
    assert_eq!(json::encode(&x).unwrap(),
        "{\"keyboard\":[],\"resize_keyboard\":true}".to_string());

    let x = RKM {
        keyboard: vec![vec!["ABC".into()], vec!["X".into(), "Y".into()]],
        resize_keyboard: Some(false),
        ..Default::default()
    };
    assert_eq!(json::encode(&x).unwrap(),
        "{\"keyboard\":[[\"ABC\"],[\"X\",\"Y\"]],\"resize_keyboard\":false}".to_string());
}

#[test]
fn keyboard_markup() {
    use ReplyKeyboardMarkup as RKM;
    use ReplyMarkup as RM;

    // Test encoding
    let x = RM::Keyboard(RKM::default());
    assert_eq!(json::encode(&x).unwrap(), "{\"keyboard\":[]}".to_string());

    let x = RM::KeyboardHide(false);
    assert_eq!(json::encode(&x).unwrap(),
        "{\"hide_keyboard\":true,\"selective\":false}".to_string());

    let x = RM::ForceReply(true);
    assert_eq!(json::encode(&x).unwrap(),
        "{\"force_reply\":true,\"selective\":true}".to_string());
}

#[test]
fn decode_group_chat() {
    use Chat;
    use GroupChat;

    let blob = r#"{"title":"This is a group chat","id":-12345678}"#;
    let groupchat: GroupChat = json::decode(&blob).unwrap();
    let chat: Chat = json::decode(&blob).unwrap();

    assert!(chat.is_group());
    assert_eq!(Chat::Group(groupchat), chat);
}

#[test]
fn decode_user_chat() {
    use Chat;
    use User;

    let blob = r#"{"first_name":"test","id":123456789,"username":"test"}"#;
    let chat: Chat = json::decode(&blob).unwrap();
    let user: User = json::decode(&blob).unwrap();

    assert!(chat.is_user());
    assert_eq!(Chat::User(user), chat);
}
<commit_msg>types/test: test `Update` decoding<commit_after>#![allow(unused_imports)]
use rustc_serialize::json;

#[test]
fn reply_keyboard_markup() {
    use ReplyKeyboardMarkup as RKM;

    // Test default values
    assert_eq!(RKM::default(), RKM {
        keyboard: Vec::new(),
        resize_keyboard: None,
        one_time_keyboard: None,
        selective: None,
    });

    // Test encoding
    let x = RKM::default();
    assert_eq!(json::encode(&x).unwrap(), "{\"keyboard\":[]}".to_string());

    let x = RKM { resize_keyboard: Some(true), ..Default::default() };
    assert_eq!(json::encode(&x).unwrap(),
        "{\"keyboard\":[],\"resize_keyboard\":true}".to_string());

    let x = RKM {
        keyboard: vec![vec!["ABC".into()], vec!["X".into(), "Y".into()]],
        resize_keyboard: Some(false),
        ..Default::default()
    };
    assert_eq!(json::encode(&x).unwrap(),
        "{\"keyboard\":[[\"ABC\"],[\"X\",\"Y\"]],\"resize_keyboard\":false}".to_string());
}

#[test]
fn keyboard_markup() {
    use ReplyKeyboardMarkup as RKM;
    use ReplyMarkup as RM;

    // Test encoding
    let x = RM::Keyboard(RKM::default());
    assert_eq!(json::encode(&x).unwrap(), "{\"keyboard\":[]}".to_string());

    let x = RM::KeyboardHide(false);
    assert_eq!(json::encode(&x).unwrap(),
        "{\"hide_keyboard\":true,\"selective\":false}".to_string());

    let x = RM::ForceReply(true);
    assert_eq!(json::encode(&x).unwrap(),
        "{\"force_reply\":true,\"selective\":true}".to_string());
}

#[test]
fn decode_group_chat() {
    use Chat;
    use GroupChat;

    let blob = r#"{"title":"This is a group chat","id":-12345678}"#;
    let groupchat: GroupChat = json::decode(&blob).unwrap();
    let chat: Chat = json::decode(&blob).unwrap();

    assert!(chat.is_group());
    assert_eq!(Chat::Group(groupchat), chat);
}

#[test]
fn decode_user_chat() {
    use Chat;
    use User;

    let blob = r#"{"first_name":"test","id":123456789,"username":"test"}"#;
    let chat: Chat = json::decode(&blob).unwrap();
    let user: User = json::decode(&blob).unwrap();

    assert!(chat.is_user());
    assert_eq!(Chat::User(user), chat);
}

#[test]
fn decode_update() {
    use Update;
    let blob = r#"{
        "message" : {
            "text" : "The quick brown fox jumps over the lazy dog",
            "from" : {
                "first_name" : "test",
                "id" : 123456789,
                "username" : "test"
            },
            "date" : 1437821492,
            "message_id" : 74,
            "chat" : {
                "title" : "This is a group chat",
                "id" : -12345678
            }
        },
        "update_id" : 123456789
    }"#;
    let _: Update = json::decode(&blob).unwrap();
}

#[test]
fn decode_get_updates_response() {
    use Response;
    use Update;

    let blob = r#"{
        "result" : [
            {
                "message" : {
                    "text" : "This is the first message",
                    "from" : {
                        "username" : "test",
                        "id" : 123456789,
                        "first_name" : "Test"
                    },
                    "date" : 1437821579,
                    "message_id" : 78,
                    "chat" : {
                        "username" : "test",
                        "id" : 123456789,
                        "first_name" : "Test"
                    }
                },
                "update_id" : 123456789
            },
            {
                "message" : {
                    "text" : "This is the second message",
                    "from" : {
                        "username" : "test",
                        "id" : 123456789,
                        "first_name" : "Test"
                    },
                    "date" : 1437821579,
                    "message_id" : 79,
                    "chat" : {
                        "username" : "test",
                        "id" : 123456789,
                        "first_name" : "Test"
                    }
                },
                "update_id" : 123456790
            }
        ],
        "ok" : true
    }"#;

    let _: Response<Vec<Update>> = json::decode(&blob).unwrap();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 JJ Garzella and Calvin Lee. See the README.md
 * file at the top-level directory of this distribution.
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>, at your option.
 * This file may not be copied, modified, or distributed
 * except according to those terms.
 */
use core::ptr::Unique;
use spin::Mutex;

const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;

pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
	column_position: 0,
	color_code: ColorCode::new(Color::Pink, Color::Black),
	buffer: unsafe { Unique::new( 0xb8000 as *mut _ ) },
});

macro_rules! println {
	($fmt:expr) => (print!(concat!($fmt, "\n")));
	($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

macro_rules! print {
	($(arg:tt)*) => ({
		use core::fmt::Write;
		let mut writer = $crate::vga_buffer::Writer.lock();
		writer.write_fmt(format_args!($($arg)*).unwrap());
	});
}

pub fn clear_screen() {
	for _ in 0..BUFFER_HEIGHT {
		println!("");
	}
}

#[allow(dead_code)]
#[repr(u8)]
pub enum Color {
	Black      = 0x0,
	Blue       = 0x1,
	Green      = 0x2,
	Cyan       = 0x3,
	Red        = 0x4,
	Magenta    = 0x5,
	Brown      = 0x6,
	LightGray  = 0x7,
	DarkGray   = 0x8,
	LightBlue  = 0x9,
	LightGreen = 0xa,
	LightCyan  = 0xb,
	LightRed   = 0xc,
	Pink       = 0xd,
	Yellow     = 0xe,
	White      = 0xf,
}

pub struct Writer {
	column_position: usize,
	color_code: ColorCode,
	buffer: Unique<Buffer>,
}

impl Writer {
	pub fn write_byte(&mut self, byte: u8) {
		match byte {
			b'\n' => self.new_line(),
			byte  => {
				if self.column_position >= BUFFER_WIDTH {
					self.new_line();
				}

				let row = BUFFER_HEIGHT - 1;
				let col = self.column_position;

				self.buffer().chars[row][col] = ScreenChar {
					ascii_character: byte,
					color_code: self.color_code,
				};
				self.column_position += 1;
			}
		}
	}

	pub fn write_str(&mut self, s: &str) {
		for byte in s.bytes() {
			self.write_byte(byte);
		}
	}

	fn buffer(&mut self) -> &mut Buffer {
		unsafe { self.buffer.get_mut() }
	}

	fn new_line(&mut self) {
		for row in 0..(BUFFER_HEIGHT-1) {
			let buffer = self.buffer();
			buffer.chars[row] = buffer.chars[row+1]
		}
		self.clear_row(BUFFER_HEIGHT-1);
		self.column_position = 0;
	}

	fn clear_row(&mut self, row: usize) {
		let blank = ScreenChar {
			ascii_character: b' ',
			color_code: self.color_code,
		};
		self.buffer().chars[row] = [blank; BUFFER_WIDTH];
	}
}

impl ::core::fmt::Write for Writer {
	fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
		for byte in s.bytes() {
			self.write_byte(byte);
		}
		Ok(())
	}
}

#[derive(Clone, Copy)]
struct ColorCode(u8);

impl ColorCode {
	const fn new(fg: Color, bg: Color) -> ColorCode {
		ColorCode((bg as u8) << 4 | (fg as u8))
	}
}

#[repr(C)]
#[derive(Clone, Copy)]
struct ScreenChar {
	ascii_character: u8,
	color_code: ColorCode,
}

struct Buffer {
	chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
<commit_msg>Fixed macro<commit_after>/*
 * Copyright 2016 JJ Garzella and Calvin Lee. See the README.md
 * file at the top-level directory of this distribution.
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>, at your option.
 * This file may not be copied, modified, or distributed
 * except according to those terms.
 */
use core::ptr::Unique;
use core::fmt;
use spin::Mutex;

const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;

pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
	column_position: 0,
	color_code: ColorCode::new(Color::Pink, Color::Black),
	buffer: unsafe { Unique::new( 0xb8000 as *mut _ ) },
});

macro_rules! println {
	($fmt:expr) => (print!(concat!($fmt, "\n")));
	($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

macro_rules! print {
	($($arg:tt)*) => ({
		use core::fmt::Write;
		$crate::vga_buffer::WRITER.lock()
			.write_fmt(format_args!($($arg)*)).unwrap();
	});
}

pub fn clear_screen() {
	for _ in 0..BUFFER_HEIGHT {
		println!("");
	}
}

#[allow(dead_code)]
#[repr(u8)]
pub enum Color {
	Black      = 0x0,
	Blue       = 0x1,
	Green      = 0x2,
	Cyan       = 0x3,
	Red        = 0x4,
	Magenta    = 0x5,
	Brown      = 0x6,
	LightGray  = 0x7,
	DarkGray   = 0x8,
	LightBlue  = 0x9,
	LightGreen = 0xa,
	LightCyan  = 0xb,
	LightRed   = 0xc,
	Pink       = 0xd,
	Yellow     = 0xe,
	White      = 0xf,
}

pub struct Writer {
	column_position: usize,
	color_code: ColorCode,
	buffer: Unique<Buffer>,
}

impl Writer {
	pub fn write_byte(&mut self, byte: u8) {
		match byte {
			b'\n' => self.new_line(),
			byte  => {
				if self.column_position >= BUFFER_WIDTH {
					self.new_line();
				}

				let row = BUFFER_HEIGHT - 1;
				let col = self.column_position;

				self.buffer().chars[row][col] = ScreenChar {
					ascii_character: byte,
					color_code: self.color_code,
				};
				self.column_position += 1;
			}
		}
	}

	pub fn write_str(&mut self, s: &str) {
		for byte in s.bytes() {
			self.write_byte(byte);
		}
	}

	fn buffer(&mut self) -> &mut Buffer {
		unsafe { self.buffer.get_mut() }
	}

	fn new_line(&mut self) {
		for row in 0..(BUFFER_HEIGHT-1) {
			let buffer = self.buffer();
			buffer.chars[row] = buffer.chars[row+1]
		}
		self.clear_row(BUFFER_HEIGHT-1);
		self.column_position = 0;
	}

	fn clear_row(&mut self, row: usize) {
		let blank = ScreenChar {
			ascii_character: b' ',
			color_code: self.color_code,
		};
		self.buffer().chars[row] = [blank; BUFFER_WIDTH];
	}
}

impl ::core::fmt::Write for Writer {
	fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
		for byte in s.bytes() {
			self.write_byte(byte);
		}
		Ok(())
	}
}

#[derive(Clone, Copy)]
struct ColorCode(u8);

impl ColorCode {
	const fn new(fg: Color, bg: Color) -> ColorCode {
		ColorCode((bg as u8) << 4 | (fg as u8))
	}
}

#[repr(C)]
#[derive(Clone, Copy)]
struct ScreenChar {
	ascii_character: u8,
	color_code: ColorCode,
}

struct Buffer {
	chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
<|endoftext|>


<commit_before>use cw::{BLOCK, CVec};
use std::cmp;
use std::collections::HashMap;

#[derive(Hash, Eq, PartialEq)]
struct WordConstraint {
    ngram: CVec,
    pos: usize,
}

pub struct WordStats {
    freq: Vec<HashMap<WordConstraint, usize>>,
    total: Vec<usize>,
    max_n: usize,
}

impl WordStats {
    pub fn new(max_n: usize) -> WordStats {
        WordStats {
            freq: Vec::new(),
            total: Vec::new(),
            max_n: max_n,
        }
    }

    pub fn add_words<'a, T: Iterator<Item = &'a CVec>>(&mut self, words: T) {
        for word in words {
            self.add_word(word);
        }
    }

    pub fn add_word(&mut self, word: &CVec) {
        while self.total.len() <= word.len() {
            self.total.push(0);
            self.freq.push(HashMap::new());
        }
        self.total[word.len()] += 1;
        for n in 1..(self.max_n + 1) {
            for (pos, ngram) in word.windows(n).enumerate() {
                let wc = WordConstraint { ngram: ngram.to_vec(), pos: pos };
                let prev_freq = *self.freq[word.len()].get(&wc).unwrap_or(&0);
                self.freq[word.len()].insert(wc, prev_freq + 1);
            }
        }
    }

    fn get_freq(&self, ngram: &[char], pos: usize, len: usize) -> usize {
        *self.freq[len].get(&WordConstraint {
            pos: pos,
            ngram: ngram.to_vec(),
        }).unwrap_or(&0)
    }

    fn get_estimate(&self, subword: &[char], pos: usize, len: usize) -> f32 {
        let n = cmp::min(self.max_n, subword.len());
        let mut estimate = self.get_freq(&subword[0..n], pos, len) as f32;
        if estimate == 0_f32 {
            return 0_f32;
        }
        for dp in 1..(subword.len() - n) {
            let next_est = self.get_freq(&subword[dp..(dp + n)], pos + dp, len) as f32;
            if next_est == 0_f32 {
                return 0_f32;
            }
            estimate *= next_est;
            if n > 1 {
                estimate /= self.get_freq(&subword[dp..(dp + n - 1)], pos + dp, len) as f32;
            }
        }
        estimate
    }

    pub fn estimate_matches(&self, pattern: &CVec) -> f32 {
        let len = pattern.len();
        let total = *self.total.get(len).unwrap_or(&0) as f32;
        if total == 0_f32 {
            return 0_f32;
        }
        let mut probability = 1_f32;
        let mut pos = 0;
        for i in pattern.iter().enumerate()
                .filter(|&(_, ch)| ch == &BLOCK)
                .map(|(i, _)| i)
                .chain(Some(len).into_iter()) {
            if i > pos {
                probability *= self.get_estimate(&pattern[pos..i], pos, len) / total;
                if probability == 0_f32 {
                    return 0_f32;
                }
            }
            pos = i + 1;
        }
        probability * total
    }
}

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

    #[test]
    fn test() {
        let mut words: HashSet<CVec> = HashSet::new();
        words.insert("ABCD".chars().collect());
        words.insert("AXYZ".chars().collect());
        let mut ws = WordStats::new(2);
        ws.add_words(words.iter());
        assert_eq!(1_f32, ws.estimate_matches(&"AB##".chars().collect()));
        assert_eq!(1_f32, ws.estimate_matches(&"#B##".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"#AB#".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"###A".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"##".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"#####".chars().collect()));
        assert_eq!(2_f32, ws.estimate_matches(&"A###".chars().collect()));
        assert_eq!(1_f32, ws.estimate_matches(&"#B##".chars().collect()));
        assert_eq!(1_f32, ws.estimate_matches(&"ABC#".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"#C##".chars().collect()));
    }
}
<commit_msg>Refactor WordConstraint<commit_after>use cw::{BLOCK, CVec};
use std::cmp;
use std::collections::HashMap;

#[derive(Hash, Eq, PartialEq)]
enum WordConstraint {
    Length(usize),
    CharAt(char, usize, usize),
    BigramAt([char; 2], usize, usize),
    TrigramAt([char; 3], usize, usize),
    NGramAt(CVec, usize, usize),
}

impl WordConstraint {
    fn with_ngram(ngram: &[char], pos: usize, len: usize) -> WordConstraint {
        match ngram.len() {
            0 => WordConstraint::Length(len),
            1 => WordConstraint::CharAt(ngram[0], pos, len),
            2 => WordConstraint::BigramAt([ngram[0], ngram[1]], pos, len),
            3 => WordConstraint::TrigramAt([ngram[0], ngram[1], ngram[2]], pos, len),
            _ => WordConstraint::NGramAt(ngram.to_vec(), pos, len),
        }
    }
}

pub struct WordStats {
    freq: HashMap<WordConstraint, usize>,
    max_n: usize,
}

impl WordStats {
    pub fn new(max_n: usize) -> WordStats {
        WordStats {
            freq: HashMap::new(),
            max_n: max_n,
        }
    }

    pub fn add_words<'a, T: Iterator<Item = &'a CVec>>(&mut self, words: T) {
        for word in words {
            self.add_word(word);
        }
    }

    fn get(&self, wc: &WordConstraint) -> usize { *self.freq.get(wc).unwrap_or(&0) }

    fn get_total(&self, len: usize) -> usize { self.get(&WordConstraint::Length(len)) }

    fn get_freq(&self, ngram: &[char], pos: usize, len: usize) -> usize {
        self.get(&WordConstraint::with_ngram(ngram, pos, len))
    }

    fn increase(&mut self, wc: WordConstraint) {
        let prev_freq = self.get(&wc);
        self.freq.insert(wc, prev_freq + 1);
    }

    pub fn add_word(&mut self, word: &CVec) {
        self.increase(WordConstraint::Length(word.len()));
        for n in 1..(self.max_n + 1) {
            for (pos, ngram) in word.windows(n).enumerate() {
                self.increase(WordConstraint::with_ngram(ngram, pos, word.len()));
            }
        }
    }

    fn get_estimate(&self, subword: &[char], pos: usize, len: usize) -> f32 {
        let n = cmp::min(self.max_n, subword.len());
        let mut estimate = self.get_freq(&subword[0..n], pos, len) as f32;
        if estimate == 0_f32 {
            return 0_f32;
        }
        for dp in 1..(subword.len() - n) {
            let next_est = self.get_freq(&subword[dp..(dp + n)], pos + dp, len) as f32;
            if next_est == 0_f32 {
                return 0_f32;
            }
            estimate *= next_est;
            if n > 1 {
                estimate /= self.get_freq(&subword[dp..(dp + n - 1)], pos + dp, len) as f32;
            }
        }
        estimate
    }

    pub fn estimate_matches(&self, pattern: &CVec) -> f32 {
        let len = pattern.len();
        let total = self.get_total(len) as f32;
        if total == 0_f32 {
            return 0_f32;
        }
        let mut probability = 1_f32;
        let mut pos = 0;
        for i in pattern.iter().enumerate()
                .filter(|&(_, ch)| ch == &BLOCK)
                .map(|(i, _)| i)
                .chain(Some(len).into_iter()) {
            if i > pos {
                probability *= self.get_estimate(&pattern[pos..i], pos, len) / total;
                if probability == 0_f32 {
                    return 0_f32;
                }
            }
            pos = i + 1;
        }
        probability * total
    }
}

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

    #[test]
    fn test() {
        let mut words: HashSet<CVec> = HashSet::new();
        words.insert("ABCD".chars().collect());
        words.insert("AXYZ".chars().collect());
        let mut ws = WordStats::new(2);
        ws.add_words(words.iter());
        assert_eq!(1_f32, ws.estimate_matches(&"AB##".chars().collect()));
        assert_eq!(1_f32, ws.estimate_matches(&"#B##".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"#AB#".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"###A".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"##".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"#####".chars().collect()));
        assert_eq!(2_f32, ws.estimate_matches(&"A###".chars().collect()));
        assert_eq!(1_f32, ws.estimate_matches(&"#B##".chars().collect()));
        assert_eq!(1_f32, ws.estimate_matches(&"ABC#".chars().collect()));
        assert_eq!(0_f32, ws.estimate_matches(&"#C##".chars().collect()));
    }
}
<|endoftext|>


<commit_before>pub use self::emitter::EmitterResult as EventWriterResult;
pub use self::config::EmitterConfig;

use self::emitter::Emitter;
use self::events::XmlEvent;

use std::io::prelude::*;

mod emitter;
pub mod config;
pub mod events;

pub struct EventWriter<W> {
    sink: W,
    emitter: Emitter
}

impl<W: Write> EventWriter<W> {
    #[inline]
    pub fn new(sink: W) -> EventWriter<W> {
        EventWriter::new_with_config(sink, EmitterConfig::new())
    }

    #[inline]
    pub fn new_with_config(sink: W, config: EmitterConfig) -> EventWriter<W> {
        EventWriter {
            sink: sink,
            emitter: Emitter::new(config)
        }
    }

    pub fn write(&mut self, event: XmlEvent) -> EventWriterResult<()> {
        match event {
            XmlEvent::StartDocument { version, encoding, standalone } =>
                self.emitter.emit_start_document(&mut self.sink, version, encoding.unwrap_or("UTF-8"), standalone),
            XmlEvent::ProcessingInstruction { name, data } =>
                self.emitter.emit_processing_instruction(&mut self.sink, name, data),
            XmlEvent::StartElement { name, attributes, namespace } =>
                self.emitter.emit_start_element(&mut self.sink, name, &attributes, namespace),
            XmlEvent::EndElement { name } =>
                self.emitter.emit_end_element(&mut self.sink, name),
            XmlEvent::Comment(content) =>
                self.emitter.emit_comment(&mut self.sink, content),
            XmlEvent::CData(content) =>
                self.emitter.emit_cdata(&mut self.sink, content),
            XmlEvent::Characters(content) =>
                self.emitter.emit_characters(&mut self.sink, content)
        }
    }
}

impl EventWriter<Vec<u8>> {
    #[inline]
    pub fn new_into_mem(sink: Vec<u8>) -> EventWriter<Vec<u8>> {
        EventWriter::new_into_mem_config(sink, EmitterConfig::new())
    }

    #[inline]
    pub fn new_into_mem_config(sink: Vec<u8>, config: EmitterConfig) -> EventWriter<Vec<u8>> {
        EventWriter::new_with_config(sink, config)
    }
}

#[cfg(test)]
mod tests {
    use std::io::{BufReader, SeekFrom};
    use std::io::prelude::*;
    use std::fs::File;
    use std::path::Path;

    use reader::EventReader;
    use writer::EventWriter;

    #[ignore]
    #[test]
    fn writer_test() {
        let mut f = File::open(Path::new("data/sample_1.xml")).unwrap();
        let mut b = Vec::new();

        {
            let mut r = EventReader::new(BufReader::new(&mut f));
            let mut w = EventWriter::new(&mut b);

            for e in r.events() {
                match e.as_writer_event() {
                    Some(e) => match w.write(e) {
                        Ok(_) => {},
                        Err(e) => panic!("Writer error: {:?}", e)
                    },
                    None => println!("Non-writer event: {:?}", e)
                }
            }
        }

        f.seek(SeekFrom::Start(0)).unwrap();
        let mut fs = String::new();
        f.read_to_string(&mut fs).unwrap();

        let bs = String::from_utf8(b).unwrap();

        assert_eq!(fs, bs)
    }
}
<commit_msg>Make EmitterError nameable<commit_after>pub use self::emitter::EmitterResult as EventWriterResult;
pub use self::emitter::EmitterError as EventWriterError;
pub use self::config::EmitterConfig;

use self::emitter::Emitter;
use self::events::XmlEvent;

use std::io::prelude::*;

mod emitter;
pub mod config;
pub mod events;

pub struct EventWriter<W> {
    sink: W,
    emitter: Emitter
}

impl<W: Write> EventWriter<W> {
    #[inline]
    pub fn new(sink: W) -> EventWriter<W> {
        EventWriter::new_with_config(sink, EmitterConfig::new())
    }

    #[inline]
    pub fn new_with_config(sink: W, config: EmitterConfig) -> EventWriter<W> {
        EventWriter {
            sink: sink,
            emitter: Emitter::new(config)
        }
    }

    pub fn write(&mut self, event: XmlEvent) -> EventWriterResult<()> {
        match event {
            XmlEvent::StartDocument { version, encoding, standalone } =>
                self.emitter.emit_start_document(&mut self.sink, version, encoding.unwrap_or("UTF-8"), standalone),
            XmlEvent::ProcessingInstruction { name, data } =>
                self.emitter.emit_processing_instruction(&mut self.sink, name, data),
            XmlEvent::StartElement { name, attributes, namespace } =>
                self.emitter.emit_start_element(&mut self.sink, name, &attributes, namespace),
            XmlEvent::EndElement { name } =>
                self.emitter.emit_end_element(&mut self.sink, name),
            XmlEvent::Comment(content) =>
                self.emitter.emit_comment(&mut self.sink, content),
            XmlEvent::CData(content) =>
                self.emitter.emit_cdata(&mut self.sink, content),
            XmlEvent::Characters(content) =>
                self.emitter.emit_characters(&mut self.sink, content)
        }
    }
}

impl EventWriter<Vec<u8>> {
    #[inline]
    pub fn new_into_mem(sink: Vec<u8>) -> EventWriter<Vec<u8>> {
        EventWriter::new_into_mem_config(sink, EmitterConfig::new())
    }

    #[inline]
    pub fn new_into_mem_config(sink: Vec<u8>, config: EmitterConfig) -> EventWriter<Vec<u8>> {
        EventWriter::new_with_config(sink, config)
    }
}

#[cfg(test)]
mod tests {
    use std::io::{BufReader, SeekFrom};
    use std::io::prelude::*;
    use std::fs::File;
    use std::path::Path;

    use reader::EventReader;
    use writer::EventWriter;

    #[ignore]
    #[test]
    fn writer_test() {
        let mut f = File::open(Path::new("data/sample_1.xml")).unwrap();
        let mut b = Vec::new();

        {
            let mut r = EventReader::new(BufReader::new(&mut f));
            let mut w = EventWriter::new(&mut b);

            for e in r.events() {
                match e.as_writer_event() {
                    Some(e) => match w.write(e) {
                        Ok(_) => {},
                        Err(e) => panic!("Writer error: {:?}", e)
                    },
                    None => println!("Non-writer event: {:?}", e)
                }
            }
        }

        f.seek(SeekFrom::Start(0)).unwrap();
        let mut fs = String::new();
        f.read_to_string(&mut fs).unwrap();

        let bs = String::from_utf8(b).unwrap();

        assert_eq!(fs, bs)
    }
}
<|endoftext|>


<commit_before>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;
use error::check;

struct Platform {
    id: cl_platform_id
}

impl Platform {
    fn get_devices() -> ~[Device] {
        get_devices(self)
    }

    fn name() -> ~str {
        let mut size = 0;

        clGetPlatformInfo(self.id,
                          CL_PLATFORM_NAME,
                          0,
                          ptr::null(),
                          ptr::mut_addr_of(&size));

        let name = str::repeat(" ", size as uint);

        do str::as_buf(name) |p, len| {
            clGetPlatformInfo(self.id,
                              CL_PLATFORM_NAME,
                              len as libc::size_t,
                              p as *libc::c_void,
                              ptr::mut_addr_of(&size));
        };

        move name
    }
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    check(errcode, "Failed to create opencl context!");

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,
    device: Device,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: & Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    check(errcode, "Failed to create command queue!");

    CommandQueue {
        cqueue: cqueue,
        device: device
    }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: & Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    check(errcode, "Failed to create buffer!");

    Buffer { buffer: buffer, size: size }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

pub impl Program {
    fn build(&self, device: Device) -> Result<(), ~str> {
        build_program(self, device)
    }

    fn create_kernel(&self, name: &str) -> Kernel {
        create_kernel(self, name)
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: & Context, device: Device,
                                  binary_path: & Path) -> Program {
    let mut errcode = 0;
    let binary = match move io::read_whole_file_str(binary_path) {
        result::Ok(move binary) => move binary,
        Err(e) => fail fmt!("%?", e)
    };
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.len() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::c_uchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    check(errcode, "Failed to create open cl program with binary!");

    Program { prg: program }
}

pub fn build_program(program: & Program, device: Device) -> Result<(), ~str> {
    let ret = clBuildProgram(program.prg, 1, ptr::addr_of(&device.id),
                             ptr::null(), ptr::null(), ptr::null());
    if ret == CL_SUCCESS as cl_int {
        Ok(())
    }
    else {
        let mut size = 0 as libc::size_t;
        let status = clGetProgramBuildInfo(
            program.prg,
            device.id,
            CL_PROGRAM_BUILD_LOG,
            0,
            ptr::null(),
            ptr::addr_of(&size));
        check(status, "Could not get build log");

        let buf = vec::from_elem(size as uint, 0u8);
        do vec::as_imm_buf(buf) |p, len| {
            let status = clGetProgramBuildInfo(
                program.prg,
                device.id,
                CL_PROGRAM_BUILD_LOG,
                len as libc::size_t,
                p as *libc::c_void,
                ptr::null());
            check(status, "Could not get build log");

            unsafe { Err(str::raw::from_c_str(p as *libc::c_char)) }
        }
    }
}

struct Kernel {
    kernel: cl_kernel,

    drop {
        clReleaseKernel(self.kernel);
    }
}

pub impl Kernel {
    fn set_arg<T: KernelArg>(i: uint, x: &T) {
        set_kernel_arg(&self, i as CL::cl_uint, x)
    }
}

pub fn create_kernel(program: & Program, kernel: & str) -> Kernel unsafe{
    let mut errcode = 0;
    let bytes = str::to_bytes(kernel);
    let kernel = clCreateKernel(program.prg, vec::raw::to_ptr(bytes) as *libc::c_char, ptr::addr_of(&errcode));

    check(errcode, "Failed to create kernel!");

    Kernel { kernel: kernel }
}

pub trait KernelArg {
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void);
}

macro_rules! scalar_kernel_arg (
    ($t:ty) => (pub impl $t: KernelArg {
        pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
            (sys::size_of::<$t>() as libc::size_t,
             ptr::addr_of(self) as *libc::c_void)
        }
    })
)

scalar_kernel_arg!(int)
scalar_kernel_arg!(uint)

pub fn set_kernel_arg<T: KernelArg>(kernel: & Kernel,
                                    position: cl_uint,
                                    arg: &T)
{
    let (size, p) = arg.get_value();
    let ret = clSetKernelArg(kernel.kernel, position,
                             size,
                             p);

    check(ret, "Failed to set kernel arg!");
}

pub fn enqueue_nd_range_kernel(cqueue: & CommandQueue, kernel: & Kernel, work_dim: cl_uint,
                               _global_work_offset: int, global_work_size: int,
                               local_work_size: int) unsafe{
    let ret = clEnqueueNDRangeKernel(cqueue.cqueue, kernel.kernel, work_dim,
                                     // ptr::addr_of(&global_work_offset) as *libc::size_t,
                                     ptr::null(),
                                     ptr::addr_of(&global_work_size) as *libc::size_t,
                                     ptr::addr_of(&local_work_size) as *libc::size_t,
                                     0, ptr::null(), ptr::null());
    check(ret, "Failed to enqueue nd range kernel!");
}

pub impl Buffer: KernelArg{
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
        (sys::size_of::<cl_mem>() as libc::size_t,
         ptr::addr_of(&self.buffer) as *libc::c_void)
    }
}

struct Event {
    event: cl_event,

    drop {
        clReleaseEvent(self.event);
    }
}

impl Event {
    fn wait() {
        let status = clWaitForEvents(1, ptr::addr_of(&self.event));
        check(status, "Error waiting for event");
    }
}

/**
This packages an OpenCL context, a device, and a command queue to
simplify handling all these structures.
*/
struct ComputeContext {
    ctx: Context,
    device: Device,
    q: CommandQueue
}

impl ComputeContext {
    fn create_program_from_source(src: &str) -> Program {
        do str::as_c_str(src) |src| {
            let mut status = CL_SUCCESS as cl_int;
            let program = clCreateProgramWithSource(
                self.ctx.ctx,
                1,
                ptr::addr_of(&src),
                ptr::null(),
                ptr::addr_of(&status));
            check(status, "Could not create program");

            Program { prg: program }
        }
    }

    fn enqueue_async_kernel<I: KernelIndex>(k: &Kernel, global: I, local: I)
        -> Event
    {
        let e: cl_event = ptr::null();
        let status = clEnqueueNDRangeKernel(
            self.q.cqueue,
            k.kernel,
            KernelIndex::num_dimensions::<I>(),
            ptr::null(),
            global.get_ptr(),
            local.get_ptr(),
            0,
            ptr::null(),
            ptr::addr_of(&e));
        check(status, "Error enqueuing kernel.");

        Event { event: e }
    }
}

pub fn create_compute_context() -> @ComputeContext {
    // Enumerate all platforms until we find a device that works.

    for get_platforms().each |p| {
        let devices = p.get_devices();
        if devices.len() > 0 {
            let device = devices[0];
            let ctx = create_context(device);
            let q = create_commandqueue(&ctx, device);
            return @ComputeContext {
                ctx: move ctx,
                device: move device,
                q: move q
            }
        }
    }
    fail ~"Could not find an acceptable device."
}

trait KernelIndex {
    static pure fn num_dimensions() -> cl_uint;
    pure fn get_ptr(&self) -> *libc::size_t;
}

impl int: KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 1 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl (int, int): KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 2 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl uint: KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 1 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl (uint, uint): KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 2 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

#[cfg(test)]
mod test {
    macro_rules! expect (
        ($test: expr, $expected: expr) => ({
            let test = $test;
            let expected = $expected;
            if test != expected {
                fail fmt!("Test failure in %s: expected %?, got %?",
                          stringify!($test),
                          expected, test)
            }
        })
    )

    use vector::*;

    #[test]
    fn program_build() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);
    }

    #[test]
    fn simple_kernel() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn add_k() {
        let src = "__kernel void test(__global int *i, int k) { \
                       *i += k; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);
        k.set_arg(1, &42);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 43);
    }

    #[test]
    fn simple_kernel_index() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        ctx.enqueue_async_kernel(&k, 1, 1).wait();

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn kernel_2d() {
        let src = "__kernel void test(__global long int *N) { \
                       int i = get_global_id(0); \
                       int j = get_global_id(1); \
                       int s = get_global_size(0); \
                       N[i * s + j] = i * j;
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);

        match prog.build(ctx.device) {
            Ok(()) => (),
            Err(build_log) => {
                io::println("Error building program:\n");
                io::println(build_log);
                fail
            }
        }

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1, 2, 3, 4, 5, 6, 7, 8, 9]);

        k.set_arg(0, &v);

        ctx.enqueue_async_kernel(&k, (3, 3), (1, 1)).wait();

        let v = v.to_vec();

        expect!(v, ~[0, 0, 0, 0, 1, 2, 0, 2, 4]);
    }
}<commit_msg>get_devices uses CL_DEVICE_TYPE_ALL<commit_after>// Higher level OpenCL wrappers.

use CL::*;
use CL::ll::*;
use error::check;

struct Platform {
    id: cl_platform_id
}

impl Platform {
    fn get_devices() -> ~[Device] {
        get_devices(self)
    }

    fn name() -> ~str {
        let mut size = 0;

        clGetPlatformInfo(self.id,
                          CL_PLATFORM_NAME,
                          0,
                          ptr::null(),
                          ptr::mut_addr_of(&size));

        let name = str::repeat(" ", size as uint);

        do str::as_buf(name) |p, len| {
            clGetPlatformInfo(self.id,
                              CL_PLATFORM_NAME,
                              len as libc::size_t,
                              p as *libc::c_void,
                              ptr::mut_addr_of(&size));
        };

        move name
    }
}

pub fn get_platforms() -> ~[Platform] {
    let mut num_platforms = 0;

    // TODO: Check result status
    clGetPlatformIDs(0, ptr::null(), ptr::addr_of(&num_platforms));

    let ids = vec::to_mut(vec::from_elem(num_platforms as uint,
                                         0 as cl_platform_id));

    do vec::as_imm_buf(ids) |ids, len| {
        clGetPlatformIDs(len as cl_uint,
                         ids, ptr::addr_of(&num_platforms))
    };

    do ids.map |id| { Platform { id: *id } }
}

struct Device {
    id: cl_device_id
}

pub fn get_devices(platform: Platform) -> ~[Device] {
    let mut num_devices = 0;

    clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_ALL, 0, ptr::null(),
                   ptr::addr_of(&num_devices));

    let ids = vec::to_mut(vec::from_elem(num_devices as uint,
                                         0 as cl_device_id));
    do vec::as_imm_buf(ids) |ids, len| {
        clGetDeviceIDs(platform.id, CL_DEVICE_TYPE_GPU, len as cl_uint,
                       ids, ptr::addr_of(&num_devices));
    };

    do ids.map |id| { Device {id: *id }}
}

struct Context {
    ctx: cl_context,

    drop {
        clReleaseContext(self.ctx);
    }
}

pub fn create_context(device: Device) -> Context {
    // TODO: Support for multiple devices
    let mut errcode = 0;

    // TODO: Proper error messages
    let ctx = clCreateContext(ptr::null(), 1, ptr::addr_of(&device.id),
                              ptr::null(), ptr::null(), ptr::addr_of(&errcode));

    check(errcode, "Failed to create opencl context!");

    Context { ctx: ctx }
}

struct CommandQueue {
    cqueue: cl_command_queue,
    device: Device,

    drop {
        clReleaseCommandQueue(self.cqueue);
    }
}

pub fn create_commandqueue(ctx: & Context, device: Device) -> CommandQueue {
    let mut errcode = 0;

    let cqueue = clCreateCommandQueue(ctx.ctx, device.id, 0, ptr::addr_of(&errcode));

    check(errcode, "Failed to create command queue!");

    CommandQueue {
        cqueue: cqueue,
        device: device
    }
}

struct Buffer {
    buffer: cl_mem,
    size: int,

    drop {
        clReleaseMemObject(self.buffer);
    }
}

// TODO: How to make this function cleaner and nice
pub fn create_buffer(ctx: & Context, size: int, flags: cl_mem_flags) -> Buffer {
    let mut errcode = 0;

    let buffer = clCreateBuffer(ctx.ctx, flags, size as libc::size_t, ptr::null(),
                                ptr::addr_of(&errcode));

    check(errcode, "Failed to create buffer!");

    Buffer { buffer: buffer, size: size }
}

struct Program {
    prg: cl_program,

    drop {
        clReleaseProgram(self.prg);
    }
}

pub impl Program {
    fn build(&self, device: Device) -> Result<(), ~str> {
        build_program(self, device)
    }

    fn create_kernel(&self, name: &str) -> Kernel {
        create_kernel(self, name)
    }
}

// TODO: Support multiple devices
pub fn create_program_with_binary(ctx: & Context, device: Device,
                                  binary_path: & Path) -> Program {
    let mut errcode = 0;
    let binary = match move io::read_whole_file_str(binary_path) {
        result::Ok(move binary) => move binary,
        Err(e) => fail fmt!("%?", e)
    };
    let program = do str::as_c_str(binary) |kernel_binary| {
        clCreateProgramWithBinary(ctx.ctx, 1, ptr::addr_of(&device.id),
                                  ptr::addr_of(&(binary.len() + 1)) as *libc::size_t,
                                  ptr::addr_of(&kernel_binary) as **libc::c_uchar,
                                  ptr::null(),
                                  ptr::addr_of(&errcode))
    };

    check(errcode, "Failed to create open cl program with binary!");

    Program { prg: program }
}

pub fn build_program(program: & Program, device: Device) -> Result<(), ~str> {
    let ret = clBuildProgram(program.prg, 1, ptr::addr_of(&device.id),
                             ptr::null(), ptr::null(), ptr::null());
    if ret == CL_SUCCESS as cl_int {
        Ok(())
    }
    else {
        let mut size = 0 as libc::size_t;
        let status = clGetProgramBuildInfo(
            program.prg,
            device.id,
            CL_PROGRAM_BUILD_LOG,
            0,
            ptr::null(),
            ptr::addr_of(&size));
        check(status, "Could not get build log");

        let buf = vec::from_elem(size as uint, 0u8);
        do vec::as_imm_buf(buf) |p, len| {
            let status = clGetProgramBuildInfo(
                program.prg,
                device.id,
                CL_PROGRAM_BUILD_LOG,
                len as libc::size_t,
                p as *libc::c_void,
                ptr::null());
            check(status, "Could not get build log");

            unsafe { Err(str::raw::from_c_str(p as *libc::c_char)) }
        }
    }
}

struct Kernel {
    kernel: cl_kernel,

    drop {
        clReleaseKernel(self.kernel);
    }
}

pub impl Kernel {
    fn set_arg<T: KernelArg>(i: uint, x: &T) {
        set_kernel_arg(&self, i as CL::cl_uint, x)
    }
}

pub fn create_kernel(program: & Program, kernel: & str) -> Kernel unsafe{
    let mut errcode = 0;
    let bytes = str::to_bytes(kernel);
    let kernel = clCreateKernel(program.prg, vec::raw::to_ptr(bytes) as *libc::c_char, ptr::addr_of(&errcode));

    check(errcode, "Failed to create kernel!");

    Kernel { kernel: kernel }
}

pub trait KernelArg {
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void);
}

macro_rules! scalar_kernel_arg (
    ($t:ty) => (pub impl $t: KernelArg {
        pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
            (sys::size_of::<$t>() as libc::size_t,
             ptr::addr_of(self) as *libc::c_void)
        }
    })
)

scalar_kernel_arg!(int)
scalar_kernel_arg!(uint)

pub fn set_kernel_arg<T: KernelArg>(kernel: & Kernel,
                                    position: cl_uint,
                                    arg: &T)
{
    let (size, p) = arg.get_value();
    let ret = clSetKernelArg(kernel.kernel, position,
                             size,
                             p);

    check(ret, "Failed to set kernel arg!");
}

pub fn enqueue_nd_range_kernel(cqueue: & CommandQueue, kernel: & Kernel, work_dim: cl_uint,
                               _global_work_offset: int, global_work_size: int,
                               local_work_size: int) unsafe{
    let ret = clEnqueueNDRangeKernel(cqueue.cqueue, kernel.kernel, work_dim,
                                     // ptr::addr_of(&global_work_offset) as *libc::size_t,
                                     ptr::null(),
                                     ptr::addr_of(&global_work_size) as *libc::size_t,
                                     ptr::addr_of(&local_work_size) as *libc::size_t,
                                     0, ptr::null(), ptr::null());
    check(ret, "Failed to enqueue nd range kernel!");
}

pub impl Buffer: KernelArg{
    pure fn get_value(&self) -> (libc::size_t, *libc::c_void) {
        (sys::size_of::<cl_mem>() as libc::size_t,
         ptr::addr_of(&self.buffer) as *libc::c_void)
    }
}

struct Event {
    event: cl_event,

    drop {
        clReleaseEvent(self.event);
    }
}

impl Event {
    fn wait() {
        let status = clWaitForEvents(1, ptr::addr_of(&self.event));
        check(status, "Error waiting for event");
    }
}

/**
This packages an OpenCL context, a device, and a command queue to
simplify handling all these structures.
*/
struct ComputeContext {
    ctx: Context,
    device: Device,
    q: CommandQueue
}

impl ComputeContext {
    fn create_program_from_source(src: &str) -> Program {
        do str::as_c_str(src) |src| {
            let mut status = CL_SUCCESS as cl_int;
            let program = clCreateProgramWithSource(
                self.ctx.ctx,
                1,
                ptr::addr_of(&src),
                ptr::null(),
                ptr::addr_of(&status));
            check(status, "Could not create program");

            Program { prg: program }
        }
    }

    fn enqueue_async_kernel<I: KernelIndex>(k: &Kernel, global: I, local: I)
        -> Event
    {
        let e: cl_event = ptr::null();
        let status = clEnqueueNDRangeKernel(
            self.q.cqueue,
            k.kernel,
            KernelIndex::num_dimensions::<I>(),
            ptr::null(),
            global.get_ptr(),
            local.get_ptr(),
            0,
            ptr::null(),
            ptr::addr_of(&e));
        check(status, "Error enqueuing kernel.");

        Event { event: e }
    }
}

pub fn create_compute_context() -> @ComputeContext {
    // Enumerate all platforms until we find a device that works.

    for get_platforms().each |p| {
        let devices = p.get_devices();
        if devices.len() > 0 {
            let device = devices[0];
            let ctx = create_context(device);
            let q = create_commandqueue(&ctx, device);
            return @ComputeContext {
                ctx: move ctx,
                device: move device,
                q: move q
            }
        }
    }
    fail ~"Could not find an acceptable device."
}

trait KernelIndex {
    static pure fn num_dimensions() -> cl_uint;
    pure fn get_ptr(&self) -> *libc::size_t;
}

impl int: KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 1 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl (int, int): KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 2 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl uint: KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 1 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

impl (uint, uint): KernelIndex {
    static pure fn num_dimensions() -> cl_uint { 2 }

    pure fn get_ptr(&self) -> *libc::size_t {
        ptr::addr_of(self) as *libc::size_t
    }
}

#[cfg(test)]
mod test {
    macro_rules! expect (
        ($test: expr, $expected: expr) => ({
            let test = $test;
            let expected = $expected;
            if test != expected {
                fail fmt!("Test failure in %s: expected %?, got %?",
                          stringify!($test),
                          expected, test)
            }
        })
    )

    use vector::*;

    #[test]
    fn program_build() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);
    }

    #[test]
    fn simple_kernel() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn add_k() {
        let src = "__kernel void test(__global int *i, int k) { \
                       *i += k; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);
        k.set_arg(1, &42);

        enqueue_nd_range_kernel(
            &ctx.q,
            &k,
            1, 0, 1, 1);

        let v = v.to_vec();

        expect!(v[0], 43);
    }

    #[test]
    fn simple_kernel_index() {
        let src = "__kernel void test(__global int *i) { \
                       *i += 1; \
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);
        prog.build(ctx.device);

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1]);

        k.set_arg(0, &v);

        ctx.enqueue_async_kernel(&k, 1, 1).wait();

        let v = v.to_vec();

        expect!(v[0], 2);
    }

    #[test]
    fn kernel_2d() {
        let src = "__kernel void test(__global long int *N) { \
                       int i = get_global_id(0); \
                       int j = get_global_id(1); \
                       int s = get_global_size(0); \
                       N[i * s + j] = i * j;
                   }";
        let ctx = create_compute_context();
        let prog = ctx.create_program_from_source(src);

        match prog.build(ctx.device) {
            Ok(()) => (),
            Err(build_log) => {
                io::println("Error building program:\n");
                io::println(build_log);
                fail
            }
        }

        let k = prog.create_kernel("test");

        let v = Vector::from_vec(ctx, [1, 2, 3, 4, 5, 6, 7, 8, 9]);

        k.set_arg(0, &v);

        ctx.enqueue_async_kernel(&k, (3, 3), (1, 1)).wait();

        let v = v.to_vec();

        expect!(v, ~[0, 0, 0, 0, 1, 2, 0, 2, 4]);
    }
}<|endoftext|>


<commit_before>//! The Ref object is a helper over the link functionality, so one is able to create references to
//! files outside of the imag store.

use std::path::PathBuf;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::Store;
use libimagerror::into::IntoError;

use toml::Value;
use crypto::sha1::Sha1;
use crypto::digest::Digest;

use error::RefErrorKind as REK;
use flags::RefFlags;
use result::Result;
use module_path::ModuleEntryPath;

pub struct Ref<'a>(FileLockEntry<'a>);

impl<'a> Ref<'a> {

    /// Try to get `si` as Ref object from the store
    pub fn get(store: &'a Store, si: StoreId) -> Result<Ref<'a>> {
        match store.get(si) {
            Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
            Ok(None) => return Err(REK::RefNotInStore.into_error()),
            Ok(Some(fle)) => Ref::read_reference(&fle).map(|_| Ref(fle)),
        }
    }

    fn read_reference(fle: &FileLockEntry<'a>) -> Result<PathBuf> {
        match fle.get_header().read("ref.reference") {
            Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Create a Ref object which refers to `pb`
    pub fn create(store: &'a Store, pb: PathBuf, flags: RefFlags) -> Result<Ref<'a>> {
        if !pb.exists() {
            return Err(REK::RefTargetDoesNotExist.into_error());
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(REK::RefTargetCannotBeHashed.into_error());
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .map_err(Box::new)
                .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e))

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        Some(hash_file_contents(&mut file))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .map_err(Box::new)
                                  .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
                        ))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (file, opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e)))
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(file, opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(Ref::hash_path(&can)
                        .map_err(Box::new)
                        .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
                    );

                    Ok((file, opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(REK::PathUTF8Error.into_error()),
                        Some(can) => Ok((file, opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(store
                                   .create(ModuleEntryPath::new(path_hash))
                                   .map_err(Box::new)
                                   .map_err(|e| REK::StoreWriteError.into_error_with_cause(e))
                    );

                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some(("ref",             Value::Table(BTreeMap::new()))),
                Some(("ref.permissions", Value::Table(BTreeMap::new()))),

                Some(("ref.path", Value::String(canonical_path))),

                content_hash.map(|h| ("ref.content_hash", Value::String(h))),
                permissions.map(|p| ("ref.permissions.ro", Value::Boolean(p.readonly()))),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(false) => {
                            let e = REK::HeaderFieldAlreadyExistsError.into_error();
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        Err(e) => {
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        _ => (),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(Ref(fle))
    }

    /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
    /// algorithm on it
    fn hash_path(pb: &PathBuf) -> Result<String> {
        use std::io::Read;
        use crypto::sha1::Sha1;
        use crypto::digest::Digest;

        match pb.to_str() {
            Some(s) => {
                let mut hasher = Sha1::new();
                hasher.input_str(s);
                Ok(hasher.result_str())
            },
            None => return Err(REK::PathUTF8Error.into_error()),
        }
    }

    /// check whether the pointer the Ref represents still points to a file which exists
    pub fn fs_link_exists(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.exists())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_file()`
    pub fn is_ref_to_file(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_file())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_dir()`
    pub fn is_ref_to_dir(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_dir())
    }

    /// Alias for `!Ref::fs_link_exists()`
    pub fn is_dangling(&self) -> Result<bool> {
        self.fs_link_exists().map(|b| !b)
    }

    /// check whether the pointer the Ref represents is valid
    /// This includes:
    ///     - Hashsum of the file is still the same as stored in the Ref
    ///     - file permissions are still valid
    pub fn fs_link_valid(&self) -> Result<bool> {
        match (self.fs_link_valid_permissions(), self.fs_link_valid_hash()) {
            (Ok(true) , Ok(true)) => Ok(true),
            (Ok(_)    , Ok(_))    => Ok(false),
            (Err(e)   , _)        => Err(e),
            (_        , Err(e))   => Err(e),
        }
    }

    /// Check whether the file permissions of the referenced file are equal to the stored
    /// permissions
    pub fn fs_link_valid_permissions(&self) -> Result<bool> {
        self.0
            .get_header()
            .read("ref.permissions.ro")
            .map_err(Box::new)
            .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            .and_then(|ro| {
                match ro {
                    Some(Value::Boolean(b)) => Ok(b),
                    Some(_)                 => Err(REK::HeaderTypeError.into_error()),
                    None                    => Err(REK::HeaderFieldMissingError.into_error()),
                }
            })
            .and_then(|ro| self.fs_file().map(|pb| (ro, pb)))
            .and_then(|(ro, pb)| {
                File::open(pb)
                    .map(|file| (ro, file))
                    .map_err(Box::new)
                    .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            })
            .and_then(|(ro, file)| {
                file
                    .metadata()
                    .map(|md| md.permissions())
                    .map(|perm| perm.readonly() == ro)
                    .map_err(Box::new)
                    .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
            })
            .map_err(Box::new)
            .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
    }

    /// Check whether the Hashsum of the referenced file is equal to the stored hashsum
    pub fn fs_link_valid_hash(&self) -> Result<bool> {
        let stored_hash = try!(match self.0.get_header().read("ref.content_hash") {
            // content hash stored...
            Ok(Some(Value::String(s))) => Ok(s),

            // content hash header field has wrong type
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),

            // content hash not stored
            Ok(None) => Err(REK::HeaderFieldMissingError.into_error()),

            // Error
            Err(e) => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        });

        let current_hash = try!(self.fs_file()
            .and_then(|pb| {
                File::open(pb)
                    .map_err(Box::new)
                    .map_err(|e| REK::IOError.into_error_with_cause(e))
            })
            .map(|mut file| hash_file_contents(&mut file))
        );

        Ok(stored_hash == current_hash)
    }

    /// Update the Ref by re-checking the file from FS
    /// This errors if the file is not present or cannot be read()
    pub fn update_ref(&mut self) -> Result<()> {
        unimplemented!()
    }

    /// Get the path of the file which is reffered to by this Ref
    pub fn fs_file(&self) -> Result<PathBuf> {
        match self.0.get_header().read("ref.path") {
            Ok(Some(Value::String(ref s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Check whether there is a reference to the file at `pb`
    pub fn exists(store: &Store, pb: PathBuf) -> Result<bool> {
        pb.canonicalize()
            .map_err(Box::new)
            .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e))
            .and_then(|can| {
                Ref::hash_path(&can)
                    .map_err(Box::new)
                    .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
            })
            .and_then(|hash| {
                store.retrieve_for_module("ref").map(|iter| (hash, iter))
                    .map_err(Box::new)
                    .map_err(|e| REK::StoreReadError.into_error_with_cause(e))
            })
            .and_then(|(hash, possible_refs)| {
                // This is kind of a manual Iterator::filter() call what we do here, but with the
                // actual ::filter method we cannot return the error in a nice way, so we do it
                // manually here. If you can come up with a better version of this, feel free to
                // take this note as a todo.
                for r in possible_refs {
                    let contains_hash = match r.to_str() {
                        None => { // couldn't parse StoreId -> PathBuf -> &str
                            // TODO: How to report this?
                            return Err(REK::TypeConversionError.into_error());
                        },
                        Some(s) => s.contains(&hash[..]),
                    };

                    if !contains_hash {
                        continue;
                    }

                    match store.get(r) {
                        Ok(Some(fle)) => {
                            if Ref::read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
                                return Ok(true)
                            }
                        },

                        Ok(None) => { // Something weird just happened
                            return Err(REK::StoreReadError.into_error());
                        },

                        Err(e) => {
                            return Err(REK::StoreReadError.into_error_with_cause(Box::new(e)));
                        },
                    }
                }

                Ok(false)
            })
    }

    /// Re-find a referenced file
    ///
    /// This function tries to re-find a ref by searching all directories in `search_roots` recursively
    /// for a file which matches the hash of the Ref `ref`.
    ///
    /// If `search_roots` is `None`, it starts at the filesystem root `/`.
    ///
    /// # Warning
    ///
    /// This option causes heavy I/O as it recursively searches the Filesystem.
    pub fn refind(&self, search_roots: Option<Vec<PathBuf>>) -> Option<PathBuf> {
        unimplemented!()
    }

}

impl<'a> Deref for Ref<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Ref<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

fn hash_file_contents(f: &mut File) -> String {
    let mut hasher = Sha1::new();
    let mut s = String::new();
    f.read_to_string(&mut s);
    hasher.input_str(&s[..]);
    hasher.result_str()
}

<commit_msg>Outsource hash getter functions into new (pub) functions<commit_after>//! The Ref object is a helper over the link functionality, so one is able to create references to
//! files outside of the imag store.

use std::path::PathBuf;
use std::ops::Deref;
use std::ops::DerefMut;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Read;

use libimagstore::store::FileLockEntry;
use libimagstore::storeid::StoreId;
use libimagstore::storeid::IntoStoreId;
use libimagstore::store::Store;
use libimagerror::into::IntoError;

use toml::Value;
use crypto::sha1::Sha1;
use crypto::digest::Digest;

use error::RefErrorKind as REK;
use flags::RefFlags;
use result::Result;
use module_path::ModuleEntryPath;

pub struct Ref<'a>(FileLockEntry<'a>);

impl<'a> Ref<'a> {

    /// Try to get `si` as Ref object from the store
    pub fn get(store: &'a Store, si: StoreId) -> Result<Ref<'a>> {
        match store.get(si) {
            Err(e) => return Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
            Ok(None) => return Err(REK::RefNotInStore.into_error()),
            Ok(Some(fle)) => Ref::read_reference(&fle).map(|_| Ref(fle)),
        }
    }

    fn read_reference(fle: &FileLockEntry<'a>) -> Result<PathBuf> {
        match fle.get_header().read("ref.reference") {
            Ok(Some(Value::String(s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Create a Ref object which refers to `pb`
    pub fn create(store: &'a Store, pb: PathBuf, flags: RefFlags) -> Result<Ref<'a>> {
        if !pb.exists() {
            return Err(REK::RefTargetDoesNotExist.into_error());
        }
        if flags.get_content_hashing() && pb.is_dir() {
            return Err(REK::RefTargetCannotBeHashed.into_error());
        }

        let (mut fle, content_hash, permissions, canonical_path) = { // scope to be able to fold
            try!(File::open(pb.clone())
                .map_err(Box::new)
                .map_err(|e| REK::RefTargetFileCannotBeOpened.into_error_with_cause(e))

                // If we were able to open this file,
                // we hash the contents of the file and return (file, hash)
                .and_then(|mut file| {
                    let opt_contenthash = if flags.get_content_hashing() {
                        Some(hash_file_contents(&mut file))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash))
                })

                // and then we get the permissions if we have to
                // and return (file, content hash, permissions)
                .and_then(|(file, opt_contenthash)| {
                    let opt_permissions = if flags.get_permission_tracking() {
                        Some(try!(file
                                  .metadata()
                                  .map(|md| md.permissions())
                                  .map_err(Box::new)
                                  .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
                        ))
                    } else {
                        None
                    };

                    Ok((file, opt_contenthash, opt_permissions))
                })

                // and then we try to canonicalize the PathBuf, because we want to store a
                // canonicalized path
                // and return (file, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_contenthash, opt_permissions)| {
                    pb.canonicalize()
                        .map(|can| (file, opt_contenthash, opt_permissions, can))
                        // if PathBuf::canonicalize() failed, build an error from the return value
                        .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(Box::new(e)))
                })

                // and then we hash the canonicalized path
                // and return (file, content hash, permissions, canonicalized path, path hash)
                .and_then(|(file, opt_contenthash, opt_permissions, can)| {
                    let path_hash = try!(Ref::hash_path(&can)
                        .map_err(Box::new)
                        .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
                    );

                    Ok((file, opt_contenthash, opt_permissions, can, path_hash))
                })

                // and then we convert the PathBuf of the canonicalized path to a String to be able
                // to save it in the Ref FileLockEntry obj
                // and return
                // (file, content hash, permissions, canonicalized path as String, path hash)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    match can.to_str().map(String::from) {
                        // UTF convert error in PathBuf::to_str(),
                        None      => Err(REK::PathUTF8Error.into_error()),
                        Some(can) => Ok((file, opt_conhash, opt_perm, can, path_hash))
                    }
                })

                // and then we create the FileLockEntry in the Store
                // and return (filelockentry, content hash, permissions, canonicalized path)
                .and_then(|(file, opt_conhash, opt_perm, can, path_hash)| {
                    let fle = try!(store
                                   .create(ModuleEntryPath::new(path_hash))
                                   .map_err(Box::new)
                                   .map_err(|e| REK::StoreWriteError.into_error_with_cause(e))
                    );

                    Ok((fle, opt_conhash, opt_perm, can))
                })
            )
        };

        for tpl in [
                Some(("ref",             Value::Table(BTreeMap::new()))),
                Some(("ref.permissions", Value::Table(BTreeMap::new()))),

                Some(("ref.path", Value::String(canonical_path))),

                content_hash.map(|h| ("ref.content_hash", Value::String(h))),
                permissions.map(|p| ("ref.permissions.ro", Value::Boolean(p.readonly()))),
            ].into_iter()
        {
            match tpl {
                &Some((ref s, ref v)) => {
                    match fle.get_header_mut().insert(s, v.clone()) {
                        Ok(false) => {
                            let e = REK::HeaderFieldAlreadyExistsError.into_error();
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        Err(e) => {
                            let e = Box::new(e);
                            let e = REK::HeaderFieldWriteError.into_error_with_cause(e);
                            return Err(e);
                        },
                        _ => (),
                    }
                }
                &None => {
                    debug!("Not going to insert.");
                }
            }
        }

        Ok(Ref(fle))
    }

    /// Creates a Hash from a PathBuf by making the PathBuf absolute and then running a hash
    /// algorithm on it
    fn hash_path(pb: &PathBuf) -> Result<String> {
        use std::io::Read;
        use crypto::sha1::Sha1;
        use crypto::digest::Digest;

        match pb.to_str() {
            Some(s) => {
                let mut hasher = Sha1::new();
                hasher.input_str(s);
                Ok(hasher.result_str())
            },
            None => return Err(REK::PathUTF8Error.into_error()),
        }
    }

    /// Get the hash of the link target which is stored in the ref object
    pub fn get_stored_hash(&self) -> Result<String> {
        match self.0.get_header().read("ref.content_hash") {
            // content hash stored...
            Ok(Some(Value::String(s))) => Ok(s),

            // content hash header field has wrong type
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),

            // content hash not stored
            Ok(None) => Err(REK::HeaderFieldMissingError.into_error()),

            // Error
            Err(e) => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Get the hash of the link target by reading the link target and hashing the contents
    pub fn get_current_hash(&self) -> Result<String> {
        self.fs_file()
            .and_then(|pb| {
                File::open(pb)
                    .map_err(Box::new)
                    .map_err(|e| REK::IOError.into_error_with_cause(e))
            })
            .map(|mut file| hash_file_contents(&mut file))
    }

    /// check whether the pointer the Ref represents still points to a file which exists
    pub fn fs_link_exists(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.exists())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_file()`
    pub fn is_ref_to_file(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_file())
    }

    /// Alias for `r.fs_link_exists() && r.deref().is_dir()`
    pub fn is_ref_to_dir(&self) -> Result<bool> {
        self.fs_file().map(|pathbuf| pathbuf.is_dir())
    }

    /// Alias for `!Ref::fs_link_exists()`
    pub fn is_dangling(&self) -> Result<bool> {
        self.fs_link_exists().map(|b| !b)
    }

    /// check whether the pointer the Ref represents is valid
    /// This includes:
    ///     - Hashsum of the file is still the same as stored in the Ref
    ///     - file permissions are still valid
    pub fn fs_link_valid(&self) -> Result<bool> {
        match (self.fs_link_valid_permissions(), self.fs_link_valid_hash()) {
            (Ok(true) , Ok(true)) => Ok(true),
            (Ok(_)    , Ok(_))    => Ok(false),
            (Err(e)   , _)        => Err(e),
            (_        , Err(e))   => Err(e),
        }
    }

    /// Check whether the file permissions of the referenced file are equal to the stored
    /// permissions
    pub fn fs_link_valid_permissions(&self) -> Result<bool> {
        self.0
            .get_header()
            .read("ref.permissions.ro")
            .map_err(Box::new)
            .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            .and_then(|ro| {
                match ro {
                    Some(Value::Boolean(b)) => Ok(b),
                    Some(_)                 => Err(REK::HeaderTypeError.into_error()),
                    None                    => Err(REK::HeaderFieldMissingError.into_error()),
                }
            })
            .and_then(|ro| self.fs_file().map(|pb| (ro, pb)))
            .and_then(|(ro, pb)| {
                File::open(pb)
                    .map(|file| (ro, file))
                    .map_err(Box::new)
                    .map_err(|e| REK::HeaderFieldReadError.into_error_with_cause(e))
            })
            .and_then(|(ro, file)| {
                file
                    .metadata()
                    .map(|md| md.permissions())
                    .map(|perm| perm.readonly() == ro)
                    .map_err(Box::new)
                    .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
            })
            .map_err(Box::new)
            .map_err(|e| REK::RefTargetCannotReadPermissions.into_error_with_cause(e))
    }

    /// Check whether the Hashsum of the referenced file is equal to the stored hashsum
    pub fn fs_link_valid_hash(&self) -> Result<bool> {
        let stored_hash  = try!(self.get_stored_hash());
        let current_hash = try!(self.get_current_hash());
        Ok(stored_hash == current_hash)
    }

    /// Update the Ref by re-checking the file from FS
    /// This errors if the file is not present or cannot be read()
    pub fn update_ref(&mut self) -> Result<()> {
        unimplemented!()
    }

    /// Get the path of the file which is reffered to by this Ref
    pub fn fs_file(&self) -> Result<PathBuf> {
        match self.0.get_header().read("ref.path") {
            Ok(Some(Value::String(ref s))) => Ok(PathBuf::from(s)),
            Ok(Some(_)) => Err(REK::HeaderTypeError.into_error()),
            Ok(None)    => Err(REK::HeaderFieldMissingError.into_error()),
            Err(e)      => Err(REK::StoreReadError.into_error_with_cause(Box::new(e))),
        }
    }

    /// Check whether there is a reference to the file at `pb`
    pub fn exists(store: &Store, pb: PathBuf) -> Result<bool> {
        pb.canonicalize()
            .map_err(Box::new)
            .map_err(|e| REK::PathCanonicalizationError.into_error_with_cause(e))
            .and_then(|can| {
                Ref::hash_path(&can)
                    .map_err(Box::new)
                    .map_err(|e| REK::PathHashingError.into_error_with_cause(e))
            })
            .and_then(|hash| {
                store.retrieve_for_module("ref").map(|iter| (hash, iter))
                    .map_err(Box::new)
                    .map_err(|e| REK::StoreReadError.into_error_with_cause(e))
            })
            .and_then(|(hash, possible_refs)| {
                // This is kind of a manual Iterator::filter() call what we do here, but with the
                // actual ::filter method we cannot return the error in a nice way, so we do it
                // manually here. If you can come up with a better version of this, feel free to
                // take this note as a todo.
                for r in possible_refs {
                    let contains_hash = match r.to_str() {
                        None => { // couldn't parse StoreId -> PathBuf -> &str
                            // TODO: How to report this?
                            return Err(REK::TypeConversionError.into_error());
                        },
                        Some(s) => s.contains(&hash[..]),
                    };

                    if !contains_hash {
                        continue;
                    }

                    match store.get(r) {
                        Ok(Some(fle)) => {
                            if Ref::read_reference(&fle).map(|path| path == pb).unwrap_or(false) {
                                return Ok(true)
                            }
                        },

                        Ok(None) => { // Something weird just happened
                            return Err(REK::StoreReadError.into_error());
                        },

                        Err(e) => {
                            return Err(REK::StoreReadError.into_error_with_cause(Box::new(e)));
                        },
                    }
                }

                Ok(false)
            })
    }

    /// Re-find a referenced file
    ///
    /// This function tries to re-find a ref by searching all directories in `search_roots` recursively
    /// for a file which matches the hash of the Ref `ref`.
    ///
    /// If `search_roots` is `None`, it starts at the filesystem root `/`.
    ///
    /// # Warning
    ///
    /// This option causes heavy I/O as it recursively searches the Filesystem.
    pub fn refind(&self, search_roots: Option<Vec<PathBuf>>) -> Option<PathBuf> {
        unimplemented!()
    }

}

impl<'a> Deref for Ref<'a> {
    type Target = FileLockEntry<'a>;

    fn deref(&self) -> &FileLockEntry<'a> {
        &self.0
    }

}

impl<'a> DerefMut for Ref<'a> {

    fn deref_mut(&mut self) -> &mut FileLockEntry<'a> {
        &mut self.0
    }

}

fn hash_file_contents(f: &mut File) -> String {
    let mut hasher = Sha1::new();
    let mut s = String::new();
    f.read_to_string(&mut s);
    hasher.input_str(&s[..]);
    hasher.result_str()
}

<|endoftext|>