<commit_before>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Kata OS CAmkES component helpers

#![no_std]
#![allow(non_camel_case_types)]

use allocator;
use log::trace;
use logger::KataLogger;
use sel4_sys;
use slot_allocator::KATA_CSPACE_SLOTS;

use sel4_sys::seL4_CNode_Delete;
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_GetCap;
use sel4_sys::seL4_GetCapReceivePath;
use sel4_sys::seL4_Result;
use sel4_sys::seL4_SetCap;
use sel4_sys::seL4_SetCapReceivePath;
use sel4_sys::seL4_Word;
use sel4_sys::seL4_WordBits;

pub type seL4_CPath = (seL4_CPtr, seL4_CPtr, seL4_Word);

extern "C" {
    // CAmkES components marked with
    //    attribute integer kataos = 1;
    // automatically get a self-reference to their top-level CNode and
    // the slot #'s of the first & last free slots in the CNode.
    static SELF_CNODE: seL4_CPtr;
    static SELF_CNODE_FIRST_SLOT: seL4_CPtr;
    static SELF_CNODE_LAST_SLOT: seL4_CPtr;
}

// Flag or'd into reply capability to indicate the cap should be
// deleted _after_ the reply is done. This depends on KataOS-specific
// CAmkES support enabled through build glue (cbindgen processes this
// crate to generate CamkesBindings.h which exports #define CAP_RELEASE
// that enables the necessaery #ifdef's).
pub const CAP_RELEASE: usize = 0x8000_0000;

// RAII wrapper for handling request cap cleanup.
pub struct RequestCapCleanup {}
impl Drop for RequestCapCleanup {
    fn drop(&mut self) {
        unsafe {
            seL4_SetCap(0, 0);
        }
    }
}

pub struct Camkes {
    name: &'static str,    // Component name
    recv_path: seL4_CPath, // IPCBuffer receive path
}

impl Camkes {
    pub const fn new(name: &'static str) -> Self {
        Camkes {
            name,
            recv_path: (seL4_CPtr::MAX, seL4_CPtr::MAX, seL4_Word::MAX),
        }
    }

    pub fn init_logger(self: &Camkes, level: log::LevelFilter) {
        static KATA_LOGGER: KataLogger = KataLogger;
        log::set_logger(&KATA_LOGGER).unwrap();
        log::set_max_level(level);
    }

    pub fn init_allocator(self: &Camkes, heap: &'static mut [u8]) {
        unsafe {
            allocator::ALLOCATOR.init(heap.as_mut_ptr() as usize, heap.len());
        }
        trace!("setup heap: start_addr {:p} size {}", heap.as_ptr(), heap.len());
    }

    pub fn init_slot_allocator(self: &Camkes, first_slot: seL4_CPtr, last_slot: seL4_CPtr) {
        unsafe {
            KATA_CSPACE_SLOTS.init(self.name, first_slot, last_slot - first_slot);
            trace!(
                "setup cspace slots: first slot {} free {}",
                KATA_CSPACE_SLOTS.base_slot(),
                KATA_CSPACE_SLOTS.free_slots()
            );
        }
    }

    pub fn pre_init(self: &Camkes, level: log::LevelFilter, heap: &'static mut [u8]) {
        self.init_logger(level);
        self.init_allocator(heap);
        unsafe {
            self.init_slot_allocator(SELF_CNODE_FIRST_SLOT, SELF_CNODE_LAST_SLOT);
        }
    }

    pub fn top_level_path(slot: seL4_CPtr) -> seL4_CPath {
        unsafe { (SELF_CNODE, slot, seL4_WordBits) }
    }

    // Initializes the IPCBuffer receive path with |path|.
    pub fn init_recv_path(self: &mut Camkes, path: &seL4_CPath) {
        self.recv_path = *path;
        unsafe {
            seL4_SetCapReceivePath(path.0, path.1, path.2);
        }
        trace!("{}: Cap receive path {:?}", self.name, path);
    }

    // Returns the path specified with init_recv_path.
    pub fn get_recv_path(self: &Camkes) -> seL4_CPath { self.recv_path }

    // Returns the component name.
    pub fn get_name(self: &Camkes) -> &'static str { self.name }

    // Returns the current receive path from the IPCBuffer.
    pub fn get_current_recv_path(self: &Camkes) -> seL4_CPath {
        unsafe { seL4_GetCapReceivePath() }
    }

    // Clears any capability the receive path path points to.
    pub fn clear_recv_path(self: &Camkes) {
        let path = &self.recv_path;
        // Assert since future receives are likely to fail
        unsafe { seL4_CNode_Delete(path.0, path.1, path.2 as u8) }.expect(self.name);
    }

    // Check the current receive path in the IPCBuffer against what was
    // setup with init_recv_path.
    pub fn check_recv_path(self: &Camkes) -> bool {
        self.get_current_recv_path() == self.get_recv_path()
    }

    // Like check_recv_path but asserts if there is an inconsistency.
    pub fn assert_recv_path(self: &Camkes) {
        assert!(
            self.check_recv_path(),
            "Current receive path {:?} does not match init'd path {:?}",
            self.get_current_recv_path(),
            self.recv_path
        );
    }

    // Attaches a capability to a CAmkES RPC request msg. seL4 will copy
    // the capabiltiy.
    #[must_use]
    pub fn set_request_cap(cptr: seL4_CPtr) -> RequestCapCleanup {
        unsafe {
            seL4_SetCap(0, cptr);
        }
        RequestCapCleanup {}
    }

    // Returns the capability attached to an seL4 IPC.
    pub fn get_request_cap() -> seL4_CPtr { unsafe { seL4_GetCap(0) } }

    // Attaches a capability to a CAmkES RPC reply msg. seL4 will copy
    // the capabiltiy.
    pub fn set_reply_cap(cptr: seL4_CPtr) {
        unsafe {
            seL4_SetCap(0, cptr);
        }
    }

    // Attaches a capability to a CAmkES RPC reply msg and arranges for
    // the capability to be released after the reply completes.
    pub fn set_reply_cap_release(cptr: seL4_CPtr) {
        unsafe {
            // NB: logically this belongs in the CAmkES code where the
            // cap is deleted but that's not possible so do it here--there
            // should be no race to re-purpose the slot since everything
            // is assumed single-threaded (and CAmkES-generated code does
            // not short-circuit the cap delete).
            KATA_CSPACE_SLOTS.free(cptr, 1);
            seL4_SetCap(0, cptr | CAP_RELEASE);
        }
    }

    // Clears any capability attached to a CAmkES RPC reply msg.
    pub fn clear_reply_cap() { Camkes::set_reply_cap(0); }

    // Wrappers for sel4_sys::debug_assert macros.
    pub fn debug_assert_slot_empty(tag: &str, path: &seL4_CPath) {
        sel4_sys::debug_assert_slot_empty!(
            path.1,
            "{}: expected slot {:?} empty but has cap type {:?}",
            tag,
            path,
            sel4_sys::cap_identify(path.1)
        );
    }
    pub fn debug_assert_slot_cnode(tag: &str, path: &seL4_CPath) {
        sel4_sys::debug_assert_slot_cnode!(
            path.1,
            "{}: expected cnode in slot {:?} but found cap type {:?}",
            tag,
            path,
            sel4_sys::cap_identify(path.1)
        );
    }
    pub fn debug_assert_slot_frame(tag: &str, path: &seL4_CPath) {
        sel4_sys::debug_assert_slot_frame!(
            path.1,
            "{}: expected frame in slot {:?} but found cap type {:?}",
            tag,
            path,
            sel4_sys::cap_identify(path.1)
        );
    }

    // Dumps the contents of the toplevel CNode to the serial console.
    pub fn capscan() -> seL4_Result {
        // TODO(sleffler): requires CONFIG_PRINTING in the kernel
        #[cfg(feature = "CONFIG_PRINTING")]
        unsafe {
            sel4_sys::seL4_DebugDumpCNode(SELF_CNODE);
        }
        // XXX until seL4_Error is correctly returned
        Ok(())
    }
}
<commit_msg>kata-os-camkes: misc additions<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Kata OS CAmkES component helpers

#![no_std]
#![allow(non_camel_case_types)]

use allocator;
use log::trace;
use logger::KataLogger;
use sel4_sys;
use slot_allocator::KATA_CSPACE_SLOTS;

use sel4_sys::seL4_CNode_Delete;
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_GetCap;
use sel4_sys::seL4_GetCapReceivePath;
use sel4_sys::seL4_Result;
use sel4_sys::seL4_SetCap;
use sel4_sys::seL4_SetCapReceivePath;
use sel4_sys::seL4_Word;
use sel4_sys::seL4_WordBits;

pub type seL4_CPath = (seL4_CPtr, seL4_CPtr, seL4_Word);

extern "C" {
    // CAmkES components marked with
    //    attribute integer kataos = 1;
    // automatically get a self-reference to their top-level CNode and
    // the slot #'s of the first & last free slots in the CNode.
    static SELF_CNODE: seL4_CPtr;
    static SELF_CNODE_FIRST_SLOT: seL4_CPtr;
    static SELF_CNODE_LAST_SLOT: seL4_CPtr;
}

// Flag or'd into reply capability to indicate the cap should be
// deleted _after_ the reply is done. This depends on KataOS-specific
// CAmkES support enabled through build glue (cbindgen processes this
// crate to generate CamkesBindings.h which exports #define CAP_RELEASE
// that enables the necessaery #ifdef's).
pub const CAP_RELEASE: usize = 0x8000_0000;

// RAII wrapper for handling request cap cleanup.
pub struct RequestCapCleanup {}
impl Drop for RequestCapCleanup {
    fn drop(&mut self) {
        unsafe {
            seL4_SetCap(0, 0);
        }
    }
}

pub struct Camkes {
    name: &'static str,    // Component name
    recv_path: seL4_CPath, // IPCBuffer receive path
}

impl Camkes {
    pub const fn new(name: &'static str) -> Self {
        Camkes {
            name,
            recv_path: (seL4_CPtr::MAX, seL4_CPtr::MAX, seL4_Word::MAX),
        }
    }

    pub fn init_logger(self: &Camkes, level: log::LevelFilter) {
        static KATA_LOGGER: KataLogger = KataLogger;
        log::set_logger(&KATA_LOGGER).unwrap();
        log::set_max_level(level);
    }

    pub fn init_allocator(self: &Camkes, heap: &'static mut [u8]) {
        unsafe {
            allocator::ALLOCATOR.init(heap.as_mut_ptr() as usize, heap.len());
        }
        trace!("setup heap: start_addr {:p} size {}", heap.as_ptr(), heap.len());
    }

    pub fn init_slot_allocator(self: &Camkes, first_slot: seL4_CPtr, last_slot: seL4_CPtr) {
        unsafe {
            KATA_CSPACE_SLOTS.init(self.name, first_slot, last_slot - first_slot);
            trace!(
                "setup cspace slots: first slot {} free {}",
                KATA_CSPACE_SLOTS.base_slot(),
                KATA_CSPACE_SLOTS.free_slots()
            );
        }
    }

