<commit_before>//! OpenGL implementation of a device, striving to support OpenGL 2.0 with at
//! least VAOs, but using newer extensions when available.

#![allow(missing_docs, missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
extern crate gfx_gl as gl;
extern crate gfx_hal as hal;
#[cfg(feature = "glutin")]
pub extern crate glutin;
extern crate smallvec;
extern crate spirv_cross;

use std::cell::Cell;
use std::fmt;
use std::ops::Deref;
use std::sync::{Arc, Weak};
use std::thread::{self, ThreadId};

use hal::queue::{QueueFamilyId, Queues};
use hal::{error, image, pso};

pub use self::device::Device;
pub use self::info::{Info, PlatformName, Version};

mod command;
mod conv;
mod device;
mod info;
mod native;
mod pool;
mod queue;
mod state;
mod window;

#[cfg(feature = "glutin")]
pub use window::glutin::{config_context, Headless, Surface, Swapchain};

pub(crate) struct GlContainer {
    context: gl::Gl,
}

impl GlContainer {
    fn make_current(&self) {
        // Unimplemented
    }
}

impl Deref for GlContainer {
    type Target = gl::Gl;
    fn deref(&self) -> &gl::Gl {
        #[cfg(feature = "glutin")]
        self.make_current();
        &self.context
    }
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Backend {}
impl hal::Backend for Backend {
    type PhysicalDevice = PhysicalDevice;
    type Device = Device;

    type Surface = Surface;
    type Swapchain = Swapchain;

    type QueueFamily = QueueFamily;
    type CommandQueue = queue::CommandQueue;
    type CommandBuffer = command::RawCommandBuffer;

    type Memory = native::Memory;
    type CommandPool = pool::RawCommandPool;

    type ShaderModule = native::ShaderModule;
    type RenderPass = native::RenderPass;
    type Framebuffer = native::FrameBuffer;

    type Buffer = native::Buffer;
    type BufferView = native::BufferView;
    type Image = native::Image;
    type ImageView = native::ImageView;
    type Sampler = native::FatSampler;

    type ComputePipeline = native::ComputePipeline;
    type GraphicsPipeline = native::GraphicsPipeline;
    type PipelineLayout = native::PipelineLayout;
    type PipelineCache = ();
    type DescriptorSetLayout = native::DescriptorSetLayout;
    type DescriptorPool = native::DescriptorPool;
    type DescriptorSet = native::DescriptorSet;

    type Fence = native::Fence;
    type Semaphore = native::Semaphore;
    type QueryPool = ();
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
    NoError,
    InvalidEnum,
    InvalidValue,
    InvalidOperation,
    InvalidFramebufferOperation,
    OutOfMemory,
    UnknownError,
}

impl Error {
    pub fn from_error_code(error_code: gl::types::GLenum) -> Error {
        match error_code {
            gl::NO_ERROR => Error::NoError,
            gl::INVALID_ENUM => Error::InvalidEnum,
            gl::INVALID_VALUE => Error::InvalidValue,
            gl::INVALID_OPERATION => Error::InvalidOperation,
            gl::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
            gl::OUT_OF_MEMORY => Error::OutOfMemory,
            _ => Error::UnknownError,
        }
    }
}

/// Internal struct of shared data between the physical and logical device.
struct Share {
    context: GlContainer,
    info: Info,
    features: hal::Features,
    legacy_features: info::LegacyFeatures,
    limits: hal::Limits,
    private_caps: info::PrivateCaps,
    // Indicates if there is an active logical device.
    open: Cell<bool>,
}

impl Share {
    /// Fails during a debug build if the implementation's error flag was set.
    fn check(&self) -> Result<(), Error> {
        if cfg!(debug_assertions) {
            let gl = &self.context;
            let err = Error::from_error_code(unsafe { gl.GetError() });
            if err != Error::NoError {
                return Err(err);
            }
        }
        Ok(())
    }
}

/// Single-threaded `Arc`.
/// Wrapper for `Arc` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Starc<T: ?Sized> {
    arc: Arc<T>,
    thread: ThreadId,
}

impl<T: ?Sized> Clone for Starc<T> {
    fn clone(&self) -> Self {
        Self {
            arc: self.arc.clone(),
            thread: self.thread,
        }
    }
}

impl<T: ?Sized> fmt::Debug for Starc<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:p}@{:?}", self.arc, self.thread)
    }
}

impl<T> Starc<T> {
    #[inline]
    fn new(value: T) -> Self {
        Starc {
            arc: Arc::new(value),
            thread: thread::current().id(),
        }
    }

    #[inline]
    pub fn try_unwrap(self) -> Result<T, Self> {
        let a = Arc::try_unwrap(self.arc);
        let thread = self.thread;
        a.map_err(|a| Starc {
            arc: a,
            thread: thread,
        })
    }

    #[inline]
    pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
        Wstarc {
            weak: Arc::downgrade(&this.arc),
            thread: this.thread,
        }
    }

    #[inline]
    pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
        Arc::get_mut(&mut this.arc)
    }
}

unsafe impl<T: ?Sized> Send for Starc<T> {}
unsafe impl<T: ?Sized> Sync for Starc<T> {}

impl<T: ?Sized> Deref for Starc<T> {
    type Target = T;
    fn deref(&self) -> &T {
        assert_eq!(thread::current().id(), self.thread);
        &*self.arc
    }
}

/// Single-threaded `Weak`.
/// Wrapper for `Weak` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Wstarc<T: ?Sized> {
    weak: Weak<T>,
    thread: ThreadId,
}
impl<T> Wstarc<T> {
    pub fn upgrade(&self) -> Option<Starc<T>> {
        let thread = self.thread;
        self.weak.upgrade().map(|arc| Starc { arc, thread })
    }
}
unsafe impl<T: ?Sized> Send for Wstarc<T> {}
unsafe impl<T: ?Sized> Sync for Wstarc<T> {}

#[derive(Debug)]
pub struct PhysicalDevice(Starc<Share>);

impl PhysicalDevice {
    fn new_adapter<F>(fn_proc: F) -> hal::Adapter<Backend>
    where
        F: FnMut(&str) -> *const std::os::raw::c_void,
    {
        let gl = GlContainer {
            context: gl::Gl::load_with(fn_proc),
        };

        // query information
        let (info, features, legacy_features, limits, private_caps) = info::query_all(&gl);
        info!("Vendor: {:?}", info.platform_name.vendor);
        info!("Renderer: {:?}", info.platform_name.renderer);
        info!("Version: {:?}", info.version);
        info!("Shading Language: {:?}", info.shading_language);
        info!("Features: {:?}", features);
        info!("Legacy Features: {:?}", legacy_features);
        debug!("Loaded Extensions:");
        for extension in info.extensions.iter() {
            debug!("- {}", *extension);
        }
        let name = info.platform_name.renderer.into();
        let vendor: std::string::String = info.platform_name.vendor.into();
        let renderer: std::string::String = info.platform_name.renderer.into();

        // create the shared context
        let share = Share {
            context: gl,
            info,
            features,
            legacy_features,
            limits,
            private_caps,
            open: Cell::new(false),
        };
        if let Err(err) = share.check() {
            panic!("Error querying info: {:?}", err);
        }

        // opengl has no way to discern device_type, so we can try to infer it from the renderer string
        let vendor_lower = vendor.to_lowercase();
        let renderer_lower = renderer.to_lowercase();
        let strings_that_imply_integrated = [
            " xpress", // space here is on purpose so we don't match express
            "radeon hd 4200",
            "radeon hd 4250",
            "radeon hd 4290",
            "radeon hd 4270",
            "radeon hd 4225",
            "radeon hd 3100",
            "radeon hd 3200",
            "radeon hd 3000",
            "radeon hd 3300",
            "radeon(tm) r4 graphics",
            "radeon(tm) r5 graphics",
            "radeon(tm) r6 graphics",
            "radeon(tm) r7 graphics",
            "radeon r7 graphics",
            "nforce", // All nvidia nforce are integrated
            "tegra",  // all nvidia tegra are integrated
            "shield",
            "igp",
            "mali",
            "intel",
        ];
        // todo Intel will release a discrete gpu soon, and we will need to update this logic when they do
        let inferred_device_type = if vendor_lower.contains("qualcomm")
            || vendor_lower.contains("intel")
            || strings_that_imply_integrated
                .into_iter()
                .any(|&s| renderer_lower.contains(s))
        {
            hal::adapter::DeviceType::IntegratedGpu
        } else {
            hal::adapter::DeviceType::DiscreteGpu
        };

        // source: Sascha Willems at Vulkan
        let vendor_id = if vendor_lower.contains("amd") {
            0x1002
        } else if vendor_lower.contains("imgtec") {
            0x1010
        } else if vendor_lower.contains("nvidia") {
            0x10DE
        } else if vendor_lower.contains("arm") {
            0x13B5
        } else if vendor_lower.contains("qualcomm") {
            0x5143
        } else if vendor_lower.contains("intel") {
            0x8086
        } else {
            0
        };

        hal::Adapter {
            info: hal::AdapterInfo {
                name,
                vendor: vendor_id,
                device: 0,
                device_type: inferred_device_type,
            },
            physical_device: PhysicalDevice(Starc::new(share)),
            queue_families: vec![QueueFamily],
        }
    }

