<commit_before>use std::io::Read;
use std::path;

use gfx;
use image;

use crate::context::{Context, DebugId};
use crate::error::GameError;
use crate::error::GameResult;
use crate::filesystem;
use crate::graphics::shader::*;
use crate::graphics::*;

/// Generic in-GPU-memory image data available to be drawn on the screen.
/// You probably just want to look at the `Image` type.
#[derive(Clone, PartialEq)]
pub struct ImageGeneric<B>
where
    B: BackendSpec,
{
    // TODO: Rename to shader_view or such.
    pub(crate) texture: gfx::handle::RawShaderResourceView<B::Resources>,
    pub(crate) texture_handle: gfx::handle::RawTexture<B::Resources>,
    pub(crate) sampler_info: gfx::texture::SamplerInfo,
    pub(crate) blend_mode: Option<BlendMode>,
    pub(crate) width: u16,
    pub(crate) height: u16,

    pub(crate) debug_id: DebugId,
}

impl<B> ImageGeneric<B>
where
    B: BackendSpec,
{
    /// A helper function that just takes a factory directly so we can make an image
    /// without needing the full context object, so we can create an Image while still
    /// creating the GraphicsContext.
    pub(crate) fn make_raw(
        factory: &mut <B as BackendSpec>::Factory,
        sampler_info: &texture::SamplerInfo,
        width: u16,
        height: u16,
        rgba: &[u8],
        color_format: gfx::format::Format,
        debug_id: DebugId,
    ) -> GameResult<Self> {
        if width == 0 || height == 0 {
            let msg = format!(
                "Tried to create a texture of size {}x{}, each dimension must
                be >0",
                width, height
            );
            return Err(GameError::ResourceLoadError(msg));
        }
        // Check for overflow, which might happen on 32-bit systems.
        // Textures can be max u16*u16, pixels, but then have 4 bytes per pixel.
        let uwidth = width as usize;
        let uheight = height as usize;
        let expected_bytes = uwidth
            .checked_mul(uheight)
            .and_then(|size| size.checked_mul(4))
            .ok_or_else(|| {
                let msg = format!(
                    "Integer overflow in Image::make_raw, image size: {} {}",
                    uwidth, uheight
                );
                GameError::ResourceLoadError(msg)
            })?;
        if expected_bytes != rgba.len() {
            let msg = format!(
                "Tried to create a texture of size {}x{}, but gave {} bytes of data (expected {})",
                width,
                height,
                rgba.len(),
                expected_bytes
            );
            return Err(GameError::ResourceLoadError(msg));
        }
        let kind = gfx::texture::Kind::D2(width, height, gfx::texture::AaMode::Single);
        use gfx::memory::Bind;
        let gfx::format::Format(surface_format, channel_type) = color_format;
        let texinfo = gfx::texture::Info {
            kind,
            levels: 1,
            format: surface_format,
            bind: Bind::SHADER_RESOURCE
                | Bind::RENDER_TARGET
                | Bind::TRANSFER_SRC
                | Bind::TRANSFER_DST,
            usage: gfx::memory::Usage::Dynamic,
        };
        let raw_tex = factory.create_texture_raw(
            texinfo,
            Some(channel_type),
            Some((&[rgba], gfx::texture::Mipmap::Provided)),
        )?;
        let resource_desc = gfx::texture::ResourceDesc {
            channel: channel_type,
            layer: None,
            min: 0,
            max: raw_tex.get_info().levels - 1,
            swizzle: gfx::format::Swizzle::new(),
        };
        let raw_view = factory.view_texture_as_shader_resource_raw(&raw_tex, resource_desc)?;
        Ok(Self {
            texture: raw_view,
            texture_handle: raw_tex,
            sampler_info: *sampler_info,
            blend_mode: None,
            width,
            height,
            debug_id,
        })
    }
}

/// In-GPU-memory image data available to be drawn on the screen,
/// using the OpenGL backend.
///
/// Under the hood this is just an `Arc`'ed texture handle and
/// some metadata, so cloning it is fairly cheap; it doesn't
/// make another copy of the underlying image data.
pub type Image = ImageGeneric<GlBackendSpec>;

/// The supported formats for saving an image.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ImageFormat {
    /// .png image format (defaults to RGBA with 8-bit channels.)
    Png,
}

impl Image {
    /// Load a new image from the file at the given path. The documentation for the
    /// [`filesystem`](../filesystem/index.html) module explains how the path must be specified.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let img = {
            let mut buf = Vec::new();
            let mut reader = context.filesystem.open(path)?;
            let _ = reader.read_to_end(&mut buf)?;
            image::load_from_memory(&buf)?.to_rgba()
        };
        let (width, height) = img.dimensions();
        Self::from_rgba8(context, width as u16, height as u16, &img)
    }

    /// Creates a new `Image` from the given buffer of `u8` RGBA values.
    ///
    /// The pixel layout is row-major.  That is,
    /// the first 4 `u8` values make the top-left pixel in the `Image`, the
    /// next 4 make the next pixel in the same row, and so on to the end of
    /// the row.  The next `width * 4` values make up the second row, and so
    /// on.
    pub fn from_rgba8(
        context: &mut Context,
        width: u16,
        height: u16,
        rgba: &[u8],
    ) -> GameResult<Self> {
        let debug_id = DebugId::get(context);
        let color_format = context.gfx_context.color_format();
        Self::make_raw(
            &mut *context.gfx_context.factory,
            &context.gfx_context.default_sampler_info,
            width,
            height,
            rgba,
            color_format,
            debug_id,
        )
    }

    /// Dumps the `Image`'s data to a `Vec` of `u8` RGBA values.
    pub fn to_rgba8(&self, ctx: &mut Context) -> GameResult<Vec<u8>> {
        use gfx::memory::Typed;
        use gfx::traits::FactoryExt;

        let gfx = &mut ctx.gfx_context;
        let w = self.width;
        let h = self.height;

        // Note: In the GFX example, the download buffer is created ahead of time
        // and updated on screen resize events. This may be preferable, but then
        // the buffer also needs to be updated when we switch to/from a canvas.
        // Unsure of the performance impact of creating this as it is needed.
        // Probably okay for now though, since this probably won't be a super
        // common operation.
        let dl_buffer = gfx
            .factory
            .create_download_buffer::<[u8; 4]>(w as usize * h as usize)?;

        let factory = &mut *gfx.factory;
        let mut local_encoder = GlBackendSpec::encoder(factory);

        local_encoder.copy_texture_to_buffer_raw(
            &self.texture_handle,
            None,
            gfx::texture::RawImageInfo {
                xoffset: 0,
                yoffset: 0,
                zoffset: 0,
                width: w as u16,
                height: h as u16,
                depth: 0,
                format: gfx.color_format(),
                mipmap: 0,
            },
            dl_buffer.raw(),
            0,
        )?;
        local_encoder.flush(&mut *gfx.device);

        let reader = gfx.factory.read_mapping(&dl_buffer)?;

        // intermediary buffer to avoid casting
        // and also to reverse the order in which we pass the rows
        // so the screenshot isn't upside-down
        let mut data = Vec::with_capacity(self.width as usize * self.height as usize * 4);
        for row in reader.chunks(w as usize).rev() {
            // TODO: Test if we can just do row.flatten() here?
            // data.extend(row.iter().flatten());
            for pixel in row.iter() {
                data.extend(pixel);
            }
        }
        Ok(data)
    }

    /// Encode the `Image` to the given file format and
    /// write it out to the given path.
    ///
    /// See the [`filesystem`](../filesystem/index.html) module docs for where exactly
    /// the file will end up.
    pub fn encode<P: AsRef<path::Path>>(
        &self,
        ctx: &mut Context,
        format: ImageFormat,
        path: P,
    ) -> GameResult {
        use std::io;
        let data = self.to_rgba8(ctx)?;
        let f = filesystem::create(ctx, path)?;
        let writer = &mut io::BufWriter::new(f);
        let color_format = image::ColorType::RGBA(8);
        match format {
            ImageFormat::Png => image::png::PNGEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width),
                    u32::from(self.height),
                    color_format,
                )
                .map_err(|e| e.into()),
        }
    }

    /// A little helper function that creates a new Image that is just
    /// a solid square of the given size and color.  Mainly useful for
    /// debugging.
    pub fn solid(context: &mut Context, size: u16, color: Color) -> GameResult<Self> {
        let (r, g, b, a) = color.into();
        let pixel_array: [u8; 4] = [r, g, b, a];
        let size_squared = size as usize * size as usize;
        let mut buffer = Vec::with_capacity(size_squared);
        for _i in 0..size_squared {
            buffer.extend(&pixel_array[..]);
        }
        Image::from_rgba8(context, size, size, &buffer)
    }

    /// Return the width of the image.
    pub fn width(&self) -> u16 {
        self.width
    }

    /// Return the height of the image.
    pub fn height(&self) -> u16 {
        self.height
    }

    /// Get the filter mode for the image.
    pub fn filter(&self) -> FilterMode {
        self.sampler_info.filter.into()
    }

    /// Set the filter mode for the image.
    pub fn set_filter(&mut self, mode: FilterMode) {
        self.sampler_info.filter = mode.into();
    }

    /// Returns the dimensions of the image.
    pub fn dimensions(&self) -> Rect {
        Rect::new(0.0, 0.0, self.width() as f32, self.height() as f32)
    }

    /// Gets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn wrap(&self) -> (WrapMode, WrapMode) {
        (self.sampler_info.wrap_mode.0, self.sampler_info.wrap_mode.1)
    }

    /// Sets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn set_wrap(&mut self, wrap_x: WrapMode, wrap_y: WrapMode) {
        self.sampler_info.wrap_mode.0 = wrap_x;
        self.sampler_info.wrap_mode.1 = wrap_y;
    }
}

impl fmt::Debug for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "<Image: {}x{}, {:p}, texture address {:p}, sampler: {:?}>",
            self.width(),
            self.height(),
            self,
            &self.texture,
            &self.sampler_info
        )
    }
}

