<commit_before>/// The Toml Set extensions

use toml::Value;

use tokenizer::tokenize_with_seperator;
use tokenizer::Token;
use error::*;

pub trait TomlValueSetExt {

    /// Extension function for setting a value in the current toml::Value document
    /// using a custom seperator
    ///
    /// # Semantics
    ///
    /// The function _never_ creates intermediate data structures (Tables or Arrays) in the
    /// document.
    ///
    /// # Return value
    ///
    /// * If the set operation worked correctly, `Ok(None)` is returned.
    /// * If the set operation replaced an existing value `Ok(Some(old_value))` is returned
    /// * On failure, `Err(e)` is returned:
    ///     * If the query is `"a.b.c"` but there is no table `"b"`: error
    ///     * If the query is `"a.b.[0]"` but "`b"` is not an array: error
    ///     * If the query is `"a.b.[3]"` but the array at "`b"` has no index `3`: error
    ///     * etc.
    ///
    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>>;

    /// Extension function for setting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueSetExt::set_with_seperator`
    fn set(&mut self, query: &String, value: Value) -> Result<Option<Value>> {
        self.set_with_seperator(query, '.', value)
    }

}

impl TomlValueSetExt for Value {

    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last = tokens.pop_last();

        let mut val = try!(resolve(self, &tokens));
        let last = last.unwrap_or_else(|| Box::new(tokens));

        match *last {
            Token::Identifier { ident, .. } => {
                match val {
                    &mut Value::Table(ref mut t) => {
                        Ok(t.insert(ident, value))
                    },
                    &mut Value::Array(_) => {
                        let kind = ErrorKind::NoIdentifierInArray(ident);
                        Err(Error::from(kind))
                    }
                    _ => unimplemented!()
                }
            }

            Token::Index { idx, .. } => {
                match val {
                    &mut Value::Array(ref mut a) => {
                        if a.len() > idx {
                            let result = a.swap_remove(idx);
                            a.insert(idx, value);
                            Ok(Some(result))
                        } else {
                            a.push(value);
                            Ok(None)
                        }
                    }
                    _ => unimplemented!()
                }
            }

        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_set_with_seperator_into_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_key_nonexistent() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());
        let res = res.unwrap();

        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = [ 0 ]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_index_nonexistent() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nested_table() {
        let mut toml : Value = toml_from_str(r#"
        [a.b.c]
        d = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let a = t.get("a");
                assert!(a.is_some());

                let a = a.unwrap();
                assert!(is_match!(a, &Value::Table(_)));
                match a {
                    &Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, &Value::Table(_)));
                        match b_tab {
                            &Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, &Value::Table(_)));
                                match c_tab {
                                    &Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, &Value::Integer(1)));
                                    },
                                    _ => panic!("What just happenend?"),
                                }
                            },
                            _ => panic!("What just happenend?"),
                        }
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_table() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::IdentifierNotFoundInDocument(_)));
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_array() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::NoIndexInTable(0)));
    }

    #[test]
    fn test_set_with_seperator_ident_into_ary() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 0 ]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.foo"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res.kind(), &ErrorKind::NoIdentifierInArray(_)));
    }

    #[test]
    fn test_set_with_seperator_index_into_table() {
        let mut toml : Value = toml_from_str(r#"
        foo = { bar = 1 }
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res.kind(), &ErrorKind::NoIndexInTable(_)));
    }

}

<commit_msg>Impl Value::set_with_seperator to fix test_set_with_seperator_index_into_table<commit_after>/// The Toml Set extensions

use toml::Value;

use tokenizer::tokenize_with_seperator;
use tokenizer::Token;
use error::*;

pub trait TomlValueSetExt {

    /// Extension function for setting a value in the current toml::Value document
    /// using a custom seperator
    ///
    /// # Semantics
    ///
    /// The function _never_ creates intermediate data structures (Tables or Arrays) in the
    /// document.
    ///
    /// # Return value
    ///
    /// * If the set operation worked correctly, `Ok(None)` is returned.
    /// * If the set operation replaced an existing value `Ok(Some(old_value))` is returned
    /// * On failure, `Err(e)` is returned:
    ///     * If the query is `"a.b.c"` but there is no table `"b"`: error
    ///     * If the query is `"a.b.[0]"` but "`b"` is not an array: error
    ///     * If the query is `"a.b.[3]"` but the array at "`b"` has no index `3`: error
    ///     * etc.
    ///
    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>>;

    /// Extension function for setting a value from the current toml::Value document
    ///
    /// See documentation of `TomlValueSetExt::set_with_seperator`
    fn set(&mut self, query: &String, value: Value) -> Result<Option<Value>> {
        self.set_with_seperator(query, '.', value)
    }

}

impl TomlValueSetExt for Value {