    /// Get GL-specific legacy feature flags.
    pub fn legacy_features(&self) -> &info::LegacyFeatures {
        &self.0.legacy_features
    }
}

impl hal::PhysicalDevice<Backend> for PhysicalDevice {
    unsafe fn open(
        &self,
        families: &[(&QueueFamily, &[hal::QueuePriority])],
        requested_features: hal::Features,
    ) -> Result<hal::Gpu<Backend>, error::DeviceCreationError> {
        // Can't have multiple logical devices at the same time
        // as they would share the same context.
        if self.0.open.get() {
            return Err(error::DeviceCreationError::TooManyObjects);
        }
        self.0.open.set(true);

        // TODO: Check for support in the LeagcyFeatures struct too
        if !self.features().contains(requested_features) {
            return Err(error::DeviceCreationError::MissingFeature);
        }

        // initialize permanent states
        let gl = &self.0.context;
        if self
            .0
            .legacy_features
            .contains(info::LegacyFeatures::SRGB_COLOR)
        {
            // TODO: Find way to emulate this on older Opengl versions.
            unsafe {
                gl.Enable(gl::FRAMEBUFFER_SRGB);
            }
        }
        unsafe {
            gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);

            if !self.0.info.version.is_embedded {
                gl.Enable(gl::PROGRAM_POINT_SIZE);
            }
        }

        // create main VAO and bind it
        let mut vao = 0;
        if self.0.private_caps.vertex_array {
            unsafe {
                gl.GenVertexArrays(1, &mut vao);
                gl.BindVertexArray(vao);
            }
        }

        if let Err(err) = self.0.check() {
            panic!("Error opening adapter: {:?}", err);
        }

        Ok(hal::Gpu {
            device: Device::new(self.0.clone()),
            queues: Queues::new(
                families
                    .into_iter()
                    .map(|&(proto_family, priorities)| {
                        assert_eq!(priorities.len(), 1);
                        let mut family = hal::backend::RawQueueGroup::new(proto_family.clone());
                        let queue = queue::CommandQueue::new(&self.0, vao);
                        family.add_queue(queue);
                        family
                    })
                    .collect(),
            ),
        })
    }

    fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
        unimplemented!()
    }

    fn image_format_properties(
        &self,
        _format: hal::format::Format,
        _dimensions: u8,
        _tiling: image::Tiling,
        _usage: image::Usage,
        _view_caps: image::ViewCapabilities,
    ) -> Option<image::FormatProperties> {
        None //TODO
    }

    fn memory_properties(&self) -> hal::MemoryProperties {
        use hal::memory::Properties;

        // COHERENT flags require that the backend does flushing and invalidation
        // by itself. If we move towards persistent mapping we need to re-evaluate it.
        let memory_types = if self.0.private_caps.map {
            vec![
                hal::MemoryType {
                    properties: Properties::DEVICE_LOCAL,
                    heap_index: 1,
                },
                hal::MemoryType {
                    // upload
                    properties: Properties::CPU_VISIBLE | Properties::COHERENT,
                    heap_index: 0,
                },
                hal::MemoryType {
                    // download
                    properties: Properties::CPU_VISIBLE
                        | Properties::COHERENT
                        | Properties::CPU_CACHED,
                    heap_index: 0,
                },
            ]
        } else {
            vec![hal::MemoryType {
                properties: Properties::DEVICE_LOCAL,
                heap_index: 0,
            }]
        };

        hal::MemoryProperties {
            memory_types,
            memory_heaps: vec![!0, !0],
        }
    }

    fn features(&self) -> hal::Features {
        self.0.features
    }

    fn limits(&self) -> hal::Limits {
        self.0.limits
    }
}

#[derive(Debug, Clone, Copy)]
pub struct QueueFamily;

impl hal::QueueFamily for QueueFamily {
    fn queue_type(&self) -> hal::QueueType {
        hal::QueueType::General
    }
    fn max_queues(&self) -> usize {
        1
    }
    fn id(&self) -> QueueFamilyId {
        QueueFamilyId(0)
    }
}
<commit_msg>rustfmt<commit_after>//! OpenGL implementation of a device, striving to support OpenGL 2.0 with at
//! least VAOs, but using newer extensions when available.

#![allow(missing_docs, missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate log;
extern crate gfx_gl as gl;
extern crate gfx_hal as hal;
#[cfg(feature = "glutin")]
pub extern crate glutin;
extern crate smallvec;
extern crate spirv_cross;

use std::cell::Cell;
use std::fmt;
use std::ops::Deref;
use std::sync::{Arc, Weak};
use std::thread::{self, ThreadId};

use hal::queue::{QueueFamilyId, Queues};
use hal::{error, image, pso};

pub use self::device::Device;
pub use self::info::{Info, PlatformName, Version};

mod command;
mod conv;
mod device;
mod info;
mod native;
mod pool;
mod queue;
mod state;
mod window;

#[cfg(feature = "glutin")]
pub use window::glutin::{config_context, Headless, Surface, Swapchain};

pub(crate) struct GlContainer {
    context: gl::Gl,
}

impl GlContainer {
    fn make_current(&self) {
        // Unimplemented
    }
}

impl Deref for GlContainer {
    type Target = gl::Gl;
    fn deref(&self) -> &gl::Gl {
        #[cfg(feature = "glutin")]
        self.make_current();
        &self.context
    }
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Backend {}
impl hal::Backend for Backend {
    type PhysicalDevice = PhysicalDevice;
    type Device = Device;

    type Surface = Surface;
    type Swapchain = Swapchain;

    type QueueFamily = QueueFamily;
    type CommandQueue = queue::CommandQueue;
    type CommandBuffer = command::RawCommandBuffer;

    type Memory = native::Memory;
    type CommandPool = pool::RawCommandPool;

    type ShaderModule = native::ShaderModule;
    type RenderPass = native::RenderPass;
    type Framebuffer = native::FrameBuffer;

    type Buffer = native::Buffer;
    type BufferView = native::BufferView;
    type Image = native::Image;
    type ImageView = native::ImageView;
    type Sampler = native::FatSampler;

    type ComputePipeline = native::ComputePipeline;
    type GraphicsPipeline = native::GraphicsPipeline;
    type PipelineLayout = native::PipelineLayout;
    type PipelineCache = ();
    type DescriptorSetLayout = native::DescriptorSetLayout;
    type DescriptorPool = native::DescriptorPool;
    type DescriptorSet = native::DescriptorSet;

    type Fence = native::Fence;
    type Semaphore = native::Semaphore;
    type QueryPool = ();
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
    NoError,
    InvalidEnum,
    InvalidValue,
    InvalidOperation,
    InvalidFramebufferOperation,
    OutOfMemory,
    UnknownError,
}

impl Error {
    pub fn from_error_code(error_code: gl::types::GLenum) -> Error {
        match error_code {
            gl::NO_ERROR => Error::NoError,
            gl::INVALID_ENUM => Error::InvalidEnum,
            gl::INVALID_VALUE => Error::InvalidValue,
            gl::INVALID_OPERATION => Error::InvalidOperation,
            gl::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
            gl::OUT_OF_MEMORY => Error::OutOfMemory,
            _ => Error::UnknownError,
        }
    }
}

/// Internal struct of shared data between the physical and logical device.
struct Share {
    context: GlContainer,
    info: Info,
    features: hal::Features,
    legacy_features: info::LegacyFeatures,
    limits: hal::Limits,
    private_caps: info::PrivateCaps,
    // Indicates if there is an active logical device.
    open: Cell<bool>,
}

impl Share {
    /// Fails during a debug build if the implementation's error flag was set.
    fn check(&self) -> Result<(), Error> {
        if cfg!(debug_assertions) {
            let gl = &self.context;
            let err = Error::from_error_code(unsafe { gl.GetError() });
            if err != Error::NoError {
                return Err(err);
            }
        }
        Ok(())
    }
}

/// Single-threaded `Arc`.
/// Wrapper for `Arc` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Starc<T: ?Sized> {
    arc: Arc<T>,
    thread: ThreadId,
}

impl<T: ?Sized> Clone for Starc<T> {
    fn clone(&self) -> Self {
        Self {
            arc: self.arc.clone(),
            thread: self.thread,
        }
    }
}

impl<T: ?Sized> fmt::Debug for Starc<T> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{:p}@{:?}", self.arc, self.thread)
    }
}

impl<T> Starc<T> {
    #[inline]
    fn new(value: T) -> Self {
        Starc {
            arc: Arc::new(value),
            thread: thread::current().id(),
        }
    }

    #[inline]
    pub fn try_unwrap(self) -> Result<T, Self> {
        let a = Arc::try_unwrap(self.arc);
        let thread = self.thread;
        a.map_err(|a| Starc {
            arc: a,
            thread: thread,
        })
    }

    #[inline]
    pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
        Wstarc {
            weak: Arc::downgrade(&this.arc),
            thread: this.thread,
        }
    }

    #[inline]
    pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
        Arc::get_mut(&mut this.arc)
    }
}

unsafe impl<T: ?Sized> Send for Starc<T> {}
unsafe impl<T: ?Sized> Sync for Starc<T> {}

