<commit_before>macro_rules! compile_prim(
    ($ty:ty, $type_name:ident, $make_constant:ident) => (
impl Compile for $ty {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        use types::consts;
        unsafe {
            from_ptr($make_constant(func.as_ptr(), consts::$type_name.as_ptr(), *self) )
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<$ty>) -> Type {
        use types::consts;
        consts::$type_name.clone()
    }
});
    ($ty:ty, $type_name:ident, $make_constant:ident, $cast:ty) => (
impl Compile for $ty {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        use types::consts;
        unsafe {
            from_ptr($make_constant(func.as_ptr(), consts::$type_name.as_ptr(), *self as $cast) )
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<$ty>) -> Type {
        use types::consts;
        consts::$type_name.clone()
    }
});
);
macro_rules! compile_ptr(
    ($func:expr, $ptr:expr) => (unsafe {
        use std::mem;
        let ptr = mem::transmute::<_, usize>($ptr);
        ptr.compile($func)
    })
);
macro_rules! compile_func(
    (fn($($arg:ident),*) -> $ret:ty, $sig:ty, $ext_sig:ty) => (
        impl<$($arg:Compile,)* R:Compile> Compile for $sig {
            #[inline(always)]
            fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
                compile_ptr!(func, self)
            }
            #[inline(always)]
            fn jit_type(_:Option<$sig>) -> Type {
                Type::create_signature(CDecl, get::<R>(), [$(get::<$arg>()),*].as_mut_slice())
            }
        }
        impl<$($arg:Compile,)* R:Compile> Compile for $ext_sig {
            #[inline(always)]
            fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
                compile_ptr!(func, self)
            }
            #[inline(always)]
            fn jit_type(_:Option<$ext_sig>) -> Type {
                get::<$sig>()
            }
        }
    )
);
macro_rules! compile_tuple(
    ($($ty:ident),+ => $($name:ident),+) => (
        impl<$($ty),+> Compile for ($($ty),+) where $($ty:Compile),+ {
            #[inline(always)]
            fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
                let ($(ref $name),+) = *self;
                let ty = get::<($($ty),+)>();
                let tuple = Value::new(func, ty.clone());
                let ($($name),+) = ($(func.insn_of($name)),+);
                let mut fields = ty.fields();
                $(func.insn_store_relative(tuple, fields.next().unwrap().get_offset() as isize, $name);)+
                tuple
            }
            #[inline(always)]
            fn jit_type(_:Option<($($ty),+)>) -> Type {
                let mut types = [$(get::<$ty>()),+];
                Type::create_struct(types.as_mut_slice())
            }
        }
    )
);

macro_rules! compile_prims(
    ($(($ty:ty, $cast: ty) => ($type_name:ident, $make_constant:ident)),+) => (
        $(compile_prim!($ty, $type_name, $make_constant, $cast);)+
    );
);

macro_rules! native_ref(
    ($name:ident, $field:ident, $pointer_ty:ty) => (
        impl PartialEq for $name {
            fn eq(&self, other: &$name) -> bool {
                self.$field == other.$field
            }
        }
        impl Eq for $name {}
        impl NativeRef for $name {
            #[inline(always)]
            /// Convert to a native pointer
            unsafe fn as_ptr(&self) -> $pointer_ty {
                self.$field
            }
            #[inline(always)]
            /// Convert from a native pointer
            unsafe fn from_ptr(ptr:$pointer_ty) -> $name {
                $name {
                    $field: ptr
                }
            }
        }
    );
    ($name:ident, $field:ident, $pointer_ty:ty, $lifetime:ident) => (
        impl<'a> PartialEq for $name<'a> {
            fn eq(&self, other: &$name<'a>) -> bool {
                self.$field == other.$field
            }
        }
        impl<'a> Eq for $name<'a> {}
        impl<'a> NativeRef for $name<'a> {
            #[inline]
            /// Convert to a native pointer
            unsafe fn as_ptr(&self) -> $pointer_ty {
                self.$field
            }
            #[inline]
            /// Convert from a native pointer
            unsafe fn from_ptr(ptr:$pointer_ty) -> $name<'a> {
                $name {
                    $field: ptr,
                    marker: $lifetime::<'a>
                }
            }
        }
    )
);
macro_rules! builtin_type(
    ($c_name:ident -> $rust_name:ident) => (
        #[allow(missing_copy_implementations)]
        #[allow(non_camel_case_types)]
        #[allow(dead_code)]
        pub struct $rust_name;
        impl Copy for $rust_name {}
        impl ::std::ops::Deref for $rust_name {
            type Target = Type;
            fn deref(&self) -> &Type {
                use std::mem;
                unsafe { mem::transmute(&$c_name) }
            }
        }
        impl ::types::StaticType for $rust_name {
            fn get(self) -> Type {
                use util::from_ptr;
                unsafe { from_ptr($c_name) }
            }
        }
    )
);
macro_rules! builtin_types(
    ($($c_name:ident -> $rust_name:ident);+) => (
        $(builtin_type!($c_name -> $rust_name);)+
    )
);<commit_msg>Inline static type getter<commit_after>macro_rules! compile_prim(
    ($ty:ty, $type_name:ident, $make_constant:ident) => (
impl Compile for $ty {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        use types::consts;
        unsafe {
            from_ptr($make_constant(func.as_ptr(), consts::$type_name.as_ptr(), *self) )
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<$ty>) -> Type {
        use types::consts;
        consts::$type_name.clone()
    }
});
    ($ty:ty, $type_name:ident, $make_constant:ident, $cast:ty) => (
impl Compile for $ty {
    #[inline(always)]
    fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
        use types::consts;
        unsafe {
            from_ptr($make_constant(func.as_ptr(), consts::$type_name.as_ptr(), *self as $cast) )
        }
    }
    #[inline(always)]
    fn jit_type(_:Option<$ty>) -> Type {
        use types::consts;
        consts::$type_name.clone()
    }
});
);
macro_rules! compile_ptr(
    ($func:expr, $ptr:expr) => (unsafe {
        use std::mem;
        let ptr = mem::transmute::<_, usize>($ptr);
        ptr.compile($func)
    })
);
macro_rules! compile_func(
    (fn($($arg:ident),*) -> $ret:ty, $sig:ty, $ext_sig:ty) => (
        impl<$($arg:Compile,)* R:Compile> Compile for $sig {
            #[inline(always)]
            fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
                compile_ptr!(func, self)
            }
            #[inline(always)]
            fn jit_type(_:Option<$sig>) -> Type {
                Type::create_signature(CDecl, get::<R>(), [$(get::<$arg>()),*].as_mut_slice())
            }
        }
        impl<$($arg:Compile,)* R:Compile> Compile for $ext_sig {
            #[inline(always)]
            fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
                compile_ptr!(func, self)
            }
            #[inline(always)]
            fn jit_type(_:Option<$ext_sig>) -> Type {
                get::<$sig>()
            }
        }
    )
);
macro_rules! compile_tuple(
    ($($ty:ident),+ => $($name:ident),+) => (
        impl<$($ty),+> Compile for ($($ty),+) where $($ty:Compile),+ {
            #[inline(always)]
            fn compile<'a>(&self, func:&UncompiledFunction<'a>) -> Value<'a> {
                let ($(ref $name),+) = *self;
                let ty = get::<($($ty),+)>();
                let tuple = Value::new(func, ty.clone());
                let ($($name),+) = ($(func.insn_of($name)),+);
                let mut fields = ty.fields();
                $(func.insn_store_relative(tuple, fields.next().unwrap().get_offset() as isize, $name);)+
                tuple
            }
            #[inline(always)]
            fn jit_type(_:Option<($($ty),+)>) -> Type {
                let mut types = [$(get::<$ty>()),+];
                Type::create_struct(types.as_mut_slice())
            }
        }
    )
);

macro_rules! compile_prims(
    ($(($ty:ty, $cast: ty) => ($type_name:ident, $make_constant:ident)),+) => (
        $(compile_prim!($ty, $type_name, $make_constant, $cast);)+
    );
);

macro_rules! native_ref(
    ($name:ident, $field:ident, $pointer_ty:ty) => (
        impl PartialEq for $name {
            fn eq(&self, other: &$name) -> bool {
                self.$field == other.$field
            }
        }
        impl Eq for $name {}
        impl NativeRef for $name {
            #[inline(always)]
            /// Convert to a native pointer
            unsafe fn as_ptr(&self) -> $pointer_ty {
                self.$field
            }
            #[inline(always)]
            /// Convert from a native pointer
            unsafe fn from_ptr(ptr:$pointer_ty) -> $name {
                $name {
                    $field: ptr
                }
            }
        }
    );
    ($name:ident, $field:ident, $pointer_ty:ty, $lifetime:ident) => (
        impl<'a> PartialEq for $name<'a> {
            fn eq(&self, other: &$name<'a>) -> bool {
                self.$field == other.$field
            }
        }
        impl<'a> Eq for $name<'a> {}
        impl<'a> NativeRef for $name<'a> {
            #[inline]
            /// Convert to a native pointer
            unsafe fn as_ptr(&self) -> $pointer_ty {
                self.$field
            }
            #[inline]
            /// Convert from a native pointer
            unsafe fn from_ptr(ptr:$pointer_ty) -> $name<'a> {
                $name {
                    $field: ptr,
                    marker: $lifetime::<'a>
                }
            }
        }
    )
);
macro_rules! builtin_type(
    ($c_name:ident -> $rust_name:ident) => (
        #[allow(missing_copy_implementations)]
        #[allow(non_camel_case_types)]
        #[allow(dead_code)]
        pub struct $rust_name;
        impl Copy for $rust_name {}
        impl ::std::ops::Deref for $rust_name {
            type Target = Type;
            fn deref(&self) -> &Type {
                use std::mem;
                unsafe { mem::transmute(&$c_name) }
            }
        }
        impl ::types::StaticType for $rust_name {
            #[inline(always)]
            fn get(self) -> Type {
                use util::from_ptr;
                unsafe { from_ptr($c_name) }
            }
        }
    )
);
macro_rules! builtin_types(
    ($($c_name:ident -> $rust_name:ident);+) => (
        $(builtin_type!($c_name -> $rust_name);)+
    )
);<|endoftext|>


<commit_before>/// Return early with an error.
///
/// This macro is equivalent to `return Err(`[`anyhow!($args...)`][anyhow!]`)`.
///
/// The surrounding function's or closure's return value is required to be
/// `Result<_,`[`anyhow::Error`][crate::Error]`>`.
///
/// # Example
///
/// ```
/// # use anyhow::{bail, Result};
/// #
/// # fn has_permission(user: usize, resource: usize) -> bool {
/// #     true
/// # }
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #     let resource = 0;
/// #
/// if !has_permission(user, resource) {
///     bail!("permission denied for accessing {}", resource);
/// }
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{bail, Result};
/// # use thiserror::Error;
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error("recursion limit exceeded")]
///     RecursionLimitExceeded,
///     # #[error("...")]
///     # More = (stringify! {
///     ...
///     # }, 1).1,
/// }
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// if depth > MAX_DEPTH {
///     bail!(ScienceError::RecursionLimitExceeded);
/// }
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! bail {
    ($msg:literal $(,)?) => {
        return $crate::private::Err($crate::anyhow!($msg));
    };
    ($err:expr $(,)?) => {
        return $crate::private::Err($crate::anyhow!($err));
    };
    ($fmt:expr, $($arg:tt)*) => {
        return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
    };
}

