<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::iter::Empty;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;

use device::Device;
use device::Queue;
use format::ClearValue;
use format::Format;
use format::FormatDesc;
use format::FormatTy;
use image::Dimensions;
use image::ImageDimensions;
use image::ViewType;
use image::sys::ImageCreationError;
use image::ImageLayout;
use image::ImageUsage;
use image::sys::UnsafeImage;
use image::sys::UnsafeImageView;
use image::traits::ImageAccess;
use image::traits::ImageClearValue;
use image::traits::ImageContent;
use image::traits::ImageViewAccess;
use image::traits::Image;
use image::traits::ImageView;
use memory::pool::AllocLayout;
use memory::pool::MemoryPool;
use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPoolAlloc;
use sync::AccessError;
use sync::Sharing;

/// ImageAccess whose purpose is to be used as a framebuffer attachment.
///
/// The image is always two-dimensional and has only one mipmap, but it can have any kind of
/// format. Trying to use a format that the backend doesn't support for rendering will result in
/// an error being returned when creating the image. Once you have an `AttachmentImage`, you are
/// guaranteed that you will be able to draw on it.
///
/// The template parameter of `AttachmentImage` is a type that describes the format of the image.
///
/// # Regular vs transient
///
/// Calling `AttachmentImage::new` will create a regular image, while calling
/// `AttachmentImage::transient` will create a *transient* image. Transient image are only
/// relevant for images that serve as attachments, so `AttachmentImage` is the only type of
/// image in vulkano that provides a shortcut for this.
///
/// A transient image is a special kind of image whose content is undefined outside of render
/// passes. Once you finish drawing, reading from it will returned undefined data (which can be
/// either valid or garbage, depending on the implementation).
///
/// This gives a hint to the Vulkan implementation that it is possible for the image's content to
/// live exclusively in some cache memory, and that no real memory has to be allocated for it.
///
/// In other words, if you are going to read from the image after drawing to it, use a regular
/// image. If you don't need to read from it (for example if it's some kind of intermediary color,
/// or a depth buffer that is only used once) then use a transient image as it may improve
/// performances.
///
// TODO: forbid reading transient images outside render passes?
#[derive(Debug)]
pub struct AttachmentImage<F = Format, A = StdMemoryPoolAlloc> {
    // Inner implementation.
    image: UnsafeImage,

    // We maintain a view of the whole image since we will need it when rendering.
    view: UnsafeImageView,

    // Memory used to back the image.
    memory: A,

    // Format.
    format: F,

    // Layout to use when the image is used as a framebuffer attachment.
    // Must be either "depth-stencil optimal" or "color optimal".
    attachment_layout: ImageLayout,

    // Number of times this image is locked on the GPU side.
    gpu_lock: AtomicUsize,
}

impl<F> AttachmentImage<F> {
    /// Creates a new image with the given dimensions and format.
    ///
    /// Returns an error if the dimensions are too large or if the backend doesn't support this
    /// format as a framebuffer attachment.
    #[inline]
    pub fn new(device: Arc<Device>, dimensions: [u32; 2], format: F)
               -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        AttachmentImage::new_impl(device, dimensions, format, ImageUsage::none())
    }

    /// Same as `new`, but lets you specify additional usages.
    #[inline]
    pub fn with_usage(device: Arc<Device>, dimensions: [u32; 2], format: F, usage: ImageUsage)
                      -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        AttachmentImage::new_impl(device, dimensions, format, usage)
    }

    /// Same as `new`, except that the image will be transient.
    ///
    /// A transient image is special because its content is undefined outside of a render pass.
    /// This means that the implementation has the possibility to not allocate any memory for it.
    #[inline]
    pub fn transient(device: Arc<Device>, dimensions: [u32; 2], format: F)
                     -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        let base_usage = ImageUsage {
            transient_attachment: true,
            .. ImageUsage::none()
        };

        AttachmentImage::new_impl(device, dimensions, format, base_usage)
    }

    fn new_impl(device: Arc<Device>, dimensions: [u32; 2], format: F, base_usage: ImageUsage)
                -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        // TODO: check dimensions against the max_framebuffer_width/height/layers limits

        let is_depth = match format.format().ty() {
            FormatTy::Depth => true,
            FormatTy::DepthStencil => true,
            FormatTy::Stencil => true,
            FormatTy::Compressed => panic!(),
            _ => false
        };

        let usage = ImageUsage {
            color_attachment: !is_depth,
            depth_stencil_attachment: is_depth,
            .. base_usage
        };

        let (image, mem_reqs) = unsafe {
            try!(UnsafeImage::new(device.clone(), usage, format.format(),
                                  ImageDimensions::Dim2d { width: dimensions[0], height: dimensions[1], array_layers: 1, cubemap_compatible: false },
                                  1, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
        };

        let mem_ty = {
            let device_local = device.physical_device().memory_types()
                                     .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                                     .filter(|t| t.is_device_local());
            let any = device.physical_device().memory_types()
                            .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
                                         mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal));
        debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
        unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); }

        let view = unsafe {
            try!(UnsafeImageView::raw(&image, ViewType::Dim2d, 0 .. 1, 0 .. 1))
        };

        Ok(Arc::new(AttachmentImage {
            image: image,
            view: view,
            memory: mem,
            format: format,
            attachment_layout: if is_depth { ImageLayout::DepthStencilAttachmentOptimal }
                               else { ImageLayout::ColorAttachmentOptimal },
            gpu_lock: AtomicUsize::new(0),
        }))
    }
}

impl<F, A> AttachmentImage<F, A> {
    /// Returns the dimensions of the image.
    #[inline]
    pub fn dimensions(&self) -> [u32; 2] {
        let dims = self.image.dimensions();
        [dims.width(), dims.height()]
    }
}

/// GPU access to an attachment image.
pub struct AttachmentImageAccess<F, A> {
    img: Arc<AttachmentImage<F, A>>,
    // True if `try_gpu_lock` was already called on it.
    already_locked: AtomicBool,
}

impl<F, A> Clone for AttachmentImageAccess<F, A> {
    #[inline]
    fn clone(&self) -> AttachmentImageAccess<F, A> {
        AttachmentImageAccess {
            img: self.img.clone(),
            already_locked: AtomicBool::new(self.already_locked.load(Ordering::SeqCst))
        }
    }
}

unsafe impl<F, A> ImageAccess for AttachmentImageAccess<F, A>
    where F: 'static + Send + Sync
{
    #[inline]
    fn inner(&self) -> &UnsafeImage {
        &self.img.image
    }

    #[inline]
    fn initial_layout_requirement(&self) -> ImageLayout {
        self.img.attachment_layout
    }

    #[inline]
    fn final_layout_requirement(&self) -> ImageLayout {
        self.img.attachment_layout
    }

    #[inline]
    fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
        self.img.image.key()
    }

    #[inline]
    fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
        // FIXME: uncomment when it's working
        //        the problem is in the submit sync layer which locks framebuffer attachments and
        //        keeps them locked even after destruction
        Ok(())
        /*if self.already_locked.swap(true, Ordering::SeqCst) == true {
            return false;
        }

        self.img.gpu_lock.compare_and_swap(0, 1, Ordering::SeqCst) == 0*/
    }

    #[inline]
    unsafe fn increase_gpu_lock(&self) {
        // FIXME: uncomment when it's working
        //        the problem is in the submit sync layer which locks framebuffer attachments and
        //        keeps them locked even after destruction
        /*debug_assert!(self.already_locked.load(Ordering::SeqCst));
        let val = self.img.gpu_lock.fetch_add(1, Ordering::SeqCst);
        debug_assert!(val >= 1);*/
    }
}

impl<F, A> Drop for AttachmentImageAccess<F, A> {
    fn drop(&mut self) {
        if self.already_locked.load(Ordering::SeqCst) {
            let prev_val = self.img.gpu_lock.fetch_sub(1, Ordering::SeqCst);
            debug_assert!(prev_val >= 1);
        }
    }
}

unsafe impl<F, A> ImageClearValue<F::ClearValue> for AttachmentImageAccess<F, A>
    where F: FormatDesc + 'static + Send + Sync
{
    #[inline]
    fn decode(&self, value: F::ClearValue) -> Option<ClearValue> {
        Some(self.img.format.decode_clear_value(value))
    }
}

unsafe impl<P, F, A> ImageContent<P> for AttachmentImageAccess<F, A>
    where F: 'static + Send + Sync
{
    #[inline]
    fn matches_format(&self) -> bool {
        true        // FIXME:
    }
}

unsafe impl<F, A> Image for Arc<AttachmentImage<F, A>>
    where F: 'static + Send + Sync
{
    type Access = AttachmentImageAccess<F, A>;

    #[inline]
    fn access(self) -> AttachmentImageAccess<F, A> {
        AttachmentImageAccess {
            img: self,
            already_locked: AtomicBool::new(false),
        }
    }

    #[inline]
    fn format(&self) -> Format {
        self.image.format()
    }

    #[inline]
    fn samples(&self) -> u32 {
        self.image.samples()
    }

    #[inline]
    fn dimensions(&self) -> ImageDimensions {
        self.image.dimensions()
    }
}

unsafe impl<F, A> ImageView for Arc<AttachmentImage<F, A>>
    where F: 'static + Send + Sync
{
    type Access = AttachmentImageAccess<F, A>;

    #[inline]
    fn access(self) -> AttachmentImageAccess<F, A> {
        AttachmentImageAccess {
            img: self,
            already_locked: AtomicBool::new(false),
        }
    }
}

