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

#[rustfmt::skip]
use ::image;

use crate::context::{Context, DebugId};
use crate::error::GameError;
use crate::error::GameResult;
use crate::filesystem;
use crate::graphics;
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,
{
    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 = usize::from(width);
        let uheight = usize::from(height);
        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,
        })
    }

    /// A helper function to get the raw gfx texture handle
    pub fn get_raw_texture_handle(&self) -> gfx::handle::RawTexture<B::Resources> {
        self.texture_handle.clone()
    }

    /// A helper function to get the raw gfx texture view
    pub fn get_raw_texture_view(&self) -> gfx::handle::RawShaderResourceView<B::Resources> {
        self.texture.clone()
    }
}

/// 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 mut buf = Vec::new();
        let mut reader = context.filesystem.open(path)?;
        let _ = reader.read_to_end(&mut buf)?;
        Self::from_bytes(context, buf)
    }

    /// Creates a new `Image` from the given buffer, which should contain an image encoded
    /// in a supported image file format.
    pub fn from_bytes(context: &mut Context, bytes: Vec<u8>) -> GameResult<Self> {
        let img = image::load_from_memory(&bytes)?.to_rgba8();
        let (width, height) = img.dimensions();
        let better_width = u16::try_from(width)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image width > u16::MAX")))?;
        let better_height = u16::try_from(height)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image height > u16::MAX")))?;
        Self::from_rgba8(context, better_width, better_height, &img.into_raw())
    }

    /// 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]>(usize::from(w) * usize::from(h))?;

        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,
                height: h,
                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.
        // TODO: This can overflow on 32-bit platforms
        let mut data = Vec::with_capacity(usize::from(self.width) * usize::from(self.height) * 4);
        // Assuming OpenGL backend whose typical readback option (glReadPixels) has origin at bottom left.
        // Image formats on the other hand usually deal with top right.
        for y in (0..usize::from(self.height)).rev() {
            data.extend(
                reader
                    .iter()
                    .skip(y * usize::from(self.width))
                    .take(usize::from(self.width))
                    .flatten(),
            );
        }
        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::Rgba8;
        match format {
            ImageFormat::Png => image::png::PngEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width),
                    u32::from(self.height),
                    color_format,
                )
                .map_err(Into::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 = usize::from(size) * usize::from(size);
        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, f32::from(self.width()), f32::from(self.height()))
    }

    /// 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(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
        self.debug_id.assert(ctx);

        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.
        let scale_x = src_width * f32::from(self.width);
        let scale_y = src_height * f32::from(self.height);
        let new_param = match param.trans {
            Transform::Values { scale, .. } => param.scale(mint::Vector2 {
                x: scale.x * scale_x,
                y: scale.y * scale_y,
            }),
            Transform::Matrix(m) => param.transform(
                Matrix4::from(m) * Matrix4::from_scale(glam::vec3(scale_x, scale_y, 1.0)),
            ),
        };

        draw_image_raw(self, ctx, new_param)
    }

    fn dimensions(&self, _: &mut Context) -> Option<graphics::Rect> {
        Some(self.dimensions())
    }

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

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

pub(crate) fn draw_image_raw(image: &Image, ctx: &mut Context, param: DrawParam) -> GameResult {
    let gfx = &mut ctx.gfx_context;

    gfx.update_instance_properties(param)?;
    let sampler = gfx
        .samplers
        .get_or_insert(image.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(image.texture.clone());
    gfx.data.tex = (typed_thingy, sampler);
    let previous_mode: Option<BlendMode> = if let Some(mode) = image.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(())
}

#[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, &[]).is_err());
        let _i = assert!(Image::from_rgba8(ctx, 3432, 432, &[]).is_err());
        let _i = Image::from_rgba8(ctx, 2, 2, &[99; 16]).unwrap();
    }
}
<commit_msg>Unflips Image::to_rgba8<commit_after>use std::convert::TryFrom;
use std::io::Read;
use std::path;

#[rustfmt::skip]
use ::image;

use crate::context::{Context, DebugId};
use crate::error::GameError;
use crate::error::GameResult;
use crate::filesystem;
use crate::graphics;
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,
{
    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 = usize::from(width);
        let uheight = usize::from(height);
        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,
        })
    }

    /// A helper function to get the raw gfx texture handle
    pub fn get_raw_texture_handle(&self) -> gfx::handle::RawTexture<B::Resources> {
        self.texture_handle.clone()
    }

    /// A helper function to get the raw gfx texture view
    pub fn get_raw_texture_view(&self) -> gfx::handle::RawShaderResourceView<B::Resources> {
        self.texture.clone()
    }
}

/// 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 mut buf = Vec::new();
        let mut reader = context.filesystem.open(path)?;
        let _ = reader.read_to_end(&mut buf)?;
        Self::from_bytes(context, buf)
    }

    /// Creates a new `Image` from the given buffer, which should contain an image encoded
    /// in a supported image file format.
    pub fn from_bytes(context: &mut Context, bytes: Vec<u8>) -> GameResult<Self> {
        let img = image::load_from_memory(&bytes)?.to_rgba8();
        let (width, height) = img.dimensions();
        let better_width = u16::try_from(width)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image width > u16::MAX")))?;
        let better_height = u16::try_from(height)
            .map_err(|_| GameError::ResourceLoadError(String::from("Image height > u16::MAX")))?;
        Self::from_rgba8(context, better_width, better_height, &img.into_raw())
    }

    /// 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>(usize::from(w) * usize::from(h) * 4)?;

        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,
                height: h,
                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)?.to_vec();
        Ok(reader.to_vec())
    }

    /// 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::Rgba8;
        match format {
            ImageFormat::Png => image::png::PngEncoder::new(writer)
                .encode(
                    &data,
                    u32::from(self.width),
                    u32::from(self.height),
                    color_format,
                )
                .map_err(Into::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 = usize::from(size) * usize::from(size);
        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, f32::from(self.width()), f32::from(self.height()))
    }

    /// 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(&self, ctx: &mut Context, param: DrawParam) -> GameResult {
        self.debug_id.assert(ctx);

        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.
        let scale_x = src_width * f32::from(self.width);
        let scale_y = src_height * f32::from(self.height);
        let new_param = match param.trans {
            Transform::Values { scale, .. } => param.scale(mint::Vector2 {
                x: scale.x * scale_x,
                y: scale.y * scale_y,
            }),
            Transform::Matrix(m) => param.transform(
                Matrix4::from(m) * Matrix4::from_scale(glam::vec3(scale_x, scale_y, 1.0)),
            ),
        };

        draw_image_raw(self, ctx, new_param)
    }

    fn dimensions(&self, _: &mut Context) -> Option<graphics::Rect> {
        Some(self.dimensions())
    }

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

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