/// Return early with an error if a condition is not satisfied.
///
/// This macro is equivalent to `if !$cond { return
/// Err(`[`anyhow!($args...)`][anyhow!]`); }`.
///
/// The surrounding function's or closure's return value is required to be
/// `Result<_,`[`anyhow::Error`][crate::Error]`>`.
///
/// Analogously to `assert!`, `ensure!` takes a condition and exits the function
/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`
/// rather than panicking.
///
/// # Example
///
/// ```
/// # use anyhow::{ensure, Result};
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #
/// ensure!(user == 0, "only user 0 is allowed");
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{ensure, Result};
/// # use thiserror::Error;
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error("recursion limit exceeded")]
///     RecursionLimitExceeded,
///     # #[error("...")]
///     # More = (stringify! {
///     ...
///     # }, 1).1,
/// }
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! ensure {
    ($cond:expr, $msg:literal $(,)?) => {
        if !$cond {
            return $crate::private::Err($crate::anyhow!($msg));
        }
    };
    ($cond:expr, $err:expr $(,)?) => {
        if !$cond {
            return $crate::private::Err($crate::anyhow!($err));
        }
    };
    ($cond:expr, $fmt:expr, $($arg:tt)*) => {
        if !$cond {
            return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
        }
    };
}

/// Construct an ad-hoc error from a string or existing non-`anyhow` error
/// value.
///
/// This evaluates to an [`Error`][crate::Error]. It can take either just a
/// string, or a format string with arguments. It also can take any custom type
/// which implements `Debug` and `Display`.
///
/// If called with a single argument whose type implements `std::error::Error`
/// (in addition to `Debug` and `Display`, which are always required), then that
/// Error impl's `source` is preserved as the `source` of the resulting
/// `anyhow::Error`.
///
/// # Example
///
/// ```
/// # type V = ();
/// #
/// use anyhow::{anyhow, Result};
///
/// fn lookup(key: &str) -> Result<V> {
///     if key.len() != 16 {
///         return Err(anyhow!("key length must be 16 characters, got {:?}", key));
///     }
///
///     // ...
///     # Ok(())
/// }
/// ```
#[macro_export]
macro_rules! anyhow {
    ($msg:literal $(,)?) => {
        // Handle $:literal as a special case to make cargo-expanded code more
        // concise in the common case.
        $crate::private::new_adhoc($msg)
    };
    ($err:expr $(,)?) => ({
        use $crate::private::kind::*;
        let error = $err;
        (&error).anyhow_kind().new(error)
    });
    ($fmt:expr, $($arg:tt)*) => {
        $crate::private::new_adhoc(format!($fmt, $($arg)*))
    };
}
<commit_msg>Don't emit trailing semicolon from `bail!`<commit_after>/// Return early with an error.
///
/// This macro is equivalent to `return Err(`[`anyhow!($args...)`][anyhow!]`)`.
///
/// The surrounding function's or closure's return value is required to be
/// `Result<_,`[`anyhow::Error`][crate::Error]`>`.
///
/// # Example
///
/// ```
/// # use anyhow::{bail, Result};
/// #
/// # fn has_permission(user: usize, resource: usize) -> bool {
/// #     true
/// # }
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #     let resource = 0;
/// #
/// if !has_permission(user, resource) {
///     bail!("permission denied for accessing {}", resource);
/// }
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{bail, Result};
/// # use thiserror::Error;
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error("recursion limit exceeded")]
///     RecursionLimitExceeded,
///     # #[error("...")]
///     # More = (stringify! {
///     ...
///     # }, 1).1,
/// }
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// if depth > MAX_DEPTH {
///     bail!(ScienceError::RecursionLimitExceeded);
/// }
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! bail {
    ($msg:literal $(,)?) => {
        return $crate::private::Err($crate::anyhow!($msg))
    };
    ($err:expr $(,)?) => {
        return $crate::private::Err($crate::anyhow!($err))
    };
    ($fmt:expr, $($arg:tt)*) => {
        return $crate::private::Err($crate::anyhow!($fmt, $($arg)*))
    };
}

/// Return early with an error if a condition is not satisfied.
///
/// This macro is equivalent to `if !$cond { return
/// Err(`[`anyhow!($args...)`][anyhow!]`); }`.
///
/// The surrounding function's or closure's return value is required to be
/// `Result<_,`[`anyhow::Error`][crate::Error]`>`.
///
/// Analogously to `assert!`, `ensure!` takes a condition and exits the function
/// if the condition fails. Unlike `assert!`, `ensure!` returns an `Error`
/// rather than panicking.
///
/// # Example
///
/// ```
/// # use anyhow::{ensure, Result};
/// #
/// # fn main() -> Result<()> {
/// #     let user = 0;
/// #
/// ensure!(user == 0, "only user 0 is allowed");
/// #     Ok(())
/// # }
/// ```
///
/// ```
/// # use anyhow::{ensure, Result};
/// # use thiserror::Error;
/// #
/// # const MAX_DEPTH: usize = 1;
/// #
/// #[derive(Error, Debug)]
/// enum ScienceError {
///     #[error("recursion limit exceeded")]
///     RecursionLimitExceeded,
///     # #[error("...")]
///     # More = (stringify! {
///     ...
///     # }, 1).1,
/// }
///
/// # fn main() -> Result<()> {
/// #     let depth = 0;
/// #
/// ensure!(depth <= MAX_DEPTH, ScienceError::RecursionLimitExceeded);
/// #     Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! ensure {
    ($cond:expr, $msg:literal $(,)?) => {
        if !$cond {
            return $crate::private::Err($crate::anyhow!($msg));
        }
    };
    ($cond:expr, $err:expr $(,)?) => {
        if !$cond {
            return $crate::private::Err($crate::anyhow!($err));
        }
    };
    ($cond:expr, $fmt:expr, $($arg:tt)*) => {
        if !$cond {
            return $crate::private::Err($crate::anyhow!($fmt, $($arg)*));
        }
    };
}

/// Construct an ad-hoc error from a string or existing non-`anyhow` error
/// value.
///
/// This evaluates to an [`Error`][crate::Error]. It can take either just a
/// string, or a format string with arguments. It also can take any custom type
/// which implements `Debug` and `Display`.
///
/// If called with a single argument whose type implements `std::error::Error`
/// (in addition to `Debug` and `Display`, which are always required), then that
/// Error impl's `source` is preserved as the `source` of the resulting
/// `anyhow::Error`.
///
/// # Example
///
/// ```
/// # type V = ();
/// #
/// use anyhow::{anyhow, Result};
///
/// fn lookup(key: &str) -> Result<V> {
///     if key.len() != 16 {
///         return Err(anyhow!("key length must be 16 characters, got {:?}", key));
///     }
///
///     // ...
///     # Ok(())
/// }
/// ```
#[macro_export]
macro_rules! anyhow {
    ($msg:literal $(,)?) => {
        // Handle $:literal as a special case to make cargo-expanded code more
        // concise in the common case.
        $crate::private::new_adhoc($msg)
    };
    ($err:expr $(,)?) => ({
        use $crate::private::kind::*;
        let error = $err;
        (&error).anyhow_kind().new(error)
    });
    ($fmt:expr, $($arg:tt)*) => {
        $crate::private::new_adhoc(format!($fmt, $($arg)*))
    };
}
<|endoftext|>


<commit_before>/**
Gets a reference to a `Class`.

Panics if no class with the given name can be found.
To check for a class that may not exist, use `Class::get`.

# Example
``` no_run
# #[macro_use] extern crate objc;
# fn main() {
let cls = class!(NSObject);
# }
```
*/
#[macro_export]
macro_rules! class {
    ($name:ident) => ({
        #[inline(always)]
        fn get_class(name: &str) -> Option<&'static $crate::runtime::Class> {
            unsafe {
                static CLASS: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
                // `Relaxed` should be fine since `objc_getClass` is thread-safe.
                let ptr = CLASS.load(::std::sync::atomic::Ordering::Relaxed) as *const $crate::runtime::Class;
                if ptr.is_null() {
                    let cls = $crate::runtime::objc_getClass(name.as_ptr() as *const _);
                    CLASS.store(cls as usize, ::std::sync::atomic::Ordering::Relaxed);
                    if cls.is_null() { None } else { Some(&*cls) }
                } else {
                    Some(&*ptr)
                }
            }
        }
        match get_class(concat!(stringify!($name), '\0')) {
            Some(cls) => cls,
            None => panic!("Class with name {} could not be found", stringify!($name)),
        }
    })
}

#[doc(hidden)]
#[macro_export]
macro_rules! sel_impl {
    // Declare a function to hide unsafety, otherwise we can trigger the
    // unused_unsafe lint; see rust-lang/rust#8472
    ($name:expr) => ({
        #[inline(always)]
        fn register_sel(name: &str) -> $crate::runtime::Sel {
            unsafe {
                static SEL: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
                let ptr = SEL.load(::std::sync::atomic::Ordering::Relaxed) as *const ::std::os::raw::c_void;
                // It should be fine to use `Relaxed` ordering here because `sel_registerName` is
                // thread-safe.
                if ptr.is_null() {
                    let sel = $crate::runtime::sel_registerName(name.as_ptr() as *const _);
                    SEL.store(sel.as_ptr() as usize, ::std::sync::atomic::Ordering::Relaxed);
                    sel
                } else {
                    $crate::runtime::Sel::from_ptr(ptr)
                }
            }
        }
        register_sel($name)
    })
}

/**
Registers a selector, returning a `Sel`.

# Example
```
# #[macro_use] extern crate objc;
# fn main() {
let sel = sel!(description);
let sel = sel!(setObject:forKey:);
# }
```
*/
#[macro_export]
macro_rules! sel {
    ($name:ident) => ({sel_impl!(concat!(stringify!($name), '\0'))});
    ($($name:ident :)+) => ({sel_impl!(concat!($(stringify!($name), ':'),+, '\0'))});
}