    pub fn pre_init(self: &Camkes, level: log::LevelFilter, heap: &'static mut [u8]) {
        self.init_logger(level);
        self.init_allocator(heap);
        unsafe {
            self.init_slot_allocator(SELF_CNODE_FIRST_SLOT, SELF_CNODE_LAST_SLOT);
        }
    }

    pub fn top_level_path(slot: seL4_CPtr) -> seL4_CPath {
        unsafe { (SELF_CNODE, slot, seL4_WordBits) }
    }

    // Initializes the IPCBuffer receive path with |path|.
    pub fn init_recv_path(self: &mut Camkes, path: &seL4_CPath) {
        self.recv_path = *path;
        unsafe {
            seL4_SetCapReceivePath(path.0, path.1, path.2);
        }
        trace!("{}: Cap receive path {:?}", self.name, path);
    }

    // Returns the path specified with init_recv_path.
    pub fn get_recv_path(self: &Camkes) -> seL4_CPath { self.recv_path }

    // Returns the component name.
    pub fn get_name(self: &Camkes) -> &'static str { self.name }

    // Returns the current receive path from the IPCBuffer.
    pub fn get_current_recv_path(self: &Camkes) -> seL4_CPath {
        unsafe { seL4_GetCapReceivePath() }
    }

    // Clears any capability the receive path path points to.
    pub fn clear_recv_path(self: &Camkes) {
        let path = &self.recv_path;
        // Assert since future receives are likely to fail
        unsafe { seL4_CNode_Delete(path.0, path.1, path.2 as u8) }.expect(self.name);
    }

    // Check the current receive path in the IPCBuffer against what was
    // setup with init_recv_path.
    pub fn check_recv_path(self: &Camkes) -> bool {
        self.get_current_recv_path() == self.get_recv_path()
    }

    // Like check_recv_path but asserts if there is an inconsistency.
    pub fn assert_recv_path(self: &Camkes) {
        assert!(
            self.check_recv_path(),
            "Current receive path {:?} does not match init'd path {:?}",
            self.get_current_recv_path(),
            self.recv_path
        );
    }

    // Attaches a capability to a CAmkES RPC request msg. seL4 will copy
    // the capabiltiy.
    #[must_use]
    pub fn set_request_cap(cptr: seL4_CPtr) -> RequestCapCleanup {
        unsafe {
            seL4_SetCap(0, cptr);
        }
        RequestCapCleanup {}
    }

    // Clears any capability attached to a CAmkES RPC request msg.
    pub fn clear_request_cap() {
        unsafe {
            seL4_SetCap(0, 0);
        }
    }

    // Returns the capability attached to an seL4 IPC.
    pub fn get_request_cap() -> seL4_CPtr { unsafe { seL4_GetCap(0) } }

    // Attaches a capability to a CAmkES RPC reply msg. seL4 will copy
    // the capabiltiy.
    pub fn set_reply_cap(cptr: seL4_CPtr) {
        unsafe {
            seL4_SetCap(0, cptr);
        }
    }

    // Attaches a capability to a CAmkES RPC reply msg and arranges for
    // the capability to be released after the reply completes.
    pub fn set_reply_cap_release(cptr: seL4_CPtr) {
        unsafe {
            // NB: logically this belongs in the CAmkES code where the
            // cap is deleted but that's not possible so do it here--there
            // should be no race to re-purpose the slot since everything
            // is assumed single-threaded (and CAmkES-generated code does
            // not short-circuit the cap delete).
            KATA_CSPACE_SLOTS.free(cptr, 1);
            seL4_SetCap(0, cptr | CAP_RELEASE);
        }
    }

    // Clears any capability attached to a CAmkES RPC reply msg.
    pub fn clear_reply_cap() { Camkes::set_reply_cap(0); }

    // Wrappers for sel4_sys::debug_assert macros.
    pub fn debug_assert_slot_empty(tag: &str, path: &seL4_CPath) {
        sel4_sys::debug_assert_slot_empty!(
            path.1,
            "{}: expected slot {:?} empty but has cap type {:?}",
            tag,
            path,
            sel4_sys::cap_identify(path.1)
        );
    }
    pub fn debug_assert_slot_cnode(tag: &str, path: &seL4_CPath) {
        sel4_sys::debug_assert_slot_cnode!(
            path.1,
            "{}: expected cnode in slot {:?} but found cap type {:?}",
            tag,
            path,
            sel4_sys::cap_identify(path.1)
        );
    }
    pub fn debug_assert_slot_frame(tag: &str, path: &seL4_CPath) {
        sel4_sys::debug_assert_slot_frame!(
            path.1,
            "{}: expected frame in slot {:?} but found cap type {:?}",
            tag,
            path,
            sel4_sys::cap_identify(path.1)
        );
    }

    // debug_assert wrappers for the current recv_path.
    pub fn debug_assert_recv_path_empty(&self, tag: &str) {
        Self::debug_assert_slot_empty(tag, &self.get_current_recv_path());
    }
    pub fn debug_assert_recv_path_cnode(&self, tag: &str) {
        Self::debug_assert_slot_cnode(tag, &self.get_current_recv_path());
    }
    pub fn debug_assert_recv_path_frame(&self, tag: &str) {
        Self::debug_assert_slot_frame(tag, &self.get_current_recv_path());
    }

    // Dumps the contents of the toplevel CNode to the serial console.
    pub fn capscan() -> seL4_Result {
        // TODO(sleffler): requires CONFIG_PRINTING in the kernel
        #[cfg(feature = "CONFIG_PRINTING")]
        unsafe {
            sel4_sys::seL4_DebugDumpCNode(SELF_CNODE);
        }
        // XXX until seL4_Error is correctly returned
        Ok(())
    }
}
<|endoftext|>


<commit_before>impl_var!(
    /// Internet address families
    Af, libc::c_uchar,
    Inet => libc::AF_INET as libc::c_uchar,
    Inet6 => libc::AF_INET6 as libc::c_uchar
);

impl_var!(
    /// General address families for sockets
    RtAddrFamily, u8,
    UnixOrLocal => libc::AF_UNIX as u8,
    Inet => libc::AF_INET as u8,
    Inet6 => libc::AF_INET6 as u8,
    Ipx => libc::AF_IPX as u8,
    Netlink => libc::AF_NETLINK as u8,
    X25 => libc::AF_X25 as u8,
    Ax25 => libc::AF_AX25 as u8,
    Atmpvc => libc::AF_ATMPVC as u8,
    Appletalk => libc::AF_APPLETALK as u8,
    Packet => libc::AF_PACKET as u8,
    Alg => libc::AF_ALG as u8
);

impl_var!(
    /// Interface address flags
    IfaF, u32,
    Secondary => libc::IFA_F_SECONDARY,
    Temporary => libc::IFA_F_TEMPORARY,
    Nodad => libc::IFA_F_NODAD,
    Optimistic => libc::IFA_F_OPTIMISTIC,
    Dadfailed => libc::IFA_F_DADFAILED,
    Homeaddress => libc::IFA_F_HOMEADDRESS,
    Deprecated => libc::IFA_F_DEPRECATED,
    Tentative => libc::IFA_F_TENTATIVE,
    Permanent => libc::IFA_F_PERMANENT,
    #[cfg(target_env="gnu")]
    Managetempaddr => libc::IFA_F_MANAGETEMPADDR,
    #[cfg(target_env="gnu")]
    Noprefixroute => libc::IFA_F_NOPREFIXROUTE,
    #[cfg(target_env="gnu")]
    Mcautojoin => libc::IFA_F_MCAUTOJOIN,
    #[cfg(target_env="gnu")]
    StablePrivacy => libc::IFA_F_STABLE_PRIVACY
);

impl_var!(
    /// `rtm_type`
    /// The results of a lookup from a route table
    Rtn, libc::c_uchar,
    Unspec => libc::RTN_UNSPEC,
    Unicast => libc::RTN_UNICAST,
    Local => libc::RTN_LOCAL,
    Broadcast => libc::RTN_BROADCAST,
    Anycast => libc::RTN_ANYCAST,
    Multicast => libc::RTN_MULTICAST,
    Blackhole => libc::RTN_BLACKHOLE,
    Unreachable => libc::RTN_UNREACHABLE,
    Prohibit => libc::RTN_PROHIBIT,
    Throw => libc::RTN_THROW,
    Nat => libc::RTN_NAT,
    Xresolve => libc::RTN_XRESOLVE
);

impl_var!(
    /// `rtm_protocol`
    /// The origins of routes that are defined in the kernel
    Rtprot, libc::c_uchar,
    Unspec => libc::RTPROT_UNSPEC,
    Redirect => libc::RTPROT_REDIRECT,
    Kernel => libc::RTPROT_KERNEL,
    Boot => libc::RTPROT_BOOT,
    Static => libc::RTPROT_STATIC
);

impl_var!(
    /// `rtm_scope`
    /// The distance between destinations
    RtScope, libc::c_uchar,
    Universe => libc::RT_SCOPE_UNIVERSE,
    Site => libc::RT_SCOPE_SITE,
    Link => libc::RT_SCOPE_LINK,
    Host => libc::RT_SCOPE_HOST,
    Nowhere => libc::RT_SCOPE_NOWHERE
);

impl_var!(
    /// `rt_class_t`
    /// Reserved route table identifiers
    RtTable, libc::c_uchar,
    Unspec => libc::RT_TABLE_UNSPEC,
    Compat => libc::RT_TABLE_COMPAT,
    Default => libc::RT_TABLE_DEFAULT,
    Main => libc::RT_TABLE_MAIN,
    Local => libc::RT_TABLE_LOCAL
);

impl_var!(
    /// `rtm_flags`
    /// Flags for rtnetlink messages
    RtmF, libc::c_uint,
    Notify => libc::RTM_F_NOTIFY,
    Cloned => libc::RTM_F_CLONED,
    Equalize => libc::RTM_F_EQUALIZE,
    Prefix => libc::RTM_F_PREFIX,

    #[cfg(target_env="gnu")]
    LookupTable => libc::RTM_F_LOOKUP_TABLE,
    #[cfg(target_env="gnu")]
    FibMatch => libc::RTM_F_FIB_MATCH
);

impl_var!(
    /// Arp neighbor cache entry states
    Nud, u16,
    None => libc::NUD_NONE,
    Incomplete => libc::NUD_INCOMPLETE,
    Reachable => libc::NUD_REACHABLE,
    Stale => libc::NUD_STALE,
    Delay => libc::NUD_DELAY,
    Probe => libc::NUD_PROBE,
    Failed => libc::NUD_FAILED,
    Noarp => libc::NUD_NOARP,
    Permanent => libc::NUD_PERMANENT
);

impl_var!(
    /// Arp neighbor cache entry flags
    Ntf, u8,
    Use => libc::NTF_USE,
    Self_ => libc::NTF_SELF,
    Master => libc::NTF_MASTER,
    Proxy => libc::NTF_PROXY,
    #[cfg(target_env="gnu")]
    ExtLearned => libc::NTF_EXT_LEARNED,
    #[cfg(target_env="gnu")]
    Offloaded => libc::NTF_OFFLOADED,
    Router => libc::NTF_ROUTER
);