impl Drawable for Image {
    fn draw<D>(&self, ctx: &mut Context, param: D) -> GameResult
    where
        D: Into<DrawParam>,
    {
        let param = param.into();
        self.debug_id.assert(ctx);

        let gfx = &mut ctx.gfx_context;
        let src_width = param.src.w;
        let src_height = param.src.h;
        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        use nalgebra;
        let real_scale = nalgebra::Vector2::new(
            param.scale.x * src_width * f32::from(self.width),
            param.scale.y * src_height * f32::from(self.height),
        );

        let mut new_param = param;
        new_param.scale = real_scale;

        gfx.update_instance_properties(new_param.into())?;
        let sampler = gfx
            .samplers
            .get_or_insert(self.sampler_info, gfx.factory.as_mut());
        gfx.data.vbuf = gfx.quad_vertex_buffer.clone();
        let typed_thingy = gfx
            .backend_spec
            .raw_to_typed_shader_resource(self.texture.clone());
        gfx.data.tex = (typed_thingy, sampler);
        let previous_mode: Option<BlendMode> = if let Some(mode) = self.blend_mode {
            let current_mode = gfx.blend_mode();
            if current_mode != mode {
                gfx.set_blend_mode(mode)?;
                Some(current_mode)
            } else {
                None
            }
        } else {
            None
        };

        gfx.draw(None)?;
        if let Some(mode) = previous_mode {
            gfx.set_blend_mode(mode)?;
        }
        Ok(())
    }

    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.blend_mode = mode;
    }

    fn blend_mode(&self) -> Option<BlendMode> {
        self.blend_mode
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ContextBuilder;
    #[test]
    fn test_invalid_image_size() {
        let (ctx, _) = &mut ContextBuilder::new("unittest", "unittest").build().unwrap();
        let _i = assert!(Image::from_rgba8(ctx, 0, 0, &vec![]).is_err());
        let _i = assert!(Image::from_rgba8(ctx, 3432, 432, &vec![]).is_err());
        let _i = Image::from_rgba8(ctx, 2, 2, &vec![99; 16]).unwrap();
    }
}
<commit_msg>Change `use` statement so it compiles<commit_after>use std::io::Read;
use std::path;

use gfx;
use ::image;

use crate::context::{Context, DebugId};
use crate::error::GameError;
use crate::error::GameResult;
use crate::filesystem;
use crate::graphics::shader::*;
use crate::graphics::*;

/// Generic in-GPU-memory image data available to be drawn on the screen.
/// You probably just want to look at the `Image` type.
#[derive(Clone, PartialEq)]
pub struct ImageGeneric<B>
where
    B: BackendSpec,
{
    // TODO: Rename to shader_view or such.
    pub(crate) texture: gfx::handle::RawShaderResourceView<B::Resources>,
    pub(crate) texture_handle: gfx::handle::RawTexture<B::Resources>,
    pub(crate) sampler_info: gfx::texture::SamplerInfo,
    pub(crate) blend_mode: Option<BlendMode>,
    pub(crate) width: u16,
    pub(crate) height: u16,

    pub(crate) debug_id: DebugId,
}

impl<B> ImageGeneric<B>
where
    B: BackendSpec,
{
    /// A helper function that just takes a factory directly so we can make an image
    /// without needing the full context object, so we can create an Image while still
    /// creating the GraphicsContext.
    pub(crate) fn make_raw(
        factory: &mut <B as BackendSpec>::Factory,
        sampler_info: &texture::SamplerInfo,
        width: u16,
        height: u16,
        rgba: &[u8],
        color_format: gfx::format::Format,
        debug_id: DebugId,
    ) -> GameResult<Self> {
        if width == 0 || height == 0 {
            let msg = format!(
                "Tried to create a texture of size {}x{}, each dimension must
                be >0",
                width, height
            );
            return Err(GameError::ResourceLoadError(msg));
        }
        // Check for overflow, which might happen on 32-bit systems.
        // Textures can be max u16*u16, pixels, but then have 4 bytes per pixel.
        let uwidth = width as usize;
        let uheight = height as usize;
        let expected_bytes = uwidth
            .checked_mul(uheight)
            .and_then(|size| size.checked_mul(4))
            .ok_or_else(|| {
                let msg = format!(
                    "Integer overflow in Image::make_raw, image size: {} {}",
                    uwidth, uheight
                );
                GameError::ResourceLoadError(msg)
            })?;
        if expected_bytes != rgba.len() {
            let msg = format!(
                "Tried to create a texture of size {}x{}, but gave {} bytes of data (expected {})",
                width,
                height,
                rgba.len(),
                expected_bytes
            );
            return Err(GameError::ResourceLoadError(msg));
        }
        let kind = gfx::texture::Kind::D2(width, height, gfx::texture::AaMode::Single);
        use gfx::memory::Bind;
        let gfx::format::Format(surface_format, channel_type) = color_format;
        let texinfo = gfx::texture::Info {
            kind,
            levels: 1,
            format: surface_format,
            bind: Bind::SHADER_RESOURCE
                | Bind::RENDER_TARGET
                | Bind::TRANSFER_SRC
                | Bind::TRANSFER_DST,
            usage: gfx::memory::Usage::Dynamic,
        };
        let raw_tex = factory.create_texture_raw(
            texinfo,
            Some(channel_type),
            Some((&[rgba], gfx::texture::Mipmap::Provided)),
        )?;
        let resource_desc = gfx::texture::ResourceDesc {
            channel: channel_type,
            layer: None,
            min: 0,
            max: raw_tex.get_info().levels - 1,
            swizzle: gfx::format::Swizzle::new(),
        };
        let raw_view = factory.view_texture_as_shader_resource_raw(&raw_tex, resource_desc)?;
        Ok(Self {
            texture: raw_view,
            texture_handle: raw_tex,
            sampler_info: *sampler_info,
            blend_mode: None,
            width,
            height,
            debug_id,
        })
    }
}

/// In-GPU-memory image data available to be drawn on the screen,
/// using the OpenGL backend.
///
/// Under the hood this is just an `Arc`'ed texture handle and
/// some metadata, so cloning it is fairly cheap; it doesn't
/// make another copy of the underlying image data.
pub type Image = ImageGeneric<GlBackendSpec>;

/// The supported formats for saving an image.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ImageFormat {
    /// .png image format (defaults to RGBA with 8-bit channels.)
    Png,
}

impl Image {
    /// Load a new image from the file at the given path. The documentation for the
    /// [`filesystem`](../filesystem/index.html) module explains how the path must be specified.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let img = {
            let mut buf = Vec::new();
            let mut reader = context.filesystem.open(path)?;
            let _ = reader.read_to_end(&mut buf)?;
            image::load_from_memory(&buf)?.to_rgba()
        };
        let (width, height) = img.dimensions();
        Self::from_rgba8(context, width as u16, height as u16, &img)
    }

    /// Creates a new `Image` from the given buffer of `u8` RGBA values.
    ///
    /// The pixel layout is row-major.  That is,
    /// the first 4 `u8` values make the top-left pixel in the `Image`, the
    /// next 4 make the next pixel in the same row, and so on to the end of
    /// the row.  The next `width * 4` values make up the second row, and so
    /// on.
    pub fn from_rgba8(
        context: &mut Context,
        width: u16,
        height: u16,
        rgba: &[u8],
    ) -> GameResult<Self> {
        let debug_id = DebugId::get(context);
        let color_format = context.gfx_context.color_format();
        Self::make_raw(
            &mut *context.gfx_context.factory,
            &context.gfx_context.default_sampler_info,
            width,
            height,
            rgba,
            color_format,
            debug_id,
        )
    }

    /// Dumps the `Image`'s data to a `Vec` of `u8` RGBA values.
    pub fn to_rgba8(&self, ctx: &mut Context) -> GameResult<Vec<u8>> {
        use gfx::memory::Typed;
        use gfx::traits::FactoryExt;

        let gfx = &mut ctx.gfx_context;
        let w = self.width;
        let h = self.height;

        // Note: In the GFX example, the download buffer is created ahead of time
        // and updated on screen resize events. This may be preferable, but then
        // the buffer also needs to be updated when we switch to/from a canvas.
        // Unsure of the performance impact of creating this as it is needed.
        // Probably okay for now though, since this probably won't be a super
        // common operation.
        let dl_buffer = gfx
            .factory
            .create_download_buffer::<[u8; 4]>(w as usize * h as usize)?;

        let factory = &mut *gfx.factory;
        let mut local_encoder = GlBackendSpec::encoder(factory);

        local_encoder.copy_texture_to_buffer_raw(
            &self.texture_handle,
            None,
            gfx::texture::RawImageInfo {
                xoffset: 0,
                yoffset: 0,
                zoffset: 0,
                width: w as u16,
                height: h as u16,
                depth: 0,
                format: gfx.color_format(),
                mipmap: 0,
            },
            dl_buffer.raw(),
            0,
        )?;
        local_encoder.flush(&mut *gfx.device);

        let reader = gfx.factory.read_mapping(&dl_buffer)?;

        // intermediary buffer to avoid casting
        // and also to reverse the order in which we pass the rows
        // so the screenshot isn't upside-down
        let mut data = Vec::with_capacity(self.width as usize * self.height as usize * 4);
        for row in reader.chunks(w as usize).rev() {
            // TODO: Test if we can just do row.flatten() here?
            // data.extend(row.iter().flatten());
            for pixel in row.iter() {
                data.extend(pixel);
            }
        }
        Ok(data)
    }

    /// Encode the `Image` to the given file format and
    /// write it out to the given path.
    ///
    /// See the [`filesystem`](../filesystem/index.html) module docs for where exactly
    /// the file will end up.
    pub fn encode<P: AsRef<path::Path>>(
        &self,
        ctx: &mut Context,
        format: ImageFormat,
        path: P,
    ) -> GameResult {
        use std::io;
        let data = self.to_rgba8(ctx)?;
        let f = filesystem::create(ctx, path)?;
        let writer = &mut io::BufWriter::new(f);
        let color_format = image::ColorType::RGBA(8);
        match format {
            ImageFormat::Png => image::png::PNGEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width),
                    u32::from(self.height),
                    color_format,
                )
                .map_err(|e| e.into()),
        }
    }

    /// A little helper function that creates a new Image that is just
    /// a solid square of the given size and color.  Mainly useful for
    /// debugging.
    pub fn solid(context: &mut Context, size: u16, color: Color) -> GameResult<Self> {
        let (r, g, b, a) = color.into();
        let pixel_array: [u8; 4] = [r, g, b, a];
        let size_squared = size as usize * size as usize;
        let mut buffer = Vec::with_capacity(size_squared);
        for _i in 0..size_squared {
            buffer.extend(&pixel_array[..]);
        }
        Image::from_rgba8(context, size, size, &buffer)
    }

    /// Return the width of the image.
    pub fn width(&self) -> u16 {
        self.width
    }

    /// Return the height of the image.
    pub fn height(&self) -> u16 {
        self.height
    }

    /// Get the filter mode for the image.
    pub fn filter(&self) -> FilterMode {
        self.sampler_info.filter.into()
    }

    /// Set the filter mode for the image.
    pub fn set_filter(&mut self, mode: FilterMode) {
        self.sampler_info.filter = mode.into();
    }

    /// Returns the dimensions of the image.
    pub fn dimensions(&self) -> Rect {
        Rect::new(0.0, 0.0, self.width() as f32, self.height() as f32)
    }

    /// Gets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn wrap(&self) -> (WrapMode, WrapMode) {
        (self.sampler_info.wrap_mode.0, self.sampler_info.wrap_mode.1)
    }

    /// Sets the `Image`'s `WrapMode` along the X and Y axes.
    pub fn set_wrap(&mut self, wrap_x: WrapMode, wrap_y: WrapMode) {
        self.sampler_info.wrap_mode.0 = wrap_x;
        self.sampler_info.wrap_mode.1 = wrap_y;
    }
}

