<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::ffi;
use std::mem;
use std::os::raw;
use std::ptr;
use std::slice;

use image::Pixel;
use image::RgbaImage;
use image::Rgba;
use libc;
use x11::xlib;
use x11::xrandr;

use util;

pub const ALL_PLANES: libc::c_ulong = !0;

pub struct Display {
    handle: *mut xlib::Display,
}

pub struct Image {
    handle: *mut xlib::XImage,
}

pub struct ScreenRectIter<'a> {
    dpy: &'a Display,
    res: *mut xrandr::XRRScreenResources,
    crtcs: &'a [xrandr::RRCrtc],
    i: usize,
}

impl Display {
    pub fn open(name: Option<ffi::CString>) -> Option<Display> {
        unsafe {
            let name = match name {
                None => ptr::null(),
                Some(cstr) => cstr.as_ptr(),
            };
            let d = xlib::XOpenDisplay(name);

            if d.is_null() {
                return None;
            }

            Some(Display {
                handle: d,
            })
        }
    }

    pub fn get_default_root(&self) -> xlib::Window {
        unsafe {
            xlib::XDefaultRootWindow(self.handle)
        }
    }

    pub fn get_window_rect(&self, window: xlib::Window) -> util::Rect {
        unsafe {
            let mut attrs = mem::uninitialized();
            xlib::XGetWindowAttributes(self.handle, window, &mut attrs);

            let mut root = 0;
            let mut parent = 0;
            let mut children: *mut xlib::Window = ptr::null_mut();
            let mut nchildren = 0;
            xlib::XQueryTree(self.handle, window, &mut root, &mut parent,
                             &mut children, &mut nchildren);
            if !children.is_null() {
                xlib::XFree(children as *mut raw::c_void);
            }

            let mut x = attrs.x;
            let mut y = attrs.y;

            if parent != 0 {
                let mut child = 0;
                xlib::XTranslateCoordinates(self.handle, parent, root, attrs.x, attrs.y,
                                            &mut x, &mut y, &mut child);
            }

            util::Rect {
                x: x,
                y: y,
                w: attrs.width,
                h: attrs.height,
            }
        }
    }

    pub fn get_image(&self, window: xlib::Window, rect: util::Rect, plane_mask: libc::c_ulong,
                     format: libc::c_int) -> Option<Image> {
        unsafe {
            let image = xlib::XGetImage(self.handle, window,
                                        rect.x, rect.y,
                                        rect.w as libc::c_uint, rect.h as libc::c_uint,
                                        plane_mask, format);

            if image.is_null() {
                return None;
            }

            Some(Image::from_raw_ximage(image))
        }
    }

    pub fn get_screen_rects(&self, root: xlib::Window) -> Option<ScreenRectIter> {
        unsafe {
            let xrr_res = xrandr::XRRGetScreenResourcesCurrent(self.handle, root);

            if xrr_res.is_null() {
                return None
            }

            Some(ScreenRectIter {
                dpy: &self,
                res: xrr_res,
                crtcs: slice::from_raw_parts((*xrr_res).crtcs, (*xrr_res).ncrtc as usize),
                i: 0,
            })
        }
    }
}

impl Drop for Display {
    fn drop(&mut self) {
        unsafe {
            xlib::XCloseDisplay(self.handle);
        }
    }
}

impl Image {
    pub fn from_raw_ximage(ximage: *mut xlib::XImage) -> Image {
        Image {
            handle: ximage,
        }
    }

    pub fn into_image_buffer(&self) -> Option<RgbaImage> {
        unsafe {
            // Extract values from the XImage into our own scope
            macro_rules! get {
                ($($a:ident),+) => ($(let $a = (*self.handle).$a;)+);
            }
            get!(width, height,
                 byte_order, depth, bytes_per_line, bits_per_pixel,
                 red_mask, green_mask, blue_mask);

            // Pixel size
            let stride = match (depth, bits_per_pixel) {
                (24, 24) => 3,
                (24, 32) | (32, 32) => 4,
                _ => return None,
            };

            // Compute subpixel offsets into each pixel according the the bitmasks X gives us
            // Only 8 bit, byte-aligned values are supported
            // Truncate masks to the lower 32 bits as that is the maximum pixel size
            macro_rules! channel_offset {
                ($mask:expr) => (match (byte_order, $mask & 0xFFFFFFFF) {
                    (0, 0xFF) | (1, 0xFF000000) => 0,
                    (0, 0xFF00) | (1, 0xFF0000) => 1,
                    (0, 0xFF0000) | (1, 0xFF00) => 2,
                    (0, 0xFF000000) | (1, 0xFF) => 3,
                    _ => return None,
                })
            }
            let red_offset = channel_offset!(red_mask);
            let green_offset = channel_offset!(green_mask);
            let blue_offset = channel_offset!(blue_mask);
            let alpha_offset = channel_offset!(!(red_mask | green_mask | blue_mask));

            // Wrap the pixel buffer into a slice
            let size = (bytes_per_line * height) as usize;
            let data = slice::from_raw_parts((*self.handle).data as *const u8, size);

            // Finally, generate the image object
            Some(RgbaImage::from_fn(width as u32, height as u32, |x, y| {
                macro_rules! subpixel {
                    ($channel_offset:ident) => (data[(y * bytes_per_line as u32
                                                + x * stride as u32
                                                + $channel_offset) as usize]);
                }
                Rgba::from_channels(subpixel!(red_offset),
                                    subpixel!(green_offset),
                                    subpixel!(blue_offset),
                                    // Make the alpha channel fully opaque if none is provided
                                    if depth == 24 { 0xFF } else { subpixel!(alpha_offset) })
            }))
        }
    }
}

impl Drop for Image {
    fn drop(&mut self) {
        unsafe {
            xlib::XDestroyImage(self.handle);
        }
    }
}

impl<'a> Iterator for ScreenRectIter<'a> {
    type Item = util::Rect;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.crtcs.len() {
            return None
        }

        unsafe {
            // TODO Handle failure here?
            let crtc = xrandr::XRRGetCrtcInfo((*self.dpy).handle, self.res, self.crtcs[self.i]);
            let x = (*crtc).x;
            let y = (*crtc).y;
            let w = (*crtc).width;
            let h = (*crtc).height;
            xrandr::XRRFreeCrtcInfo(crtc);

            self.i += 1;

            //Some((w as i32, h as i32, x as i32, y as i32))
            Some(util::Rect {
                x: x,
                y: y,
                w: w as i32,
                h: h as i32,
            })
        }
    }
}

impl<'a> Drop for ScreenRectIter<'a> {
    fn drop(&mut self) {
        unsafe {
            xrandr::XRRFreeScreenResources(self.res);
        }
    }
}

pub fn parse_geometry(g: ffi::CString) -> util::Rect {
    unsafe {
        let mut x = 0;
        let mut y = 0;
        let mut w = 0;
        let mut h = 0;
        xlib::XParseGeometry(g.as_ptr() as *const raw::c_char, &mut x, &mut y, &mut w, &mut h);

        util::Rect {
            x: x,
            y: y,
            w: w as i32,
            h: h as i32,
        }
    }
}
<commit_msg>Use std::mem::MaybeUninit<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::ffi;
use std::mem;
use std::os::raw;
use std::ptr;
use std::slice;

use image::Pixel;
use image::RgbaImage;
use image::Rgba;
use libc;
use x11::xlib;
use x11::xrandr;

use util;

pub const ALL_PLANES: libc::c_ulong = !0;

pub struct Display {
    handle: *mut xlib::Display,
}

pub struct Image {
    handle: *mut xlib::XImage,
}

pub struct ScreenRectIter<'a> {
    dpy: &'a Display,
    res: *mut xrandr::XRRScreenResources,
    crtcs: &'a [xrandr::RRCrtc],
    i: usize,
}

impl Display {
    pub fn open(name: Option<ffi::CString>) -> Option<Display> {
        unsafe {
            let name = match name {
                None => ptr::null(),
                Some(cstr) => cstr.as_ptr(),
            };
            let d = xlib::XOpenDisplay(name);

            if d.is_null() {
                return None;
            }

            Some(Display {
                handle: d,
            })
        }
    }

    pub fn get_default_root(&self) -> xlib::Window {
        unsafe {
            xlib::XDefaultRootWindow(self.handle)
        }
    }

    pub fn get_window_rect(&self, window: xlib::Window) -> util::Rect {
        unsafe {
            let mut attrs = mem::MaybeUninit::uninit();
            xlib::XGetWindowAttributes(self.handle, window, attrs.as_mut_ptr());
            let attrs = attrs.assume_init();

            let mut root = 0;
            let mut parent = 0;
            let mut children: *mut xlib::Window = ptr::null_mut();
            let mut nchildren = 0;
            xlib::XQueryTree(self.handle, window, &mut root, &mut parent,
                             &mut children, &mut nchildren);
            if !children.is_null() {
                xlib::XFree(children as *mut raw::c_void);
            }

            let mut x = attrs.x;
            let mut y = attrs.y;

            if parent != 0 {
                let mut child = 0;
                xlib::XTranslateCoordinates(self.handle, parent, root, attrs.x, attrs.y,
                                            &mut x, &mut y, &mut child);
            }

            util::Rect {
                x: x,
                y: y,
                w: attrs.width,
                h: attrs.height,
            }
        }
    }

