<commit_before>use time::now_utc;
use time::Tm;
use std::ascii::AsciiExt;
use openssl::crypto::hash::Hasher;
use openssl::crypto::hash::HashType::SHA256;
use serialize::hex::ToHex;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use request::Header;
use url::percent_encoding::{percent_encode_to, FORM_URLENCODED_ENCODE_SET};

struct QP<'a> {
    k: &'a str,
    v: &'a str,
}

#[derive(Clone)]
pub struct SigV4 {
    headers: BTreeMap<String, Vec<String>>,
    path: Option<String>,
    method: Option<String>,
    query: Option<String>,
    payload: Option<String>,
    date: Tm,
}

impl SigV4 {
    pub fn new() -> SigV4{
        let dt = now_utc();
        SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: dt,
        }
    }

    pub fn header(mut self, header: Header) -> SigV4 {
        append_header(&mut self.headers, header.key.as_slice(), header.value.as_slice());
        self
    }

    pub fn date(mut self) -> SigV4 {
        append_header(&mut self.headers, "X-Amz-Date",
                      self.date.strftime("%Y%m%dT%H%M%SZ").unwrap().to_string().as_slice());
        self
    }

    pub fn path(mut self, path: String) -> SigV4 {
        self.path = Some(path);
        self
    }

    pub fn method(mut self, method: String) -> SigV4 {
        self.method = Some(method);
        self
    }

    pub fn query(mut self, query: String) -> SigV4 {
        self.query = Some(query);
        self
    }

    pub fn payload(mut self, payload: String) -> SigV4 {
        self.payload = Some(payload);
        self
    }

    pub fn signature(self) -> String {
        self.hashed_canonical_request()
    }

    fn hashed_canonical_request(self) -> String {
        let mut h = Hasher::new(SHA256);
        h.update(self.canonical_request().as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn hashed_payload(&self) -> String {
        let val = match self.payload {
            Some(ref x) => x.to_string(),
            None => "".to_string(),
        };
        let mut h = Hasher::new(SHA256);
        h.update(val.as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn signed_headers(&self) -> String {
        let mut h = String::new();

        for (key,_) in self.headers.iter() {
            if h.len() > 0 {
                h.push(';')
            }
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(key.as_slice());
        }
        h
    }

    fn canonical_headers(&self) -> String {
        let mut h = String::new();

        for (key,value) in self.headers.iter() {
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(format!("{}:{}\n", key, canonical_value(value)).as_slice());
        }
        h
    }

    fn canonical_query_string(&self) -> String {
        match self.query {
            None => String::new(),
            Some(ref x) => {
                let mut h: Vec<QP> = Vec::new();
                for q in x.split('&') {
                    if q.contains_char('=') {
                        let n: Vec<&str> = q.splitn(1, '=').collect();
                        h.push(QP{k: n[0], v: n[1]})
                    } else {
                        h.push(QP{k: q, v: ""})
                    }
                };
                sort_query_string(h)
            }
        }
    }

    fn canonical_request(&self) -> String {
        format!("{}\n{}\n{}\n{}\n{}\n{}", expand_string(&self.method),
                expand_string(&self.path),
                self.canonical_query_string(),
                self.canonical_headers(),
                self.signed_headers(),
                self.hashed_payload()
        )
    }

}

fn sort_query_string(mut query: Vec<QP>) -> String {
    #[inline]
    fn byte_serialize(input: &str, output: &mut String) {
        for &byte in input.as_bytes().iter() {
            percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
        }
    }

    let mut output = String::new();

    query.sort_by( |a, b| a.k.cmp(b.k));
    for item in query.iter() {
        if output.len() > 0 {
            output.push_str("&");
        }
        byte_serialize(item.k, &mut output);
        output.push_str("=");
        byte_serialize(item.v, &mut output);
    }

    output
    // it would be marvelous to use the below, but the AWS SigV4 spec says space must be %20, and
    // rust-url gives me back a +. So, roll our own for now.
    // let qs: Vec<(String, String)> = query.iter().map(|n| (n.k.to_string(), n.v.to_string())).collect();
    // form_urlencoded::serialize_owned(qs.as_slice())
}

fn append_header(map: &mut BTreeMap<String, Vec<String>>, key: &str, value: &str) {
    let k = key.to_ascii_lowercase().to_string();

    match map.entry(k) {
        Entry::Vacant(entry) => {
            let mut values = Vec::new();
            values.push(value.to_string());
            entry.set(values);
        },
        Entry::Occupied(entry) => {
            entry.into_mut().push(value.to_string());
        }
    };
}

fn expand_string(val: &Option<String>) -> String {
    match *val {
        None => "".to_string(),
        Some(ref x) => x.to_string(),
    }
}

fn canonical_value(val: &Vec<String>) -> String {
    let mut st = String::new();
    for v in val.iter() {
        if st.len() > 0 {
            st.push(',')
        }
        if v.starts_with("\""){
            st.push_str(v.as_slice());
        } else {
            st.push_str(v.replace("  ", " ").trim());
        }
    }
    st
}

#[cfg(test)]
mod tests {
    use super::SigV4;
    use request::Header;
    use time::strptime;
    use std::collections::BTreeMap;

    macro_rules! wrap_header (
        ($key:expr) => (
            Some(&vec!($key.to_string()))
        )
    );

    #[test]
    fn test_new_sigv4() {
        let sig = SigV4::new();
        assert_eq!(sig.headers.len(), 0)
    }

    #[test]
    fn test_date() {
        let sig = SigV4::new().date();
        assert!(sig.headers.contains_key("x-amz-date"))
    }

    #[test]
    fn test_add_header() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};

        let sig = SigV4::new().header(h);
        assert_eq!(sig.headers.get("test"), wrap_header!("a string"))
    }

    #[test]
    fn test_add_second_value() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};
        let h2 = Header{ key: "test".to_string(), value: "another string".to_string()};

        let sig = SigV4::new().header(h).header(h2);
        assert_eq!(sig.headers.get("test"), Some(&vec!("a string".to_string(), "another string".to_string())))
    }

    #[test]
    fn test_canonical_query_encoded() {
        let sig = SigV4::new().query("a space=woo woo&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "a%20space=woo%20woo&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_valueless() {
        let sig = SigV4::new().query("other=&test&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "other=&test=&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_sorted() {
        let sig = SigV4::new().query("foo=&bar=&baz=".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "bar=&baz=&foo=")
    }

    // Ensure that params with the same name stay in the same order after sorting
    #[test]
    fn test_canonical_query_complex() {
        let sig = SigV4::new().query("q.options=abc&q=xyz&q=mno".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "q=xyz&q=mno&q.options=abc")
    }

    #[test]
    fn test_hashed_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date().header(h).header(h2);

        assert_eq!(sig.hashed_canonical_request().as_slice(), "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2")
    }

    #[test]
    fn test_signed_headers() {
        let h = Header{ key: "test".to_string(),
            value: "a string".to_string()};
        let h2 = Header{ key: "Content-Type".to_string(),
            value: "application/x-www-form-urlencoded; charset=utf-8".to_string()};
        let h3 = Header{ key: "Authorization".to_string(),
            value: "none".to_string()};

        let sig = SigV4::new().date().header(h).header(h2).header(h3);
        assert_eq!(sig.signed_headers().as_slice(), "content-type;test;x-amz-date")
    }

    #[test]
    fn test_hashed_payload() {
        let sig = SigV4::new().
            payload("Action=ListUsers&Version=2010-05-08".to_string());
        assert_eq!(sig.hashed_payload(),
        "b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

    #[test]
    fn test_empty_payload() {
        let sig = SigV4::new();
        assert_eq!(sig.hashed_payload(),
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_headers() {
        let h = Header{ key: "Xyz".to_string(), value: "1".to_string() };
        let h2 = Header{ key: "Abc".to_string(), value: "2".to_string() };
        let h3 = Header{ key: "Mno".to_string(), value: "3".to_string() };
        let h4 = Header{ key: "Authorization".to_string(), value: "4".to_string() };
        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:2\nmno:3\nxyz:1\n")
    }

    #[test]
    fn test_prune_whitespace() {
        let h = Header{ key: "Abc".to_string(), value: "a  b  c".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:a b c\n")
    }

    #[test]
    fn test_no_prune_quoted() {
        let h = Header{ key: "Abc".to_string(), value: "\"a  b  c\"".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:\"a  b  c\"\n")
    }

    #[test]
    fn test_specific_date() {
        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date();
        assert_eq!(sig.headers.get("x-amz-date"), wrap_header!("20110909T233600Z"))
    }

    #[test]
    fn test_empty_canonical_request() {
        let sig = SigV4::new();
        assert_eq!(sig.canonical_request().as_slice(), "\n\n\n\n\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
        }.date().header(h).header(h2);

        assert_eq!(sig.canonical_request().as_slice(), r"POST
/

content-type:application/x-www-form-urlencoded; charset=utf-8
host:iam.amazonaws.com
x-amz-date:20110909T233600Z

content-type;host;x-amz-date
b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

}
<commit_msg>add region and service<commit_after>use time::now_utc;
use time::Tm;
use std::ascii::AsciiExt;
use openssl::crypto::hash::Hasher;
use openssl::crypto::hash::HashType::SHA256;
use serialize::hex::ToHex;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use request::Header;
use url::percent_encoding::{percent_encode_to, FORM_URLENCODED_ENCODE_SET};

struct QP<'a> {
    k: &'a str,
    v: &'a str,
}

#[derive(Clone)]
pub struct SigV4 {
    date: Tm,
    headers: BTreeMap<String, Vec<String>>,
    method: Option<String>,
    path: Option<String>,
    payload: Option<String>,
    query: Option<String>,
    region: Option<String>,
    service: Option<String>,
}

impl SigV4 {
    pub fn new() -> SigV4{
        let dt = now_utc();
        SigV4 {
            date: dt,
            headers: BTreeMap::new(),
            method: None,
            path: None,
            payload: None,
            query: None,
            region: None,
            service: None,
        }
    }

    pub fn header(mut self, header: Header) -> SigV4 {
        append_header(&mut self.headers, header.key.as_slice(), header.value.as_slice());
        self
    }

    pub fn date(mut self) -> SigV4 {
        append_header(&mut self.headers, "X-Amz-Date",
                      self.date.strftime("%Y%m%dT%H%M%SZ").unwrap().to_string().as_slice());
        self
    }

    pub fn path(mut self, path: String) -> SigV4 {
        self.path = Some(path);
        self
    }

    pub fn method(mut self, method: String) -> SigV4 {
        self.method = Some(method);
        self
    }

    pub fn query(mut self, query: String) -> SigV4 {
        self.query = Some(query);
        self
    }

    pub fn payload(mut self, payload: String) -> SigV4 {
        self.payload = Some(payload);
        self
    }

    pub fn signature(self) -> String {
        self.hashed_canonical_request()
    }

    fn hashed_canonical_request(self) -> String {
        let mut h = Hasher::new(SHA256);
        h.update(self.canonical_request().as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn hashed_payload(&self) -> String {
        let val = match self.payload {
            Some(ref x) => x.to_string(),
            None => "".to_string(),
        };
        let mut h = Hasher::new(SHA256);
        h.update(val.as_bytes());
        h.finalize().as_slice().to_hex().to_string()
    }

    fn signed_headers(&self) -> String {
        let mut h = String::new();

        for (key,_) in self.headers.iter() {
            if h.len() > 0 {
                h.push(';')
            }
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(key.as_slice());
        }
        h
    }

    fn canonical_headers(&self) -> String {
        let mut h = String::new();

        for (key,value) in self.headers.iter() {
            if key.as_slice() == "authorization" {
                continue;
            }
            h.push_str(format!("{}:{}\n", key, canonical_value(value)).as_slice());
        }
        h
    }

    fn canonical_query_string(&self) -> String {
        match self.query {
            None => String::new(),
            Some(ref x) => {
                let mut h: Vec<QP> = Vec::new();
                for q in x.split('&') {
                    if q.contains_char('=') {
                        let n: Vec<&str> = q.splitn(1, '=').collect();
                        h.push(QP{k: n[0], v: n[1]})
                    } else {
                        h.push(QP{k: q, v: ""})
                    }
                };
                sort_query_string(h)
            }
        }
    }

    fn canonical_request(&self) -> String {
        format!("{}\n{}\n{}\n{}\n{}\n{}", expand_string(&self.method),
                expand_string(&self.path),
                self.canonical_query_string(),
                self.canonical_headers(),
                self.signed_headers(),
                self.hashed_payload()
        )
    }

}

fn sort_query_string(mut query: Vec<QP>) -> String {
    #[inline]
    fn byte_serialize(input: &str, output: &mut String) {
        for &byte in input.as_bytes().iter() {
            percent_encode_to(&[byte], FORM_URLENCODED_ENCODE_SET, output)
        }
    }

    let mut output = String::new();

    query.sort_by( |a, b| a.k.cmp(b.k));
    for item in query.iter() {
        if output.len() > 0 {
            output.push_str("&");
        }
        byte_serialize(item.k, &mut output);
        output.push_str("=");
        byte_serialize(item.v, &mut output);
    }

    output
    // it would be marvelous to use the below, but the AWS SigV4 spec says space must be %20, and
    // rust-url gives me back a +. So, roll our own for now.
    // let qs: Vec<(String, String)> = query.iter().map(|n| (n.k.to_string(), n.v.to_string())).collect();
    // form_urlencoded::serialize_owned(qs.as_slice())
}

fn append_header(map: &mut BTreeMap<String, Vec<String>>, key: &str, value: &str) {
    let k = key.to_ascii_lowercase().to_string();

    match map.entry(k) {
        Entry::Vacant(entry) => {
            let mut values = Vec::new();
            values.push(value.to_string());
            entry.set(values);
        },
        Entry::Occupied(entry) => {
            entry.into_mut().push(value.to_string());
        }
    };
}

fn expand_string(val: &Option<String>) -> String {
    match *val {
        None => "".to_string(),
        Some(ref x) => x.to_string(),
    }
}

fn canonical_value(val: &Vec<String>) -> String {
    let mut st = String::new();
    for v in val.iter() {
        if st.len() > 0 {
            st.push(',')
        }
        if v.starts_with("\""){
            st.push_str(v.as_slice());
        } else {
            st.push_str(v.replace("  ", " ").trim());
        }
    }
    st
}

#[cfg(test)]
mod tests {
    use super::SigV4;
    use request::Header;
    use time::strptime;
    use std::collections::BTreeMap;

    macro_rules! wrap_header (
        ($key:expr) => (
            Some(&vec!($key.to_string()))
        )
    );

    #[test]
    fn test_new_sigv4() {
        let sig = SigV4::new();
        assert_eq!(sig.headers.len(), 0)
    }

    #[test]
    fn test_date() {
        let sig = SigV4::new().date();
        assert!(sig.headers.contains_key("x-amz-date"))
    }

    #[test]
    fn test_add_header() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};

        let sig = SigV4::new().header(h);
        assert_eq!(sig.headers.get("test"), wrap_header!("a string"))
    }

    #[test]
    fn test_add_second_value() {
        let h = Header{ key: "test".to_string(), value: "a string".to_string()};
        let h2 = Header{ key: "test".to_string(), value: "another string".to_string()};

        let sig = SigV4::new().header(h).header(h2);
        assert_eq!(sig.headers.get("test"), Some(&vec!("a string".to_string(), "another string".to_string())))
    }

    #[test]
    fn test_canonical_query_encoded() {
        let sig = SigV4::new().query("a space=woo woo&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "a%20space=woo%20woo&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_valueless() {
        let sig = SigV4::new().query("other=&test&x-amz-header=foo".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "other=&test=&x-amz-header=foo")
    }

    #[test]
    fn test_canonical_query_sorted() {
        let sig = SigV4::new().query("foo=&bar=&baz=".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "bar=&baz=&foo=")
    }

    // Ensure that params with the same name stay in the same order after sorting
    #[test]
    fn test_canonical_query_complex() {
        let sig = SigV4::new().query("q.options=abc&q=xyz&q=mno".to_string());
        assert_eq!(sig.canonical_query_string().as_slice(), "q=xyz&q=mno&q.options=abc")
    }

    #[test]
    fn test_hashed_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
            region: None,
            service: None,
        }.date().header(h).header(h2);

        assert_eq!(sig.hashed_canonical_request().as_slice(), "3511de7e95d28ecd39e9513b642aee07e54f4941150d8df8bf94b328ef7e55e2")
    }

    #[test]
    fn test_signed_headers() {
        let h = Header{ key: "test".to_string(),
            value: "a string".to_string()};
        let h2 = Header{ key: "Content-Type".to_string(),
            value: "application/x-www-form-urlencoded; charset=utf-8".to_string()};
        let h3 = Header{ key: "Authorization".to_string(),
            value: "none".to_string()};

        let sig = SigV4::new().date().header(h).header(h2).header(h3);
        assert_eq!(sig.signed_headers().as_slice(), "content-type;test;x-amz-date")
    }

    #[test]
    fn test_hashed_payload() {
        let sig = SigV4::new().
            payload("Action=ListUsers&Version=2010-05-08".to_string());
        assert_eq!(sig.hashed_payload(),
        "b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

    #[test]
    fn test_empty_payload() {
        let sig = SigV4::new();
        assert_eq!(sig.hashed_payload(),
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_headers() {
        let h = Header{ key: "Xyz".to_string(), value: "1".to_string() };
        let h2 = Header{ key: "Abc".to_string(), value: "2".to_string() };
        let h3 = Header{ key: "Mno".to_string(), value: "3".to_string() };
        let h4 = Header{ key: "Authorization".to_string(), value: "4".to_string() };
        let sig = SigV4::new().header(h).header(h2).header(h3).header(h4);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:2\nmno:3\nxyz:1\n")
    }

    #[test]
    fn test_prune_whitespace() {
        let h = Header{ key: "Abc".to_string(), value: "a  b  c".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:a b c\n")
    }

    #[test]
    fn test_no_prune_quoted() {
        let h = Header{ key: "Abc".to_string(), value: "\"a  b  c\"".to_string() };
        let sig = SigV4::new().header(h);
        assert_eq!(sig.canonical_headers().as_slice(), "abc:\"a  b  c\"\n")
    }

    #[test]
    fn test_specific_date() {
        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: None,
            method: None,
            query: None,
            payload: None,
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
            region: None,
            service: None,
        }.date();
        assert_eq!(sig.headers.get("x-amz-date"), wrap_header!("20110909T233600Z"))
    }

    #[test]
    fn test_empty_canonical_request() {
        let sig = SigV4::new();
        assert_eq!(sig.canonical_request().as_slice(), "\n\n\n\n\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
    }

    #[test]
    fn test_canonical_request() {
        let h = Header{ key: "Content-Type".to_string(), value: "application/x-www-form-urlencoded; charset=utf-8".to_string() };
        let h2 = Header{ key: "Host".to_string(), value: "iam.amazonaws.com".to_string() };

        let sig = SigV4 {
            headers: BTreeMap::new(),
            path: Some("/".to_string()),
            method: Some("POST".to_string()),
            query: None,
            payload: Some("Action=ListUsers&Version=2010-05-08".to_string()),
            date: strptime("20110909T233600Z", "%Y%m%dT%H%M%SZ").unwrap(),
            region: None,
            service: None,
        }.date().header(h).header(h2);

        assert_eq!(sig.canonical_request().as_slice(), r"POST
/

content-type:application/x-www-form-urlencoded; charset=utf-8
host:iam.amazonaws.com
x-amz-date:20110909T233600Z

content-type;host;x-amz-date
b6359072c78d70ebee1e81adcbab4f01bf2c23245fa365ef83fe8f1f955085e2")
    }

}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions, metadata};
use std::path::{Path, PathBuf};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, encode_into, decode_from};

use super::Error;
use super::database::Database;
use super::column::Column;
use super::rows::Rows;

use super::super::identifier::Identifier;

pub struct Table<'a> {
    database: &'a Database,
    name: Identifier,
    meta_data: TableMetadata
}

#[derive(Clone, RustcDecodable, RustcEncodable)]
struct TableMetadata {
    engine_id: u8,
    columns: Vec<Column>
}

/// Table representation
impl<'a> Table<'a> {
    /// Create table in database
    pub fn create<'b>(name: &str, engine_id: u8, columns: Vec<Column>, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("creating table: {}", name);

        // TODO: verify if name is a valid identifier

        let metadata = TableMetadata {
            engine_id: engine_id,
            columns: columns
        };

        let mut buf = try!(File::create(db.get_path().clone().join(&name).with_extension("tbl")));

        encode_into(&metadata, &mut buf, SizeLimit::Infinite).unwrap();

        Ok(Table {
            database: &db,
            name: try!(Identifier::new(name)),
            meta_data: metadata.clone()
        })
    }

    /// Load table from database
    pub fn load<'b>(name: &str, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("loading table: {}", &name);
        let path = db.get_path().clone().join(&name).with_extension("tbl");
        if !try!(metadata(&path)).is_dir()
        {
            let mut file = try!(OpenOptions::new()
                        .read(true)
                        .open(&path));

            let metadata: TableMetadata = try!(decode_from(&mut file, SizeLimit::Infinite));
            info!("loaded table: {}", &name);

            Ok(Table {
                database: db,
                name: try!(Identifier::new(name)),
                meta_data: metadata
            })
        }else {
            error!("could not load table: {} at {}", &name, &path.to_str().unwrap());
            Err(Error::LoadTable)
        }
    }

    // TODO: write table delete static function

    /// Returns a vector of the sizes of the columns
    pub fn get_cols_sizes(&self) -> Vec<u32> {
        let mut column_sizes = Vec::<u32>::new();

        for v in &self.meta_data.columns {
            column_sizes.push(v.size());
        }

        column_sizes
    }

    pub fn get_table_header_offset(&self) -> u32 {
        let bytes: Vec<u8> = encode(&self.meta_data, SizeLimit::Infinite).unwrap();
        bytes.len() as u32
    }

    pub fn get_cols_offset(&self) -> u32 {
        let mut size = 0;

        for v in &self.meta_data.columns {
            size += v.size();
        }

        size
    }

    /// Get full file path including filename and ext
    pub fn get_path(&self) -> PathBuf {
        self.database.get_path().clone().join(&self.name.get_name()).with_extension("tbl")
    }

    pub fn get_engine_id(&self) -> u8 {
        self.meta_data.engine_id
    }

}

#[cfg(test)]
mod test {
    use super::Table;
    use super::super::database::*;
    use std::fs::metadata;
    use super::super::column::Column;
    use super::super::types::Types;

    #[test]
    fn create_table() {
        let path = "/tmp/test1/";
        let name = "test_db3";
        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 0, columns, &db).unwrap();

        assert!(!metadata("/tmp/test1/test_db3/test_table1.tbl").unwrap().is_dir());
    }

    #[test]
    fn load_table() {
        let path = "/tmp/test1/";
        let name = "test_db4";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db2 = Database::create(&name, DatabaseConfig::new(&path));
        assert!(db2.is_ok());
        let db = db2.unwrap();
        let table2 = Table::create("test_table2", 34, columns, &db);
        assert!(table2.is_ok());
        let table = table2.unwrap();

       // assert!(!metadata("/tmp/test1/test_db3/test_table2.tbl").unwrap().is_dir());

        let table2 = Table::load("test_table1", &db).unwrap();

        assert_eq!(table.get_engine_id(), table2.get_engine_id());
        assert_eq!(&table.meta_data.columns, &table2.meta_data.columns);
    }

    #[test]
    fn table_offset() {
        let path = "/tmp/test1/";
        let name = "test_db5";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 34, columns, &db).unwrap();

        assert_eq!(table.get_table_header_offset(), 42u32);

    }

}
<commit_msg>fix for travis<commit_after>use std::fs::{File, OpenOptions, metadata};
use std::path::{Path, PathBuf};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, encode_into, decode_from};

use super::Error;
use super::database::Database;
use super::column::Column;
use super::rows::Rows;

use super::super::identifier::Identifier;

pub struct Table<'a> {
    database: &'a Database,
    name: Identifier,
    meta_data: TableMetadata
}

#[derive(Clone, RustcDecodable, RustcEncodable)]
struct TableMetadata {
    engine_id: u8,
    columns: Vec<Column>
}

/// Table representation
impl<'a> Table<'a> {
    /// Create table in database
    pub fn create<'b>(name: &str, engine_id: u8, columns: Vec<Column>, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("creating table: {}", name);

        // TODO: verify if name is a valid identifier

        let metadata = TableMetadata {
            engine_id: engine_id,
            columns: columns
        };

        let mut buf = try!(File::create(db.get_path().clone().join(&name).with_extension("tbl")));

        encode_into(&metadata, &mut buf, SizeLimit::Infinite).unwrap();

        Ok(Table {
            database: &db,
            name: try!(Identifier::new(name)),
            meta_data: metadata.clone()
        })
    }

    /// Load table from database
    pub fn load<'b>(name: &str, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("loading table: {}", &name);
        let path = db.get_path().clone().join(&name).with_extension("tbl");
        if !try!(metadata(&path)).is_dir()
        {
            let mut file = try!(OpenOptions::new()
                        .read(true)
                        .open(&path));

            let metadata: TableMetadata = try!(decode_from(&mut file, SizeLimit::Infinite));
            info!("loaded table: {}", &name);

            Ok(Table {
                database: db,
                name: try!(Identifier::new(name)),
                meta_data: metadata
            })
        }else {
            error!("could not load table: {} at {}", &name, &path.to_str().unwrap());
            Err(Error::LoadTable)
        }
    }

    // TODO: write table delete static function

    /// Returns a vector of the sizes of the columns
    pub fn get_cols_sizes(&self) -> Vec<u32> {
        let mut column_sizes = Vec::<u32>::new();

        for v in &self.meta_data.columns {
            column_sizes.push(v.size());
        }

        column_sizes
    }

    pub fn get_table_header_offset(&self) -> u32 {
        let bytes: Vec<u8> = encode(&self.meta_data, SizeLimit::Infinite).unwrap();
        bytes.len() as u32
    }

    pub fn get_cols_offset(&self) -> u32 {
        let mut size = 0;

        for v in &self.meta_data.columns {
            size += v.size();
        }

        size
    }

    /// Get full file path including filename and ext
    pub fn get_path(&self) -> PathBuf {
        self.database.get_path().clone().join(&self.name.get_name()).with_extension("tbl")
    }

    pub fn get_engine_id(&self) -> u8 {
        self.meta_data.engine_id
    }

}

#[cfg(test)]
mod test {
    use super::Table;
    use super::super::database::*;
    use std::fs::metadata;
    use super::super::column::Column;
    use super::super::types::Types;

    #[test]
    fn create_table() {
        let path = "/tmp/test1/";
        let name = "test_db3";
        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 0, columns, &db).unwrap();

        assert!(!metadata("/tmp/test1/test_db3/test_table1.tbl").unwrap().is_dir());
    }

    #[test]
    fn load_table() {
        let path = "/tmp/test1/";
        let name = "test_db4";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db2 = Database::create(&name, DatabaseConfig::new(&path));
        assert!(db2.is_ok());
        let db = db2.unwrap();
        let table3 = Table::create("test_table2", 34, columns, &db);
        assert!(table2.is_ok());
        let table = table3.unwrap();

       // assert!(!metadata("/tmp/test1/test_db3/test_table2.tbl").unwrap().is_dir());

        let table2 = Table::load("test_table2", &db).unwrap();

        assert_eq!(table.get_engine_id(), table2.get_engine_id());
        assert_eq!(&table.meta_data.columns, &table2.meta_data.columns);
    }

    #[test]
    fn table_offset() {
        let path = "/tmp/test1/";
        let name = "test_db5";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 34, columns, &db).unwrap();

        assert_eq!(table.get_table_header_offset(), 42u32);

    }

}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use std::os::unix::io::{RawFd, AsRawFd};
use std::io::{Read, Write, Error, ErrorKind};

use libc;
use errno::errno;
use libc::{c_int, c_void};

#[derive(Clone)]
pub struct Socket {
    /// This socket's file descriptor
    pub fd: c_int,
}

impl Socket {

    pub fn set_tcp_keepalive(&self, keepalive: bool) {
        let optval: c_int = match keepalive {
            true => 1,
            false => 0
        };
        let opt_result = libc::setsockopt(self.fd,
                                          libc::SOL_SOCKET,
                                          libc::SO_KEEPALIVE,
                                          &optval as *const _ as *const c_void,
                                          mem::size_of::<c_int>() as u32);
        if opt_result < 0 {
            error!("Setting SO_KEEPALIVE: {}",
                   Error::from_raw_os_error(errno().0 as i32));
        }
    }

    pub fn set_tcp_nodelay(&self, nodelay: bool) {
        let optval: c_int = match nodelay {
            true => 1,
            false => 0
        };
        let opt_result = libc::setsockopt(self.fd,
                                          libc::SOL_TCP,
                                          libc::SO_KEEPALIVE,
                                          &optval as *const _ as *const c_void,
                                          mem::size_of::<c_int>() as u32);
        if opt_result < 0 {
            error!("Setting TCP_NODELAY: {}",
                   Error::from_raw_os_error(errno().0 as i32));
        }
    }
}

impl Read for Socket {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        if buf.len() < 1 {
            return Err(Error::new(ErrorKind::Other, "Invalid buffer"));
        }

        let result = unsafe { libc::read(self.fd, buf as *mut _ as *mut c_void, buf.len()) };

        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32));
        }

        if result == 0 {
            return Err(Error::new(ErrorKind::Other, "EOF"));
        }

        Ok(result as usize)
    }
}