impl fmt::Debug for Image {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "<Image: {}x{}, {:p}, texture address {:p}, sampler: {:?}>",
            self.width(),
            self.height(),
            self,
            &self.texture,
            &self.sampler_info
        )
    }
}

impl Drawable for Image {
    fn draw<D>(&self, ctx: &mut Context, param: D) -> GameResult
    where
        D: Into<DrawParam>,
    {
        let param = param.into();
        self.debug_id.assert(ctx);

        let gfx = &mut ctx.gfx_context;
        let src_width = param.src.w;
        let src_height = param.src.h;
        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        use nalgebra;
        let real_scale = nalgebra::Vector2::new(
            param.scale.x * src_width * f32::from(self.width),
            param.scale.y * src_height * f32::from(self.height),
        );

        let mut new_param = param;
        new_param.scale = real_scale;

        gfx.update_instance_properties(new_param.into())?;
        let sampler = gfx
            .samplers
            .get_or_insert(self.sampler_info, gfx.factory.as_mut());
        gfx.data.vbuf = gfx.quad_vertex_buffer.clone();
        let typed_thingy = gfx
            .backend_spec
            .raw_to_typed_shader_resource(self.texture.clone());
        gfx.data.tex = (typed_thingy, sampler);
        let previous_mode: Option<BlendMode> = if let Some(mode) = self.blend_mode {
            let current_mode = gfx.blend_mode();
            if current_mode != mode {
                gfx.set_blend_mode(mode)?;
                Some(current_mode)
            } else {
                None
            }
        } else {
            None
        };

        gfx.draw(None)?;
        if let Some(mode) = previous_mode {
            gfx.set_blend_mode(mode)?;
        }
        Ok(())
    }

    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.blend_mode = mode;
    }

    fn blend_mode(&self) -> Option<BlendMode> {
        self.blend_mode
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ContextBuilder;
    #[test]
    fn test_invalid_image_size() {
        let (ctx, _) = &mut ContextBuilder::new("unittest", "unittest").build().unwrap();
        let _i = assert!(Image::from_rgba8(ctx, 0, 0, &vec![]).is_err());
        let _i = assert!(Image::from_rgba8(ctx, 3432, 432, &vec![]).is_err());
        let _i = Image::from_rgba8(ctx, 2, 2, &vec![99; 16]).unwrap();
    }
}
<|endoftext|>


<commit_before>use super::*;
use super::NumNode::*;
use super::ExprNode::*;

use svm::cell::Atom::*;
use svm::cell::Inst::*;
use svm::cell::SVMCell::*;

#[test]
fn test_compile_add() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_sub() {
    let ast = SExprNode {
        operator: NameNode { name: "-".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB)
        ])
    )
}