unsafe impl<F, A> ImageViewAccess for AttachmentImageAccess<F, A>
    where F: 'static + Send + Sync
{
    #[inline]
    fn parent(&self) -> &ImageAccess {
        self
    }

    #[inline]
    fn dimensions(&self) -> Dimensions {
        let dims = self.img.image.dimensions();
        Dimensions::Dim2d { width: dims.width(), height: dims.height() }
    }

    #[inline]
    fn inner(&self) -> &UnsafeImageView {
        &self.img.view
    }

    #[inline]
    fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn identity_swizzle(&self) -> bool {
        true
    }
}

#[cfg(test)]
mod tests {
    use super::AttachmentImage;
    use format::Format;

    #[test]
    fn create_regular() {
        let (device, _) = gfx_dev_and_queue!();
        let _img = AttachmentImage::new(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
    }

    #[test]
    fn create_transient() {
        let (device, _) = gfx_dev_and_queue!();
        let _img = AttachmentImage::transient(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
    }

    #[test]
    fn d16_unorm_always_supported() {
        let (device, _) = gfx_dev_and_queue!();
        let _img = AttachmentImage::new(device, [32, 32], Format::D16Unorm).unwrap();
    }
}
<commit_msg>Allow creating multisampled attachment images<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::iter::Empty;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;

use device::Device;
use device::Queue;
use format::ClearValue;
use format::Format;
use format::FormatDesc;
use format::FormatTy;
use image::Dimensions;
use image::ImageDimensions;
use image::ViewType;
use image::sys::ImageCreationError;
use image::ImageLayout;
use image::ImageUsage;
use image::sys::UnsafeImage;
use image::sys::UnsafeImageView;
use image::traits::ImageAccess;
use image::traits::ImageClearValue;
use image::traits::ImageContent;
use image::traits::ImageViewAccess;
use image::traits::Image;
use image::traits::ImageView;
use memory::pool::AllocLayout;
use memory::pool::MemoryPool;
use memory::pool::MemoryPoolAlloc;
use memory::pool::StdMemoryPoolAlloc;
use sync::AccessError;
use sync::Sharing;

/// ImageAccess whose purpose is to be used as a framebuffer attachment.
///
/// The image is always two-dimensional and has only one mipmap, but it can have any kind of
/// format. Trying to use a format that the backend doesn't support for rendering will result in
/// an error being returned when creating the image. Once you have an `AttachmentImage`, you are
/// guaranteed that you will be able to draw on it.
///
/// The template parameter of `AttachmentImage` is a type that describes the format of the image.
///
/// # Regular vs transient
///
/// Calling `AttachmentImage::new` will create a regular image, while calling
/// `AttachmentImage::transient` will create a *transient* image. Transient image are only
/// relevant for images that serve as attachments, so `AttachmentImage` is the only type of
/// image in vulkano that provides a shortcut for this.
///
/// A transient image is a special kind of image whose content is undefined outside of render
/// passes. Once you finish drawing, reading from it will returned undefined data (which can be
/// either valid or garbage, depending on the implementation).
///
/// This gives a hint to the Vulkan implementation that it is possible for the image's content to
/// live exclusively in some cache memory, and that no real memory has to be allocated for it.
///
/// In other words, if you are going to read from the image after drawing to it, use a regular
/// image. If you don't need to read from it (for example if it's some kind of intermediary color,
/// or a depth buffer that is only used once) then use a transient image as it may improve
/// performances.
///
// TODO: forbid reading transient images outside render passes?
#[derive(Debug)]
pub struct AttachmentImage<F = Format, A = StdMemoryPoolAlloc> {
    // Inner implementation.
    image: UnsafeImage,

    // We maintain a view of the whole image since we will need it when rendering.
    view: UnsafeImageView,

    // Memory used to back the image.
    memory: A,

    // Format.
    format: F,

    // Layout to use when the image is used as a framebuffer attachment.
    // Must be either "depth-stencil optimal" or "color optimal".
    attachment_layout: ImageLayout,

    // Number of times this image is locked on the GPU side.
    gpu_lock: AtomicUsize,
}

impl<F> AttachmentImage<F> {
    /// Creates a new image with the given dimensions and format.
    ///
    /// Returns an error if the dimensions are too large or if the backend doesn't support this
    /// format as a framebuffer attachment.
    #[inline]
    pub fn new(device: Arc<Device>, dimensions: [u32; 2], format: F)
               -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        AttachmentImage::new_impl(device, dimensions, format, ImageUsage::none(), 1)
    }

    /// Same as `new`, but creates a multisampled image.
    ///
    /// > **Note**: You can also use this function and pass `1` for the number of samples if you
    /// > want a regular image.
    #[inline]
    pub fn multisampled(device: Arc<Device>, dimensions: [u32; 2], samples: u32, format: F)
                        -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        AttachmentImage::new_impl(device, dimensions, format, ImageUsage::none(), samples)
    }

    /// Same as `new`, but lets you specify additional usages.
    #[inline]
    pub fn with_usage(device: Arc<Device>, dimensions: [u32; 2], format: F, usage: ImageUsage)
                      -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        AttachmentImage::new_impl(device, dimensions, format, usage, 1)
    }

    /// Same as `with_usage`, but creates a multisampled image.
    ///
    /// > **Note**: You can also use this function and pass `1` for the number of samples if you
    /// > want a regular image.
    #[inline]
    pub fn multisampled_with_usage(device: Arc<Device>, dimensions: [u32; 2], samples: u32,
                                   format: F, usage: ImageUsage)
                                   -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        AttachmentImage::new_impl(device, dimensions, format, usage, samples)
    }

    /// Same as `new`, except that the image will be transient.
    ///
    /// A transient image is special because its content is undefined outside of a render pass.
    /// This means that the implementation has the possibility to not allocate any memory for it.
    #[inline]
    pub fn transient(device: Arc<Device>, dimensions: [u32; 2], format: F)
                     -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        let base_usage = ImageUsage {
            transient_attachment: true,
            .. ImageUsage::none()
        };

        AttachmentImage::new_impl(device, dimensions, format, base_usage, 1)
    }

    /// Same as `transient`, but creates a multisampled image.
    ///
    /// > **Note**: You can also use this function and pass `1` for the number of samples if you
    /// > want a regular image.
    #[inline]
    pub fn transient_multisampled(device: Arc<Device>, dimensions: [u32; 2], samples: u32, format: F)
                                  -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        let base_usage = ImageUsage {
            transient_attachment: true,
            .. ImageUsage::none()
        };

        AttachmentImage::new_impl(device, dimensions, format, base_usage, samples)
    }

    fn new_impl(device: Arc<Device>, dimensions: [u32; 2], format: F, base_usage: ImageUsage,
                samples: u32) -> Result<Arc<AttachmentImage<F>>, ImageCreationError>
        where F: FormatDesc
    {
        // TODO: check dimensions against the max_framebuffer_width/height/layers limits

        let is_depth = match format.format().ty() {
            FormatTy::Depth => true,
            FormatTy::DepthStencil => true,
            FormatTy::Stencil => true,
            FormatTy::Compressed => panic!(),
            _ => false
        };

        let usage = ImageUsage {
            color_attachment: !is_depth,
            depth_stencil_attachment: is_depth,
            .. base_usage
        };

        let (image, mem_reqs) = unsafe {
            let dims = ImageDimensions::Dim2d {
                width: dimensions[0],
                height: dimensions[1],
                array_layers: 1,
                cubemap_compatible: false
            };

            try!(UnsafeImage::new(device.clone(), usage, format.format(), dims,
                                  samples, 1, Sharing::Exclusive::<Empty<u32>>, false, false))
        };

        let mem_ty = {
            let device_local = device.physical_device().memory_types()
                                     .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0)
                                     .filter(|t| t.is_device_local());
            let any = device.physical_device().memory_types()
                            .filter(|t| (mem_reqs.memory_type_bits & (1 << t.id())) != 0);
            device_local.chain(any).next().unwrap()
        };

        let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty,
                                         mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal));
        debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
        unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); }

        let view = unsafe {
            try!(UnsafeImageView::raw(&image, ViewType::Dim2d, 0 .. 1, 0 .. 1))
        };

        Ok(Arc::new(AttachmentImage {
            image: image,
            view: view,
            memory: mem,
            format: format,
            attachment_layout: if is_depth { ImageLayout::DepthStencilAttachmentOptimal }
                               else { ImageLayout::ColorAttachmentOptimal },
            gpu_lock: AtomicUsize::new(0),
        }))
    }
}

impl<F, A> AttachmentImage<F, A> {
    /// Returns the dimensions of the image.
    #[inline]
    pub fn dimensions(&self) -> [u32; 2] {
        let dims = self.image.dimensions();
        [dims.width(), dims.height()]
    }
}

/// GPU access to an attachment image.
pub struct AttachmentImageAccess<F, A> {
    img: Arc<AttachmentImage<F, A>>,
    // True if `try_gpu_lock` was already called on it.
    already_locked: AtomicBool,
}

impl<F, A> Clone for AttachmentImageAccess<F, A> {
    #[inline]
    fn clone(&self) -> AttachmentImageAccess<F, A> {
        AttachmentImageAccess {
            img: self.img.clone(),
            already_locked: AtomicBool::new(self.already_locked.load(Ordering::SeqCst))
        }
    }
}