impl_trait!(
    /// Marker trait for `Rtattr.rta_type` field
    RtaType,
    libc::c_ushort
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type`
    Ifla, libc::c_ushort, RtaType,
    Unspec => libc::IFLA_UNSPEC,
    Address => libc::IFLA_ADDRESS,
    Broadcast => libc::IFLA_BROADCAST,
    Ifname => libc::IFLA_IFNAME,
    Mtu => libc::IFLA_MTU,
    Link => libc::IFLA_LINK,
    Qdisc => libc::IFLA_QDISC,
    Stats => libc::IFLA_STATS
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type`
    Ifa, libc::c_ushort, RtaType,
    Unspec => libc::IFA_UNSPEC,
    Address => libc::IFA_ADDRESS,
    Local => libc::IFA_LOCAL,
    Label => libc::IFA_LABEL,
    Broadcast => libc::IFA_BROADCAST,
    Anycast => libc::IFA_ANYCAST,
    Cacheinfo => libc::IFA_CACHEINFO,
    Multicast => libc::IFA_MULTICAST,
    #[cfg(target_env="gnu")]
    Flags => libc::IFA_FLAGS
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type` -
    /// Values are routing message attributes
    Rta, libc::c_ushort, RtaType,
    Unspec => libc::RTA_UNSPEC,
    Dst => libc::RTA_DST,
    Src => libc::RTA_SRC,
    Iif => libc::RTA_IIF,
    Oif => libc::RTA_OIF,
    Gateway => libc::RTA_GATEWAY,
    Priority => libc::RTA_PRIORITY,
    Prefsrc => libc::RTA_PREFSRC,
    Metrics => libc::RTA_METRICS,
    Multipath => libc::RTA_MULTIPATH,
    Protoinfo => libc::RTA_PROTOINFO, // no longer used in Linux
    Flow => libc::RTA_FLOW,
    Cacheinfo => libc::RTA_CACHEINFO,
    Session => libc::RTA_SESSION, // no longer used in Linux
    MpAlgo => libc::RTA_MP_ALGO, // no longer used in Linux
    Table => libc::RTA_TABLE,
    Mark => libc::RTA_MARK,
    MfcStats => libc::RTA_MFC_STATS,
    #[cfg(target_env="gnu")]
    Via => libc::RTA_VIA,
    #[cfg(target_env="gnu")]
    Newdst => libc::RTA_NEWDST,
    #[cfg(target_env="gnu")]
    Pref => libc::RTA_PREF,
    #[cfg(target_env="gnu")]
    EncapType => libc::RTA_ENCAP_TYPE,
    #[cfg(target_env="gnu")]
    Encap => libc::RTA_ENCAP,
    #[cfg(target_env="gnu")]
    Expires => libc::RTA_EXPIRES,
    #[cfg(target_env="gnu")]
    Pad => libc::RTA_PAD,
    #[cfg(target_env="gnu")]
    Uid => libc::RTA_UID,
    #[cfg(target_env="gnu")]
    TtlPropagate => libc::RTA_TTL_PROPAGATE
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type` -
    /// Values specify queuing discipline attributes
    Tca, libc::c_ushort, RtaType,
    Unspec => libc::TCA_UNSPEC,
    Kind => libc::TCA_KIND,
    Options => libc::TCA_OPTIONS,
    Stats => libc::TCA_STATS,
    Xstats => libc::TCA_XSTATS,
    Rate => libc::TCA_RATE,
    Fcnt => libc::TCA_FCNT,
    Stats2 => libc::TCA_STATS2,
    Stab => libc::TCA_STAB
);

impl_var!(
    /// Interface types
    Arphrd, libc::c_ushort,
    Netrom => libc::ARPHRD_NETROM,
    Ether => libc::ARPHRD_ETHER,
    Eether => libc::ARPHRD_EETHER,
    AX25 => libc::ARPHRD_AX25,
    Pronet => libc::ARPHRD_PRONET,
    Chaos => libc::ARPHRD_CHAOS,
    Ieee802 => libc::ARPHRD_IEEE802,
    Arcnet => libc::ARPHRD_ARCNET,
    Appletlk => libc::ARPHRD_APPLETLK,
    Dlci => libc::ARPHRD_DLCI,
    Atm => libc::ARPHRD_APPLETLK,
    Metricom => libc::ARPHRD_METRICOM,
    Ieee1394 => libc::ARPHRD_IEEE1394,
    Eui64 => libc::ARPHRD_EUI64,
    Infiniband => libc::ARPHRD_INFINIBAND,

    // Possibly more types here - need to look into ARP more

    Void => libc::ARPHRD_VOID,
    None => libc::ARPHRD_NONE
);

impl_var!(
    /// Values for `ifi_flags` in `Ifinfomsg`
    Iff, libc::c_uint,
    Up => libc::IFF_UP as libc::c_uint,
    Broadcast => libc::IFF_BROADCAST as libc::c_uint,
    Debug => libc::IFF_DEBUG as libc::c_uint,
    Loopback => libc::IFF_LOOPBACK as libc::c_uint,
    Pointopoint => libc::IFF_POINTOPOINT as libc::c_uint,
    Running => libc::IFF_RUNNING as libc::c_uint,
    Noarp => libc::IFF_NOARP as libc::c_uint,
    Promisc => libc::IFF_PROMISC as libc::c_uint,
    Notrailers => libc::IFF_NOTRAILERS as libc::c_uint,
    Allmulti => libc::IFF_ALLMULTI as libc::c_uint,
    Master => libc::IFF_MASTER as libc::c_uint,
    Slave => libc::IFF_SLAVE as libc::c_uint,
    Multicast => libc::IFF_MULTICAST as libc::c_uint,
    Portsel => libc::IFF_PORTSEL as libc::c_uint,
    Automedia => libc::IFF_AUTOMEDIA as libc::c_uint,
    Dynamic => libc::IFF_DYNAMIC as libc::c_uint,
    LowerUp => libc::IFF_LOWER_UP as libc::c_uint,
    Dormant => libc::IFF_DORMANT as libc::c_uint,
    Echo => libc::IFF_ECHO as libc::c_uint

    // Possibly more types here - need to look into private flags for interfaces
);
<commit_msg>Document which Rtattr.rta_type goes with what rtnetlink struct.<commit_after>impl_var!(
    /// Internet address families
    Af, libc::c_uchar,
    Inet => libc::AF_INET as libc::c_uchar,
    Inet6 => libc::AF_INET6 as libc::c_uchar
);

impl_var!(
    /// General address families for sockets
    RtAddrFamily, u8,
    UnixOrLocal => libc::AF_UNIX as u8,
    Inet => libc::AF_INET as u8,
    Inet6 => libc::AF_INET6 as u8,
    Ipx => libc::AF_IPX as u8,
    Netlink => libc::AF_NETLINK as u8,
    X25 => libc::AF_X25 as u8,
    Ax25 => libc::AF_AX25 as u8,
    Atmpvc => libc::AF_ATMPVC as u8,
    Appletalk => libc::AF_APPLETALK as u8,
    Packet => libc::AF_PACKET as u8,
    Alg => libc::AF_ALG as u8
);

impl_var!(
    /// Interface address flags
    IfaF, u32,
    Secondary => libc::IFA_F_SECONDARY,
    Temporary => libc::IFA_F_TEMPORARY,
    Nodad => libc::IFA_F_NODAD,
    Optimistic => libc::IFA_F_OPTIMISTIC,
    Dadfailed => libc::IFA_F_DADFAILED,
    Homeaddress => libc::IFA_F_HOMEADDRESS,
    Deprecated => libc::IFA_F_DEPRECATED,
    Tentative => libc::IFA_F_TENTATIVE,
    Permanent => libc::IFA_F_PERMANENT,
    #[cfg(target_env="gnu")]
    Managetempaddr => libc::IFA_F_MANAGETEMPADDR,
    #[cfg(target_env="gnu")]
    Noprefixroute => libc::IFA_F_NOPREFIXROUTE,
    #[cfg(target_env="gnu")]
    Mcautojoin => libc::IFA_F_MCAUTOJOIN,
    #[cfg(target_env="gnu")]
    StablePrivacy => libc::IFA_F_STABLE_PRIVACY
);

impl_var!(
    /// `rtm_type`
    /// The results of a lookup from a route table
    Rtn, libc::c_uchar,
    Unspec => libc::RTN_UNSPEC,
    Unicast => libc::RTN_UNICAST,
    Local => libc::RTN_LOCAL,
    Broadcast => libc::RTN_BROADCAST,
    Anycast => libc::RTN_ANYCAST,
    Multicast => libc::RTN_MULTICAST,
    Blackhole => libc::RTN_BLACKHOLE,
    Unreachable => libc::RTN_UNREACHABLE,
    Prohibit => libc::RTN_PROHIBIT,
    Throw => libc::RTN_THROW,
    Nat => libc::RTN_NAT,
    Xresolve => libc::RTN_XRESOLVE
);

impl_var!(
    /// `rtm_protocol`
    /// The origins of routes that are defined in the kernel
    Rtprot, libc::c_uchar,
    Unspec => libc::RTPROT_UNSPEC,
    Redirect => libc::RTPROT_REDIRECT,
    Kernel => libc::RTPROT_KERNEL,
    Boot => libc::RTPROT_BOOT,
    Static => libc::RTPROT_STATIC
);

impl_var!(
    /// `rtm_scope`
    /// The distance between destinations
    RtScope, libc::c_uchar,
    Universe => libc::RT_SCOPE_UNIVERSE,
    Site => libc::RT_SCOPE_SITE,
    Link => libc::RT_SCOPE_LINK,
    Host => libc::RT_SCOPE_HOST,
    Nowhere => libc::RT_SCOPE_NOWHERE
);

impl_var!(
    /// `rt_class_t`
    /// Reserved route table identifiers
    RtTable, libc::c_uchar,
    Unspec => libc::RT_TABLE_UNSPEC,
    Compat => libc::RT_TABLE_COMPAT,
    Default => libc::RT_TABLE_DEFAULT,
    Main => libc::RT_TABLE_MAIN,
    Local => libc::RT_TABLE_LOCAL
);

impl_var!(
    /// `rtm_flags`
    /// Flags for rtnetlink messages
    RtmF, libc::c_uint,
    Notify => libc::RTM_F_NOTIFY,
    Cloned => libc::RTM_F_CLONED,
    Equalize => libc::RTM_F_EQUALIZE,
    Prefix => libc::RTM_F_PREFIX,

    #[cfg(target_env="gnu")]
    LookupTable => libc::RTM_F_LOOKUP_TABLE,
    #[cfg(target_env="gnu")]
    FibMatch => libc::RTM_F_FIB_MATCH
);

impl_var!(
    /// Arp neighbor cache entry states
    Nud, u16,
    None => libc::NUD_NONE,
    Incomplete => libc::NUD_INCOMPLETE,
    Reachable => libc::NUD_REACHABLE,
    Stale => libc::NUD_STALE,
    Delay => libc::NUD_DELAY,
    Probe => libc::NUD_PROBE,
    Failed => libc::NUD_FAILED,
    Noarp => libc::NUD_NOARP,
    Permanent => libc::NUD_PERMANENT
);