#[test]
fn test_compile_div() {
    let ast = SExprNode {
        operator: NameNode { name: "/".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
            NumConst(IntConst(IntNode{ value: 3isize })),
            NumConst(IntConst(IntNode{ value: 4isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(4isize)),
            InstCell(LDC), AtomCell(SInt(3isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(1isize)),
            InstCell(DIV)

        ])
    )
}

#[test]
fn test_compile_mul() {
    let ast = SExprNode {
        operator: NameNode { name: "*".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(FloatConst(FloatNode{ value: 2f64 })),
            NumConst(FloatConst(FloatNode{ value: 3f64 })),
            NumConst(FloatConst(FloatNode{ value: 4f64 }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(Float(4f64)),
            InstCell(LDC), AtomCell(Float(3f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(2f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(MUL)
        ])
    )
}<commit_msg>Add more AST tests<commit_after>use super::*;
use super::NumNode::*;
use super::ExprNode::*;

use svm::cell::Atom::*;
use svm::cell::Inst::*;
use svm::cell::SVMCell::*;

#[test]
fn test_compile_add() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_sub() {
    let ast = SExprNode {
        operator: NameNode { name: "-".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB)
        ])
    )
}

#[test]
fn test_compile_div() {
    let ast = SExprNode {
        operator: NameNode { name: "/".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
            NumConst(IntConst(IntNode{ value: 3isize })),
            NumConst(IntConst(IntNode{ value: 4isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(4isize)),
            InstCell(LDC), AtomCell(SInt(3isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(1isize)),
            InstCell(DIV)

        ])
    )
}

#[test]
fn test_compile_mul() {
    let ast = SExprNode {
        operator: NameNode { name: "*".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(FloatConst(FloatNode{ value: 2f64 })),
            NumConst(FloatConst(FloatNode{ value: 3f64 })),
            NumConst(FloatConst(FloatNode{ value: 4f64 }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(Float(4f64)),
            InstCell(LDC), AtomCell(Float(3f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(2f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(MUL)
        ])
    )
}

#[test]
fn test_compile_nested_sexpr() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 4isize })),
            SExpr(SExprNode {
                operator: NameNode { name: "-".to_string() },
                operands: vec![
                    NumConst(IntConst(IntNode{ value: 1isize })),
                    NumConst(IntConst(IntNode{ value: 2isize })),
                    NumConst(IntConst(IntNode{ value: 3isize }))
                ]
            })
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(3)),
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(4)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_gte() {
    let ast = SExprNode {
        operator: NameNode { name: ">=".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(IntConst(IntNode{ value: 2isize })),
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(GTE)
        ])
    )
}

#[test]
fn test_compile_lte() {
    let ast = SExprNode {
        operator: NameNode { name: "<=".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 3usize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(LDC), AtomCell(UInt(3usize)),
            InstCell(LTE)
        ])
    )
}

<|endoftext|>


<commit_before>use crate::controllers::frontend_prelude::*;

use crate::models::{CrateOwner, OwnerKind, User};
use crate::schema::{crate_owners, crates, users};
use crate::sql::lower;
use crate::views::EncodablePublicUser;

/// Handles the `GET /users/:user_id` route.
pub fn show(req: &mut dyn RequestExt) -> EndpointResult {
    use self::users::dsl::{gh_login, id, users};

    let name = lower(&req.params()["user_id"]);
    let conn = req.db_write()?;
    let user: User = users
        .filter(lower(gh_login).eq(name))
        .order(id.desc())
        .first(&*conn)?;

    Ok(req.json(&json!({ "user": EncodablePublicUser::from(user) })))
}

/// Handles the `GET /users/:user_id/stats` route.
pub fn stats(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::sum;

    let user_id = &req.params()["user_id"]
        .parse::<i32>()
        .map_err(|err| err.chain(bad_request("invalid user_id")))?;
    let conn = req.db_write()?;

    let data: i64 = CrateOwner::by_owner_kind(OwnerKind::User)
        .inner_join(crates::table)
        .filter(crate_owners::owner_id.eq(user_id))
        .select(sum(crates::downloads))
        .first::<Option<i64>>(&*conn)?
        .unwrap_or(0);

    Ok(req.json(&json!({ "total_downloads": data })))
}
<commit_msg>Use `db_read_prefer_primary()` for `GET /users/:user_id` endpoint<commit_after>use crate::controllers::frontend_prelude::*;

use crate::models::{CrateOwner, OwnerKind, User};
use crate::schema::{crate_owners, crates, users};
use crate::sql::lower;
use crate::views::EncodablePublicUser;

/// Handles the `GET /users/:user_id` route.
pub fn show(req: &mut dyn RequestExt) -> EndpointResult {
    use self::users::dsl::{gh_login, id, users};

    let name = lower(&req.params()["user_id"]);
    let conn = req.db_read_prefer_primary()?;
    let user: User = users
        .filter(lower(gh_login).eq(name))
        .order(id.desc())
        .first(&*conn)?;

    Ok(req.json(&json!({ "user": EncodablePublicUser::from(user) })))
}

/// Handles the `GET /users/:user_id/stats` route.
pub fn stats(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::sum;

    let user_id = &req.params()["user_id"]
        .parse::<i32>()
        .map_err(|err| err.chain(bad_request("invalid user_id")))?;
    let conn = req.db_write()?;

    let data: i64 = CrateOwner::by_owner_kind(OwnerKind::User)
        .inner_join(crates::table)
        .filter(crate_owners::owner_id.eq(user_id))
        .select(sum(crates::downloads))
        .first::<Option<i64>>(&*conn)?
        .unwrap_or(0);

    Ok(req.json(&json!({ "total_downloads": data })))
}
<|endoftext|>


<commit_before>//! Higher order geometric traits.
//!
//! This module defines higher order traits for operations on a mesh. It also
//! provides aliases for geometric types to improve readability of type
//! constraints.

use std::ops::Sub;

use geometry::Geometry;
use geometry::convert::AsPosition;
use geometry::ops::{Average, Cross, Interpolate, Normalize};
use graph::topology::{EdgeRef, FaceRef};
use self::alias::*;

pub trait FaceNormal: Geometry {
    type Normal;

    fn normal(face: FaceRef<Self>) -> Result<Self::Normal, ()>;
}

impl<G> FaceNormal for G
where
    G: Geometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Clone + Sub,
    <VertexPosition<G> as Sub>::Output: Cross,
    <<VertexPosition<G> as Sub>::Output as Cross>::Output: Normalize,
{
    type Normal = <<VertexPosition<G> as Sub>::Output as Cross>::Output;

    fn normal(face: FaceRef<Self>) -> Result<Self::Normal, ()> {
        let positions = face.vertices()
            .take(3)
            .map(|vertex| vertex.geometry.as_position().clone())
            .collect::<Vec<_>>();
        let (a, b, c) = (&positions[0], &positions[1], &positions[2]);
        let ab = a.clone() - b.clone();
        let bc = b.clone() - c.clone();
        Ok(ab.cross(bc).normalize())
    }
}

pub trait FaceCentroid: Geometry {
    type Centroid;

    fn centroid(face: FaceRef<Self>) -> Result<Self::Centroid, ()>;
}

impl<G> FaceCentroid for G
where
    G: Geometry,
    G::Vertex: Average,
{
    type Centroid = G::Vertex;

    fn centroid(face: FaceRef<Self>) -> Result<Self::Centroid, ()> {
        Ok(G::Vertex::average(
            face.vertices().map(|vertex| vertex.geometry.clone()),
        ))
    }
}

pub trait EdgeMidpoint: Geometry {
    type Midpoint;

    fn midpoint(edge: EdgeRef<Self>) -> Result<Self::Midpoint, ()>;
}

impl<G> EdgeMidpoint for G
where
    G: Geometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Clone + Interpolate,
{
    type Midpoint = <VertexPosition<G> as Interpolate>::Output;

    fn midpoint(edge: EdgeRef<Self>) -> Result<Self::Midpoint, ()> {
        let a = edge.source_vertex().geometry.as_position().clone();
        let b = edge.destination_vertex().geometry.as_position().clone();
        Ok(a.midpoint(b))
    }
}

pub trait EdgeLateral: Geometry {
    type Lateral;

    fn lateral(edge: EdgeRef<Self>) -> Result<Self::Lateral, ()>;
}

// TODO: rustfmt mangles the type constraints here.
#[cfg_attr(rustfmt, rustfmt_skip)]
impl<G> EdgeLateral for G
where
    G: FaceNormal + Geometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Clone + Sub,
    <VertexPosition<G> as Sub>::Output: Cross,
    <VertexPosition<G> as Sub>::Output: Cross<<G as FaceNormal>::Normal>,
    <<VertexPosition<G> as Sub>::Output as Cross>::Output: Normalize,
    <<VertexPosition<G> as Sub>::Output as Cross<<G as FaceNormal>::Normal>>::Output: Normalize,
{
    type Lateral = <<VertexPosition<G> as Sub>::Output as Cross<<G as FaceNormal>::Normal>>::Output;

    fn lateral(edge: EdgeRef<Self>) -> Result<Self::Lateral, ()> {
        let a = edge.source_vertex().geometry.as_position().clone();
        let b = edge.destination_vertex().geometry.as_position().clone();
        let ab = a - b;
        let normal = <G as FaceNormal>::normal(edge.face().ok_or(())?)?;
        Ok(ab.cross(normal).normalize())
    }
}

pub mod alias {
    use std::ops::Mul;

    use super::*;

    pub type VertexPosition<G> =
        <<G as Geometry>::Vertex as AsPosition>::Target;
    pub type ScaledFaceNormal<G, T> =
        <<G as FaceNormal>::Normal as Mul<T>>::Output;
    pub type ScaledEdgeLateral<G, T> =
        <<G as EdgeLateral>::Lateral as Mul<T>>::Output;
}
<commit_msg>Upgrade rustfmt and reformat.<commit_after>//! Higher order geometric traits.
//!
//! This module defines higher order traits for operations on a mesh. It also
//! provides aliases for geometric types to improve readability of type
//! constraints.

use std::ops::Sub;

use geometry::Geometry;
use geometry::convert::AsPosition;
use geometry::ops::{Average, Cross, Interpolate, Normalize};
use graph::topology::{EdgeRef, FaceRef};
use self::alias::*;

pub trait FaceNormal: Geometry {
    type Normal;

    fn normal(face: FaceRef<Self>) -> Result<Self::Normal, ()>;
}

impl<G> FaceNormal for G
where
    G: Geometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Clone + Sub,
    <VertexPosition<G> as Sub>::Output: Cross,
    <<VertexPosition<G> as Sub>::Output as Cross>::Output: Normalize,
{
    type Normal = <<VertexPosition<G> as Sub>::Output as Cross>::Output;

    fn normal(face: FaceRef<Self>) -> Result<Self::Normal, ()> {
        let positions = face.vertices()
            .take(3)
            .map(|vertex| vertex.geometry.as_position().clone())
            .collect::<Vec<_>>();
        let (a, b, c) = (&positions[0], &positions[1], &positions[2]);
        let ab = a.clone() - b.clone();
        let bc = b.clone() - c.clone();
        Ok(ab.cross(bc).normalize())
    }
}

pub trait FaceCentroid: Geometry {
    type Centroid;

    fn centroid(face: FaceRef<Self>) -> Result<Self::Centroid, ()>;
}

impl<G> FaceCentroid for G
where
    G: Geometry,
    G::Vertex: Average,
{
    type Centroid = G::Vertex;

    fn centroid(face: FaceRef<Self>) -> Result<Self::Centroid, ()> {
        Ok(G::Vertex::average(
            face.vertices().map(|vertex| vertex.geometry.clone()),
        ))
    }
}

pub trait EdgeMidpoint: Geometry {
    type Midpoint;

    fn midpoint(edge: EdgeRef<Self>) -> Result<Self::Midpoint, ()>;
}

impl<G> EdgeMidpoint for G
where
    G: Geometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Clone + Interpolate,
{
    type Midpoint = <VertexPosition<G> as Interpolate>::Output;

    fn midpoint(edge: EdgeRef<Self>) -> Result<Self::Midpoint, ()> {
        let a = edge.source_vertex().geometry.as_position().clone();
        let b = edge.destination_vertex().geometry.as_position().clone();
        Ok(a.midpoint(b))
    }
}

pub trait EdgeLateral: Geometry {
    type Lateral;

    fn lateral(edge: EdgeRef<Self>) -> Result<Self::Lateral, ()>;
}

impl<G> EdgeLateral for G
where
    G: FaceNormal + Geometry,
    G::Vertex: AsPosition,
    VertexPosition<G>: Clone + Sub,
    <VertexPosition<G> as Sub>::Output: Cross,
    <VertexPosition<G> as Sub>::Output: Cross<<G as FaceNormal>::Normal>,
    <<VertexPosition<G> as Sub>::Output as Cross>::Output: Normalize,
    <<VertexPosition<G> as Sub>::Output as Cross<<G as FaceNormal>::Normal>>::Output: Normalize,
{
    type Lateral = <<VertexPosition<G> as Sub>::Output as Cross<<G as FaceNormal>::Normal>>::Output;

    fn lateral(edge: EdgeRef<Self>) -> Result<Self::Lateral, ()> {
        let a = edge.source_vertex().geometry.as_position().clone();
        let b = edge.destination_vertex().geometry.as_position().clone();
        let ab = a - b;
        let normal = <G as FaceNormal>::normal(edge.face().ok_or(())?)?;
        Ok(ab.cross(normal).normalize())
    }
}

pub mod alias {
    use std::ops::Mul;

    use super::*;

    pub type VertexPosition<G> = <<G as Geometry>::Vertex as AsPosition>::Target;
    pub type ScaledFaceNormal<G, T> = <<G as FaceNormal>::Normal as Mul<T>>::Output;
    pub type ScaledEdgeLateral<G, T> = <<G as EdgeLateral>::Lateral as Mul<T>>::Output;
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use super::{ElfResult, ElfWord, Section, section};

use core::{fmt, mem};

/// An ELF file header
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Header<W: ElfWord> {
    pub ident: Ident
  , elftype: TypeRepr
  , pub machine: Machine
  , /// Program entry point
    pub entry_point: W
  , /// Offset for start of program headers
    pub ph_offset: W
  , /// Offset for start of section headers
    pub sh_offset: W
  , pub flags: u32
  , pub header_size: u16
  , pub ph_entry_size: u16
  , pub ph_count: u16
  , pub sh_entry_size: u16
  , pub sh_count: u16
  , pub sh_str_idx: u16
}

impl<W> Header<W> where W: ElfWord {

    /// Attempt to extract an ELF file header from a slice of bytes.
    pub fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Header<W>> {
        if input.len() < mem::size_of::<Header<W>>() {
            Err("Input too short to extract ELF header")
        } else {
            unsafe { Ok(&super::extract_from_slice::<Header<W>>(input, 0, 1)[0]) }
        }
    }

    #[inline]
    pub fn get_type(&self) -> Type { self.elftype.as_type() }

}

impl Header<u64> {

    /// Attempt to extract a section header from a slice of bytes.
    pub fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                            -> ElfResult<&'a Section>
    {
        if idx < section::SHN_LORESERVE {
            Err("Cannot parse reserved section.")
        } else {
            let start: u64// start offset for section
                = self.sh_offset + idx as u64 * self.sh_entry_size as u64;
            let end: u64 // end offset for section
                = start + self.sh_entry_size as u64;
            let raw
                = &input[start as usize .. end as usize];

            match self.ident.class {
                Class::None => Err("Invalid ELF class (ELFCLASSNONE).")
              , Class::Elf32 => Err("Cannot parse 32-bit section from 64-bit \
                                     ELF file.")
              , Class::Elf64 => unsafe {
                    Ok(mem::transmute(raw as *const [u8] as *const u8 as *const Section))
                }
            }
        }
    }

}

impl Header<u32> {

    /// Attempt to extract a section header from a slice of bytes.
    pub fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                            -> ElfResult<&'a Section>
    {
        if idx < section::SHN_LORESERVE {
            Err("Cannot parse reserved section.")
        } else {
            let start: u32// start offset for section
                = self.sh_offset + idx as u32 * self.sh_entry_size as u32;
            let end: u32 // end offset for section
                = start + self.sh_entry_size as u32;
            let raw
                = &input[start as usize .. end as usize];

            match self.ident.class {
                Class::None => Err("Invalid ELF class (ELFCLASSNONE).")
              , Class::Elf32 => unsafe {
                    Ok(mem::transmute(raw as *const [u8] as *const u8 as *const Section))
                }
              , Class::Elf64 => Err("Cannot parse 64-bit section from 32-bit \
                                     ELF file.")
            }
        }
    }

}

/// ELF header magic
pub const MAGIC: Magic = [0x7f, b'E', b'L', b'F'];

/// Type of header magic
pub type Magic = [u8; 4];

/// ELF identifier (`e_ident` in the ELF standard)
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Ident {
    /// ELF magic numbers. Must be [0x7, E, L, F]
    pub magic: Magic
  , /// ELF file class (32- or 64-bit)
    pub class: Class
  , /// ELF data encoding (big- or little-endian)
    pub data: DataEncoding
  , /// ELF file version
    pub version: Version
  , pub abi: OsAbi
  , /// ABI version (often this is just padding)
    pub abi_version: u8
  , _padding: [u8; 7]
}

impl Ident {
    #[inline] pub fn check_magic(&self) -> bool { self.magic == MAGIC }
}

/// Identifies the class of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Class {
    /// Invalid ELF class file (`ELFCLASSNONE` in the standard)
    None  = 0
  , /// 32-bit ELF file (`ELFCLASS32` in the standard)
    Elf32 = 1
  , /// 64-bit ELF file (`ELFCLASS64` in the standard)
    Elf64 = 2
}

/// Identifies the data encoding of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum DataEncoding {
    /// Invalid data encoding (`ELFDATANONE` in the standard)
    None  = 0
  , /// Twos-complement little-endian data encoding
    /// (`ELFDATA2LSB` in the standard)
    LittleEndian = 1
  , /// Twos-complement big-endian data encoding
    /// (`ELFDATA2MSB` in the standard)
    BigEndian = 2
}

/// Operating system ABI
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum OsAbi { /// Ox00 also represents "none"
                 SystemV = 0x00
               , HpUx    = 0x01
               , NetBsd  = 0x02
               , Linux   = 0x03
               , Solaris = 0x06
               , Aix     = 0x07
               , Irix    = 0x08
               , FreeBsd = 0x09
               , OpenBsd = 0x0C
               , OpenVms = 0x0D
               }

/// Identifies the version of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Version { None = 0, Current = 1 }

#[derive(Clone, Copy, PartialEq)]
struct TypeRepr(u16);

impl TypeRepr {
    pub fn as_type(&self) -> Type {
        match self.0 {
            0 => Type::None
          , 1 => Type::Relocatable
          , 2 => Type::Executable
          , 3 => Type::SharedObject
          , 4 => Type::Core
          , anything => Type::Other(anything)
        }
    }
}

impl fmt::Debug for TypeRepr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_type().fmt(f)
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Type { None
              , Relocatable
              , Executable
              , SharedObject
              , Core
              , Other(u16)
              }

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u16)]
pub enum Machine { None    = 0
                 , Sparc   = 0x02
                 , X86     = 0x03
                 , Mips    = 0x08
                 , PowerPc = 0x14
                 , Arm     = 0x28
                 , SuperH  = 0x2A
                 , Ia64    = 0x32
                 , X86_64  = 0x3E
                 , AArch64 = 0xB7
                 }
<commit_msg>[elf] replace some mem::transmutes with raw pointer deref according to https://doc.rust-lang.org/stable/book/raw-pointers.html#references-and-raw-pointers, this is better style<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use super::{ElfResult, ElfWord, Section, section};

use core::{fmt, mem};

/// An ELF file header
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Header<W: ElfWord> {
    pub ident: Ident
  , elftype: TypeRepr
  , pub machine: Machine
  , /// Program entry point
    pub entry_point: W
  , /// Offset for start of program headers
    pub ph_offset: W
  , /// Offset for start of section headers
    pub sh_offset: W
  , pub flags: u32
  , pub header_size: u16
  , pub ph_entry_size: u16
  , pub ph_count: u16
  , pub sh_entry_size: u16
  , pub sh_count: u16
  , pub sh_str_idx: u16
}

impl<W> Header<W> where W: ElfWord {

    /// Attempt to extract an ELF file header from a slice of bytes.
    pub fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Header<W>> {
        if input.len() < mem::size_of::<Header<W>>() {
            Err("Input too short to extract ELF header")
        } else {
            unsafe { Ok(&super::extract_from_slice::<Header<W>>(input, 0, 1)[0]) }
        }
    }

    #[inline]
    pub fn get_type(&self) -> Type { self.elftype.as_type() }

}

impl Header<u64> {

    /// Attempt to extract a section header from a slice of bytes.
    pub fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                            -> ElfResult<&'a Section>
    {
        if idx < section::SHN_LORESERVE {
            Err("Cannot parse reserved section.")
        } else {
            let start: u64// start offset for section
                = self.sh_offset + idx as u64 * self.sh_entry_size as u64;
            let end: u64 // end offset for section
                = start + self.sh_entry_size as u64;
            let raw
                = &input[start as usize .. end as usize];

            match self.ident.class {
                Class::None => Err("Invalid ELF class (ELFCLASSNONE).")
              , Class::Elf32 => Err("Cannot parse 32-bit section from 64-bit \
                                     ELF file.")
              , Class::Elf64 => unsafe {
                    Ok(&*(raw as *const [u8] as *const u8 as *const Section))
                }
            }
        }
    }

}

impl Header<u32> {

    /// Attempt to extract a section header from a slice of bytes.
    pub fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                            -> ElfResult<&'a Section>
    {
        if idx < section::SHN_LORESERVE {
            Err("Cannot parse reserved section.")
        } else {
            let start: u32// start offset for section
                = self.sh_offset + idx as u32 * self.sh_entry_size as u32;
            let end: u32 // end offset for section
                = start + self.sh_entry_size as u32;
            let raw
                = &input[start as usize .. end as usize];

            match self.ident.class {
                Class::None => Err("Invalid ELF class (ELFCLASSNONE).")
              , Class::Elf32 => unsafe {
                    Ok(&*(raw as *const [u8] as *const u8 as *const Section))
                }
              , Class::Elf64 => Err("Cannot parse 64-bit section from 32-bit \
                                     ELF file.")
            }
        }
    }

}

/// ELF header magic
pub const MAGIC: Magic = [0x7f, b'E', b'L', b'F'];

/// Type of header magic
pub type Magic = [u8; 4];

/// ELF identifier (`e_ident` in the ELF standard)
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Ident {
    /// ELF magic numbers. Must be [0x7, E, L, F]
    pub magic: Magic
  , /// ELF file class (32- or 64-bit)
    pub class: Class
  , /// ELF data encoding (big- or little-endian)
    pub data: DataEncoding
  , /// ELF file version
    pub version: Version
  , pub abi: OsAbi
  , /// ABI version (often this is just padding)
    pub abi_version: u8
  , _padding: [u8; 7]
}

impl Ident {
    #[inline] pub fn check_magic(&self) -> bool { self.magic == MAGIC }
}

/// Identifies the class of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Class {
    /// Invalid ELF class file (`ELFCLASSNONE` in the standard)
    None  = 0
  , /// 32-bit ELF file (`ELFCLASS32` in the standard)
    Elf32 = 1
  , /// 64-bit ELF file (`ELFCLASS64` in the standard)
    Elf64 = 2
}