unsafe impl<F, A> ImageAccess for AttachmentImageAccess<F, A>
    where F: 'static + Send + Sync
{
    #[inline]
    fn inner(&self) -> &UnsafeImage {
        &self.img.image
    }

    #[inline]
    fn initial_layout_requirement(&self) -> ImageLayout {
        self.img.attachment_layout
    }

    #[inline]
    fn final_layout_requirement(&self) -> ImageLayout {
        self.img.attachment_layout
    }

    #[inline]
    fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 {
        self.img.image.key()
    }

    #[inline]
    fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
        // FIXME: uncomment when it's working
        //        the problem is in the submit sync layer which locks framebuffer attachments and
        //        keeps them locked even after destruction
        Ok(())
        /*if self.already_locked.swap(true, Ordering::SeqCst) == true {
            return false;
        }

        self.img.gpu_lock.compare_and_swap(0, 1, Ordering::SeqCst) == 0*/
    }

    #[inline]
    unsafe fn increase_gpu_lock(&self) {
        // FIXME: uncomment when it's working
        //        the problem is in the submit sync layer which locks framebuffer attachments and
        //        keeps them locked even after destruction
        /*debug_assert!(self.already_locked.load(Ordering::SeqCst));
        let val = self.img.gpu_lock.fetch_add(1, Ordering::SeqCst);
        debug_assert!(val >= 1);*/
    }
}

impl<F, A> Drop for AttachmentImageAccess<F, A> {
    fn drop(&mut self) {
        if self.already_locked.load(Ordering::SeqCst) {
            let prev_val = self.img.gpu_lock.fetch_sub(1, Ordering::SeqCst);
            debug_assert!(prev_val >= 1);
        }
    }
}

unsafe impl<F, A> ImageClearValue<F::ClearValue> for AttachmentImageAccess<F, A>
    where F: FormatDesc + 'static + Send + Sync
{
    #[inline]
    fn decode(&self, value: F::ClearValue) -> Option<ClearValue> {
        Some(self.img.format.decode_clear_value(value))
    }
}

unsafe impl<P, F, A> ImageContent<P> for AttachmentImageAccess<F, A>
    where F: 'static + Send + Sync
{
    #[inline]
    fn matches_format(&self) -> bool {
        true        // FIXME:
    }
}

unsafe impl<F, A> Image for Arc<AttachmentImage<F, A>>
    where F: 'static + Send + Sync
{
    type Access = AttachmentImageAccess<F, A>;

    #[inline]
    fn access(self) -> AttachmentImageAccess<F, A> {
        AttachmentImageAccess {
            img: self,
            already_locked: AtomicBool::new(false),
        }
    }

    #[inline]
    fn format(&self) -> Format {
        self.image.format()
    }

    #[inline]
    fn samples(&self) -> u32 {
        self.image.samples()
    }

    #[inline]
    fn dimensions(&self) -> ImageDimensions {
        self.image.dimensions()
    }
}

unsafe impl<F, A> ImageView for Arc<AttachmentImage<F, A>>
    where F: 'static + Send + Sync
{
    type Access = AttachmentImageAccess<F, A>;

    #[inline]
    fn access(self) -> AttachmentImageAccess<F, A> {
        AttachmentImageAccess {
            img: self,
            already_locked: AtomicBool::new(false),
        }
    }
}

unsafe impl<F, A> ImageViewAccess for AttachmentImageAccess<F, A>
    where F: 'static + Send + Sync
{
    #[inline]
    fn parent(&self) -> &ImageAccess {
        self
    }

    #[inline]
    fn dimensions(&self) -> Dimensions {
        let dims = self.img.image.dimensions();
        Dimensions::Dim2d { width: dims.width(), height: dims.height() }
    }

    #[inline]
    fn inner(&self) -> &UnsafeImageView {
        &self.img.view
    }

    #[inline]
    fn descriptor_set_storage_image_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn descriptor_set_sampled_image_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn descriptor_set_input_attachment_layout(&self) -> ImageLayout {
        ImageLayout::ShaderReadOnlyOptimal
    }

    #[inline]
    fn identity_swizzle(&self) -> bool {
        true
    }
}

#[cfg(test)]
mod tests {
    use super::AttachmentImage;
    use format::Format;

    #[test]
    fn create_regular() {
        let (device, _) = gfx_dev_and_queue!();
        let _img = AttachmentImage::new(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
    }

    #[test]
    fn create_transient() {
        let (device, _) = gfx_dev_and_queue!();
        let _img = AttachmentImage::transient(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
    }

    #[test]
    fn d16_unorm_always_supported() {
        let (device, _) = gfx_dev_and_queue!();
        let _img = AttachmentImage::new(device, [32, 32], Format::D16Unorm).unwrap();
    }
}
<|endoftext|>


<commit_before>//! utils used in proc-macro tests

use crate::dylib;
use crate::ProcMacroSrv;
use expect_test::Expect;
use proc_macro_api::ListMacrosTask;
use std::str::FromStr;

pub mod fixtures {
    use cargo_metadata::Message;
    use std::path::PathBuf;
    use std::process::Command;

    // Use current project metadata to get the proc-macro dylib path
    pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
        let name = "proc_macro_test";
        let version = "0.0.0";
        let command = Command::new(toolchain::cargo())
            .args(&["build", "-p", name, "--message-format", "json"])
            .output()
            .unwrap()
            .stdout;

        for message in Message::parse_stream(command.as_slice()) {
            match message.unwrap() {
                Message::CompilerArtifact(artifact) => {
                    if artifact.target.kind.contains(&"proc-macro".to_string()) {
                        let repr = format!("{} {}", name, version);
                        if artifact.package_id.repr.starts_with(&repr) {
                            return PathBuf::from(&artifact.filenames[0]);
                        }
                    }
                }
                _ => (), // Unknown message
            }
        }

        panic!("No proc-macro dylib for {} found!", name);
    }
}

fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
    Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
}

pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {
    assert_expand_impl(macro_name, ra_fixture, None, expect);
}

pub fn assert_expand_attr(macro_name: &str, ra_fixture: &str, attr_args: &str, expect: Expect) {
    assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect);
}

fn assert_expand_impl(macro_name: &str, input: &str, attr: Option<&str>, expect: Expect) {
    let path = fixtures::proc_macro_test_dylib_path();
    let expander = dylib::Expander::new(&path).unwrap();
    let fixture = parse_string(input).unwrap();
    let attr = attr.map(|attr| parse_string(attr).unwrap().into_subtree());

    let res = expander.expand(macro_name, &fixture.into_subtree(), attr.as_ref()).unwrap();
    expect.assert_eq(&format!("{:?}", res));
}

pub fn list() -> Vec<String> {
    let path = fixtures::proc_macro_test_dylib_path();
    let task = ListMacrosTask { lib: path };
    let mut srv = ProcMacroSrv::default();
    let res = srv.list_macros(&task).unwrap();
    res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
}
<commit_msg>Use `cargo check` again<commit_after>//! utils used in proc-macro tests

use crate::dylib;
use crate::ProcMacroSrv;
use expect_test::Expect;
use proc_macro_api::ListMacrosTask;
use std::str::FromStr;

pub mod fixtures {
    use cargo_metadata::Message;
    use std::path::PathBuf;
    use std::process::Command;

    // Use current project metadata to get the proc-macro dylib path
    pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
        let name = "proc_macro_test";
        let version = "0.0.0";
        let command = Command::new(toolchain::cargo())
            .args(&["check", "--tests", "--message-format", "json"])
            .output()
            .unwrap()
            .stdout;

        for message in Message::parse_stream(command.as_slice()) {
            match message.unwrap() {
                Message::CompilerArtifact(artifact) => {
                    if artifact.target.kind.contains(&"proc-macro".to_string()) {
                        let repr = format!("{} {}", name, version);
                        if artifact.package_id.repr.starts_with(&repr) {
                            return PathBuf::from(&artifact.filenames[0]);
                        }
                    }
                }
                _ => (), // Unknown message
            }
        }

        panic!("No proc-macro dylib for {} found!", name);
    }
}

fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
    Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
}

pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {
    assert_expand_impl(macro_name, ra_fixture, None, expect);
}

pub fn assert_expand_attr(macro_name: &str, ra_fixture: &str, attr_args: &str, expect: Expect) {
    assert_expand_impl(macro_name, ra_fixture, Some(attr_args), expect);
}

fn assert_expand_impl(macro_name: &str, input: &str, attr: Option<&str>, expect: Expect) {
    let path = fixtures::proc_macro_test_dylib_path();
    let expander = dylib::Expander::new(&path).unwrap();
    let fixture = parse_string(input).unwrap();
    let attr = attr.map(|attr| parse_string(attr).unwrap().into_subtree());

    let res = expander.expand(macro_name, &fixture.into_subtree(), attr.as_ref()).unwrap();
    expect.assert_eq(&format!("{:?}", res));
}

pub fn list() -> Vec<String> {
    let path = fixtures::proc_macro_test_dylib_path();
    let task = ListMacrosTask { lib: path };
    let mut srv = ProcMacroSrv::default();
    let res = srv.list_macros(&task).unwrap();
    res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
}
<|endoftext|>


<commit_before>use std::fmt;

use serde::{de::DeserializeOwned, Serialize};
use serde_json::value::RawValue as RawJsonValue;

use crate::serde::Raw;