impl Write for Socket {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
        let result = unsafe { libc::write(self.fd, buf as *const _ as *const c_void, buf.len()) };

        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32));
        }

        Ok(result as usize)
    }

    fn flush(&mut self) -> Result<(), Error> {
        Ok(())
    }
}

impl AsRawFd for Socket {
    fn as_raw_fd(&self) -> RawFd {
        self.fd
    }
}
<commit_msg>Remote debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use std::mem;
use std::os::unix::io::{RawFd, AsRawFd};
use std::io::{Read, Write, Error, ErrorKind};

use libc;
use errno::errno;
use libc::{c_int, c_void};

#[derive(Clone)]
pub struct Socket {
    /// This socket's file descriptor
    pub fd: c_int,
}

impl Socket {

    pub fn set_tcp_keepalive(&self, keepalive: bool) {
        let optval: c_int = match keepalive {
            true => 1,
            false => 0
        };
        let opt_result = libc::setsockopt(self.fd,
                                          libc::SOL_SOCKET,
                                          libc::SO_KEEPALIVE,
                                          &optval as *const _ as *const c_void,
                                          mem::size_of::<c_int>() as u32);
        if opt_result < 0 {
            error!("Setting SO_KEEPALIVE: {}",
                   Error::from_raw_os_error(errno().0 as i32));
        }
    }