/// Identifies the data encoding of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum DataEncoding {
    /// Invalid data encoding (`ELFDATANONE` in the standard)
    None  = 0
  , /// Twos-complement little-endian data encoding
    /// (`ELFDATA2LSB` in the standard)
    LittleEndian = 1
  , /// Twos-complement big-endian data encoding
    /// (`ELFDATA2MSB` in the standard)
    BigEndian = 2
}

/// Operating system ABI
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum OsAbi { /// Ox00 also represents "none"
                 SystemV = 0x00
               , HpUx    = 0x01
               , NetBsd  = 0x02
               , Linux   = 0x03
               , Solaris = 0x06
               , Aix     = 0x07
               , Irix    = 0x08
               , FreeBsd = 0x09
               , OpenBsd = 0x0C
               , OpenVms = 0x0D
               }

/// Identifies the version of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Version { None = 0, Current = 1 }

#[derive(Clone, Copy, PartialEq)]
struct TypeRepr(u16);

impl TypeRepr {
    pub fn as_type(&self) -> Type {
        match self.0 {
            0 => Type::None
          , 1 => Type::Relocatable
          , 2 => Type::Executable
          , 3 => Type::SharedObject
          , 4 => Type::Core
          , anything => Type::Other(anything)
        }
    }
}

impl fmt::Debug for TypeRepr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_type().fmt(f)
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Type { None
              , Relocatable
              , Executable
              , SharedObject
              , Core
              , Other(u16)
              }

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u16)]
pub enum Machine { None    = 0
                 , Sparc   = 0x02
                 , X86     = 0x03
                 , Mips    = 0x08
                 , PowerPc = 0x14
                 , Arm     = 0x28
                 , SuperH  = 0x2A
                 , Ia64    = 0x32
                 , X86_64  = 0x3E
                 , AArch64 = 0xB7
                 }
<|endoftext|>


<commit_before>use colours::{Plain, Style, Red, Green, Yellow, Blue, Cyan, Fixed};
use file::File;
use std::io;

pub enum FileType {
    Normal, Directory, Executable, Immediate, Compiled, Symlink, Special,
    Image, Video, Music, Lossless, Compressed, Document, Temp, Crypto,
}

static IMAGE_TYPES: &'static [&'static str] = &[
    "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
    "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
    "svg", "stl", "eps", "dvi", "ps", "cbr",
    "cbz", "xpm", "ico" ];

static VIDEO_TYPES: &'static [&'static str] = &[
    "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg",
    "mpg", "ogm", "ogv", "vob", "wmv" ];

static MUSIC_TYPES: &'static [&'static str] = &[
    "aac", "m4a", "mp3", "ogg" ];

static MUSIC_LOSSLESS: &'static [&'static str] = &[
    "alac", "ape", "flac", "wav" ];

static COMPRESSED_TYPES: &'static [&'static str] = &[
    "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z",
    "iso", "dmg", "tc", "rar", "par" ];

static DOCUMENT_TYPES: &'static [&'static str] = &[
    "djvu", "doc", "docx", "eml", "eps", "odp", "ods",
    "odt", "pdf", "ppt", "pptx", "xls", "xlsx" ];

static TEMP_TYPES: &'static [&'static str] = &[
    "tmp", "swp", "swo", "swn", "bak" ];

static CRYPTO_TYPES: &'static [&'static str] = &[
    "asc", "gpg", "sig", "signature", "pgp" ];

static COMPILED_TYPES: &'static [&'static str] = &[
    "class", "elc", "hi", "o", "pyc" ];

impl FileType {
    pub fn style(&self) -> Style {
        match *self {
            Normal => Plain,
            Directory => Blue.bold(),
            Symlink => Cyan.normal(),
            Special => Yellow.normal(),
            Executable => Green.bold(),
            Image => Fixed(133).normal(),
            Video => Fixed(135).normal(),
            Music => Fixed(92).normal(),
            Lossless => Fixed(93).normal(),
            Crypto => Fixed(109).normal(),
            Document => Fixed(105).normal(),
            Compressed => Red.normal(),
            Temp => Fixed(244).normal(),
            Immediate => Yellow.bold().underline(),
            Compiled => Fixed(137).normal(),
        }
    }
}

pub trait HasType {
    fn get_type(&self) -> FileType;
}

impl<'a> HasType for File<'a> {
    fn get_type(&self) -> FileType {
        if self.stat.kind == io::TypeDirectory {
            return Directory;
        }
        else if self.stat.kind == io::TypeSymlink {
            return Symlink;
        }
        else if self.stat.kind == io::TypeBlockSpecial || self.stat.kind == io::TypeNamedPipe || self.stat.kind == io::TypeUnknown {
            return Special;
        }
        else if self.stat.perm.contains(io::UserExecute) {
            return Executable;
        }
        else if self.name.starts_with("README") {
            return Immediate;
        }
        else if self.ext.is_some() {
            let ext = self.ext.unwrap();
            if IMAGE_TYPES.iter().any(|&s| s == ext) {
                return Image;
            }
            else if VIDEO_TYPES.iter().any(|&s| s == ext) {
                return Video;
            }
            else if MUSIC_TYPES.iter().any(|&s| s == ext) {
                return Music;
            }
            else if MUSIC_LOSSLESS.iter().any(|&s| s == ext) {
                return Lossless;
            }
            else if CRYPTO_TYPES.iter().any(|&s| s == ext) {
                return Crypto;
            }
            else if DOCUMENT_TYPES.iter().any(|&s| s == ext) {
                return Document;
            }
            else if COMPRESSED_TYPES.iter().any(|&s| s == ext) {
                return Compressed;
            }
            else if self.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) {
                return Temp;
            }

            let source_files = self.get_source_files();
            if source_files.len() == 0 {
                return Normal;
            }
            else if source_files.iter().any(|path| self.dir.contains(path)) {
                return Temp;
            }
            else {
                if COMPILED_TYPES.iter().any(|&s| s == ext) {
                    return Compiled;
                }
                else {
                    return Normal;
                }
            }
        }
        return Normal;  // no filetype
    }
}

<commit_msg>Highlight common build files<commit_after>use colours::{Plain, Style, Red, Green, Yellow, Blue, Cyan, Fixed};
use file::File;
use std::io;