    pub fn get_image(&self, window: xlib::Window, rect: util::Rect, plane_mask: libc::c_ulong,
                     format: libc::c_int) -> Option<Image> {
        unsafe {
            let image = xlib::XGetImage(self.handle, window,
                                        rect.x, rect.y,
                                        rect.w as libc::c_uint, rect.h as libc::c_uint,
                                        plane_mask, format);

            if image.is_null() {
                return None;
            }

            Some(Image::from_raw_ximage(image))
        }
    }

    pub fn get_screen_rects(&self, root: xlib::Window) -> Option<ScreenRectIter> {
        unsafe {
            let xrr_res = xrandr::XRRGetScreenResourcesCurrent(self.handle, root);

            if xrr_res.is_null() {
                return None
            }

            Some(ScreenRectIter {
                dpy: &self,
                res: xrr_res,
                crtcs: slice::from_raw_parts((*xrr_res).crtcs, (*xrr_res).ncrtc as usize),
                i: 0,
            })
        }
    }
}

impl Drop for Display {
    fn drop(&mut self) {
        unsafe {
            xlib::XCloseDisplay(self.handle);
        }
    }
}

impl Image {
    pub fn from_raw_ximage(ximage: *mut xlib::XImage) -> Image {
        Image {
            handle: ximage,
        }
    }

    pub fn into_image_buffer(&self) -> Option<RgbaImage> {
        unsafe {
            // Extract values from the XImage into our own scope
            macro_rules! get {
                ($($a:ident),+) => ($(let $a = (*self.handle).$a;)+);
            }
            get!(width, height,
                 byte_order, depth, bytes_per_line, bits_per_pixel,
                 red_mask, green_mask, blue_mask);

            // Pixel size
            let stride = match (depth, bits_per_pixel) {
                (24, 24) => 3,
                (24, 32) | (32, 32) => 4,
                _ => return None,
            };

            // Compute subpixel offsets into each pixel according the the bitmasks X gives us
            // Only 8 bit, byte-aligned values are supported
            // Truncate masks to the lower 32 bits as that is the maximum pixel size
            macro_rules! channel_offset {
                ($mask:expr) => (match (byte_order, $mask & 0xFFFFFFFF) {
                    (0, 0xFF) | (1, 0xFF000000) => 0,
                    (0, 0xFF00) | (1, 0xFF0000) => 1,
                    (0, 0xFF0000) | (1, 0xFF00) => 2,
                    (0, 0xFF000000) | (1, 0xFF) => 3,
                    _ => return None,
                })
            }
            let red_offset = channel_offset!(red_mask);
            let green_offset = channel_offset!(green_mask);
            let blue_offset = channel_offset!(blue_mask);
            let alpha_offset = channel_offset!(!(red_mask | green_mask | blue_mask));

            // Wrap the pixel buffer into a slice
            let size = (bytes_per_line * height) as usize;
            let data = slice::from_raw_parts((*self.handle).data as *const u8, size);

            // Finally, generate the image object
            Some(RgbaImage::from_fn(width as u32, height as u32, |x, y| {
                macro_rules! subpixel {
                    ($channel_offset:ident) => (data[(y * bytes_per_line as u32
                                                + x * stride as u32
                                                + $channel_offset) as usize]);
                }
                Rgba::from_channels(subpixel!(red_offset),
                                    subpixel!(green_offset),
                                    subpixel!(blue_offset),
                                    // Make the alpha channel fully opaque if none is provided
                                    if depth == 24 { 0xFF } else { subpixel!(alpha_offset) })
            }))
        }
    }
}

impl Drop for Image {
    fn drop(&mut self) {
        unsafe {
            xlib::XDestroyImage(self.handle);
        }
    }
}

impl<'a> Iterator for ScreenRectIter<'a> {
    type Item = util::Rect;

    fn next(&mut self) -> Option<Self::Item> {
        if self.i >= self.crtcs.len() {
            return None
        }

        unsafe {
            // TODO Handle failure here?
            let crtc = xrandr::XRRGetCrtcInfo((*self.dpy).handle, self.res, self.crtcs[self.i]);
            let x = (*crtc).x;
            let y = (*crtc).y;
            let w = (*crtc).width;
            let h = (*crtc).height;
            xrandr::XRRFreeCrtcInfo(crtc);

            self.i += 1;

            //Some((w as i32, h as i32, x as i32, y as i32))
            Some(util::Rect {
                x: x,
                y: y,
                w: w as i32,
                h: h as i32,
            })
        }
    }
}

impl<'a> Drop for ScreenRectIter<'a> {
    fn drop(&mut self) {
        unsafe {
            xrandr::XRRFreeScreenResources(self.res);
        }
    }
}

pub fn parse_geometry(g: ffi::CString) -> util::Rect {
    unsafe {
        let mut x = 0;
        let mut y = 0;
        let mut w = 0;
        let mut h = 0;
        xlib::XParseGeometry(g.as_ptr() as *const raw::c_char, &mut x, &mut y, &mut w, &mut h);

        util::Rect {
            x: x,
            y: y,
            w: w as i32,
            h: h as i32,
        }
    }
}
<|endoftext|>


<commit_before>
use std::fmt::Debug;

use itertools::zip;

use {Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, IxDyn, Dim, Dimension, IntoDimension};
use {zipsl, ZipExt};
use super::{stride_offset};
use super::DimPrivate;

/// Tuple or fixed size arrays that can be used to index an array.
///
/// ```
/// use ndarray::arr2;
///
/// let mut a = arr2(&[[0, 1], [0, 0]]);
/// a[[1, 1]] = 1;
/// assert_eq!(a[[0, 1]], 1);
/// assert_eq!(a[[1, 1]], 1);
/// ```
///
/// **Note** that `NdIndex` is implemented for all `D where D: Dimension`.
pub unsafe trait NdIndex<E> : Debug {
    #[doc(hidden)]
    fn index_checked(&self, dim: &E, strides: &E) -> Option<isize>;
    fn index_unchecked(&self, strides: &E) -> isize;
}

unsafe impl<D> NdIndex<D> for D
    where D: Dimension
{
    fn index_checked(&self, dim: &D, strides: &D) -> Option<isize> {
        dim.stride_offset_checked(strides, self)
    }
    fn index_unchecked(&self, strides: &D) -> isize {
        D::stride_offset(self, strides)
    }
}

unsafe impl NdIndex<Ix0> for () {
    #[inline]
    fn index_checked(&self, dim: &Ix0, strides: &Ix0) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix0())
    }
    #[inline(always)]
    fn index_unchecked(&self, _strides: &Ix0) -> isize {
        0
    }
}

unsafe impl NdIndex<Ix1> for Ix {
    #[inline]
    fn index_checked(&self, dim: &Ix1, strides: &Ix1) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix1(*self))
    }
    #[inline(always)]
    fn index_unchecked(&self, strides: &Ix1) -> isize {
        stride_offset(*self, get!(strides, 0))
    }
}

unsafe impl NdIndex<Ix2> for (Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix2(self.0, self.1))
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix2) -> isize {
        stride_offset(self.0, get!(strides, 0)) +
        stride_offset(self.1, get!(strides, 1))
    }
}
unsafe impl NdIndex<Ix3> for (Ix, Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option<isize> {
        dim.stride_offset_checked(strides, &self.into_dimension())
    }

    #[inline]
    fn index_unchecked(&self, strides: &Ix3) -> isize {
        stride_offset(self.0, get!(strides, 0)) +
        stride_offset(self.1, get!(strides, 1)) +
        stride_offset(self.2, get!(strides, 2))
    }
}

unsafe impl NdIndex<Ix4> for (Ix, Ix, Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix4, strides: &Ix4) -> Option<isize> {
        dim.stride_offset_checked(strides, &self.into_dimension())
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix4) -> isize {
        zip(strides.ix(), self.into_dimension().ix()).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}
unsafe impl NdIndex<Ix5> for (Ix, Ix, Ix, Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix5, strides: &Ix5) -> Option<isize> {
        dim.stride_offset_checked(strides, &self.into_dimension())
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix5) -> isize {
        zip(strides.ix(), self.into_dimension().ix()).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}

unsafe impl NdIndex<Ix2> for [Ix; 2] {
    #[inline]
    fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix2(self[0], self[1]))
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix2) -> isize {
        stride_offset(self[0], get!(strides, 0)) +
        stride_offset(self[1], get!(strides, 1))
    }
}

unsafe impl NdIndex<Ix3> for [Ix; 3] {
    #[inline]
    fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix3(self[0], self[1], self[2]))
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix3) -> isize {
        stride_offset(self[0], get!(strides, 0)) +
        stride_offset(self[1], get!(strides, 1)) +
        stride_offset(self[2], get!(strides, 2))
    }
}