    pub fn set_tcp_nodelay(&self, nodelay: bool) {
        let optval: c_int = match nodelay {
            true => 1,
            false => 0
        };
        let opt_result = libc::setsockopt(self.fd,
                                          libc::SOL_TCP,
                                          libc::SO_KEEPALIVE,
                                          &optval as *const _ as *const c_void,
                                          mem::size_of::<c_int>() as u32);
        if opt_result < 0 {
            error!("Setting TCP_NODELAY: {}",
                   Error::from_raw_os_error(errno().0 as i32));
        }
    }
}

impl Read for Socket {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        if buf.len() < 1 {
            return Err(Error::new(ErrorKind::Other, "Invalid buffer"));
        }

        let result = unsafe { libc::read(self.fd, buf as *mut _ as *mut c_void, buf.len()) };

        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32));
        }

        if result == 0 {
            return Err(Error::new(ErrorKind::Other, "EOF"));
        }

        Ok(result as usize)
    }
}

impl Write for Socket {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
        let result = unsafe { libc::write(self.fd, buf as *const _ as *const c_void, buf.len()) };

        if result < 0 {
            return Err(Error::from_raw_os_error(errno().0 as i32));
        }

        Ok(result as usize)
    }

    fn flush(&mut self) -> Result<(), Error> {
        Ok(())
    }
}