/**
Sends a message to an object.

The first argument can be any type that dereferences to a type that implements
`Message`, like a reference, pointer, or an `Id`.
The syntax is similar to the message syntax in Objective-C.
Variadic arguments are not currently supported.

# Example
``` no_run
# #[macro_use] extern crate objc;
# use objc::runtime::Object;
# fn main() {
# unsafe {
let obj: *mut Object;
# let obj: *mut Object = 0 as *mut Object;
let description: *const Object = msg_send![obj, description];
let _: () = msg_send![obj, setArg1:1 arg2:2];
# }
# }
```
*/
#[macro_export]
macro_rules! msg_send {
    (super($obj:expr, $superclass:expr), $name:ident) => ({
        let sel = sel!($name);
        match $crate::__send_super_message(&*$obj, $superclass, sel, ()) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
    (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({
        let sel = sel!($($name:)+);
        match $crate::__send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
    ($obj:expr, $name:ident) => ({
        let sel = sel!($name);
        match $crate::__send_message(&*$obj, sel, ()) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
    ($obj:expr, $($name:ident : $arg:expr)+) => ({
        let sel = sel!($($name:)+);
        match $crate::__send_message(&*$obj, sel, ($($arg,)*)) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
}
<commit_msg>Use a cfg-attr to suppress the warning for using ATOMIC_USIZE_INIT in clippy<commit_after>/**
Gets a reference to a `Class`.

Panics if no class with the given name can be found.
To check for a class that may not exist, use `Class::get`.

# Example
``` no_run
# #[macro_use] extern crate objc;
# fn main() {
let cls = class!(NSObject);
# }
```
*/
#[macro_export]
macro_rules! class {
    ($name:ident) => ({
        #[inline(always)]
        fn get_class(name: &str) -> Option<&'static $crate::runtime::Class> {
            unsafe {
                #[cfg_attr(feature = "cargo-clippy", allow(replace_consts))]
                static CLASS: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
                // `Relaxed` should be fine since `objc_getClass` is thread-safe.
                let ptr = CLASS.load(::std::sync::atomic::Ordering::Relaxed) as *const $crate::runtime::Class;
                if ptr.is_null() {
                    let cls = $crate::runtime::objc_getClass(name.as_ptr() as *const _);
                    CLASS.store(cls as usize, ::std::sync::atomic::Ordering::Relaxed);
                    if cls.is_null() { None } else { Some(&*cls) }
                } else {
                    Some(&*ptr)
                }
            }
        }
        match get_class(concat!(stringify!($name), '\0')) {
            Some(cls) => cls,
            None => panic!("Class with name {} could not be found", stringify!($name)),
        }
    })
}

#[doc(hidden)]
#[macro_export]
macro_rules! sel_impl {
    // Declare a function to hide unsafety, otherwise we can trigger the
    // unused_unsafe lint; see rust-lang/rust#8472
    ($name:expr) => ({
        #[inline(always)]
        fn register_sel(name: &str) -> $crate::runtime::Sel {
            unsafe {
                #[cfg_attr(feature = "cargo-clippy", allow(replace_consts))]
                static SEL: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
                let ptr = SEL.load(::std::sync::atomic::Ordering::Relaxed) as *const ::std::os::raw::c_void;
                // It should be fine to use `Relaxed` ordering here because `sel_registerName` is
                // thread-safe.
                if ptr.is_null() {
                    let sel = $crate::runtime::sel_registerName(name.as_ptr() as *const _);
                    SEL.store(sel.as_ptr() as usize, ::std::sync::atomic::Ordering::Relaxed);
                    sel
                } else {
                    $crate::runtime::Sel::from_ptr(ptr)
                }
            }
        }
        register_sel($name)
    })
}

/**
Registers a selector, returning a `Sel`.

# Example
```
# #[macro_use] extern crate objc;
# fn main() {
let sel = sel!(description);
let sel = sel!(setObject:forKey:);
# }
```
*/
#[macro_export]
macro_rules! sel {
    ($name:ident) => ({sel_impl!(concat!(stringify!($name), '\0'))});
    ($($name:ident :)+) => ({sel_impl!(concat!($(stringify!($name), ':'),+, '\0'))});
}

/**
Sends a message to an object.

The first argument can be any type that dereferences to a type that implements
`Message`, like a reference, pointer, or an `Id`.
The syntax is similar to the message syntax in Objective-C.
Variadic arguments are not currently supported.

# Example
``` no_run
# #[macro_use] extern crate objc;
# use objc::runtime::Object;
# fn main() {
# unsafe {
let obj: *mut Object;
# let obj: *mut Object = 0 as *mut Object;
let description: *const Object = msg_send![obj, description];
let _: () = msg_send![obj, setArg1:1 arg2:2];
# }
# }
```
*/
#[macro_export]
macro_rules! msg_send {
    (super($obj:expr, $superclass:expr), $name:ident) => ({
        let sel = sel!($name);
        match $crate::__send_super_message(&*$obj, $superclass, sel, ()) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
    (super($obj:expr, $superclass:expr), $($name:ident : $arg:expr)+) => ({
        let sel = sel!($($name:)+);
        match $crate::__send_super_message(&*$obj, $superclass, sel, ($($arg,)*)) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
    ($obj:expr, $name:ident) => ({
        let sel = sel!($name);
        match $crate::__send_message(&*$obj, sel, ()) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
    ($obj:expr, $($name:ident : $arg:expr)+) => ({
        let sel = sel!($($name:)+);
        match $crate::__send_message(&*$obj, sel, ($($arg,)*)) {
            Err(s) => panic!("{}", s),
            Ok(r) => r,
        }
    });
}
<|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 matches::matches;
use std::mem::MaybeUninit;

/// Expands to a `match` expression with string patterns,
/// matching case-insensitively in the ASCII range.
///
/// The patterns must not contain ASCII upper case letters. (They must be already be lower-cased.)
///
/// # Example
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everythig in its own main
/// # fn dummy(function_name: &String) { let _ =
/// match_ignore_ascii_case! { &function_name,
///     "rgb" => parse_rgb(..),
///     "rgba" => parse_rgba(..),
///     "hsl" => parse_hsl(..),
///     "hsla" => parse_hsla(..),
///     _ => Err(format!("unknown function: {}", function_name))
/// }
/// # ;}
/// # use std::ops::RangeFull;
/// # fn parse_rgb(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_rgba(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsl(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsla(_: RangeFull) -> Result<(), String> { Ok(()) }
/// ```
#[macro_export]
macro_rules! match_ignore_ascii_case {
    ( $input:expr,
        $(
            $( $pattern: pat )|+ $( if $guard: expr )? => $then: expr
        ),+
        $(,)?
    ) => {
        {
            // This dummy module works around the feature gate
            // `error[E0658]: procedural macros cannot be expanded to statements`
            // by forcing the macro to be in an item context
            // rather than expression/statement context,
            // even though the macro only expands to items.
            mod cssparser_internal {
                $crate::cssparser_internal__match_ignore_ascii_case__support! {
                    $( $( $pattern )+ )+
                }
            }
            cssparser_internal__to_lowercase!($input, cssparser_internal::MAX_LENGTH => lowercase);
            // "A" is a short string that we know is different for every string pattern,
            // since we’ve verified that none of them include ASCII upper case letters.
            match lowercase.unwrap_or("A") {
                $(
                    $( $pattern )|+ $( if $guard )? => $then,
                )+
            }
        }
    };
}

/// Define a function `$name(&str) -> Option<&'static $ValueType>`
///
/// The function finds a match for the input string
/// in a [`phf` map](https://github.com/sfackler/rust-phf)
/// and returns a reference to the corresponding value.
/// Matching is case-insensitive in the ASCII range.
///
/// ## Example:
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everything in its own main
///
/// fn color_rgb(input: &str) -> Option<(u8, u8, u8)> {
///     ascii_case_insensitive_phf_map! {
///         keyword -> (u8, u8, u8) = {
///             "red" => (255, 0, 0),
///             "green" => (0, 255, 0),
///             "blue" => (0, 0, 255),
///         }
///     }
///     keyword(input).cloned()
/// }
#[macro_export]
macro_rules! ascii_case_insensitive_phf_map {
    ($name: ident -> $ValueType: ty = { $( $key: tt => $value: expr ),+ }) => {
        ascii_case_insensitive_phf_map!($name -> $ValueType = { $( $key => $value, )+ })
    };
    ($name: ident -> $ValueType: ty = { $( $key: tt => $value: expr, )+ }) => {
        pub fn $name(input: &str) -> Option<&'static $ValueType> {
            // This dummy module works around a feature gate,
            // see comment on the similar module in `match_ignore_ascii_case!` above.
            mod cssparser_internal {
                use $crate::_internal__phf::{Map, phf_map};
                #[allow(unused)] use super::*;
                $crate::cssparser_internal__ascii_case_insensitive_phf_map__support! {
                    $ValueType $( $key $value )+
                }
            }
            cssparser_internal__to_lowercase!(input, cssparser_internal::MAX_LENGTH => lowercase);
            lowercase.and_then(|s| cssparser_internal::MAP.get(s))
        }
    }
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This macro is not part of the public API. It can change or be removed between any versions.**
///
/// Define a local variable named `$output`
/// and assign it the result of calling `_internal__to_lowercase`
/// with a stack-allocated buffer of length `$BUFFER_SIZE`.
#[macro_export]
#[doc(hidden)]
macro_rules! cssparser_internal__to_lowercase {
    ($input: expr, $BUFFER_SIZE: expr => $output: ident) => {
        #[allow(unsafe_code)]
        let mut buffer = unsafe {
            ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit<u8>; $BUFFER_SIZE]>::uninit()
                .assume_init()
        };
        let input: &str = $input;
        let $output = $crate::_internal__to_lowercase(&mut buffer, input);
    };
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This function is not part of the public API. It can change or be removed between any verisons.**
///
/// If `input` is larger than buffer, return `None`.
/// Otherwise, return `input` ASCII-lowercased, using `buffer` as temporary space if necessary.
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn _internal__to_lowercase<'a>(
    buffer: &'a mut [MaybeUninit<u8>],
    input: &'a str,
) -> Option<&'a str> {
    if let Some(buffer) = buffer.get_mut(..input.len()) {
        if let Some(first_uppercase) = input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) {
            unsafe {
                // This cast doesn’t change the pointer’s validity
                // since `u8` has the same layout as `MaybeUninit<u8>`:
                let input_bytes = &*(input.as_bytes() as *const [u8] as *const [MaybeUninit<u8>]);

                buffer.copy_from_slice(&*input_bytes);

                // Same as above re layout, plus these bytes have been initialized:
                let buffer = &mut *(buffer as *mut [MaybeUninit<u8>] as *mut [u8]);

                buffer[first_uppercase..].make_ascii_lowercase();
                // `buffer` was initialized to a copy of `input`
                // (which is `&str` so well-formed UTF-8)
                // then ASCII-lowercased (which preserves UTF-8 well-formedness):
                Some(::std::str::from_utf8_unchecked(buffer))
            }
        } else {
            // Input is already lower-case
            Some(input)
        }
    } else {
        // Input is longer than buffer, which has the length of the longest expected string:
        // none of the expected strings would match.
        None
    }
}

#[cfg(feature = "dummy_match_byte")]
macro_rules! match_byte {
    ($value:expr, $($rest:tt)* ) => {
        match $value {
            $(
                $rest
            )+
        }
    };
}
<commit_msg>Add support for attributes on match arms in match_ignore_ascii_case!<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 matches::matches;
use std::mem::MaybeUninit;

/// Expands to a `match` expression with string patterns,
/// matching case-insensitively in the ASCII range.
///
/// The patterns must not contain ASCII upper case letters. (They must be already be lower-cased.)
///
/// # Example
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everythig in its own main
/// # fn dummy(function_name: &String) { let _ =
/// match_ignore_ascii_case! { &function_name,
///     "rgb" => parse_rgb(..),
/// #   #[cfg(not(something))]
///     "rgba" => parse_rgba(..),
///     "hsl" => parse_hsl(..),
///     "hsla" => parse_hsla(..),
///     _ => Err(format!("unknown function: {}", function_name))
/// }
/// # ;}
/// # use std::ops::RangeFull;
/// # fn parse_rgb(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_rgba(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsl(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsla(_: RangeFull) -> Result<(), String> { Ok(()) }
/// ```
#[macro_export]
macro_rules! match_ignore_ascii_case {
    ( $input:expr,
        $(
            $( #[$meta: meta] )*
            $( $pattern: pat )|+ $( if $guard: expr )? => $then: expr
        ),+
        $(,)?
    ) => {
        {
            // This dummy module works around the feature gate
            // `error[E0658]: procedural macros cannot be expanded to statements`
            // by forcing the macro to be in an item context
            // rather than expression/statement context,
            // even though the macro only expands to items.
            mod cssparser_internal {
                $crate::cssparser_internal__match_ignore_ascii_case__support! {
                    $( $( $pattern )+ )+
                }
            }
            cssparser_internal__to_lowercase!($input, cssparser_internal::MAX_LENGTH => lowercase);
            // "A" is a short string that we know is different for every string pattern,
            // since we’ve verified that none of them include ASCII upper case letters.
            match lowercase.unwrap_or("A") {
                $(
                    $( #[$meta] )*
                    $( $pattern )|+ $( if $guard )? => $then,
                )+
            }
        }
    };
}

/// Define a function `$name(&str) -> Option<&'static $ValueType>`
///
/// The function finds a match for the input string
/// in a [`phf` map](https://github.com/sfackler/rust-phf)
/// and returns a reference to the corresponding value.
/// Matching is case-insensitive in the ASCII range.
///
/// ## Example:
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everything in its own main
///
/// fn color_rgb(input: &str) -> Option<(u8, u8, u8)> {
///     ascii_case_insensitive_phf_map! {
///         keyword -> (u8, u8, u8) = {
///             "red" => (255, 0, 0),
///             "green" => (0, 255, 0),
///             "blue" => (0, 0, 255),
///         }
///     }
///     keyword(input).cloned()
/// }
#[macro_export]
macro_rules! ascii_case_insensitive_phf_map {
    ($name: ident -> $ValueType: ty = { $( $key: tt => $value: expr ),+ }) => {
        ascii_case_insensitive_phf_map!($name -> $ValueType = { $( $key => $value, )+ })
    };
    ($name: ident -> $ValueType: ty = { $( $key: tt => $value: expr, )+ }) => {
        pub fn $name(input: &str) -> Option<&'static $ValueType> {
            // This dummy module works around a feature gate,
            // see comment on the similar module in `match_ignore_ascii_case!` above.
            mod cssparser_internal {
                use $crate::_internal__phf::{Map, phf_map};
                #[allow(unused)] use super::*;
                $crate::cssparser_internal__ascii_case_insensitive_phf_map__support! {
                    $ValueType $( $key $value )+
                }
            }
            cssparser_internal__to_lowercase!(input, cssparser_internal::MAX_LENGTH => lowercase);
            lowercase.and_then(|s| cssparser_internal::MAP.get(s))
        }
    }
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This macro is not part of the public API. It can change or be removed between any versions.**
///
/// Define a local variable named `$output`
/// and assign it the result of calling `_internal__to_lowercase`
/// with a stack-allocated buffer of length `$BUFFER_SIZE`.
#[macro_export]
#[doc(hidden)]
macro_rules! cssparser_internal__to_lowercase {
    ($input: expr, $BUFFER_SIZE: expr => $output: ident) => {
        #[allow(unsafe_code)]
        let mut buffer = unsafe {
            ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit<u8>; $BUFFER_SIZE]>::uninit()
                .assume_init()
        };
        let input: &str = $input;
        let $output = $crate::_internal__to_lowercase(&mut buffer, input);
    };
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This function is not part of the public API. It can change or be removed between any verisons.**
///
/// If `input` is larger than buffer, return `None`.
/// Otherwise, return `input` ASCII-lowercased, using `buffer` as temporary space if necessary.
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn _internal__to_lowercase<'a>(
    buffer: &'a mut [MaybeUninit<u8>],
    input: &'a str,
) -> Option<&'a str> {
    if let Some(buffer) = buffer.get_mut(..input.len()) {
        if let Some(first_uppercase) = input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) {
            unsafe {
                // This cast doesn’t change the pointer’s validity
                // since `u8` has the same layout as `MaybeUninit<u8>`:
                let input_bytes = &*(input.as_bytes() as *const [u8] as *const [MaybeUninit<u8>]);

                buffer.copy_from_slice(&*input_bytes);

                // Same as above re layout, plus these bytes have been initialized:
                let buffer = &mut *(buffer as *mut [MaybeUninit<u8>] as *mut [u8]);

                buffer[first_uppercase..].make_ascii_lowercase();
                // `buffer` was initialized to a copy of `input`
                // (which is `&str` so well-formed UTF-8)
                // then ASCII-lowercased (which preserves UTF-8 well-formedness):
                Some(::std::str::from_utf8_unchecked(buffer))
            }
        } else {
            // Input is already lower-case
            Some(input)
        }
    } else {
        // Input is longer than buffer, which has the length of the longest expected string:
        // none of the expected strings would match.
        None
    }
}

#[cfg(feature = "dummy_match_byte")]
macro_rules! match_byte {
    ($value:expr, $($rest:tt)* ) => {
        match $value {
            $(
                $rest
            )+
        }
    };
}
<|endoftext|>


<commit_before>
use std::io::{Read, Seek, Result};
use std::collections::HashMap;
use enum_primitive::FromPrimitive;
use byteorder::{ReadBytesExt, LittleEndian};

use utils::stream::*;
use metadata::heap::{StringHeap, UserStringHeap, UserString};
use loader::tables::{ModuleEntry, TableEntryReader};

#[derive(Debug, Clone)]
pub struct StreamHeader {
  pub offset: u32,
  pub size: u32,
  pub name: String
}

impl ReadableStruct for StreamHeader {
  fn read_from<R: Read + Seek>(reader: &mut R) -> Result<StreamHeader> {
    let offset = reader.read_u32::<LittleEndian>()?;
    let size = reader.read_u32::<LittleEndian>()?;
    let name = reader.read_c_str_aligned(4)?;

    Ok(StreamHeader { offset, size, name })
  }
}

/// The #~ stream.
#[derive(Debug, Clone)]
pub struct MetaDataTablesStream {

}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum IndexSize {
  Word,
  Dword
}

impl IndexSize {
  pub fn to_u32(self) -> u32 {
    match self {
      IndexSize::Word => u16::max_value() as u32,
      IndexSize::Dword => u32::max_value()
    }
  }
}

impl From<bool> for IndexSize {
  fn from(x: bool) -> IndexSize {
    if x {IndexSize::Dword} else {IndexSize::Word}
  }
}

#[derive(Debug)]
pub struct HeapOffsetSizes {
  pub string_index: IndexSize,
  pub guid_index: IndexSize,
  pub blob_index: IndexSize
}

enum_from_primitive! {
  #[derive(Hash, PartialEq, Eq, Debug, Copy, Clone)]
  pub enum TableId {
    Module                  = 00,
    Field                   = 04,
    InterfaceImpl           = 09,
    CustomAttribute         = 12,
    ClassLayout             = 15,
    EventMap                = 18,
    Property                = 23,
    ModuleRef               = 26,
    FieldRVA                = 29,
    AssemblyOS              = 34,
    AssemblyRefOS           = 37,
    ManifestResource        = 40,
    GenericParamConstraint  = 44,
    TypeRef                 = 01,
    TypeDef                 = 02,
    MethodDef               = 06,
    Param                   = 08,
    MemberRef               = 10,
    Constant                = 11,
    FieldMarshal            = 13,
    DeclSecurity            = 14,
    FieldLayout             = 16,
    StandAloneSig           = 17,
    Event                   = 20,
    PropertyMap             = 21,
    MethodSemantics         = 24,
    MethodImpl              = 25,
    TypeSpec                = 27,
    ImplMap                 = 28,
    Assembly                = 32,
    AssemblyProcessor       = 33,
    AssemblyRef             = 35,
    AssemblyRefProcessor    = 36,
    File                    = 38,
    ExportedType            = 39,
    NestedClass             = 41,
    GenericParam            = 42
  }
}

#[derive(Debug)]
pub struct TableIds(Vec<TableId>);

impl From<u64> for TableIds {
  fn from(x: u64) -> TableIds {
    let mut table_ids = vec![];

    for i in 0 .. 64  {
      if ((1 << i) & x) > 0 {
        let table_id_opt = TableId::from_u8(i);
        if let Some(table_id) = table_id_opt {
          table_ids.push(table_id);
        } else {
          panic!("Unknown metadata table: {:02x}", i);
        }
      }
    }

    TableIds(table_ids)
  }
}

pub type RowCounts = HashMap<TableId, u32>;
pub type IndexSizes = HashMap<TableId, IndexSize>;

impl From<u8> for HeapOffsetSizes {
  fn from(x: u8) -> HeapOffsetSizes {
    let str_index_bit = (x & 0b1) > 0;
    let guid_index_bit = (x & 0b10) > 0;
    let blob_index_bit = (x & 0b100) > 0;

    HeapOffsetSizes {
      string_index: IndexSize::from(str_index_bit),
      guid_index: IndexSize::from(guid_index_bit),
      blob_index: IndexSize::from(blob_index_bit)
    }
  }
}

impl MetaDataTablesStream {
  fn get_index_sizes(row_counts: &HashMap<TableId, u32>) -> IndexSizes {
    row_counts.iter().map(|(&k, &v)|
      (k.clone(), if v > (u16::max_value() as u32) { IndexSize::Dword } else { IndexSize::Word } )).collect()
  }
}

impl StreamReader for MetaDataTablesStream {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<MetaDataTablesStream> {
    // Reserved, not used
    assert_eq!(0, reader.read_u32::<LittleEndian>()?);

    let major_version = reader.read_u8()?;
    let minor_version = reader.read_u8()?;
    println!("#~ version: {}.{}", major_version, minor_version);

    let heap_offset_sizes = HeapOffsetSizes::from(reader.read_u8()?);
    println!("{:?}", heap_offset_sizes);

    // Reserved, not used. Specified to always be 1, but mcs seems to emit binaries with it set to 0x10.
    reader.read_u8()?;

    let table_ids = TableIds::from(reader.read_u64::<LittleEndian>()?);
    println!("Metadata tables: {:?}", table_ids);

    let sorted_table_ids = TableIds::from(reader.read_u64::<LittleEndian>()?);
    println!("Metadata tables (sorted): {:?}", sorted_table_ids);

    let mut table_row_counts_vec = vec![];

    for table_id in table_ids.0 {
      let table_count = reader.read_u32::<LittleEndian>()?;
      table_row_counts_vec.push((table_id, table_count));
    }

    let table_rows_counts = table_row_counts_vec.into_iter().collect::<HashMap<TableId, u32>>();
    println!("Metadata table row counts: {:?}", table_rows_counts);

    let index_sizes = MetaDataTablesStream::get_index_sizes(&table_rows_counts);
    println!("Metadata table index sizes: {:?}", index_sizes);

    let module = ModuleEntry::read_entry(reader, heap_offset_sizes, index_sizes)?;
    println!("Module: {:?}", module);

    Ok(MetaDataTablesStream { })
  }
}

pub trait StreamReader {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<Self> where Self : Sized;
}

impl StreamReader for StringHeap {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<StringHeap> {
    let mut strings: Vec<(u32, String)> = vec![];
    let mut bytes_read: usize = 0;

    while bytes_read < (header.size as usize) {
      let start = bytes_read;
      let string_buffer = reader.read_c_str()?;
      bytes_read += string_buffer.len() + 1;
      strings.push((start as u32, string_buffer));
    }

    let string_heap = StringHeap {
      strings: strings.into_iter().collect()
    };

    Ok(string_heap)
  }
}

struct StreamUtils { }

pub struct CompressedUint {
  pub value: u32,
  pub compressed_size: u8
}

impl StreamUtils {
  // ECMA 335, page 272
  // Inspired by
  // https://github.com/jbevain/cecil/blob/505b07d6974d8405a63124139733c6fdc0e67bc7/Mono.Cecil.PE/ByteBuffer.cs#L101
  pub fn decode_compressed_int<R: Read + Seek>(reader: &mut R) -> Result<CompressedUint> {
    let first_byte = reader.read_u8()?;

    // Starts with a zero bit -> bits 1-7 are the length
    let (value, compressed_size) = if (first_byte & 0x80) == 0 {
      (first_byte as u32, 1)
    // Starts with 0b10 -> bits 2-7 + the next byte is the length
    } else if (first_byte & 0x40) == 0 {
      ((((first_byte & !0x80) as u32) << 8) | (reader.read_u8()? as u32), 2)
    // We assume the blob starts with 0b110 -> bits 3-7 + the next 3 bytes is the length
    } else {
      let mut rest = [0u8; 3];
      reader.read_exact(&mut rest)?;
      ((((first_byte & !0xc0) as u32) << 24)
        | ((rest[0] as u32) << 16)
        | ((rest[1] as u32) << 8)
        | (rest[2] as u32), 4)
    };

    Ok(CompressedUint { value, compressed_size })
  }
}

impl UserString {
  pub fn from_utf16(buffer: &[u16]) -> UserString {
    match String::from_utf16(buffer) {
      Ok(string) => UserString::Valid(string),
      _ => UserString::Garbage
    }
  }
}

impl StreamReader for UserStringHeap {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<UserStringHeap> {
    let mut strings: Vec<(u32, UserString)> = vec![];
    let mut bytes_read: usize = 0;

    // Always starts with a null byte, which is handled like any other table entry

    while bytes_read < header.size as usize {
      let start = bytes_read;
      let decoded = StreamUtils::decode_compressed_int(reader)?;
      bytes_read += decoded.compressed_size as usize;

      if decoded.value == 0 {
        strings.push((start as u32, UserString::Valid("".to_string())));
        continue;
      }

      let mut string_buffer = vec![0u16; (decoded.value / 2) as usize];
      reader.read_exact_16(&mut string_buffer)?;
      strings.push((start as u32, UserString::from_utf16(&string_buffer)));

      let is_ascii = reader.read_u8()?;
      bytes_read += decoded.value as usize;
    }

    let string_heap = UserStringHeap {
      strings: strings.into_iter().collect()
    };

    Ok(string_heap)
  }
}
<commit_msg>Add a missing table id, reorder them<commit_after>
use std::io::{Read, Seek, Result};
use std::collections::HashMap;
use enum_primitive::FromPrimitive;
use byteorder::{ReadBytesExt, LittleEndian};

use utils::stream::*;
use metadata::heap::{StringHeap, UserStringHeap, UserString};
use loader::tables::{ModuleEntry, TableEntryReader};

#[derive(Debug, Clone)]
pub struct StreamHeader {
  pub offset: u32,
  pub size: u32,
  pub name: String
}

impl ReadableStruct for StreamHeader {
  fn read_from<R: Read + Seek>(reader: &mut R) -> Result<StreamHeader> {
    let offset = reader.read_u32::<LittleEndian>()?;
    let size = reader.read_u32::<LittleEndian>()?;
    let name = reader.read_c_str_aligned(4)?;

    Ok(StreamHeader { offset, size, name })
  }
}

/// The #~ stream.
#[derive(Debug, Clone)]
pub struct MetaDataTablesStream {

}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum IndexSize {
  Word,
  Dword
}

impl IndexSize {
  pub fn to_u32(self) -> u32 {
    match self {
      IndexSize::Word => u16::max_value() as u32,
      IndexSize::Dword => u32::max_value()
    }
  }
}

impl From<bool> for IndexSize {
  fn from(x: bool) -> IndexSize {
    if x {IndexSize::Dword} else {IndexSize::Word}
  }
}

#[derive(Debug)]
pub struct HeapOffsetSizes {
  pub string_index: IndexSize,
  pub guid_index: IndexSize,
  pub blob_index: IndexSize
}

enum_from_primitive! {
  #[derive(Hash, PartialEq, Eq, Debug, Copy, Clone)]
  pub enum TableId {
    Module                  = 00,
    TypeRef                 = 01,
    TypeDef                 = 02,
    Field                   = 04,
    MethodDef               = 06,
    Param                   = 08,
    InterfaceImpl           = 09,
    MemberRef               = 10,
    Constant                = 11,
    CustomAttribute         = 12,
    FieldMarshal            = 13,
    DeclSecurity            = 14,
    ClassLayout             = 15,
    FieldLayout             = 16,
    StandAloneSig           = 17,
    EventMap                = 18,
    Event                   = 20,
    PropertyMap             = 21,
    Property                = 23,
    MethodSemantics         = 24,
    MethodImpl              = 25,
    ModuleRef               = 26,
    TypeSpec                = 27,
    ImplMap                 = 28,
    FieldRVA                = 29,
    Assembly                = 32,
    AssemblyProcessor       = 33,
    AssemblyOS              = 34,
    AssemblyRef             = 35,
    AssemblyRefProcessor    = 36,
    AssemblyRefOS           = 37,
    File                    = 38,
    ExportedType            = 39,
    ManifestResource        = 40,
    NestedClass             = 41,
    GenericParam            = 42,
    MethodSpec              = 43,
    GenericParamConstraint  = 44
  }
}

#[derive(Debug)]
pub struct TableIds(Vec<TableId>);

impl From<u64> for TableIds {
  fn from(x: u64) -> TableIds {
    let mut table_ids = vec![];

    for i in 0 .. 64  {
      if ((1 << i) & x) > 0 {
        let table_id_opt = TableId::from_u8(i);
        if let Some(table_id) = table_id_opt {
          table_ids.push(table_id);
        } else {
          panic!("Unknown metadata table: {:02x}", i);
        }
      }
    }

    TableIds(table_ids)
  }
}

pub type RowCounts = HashMap<TableId, u32>;
pub type IndexSizes = HashMap<TableId, IndexSize>;

impl From<u8> for HeapOffsetSizes {
  fn from(x: u8) -> HeapOffsetSizes {
    let str_index_bit = (x & 0b1) > 0;
    let guid_index_bit = (x & 0b10) > 0;
    let blob_index_bit = (x & 0b100) > 0;

    HeapOffsetSizes {
      string_index: IndexSize::from(str_index_bit),
      guid_index: IndexSize::from(guid_index_bit),
      blob_index: IndexSize::from(blob_index_bit)
    }
  }
}

impl MetaDataTablesStream {
  fn get_index_sizes(row_counts: &HashMap<TableId, u32>) -> IndexSizes {
    row_counts.iter().map(|(&k, &v)|
      (k.clone(), if v > (u16::max_value() as u32) { IndexSize::Dword } else { IndexSize::Word } )).collect()
  }
}

impl StreamReader for MetaDataTablesStream {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<MetaDataTablesStream> {
    // Reserved, not used
    assert_eq!(0, reader.read_u32::<LittleEndian>()?);

    let major_version = reader.read_u8()?;
    let minor_version = reader.read_u8()?;
    println!("#~ version: {}.{}", major_version, minor_version);

    let heap_offset_sizes = HeapOffsetSizes::from(reader.read_u8()?);
    println!("{:?}", heap_offset_sizes);

    // Reserved, not used. Specified to always be 1, but mcs seems to emit binaries with it set to 0x10.
    reader.read_u8()?;

    let table_ids = TableIds::from(reader.read_u64::<LittleEndian>()?);
    println!("Metadata tables: {:?}", table_ids);

    let sorted_table_ids = TableIds::from(reader.read_u64::<LittleEndian>()?);
    println!("Metadata tables (sorted): {:?}", sorted_table_ids);

    let mut table_row_counts_vec = vec![];

    for table_id in table_ids.0 {
      let table_count = reader.read_u32::<LittleEndian>()?;
      table_row_counts_vec.push((table_id, table_count));
    }

    let table_rows_counts = table_row_counts_vec.into_iter().collect::<HashMap<TableId, u32>>();
    println!("Metadata table row counts: {:?}", table_rows_counts);

    let index_sizes = MetaDataTablesStream::get_index_sizes(&table_rows_counts);
    println!("Metadata table index sizes: {:?}", index_sizes);

    let module = ModuleEntry::read_entry(reader, heap_offset_sizes, index_sizes)?;
    println!("Module: {:?}", module);

    Ok(MetaDataTablesStream { })
  }
}

pub trait StreamReader {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<Self> where Self : Sized;
}

impl StreamReader for StringHeap {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<StringHeap> {
    let mut strings: Vec<(u32, String)> = vec![];
    let mut bytes_read: usize = 0;

    while bytes_read < (header.size as usize) {
      let start = bytes_read;
      let string_buffer = reader.read_c_str()?;
      bytes_read += string_buffer.len() + 1;
      strings.push((start as u32, string_buffer));
    }

    let string_heap = StringHeap {
      strings: strings.into_iter().collect()
    };

    Ok(string_heap)
  }
}

struct StreamUtils { }

pub struct CompressedUint {
  pub value: u32,
  pub compressed_size: u8
}

impl StreamUtils {
  // ECMA 335, page 272
  // Inspired by
  // https://github.com/jbevain/cecil/blob/505b07d6974d8405a63124139733c6fdc0e67bc7/Mono.Cecil.PE/ByteBuffer.cs#L101
  pub fn decode_compressed_int<R: Read + Seek>(reader: &mut R) -> Result<CompressedUint> {
    let first_byte = reader.read_u8()?;

    // Starts with a zero bit -> bits 1-7 are the length
    let (value, compressed_size) = if (first_byte & 0x80) == 0 {
      (first_byte as u32, 1)
    // Starts with 0b10 -> bits 2-7 + the next byte is the length
    } else if (first_byte & 0x40) == 0 {
      ((((first_byte & !0x80) as u32) << 8) | (reader.read_u8()? as u32), 2)
    // We assume the blob starts with 0b110 -> bits 3-7 + the next 3 bytes is the length
    } else {
      let mut rest = [0u8; 3];
      reader.read_exact(&mut rest)?;
      ((((first_byte & !0xc0) as u32) << 24)
        | ((rest[0] as u32) << 16)
        | ((rest[1] as u32) << 8)
        | (rest[2] as u32), 4)
    };

    Ok(CompressedUint { value, compressed_size })
  }
}

impl UserString {
  pub fn from_utf16(buffer: &[u16]) -> UserString {
    match String::from_utf16(buffer) {
      Ok(string) => UserString::Valid(string),
      _ => UserString::Garbage
    }
  }
}

impl StreamReader for UserStringHeap {
  fn read_from<R: Read + Seek>(reader: &mut R, header: &StreamHeader) -> Result<UserStringHeap> {
    let mut strings: Vec<(u32, UserString)> = vec![];
    let mut bytes_read: usize = 0;

    // Always starts with a null byte, which is handled like any other table entry

    while bytes_read < header.size as usize {
      let start = bytes_read;
      let decoded = StreamUtils::decode_compressed_int(reader)?;
      bytes_read += decoded.compressed_size as usize;

      if decoded.value == 0 {
        strings.push((start as u32, UserString::Valid("".to_string())));
        continue;
      }

      let mut string_buffer = vec![0u16; (decoded.value / 2) as usize];
      reader.read_exact_16(&mut string_buffer)?;
      strings.push((start as u32, UserString::from_utf16(&string_buffer)));

      let is_ascii = reader.read_u8()?;
      bytes_read += decoded.value as usize;
    }

    let string_heap = UserStringHeap {
      strings: strings.into_iter().collect()
    };

    Ok(string_heap)
  }
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use libc::{c_char, c_int, c_void};
use std::mem;
use sys::ffi;
use ComponentExt;

pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
    ("CPU", &['T' as i8, 'C' as i8, '0' as i8, 'F' as i8]), // CPU Die "TC0F"
    (
        "CPU Proximity",
        &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8],
    ), // CPU Proximity (heat spreader) "TC0P"
    ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P"
    ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T"
];

pub struct ComponentFFI {
    input_structure: ffi::KeyData_t,
    val: ffi::Val_t,
}

impl ComponentFFI {
    fn new(key: &[i8], con: ffi::io_connect_t) -> Option<ComponentFFI> {
        unsafe { get_key_size(con, key) }
            .ok()
            .map(|(input_structure, val)| ComponentFFI {
                input_structure,
                val,
            })
    }

    fn get_temperature(&self, con: ffi::io_connect_t) -> Option<f32> {
        get_temperature_inner(con, &self.input_structure, &self.val)
    }
}

/// Struct containing a component information (temperature and name for the moment).
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    ffi_part: ComponentFFI,
    connection: ffi::io_connect_t,
}

impl Component {
    /// Creates a new `Component` with the given information.
    pub(crate) fn new(
        label: String,
        max: Option<f32>,
        critical: Option<f32>,
        key: &[i8],
        connection: ffi::io_connect_t,
    ) -> Option<Component> {
        let ffi_part = ComponentFFI::new(key, connection)?;
        ffi_part
            .get_temperature(connection)
            .map(|temperature| Component {
                temperature,
                label,
                max: max.unwrap_or(0.0),
                critical,
                ffi_part,
                connection,
            })
    }
}

impl ComponentExt for Component {
    fn get_temperature(&self) -> f32 {
        self.temperature
    }

    fn get_max(&self) -> f32 {
        self.max
    }

    fn get_critical(&self) -> Option<f32> {
        self.critical
    }

    fn get_label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if let Some(temp) = self.ffi_part.get_temperature(self.connection) {
            self.temperature = temp;
            if self.temperature > self.max {
                self.max = self.temperature;
            }
        }
    }
}

unsafe fn perform_call(
    conn: ffi::io_connect_t,
    index: c_int,
    input_structure: *const ffi::KeyData_t,
    output_structure: *mut ffi::KeyData_t,
) -> i32 {
    let mut structure_output_size = mem::size_of::<ffi::KeyData_t>();

    ffi::IOConnectCallStructMethod(
        conn,
        index as u32,
        input_structure,
        mem::size_of::<ffi::KeyData_t>(),
        output_structure,
        &mut structure_output_size,
    )
}

// Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28
#[inline]
fn strtoul(s: &[i8]) -> u32 {
    ((s[0] as u32) << (3u32 << 3))
        + ((s[1] as u32) << (2u32 << 3))
        + ((s[2] as u32) << (1u32 << 3))
        + ((s[3] as u32) << (0u32 << 3))
}

#[inline]
unsafe fn ultostr(s: *mut c_char, val: u32) {
    *s.offset(0) = ((val >> 24) % 128) as i8;
    *s.offset(1) = ((val >> 16) % 128) as i8;
    *s.offset(2) = ((val >> 8) % 128) as i8;
    *s.offset(3) = (val % 128) as i8;
    *s.offset(4) = 0;
}

unsafe fn get_key_size(
    con: ffi::io_connect_t,
    key: &[i8],
) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> {
    let mut input_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut val: ffi::Val_t = mem::zeroed::<ffi::Val_t>();

    input_structure.key = strtoul(key);
    input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO;

    let result = perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        &input_structure,
        &mut output_structure,
    );
    if result != ffi::KIO_RETURN_SUCCESS {
        return Err(result);
    }

    val.data_size = output_structure.key_info.data_size;
    ultostr(
        val.data_type.as_mut_ptr(),
        output_structure.key_info.data_type,
    );
    input_structure.key_info.data_size = val.data_size;
    input_structure.data8 = ffi::SMC_CMD_READ_BYTES;
    Ok((input_structure, val))
}

unsafe fn read_key(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    mut val: ffi::Val_t,
) -> Result<ffi::Val_t, i32> {
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();

    match perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        input_structure,
        &mut output_structure,
    ) {
        ffi::KIO_RETURN_SUCCESS => {
            libc::memcpy(
                val.bytes.as_mut_ptr() as *mut c_void,
                output_structure.bytes.as_mut_ptr() as *mut c_void,
                mem::size_of::<[u8; 32]>(),
            );
            Ok(val)
        }
        result => Err(result),
    }
}

fn get_temperature_inner(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    original_val: &ffi::Val_t,
) -> Option<f32> {
    if let Ok(val) = unsafe { read_key(con, input_structure, (*original_val).clone()) } {
        if val.data_size > 0
            && unsafe { libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) } == 0
        {
            // convert sp78 value to temperature
            let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2);
            return Some(x as f32 / 64f32);
        }
    }
    None
}

pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> {
    let (input_structure, val) = unsafe { get_key_size(con, &key) }.ok()?;
    get_temperature_inner(con, &input_structure, &val)
}
<commit_msg>Add PECI CPU temperature sensors that provide correct value on iMac and MacBook<commit_after>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use libc::{c_char, c_int, c_void};
use std::mem;
use sys::ffi;
use ComponentExt;

pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[
    ("PECI CPU", &['T' as i8, 'C' as i8, 'X' as i8, 'C' as i8]), // PECI CPU "TCXC"
    ("PECI CPU", &['T' as i8, 'C' as i8, 'X' as i8, 'c' as i8]), // PECI CPU "TCXc"
    ("CPU", &['T' as i8, 'C' as i8, '0' as i8, 'F' as i8]), // CPU Die "TC0F"
    (
        "CPU Proximity",
        &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8],
    ), // CPU Proximity (heat spreader) "TC0P"
    ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P"
    ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T"
];

pub struct ComponentFFI {
    input_structure: ffi::KeyData_t,
    val: ffi::Val_t,
}

impl ComponentFFI {
    fn new(key: &[i8], con: ffi::io_connect_t) -> Option<ComponentFFI> {
        unsafe { get_key_size(con, key) }
            .ok()
            .map(|(input_structure, val)| ComponentFFI {
                input_structure,
                val,
            })
    }

    fn get_temperature(&self, con: ffi::io_connect_t) -> Option<f32> {
        get_temperature_inner(con, &self.input_structure, &self.val)
    }
}

/// Struct containing a component information (temperature and name for the moment).
pub struct Component {
    temperature: f32,
    max: f32,
    critical: Option<f32>,
    label: String,
    ffi_part: ComponentFFI,
    connection: ffi::io_connect_t,
}

impl Component {
    /// Creates a new `Component` with the given information.
    pub(crate) fn new(
        label: String,
        max: Option<f32>,
        critical: Option<f32>,
        key: &[i8],
        connection: ffi::io_connect_t,
    ) -> Option<Component> {
        let ffi_part = ComponentFFI::new(key, connection)?;
        ffi_part
            .get_temperature(connection)
            .map(|temperature| Component {
                temperature,
                label,
                max: max.unwrap_or(0.0),
                critical,
                ffi_part,
                connection,
            })
    }
}

impl ComponentExt for Component {
    fn get_temperature(&self) -> f32 {
        self.temperature
    }

    fn get_max(&self) -> f32 {
        self.max
    }

    fn get_critical(&self) -> Option<f32> {
        self.critical
    }

    fn get_label(&self) -> &str {
        &self.label
    }

    fn refresh(&mut self) {
        if let Some(temp) = self.ffi_part.get_temperature(self.connection) {
            self.temperature = temp;
            if self.temperature > self.max {
                self.max = self.temperature;
            }
        }
    }
}

unsafe fn perform_call(
    conn: ffi::io_connect_t,
    index: c_int,
    input_structure: *const ffi::KeyData_t,
    output_structure: *mut ffi::KeyData_t,
) -> i32 {
    let mut structure_output_size = mem::size_of::<ffi::KeyData_t>();

    ffi::IOConnectCallStructMethod(
        conn,
        index as u32,
        input_structure,
        mem::size_of::<ffi::KeyData_t>(),
        output_structure,
        &mut structure_output_size,
    )
}

// Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28
#[inline]
fn strtoul(s: &[i8]) -> u32 {
    ((s[0] as u32) << (3u32 << 3))
        + ((s[1] as u32) << (2u32 << 3))
        + ((s[2] as u32) << (1u32 << 3))
        + ((s[3] as u32) << (0u32 << 3))
}

#[inline]
unsafe fn ultostr(s: *mut c_char, val: u32) {
    *s.offset(0) = ((val >> 24) % 128) as i8;
    *s.offset(1) = ((val >> 16) % 128) as i8;
    *s.offset(2) = ((val >> 8) % 128) as i8;
    *s.offset(3) = (val % 128) as i8;
    *s.offset(4) = 0;
}

unsafe fn get_key_size(
    con: ffi::io_connect_t,
    key: &[i8],
) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> {
    let mut input_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();
    let mut val: ffi::Val_t = mem::zeroed::<ffi::Val_t>();

    input_structure.key = strtoul(key);
    input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO;

    let result = perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        &input_structure,
        &mut output_structure,
    );
    if result != ffi::KIO_RETURN_SUCCESS {
        return Err(result);
    }

    val.data_size = output_structure.key_info.data_size;
    ultostr(
        val.data_type.as_mut_ptr(),
        output_structure.key_info.data_type,
    );
    input_structure.key_info.data_size = val.data_size;
    input_structure.data8 = ffi::SMC_CMD_READ_BYTES;
    Ok((input_structure, val))
}

unsafe fn read_key(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    mut val: ffi::Val_t,
) -> Result<ffi::Val_t, i32> {
    let mut output_structure: ffi::KeyData_t = mem::zeroed::<ffi::KeyData_t>();

    match perform_call(
        con,
        ffi::KERNEL_INDEX_SMC,
        input_structure,
        &mut output_structure,
    ) {
        ffi::KIO_RETURN_SUCCESS => {
            libc::memcpy(
                val.bytes.as_mut_ptr() as *mut c_void,
                output_structure.bytes.as_mut_ptr() as *mut c_void,
                mem::size_of::<[u8; 32]>(),
            );
            Ok(val)
        }
        result => Err(result),
    }
}

fn get_temperature_inner(
    con: ffi::io_connect_t,
    input_structure: &ffi::KeyData_t,
    original_val: &ffi::Val_t,
) -> Option<f32> {
    if let Ok(val) = unsafe { read_key(con, input_structure, (*original_val).clone()) } {
        if val.data_size > 0
            && unsafe { libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) } == 0
        {
            // convert sp78 value to temperature
            let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2);
            return Some(x as f32 / 64f32);
        }
    }
    None
}

pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option<f32> {
    let (input_structure, val) = unsafe { get_key_size(con, &key) }.ok()?;
    get_temperature_inner(con, &input_structure, &val)
}
<|endoftext|>


<commit_before>use super::common::sha256_hex;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{Cursor, Read, Write};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

pub trait CertificateCache: Send + Sync {
    fn store_certificate(&self, identifier: &str, chain: &str, private_key: &str);
    fn fetch_certificate(&self, identifier: &str) -> Option<(String, String)>;
}

fn domains_to_identifier(acme_url: &str, domains: &[String]) -> String {
    sha256_hex(format!("{}|{}", acme_url, domains.join("|")).as_bytes())
}

pub struct DiskCache {
    location: PathBuf,
}

impl DiskCache {
    pub fn new(location: PathBuf) -> DiskCache {
        fs::create_dir_all(&location).unwrap();
        DiskCache { location }
    }

    fn chain_path(&self, identifier: &str) -> PathBuf {
        self.location.join(identifier.to_string() + ".chain.pem")
    }

    fn key_path(&self, identifier: &str) -> PathBuf {
        self.location.join(identifier.to_string() + ".key.pem")
    }
}

impl CertificateCache for DiskCache {
    fn store_certificate(&self, identifier: &str, chain: &str, private_key: &str) {
        File::create(self.chain_path(identifier))
            .unwrap()
            .write_all(chain.as_bytes())
            .unwrap();
        File::create(self.key_path(identifier))
            .unwrap()
            .write_all(private_key.as_bytes())
            .unwrap();
    }