impl<'a> IntoDimension for &'a [Ix] {
    type Dim = Dim<Vec<Ix>>;
    fn into_dimension(self) -> Self::Dim {
        Dim(self.to_vec())
    }
}

unsafe impl<'a> NdIndex<IxDyn> for &'a [Ix] {
    fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option<isize> {
        let mut offset = 0;
        for (&d, &i, &s) in zipsl(&dim[..], &self[..]).zip_cons(strides.slice()) {
            if i >= d {
                return None;
            }
            offset += stride_offset(i, s);
        }
        Some(offset)
    }
    fn index_unchecked(&self, strides: &IxDyn) -> isize {
        zip(strides.ix(), *self).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}

unsafe impl NdIndex<IxDyn> for Vec<Ix> {
    fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option<isize> {
        let mut offset = 0;
        for (&d, &i, &s) in zipsl(&dim[..], &self[..]).zip_cons(strides.slice()) {
            if i >= d {
                return None;
            }
            offset += stride_offset(i, s);
        }
        Some(offset)
    }
    fn index_unchecked(&self, strides: &IxDyn) -> isize {
        zip(strides.ix(), self).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}
<commit_msg>Edit example for ndindex<commit_after>
use std::fmt::Debug;

use itertools::zip;

use {Ix, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, IxDyn, Dim, Dimension, IntoDimension};
use {zipsl, ZipExt};
use super::{stride_offset};
use super::DimPrivate;

/// Tuple or fixed size arrays that can be used to index an array.
///
/// ```
/// use ndarray::arr2;
///
/// let mut a = arr2(&[[0, 1],
///                    [2, 3]]);
/// assert_eq!(a[[0, 1]], 1);
/// assert_eq!(a[[1, 1]], 3);
/// a[[1, 1]] += 1;
/// assert_eq!(a[(1, 1)], 4);
/// ```
///
/// **Note** that `NdIndex` is implemented for all `D where D: Dimension`.
pub unsafe trait NdIndex<E> : Debug {
    #[doc(hidden)]
    fn index_checked(&self, dim: &E, strides: &E) -> Option<isize>;
    fn index_unchecked(&self, strides: &E) -> isize;
}

unsafe impl<D> NdIndex<D> for D
    where D: Dimension
{
    fn index_checked(&self, dim: &D, strides: &D) -> Option<isize> {
        dim.stride_offset_checked(strides, self)
    }
    fn index_unchecked(&self, strides: &D) -> isize {
        D::stride_offset(self, strides)
    }
}

unsafe impl NdIndex<Ix0> for () {
    #[inline]
    fn index_checked(&self, dim: &Ix0, strides: &Ix0) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix0())
    }
    #[inline(always)]
    fn index_unchecked(&self, _strides: &Ix0) -> isize {
        0
    }
}

unsafe impl NdIndex<Ix1> for Ix {
    #[inline]
    fn index_checked(&self, dim: &Ix1, strides: &Ix1) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix1(*self))
    }
    #[inline(always)]
    fn index_unchecked(&self, strides: &Ix1) -> isize {
        stride_offset(*self, get!(strides, 0))
    }
}

unsafe impl NdIndex<Ix2> for (Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix2(self.0, self.1))
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix2) -> isize {
        stride_offset(self.0, get!(strides, 0)) +
        stride_offset(self.1, get!(strides, 1))
    }
}
unsafe impl NdIndex<Ix3> for (Ix, Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option<isize> {
        dim.stride_offset_checked(strides, &self.into_dimension())
    }

    #[inline]
    fn index_unchecked(&self, strides: &Ix3) -> isize {
        stride_offset(self.0, get!(strides, 0)) +
        stride_offset(self.1, get!(strides, 1)) +
        stride_offset(self.2, get!(strides, 2))
    }
}

unsafe impl NdIndex<Ix4> for (Ix, Ix, Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix4, strides: &Ix4) -> Option<isize> {
        dim.stride_offset_checked(strides, &self.into_dimension())
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix4) -> isize {
        zip(strides.ix(), self.into_dimension().ix()).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}
unsafe impl NdIndex<Ix5> for (Ix, Ix, Ix, Ix, Ix) {
    #[inline]
    fn index_checked(&self, dim: &Ix5, strides: &Ix5) -> Option<isize> {
        dim.stride_offset_checked(strides, &self.into_dimension())
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix5) -> isize {
        zip(strides.ix(), self.into_dimension().ix()).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}

unsafe impl NdIndex<Ix2> for [Ix; 2] {
    #[inline]
    fn index_checked(&self, dim: &Ix2, strides: &Ix2) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix2(self[0], self[1]))
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix2) -> isize {
        stride_offset(self[0], get!(strides, 0)) +
        stride_offset(self[1], get!(strides, 1))
    }
}

unsafe impl NdIndex<Ix3> for [Ix; 3] {
    #[inline]
    fn index_checked(&self, dim: &Ix3, strides: &Ix3) -> Option<isize> {
        dim.stride_offset_checked(strides, &Ix3(self[0], self[1], self[2]))
    }
    #[inline]
    fn index_unchecked(&self, strides: &Ix3) -> isize {
        stride_offset(self[0], get!(strides, 0)) +
        stride_offset(self[1], get!(strides, 1)) +
        stride_offset(self[2], get!(strides, 2))
    }
}

impl<'a> IntoDimension for &'a [Ix] {
    type Dim = Dim<Vec<Ix>>;
    fn into_dimension(self) -> Self::Dim {
        Dim(self.to_vec())
    }
}

unsafe impl<'a> NdIndex<IxDyn> for &'a [Ix] {
    fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option<isize> {
        let mut offset = 0;
        for (&d, &i, &s) in zipsl(&dim[..], &self[..]).zip_cons(strides.slice()) {
            if i >= d {
                return None;
            }
            offset += stride_offset(i, s);
        }
        Some(offset)
    }
    fn index_unchecked(&self, strides: &IxDyn) -> isize {
        zip(strides.ix(), *self).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}

unsafe impl NdIndex<IxDyn> for Vec<Ix> {
    fn index_checked(&self, dim: &IxDyn, strides: &IxDyn) -> Option<isize> {
        let mut offset = 0;
        for (&d, &i, &s) in zipsl(&dim[..], &self[..]).zip_cons(strides.slice()) {
            if i >= d {
                return None;
            }
            offset += stride_offset(i, s);
        }
        Some(offset)
    }
    fn index_unchecked(&self, strides: &IxDyn) -> isize {
        zip(strides.ix(), self).map(|(&s, &i)| stride_offset(i, s)).sum()
    }
}
<|endoftext|>


<commit_before>//! Utility functions for `hyper` crate
//!
//! Creates a few utility function to create ResponseJSON from Hyper Response
//!
//! Also implements `Into<Result<ResponseJSON, GenericError>>` for `Result<hyper::client::response::Response>` and `hyper::client::response::Response` to allow simple chaining
//!

// =======================================================================
// LIBRARY IMPORTS
// =======================================================================
use std::str::FromStr;

use hyper::client::Client;
use hyper::method::Method;
use hyper::Url;

use error::GenericError;
use types::ResponseJSON;

// =======================================================================
// PUBLIC FUNCTIONS
// =======================================================================
/// Send a simple `method` request to `url` and pre-process the response to try to build a `ResponseJSON` from it
pub fn request(method: &str, url: &str) -> Result<ResponseJSON, GenericError> {
    let hyper_method = Method::from_str(method.to_uppercase().as_str());
    let hyper_url = Url::parse(url);

    if hyper_method.is_err() {
        return Err(GenericError::Hyper(hyper_method.unwrap_err()));
    }
    if hyper_url.is_err() {
        return Err(GenericError::Hyper(hyper_method.unwrap_err()));
    }

    Client::new().request(hyper_method.unwrap(), hyper_url.unwrap()).send()
    .map_err(|hyper_error| GenericError::Hyper(hyper_error))
    .and_then(|response| ResponseJSON::from_reader(response))
}

// =======================================================================
// TRAIT IMPLEMENTATION
// =======================================================================

// =======================================================================
// UNIT TESTS
// =======================================================================
#[cfg(test)]
mod tests {
    #![allow(non_snake_case)]

    use types::ResponseJSON;

    use super::request;