impl<T: ?Sized> Deref for Starc<T> {
    type Target = T;
    fn deref(&self) -> &T {
        assert_eq!(thread::current().id(), self.thread);
        &*self.arc
    }
}

/// Single-threaded `Weak`.
/// Wrapper for `Weak` that allows you to `Send` it even if `T: !Sync`.
/// Yet internal data cannot be accessed outside of the thread where it was created.
pub struct Wstarc<T: ?Sized> {
    weak: Weak<T>,
    thread: ThreadId,
}
impl<T> Wstarc<T> {
    pub fn upgrade(&self) -> Option<Starc<T>> {
        let thread = self.thread;
        self.weak.upgrade().map(|arc| Starc { arc, thread })
    }
}
unsafe impl<T: ?Sized> Send for Wstarc<T> {}
unsafe impl<T: ?Sized> Sync for Wstarc<T> {}

#[derive(Debug)]
pub struct PhysicalDevice(Starc<Share>);

impl PhysicalDevice {
    fn new_adapter<F>(fn_proc: F) -> hal::Adapter<Backend>
    where
        F: FnMut(&str) -> *const std::os::raw::c_void,
    {
        let gl = GlContainer {
            context: gl::Gl::load_with(fn_proc),
        };

        // query information
        let (info, features, legacy_features, limits, private_caps) = info::query_all(&gl);
        info!("Vendor: {:?}", info.platform_name.vendor);
        info!("Renderer: {:?}", info.platform_name.renderer);
        info!("Version: {:?}", info.version);
        info!("Shading Language: {:?}", info.shading_language);
        info!("Features: {:?}", features);
        info!("Legacy Features: {:?}", legacy_features);
        debug!("Loaded Extensions:");
        for extension in info.extensions.iter() {
            debug!("- {}", *extension);
        }
        let name = info.platform_name.renderer.into();
        let vendor: std::string::String = info.platform_name.vendor.into();
        let renderer: std::string::String = info.platform_name.renderer.into();

        // create the shared context
        let share = Share {
            context: gl,
            info,
            features,
            legacy_features,
            limits,
            private_caps,
            open: Cell::new(false),
        };
        if let Err(err) = share.check() {
            panic!("Error querying info: {:?}", err);
        }

        // opengl has no way to discern device_type, so we can try to infer it from the renderer string
        let vendor_lower = vendor.to_lowercase();
        let renderer_lower = renderer.to_lowercase();
        let strings_that_imply_integrated = [
            " xpress", // space here is on purpose so we don't match express
            "radeon hd 4200",
            "radeon hd 4250",
            "radeon hd 4290",
            "radeon hd 4270",
            "radeon hd 4225",
            "radeon hd 3100",
            "radeon hd 3200",
            "radeon hd 3000",
            "radeon hd 3300",
            "radeon(tm) r4 graphics",
            "radeon(tm) r5 graphics",
            "radeon(tm) r6 graphics",
            "radeon(tm) r7 graphics",
            "radeon r7 graphics",
            "nforce", // All nvidia nforce are integrated
            "tegra",  // all nvidia tegra are integrated
            "shield",
            "igp",
            "mali",
            "intel",
        ];
        // todo Intel will release a discrete gpu soon, and we will need to update this logic when they do
        let inferred_device_type = if vendor_lower.contains("qualcomm")
            || vendor_lower.contains("intel")
            || strings_that_imply_integrated
                .into_iter()
                .any(|&s| renderer_lower.contains(s))
        {
            hal::adapter::DeviceType::IntegratedGpu
        } else {
            hal::adapter::DeviceType::DiscreteGpu
        };

        // source: Sascha Willems at Vulkan
        let vendor_id = if vendor_lower.contains("amd") {
            0x1002
        } else if vendor_lower.contains("imgtec") {
            0x1010
        } else if vendor_lower.contains("nvidia") {
            0x10DE
        } else if vendor_lower.contains("arm") {
            0x13B5
        } else if vendor_lower.contains("qualcomm") {
            0x5143
        } else if vendor_lower.contains("intel") {
            0x8086
        } else {
            0
        };

        hal::Adapter {
            info: hal::AdapterInfo {
                name,
                vendor: vendor_id,
                device: 0,
                device_type: inferred_device_type,
            },
            physical_device: PhysicalDevice(Starc::new(share)),
            queue_families: vec![QueueFamily],
        }
    }

    /// Get GL-specific legacy feature flags.
    pub fn legacy_features(&self) -> &info::LegacyFeatures {
        &self.0.legacy_features
    }
}

impl hal::PhysicalDevice<Backend> for PhysicalDevice {
    unsafe fn open(
        &self,
        families: &[(&QueueFamily, &[hal::QueuePriority])],
        requested_features: hal::Features,
    ) -> Result<hal::Gpu<Backend>, error::DeviceCreationError> {
        // Can't have multiple logical devices at the same time
        // as they would share the same context.
        if self.0.open.get() {
            return Err(error::DeviceCreationError::TooManyObjects);
        }
        self.0.open.set(true);

        // TODO: Check for support in the LeagcyFeatures struct too
        if !self.features().contains(requested_features) {
            return Err(error::DeviceCreationError::MissingFeature);
        }

        // initialize permanent states
        let gl = &self.0.context;
        if self
            .0
            .legacy_features
            .contains(info::LegacyFeatures::SRGB_COLOR)
        {
            // TODO: Find way to emulate this on older Opengl versions.
            unsafe {
                gl.Enable(gl::FRAMEBUFFER_SRGB);
            }
        }
        unsafe {
            gl.PixelStorei(gl::UNPACK_ALIGNMENT, 1);

            if !self.0.info.version.is_embedded {
                gl.Enable(gl::PROGRAM_POINT_SIZE);
            }
        }

        // create main VAO and bind it
        let mut vao = 0;
        if self.0.private_caps.vertex_array {
            unsafe {
                gl.GenVertexArrays(1, &mut vao);
                gl.BindVertexArray(vao);
            }
        }

        if let Err(err) = self.0.check() {
            panic!("Error opening adapter: {:?}", err);
        }

        Ok(hal::Gpu {
            device: Device::new(self.0.clone()),
            queues: Queues::new(
                families
                    .into_iter()
                    .map(|&(proto_family, priorities)| {
                        assert_eq!(priorities.len(), 1);
                        let mut family = hal::backend::RawQueueGroup::new(proto_family.clone());
                        let queue = queue::CommandQueue::new(&self.0, vao);
                        family.add_queue(queue);
                        family
                    })
                    .collect(),
            ),
        })
    }

    fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
        unimplemented!()
    }

    fn image_format_properties(
        &self,
        _format: hal::format::Format,
        _dimensions: u8,
        _tiling: image::Tiling,
        _usage: image::Usage,
        _view_caps: image::ViewCapabilities,
    ) -> Option<image::FormatProperties> {
        None //TODO
    }

    fn memory_properties(&self) -> hal::MemoryProperties {
        use hal::memory::Properties;

        // COHERENT flags require that the backend does flushing and invalidation
        // by itself. If we move towards persistent mapping we need to re-evaluate it.
        let memory_types = if self.0.private_caps.map {
            vec![
                hal::MemoryType {
                    properties: Properties::DEVICE_LOCAL,
                    heap_index: 1,
                },
                hal::MemoryType {
                    // upload
                    properties: Properties::CPU_VISIBLE | Properties::COHERENT,
                    heap_index: 0,
                },
                hal::MemoryType {
                    // download
                    properties: Properties::CPU_VISIBLE
                        | Properties::COHERENT
                        | Properties::CPU_CACHED,
                    heap_index: 0,
                },
            ]
        } else {
            vec![hal::MemoryType {
                properties: Properties::DEVICE_LOCAL,
                heap_index: 0,
            }]
        };

        hal::MemoryProperties {
            memory_types,
            memory_heaps: vec![!0, !0],
        }
    }

    fn features(&self) -> hal::Features {
        self.0.features
    }

    fn limits(&self) -> hal::Limits {
        self.0.limits
    }
}

#[derive(Debug, Clone, Copy)]
pub struct QueueFamily;

impl hal::QueueFamily for QueueFamily {
    fn queue_type(&self) -> hal::QueueType {
        hal::QueueType::General
    }
    fn max_queues(&self) -> usize {
        1
    }
    fn id(&self) -> QueueFamilyId {
        QueueFamilyId(0)
    }
}
<|endoftext|>


<commit_before>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        self._beta_once(0);
    }

    fn _beta_once(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce), Var(1))
    }
}
<commit_msg>start preparations for applicative order<commit_after>//! [β-reduction](https://en.wikipedia.org/wiki/Beta_normal_form) for lambda `Term`s

use term::*;
use term::Term::*;
use self::Order::*;

/// Set to `true` to see all the steps of β-reductions. The default is `false`.
pub const SHOW_REDUCTIONS: bool = false;

pub const EVAL_ORDER: Order = Normal;

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Order {
    Normal,
    Applicative
}