    fn fetch_certificate(&self, identifier: &str) -> Option<(String, String)> {
        match (
            File::open(self.chain_path(identifier)),
            File::open(self.key_path(identifier)),
        ) {
            (Ok(mut chain_file), Ok(mut key_file)) => {
                let mut chain_pem = String::new();
                chain_file.read_to_string(&mut chain_pem).unwrap();
                let mut key_pem = String::new();
                key_file.read_to_string(&mut key_pem).unwrap();
                Some((chain_pem, key_pem))
            }
            _ => None,
        }
    }
}

pub struct AutomaticCertResolver<C>
where
    C: CertificateCache,
{
    domains: Vec<String>,
    acme_url: String,
    acme_account: acme_client::Account,
    active_cert: Mutex<Option<rustls::sign::CertifiedKey>>,
    cert_cache: C,
    sni_challenges: Mutex<HashMap<String, rustls::sign::CertifiedKey>>,
}

impl<C> AutomaticCertResolver<C>
where
    C: CertificateCache,
{
    pub fn new(acme_url: &str, domains: Vec<String>, cache: C) -> AutomaticCertResolver<C> {
        let acme_directory = acme_client::Directory::from_url(acme_url).unwrap();
        let pems = cache.fetch_certificate(&domains_to_identifier(acme_url, &domains));
        let active_cert = Mutex::new(
            pems.map(|(chain_pem, private_key_pem)| pems_to_rustls(&chain_pem, &private_key_pem)),
        );
        AutomaticCertResolver {
            domains,
            cert_cache: cache,
            acme_url: acme_url.to_string(),
            acme_account: acme_directory.account_registration().register().unwrap(),
            active_cert,
            sni_challenges: Mutex::new(HashMap::new()),
        }
    }

    fn obtain_new_certificate(&self) {
        // Can't do the smart thing of setting them all up, and then triggering the validations in
        // parallel and waiting for the results because acme-client doesn't expose seperate
        // "trigger validation" and "wait for success" functions.
        for domain in &self.domains {
            let authorization = self.acme_account.authorization(domain).unwrap();
            let tls_sni_challenge = authorization.get_tls_sni_challenge().unwrap();
            self.setup_sni_challenge(tls_sni_challenge);
            tls_sni_challenge.validate().unwrap();
            self.teardown_sni_challenge(tls_sni_challenge);
        }
        let cert = self
            .acme_account
            .certificate_signer(
                &self
                    .domains
                    .iter()
                    .map(|s| s.as_ref())
                    .collect::<Vec<&str>>(),
            )
            .sign_certificate()
            .unwrap();
        // TODO: intermediates
        let chain = vec![openssl_cert_to_rustls(cert.cert())];
        let signer = openssl_pkey_to_rustls_signer(cert.pkey());
        *self.active_cert.lock().unwrap() =
            Some(rustls::sign::CertifiedKey::new(chain, Arc::new(signer)));
        self.cert_cache.store_certificate(
            &domains_to_identifier(&self.acme_url, &self.domains),
            std::str::from_utf8(&cert.cert().to_pem().unwrap()).unwrap(),
            // TODO: ECDSA
            std::str::from_utf8(&cert.pkey().rsa().unwrap().private_key_to_pem().unwrap()).unwrap(),
        );
    }

    fn setup_sni_challenge(&self, challenge: &acme_client::Challenge<'_>) {
        let z_domain = z_domain(challenge);
        let (cert, pkey) = generate_temporary_cert(&z_domain);

        let chain = vec![openssl_cert_to_rustls(&cert)];
        let signer = openssl_pkey_to_rustls_signer(&pkey);
        self.sni_challenges.lock().unwrap().insert(
            z_domain,
            rustls::sign::CertifiedKey::new(chain, Arc::new(signer)),
        );
    }

    fn teardown_sni_challenge(&self, challenge: &acme_client::Challenge<'_>) {
        let z_domain = z_domain(challenge);
        self.sni_challenges.lock().unwrap().remove(&z_domain);
    }
}