    #[test]
    fn macros_test_hyper_request() {
        let rjson : Result<ResponseJSON,_> = request("Get", "http://localhost:9876/test/back/hello");
        println!("Response = {:?}", rjson);
    }
}<commit_msg>Removed test (requires a running backend)<commit_after>//! Utility functions for `hyper` crate
//!
//! Creates a few utility function to create ResponseJSON from Hyper Response.
//!
//! Also implements `Into<Result<ResponseJSON, GenericError>>` for `Result<hyper::client::response::Response>` and `hyper::client::response::Response` to allow simple chaining
//!

// =======================================================================
// LIBRARY IMPORTS
// =======================================================================
use std::str::FromStr;

use hyper::client::Client;
use hyper::method::Method;
use hyper::Url;

use error::GenericError;
use types::ResponseJSON;

// =======================================================================
// PUBLIC FUNCTIONS
// =======================================================================
/// Send a simple `method` request to `url` and pre-process the response to try to build a `ResponseJSON` from it
pub fn request(method: &str, url: &str) -> Result<ResponseJSON, GenericError> {
    let hyper_method = Method::from_str(method.to_uppercase().as_str());
    let hyper_url = Url::parse(url);

    if hyper_method.is_err() {
        return Err(GenericError::Hyper(hyper_method.unwrap_err()));
    }
    if hyper_url.is_err() {
        return Err(GenericError::Hyper(hyper_method.unwrap_err()));
    }

    Client::new().request(hyper_method.unwrap(), hyper_url.unwrap()).send()
    .map_err(|hyper_error| GenericError::Hyper(hyper_error))
    .and_then(|response| ResponseJSON::from_reader(response))
}<|endoftext|>


<commit_before>// Copyright (C) 2020 Jason Ish
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

pub fn parse_query_string(input: &str) -> (Option<String>, String, &str) {
    let (rem, token) = next_token(input);
    if !rem.is_empty() && rem.starts_with(':') {
        let (rem, mut val) = next_token(&rem[1..]);
        if val.starts_with('"') && val.ends_with('"') {
            val = val[1..val.len() - 1].to_string();
        }
        return (Some(token), val, rem);
    }
    (None, token, rem)
}

fn next_token(input: &str) -> (&str, String) {
    let mut output = Vec::new();
    let mut escaped = false;
    let mut end = 0;
    let mut inquotes = false;
    let mut trim_terminator = true;
    for (i, c) in input.chars().enumerate() {
        end = i;
        if c == '\\' {
            // Disable escaping for now, not sure if its needed.
            //escaped = true;
        } else if escaped {
            if c == ';' {
                output.push(c);
            } else {
                output.push('\\');
                output.push(c);
            }
            escaped = false;
        } else if c == ' ' {
            if !inquotes {
                break;
            } else {
                output.push(c);
            }
        } else if c == '"' {
            if inquotes {
                inquotes = false;
            } else {
                inquotes = true;
            }
            output.push(c);
        } else if c == ':' {
            if inquotes {
                output.push(c);
            } else {
                trim_terminator = false;
                break;
            }
        } else {
            output.push(c);
        }
    }
    if trim_terminator {
        end += 1
    }
    let (_, rem) = input.split_at(std::cmp::min(end, input.len()));
    (rem, output.into_iter().collect())
}

#[cfg(test)]
mod test {
    #[test]
    fn test_next_token() -> Result<(), Box<dyn std::error::Error>> {
        let token = next_token("token asdf");
        assert_eq!(token, ("asdf", "token".to_string()));

        let token = next_token("\"quoted string\"");
        assert_eq!(token, ("", "\"quoted string\"".to_string()));

        let token = next_token("key:val");
        assert_eq!(token, (":val", "key".to_string()));

        let (rem, token) = next_token("\"quoted key\":\"quoted value\" and some other text");
        assert_eq!(token, r#""quoted key""#.to_string());
        let (rem, token) = next_token(&rem[1..]);
        assert_eq!(token, r#""quoted value""#.to_string());
        assert_eq!(rem, "and some other text");

        Ok(())
    }

    #[test]
    fn test_parse_query_string() -> Result<(), Box<dyn std::error::Error>> {
        let qs = "alert.signature:\"WPAD\" 10.16.1.1";

        let (key, val, rem) = parse_query_string(qs);
        assert_eq!(key, Some("alert.signature".to_string()));
        assert_eq!(val, "\"WPAD\"".to_string());
        assert_eq!(rem, "10.16.1.1".to_string());

        let (key, val, rem) = parse_query_string(rem);
        assert_eq!(key, None);
        assert_eq!(val, "10.16.1.1".to_string());
        assert_eq!(rem, "");

        let (key, val, rem) = parse_query_string(rem);
        assert_eq!(key, None);
        assert_eq!(val, "".to_string());
        assert_eq!(rem, "");

        let (key, val, rem) = parse_query_string(rem);
        assert_eq!(key, None);
        assert_eq!(val, "".to_string());
        assert_eq!(rem, "");

        Ok(())
    }

    fn parse_query_string(input: &str) -> (Option<String>, String, &str) {
        let (rem, token) = next_token(input);
        if !rem.is_empty() && rem.starts_with(":") {
            let (rem, val) = next_token(&rem[1..]);
            return (Some(token), val, rem);
        }
        (None, token, rem)
    }

    fn next_token(input: &str) -> (&str, String) {
        let mut output = Vec::new();
        let mut escaped = false;
        let mut end = 0;
        let mut inquotes = false;
        let mut trim_terminator = true;
        for (i, c) in input.chars().enumerate() {
            end = i;
            if c == '\\' {
                // Disable escaping for now, not sure if its needed.
                //escaped = true;
            } else if escaped {
                if c == ';' {
                    output.push(c);
                } else {
                    output.push('\\');
                    output.push(c);
                }
                escaped = false;
            } else if c == ' ' {
                if !inquotes {
                    break;
                } else {
                    output.push(c);
                }
            } else if c == '"' {
                if inquotes {
                    inquotes = false;
                } else {
                    inquotes = true;
                }
                output.push(c);
            } else if c == ':' {
                if inquotes {
                    output.push(c);
                } else {
                    trim_terminator = false;
                    break;
                }
            } else {
                output.push(c);
            }
        }
        if trim_terminator {
            end += 1
        }
        let (_, rem) = input.split_at(std::cmp::min(end, input.len()));
        (rem, output.into_iter().collect())
    }
}
<commit_msg>query parse: suppress branches sharing code<commit_after>// Copyright (C) 2020 Jason Ish
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

pub fn parse_query_string(input: &str) -> (Option<String>, String, &str) {
    let (rem, token) = next_token(input);
    if !rem.is_empty() && rem.starts_with(':') {
        let (rem, mut val) = next_token(&rem[1..]);
        if val.starts_with('"') && val.ends_with('"') {
            val = val[1..val.len() - 1].to_string();
        }
        return (Some(token), val, rem);
    }
    (None, token, rem)
}

#[allow(clippy::branches_sharing_code)]
fn next_token(input: &str) -> (&str, String) {
    let mut output = Vec::new();
    let mut escaped = false;
    let mut end = 0;
    let mut inquotes = false;
    let mut trim_terminator = true;
    for (i, c) in input.chars().enumerate() {
        end = i;
        if c == '\\' {
            // Disable escaping for now, not sure if its needed.
            //escaped = true;
        } else if escaped {
            if c == ';' {
                output.push(c);
            } else {
                output.push('\\');
                output.push(c);
            }
            escaped = false;
        } else if c == ' ' {
            if !inquotes {
                break;
            } else {
                output.push(c);
            }
        } else if c == '"' {
            if inquotes {
                inquotes = false;
            } else {
                inquotes = true;
            }
            output.push(c);
        } else if c == ':' {
            if inquotes {
                output.push(c);
            } else {
                trim_terminator = false;
                break;
            }
        } else {
            output.push(c);
        }
    }
    if trim_terminator {
        end += 1
    }
    let (_, rem) = input.split_at(std::cmp::min(end, input.len()));
    (rem, output.into_iter().collect())
}

#[cfg(test)]
mod test {
    #[test]
    fn test_next_token() -> Result<(), Box<dyn std::error::Error>> {
        let token = next_token("token asdf");
        assert_eq!(token, ("asdf", "token".to_string()));

        let token = next_token("\"quoted string\"");
        assert_eq!(token, ("", "\"quoted string\"".to_string()));

        let token = next_token("key:val");
        assert_eq!(token, (":val", "key".to_string()));

        let (rem, token) = next_token("\"quoted key\":\"quoted value\" and some other text");
        assert_eq!(token, r#""quoted key""#.to_string());
        let (rem, token) = next_token(&rem[1..]);
        assert_eq!(token, r#""quoted value""#.to_string());
        assert_eq!(rem, "and some other text");

        Ok(())
    }

    #[test]
    fn test_parse_query_string() -> Result<(), Box<dyn std::error::Error>> {
        let qs = "alert.signature:\"WPAD\" 10.16.1.1";

        let (key, val, rem) = parse_query_string(qs);
        assert_eq!(key, Some("alert.signature".to_string()));
        assert_eq!(val, "\"WPAD\"".to_string());
        assert_eq!(rem, "10.16.1.1".to_string());

        let (key, val, rem) = parse_query_string(rem);
        assert_eq!(key, None);
        assert_eq!(val, "10.16.1.1".to_string());
        assert_eq!(rem, "");

        let (key, val, rem) = parse_query_string(rem);
        assert_eq!(key, None);
        assert_eq!(val, "".to_string());
        assert_eq!(rem, "");

        let (key, val, rem) = parse_query_string(rem);
        assert_eq!(key, None);
        assert_eq!(val, "".to_string());
        assert_eq!(rem, "");

        Ok(())
    }

    fn parse_query_string(input: &str) -> (Option<String>, String, &str) {
        let (rem, token) = next_token(input);
        if !rem.is_empty() && rem.starts_with(":") {
            let (rem, val) = next_token(&rem[1..]);
            return (Some(token), val, rem);
        }
        (None, token, rem)
    }

    fn next_token(input: &str) -> (&str, String) {
        let mut output = Vec::new();
        let mut escaped = false;
        let mut end = 0;
        let mut inquotes = false;
        let mut trim_terminator = true;
        for (i, c) in input.chars().enumerate() {
            end = i;
            if c == '\\' {
                // Disable escaping for now, not sure if its needed.
                //escaped = true;
            } else if escaped {
                if c == ';' {
                    output.push(c);
                } else {
                    output.push('\\');
                    output.push(c);
                }
                escaped = false;
            } else if c == ' ' {
                if !inquotes {
                    break;
                } else {
                    output.push(c);
                }
            } else if c == '"' {
                if inquotes {
                    inquotes = false;
                } else {
                    inquotes = true;
                }
                output.push(c);
            } else if c == ':' {
                if inquotes {
                    output.push(c);
                } else {
                    trim_terminator = false;
                    break;
                }
            } else {
                output.push(c);
            }
        }
        if trim_terminator {
            end += 1
        }
        let (_, rem) = input.split_at(std::cmp::min(end, input.len()));
        (rem, output.into_iter().collect())
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Library used by tidy and other tools
//!
//! This library contains the tidy lints and exposes it
//! to be used by tools.

#![deny(warnings)]

use std::fs;

use std::path::Path;

macro_rules! t {
    ($e:expr, $p:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
    });

    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

macro_rules! tidy_error {
    ($bad:expr, $fmt:expr, $($arg:tt)*) => ({
        *$bad = true;
        eprint!("tidy error: ");
        eprintln!($fmt, $($arg)*);
    });
}

pub mod bins;
pub mod style;
pub mod errors;
pub mod features;
pub mod cargo;
pub mod pal;
pub mod deps;
pub mod unstable_book;

fn filter_dirs(path: &Path) -> bool {
    let skip = [
        "src/binaryen",
        "src/dlmalloc",
        "src/jemalloc",
        "src/llvm",
        "src/libbacktrace",
        "src/libcompiler_builtins",
        "src/librustc_data_structures/owning_ref",
        "src/compiler-rt",
        "src/liblibc",
        "src/vendor",
        "src/rt/hoedown",
        "src/tools/cargo",
        "src/tools/rls",
        "src/tools/clippy",
        "src/tools/rust-installer",
        "src/tools/rustfmt",
        "src/tools/miri",
        "src/librustc/mir/interpret",
        "src/librustc_mir/interpret",
    ];
    skip.iter().any(|p| path.ends_with(p))
}

fn walk_many(paths: &[&Path], skip: &mut FnMut(&Path) -> bool, f: &mut FnMut(&Path)) {
    for path in paths {
        walk(path, skip, f);
    }
}

fn walk(path: &Path, skip: &mut FnMut(&Path) -> bool, f: &mut FnMut(&Path)) {
    for entry in t!(fs::read_dir(path), path) {
        let entry = t!(entry);
        let kind = t!(entry.file_type());
        let path = entry.path();
        if kind.is_dir() {
            if !skip(&path) {
                walk(&path, skip, f);
            }
        } else {
            f(&path);
        }
    }
}
<commit_msg>Rollup merge of #47535 - Manishearth:ignore-target, r=kennytm<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

//! Library used by tidy and other tools
//!
//! This library contains the tidy lints and exposes it
//! to be used by tools.

#![deny(warnings)]

use std::fs;

use std::path::Path;

macro_rules! t {
    ($e:expr, $p:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
    });

    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

macro_rules! tidy_error {
    ($bad:expr, $fmt:expr, $($arg:tt)*) => ({
        *$bad = true;
        eprint!("tidy error: ");
        eprintln!($fmt, $($arg)*);
    });
}

pub mod bins;
pub mod style;
pub mod errors;
pub mod features;
pub mod cargo;
pub mod pal;
pub mod deps;
pub mod unstable_book;

fn filter_dirs(path: &Path) -> bool {
    let skip = [
        "src/binaryen",
        "src/dlmalloc",
        "src/jemalloc",
        "src/llvm",
        "src/libbacktrace",
        "src/libcompiler_builtins",
        "src/librustc_data_structures/owning_ref",
        "src/compiler-rt",
        "src/liblibc",
        "src/vendor",
        "src/rt/hoedown",
        "src/tools/cargo",
        "src/tools/rls",
        "src/tools/clippy",
        "src/tools/rust-installer",
        "src/tools/rustfmt",
        "src/tools/miri",
        "src/librustc/mir/interpret",
        "src/librustc_mir/interpret",
        "src/target",
    ];
    skip.iter().any(|p| path.ends_with(p))
}

fn walk_many(paths: &[&Path], skip: &mut FnMut(&Path) -> bool, f: &mut FnMut(&Path)) {
    for path in paths {
        walk(path, skip, f);
    }
}

fn walk(path: &Path, skip: &mut FnMut(&Path) -> bool, f: &mut FnMut(&Path)) {
    for entry in t!(fs::read_dir(path), path) {
        let entry = t!(entry);
        let kind = t!(entry.file_type());
        let path = entry.path();
        if kind.is_dir() {
            if !skip(&path) {
                walk(&path, skip, f);
            }
        } else {
            f(&path);
        }
    }
}
<|endoftext|>


<commit_before>//! Tidy check to enforce rules about platform-specific code in std.
//!
//! This is intended to maintain existing standards of code
//! organization in hopes that the standard library will continue to
//! be refactored to isolate platform-specific bits, making porting
//! easier; where "standard library" roughly means "all the
//! dependencies of the std and test crates".
//!
//! This generally means placing restrictions on where `cfg(unix)`,
//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
//! the basic objective being to isolate platform-specific code to the
//! platform-specific `std::sys` modules, and to the allocation,
//! unwinding, and libc crates.
//!
//! Following are the basic rules, though there are currently
//! exceptions:
//!
//! - core may not have platform-specific code.
//! - libpanic_abort may have platform-specific code.
//! - libpanic_unwind may have platform-specific code.
//! - libunwind may have platform-specific code.
//! - other crates in the std facade may not.
//! - std may have platform-specific code in the following places:
//!   - `sys/unix/`
//!   - `sys/windows/`
//!   - `os/`
//!
//! `std/sys_common` should _not_ contain platform-specific code.
//! Finally, because std contains tests with platform-specific
//! `ignore` attributes, once the parser encounters `mod tests`,
//! platform-specific cfgs are allowed. Not sure yet how to deal with
//! this in the long term.

use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::iter::Iterator;

// Paths that may contain platform-specific code.
const EXCEPTION_PATHS: &[&str] = &[
    // std crates
    "src/libpanic_abort",
    "src/libpanic_unwind",
    "src/libunwind",
    "src/libstd/sys/", // Platform-specific code for std lives here.
                       // This has the trailing slash so that sys_common is not excepted.
    "src/libstd/os", // Platform-specific public interfaces
    "src/rtstartup", // Not sure what to do about this. magic stuff for mingw

    // temporary exceptions
    "src/libstd/lib.rs",
    "src/libstd/path.rs",
    "src/libstd/f32.rs",
    "src/libstd/f64.rs",
    // Integration test for platform-specific run-time feature detection:
    "src/libstd/tests/run-time-detect.rs" ,
    "src/libstd/sys_common/mod.rs",
    "src/libstd/sys_common/net.rs",
    "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
    "src/libtest", // Probably should defer to unstable `std::sys` APIs.

    // std testing crates, okay for now at least
    "src/libcore/tests",
    "src/liballoc/tests/lib.rs",

    // The `VaList` implementation must have platform specific code.
    // The Windows implementation of a `va_list` is always a character
    // pointer regardless of the target architecture. As a result,
    // we must use `#[cfg(windows)]` to conditionally compile the
    // correct `VaList` structure for windows.
    "src/libcore/ffi.rs",

    // non-std crates
    "src/test",
    "src/tools",
    "src/librustc",
    "src/librustdoc",
    "src/libsyntax",
    "src/bootstrap",
];

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    // Sanity check that the complex parsing here works.
    let mut saw_target_arch = false;
    let mut saw_cfg_bang = false;
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filestr = file.to_string_lossy().replace("\\", "/");
        if !filestr.ends_with(".rs") { return }

        let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
        if is_exception_path { return }

        check_cfgs(&mut contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
    });

    assert!(saw_target_arch);
    assert!(saw_cfg_bang);
}

fn check_cfgs(contents: &mut String, file: &Path,
              bad: &mut bool, saw_target_arch: &mut bool, saw_cfg_bang: &mut bool) {
    contents.truncate(0);
    t!(t!(File::open(file), file).read_to_string(contents));

    // For now it's ok to have platform-specific code after 'mod tests'.
    let mod_tests_idx = find_test_mod(contents);
    let contents = &contents[..mod_tests_idx];
    // Pull out all `cfg(...)` and `cfg!(...)` strings.
    let cfgs = parse_cfgs(contents);

    let mut line_numbers: Option<Vec<usize>> = None;
    let mut err = |idx: usize, cfg: &str| {
        if line_numbers.is_none() {
            line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
        }
        let line_numbers = line_numbers.as_ref().expect("");
        let line = match line_numbers.binary_search(&idx) {
            Ok(_) => unreachable!(),
            Err(i) => i + 1
        };
        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
    };

    for (idx, cfg) in cfgs {
        // Sanity check that the parsing here works.
        if !*saw_target_arch && cfg.contains("target_arch") { *saw_target_arch = true }
        if !*saw_cfg_bang && cfg.contains("cfg!") { *saw_cfg_bang = true }

        let contains_platform_specific_cfg =
            cfg.contains("target_os")
            || cfg.contains("target_env")
            || cfg.contains("target_vendor")
            || cfg.contains("unix")
            || cfg.contains("windows");

        if !contains_platform_specific_cfg { continue }

        let preceeded_by_doc_comment = {
            let pre_contents = &contents[..idx];
            let pre_newline = pre_contents.rfind('\n');
            let pre_doc_comment = pre_contents.rfind("///");
            match (pre_newline, pre_doc_comment) {
                (Some(n), Some(c)) => n < c,
                (None, Some(_)) => true,
                (_, None) => false,
            }
        };

        if preceeded_by_doc_comment { continue }

        err(idx, cfg);
    }
}

fn find_test_mod(contents: &str) -> usize {
    if let Some(mod_tests_idx) = contents.find("mod tests") {
        // Also capture a previous line indicating that "mod tests" is cfg'd out.
        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
        if let Some(nl) = prev_newline_idx {
            let prev_line = &contents[nl + 1 .. mod_tests_idx];
            if prev_line.contains("cfg(all(test, not(target_os")
                || prev_line.contains("cfg(all(test, not(any(target_os") {
                nl
            } else {
                mod_tests_idx
            }
        } else {
            mod_tests_idx
        }
    } else {
        contents.len()
    }
}

fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
    let candidate_cfgs = contents.match_indices("cfg");
    let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
    // This is puling out the indexes of all "cfg" strings
    // that appear to be tokens followed by a parenthesis.
    let cfgs = candidate_cfg_idxs.filter(|i| {
        let pre_idx = i.saturating_sub(*i);
        let succeeds_non_ident = !contents.as_bytes().get(pre_idx)
            .cloned()
            .map(char::from)
            .map(char::is_alphanumeric)
            .unwrap_or(false);
        let contents_after = &contents[*i..];
        let first_paren = contents_after.find('(');
        let paren_idx = first_paren.map(|ip| i + ip);
        let preceeds_whitespace_and_paren = paren_idx.map(|ip| {
            let maybe_space = &contents[*i + "cfg".len() .. ip];
            maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
        }).unwrap_or(false);

        succeeds_non_ident && preceeds_whitespace_and_paren
    });

    cfgs.map(|i| {
        let mut depth = 0;
        let contents_from = &contents[i..];
        for (j, byte) in contents_from.bytes().enumerate() {
            match byte {
                b'(' => {
                    depth += 1;
                }
                b')' => {
                    depth -= 1;
                    if depth == 0 {
                        return (i, &contents_from[..=j]);
                    }
                }
                _ => { }
            }
        }

        unreachable!()
    }).collect()
}
<commit_msg>Allow platform specific tests<commit_after>//! Tidy check to enforce rules about platform-specific code in std.
//!
//! This is intended to maintain existing standards of code
//! organization in hopes that the standard library will continue to
//! be refactored to isolate platform-specific bits, making porting
//! easier; where "standard library" roughly means "all the
//! dependencies of the std and test crates".
//!
//! This generally means placing restrictions on where `cfg(unix)`,
//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
//! the basic objective being to isolate platform-specific code to the
//! platform-specific `std::sys` modules, and to the allocation,
//! unwinding, and libc crates.
//!
//! Following are the basic rules, though there are currently
//! exceptions:
//!
//! - core may not have platform-specific code.
//! - libpanic_abort may have platform-specific code.
//! - libpanic_unwind may have platform-specific code.
//! - libunwind may have platform-specific code.
//! - other crates in the std facade may not.
//! - std may have platform-specific code in the following places:
//!   - `sys/unix/`
//!   - `sys/windows/`
//!   - `os/`
//!
//! `std/sys_common` should _not_ contain platform-specific code.
//! Finally, because std contains tests with platform-specific
//! `ignore` attributes, once the parser encounters `mod tests`,
//! platform-specific cfgs are allowed. Not sure yet how to deal with
//! this in the long term.

use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::iter::Iterator;

// Paths that may contain platform-specific code.
const EXCEPTION_PATHS: &[&str] = &[
    // std crates
    "src/libpanic_abort",
    "src/libpanic_unwind",
    "src/libunwind",
    "src/libstd/sys/", // Platform-specific code for std lives here.
                       // This has the trailing slash so that sys_common is not excepted.
    "src/libstd/os", // Platform-specific public interfaces
    "src/rtstartup", // Not sure what to do about this. magic stuff for mingw

    // temporary exceptions
    "src/libstd/lib.rs",
    "src/libstd/path.rs",
    "src/libstd/f32.rs",
    "src/libstd/f64.rs",
    // Integration test for platform-specific run-time feature detection:
    "src/libstd/tests/run-time-detect.rs" ,
    "src/libstd/sys_common/mod.rs",
    "src/libstd/sys_common/net.rs",
    "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
    "src/libtest", // Probably should defer to unstable `std::sys` APIs.
    "src/libstd/sync/mpsc", // some tests are only run on non-emscripten

    // std testing crates, okay for now at least
    "src/libcore/tests",
    "src/liballoc/tests/lib.rs",

    // The `VaList` implementation must have platform specific code.
    // The Windows implementation of a `va_list` is always a character
    // pointer regardless of the target architecture. As a result,
    // we must use `#[cfg(windows)]` to conditionally compile the
    // correct `VaList` structure for windows.
    "src/libcore/ffi.rs",

    // non-std crates
    "src/test",
    "src/tools",
    "src/librustc",
    "src/librustdoc",
    "src/libsyntax",
    "src/bootstrap",
];

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    // Sanity check that the complex parsing here works.
    let mut saw_target_arch = false;
    let mut saw_cfg_bang = false;
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filestr = file.to_string_lossy().replace("\\", "/");
        if !filestr.ends_with(".rs") { return }

        let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
        if is_exception_path { return }

        check_cfgs(&mut contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
    });

    assert!(saw_target_arch);
    assert!(saw_cfg_bang);
}

fn check_cfgs(contents: &mut String, file: &Path,
              bad: &mut bool, saw_target_arch: &mut bool, saw_cfg_bang: &mut bool) {
    contents.truncate(0);
    t!(t!(File::open(file), file).read_to_string(contents));

    // For now it's ok to have platform-specific code after 'mod tests'.
    let mod_tests_idx = find_test_mod(contents);
    let contents = &contents[..mod_tests_idx];
    // Pull out all `cfg(...)` and `cfg!(...)` strings.
    let cfgs = parse_cfgs(contents);

    let mut line_numbers: Option<Vec<usize>> = None;
    let mut err = |idx: usize, cfg: &str| {
        if line_numbers.is_none() {
            line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
        }
        let line_numbers = line_numbers.as_ref().expect("");
        let line = match line_numbers.binary_search(&idx) {
            Ok(_) => unreachable!(),
            Err(i) => i + 1
        };
        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
    };

    for (idx, cfg) in cfgs {
        // Sanity check that the parsing here works.
        if !*saw_target_arch && cfg.contains("target_arch") { *saw_target_arch = true }
        if !*saw_cfg_bang && cfg.contains("cfg!") { *saw_cfg_bang = true }

        let contains_platform_specific_cfg =
            cfg.contains("target_os")
            || cfg.contains("target_env")
            || cfg.contains("target_vendor")
            || cfg.contains("unix")
            || cfg.contains("windows");

        if !contains_platform_specific_cfg { continue }

        let preceeded_by_doc_comment = {
            let pre_contents = &contents[..idx];
            let pre_newline = pre_contents.rfind('\n');
            let pre_doc_comment = pre_contents.rfind("///");
            match (pre_newline, pre_doc_comment) {
                (Some(n), Some(c)) => n < c,
                (None, Some(_)) => true,
                (_, None) => false,
            }
        };

        if preceeded_by_doc_comment { continue }

        err(idx, cfg);
    }
}

fn find_test_mod(contents: &str) -> usize {
    if let Some(mod_tests_idx) = contents.find("mod tests") {
        // Also capture a previous line indicating that "mod tests" is cfg'd out.
        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
        if let Some(nl) = prev_newline_idx {
            let prev_line = &contents[nl + 1 .. mod_tests_idx];
            if prev_line.contains("cfg(all(test, not(target_os")
                || prev_line.contains("cfg(all(test, not(any(target_os") {
                nl
            } else {
                mod_tests_idx
            }
        } else {
            mod_tests_idx
        }
    } else {
        contents.len()
    }
}

fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
    let candidate_cfgs = contents.match_indices("cfg");
    let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
    // This is puling out the indexes of all "cfg" strings
    // that appear to be tokens followed by a parenthesis.
    let cfgs = candidate_cfg_idxs.filter(|i| {
        let pre_idx = i.saturating_sub(*i);
        let succeeds_non_ident = !contents.as_bytes().get(pre_idx)
            .cloned()
            .map(char::from)
            .map(char::is_alphanumeric)
            .unwrap_or(false);
        let contents_after = &contents[*i..];
        let first_paren = contents_after.find('(');
        let paren_idx = first_paren.map(|ip| i + ip);
        let preceeds_whitespace_and_paren = paren_idx.map(|ip| {
            let maybe_space = &contents[*i + "cfg".len() .. ip];
            maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
        }).unwrap_or(false);

        succeeds_non_ident && preceeds_whitespace_and_paren
    });