pub(crate) fn draw_image_raw(image: &Image, ctx: &mut Context, param: DrawParam) -> GameResult {
    let gfx = &mut ctx.gfx_context;

    gfx.update_instance_properties(param)?;
    let sampler = gfx
        .samplers
        .get_or_insert(image.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(image.texture.clone());
    gfx.data.tex = (typed_thingy, sampler);
    let previous_mode: Option<BlendMode> = if let Some(mode) = image.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(())
}

#[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, &[]).is_err());
        let _i = assert!(Image::from_rgba8(ctx, 3432, 432, &[]).is_err());
        let _i = Image::from_rgba8(ctx, 2, 2, &[99; 16]).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use std::mem;

use program::Program;
use vertex_buffer::{mod, VertexBuffer, BindingType};
use {DisplayImpl, IndicesSource, GlObject};

use {libc, gl};

///
pub struct VertexArrayObject {
    display: Arc<DisplayImpl>,
    id: gl::types::GLuint,
}

impl VertexArrayObject {
    ///
    fn new<T>(display: Arc<DisplayImpl>, vertex_buffer: &VertexBuffer<T>,
              ib_id: gl::types::GLuint, program: &Program) -> VertexArrayObject
    {
        let (tx, rx) = channel();

        let bindings = vertex_buffer::get_bindings(vertex_buffer).clone();
        let vb_elementssize = vertex_buffer::get_elements_size(vertex_buffer);
        let vertex_buffer = GlObject::get_id(vertex_buffer);
        let attributes = ::program::get_attributes(program);

        display.context.exec(move |: ctxt| {
            unsafe {
                let id: gl::types::GLuint = mem::uninitialized();
                ctxt.gl.GenVertexArrays(1, mem::transmute(&id));
                tx.send(id);

                ctxt.gl.BindVertexArray(id);
                ctxt.state.vertex_array = id;

                // binding vertex buffer
                if ctxt.state.array_buffer_binding != vertex_buffer {
                    ctxt.gl.BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
                    ctxt.state.array_buffer_binding = vertex_buffer;
                }

                // binding index buffer
                ctxt.gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ib_id);

                // binding attributes
                for (name, offset, ty) in bindings.into_iter() {
                    let (data_type, elements_count) = vertex_binding_type_to_gl(ty);

                    let attribute = match attributes.get(&name) {
                        Some(a) => a,
                        None => continue
                    };

                    // checking that the types are matching
                    if !vertex_type_matches(ty, attribute.ty, attribute.size) {
                        panic!("The program attributes do not match the vertex format");
                    }

                    if attribute.location != -1 {
                        match data_type {
                            gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT |
                            gl::INT | gl::UNSIGNED_INT =>
                                ctxt.gl.VertexAttribIPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type,
                                    vb_elementssize as i32, offset as *const libc::c_void),

                            _ => ctxt.gl.VertexAttribPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type, 0,
                                    vb_elementssize as i32, offset as *const libc::c_void)
                        }

                        ctxt.gl.EnableVertexAttribArray(attribute.location as u32);
                    }
                }
            }
        });

        VertexArrayObject {
            display: display,
            id: rx.recv(),
        }
    }
}

impl Drop for VertexArrayObject {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(move |: ctxt| {
            unsafe {
                // unbinding
                if ctxt.state.vertex_array == id {
                    ctxt.gl.BindVertexArray(0);
                    ctxt.state.vertex_array = 0;
                }

                // deleting
                ctxt.gl.DeleteVertexArrays(1, [ id ].as_ptr());
            }
        });
    }
}

impl GlObject for VertexArrayObject {
    fn get_id(&self) -> gl::types::GLuint {
        self.id
    }
}

pub fn get_vertex_array_object<T, I>(display: &Arc<DisplayImpl>, vertex_buffer: &VertexBuffer<T>,
                                     indices: &I, program: &Program) -> gl::types::GLuint
                                     where I: IndicesSource
{
    let mut vaos = display.vertex_array_objects.lock();

    let ib_id = indices.to_indices_source_helper().index_buffer.map(|b| b.get_id()).unwrap_or(0);
    let vb_id = GlObject::get_id(vertex_buffer);
    let program_id = program.get_id();

    if let Some(value) = vaos.get(&(vb_id, ib_id, program_id)) {
        return value.id;
    }

    let new_vao = VertexArrayObject::new(display.clone(), vertex_buffer, ib_id, program);
    let new_vao_id = new_vao.id;
    vaos.insert((vb_id, ib_id, program_id), new_vao);
    new_vao_id
}

fn vertex_binding_type_to_gl(ty: BindingType) -> (gl::types::GLenum, gl::types::GLint) {
    match ty {
        BindingType::I8 => (gl::BYTE, 1),
        BindingType::I8I8 => (gl::BYTE, 2),
        BindingType::I8I8I8 => (gl::BYTE, 3),
        BindingType::I8I8I8I8 => (gl::BYTE, 4),
        BindingType::U8 => (gl::UNSIGNED_BYTE, 1),
        BindingType::U8U8 => (gl::UNSIGNED_BYTE, 2),
        BindingType::U8U8U8 => (gl::UNSIGNED_BYTE, 3),
        BindingType::U8U8U8U8 => (gl::UNSIGNED_BYTE, 4),
        BindingType::I16 => (gl::SHORT, 1),
        BindingType::I16I16 => (gl::SHORT, 2),
        BindingType::I16I16I16 => (gl::SHORT, 3),
        BindingType::I16I16I16I16 => (gl::SHORT, 4),
        BindingType::U16 => (gl::UNSIGNED_SHORT, 1),
        BindingType::U16U16 => (gl::UNSIGNED_SHORT, 2),
        BindingType::U16U16U16 => (gl::UNSIGNED_SHORT, 3),
        BindingType::U16U16U16U16 => (gl::UNSIGNED_SHORT, 4),
        BindingType::I32 => (gl::INT, 1),
        BindingType::I32I32 => (gl::INT, 2),
        BindingType::I32I32I32 => (gl::INT, 3),
        BindingType::I32I32I32I32 => (gl::INT, 4),
        BindingType::U32 => (gl::UNSIGNED_INT, 1),
        BindingType::U32U32 => (gl::UNSIGNED_INT, 2),
        BindingType::U32U32U32 => (gl::UNSIGNED_INT, 3),
        BindingType::U32U32U32U32 => (gl::UNSIGNED_INT, 4),
        BindingType::F32 => (gl::FLOAT, 1),
        BindingType::F32F32 => (gl::FLOAT, 2),
        BindingType::F32F32F32 => (gl::FLOAT, 3),
        BindingType::F32F32F32F32 => (gl::FLOAT, 4),
    }
}