fn z_domain(challenge: &acme_client::Challenge<'_>) -> String {
    let z = sha256_hex(challenge.key_authorization().as_bytes());
    let (z1, z2) = z.split_at(32);
    return format!("{}.{}.acme.invalid", z1, z2);
}

pub fn generate_temporary_cert(
    domain: &str,
) -> (
    openssl::x509::X509,
    openssl::pkey::PKey<openssl::pkey::Private>,
) {
    let pkey = openssl::pkey::PKey::from_rsa(openssl::rsa::Rsa::generate(2048).unwrap()).unwrap();
    let mut cert_builder = openssl::x509::X509Builder::new().unwrap();
    cert_builder.set_version(2).unwrap();
    cert_builder.set_pubkey(&pkey).unwrap();

    let mut serial = openssl::bn::BigNum::new().unwrap();
    serial
        .rand(128, openssl::bn::MsbOption::MAYBE_ZERO, false)
        .unwrap();
    cert_builder
        .set_serial_number(&serial.to_asn1_integer().unwrap())
        .unwrap();

    let mut subject_builder = openssl::x509::X509NameBuilder::new().unwrap();
    subject_builder
        .append_entry_by_text("CN", "ACME SNI Challenge Certificate")
        .unwrap();
    let subject = subject_builder.build();
    cert_builder.set_subject_name(&subject).unwrap();
    cert_builder.set_issuer_name(&subject).unwrap();

    cert_builder
        .set_not_before(&openssl::asn1::Asn1Time::days_from_now(0).unwrap())
        .unwrap();
    cert_builder
        .set_not_after(&openssl::asn1::Asn1Time::days_from_now(1).unwrap())
        .unwrap();

    let mut san = openssl::x509::extension::SubjectAlternativeName::new();
    san.dns(domain);
    let san_ext = san.build(&cert_builder.x509v3_context(None, None)).unwrap();
    cert_builder.append_extension(san_ext).unwrap();

    cert_builder
        .sign(&pkey, openssl::hash::MessageDigest::sha256())
        .unwrap();

    (cert_builder.build(), pkey)
}