use super::{
    EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType,
    RoomAccountDataEventType, StateEventType, ToDeviceEventType,
};

/// The base trait that all event content types implement.
///
/// Use [`macros::EventContent`] to derive this traits. It is not meant to be implemented manually.
///
/// [`macros::EventContent`]: super::macros::EventContent
pub trait EventContent: Sized + Serialize {
    /// The Rust enum for the event kind's known types.
    type EventType;

    /// Get the event's type, like `m.room.message`.
    fn event_type(&self) -> Self::EventType;

    /// Constructs the given event content.
    #[doc(hidden)]
    fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self>;
}

impl<T> Raw<T>
where
    T: EventContent,
    T::EventType: fmt::Display,
{
    /// Try to deserialize the JSON as an event's content.
    pub fn deserialize_content(&self, event_type: T::EventType) -> serde_json::Result<T> {
        T::from_parts(&event_type.to_string(), self.json())
    }
}

/// The base trait that all redacted event content types implement.
///
/// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageLikeEvent` and their "sync" and "stripped" counterparts.
/// The `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
pub trait RedactedEventContent: EventContent {
    /// Constructs the redacted event content.
    ///
    /// If called for anything but "empty" redacted content this will error.
    #[doc(hidden)]
    fn empty(_event_type: &str) -> serde_json::Result<Self> {
        Err(serde::de::Error::custom("this event is not redacted"))
    }

    /// Determines if the redacted event content needs to serialize fields.
    #[doc(hidden)]
    fn has_serialize_fields(&self) -> bool;

    /// Determines if the redacted event content needs to deserialize fields.
    #[doc(hidden)]
    fn has_deserialize_fields() -> HasDeserializeFields;
}

/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum HasDeserializeFields {
    /// Deserialize the event's content, failing if invalid.
    True,

    /// Return the redacted version of this event's content.
    False,

    /// `Optional` is used for `RedactedAliasesEventContent` since it has
    /// an empty version and one with content left after redaction that
    /// must be supported together.
    Optional,
}

/// Trait for abstracting over event content structs.
///
/// … but *not* enums which don't always have an event type and kind (e.g. message vs state) that's
/// fixed / known at compile time.
pub trait StaticEventContent: EventContent {
    /// The event's "kind".
    ///
    /// See the type's documentation.
    const KIND: EventKind;

    /// The event type.
    const TYPE: &'static str;
}

/// The "kind" of an event.
///
/// This corresponds directly to the event content marker traits.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum EventKind {
    /// Global account data event kind.
    GlobalAccountData,

    /// Room account data event kind.
    RoomAccountData,

    /// Ephemeral room event kind.
    EphemeralRoomData,

    /// Message-like event kind.
    ///
    /// Since redacted / non-redacted message-like events are used in the same places but have
    /// different sets of fields, these two variations are treated as two closely-related event
    /// kinds.
    MessageLike {
        /// Redacted variation?
        redacted: bool,
    },

    /// State event kind.
    ///
    /// Since redacted / non-redacted state events are used in the same places but have different
    /// sets of fields, these two variations are treated as two closely-related event kinds.
    State {
        /// Redacted variation?
        redacted: bool,
    },

    /// To-device event kind.
    ToDevice,

    /// Presence event kind.
    Presence,
}

macro_rules! trait_aliases {
    // need to use `,` instead of `+` because (1) path can't be followed by `+`
    // and (2) `+` can't be used as a separator since it's a repetition operator
    ($(
        $( #[doc = $docs:literal] )*
        trait $id:ident = $( $def:path ),+;
    )*) => {
        $(
            $( #[doc = $docs] )*
            pub trait $id: $($def+)+ {}
            impl<T: $($def+)+> $id for T {}
        )*
    }
}

trait_aliases! {
    /// An alias for `EventContent<EventType = GlobalAccountDataEventType>`.
    trait GlobalAccountDataEventContent = EventContent<EventType = GlobalAccountDataEventType>;

    /// An alias for `EventContent<EventType = RoomAccountDataEventType>`.
    trait RoomAccountDataEventContent = EventContent<EventType = RoomAccountDataEventType>;

    /// An alias for `EventContent<EventType = EphemeralRoomEventType>`.
    trait EphemeralRoomEventContent = EventContent<EventType = EphemeralRoomEventType>;

    /// An alias for `EventContent<EventType = MessageLikeEventType>`.
    trait MessageLikeEventContent = EventContent<EventType = MessageLikeEventType>;

    /// An alias for `MessageLikeEventContent + RedactedEventContent`.
    trait RedactedMessageLikeEventContent = MessageLikeEventContent, RedactedEventContent;

    /// An alias for `StateEventContent + RedactedEventContent`.
    trait RedactedStateEventContent = StateEventContent, RedactedEventContent;

    /// An alias for `EventContent<EventType = ToDeviceEventType>`.
    trait ToDeviceEventContent = EventContent<EventType = ToDeviceEventType>;
}

/// An alias for `EventContent<EventType = StateEventType>`.
pub trait StateEventContent: EventContent<EventType = StateEventType> {
    /// The type of the event's `state_key` field.
    type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}
<commit_msg>events: Expand trait_aliases! macro<commit_after>use std::fmt;

use serde::{de::DeserializeOwned, Serialize};
use serde_json::value::RawValue as RawJsonValue;

use crate::serde::Raw;

use super::{
    EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType,
    RoomAccountDataEventType, StateEventType, ToDeviceEventType,
};

/// The base trait that all event content types implement.
///
/// Use [`macros::EventContent`] to derive this traits. It is not meant to be implemented manually.
///
/// [`macros::EventContent`]: super::macros::EventContent
pub trait EventContent: Sized + Serialize {
    /// The Rust enum for the event kind's known types.
    type EventType;

    /// Get the event's type, like `m.room.message`.
    fn event_type(&self) -> Self::EventType;

    /// Constructs the given event content.
    #[doc(hidden)]
    fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self>;
}

impl<T> Raw<T>
where
    T: EventContent,
    T::EventType: fmt::Display,
{
    /// Try to deserialize the JSON as an event's content.
    pub fn deserialize_content(&self, event_type: T::EventType) -> serde_json::Result<T> {
        T::from_parts(&event_type.to_string(), self.json())
    }
}

/// The base trait that all redacted event content types implement.
///
/// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageLikeEvent` and their "sync" and "stripped" counterparts.
/// The `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
pub trait RedactedEventContent: EventContent {
    /// Constructs the redacted event content.
    ///
    /// If called for anything but "empty" redacted content this will error.
    #[doc(hidden)]
    fn empty(_event_type: &str) -> serde_json::Result<Self> {
        Err(serde::de::Error::custom("this event is not redacted"))
    }

    /// Determines if the redacted event content needs to serialize fields.
    #[doc(hidden)]
    fn has_serialize_fields(&self) -> bool;

    /// Determines if the redacted event content needs to deserialize fields.
    #[doc(hidden)]
    fn has_deserialize_fields() -> HasDeserializeFields;
}

/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum HasDeserializeFields {
    /// Deserialize the event's content, failing if invalid.
    True,

    /// Return the redacted version of this event's content.
    False,

    /// `Optional` is used for `RedactedAliasesEventContent` since it has
    /// an empty version and one with content left after redaction that
    /// must be supported together.
    Optional,
}

/// Trait for abstracting over event content structs.
///
/// … but *not* enums which don't always have an event type and kind (e.g. message vs state) that's
/// fixed / known at compile time.
pub trait StaticEventContent: EventContent {
    /// The event's "kind".
    ///
    /// See the type's documentation.
    const KIND: EventKind;

    /// The event type.
    const TYPE: &'static str;
}

/// The "kind" of an event.
///
/// This corresponds directly to the event content marker traits.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum EventKind {
    /// Global account data event kind.
    GlobalAccountData,

    /// Room account data event kind.
    RoomAccountData,

    /// Ephemeral room event kind.
    EphemeralRoomData,

    /// Message-like event kind.
    ///
    /// Since redacted / non-redacted message-like events are used in the same places but have
    /// different sets of fields, these two variations are treated as two closely-related event
    /// kinds.
    MessageLike {
        /// Redacted variation?
        redacted: bool,
    },

    /// State event kind.
    ///
    /// Since redacted / non-redacted state events are used in the same places but have different
    /// sets of fields, these two variations are treated as two closely-related event kinds.
    State {
        /// Redacted variation?
        redacted: bool,
    },

    /// To-device event kind.
    ToDevice,

    /// Presence event kind.
    Presence,
}

/// An alias for `EventContent<EventType = GlobalAccountDataEventType>`.
pub trait GlobalAccountDataEventContent:
    EventContent<EventType = GlobalAccountDataEventType>
{
}
impl<T: EventContent<EventType = GlobalAccountDataEventType>> GlobalAccountDataEventContent for T {}

/// An alias for `EventContent<EventType = RoomAccountDataEventType>`.
pub trait RoomAccountDataEventContent: EventContent<EventType = RoomAccountDataEventType> {}
impl<T: EventContent<EventType = RoomAccountDataEventType>> RoomAccountDataEventContent for T {}

/// An alias for `EventContent<EventType = EphemeralRoomEventType>`.
pub trait EphemeralRoomEventContent: EventContent<EventType = EphemeralRoomEventType> {}
impl<T: EventContent<EventType = EphemeralRoomEventType>> EphemeralRoomEventContent for T {}

/// An alias for `EventContent<EventType = MessageLikeEventType>`.
pub trait MessageLikeEventContent: EventContent<EventType = MessageLikeEventType> {}
impl<T: EventContent<EventType = MessageLikeEventType>> MessageLikeEventContent for T {}

/// An alias for `MessageLikeEventContent + RedactedEventContent`.
pub trait RedactedMessageLikeEventContent: MessageLikeEventContent + RedactedEventContent {}
impl<T: MessageLikeEventContent + RedactedEventContent> RedactedMessageLikeEventContent for T {}

/// An alias for `StateEventContent + RedactedEventContent`.
pub trait RedactedStateEventContent: StateEventContent + RedactedEventContent {}
impl<T: StateEventContent + RedactedEventContent> RedactedStateEventContent for T {}

/// An alias for `EventContent<EventType = ToDeviceEventType>`.
pub trait ToDeviceEventContent: EventContent<EventType = ToDeviceEventType> {}
impl<T: EventContent<EventType = ToDeviceEventType>> ToDeviceEventContent for T {}

/// An alias for `EventContent<EventType = StateEventType>`.
pub trait StateEventContent: EventContent<EventType = StateEventType> {
    /// The type of the event's `state_key` field.
    type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}
<|endoftext|>


<commit_before>/*
  This code finds a solution to the NPR puzzle presented on Oct 21, 2018.

  A (paraphrased) statement of the problem:
    Given the letters in the word 'beermouth', contruct a three by three matrix of words where all
    the verticals, horizontals, and diagonals form three letter words.
*/

use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::vec::Vec;

// Gets the nth char from `word`.
fn get_char(word: &str, index: usize) -> char {
    word.chars().nth(index).unwrap()
}

// Convert the word to a map from charaters to the number of times that character appears.
fn construct_letter_map(word: &str) -> HashMap<char, u8> {
    let mut map = HashMap::new();
    for letter in word.chars() {
        *map.entry(letter).or_insert(0) += 1
    }
    map
}

// Compare if `left` is a subset of `right`.
fn letter_map_less_than(left: &HashMap<char, u8>, right: &HashMap<char, u8>) -> bool {
    for (letter, l_count) in left {
        match right.get(letter) {
            Some(r_count) => if l_count > r_count {
                return false;
            },
            _ => return false,
        }
    }
    true
}

// The middle letter has to be the middle letter of at least four words. Construct lists of
// possible sets of words.
fn shared_middle_letters(words: &[[char; 3]]) -> Vec<Vec<[char; 3]>> {
    let mut map = HashMap::new();
    for word in words {
        map.entry(word[1]).or_insert(Vec::new()).push(*word)
    }
    let mut out = Vec::new();
    for (_, val) in map.drain() {
        if val.len() >= 4 {
            out.push(val);
        }
    }
    out
}

// Checks that the first and last letters of `word` are still in `right`.
fn two_letter_less_than(word: &[char; 3], right: &HashMap<char, u8>) -> bool {
    for letter in [word[0], word[2]].iter() {
        match right.get(letter) {
            Some(count) => if *count <= 0 {
                return false;
            },
            _ => return false,
        }
    }
    true
}

// Checks if the word is a subset of the letters in `letter_map`. If so, it decrements the counts
// in `letter_map` by the first and third letters of the word. Ignores the middle letter since that
// is shared.
// Returns whether or not the word was a subset.
fn clear_two_letters(word: &[char; 3], letter_map: &mut HashMap<char, u8>) -> bool {
    if !two_letter_less_than(word, letter_map) {
        return false;
    }
    *letter_map.get_mut(&word[0]).unwrap() -= 1;
    *letter_map.get_mut(&word[2]).unwrap() -= 1;
    true
}

// If the word is a candidate, puts it in the slot provided by `index`. Otherwise, do nothing.
// Returns whether or not the word was inserted.
fn append_word_if<'a>(
    word: &[char; 3],
    letter_map: &mut HashMap<char, u8>,
    words: &mut Vec<[char; 3]>,
) -> bool {
    if !clear_two_letters(word, letter_map) {
        return false;
    }
    words.push(*word);
    true
}