/// Applies two terms with substitution and variable update, consuming the first one in the process.
///
/// # Example
/// ```
/// use lambda_calculus::reduction::apply;
/// use lambda_calculus::parser::parse;
///
/// let lhs    = parse(&"λλ42(λ13)").unwrap();
/// let rhs    = parse(&"λ51").unwrap();
/// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
///
/// assert_eq!(apply(lhs, &rhs), Ok(result));
/// ```
pub fn apply(mut lhs: Term, rhs: &Term) -> Result<Term, Error> {
    _apply(&mut lhs, rhs, 0);

    lhs.unabs()
}

fn _apply(lhs: &mut Term, rhs: &Term, depth: usize) {
    match *lhs {
        Var(i) => if i == depth {
            *lhs = rhs.clone(); // substitute a top-level variable from lhs with rhs
            update_free_variables(lhs, depth - 1, 0); // update indices of free variables from rhs
        } else if i > depth {
            *lhs = Var(i - 1) // decrement a free variable's index
        },
        Abs(_) => {
            _apply(lhs.unabs_ref_mut().unwrap(), rhs, depth + 1)
        },
        App(_, _) => {
            _apply(lhs.lhs_ref_mut().unwrap(), rhs, depth);
            _apply(lhs.rhs_ref_mut().unwrap(), rhs, depth)
        }
    }
}

fn update_free_variables(term: &mut Term, added_depth: usize, own_depth: usize) {
    match *term {
        Var(i) => if i > own_depth {
            *term = Var(i + added_depth)
        },
        Abs(_) => {
            update_free_variables(term.unabs_ref_mut().unwrap(), added_depth, own_depth + 1)
        },
        App(_, _) => {
            update_free_variables(term.lhs_ref_mut().unwrap(), added_depth, own_depth);
            update_free_variables(term.rhs_ref_mut().unwrap(), added_depth, own_depth)
        }
    }
}

impl Term {
    /// Applies `self` to another term with substitution and variable update, consuming it
    /// in the process.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::parser::parse;
    ///
    /// let lhs    = parse(&"λλ42(λ13)").unwrap();
    /// let rhs    = parse(&"λ51").unwrap();
    /// let result = parse(&"λ3(λ61)(λ1(λ71))").unwrap();
    ///
    /// assert_eq!(lhs.apply(&rhs), Ok(result));
    /// ```
    pub fn apply(self, rhs: &Term) -> Result<Term, Error> {
        apply(self, rhs)
    }

    /// Reduces an `App`lication by substitution and variable update.
    ///
    /// # Example
    /// ```
    /// use lambda_calculus::term::{app, abs};
    /// use lambda_calculus::term::Term::Var;
    /// use lambda_calculus::arithmetic::zero;
    /// use lambda_calculus::combinators::i;
    ///
    /// assert_eq!(app(i(), zero()).eval(), Ok(zero()));
    /// ```
    pub fn eval(self) -> Result<Term, Error> {
        let (lhs, rhs) = try!(self.unapp());

        apply(lhs, &rhs)
    }

    /// Performs a single normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::succ;
    ///
    /// let mut succ_one = succ().app(1.into());
    /// succ_one.beta_once();
    ///
    /// assert_eq!(succ_one, succ().apply(&1.into()).unwrap());
    /// ```
    pub fn beta_once(&mut self) {
        match EVAL_ORDER {
            Normal => self._beta_once_normal(0),
            Applicative => unimplemented!()
        };
    }

    fn _beta_once_normal(&mut self, depth: u32) -> bool {
        match *self {
            Var(_) => false,
            Abs(_) => self.unabs_ref_mut().unwrap()._beta_once_normal(depth + 1),
            App(_, _) => {
                if self.lhs_ref().unwrap().unabs_ref().is_ok() {
                    let copy = self.clone();
                    if SHOW_REDUCTIONS { print!("    {} reduces to ", show_precedence(self, 0, depth)) };
                    *self = copy.eval().unwrap();
                    if SHOW_REDUCTIONS { println!("{}", show_precedence(self, 0, depth)) }
                    true
                } else {
                    if !self.lhs_ref_mut().unwrap()._beta_once_normal(depth) {
                        self.rhs_ref_mut().unwrap()._beta_once_normal(depth)
                    } else {
                        true
                    }
                }
            }
        }
    }

    /// Performs full normal-order β-reduction on `self`.
    ///
    /// # Example
    ///
    /// ```
    /// use lambda_calculus::arithmetic::pred;
    ///
    /// let mut pred_one = pred().app(1.into());
    /// pred_one.beta_full();
    ///
    /// assert_eq!(pred_one, 0.into());
    /// ```
    pub fn beta_full(&mut self) {
        let mut tmp;
        loop {
            if SHOW_REDUCTIONS { println!("reducing {}", self) }
            tmp = self.clone();
            self.beta_once();
            if *self == tmp { break }
        }

        if SHOW_REDUCTIONS { println!("    doesn't reduce") }
    }
}

/// Performs full normal-order β-reduction on a `Term`, consuming it in the process.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::pred;
/// use lambda_calculus::reduction::beta_full;
///
/// let pred_one = pred().app(1.into());
///
/// assert_eq!(beta_full(pred_one), 0.into());
/// ```
pub fn beta_full(mut term: Term) -> Term {
    term.beta_full();
    term
}

/// Performs a single normal-order β-reduction on `self`.
///
/// # Example
///
/// ```
/// use lambda_calculus::arithmetic::succ;
/// use lambda_calculus::reduction::beta_once;
///
/// let succ_one = succ().app(1.into());
///
/// assert_eq!(beta_once(succ_one), succ().apply(&1.into()).unwrap());
/// ```
pub fn beta_once(mut term: Term) -> Term {
    term.beta_once();
    term
}

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

    #[test]
    fn normal_order() {
        let should_reduce = parse(&"(λ2)((λ111)(λ111))").unwrap();
        assert_eq!(beta_full(should_reduce), Var(1))
    }
}
<|endoftext|>


<commit_before>//! Reminisce is a lightweight library intended to be used for detecting and
//! reading from joysticks.

#![feature(core, std_misc, libc, os)]
extern crate libc;

#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "linux")]
pub use linux::*;

#[cfg(target_os = "windows")]
mod windows;

#[cfg(target_os = "windows")]
pub use windows::*;

#[derive(Copy, Debug)]
/// An event from a joystick
pub enum Event {
	/// Fires when a button is pressed with the button's index
	ButtonPressed(u8),
	/// Fires when a button is released with the button's index
	ButtonReleased(u8),
	/// Fires when a joystick / axis is moved with the axis index and its value,
	/// which is between MIN_JOYSTICK_VALUE and MAX_JOYSTICK_VALUE
	JoystickMoved(u8, i16)
}
/// Convert a raw event into a Reminisce event
pub trait IntoEvent {
	/// Convert this into a Reminisce event
	fn into_event(self) -> Event;
}

#[derive(Debug)]
/// A joystick that tracks its state
pub struct SmartJoystick {
	js: Joystick,
	/// The axes on the joystick
	///
	/// Typically the first two of these are the primary analog stick's x and y
	/// co-ordinates
	pub axes: Vec<f32>,
	/// The buttons on the joystick
	///
	/// These usually indicate the button priority with the first two being
	/// accept and back buttons
	pub buttons: Vec<bool>
}
impl SmartJoystick {
	/// Create a new joystick from its index
	pub fn new(index: u8) -> Result<SmartJoystick, &'static str> {
		let js = try!(Joystick::new(index));
		Ok(SmartJoystick {
			axes: vec![0.0f32; js.get_num_axes() as usize],
			buttons: vec![false; js.get_num_buttons() as usize],
			js: js
		})
	}
	/// Poll this joystick for events
	pub fn poll(&mut self) -> Option<Event> {
		let event = self.js.poll();
		match event {
			Some(Event::JoystickMoved(i, v)) => self.axes[i as usize] = v as f32 / MAX_JOYSTICK_VALUE as f32,
			Some(Event::ButtonPressed(i)) => self.buttons[i as usize] = true,
			Some(Event::ButtonReleased(i)) => self.buttons[i as usize] = false,
			_ => ()
		}
		event
	}
	/// Update the joystick's state
	pub fn update(&mut self) {
		while let Some(_) = self.poll() {}
	}
}
impl ::std::ops::Deref for SmartJoystick {
	type Target = Joystick;
	fn deref(&self) -> &Joystick {
		&self.js
	}
}
<commit_msg>Be more specific in documentation<commit_after>//! Reminisce is a lightweight library intended to be used for detecting and
//! reading from joysticks.

#![feature(core, std_misc, libc, os)]
extern crate libc;

#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "linux")]
pub use linux::*;

#[cfg(target_os = "windows")]
mod windows;

#[cfg(target_os = "windows")]
pub use windows::*;

use std::ops::Deref;

#[derive(Copy, Debug)]
/// An event from a joystick
pub enum Event {
	/// Fires when a button is pressed with the button's index
	ButtonPressed(u8),
	/// Fires when a button is released with the button's index
	ButtonReleased(u8),
	/// Fires when a joystick / axis is moved with the axis index and its value,
	/// which is between MIN_JOYSTICK_VALUE and MAX_JOYSTICK_VALUE
	JoystickMoved(u8, i16)
}
/// Convert a raw event into a Reminisce event
pub trait IntoEvent {
	/// Convert this into a Reminisce event
	fn into_event(self) -> Event;
}