impl AsRawFd for Socket {
    fn as_raw_fd(&self) -> RawFd {
        self.fd
    }
}
<|endoftext|>


<commit_before>// while a WIP
#![allow(unused_variables)]
use rutorch::*;
use std::ops::{Index, IndexMut};
use std::convert::From;
use std::cmp::max;
use std::hash::{Hash, Hasher};

use storage::*;
use ::*;
pub use tensor::tensor_ops::*;
use rand;
use {Ixs, RcMut};

pub enum TensorType {
    Float,
    Double,
    Byte,
    Char,
    Short,
    Int,
    Long,
}

#[derive(Hash)]
pub enum TensorKind {
    FloatTensor(Tensor<f32>),
    LongTensor(Tensor<i64>),
}
pub enum NumKind {
    Float(f32),
    Double(f64),
    Long(i64),
}

impl NumKind {
    pub fn intof32(&self) -> f32 {
        use self::NumKind::{Float, Double, Long};
        match *self {
            Float(v) => v,
            Double(v) => unimplemented!(),
            Long(v) => unimplemented!(),
        }
    }
    pub fn intoi64(&self) -> i64 {
        use self::NumKind::{Float, Double, Long};
        match *self {
            Float(v) => unimplemented!(),
            Double(v) => unimplemented!(),
            Long(v) => v,
        }
    }
}

impl<T: Copy> From<T> for NumKind {
    #[allow(unused_variables)]
    default fn from(input: T) -> Self {
        unreachable!()
    }
}
impl From<f32> for NumKind {
    fn from(input: f32) -> Self {
        NumKind::Float(input)
    }
}
impl From<f64> for NumKind {
    fn from(input: f64) -> Self {
        NumKind::Double(input)
    }
}
impl From<i64> for NumKind {
    fn from(input: i64) -> Self {
        NumKind::Long(input)
    }
}

pub type TensorList<T> = Vec<Tensor<T>>;
pub type TensorKindList = Vec<TensorKind>;
pub type RefTensorList<'a, T> = Vec<&'a mut Tensor<T>>;
pub type RefTensorKindList<'a> = Vec<&'a TensorKind>;
pub type TensorId = usize;

impl TensorKind {
    pub fn new(&self) -> Self {
        unimplemented!()
    }
    pub fn in_thft(&self) -> *mut THFloatTensor {
        unimplemented!();
    }
    pub fn in_thlt(&self) -> *mut THLongTensor {
        unimplemented!();
    }
    //    pub fn backend(&self) -> &
}

impl Index<usize> for TensorKind {
    type Output = NumKind;
    fn index(&self, idx: usize) -> &Self::Output {
        unimplemented!()
    }
}
impl PartialEq for TensorKind {
    fn eq(&self, other: &Self) -> bool {
        use self::TensorKind::{FloatTensor, LongTensor};
        match (self, other) {
            (&FloatTensor(ref t1), &FloatTensor(ref t2)) => t1.id() == t2.id(),
            (&LongTensor(ref t1), &LongTensor(ref t2)) => t1.id() == t2.id(),
            _ => false,
        }
    }
}
impl Eq for TensorKind {}
impl Clone for TensorKind {
    fn clone(&self) -> Self {
        use self::TensorKind::{FloatTensor, LongTensor};
        match *self {
            FloatTensor(ref t) => FloatTensor(t.clone()),
            LongTensor(ref t) => LongTensor(t.clone()),
        }
    }
}

impl<T> From<Tensor<T>> for TensorKind {
    #[allow(unused_variables)]
    default fn from(input: Tensor<T>) -> Self {
        unreachable!()
    }
}

impl From<Tensor<f32>> for TensorKind {
    fn from(input: Tensor<f32>) -> Self {
        TensorKind::FloatTensor(input)
    }
}

impl From<Tensor<i64>> for TensorKind {
    fn from(input: Tensor<i64>) -> Self {
        TensorKind::LongTensor(input)
    }
}

impl<T> From<TensorKind> for Tensor<T> {
    #[allow(unused_variables)]
    default fn from(input: TensorKind) -> Self {
        panic!("bad cast")
    }
}

impl<'a, T> From<&'a TensorKind> for &'a Tensor<T> {
    #[allow(unused_variables)]
    default fn from(input: &'a TensorKind) -> Self {
        panic!("bad cast")
    }
}

impl<'a> From<&'a TensorKind> for &'a Tensor<f32> {
    fn from(input: &'a TensorKind) -> Self {
        match *input {
            TensorKind::FloatTensor(ref t) => t,
            _ => unreachable!(),
        }
    }
}

impl<'a> From<&'a TensorKind> for &'a Tensor<i64> {
    fn from(input: &'a TensorKind) -> Self {
        match *input {
            TensorKind::LongTensor(ref t) => t,
            _ => unreachable!(),
        }
    }
}

impl From<TensorKind> for Tensor<f32> {
    fn from(input: TensorKind) -> Self {
        match input {
            TensorKind::FloatTensor(v) => v,
            _ => unimplemented!(),
        }
    }
}

impl From<TensorKind> for Tensor<i64> {
    fn from(input: TensorKind) -> Self {
        match input {
            TensorKind::LongTensor(v) => v,
            _ => unimplemented!(),
        }
    }
}
pub struct Tensor<T> {
    pub value: RcMut<TensorImpl<T, Output = T>>,
}
impl<T: Copy> Hash for Tensor<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id().hash(state)
    }
}

impl<T: Copy> Index<usize> for Tensor<T> {
    type Output = T;
    fn index(&self, idx: usize) -> &Self::Output {

        //        self.value.borrow_mut().index(idx as isize)
        unimplemented!()
    }
}