fn vertex_type_matches(ty: BindingType, gl_ty: gl::types::GLenum,
                       gl_size: gl::types::GLint) -> bool
{
    match (ty, gl_ty, gl_size) {
        (BindingType::I8, gl::BYTE, 1) => true,
        (BindingType::I8I8, gl::BYTE, 2) => true,
        (BindingType::I8I8I8, gl::BYTE, 3) => true,
        (BindingType::I8I8I8I8, gl::BYTE, 4) => true,
        (BindingType::U8, gl::UNSIGNED_BYTE, 1) => true,
        (BindingType::U8U8, gl::UNSIGNED_BYTE, 2) => true,
        (BindingType::U8U8U8, gl::UNSIGNED_BYTE, 3) => true,
        (BindingType::U8U8U8U8, gl::UNSIGNED_BYTE, 4) => true,
        (BindingType::I16, gl::SHORT, 1) => true,
        (BindingType::I16I16, gl::SHORT, 2) => true,
        (BindingType::I16I16I16, gl::SHORT, 3) => true,
        (BindingType::I16I16I16I16, gl::SHORT, 4) => true,
        (BindingType::U16, gl::UNSIGNED_SHORT, 1) => true,
        (BindingType::U16U16, gl::UNSIGNED_SHORT, 2) => true,
        (BindingType::U16U16U16, gl::UNSIGNED_SHORT, 3) => true,
        (BindingType::U16U16U16U16, gl::UNSIGNED_SHORT, 4) => true,
        (BindingType::I32, gl::INT, 1) => true,
        (BindingType::I32I32, gl::INT, 2) => true,
        (BindingType::I32I32, gl::INT_VEC2, 1) => true,
        (BindingType::I32I32I32, gl::INT, 3) => true,
        (BindingType::I32I32I32, gl::INT_VEC3, 1) => true,
        (BindingType::I32I32I32I32, gl::INT, 4) => true,
        (BindingType::I32I32I32I32, gl::INT_VEC4, 1) => true,
        (BindingType::I32I32I32I32, gl::INT_VEC2, 2) => true,
        (BindingType::U32, gl::UNSIGNED_INT, 1) => true,
        (BindingType::U32U32, gl::UNSIGNED_INT, 2) => true,
        (BindingType::U32U32, gl::UNSIGNED_INT_VEC2, 1) => true,
        (BindingType::U32U32U32, gl::UNSIGNED_INT, 3) => true,
        (BindingType::U32U32U32, gl::UNSIGNED_INT_VEC3, 1) => true,
        (BindingType::U32U32U32U32, gl::UNSIGNED_INT, 4) => true,
        (BindingType::U32U32U32U32, gl::UNSIGNED_INT_VEC4, 1) => true,
        (BindingType::U32U32U32U32, gl::UNSIGNED_INT_VEC2, 2) => true,
        (BindingType::F32, gl::FLOAT, 1) => true,
        (BindingType::F32F32, gl::FLOAT, 2) => true,
        (BindingType::F32F32, gl::FLOAT_VEC2, 1) => true,
        (BindingType::F32F32F32, gl::FLOAT, 3) => true,
        (BindingType::F32F32F32, gl::FLOAT_VEC3, 1) => true,
        (BindingType::F32F32F32F32, gl::FLOAT, 4) => true,
        (BindingType::F32F32F32F32, gl::FLOAT_VEC4, 1) => true,
        (BindingType::F32F32F32F32, gl::FLOAT_VEC2, 2) => true,
        _ => false,
    }
}
<commit_msg>Now checking attributes matching in the main thread<commit_after>use std::sync::Arc;
use std::mem;

use program::Program;
use vertex_buffer::{mod, VertexBuffer, BindingType};
use {DisplayImpl, IndicesSource, GlObject};

use {libc, gl};

///
pub struct VertexArrayObject {
    display: Arc<DisplayImpl>,
    id: gl::types::GLuint,
}

impl VertexArrayObject {
    ///
    fn new<T>(display: Arc<DisplayImpl>, vertex_buffer: &VertexBuffer<T>,
              ib_id: gl::types::GLuint, program: &Program) -> VertexArrayObject
    {
        let (tx, rx) = channel();

        let bindings = vertex_buffer::get_bindings(vertex_buffer).clone();
        let vb_elementssize = vertex_buffer::get_elements_size(vertex_buffer);
        let vertex_buffer = GlObject::get_id(vertex_buffer);
        let attributes = ::program::get_attributes(program);

        // checking the attributes types
        for &(ref name, _, ty) in bindings.iter() {
            let attribute = match attributes.get(name) {
                Some(a) => a,
                None => continue
            };

            if !vertex_type_matches(ty, attribute.ty, attribute.size) {
                panic!("The program attributes do not match the vertex format");
            }
        }

        display.context.exec(move |: ctxt| {
            unsafe {
                let id: gl::types::GLuint = mem::uninitialized();
                ctxt.gl.GenVertexArrays(1, mem::transmute(&id));
                tx.send(id);

                ctxt.gl.BindVertexArray(id);
                ctxt.state.vertex_array = id;

                // binding vertex buffer
                if ctxt.state.array_buffer_binding != vertex_buffer {
                    ctxt.gl.BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
                    ctxt.state.array_buffer_binding = vertex_buffer;
                }

                // binding index buffer
                ctxt.gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ib_id);

                // binding attributes
                for (name, offset, ty) in bindings.into_iter() {
                    let (data_type, elements_count) = vertex_binding_type_to_gl(ty);

                    let attribute = match attributes.get(&name) {
                        Some(a) => a,
                        None => continue
                    };

                    if attribute.location != -1 {
                        match data_type {
                            gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT |
                            gl::INT | gl::UNSIGNED_INT =>
                                ctxt.gl.VertexAttribIPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type,
                                    vb_elementssize as i32, offset as *const libc::c_void),

                            _ => ctxt.gl.VertexAttribPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type, 0,
                                    vb_elementssize as i32, offset as *const libc::c_void)
                        }

                        ctxt.gl.EnableVertexAttribArray(attribute.location as u32);
                    }
                }
            }
        });

        VertexArrayObject {
            display: display,
            id: rx.recv(),
        }
    }
}

impl Drop for VertexArrayObject {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(move |: ctxt| {
            unsafe {
                // unbinding
                if ctxt.state.vertex_array == id {
                    ctxt.gl.BindVertexArray(0);
                    ctxt.state.vertex_array = 0;
                }

                // deleting
                ctxt.gl.DeleteVertexArrays(1, [ id ].as_ptr());
            }
        });
    }
}

impl GlObject for VertexArrayObject {
    fn get_id(&self) -> gl::types::GLuint {
        self.id
    }
}

pub fn get_vertex_array_object<T, I>(display: &Arc<DisplayImpl>, vertex_buffer: &VertexBuffer<T>,
                                     indices: &I, program: &Program) -> gl::types::GLuint
                                     where I: IndicesSource
{
    let mut vaos = display.vertex_array_objects.lock();

    let ib_id = indices.to_indices_source_helper().index_buffer.map(|b| b.get_id()).unwrap_or(0);
    let vb_id = GlObject::get_id(vertex_buffer);
    let program_id = program.get_id();

    if let Some(value) = vaos.get(&(vb_id, ib_id, program_id)) {
        return value.id;
    }

    let new_vao = VertexArrayObject::new(display.clone(), vertex_buffer, ib_id, program);
    let new_vao_id = new_vao.id;
    vaos.insert((vb_id, ib_id, program_id), new_vao);
    new_vao_id
}