#[derive(Debug)]
/// A joystick that tracks its state
pub struct SmartJoystick {
	js: Joystick,
	/// The axes on the joystick, normalised to floats between -1.0 and 1.0
	///
	/// Typically the first two of these are the primary analog stick's x and y
	/// co-ordinates
	pub axes: Vec<f32>,
	/// The buttons on the joystick as booleans indicating if they are pressed
	///
	/// The order of these usually indicates the button priority with the first
	/// two being accept and back buttons
	pub buttons: Vec<bool>
}
impl SmartJoystick {
	/// Create a new joystick from its index
	pub fn new(index: u8) -> Result<SmartJoystick, &'static str> {
		let js = try!(Joystick::new(index));
		Ok(SmartJoystick {
			axes: vec![0.0f32; js.get_num_axes() as usize],
			buttons: vec![false; js.get_num_buttons() as usize],
			js: js
		})
	}
	/// Poll this joystick for events
	pub fn poll(&mut self) -> Option<Event> {
		let event = self.js.poll();
		match event {
			Some(Event::JoystickMoved(i, v)) => self.axes[i as usize] = v as f32 / MAX_JOYSTICK_VALUE as f32,
			Some(Event::ButtonPressed(i)) => self.buttons[i as usize] = true,
			Some(Event::ButtonReleased(i)) => self.buttons[i as usize] = false,
			_ => ()
		}
		event
	}
	/// Update the joystick's state
	pub fn update(&mut self) {
		while let Some(_) = self.poll() {}
	}
}
impl Deref for SmartJoystick {
	type Target = Joystick;
	fn deref(&self) -> &Joystick {
		&self.js
	}
}
<|endoftext|>


<commit_before>#![feature(collections)]

#[macro_use]
extern crate apache2;

apache2_module!(https_www_handler, c_https_www_handler, https_www_module, b"mod_https_www\0");

fn https_www_handler(r: &mut apache2::Request) -> apache2::Status {
   let scheme = match r.http_scheme() {
      None => {
         return apache2::Status::HTTP_INTERNAL_SERVER_ERROR;
      },
      Some(scheme) => scheme
   };

   let hostname = match r.hostname() {
      None => {
         return apache2::Status::HTTP_INTERNAL_SERVER_ERROR;
      },
      Some(hostname) => hostname
   };

   let already_www = &hostname[..4] == "www.";
   let already_https = scheme == "https";

   if already_www && already_https {
      return apache2::Status::DECLINED;
   };

   let full_hostname = if already_www {
      String::from_str(hostname)
   } else {
      format!("www.{}", hostname)
   };

   let uri = match r.unparsed_uri() {
      None => {
         return apache2::Status::HTTP_INTERNAL_SERVER_ERROR;
      },
      Some(uri) => uri
   };

   let location = format!("https://{}{}", full_hostname, uri);
   r.headers_out().unwrap().set("Location", location);

   r.set_status(apache2::Status::HTTP_MOVED_PERMANENTLY);

   apache2::Status::DONE
}
<commit_msg>Hooking the https_www module to the translate name phase.<commit_after>#![feature(collections)]

#[macro_use]
extern crate apache2;

apache2_module!(https_www_handler, c_https_www_handler, https_www_module, b"mod_https_www\0", ap_hook_translate_name, apache2::HookOrder::MIDDLE);

fn https_www_handler(r: &mut apache2::Request) -> apache2::Status {
   let scheme = match r.http_scheme() {
      None => {
         return apache2::Status::HTTP_INTERNAL_SERVER_ERROR;
      },
      Some(scheme) => scheme
   };

   let hostname = match r.hostname() {
      None => {
         return apache2::Status::HTTP_INTERNAL_SERVER_ERROR;
      },
      Some(hostname) => hostname
   };

   let already_www = &hostname[..4] == "www.";
   let already_https = scheme == "https";

   if already_www && already_https {
      return apache2::Status::DECLINED;
   };

   let full_hostname = if already_www {
      String::from_str(hostname)
   } else {
      format!("www.{}", hostname)
   };

   let uri = match r.unparsed_uri() {
      None => {
         return apache2::Status::HTTP_INTERNAL_SERVER_ERROR;
      },
      Some(uri) => uri
   };

   let location = format!("https://{}{}", full_hostname, uri);
   r.headers_out().unwrap().set("Location", location);

   r.set_status(apache2::Status::HTTP_MOVED_PERMANENTLY);

   apache2::Status::DONE
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate eve;
extern crate rand;

use test::Bencher;
use std::collections::BTreeMap;
use std::io::prelude::*;
use std::fs::File;
use rand::Rng;

use eve::map::*;

fn nums() -> Vec<i64> {
    let mut rng = rand::thread_rng();
    (0..100_000).map(|_| rng.gen()).collect()
}

#[bench]
fn bench_insert_int_map(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Map::new();
        for num in nums.iter() {
            map.insert(*num, 1);
        }
        map
    });
}

#[bench]
fn bench_insert_int_btree(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = BTreeMap::new();
        for num in nums.iter() {
            map.insert(*num, 1);
        }
        map
    });
}

#[bench]
fn bench_insert_int_sorted(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Vec::new();
        for num in nums.iter() {
            map.push((*num, 1));
        }
        map.sort();
        map
    });
}

#[bench]
fn bench_query_int_map(b: &mut Bencher) {
    let nums = nums();
    let mut map = Map::new();
    for num in nums.iter() {
        map.insert(*num, 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            results.push(map.query(&nums[(*num as usize) % nums.len()]).is_some());
        }
        results
    });
}

#[bench]
fn bench_query_int_btree(b: &mut Bencher) {
    let nums = nums();
    let mut map = BTreeMap::new();
    for num in nums.iter() {
        map.insert(*num, 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            results.push(map.contains_key(&nums[(*num as usize) % nums.len()]));
        }
        results
    });
}

#[bench]
fn bench_query_int_sorted(b: &mut Bencher) {
    let nums = nums();
    let mut map = Vec::new();
    for num in nums.iter() {
        map.push((*num, 1));
    }
    map.sort();
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let key = &nums[(*num as usize) % nums.len()];
            results.push(map.binary_search_by(|&(ref k,_)| k.cmp(key)).is_ok());
        }
        results
    });
}

fn words() -> Vec<String> {
    let mut contents = String::new();
    File::open("/usr/share/dict/words").unwrap().read_to_string(&mut contents).unwrap();
    let mut words = contents.lines().map(|word| word.to_owned()).collect::<Vec<_>>();
    // pick some deterministic but non-useful order
    words.sort_by(|a,b| {
        let ra = a.chars().rev().collect::<String>();
        let rb = b.chars().rev().collect::<String>();
        ra.cmp(&rb)
    });
    words
}

#[bench]
fn bench_insert_string_map(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = Map::new();
        for word in words.iter() {
            map.insert(word.clone(), 1);
        }
        map
    });
}

#[bench]
fn bench_insert_string_btree(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = BTreeMap::new();
        for word in words.iter() {
            map.insert(word.clone(), 1);
        }
        map
    });
}

#[bench]
fn bench_insert_string_sorted(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = Vec::new();
        for word in words.iter() {
            map.push((word.clone(), 1));
        }
        map.sort();
        map
    });
}

#[bench]
fn bench_query_string_map(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = Map::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            results.push(None != map.query(word));
        }
        results
    });
}

#[bench]
fn bench_query_string_btree(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = BTreeMap::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            results.push(map.contains_key(word));
        }
        results
    });
}

#[bench]
fn bench_query_string_sorted(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = Vec::new();
    for word in words.iter() {
        map.push((word.clone(), 1));
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let key = &words[(*num as usize) % words.len()];
            results.push(map.binary_search_by(|&(ref k,_)| k.cmp(key)).is_ok());
        }
        results
    });
}

// TODO bench_clone_* are not very useful at such small sizes

#[bench]
fn bench_clone_int_map(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Map::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            map.insert(*num, 1);
            maps.push(map.clone());
        }
        maps
    });
}

#[bench]
fn bench_clone_int_btree(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = BTreeMap::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            map.insert(*num, 1);
            maps.push(map.clone());
        }
        maps
    });
}

#[bench]
fn bench_clone_int_sorted(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Vec::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            match map.binary_search(num) {
                Ok(ix) => map[ix] = *num,
                Err(ix) => map.insert(ix, *num),
            }
            maps.push(map.clone());
        }
        maps
    });
}

#[allow(dead_code)]
fn main() {
    let words = words();
    let nums = nums();
    let mut map = Map::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    for _ in (0..50) {
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            test::black_box(map.query(word));
        }
    }
}<commit_msg>Bench HashMaps too<commit_after>#![feature(test)]

extern crate test;
extern crate eve;
extern crate rand;

use test::Bencher;
use std::collections::{BTreeMap, HashMap};
use std::io::prelude::*;
use std::fs::File;
use rand::Rng;

use eve::map::*;

fn nums() -> Vec<i64> {
    let mut rng = rand::thread_rng();
    (0..100_000).map(|_| rng.gen()).collect()
}

#[bench]
fn bench_insert_int_map(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Map::new();
        for num in nums.iter() {
            map.insert(*num, 1);
        }
        map
    });
}