pub fn openssl_cert_to_rustls(cert: &openssl::x509::X509) -> rustls::Certificate {
    rustls::Certificate(cert.to_der().unwrap())
}

pub fn openssl_pkey_to_rustls(
    pkey: &openssl::pkey::PKey<openssl::pkey::Private>,
) -> rustls::PrivateKey {
    rustls::PrivateKey(pkey.rsa().unwrap().private_key_to_der().unwrap())
}

fn openssl_pkey_to_rustls_signer(
    pkey: &openssl::pkey::PKey<openssl::pkey::Private>,
) -> Box<dyn rustls::sign::SigningKey> {
    // TODO: ECDSA
    Box::new(rustls::sign::RSASigningKey::new(&openssl_pkey_to_rustls(pkey)).unwrap())
}

fn pems_to_rustls(chain_pem: &str, private_key_pem: &str) -> rustls::sign::CertifiedKey {
    let chain = rustls::internal::pemfile::certs(&mut Cursor::new(chain_pem)).unwrap();
    // TODO: ECDSA
    let private_key =
        &rustls::internal::pemfile::rsa_private_keys(&mut Cursor::new(private_key_pem)).unwrap()[0];
    rustls::sign::CertifiedKey::new(
        chain,
        Arc::new(Box::new(
            rustls::sign::RSASigningKey::new(private_key).unwrap(),
        )),
    )
}

impl<C> rustls::ResolvesServerCert for AutomaticCertResolver<C>
where
    C: CertificateCache,
{
    fn resolve(&self, client_hello: rustls::ClientHello) -> Option<rustls::sign::CertifiedKey> {
        if let Some(sni) = client_hello.server_name() {
            if let Some(cert) = self.sni_challenges.lock().unwrap().get(sni.into()) {
                return Some(cert.clone());
            }
        }
        // Seperate scope so that the lock isn't held we enter `obtain_new_certificate`.
        {
            let active_cert = self.active_cert.lock().unwrap();
            if cert_is_valid(&active_cert) {
                // TODO: if it's _close_ to expiring, trigger a background "obtain new cert"
                return active_cert.clone();
            }
        }
        // TODO: Don't try to obtain a new cert if we're currently waiting for one already...
        self.obtain_new_certificate();
        self.active_cert.lock().unwrap().clone()
    }
}

fn cert_is_valid(cert: &Option<rustls::sign::CertifiedKey>) -> bool {
    match *cert {
        Some(ref key) => !cert_is_expired(&key.cert[0]),
        None => false,
    }
}