impl<T: Copy> Index<i32> for Tensor<T> {
    type Output = T;
    fn index(&self, idx: i32) -> &Self::Output {

        //        self.value.borrow_mut().index(idx as isize)
        unimplemented!()
    }
}

pub trait New<D, T> {
    fn new(args: D) -> T;
    fn new_(&self, args: D) -> T;
}

impl<T> New<usize, Tensor<T>> for Tensor<T> {
    fn new(args: usize) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: usize) -> Self {
        unimplemented!()
    }
}
impl<T> New<Vec<usize>, Tensor<T>> for Tensor<T> {
    fn new(args: Vec<usize>) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: Vec<usize>) -> Self {
        unimplemented!()
    }
}

impl<T> Tensor<T> {
    pub fn len(&self) -> usize {
        unimplemented!()
    }
    pub fn s(&self, dim: usize) -> Self {
        unimplemented!()
    }
}

impl<T> Default for Tensor<T> {
    fn default() -> Self {
        unimplemented!()
    }
}

impl<T> Clone for Tensor<T> {
    fn clone(&self) -> Self {
        Tensor { value: self.value.clone() }
    }
}

impl<T: Copy> Index<isize> for Tensor<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

type RefTI<T> = RcMut<TensorImpl<T, Output = T>>;
type TIArg<T> = TensorImpl<T, Output = T>;
pub trait TensorImpl<T>: Index<Ixs, Output = T> {
    //fn view<'a>(&self, dims: &[i32]) -> Tensor<'a>;
    fn new(&self) -> RefTI<T>;
    fn add(&self, value: T, output: &TIArg<T>);
    fn inner(&self) -> *mut ::std::os::raw::c_void;
}

impl TensorImpl<f32> for FloatTensor {
    fn new(&self) -> RefTI<f32> {
        // XXX place holder implementation
        RcMutNew(FloatTensor::new())
    }
    fn add(&self, value: f32, output: &TIArg<f32>) {
        let out = typecast!(output.inner(), THFloatTensor);
        unsafe {
            THFloatTensor_add(out, self.t, value);
        };
    }
    fn inner(&self) -> *mut ::std::os::raw::c_void {
        self.t as *mut ::std::os::raw::c_void
    }
}

impl Index<isize> for FloatTensor {
    type Output = f32;
    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

pub struct FloatTensor {
    t: *mut THFloatTensor,
    storage: FloatStorage,
    dims: Vec<isize>,
}

impl Default for FloatTensor {
    fn default() -> Self {
        FloatTensor::new()
    }
}

impl FloatTensor {
    pub fn new() -> Self {
        unsafe {
            FloatTensor {
                t: THFloatTensor_new(),
                storage: FloatStorage::new(),
                dims: Vec::new(),
            }
        }
    }
    pub fn with_capacity(dims: &[isize]) -> Self {
        let size = dims.iter().product();
        let storage = FloatStorage::with_capacity(size);
        let strides = vec![1; dims.len()];
        let mut t = THFloatTensor {
            size: dims.clone().as_ptr() as *mut ::std::os::raw::c_long,
            stride: strides.as_ptr() as *mut ::std::os::raw::c_long,
            nDimension: dims.len() as i32,
            storage: storage.t,
            storageOffset: 0,
            refcount: 1,
            flag: TH_TENSOR_REFCOUNTED as i8,
        };
        FloatTensor {
            t: &mut t,
            storage: storage,
            dims: Vec::from(dims),
        }
    }
    pub fn randn(dims: &[isize]) -> Self {
        /* XXX */
        let mut t = FloatTensor::with_capacity(dims);
        for x in t.storage.iter_mut() {
            *x = rand::random::<f32>()
        }
        t
    }
}

impl<'a> Index<&'a [isize]> for FloatTensor {
    type Output = f32;

    fn index(&self, idx: &'a [isize]) -> &Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &self.storage[index]
    }
}

impl<'a> IndexMut<&'a [isize]> for FloatTensor {
    fn index_mut(&mut self, idx: &'a [isize]) -> &mut Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &mut self.storage[index]
    }
}
<commit_msg>add polymorphic new to TensorKind, add addt to impl<commit_after>// while a WIP
#![allow(unused_variables)]
use rutorch::*;
use std::ops::{Index, IndexMut};
use std::convert::From;
use std::cmp::max;
use std::hash::{Hash, Hasher};

use storage::*;
use ::*;
pub use tensor::tensor_ops::*;
use rand;
use {Ixs, RcMut};

pub enum TensorType {
    Float,
    Double,
    Byte,
    Char,
    Short,
    Int,
    Long,
}

#[derive(Hash)]
pub enum TensorKind {
    FloatTensor(Tensor<f32>),
    LongTensor(Tensor<i64>),
}
pub enum NumKind {
    Float(f32),
    Double(f64),
    Long(i64),
}

impl NumKind {
    pub fn intof32(&self) -> f32 {
        use self::NumKind::{Float, Double, Long};
        match *self {
            Float(v) => v,
            Double(v) => unimplemented!(),
            Long(v) => unimplemented!(),
        }
    }
    pub fn intoi64(&self) -> i64 {
        use self::NumKind::{Float, Double, Long};
        match *self {
            Float(v) => unimplemented!(),
            Double(v) => unimplemented!(),
            Long(v) => v,
        }
    }
}

impl<T: Copy> From<T> for NumKind {
    #[allow(unused_variables)]
    default fn from(input: T) -> Self {
        unreachable!()
    }
}
impl From<f32> for NumKind {
    fn from(input: f32) -> Self {
        NumKind::Float(input)
    }
}
impl From<f64> for NumKind {
    fn from(input: f64) -> Self {
        NumKind::Double(input)
    }
}
impl From<i64> for NumKind {
    fn from(input: i64) -> Self {
        NumKind::Long(input)
    }
}

pub type TensorList<T> = Vec<Tensor<T>>;
pub type TensorKindList = Vec<TensorKind>;
pub type RefTensorList<'a, T> = Vec<&'a mut Tensor<T>>;
pub type RefTensorKindList<'a> = Vec<&'a TensorKind>;
pub type TensorId = usize;

impl TensorKind {
    pub fn in_thft(&self) -> *mut THFloatTensor {
        unimplemented!();
    }
    pub fn in_thlt(&self) -> *mut THLongTensor {
        unimplemented!();
    }
    //    pub fn backend(&self) -> &
}

impl Index<usize> for TensorKind {
    type Output = NumKind;
    fn index(&self, idx: usize) -> &Self::Output {
        unimplemented!()
    }
}
impl PartialEq for TensorKind {
    fn eq(&self, other: &Self) -> bool {
        use self::TensorKind::{FloatTensor, LongTensor};
        match (self, other) {
            (&FloatTensor(ref t1), &FloatTensor(ref t2)) => t1.id() == t2.id(),
            (&LongTensor(ref t1), &LongTensor(ref t2)) => t1.id() == t2.id(),
            _ => false,
        }
    }
}
impl Eq for TensorKind {}
impl Clone for TensorKind {
    fn clone(&self) -> Self {
        use self::TensorKind::{FloatTensor, LongTensor};
        match *self {
            FloatTensor(ref t) => FloatTensor(t.clone()),
            LongTensor(ref t) => LongTensor(t.clone()),
        }
    }
}

impl<T> From<Tensor<T>> for TensorKind {
    #[allow(unused_variables)]
    default fn from(input: Tensor<T>) -> Self {
        unreachable!()
    }
}

impl From<Tensor<f32>> for TensorKind {
    fn from(input: Tensor<f32>) -> Self {
        TensorKind::FloatTensor(input)
    }
}

impl From<Tensor<i64>> for TensorKind {
    fn from(input: Tensor<i64>) -> Self {
        TensorKind::LongTensor(input)
    }
}

impl<T> From<TensorKind> for Tensor<T> {
    #[allow(unused_variables)]
    default fn from(input: TensorKind) -> Self {
        panic!("bad cast")
    }
}

impl<'a, T> From<&'a TensorKind> for &'a Tensor<T> {
    #[allow(unused_variables)]
    default fn from(input: &'a TensorKind) -> Self {
        panic!("bad cast")
    }
}

impl<'a> From<&'a TensorKind> for &'a Tensor<f32> {
    fn from(input: &'a TensorKind) -> Self {
        match *input {
            TensorKind::FloatTensor(ref t) => t,
            _ => unreachable!(),
        }
    }
}

impl<'a> From<&'a TensorKind> for &'a Tensor<i64> {
    fn from(input: &'a TensorKind) -> Self {
        match *input {
            TensorKind::LongTensor(ref t) => t,
            _ => unreachable!(),
        }
    }
}

impl From<TensorKind> for Tensor<f32> {
    fn from(input: TensorKind) -> Self {
        match input {
            TensorKind::FloatTensor(v) => v,
            _ => unimplemented!(),
        }
    }
}

impl From<TensorKind> for Tensor<i64> {
    fn from(input: TensorKind) -> Self {
        match input {
            TensorKind::LongTensor(v) => v,
            _ => unimplemented!(),
        }
    }
}
pub struct Tensor<T> {
    pub value: RcMut<TensorImpl<T, Output = T>>,
}
impl<T: Copy> Hash for Tensor<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.id().hash(state)
    }
}

impl<T: Copy> Index<usize> for Tensor<T> {
    type Output = T;
    fn index(&self, idx: usize) -> &Self::Output {

        //        self.value.borrow_mut().index(idx as isize)
        unimplemented!()
    }
}

impl<T: Copy> Index<i32> for Tensor<T> {
    type Output = T;
    fn index(&self, idx: i32) -> &Self::Output {

        //        self.value.borrow_mut().index(idx as isize)
        unimplemented!()
    }
}