pub enum FileType {
    Normal, Directory, Executable, Immediate, Compiled, Symlink, Special,
    Image, Video, Music, Lossless, Compressed, Document, Temp, Crypto,
}

static IMAGE_TYPES: &'static [&'static str] = &[
    "png", "jpeg", "jpg", "gif", "bmp", "tiff", "tif",
    "ppm", "pgm", "pbm", "pnm", "webp", "raw", "arw",
    "svg", "stl", "eps", "dvi", "ps", "cbr",
    "cbz", "xpm", "ico" ];

static VIDEO_TYPES: &'static [&'static str] = &[
    "avi", "flv", "m2v", "mkv", "mov", "mp4", "mpeg",
    "mpg", "ogm", "ogv", "vob", "wmv" ];

static MUSIC_TYPES: &'static [&'static str] = &[
    "aac", "m4a", "mp3", "ogg" ];

static MUSIC_LOSSLESS: &'static [&'static str] = &[
    "alac", "ape", "flac", "wav" ];

static COMPRESSED_TYPES: &'static [&'static str] = &[
    "zip", "tar", "Z", "gz", "bz2", "a", "ar", "7z",
    "iso", "dmg", "tc", "rar", "par" ];

static DOCUMENT_TYPES: &'static [&'static str] = &[
    "djvu", "doc", "docx", "eml", "eps", "odp", "ods",
    "odt", "pdf", "ppt", "pptx", "xls", "xlsx" ];

static TEMP_TYPES: &'static [&'static str] = &[
    "tmp", "swp", "swo", "swn", "bak" ];

static CRYPTO_TYPES: &'static [&'static str] = &[
    "asc", "gpg", "sig", "signature", "pgp" ];

static COMPILED_TYPES: &'static [&'static str] = &[
    "class", "elc", "hi", "o", "pyc" ];

static BUILD_TYPES: &'static [&'static str] = &[
    "Makefile", "Cargo.toml", "SConstruct", "CMakeLists.txt",
    "build.gradle", "Rakefile", "Gruntfile.js",
    "Gruntfile.coffee" ];

impl FileType {
    pub fn style(&self) -> Style {
        match *self {
            Normal => Plain,
            Directory => Blue.bold(),
            Symlink => Cyan.normal(),
            Special => Yellow.normal(),
            Executable => Green.bold(),
            Image => Fixed(133).normal(),
            Video => Fixed(135).normal(),
            Music => Fixed(92).normal(),
            Lossless => Fixed(93).normal(),
            Crypto => Fixed(109).normal(),
            Document => Fixed(105).normal(),
            Compressed => Red.normal(),
            Temp => Fixed(244).normal(),
            Immediate => Yellow.bold().underline(),
            Compiled => Fixed(137).normal(),
        }
    }
}

pub trait HasType {
    fn get_type(&self) -> FileType;
}

impl<'a> HasType for File<'a> {
    fn get_type(&self) -> FileType {
        if self.stat.kind == io::TypeDirectory {
            return Directory;
        }
        else if self.stat.kind == io::TypeSymlink {
            return Symlink;
        }
        else if self.stat.kind == io::TypeBlockSpecial || self.stat.kind == io::TypeNamedPipe || self.stat.kind == io::TypeUnknown {
            return Special;
        }
        else if self.stat.perm.contains(io::UserExecute) {
            return Executable;
        }
        else if self.name.starts_with("README") || BUILD_TYPES.iter().any(|&s| s == self.name) {
            return Immediate;
        }
        else if self.ext.is_some() {
            let ext = self.ext.unwrap();
            if IMAGE_TYPES.iter().any(|&s| s == ext) {
                return Image;
            }
            else if VIDEO_TYPES.iter().any(|&s| s == ext) {
                return Video;
            }
            else if MUSIC_TYPES.iter().any(|&s| s == ext) {
                return Music;
            }
            else if MUSIC_LOSSLESS.iter().any(|&s| s == ext) {
                return Lossless;
            }
            else if CRYPTO_TYPES.iter().any(|&s| s == ext) {
                return Crypto;
            }
            else if DOCUMENT_TYPES.iter().any(|&s| s == ext) {
                return Document;
            }
            else if COMPRESSED_TYPES.iter().any(|&s| s == ext) {
                return Compressed;
            }
            else if self.is_tmpfile() || TEMP_TYPES.iter().any(|&s| s == ext) {
                return Temp;
            }

            let source_files = self.get_source_files();
            if source_files.len() == 0 {
                return Normal;
            }
            else if source_files.iter().any(|path| self.dir.contains(path)) {
                return Temp;
            }
            else {
                if COMPILED_TYPES.iter().any(|&s| s == ext) {
                    return Compiled;
                }
                else {
                    return Normal;
                }
            }
        }
        return Normal;  // no filetype
    }
}

<|endoftext|>


<commit_before>#[deny(non_camel_case_types)];

/*!
 * A functional key,value store that works on anything.
 *
 * This works using a binary search tree. In the first version, it's a
 * very naive algorithm, but it will probably be updated to be a
 * red-black tree or something else.
 *
 * This is copied and modified from treemap right now. It's missing a lot
 * of features.
 */

import core::cmp::{Eq, Ord};
import option::{Some, None};
import option = option;

export Treemap;
export init;
export insert;
export find;
export traverse;

type Treemap<K, V> = @TreeNode<K, V>;

enum TreeNode<K, V> {
    Empty,
    Node(@K, @V, @TreeNode<K, V>, @TreeNode<K, V>)
}

/// Create a treemap
fn init<K, V>() -> Treemap<K, V> { @Empty }

/// Insert a value into the map
fn insert<K: copy Eq Ord, V: copy>(m: Treemap<K, V>, k: K, v: V)
                                -> Treemap<K, V> {
    @match m {
       @Empty => Node(@k, @v, @Empty, @Empty),
       @Node(@kk, vv, left, right) => {
         if k < kk {
             Node(@kk, vv, insert(left, k, v), right)
         } else if k == kk {
             Node(@kk, @v, left, right)
         } else { Node(@kk, vv, left, insert(right, k, v)) }
       }
     }
}

/// Find a value based on the key
fn find<K: Eq Ord, V: copy>(m: Treemap<K, V>, k: K) -> Option<V> {
    match *m {
      Empty => None,
      Node(@kk, @v, left, right) => {
        if k == kk {
            Some(v)
        } else if k < kk { find(left, k) } else { find(right, k) }
      }
    }
}

/// Visit all pairs in the map in order.
fn traverse<K, V: copy>(m: Treemap<K, V>, f: fn(K, V)) {
    match *m {
      Empty => (),
      /*
        Previously, this had what looked like redundant
        matches to me, so I changed it. but that may be a
        de-optimization -- tjc
       */
      Node(@k, @v, left, right) => {
        // copy v to make aliases work out
        let v1 = v;
        traverse(left, f);
        f(k, v1);
        traverse(right, f);
      }
    }
}
<commit_msg>Demode fun_treemap.rs<commit_after>#[deny(non_camel_case_types)];
#[forbid(deprecated_mode)];
#[forbid(deprecated_pattern)];

/*!
 * A functional key,value store that works on anything.
 *
 * This works using a binary search tree. In the first version, it's a
 * very naive algorithm, but it will probably be updated to be a
 * red-black tree or something else.
 *
 * This is copied and modified from treemap right now. It's missing a lot
 * of features.
 */

import core::cmp::{Eq, Ord};
import option::{Some, None};
import option = option;

export Treemap;
export init;
export insert;
export find;
export traverse;

type Treemap<K, V> = @TreeNode<K, V>;

enum TreeNode<K, V> {
    Empty,
    Node(@K, @V, @TreeNode<K, V>, @TreeNode<K, V>)
}

/// Create a treemap
fn init<K, V>() -> Treemap<K, V> { @Empty }

/// Insert a value into the map
fn insert<K: copy Eq Ord, V: copy>(m: Treemap<K, V>, +k: K, +v: V)
  -> Treemap<K, V> {
    @match m {
       @Empty => Node(@k, @v, @Empty, @Empty),
       @Node(@kk, vv, left, right) => {
         if k < kk {
             Node(@kk, vv, insert(left, k, v), right)
         } else if k == kk {
             Node(@kk, @v, left, right)
         } else { Node(@kk, vv, left, insert(right, k, v)) }
       }
     }
}

/// Find a value based on the key
fn find<K: Eq Ord, V: copy>(m: Treemap<K, V>, +k: K) -> Option<V> {
    match *m {
      Empty => None,
      Node(@kk, @v, left, right) => {
        if k == kk {
            Some(v)
        } else if k < kk { find(left, k) } else { find(right, k) }
      }
    }
}

/// Visit all pairs in the map in order.
fn traverse<K, V: copy>(m: Treemap<K, V>, f: fn(K, V)) {
    match *m {
      Empty => (),
      /*
        Previously, this had what looked like redundant
        matches to me, so I changed it. but that may be a
        de-optimization -- tjc
       */
      Node(@k, @v, left, right) => {
        // copy v to make aliases work out
        let v1 = v;
        traverse(left, f);
        f(k, v1);
        traverse(right, f);
      }
    }
}
<|endoftext|>


<commit_before>/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of Dmitry Vyukov.
 */

// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue

//! A single-producer single-consumer concurrent queue
//!
//! This module contains the implementation of an SPSC queue which can be used
//! concurrently between two tasks. This data structure is safe to use and
//! enforces the semantics that there is one pusher and one popper.

#![experimental]

use core::prelude::*;

use alloc::boxed::Box;
use core::mem;
use core::cell::UnsafeCell;
use alloc::arc::Arc;

use atomic::{AtomicPtr, Relaxed, AtomicUint, Acquire, Release};

// Node within the linked list queue of messages to send
struct Node<T> {
    // FIXME: this could be an uninitialized T if we're careful enough, and
    //      that would reduce memory usage (and be a bit faster).
    //      is it worth it?
    value: Option<T>,           // nullable for re-use of nodes
    next: AtomicPtr<Node<T>>,   // next node in the queue
}

/// The single-producer single-consumer queue. This structure is not cloneable,
/// but it can be safely shared in an Arc if it is guaranteed that there
/// is only one popper and one pusher touching the queue at any one point in
/// time.
pub struct Queue<T> {
    // consumer fields
    tail: UnsafeCell<*mut Node<T>>, // where to pop from
    tail_prev: AtomicPtr<Node<T>>, // where to pop from

    // producer fields
    head: UnsafeCell<*mut Node<T>>,      // where to push to
    first: UnsafeCell<*mut Node<T>>,     // where to get new nodes from
    tail_copy: UnsafeCell<*mut Node<T>>, // between first/tail

    // Cache maintenance fields. Additions and subtractions are stored
    // separately in order to allow them to use nonatomic addition/subtraction.
    cache_bound: uint,
    cache_additions: AtomicUint,
    cache_subtractions: AtomicUint,
}