    cfgs.map(|i| {
        let mut depth = 0;
        let contents_from = &contents[i..];
        for (j, byte) in contents_from.bytes().enumerate() {
            match byte {
                b'(' => {
                    depth += 1;
                }
                b')' => {
                    depth -= 1;
                    if depth == 0 {
                        return (i, &contents_from[..=j]);
                    }
                }
                _ => { }
            }
        }

        unreachable!()
    }).collect()
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 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 smallvec::SmallVec;
use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;

use device::DeviceOwned;
use device::Queue;
use swapchain::PresentRegion;
use swapchain::Swapchain;
use sync::Semaphore;

use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use check_errors;
use vk;

/// Prototype for a submission that presents a swapchain on the screen.
// TODO: example here
pub struct SubmitPresentBuilder<'a> {
    wait_semaphores: SmallVec<[vk::Semaphore; 8]>,
    swapchains: SmallVec<[vk::SwapchainKHR; 4]>,
    image_indices: SmallVec<[u32; 4]>,
    present_regions: SmallVec<[vk::PresentRegionKHR; 4]>,
    rect_layers: SmallVec<[vk::RectLayerKHR; 4]>,
    marker: PhantomData<&'a ()>,
}

impl<'a> SubmitPresentBuilder<'a> {
    /// Builds a new empty `SubmitPresentBuilder`.
    #[inline]
    pub fn new() -> SubmitPresentBuilder<'a> {
        SubmitPresentBuilder {
            wait_semaphores: SmallVec::new(),
            swapchains: SmallVec::new(),
            image_indices: SmallVec::new(),
            present_regions: SmallVec::new(),
            rect_layers: SmallVec::new(),
            marker: PhantomData,
        }
    }