fn four_word_product<'a>(words: &[[char; 3]], base_map: &HashMap<char, u8>) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    let middle_letter = words[0][1];
    for ii in 0..words.len() {
        let mut map = base_map.clone();
        let mut four_words = Vec::new();
        *map.get_mut(&middle_letter).unwrap() -= 1;
        if !append_word_if(&words[ii], &mut map, &mut four_words) {
            continue;
        }
        assert_eq!(four_words.len(), 1);
        for jj in ii + 1..words.len() {
            // Truncate here so that we only get one word from this pass.
            four_words.truncate(1);
            // Create a new map per iteration.
            let mut map = map.clone();
            if !append_word_if(&words[jj], &mut map, &mut four_words) {
                continue;
            }
            assert_eq!(four_words.len(), 2);
            for kk in jj + 1..words.len() {
                four_words.truncate(2);
                let mut map = map.clone();
                if !append_word_if(&words[kk], &mut map, &mut four_words) {
                    continue;
                }
                assert_eq!(four_words.len(), 3);
                for ll in kk + 1..words.len() {
                    four_words.truncate(3);
                    let mut map = map.clone();
                    if append_word_if(&words[ll], &mut map, &mut four_words) {
                        assert_eq!(four_words.len(), 4);
                        out.push(four_words.clone());
                    }
                }
            }
        }
    }
    out
}

// Find all of the combinations of four words that share a middle letter and the combined letters
// of all four words are still a subset of 'base_map'.
fn four_word_candidates<'a>(
    shared_middle: &[Vec<[char; 3]>],
    base_map: &HashMap<char, u8>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for words in shared_middle {
        out.append(&mut four_word_product(words, base_map));
    }
    out
}

// For four indices between zero and 4, return the missing index.
fn missing_index(ii: usize, jj: usize, kk: usize) -> usize {
    assert!(ii < 4);
    assert!(jj < 4);
    assert!(kk < 4);
    for ll in 0..4 {
        if ll != ii && ll != jj && ll != kk {
            return ll;
        }
    }
    unreachable!()
}

// Takes the candidates and finds words that make a proper word in the top row. The words are
// returned such that the first word is the left to right diagonal (1, 5, 9 on a phone), the second
// word is the middle vertical, the third is the right to left diagonal (3, 5, 7) and the remaining
// word is last.
fn top_row_candidates<'a>(
    four_cands: &Vec<Vec<[char; 3]>>,
    possible_words: &HashSet<[char; 3]>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for cand in four_cands {
        assert!(cand.len() == 4);
        for ii in 0..cand.len() {
            let first = cand[ii];
            for jj in 0..cand.len() {
                if jj == ii {
                    continue;
                }
                let second = cand[jj];
                for kk in 0..cand.len() {
                    if kk == jj || kk == ii {
                        continue;
                    }
                    let third = cand[kk];
                    let word = [first[0], second[0], third[0]];
                    if possible_words.contains(&word) {
                        let mut top_cand = vec![first, second, third];
                        top_cand.push(cand[missing_index(ii, jj, kk)]);
                        out.push(top_cand);
                    }
                }
            }
        }
    }
    out
}

// Filter out sets of four where the bottom generated from the top three words isn't a word.
fn filter_by_bottom<'a>(
    top_cands: Vec<Vec<[char; 3]>>,
    possible_words: &HashSet<[char; 3]>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for cand in top_cands {
        assert!(cand.len() == 4);
        let first = cand[0][2];
        let second = cand[1][2];
        let third = cand[2][2];
        let word = [first, second, third];
        if possible_words.contains(&word) {
            out.push(cand);
            continue;
        }
    }
    out
}

fn filter_by_rest<'a>(
    cands: Vec<Vec<[char; 3]>>,
    possible_words: &HashSet<[char; 3]>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for cand in cands {
        // The left vertical word is made up of the first letter of the first top word (left to
        // right diagnol), the first letter of the remaining word, and the last letter of the
        // third top word (right to left diagonal).
        let left_v_word = [cand[0][0], cand[3][0], cand[2][2]];
        if !possible_words.contains(&left_v_word) {
            continue;
        }
        // The right vertical word is from the first letter of the third top word, the third letter
        // of the remaining word and the third letter of the first top word.
        let right_v_word = [cand[2][0], cand[3][2], cand[0][2]];
        if !possible_words.contains(&right_v_word) {
            continue;
        }

        out.push(cand);
    }
    out
}

// Returns the 3x3 grid of letters in row major form.
fn presentation_format(cand: &[[char; 3]]) -> Vec<Vec<char>> {
    let mut out = Vec::new();
    // Fill in the vectors with x's just as a placeholder.
    for _ in 0..3 {
        out.push(vec!['x', 'x', 'x']);
    }
    // The first word is the left to right diagonal.
    out[0][0] = cand[0][0];
    out[1][1] = cand[0][1];
    out[2][2] = cand[0][2];
    // The second word is the middle vertical.
    out[0][1] = cand[1][0];
    out[2][1] = cand[1][2];
    // The third word is the left diagonal.
    out[0][2] = cand[2][0];
    out[2][0] = cand[2][2];
    // The last word is the middle horizontal.
    out[1][0] = cand[3][0];
    out[1][2] = cand[3][2];
    out
}