fn vertex_binding_type_to_gl(ty: BindingType) -> (gl::types::GLenum, gl::types::GLint) {
    match ty {
        BindingType::I8 => (gl::BYTE, 1),
        BindingType::I8I8 => (gl::BYTE, 2),
        BindingType::I8I8I8 => (gl::BYTE, 3),
        BindingType::I8I8I8I8 => (gl::BYTE, 4),
        BindingType::U8 => (gl::UNSIGNED_BYTE, 1),
        BindingType::U8U8 => (gl::UNSIGNED_BYTE, 2),
        BindingType::U8U8U8 => (gl::UNSIGNED_BYTE, 3),
        BindingType::U8U8U8U8 => (gl::UNSIGNED_BYTE, 4),
        BindingType::I16 => (gl::SHORT, 1),
        BindingType::I16I16 => (gl::SHORT, 2),
        BindingType::I16I16I16 => (gl::SHORT, 3),
        BindingType::I16I16I16I16 => (gl::SHORT, 4),
        BindingType::U16 => (gl::UNSIGNED_SHORT, 1),
        BindingType::U16U16 => (gl::UNSIGNED_SHORT, 2),
        BindingType::U16U16U16 => (gl::UNSIGNED_SHORT, 3),
        BindingType::U16U16U16U16 => (gl::UNSIGNED_SHORT, 4),
        BindingType::I32 => (gl::INT, 1),
        BindingType::I32I32 => (gl::INT, 2),
        BindingType::I32I32I32 => (gl::INT, 3),
        BindingType::I32I32I32I32 => (gl::INT, 4),
        BindingType::U32 => (gl::UNSIGNED_INT, 1),
        BindingType::U32U32 => (gl::UNSIGNED_INT, 2),
        BindingType::U32U32U32 => (gl::UNSIGNED_INT, 3),
        BindingType::U32U32U32U32 => (gl::UNSIGNED_INT, 4),
        BindingType::F32 => (gl::FLOAT, 1),
        BindingType::F32F32 => (gl::FLOAT, 2),
        BindingType::F32F32F32 => (gl::FLOAT, 3),
        BindingType::F32F32F32F32 => (gl::FLOAT, 4),
    }
}

fn vertex_type_matches(ty: BindingType, gl_ty: gl::types::GLenum,
                       gl_size: gl::types::GLint) -> bool
{
    match (ty, gl_ty, gl_size) {
        (BindingType::I8, gl::BYTE, 1) => true,
        (BindingType::I8I8, gl::BYTE, 2) => true,
        (BindingType::I8I8I8, gl::BYTE, 3) => true,
        (BindingType::I8I8I8I8, gl::BYTE, 4) => true,
        (BindingType::U8, gl::UNSIGNED_BYTE, 1) => true,
        (BindingType::U8U8, gl::UNSIGNED_BYTE, 2) => true,
        (BindingType::U8U8U8, gl::UNSIGNED_BYTE, 3) => true,
        (BindingType::U8U8U8U8, gl::UNSIGNED_BYTE, 4) => true,
        (BindingType::I16, gl::SHORT, 1) => true,
        (BindingType::I16I16, gl::SHORT, 2) => true,
        (BindingType::I16I16I16, gl::SHORT, 3) => true,
        (BindingType::I16I16I16I16, gl::SHORT, 4) => true,
        (BindingType::U16, gl::UNSIGNED_SHORT, 1) => true,
        (BindingType::U16U16, gl::UNSIGNED_SHORT, 2) => true,
        (BindingType::U16U16U16, gl::UNSIGNED_SHORT, 3) => true,
        (BindingType::U16U16U16U16, gl::UNSIGNED_SHORT, 4) => true,
        (BindingType::I32, gl::INT, 1) => true,
        (BindingType::I32I32, gl::INT, 2) => true,
        (BindingType::I32I32, gl::INT_VEC2, 1) => true,
        (BindingType::I32I32I32, gl::INT, 3) => true,
        (BindingType::I32I32I32, gl::INT_VEC3, 1) => true,
        (BindingType::I32I32I32I32, gl::INT, 4) => true,
        (BindingType::I32I32I32I32, gl::INT_VEC4, 1) => true,
        (BindingType::I32I32I32I32, gl::INT_VEC2, 2) => true,
        (BindingType::U32, gl::UNSIGNED_INT, 1) => true,
        (BindingType::U32U32, gl::UNSIGNED_INT, 2) => true,
        (BindingType::U32U32, gl::UNSIGNED_INT_VEC2, 1) => true,
        (BindingType::U32U32U32, gl::UNSIGNED_INT, 3) => true,
        (BindingType::U32U32U32, gl::UNSIGNED_INT_VEC3, 1) => true,
        (BindingType::U32U32U32U32, gl::UNSIGNED_INT, 4) => true,
        (BindingType::U32U32U32U32, gl::UNSIGNED_INT_VEC4, 1) => true,
        (BindingType::U32U32U32U32, gl::UNSIGNED_INT_VEC2, 2) => true,
        (BindingType::F32, gl::FLOAT, 1) => true,
        (BindingType::F32F32, gl::FLOAT, 2) => true,
        (BindingType::F32F32, gl::FLOAT_VEC2, 1) => true,
        (BindingType::F32F32F32, gl::FLOAT, 3) => true,
        (BindingType::F32F32F32, gl::FLOAT_VEC3, 1) => true,
        (BindingType::F32F32F32F32, gl::FLOAT, 4) => true,
        (BindingType::F32F32F32F32, gl::FLOAT_VEC4, 1) => true,
        (BindingType::F32F32F32F32, gl::FLOAT_VEC2, 2) => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use std::bigint::{ BigUint };

use common::problem::{ Problem };

pub static problem: Problem<'static> = Problem {
    id: 56,
    answer: "972",
    solver: solve
};

fn digit_sum(n: uint) -> uint {
    let mut sum = 0;
    let mut d = n;
    while d > 0 {
        sum += d % 10;
        d = d / 10;
    }
    return sum;
}

fn solve() -> ~str {
    let ten = BigUint::from_uint(1000000000);

    let mut max = 0u;
    for uint::range(1, 100) |a| {
        let mut n = BigUint::from_uint(a);
        for 100.times {
            n = n * BigUint::from_uint(a);
            let mut sum = 0;
            let mut (d, m) = n.div_mod(&ten);
            sum += digit_sum(m.to_uint());
            while !d.is_zero() {
                let (d0, m) = d.div_mod(&ten);
                sum += digit_sum(m.to_uint());
                d = d0;
            }
            // following core causes core dumps...
            // let s = n.to_str();
            // for str::each_char(s) |c| {
            //     let d = char::to_digit(c, 10);
            //     sum += d.get();
            // }
            max = uint::max(max, sum);
        }
    }

    return max.to_str();
}
<commit_msg>Add `use core::num::{ Zero };`<commit_after>use core::num::{ Zero };
use std::bigint::{ BigUint };

use common::problem::{ Problem };

pub static problem: Problem<'static> = Problem {
    id: 56,
    answer: "972",
    solver: solve
};

fn digit_sum(n: uint) -> uint {
    let mut sum = 0;
    let mut d = n;
    while d > 0 {
        sum += d % 10;
        d = d / 10;
    }
    return sum;
}