    /// Adds a semaphore to be waited upon before the presents are executed.
    ///
    /// # Safety
    ///
    /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed
    ///   that the GPU has presented the swapchains.
    ///
    /// - If you submit this builder, no other queue must be waiting on these semaphores. In other
    ///   words, each semaphore signal can only correspond to one semaphore wait.
    ///
    /// - If you submit this builder, the semaphores must be signaled when the queue execution
    ///   reaches this submission, or there must be one or more submissions in queues that are
    ///   going to signal these semaphores. In other words, you must not block the queue with
    ///   semaphores that can't get signaled.
    ///
    /// - The swapchains and semaphores must all belong to the same device.
    ///
    #[inline]
    pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) {
        self.wait_semaphores.push(semaphore.internal_object());
    }

    /// Adds an image of a swapchain to be presented.
    ///
    /// Allows to specify a present region.
    /// Areas outside the present region *can* be ignored by the Vulkan implementation for
    /// optimizations purposes.
    ///
    /// If `VK_KHR_incremental_present` is not enabled, the `present_region` parameter is ignored.
    ///
    /// # Safety
    ///
    /// - If you submit this builder, the swapchain must be kept alive until you are
    ///   guaranteed that the GPU has finished presenting.
    ///
    /// - The swapchains and semaphores must all belong to the same device.
    ///
    #[inline]
    pub unsafe fn add_swapchain<W>(&mut self, swapchain: &'a Swapchain<W>, image_num: u32,
                                   present_region: Option<&'a PresentRegion>) {
        debug_assert!(image_num < swapchain.num_images());

        if swapchain
            .device()
            .loaded_extensions()
            .khr_incremental_present
        {
            let vk_present_region = match present_region {
                Some(present_region) => {
                    assert!(present_region.is_compatible_with(swapchain));
                    for rectangle in &present_region.rectangles {
                        self.rect_layers.push(rectangle.to_vk());
                    }
                    vk::PresentRegionKHR {
                        rectangleCount: present_region.rectangles.len() as u32,
                        // Set this to null for now; in submit fill it with self.rect_layers
                        pRectangles: ptr::null(),
                    }
                },
                None => {
                    vk::PresentRegionKHR {
                        rectangleCount: 0,
                        pRectangles: ptr::null(),
                    }
                },
            };
            self.present_regions.push(vk_present_region);
        }

        self.swapchains.push(swapchain.internal_object());
        self.image_indices.push(image_num);
    }

    /// Submits the command. Calls `vkQueuePresentKHR`.
    ///
    /// # Panic
    ///
    /// Panics if no swapchain image has been added to the builder.
    ///
    pub fn submit(mut self, queue: &Queue) -> Result<(), SubmitPresentError> {
        unsafe {
            debug_assert_eq!(self.swapchains.len(), self.image_indices.len());
            assert!(!self.swapchains.is_empty(),
                    "Tried to submit a present command without any swapchain");

            let present_regions = {
                if !self.present_regions.is_empty() {
                    debug_assert!(queue.device().loaded_extensions().khr_incremental_present);
                    debug_assert_eq!(self.swapchains.len(), self.present_regions.len());
                    let mut current_index = 0;
                    for present_region in &mut self.present_regions {
                        present_region.pRectangles = self.rect_layers[current_index ..].as_ptr();
                        current_index += present_region.rectangleCount as usize;
                    }
                    Some(vk::PresentRegionsKHR {
                             sType: vk::STRUCTURE_TYPE_PRESENT_REGIONS_KHR,
                             pNext: ptr::null(),
                             swapchainCount: self.present_regions.len() as u32,
                             pRegions: self.present_regions.as_ptr(),
                         })
                } else {
                    None
                }
            };

            let mut results = vec![mem::uninitialized(); self.swapchains.len()]; // TODO: alloca

            let vk = queue.device().pointers();
            let queue = queue.internal_object_guard();

            let infos = vk::PresentInfoKHR {
                sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
                pNext: present_regions
                    .as_ref()
                    .map(|pr| pr as *const vk::PresentRegionsKHR as *const _)
                    .unwrap_or(ptr::null()),
                waitSemaphoreCount: self.wait_semaphores.len() as u32,
                pWaitSemaphores: self.wait_semaphores.as_ptr(),
                swapchainCount: self.swapchains.len() as u32,
                pSwapchains: self.swapchains.as_ptr(),
                pImageIndices: self.image_indices.as_ptr(),
                pResults: results.as_mut_ptr(),
            };

            check_errors(vk.QueuePresentKHR(*queue, &infos))?;

            // TODO: AMD driver initially didn't write the results ; check that it's been fixed
            //for result in results {
            //try!(check_errors(result));
            //}

            Ok(())
        }
    }
}