fn cert_is_expired(cert: &rustls::Certificate) -> bool {
    let openssl_cert = openssl::x509::X509::from_der(&cert.0).unwrap();
    from_asn1_time(openssl_cert.not_after()) < chrono::Utc::now()
}

fn from_asn1_time(t: &openssl::asn1::Asn1TimeRef) -> chrono::DateTime<chrono::Utc> {
    let dt = chrono::DateTime::parse_from_str(
        &t.to_string().replace(" GMT", " +00:00"),
        "%b %e %T %Y %z",
    )
    .unwrap();
    chrono::DateTime::<chrono::Utc>::from_utc(dt.naive_utc(), chrono::Utc)
}
<commit_msg>Refactor to make clippy happy (it's also cleaner)<commit_after>use super::common::sha256_hex;

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{Cursor, Write};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

pub trait CertificateCache: Send + Sync {
    fn store_certificate(&self, identifier: &str, chain: &str, private_key: &str);
    fn fetch_certificate(&self, identifier: &str) -> Option<(String, String)>;
}

fn domains_to_identifier(acme_url: &str, domains: &[String]) -> String {
    sha256_hex(format!("{}|{}", acme_url, domains.join("|")).as_bytes())
}

pub struct DiskCache {
    location: PathBuf,
}

impl DiskCache {
    pub fn new(location: PathBuf) -> DiskCache {
        fs::create_dir_all(&location).unwrap();
        DiskCache { location }
    }

    fn chain_path(&self, identifier: &str) -> PathBuf {
        self.location.join(identifier.to_string() + ".chain.pem")
    }

    fn key_path(&self, identifier: &str) -> PathBuf {
        self.location.join(identifier.to_string() + ".key.pem")
    }
}

impl CertificateCache for DiskCache {
    fn store_certificate(&self, identifier: &str, chain: &str, private_key: &str) {
        File::create(self.chain_path(identifier))
            .unwrap()
            .write_all(chain.as_bytes())
            .unwrap();
        File::create(self.key_path(identifier))
            .unwrap()
            .write_all(private_key.as_bytes())
            .unwrap();
    }

    fn fetch_certificate(&self, identifier: &str) -> Option<(String, String)> {
        match (
            fs::read_to_string(self.chain_path(identifier)),
            fs::read_to_string(self.key_path(identifier)),
        ) {
            (Ok(chain_pem), Ok(key_pem)) => {
                return Some((chain_pem, key_pem));
            }
            _ => return None,
        }
    }
}

pub struct AutomaticCertResolver<C>
where
    C: CertificateCache,
{
    domains: Vec<String>,
    acme_url: String,
    acme_account: acme_client::Account,
    active_cert: Mutex<Option<rustls::sign::CertifiedKey>>,
    cert_cache: C,
    sni_challenges: Mutex<HashMap<String, rustls::sign::CertifiedKey>>,
}

impl<C> AutomaticCertResolver<C>
where
    C: CertificateCache,
{
    pub fn new(acme_url: &str, domains: Vec<String>, cache: C) -> AutomaticCertResolver<C> {
        let acme_directory = acme_client::Directory::from_url(acme_url).unwrap();
        let pems = cache.fetch_certificate(&domains_to_identifier(acme_url, &domains));
        let active_cert = Mutex::new(
            pems.map(|(chain_pem, private_key_pem)| pems_to_rustls(&chain_pem, &private_key_pem)),
        );
        AutomaticCertResolver {
            domains,
            cert_cache: cache,
            acme_url: acme_url.to_string(),
            acme_account: acme_directory.account_registration().register().unwrap(),
            active_cert,
            sni_challenges: Mutex::new(HashMap::new()),
        }
    }

    fn obtain_new_certificate(&self) {
        // Can't do the smart thing of setting them all up, and then triggering the validations in
        // parallel and waiting for the results because acme-client doesn't expose seperate
        // "trigger validation" and "wait for success" functions.
        for domain in &self.domains {
            let authorization = self.acme_account.authorization(domain).unwrap();
            let tls_sni_challenge = authorization.get_tls_sni_challenge().unwrap();
            self.setup_sni_challenge(tls_sni_challenge);
            tls_sni_challenge.validate().unwrap();
            self.teardown_sni_challenge(tls_sni_challenge);
        }
        let cert = self
            .acme_account
            .certificate_signer(
                &self
                    .domains
                    .iter()
                    .map(|s| s.as_ref())
                    .collect::<Vec<&str>>(),
            )
            .sign_certificate()
            .unwrap();
        // TODO: intermediates
        let chain = vec![openssl_cert_to_rustls(cert.cert())];
        let signer = openssl_pkey_to_rustls_signer(cert.pkey());
        *self.active_cert.lock().unwrap() =
            Some(rustls::sign::CertifiedKey::new(chain, Arc::new(signer)));
        self.cert_cache.store_certificate(
            &domains_to_identifier(&self.acme_url, &self.domains),
            std::str::from_utf8(&cert.cert().to_pem().unwrap()).unwrap(),
            // TODO: ECDSA
            std::str::from_utf8(&cert.pkey().rsa().unwrap().private_key_to_pem().unwrap()).unwrap(),
        );
    }

    fn setup_sni_challenge(&self, challenge: &acme_client::Challenge<'_>) {
        let z_domain = z_domain(challenge);
        let (cert, pkey) = generate_temporary_cert(&z_domain);

        let chain = vec![openssl_cert_to_rustls(&cert)];
        let signer = openssl_pkey_to_rustls_signer(&pkey);
        self.sni_challenges.lock().unwrap().insert(
            z_domain,
            rustls::sign::CertifiedKey::new(chain, Arc::new(signer)),
        );
    }

    fn teardown_sni_challenge(&self, challenge: &acme_client::Challenge<'_>) {
        let z_domain = z_domain(challenge);
        self.sni_challenges.lock().unwrap().remove(&z_domain);
    }
}

fn z_domain(challenge: &acme_client::Challenge<'_>) -> String {
    let z = sha256_hex(challenge.key_authorization().as_bytes());
    let (z1, z2) = z.split_at(32);
    return format!("{}.{}.acme.invalid", z1, z2);
}

pub fn generate_temporary_cert(
    domain: &str,
) -> (
    openssl::x509::X509,
    openssl::pkey::PKey<openssl::pkey::Private>,
) {
    let pkey = openssl::pkey::PKey::from_rsa(openssl::rsa::Rsa::generate(2048).unwrap()).unwrap();
    let mut cert_builder = openssl::x509::X509Builder::new().unwrap();
    cert_builder.set_version(2).unwrap();
    cert_builder.set_pubkey(&pkey).unwrap();

    let mut serial = openssl::bn::BigNum::new().unwrap();
    serial
        .rand(128, openssl::bn::MsbOption::MAYBE_ZERO, false)
        .unwrap();
    cert_builder
        .set_serial_number(&serial.to_asn1_integer().unwrap())
        .unwrap();

    let mut subject_builder = openssl::x509::X509NameBuilder::new().unwrap();
    subject_builder
        .append_entry_by_text("CN", "ACME SNI Challenge Certificate")
        .unwrap();
    let subject = subject_builder.build();
    cert_builder.set_subject_name(&subject).unwrap();
    cert_builder.set_issuer_name(&subject).unwrap();

    cert_builder
        .set_not_before(&openssl::asn1::Asn1Time::days_from_now(0).unwrap())
        .unwrap();
    cert_builder
        .set_not_after(&openssl::asn1::Asn1Time::days_from_now(1).unwrap())
        .unwrap();

    let mut san = openssl::x509::extension::SubjectAlternativeName::new();
    san.dns(domain);
    let san_ext = san.build(&cert_builder.x509v3_context(None, None)).unwrap();
    cert_builder.append_extension(san_ext).unwrap();

    cert_builder
        .sign(&pkey, openssl::hash::MessageDigest::sha256())
        .unwrap();

    (cert_builder.build(), pkey)
}

pub fn openssl_cert_to_rustls(cert: &openssl::x509::X509) -> rustls::Certificate {
    rustls::Certificate(cert.to_der().unwrap())
}

pub fn openssl_pkey_to_rustls(
    pkey: &openssl::pkey::PKey<openssl::pkey::Private>,
) -> rustls::PrivateKey {
    rustls::PrivateKey(pkey.rsa().unwrap().private_key_to_der().unwrap())
}

fn openssl_pkey_to_rustls_signer(
    pkey: &openssl::pkey::PKey<openssl::pkey::Private>,
) -> Box<dyn rustls::sign::SigningKey> {
    // TODO: ECDSA
    Box::new(rustls::sign::RSASigningKey::new(&openssl_pkey_to_rustls(pkey)).unwrap())
}

fn pems_to_rustls(chain_pem: &str, private_key_pem: &str) -> rustls::sign::CertifiedKey {
    let chain = rustls::internal::pemfile::certs(&mut Cursor::new(chain_pem)).unwrap();
    // TODO: ECDSA
    let private_key =
        &rustls::internal::pemfile::rsa_private_keys(&mut Cursor::new(private_key_pem)).unwrap()[0];
    rustls::sign::CertifiedKey::new(
        chain,
        Arc::new(Box::new(
            rustls::sign::RSASigningKey::new(private_key).unwrap(),
        )),
    )
}

impl<C> rustls::ResolvesServerCert for AutomaticCertResolver<C>
where
    C: CertificateCache,
{
    fn resolve(&self, client_hello: rustls::ClientHello) -> Option<rustls::sign::CertifiedKey> {
        if let Some(sni) = client_hello.server_name() {
            if let Some(cert) = self.sni_challenges.lock().unwrap().get(sni.into()) {
                return Some(cert.clone());
            }
        }
        // Seperate scope so that the lock isn't held we enter `obtain_new_certificate`.
        {
            let active_cert = self.active_cert.lock().unwrap();
            if cert_is_valid(&active_cert) {
                // TODO: if it's _close_ to expiring, trigger a background "obtain new cert"
                return active_cert.clone();
            }
        }
        // TODO: Don't try to obtain a new cert if we're currently waiting for one already...
        self.obtain_new_certificate();
        self.active_cert.lock().unwrap().clone()
    }
}

fn cert_is_valid(cert: &Option<rustls::sign::CertifiedKey>) -> bool {
    match *cert {
        Some(ref key) => !cert_is_expired(&key.cert[0]),
        None => false,
    }
}

fn cert_is_expired(cert: &rustls::Certificate) -> bool {
    let openssl_cert = openssl::x509::X509::from_der(&cert.0).unwrap();
    from_asn1_time(openssl_cert.not_after()) < chrono::Utc::now()
}

fn from_asn1_time(t: &openssl::asn1::Asn1TimeRef) -> chrono::DateTime<chrono::Utc> {
    let dt = chrono::DateTime::parse_from_str(
        &t.to_string().replace(" GMT", " +00:00"),
        "%b %e %T %Y %z",
    )
    .unwrap();
    chrono::DateTime::<chrono::Utc>::from_utc(dt.naive_utc(), chrono::Utc)
}
<|endoftext|>