fn solve() -> ~str {
    let ten = BigUint::from_uint(1000000000);

    let mut max = 0u;
    for uint::range(1, 100) |a| {
        let mut n = BigUint::from_uint(a);
        for 100.times {
            n = n * BigUint::from_uint(a);
            let mut sum = 0;
            let mut (d, m) = n.div_mod(&ten);
            sum += digit_sum(m.to_uint());
            while !d.is_zero() {
                let (d0, m) = d.div_mod(&ten);
                sum += digit_sum(m.to_uint());
                d = d0;
            }
            // following core causes core dumps...
            // let s = n.to_str();
            // for str::each_char(s) |c| {
            //     let d = char::to_digit(c, 10);
            //     sum += d.get();
            // }
            max = uint::max(max, sum);
        }
    }

    return max.to_str();
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate metadeps;

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    // Use the installed libcares if it is available.
    if metadeps::probe().is_ok() {
        return;
    }

    // Rerun if the c-ares source code has changed.
    println!("cargo:rerun-if-changed=c-ares");

    // MSVC builds are different.
    let target = env::var("TARGET").unwrap();
    if target.contains("msvc") {
        build_msvc(&target);
        return;
    }

    // Set up compiler options.
    let mut cflags = env::var("CFLAGS").unwrap_or_else(|_| String::new());
    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let _ = fs::create_dir(&build);

    let mut config_opts = Vec::new();
    config_opts.push("--enable-static=yes".to_string());
    config_opts.push("--enable-shared=no".to_string());
    config_opts.push("--enable-optimize".to_string());
    config_opts.push(format!("--prefix={}", dst.display()));

    // Prepare.
    let src = env::current_dir().unwrap();
    run(
        Command::new("sh")
            .current_dir(&src.join("c-ares"))
            .arg("buildconf"),
    );
    run(
        Command::new("sh")
            .env("CFLAGS", &cflags)
            .current_dir(&build)
            .arg("-c")
            .arg(format!(
                "{} {}",
                src.join("c-ares/configure").display(),
                config_opts.join(" ")
            )),
    );

    // Compile.
    run(
        Command::new(make())
            .arg(format!("-j{}", env::var("NUM_JOBS").unwrap()))
            .current_dir(&build),
    );

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/.libs", build.display());
    println!("cargo:rustc-link-lib=static=cares");
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {
        "gmake"
    } else {
        "make"
    }
}

fn nmake(target: &str) -> Command {
    // cargo messes with the environment in a way that nmake does not like -
    // see https://github.com/rust-lang/cargo/issues/4156.  Explicitly remove
    // the unwanted variables.
    let mut cmd = gcc::windows_registry::find(target, "nmake.exe").unwrap();
    cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    cmd
}

fn build_msvc(target: &str) {
    // Prepare.
    let src = env::current_dir().unwrap();
    let c_ares_dir = &src.join("c-ares");
    run(
        Command::new("cmd")
            .current_dir(c_ares_dir)
            .arg("/c")
            .arg("buildconf.bat"),
    );

    // Compile.
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "CFG=lib-release", "c-ares"]);
    run(&mut cmd);

    // Install library.
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "/a", "CFG=lib-release", "install"]);
    cmd.env("INSTALL_DIR", format!("{}", build.display()));
    run(&mut cmd);

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/lib", build.display());
    println!("cargo:rustc-link-lib=static=libcares");
}
<commit_msg>rearrange the ./configure stage<commit_after>extern crate gcc;
extern crate metadeps;

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    // Use the installed libcares if it is available.
    if metadeps::probe().is_ok() {
        return;
    }

    // Rerun if the c-ares source code has changed.
    println!("cargo:rerun-if-changed=c-ares");

    // MSVC builds are different.
    let target = env::var("TARGET").unwrap();
    if target.contains("msvc") {
        build_msvc(&target);
        return;
    }

    // Set up compiler options.
    let mut cflags = env::var("CFLAGS").unwrap_or_else(|_| String::new());
    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let _ = fs::create_dir(&build);

    // Prepare.
    let src = env::current_dir().unwrap();
    run(
        Command::new("sh")
            .current_dir(&src.join("c-ares"))
            .arg("buildconf"),
    );

    // Configure.
    let mut cmd = Command::new("sh");
    cmd.env("CFLAGS", &cflags)
        .current_dir(&build)
        .arg("-c")
        .arg(format!("{}", src.join("c-ares/configure").display()))
        .arg("--enable-static=yes")
        .arg("--enable-shared=no")
        .arg("--enable-optimize")
        .arg(format!("--prefix={}", dst.display()));
    run(&mut cmd);

    // Compile.
    run(
        Command::new(make())
            .arg(format!("-j{}", env::var("NUM_JOBS").unwrap()))
            .current_dir(&build),
    );

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/.libs", build.display());
    println!("cargo:rustc-link-lib=static=cares");
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {
        "gmake"
    } else {
        "make"
    }
}

fn nmake(target: &str) -> Command {
    // cargo messes with the environment in a way that nmake does not like -
    // see https://github.com/rust-lang/cargo/issues/4156.  Explicitly remove
    // the unwanted variables.
    let mut cmd = gcc::windows_registry::find(target, "nmake.exe").unwrap();
    cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    cmd
}

fn build_msvc(target: &str) {
    // Prepare.
    let src = env::current_dir().unwrap();
    let c_ares_dir = &src.join("c-ares");
    run(
        Command::new("cmd")
            .current_dir(c_ares_dir)
            .arg("/c")
            .arg("buildconf.bat"),
    );

    // Compile.
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "CFG=lib-release", "c-ares"]);
    run(&mut cmd);

    // Install library.
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dst.join("build");
    let mut cmd = nmake(target);
    cmd.current_dir(c_ares_dir);
    cmd.args(&["/f", "Makefile.msvc", "/a", "CFG=lib-release", "install"]);
    cmd.env("INSTALL_DIR", format!("{}", build.display()));
    run(&mut cmd);

    // Link to compiled library.
    println!("cargo:rustc-link-search={}/lib", build.display());
    println!("cargo:rustc-link-lib=static=libcares");
}
<|endoftext|>


<commit_before>use std::alloc::alloc;
use std::alloc::dealloc;
use std::alloc::Layout;
use std::mem::size_of;
use std::ptr::null_mut;

const _PAGE_SIZE: usize = 0x1000;

pub struct _Page {
    current_page: *mut _Page,
    next_object_offset: i32,
    exclusive_pages: i32,
}

impl _Page {
    pub fn reset(&mut self) {
        unsafe {
            *(self.get_extension_page_location()) = null_mut();
        }
        self.next_object_offset = size_of::<_Page>() as i32;
        self.exclusive_pages = 0;
        self.current_page = &mut *self;
    }

    pub fn clear(&mut self) {
        self.deallocate_extensions();
        self.reset();
    }

    pub fn is_oversized(&self) -> bool {
        self.current_page.is_null()
    }