pub trait New<D, T> {
    fn new(args: D) -> T;
    fn new_(&self, args: D) -> T;
}

impl<T> New<usize, Tensor<T>> for Tensor<T> {
    fn new(args: usize) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: usize) -> Self {
        unimplemented!()
    }
}
impl<T> New<Vec<usize>, Tensor<T>> for Tensor<T> {
    fn new(args: Vec<usize>) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: Vec<usize>) -> Self {
        unimplemented!()
    }
}

impl New<usize, TensorKind> for TensorKind {
    fn new(args: usize) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: usize) -> Self {
        unimplemented!()
    }
}
impl New<(), TensorKind> for TensorKind {
    fn new(args: ()) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: ()) -> Self {
        unimplemented!()
    }
}
impl New<Vec<usize>, TensorKind> for TensorKind {
    fn new(args: Vec<usize>) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: Vec<usize>) -> Self {
        unimplemented!()
    }
}
impl New<[usize; 2], TensorKind> for TensorKind {
    fn new(args: [usize; 2]) -> Self {
        unimplemented!()
    }
    fn new_(&self, args: [usize; 2]) -> Self {
        unimplemented!()
    }
}

impl<T> Tensor<T> {
    pub fn len(&self) -> usize {
        unimplemented!()
    }
    pub fn s(&self, dim: usize) -> Self {
        unimplemented!()
    }
}

impl<T> Default for Tensor<T> {
    fn default() -> Self {
        unimplemented!()
    }
}

impl<T> Clone for Tensor<T> {
    fn clone(&self) -> Self {
        Tensor { value: self.value.clone() }
    }
}

impl<T: Copy> Index<isize> for Tensor<T> {
    type Output = T;

    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

type RefTI<T> = RcMut<TensorImpl<T, Output = T>>;
pub type TIArg<T> = TensorImpl<T, Output = T>;
pub trait TensorImpl<T>: Index<Ixs, Output = T> {
    //fn view<'a>(&self, dims: &[i32]) -> Tensor<'a>;
    fn new(&self) -> RefTI<T>;
    fn add(&self, value: T, output: &TIArg<T>);
    fn addt(&self, value: T, rhs: &TIArg<T>, output: &TIArg<T>);
    fn inner(&self) -> *mut ::std::os::raw::c_void;
}

impl TensorImpl<f32> for FloatTensor {
    fn new(&self) -> RefTI<f32> {
        // XXX place holder implementation
        RcMutNew(FloatTensor::new())
    }
    fn add(&self, value: f32, output: &TIArg<f32>) {
        let out = typecast!(output.inner(), THFloatTensor);
        unsafe {
            THFloatTensor_add(out, self.t, value);
        };
    }
    fn inner(&self) -> *mut ::std::os::raw::c_void {
        self.t as *mut ::std::os::raw::c_void
    }
    fn addt(&self, value: f32, rhs: &TIArg<f32>, output: &TIArg<f32>) {
        let rhsp = typecast!(rhs.inner(), THFloatTensor);
    }
}

impl Index<isize> for FloatTensor {
    type Output = f32;
    fn index(&self, idx: isize) -> &Self::Output {
        unimplemented!()
    }
}

pub struct FloatTensor {
    t: *mut THFloatTensor,
    storage: FloatStorage,
    dims: Vec<isize>,
}

impl Default for FloatTensor {
    fn default() -> Self {
        FloatTensor::new()
    }
}

impl FloatTensor {
    pub fn new() -> Self {
        unsafe {
            FloatTensor {
                t: THFloatTensor_new(),
                storage: FloatStorage::new(),
                dims: Vec::new(),
            }
        }
    }
    pub fn with_capacity(dims: &[isize]) -> Self {
        let size = dims.iter().product();
        let storage = FloatStorage::with_capacity(size);
        let strides = vec![1; dims.len()];
        let mut t = THFloatTensor {
            size: dims.clone().as_ptr() as *mut ::std::os::raw::c_long,
            stride: strides.as_ptr() as *mut ::std::os::raw::c_long,
            nDimension: dims.len() as i32,
            storage: storage.t,
            storageOffset: 0,
            refcount: 1,
            flag: TH_TENSOR_REFCOUNTED as i8,
        };
        FloatTensor {
            t: &mut t,
            storage: storage,
            dims: Vec::from(dims),
        }
    }
    pub fn randn(dims: &[isize]) -> Self {
        /* XXX */
        let mut t = FloatTensor::with_capacity(dims);
        for x in t.storage.iter_mut() {
            *x = rand::random::<f32>()
        }
        t
    }
}

impl<'a> Index<&'a [isize]> for FloatTensor {
    type Output = f32;

    fn index(&self, idx: &'a [isize]) -> &Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &self.storage[index]
    }
}

impl<'a> IndexMut<&'a [isize]> for FloatTensor {
    fn index_mut(&mut self, idx: &'a [isize]) -> &mut Self::Output {
        let mut index: isize = 0;
        let lastidx = max(0, idx.len() as isize - 1) as usize;
        if idx.len() != self.dims.len() {
            panic!("bad dimlen")
        }
        for i in 0..lastidx {
            if idx[i] >= self.dims[i] {
                panic!("bad dimlen")
            }
            index += idx[i] * self.dims[i]
        }
        if idx[lastidx] >= self.dims[lastidx] {
            panic!("bad dimlen")
        }
        index += idx[lastidx];
        &mut self.storage[index]
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_traits::{BluetoothError, BluetoothRequest};
use bluetooth_traits::{BluetoothResponse, BluetoothResponseListener, BluetoothResponseResult};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
use bluetooth_traits::scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
use core::clone::Clone;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothBinding::{self, BluetoothMethods, BluetoothRequestDeviceFilter};
use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::error::Error::{self, NotFound, Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::{JSAutoCompartment, JSContext};
use network_listener::{NetworkListener, PreInvoke};
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

const FILTER_EMPTY_ERROR: &'static str = "'filters' member, if present, must be nonempty to find any devices.";
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
// 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name.
const MAX_DEVICE_NAME_LENGTH: usize = 248;
// A device name can never be longer than 29 bytes.
// An advertising packet is at most 31 bytes long.
// The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_FILTER_NAME_LENGTH: usize = 29;
const NAME_PREFIX_ERROR: &'static str = "'namePrefix', if present, must be nonempty.";
const NAME_TOO_LONG_ERROR: &'static str = "A device name can't be longer than 248 bytes.";
const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service.";
const OPTIONS_ERROR: &'static str = "Fields of 'options' conflict with each other.
 Either 'acceptAllDevices' member must be true, or 'filters' member must be set to a value.";

struct BluetoothContext<T: AsyncBluetoothListener + Reflectable> {
    promise: Option<TrustedPromise>,
    receiver: Trusted<T>,
}

pub trait AsyncBluetoothListener {
    fn handle_response(&self, result: BluetoothResponse, cx: *mut JSContext, promise: &Rc<Promise>);
}

impl<Listener: AsyncBluetoothListener + Reflectable> PreInvoke for BluetoothContext<Listener> {}

impl<Listener: AsyncBluetoothListener + Reflectable> BluetoothResponseListener for BluetoothContext<Listener> {
    #[allow(unrooted_must_root)]
    fn response(&mut self, response: BluetoothResponseResult) {
        let promise = self.promise.take().expect("bt promise is missing").root();
        let promise_cx = promise.global().get_cx();

        // JSAutoCompartment needs to be manually made.
        // Otherwise, Servo will crash.
        let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
        match response {
            Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise),
            Err(error) => promise.reject_error(promise_cx, Error::from(error)),
        }
    }
}

// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
#[dom_struct]
pub struct Bluetooth {
    reflector_: Reflector,
    device_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothDevice>>>>,
    service_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTService>>>>,
    characteristic_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTCharacteristic>>>>,
    descriptor_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTDescriptor>>>>,
}

impl Bluetooth {
    pub fn new_inherited() -> Bluetooth {
        Bluetooth {
            reflector_: Reflector::new(),
            device_instance_map: DOMRefCell::new(HashMap::new()),
            service_instance_map: DOMRefCell::new(HashMap::new()),
            characteristic_instance_map: DOMRefCell::new(HashMap::new()),
            descriptor_instance_map: DOMRefCell::new(HashMap::new()),
        }
    }

    pub fn new(global: &GlobalScope) -> Root<Bluetooth> {
        reflect_dom_object(box Bluetooth::new_inherited(),
                           global,
                           BluetoothBinding::Wrap)
    }

    pub fn get_service_map(&self) -> &DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTService>>>> {
        &self.service_instance_map
    }

    pub fn get_characteristic_map(&self)
            -> &DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTCharacteristic>>>> {
        &self.characteristic_instance_map
    }

    pub fn get_descriptor_map(&self) -> &DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTDescriptor>>>> {
        &self.descriptor_instance_map
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
        self.global().as_window().bluetooth_thread()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
    fn request_bluetooth_devices(&self,
                                 p: &Rc<Promise>,
                                 filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
                                 optional_services: &Option<Vec<BluetoothServiceUUID>>) {
        // TODO: Step 1: Triggered by user activation.

        // Step 2.
        let option = match convert_request_device_options(filters, optional_services) {
            Ok(o) => o,
            Err(e) => {
                p.reject_error(p.global().get_cx(), e);
                return;
            }
        };

        // TODO: Step 3-5: Implement the permission API.

        // Note: Steps 6-8 are implemented in
        // components/net/bluetooth_thread.rs in request_device function.
        let sender = response_async(p, self);
        self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap();
    }
}

pub fn response_async<T: AsyncBluetoothListener + Reflectable + 'static>(
        promise: &Rc<Promise>,
        receiver: &T) -> IpcSender<BluetoothResponseResult> {
    let (action_sender, action_receiver) = ipc::channel().unwrap();
    let task_source = receiver.global().networking_task_source();
    let context = Arc::new(Mutex::new(BluetoothContext {
        promise: Some(TrustedPromise::new(promise.clone())),
        receiver: Trusted::new(receiver),
    }));
    let listener = NetworkListener {
        context: context,
        task_source: task_source,
        wrapper: None,
    };
    ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
        listener.notify_response(message.to().unwrap());
    });
    action_sender
}

// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn convert_request_device_options(filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
                                  optional_services: &Option<Vec<BluetoothServiceUUID>>)
                                  -> Fallible<RequestDeviceoptions> {
    // Step 2.2: There is no requiredServiceUUIDS, we scan for all devices.
    let mut uuid_filters = vec!();

    if let &Some(ref filters) = filters {
        // Step 2.1.
        if filters.is_empty()  {
            return Err(Type(FILTER_EMPTY_ERROR.to_owned()));
        }

        // Step 2.3: There is no requiredServiceUUIDS, we scan for all devices.

        // Step 2.4.
        for filter in filters {
            // Step 2.4.8.
            uuid_filters.push(try!(canonicalize_filter(&filter)));
        }
    }

    let mut optional_services_uuids = vec!();
    if let &Some(ref opt_services) = optional_services {
        for opt_service in opt_services {
            // Step 2.5 - 2.6.
            let uuid = try!(BluetoothUUID::service(opt_service.clone())).to_string();

            // Step 2.7.
            // Note: What we are doing here is adding the not blacklisted UUIDs to the result vector,
            // insted of removing them from an already filled vector.
            if !uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                optional_services_uuids.push(uuid);
            }
        }
    }

    Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters),
                                 ServiceUUIDSequence::new(optional_services_uuids)))
}

// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter) -> Fallible<BluetoothScanfilter> {
    // Step 2.4.1.
    if filter.services.is_none() &&
       filter.name.is_none() &&
       filter.namePrefix.is_none() &&
       filter.manufacturerId.is_none() &&
       filter.serviceDataUUID.is_none() {
           return Err(Type(FILTER_ERROR.to_owned()));
    }

    // Step 2.4.2: There is no empty canonicalizedFilter member,
    // we create a BluetoothScanfilter instance at the end of the function.

    // Step 2.4.3.
    let services_vec = match filter.services {
        Some(ref services) => {
            // Step 2.4.3.1.
            if services.is_empty() {
                return Err(Type(SERVICE_ERROR.to_owned()));
            }

            let mut services_vec = vec!();

            for service in services {
                // Step 2.4.3.2 - 2.4.3.3.
                let uuid = try!(BluetoothUUID::service(service.clone())).to_string();

                // Step 2.4.3.4.
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }

                services_vec.push(uuid);
            }
            // Step 2.4.3.5.
            services_vec
            // Step 2.4.3.6: There is no requiredServiceUUIDS, we scan for all devices.
        },
        None => vec!(),
    };

    // Step 2.4.4.
    let name = match filter.name {
        Some(ref name) => {
            // Step 2.4.4.1.
            // Note: DOMString::len() gives back the size in bytes.
            if name.len() > MAX_DEVICE_NAME_LENGTH {
                return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
            }
            if name.len() > MAX_FILTER_NAME_LENGTH {
                return Err(NotFound);
            }

            // Step 2.4.4.2.
            Some(name.to_string())
        },
        None => None,
    };

    // Step 2.4.5.
    let name_prefix = match filter.namePrefix {
        Some(ref name_prefix) => {
            // Step 2.4.5.1.
            if name_prefix.is_empty() {
                return Err(Type(NAME_PREFIX_ERROR.to_owned()));
            }
            if name_prefix.len() > MAX_DEVICE_NAME_LENGTH {
                return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
            }
            if name_prefix.len() > MAX_FILTER_NAME_LENGTH {
                return Err(NotFound);
            }

            // Step 2.4.5.2.
            name_prefix.to_string()
        },
        None => String::new(),
    };

    // Step 2.4.6.
    let manufacturer_id = filter.manufacturerId;

    // Step 2.4.7.
    let service_data_uuid = match filter.serviceDataUUID {
        Some(ref service_data_uuid) => {
            // Step 2.4.7.1 - 2.4.7.2.
            let uuid = try!(BluetoothUUID::service(service_data_uuid.clone())).to_string();

            // Step 2.4.7.3.
            if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                return Err(Security)
            }

            // Step 2.4.7.4.
            uuid
        },
        None => String::new(),
    };

    Ok(BluetoothScanfilter::new(name,
                                name_prefix,
                                services_vec,
                                manufacturer_id,
                                service_data_uuid))
}

impl From<BluetoothError> for Error {
    fn from(error: BluetoothError) -> Self {
        match error {
            BluetoothError::Type(message) => Error::Type(message),
            BluetoothError::Network => Error::Network,
            BluetoothError::NotFound => Error::NotFound,
            BluetoothError::NotSupported => Error::NotSupported,
            BluetoothError::Security => Error::Security,
            BluetoothError::InvalidState => Error::InvalidState,
        }
    }
}

impl BluetoothMethods for Bluetooth {
    #[allow(unrooted_must_root)]
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
    fn RequestDevice(&self, option: &RequestDeviceOptions) -> Rc<Promise> {
        let p = Promise::new(&self.global());
        // Step 1.
        if (option.filters.is_some() && option.acceptAllDevices) ||
           (option.filters.is_none() && !option.acceptAllDevices) {
            p.reject_error(p.global().get_cx(), Error::Type(OPTIONS_ERROR.to_owned()));
            return p;
        }
        // Step 2.
        if !option.acceptAllDevices {
            self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices);
        } else {
            self.request_bluetooth_devices(&p, &None, &option.optionalServices);
        }
        // TODO(#4282): Step 3-5: Reject and resolve promise.
        return p;
    }
}

impl AsyncBluetoothListener for Bluetooth {
    fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
        match response {
            BluetoothResponse::RequestDevice(device) => {
                let mut device_instance_map = self.device_instance_map.borrow_mut();
                if let Some(existing_device) = device_instance_map.get(&device.id.clone()) {
                    return promise.resolve_native(promise_cx, &existing_device.get());
                }
                let ad_data = BluetoothAdvertisingData::new(&self.global(),
                                                            device.appearance,
                                                            device.tx_power,
                                                            device.rssi);
                let bt_device = BluetoothDevice::new(&self.global(),
                                                     DOMString::from(device.id.clone()),
                                                     device.name.map(DOMString::from),
                                                     &ad_data,
                                                     &self);
                device_instance_map.insert(device.id, MutHeap::new(&bt_device));
                promise.resolve_native(promise_cx, &bt_device);
            },
            _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
        }
    }
}
<commit_msg>Fix a typo in dom/bluetooth.rs<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use bluetooth_traits::{BluetoothError, BluetoothRequest};
use bluetooth_traits::{BluetoothResponse, BluetoothResponseListener, BluetoothResponseResult};
use bluetooth_traits::blacklist::{Blacklist, uuid_is_blacklisted};
use bluetooth_traits::scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence};
use bluetooth_traits::scanfilter::{RequestDeviceoptions, ServiceUUIDSequence};
use core::clone::Clone;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BluetoothBinding::{self, BluetoothMethods, BluetoothRequestDeviceFilter};
use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions;
use dom::bindings::error::Error::{self, NotFound, Security, Type};
use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, MutHeap, Root};
use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::bluetoothadvertisingdata::BluetoothAdvertisingData;
use dom::bluetoothdevice::BluetoothDevice;
use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
use dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID};
use dom::globalscope::GlobalScope;
use dom::promise::Promise;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::jsapi::{JSAutoCompartment, JSContext};
use network_listener::{NetworkListener, PreInvoke};
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

const FILTER_EMPTY_ERROR: &'static str = "'filters' member, if present, must be nonempty to find any devices.";
const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
// 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name.
const MAX_DEVICE_NAME_LENGTH: usize = 248;
// A device name can never be longer than 29 bytes.
// An advertising packet is at most 31 bytes long.
// The length and identifier of the length field take 2 bytes.
// That leaves 29 bytes for the name.
const MAX_FILTER_NAME_LENGTH: usize = 29;
const NAME_PREFIX_ERROR: &'static str = "'namePrefix', if present, must be nonempty.";
const NAME_TOO_LONG_ERROR: &'static str = "A device name can't be longer than 248 bytes.";
const SERVICE_ERROR: &'static str = "'services', if present, must contain at least one service.";
const OPTIONS_ERROR: &'static str = "Fields of 'options' conflict with each other.
 Either 'acceptAllDevices' member must be true, or 'filters' member must be set to a value.";

struct BluetoothContext<T: AsyncBluetoothListener + Reflectable> {
    promise: Option<TrustedPromise>,
    receiver: Trusted<T>,
}

pub trait AsyncBluetoothListener {
    fn handle_response(&self, result: BluetoothResponse, cx: *mut JSContext, promise: &Rc<Promise>);
}

impl<Listener: AsyncBluetoothListener + Reflectable> PreInvoke for BluetoothContext<Listener> {}