#[bench]
fn bench_insert_int_btree(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = BTreeMap::new();
        for num in nums.iter() {
            map.insert(*num, 1);
        }
        map
    });
}

#[bench]
fn bench_insert_int_hash(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = HashMap::new();
        for num in nums.iter() {
            map.insert(*num, 1);
        }
        map
    });
}

#[bench]
fn bench_insert_int_sorted(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = nums.clone();
        map.sort();
        map
    });
}

#[bench]
fn bench_query_int_map(b: &mut Bencher) {
    let nums = nums();
    let mut map = Map::new();
    for num in nums.iter() {
        map.insert(*num, 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            results.push(map.query(&nums[(*num as usize) % nums.len()]).is_some());
        }
        results
    });
}

#[bench]
fn bench_query_int_btree(b: &mut Bencher) {
    let nums = nums();
    let mut map = BTreeMap::new();
    for num in nums.iter() {
        map.insert(*num, 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            results.push(map.contains_key(&nums[(*num as usize) % nums.len()]));
        }
        results
    });
}

#[bench]
fn bench_query_int_hash(b: &mut Bencher) {
    let nums = nums();
    let mut map = HashMap::new();
    for num in nums.iter() {
        map.insert(*num, 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            results.push(map.contains_key(&nums[(*num as usize) % nums.len()]));
        }
        results
    });
}

#[bench]
fn bench_query_int_sorted(b: &mut Bencher) {
    let nums = nums();
    let mut map = nums.clone();
    map.sort();
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let key = &nums[(*num as usize) % nums.len()];
            results.push(map.binary_search(key).is_ok());
        }
        results
    });
}

fn words() -> Vec<String> {
    let mut contents = String::new();
    File::open("/usr/share/dict/words").unwrap().read_to_string(&mut contents).unwrap();
    let mut words = contents.lines().map(|word| word.to_owned()).collect::<Vec<_>>();
    // pick some deterministic but non-useful order
    words.sort_by(|a,b| {
        let ra = a.chars().rev().collect::<String>();
        let rb = b.chars().rev().collect::<String>();
        ra.cmp(&rb)
    });
    words
}

#[bench]
fn bench_insert_string_map(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = Map::new();
        for word in words.iter() {
            map.insert(word.clone(), 1);
        }
        map
    });
}

#[bench]
fn bench_insert_string_btree(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = BTreeMap::new();
        for word in words.iter() {
            map.insert(word.clone(), 1);
        }
        map
    });
}

#[bench]
fn bench_insert_string_hash(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = HashMap::new();
        for word in words.iter() {
            map.insert(word.clone(), 1);
        }
        map
    });
}

#[bench]
fn bench_insert_string_sorted(b: &mut Bencher) {
    let words = words();
    b.iter(|| {
        let mut map = words.clone();
        map.sort();
        map
    });
}

#[bench]
fn bench_query_string_map(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = Map::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            results.push(None != map.query(word));
        }
        results
    });
}

#[bench]
fn bench_query_string_btree(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = BTreeMap::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            results.push(map.contains_key(word));
        }
        results
    });
}

#[bench]
fn bench_query_string_hash(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = HashMap::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            results.push(map.contains_key(word));
        }
        results
    });
}

#[bench]
fn bench_query_string_sorted(b: &mut Bencher) {
    let words = words();
    let nums = nums();
    let mut map = words.clone();
    map.sort();
    b.iter(|| {
        let mut results = vec![];
        for num in nums.iter() {
            let key = &words[(*num as usize) % words.len()];
            results.push(map.binary_search(key).is_ok());
        }
        results
    });
}

// TODO bench_clone_* are not very useful at such small sizes

#[bench]
fn bench_clone_int_map(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Map::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            map.insert(*num, 1);
            maps.push(map.clone());
        }
        maps
    });
}

#[bench]
fn bench_clone_int_btree(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = BTreeMap::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            map.insert(*num, 1);
            maps.push(map.clone());
        }
        maps
    });
}

#[bench]
fn bench_clone_int_hash(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = BTreeMap::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            map.insert(*num, 1);
            maps.push(map.clone());
        }
        maps
    });
}

#[bench]
fn bench_clone_int_sorted(b: &mut Bencher) {
    let nums = nums();
    b.iter(|| {
        let mut map = Vec::new();
        let mut maps = Vec::new();
        for num in nums[..1000].iter() {
            match map.binary_search(num) {
                Ok(ix) => map[ix] = *num,
                Err(ix) => map.insert(ix, *num),
            }
            maps.push(map.clone());
        }
        maps
    });
}

#[allow(dead_code)]
fn main() {
    let words = words();
    let nums = nums();
    let mut map = Map::new();
    for word in words.iter() {
        map.insert(word.clone(), 1);
    }
    for _ in (0..50) {
        for num in nums.iter() {
            let word = &words[(*num as usize) % words.len()];
            test::black_box(map.query(word));
        }
    }
}<|endoftext|>


<commit_before>// Copyright (c) 2014-2015 Michael J. Sullivan
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file.

#![feature(plugin_registrar, rustc_private)]
extern crate rustc;
use rustc::plugin::Registry;
extern crate rustc_llvm;
use std::ffi::CString;

use std::process::Command;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    let output = Command::new("rmc-config").arg("--lib").output().
        unwrap_or_else(|e| {
            panic!("failed to run rmc-config: {}", e)
        });

    let s = CString::new(output.stdout).unwrap();
    if unsafe {rustc_llvm::LLVMRustLoadDynamicLibrary(s.as_ptr()) } == 0 {
        panic!("couldn't load rmc library")
    }
    reg.register_llvm_pass("realize-rmc");
    reg.register_llvm_pass("simplifycfg");
}
<commit_msg>Make the rust plugin report the llvm error when it fails<commit_after>// Copyright (c) 2014-2015 Michael J. Sullivan
// Use of this source code is governed by an MIT-style license that can be
// found in the LICENSE file.

#![feature(plugin_registrar, rustc_private)]
extern crate rustc;
use rustc::plugin::Registry;
extern crate rustc_llvm;
use std::ffi::{CString,CStr};

use std::process::Command;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    let output = Command::new("rmc-config").arg("--lib").output().
        unwrap_or_else(|e| {
            panic!("failed to run rmc-config: {}", e)
        });

    let s = CString::new(output.stdout).unwrap();
    unsafe {
        if rustc_llvm::LLVMRustLoadDynamicLibrary(s.as_ptr()) == 0 {
            let err = rustc_llvm::LLVMRustGetLastError();
            let s = CStr::from_ptr(err).to_string_lossy().into_owned();
            panic!("couldn't load rmc library: {}", s)
        }
    }
    reg.register_llvm_pass("realize-rmc");
    reg.register_llvm_pass("simplifycfg");
}
<|endoftext|>


<commit_before>use std::cmp;
use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use filetime::FileTime;
use git2;
use glob::Pattern;

use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
use ops;
use util::{self, CargoResult, internal, internal_error, human, ChainError};
use util::Config;

pub struct PathSource<'cfg> {
    id: SourceId,
    path: PathBuf,
    updated: bool,
    packages: Vec<Package>,
    config: &'cfg Config,
}

// TODO: Figure out if packages should be discovered in new or self should be
// mut and packages are discovered in update
impl<'cfg> PathSource<'cfg> {
    pub fn for_path(path: &Path, config: &'cfg Config)
                    -> CargoResult<PathSource<'cfg>> {
        trace!("PathSource::for_path; path={}", path.display());
        Ok(PathSource::new(path, &try!(SourceId::for_path(path)), config))
    }

    /// Invoked with an absolute path to a directory that contains a Cargo.toml.
    /// The source will read the manifest and find any other packages contained
    /// in the directory structure reachable by the root manifest.
    pub fn new(path: &Path, id: &SourceId, config: &'cfg Config)
               -> PathSource<'cfg> {
        trace!("new; id={}", id);

        PathSource {
            id: id.clone(),
            path: path.to_path_buf(),
            updated: false,
            packages: Vec::new(),
            config: config,
        }
    }

    pub fn root_package(&mut self) -> CargoResult<Package> {
        trace!("root_package; source={:?}", self);

        try!(self.update());

        match self.packages.iter().find(|p| p.root() == &*self.path) {
            Some(pkg) => Ok(pkg.clone()),
            None => Err(internal("no package found in source"))
        }
    }

    fn read_packages(&self) -> CargoResult<Vec<Package>> {
        if self.updated {
            Ok(self.packages.clone())
        } else if self.id.is_path() && self.id.precise().is_some() {
            // If our source id is a path and it's listed with a precise
            // version, then it means that we're not allowed to have nested
            // dependencies (they've been rewritten to crates.io dependencies)
            // In this case we specifically read just one package, not a list of
            // packages.
            let path = self.path.join("Cargo.toml");
            let (pkg, _) = try!(ops::read_package(&path, &self.id,
                                                  self.config));
            Ok(vec![pkg])
        } else {
            ops::read_packages(&self.path, &self.id, self.config)
        }
    }