    fn get_extension_page_location(&self) -> *mut *mut _Page {
        // Convert self to page location
        let self_location = self as *const _Page as usize;

        // Advance one page size so we are past the end of our page
        let location_behind_page = (self_location + _PAGE_SIZE) as *mut *mut _Page;

        // Go back one pointer size
        unsafe { location_behind_page.offset(-1) }
    }

    fn get_next_location(&self) -> usize {
        self as *const _Page as usize + self.next_object_offset as usize
    }

    fn set_next_location(&mut self, location: usize) {
        let self_location = self as *const _Page as usize;
        self.next_object_offset = (location - self_location as usize) as i32;
    }

    fn get_next_exclusive_page_location(&self) -> *mut *mut _Page {
        unsafe {
            self.get_extension_page_location()
                .offset(-(self.exclusive_pages as isize + 1))
        }
    }

    pub fn allocate_raw(&mut self, size: usize, align: usize) -> *mut u8 {
        if self as *mut _Page != self.current_page {
            unsafe {
                // We're already known to be full, so we delegate to the current page
                let new_object = (*self.current_page).allocate_raw(size, align);

                // Possibly our current page was also full so we propagate back the new current page
                let allocating_page = _Page::get_page(new_object as usize);
                if allocating_page != self.current_page && (!(*allocating_page).is_oversized()) {
                    self.current_page = allocating_page;
                }
                return new_object;
            }
        }

        // Try to allocate from ourselves
        let location = self.get_next_location();
        let aligned_location = (location + align - 1) & !(align - 1);
        let next_location = aligned_location + size;
        if next_location <= self.get_next_exclusive_page_location() as usize {
            self.set_next_location(next_location);
            return location as *mut u8;
        }

        // So the space did not fit.

        // Calculate gross size to decide whether we're oversized
        if size_of::<_Page>() + size + size_of::<*mut *mut _Page>() > _PAGE_SIZE {
            if self.get_next_location() >= self.get_next_exclusive_page_location() as usize {
                // Allocate an extension page and try again with it
                unsafe {
                    return (*self.allocate_extension_page()).allocate_raw(size, align);
                }
            }

            unsafe {
                let gross_size = size + size_of::<_Page>();
                // We allocate oversized objects directly.
                let memory = alloc(Layout::from_size_align_unchecked(
                    gross_size,
                    _PAGE_SIZE,
                ));

                // Initialize a _Page object at the page start
                let page = memory as *mut _Page;

                // Oversized pages have no current_page
                (*page).current_page = null_mut();

                // Set the size since we will need it when deallocating
                (*page).next_object_offset = (gross_size % 0x100000000) as i32;
                (*page).exclusive_pages = (gross_size / 0x100000000) as i32;

                *(self.get_next_exclusive_page_location()) = page;
                self.exclusive_pages += 1;
                return page.offset(1) as *mut u8;
            }
        }

        // So we're not oversized. Create extension page and let it allocate.
        unsafe { (*self.allocate_extension_page()).allocate_raw(size, align) }
    }

    fn allocate_extension_page(&mut self) -> *mut _Page {
        unsafe {
            let page = _Page::allocate_page();
            *(self.get_extension_page_location()) = page;
            self.current_page = page;
            &mut *page
        }
    }

    fn allocate_page() -> *mut _Page {
        unsafe {
            let page =
                alloc(Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE)) as *mut _Page;
            (*page).reset();
            page
        }
    }

    pub fn allocate_exclusive_page(&mut self) -> *mut _Page {
        unsafe {
            if self as *mut _Page != self.current_page {
                // We're already known to be full, so we delegate to the current page
                return (*self.current_page).allocate_exclusive_page();
            }

            // Check first whether we need an ordinary extension
            if self.get_next_location() as usize >= self.get_next_exclusive_page_location() as usize
            {
                // Allocate an extension page with default size
                return (*self.allocate_extension_page()).allocate_exclusive_page();
            }

            let page = _Page::allocate_page();
            *(self.get_next_exclusive_page_location()) = page;
            self.exclusive_pages += 1;
            &mut *page
        }
    }

    pub fn extend(&mut self, top: usize, size: usize) -> bool {
        if top != self.get_next_location() {
            return false;
        }

        let new_top = top + size;
        if new_top > self.get_next_exclusive_page_location() as usize {
            return false;
        }
        self.set_next_location(new_top);

        true
    }

    fn deallocate_extensions(&mut self) {
        let mut page: *mut _Page = self;
        unsafe {
            while !page.is_null() {
                let extension_pointer = (*page).get_extension_page_location();
                for i in 1..(*page).exclusive_pages {
                    let mut exclusive_page = &mut **(extension_pointer.offset(-(i as isize)));
                    if !exclusive_page.is_oversized() {
                        exclusive_page.deallocate_extensions();
                    }
                    exclusive_page.forget();
                }

                if page != self as *mut _Page {
                    (*page).forget();
                }

                page = *extension_pointer;
            }
        }
    }

    pub fn forget(&self) {
        let size = if self.current_page == null_mut() {
            self.next_object_offset as usize + self.exclusive_pages as usize * 0x100000000
        } else {
            _PAGE_SIZE
        };
        unsafe {
            dealloc(
                self as *const _Page as *mut u8,
                Layout::from_size_align_unchecked(size, _PAGE_SIZE),
            )
        }
    }

    pub fn reclaim_array(&mut self, address: *mut _Page) -> bool {
        unsafe {
            let mut page: *mut _Page = self;
            while !page.is_null() {
                if (*page).deallocate_exclusive_page(address) {
                    return true;
                }
                page = *(*page).get_extension_page_location();
            }
        }

        // If we arrive here, we have a memory leak.
        false
    }

    pub fn get_page(address: usize) -> *mut _Page {
        (address & !(_PAGE_SIZE - 1)) as *mut _Page
    }

    fn deallocate_exclusive_page(&mut self, page: *mut _Page) -> bool {
        unsafe {
            // Find the extension Page pointer
            let mut extension_pointer = self.get_extension_page_location().offset(-1);
            let next_extension_page_location = self.get_next_exclusive_page_location();
            while extension_pointer > next_extension_page_location {
                if *extension_pointer == page {
                    break;
                }
                extension_pointer = extension_pointer.offset(-1);
            }

            // Report if we could not find it
            if extension_pointer == next_extension_page_location {
                return false;
            }

            // Shift the remaining array one position up
            while extension_pointer > next_extension_page_location {
                let pp_page = extension_pointer;
                *pp_page = *(pp_page.offset(-1));
                extension_pointer = extension_pointer.offset(-1);
            }
        }

        // Make room for one more extension
        self.exclusive_pages -= 1;
        unsafe {
            (*page).forget();
        }

        true
    }
}