fn main() {
    let f = match File::open("dictionary.txt") {
        Ok(f) => f,
        Err(why) => panic!("Failed to open links.txt: {}", Error::description(&why)),
    };

    // Construct a vector of three letter words that are a subset of beermouth.
    let source_word = construct_letter_map("beermouth");
    let words: Vec<[char; 3]> = BufReader::new(f)
        .lines()
        .map(|s| s.unwrap())
        .filter(|s| s.len() == 3)
        .filter(|s| letter_map_less_than(&construct_letter_map(s), &source_word))
        .map(|s| [get_char(&s, 0), get_char(&s, 1), get_char(&s, 2)])
        .collect();

    // Find sets of words that share a middle letter.
    let middle_candidates = shared_middle_letters(&words);
    // Find sets of four words that share a middle letter and could make up the solution.
    let four_cands = four_word_candidates(&middle_candidates, &source_word);

    // Find possible top rows.
    let word_set = {
        let mut word_set = HashSet::with_capacity(words.len());
        for word in words.iter() {
            word_set.insert(word.clone());
        }
        word_set
    };
    let top_cands = top_row_candidates(&four_cands, &word_set);
    let bottom_cands = filter_by_bottom(top_cands, &word_set);
    let last_cands = filter_by_rest(bottom_cands, &word_set);

    for last_cand in last_cands {
        println!("Last candidate words:");
        for word in last_cand.iter() {
            let word_str: String = word.iter().collect();
            print!("{} ", word_str);
        }
        println!("\nIn grid format:");
        let present = presentation_format(&last_cand);
        for line in present {
            for ch in line {
                print!(" {} ", ch);
            }
            print!("\n");
        }
        print!("\n");
    }
}

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

    #[test]
    fn test_letter_map() {
        let mut expected = HashMap::new();
        expected.insert('b', 1);
        expected.insert('e', 2);
        expected.insert('r', 1);
        expected.insert('m', 1);
        expected.insert('o', 1);
        expected.insert('u', 1);
        expected.insert('t', 1);
        expected.insert('h', 1);
        assert_eq!(expected, construct_letter_map("beermouth"));
    }

    #[test]
    fn test_map_less_than() {
        let source_word = construct_letter_map("beermouth");
        assert!(letter_map_less_than(
            &construct_letter_map("bee"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("bum"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("but"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("out"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("boo"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("oom"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("ouf"),
            &source_word
        ));
    }

    #[test]
    fn test_shared_middle() {
        let mut words = Vec::new();
        words.push(['b', 'e', 'e']);
        words.push(['b', 'e', 't']);
        words.push(['m', 'e', 't']);
        words.push(['b', 'u', 'm']);
        words.push(['b', 'u', 't']);
        words.push(['o', 'u', 't']);
        words.push(['o', 'u', 'f']);
        words.push(['b', 'o', 't']);
        words.push(['h', 'o', 't']);
        words.push(['m', 'o', 't']);
        words.push(['t', 'o', 'm']);
        words.push(['r', 'o', 't']);
        let mut expected = Vec::new();
        {
            let mut group = Vec::new();
            for ii in 3..7 {
                group.push(words[ii]);
            }
            group.sort();
            expected.push(group);
        }
        {
            let mut group = Vec::new();
            for ii in 7..12 {
                group.push(words[ii]);
            }
            group.sort();
            expected.push(group);
        }
        let mut input_words = words.clone();
        input_words.sort();
        let mut shared = shared_middle_letters(&input_words);
        // Sort by the lengths of the sub-Vecs to prevent test flakes.
        shared.sort_by(|l, r| l.len().cmp(&r.len()));
        assert_eq!(expected, shared);
    }
}
<commit_msg>Fix comment<commit_after>/*
  This code finds a solution to the NPR puzzle presented on Oct 21, 2018.

  A (paraphrased) statement of the problem:
    Given the letters in the word 'beermouth', contruct a three by three matrix of words where all
    the verticals, horizontals, and diagonals form three letter words.
*/

use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::vec::Vec;

// Gets the nth char from `word`.
fn get_char(word: &str, index: usize) -> char {
    word.chars().nth(index).unwrap()
}

// Convert the word to a map from charaters to the number of times that character appears.
fn construct_letter_map(word: &str) -> HashMap<char, u8> {
    let mut map = HashMap::new();
    for letter in word.chars() {
        *map.entry(letter).or_insert(0) += 1
    }
    map
}

// Compare if `left` is a subset of `right`.
fn letter_map_less_than(left: &HashMap<char, u8>, right: &HashMap<char, u8>) -> bool {
    for (letter, l_count) in left {
        match right.get(letter) {
            Some(r_count) => if l_count > r_count {
                return false;
            },
            _ => return false,
        }
    }
    true
}

// The middle letter has to be the middle letter of at least four words. Construct lists of
// possible sets of words.
fn shared_middle_letters(words: &[[char; 3]]) -> Vec<Vec<[char; 3]>> {
    let mut map = HashMap::new();
    for word in words {
        map.entry(word[1]).or_insert(Vec::new()).push(*word)
    }
    let mut out = Vec::new();
    for (_, val) in map.drain() {
        if val.len() >= 4 {
            out.push(val);
        }
    }
    out
}

// Checks that the first and last letters of `word` are still in `right`.
fn two_letter_less_than(word: &[char; 3], right: &HashMap<char, u8>) -> bool {
    for letter in [word[0], word[2]].iter() {
        match right.get(letter) {
            Some(count) => if *count <= 0 {
                return false;
            },
            _ => return false,
        }
    }
    true
}

// Checks if the word is a subset of the letters in `letter_map`. If so, it decrements the counts
// in `letter_map` by the first and third letters of the word. Ignores the middle letter since that
// is shared.
// Returns whether or not the word was a subset.
fn clear_two_letters(word: &[char; 3], letter_map: &mut HashMap<char, u8>) -> bool {
    if !two_letter_less_than(word, letter_map) {
        return false;
    }
    *letter_map.get_mut(&word[0]).unwrap() -= 1;
    *letter_map.get_mut(&word[2]).unwrap() -= 1;
    true
}

// If the word is a candidate, append it. Otherwise, do nothing. Returns whether or not the word
// was appended.
fn append_word_if<'a>(
    word: &[char; 3],
    letter_map: &mut HashMap<char, u8>,
    words: &mut Vec<[char; 3]>,
) -> bool {
    if !clear_two_letters(word, letter_map) {
        return false;
    }
    words.push(*word);
    true
}

fn four_word_product<'a>(words: &[[char; 3]], base_map: &HashMap<char, u8>) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    let middle_letter = words[0][1];
    for ii in 0..words.len() {
        let mut map = base_map.clone();
        let mut four_words = Vec::new();
        *map.get_mut(&middle_letter).unwrap() -= 1;
        if !append_word_if(&words[ii], &mut map, &mut four_words) {
            continue;
        }
        assert_eq!(four_words.len(), 1);
        for jj in ii + 1..words.len() {
            // Truncate here so that we only get one word from this pass.
            four_words.truncate(1);
            // Create a new map per iteration.
            let mut map = map.clone();
            if !append_word_if(&words[jj], &mut map, &mut four_words) {
                continue;
            }
            assert_eq!(four_words.len(), 2);
            for kk in jj + 1..words.len() {
                four_words.truncate(2);
                let mut map = map.clone();
                if !append_word_if(&words[kk], &mut map, &mut four_words) {
                    continue;
                }
                assert_eq!(four_words.len(), 3);
                for ll in kk + 1..words.len() {
                    four_words.truncate(3);
                    let mut map = map.clone();
                    if append_word_if(&words[ll], &mut map, &mut four_words) {
                        assert_eq!(four_words.len(), 4);
                        out.push(four_words.clone());
                    }
                }
            }
        }
    }
    out
}

// Find all of the combinations of four words that share a middle letter and the combined letters
// of all four words are still a subset of 'base_map'.
fn four_word_candidates<'a>(
    shared_middle: &[Vec<[char; 3]>],
    base_map: &HashMap<char, u8>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for words in shared_middle {
        out.append(&mut four_word_product(words, base_map));
    }
    out
}

// For four indices between zero and 4, return the missing index.
fn missing_index(ii: usize, jj: usize, kk: usize) -> usize {
    assert!(ii < 4);
    assert!(jj < 4);
    assert!(kk < 4);
    for ll in 0..4 {
        if ll != ii && ll != jj && ll != kk {
            return ll;
        }
    }
    unreachable!()
}

// Takes the candidates and finds words that make a proper word in the top row. The words are
// returned such that the first word is the left to right diagonal (1, 5, 9 on a phone), the second
// word is the middle vertical, the third is the right to left diagonal (3, 5, 7) and the remaining
// word is last.
fn top_row_candidates<'a>(
    four_cands: &Vec<Vec<[char; 3]>>,
    possible_words: &HashSet<[char; 3]>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for cand in four_cands {
        assert!(cand.len() == 4);
        for ii in 0..cand.len() {
            let first = cand[ii];
            for jj in 0..cand.len() {
                if jj == ii {
                    continue;
                }
                let second = cand[jj];
                for kk in 0..cand.len() {
                    if kk == jj || kk == ii {
                        continue;
                    }
                    let third = cand[kk];
                    let word = [first[0], second[0], third[0]];
                    if possible_words.contains(&word) {
                        let mut top_cand = vec![first, second, third];
                        top_cand.push(cand[missing_index(ii, jj, kk)]);
                        out.push(top_cand);
                    }
                }
            }
        }
    }
    out
}

// Filter out sets of four where the bottom generated from the top three words isn't a word.
fn filter_by_bottom<'a>(
    top_cands: Vec<Vec<[char; 3]>>,
    possible_words: &HashSet<[char; 3]>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for cand in top_cands {
        assert!(cand.len() == 4);
        let first = cand[0][2];
        let second = cand[1][2];
        let third = cand[2][2];
        let word = [first, second, third];
        if possible_words.contains(&word) {
            out.push(cand);
            continue;
        }
    }
    out
}