impl<'a> fmt::Debug for SubmitPresentBuilder<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt.debug_struct("SubmitPresentBuilder")
            .field("wait_semaphores", &self.wait_semaphores)
            .field("swapchains", &self.swapchains)
            .field("image_indices", &self.image_indices)
            .finish()
    }
}

/// Error that can happen when submitting the present prototype.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SubmitPresentError {
    /// Not enough memory.
    OomError(OomError),

    /// The connection to the device has been lost.
    DeviceLost,

    /// The surface is no longer accessible and must be recreated.
    SurfaceLost,

    /// The surface has changed in a way that makes the swapchain unusable. You must query the
    /// surface's new properties and recreate a new swapchain if you want to continue drawing.
    OutOfDate,
}

impl error::Error for SubmitPresentError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SubmitPresentError::OomError(_) => "not enough memory",
            SubmitPresentError::DeviceLost => "the connection to the device has been lost",
            SubmitPresentError::SurfaceLost => "the surface of this swapchain is no longer valid",
            SubmitPresentError::OutOfDate => "the swapchain needs to be recreated",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SubmitPresentError::OomError(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for SubmitPresentError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for SubmitPresentError {
    #[inline]
    fn from(err: Error) -> SubmitPresentError {
        match err {
            err @ Error::OutOfHostMemory => SubmitPresentError::OomError(OomError::from(err)),
            err @ Error::OutOfDeviceMemory => SubmitPresentError::OomError(OomError::from(err)),
            Error::DeviceLost => SubmitPresentError::DeviceLost,
            Error::SurfaceLost => SubmitPresentError::SurfaceLost,
            Error::OutOfDate => SubmitPresentError::OutOfDate,
            _ => panic!("unexpected error: {:?}", err),
        }
    }
}

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

    #[test]
    fn no_swapchain_added() {
        let (_, queue) = gfx_dev_and_queue!();
        assert_should_panic!("Tried to submit a present command without any swapchain", {
            let _ = SubmitPresentBuilder::new().submit(&queue);
        });
    }
}
<commit_msg>Reenable per swapchain check in SubmitPresentBuilder::submit (#1088)<commit_after>// Copyright (c) 2017 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 smallvec::SmallVec;
use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::ptr;

use device::DeviceOwned;
use device::Queue;
use swapchain::PresentRegion;
use swapchain::Swapchain;
use sync::Semaphore;

use Error;
use OomError;
use SynchronizedVulkanObject;
use VulkanObject;
use check_errors;
use vk;

/// Prototype for a submission that presents a swapchain on the screen.
// TODO: example here
pub struct SubmitPresentBuilder<'a> {
    wait_semaphores: SmallVec<[vk::Semaphore; 8]>,
    swapchains: SmallVec<[vk::SwapchainKHR; 4]>,
    image_indices: SmallVec<[u32; 4]>,
    present_regions: SmallVec<[vk::PresentRegionKHR; 4]>,
    rect_layers: SmallVec<[vk::RectLayerKHR; 4]>,
    marker: PhantomData<&'a ()>,
}