#[test]
fn test_page() {
    unsafe {
        // Allocate a page
        let memory = alloc(Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE));
        assert_ne!(memory, null_mut());

        // Write some numbers to the page memory
        for i in 0.._PAGE_SIZE {
            let ptr = memory.offset(i as isize);
            *ptr = i as u8
        }

        // Initialize a _Page object at the page start
        let page = &mut *(memory as *mut _Page);

        assert_ne!(page.next_object_offset, 0);
        assert_ne!(page.exclusive_pages, 0);

        page.reset();

        assert_eq!(page.next_object_offset, size_of::<_Page>() as i32);
        assert_eq!(page.exclusive_pages, 0);
        assert_eq!(page.is_oversized(), false);
        assert_eq!(page as *mut _Page, page.current_page);

        {
            let extension_page_location = page.get_extension_page_location();
            assert_eq!(
                extension_page_location as usize,
                page as *const _Page as usize + _PAGE_SIZE - size_of::<*mut *mut _Page>()
            );
        }

        {
            let mut location = page.get_next_location();
            assert_eq!(
                location as usize,
                page as *const _Page as usize + size_of::<_Page>()
            );

            let answer = page.allocate_raw(1, 1);
            location += 1;
            assert_eq!(page.get_next_location(), location);
            *answer = 42;

            let another = page.allocate_raw(1, 2);
            location += 2;
            assert_eq!(page.get_next_location(), location);
            *another = 43;

            let eau = page.allocate_raw(4, 4) as *mut i32;
            location += 5;
            assert_eq!(page.get_next_location(), location);
            *eau = 4711;
            assert_eq!(_Page::get_page(eau as usize), page as *mut _Page);

            // Allocate an oversized page which should cause allocating an exclusive page
            let array = page.allocate_raw(_PAGE_SIZE, 8) as *mut usize;
            for i in 0.._PAGE_SIZE / size_of::<usize>() - 1 {
                *(array.offset(i as isize)) = i;
            }
            assert_eq!(page.get_next_location(), location);
            assert_eq!(page as *mut _Page, page.current_page);

            // Overflow the page
            for _ in 0.._PAGE_SIZE {
                page.allocate_raw(1, 1);
            }

            assert_ne!(page as *mut _Page, page.current_page);
            assert_eq!(*(page.get_extension_page_location()), page.current_page);
            assert_eq!((*page.current_page).exclusive_pages, 0);
            assert_eq!((*page.current_page).current_page, page.current_page);

            assert_eq!(page.exclusive_pages, 1);
            assert_eq!(*answer, 42);
            assert_eq!(*another, 43);
            assert_eq!(*eau, 4711);

            let exclusive_page = page.get_next_exclusive_page_location().offset(1);
            assert_eq!((**exclusive_page).current_page, null_mut());
            assert_eq!((**exclusive_page).exclusive_pages, 0);
            assert_eq!((**exclusive_page).next_object_offset as usize, size_of::<_Page>() + _PAGE_SIZE );
            assert_eq!(_Page::get_page(array as usize), *exclusive_page);

            let success = page.reclaim_array(_Page::get_page(array as usize));
            assert_eq!(success, true);
            assert_eq!(page.exclusive_pages, 0);

            page.clear();
            page.forget();
        }
    }
}
<commit_msg>allocate<commit_after>use std::alloc::alloc;
use std::alloc::dealloc;
use std::alloc::Layout;
use std::mem::size_of;
use std::mem::align_of;
use std::ptr::null_mut;
use std::ptr::write;

const _PAGE_SIZE: usize = 0x1000;

pub struct _Page {
    current_page: *mut _Page,
    next_object_offset: i32,
    exclusive_pages: i32,
}

impl _Page {
    pub fn reset(&mut self) {
        unsafe {
            *(self.get_extension_page_location()) = null_mut();
        }
        self.next_object_offset = size_of::<_Page>() as i32;
        self.exclusive_pages = 0;
        self.current_page = &mut *self;
    }

    pub fn clear(&mut self) {
        self.deallocate_extensions();
        self.reset();
    }

    pub fn is_oversized(&self) -> bool {
        self.current_page.is_null()
    }

    fn get_extension_page_location(&self) -> *mut *mut _Page {
        // Convert self to page location
        let self_location = self as *const _Page as usize;

        // Advance one page size so we are past the end of our page
        let location_behind_page = (self_location + _PAGE_SIZE) as *mut *mut _Page;

        // Go back one pointer size
        unsafe { location_behind_page.offset(-1) }
    }

    fn get_next_location(&self) -> usize {
        self as *const _Page as usize + self.next_object_offset as usize
    }

    fn set_next_location(&mut self, location: usize) {
        let self_location = self as *const _Page as usize;
        self.next_object_offset = (location - self_location as usize) as i32;
    }

    fn get_next_exclusive_page_location(&self) -> *mut *mut _Page {
        unsafe {
            self.get_extension_page_location()
                .offset(-(self.exclusive_pages as isize + 1))
        }
    }

    pub fn allocate<T>(&mut self, object: T) -> *mut T {

        let memory = self.allocate_raw(size_of::<T>(), align_of::<T>()) as *mut T;

        unsafe {
            // Write into uninitialized memory.
            write(memory, object);
            memory
        }
    }

    pub fn allocate_raw(&mut self, size: usize, align: usize) -> *mut u8 {
        if self as *mut _Page != self.current_page {
            unsafe {
                // We're already known to be full, so we delegate to the current page
                let new_object = (*self.current_page).allocate_raw(size, align);

                // Possibly our current page was also full so we propagate back the new current page
                let allocating_page = _Page::get_page(new_object as usize);
                if allocating_page != self.current_page && (!(*allocating_page).is_oversized()) {
                    self.current_page = allocating_page;
                }
                return new_object;
            }
        }

        // Try to allocate from ourselves
        let location = self.get_next_location();
        let aligned_location = (location + align - 1) & !(align - 1);
        let next_location = aligned_location + size;
        if next_location <= self.get_next_exclusive_page_location() as usize {
            self.set_next_location(next_location);
            return location as *mut u8;
        }

        // So the space did not fit.

        // Calculate gross size to decide whether we're oversized
        if size_of::<_Page>() + size + size_of::<*mut *mut _Page>() > _PAGE_SIZE {
            if self.get_next_location() >= self.get_next_exclusive_page_location() as usize {
                // Allocate an extension page and try again with it
                unsafe {
                    return (*self.allocate_extension_page()).allocate_raw(size, align);
                }
            }

            unsafe {
                let gross_size = size + size_of::<_Page>();
                // We allocate oversized objects directly.
                let memory = alloc(Layout::from_size_align_unchecked(
                    gross_size,
                    _PAGE_SIZE,
                ));

                // Initialize a _Page object at the page start
                let page = memory as *mut _Page;

                // Oversized pages have no current_page
                (*page).current_page = null_mut();

                // Set the size since we will need it when deallocating
                (*page).next_object_offset = (gross_size % 0x100000000) as i32;
                (*page).exclusive_pages = (gross_size / 0x100000000) as i32;

                *(self.get_next_exclusive_page_location()) = page;
                self.exclusive_pages += 1;
                return page.offset(1) as *mut u8;
            }
        }

        // So we're not oversized. Create extension page and let it allocate.
        unsafe { (*self.allocate_extension_page()).allocate_raw(size, align) }
    }