    /// List all files relevant to building this package inside this source.
    ///
    /// This function will use the appropriate methods to determine the
    /// set of files underneath this source's directory which are relevant for
    /// building `pkg`.
    ///
    /// The basic assumption of this method is that all files in the directory
    /// are relevant for building this package, but it also contains logic to
    /// use other methods like .gitignore to filter the list of files.
    pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
        let root = pkg.root();

        let parse = |p: &String| {
            Pattern::new(p).map_err(|e| {
                human(format!("could not parse pattern `{}`: {}", p, e))
            })
        };
        let exclude = try!(pkg.manifest().exclude().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());
        let include = try!(pkg.manifest().include().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());

        let mut filter = |p: &Path| {
            let relative_path = util::without_prefix(p, &root).unwrap();
            include.iter().any(|p| p.matches_path(&relative_path)) || {
                include.len() == 0 &&
                 !exclude.iter().any(|p| p.matches_path(&relative_path))
            }
        };

        // If this package is a git repository, then we really do want to query
        // the git repository as it takes into account items such as .gitignore.
        // We're not quite sure where the git repository is, however, so we do a
        // bit of a probe.
        //
        // We check all packages in this source that are ancestors of the
        // specified package (including the same package) to see if they're at
        // the root of the git repository. This isn't always true, but it'll get
        // us there most of the time!
        let repo = self.packages.iter()
                       .map(|pkg| pkg.root())
                       .filter(|path| root.starts_with(path))
                       .filter_map(|path| git2::Repository::open(&path).ok())
                       .next();
        match repo {
            Some(repo) => self.list_files_git(pkg, repo, &mut filter),
            None => self.list_files_walk(pkg, &mut filter),
        }
    }

    fn list_files_git(&self, pkg: &Package, repo: git2::Repository,
                      filter: &mut FnMut(&Path) -> bool)
                      -> CargoResult<Vec<PathBuf>> {
        warn!("list_files_git {}", pkg.package_id());
        let index = try!(repo.index());
        let root = try!(repo.workdir().chain_error(|| {
            internal_error("Can't list files on a bare repository.", "")
        }));
        let pkg_path = pkg.root();

        let mut ret = Vec::new();

        // We use information from the git repository to guide us in traversing
        // its tree. The primary purpose of this is to take advantage of the
        // .gitignore and auto-ignore files that don't matter.
        //
        // Here we're also careful to look at both tracked and untracked files as
        // the untracked files are often part of a build and may become relevant
        // as part of a future commit.
        let index_files = index.iter().map(|entry| {
            use libgit2_sys::git_filemode_t::GIT_FILEMODE_COMMIT;
            let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
            (join(&root, &entry.path), Some(is_dir))
        });
        let mut opts = git2::StatusOptions::new();
        opts.include_untracked(true);
        if let Some(suffix) = util::without_prefix(pkg_path, &root) {
            opts.pathspec(suffix);
        }
        let statuses = try!(repo.statuses(Some(&mut opts)));
        let untracked = statuses.iter().map(|entry| {
            (join(&root, entry.path_bytes()), None)
        });

        'outer: for (file_path, is_dir) in index_files.chain(untracked) {
            let file_path = try!(file_path);

            // Filter out files outside this package.
            if !file_path.starts_with(pkg_path) { continue }

            // Filter out Cargo.lock and target always
            {
                let fname = file_path.file_name().and_then(|s| s.to_str());
                if fname == Some("Cargo.lock") { continue }
                if fname == Some("target") { continue }
            }

            // Filter out sub-packages of this package
            for other_pkg in self.packages.iter().filter(|p| *p != pkg) {
                let other_path = other_pkg.root();
                if other_path.starts_with(pkg_path) &&
                   file_path.starts_with(other_path) {
                    continue 'outer;
                }
            }

            let is_dir = is_dir.or_else(|| {
                fs::metadata(&file_path).ok().map(|m| m.is_dir())
            }).unwrap_or(false);
            if is_dir {
                warn!("  found submodule {}", file_path.display());
                let rel = util::without_prefix(&file_path, &root).unwrap();
                let rel = try!(rel.to_str().chain_error(|| {
                    human(format!("invalid utf-8 filename: {}", rel.display()))
                }));
                // Git submodules are currently only named through `/` path
                // separators, explicitly not `\` which windows uses. Who knew?
                let rel = rel.replace(r"\", "/");
                match repo.find_submodule(&rel).and_then(|s| s.open()) {
                    Ok(repo) => {
                        let files = try!(self.list_files_git(pkg, repo, filter));
                        ret.extend(files.into_iter());
                    }
                    Err(..) => {
                        try!(PathSource::walk(&file_path, &mut ret, false,
                                              filter));
                    }
                }
            } else if (*filter)(&file_path) {
                // We found a file!
                warn!("  found {}", file_path.display());
                ret.push(file_path);
            }
        }
        return Ok(ret);

        #[cfg(unix)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::os::unix::prelude::*;
            use std::ffi::OsStr;
            Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
        }
        #[cfg(windows)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::str;
            match str::from_utf8(data) {
                Ok(s) => Ok(path.join(s)),
                Err(..) => Err(internal("cannot process path in git with a non \
                                         unicode filename")),
            }
        }
    }

    fn list_files_walk(&self, pkg: &Package, filter: &mut FnMut(&Path) -> bool)
                       -> CargoResult<Vec<PathBuf>> {
        let mut ret = Vec::new();
        for pkg in self.packages.iter().filter(|p| *p == pkg) {
            let loc = pkg.root();
            try!(PathSource::walk(loc, &mut ret, true, filter));
        }
        return Ok(ret);
    }

    fn walk(path: &Path, ret: &mut Vec<PathBuf>,
            is_root: bool, filter: &mut FnMut(&Path) -> bool) -> CargoResult<()>
    {
        if !fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
            if (*filter)(path) {
                ret.push(path.to_path_buf());
            }
            return Ok(())
        }
        // Don't recurse into any sub-packages that we have
        if !is_root && fs::metadata(&path.join("Cargo.toml")).is_ok() {
            return Ok(())
        }
        for dir in try!(fs::read_dir(path)) {
            let dir = try!(dir).path();
            let name = dir.file_name().and_then(|s| s.to_str());
            // Skip dotfile directories
            if name.map(|s| s.starts_with(".")) == Some(true) {
                continue
            } else if is_root {
                // Skip cargo artifacts
                match name {
                    Some("target") | Some("Cargo.lock") => continue,
                    _ => {}
                }
            }
            try!(PathSource::walk(&dir, ret, false, filter));
        }
        return Ok(())
    }
}

impl<'cfg> Debug for PathSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "the paths source")
    }
}

impl<'cfg> Registry for PathSource<'cfg> {
    fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        self.packages.query(dep)
    }
}

impl<'cfg> Source for PathSource<'cfg> {
    fn update(&mut self) -> CargoResult<()> {
        if !self.updated {
            let packages = try!(self.read_packages());
            self.packages.extend(packages.into_iter());
            self.updated = true;
        }

        Ok(())
    }

    fn download(&mut self, _: &[PackageId])  -> CargoResult<()>{
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        trace!("getting packages; ids={:?}", ids);

        Ok(self.packages.iter()
           .filter(|pkg| ids.iter().any(|id| pkg.package_id() == id))
           .map(|pkg| pkg.clone())
           .collect())
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        if !self.updated {
            return Err(internal_error("BUG: source was not updated", ""));
        }

        let mut max = FileTime::zero();
        for file in try!(self.list_files(pkg)).iter() {
            // An fs::stat error here is either because path is a
            // broken symlink, a permissions error, or a race
            // condition where this path was rm'ed - either way,
            // we can ignore the error and treat the path's mtime
            // as 0.
            let mtime = fs::metadata(file).map(|meta| {
                FileTime::from_last_modification_time(&meta)
            }).unwrap_or(FileTime::zero());
            warn!("{} {}", mtime, file.display());
            max = cmp::max(max, mtime);
        }
        trace!("fingerprint {}: {}", self.path.display(), max);
        Ok(max.to_string())
    }
}
<commit_msg>Auto merge of #2044 - alexcrichton:fingerprint-better, r=brson<commit_after>use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use filetime::FileTime;
use git2;
use glob::Pattern;

use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
use ops;
use util::{self, CargoResult, internal, internal_error, human, ChainError};
use util::Config;

pub struct PathSource<'cfg> {
    id: SourceId,
    path: PathBuf,
    updated: bool,
    packages: Vec<Package>,
    config: &'cfg Config,
}

// TODO: Figure out if packages should be discovered in new or self should be
// mut and packages are discovered in update
impl<'cfg> PathSource<'cfg> {
    pub fn for_path(path: &Path, config: &'cfg Config)
                    -> CargoResult<PathSource<'cfg>> {
        trace!("PathSource::for_path; path={}", path.display());
        Ok(PathSource::new(path, &try!(SourceId::for_path(path)), config))
    }

    /// Invoked with an absolute path to a directory that contains a Cargo.toml.
    /// The source will read the manifest and find any other packages contained
    /// in the directory structure reachable by the root manifest.
    pub fn new(path: &Path, id: &SourceId, config: &'cfg Config)
               -> PathSource<'cfg> {
        trace!("new; id={}", id);

        PathSource {
            id: id.clone(),
            path: path.to_path_buf(),
            updated: false,
            packages: Vec::new(),
            config: config,
        }
    }