/// A safe abstraction for the consumer in a single-producer single-consumer
/// queue.
pub struct Consumer<T> {
    inner: Arc<Queue<T>>
}

impl<T: Send> Consumer<T> {
    /// Attempts to pop the value from the head of the queue, returning `None`
    /// if the queue is empty.
    pub fn pop(&mut self) -> Option<T> {
        self.inner.pop()
    }

    /// Attempts to peek at the head of the queue, returning `None` if the queue
    /// is empty.
    pub fn peek<'a>(&'a mut self) -> Option<&'a mut T> {
        self.inner.peek()
    }
}

/// A safe abstraction for the producer in a single-producer single-consumer
/// queue.
pub struct Producer<T> {
    inner: Arc<Queue<T>>
}

impl<T: Send> Producer<T> {
    /// Pushes a new value onto the queue.
    pub fn push(&mut self, t: T) {
        self.inner.push(t)
    }
}

impl<T: Send> Node<T> {
    fn new() -> *mut Node<T> {
        unsafe {
            mem::transmute(box Node {
                value: None,
                next: AtomicPtr::new(0 as *mut Node<T>),
            })
        }
    }
}

/// Creates a new queue with a consumer-producer pair.
///
/// The producer returned is connected to the consumer to push all data to
/// the consumer.
///
/// # Arguments
///
///   * `bound` - This queue implementation is implemented with a linked
///               list, and this means that a push is always a malloc. In
///               order to amortize this cost, an internal cache of nodes is
///               maintained to prevent a malloc from always being
///               necessary. This bound is the limit on the size of the
///               cache (if desired). If the value is 0, then the cache has
///               no bound. Otherwise, the cache will never grow larger than
///               `bound` (although the queue itself could be much larger.
pub fn queue<T: Send>(bound: uint) -> (Consumer<T>, Producer<T>) {
    let q = unsafe { Queue::new(bound) };
    let arc = Arc::new(q);
    let consumer = Consumer { inner: arc.clone() };
    let producer = Producer { inner: arc };

    (consumer, producer)
}

impl<T: Send> Queue<T> {
    /// Creates a new queue.
    ///
    /// This is unsafe as the type system doesn't enforce a single
    /// consumer-producer relationship. It also allows the consumer to `pop`
    /// items while there is a `peek` active due to all methods having a
    /// non-mutable receiver.
    ///
    /// # Arguments
    ///
    ///   * `bound` - This queue implementation is implemented with a linked
    ///               list, and this means that a push is always a malloc. In
    ///               order to amortize this cost, an internal cache of nodes is
    ///               maintained to prevent a malloc from always being
    ///               necessary. This bound is the limit on the size of the
    ///               cache (if desired). If the value is 0, then the cache has
    ///               no bound. Otherwise, the cache will never grow larger than
    ///               `bound` (although the queue itself could be much larger.
    pub unsafe fn new(bound: uint) -> Queue<T> {
        let n1 = Node::new();
        let n2 = Node::new();
        (*n1).next.store(n2, Relaxed);
        Queue {
            tail: UnsafeCell::new(n2),
            tail_prev: AtomicPtr::new(n1),
            head: UnsafeCell::new(n2),
            first: UnsafeCell::new(n1),
            tail_copy: UnsafeCell::new(n1),
            cache_bound: bound,
            cache_additions: AtomicUint::new(0),
            cache_subtractions: AtomicUint::new(0),
        }
    }

    /// Pushes a new value onto this queue. Note that to use this function
    /// safely, it must be externally guaranteed that there is only one pusher.
    pub fn push(&self, t: T) {
        unsafe {
            // Acquire a node (which either uses a cached one or allocates a new
            // one), and then append this to the 'head' node.
            let n = self.alloc();
            assert!((*n).value.is_none());
            (*n).value = Some(t);
            (*n).next.store(0 as *mut Node<T>, Relaxed);
            (**self.head.get()).next.store(n, Release);
            *self.head.get() = n;
        }
    }

    unsafe fn alloc(&self) -> *mut Node<T> {
        // First try to see if we can consume the 'first' node for our uses.
        // We try to avoid as many atomic instructions as possible here, so
        // the addition to cache_subtractions is not atomic (plus we're the
        // only one subtracting from the cache).
        if *self.first.get() != *self.tail_copy.get() {
            if self.cache_bound > 0 {
                let b = self.cache_subtractions.load(Relaxed);
                self.cache_subtractions.store(b + 1, Relaxed);
            }
            let ret = *self.first.get();
            *self.first.get() = (*ret).next.load(Relaxed);
            return ret;
        }
        // If the above fails, then update our copy of the tail and try
        // again.
        *self.tail_copy.get() = self.tail_prev.load(Acquire);
        if *self.first.get() != *self.tail_copy.get() {
            if self.cache_bound > 0 {
                let b = self.cache_subtractions.load(Relaxed);
                self.cache_subtractions.store(b + 1, Relaxed);
            }
            let ret = *self.first.get();
            *self.first.get() = (*ret).next.load(Relaxed);
            return ret;
        }
        // If all of that fails, then we have to allocate a new node
        // (there's nothing in the node cache).
        Node::new()
    }

    /// Attempts to pop a value from this queue. Remember that to use this type
    /// safely you must ensure that there is only one popper at a time.
    pub fn pop(&self) -> Option<T> {
        unsafe {
            // The `tail` node is not actually a used node, but rather a
            // sentinel from where we should start popping from. Hence, look at
            // tail's next field and see if we can use it. If we do a pop, then
            // the current tail node is a candidate for going into the cache.
            let tail = *self.tail.get();
            let next = (*tail).next.load(Acquire);
            if next.is_null() { return None }
            assert!((*next).value.is_some());
            let ret = (*next).value.take();

            *self.tail.get() = next;
            if self.cache_bound == 0 {
                self.tail_prev.store(tail, Release);
            } else {
                // FIXME: this is dubious with overflow.
                let additions = self.cache_additions.load(Relaxed);
                let subtractions = self.cache_subtractions.load(Relaxed);
                let size = additions - subtractions;

                if size < self.cache_bound {
                    self.tail_prev.store(tail, Release);
                    self.cache_additions.store(additions + 1, Relaxed);
                } else {
                    (*self.tail_prev.load(Relaxed)).next.store(next, Relaxed);
                    // We have successfully erased all references to 'tail', so
                    // now we can safely drop it.
                    let _: Box<Node<T>> = mem::transmute(tail);
                }
            }
            return ret;
        }
    }

    /// Attempts to peek at the head of the queue, returning `None` if the queue
    /// has no data currently
    ///
    /// # Warning
    /// The reference returned is invalid if it is not used before the consumer
    /// pops the value off the queue. If the producer then pushes another value
    /// onto the queue, it will overwrite the value pointed to by the reference.
    pub fn peek<'a>(&'a self) -> Option<&'a mut T> {
        // This is essentially the same as above with all the popping bits
        // stripped out.
        unsafe {
            let tail = *self.tail.get();
            let next = (*tail).next.load(Acquire);
            if next.is_null() { return None }
            return (*next).value.as_mut();
        }
    }
}