    fn allocate_extension_page(&mut self) -> *mut _Page {
        unsafe {
            let page = _Page::allocate_page();
            *(self.get_extension_page_location()) = page;
            self.current_page = page;
            &mut *page
        }
    }

    fn allocate_page() -> *mut _Page {
        unsafe {
            let page =
                alloc(Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE)) as *mut _Page;
            (*page).reset();
            page
        }
    }

    pub fn allocate_exclusive_page(&mut self) -> *mut _Page {
        unsafe {
            if self as *mut _Page != self.current_page {
                // We're already known to be full, so we delegate to the current page
                return (*self.current_page).allocate_exclusive_page();
            }

            // Check first whether we need an ordinary extension
            if self.get_next_location() as usize >= self.get_next_exclusive_page_location() as usize
            {
                // Allocate an extension page with default size
                return (*self.allocate_extension_page()).allocate_exclusive_page();
            }

            let page = _Page::allocate_page();
            *(self.get_next_exclusive_page_location()) = page;
            self.exclusive_pages += 1;
            &mut *page
        }
    }

    pub fn extend(&mut self, top: usize, size: usize) -> bool {
        if top != self.get_next_location() {
            return false;
        }

        let new_top = top + size;
        if new_top > self.get_next_exclusive_page_location() as usize {
            return false;
        }
        self.set_next_location(new_top);

        true
    }

    fn deallocate_extensions(&mut self) {
        let mut page: *mut _Page = self;
        unsafe {
            while !page.is_null() {
                let extension_pointer = (*page).get_extension_page_location();
                for i in 1..(*page).exclusive_pages {
                    let mut exclusive_page = &mut **(extension_pointer.offset(-(i as isize)));
                    if !exclusive_page.is_oversized() {
                        exclusive_page.deallocate_extensions();
                    }
                    exclusive_page.forget();
                }

                if page != self as *mut _Page {
                    (*page).forget();
                }

                page = *extension_pointer;
            }
        }
    }

    pub fn forget(&self) {
        let size = if self.current_page == null_mut() {
            self.next_object_offset as usize + self.exclusive_pages as usize * 0x100000000
        } else {
            _PAGE_SIZE
        };
        unsafe {
            dealloc(
                self as *const _Page as *mut u8,
                Layout::from_size_align_unchecked(size, _PAGE_SIZE),
            )
        }
    }

    pub fn reclaim_array(&mut self, address: *mut _Page) -> bool {
        unsafe {
            let mut page: *mut _Page = self;
            while !page.is_null() {
                if (*page).deallocate_exclusive_page(address) {
                    return true;
                }
                page = *(*page).get_extension_page_location();
            }
        }

        // If we arrive here, we have a memory leak.
        false
    }

    pub fn get_page(address: usize) -> *mut _Page {
        (address & !(_PAGE_SIZE - 1)) as *mut _Page
    }

    fn deallocate_exclusive_page(&mut self, page: *mut _Page) -> bool {
        unsafe {
            // Find the extension Page pointer
            let mut extension_pointer = self.get_extension_page_location().offset(-1);
            let next_extension_page_location = self.get_next_exclusive_page_location();
            while extension_pointer > next_extension_page_location {
                if *extension_pointer == page {
                    break;
                }
                extension_pointer = extension_pointer.offset(-1);
            }

            // Report if we could not find it
            if extension_pointer == next_extension_page_location {
                return false;
            }

            // Shift the remaining array one position up
            while extension_pointer > next_extension_page_location {
                let pp_page = extension_pointer;
                *pp_page = *(pp_page.offset(-1));
                extension_pointer = extension_pointer.offset(-1);
            }
        }

        // Make room for one more extension
        self.exclusive_pages -= 1;
        unsafe {
            (*page).forget();
        }

        true
    }
}

#[test]
fn test_page() {
    unsafe {
        // Allocate a page
        let memory = alloc(Layout::from_size_align_unchecked(_PAGE_SIZE, _PAGE_SIZE));
        assert_ne!(memory, null_mut());

        // Write some numbers to the page memory
        for i in 0.._PAGE_SIZE {
            let ptr = memory.offset(i as isize);
            *ptr = i as u8
        }

        // Initialize a _Page object at the page start
        let page = &mut *(memory as *mut _Page);

        assert_ne!(page.next_object_offset, 0);
        assert_ne!(page.exclusive_pages, 0);

        page.reset();

        assert_eq!(page.next_object_offset, size_of::<_Page>() as i32);
        assert_eq!(page.exclusive_pages, 0);
        assert_eq!(page.is_oversized(), false);
        assert_eq!(page as *mut _Page, page.current_page);

        {
            let extension_page_location = page.get_extension_page_location();
            assert_eq!(
                extension_page_location as usize,
                page as *const _Page as usize + _PAGE_SIZE - size_of::<*mut *mut _Page>()
            );
        }

        {
            let mut location = page.get_next_location();
            assert_eq!(
                location as usize,
                page as *const _Page as usize + size_of::<_Page>()
            );

            let answer = page.allocate(42);
            location += 4;
            assert_eq!(page.get_next_location(), location);

            let another = page.allocate_raw(1, 2);
            location += 1;
            assert_eq!(page.get_next_location(), location);
            *another = 43;

            let eau = page.allocate_raw(4, 4) as *mut i32;
            location += 7;
            assert_eq!(page.get_next_location(), location);
            *eau = 4711;
            assert_eq!(_Page::get_page(eau as usize), page as *mut _Page);

            // Allocate an oversized page which should cause allocating an exclusive page
            let array = page.allocate_raw(_PAGE_SIZE, 8) as *mut usize;
            for i in 0.._PAGE_SIZE / size_of::<usize>() - 1 {
                *(array.offset(i as isize)) = i;
            }
            assert_eq!(page.get_next_location(), location);
            assert_eq!(page as *mut _Page, page.current_page);

            // Overflow the page
            for _ in 0.._PAGE_SIZE {
                page.allocate_raw(1, 1);
            }

            assert_ne!(page as *mut _Page, page.current_page);
            assert_eq!(*(page.get_extension_page_location()), page.current_page);
            assert_eq!((*page.current_page).exclusive_pages, 0);
            assert_eq!((*page.current_page).current_page, page.current_page);

            assert_eq!(page.exclusive_pages, 1);
            assert_eq!(*answer, 42);
            assert_eq!(*another, 43);
            assert_eq!(*eau, 4711);

            let exclusive_page = page.get_next_exclusive_page_location().offset(1);
            assert_eq!((**exclusive_page).current_page, null_mut());
            assert_eq!((**exclusive_page).exclusive_pages, 0);
            assert_eq!((**exclusive_page).next_object_offset as usize, size_of::<_Page>() + _PAGE_SIZE );
            assert_eq!(_Page::get_page(array as usize), *exclusive_page);

            let success = page.reclaim_array(_Page::get_page(array as usize));
            assert_eq!(success, true);
            assert_eq!(page.exclusive_pages, 0);

            page.clear();
            page.forget();
        }
    }
}
<|endoftext|>