    pub fn root_package(&mut self) -> CargoResult<Package> {
        trace!("root_package; source={:?}", self);

        try!(self.update());

        match self.packages.iter().find(|p| p.root() == &*self.path) {
            Some(pkg) => Ok(pkg.clone()),
            None => Err(internal("no package found in source"))
        }
    }

    fn read_packages(&self) -> CargoResult<Vec<Package>> {
        if self.updated {
            Ok(self.packages.clone())
        } else if self.id.is_path() && self.id.precise().is_some() {
            // If our source id is a path and it's listed with a precise
            // version, then it means that we're not allowed to have nested
            // dependencies (they've been rewritten to crates.io dependencies)
            // In this case we specifically read just one package, not a list of
            // packages.
            let path = self.path.join("Cargo.toml");
            let (pkg, _) = try!(ops::read_package(&path, &self.id,
                                                  self.config));
            Ok(vec![pkg])
        } else {
            ops::read_packages(&self.path, &self.id, self.config)
        }
    }

    /// List all files relevant to building this package inside this source.
    ///
    /// This function will use the appropriate methods to determine the
    /// set of files underneath this source's directory which are relevant for
    /// building `pkg`.
    ///
    /// The basic assumption of this method is that all files in the directory
    /// are relevant for building this package, but it also contains logic to
    /// use other methods like .gitignore to filter the list of files.
    pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
        let root = pkg.root();

        let parse = |p: &String| {
            Pattern::new(p).map_err(|e| {
                human(format!("could not parse pattern `{}`: {}", p, e))
            })
        };
        let exclude = try!(pkg.manifest().exclude().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());
        let include = try!(pkg.manifest().include().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());

        let mut filter = |p: &Path| {
            let relative_path = util::without_prefix(p, &root).unwrap();
            include.iter().any(|p| p.matches_path(&relative_path)) || {
                include.len() == 0 &&
                 !exclude.iter().any(|p| p.matches_path(&relative_path))
            }
        };

        // If this package is a git repository, then we really do want to query
        // the git repository as it takes into account items such as .gitignore.
        // We're not quite sure where the git repository is, however, so we do a
        // bit of a probe.
        //
        // We check all packages in this source that are ancestors of the
        // specified package (including the same package) to see if they're at
        // the root of the git repository. This isn't always true, but it'll get
        // us there most of the time!
        let repo = self.packages.iter()
                       .map(|pkg| pkg.root())
                       .filter(|path| root.starts_with(path))
                       .filter_map(|path| git2::Repository::open(&path).ok())
                       .next();
        match repo {
            Some(repo) => self.list_files_git(pkg, repo, &mut filter),
            None => self.list_files_walk(pkg, &mut filter),
        }
    }

    fn list_files_git(&self, pkg: &Package, repo: git2::Repository,
                      filter: &mut FnMut(&Path) -> bool)
                      -> CargoResult<Vec<PathBuf>> {
        warn!("list_files_git {}", pkg.package_id());
        let index = try!(repo.index());
        let root = try!(repo.workdir().chain_error(|| {
            internal_error("Can't list files on a bare repository.", "")
        }));
        let pkg_path = pkg.root();

        let mut ret = Vec::new();

        // We use information from the git repository to guide us in traversing
        // its tree. The primary purpose of this is to take advantage of the
        // .gitignore and auto-ignore files that don't matter.
        //
        // Here we're also careful to look at both tracked and untracked files as
        // the untracked files are often part of a build and may become relevant
        // as part of a future commit.
        let index_files = index.iter().map(|entry| {
            use libgit2_sys::git_filemode_t::GIT_FILEMODE_COMMIT;
            let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
            (join(&root, &entry.path), Some(is_dir))
        });
        let mut opts = git2::StatusOptions::new();
        opts.include_untracked(true);
        if let Some(suffix) = util::without_prefix(pkg_path, &root) {
            opts.pathspec(suffix);
        }
        let statuses = try!(repo.statuses(Some(&mut opts)));
        let untracked = statuses.iter().map(|entry| {
            (join(&root, entry.path_bytes()), None)
        });

        'outer: for (file_path, is_dir) in index_files.chain(untracked) {
            let file_path = try!(file_path);

            // Filter out files outside this package.
            if !file_path.starts_with(pkg_path) { continue }

            // Filter out Cargo.lock and target always
            {
                let fname = file_path.file_name().and_then(|s| s.to_str());
                if fname == Some("Cargo.lock") { continue }
                if fname == Some("target") { continue }
            }

            // Filter out sub-packages of this package
            for other_pkg in self.packages.iter().filter(|p| *p != pkg) {
                let other_path = other_pkg.root();
                if other_path.starts_with(pkg_path) &&
                   file_path.starts_with(other_path) {
                    continue 'outer;
                }
            }

            let is_dir = is_dir.or_else(|| {
                fs::metadata(&file_path).ok().map(|m| m.is_dir())
            }).unwrap_or(false);
            if is_dir {
                warn!("  found submodule {}", file_path.display());
                let rel = util::without_prefix(&file_path, &root).unwrap();
                let rel = try!(rel.to_str().chain_error(|| {
                    human(format!("invalid utf-8 filename: {}", rel.display()))
                }));
                // Git submodules are currently only named through `/` path
                // separators, explicitly not `\` which windows uses. Who knew?
                let rel = rel.replace(r"\", "/");
                match repo.find_submodule(&rel).and_then(|s| s.open()) {
                    Ok(repo) => {
                        let files = try!(self.list_files_git(pkg, repo, filter));
                        ret.extend(files.into_iter());
                    }
                    Err(..) => {
                        try!(PathSource::walk(&file_path, &mut ret, false,
                                              filter));
                    }
                }
            } else if (*filter)(&file_path) {
                // We found a file!
                warn!("  found {}", file_path.display());
                ret.push(file_path);
            }
        }
        return Ok(ret);

        #[cfg(unix)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::os::unix::prelude::*;
            use std::ffi::OsStr;
            Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
        }
        #[cfg(windows)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::str;
            match str::from_utf8(data) {
                Ok(s) => Ok(path.join(s)),
                Err(..) => Err(internal("cannot process path in git with a non \
                                         unicode filename")),
            }
        }
    }

    fn list_files_walk(&self, pkg: &Package, filter: &mut FnMut(&Path) -> bool)
                       -> CargoResult<Vec<PathBuf>> {
        let mut ret = Vec::new();
        for pkg in self.packages.iter().filter(|p| *p == pkg) {
            let loc = pkg.root();
            try!(PathSource::walk(loc, &mut ret, true, filter));
        }
        return Ok(ret);
    }

    fn walk(path: &Path, ret: &mut Vec<PathBuf>,
            is_root: bool, filter: &mut FnMut(&Path) -> bool) -> CargoResult<()>
    {
        if !fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
            if (*filter)(path) {
                ret.push(path.to_path_buf());
            }
            return Ok(())
        }
        // Don't recurse into any sub-packages that we have
        if !is_root && fs::metadata(&path.join("Cargo.toml")).is_ok() {
            return Ok(())
        }
        for dir in try!(fs::read_dir(path)) {
            let dir = try!(dir).path();
            let name = dir.file_name().and_then(|s| s.to_str());
            // Skip dotfile directories
            if name.map(|s| s.starts_with(".")) == Some(true) {
                continue
            } else if is_root {
                // Skip cargo artifacts
                match name {
                    Some("target") | Some("Cargo.lock") => continue,
                    _ => {}
                }
            }
            try!(PathSource::walk(&dir, ret, false, filter));
        }
        return Ok(())
    }
}

impl<'cfg> Debug for PathSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "the paths source")
    }
}

impl<'cfg> Registry for PathSource<'cfg> {
    fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        self.packages.query(dep)
    }
}

impl<'cfg> Source for PathSource<'cfg> {
    fn update(&mut self) -> CargoResult<()> {
        if !self.updated {
            let packages = try!(self.read_packages());
            self.packages.extend(packages.into_iter());
            self.updated = true;
        }

        Ok(())
    }

    fn download(&mut self, _: &[PackageId])  -> CargoResult<()>{
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        trace!("getting packages; ids={:?}", ids);

        Ok(self.packages.iter()
           .filter(|pkg| ids.iter().any(|id| pkg.package_id() == id))
           .map(|pkg| pkg.clone())
           .collect())
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        if !self.updated {
            return Err(internal_error("BUG: source was not updated", ""));
        }

        let mut max = FileTime::zero();
        let mut max_path = PathBuf::from("");
        for file in try!(self.list_files(pkg)) {
            // An fs::stat error here is either because path is a
            // broken symlink, a permissions error, or a race
            // condition where this path was rm'ed - either way,
            // we can ignore the error and treat the path's mtime
            // as 0.
            let mtime = fs::metadata(&file).map(|meta| {
                FileTime::from_last_modification_time(&meta)
            }).unwrap_or(FileTime::zero());
            warn!("{} {}", mtime, file.display());
            if mtime > max {
                max = mtime;
                max_path = file;
            }
        }
        trace!("fingerprint {}: {}", self.path.display(), max);
        Ok(format!("{} ({})", max, max_path.display()))
    }
}
<|endoftext|>