    fn set_with_seperator(&mut self, query: &String, sep: char, value: Value) -> Result<Option<Value>> {
        use resolver::mut_resolver::resolve;

        let mut tokens = try!(tokenize_with_seperator(query, sep));
        let last = tokens.pop_last();

        let mut val = try!(resolve(self, &tokens));
        let last = last.unwrap_or_else(|| Box::new(tokens));

        match *last {
            Token::Identifier { ident, .. } => {
                match val {
                    &mut Value::Table(ref mut t) => {
                        Ok(t.insert(ident, value))
                    },
                    &mut Value::Array(_) => {
                        let kind = ErrorKind::NoIdentifierInArray(ident);
                        Err(Error::from(kind))
                    }
                    _ => unimplemented!()
                }
            }

            Token::Index { idx, .. } => {
                match val {
                    &mut Value::Array(ref mut a) => {
                        if a.len() > idx {
                            let result = a.swap_remove(idx);
                            a.insert(idx, value);
                            Ok(Some(result))
                        } else {
                            a.push(value);
                            Ok(None)
                        }
                    }
                    &mut Value::Table(_) => {
                        let kind = ErrorKind::NoIndexInTable(idx);
                        Err(Error::from(kind))
                    }
                    _ => unimplemented!()
                }
            }

        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use toml::Value;
    use toml::from_str as toml_from_str;

    #[test]
    fn test_set_with_seperator_into_table() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        a = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_key_nonexistent() {
        let mut toml : Value = toml_from_str(r#"
        [table]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_ok());
        let res = res.unwrap();

        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("table");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Table(_)));
                match inner {
                    &Value::Table(ref t) => {
                        assert!(!t.is_empty());

                        let a = t.get("a");
                        assert!(a.is_some());

                        let a = a.unwrap();
                        assert!(is_match!(a, &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_array() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = [ 0 ]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_table_index_nonexistent() {
        use std::ops::Index;

        let mut toml : Value = toml_from_str(r#"
        array = []
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.[0]"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_none());

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let inner = t.get("array");
                assert!(inner.is_some());

                let inner = inner.unwrap();
                assert!(is_match!(inner, &Value::Array(_)));
                match inner {
                    &Value::Array(ref a) => {
                        assert!(!a.is_empty());
                        assert!(is_match!(a.index(0), &Value::Integer(1)));
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nested_table() {
        let mut toml : Value = toml_from_str(r#"
        [a.b.c]
        d = 0
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("a.b.c.d"), '.', Value::Integer(1));

        assert!(res.is_ok());

        let res = res.unwrap();
        assert!(res.is_some());
        let res = res.unwrap();
        assert!(is_match!(res, Value::Integer(0)));

        assert!(is_match!(toml, Value::Table(_)));
        match toml {
            Value::Table(ref t) => {
                assert!(!t.is_empty());

                let a = t.get("a");
                assert!(a.is_some());

                let a = a.unwrap();
                assert!(is_match!(a, &Value::Table(_)));
                match a {
                    &Value::Table(ref a) => {
                        assert!(!a.is_empty());

                        let b_tab = a.get("b");
                        assert!(b_tab.is_some());

                        let b_tab = b_tab.unwrap();
                        assert!(is_match!(b_tab, &Value::Table(_)));
                        match b_tab {
                            &Value::Table(ref b) => {
                                assert!(!b.is_empty());

                                let c_tab = b.get("c");
                                assert!(c_tab.is_some());

                                let c_tab = c_tab.unwrap();
                                assert!(is_match!(c_tab, &Value::Table(_)));
                                match c_tab {
                                    &Value::Table(ref c) => {
                                        assert!(!c.is_empty());

                                        let d = c.get("d");
                                        assert!(d.is_some());

                                        let d = d.unwrap();
                                        assert!(is_match!(d, &Value::Integer(1)));
                                    },
                                    _ => panic!("What just happenend?"),
                                }
                            },
                            _ => panic!("What just happenend?"),
                        }
                    },
                    _ => panic!("What just happenend?"),
                }
            },
            _ => panic!("What just happenend?"),
        }
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_table() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("table.a"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::IdentifierNotFoundInDocument(_)));
    }

    #[test]
    fn test_set_with_seperator_into_nonexistent_array() {
        let mut toml : Value = toml_from_str("").unwrap();

        let res = toml.set_with_seperator(&String::from("[0]"), '.', Value::Integer(1));

        assert!(res.is_err());

        let res = res.unwrap_err();
        assert!(is_match!(res.kind(), &ErrorKind::NoIndexInTable(0)));
    }

    #[test]
    fn test_set_with_seperator_ident_into_ary() {
        let mut toml : Value = toml_from_str(r#"
        array = [ 0 ]
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("array.foo"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res.kind(), &ErrorKind::NoIdentifierInArray(_)));
    }

    #[test]
    fn test_set_with_seperator_index_into_table() {
        let mut toml : Value = toml_from_str(r#"
        foo = { bar = 1 }
        "#).unwrap();

        let res = toml.set_with_seperator(&String::from("foo.[0]"), '.', Value::Integer(2));

        assert!(res.is_err());
        let res = res.unwrap_err();

        assert!(is_match!(res.kind(), &ErrorKind::NoIndexInTable(_)));
    }

}

<|endoftext|>


<commit_before>use std;
use libc::size_t;
use mdb::types::MDB_val;

pub trait ToMdbValue {
    fn to_mdb_value(&self) -> MDB_val;
}

pub trait FromMdbValue {
    fn from_mdb_value(value: &MDB_val) -> Self;
}

impl ToMdbValue for Vec<u8> {
    fn to_mdb_value(&self) -> MDB_val {
        unsafe {
            MDB_val {
                mv_data: std::mem::transmute(self.as_ptr()),
                mv_size: self.len() as size_t
            }
        }
    }
}

impl FromMdbValue for Vec<u8> {
    fn from_mdb_value(value: &MDB_val) -> Vec<u8> {
        unsafe {
            std::vec::raw::from_buf(std::mem::transmute(value.mv_data), value.mv_size as uint)
        }
    }
}

impl ToMdbValue for String {
    fn to_mdb_value(&self) -> MDB_val {
        unsafe {
            let t = self.as_slice();
            MDB_val {
                mv_data: std::mem::transmute(t.as_ptr()),
                mv_size: t.len() as size_t
            }
        }
    }
}

impl<'a> ToMdbValue for &'a str {
    fn to_mdb_value(&self) -> MDB_val {
        unsafe {
            let t = self.as_slice();
            MDB_val {
                mv_data: std::mem::transmute(t.as_ptr()),
                mv_size: t.len() as size_t
            }
        }
    }
}

impl ToMdbValue for MDB_val {
    fn to_mdb_value(&self) -> MDB_val {
        MDB_val {
            mv_data: (*self).mv_data,
            mv_size: (*self).mv_size
        }
    }
}

impl FromMdbValue for String {
    fn from_mdb_value(value: &MDB_val) -> String {
        unsafe {
            std::str::raw::from_buf_len(std::mem::transmute(value.mv_data), value.mv_size as uint).to_string()
        }
    }
}

impl FromMdbValue for () {
    fn from_mdb_value(_: &MDB_val) {
    }
}

pub trait StateError {
    fn new_state_error(msg: String) -> Self;
}
<commit_msg>FromMdbValue for &str<commit_after>use std;
use libc::size_t;
use mdb::types::MDB_val;

pub trait ToMdbValue {
    fn to_mdb_value(&self) -> MDB_val;
}

pub trait FromMdbValue {
    fn from_mdb_value(value: &MDB_val) -> Self;
}

impl ToMdbValue for Vec<u8> {
    fn to_mdb_value(&self) -> MDB_val {
        unsafe {
            MDB_val {
                mv_data: std::mem::transmute(self.as_ptr()),
                mv_size: self.len() as size_t
            }
        }
    }
}

impl FromMdbValue for Vec<u8> {
    fn from_mdb_value(value: &MDB_val) -> Vec<u8> {
        unsafe {
            std::vec::raw::from_buf(std::mem::transmute(value.mv_data), value.mv_size as uint)
        }
    }
}

impl ToMdbValue for String {
    fn to_mdb_value(&self) -> MDB_val {
        unsafe {
            let t = self.as_slice();
            MDB_val {
                mv_data: std::mem::transmute(t.as_ptr()),
                mv_size: t.len() as size_t
            }
        }
    }
}

impl<'a> ToMdbValue for &'a str {
    fn to_mdb_value(&self) -> MDB_val {
        unsafe {
            let t = self.as_slice();
            MDB_val {
                mv_data: std::mem::transmute(t.as_ptr()),
                mv_size: t.len() as size_t
            }
        }
    }
}

impl ToMdbValue for MDB_val {
    fn to_mdb_value(&self) -> MDB_val {
        MDB_val {
            mv_data: (*self).mv_data,
            mv_size: (*self).mv_size
        }
    }
}

impl FromMdbValue for String {
    fn from_mdb_value(value: &MDB_val) -> String {
        unsafe {
            std::str::raw::from_buf_len(std::mem::transmute(value.mv_data), value.mv_size as uint).to_string()
        }
    }
}

impl FromMdbValue for () {
    fn from_mdb_value(_: &MDB_val) {
    }
}

impl<'a> FromMdbValue for &'a str {
    fn from_mdb_value(value: &MDB_val) -> &'a str {
        unsafe {
            std::mem::transmute(std::raw::Slice {
                data: value.mv_data,
                len: value.mv_size as uint,
            })
        }
    }
}

pub trait StateError {
    fn new_state_error(msg: String) -> Self;
}
<|endoftext|>


<commit_before>//! The traits defined here are implementation details of cell_gc.
//!
//! Application code does not need to use these traits. They are public only so
//! that `#[derive(IntoHeap)]` can use it. Use the friendly macro!

use gc_leaf::GcLeaf;
use gc_ref::GcRef;
use ptr::Pointer;
use std::hash::Hash;
use std::marker::PhantomData;

/// Base trait for values that can be moved into a GC heap.
pub trait IntoHeapBase: Sized {
    /// The type of the value when it is physically stored in the heap.
    ///
    /// GC types come in pairs: an `IntoHeap` type and an `In` type. Both
    /// types have the same layout, bit for bit.
    ///
    /// The `In` type is stored physically inside the heap, and may be packed
    /// with pointer fields and unsafe methods. The `IntoHeap` type is safe, is
    /// used in application code, and lives on the stack.
    ///
    /// *   For primitive types, like `i32`, the two types are the same.
    ///
    /// *   Macro-generated "Ref" types are `IntoHeap` types; the corresponding
    ///     in-heap types are effectively raw pointers.
    ///
    /// *   For user-defined structs and enums, the `#[derive(IntoHeap)]` macro
    ///     autogenerates an `In` type for you. It has identical fields or
    ///     variants, but fields of the `IntoHeap` type are `IntoHeap`, and
    ///     fields of the `In` type are the corresponding `In` types.
    ///
    /// (The word "safe" above means that, as with everything generally in Rust, you
    /// can hack without fear. `IntoHeap` types don't expose raw Rust pointers or
    /// references to GC memory, they don't expose the unsafe in-heap types, and
    /// they obey Rust's safety and aliasing rules.)
    ///
    /// Users never get a direct `&` reference to any in-heap value. All access is
    /// through safe `IntoHeap` types, like the `Ref` type that is automatically
    /// declared for you when you use `#[derive(IntoHeap)]` on a struct or union.
    type In: 'static;

    /// Convert the value to the form it should have in the heap.
    /// This is for cell-gc to call.
    ///
    /// This method must not be called while any direct Rust references to
    /// heap objects exist. (However, user code never runs while such
    /// references exist, so the method is not marked `unsafe`.)
    fn into_heap(self) -> Self::In;

    /// Extract the value from the heap. This turns any raw pointers in the
    /// in-heap value into safe references, so while it's an unsafe function,
    /// the result of a correct call can be safely handed out to user code.
    ///
    /// # Safety
    ///
    /// It is impossible for ordinary users to call this safely, because `self`
    /// must be a direct, unwrapped reference to a value stored in the GC heap,
    /// which ordinary users cannot obtain.
    unsafe fn from_heap(&Self::In) -> Self;

    /// Traverse a value in the heap.
    ///
    /// This calls `tracer.visit(ptr)` for each edge from this value
    /// to other heap values.
    ///
    /// For user-defined structs, this is achieved by calling the
    /// `field.trace(tracer)` method of each field of this value. (They are
    /// typically all `In` types.) The implementation of this method for
    /// `Pointer<T>` calls `tracer.visit()` (if the pointer is non-null?).
    ///
    /// # Safety
    ///
    /// It is impossible for ordinary users to call this safely, because `self`
    /// must be a direct, unwrapped reference to a value stored in the GC heap,
    /// which ordinary users cannot obtain.
    unsafe fn trace<R>(&Self::In, tracer: &mut R)
    where
        R: Tracer;
}

/// Trait for values that can be moved into a GC heap.
///
/// Cell-gc does not support GC allocation of arbitrary values: only values of
/// types that implement `IntoHeap`. This trait is **not** meant to be
/// implemented by hand: use `#[derive(IntoHeap)]` instead. All primitive types
/// and many standard types support `IntoHeap`.
///
/// That is all you need to know in order to use cell-gc. The rest is really
/// implementation detail.
///
/// Implementation note: every type with `'static` lifetime should be safe to
/// store in the heap, because ref types are never `'static`. Unfortunately
/// rustc cannot be made to understand this. It would be *legal* to write this:
///
/// ```ignore
/// unsafe impl<'h, T: Clone + 'static> IntoHeap<'h> for T { ...trivial... }
/// ```
///
/// but alas, Rust thinks this impl conflicts with almost all others, making it
/// impossible to get the macro to work. So instead we `impl IntoHeap for` a
/// lot of individual types by hand.
///
/// **Liveness.** Some `IntoHeap` types ("Ref" types) are effectively smart
/// pointers to in-heap values. To preserve safety, if an `IntoHeap` value is
/// (or contains) a smart pointer to an in-heap value, then that value (and
/// everything reachable from it) is protected from GC.
///
/// # Safety
///
/// Implementing `IntoHeap<'h>` tells cell-gc that a type integrates with
/// garbage collection. All the methods in the type's `IntoHeapBase` impl must
/// be correct or it will explode.
///
/// *   In-heap values are typically full of pointers; if `into_heap` puts
///     garbage into them, GC will crash.
///
/// *   `trace` must be implemented with care in order to preserve the
///     invariants of the GC graph-walking algorithm. Bugs there are very
///     likely to lead to dangling pointers and hard-to-debug crashes down the
///     road.
///
/// *   `GcHeap` implements `Send`. This means any value that can be stored in
///     the heap can be sent to other threads (by sending the whole heap). So
///     don't implement this trait for any type that isn't itself already
///     `Send`. (The reason this trait doesn't *require* `Send` is that at
///     present, `Ref` types must *not* be `Send` but must implement
///     `IntoHeap`.)  It's OK to store non-`Sync` values in the heap.
///
/// *   And this probably goes without saying, but none of these methods may
///     allocate or do anything else that could trigger garbage collection.
///
pub unsafe trait IntoHeap<'h>: IntoHeapBase {}

/// Types that can be allocated in the heap.
pub trait IntoHeapAllocation<'h>: IntoHeap<'h> {
    /// The safe reference type that's returned when a value of this type is
    /// moved into the heap (i.e. when it's allocated).
    type Ref: Hash + IntoHeap<'h>;

    fn wrap_gc_ref(gc_ref: GcRef<'h, Self>) -> Self::Ref;

    fn into_gc_ref(r: Self::Ref) -> GcRef<'h, Self>;
}

pub trait Tracer {
    fn visit<'h, T>(&mut self, Pointer<T::In>)
    where
        T: IntoHeapAllocation<'h>;
}

// === Provided implmentations for primitive types

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        impl IntoHeapBase for $t {
            type In = $t;
            fn into_heap(self) -> $t { self }
            unsafe fn from_heap(storage: &$t) -> $t { storage.clone() }
            unsafe fn trace<R>(_storage: &$t, _tracer: &mut R) where R: Tracer {}
        }
        unsafe impl<'h> IntoHeap<'h> for $t {}
        impl<'h> IntoHeapAllocation<'h> for $t {
            type Ref = GcRef<'h, Self>;
            fn wrap_gc_ref(gc_ref: GcRef<'h, Self>) -> Self::Ref { gc_ref }
            fn into_gc_ref(gc_ref: Self::Ref) -> GcRef<'h, Self> { gc_ref }
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

gc_trivial_impl!(String);

macro_rules! gc_generic_trivial_impl {
    (@as_item $it:item) => { $it };
    ([$($x:tt)*] $t:ty) => {
        gc_generic_trivial_impl! {
            @as_item
            impl<$($x)*> IntoHeapBase for $t {
                type In = $t;
                fn into_heap(self) -> $t { self }
                unsafe fn from_heap(storage: &$t) -> $t { (*storage).clone() }
                unsafe fn trace<R>(_storage: &$t, _tracer: &mut R) where R: Tracer {}
            }
        }
        gc_generic_trivial_impl! {
            @as_item
            unsafe impl<'h, $($x)*> IntoHeap<'h> for $t {}
        }
        gc_generic_trivial_impl! {
            @as_item
            impl<'h, $($x)*> IntoHeapAllocation<'h> for $t {
                type Ref = GcRef<'h, Self>;
                fn wrap_gc_ref(gc_ref: GcRef<'h, Self>) -> Self::Ref { gc_ref }
                fn into_gc_ref(gc_ref: Self::Ref) -> GcRef<'h, Self> { gc_ref }
            }
        }
    }
}

gc_generic_trivial_impl!([T: ?Sized + Sync] &'static T);
gc_generic_trivial_impl!([T: Clone + Send + 'static] GcLeaf<T>);
gc_generic_trivial_impl!([T: Clone + Send + 'static] Box<T>);
gc_generic_trivial_impl!([T: Clone + Sync + 'static] ::std::sync::Arc<T>);

/// Currently, `#[derive(IntoHeap)]` only works for types that have a lifetime
/// parameter.  This poses a problem because sometimes you want to store stuff
/// in the heap that doesn't contain any `GcRef`s or other heap lifetimes.
/// As a hackaround, we allow `PhantomData<&'h T>` fields (for now).
impl<'h, T: 'static> IntoHeapBase for PhantomData<&'h T> {
    type In = PhantomData<&'static T>;
    fn into_heap(self) -> Self::In { PhantomData }
    unsafe fn from_heap(_storage: &Self::In) -> Self { PhantomData }
    unsafe fn trace<R: Tracer>(_storage: &Self::In, _tracer: &mut R) {}
}

// GCRef has a special implementation.
impl<'h, T: IntoHeapAllocation<'h>> IntoHeapBase for GcRef<'h, T> {
    type In = Pointer<T::In>;

    fn into_heap(self) -> Self::In {
        self.ptr()
    }

    unsafe fn from_heap(storage: &Self::In) -> Self {
        Self::new(*storage)
    }

    unsafe fn trace<R: Tracer>(storage: &Self::In, tracer: &mut R) {
        tracer.visit::<T>(*storage);
    }
}

unsafe impl<'h, T: IntoHeapAllocation<'h>> IntoHeap<'h> for GcRef<'h, T> {}

// Transitive implementations ("this particular kind of struct/enum is IntoHeap
// if all its fields are") are slightly less trivial.

impl<T: IntoHeapBase> IntoHeapBase for Option<T> {
    type In = Option<T::In>;

    fn into_heap(self) -> Option<T::In> {
        self.map(|t| t.into_heap())
    }

    unsafe fn trace<R>(storage: &Option<T::In>, tracer: &mut R)
    where
        R: Tracer,
    {
        match storage {
            &None => (),
            &Some(ref u) => T::trace(u, tracer),
        }
    }

    unsafe fn from_heap(storage: &Option<T::In>) -> Option<T> {
        match storage {
            &None => None,
            &Some(ref u) => Some(T::from_heap(u)),
        }
    }
}

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Option<T> {}

macro_rules! gc_trivial_tuple_impl {
    (@as_item $it:item) => { $it };
    ($($t:ident),*) => {
        gc_trivial_tuple_impl! {
            @as_item
            impl<$($t: IntoHeapBase,)*> IntoHeapBase for ($($t,)*) {
                type In = ($($t::In,)*);

                #[allow(non_snake_case)]  // because we use the type names as variable names (!)
                fn into_heap(self) -> Self::In {
                    let ($($t,)*) = self;
                    ($($t.into_heap(),)*)
                }

                #[allow(non_snake_case)]
                unsafe fn trace<R>(storage: &Self::In, tracer: &mut R)
                    where R: Tracer
                {
                    let &($(ref $t,)*) = storage;

                    $(
                        <$t as $crate::traits::IntoHeapBase>::trace($t, tracer);
                    )*

                    // If the above `$(...)*` expansion is empty, we need this
                    // to quiet unused variable warnings for `tracer`.
                    let _ = tracer;
                }

                #[allow(non_snake_case)]
                unsafe fn from_heap(storage: &Self::In) -> Self {
                    let &($(ref $t,)*) = storage;
                    ($( <$t as $crate::traits::IntoHeapBase>::from_heap($t), )*)
                }
            }
        }

        gc_trivial_tuple_impl! {
            @as_item
            unsafe impl<'h, $($t: IntoHeap<'h>,)*> IntoHeap<'h> for ($($t,)*) {}
        }
    }
}

gc_trivial_tuple_impl!();
gc_trivial_tuple_impl!(T);
gc_trivial_tuple_impl!(T, U);
gc_trivial_tuple_impl!(T, U, V);
gc_trivial_tuple_impl!(T, U, V, W);
gc_trivial_tuple_impl!(T, U, V, W, X);
gc_trivial_tuple_impl!(T, U, V, W, X, Y);
gc_trivial_tuple_impl!(T, U, V, W, X, Y, Z);
<commit_msg>Fix a comment promising identical layout for T and T::In.<commit_after>//! The traits defined here are implementation details of cell_gc.
//!
//! Application code does not need to use these traits. They are public only so
//! that `#[derive(IntoHeap)]` can use it. Use the friendly macro!

use gc_leaf::GcLeaf;
use gc_ref::GcRef;
use ptr::Pointer;
use std::hash::Hash;
use std::marker::PhantomData;

/// Base trait for values that can be moved into a GC heap.
pub trait IntoHeapBase: Sized {
    /// The type of the value when it is physically stored in the heap.
    ///
    /// GC types come in pairs: an `IntoHeap` type and an `In` type. Ideally
    /// both types would have the same layout, bit for bit, but Rust makes no
    /// promises about struct and enum layout generally, so we can't guarantee
    /// this.
    ///
    /// The `In` type is stored physically inside the heap, and may be packed
    /// with pointer fields and unsafe methods. The `IntoHeap` type is safe, is
    /// used in application code, and lives on the stack.
    ///
    /// *   For primitive types, like `i32`, the two types are the same.
    ///
    /// *   Macro-generated "Ref" types are `IntoHeap` types; the corresponding
    ///     in-heap types are effectively raw pointers.
    ///
    /// *   For user-defined structs and enums, the `#[derive(IntoHeap)]` macro
    ///     autogenerates an `In` type for you. It has identical fields or
    ///     variants, but fields of the `IntoHeap` type are `IntoHeap`, and
    ///     fields of the `In` type are the corresponding `In` types.
    ///
    /// (The word "safe" above means that, as with everything generally in Rust, you
    /// can hack without fear. `IntoHeap` types don't expose raw Rust pointers or
    /// references to GC memory, they don't expose the unsafe in-heap types, and
    /// they obey Rust's safety and aliasing rules.)
    ///
    /// Users never get a direct `&` reference to any in-heap value. All access is
    /// through safe `IntoHeap` types, like the `Ref` type that is automatically
    /// declared for you when you use `#[derive(IntoHeap)]` on a struct or union.
    type In: 'static;

    /// Convert the value to the form it should have in the heap.
    /// This is for cell-gc to call.
    ///
    /// This method must not be called while any direct Rust references to
    /// heap objects exist. (However, user code never runs while such
    /// references exist, so the method is not marked `unsafe`.)
    fn into_heap(self) -> Self::In;

    /// Extract the value from the heap. This turns any raw pointers in the
    /// in-heap value into safe references, so while it's an unsafe function,
    /// the result of a correct call can be safely handed out to user code.
    ///
    /// # Safety
    ///
    /// It is impossible for ordinary users to call this safely, because `self`
    /// must be a direct, unwrapped reference to a value stored in the GC heap,
    /// which ordinary users cannot obtain.
    unsafe fn from_heap(&Self::In) -> Self;

    /// Traverse a value in the heap.
    ///
    /// This calls `tracer.visit(ptr)` for each edge from this value
    /// to other heap values.
    ///
    /// For user-defined structs, this is achieved by calling the
    /// `field.trace(tracer)` method of each field of this value. (They are
    /// typically all `In` types.) The implementation of this method for
    /// `Pointer<T>` calls `tracer.visit()` (if the pointer is non-null?).
    ///
    /// # Safety
    ///
    /// It is impossible for ordinary users to call this safely, because `self`
    /// must be a direct, unwrapped reference to a value stored in the GC heap,
    /// which ordinary users cannot obtain.
    unsafe fn trace<R>(&Self::In, tracer: &mut R)
    where
        R: Tracer;
}

/// Trait for values that can be moved into a GC heap.
///
/// Cell-gc does not support GC allocation of arbitrary values: only values of
/// types that implement `IntoHeap`. This trait is **not** meant to be
/// implemented by hand: use `#[derive(IntoHeap)]` instead. All primitive types
/// and many standard types support `IntoHeap`.
///
/// That is all you need to know in order to use cell-gc. The rest is really
/// implementation detail.
///
/// Implementation note: every type with `'static` lifetime should be safe to
/// store in the heap, because ref types are never `'static`. Unfortunately
/// rustc cannot be made to understand this. It would be *legal* to write this:
///
/// ```ignore
/// unsafe impl<'h, T: Clone + 'static> IntoHeap<'h> for T { ...trivial... }
/// ```
///
/// but alas, Rust thinks this impl conflicts with almost all others, making it
/// impossible to get the macro to work. So instead we `impl IntoHeap for` a
/// lot of individual types by hand.
///
/// **Liveness.** Some `IntoHeap` types ("Ref" types) are effectively smart
/// pointers to in-heap values. To preserve safety, if an `IntoHeap` value is
/// (or contains) a smart pointer to an in-heap value, then that value (and
/// everything reachable from it) is protected from GC.
///
/// # Safety
///
/// Implementing `IntoHeap<'h>` tells cell-gc that a type integrates with
/// garbage collection. All the methods in the type's `IntoHeapBase` impl must
/// be correct or it will explode.
///
/// *   In-heap values are typically full of pointers; if `into_heap` puts
///     garbage into them, GC will crash.
///
/// *   `trace` must be implemented with care in order to preserve the
///     invariants of the GC graph-walking algorithm. Bugs there are very
///     likely to lead to dangling pointers and hard-to-debug crashes down the
///     road.
///
/// *   `GcHeap` implements `Send`. This means any value that can be stored in
///     the heap can be sent to other threads (by sending the whole heap). So
///     don't implement this trait for any type that isn't itself already
///     `Send`. (The reason this trait doesn't *require* `Send` is that at
///     present, `Ref` types must *not* be `Send` but must implement
///     `IntoHeap`.)  It's OK to store non-`Sync` values in the heap.
///
/// *   And this probably goes without saying, but none of these methods may
///     allocate or do anything else that could trigger garbage collection.
///
pub unsafe trait IntoHeap<'h>: IntoHeapBase {}

/// Types that can be allocated in the heap.
pub trait IntoHeapAllocation<'h>: IntoHeap<'h> {
    /// The safe reference type that's returned when a value of this type is
    /// moved into the heap (i.e. when it's allocated).
    type Ref: Hash + IntoHeap<'h>;

    fn wrap_gc_ref(gc_ref: GcRef<'h, Self>) -> Self::Ref;

    fn into_gc_ref(r: Self::Ref) -> GcRef<'h, Self>;
}

pub trait Tracer {
    fn visit<'h, T>(&mut self, Pointer<T::In>)
    where
        T: IntoHeapAllocation<'h>;
}

// === Provided implmentations for primitive types

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        impl IntoHeapBase for $t {
            type In = $t;
            fn into_heap(self) -> $t { self }
            unsafe fn from_heap(storage: &$t) -> $t { storage.clone() }
            unsafe fn trace<R>(_storage: &$t, _tracer: &mut R) where R: Tracer {}
        }
        unsafe impl<'h> IntoHeap<'h> for $t {}
        impl<'h> IntoHeapAllocation<'h> for $t {
            type Ref = GcRef<'h, Self>;
            fn wrap_gc_ref(gc_ref: GcRef<'h, Self>) -> Self::Ref { gc_ref }
            fn into_gc_ref(gc_ref: Self::Ref) -> GcRef<'h, Self> { gc_ref }
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

gc_trivial_impl!(String);

macro_rules! gc_generic_trivial_impl {
    (@as_item $it:item) => { $it };
    ([$($x:tt)*] $t:ty) => {
        gc_generic_trivial_impl! {
            @as_item
            impl<$($x)*> IntoHeapBase for $t {
                type In = $t;
                fn into_heap(self) -> $t { self }
                unsafe fn from_heap(storage: &$t) -> $t { (*storage).clone() }
                unsafe fn trace<R>(_storage: &$t, _tracer: &mut R) where R: Tracer {}
            }
        }
        gc_generic_trivial_impl! {
            @as_item
            unsafe impl<'h, $($x)*> IntoHeap<'h> for $t {}
        }
        gc_generic_trivial_impl! {
            @as_item
            impl<'h, $($x)*> IntoHeapAllocation<'h> for $t {
                type Ref = GcRef<'h, Self>;
                fn wrap_gc_ref(gc_ref: GcRef<'h, Self>) -> Self::Ref { gc_ref }
                fn into_gc_ref(gc_ref: Self::Ref) -> GcRef<'h, Self> { gc_ref }
            }
        }
    }
}

gc_generic_trivial_impl!([T: ?Sized + Sync] &'static T);
gc_generic_trivial_impl!([T: Clone + Send + 'static] GcLeaf<T>);
gc_generic_trivial_impl!([T: Clone + Send + 'static] Box<T>);
gc_generic_trivial_impl!([T: Clone + Sync + 'static] ::std::sync::Arc<T>);

/// Currently, `#[derive(IntoHeap)]` only works for types that have a lifetime
/// parameter.  This poses a problem because sometimes you want to store stuff
/// in the heap that doesn't contain any `GcRef`s or other heap lifetimes.
/// As a hackaround, we allow `PhantomData<&'h T>` fields (for now).
impl<'h, T: 'static> IntoHeapBase for PhantomData<&'h T> {
    type In = PhantomData<&'static T>;
    fn into_heap(self) -> Self::In { PhantomData }
    unsafe fn from_heap(_storage: &Self::In) -> Self { PhantomData }
    unsafe fn trace<R: Tracer>(_storage: &Self::In, _tracer: &mut R) {}
}

// GCRef has a special implementation.
impl<'h, T: IntoHeapAllocation<'h>> IntoHeapBase for GcRef<'h, T> {
    type In = Pointer<T::In>;

    fn into_heap(self) -> Self::In {
        self.ptr()
    }

    unsafe fn from_heap(storage: &Self::In) -> Self {
        Self::new(*storage)
    }

    unsafe fn trace<R: Tracer>(storage: &Self::In, tracer: &mut R) {
        tracer.visit::<T>(*storage);
    }
}

unsafe impl<'h, T: IntoHeapAllocation<'h>> IntoHeap<'h> for GcRef<'h, T> {}

// Transitive implementations ("this particular kind of struct/enum is IntoHeap
// if all its fields are") are slightly less trivial.

impl<T: IntoHeapBase> IntoHeapBase for Option<T> {
    type In = Option<T::In>;

    fn into_heap(self) -> Option<T::In> {
        self.map(|t| t.into_heap())
    }

    unsafe fn trace<R>(storage: &Option<T::In>, tracer: &mut R)
    where
        R: Tracer,
    {
        match storage {
            &None => (),
            &Some(ref u) => T::trace(u, tracer),
        }
    }

    unsafe fn from_heap(storage: &Option<T::In>) -> Option<T> {
        match storage {
            &None => None,
            &Some(ref u) => Some(T::from_heap(u)),
        }
    }
}

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Option<T> {}

macro_rules! gc_trivial_tuple_impl {
    (@as_item $it:item) => { $it };
    ($($t:ident),*) => {
        gc_trivial_tuple_impl! {
            @as_item
            impl<$($t: IntoHeapBase,)*> IntoHeapBase for ($($t,)*) {
                type In = ($($t::In,)*);

                #[allow(non_snake_case)]  // because we use the type names as variable names (!)
                fn into_heap(self) -> Self::In {
                    let ($($t,)*) = self;
                    ($($t.into_heap(),)*)
                }

                #[allow(non_snake_case)]
                unsafe fn trace<R>(storage: &Self::In, tracer: &mut R)
                    where R: Tracer
                {
                    let &($(ref $t,)*) = storage;

                    $(
                        <$t as $crate::traits::IntoHeapBase>::trace($t, tracer);
                    )*

                    // If the above `$(...)*` expansion is empty, we need this
                    // to quiet unused variable warnings for `tracer`.
                    let _ = tracer;
                }

                #[allow(non_snake_case)]
                unsafe fn from_heap(storage: &Self::In) -> Self {
                    let &($(ref $t,)*) = storage;
                    ($( <$t as $crate::traits::IntoHeapBase>::from_heap($t), )*)
                }
            }
        }

        gc_trivial_tuple_impl! {
            @as_item
            unsafe impl<'h, $($t: IntoHeap<'h>,)*> IntoHeap<'h> for ($($t,)*) {}
        }
    }
}

gc_trivial_tuple_impl!();
gc_trivial_tuple_impl!(T);
gc_trivial_tuple_impl!(T, U);
gc_trivial_tuple_impl!(T, U, V);
gc_trivial_tuple_impl!(T, U, V, W);
gc_trivial_tuple_impl!(T, U, V, W, X);
gc_trivial_tuple_impl!(T, U, V, W, X, Y);
gc_trivial_tuple_impl!(T, U, V, W, X, Y, Z);
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate fatfs;
extern crate fscommon;

use std::fs;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::str;

use fatfs::{FatType, FsOptions, DefaultTimeProvider, LossyOemCpConverter};
use fscommon::BufStream;

const TEST_TEXT: &str = "Rust is cool!\n";
const FAT12_IMG: &str = "resources/fat12.img";
const FAT16_IMG: &str = "resources/fat16.img";
const FAT32_IMG: &str = "resources/fat32.img";

type FileSystem = fatfs::FileSystem<BufStream<fs::File>, DefaultTimeProvider, LossyOemCpConverter>;

fn call_with_fs<F: Fn(FileSystem) -> ()>(f: F, filename: &str) {
    let _ = env_logger::builder().is_test(true).try_init();
    let file = fs::File::open(filename).unwrap();
    let buf_file = BufStream::new(file);
    let fs = FileSystem::new(buf_file, FsOptions::new()).unwrap();
    f(fs);
}

fn test_root_dir(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
    let short_names = entries.iter().map(|e| e.short_file_name()).collect::<Vec<String>>();
    assert_eq!(short_names, ["LONG.TXT", "SHORT.TXT", "VERY", "VERY-L~1"]);
    let names = entries.iter().map(|e| e.file_name()).collect::<Vec<String>>();
    assert_eq!(names, ["long.txt", "short.txt", "very", "very-long-dir-name"]);
    // Try read again
    let names2 = root_dir.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(names2, names);
}

#[test]
fn test_root_dir_fat12() {
    call_with_fs(test_root_dir, FAT12_IMG)
}

#[test]
fn test_root_dir_fat16() {
    call_with_fs(test_root_dir, FAT16_IMG)
}

#[test]
fn test_root_dir_fat32() {
    call_with_fs(test_root_dir, FAT32_IMG)
}

fn test_read_seek_short_file(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let mut short_file = root_dir.open_file("short.txt").unwrap();
    let mut buf = Vec::new();
    short_file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);

    assert_eq!(short_file.seek(SeekFrom::Start(5)).unwrap(), 5);
    let mut buf2 = [0; 5];
    short_file.read_exact(&mut buf2).unwrap();
    assert_eq!(str::from_utf8(&buf2).unwrap(), &TEST_TEXT[5..10]);

    assert_eq!(short_file.seek(SeekFrom::Start(1000)).unwrap(), TEST_TEXT.len() as u64);
    let mut buf2 = [0; 5];
    assert_eq!(short_file.read(&mut buf2).unwrap(), 0);
}

#[test]
fn test_read_seek_short_file_fat12() {
    call_with_fs(test_read_seek_short_file, FAT12_IMG)
}

#[test]
fn test_read_seek_short_file_fat16() {
    call_with_fs(test_read_seek_short_file, FAT16_IMG)
}

#[test]
fn test_read_seek_short_file_fat32() {
    call_with_fs(test_read_seek_short_file, FAT32_IMG)
}

fn test_read_long_file(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let mut long_file = root_dir.open_file("long.txt").unwrap();
    let mut buf = Vec::new();
    long_file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT.repeat(1000));

    assert_eq!(long_file.seek(SeekFrom::Start(2017)).unwrap(), 2017);
    buf.clear();
    let mut buf2 = [0; 10];
    long_file.read_exact(&mut buf2).unwrap();
    assert_eq!(str::from_utf8(&buf2).unwrap(), &TEST_TEXT.repeat(1000)[2017..2027]);
}

#[test]
fn test_read_long_file_fat12() {
    call_with_fs(test_read_long_file, FAT12_IMG)
}

#[test]
fn test_read_long_file_fat16() {
    call_with_fs(test_read_long_file, FAT16_IMG)
}

#[test]
fn test_read_long_file_fat32() {
    call_with_fs(test_read_long_file, FAT32_IMG)
}

fn test_get_dir_by_path(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let dir = root_dir.open_dir("very/long/path/").unwrap();
    let names = dir.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(names, [".", "..", "test.txt"]);

    let dir2 = root_dir.open_dir("very/long/path/././.").unwrap();
    let names2 = dir2.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(names2, [".", "..", "test.txt"]);

    let root_dir2 = root_dir.open_dir("very/long/path/../../..").unwrap();
    let root_names = root_dir2.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    let root_names2 = root_dir.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(root_names, root_names2);

    root_dir.open_dir("VERY-L~1").unwrap();
}

#[test]
fn test_get_dir_by_path_fat12() {
    call_with_fs(test_get_dir_by_path, FAT12_IMG)
}

#[test]
fn test_get_dir_by_path_fat16() {
    call_with_fs(test_get_dir_by_path, FAT16_IMG)
}

#[test]
fn test_get_dir_by_path_fat32() {
    call_with_fs(test_get_dir_by_path, FAT32_IMG)
}

fn test_get_file_by_path(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let mut file = root_dir.open_file("very/long/path/test.txt").unwrap();
    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);

    let mut file = root_dir.open_file("very-long-dir-name/very-long-file-name.txt").unwrap();
    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);

    root_dir.open_file("VERY-L~1/VERY-L~1.TXT").unwrap();

    // try opening dir as file
    assert!(root_dir.open_file("very/long/path").is_err());
    // try opening file as dir
    assert!(root_dir.open_dir("very/long/path/test.txt").is_err());
    // try using invalid path containing file as non-last component
    assert!(root_dir.open_file("very/long/path/test.txt/abc").is_err());
    assert!(root_dir.open_dir("very/long/path/test.txt/abc").is_err());
}

#[test]
fn test_get_file_by_path_fat12() {
    call_with_fs(test_get_file_by_path, FAT12_IMG)
}

#[test]
fn test_get_file_by_path_fat16() {
    call_with_fs(test_get_file_by_path, FAT16_IMG)
}

#[test]
fn test_get_file_by_path_fat32() {
    call_with_fs(test_get_file_by_path, FAT32_IMG)
}

fn test_volume_metadata(fs: FileSystem, fat_type: FatType) {
    assert_eq!(fs.volume_id(), 0x12345678);
    assert_eq!(fs.volume_label(), "Test!");
    assert_eq!(&fs.read_volume_label_from_root_dir().unwrap().unwrap(), "Test!");
    assert_eq!(fs.fat_type(), fat_type);
}

#[test]
fn test_volume_metadata_fat12() {
    call_with_fs(|fs| test_volume_metadata(fs, FatType::Fat12), FAT12_IMG)
}

#[test]
fn test_volume_metadata_fat16() {
    call_with_fs(|fs| test_volume_metadata(fs, FatType::Fat16), FAT16_IMG)
}

#[test]
fn test_volume_metadata_fat32() {
    call_with_fs(|fs| test_volume_metadata(fs, FatType::Fat32), FAT32_IMG)
}

fn test_status_flags(fs: FileSystem) {
    let status_flags = fs.read_status_flags().unwrap();
    assert_eq!(status_flags.dirty(), false);
    assert_eq!(status_flags.io_error(), false);
}

#[test]
fn test_status_flags_fat12() {
    call_with_fs(|fs| test_status_flags(fs), FAT12_IMG)
}

#[test]
fn test_status_flags_fat16() {
    call_with_fs(|fs| test_status_flags(fs), FAT16_IMG)
}

#[test]
fn test_status_flags_fat32() {
    call_with_fs(|fs| test_status_flags(fs), FAT32_IMG)
}

#[test]
fn test_stats_fat12() {
    call_with_fs(
        |fs| {
            let stats = fs.stats().unwrap();
            assert_eq!(stats.cluster_size(), 512);
            assert_eq!(stats.total_clusters(), 1955); // 1000 * 1024 / 512 = 2000
            assert_eq!(stats.free_clusters(), 1920);
        },
        FAT12_IMG,
    )
}

#[test]
fn test_stats_fat16() {
    call_with_fs(
        |fs| {
            let stats = fs.stats().unwrap();
            assert_eq!(stats.cluster_size(), 512);
            assert_eq!(stats.total_clusters(), 4927); // 2500 * 1024 / 512 = 5000
            assert_eq!(stats.free_clusters(), 4892);
        },
        FAT16_IMG,
    )
}

#[test]
fn test_stats_fat32() {
    call_with_fs(
        |fs| {
            let stats = fs.stats().unwrap();
            assert_eq!(stats.cluster_size(), 512);
            assert_eq!(stats.total_clusters(), 66922); // 34000 * 1024 / 512 = 68000
            assert_eq!(stats.free_clusters(), 66886);
        },
        FAT32_IMG,
    )
}
<commit_msg>Add test checking if FileSystem can be used with multiple threads<commit_after>extern crate env_logger;
extern crate fatfs;
extern crate fscommon;

use std::fs;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::str;

use fatfs::{FatType, FsOptions, DefaultTimeProvider, LossyOemCpConverter};
use fscommon::BufStream;

const TEST_TEXT: &str = "Rust is cool!\n";
const FAT12_IMG: &str = "resources/fat12.img";
const FAT16_IMG: &str = "resources/fat16.img";
const FAT32_IMG: &str = "resources/fat32.img";

type FileSystem = fatfs::FileSystem<BufStream<fs::File>, DefaultTimeProvider, LossyOemCpConverter>;

fn call_with_fs<F: Fn(FileSystem) -> ()>(f: F, filename: &str) {
    let _ = env_logger::builder().is_test(true).try_init();
    let file = fs::File::open(filename).unwrap();
    let buf_file = BufStream::new(file);
    let fs = FileSystem::new(buf_file, FsOptions::new()).unwrap();
    f(fs);
}

fn test_root_dir(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let entries = root_dir.iter().map(|r| r.unwrap()).collect::<Vec<_>>();
    let short_names = entries.iter().map(|e| e.short_file_name()).collect::<Vec<String>>();
    assert_eq!(short_names, ["LONG.TXT", "SHORT.TXT", "VERY", "VERY-L~1"]);
    let names = entries.iter().map(|e| e.file_name()).collect::<Vec<String>>();
    assert_eq!(names, ["long.txt", "short.txt", "very", "very-long-dir-name"]);
    // Try read again
    let names2 = root_dir.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(names2, names);
}

#[test]
fn test_root_dir_fat12() {
    call_with_fs(test_root_dir, FAT12_IMG)
}

#[test]
fn test_root_dir_fat16() {
    call_with_fs(test_root_dir, FAT16_IMG)
}

#[test]
fn test_root_dir_fat32() {
    call_with_fs(test_root_dir, FAT32_IMG)
}

fn test_read_seek_short_file(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let mut short_file = root_dir.open_file("short.txt").unwrap();
    let mut buf = Vec::new();
    short_file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);

    assert_eq!(short_file.seek(SeekFrom::Start(5)).unwrap(), 5);
    let mut buf2 = [0; 5];
    short_file.read_exact(&mut buf2).unwrap();
    assert_eq!(str::from_utf8(&buf2).unwrap(), &TEST_TEXT[5..10]);

    assert_eq!(short_file.seek(SeekFrom::Start(1000)).unwrap(), TEST_TEXT.len() as u64);
    let mut buf2 = [0; 5];
    assert_eq!(short_file.read(&mut buf2).unwrap(), 0);
}

#[test]
fn test_read_seek_short_file_fat12() {
    call_with_fs(test_read_seek_short_file, FAT12_IMG)
}

#[test]
fn test_read_seek_short_file_fat16() {
    call_with_fs(test_read_seek_short_file, FAT16_IMG)
}

#[test]
fn test_read_seek_short_file_fat32() {
    call_with_fs(test_read_seek_short_file, FAT32_IMG)
}

fn test_read_long_file(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let mut long_file = root_dir.open_file("long.txt").unwrap();
    let mut buf = Vec::new();
    long_file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT.repeat(1000));

    assert_eq!(long_file.seek(SeekFrom::Start(2017)).unwrap(), 2017);
    buf.clear();
    let mut buf2 = [0; 10];
    long_file.read_exact(&mut buf2).unwrap();
    assert_eq!(str::from_utf8(&buf2).unwrap(), &TEST_TEXT.repeat(1000)[2017..2027]);
}

#[test]
fn test_read_long_file_fat12() {
    call_with_fs(test_read_long_file, FAT12_IMG)
}

#[test]
fn test_read_long_file_fat16() {
    call_with_fs(test_read_long_file, FAT16_IMG)
}

#[test]
fn test_read_long_file_fat32() {
    call_with_fs(test_read_long_file, FAT32_IMG)
}

fn test_get_dir_by_path(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let dir = root_dir.open_dir("very/long/path/").unwrap();
    let names = dir.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(names, [".", "..", "test.txt"]);

    let dir2 = root_dir.open_dir("very/long/path/././.").unwrap();
    let names2 = dir2.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(names2, [".", "..", "test.txt"]);

    let root_dir2 = root_dir.open_dir("very/long/path/../../..").unwrap();
    let root_names = root_dir2.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    let root_names2 = root_dir.iter().map(|r| r.unwrap().file_name()).collect::<Vec<String>>();
    assert_eq!(root_names, root_names2);

    root_dir.open_dir("VERY-L~1").unwrap();
}

#[test]
fn test_get_dir_by_path_fat12() {
    call_with_fs(test_get_dir_by_path, FAT12_IMG)
}

#[test]
fn test_get_dir_by_path_fat16() {
    call_with_fs(test_get_dir_by_path, FAT16_IMG)
}

#[test]
fn test_get_dir_by_path_fat32() {
    call_with_fs(test_get_dir_by_path, FAT32_IMG)
}

fn test_get_file_by_path(fs: FileSystem) {
    let root_dir = fs.root_dir();
    let mut file = root_dir.open_file("very/long/path/test.txt").unwrap();
    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);

    let mut file = root_dir.open_file("very-long-dir-name/very-long-file-name.txt").unwrap();
    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);

    root_dir.open_file("VERY-L~1/VERY-L~1.TXT").unwrap();

    // try opening dir as file
    assert!(root_dir.open_file("very/long/path").is_err());
    // try opening file as dir
    assert!(root_dir.open_dir("very/long/path/test.txt").is_err());
    // try using invalid path containing file as non-last component
    assert!(root_dir.open_file("very/long/path/test.txt/abc").is_err());
    assert!(root_dir.open_dir("very/long/path/test.txt/abc").is_err());
}

#[test]
fn test_get_file_by_path_fat12() {
    call_with_fs(test_get_file_by_path, FAT12_IMG)
}

#[test]
fn test_get_file_by_path_fat16() {
    call_with_fs(test_get_file_by_path, FAT16_IMG)
}

#[test]
fn test_get_file_by_path_fat32() {
    call_with_fs(test_get_file_by_path, FAT32_IMG)
}

fn test_volume_metadata(fs: FileSystem, fat_type: FatType) {
    assert_eq!(fs.volume_id(), 0x12345678);
    assert_eq!(fs.volume_label(), "Test!");
    assert_eq!(&fs.read_volume_label_from_root_dir().unwrap().unwrap(), "Test!");
    assert_eq!(fs.fat_type(), fat_type);
}

#[test]
fn test_volume_metadata_fat12() {
    call_with_fs(|fs| test_volume_metadata(fs, FatType::Fat12), FAT12_IMG)
}

#[test]
fn test_volume_metadata_fat16() {
    call_with_fs(|fs| test_volume_metadata(fs, FatType::Fat16), FAT16_IMG)
}

#[test]
fn test_volume_metadata_fat32() {
    call_with_fs(|fs| test_volume_metadata(fs, FatType::Fat32), FAT32_IMG)
}

fn test_status_flags(fs: FileSystem) {
    let status_flags = fs.read_status_flags().unwrap();
    assert_eq!(status_flags.dirty(), false);
    assert_eq!(status_flags.io_error(), false);
}

#[test]
fn test_status_flags_fat12() {
    call_with_fs(|fs| test_status_flags(fs), FAT12_IMG)
}

#[test]
fn test_status_flags_fat16() {
    call_with_fs(|fs| test_status_flags(fs), FAT16_IMG)
}

#[test]
fn test_status_flags_fat32() {
    call_with_fs(|fs| test_status_flags(fs), FAT32_IMG)
}

#[test]
fn test_stats_fat12() {
    call_with_fs(
        |fs| {
            let stats = fs.stats().unwrap();
            assert_eq!(stats.cluster_size(), 512);
            assert_eq!(stats.total_clusters(), 1955); // 1000 * 1024 / 512 = 2000
            assert_eq!(stats.free_clusters(), 1920);
        },
        FAT12_IMG,
    )
}

#[test]
fn test_stats_fat16() {
    call_with_fs(
        |fs| {
            let stats = fs.stats().unwrap();
            assert_eq!(stats.cluster_size(), 512);
            assert_eq!(stats.total_clusters(), 4927); // 2500 * 1024 / 512 = 5000
            assert_eq!(stats.free_clusters(), 4892);
        },
        FAT16_IMG,
    )
}

#[test]
fn test_stats_fat32() {
    call_with_fs(
        |fs| {
            let stats = fs.stats().unwrap();
            assert_eq!(stats.cluster_size(), 512);
            assert_eq!(stats.total_clusters(), 66922); // 34000 * 1024 / 512 = 68000
            assert_eq!(stats.free_clusters(), 66886);
        },
        FAT32_IMG,
    )
}

#[test]
fn test_multi_thread() {
    call_with_fs(
        |fs| {
            use std::sync::{Arc, Mutex};
            use std::thread;
            let shared_fs = Arc::new(Mutex::new(fs));
            let mut handles = vec![];
            for _ in 0..2 {
                let shared_fs_cloned = Arc::clone(&shared_fs);
                let handle = thread::spawn(move || {
                    let fs2 = shared_fs_cloned.lock().unwrap();
                    assert_eq!(fs2.fat_type(), FatType::Fat32);
                });
                handles.push(handle);
            }
            for handle in handles {
                handle.join().unwrap();
            }
        },
        FAT32_IMG,
    )
}
<|endoftext|>


<commit_before>#[cfg(feature = "lazy-blob")]
compile_error!("Tests must be run without lazy blobs.");

use libtest_mimic::{run_tests, Arguments, Outcome, Test};
use std::error::Error;
use std::fs::{read_dir, read_to_string};
use std::path::Path;
use wabt::script::{Command, CommandKind, ScriptParser};
use wasmbin::io::Decode;
use wasmbin::module::Module;

fn fmt_wabt_error(err: wabt::Error) -> String {
    use wabt::ErrorKind;

    match err.kind() {
        ErrorKind::Nul => "string contained nul-byte".to_owned(),
        ErrorKind::Deserialize(err) => format!("failed to deserialize:\n{:#}", err),
        ErrorKind::Parse(_) => "failed to parse".to_owned(),
        ErrorKind::WriteText => "failed to write text".to_owned(),
        ErrorKind::NonUtf8Result => "result is not a valid utf8".to_owned(),
        ErrorKind::WriteBinary => "failed to write binary".to_owned(),
        ErrorKind::ResolveNames(err) => format!("failed to resolve names:\n{:#}", err),
        ErrorKind::Validate(err) => format!("failed to validate:\n{:#}", err),
    }
}

fn fmt_wabt_script_error(err: wabt::script::Error) -> String {
    match err {
        wabt::script::Error::IoError(err) => format!("{}", err),
        wabt::script::Error::WabtError(err) => format!("Wabt: {}", fmt_wabt_error(err)),
        wabt::script::Error::Other(err) => format!("{}", err),
        wabt::script::Error::WithLineInfo { line, error } => {
            format!("at line {}: {}", line, fmt_wabt_script_error(*error))
        }
    }
}

struct WasmTest {
    module: Vec<u8>,
    expect_result: Result<(), String>,
}

fn read_tests(path: &Path, dest: &mut Vec<Test<WasmTest>>) -> Result<(), Box<dyn Error>> {
    let src = read_to_string(path)?;
    let mut parser = ScriptParser::<f32, f64>::from_str(&src).map_err(fmt_wabt_script_error)?;
    while let Some(Command { kind, line }) = parser.next().map_err(fmt_wabt_script_error)? {
        let (module, expect_result) = match kind {
            CommandKind::AssertMalformed { module, message } => (module, Err(message)),
            CommandKind::Module { module, .. } => (module, Ok(())),
            _ => {
                // Skipping interpreted
                continue;
            }
        };
        dest.push(Test {
            name: format!("{}:{}", path.display(), line),
            kind: String::default(),
            is_ignored: false,
            is_bench: false,
            data: WasmTest {
                module: module.into_vec(),
                expect_result,
            },
        });
    }
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut tests = Vec::new();
    for file in read_dir(Path::new("tests").join("testsuite"))? {
        let path = file?.path();
        if !path.extension().map_or(false, |ext| ext == "wast") {
            continue;
        }
        if let Err(err) = read_tests(&path, &mut tests) {
            // If an error comes from the Wabt, ignore for now - most likely it uses a new feature we don't support yet.
            eprintln!("Could not read test {}: {}", path.display(), err);
        }
    }
    assert!(
        !tests.is_empty(),
        "Couldn't find any tests. Did you run `git submodule update --init`?"
    );
    run_tests(&Arguments::from_args(), tests, |test| {
        match (
            Module::decode(&mut test.data.module.as_slice()),
            &test.data.expect_result,
        ) {
            (Ok(_), Err(err)) => Outcome::Failed {
                msg: Some(format!(
                    "Expected an invalid module definition with an error: {}",
                    err
                )),
            },
            (Err(err), Ok(())) => Outcome::Failed {
                msg: Some(format!(
                    "Expected a valid module definition, but got an error:\n{:#}",
                    err
                )),
            },
            _ => Outcome::Passed,
        }
    })
    .exit()
}
<commit_msg>Enable all Wabt features<commit_after>#[cfg(feature = "lazy-blob")]
compile_error!("Tests must be run without lazy blobs.");

use libtest_mimic::{run_tests, Arguments, Outcome, Test};
use std::error::Error;
use std::fs::{read, read_dir};
use std::path::Path;
use wabt::script::{Command, CommandKind, ScriptParser};
use wasmbin::io::Decode;
use wasmbin::module::Module;

fn fmt_wabt_error(err: wabt::Error) -> String {
    use wabt::ErrorKind;

    match err.kind() {
        ErrorKind::Nul => "string contained nul-byte".to_owned(),
        ErrorKind::Deserialize(err) => format!("failed to deserialize:\n{:#}", err),
        ErrorKind::Parse(_) => "failed to parse".to_owned(),
        ErrorKind::WriteText => "failed to write text".to_owned(),
        ErrorKind::NonUtf8Result => "result is not a valid utf8".to_owned(),
        ErrorKind::WriteBinary => "failed to write binary".to_owned(),
        ErrorKind::ResolveNames(err) => format!("failed to resolve names:\n{:#}", err),
        ErrorKind::Validate(err) => format!("failed to validate:\n{:#}", err),
    }
}

fn fmt_wabt_script_error(err: wabt::script::Error) -> String {
    match err {
        wabt::script::Error::IoError(err) => format!("{}", err),
        wabt::script::Error::WabtError(err) => format!("Wabt: {}", fmt_wabt_error(err)),
        wabt::script::Error::Other(err) => format!("{}", err),
        wabt::script::Error::WithLineInfo { line, error } => {
            format!("at line {}: {}", line, fmt_wabt_script_error(*error))
        }
    }
}

struct WasmTest {
    module: Vec<u8>,
    expect_result: Result<(), String>,
}

fn read_tests(path: &Path, dest: &mut Vec<Test<WasmTest>>) -> Result<(), Box<dyn Error>> {
    let src = read(path)?;
    let mut features = wabt::Features::new();
    features.enable_all();
    let mut parser =
        ScriptParser::<f32, f64>::from_source_and_name_with_features(&src, "test.wast", features)
            .map_err(fmt_wabt_script_error)?;
    while let Some(Command { kind, line }) = parser.next().map_err(fmt_wabt_script_error)? {
        let (module, expect_result) = match kind {
            CommandKind::AssertMalformed { module, message } => (module, Err(message)),
            CommandKind::Module { module, .. } => (module, Ok(())),
            _ => {
                // Skipping interpreted
                continue;
            }
        };
        dest.push(Test {
            name: format!("{}:{}", path.display(), line),
            kind: String::default(),
            is_ignored: false,
            is_bench: false,
            data: WasmTest {
                module: module.into_vec(),
                expect_result,
            },
        });
    }
    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut tests = Vec::new();
    for file in read_dir(Path::new("tests").join("testsuite"))? {
        let path = file?.path();
        if !path.extension().map_or(false, |ext| ext == "wast") {
            continue;
        }
        if let Err(err) = read_tests(&path, &mut tests) {
            // If an error comes from the Wabt, ignore for now - most likely it uses a new feature we don't support yet.
            eprintln!("Could not read test {}: {}", path.display(), err);
        }
    }
    assert!(
        !tests.is_empty(),
        "Couldn't find any tests. Did you run `git submodule update --init`?"
    );
    run_tests(&Arguments::from_args(), tests, |test| {
        match (
            Module::decode(&mut test.data.module.as_slice()),
            &test.data.expect_result,
        ) {
            (Ok(_), Err(err)) => Outcome::Failed {
                msg: Some(format!(
                    "Expected an invalid module definition with an error: {}",
                    err
                )),
            },
            (Err(err), Ok(())) => Outcome::Failed {
                msg: Some(format!(
                    "Expected a valid module definition, but got an error:\n{:#}",
                    err
                )),
            },
            _ => Outcome::Passed,
        }
    })
    .exit()
}
<|endoftext|>


<commit_before>pub use self::os::{
    Semaphore,
    SemaphoreGuard,
};

#[cfg(not(target_os = "macos"))]
mod os {
    use std::cell::UnsafeCell;
    use std::mem;
    use std::io;
    use libc::{
        c_int,
        c_uint,
    };

    #[cfg(target_pointer_width = "64")]
    const SIZEOF_SEM_T: usize = 32;
    #[cfg(not(target_pointer_width = "64"))]
    const SIZEOF_SEM_T: usize = 16;

    extern {
        fn sem_init(sem: *mut sem_t, pshared: c_int, value: c_uint) -> c_int;
        fn sem_post(sem: *mut sem_t) -> c_int;
        fn sem_wait(sem: *mut sem_t) -> c_int;
        fn sem_destroy(sem: *mut sem_t) -> c_int;
    }

    #[repr(C)]
    #[derive(Debug)]
    struct sem_t {
        __opaque: [u8; SIZEOF_SEM_T],
    }

    pub struct Semaphore {
        inner: UnsafeCell<sem_t>,
    }

    pub struct SemaphoreGuard<'a> {
        sem: &'a Semaphore,
    }

    impl Semaphore {
        pub fn new(value: u32) -> Semaphore {
            let mut sem: sem_t = unsafe {
                mem::uninitialized()
            };
            let res = unsafe {
                sem_init(&mut sem, 0, value as c_uint)
            };
            debug_assert_eq!(res, 0);

            Semaphore {
                inner: UnsafeCell::new(sem),
            }
        }

        pub fn wait(&self) {
            loop {
                let res = unsafe {
                    sem_wait(self.inner.get())
                };
                if res == -1 {
                    match io::Error::last_os_error() {
                        ref e if e.kind() == io::ErrorKind::Interrupted => continue,
                        other => panic!("{}", other),
                    }
                } else {
                    break;
                }
            }
        }

        pub fn post(&self) {
            let res = unsafe {
                sem_post(self.inner.get())
            };
            debug_assert_eq!(res, 0);
        }

        pub fn access(&self) -> SemaphoreGuard {
            self.wait();
            SemaphoreGuard { sem: self }
        }
    }

    unsafe impl Send for Semaphore {}
    unsafe impl Sync for Semaphore {}

    impl Drop for Semaphore {
        fn drop(&mut self) {
            let res = unsafe {
                sem_destroy(self.inner.get())
            };
            debug_assert_eq!(res, 0);
        }
    }

    impl<'a> Drop for SemaphoreGuard<'a> {
        fn drop(&mut self) {
            self.sem.post();
        }
    }
}

#[cfg(target_os = "macos")]
mod os {
    use std::ffi::CString;
    use rand::{
        thread_rng,
        Rng,
    };
    use libc::{
        c_int,
        c_uint,
        c_char,
        mode_t,
        O_CREAT,
        O_EXCL,
        S_IRWXU,
    };

    const SIZEOF_SEM_T: usize = 4;
    const SEM_NAME_MAX: usize = 28; // No definitive value for this on OS X. Erring on the side of caution.
    const SEM_FAILED: *mut sem_t = 0 as *mut sem_t;

    extern {
        fn sem_open(name: *const c_char, oflag: c_int, mode: mode_t, value: c_uint) -> *mut sem_t;
        fn sem_post(sem: *mut sem_t) -> c_int;
        fn sem_wait(sem: *mut sem_t) -> c_int;
        fn sem_close(sem: *mut sem_t) -> c_int;
        fn sem_unlink(sem: *const c_char) -> c_int;
    }

    #[repr(C)]
    #[derive(Debug)]
    pub struct sem_t {
        __opaque: c_int,
    }

    pub struct Semaphore {
        inner: UnsafeCell<*mut sem_t>,
        name: CString,
    }

    pub struct SemaphoreGuard<'a> {
        sem: &'a Semaphore,
    }

    impl Semaphore {
        pub fn new(value: u32) -> Semaphore {
            let name: String = thread_rng().gen_ascii_chars().take(SEM_NAME_MAX).collect();
            let c_name = CString::new(name).unwrap(); // Rng does not emit 0 bytes.

            let sem: *mut sem_t = unsafe {
                sem_open(c_name.as_ptr(), O_CREAT | O_EXCL, S_IRWXU, value as c_uint)
            };
            debug_assert!(sem != SEM_FAILED);

            Semaphore {
                inner: UnsafeCell::new(sem),
                name: c_name,
            }
        }

        pub fn wait(&self) {
            loop {
                let res = unsafe {
                    sem_wait(self.inner.get())
                };
                if res == -1 {
                    match io::Error::last_os_error() {
                        ref e if e.kind() == io::ErrorKind::Interrupted => continue,
                        other => panic!("{}", other),
                    }
                } else {
                    break;
                }
            }
        }

        pub fn post(&self) {
            let res = unsafe {
                sem_post(self.inner.get())
            };
            debug_assert_eq!(res, 0);
        }

        pub fn access(&self) -> SemaphoreGuard {
            self.wait();
            SemaphoreGuard { sem: self }
        }
    }

    impl Drop for Semaphore {
        fn drop(&mut self) {
            let res = unsafe {
                sem_close(self.inner.get())
            };
            debug_assert_eq!(res, 0);
            let res = unsafe {
                sem_unlink(self.name.as_ptr())
            };
            debug_assert_eq!(res, 0);
        }
    }

    impl<'a> Drop for SemaphoreGuard<'a> {
        fn drop(&mut self) {
            self.sem.post();
        }
    }
}
<commit_msg>fix os x support<commit_after>pub use self::os::{
    Semaphore,
    SemaphoreGuard,
};

#[cfg(not(target_os = "macos"))]
mod os {
    use std::cell::UnsafeCell;
    use std::mem;
    use std::io;
    use libc::{
        c_int,
        c_uint,
    };

    #[cfg(target_pointer_width = "64")]
    const SIZEOF_SEM_T: usize = 32;
    #[cfg(not(target_pointer_width = "64"))]
    const SIZEOF_SEM_T: usize = 16;

    extern {
        fn sem_init(sem: *mut sem_t, pshared: c_int, value: c_uint) -> c_int;
        fn sem_post(sem: *mut sem_t) -> c_int;
        fn sem_wait(sem: *mut sem_t) -> c_int;
        fn sem_destroy(sem: *mut sem_t) -> c_int;
    }

    #[repr(C)]
    #[derive(Debug)]
    struct sem_t {
        __opaque: [u8; SIZEOF_SEM_T],
    }

    pub struct Semaphore {
        inner: UnsafeCell<sem_t>,
    }

    pub struct SemaphoreGuard<'a> {
        sem: &'a Semaphore,
    }

    impl Semaphore {
        pub fn new(value: u32) -> Semaphore {
            let mut sem: sem_t = unsafe {
                mem::uninitialized()
            };
            let res = unsafe {
                sem_init(&mut sem, 0, value as c_uint)
            };
            debug_assert_eq!(res, 0);

            Semaphore {
                inner: UnsafeCell::new(sem),
            }
        }

        pub fn wait(&self) {
            loop {
                let res = unsafe {
                    sem_wait(self.inner.get())
                };
                if res == -1 {
                    match io::Error::last_os_error() {
                        ref e if e.kind() == io::ErrorKind::Interrupted => continue,
                        other => panic!("{}", other),
                    }
                } else {
                    break;
                }
            }
        }

        pub fn post(&self) {
            let res = unsafe {
                sem_post(self.inner.get())
            };
            debug_assert_eq!(res, 0);
        }

        pub fn access(&self) -> SemaphoreGuard {
            self.wait();
            SemaphoreGuard { sem: self }
        }
    }

    unsafe impl Send for Semaphore {}
    unsafe impl Sync for Semaphore {}

    impl Drop for Semaphore {
        fn drop(&mut self) {
            let res = unsafe {
                sem_destroy(self.inner.get())
            };
            debug_assert_eq!(res, 0);
        }
    }

    impl<'a> Drop for SemaphoreGuard<'a> {
        fn drop(&mut self) {
            self.sem.post();
        }
    }
}

#[cfg(target_os = "macos")]
mod os {
    use std::ffi::CString;
    use std::cell::UnsafeCell;
    use std::io;
    use rand::{
        thread_rng,
        Rng,
    };
    use libc::{
        c_int,
        c_uint,
        c_char,
        mode_t,
        O_CREAT,
        O_EXCL,
        S_IRWXU,
    };

    const SIZEOF_SEM_T: usize = 4;
    const SEM_NAME_MAX: usize = 28; // No definitive value for this on OS X. Erring on the side of caution.
    const SEM_FAILED: *mut sem_t = 0 as *mut sem_t;

    extern {
        fn sem_open(name: *const c_char, oflag: c_int, mode: mode_t, value: c_uint) -> *mut sem_t;
        fn sem_post(sem: *mut sem_t) -> c_int;
        fn sem_wait(sem: *mut sem_t) -> c_int;
        fn sem_close(sem: *mut sem_t) -> c_int;
        fn sem_unlink(sem: *const c_char) -> c_int;
    }

    #[repr(C)]
    #[derive(Debug)]
    pub struct sem_t {
        __opaque: c_int,
    }

    pub struct Semaphore {
        inner: UnsafeCell<*mut sem_t>,
        name: CString,
    }

    pub struct SemaphoreGuard<'a> {
        sem: &'a Semaphore,
    }

    impl Semaphore {
        pub fn new(value: u32) -> Semaphore {
            let name: String = thread_rng().gen_ascii_chars().take(SEM_NAME_MAX).collect();
            let c_name = CString::new(name).unwrap(); // Rng does not emit 0 bytes.

            let sem: *mut sem_t = unsafe {
                sem_open(c_name.as_ptr(), O_CREAT | O_EXCL, S_IRWXU, value as c_uint)
            };
            debug_assert!(sem != SEM_FAILED);

            Semaphore {
                inner: UnsafeCell::new(sem),
                name: c_name,
            }
        }

        pub fn wait(&self) {
            loop {
                let res = unsafe {
                    sem_wait(*self.inner.get())
                };
                if res == -1 {
                    match io::Error::last_os_error() {
                        ref e if e.kind() == io::ErrorKind::Interrupted => continue,
                        other => panic!("{}", other),
                    }
                } else {
                    break;
                }
            }
        }

        pub fn post(&self) {
            let res = unsafe {
                sem_post(*self.inner.get())
            };
            debug_assert_eq!(res, 0);
        }

        pub fn access(&self) -> SemaphoreGuard {
            self.wait();
            SemaphoreGuard { sem: self }
        }
    }

    unsafe impl Send for Semaphore {}
    unsafe impl Sync for Semaphore {}

    impl Drop for Semaphore {
        fn drop(&mut self) {
            let res = unsafe {
                sem_close(*self.inner.get())
            };
            debug_assert_eq!(res, 0);
            let res = unsafe {
                sem_unlink(self.name.as_ptr())
            };
            debug_assert_eq!(res, 0);
        }
    }

    impl<'a> Drop for SemaphoreGuard<'a> {
        fn drop(&mut self) {
            self.sem.post();
        }
    }
}
<|endoftext|>