#[unsafe_destructor]
impl<T: Send> Drop for Queue<T> {
    fn drop(&mut self) {
        unsafe {
            let mut cur = *self.first.get();
            while !cur.is_null() {
                let next = (*cur).next.load(Relaxed);
                let _n: Box<Node<T>> = mem::transmute(cur);
                cur = next;
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;

    use native;

    use super::{queue, Queue};

    #[test]
    fn smoke() {
        let (mut consumer, mut producer) = queue(0);
        producer.push(1i);
        producer.push(2);
        assert_eq!(consumer.pop(), Some(1i));
        assert_eq!(consumer.pop(), Some(2));
        assert_eq!(consumer.pop(), None);
        producer.push(3);
        producer.push(4);
        assert_eq!(consumer.pop(), Some(3));
        assert_eq!(consumer.pop(), Some(4));
        assert_eq!(consumer.pop(), None);
    }

    // This behaviour is blocked by the type system if using the safe constructor
    #[test]
    fn pop_peeked_unchecked() {
        let q = unsafe { Queue::new(0) };
        q.push(vec![1i]);
        q.push(vec![2]);
        let peeked = q.peek().unwrap();

        assert_eq!(*peeked, vec![1]);
        assert_eq!(q.pop(), Some(vec![1]));

        assert_eq!(*peeked, vec![1]);
        q.push(vec![7]);

        // Note: This should actually expect 1, but this test is to highlight
        // the unsafety allowed by the unchecked usage. A Rust user would not
        // expect their peeked value to mutate like this without the type system
        // complaining.
        assert_eq!(*peeked, vec![7]);
    }

    #[test]
    fn peek() {
        let (mut consumer, mut producer) = queue(0);
        producer.push(vec![1i]);

        // Ensure the borrowchecker works
        match consumer.peek() {
            Some(vec) => match vec.as_slice() {
                // Note that `pop` is not allowed here due to borrow
                [1] => {}
                _ => return
            },
            None => unreachable!()
        }

        consumer.pop();
    }

    #[test]
    fn drop_full() {
        let (_, mut producer) = queue(0);
        producer.push(box 1i);
        producer.push(box 2i);
    }

    #[test]
    fn smoke_bound() {
        let (mut consumer, mut producer) = queue(1);
        producer.push(1i);
        producer.push(2);
        assert_eq!(consumer.pop(), Some(1));
        assert_eq!(consumer.pop(), Some(2));
        assert_eq!(consumer.pop(), None);
        producer.push(3);
        producer.push(4);
        assert_eq!(consumer.pop(), Some(3));
        assert_eq!(consumer.pop(), Some(4));
        assert_eq!(consumer.pop(), None);
    }

    #[test]
    fn stress() {
        stress_bound(0);
        stress_bound(1);

        fn stress_bound(bound: uint) {
            let (consumer, mut producer) = queue(bound);

            let (tx, rx) = channel();
            native::task::spawn(proc() {
                // Move the consumer to a local mutable slot
                let mut consumer = consumer;
                for _ in range(0u, 100000) {
                    loop {
                        match consumer.pop() {
                            Some(1i) => break,
                            Some(_) => fail!(),
                            None => {}
                        }
                    }
                }
                tx.send(());
            });
            for _ in range(0i, 100000) {
                producer.push(1);
            }
            rx.recv();
        }
    }
}
<commit_msg>Remove invalid test -- this test reads freed memory, from what I can tell<commit_after>/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of Dmitry Vyukov.
 */

// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue

//! A single-producer single-consumer concurrent queue
//!
//! This module contains the implementation of an SPSC queue which can be used
//! concurrently between two tasks. This data structure is safe to use and
//! enforces the semantics that there is one pusher and one popper.

#![experimental]

use core::prelude::*;

use alloc::boxed::Box;
use core::mem;
use core::cell::UnsafeCell;
use alloc::arc::Arc;

use atomic::{AtomicPtr, Relaxed, AtomicUint, Acquire, Release};

// Node within the linked list queue of messages to send
struct Node<T> {
    // FIXME: this could be an uninitialized T if we're careful enough, and
    //      that would reduce memory usage (and be a bit faster).
    //      is it worth it?
    value: Option<T>,           // nullable for re-use of nodes
    next: AtomicPtr<Node<T>>,   // next node in the queue
}

/// The single-producer single-consumer queue. This structure is not cloneable,
/// but it can be safely shared in an Arc if it is guaranteed that there
/// is only one popper and one pusher touching the queue at any one point in
/// time.
pub struct Queue<T> {
    // consumer fields
    tail: UnsafeCell<*mut Node<T>>, // where to pop from
    tail_prev: AtomicPtr<Node<T>>, // where to pop from

    // producer fields
    head: UnsafeCell<*mut Node<T>>,      // where to push to
    first: UnsafeCell<*mut Node<T>>,     // where to get new nodes from
    tail_copy: UnsafeCell<*mut Node<T>>, // between first/tail

    // Cache maintenance fields. Additions and subtractions are stored
    // separately in order to allow them to use nonatomic addition/subtraction.
    cache_bound: uint,
    cache_additions: AtomicUint,
    cache_subtractions: AtomicUint,
}

/// A safe abstraction for the consumer in a single-producer single-consumer
/// queue.
pub struct Consumer<T> {
    inner: Arc<Queue<T>>
}

impl<T: Send> Consumer<T> {
    /// Attempts to pop the value from the head of the queue, returning `None`
    /// if the queue is empty.
    pub fn pop(&mut self) -> Option<T> {
        self.inner.pop()
    }

    /// Attempts to peek at the head of the queue, returning `None` if the queue
    /// is empty.
    pub fn peek<'a>(&'a mut self) -> Option<&'a mut T> {
        self.inner.peek()
    }
}

/// A safe abstraction for the producer in a single-producer single-consumer
/// queue.
pub struct Producer<T> {
    inner: Arc<Queue<T>>
}

impl<T: Send> Producer<T> {
    /// Pushes a new value onto the queue.
    pub fn push(&mut self, t: T) {
        self.inner.push(t)
    }
}

impl<T: Send> Node<T> {
    fn new() -> *mut Node<T> {
        unsafe {
            mem::transmute(box Node {
                value: None,
                next: AtomicPtr::new(0 as *mut Node<T>),
            })
        }
    }
}

/// Creates a new queue with a consumer-producer pair.
///
/// The producer returned is connected to the consumer to push all data to
/// the consumer.
///
/// # Arguments
///
///   * `bound` - This queue implementation is implemented with a linked
///               list, and this means that a push is always a malloc. In
///               order to amortize this cost, an internal cache of nodes is
///               maintained to prevent a malloc from always being
///               necessary. This bound is the limit on the size of the
///               cache (if desired). If the value is 0, then the cache has
///               no bound. Otherwise, the cache will never grow larger than
///               `bound` (although the queue itself could be much larger.
pub fn queue<T: Send>(bound: uint) -> (Consumer<T>, Producer<T>) {
    let q = unsafe { Queue::new(bound) };
    let arc = Arc::new(q);
    let consumer = Consumer { inner: arc.clone() };
    let producer = Producer { inner: arc };

    (consumer, producer)
}

impl<T: Send> Queue<T> {
    /// Creates a new queue.
    ///
    /// This is unsafe as the type system doesn't enforce a single
    /// consumer-producer relationship. It also allows the consumer to `pop`
    /// items while there is a `peek` active due to all methods having a
    /// non-mutable receiver.
    ///
    /// # Arguments
    ///
    ///   * `bound` - This queue implementation is implemented with a linked
    ///               list, and this means that a push is always a malloc. In
    ///               order to amortize this cost, an internal cache of nodes is
    ///               maintained to prevent a malloc from always being
    ///               necessary. This bound is the limit on the size of the
    ///               cache (if desired). If the value is 0, then the cache has
    ///               no bound. Otherwise, the cache will never grow larger than
    ///               `bound` (although the queue itself could be much larger.
    pub unsafe fn new(bound: uint) -> Queue<T> {
        let n1 = Node::new();
        let n2 = Node::new();
        (*n1).next.store(n2, Relaxed);
        Queue {
            tail: UnsafeCell::new(n2),
            tail_prev: AtomicPtr::new(n1),
            head: UnsafeCell::new(n2),
            first: UnsafeCell::new(n1),
            tail_copy: UnsafeCell::new(n1),
            cache_bound: bound,
            cache_additions: AtomicUint::new(0),
            cache_subtractions: AtomicUint::new(0),
        }
    }

    /// Pushes a new value onto this queue. Note that to use this function
    /// safely, it must be externally guaranteed that there is only one pusher.
    pub fn push(&self, t: T) {
        unsafe {
            // Acquire a node (which either uses a cached one or allocates a new
            // one), and then append this to the 'head' node.
            let n = self.alloc();
            assert!((*n).value.is_none());
            (*n).value = Some(t);
            (*n).next.store(0 as *mut Node<T>, Relaxed);
            (**self.head.get()).next.store(n, Release);
            *self.head.get() = n;
        }
    }

    unsafe fn alloc(&self) -> *mut Node<T> {
        // First try to see if we can consume the 'first' node for our uses.
        // We try to avoid as many atomic instructions as possible here, so
        // the addition to cache_subtractions is not atomic (plus we're the
        // only one subtracting from the cache).
        if *self.first.get() != *self.tail_copy.get() {
            if self.cache_bound > 0 {
                let b = self.cache_subtractions.load(Relaxed);
                self.cache_subtractions.store(b + 1, Relaxed);
            }
            let ret = *self.first.get();
            *self.first.get() = (*ret).next.load(Relaxed);
            return ret;
        }
        // If the above fails, then update our copy of the tail and try
        // again.
        *self.tail_copy.get() = self.tail_prev.load(Acquire);
        if *self.first.get() != *self.tail_copy.get() {
            if self.cache_bound > 0 {
                let b = self.cache_subtractions.load(Relaxed);
                self.cache_subtractions.store(b + 1, Relaxed);
            }
            let ret = *self.first.get();
            *self.first.get() = (*ret).next.load(Relaxed);
            return ret;
        }
        // If all of that fails, then we have to allocate a new node
        // (there's nothing in the node cache).
        Node::new()
    }

    /// Attempts to pop a value from this queue. Remember that to use this type
    /// safely you must ensure that there is only one popper at a time.
    pub fn pop(&self) -> Option<T> {
        unsafe {
            // The `tail` node is not actually a used node, but rather a
            // sentinel from where we should start popping from. Hence, look at
            // tail's next field and see if we can use it. If we do a pop, then
            // the current tail node is a candidate for going into the cache.
            let tail = *self.tail.get();
            let next = (*tail).next.load(Acquire);
            if next.is_null() { return None }
            assert!((*next).value.is_some());
            let ret = (*next).value.take();

            *self.tail.get() = next;
            if self.cache_bound == 0 {
                self.tail_prev.store(tail, Release);
            } else {
                // FIXME: this is dubious with overflow.
                let additions = self.cache_additions.load(Relaxed);
                let subtractions = self.cache_subtractions.load(Relaxed);
                let size = additions - subtractions;

                if size < self.cache_bound {
                    self.tail_prev.store(tail, Release);
                    self.cache_additions.store(additions + 1, Relaxed);
                } else {
                    (*self.tail_prev.load(Relaxed)).next.store(next, Relaxed);
                    // We have successfully erased all references to 'tail', so
                    // now we can safely drop it.
                    let _: Box<Node<T>> = mem::transmute(tail);
                }
            }
            return ret;
        }
    }

    /// Attempts to peek at the head of the queue, returning `None` if the queue
    /// has no data currently
    ///
    /// # Warning
    /// The reference returned is invalid if it is not used before the consumer
    /// pops the value off the queue. If the producer then pushes another value
    /// onto the queue, it will overwrite the value pointed to by the reference.
    pub fn peek<'a>(&'a self) -> Option<&'a mut T> {
        // This is essentially the same as above with all the popping bits
        // stripped out.
        unsafe {
            let tail = *self.tail.get();
            let next = (*tail).next.load(Acquire);
            if next.is_null() { return None }
            return (*next).value.as_mut();
        }
    }
}

#[unsafe_destructor]
impl<T: Send> Drop for Queue<T> {
    fn drop(&mut self) {
        unsafe {
            let mut cur = *self.first.get();
            while !cur.is_null() {
                let next = (*cur).next.load(Relaxed);
                let _n: Box<Node<T>> = mem::transmute(cur);
                cur = next;
            }
        }
    }
}

#[cfg(test)]
mod test {
    use std::prelude::*;

    use native;

    use super::{queue, Queue};

    #[test]
    fn smoke() {
        let (mut consumer, mut producer) = queue(0);
        producer.push(1i);
        producer.push(2);
        assert_eq!(consumer.pop(), Some(1i));
        assert_eq!(consumer.pop(), Some(2));
        assert_eq!(consumer.pop(), None);
        producer.push(3);
        producer.push(4);
        assert_eq!(consumer.pop(), Some(3));
        assert_eq!(consumer.pop(), Some(4));
        assert_eq!(consumer.pop(), None);
    }

    #[test]
    fn peek() {
        let (mut consumer, mut producer) = queue(0);
        producer.push(vec![1i]);

        // Ensure the borrowchecker works
        match consumer.peek() {
            Some(vec) => match vec.as_slice() {
                // Note that `pop` is not allowed here due to borrow
                [1] => {}
                _ => return
            },
            None => unreachable!()
        }

        consumer.pop();
    }

    #[test]
    fn drop_full() {
        let (_, mut producer) = queue(0);
        producer.push(box 1i);
        producer.push(box 2i);
    }

    #[test]
    fn smoke_bound() {
        let (mut consumer, mut producer) = queue(1);
        producer.push(1i);
        producer.push(2);
        assert_eq!(consumer.pop(), Some(1));
        assert_eq!(consumer.pop(), Some(2));
        assert_eq!(consumer.pop(), None);
        producer.push(3);
        producer.push(4);
        assert_eq!(consumer.pop(), Some(3));
        assert_eq!(consumer.pop(), Some(4));
        assert_eq!(consumer.pop(), None);
    }

    #[test]
    fn stress() {
        stress_bound(0);
        stress_bound(1);

        fn stress_bound(bound: uint) {
            let (consumer, mut producer) = queue(bound);

            let (tx, rx) = channel();
            native::task::spawn(proc() {
                // Move the consumer to a local mutable slot
                let mut consumer = consumer;
                for _ in range(0u, 100000) {
                    loop {
                        match consumer.pop() {
                            Some(1i) => break,
                            Some(_) => fail!(),
                            None => {}
                        }
                    }
                }
                tx.send(());
            });
            for _ in range(0i, 100000) {
                producer.push(1);
            }
            rx.recv();
        }
    }
}
<|endoftext|>