impl<'a> SubmitPresentBuilder<'a> {
    /// Builds a new empty `SubmitPresentBuilder`.
    #[inline]
    pub fn new() -> SubmitPresentBuilder<'a> {
        SubmitPresentBuilder {
            wait_semaphores: SmallVec::new(),
            swapchains: SmallVec::new(),
            image_indices: SmallVec::new(),
            present_regions: SmallVec::new(),
            rect_layers: SmallVec::new(),
            marker: PhantomData,
        }
    }

    /// Adds a semaphore to be waited upon before the presents are executed.
    ///
    /// # Safety
    ///
    /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed
    ///   that the GPU has presented the swapchains.
    ///
    /// - If you submit this builder, no other queue must be waiting on these semaphores. In other
    ///   words, each semaphore signal can only correspond to one semaphore wait.
    ///
    /// - If you submit this builder, the semaphores must be signaled when the queue execution
    ///   reaches this submission, or there must be one or more submissions in queues that are
    ///   going to signal these semaphores. In other words, you must not block the queue with
    ///   semaphores that can't get signaled.
    ///
    /// - The swapchains and semaphores must all belong to the same device.
    ///
    #[inline]
    pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) {
        self.wait_semaphores.push(semaphore.internal_object());
    }

    /// Adds an image of a swapchain to be presented.
    ///
    /// Allows to specify a present region.
    /// Areas outside the present region *can* be ignored by the Vulkan implementation for
    /// optimizations purposes.
    ///
    /// If `VK_KHR_incremental_present` is not enabled, the `present_region` parameter is ignored.
    ///
    /// # Safety
    ///
    /// - If you submit this builder, the swapchain must be kept alive until you are
    ///   guaranteed that the GPU has finished presenting.
    ///
    /// - The swapchains and semaphores must all belong to the same device.
    ///
    #[inline]
    pub unsafe fn add_swapchain<W>(&mut self, swapchain: &'a Swapchain<W>, image_num: u32,
                                   present_region: Option<&'a PresentRegion>) {
        debug_assert!(image_num < swapchain.num_images());

        if swapchain
            .device()
            .loaded_extensions()
            .khr_incremental_present
        {
            let vk_present_region = match present_region {
                Some(present_region) => {
                    assert!(present_region.is_compatible_with(swapchain));
                    for rectangle in &present_region.rectangles {
                        self.rect_layers.push(rectangle.to_vk());
                    }
                    vk::PresentRegionKHR {
                        rectangleCount: present_region.rectangles.len() as u32,
                        // Set this to null for now; in submit fill it with self.rect_layers
                        pRectangles: ptr::null(),
                    }
                },
                None => {
                    vk::PresentRegionKHR {
                        rectangleCount: 0,
                        pRectangles: ptr::null(),
                    }
                },
            };
            self.present_regions.push(vk_present_region);
        }

        self.swapchains.push(swapchain.internal_object());
        self.image_indices.push(image_num);
    }

    /// Submits the command. Calls `vkQueuePresentKHR`.
    ///
    /// # Panic
    ///
    /// Panics if no swapchain image has been added to the builder.
    ///
    pub fn submit(mut self, queue: &Queue) -> Result<(), SubmitPresentError> {
        unsafe {
            debug_assert_eq!(self.swapchains.len(), self.image_indices.len());
            assert!(!self.swapchains.is_empty(),
                    "Tried to submit a present command without any swapchain");

            let present_regions = {
                if !self.present_regions.is_empty() {
                    debug_assert!(queue.device().loaded_extensions().khr_incremental_present);
                    debug_assert_eq!(self.swapchains.len(), self.present_regions.len());
                    let mut current_index = 0;
                    for present_region in &mut self.present_regions {
                        present_region.pRectangles = self.rect_layers[current_index ..].as_ptr();
                        current_index += present_region.rectangleCount as usize;
                    }
                    Some(vk::PresentRegionsKHR {
                             sType: vk::STRUCTURE_TYPE_PRESENT_REGIONS_KHR,
                             pNext: ptr::null(),
                             swapchainCount: self.present_regions.len() as u32,
                             pRegions: self.present_regions.as_ptr(),
                         })
                } else {
                    None
                }
            };

            let mut results = vec![vk::SUCCESS; self.swapchains.len()];

            let vk = queue.device().pointers();
            let queue = queue.internal_object_guard();

            let infos = vk::PresentInfoKHR {
                sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
                pNext: present_regions
                    .as_ref()
                    .map(|pr| pr as *const vk::PresentRegionsKHR as *const _)
                    .unwrap_or(ptr::null()),
                waitSemaphoreCount: self.wait_semaphores.len() as u32,
                pWaitSemaphores: self.wait_semaphores.as_ptr(),
                swapchainCount: self.swapchains.len() as u32,
                pSwapchains: self.swapchains.as_ptr(),
                pImageIndices: self.image_indices.as_ptr(),
                pResults: results.as_mut_ptr(),
            };

            check_errors(vk.QueuePresentKHR(*queue, &infos))?;

            for result in results {
                check_errors(result)?;
            }

            Ok(())
        }
    }
}

impl<'a> fmt::Debug for SubmitPresentBuilder<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        fmt.debug_struct("SubmitPresentBuilder")
            .field("wait_semaphores", &self.wait_semaphores)
            .field("swapchains", &self.swapchains)
            .field("image_indices", &self.image_indices)
            .finish()
    }
}

/// Error that can happen when submitting the present prototype.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum SubmitPresentError {
    /// Not enough memory.
    OomError(OomError),

    /// The connection to the device has been lost.
    DeviceLost,

    /// The surface is no longer accessible and must be recreated.
    SurfaceLost,

    /// The surface has changed in a way that makes the swapchain unusable. You must query the
    /// surface's new properties and recreate a new swapchain if you want to continue drawing.
    OutOfDate,
}

impl error::Error for SubmitPresentError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SubmitPresentError::OomError(_) => "not enough memory",
            SubmitPresentError::DeviceLost => "the connection to the device has been lost",
            SubmitPresentError::SurfaceLost => "the surface of this swapchain is no longer valid",
            SubmitPresentError::OutOfDate => "the swapchain needs to be recreated",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SubmitPresentError::OomError(ref err) => Some(err),
            _ => None,
        }
    }
}

impl fmt::Display for SubmitPresentError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for SubmitPresentError {
    #[inline]
    fn from(err: Error) -> SubmitPresentError {
        match err {
            err @ Error::OutOfHostMemory => SubmitPresentError::OomError(OomError::from(err)),
            err @ Error::OutOfDeviceMemory => SubmitPresentError::OomError(OomError::from(err)),
            Error::DeviceLost => SubmitPresentError::DeviceLost,
            Error::SurfaceLost => SubmitPresentError::SurfaceLost,
            Error::OutOfDate => SubmitPresentError::OutOfDate,
            _ => panic!("unexpected error: {:?}", err),
        }
    }
}

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

    #[test]
    fn no_swapchain_added() {
        let (_, queue) = gfx_dev_and_queue!();
        assert_should_panic!("Tried to submit a present command without any swapchain", {
            let _ = SubmitPresentBuilder::new().submit(&queue);
        });
    }
}
<|endoftext|>