impl_var!(
    /// Arp neighbor cache entry flags
    Ntf, u8,
    Use => libc::NTF_USE,
    Self_ => libc::NTF_SELF,
    Master => libc::NTF_MASTER,
    Proxy => libc::NTF_PROXY,
    #[cfg(target_env="gnu")]
    ExtLearned => libc::NTF_EXT_LEARNED,
    #[cfg(target_env="gnu")]
    Offloaded => libc::NTF_OFFLOADED,
    Router => libc::NTF_ROUTER
);

impl_trait!(
    /// Marker trait for `Rtattr.rta_type` field
    RtaType,
    libc::c_ushort
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type`.
    /// Values are interface information message attributes. Used with `Ifinfomsg`.
    Ifla, libc::c_ushort, RtaType,
    Unspec => libc::IFLA_UNSPEC,
    Address => libc::IFLA_ADDRESS,
    Broadcast => libc::IFLA_BROADCAST,
    Ifname => libc::IFLA_IFNAME,
    Mtu => libc::IFLA_MTU,
    Link => libc::IFLA_LINK,
    Qdisc => libc::IFLA_QDISC,
    Stats => libc::IFLA_STATS
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type`.
    /// Values are interface address message attributes. Used with `Ifaddrmsg`.
    Ifa, libc::c_ushort, RtaType,
    Unspec => libc::IFA_UNSPEC,
    Address => libc::IFA_ADDRESS,
    Local => libc::IFA_LOCAL,
    Label => libc::IFA_LABEL,
    Broadcast => libc::IFA_BROADCAST,
    Anycast => libc::IFA_ANYCAST,
    Cacheinfo => libc::IFA_CACHEINFO,
    Multicast => libc::IFA_MULTICAST,
    #[cfg(target_env="gnu")]
    Flags => libc::IFA_FLAGS
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type`.
    /// Values are routing message attributes. Used with `Rtmsg`.
    Rta, libc::c_ushort, RtaType,
    Unspec => libc::RTA_UNSPEC,
    Dst => libc::RTA_DST,
    Src => libc::RTA_SRC,
    Iif => libc::RTA_IIF,
    Oif => libc::RTA_OIF,
    Gateway => libc::RTA_GATEWAY,
    Priority => libc::RTA_PRIORITY,
    Prefsrc => libc::RTA_PREFSRC,
    Metrics => libc::RTA_METRICS,
    Multipath => libc::RTA_MULTIPATH,
    Protoinfo => libc::RTA_PROTOINFO, // no longer used in Linux
    Flow => libc::RTA_FLOW,
    Cacheinfo => libc::RTA_CACHEINFO,
    Session => libc::RTA_SESSION, // no longer used in Linux
    MpAlgo => libc::RTA_MP_ALGO, // no longer used in Linux
    Table => libc::RTA_TABLE,
    Mark => libc::RTA_MARK,
    MfcStats => libc::RTA_MFC_STATS,
    #[cfg(target_env="gnu")]
    Via => libc::RTA_VIA,
    #[cfg(target_env="gnu")]
    Newdst => libc::RTA_NEWDST,
    #[cfg(target_env="gnu")]
    Pref => libc::RTA_PREF,
    #[cfg(target_env="gnu")]
    EncapType => libc::RTA_ENCAP_TYPE,
    #[cfg(target_env="gnu")]
    Encap => libc::RTA_ENCAP,
    #[cfg(target_env="gnu")]
    Expires => libc::RTA_EXPIRES,
    #[cfg(target_env="gnu")]
    Pad => libc::RTA_PAD,
    #[cfg(target_env="gnu")]
    Uid => libc::RTA_UID,
    #[cfg(target_env="gnu")]
    TtlPropagate => libc::RTA_TTL_PROPAGATE
);

impl_var_trait!(
    /// Enum for use with `Rtattr.rta_type` -
    /// Values specify queuing discipline attributes. Used with `Tcmsg`.
    Tca, libc::c_ushort, RtaType,
    Unspec => libc::TCA_UNSPEC,
    Kind => libc::TCA_KIND,
    Options => libc::TCA_OPTIONS,
    Stats => libc::TCA_STATS,
    Xstats => libc::TCA_XSTATS,
    Rate => libc::TCA_RATE,
    Fcnt => libc::TCA_FCNT,
    Stats2 => libc::TCA_STATS2,
    Stab => libc::TCA_STAB
);

impl_var!(
    /// Interface types
    Arphrd, libc::c_ushort,
    Netrom => libc::ARPHRD_NETROM,
    Ether => libc::ARPHRD_ETHER,
    Eether => libc::ARPHRD_EETHER,
    AX25 => libc::ARPHRD_AX25,
    Pronet => libc::ARPHRD_PRONET,
    Chaos => libc::ARPHRD_CHAOS,
    Ieee802 => libc::ARPHRD_IEEE802,
    Arcnet => libc::ARPHRD_ARCNET,
    Appletlk => libc::ARPHRD_APPLETLK,
    Dlci => libc::ARPHRD_DLCI,
    Atm => libc::ARPHRD_APPLETLK,
    Metricom => libc::ARPHRD_METRICOM,
    Ieee1394 => libc::ARPHRD_IEEE1394,
    Eui64 => libc::ARPHRD_EUI64,
    Infiniband => libc::ARPHRD_INFINIBAND,

    // Possibly more types here - need to look into ARP more

    Void => libc::ARPHRD_VOID,
    None => libc::ARPHRD_NONE
);

impl_var!(
    /// Values for `ifi_flags` in `Ifinfomsg`
    Iff, libc::c_uint,
    Up => libc::IFF_UP as libc::c_uint,
    Broadcast => libc::IFF_BROADCAST as libc::c_uint,
    Debug => libc::IFF_DEBUG as libc::c_uint,
    Loopback => libc::IFF_LOOPBACK as libc::c_uint,
    Pointopoint => libc::IFF_POINTOPOINT as libc::c_uint,
    Running => libc::IFF_RUNNING as libc::c_uint,
    Noarp => libc::IFF_NOARP as libc::c_uint,
    Promisc => libc::IFF_PROMISC as libc::c_uint,
    Notrailers => libc::IFF_NOTRAILERS as libc::c_uint,
    Allmulti => libc::IFF_ALLMULTI as libc::c_uint,
    Master => libc::IFF_MASTER as libc::c_uint,
    Slave => libc::IFF_SLAVE as libc::c_uint,
    Multicast => libc::IFF_MULTICAST as libc::c_uint,
    Portsel => libc::IFF_PORTSEL as libc::c_uint,
    Automedia => libc::IFF_AUTOMEDIA as libc::c_uint,
    Dynamic => libc::IFF_DYNAMIC as libc::c_uint,
    LowerUp => libc::IFF_LOWER_UP as libc::c_uint,
    Dormant => libc::IFF_DORMANT as libc::c_uint,
    Echo => libc::IFF_ECHO as libc::c_uint

    // Possibly more types here - need to look into private flags for interfaces
);
<|endoftext|>


<commit_before>//! Trait definitions and type aliases.

use image::{ImageBuffer, Luma, Pixel, Rgb, Rgba};
use std::{i16, u16, u8};

/// An `ImageBuffer` containing Pixels of type P with storage `Vec<P::Subpixel>`.
/// Most operations in this library only support inputs of type `Image`, rather
/// than arbitrary `image::GenericImage`s. This is obviously less flexible, but
/// has the advantage of allowing many functions to be more performant. We may want
/// to add more flexibility later, but this should not be at the expense of performance.
/// When specialisation lands we should be able to do this by defining traits for images
/// with contiguous storage.
pub type Image<P> = ImageBuffer<P, Vec<<P as Pixel>::Subpixel>>;

/// Pixels which have a named Black value.
pub trait HasBlack {
    /// Returns a black pixel of this type.
    fn black() -> Self;
}

/// Pixels which have a named White value.
pub trait HasWhite {
    /// Returns a white pixel of this type.
    fn white() -> Self;
}

macro_rules! impl_black_white {
    ($for_:ty, $min:expr, $max:expr) => {
        impl HasBlack for $for_ {
            fn black() -> Self {
                $min
            }
        }

        impl HasWhite for $for_ {
            fn white() -> Self {
                $max
            }
        }
    };
}

impl_black_white!(Luma<u8>, Luma([u8::MIN]), Luma([u8::MAX]));
impl_black_white!(Luma<u16>, Luma([u16::MIN]), Luma([u16::MAX]));
impl_black_white!(Rgb<u8>, Rgb([u8::MIN; 3]), Rgb([u8::MAX; 3]));
impl_black_white!(Rgb<u16>, Rgb([u16::MIN; 3]), Rgb([u16::MAX; 3]));
impl_black_white!(
    Rgba<u8>,
    Rgba([u8::MIN, u8::MIN, u8::MIN, u8::MAX]),
    Rgba([u8::MAX, u8::MAX, u8::MAX, u8::MAX])
);
impl_black_white!(
    Rgba<u16>,
    Rgba([u16::MIN, u16::MIN, u16::MIN, u16::MAX]),
    Rgba([u16::MAX, u16::MAX, u16::MAX, u16::MAX])
);

/// Something with a 2d position.
pub trait Position {
    /// x-coordinate.
    fn x(&self) -> u32;
    /// y-coordinate.
    fn y(&self) -> u32;
}

/// Something with a score.
pub trait Score {
    /// Score of this item.
    fn score(&self) -> f32;
}

/// A type to which we can clamp a value of type T.
/// Implementations are not required to handle `NaN`s gracefully.
pub trait Clamp<T> {
    /// Clamp `x` to a valid value for this type.
    fn clamp(x: T) -> Self;
}

/// Creates an implementation of Clamp<From> for type To.
macro_rules! implement_clamp {
    ($from:ty, $to:ty, $min:expr, $max:expr, $min_from:expr, $max_from:expr) => {
        impl Clamp<$from> for $to {
            fn clamp(x: $from) -> $to {
                if x < $max_from as $from {
                    if x > $min_from as $from {
                        x as $to
                    } else {
                        $min
                    }
                } else {
                    $max
                }
            }
        }
    };
}

/// Implements Clamp<T> for T, for all input types T.
macro_rules! implement_identity_clamp {
    ( $($t:ty),* ) => {
        $(
            impl Clamp<$t> for $t {
                fn clamp(x: $t) -> $t { x }
            }
        )*
    };
}

implement_clamp!(i16, u8, u8::MIN, u8::MAX, u8::MIN as i16, u8::MAX as i16);
implement_clamp!(u16, u8, u8::MIN, u8::MAX, u8::MIN as u16, u8::MAX as u16);
implement_clamp!(i32, u8, u8::MIN, u8::MAX, u8::MIN as i32, u8::MAX as i32);
implement_clamp!(u32, u8, u8::MIN, u8::MAX, u8::MIN as u32, u8::MAX as u32);
implement_clamp!(f32, u8, u8::MIN, u8::MAX, u8::MIN as f32, u8::MAX as f32);
implement_clamp!(f64, u8, u8::MIN, u8::MAX, u8::MIN as f64, u8::MAX as f64);

implement_clamp!(
    i32,
    u16,
    u16::MIN,
    u16::MAX,
    u16::MIN as i32,
    u16::MAX as i32
);
implement_clamp!(
    f32,
    u16,
    u16::MIN,
    u16::MAX,
    u16::MIN as f32,
    u16::MAX as f32
);
implement_clamp!(
    f64,
    u16,
    u16::MIN,
    u16::MAX,
    u16::MIN as f64,
    u16::MAX as f64
);

implement_clamp!(
    i32,
    i16,
    i16::MIN,
    i16::MAX,
    i16::MIN as i32,
    i16::MAX as i32
);

implement_identity_clamp!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);

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

    #[test]
    fn test_clamp_f32_u8() {
        let t: u8 = Clamp::clamp(255f32);
        assert_eq!(t, 255u8);
        let u: u8 = Clamp::clamp(300f32);
        assert_eq!(u, 255u8);
        let v: u8 = Clamp::clamp(0f32);
        assert_eq!(v, 0u8);
        let w: u8 = Clamp::clamp(-5f32);
        assert_eq!(w, 0u8);
    }

    #[test]
    fn test_clamp_f32_u16() {
        let t: u16 = Clamp::clamp(65535f32);
        assert_eq!(t, 65535u16);
        let u: u16 = Clamp::clamp(300000f32);
        assert_eq!(u, 65535u16);
        let v: u16 = Clamp::clamp(0f32);
        assert_eq!(v, 0u16);
        let w: u16 = Clamp::clamp(-5f32);
        assert_eq!(w, 0u16);
    }
}
<commit_msg>Implement HasBlack and HasWhite for Bgr, Bgra and LumaA (#381)<commit_after>//! Trait definitions and type aliases.

use image::{Bgr, Bgra, ImageBuffer, Luma, LumaA, Pixel, Rgb, Rgba};
use std::{i16, u16, u8};

/// An `ImageBuffer` containing Pixels of type P with storage `Vec<P::Subpixel>`.
/// Most operations in this library only support inputs of type `Image`, rather
/// than arbitrary `image::GenericImage`s. This is obviously less flexible, but
/// has the advantage of allowing many functions to be more performant. We may want
/// to add more flexibility later, but this should not be at the expense of performance.
/// When specialisation lands we should be able to do this by defining traits for images
/// with contiguous storage.
pub type Image<P> = ImageBuffer<P, Vec<<P as Pixel>::Subpixel>>;

/// Pixels which have a named Black value.
pub trait HasBlack {
    /// Returns a black pixel of this type.
    fn black() -> Self;
}

/// Pixels which have a named White value.
pub trait HasWhite {
    /// Returns a white pixel of this type.
    fn white() -> Self;
}

macro_rules! impl_black_white {
    ($for_:ty, $min:expr, $max:expr) => {
        impl HasBlack for $for_ {
            fn black() -> Self {
                $min
            }
        }

        impl HasWhite for $for_ {
            fn white() -> Self {
                $max
            }
        }
    };
}

impl_black_white!(Luma<u8>, Luma([u8::MIN]), Luma([u8::MAX]));
impl_black_white!(Luma<u16>, Luma([u16::MIN]), Luma([u16::MAX]));
impl_black_white!(
    LumaA<u8>,
    LumaA([u8::MIN, u8::MAX]),
    LumaA([u8::MAX, u8::MAX])
);
impl_black_white!(
    LumaA<u16>,
    LumaA([u16::MIN, u16::MAX]),
    LumaA([u16::MAX, u16::MAX])
);
impl_black_white!(Rgb<u8>, Rgb([u8::MIN; 3]), Rgb([u8::MAX; 3]));
impl_black_white!(Rgb<u16>, Rgb([u16::MIN; 3]), Rgb([u16::MAX; 3]));
impl_black_white!(
    Rgba<u8>,
    Rgba([u8::MIN, u8::MIN, u8::MIN, u8::MAX]),
    Rgba([u8::MAX, u8::MAX, u8::MAX, u8::MAX])
);
impl_black_white!(
    Rgba<u16>,
    Rgba([u16::MIN, u16::MIN, u16::MIN, u16::MAX]),
    Rgba([u16::MAX, u16::MAX, u16::MAX, u16::MAX])
);
impl_black_white!(Bgr<u8>, Bgr([u8::MIN; 3]), Bgr([u8::MAX; 3]));
impl_black_white!(Bgr<u16>, Bgr([u16::MIN; 3]), Bgr([u16::MAX; 3]));
impl_black_white!(
    Bgra<u8>,
    Bgra([u8::MIN, u8::MIN, u8::MIN, u8::MAX]),
    Bgra([u8::MAX, u8::MAX, u8::MAX, u8::MAX])
);
impl_black_white!(
    Bgra<u16>,
    Bgra([u16::MIN, u16::MIN, u16::MIN, u16::MAX]),
    Bgra([u16::MAX, u16::MAX, u16::MAX, u16::MAX])
);

/// Something with a 2d position.
pub trait Position {
    /// x-coordinate.
    fn x(&self) -> u32;
    /// y-coordinate.
    fn y(&self) -> u32;
}

/// Something with a score.
pub trait Score {
    /// Score of this item.
    fn score(&self) -> f32;
}

/// A type to which we can clamp a value of type T.
/// Implementations are not required to handle `NaN`s gracefully.
pub trait Clamp<T> {
    /// Clamp `x` to a valid value for this type.
    fn clamp(x: T) -> Self;
}

/// Creates an implementation of Clamp<From> for type To.
macro_rules! implement_clamp {
    ($from:ty, $to:ty, $min:expr, $max:expr, $min_from:expr, $max_from:expr) => {
        impl Clamp<$from> for $to {
            fn clamp(x: $from) -> $to {
                if x < $max_from as $from {
                    if x > $min_from as $from {
                        x as $to
                    } else {
                        $min
                    }
                } else {
                    $max
                }
            }
        }
    };
}

/// Implements Clamp<T> for T, for all input types T.
macro_rules! implement_identity_clamp {
    ( $($t:ty),* ) => {
        $(
            impl Clamp<$t> for $t {
                fn clamp(x: $t) -> $t { x }
            }
        )*
    };
}

implement_clamp!(i16, u8, u8::MIN, u8::MAX, u8::MIN as i16, u8::MAX as i16);
implement_clamp!(u16, u8, u8::MIN, u8::MAX, u8::MIN as u16, u8::MAX as u16);
implement_clamp!(i32, u8, u8::MIN, u8::MAX, u8::MIN as i32, u8::MAX as i32);
implement_clamp!(u32, u8, u8::MIN, u8::MAX, u8::MIN as u32, u8::MAX as u32);
implement_clamp!(f32, u8, u8::MIN, u8::MAX, u8::MIN as f32, u8::MAX as f32);
implement_clamp!(f64, u8, u8::MIN, u8::MAX, u8::MIN as f64, u8::MAX as f64);

implement_clamp!(
    i32,
    u16,
    u16::MIN,
    u16::MAX,
    u16::MIN as i32,
    u16::MAX as i32
);
implement_clamp!(
    f32,
    u16,
    u16::MIN,
    u16::MAX,
    u16::MIN as f32,
    u16::MAX as f32
);
implement_clamp!(
    f64,
    u16,
    u16::MIN,
    u16::MAX,
    u16::MIN as f64,
    u16::MAX as f64
);

implement_clamp!(
    i32,
    i16,
    i16::MIN,
    i16::MAX,
    i16::MIN as i32,
    i16::MAX as i32
);

implement_identity_clamp!(u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);

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

    #[test]
    fn test_clamp_f32_u8() {
        let t: u8 = Clamp::clamp(255f32);
        assert_eq!(t, 255u8);
        let u: u8 = Clamp::clamp(300f32);
        assert_eq!(u, 255u8);
        let v: u8 = Clamp::clamp(0f32);
        assert_eq!(v, 0u8);
        let w: u8 = Clamp::clamp(-5f32);
        assert_eq!(w, 0u8);
    }

    #[test]
    fn test_clamp_f32_u16() {
        let t: u16 = Clamp::clamp(65535f32);
        assert_eq!(t, 65535u16);
        let u: u16 = Clamp::clamp(300000f32);
        assert_eq!(u, 65535u16);
        let v: u16 = Clamp::clamp(0f32);
        assert_eq!(v, 0u16);
        let w: u16 = Clamp::clamp(-5f32);
        assert_eq!(w, 0u16);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

extern crate exif;

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_dynamodb::model::AttributeValue;
use std::process;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The S3 bucket.
    #[structopt(short, long)]
    bucket: String,

    /// The filename.
    #[structopt(short, long)]
    filename: String,

    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// The DynamoDB table.
    #[structopt(short, long)]
    table: String,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

#[derive(Clone, Debug)]
struct Litem {
    name: String,
    confidence: f32,
}

#[derive(Debug)]
struct Edata {
    height: String,
    width: String,
    created: String,
}

#[derive(Debug)]
struct Ldata {
    label1: String,
    value1: String,
    label2: String,
    value2: String,
    label3: String,
    value3: String,
}

// snippet-start:[detect_labels-add_file_to_bucket.rust.main]
async fn add_file_to_bucket(client: &aws_sdk_s3::Client, bucket: &str, filename: &str) {
    let body = aws_sdk_s3::types::ByteStream::from_path(std::path::Path::new(filename)).await;

    match body {
        Ok(b) => {
            match client
                .put_object()
                .bucket(bucket)
                .key(filename)
                .body(b)
                .send()
                .await
            {
                Err(e) => {
                    println!("Got an error uploading file to bucket:");
                    println!("{}", e);
                    process::exit(1);
                }
                Ok(_) => {
                    println!("Uploaded file to bucket.");
                }
            }
        }
        Err(e) => {
            println!("Got an error uploading file to bucket:");
            println!("{}", e);
            process::exit(1);
        }
    }
}
// snippet-end:[detect_labels-add_file_to_bucket.rust.main]

// snippet-start:[detect_labels-get_exif_data.rust.main]
fn get_exif_data(filename: &str) -> Edata {
    let height: String = "".to_owned();
    let width: String = "".to_owned();
    let created: String = "".to_owned();
    let mut edata = Edata {
        height,
        width,
        created,
    };

    let file = std::fs::File::open(&filename).unwrap();
    let mut bufreader = std::io::BufReader::new(&file);
    let exifreader = exif::Reader::new();

    match exifreader.read_from_container(&mut bufreader) {
        Ok(exif) => {
            println!("{}", &filename);

            for f in exif.fields() {
                // Get EXIF values for image width, height, and when image was created.
                match &*f.tag.to_string() {
                    "ImageWidth" => edata.height.push_str(&*f.display_value().to_string()),
                    "ImageLength" => edata.width.push_str(&*f.display_value().to_string()),
                    "DateTimeOriginal" => edata.created.push_str(&*f.display_value().to_string()),
                    _ => {}
                }
            }
        }
        Err(_) => {
            println!();
            println!("File does not contain ELIF data");
        }
    };

    edata
}
// snippet-end:[detect_labels-get_exif_data.rust.main]

// snippet-start:[detect_labels-add_data_to_table.rust.main]
async fn add_data_to_table(
    verbose: bool,
    client: &aws_sdk_dynamodb::Client,
    table: &str,
    filename: &str,
    edata: Edata,
    labels: Vec<Litem>,
) {
    if verbose {
        println!("Added ")
    }
    let filename_string = AttributeValue::S(filename.to_string());
    let height_string = AttributeValue::S(edata.height);
    let created_string = AttributeValue::S(edata.created);
    let width_string = AttributeValue::S(edata.width);
    let label1_label = AttributeValue::S(labels[0].name.to_string());
    let label1_value = AttributeValue::S(labels[0].confidence.to_string());
    let label2_label = AttributeValue::S(labels[1].name.to_string());
    let label2_value = AttributeValue::S(labels[1].confidence.to_string());
    let label3_label = AttributeValue::S(labels[2].name.to_string());
    let label3_value = AttributeValue::S(labels[2].confidence.to_string());

    match client
        .put_item()
        .table_name(table)
        .item("filename", filename_string) // Table key.
        .item("height", height_string)
        .item("width", width_string)
        .item("created", created_string)
        .item("Label1", label1_label)
        .item("Value1", label1_value)
        .item("Label2", label2_label)
        .item("Value2", label2_value)
        .item("Label3", label3_label)
        .item("Value3", label3_value)
        .send()
        .await
    {
        Err(e) => {
            println!("Got an error adding data to table:");
            println!("{}", e);
            process::exit(1);
        }
        Ok(_) => {
            println!("Added info to table.");
        }
    }
}
// snippet-end:[detect_labels-add_data_to_table.rust.main]

// snippet-start:[detect_labels-get_label_data.rust.main]
async fn get_label_data(
    rekog_client: &aws_sdk_rekognition::Client,
    bucket: &str,
    key: &str,
) -> Vec<Litem> {
    let s3_obj = aws_sdk_rekognition::model::S3Object::builder()
        .bucket(bucket)
        .name(key)
        .build();

    let s3_img = aws_sdk_rekognition::model::Image::builder()
        .s3_object(s3_obj)
        .build();

    let resp = rekog_client.detect_labels().image(s3_img).send().await;

    let labels = resp.unwrap().labels.unwrap_or_default();

    // Create vector of Labels.
    let mut label_vec: Vec<Litem> = vec![];

    for label in labels {
        let name = label.name.as_deref().unwrap_or_default();
        let confidence = label.confidence.unwrap();

        let label = Litem {
            name: name.to_string(),
            confidence,
        };
        label_vec.push(label);
    }

    // Sort label items by confidence.
    label_vec.sort_by(|b, a| a.confidence.partial_cmp(&b.confidence).unwrap());

    // Return the first three items.
    label_vec[0..3].to_vec()
}
// snippet-end:[detect_labels-get_label_data.rust.main]

/// Gets EXIF information from a a JPG, JPEG, or PNG file,
/// uploads it to an S3 bucket,
/// uses Rekognition to identify the three top attributes (labels in Rekognition) in the file,
/// and adds the EXIF and label information to a DynamoDB table in the Region.
///
/// # Arguments
///
/// * `-b BUCKET` - The S3 bucket to which the file is uploaded.aws_sdk_dynamodb
/// * `-t TABLE` - The DynamoDB table in which the EXIF and label information is stored.
///   It must use the primary key `filename`.
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), exif::Error> {
    tracing_subscriber::fmt::init();

    let Opt {
        bucket,
        filename,
        region,
        table,
        verbose,
    } = Opt::from_args();

    let dynamo_region = region.clone();
    let s3_region = region.clone();
    let rek_region = region.clone();

    let dynamo_region_provider =
        RegionProviderChain::first_try(dynamo_region.map(aws_sdk_dynamodb::Region::new))
            .or_default_provider()
            .or_else(aws_sdk_dynamodb::Region::new("us-west-2"));

    let rek_region_provider =
        RegionProviderChain::first_try(rek_region.map(aws_sdk_rekognition::Region::new))
            .or_default_provider()
            .or_else(aws_sdk_rekognition::Region::new("us-west-2"));

    let s3_region_provider = RegionProviderChain::first_try(s3_region.map(aws_sdk_s3::Region::new))
        .or_default_provider()
        .or_else(aws_sdk_s3::Region::new("us-west-2"));

    println!();

    if verbose {
        println!(
            "DynamoDB client version:    {}",
            aws_sdk_dynamodb::PKG_VERSION
        );
        println!(
            "Rekognition client version: {}",
            aws_sdk_dynamodb::PKG_VERSION
        );
        println!("S3 client version:          {}", aws_sdk_s3::PKG_VERSION);
        println!("Filename:                   {}", &filename);
        println!("Bucket:                     {}", &bucket);
        println!("Table:                      {}", &table);
        println!();
        println!(
            "Region:                     {}",
            dynamo_region_provider.region().await.unwrap().as_ref()
        );
        println!();
    }

    let s3_shared_config = aws_config::from_env()
        .region(s3_region_provider)
        .load()
        .await;
    let s3_client = aws_sdk_s3::Client::new(&s3_shared_config);

    add_file_to_bucket(&s3_client, &bucket, &filename).await;

    let edata = get_exif_data(&filename);

    let rek_shared_config = aws_config::from_env()
        .region(rek_region_provider)
        .load()
        .await;
    let rek_client = aws_sdk_rekognition::Client::new(&rek_shared_config);

    let labels = get_label_data(&rek_client, &bucket, &filename).await;

    let dynamo_shared_config = aws_config::from_env()
        .region(dynamo_region_provider)
        .load()
        .await;
    let dynamo_client = aws_sdk_dynamodb::Client::new(&dynamo_shared_config);

    // Add data to table.
    add_data_to_table(verbose, &dynamo_client, &table, &filename, edata, labels).await;

    Ok(())
}
<commit_msg>remove: unused struct<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

extern crate exif;

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_dynamodb::model::AttributeValue;
use std::process;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The S3 bucket.
    #[structopt(short, long)]
    bucket: String,

    /// The filename.
    #[structopt(short, long)]
    filename: String,

    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// The DynamoDB table.
    #[structopt(short, long)]
    table: String,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

#[derive(Clone, Debug)]
struct Litem {
    name: String,
    confidence: f32,
}

#[derive(Debug)]
struct Edata {
    height: String,
    width: String,
    created: String,
}

// snippet-start:[detect_labels-add_file_to_bucket.rust.main]
async fn add_file_to_bucket(client: &aws_sdk_s3::Client, bucket: &str, filename: &str) {
    let body = aws_sdk_s3::types::ByteStream::from_path(std::path::Path::new(filename)).await;

    match body {
        Ok(b) => {
            match client
                .put_object()
                .bucket(bucket)
                .key(filename)
                .body(b)
                .send()
                .await
            {
                Err(e) => {
                    println!("Got an error uploading file to bucket:");
                    println!("{}", e);
                    process::exit(1);
                }
                Ok(_) => {
                    println!("Uploaded file to bucket.");
                }
            }
        }
        Err(e) => {
            println!("Got an error uploading file to bucket:");
            println!("{}", e);
            process::exit(1);
        }
    }
}
// snippet-end:[detect_labels-add_file_to_bucket.rust.main]

// snippet-start:[detect_labels-get_exif_data.rust.main]
fn get_exif_data(filename: &str) -> Edata {
    let height: String = "".to_owned();
    let width: String = "".to_owned();
    let created: String = "".to_owned();
    let mut edata = Edata {
        height,
        width,
        created,
    };

    let file = std::fs::File::open(&filename).unwrap();
    let mut bufreader = std::io::BufReader::new(&file);
    let exifreader = exif::Reader::new();

    match exifreader.read_from_container(&mut bufreader) {
        Ok(exif) => {
            println!("{}", &filename);

            for f in exif.fields() {
                // Get EXIF values for image width, height, and when image was created.
                match &*f.tag.to_string() {
                    "ImageWidth" => edata.height.push_str(&*f.display_value().to_string()),
                    "ImageLength" => edata.width.push_str(&*f.display_value().to_string()),
                    "DateTimeOriginal" => edata.created.push_str(&*f.display_value().to_string()),
                    _ => {}
                }
            }
        }
        Err(_) => {
            println!();
            println!("File does not contain ELIF data");
        }
    };

    edata
}
// snippet-end:[detect_labels-get_exif_data.rust.main]

// snippet-start:[detect_labels-add_data_to_table.rust.main]
async fn add_data_to_table(
    verbose: bool,
    client: &aws_sdk_dynamodb::Client,
    table: &str,
    filename: &str,
    edata: Edata,
    labels: Vec<Litem>,
) {
    if verbose {
        println!("Added ")
    }
    let filename_string = AttributeValue::S(filename.to_string());
    let height_string = AttributeValue::S(edata.height);
    let created_string = AttributeValue::S(edata.created);
    let width_string = AttributeValue::S(edata.width);
    let label1_label = AttributeValue::S(labels[0].name.to_string());
    let label1_value = AttributeValue::S(labels[0].confidence.to_string());
    let label2_label = AttributeValue::S(labels[1].name.to_string());
    let label2_value = AttributeValue::S(labels[1].confidence.to_string());
    let label3_label = AttributeValue::S(labels[2].name.to_string());
    let label3_value = AttributeValue::S(labels[2].confidence.to_string());

    match client
        .put_item()
        .table_name(table)
        .item("filename", filename_string) // Table key.
        .item("height", height_string)
        .item("width", width_string)
        .item("created", created_string)
        .item("Label1", label1_label)
        .item("Value1", label1_value)
        .item("Label2", label2_label)
        .item("Value2", label2_value)
        .item("Label3", label3_label)
        .item("Value3", label3_value)
        .send()
        .await
    {
        Err(e) => {
            println!("Got an error adding data to table:");
            println!("{}", e);
            process::exit(1);
        }
        Ok(_) => {
            println!("Added info to table.");
        }
    }
}
// snippet-end:[detect_labels-add_data_to_table.rust.main]

// snippet-start:[detect_labels-get_label_data.rust.main]
async fn get_label_data(
    rekog_client: &aws_sdk_rekognition::Client,
    bucket: &str,
    key: &str,
) -> Vec<Litem> {
    let s3_obj = aws_sdk_rekognition::model::S3Object::builder()
        .bucket(bucket)
        .name(key)
        .build();

    let s3_img = aws_sdk_rekognition::model::Image::builder()
        .s3_object(s3_obj)
        .build();

    let resp = rekog_client.detect_labels().image(s3_img).send().await;

    let labels = resp.unwrap().labels.unwrap_or_default();

    // Create vector of Labels.
    let mut label_vec: Vec<Litem> = vec![];

    for label in labels {
        let name = label.name.as_deref().unwrap_or_default();
        let confidence = label.confidence.unwrap();

        let label = Litem {
            name: name.to_string(),
            confidence,
        };
        label_vec.push(label);
    }

    // Sort label items by confidence.
    label_vec.sort_by(|b, a| a.confidence.partial_cmp(&b.confidence).unwrap());

    // Return the first three items.
    label_vec[0..3].to_vec()
}
// snippet-end:[detect_labels-get_label_data.rust.main]

/// Gets EXIF information from a a JPG, JPEG, or PNG file,
/// uploads it to an S3 bucket,
/// uses Rekognition to identify the three top attributes (labels in Rekognition) in the file,
/// and adds the EXIF and label information to a DynamoDB table in the Region.
///
/// # Arguments
///
/// * `-b BUCKET` - The S3 bucket to which the file is uploaded.aws_sdk_dynamodb
/// * `-t TABLE` - The DynamoDB table in which the EXIF and label information is stored.
///   It must use the primary key `filename`.
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), exif::Error> {
    tracing_subscriber::fmt::init();

    let Opt {
        bucket,
        filename,
        region,
        table,
        verbose,
    } = Opt::from_args();

    let dynamo_region = region.clone();
    let s3_region = region.clone();
    let rek_region = region.clone();

    let dynamo_region_provider =
        RegionProviderChain::first_try(dynamo_region.map(aws_sdk_dynamodb::Region::new))
            .or_default_provider()
            .or_else(aws_sdk_dynamodb::Region::new("us-west-2"));

    let rek_region_provider =
        RegionProviderChain::first_try(rek_region.map(aws_sdk_rekognition::Region::new))
            .or_default_provider()
            .or_else(aws_sdk_rekognition::Region::new("us-west-2"));

    let s3_region_provider = RegionProviderChain::first_try(s3_region.map(aws_sdk_s3::Region::new))
        .or_default_provider()
        .or_else(aws_sdk_s3::Region::new("us-west-2"));

    println!();

    if verbose {
        println!(
            "DynamoDB client version:    {}",
            aws_sdk_dynamodb::PKG_VERSION
        );
        println!(
            "Rekognition client version: {}",
            aws_sdk_dynamodb::PKG_VERSION
        );
        println!("S3 client version:          {}", aws_sdk_s3::PKG_VERSION);
        println!("Filename:                   {}", &filename);
        println!("Bucket:                     {}", &bucket);
        println!("Table:                      {}", &table);
        println!();
        println!(
            "Region:                     {}",
            dynamo_region_provider.region().await.unwrap().as_ref()
        );
        println!();
    }

    let s3_shared_config = aws_config::from_env()
        .region(s3_region_provider)
        .load()
        .await;
    let s3_client = aws_sdk_s3::Client::new(&s3_shared_config);

    add_file_to_bucket(&s3_client, &bucket, &filename).await;

    let edata = get_exif_data(&filename);

    let rek_shared_config = aws_config::from_env()
        .region(rek_region_provider)
        .load()
        .await;
    let rek_client = aws_sdk_rekognition::Client::new(&rek_shared_config);

    let labels = get_label_data(&rek_client, &bucket, &filename).await;

    let dynamo_shared_config = aws_config::from_env()
        .region(dynamo_region_provider)
        .load()
        .await;
    let dynamo_client = aws_sdk_dynamodb::Client::new(&dynamo_shared_config);

    // Add data to table.
    add_data_to_table(verbose, &dynamo_client, &table, &filename, edata, labels).await;

    Ok(())
}
<|endoftext|>


<commit_before>//! Phase infrastructure for Gfx.

use std::cmp::Ordering;
use std::collections::HashMap;
use draw_queue;
use gfx;
use mem;

/// Potential error occuring during rendering.
pub type FlushError = gfx::DrawError<gfx::batch::OutOfBounds>;

/// An abstract rendering phase.
pub trait AbstractPhase<R: gfx::Resources, M, V: ::ToDepth> {
    /// Add an entity to the queue.
    fn enqueue(&mut self, &gfx::Mesh<R>, &gfx::Slice<R>, &M, &V)
               -> Result<bool, gfx::batch::Error>;
    /// Flush the queue into a given stream.
    fn flush<S: gfx::Stream<R>>(&mut self, stream: &mut S)
             -> Result<(), FlushError>;
}

/// A rendering object, encapsulating the batch and additional info
/// needed for sorting. It is only exposed for this matter and
/// accessed by immutable references by the user.
#[allow(missing_docs)]
pub struct Object<S, K, P: gfx::shade::ShaderParam> {
    pub batch: gfx::batch::CoreBatch<P>,
    pub params: P,
    pub slice: gfx::Slice<P::Resources>,
    pub depth: S,
    pub kernel: K,
}

impl<S: Copy, K: Copy, P: gfx::shade::ShaderParam + Clone> Clone
for Object<S, K, P> where
    P::Link: Clone,
{
    fn clone(&self) -> Object<S, K, P> {
        Object {
            batch: self.batch.clone(),
            params: self.params.clone(),
            slice: self.slice.clone(),
            depth: self.depth,
            kernel: self.kernel,
        }
    }
}

impl<S, K, P: gfx::shade::ShaderParam> Object<S, K, P> {
    /// Make a full batch from this object.
    pub fn with<'a>(&'a self, context: &'a gfx::batch::Context<P::Resources>)
                -> gfx::batch::CoreBatchFull<'a, P> {
        context.bind(&self.batch, &self.slice, &self.params)
    }
}

impl<S: PartialOrd, K, P: gfx::shade::ShaderParam> Object<S, K, P> {
    /// A helper method to compare the depth, which is only partially ordered.
    pub fn cmp_depth(&self, other: &Object<S, K, P>) -> Ordering {
        self.depth.partial_cmp(&other.depth)
            .unwrap_or(Ordering::Equal)
    }
}

/// A container for the standard sorting methods.
pub mod sort {
    use std::cmp::Ordering;
    use gfx::shade::ShaderParam;
    use super::Object;
    /// Sort by depth, front-to-back. Useful for opaque objects that updates
    /// the depth buffer. The front stuff will occlude more pixels, leaving
    /// less work to be done for the farther objects.
    pub fn front_to_back<S: PartialOrd, K, P: ShaderParam>(
                         a: &Object<S, K, P>, b: &Object<S, K, P>) -> Ordering
    {
        a.cmp_depth(b)
    }
    /// Sort by depth, back-to-front. Useful for transparent objects, since
    /// blending should take into account everything that lies behind.
    pub fn back_to_front<S: PartialOrd, K, P: ShaderParam>(
                         a: &Object<S, K, P>, b: &Object<S, K, P>) -> Ordering
    {
        b.cmp_depth(a)
    }
    /// Sort by shader program. Switching a program is one of the heaviest
    /// state changes, so this variant is useful when the order is not important.
    pub fn program<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
                   -> Ordering
    {
        a.batch.cmp_program(&b.batch)
    }
    /// Sort by mesh. Allows minimizing the vertex format changes.
    pub fn mesh<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
                -> Ordering
    {
        a.batch.cmp_mesh(&b.batch)
    }
    /// Sort by draw state.
    pub fn state<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
                 -> Ordering
    {
        a.batch.cmp_state(&b.batch)
    }
}

/// Ordering function.
pub type OrderFun<S, K, P> = fn(&Object<S, K, P>, &Object<S, K, P>) -> Ordering;

/// Phase is doing batch construction, accumulation, and memorization,
/// based on a given technique.
pub struct Phase<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
    Y,  // Memory
>{
    /// Phase name.
    pub name: String,
    /// Contained technique.
    pub technique: T,
    /// Sorting function.
    pub sort: Option<OrderFun<V::Depth, T::Kernel, T::Params>>,
    /// Phase memory.
    memory: Y,
    /// Sorted draw queue.
    queue: draw_queue::Queue<Object<V::Depth, T::Kernel, T::Params>>,
    /// Batch context.
    context: gfx::batch::Context<R>,
}

/// Memory typedef using a `HashMap`.
pub type CacheMap<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
> = HashMap<(T::Kernel, gfx::Mesh<R>),
    mem::MemResult<Object<V::Depth, T::Kernel, T::Params>>,
>;

/// A render phase that caches created render objects.
pub type CachedPhase<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
> = Phase<R, M, V, T, CacheMap<R, M, V, T>>;

impl<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
> Phase<R, M, V, T, ()> {
    /// Create a new phase from a given technique.
    pub fn new(name: &str, tech: T) -> Phase<R, M, V, T, ()> {
        Phase {
            name: name.to_string(),
            technique: tech,
            sort: None,
            memory: (),
            queue: draw_queue::Queue::new(),
            context: gfx::batch::Context::new(),
        }
    }

    /// Enable sorting of rendered objects.
    pub fn with_sort(self, fun: OrderFun<V::Depth, T::Kernel, T::Params>)
                     -> Phase<R, M, V, T, ()> {
        Phase {
            sort: Some(fun),
            .. self
        }
    }

    /// Enable caching of created render objects.
    pub fn with_cache(self) -> CachedPhase<R, M, V, T> {
        Phase {
            name: self.name,
            technique: self.technique,
            sort: self.sort,
            memory: HashMap::new(),
            queue: self.queue,
            context: self.context,
        }
    }
}

impl<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth + Copy,
    T: ::Technique<R, M, V>,
    Y: mem::Memory<(T::Kernel, gfx::Mesh<R>),
        Object<V::Depth, T::Kernel, T::Params>
    >,
>AbstractPhase<R, M, V> for Phase<R, M, V, T, Y> where
    T::Params: Clone,
    <T::Params as gfx::shade::ShaderParam>::Link: Clone,
{
    fn enqueue(&mut self, orig_mesh: &gfx::Mesh<R>, slice: &gfx::Slice<R>,
               material: &M, view_info: &V)
               -> Result<bool, gfx::batch::Error> {
        let kernel = match self.technique.test(orig_mesh, material) {
            Some(k) => k,
            None => return Ok(false),
        };
        let depth = view_info.to_depth();
        let key = (kernel, orig_mesh.clone()); //TODO: avoid clone() here
        // Try recalling from memory
        match self.memory.lookup(&key) {
            Some(Ok(mut o)) => {
                o.slice = slice.clone();
                o.depth = depth;
                assert_eq!(o.kernel, kernel);
                self.technique.fix_params(material, view_info, &mut o.params);
                self.queue.objects.push(o);
                return Ok(true)
            },
            Some(Err(e)) => return Err(e),
            None => ()
        }
        // Compile with the technique
        let (program, mut params, inst_mesh, state) =
            self.technique.compile(kernel, view_info);
        self.technique.fix_params(material, view_info, &mut params);
        let mut temp_mesh = gfx::Mesh::new(orig_mesh.num_vertices);
        let mesh = match inst_mesh {
            Some(m) => {
                temp_mesh.attributes.extend(orig_mesh.attributes.iter()
                    .chain(m.attributes.iter()).map(|a| a.clone()));
                &temp_mesh
            },
            None => orig_mesh,
        };
        // Create queue object
        let object = self.context.make_core(program, mesh, state)
            .map(|b| Object {
                batch: b,
                params: params,
                slice: slice.clone(),
                depth: depth,
                kernel: kernel,
            });
        // Remember and return
        self.memory.store(key, object.clone());
        match object {
            Ok(o) => {
                self.queue.objects.push(o);
                Ok(true)
            },
            Err(e) => {
                warn!("Phase {}: batch creation failed: {:?}", self.name, e);
                Err(e)
            },
        }
    }

    fn flush<S: gfx::Stream<R>>(&mut self, stream: &mut S)
             -> Result<(), FlushError> {
        match self.sort {
            Some(fun) => {
                // sort the queue
                self.queue.sort(fun);
                // accumulate the sorted draws into the renderer
                for o in self.queue.iter() {
                    try!(stream.draw(&o.with(&self.context)));
                }
            },
            None => {
                // accumulate the raw draws into the renderer
                for o in self.queue.objects.iter() {
                    try!(stream.draw(&o.with(&self.context)));
                }
            }
        }
        // done
        self.queue.objects.clear();
        Ok(())
    }
}
<commit_msg>OutOfBounds -> Error<commit_after>//! Phase infrastructure for Gfx.

use std::cmp::Ordering;
use std::collections::HashMap;
use draw_queue;
use gfx;
use mem;

/// Potential error occuring during rendering.
pub type FlushError = gfx::DrawError<gfx::batch::Error>;

/// An abstract rendering phase.
pub trait AbstractPhase<R: gfx::Resources, M, V: ::ToDepth> {
    /// Add an entity to the queue.
    fn enqueue(&mut self, &gfx::Mesh<R>, &gfx::Slice<R>, &M, &V)
               -> Result<bool, gfx::batch::Error>;
    /// Flush the queue into a given stream.
    fn flush<S: gfx::Stream<R>>(&mut self, stream: &mut S)
             -> Result<(), FlushError>;
}

/// A rendering object, encapsulating the batch and additional info
/// needed for sorting. It is only exposed for this matter and
/// accessed by immutable references by the user.
#[allow(missing_docs)]
pub struct Object<S, K, P: gfx::shade::ShaderParam> {
    pub batch: gfx::batch::CoreBatch<P>,
    pub params: P,
    pub slice: gfx::Slice<P::Resources>,
    pub depth: S,
    pub kernel: K,
}

impl<S: Copy, K: Copy, P: gfx::shade::ShaderParam + Clone> Clone
for Object<S, K, P> where
    P::Link: Clone,
{
    fn clone(&self) -> Object<S, K, P> {
        Object {
            batch: self.batch.clone(),
            params: self.params.clone(),
            slice: self.slice.clone(),
            depth: self.depth,
            kernel: self.kernel,
        }
    }
}

impl<S, K, P: gfx::shade::ShaderParam> Object<S, K, P> {
    /// Make a full batch from this object.
    pub fn with<'a>(&'a self, context: &'a gfx::batch::Context<P::Resources>)
                -> gfx::batch::CoreBatchFull<'a, P> {
        context.bind(&self.batch, &self.slice, &self.params)
    }
}

impl<S: PartialOrd, K, P: gfx::shade::ShaderParam> Object<S, K, P> {
    /// A helper method to compare the depth, which is only partially ordered.
    pub fn cmp_depth(&self, other: &Object<S, K, P>) -> Ordering {
        self.depth.partial_cmp(&other.depth)
            .unwrap_or(Ordering::Equal)
    }
}

/// A container for the standard sorting methods.
pub mod sort {
    use std::cmp::Ordering;
    use gfx::shade::ShaderParam;
    use super::Object;
    /// Sort by depth, front-to-back. Useful for opaque objects that updates
    /// the depth buffer. The front stuff will occlude more pixels, leaving
    /// less work to be done for the farther objects.
    pub fn front_to_back<S: PartialOrd, K, P: ShaderParam>(
                         a: &Object<S, K, P>, b: &Object<S, K, P>) -> Ordering
    {
        a.cmp_depth(b)
    }
    /// Sort by depth, back-to-front. Useful for transparent objects, since
    /// blending should take into account everything that lies behind.
    pub fn back_to_front<S: PartialOrd, K, P: ShaderParam>(
                         a: &Object<S, K, P>, b: &Object<S, K, P>) -> Ordering
    {
        b.cmp_depth(a)
    }
    /// Sort by shader program. Switching a program is one of the heaviest
    /// state changes, so this variant is useful when the order is not important.
    pub fn program<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
                   -> Ordering
    {
        a.batch.cmp_program(&b.batch)
    }
    /// Sort by mesh. Allows minimizing the vertex format changes.
    pub fn mesh<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
                -> Ordering
    {
        a.batch.cmp_mesh(&b.batch)
    }
    /// Sort by draw state.
    pub fn state<S, K, P: ShaderParam>(a: &Object<S, K, P>, b: &Object<S, K, P>)
                 -> Ordering
    {
        a.batch.cmp_state(&b.batch)
    }
}

/// Ordering function.
pub type OrderFun<S, K, P> = fn(&Object<S, K, P>, &Object<S, K, P>) -> Ordering;

/// Phase is doing batch construction, accumulation, and memorization,
/// based on a given technique.
pub struct Phase<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
    Y,  // Memory
>{
    /// Phase name.
    pub name: String,
    /// Contained technique.
    pub technique: T,
    /// Sorting function.
    pub sort: Option<OrderFun<V::Depth, T::Kernel, T::Params>>,
    /// Phase memory.
    memory: Y,
    /// Sorted draw queue.
    queue: draw_queue::Queue<Object<V::Depth, T::Kernel, T::Params>>,
    /// Batch context.
    context: gfx::batch::Context<R>,
}

/// Memory typedef using a `HashMap`.
pub type CacheMap<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
> = HashMap<(T::Kernel, gfx::Mesh<R>),
    mem::MemResult<Object<V::Depth, T::Kernel, T::Params>>,
>;

/// A render phase that caches created render objects.
pub type CachedPhase<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
> = Phase<R, M, V, T, CacheMap<R, M, V, T>>;

impl<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth,
    T: ::Technique<R, M, V>,
> Phase<R, M, V, T, ()> {
    /// Create a new phase from a given technique.
    pub fn new(name: &str, tech: T) -> Phase<R, M, V, T, ()> {
        Phase {
            name: name.to_string(),
            technique: tech,
            sort: None,
            memory: (),
            queue: draw_queue::Queue::new(),
            context: gfx::batch::Context::new(),
        }
    }

    /// Enable sorting of rendered objects.
    pub fn with_sort(self, fun: OrderFun<V::Depth, T::Kernel, T::Params>)
                     -> Phase<R, M, V, T, ()> {
        Phase {
            sort: Some(fun),
            .. self
        }
    }

    /// Enable caching of created render objects.
    pub fn with_cache(self) -> CachedPhase<R, M, V, T> {
        Phase {
            name: self.name,
            technique: self.technique,
            sort: self.sort,
            memory: HashMap::new(),
            queue: self.queue,
            context: self.context,
        }
    }
}

impl<
    R: gfx::Resources,
    M: ::Material,
    V: ::ToDepth + Copy,
    T: ::Technique<R, M, V>,
    Y: mem::Memory<(T::Kernel, gfx::Mesh<R>),
        Object<V::Depth, T::Kernel, T::Params>
    >,
>AbstractPhase<R, M, V> for Phase<R, M, V, T, Y> where
    T::Params: Clone,
    <T::Params as gfx::shade::ShaderParam>::Link: Clone,
{
    fn enqueue(&mut self, orig_mesh: &gfx::Mesh<R>, slice: &gfx::Slice<R>,
               material: &M, view_info: &V)
               -> Result<bool, gfx::batch::Error> {
        let kernel = match self.technique.test(orig_mesh, material) {
            Some(k) => k,
            None => return Ok(false),
        };
        let depth = view_info.to_depth();
        let key = (kernel, orig_mesh.clone()); //TODO: avoid clone() here
        // Try recalling from memory
        match self.memory.lookup(&key) {
            Some(Ok(mut o)) => {
                o.slice = slice.clone();
                o.depth = depth;
                assert_eq!(o.kernel, kernel);
                self.technique.fix_params(material, view_info, &mut o.params);
                self.queue.objects.push(o);
                return Ok(true)
            },
            Some(Err(e)) => return Err(e),
            None => ()
        }
        // Compile with the technique
        let (program, mut params, inst_mesh, state) =
            self.technique.compile(kernel, view_info);
        self.technique.fix_params(material, view_info, &mut params);
        let mut temp_mesh = gfx::Mesh::new(orig_mesh.num_vertices);
        let mesh = match inst_mesh {
            Some(m) => {
                temp_mesh.attributes.extend(orig_mesh.attributes.iter()
                    .chain(m.attributes.iter()).map(|a| a.clone()));
                &temp_mesh
            },
            None => orig_mesh,
        };
        // Create queue object
        let object = self.context.make_core(program, mesh, state)
            .map(|b| Object {
                batch: b,
                params: params,
                slice: slice.clone(),
                depth: depth,
                kernel: kernel,
            });
        // Remember and return
        self.memory.store(key, object.clone());
        match object {
            Ok(o) => {
                self.queue.objects.push(o);
                Ok(true)
            },
            Err(e) => {
                warn!("Phase {}: batch creation failed: {:?}", self.name, e);
                Err(e)
            },
        }
    }

    fn flush<S: gfx::Stream<R>>(&mut self, stream: &mut S)
             -> Result<(), FlushError> {
        match self.sort {
            Some(fun) => {
                // sort the queue
                self.queue.sort(fun);
                // accumulate the sorted draws into the renderer
                for o in self.queue.iter() {
                    try!(stream.draw(&o.with(&self.context)));
                }
            },
            None => {
                // accumulate the raw draws into the renderer
                for o in self.queue.objects.iter() {
                    try!(stream.draw(&o.with(&self.context)));
                }
            }
        }
        // done
        self.queue.objects.clear();
        Ok(())
    }
}
<|endoftext|>


<commit_before>use game::gfx_macros::Projection;
use cgmath;
use cgmath::{Matrix4, Point3, Vector3};
use specs;
use terrain;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};

pub type Delta = f32;
pub type Planner = specs::Planner<Delta>;

#[derive(Debug, Clone)]
pub struct Dimensions {
  width: u32,
  height: u32,
}

impl Dimensions {
  pub fn new(_window_width: u32, _window_height: u32) -> Dimensions {
    Dimensions {
      width: RESOLUTION_X,
      height: RESOLUTION_Y,
    }
  }

  pub fn world_to_projection(&self, input: &mut terrain::controls::TerrainInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(input.x_pos, -input.y_pos, input.distance),
      Point3::new(input.x_pos, -input.y_pos, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.width as f32 / self.height as f32;
    Projection {
      model: Matrix4::from(view).into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(60.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}
<commit_msg>Add direction function to calculate direction from one point to another<commit_after>use game::gfx_macros::Projection;
use cgmath;
use cgmath::{Matrix4, Point3, Vector3, Point2, Rad};
use specs;
use terrain;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};

pub type Delta = f32;
pub type Planner = specs::Planner<Delta>;

#[derive(Debug, Clone)]
pub struct Dimensions {
  width: u32,
  height: u32,
}

impl Dimensions {
  pub fn new(_window_width: u32, _window_height: u32) -> Dimensions {
    Dimensions {
      width: RESOLUTION_X,
      height: RESOLUTION_Y,
    }
  }

  pub fn world_to_projection(&self, input: &mut terrain::controls::TerrainInputState) -> Projection {
    let view: Matrix4<f32> = Matrix4::look_at(
      Point3::new(input.x_pos, -input.y_pos, input.distance),
      Point3::new(input.x_pos, -input.y_pos, 0.0),
      Vector3::unit_y(),
    );
    let aspect_ratio = self.width as f32 / self.height as f32;
    Projection {
      model: Matrix4::from(view).into(),
      view: view.into(),
      proj: cgmath::perspective(cgmath::Deg(60.0f32), aspect_ratio, 0.1, 4000.0).into(),
    }
  }
}

fn direction(start_point: Point2<f32>, end_point: Point2<f32>) -> Rad<f32> {
  cgmath::Angle::atan2(end_point.y - start_point.y, (end_point.x - start_point.x))
}
<|endoftext|>