impl<Listener: AsyncBluetoothListener + Reflectable> BluetoothResponseListener for BluetoothContext<Listener> {
    #[allow(unrooted_must_root)]
    fn response(&mut self, response: BluetoothResponseResult) {
        let promise = self.promise.take().expect("bt promise is missing").root();
        let promise_cx = promise.global().get_cx();

        // JSAutoCompartment needs to be manually made.
        // Otherwise, Servo will crash.
        let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
        match response {
            Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise),
            Err(error) => promise.reject_error(promise_cx, Error::from(error)),
        }
    }
}

// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth
#[dom_struct]
pub struct Bluetooth {
    reflector_: Reflector,
    device_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothDevice>>>>,
    service_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTService>>>>,
    characteristic_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTCharacteristic>>>>,
    descriptor_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTDescriptor>>>>,
}

impl Bluetooth {
    pub fn new_inherited() -> Bluetooth {
        Bluetooth {
            reflector_: Reflector::new(),
            device_instance_map: DOMRefCell::new(HashMap::new()),
            service_instance_map: DOMRefCell::new(HashMap::new()),
            characteristic_instance_map: DOMRefCell::new(HashMap::new()),
            descriptor_instance_map: DOMRefCell::new(HashMap::new()),
        }
    }

    pub fn new(global: &GlobalScope) -> Root<Bluetooth> {
        reflect_dom_object(box Bluetooth::new_inherited(),
                           global,
                           BluetoothBinding::Wrap)
    }

    pub fn get_service_map(&self) -> &DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTService>>>> {
        &self.service_instance_map
    }

    pub fn get_characteristic_map(&self)
            -> &DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTCharacteristic>>>> {
        &self.characteristic_instance_map
    }

    pub fn get_descriptor_map(&self) -> &DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTDescriptor>>>> {
        &self.descriptor_instance_map
    }

    fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
        self.global().as_window().bluetooth_thread()
    }

    // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
    fn request_bluetooth_devices(&self,
                                 p: &Rc<Promise>,
                                 filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
                                 optional_services: &Option<Vec<BluetoothServiceUUID>>) {
        // TODO: Step 1: Triggered by user activation.

        // Step 2.
        let option = match convert_request_device_options(filters, optional_services) {
            Ok(o) => o,
            Err(e) => {
                p.reject_error(p.global().get_cx(), e);
                return;
            }
        };

        // TODO: Step 3-5: Implement the permission API.

        // Note: Steps 6-8 are implemented in
        // components/net/bluetooth_thread.rs in request_device function.
        let sender = response_async(p, self);
        self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap();
    }
}

pub fn response_async<T: AsyncBluetoothListener + Reflectable + 'static>(
        promise: &Rc<Promise>,
        receiver: &T) -> IpcSender<BluetoothResponseResult> {
    let (action_sender, action_receiver) = ipc::channel().unwrap();
    let task_source = receiver.global().networking_task_source();
    let context = Arc::new(Mutex::new(BluetoothContext {
        promise: Some(TrustedPromise::new(promise.clone())),
        receiver: Trusted::new(receiver),
    }));
    let listener = NetworkListener {
        context: context,
        task_source: task_source,
        wrapper: None,
    };
    ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
        listener.notify_response(message.to().unwrap());
    });
    action_sender
}

// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn convert_request_device_options(filters: &Option<Vec<BluetoothRequestDeviceFilter>>,
                                  optional_services: &Option<Vec<BluetoothServiceUUID>>)
                                  -> Fallible<RequestDeviceoptions> {
    // Step 2.2: There is no requiredServiceUUIDS, we scan for all devices.
    let mut uuid_filters = vec!();

    if let &Some(ref filters) = filters {
        // Step 2.1.
        if filters.is_empty()  {
            return Err(Type(FILTER_EMPTY_ERROR.to_owned()));
        }

        // Step 2.3: There is no requiredServiceUUIDS, we scan for all devices.

        // Step 2.4.
        for filter in filters {
            // Step 2.4.8.
            uuid_filters.push(try!(canonicalize_filter(&filter)));
        }
    }

    let mut optional_services_uuids = vec!();
    if let &Some(ref opt_services) = optional_services {
        for opt_service in opt_services {
            // Step 2.5 - 2.6.
            let uuid = try!(BluetoothUUID::service(opt_service.clone())).to_string();

            // Step 2.7.
            // Note: What we are doing here is adding the not blacklisted UUIDs to the result vector,
            // instead of removing them from an already filled vector.
            if !uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                optional_services_uuids.push(uuid);
            }
        }
    }

    Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters),
                                 ServiceUUIDSequence::new(optional_services_uuids)))
}

// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices
fn canonicalize_filter(filter: &BluetoothRequestDeviceFilter) -> Fallible<BluetoothScanfilter> {
    // Step 2.4.1.
    if filter.services.is_none() &&
       filter.name.is_none() &&
       filter.namePrefix.is_none() &&
       filter.manufacturerId.is_none() &&
       filter.serviceDataUUID.is_none() {
           return Err(Type(FILTER_ERROR.to_owned()));
    }

    // Step 2.4.2: There is no empty canonicalizedFilter member,
    // we create a BluetoothScanfilter instance at the end of the function.

    // Step 2.4.3.
    let services_vec = match filter.services {
        Some(ref services) => {
            // Step 2.4.3.1.
            if services.is_empty() {
                return Err(Type(SERVICE_ERROR.to_owned()));
            }

            let mut services_vec = vec!();

            for service in services {
                // Step 2.4.3.2 - 2.4.3.3.
                let uuid = try!(BluetoothUUID::service(service.clone())).to_string();

                // Step 2.4.3.4.
                if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                    return Err(Security)
                }

                services_vec.push(uuid);
            }
            // Step 2.4.3.5.
            services_vec
            // Step 2.4.3.6: There is no requiredServiceUUIDS, we scan for all devices.
        },
        None => vec!(),
    };

    // Step 2.4.4.
    let name = match filter.name {
        Some(ref name) => {
            // Step 2.4.4.1.
            // Note: DOMString::len() gives back the size in bytes.
            if name.len() > MAX_DEVICE_NAME_LENGTH {
                return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
            }
            if name.len() > MAX_FILTER_NAME_LENGTH {
                return Err(NotFound);
            }

            // Step 2.4.4.2.
            Some(name.to_string())
        },
        None => None,
    };

    // Step 2.4.5.
    let name_prefix = match filter.namePrefix {
        Some(ref name_prefix) => {
            // Step 2.4.5.1.
            if name_prefix.is_empty() {
                return Err(Type(NAME_PREFIX_ERROR.to_owned()));
            }
            if name_prefix.len() > MAX_DEVICE_NAME_LENGTH {
                return Err(Type(NAME_TOO_LONG_ERROR.to_owned()));
            }
            if name_prefix.len() > MAX_FILTER_NAME_LENGTH {
                return Err(NotFound);
            }

            // Step 2.4.5.2.
            name_prefix.to_string()
        },
        None => String::new(),
    };

    // Step 2.4.6.
    let manufacturer_id = filter.manufacturerId;

    // Step 2.4.7.
    let service_data_uuid = match filter.serviceDataUUID {
        Some(ref service_data_uuid) => {
            // Step 2.4.7.1 - 2.4.7.2.
            let uuid = try!(BluetoothUUID::service(service_data_uuid.clone())).to_string();

            // Step 2.4.7.3.
            if uuid_is_blacklisted(uuid.as_ref(), Blacklist::All) {
                return Err(Security)
            }

            // Step 2.4.7.4.
            uuid
        },
        None => String::new(),
    };

    Ok(BluetoothScanfilter::new(name,
                                name_prefix,
                                services_vec,
                                manufacturer_id,
                                service_data_uuid))
}

impl From<BluetoothError> for Error {
    fn from(error: BluetoothError) -> Self {
        match error {
            BluetoothError::Type(message) => Error::Type(message),
            BluetoothError::Network => Error::Network,
            BluetoothError::NotFound => Error::NotFound,
            BluetoothError::NotSupported => Error::NotSupported,
            BluetoothError::Security => Error::Security,
            BluetoothError::InvalidState => Error::InvalidState,
        }
    }
}

impl BluetoothMethods for Bluetooth {
    #[allow(unrooted_must_root)]
    // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice
    fn RequestDevice(&self, option: &RequestDeviceOptions) -> Rc<Promise> {
        let p = Promise::new(&self.global());
        // Step 1.
        if (option.filters.is_some() && option.acceptAllDevices) ||
           (option.filters.is_none() && !option.acceptAllDevices) {
            p.reject_error(p.global().get_cx(), Error::Type(OPTIONS_ERROR.to_owned()));
            return p;
        }
        // Step 2.
        if !option.acceptAllDevices {
            self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices);
        } else {
            self.request_bluetooth_devices(&p, &None, &option.optionalServices);
        }
        // TODO(#4282): Step 3-5: Reject and resolve promise.
        return p;
    }
}

impl AsyncBluetoothListener for Bluetooth {
    fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) {
        match response {
            BluetoothResponse::RequestDevice(device) => {
                let mut device_instance_map = self.device_instance_map.borrow_mut();
                if let Some(existing_device) = device_instance_map.get(&device.id.clone()) {
                    return promise.resolve_native(promise_cx, &existing_device.get());
                }
                let ad_data = BluetoothAdvertisingData::new(&self.global(),
                                                            device.appearance,
                                                            device.tx_power,
                                                            device.rssi);
                let bt_device = BluetoothDevice::new(&self.global(),
                                                     DOMString::from(device.id.clone()),
                                                     device.name.map(DOMString::from),
                                                     &ad_data,
                                                     &self);
                device_instance_map.insert(device.id, MutHeap::new(&bt_device));
                promise.resolve_native(promise_cx, &bt_device);
            },
            _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())),
        }
    }
}
<|endoftext|>