fn filter_by_rest<'a>(
    cands: Vec<Vec<[char; 3]>>,
    possible_words: &HashSet<[char; 3]>,
) -> Vec<Vec<[char; 3]>> {
    let mut out = Vec::new();
    for cand in cands {
        // The left vertical word is made up of the first letter of the first top word (left to
        // right diagnol), the first letter of the remaining word, and the last letter of the
        // third top word (right to left diagonal).
        let left_v_word = [cand[0][0], cand[3][0], cand[2][2]];
        if !possible_words.contains(&left_v_word) {
            continue;
        }
        // The right vertical word is from the first letter of the third top word, the third letter
        // of the remaining word and the third letter of the first top word.
        let right_v_word = [cand[2][0], cand[3][2], cand[0][2]];
        if !possible_words.contains(&right_v_word) {
            continue;
        }

        out.push(cand);
    }
    out
}

// Returns the 3x3 grid of letters in row major form.
fn presentation_format(cand: &[[char; 3]]) -> Vec<Vec<char>> {
    let mut out = Vec::new();
    // Fill in the vectors with x's just as a placeholder.
    for _ in 0..3 {
        out.push(vec!['x', 'x', 'x']);
    }
    // The first word is the left to right diagonal.
    out[0][0] = cand[0][0];
    out[1][1] = cand[0][1];
    out[2][2] = cand[0][2];
    // The second word is the middle vertical.
    out[0][1] = cand[1][0];
    out[2][1] = cand[1][2];
    // The third word is the left diagonal.
    out[0][2] = cand[2][0];
    out[2][0] = cand[2][2];
    // The last word is the middle horizontal.
    out[1][0] = cand[3][0];
    out[1][2] = cand[3][2];
    out
}

fn main() {
    let f = match File::open("dictionary.txt") {
        Ok(f) => f,
        Err(why) => panic!("Failed to open links.txt: {}", Error::description(&why)),
    };

    // Construct a vector of three letter words that are a subset of beermouth.
    let source_word = construct_letter_map("beermouth");
    let words: Vec<[char; 3]> = BufReader::new(f)
        .lines()
        .map(|s| s.unwrap())
        .filter(|s| s.len() == 3)
        .filter(|s| letter_map_less_than(&construct_letter_map(s), &source_word))
        .map(|s| [get_char(&s, 0), get_char(&s, 1), get_char(&s, 2)])
        .collect();

    // Find sets of words that share a middle letter.
    let middle_candidates = shared_middle_letters(&words);
    // Find sets of four words that share a middle letter and could make up the solution.
    let four_cands = four_word_candidates(&middle_candidates, &source_word);

    // Find possible top rows.
    let word_set = {
        let mut word_set = HashSet::with_capacity(words.len());
        for word in words.iter() {
            word_set.insert(word.clone());
        }
        word_set
    };
    let top_cands = top_row_candidates(&four_cands, &word_set);
    let bottom_cands = filter_by_bottom(top_cands, &word_set);
    let last_cands = filter_by_rest(bottom_cands, &word_set);

    for last_cand in last_cands {
        println!("Last candidate words:");
        for word in last_cand.iter() {
            let word_str: String = word.iter().collect();
            print!("{} ", word_str);
        }
        println!("\nIn grid format:");
        let present = presentation_format(&last_cand);
        for line in present {
            for ch in line {
                print!(" {} ", ch);
            }
            print!("\n");
        }
        print!("\n");
    }
}

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

    #[test]
    fn test_letter_map() {
        let mut expected = HashMap::new();
        expected.insert('b', 1);
        expected.insert('e', 2);
        expected.insert('r', 1);
        expected.insert('m', 1);
        expected.insert('o', 1);
        expected.insert('u', 1);
        expected.insert('t', 1);
        expected.insert('h', 1);
        assert_eq!(expected, construct_letter_map("beermouth"));
    }

    #[test]
    fn test_map_less_than() {
        let source_word = construct_letter_map("beermouth");
        assert!(letter_map_less_than(
            &construct_letter_map("bee"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("bum"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("but"),
            &source_word
        ));
        assert!(letter_map_less_than(
            &construct_letter_map("out"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("boo"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("oom"),
            &source_word
        ));
        assert!(!letter_map_less_than(
            &construct_letter_map("ouf"),
            &source_word
        ));
    }

    #[test]
    fn test_shared_middle() {
        let mut words = Vec::new();
        words.push(['b', 'e', 'e']);
        words.push(['b', 'e', 't']);
        words.push(['m', 'e', 't']);
        words.push(['b', 'u', 'm']);
        words.push(['b', 'u', 't']);
        words.push(['o', 'u', 't']);
        words.push(['o', 'u', 'f']);
        words.push(['b', 'o', 't']);
        words.push(['h', 'o', 't']);
        words.push(['m', 'o', 't']);
        words.push(['t', 'o', 'm']);
        words.push(['r', 'o', 't']);
        let mut expected = Vec::new();
        {
            let mut group = Vec::new();
            for ii in 3..7 {
                group.push(words[ii]);
            }
            group.sort();
            expected.push(group);
        }
        {
            let mut group = Vec::new();
            for ii in 7..12 {
                group.push(words[ii]);
            }
            group.sort();
            expected.push(group);
        }
        let mut input_words = words.clone();
        input_words.sort();
        let mut shared = shared_middle_letters(&input_words);
        // Sort by the lengths of the sub-Vecs to prevent test flakes.
        shared.sort_by(|l, r| l.len().cmp(&r.len()));
        assert_eq!(expected, shared);
    }
}
<|endoftext|>


<commit_before>extern crate peel_ip;
use peel_ip::prelude::*;

extern crate pcap;
use pcap::{Active, Capture, Device};

extern crate rain;
use rain::Graph;

extern crate time;
use time::Duration;

use std::thread::{self, JoinHandle};
use std::sync::mpsc::{channel, Sender, Receiver};

type IpIdentifier = Identifier<IpProtocol>;

#[derive(Debug)]
struct Packet {
    identifier: IpIdentifier,
    length: usize,
}

impl Packet {
    fn new(identifier: IpIdentifier, length: usize) -> Packet {
        Packet {
            identifier: identifier,
            length: length,
        }
    }
}

pub fn main() {
    start().expect("Failed in main function");
}

fn start() -> Result<(), Box<Error>> {
    let (tx, rx) = channel();

    // Worker thread for capturing packets
    let capture = Device::lookup()?.open()?;
    let peel = PeelIp::new();
    let packet_capture = start_packet_capture_thread(capture, peel, tx);

    // Worker thread for doing the computation on packets
    let mut path: Path<IpProtocol, usize> = Path::new();
    path.timeout = Duration::seconds(4);
    let graph = Graph::with_prefix_length(10 /* TODO: 100 should fit good */);
    let computation = start_graph_drawing_thread(path, graph, rx);

    // Join the threads together
    computation.join().unwrap();
    packet_capture.join().unwrap();
    Ok(())
}

fn start_packet_capture_thread(mut capture: Capture<Active>, mut peel: PeelIp, tx: Sender<Packet>) -> JoinHandle<()> {
    thread::spawn(move || {
        while let Ok(packet) = capture.next() {
            // Parse the packet
            if let Ok(res) = peel.traverse(packet.data, vec![]) {
                if let Some(identifier) = get_identifier(&res) {
                    let packet = Packet::new(identifier, packet.data.len());
                    tx.send(packet).unwrap();
                }
            }
        }
    })
}

fn start_graph_drawing_thread(mut path: Path<IpProtocol, usize>,
                              mut graph: Graph<u32>,
                              rx: Receiver<Packet>)
                              -> JoinHandle<()> {
    thread::spawn(move || {
        loop {
            thread::sleep(std::time::Duration::from_secs(2));

            // Get all pending values
            for packet in rx.try_iter() {
                // Track the connection as usual if no error happended
                match path.track(packet.identifier.clone()) {
                    Ok(connection) => {
                        connection.data.custom = Some(connection.data.custom.unwrap_or_default() + packet.length);
                        graph.add(connection.identifier,
                                 connection.data.custom.unwrap_or_default() as u32)
                            .is_ok();
                    }
                    Err(_) => {
                        graph.remove(&packet.identifier).is_ok();
                    }
                };

                // Flush connections with a timeout
                for identifier in path.flush() {
                    graph.remove(identifier).is_ok();
                }
            }

            // Print the graph
            graph.print().unwrap();
        }
    })
}

fn get_identifier(result: &Vec<Layer>) -> Option<IpIdentifier> {
    // Try to get the ports
    let ports = match result.get(2) {
        Some(&Layer::Tcp(ref tcp)) => Some((tcp.header.source_port, tcp.header.dest_port)),
        Some(&Layer::Udp(ref udp)) => Some((udp.header.source_port, udp.header.dest_port)),
        _ => None,
    };

    let identifier = match (result.get(1), ports) {
        (Some(&Layer::Ipv4(ref p)), Some((src_port, dst_port))) => {
            Some(Identifier::new(IpAddr::V4(p.src),
                                 src_port,
                                 IpAddr::V4(p.dst),
                                 dst_port,
                                 p.protocol))
        }
        (Some(&Layer::Ipv6(ref p)), Some((src_port, dst_port))) => {
            Some(Identifier::new(IpAddr::V6(p.src),
                                 src_port,
                                 IpAddr::V6(p.dst),
                                 dst_port,
                                 p.next_header))
        }
        // Previous result found, but not the correct one
        _ => None,
    };

    identifier
}
<commit_msg>Removed demo example<commit_after><|endoftext|>


<commit_before>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn compile_fail(sysroot: &str) {
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = "compile-fail".parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from("tests/compile-fail".to_string());
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn run_pass() {
    let mut config = compiletest::default_config();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.src_base = PathBuf::from("tests/run-pass".to_string());
    config.target_rustcflags = Some("-Dwarnings".to_string());
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn miri_pass(path: &str, target: &str, host: &str) {
    let mut config = compiletest::default_config();
    config.mode = "mir-opt".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.rustc_path = PathBuf::from("target/debug/miri");
    // don't actually execute the final binary, it might be for other targets and we only care
    // about running miri, not the binary.
    config.runtool = Some("echo \"\" || ".to_owned());
    if target == host {
        std::env::set_var("MIRI_HOST_TARGET", "yes");
    }
    compiletest::run_tests(&config);
    std::env::set_var("MIRI_HOST_TARGET", "");
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &str, mut f: F) {
    for entry in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) { continue; }
        let target = entry.file_name().into_string().unwrap();
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    run_pass();
    let host = Path::new(&sysroot).file_name()
                                  .expect("sysroot has no last par")
                                  .to_str()
                                  .expect("sysroot contains non utf8")
                                  .splitn(2, '-')
                                  .skip(1)
                                  .next()
                                  .expect("target dir not prefixed");
    for_all_targets(&sysroot, |target| {
        miri_pass("tests/run-pass", &target, host);
        if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") {
            miri_pass(&path, &target, host);
        }
    });
    compile_fail(&sysroot);
}
<commit_msg>clamp down on hacks in compiletest<commit_after>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn compile_fail(sysroot: &Path) {
    let flags = format!("--sysroot {} -Dwarnings", sysroot.to_str().expect("non utf8 path"));
    for_all_targets(&sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = "compile-fail".parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from("tests/compile-fail".to_string());
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn run_pass() {
    let mut config = compiletest::default_config();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.src_base = PathBuf::from("tests/run-pass".to_string());
    config.target_rustcflags = Some("-Dwarnings".to_string());
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn miri_pass(path: &str, target: &str, host: &str) {
    let mut config = compiletest::default_config();
    config.mode = "mir-opt".parse().expect("Invalid mode");
    config.src_base = PathBuf::from(path);
    config.target = target.to_owned();
    config.rustc_path = PathBuf::from("target/debug/miri");
    // don't actually execute the final binary, it might be for other targets and we only care
    // about running miri, not the binary.
    config.runtool = Some("echo \"\" || ".to_owned());
    if target == host {
        std::env::set_var("MIRI_HOST_TARGET", "yes");
    }
    compiletest::run_tests(&config);
    std::env::set_var("MIRI_HOST_TARGET", "");
}

fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
    let mut path = path.into();
    path.push("lib");
    path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

fn for_all_targets<F: FnMut(String)>(sysroot: &Path, mut f: F) {
    let target_dir = sysroot.join("lib").join("rustlib");
    println!("target dir: {}", target_dir.to_str().unwrap());
    for entry in std::fs::read_dir(target_dir).expect("invalid sysroot") {
        let entry = entry.unwrap();
        if !is_target_dir(entry.path()) { continue; }
        let target = entry.file_name().into_string().unwrap();
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    let sysroot = std::process::Command::new("rustc")
        .arg("--print")
        .arg("sysroot")
        .output()
        .expect("rustc not found")
        .stdout;
    let sysroot = std::str::from_utf8(&sysroot).expect("sysroot is not utf8").trim();
    let sysroot = &Path::new(&sysroot);
    let host = std::process::Command::new("rustc")
        .arg("-vV")
        .output()
        .expect("rustc not found for -vV")
        .stdout;
    let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
    let host = host.split("\nhost: ").skip(1).next().expect("no host: part in rustc -vV");
    let host = host.split("\n").next().expect("no \n after host");
    run_pass();
    for_all_targets(&sysroot, |target| {
        miri_pass("tests/run-pass", &target, host);
        if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") {
            miri_pass(&path, &target, host);
        }
    });
    compile_fail(&sysroot);
}
<|endoftext|>


<commit_before>use rules::re::Regex;

#[test]
fn chars() {
    let re = Regex::new(r"Apple");
    assert!( re.is_match("I have an Apple. Do you?"));
    assert!(!re.is_match("I have an Pear. Do you?"));
}
#[test]
fn whitespace_escapes() {
    let re = Regex::new(r"ping\ pong");
    assert!( re.is_match("Do you like ping pong? I do."));
    assert!(!re.is_match("Do you like golf? I do."));
}
#[test]
fn literals_ticks() {
    let re = Regex::new(r"'two apples'");
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_quotes() {
    let re = Regex::new(r#""two apples""#);
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_regular() {
    let re = Regex::new(r#"'2' and "3" and "4" and '5'"#);
    assert!( re.is_match("Well, 2and3and4and5 there."));
    assert!(!re.is_match("Well, 5and4and3and2 there."));
}
#[test]
fn char_class_range() {
    let re = Regex::new(r"<[a .. z]>");
    assert!( re.is_match("a"));
    assert!( re.is_match("p"));
    assert!( re.is_match("z"));
    assert!(!re.is_match("0"));
    assert!(!re.is_match("9"));
    assert!(!re.is_match("こ"));
}

#[test]
fn char_class_multi() {
    let re = Regex::new(r"<[ 0 .. 9 ]> <[ a .. z ]>");
    assert!( re.is_match("0z"));
    assert!( re.is_match("3b"));
    assert!( re.is_match("9h"));
    assert!(!re.is_match("a3"));
    assert!(!re.is_match("3A"));
    assert!(!re.is_match("こ"));

    let re = Regex::new(r"<[ 0 .. 9 ]> ' dogs'");
    assert!( re.is_match("0 dogs"));
    assert!( re.is_match("4 dogz and 3 dogs"));
    assert!( re.is_match("9 dogs more"));
    assert!( re.is_match("4 cats and 7 dogs and 2 pigs"));

    assert!(!re.is_match("五 dogs")); // Japanese number 5
    assert!(!re.is_match("b dogs"));
    assert!(!re.is_match("A dogs"));
    assert!(!re.is_match("0 dogz"));
    assert!(!re.is_match("4 cats and 3 dogz"));
    assert!(!re.is_match("9 dogz and 3 cats"));
    assert!(!re.is_match("4 cats and 7 dogz and 2 pigs"));
}
<commit_msg>Update whitespace in tests<commit_after>use rules::re::Regex;

#[test]
fn chars() {
    let re = Regex::new(r"Apple");
    assert!( re.is_match("I have an Apple. Do you?"));
    assert!(!re.is_match("I have an Pear. Do you?"));
}
#[test]
fn whitespace_escapes() {
    let re = Regex::new(r"ping\ pong");
    assert!( re.is_match("Do you like ping pong? I do."));
    assert!(!re.is_match("Do you like golf? I do."));
}
#[test]
fn literals_ticks() {
    let re = Regex::new(r" 'two apples' ");
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_quotes() {
    let re = Regex::new(r#" "two apples" "#);
    assert!( re.is_match("Are there two apples? Really?"));
    assert!(!re.is_match("Are there two grapes? Really?"));
}
#[test]
fn literals_regular() {
    let re = Regex::new(r#" '2' and "3" and "4" and '5' "#);
    assert!( re.is_match("Well, 2and3and4and5 there."));
    assert!(!re.is_match("Well, 5and4and3and2 there."));
}
#[test]
fn char_class_range() {
    let re = Regex::new(r"<[ a .. z ]>");
    assert!( re.is_match("a"));
    assert!( re.is_match("p"));
    assert!( re.is_match("z"));
    assert!(!re.is_match("0"));
    assert!(!re.is_match("9"));
    assert!(!re.is_match("こ"));
}
#[test]
fn char_class_multi() {
    let re = Regex::new(r"<[ 0 .. 9 ]> <[ a .. z ]>");
    assert!( re.is_match("0z"));
    assert!( re.is_match("3b"));
    assert!( re.is_match("9h"));
    assert!(!re.is_match("a3"));
    assert!(!re.is_match("3A"));
    assert!(!re.is_match("こ"));

    let re = Regex::new(r"<[ 0 .. 9 ]> ' dogs'");
    assert!( re.is_match("0 dogs"));
    assert!( re.is_match("4 dogz and 3 dogs"));
    assert!( re.is_match("9 dogs more"));
    assert!( re.is_match("4 cats and 7 dogs and 2 pigs"));

    assert!(!re.is_match("五 dogs")); // Japanese number 5
    assert!(!re.is_match("b dogs"));
    assert!(!re.is_match("A dogs"));
    assert!(!re.is_match("0 dogz"));
    assert!(!re.is_match("4 cats and 3 dogz"));
    assert!(!re.is_match("9 dogz and 3 cats"));
    assert!(!re.is_match("4 cats and 7 dogz and 2 pigs"));
}
<|endoftext|>