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

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

use devicemapper::{Bytes, DM, DmDevice, DmName, DmUuid, IEC, SECTOR_SIZE, Sectors, ThinDev,
                   ThinDevId, ThinPoolDev, ThinStatus};

use mnt::{MountIter, MountParam};
use nix::mount::{MsFlags, mount, umount};
use nix::sys::statvfs::statvfs;
use tempdir::TempDir;

use super::super::super::engine::Filesystem;
use super::super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::super::types::{FilesystemUuid, Name};

use super::super::serde_structs::FilesystemSave;

use super::util::{create_fs, set_uuid, xfs_growfs};

/// TODO: confirm that 256 MiB leaves enough time for stratisd to respond and extend before
/// the filesystem is out of space.
pub const FILESYSTEM_LOWATER: Sectors = Sectors(256 * IEC::Mi / (SECTOR_SIZE as u64)); // = 256 MiB

#[derive(Debug)]
pub struct StratFilesystem {
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    XfsGrowFailed,
    ThinDevExtendFailed,
    Failed,
}

impl StratFilesystem {
    /// Create a StratFilesystem on top of the given ThinDev.
    pub fn initialize(fs_id: FilesystemUuid, thin_dev: ThinDev) -> EngineResult<StratFilesystem> {
        let fs = StratFilesystem::setup(thin_dev);

        create_fs(&fs.devnode(), fs_id)?;
        Ok(fs)
    }

    /// Build a StratFilesystem that includes the ThinDev and related info.
    pub fn setup(thin_dev: ThinDev) -> StratFilesystem {
        StratFilesystem { thin_dev }
    }

    /// Create a snapshot of the filesystem. Return the resulting filesystem/ThinDev
    /// to the caller.  Use snapshot_name for the Stratis filesystem name.  Use
    /// snapshot_dmname for the new name of the ThinDev allocated for the snapshot.
    /// Mounting a filesystem with a duplicate UUID would require special handling,
    /// so snapshot_fs_uuid is used to update the new snapshot filesystem so it has
    /// a unique UUID.
    #[allow(too_many_arguments)]
    pub fn snapshot(&self,
                    dm: &DM,
                    thin_pool: &ThinPoolDev,
                    snapshot_name: &str,
                    snapshot_dm_name: &DmName,
                    snapshot_dm_uuid: Option<&DmUuid>,
                    snapshot_fs_name: &Name,
                    snapshot_fs_uuid: FilesystemUuid,
                    snapshot_thin_id: ThinDevId)
                    -> EngineResult<StratFilesystem> {

        match self.thin_dev
                  .snapshot(dm,
                            snapshot_dm_name,
                            snapshot_dm_uuid,
                            thin_pool,
                            snapshot_thin_id) {
            Ok(thin_dev) => {
                // If the source is mounted, XFS puts a dummy record in the
                // log to enforce replay of the snapshot to deal with any
                // orphaned inodes. The dummy record put the log in a dirty
                // state. xfs_admin won't allow a filesystem UUID
                // to be updated when the log is dirty.  To clear the log
                // we mount/unmount the filesystem before updating the UUID.
                //
                // If the source is unmounted the XFS log will be clean so
                // we can skip the mount/unmount.

                // FIXME: get_mount_point doesn't work so assume we need to mount/unmount
                let tmp_dir = TempDir::new("stratis_mp_")?;
                // Mount the snapshot with the "nouuid" option. mount
                // will fail due to duplicate UUID otherwise.
                mount(Some(&thin_dev.devnode()),
                      tmp_dir.path(),
                      Some("xfs"),
                      MsFlags::empty(),
                      Some("nouuid"))?;
                umount(tmp_dir.path())?;

                set_uuid(&thin_dev.devnode(), snapshot_fs_uuid)?;
                Ok(StratFilesystem::setup(thin_dev))
            }
            Err(e) => {
                Err(EngineError::Engine(ErrorEnum::Error,
                                        format!("failed to create {} snapshot for {} - {}",
                                                snapshot_name,
                                                snapshot_fs_name,
                                                e)))
            }
        }
    }

    /// check if filesystem is getting full and needs to be extended
    /// TODO: deal with the thindev in a Fail state.
    pub fn check(&mut self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match self.thin_dev.status(dm)? {
            ThinStatus::Working(_) => {
                if let Some(mount_point) = self.get_mount_point()? {
                    let (fs_total_bytes, fs_total_used_bytes) = fs_usage(&mount_point)?;
                    let free_bytes = fs_total_bytes - fs_total_used_bytes;
                    if free_bytes.sectors() < FILESYSTEM_LOWATER {
                        let extend_size = self.extend_size(self.thin_dev.size());
                        if self.thin_dev.extend(dm, extend_size).is_err() {
                            return Ok(FilesystemStatus::ThinDevExtendFailed);
                        }
                        if xfs_growfs(&mount_point).is_err() {
                            return Ok(FilesystemStatus::XfsGrowFailed);
                        }
                    }
                }
                // TODO: do anything when filesystem is not mounted?
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }

    /// The thin id for the thin device that backs this filesystem.
    #[cfg(test)]
    pub fn thin_id(&self) -> ThinDevId {
        self.thin_dev.id()
    }

    /// Return an extend size for the thindev under the filesystem
    /// TODO: returning the current size will double the space provisioned to
    /// the thin device.  We should determine if this is a reasonable value.
    fn extend_size(&self, current_size: Sectors) -> Sectors {
        current_size
    }

    /// Get one (non-deterministic in the presence of errors) of the mount_point(s) for the file
    /// system that is contained on the block device referred to as self.devnode(), i.e. the device
    /// node, while ignoring parse errors as long as at least one mount point is found.
    pub fn get_mount_point(&self) -> EngineResult<Option<PathBuf>> {
        let device_node = self.devnode();
        let search = device_node.to_str().ok_or_else(|| EngineError::Engine(ErrorEnum::Error,
                                    format!("Unable to represent devnode as string {:?}", *self)))?;

        let m_iter = MountIter::new_from_proc()
            .map_err(|e| {
                         EngineError::Engine(ErrorEnum::Error,
                                             format!("Error reading /proc/mounts {:?}", e))
                     })?;

        let mut last_error: Option<String> = None;
        for mp in m_iter {
            match mp {
                Ok(mount) => {
                    if mount.contains(&MountParam::Spec(search)) {
                        return Ok(Some(mount.file));
                    }
                }
                Err(e) => {
                    last_error = Some(format!("Error during parsing {:?} {:?}", *self, e));
                }
            }
        }

        last_error.map_or(Ok(None), |e| Err(EngineError::Engine(ErrorEnum::Error, e)))
    }

    /// Tear down the filesystem.
    pub fn teardown(self, dm: &DM) -> EngineResult<()> {
        self.thin_dev.teardown(dm)?;
        Ok(())
    }

    /// Destroy the filesystem.
    pub fn destroy(self, dm: &DM, thin_pool: &ThinPoolDev) -> EngineResult<()> {
        self.thin_dev.destroy(dm, thin_pool)?;
        Ok(())
    }

    pub fn record(&self, name: &Name, uuid: FilesystemUuid) -> FilesystemSave {
        FilesystemSave {
            name: name.to_owned(),
            uuid,
            thin_id: self.thin_dev.id(),
            size: self.thin_dev.size(),
        }
    }

    pub fn suspend(&mut self, dm: &DM) -> EngineResult<()> {
        self.thin_dev.suspend(dm)?;
        Ok(())
    }

    pub fn resume(&mut self, dm: &DM) -> EngineResult<()> {
        self.thin_dev.resume(dm)?;
        Ok(())
    }
}

impl Filesystem for StratFilesystem {
    fn devnode(&self) -> PathBuf {
        self.thin_dev.devnode()
    }
}

/// Return total bytes allocated to the filesystem, total bytes used by data/metadata
pub fn fs_usage(mount_point: &Path) -> EngineResult<(Bytes, Bytes)> {
    let stat = statvfs(mount_point)?;

    // stat.block_size() is type c_ulong, which is 32 bits on some archs.
    // Upcast to u64.
    let f_bsize = stat.block_size() as u64;

    let blocks = stat.blocks();
    Ok((Bytes(f_bsize * blocks), Bytes(f_bsize * (blocks - stat.blocks_free()))))
}
<commit_msg>Fix fs_usage() for variable-size return values<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

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

use devicemapper::{Bytes, DM, DmDevice, DmName, DmUuid, IEC, SECTOR_SIZE, Sectors, ThinDev,
                   ThinDevId, ThinPoolDev, ThinStatus};

use mnt::{MountIter, MountParam};
use nix::mount::{MsFlags, mount, umount};
use nix::sys::statvfs::statvfs;
use tempdir::TempDir;

use super::super::super::engine::Filesystem;
use super::super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::super::types::{FilesystemUuid, Name};

use super::super::serde_structs::FilesystemSave;

use super::util::{create_fs, set_uuid, xfs_growfs};

/// TODO: confirm that 256 MiB leaves enough time for stratisd to respond and extend before
/// the filesystem is out of space.
pub const FILESYSTEM_LOWATER: Sectors = Sectors(256 * IEC::Mi / (SECTOR_SIZE as u64)); // = 256 MiB

#[derive(Debug)]
pub struct StratFilesystem {
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    XfsGrowFailed,
    ThinDevExtendFailed,
    Failed,
}

impl StratFilesystem {
    /// Create a StratFilesystem on top of the given ThinDev.
    pub fn initialize(fs_id: FilesystemUuid, thin_dev: ThinDev) -> EngineResult<StratFilesystem> {
        let fs = StratFilesystem::setup(thin_dev);

        create_fs(&fs.devnode(), fs_id)?;
        Ok(fs)
    }

    /// Build a StratFilesystem that includes the ThinDev and related info.
    pub fn setup(thin_dev: ThinDev) -> StratFilesystem {
        StratFilesystem { thin_dev }
    }

    /// Create a snapshot of the filesystem. Return the resulting filesystem/ThinDev
    /// to the caller.  Use snapshot_name for the Stratis filesystem name.  Use
    /// snapshot_dmname for the new name of the ThinDev allocated for the snapshot.
    /// Mounting a filesystem with a duplicate UUID would require special handling,
    /// so snapshot_fs_uuid is used to update the new snapshot filesystem so it has
    /// a unique UUID.
    #[allow(too_many_arguments)]
    pub fn snapshot(&self,
                    dm: &DM,
                    thin_pool: &ThinPoolDev,
                    snapshot_name: &str,
                    snapshot_dm_name: &DmName,
                    snapshot_dm_uuid: Option<&DmUuid>,
                    snapshot_fs_name: &Name,
                    snapshot_fs_uuid: FilesystemUuid,
                    snapshot_thin_id: ThinDevId)
                    -> EngineResult<StratFilesystem> {

        match self.thin_dev
                  .snapshot(dm,
                            snapshot_dm_name,
                            snapshot_dm_uuid,
                            thin_pool,
                            snapshot_thin_id) {
            Ok(thin_dev) => {
                // If the source is mounted, XFS puts a dummy record in the
                // log to enforce replay of the snapshot to deal with any
                // orphaned inodes. The dummy record put the log in a dirty
                // state. xfs_admin won't allow a filesystem UUID
                // to be updated when the log is dirty.  To clear the log
                // we mount/unmount the filesystem before updating the UUID.
                //
                // If the source is unmounted the XFS log will be clean so
                // we can skip the mount/unmount.

                // FIXME: get_mount_point doesn't work so assume we need to mount/unmount
                let tmp_dir = TempDir::new("stratis_mp_")?;
                // Mount the snapshot with the "nouuid" option. mount
                // will fail due to duplicate UUID otherwise.
                mount(Some(&thin_dev.devnode()),
                      tmp_dir.path(),
                      Some("xfs"),
                      MsFlags::empty(),
                      Some("nouuid"))?;
                umount(tmp_dir.path())?;

                set_uuid(&thin_dev.devnode(), snapshot_fs_uuid)?;
                Ok(StratFilesystem::setup(thin_dev))
            }
            Err(e) => {
                Err(EngineError::Engine(ErrorEnum::Error,
                                        format!("failed to create {} snapshot for {} - {}",
                                                snapshot_name,
                                                snapshot_fs_name,
                                                e)))
            }
        }
    }

    /// check if filesystem is getting full and needs to be extended
    /// TODO: deal with the thindev in a Fail state.
    pub fn check(&mut self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match self.thin_dev.status(dm)? {
            ThinStatus::Working(_) => {
                if let Some(mount_point) = self.get_mount_point()? {
                    let (fs_total_bytes, fs_total_used_bytes) = fs_usage(&mount_point)?;
                    let free_bytes = fs_total_bytes - fs_total_used_bytes;
                    if free_bytes.sectors() < FILESYSTEM_LOWATER {
                        let extend_size = self.extend_size(self.thin_dev.size());
                        if self.thin_dev.extend(dm, extend_size).is_err() {
                            return Ok(FilesystemStatus::ThinDevExtendFailed);
                        }
                        if xfs_growfs(&mount_point).is_err() {
                            return Ok(FilesystemStatus::XfsGrowFailed);
                        }
                    }
                }
                // TODO: do anything when filesystem is not mounted?
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }

    /// The thin id for the thin device that backs this filesystem.
    #[cfg(test)]
    pub fn thin_id(&self) -> ThinDevId {
        self.thin_dev.id()
    }

    /// Return an extend size for the thindev under the filesystem
    /// TODO: returning the current size will double the space provisioned to
    /// the thin device.  We should determine if this is a reasonable value.
    fn extend_size(&self, current_size: Sectors) -> Sectors {
        current_size
    }

    /// Get one (non-deterministic in the presence of errors) of the mount_point(s) for the file
    /// system that is contained on the block device referred to as self.devnode(), i.e. the device
    /// node, while ignoring parse errors as long as at least one mount point is found.
    pub fn get_mount_point(&self) -> EngineResult<Option<PathBuf>> {
        let device_node = self.devnode();
        let search = device_node.to_str().ok_or_else(|| EngineError::Engine(ErrorEnum::Error,
                                    format!("Unable to represent devnode as string {:?}", *self)))?;

        let m_iter = MountIter::new_from_proc()
            .map_err(|e| {
                         EngineError::Engine(ErrorEnum::Error,
                                             format!("Error reading /proc/mounts {:?}", e))
                     })?;

        let mut last_error: Option<String> = None;
        for mp in m_iter {
            match mp {
                Ok(mount) => {
                    if mount.contains(&MountParam::Spec(search)) {
                        return Ok(Some(mount.file));
                    }
                }
                Err(e) => {
                    last_error = Some(format!("Error during parsing {:?} {:?}", *self, e));
                }
            }
        }

        last_error.map_or(Ok(None), |e| Err(EngineError::Engine(ErrorEnum::Error, e)))
    }

    /// Tear down the filesystem.
    pub fn teardown(self, dm: &DM) -> EngineResult<()> {
        self.thin_dev.teardown(dm)?;
        Ok(())
    }

    /// Destroy the filesystem.
    pub fn destroy(self, dm: &DM, thin_pool: &ThinPoolDev) -> EngineResult<()> {
        self.thin_dev.destroy(dm, thin_pool)?;
        Ok(())
    }

    pub fn record(&self, name: &Name, uuid: FilesystemUuid) -> FilesystemSave {
        FilesystemSave {
            name: name.to_owned(),
            uuid,
            thin_id: self.thin_dev.id(),
            size: self.thin_dev.size(),
        }
    }

    pub fn suspend(&mut self, dm: &DM) -> EngineResult<()> {
        self.thin_dev.suspend(dm)?;
        Ok(())
    }

    pub fn resume(&mut self, dm: &DM) -> EngineResult<()> {
        self.thin_dev.resume(dm)?;
        Ok(())
    }
}

impl Filesystem for StratFilesystem {
    fn devnode(&self) -> PathBuf {
        self.thin_dev.devnode()
    }
}

/// Return total bytes allocated to the filesystem, total bytes used by data/metadata
pub fn fs_usage(mount_point: &Path) -> EngineResult<(Bytes, Bytes)> {
    let stat = statvfs(mount_point)?;

    // Upcast all arch-dependent variable width values to u64
    let (block_size, blocks, blocks_free) =
        (stat.block_size() as u64, stat.blocks() as u64, stat.blocks_free() as u64);
    Ok((Bytes(block_size * blocks), Bytes(block_size * (blocks - blocks_free))))
}
<|endoftext|>


<commit_before>//! Create textures and build texture atlas.

use gfx::Device;
use image::{ self, GenericImage, ImageBuffer, RgbaImage, Pixel, SubImage };
use std::num::Float;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{ Occupied, Vacant };
use std::mem;

pub use gfx_texture::Texture;

/// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> Result<RgbaImage, String> {
    Ok(match image::open(path) {
        Ok(image::ImageRgba8(img)) => img,
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            // We're forced to use Box::new on the closure because ImageBuffer
            // is defined in the "image" crate.
            ImageBuffer::from_fn(w, h, |&: x, y| img.get_pixel(x, y).to_rgba())
        }
        Ok(img) => {
            return Err(format!("Unsupported color type {:?} in '{}'",
                img.color(), path.display()));
        }
        Err(e)  => {
            return Err(format!("Could not load '{}': {:?}", path.display(), e));
        }
    })
}

/// A 256x256 image that stores colors.
pub struct ColorMap {
    image: RgbaImage
}

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8; 3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let col = self.image.get_pixel(x as u32, y as u32).channels();
        [col[0], col[1], col[2]]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: RgbaImage,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuffer::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.get(name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuffer::new(w * 2, h * 2));
            for ix in range(0, w) {
                for iy in range(0, h) {
                    *self.image.get_pixel_mut(ix, iy) = old[(ix, iy)];
                }
            }

            /*
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), b) in dest.pixels_mut().zip(old.pixels()) {
                *a = *b;
            }
            */
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        {
            let [x, y, w, h] = [x * uw, y * uh, uw, uh];
            for ix in range(0, w) {
                for iy in range(0, h) {
                    *self.image.get_pixel_mut(ix + x, iy + y) = img[(ix, iy)];
                }
            }
        }

        /*
        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), b) in dest.pixels_mut().zip(img.pixels()) {
            *a = *b;
        }
        */

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32; 4]) -> u8 {
        let [x, y, w, h] = rect;
        match self.min_alpha_cache.get(&(x, y, w, h)) {
            Some(alpha) => return *alpha,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p[3])
            .min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<D: Device>(self, d: &mut D) -> Texture {
        Texture::from_image_with_mipmap(d, &self.image)
    }
}
<commit_msg>Update to latest gfx_texture<commit_after>//! Create textures and build texture atlas.

use gfx::Device;
use image::{ self, GenericImage, ImageBuffer, RgbaImage, Pixel, SubImage };
use std::num::Float;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{ Occupied, Vacant };
use std::mem;

pub use gfx_texture::Texture;

/// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> Result<RgbaImage, String> {
    Ok(match image::open(path) {
        Ok(image::ImageRgba8(img)) => img,
        Ok(image::ImageRgb8(img)) => {
            let (w, h) = img.dimensions();
            // We're forced to use Box::new on the closure because ImageBuffer
            // is defined in the "image" crate.
            ImageBuffer::from_fn(w, h, |&: x, y| img.get_pixel(x, y).to_rgba())
        }
        Ok(img) => {
            return Err(format!("Unsupported color type {:?} in '{}'",
                img.color(), path.display()));
        }
        Err(e)  => {
            return Err(format!("Could not load '{}': {:?}", path.display(), e));
        }
    })
}

/// A 256x256 image that stores colors.
pub struct ColorMap {
    image: RgbaImage
}

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path(path: &Path) -> Result<ColorMap, String> {
        let img = try!(load_rgba8(path));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap {image: img}),
            (w, h) => Err(format!("ColorMap expected 256x256, found {}x{} in '{}'",
                                  w, h, path.display()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8; 3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let col = self.image.get_pixel(x as u32, y as u32).channels();
        [col[0], col[1], col[2]]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: RgbaImage,
    // Base path for loading tiles.
    path: Path,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new(path: Path, unit_width: u32, unit_height: u32) -> AtlasBuilder {
        AtlasBuilder {
            image: ImageBuffer::new(unit_width * 4, unit_height * 4),
            path: path,
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        match self.tile_positions.get(name) {
            Some(pos) => return *pos,
            None => {}
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuffer::new(w * 2, h * 2));
            for ix in range(0, w) {
                for iy in range(0, h) {
                    *self.image.get_pixel_mut(ix, iy) = old[(ix, iy)];
                }
            }

            /*
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), b) in dest.pixels_mut().zip(old.pixels()) {
                *a = *b;
            }
            */
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        {
            let [x, y, w, h] = [x * uw, y * uh, uw, uh];
            for ix in range(0, w) {
                for iy in range(0, h) {
                    *self.image.get_pixel_mut(ix + x, iy + y) = img[(ix, iy)];
                }
            }
        }

        /*
        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), b) in dest.pixels_mut().zip(img.pixels()) {
            *a = *b;
        }
        */

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32; 4]) -> u8 {
        let [x, y, w, h] = rect;
        match self.min_alpha_cache.get(&(x, y, w, h)) {
            Some(alpha) => return *alpha,
            None => {}
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p[3])
            .min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<D: Device>(self, d: &mut D) -> Texture<D> {
        Texture::from_image_with_mipmap(d, &self.image)
    }
}
<|endoftext|>


<commit_before>#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimagentrylink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagutil;

use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagerror::trace::{trace_error, trace_error_exit};
use libimagentrylink::external::ExternalLinker;
use libimagutil::warn_result::*;
use libimagutil::warn_exit::warn_exit;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-link",
                                    &version!()[..],
                                    "Link entries",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => warn_exit("No commandline call", 1)
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimagentrylink::internal::InternalLinker;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(',') {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(Some(e)) => {
                    e.get_internal_links()
                        .map(|links| {
                            let i = links
                                .iter()
                                .filter_map(|l| {
                                    l.to_str()
                                        .map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
                                        .ok()
                                })
                                .enumerate();

                            for (i, link) in i {
                                println!("{: <3}: {}", i, link);
                            }
                        })
                        .map_err(|e| trace_error(&e))
                        .ok();
                },

                Ok(None) => {
                    warn!("Entry not found: {:?}", entry);
                    break;
                }

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn_exit("No 'from' entry", 1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn_exit("No 'to' entry", 1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(Some(e)) => Some(e),
                Ok(None)    => None,
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(Some(e)) => v.push(e),
                    Ok(None) => warn!("Entry not found: {:?}", v),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
    use std::path::PathBuf;
    use libimagstore::storeid::StoreId;

    StoreId::new(Some(rt.store().path().clone()), PathBuf::from(name))
        .and_then(|id| rt.store().get(id))
}

fn handle_external_linking(rt: &Runtime) {
    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error_exit(&entry.unwrap_err(), 1);
    }
    let entry = entry.unwrap();
    if entry.is_none() {
        warn!("Entry not found: {:?}", entry_name);
        return;
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("add").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        debug!("URL parsing error...");
        trace_error_exit(&link.unwrap_err(), 1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.add_external_link(store, link) {
        debug!("Error while adding external link...");
        trace_error(&e);
    } else {
        debug!("Everything worked well");
        info!("Ok");
    }
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let link = matches.value_of("remove").unwrap();

    let link = Url::parse(link);
    if link.is_err() {
        trace_error_exit(&link.unwrap_err(), 1);
    }
    let link = link.unwrap();

    if let Err(e) = entry.remove_external_link(store, link) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(',')
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    if let Err(e) = entry.set_external_links(store, links) {
        trace_error(&e);
    } else {
        info!("Ok");
    }
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    let res = entry.get_external_links(store)
        .and_then(|links| {
            for (i, link) in links.iter().enumerate() {
                println!("{: <3}: {}", i, link);
            }
            Ok(())
        });

    match res {
        Err(e) => {
            trace_error(&e);
        },
        Ok(_) => {
            info!("Ok");
        },
    }
}

<commit_msg>imag-link: Use Err/Ok map utility functions to refactor code<commit_after>#![deny(
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate clap;
#[macro_use] extern crate semver;
extern crate toml;
extern crate url;
#[macro_use] extern crate version;

extern crate libimagentrylink;
extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagutil;

use std::ops::Deref;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagstore::error::StoreError;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Store;
use libimagerror::trace::{MapErrTrace, trace_error, trace_error_exit};
use libimagentrylink::external::ExternalLinker;
use libimagutil::warn_result::*;
use libimagutil::warn_exit::warn_exit;
use libimagutil::info_result::*;
use clap::ArgMatches;
use url::Url;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-link",
                                    &version!()[..],
                                    "Link entries",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            match name {
                "internal" => handle_internal_linking(&rt),
                "external" => handle_external_linking(&rt),
                _ => warn_exit("No commandline call", 1)
            }
        });
}

fn handle_internal_linking(rt: &Runtime) {
    use libimagentrylink::internal::InternalLinker;

    debug!("Handle internal linking call");
    let cmd = rt.cli().subcommand_matches("internal").unwrap();

    if cmd.is_present("list") {
        debug!("List...");
        for entry in cmd.value_of("list").unwrap().split(',') {
            debug!("Listing for '{}'", entry);
            match get_entry_by_name(rt, entry) {
                Ok(Some(e)) => {
                    e.get_internal_links()
                        .map(|links| {
                            let i = links
                                .iter()
                                .filter_map(|l| {
                                    l.to_str()
                                        .map_warn_err(|e| format!("Failed to convert StoreId to string: {:?}", e))
                                        .ok()
                                })
                                .enumerate();

                            for (i, link) in i {
                                println!("{: <3}: {}", i, link);
                            }
                        })
                        .map_err_trace()
                        .ok();
                },

                Ok(None) => {
                    warn!("Entry not found: {:?}", entry);
                    break;
                }

                Err(e) => {
                    trace_error(&e);
                    break;
                },
            }
        }
        debug!("Listing ready!");
    } else {
        let mut from = {
            let from = get_from_entry(&rt);
            if from.is_none() {
                warn_exit("No 'from' entry", 1);
            }
            from.unwrap()
        };
        debug!("Link from = {:?}", from.deref());

        let to = {
            let to = get_to_entries(&rt);
            if to.is_none() {
                warn_exit("No 'to' entry", 1);
            }
            to.unwrap()
        };
        debug!("Link to = {:?}", to.iter().map(|f| f.deref()).collect::<Vec<&Entry>>());

        match cmd.subcommand_name() {
            Some("add") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.add_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            Some("remove") => {
                for mut to_entry in to {
                    if let Err(e) = to_entry.remove_internal_link(&mut from) {
                        trace_error_exit(&e, 1);
                    }
                }
            },

            _ => unreachable!(),
        };
    }
}

fn get_from_entry<'a>(rt: &'a Runtime) -> Option<FileLockEntry<'a>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .value_of("from")
        .and_then(|from_name| {
            match get_entry_by_name(rt, from_name) {
                Err(e) => {
                    debug!("We couldn't get the entry from name: '{:?}'", from_name);
                    trace_error(&e); None
                },
                Ok(Some(e)) => Some(e),
                Ok(None)    => None,
            }

        })
}

fn get_to_entries<'a>(rt: &'a Runtime) -> Option<Vec<FileLockEntry<'a>>> {
    rt.cli()
        .subcommand_matches("internal")
        .unwrap() // safe, we know there is an "internal" subcommand"
        .subcommand_matches("add")
        .unwrap() // safe, we know there is an "add" subcommand
        .values_of("to")
        .map(|values| {
            let mut v = vec![];
            for entry in values.map(|v| get_entry_by_name(rt, v)) {
                match entry {
                    Err(e) => trace_error(&e),
                    Ok(Some(e)) => v.push(e),
                    Ok(None) => warn!("Entry not found: {:?}", v),
                }
            }
            v
        })
}

fn get_entry_by_name<'a>(rt: &'a Runtime, name: &str) -> Result<Option<FileLockEntry<'a>>, StoreError> {
    use std::path::PathBuf;
    use libimagstore::storeid::StoreId;

    StoreId::new(Some(rt.store().path().clone()), PathBuf::from(name))
        .and_then(|id| rt.store().get(id))
}

fn handle_external_linking(rt: &Runtime) {
    let scmd       = rt.cli().subcommand_matches("external").unwrap();
    let entry_name = scmd.value_of("id").unwrap(); // enforced by clap
    let entry      = get_entry_by_name(rt, entry_name);
    if entry.is_err() {
        trace_error_exit(&entry.unwrap_err(), 1);
    }
    let entry = entry.unwrap();
    if entry.is_none() {
        warn!("Entry not found: {:?}", entry_name);
        return;
    }
    let mut entry = entry.unwrap();

    if scmd.is_present("add") {
        debug!("Adding link to entry!");
        add_link_to_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("remove") {
        debug!("Removing link from entry!");
        remove_link_from_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("set") {
        debug!("Setting links in entry!");
        set_links_for_entry(rt.store(), scmd, &mut entry);
        return;
    }

    if scmd.is_present("list") {
        debug!("Listing links in entry!");
        list_links_for_entry(rt.store(), &mut entry);
        return;
    }

    panic!("Clap failed to enforce one of 'add', 'remove', 'set' or 'list'");
}

fn add_link_to_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    Url::parse(matches.value_of("add").unwrap())
        .map_err_trace_exit(1)
        .map(|link| entry.add_external_link(store, link).map_err_trace().map_info_str("Ok"))
        .ok();
}

fn remove_link_from_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    Url::parse(matches.value_of("remove").unwrap())
        .map_err_trace_exit(1)
        .map(|link| entry.remove_external_link(store, link).map_err_trace().map_info_str("Ok"))
        .ok();
}

fn set_links_for_entry(store: &Store, matches: &ArgMatches, entry: &mut FileLockEntry) {
    let links = matches
        .value_of("links")
        .map(String::from)
        .unwrap()
        .split(',')
        .map(|uri| {
            match Url::parse(uri) {
                Err(e) => {
                    warn!("Could not parse '{}' as URL, ignoring", uri);
                    trace_error(&e);
                    None
                },
                Ok(u) => Some(u),
            }
        })
        .filter_map(|x| x)
        .collect();

    entry.set_external_links(store, links)
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

fn list_links_for_entry(store: &Store, entry: &mut FileLockEntry) {
    entry.get_external_links(store)
        .and_then(|links| {
            for (i, link) in links.iter().enumerate() {
                println!("{: <3}: {}", i, link);
            }
            Ok(())
        })
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

<|endoftext|>


<commit_before>use conduit::{box_error, header, Body, Handler, HandlerResult, RequestExt, Response, StatusCode};
use conduit_mime_types as mime;
use filetime::FileTime;
use std::fs::File;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;

pub struct Static {
    path: PathBuf,
    types: mime::Types,
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new().expect("Couldn't load mime-types"),
        }
    }
}

impl Handler for Static {
    #[allow(deprecated)]
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let request_path = &request.path()[1..];
        if request_path.contains("..") {
            return Ok(not_found());
        }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = file.metadata().map_err(box_error)?;
        if data.is_dir() {
            return Ok(not_found());
        }
        let mtime = FileTime::from_last_modification_time(&data);
        let mtime = OffsetDateTime::from_unix_timestamp(mtime.unix_seconds() as i64);

        Response::builder()
            .header(header::CONTENT_TYPE, mime)
            .header(header::CONTENT_LENGTH, data.len())
            .header(header::LAST_MODIFIED, mtime.format("%a, %d %b %Y %T GMT"))
            .body(Body::File(file))
            .map_err(box_error)
    }
}

fn not_found() -> Response<Body> {
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .header(header::CONTENT_LENGTH, 0)
        .header(header::CONTENT_TYPE, "text/plain")
        .body(Body::empty())
        .unwrap()
}

#[cfg(test)]
mod tests {
    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use crate::Static;
    use conduit::{header, Handler, Method, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root);
        File::create(&root.join("Cargo.toml"))
            .unwrap()
            .write_all(b"[package]")
            .unwrap();
        let mut req = MockRequest::new(Method::GET, "/Cargo.toml");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(
            res.headers().get(header::CONTENT_TYPE).unwrap(),
            "text/plain"
        );
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "9");
        assert_eq!(*res.into_cow(), b"[package]"[..]);
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/src/fixture.css");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "text/css");
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "0");
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/nope");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/test");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.headers().get(header::LAST_MODIFIED).is_some());
    }
}
<commit_msg>conduit-static: Remove `#[allow(deprecated)]` attribute (#49)<commit_after>use conduit::{box_error, header, Body, Handler, HandlerResult, RequestExt, Response, StatusCode};
use conduit_mime_types as mime;
use filetime::FileTime;
use std::fs::File;
use std::path::{Path, PathBuf};
use time::OffsetDateTime;

pub struct Static {
    path: PathBuf,
    types: mime::Types,
}

impl Static {
    pub fn new<P: AsRef<Path>>(path: P) -> Static {
        Static {
            path: path.as_ref().to_path_buf(),
            types: mime::Types::new().expect("Couldn't load mime-types"),
        }
    }
}

impl Handler for Static {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let request_path = &request.path()[1..];
        if request_path.contains("..") {
            return Ok(not_found());
        }

        let path = self.path.join(request_path);
        let mime = self.types.mime_for_path(&path);
        let file = match File::open(&path) {
            Ok(f) => f,
            Err(..) => return Ok(not_found()),
        };
        let data = file.metadata().map_err(box_error)?;
        if data.is_dir() {
            return Ok(not_found());
        }
        let mtime = FileTime::from_last_modification_time(&data);
        let mtime = OffsetDateTime::from_unix_timestamp(mtime.unix_seconds() as i64);

        Response::builder()
            .header(header::CONTENT_TYPE, mime)
            .header(header::CONTENT_LENGTH, data.len())
            .header(header::LAST_MODIFIED, mtime.format("%a, %d %b %Y %T GMT"))
            .body(Body::File(file))
            .map_err(box_error)
    }
}

fn not_found() -> Response<Body> {
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .header(header::CONTENT_LENGTH, 0)
        .header(header::CONTENT_TYPE, "text/plain")
        .body(Body::empty())
        .unwrap()
}

#[cfg(test)]
mod tests {
    use std::fs::{self, File};
    use std::io::prelude::*;
    use tempdir::TempDir;

    use crate::Static;
    use conduit::{header, Handler, Method, StatusCode};
    use conduit_test::{MockRequest, ResponseExt};

    #[test]
    fn test_static() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        let handler = Static::new(root);
        File::create(&root.join("Cargo.toml"))
            .unwrap()
            .write_all(b"[package]")
            .unwrap();
        let mut req = MockRequest::new(Method::GET, "/Cargo.toml");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(
            res.headers().get(header::CONTENT_TYPE).unwrap(),
            "text/plain"
        );
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "9");
        assert_eq!(*res.into_cow(), b"[package]"[..]);
    }

    #[test]
    fn test_mime_types() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        fs::create_dir(&root.join("src")).unwrap();
        File::create(&root.join("src/fixture.css")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/src/fixture.css");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.headers().get(header::CONTENT_TYPE).unwrap(), "text/css");
        assert_eq!(res.headers().get(header::CONTENT_LENGTH).unwrap(), "0");
    }

    #[test]
    fn test_missing() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/nope");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn test_dir() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();

        fs::create_dir(&root.join("foo")).unwrap();

        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/foo");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::NOT_FOUND);
    }

    #[test]
    fn last_modified() {
        let td = TempDir::new("conduit-static").unwrap();
        let root = td.path();
        File::create(&root.join("test")).unwrap();
        let handler = Static::new(root);
        let mut req = MockRequest::new(Method::GET, "/test");
        let res = handler.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert!(res.headers().get(header::LAST_MODIFIED).is_some());
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{BufRead, BufReader};
use std::path::{PathBuf, Path};
use std::ffi::CString;
use std::ops::Deref;
use std::fs::File;
use std::collections::HashSet;
use object::Object;
use object::File as OFile;
use memmap::{Mmap, Protection};
use gimli::*;
use regex::Regex;
use rustc_demangle::demangle;
/// So far from my tests the test executable is the first compilation unit in
/// the DWARF debug information with the future ones being libraries and rustc.
const TEST_CU_OFFSET: usize = 0;

/// Describes a function as low_pc, high_pc and bool representing is_test.
type FuncDesc = (u64, u64, bool);

#[derive(Debug, Clone, Copy)]
pub enum LineType {
    /// Entry of function known to be a test
    TestEntry(u64),
    /// Entry of function. May or may not be test
    FunctionEntry(u64),
    /// Standard statement
    Statement,
    /// Condition
    Condition,
    /// Unknown type
    Unknown,
}

#[derive(Debug, Clone)]
pub struct TracerData {
    pub path: PathBuf,
    pub line: u64,
    pub address: u64,
    pub trace_type: LineType,
    pub hits: u64,
}

fn line_is_traceable(file: &PathBuf, line: u64) -> bool {
    let mut result = false;
    if line > 0 {
        // Module imports are flagged as debuggable. But are always ran so meaningless!
        let reg: Regex = Regex::new(r"(:?^|\s)mod\s+\w+;").unwrap();
        if let Ok(f) = File::open(file) {
            let reader = BufReader::new(&f);
            if let Some(Ok(l)) = reader.lines().nth((line - 1) as usize) {
                result = !reg.is_match(l.as_ref());
            }
        }
    }
    result
}

fn generate_func_desc<T: Endianity>(die: &DebuggingInformationEntry<T>,
                                    debug_str: &DebugStr<T>) -> Result<FuncDesc> {
    let mut is_test = false;
    let low = die.attr_value(DW_AT_low_pc)?;
    let high = die.attr_value(DW_AT_high_pc)?;
    let linkage = die.attr_value(DW_AT_linkage_name)?;

    // Low is a program counter address so stored in an Addr
    let low = match low {
        Some(AttributeValue::Addr(x)) => x,
        _ => 0u64,
    };
    // High is an offset from the base pc, therefore is u64 data.
    let high = match high {
        Some(AttributeValue::Udata(x)) => x,
        _ => 0u64,
    };
    if let Some(AttributeValue::DebugStrRef(offset)) = linkage {
        let empty = CString::new("").unwrap();
        let name = debug_str.get_str(offset).unwrap_or(empty.deref());
        // go from CStr to Owned String
        let name = name.to_str().unwrap_or("");
        let name = demangle(name).to_string();
        // Simplest test is whether it's in tests namespace.
        // Rust guidelines recommend all tests are in a tests module.
        is_test = name.contains("tests::");
        // May need further tests in future for completeness.
    }

    Ok((low, high, is_test))
}

/// Finds all function entry points and returns a vector
/// This will identify definite tests, but may be prone to false negatives.
/// TODO Potential to trace all function calls from __test::main and find addresses of interest
fn get_entry_points<T: Endianity>(debug_info: &CompilationUnitHeader<T>,
                                  debug_abbrev: &Abbreviations,
                                  debug_str: &DebugStr<T>) -> Vec<FuncDesc> {
    let mut result:Vec<FuncDesc> = Vec::new();
    let mut cursor = debug_info.entries(debug_abbrev);
    // skip compilation unit root.
    let _ = cursor.next_entry();
    while let Ok(Some((_, node))) = cursor.next_dfs() {
        // Function DIE
        if node.tag() == DW_TAG_subprogram {

            if let Ok(fd) = generate_func_desc(&node, &debug_str) {
                result.push(fd);
            }
        }
    }
    result
}

fn get_line_addresses<Endian: Endianity>(project: &Path, obj: &OFile) -> Result<Vec<TracerData>>  {
    let mut result: Vec<TracerData> = Vec::new();
    let debug_info = obj.get_section(".debug_info").unwrap_or(&[]);
    let debug_info = DebugInfo::<Endian>::new(debug_info);
    let debug_abbrev = obj.get_section(".debug_abbrev").unwrap_or(&[]);
    let debug_abbrev = DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_strings = obj.get_section(".debug_str").unwrap_or(&[]);
    let debug_strings = DebugStr::<Endian>::new(debug_strings);
    let cu = debug_info.header_from_offset(DebugInfoOffset(TEST_CU_OFFSET))?;

    let addr_size = cu.address_size();
    let entries = if let Ok(abbr) = cu.abbreviations(debug_abbrev) {
        get_entry_points(&cu, &abbr, &debug_strings)
            .iter()
            .map(|&(a, b, c)| {
                if c {
                    (a, LineType::TestEntry(b))
                } else {
                    (a, LineType::FunctionEntry(b))
                }
            }).collect()
    } else {
        Vec::<(u64, LineType)>::new()
    };

    let debug_line = obj.get_section(".debug_line").unwrap_or(&[]);
    let debug_line = DebugLine::<Endian>::new(debug_line);

    let prog = debug_line.program(DebugLineOffset(TEST_CU_OFFSET), addr_size, None, None)?;
    let (cprog, seq) = prog.sequences()?;

    for s in &seq {
        let mut sm = cprog.resume_from(s);
        while let Ok(Some((ref header, &ln_row))) = sm.next_row() {
            if let Some(file) = ln_row.file(header) {
                let mut path = PathBuf::new();

                if let Some(dir) = file.directory(header) {
                    if let Ok(temp) = String::from_utf8(dir.to_bytes().to_vec()) {
                        path.push(temp);
                    }
                }
                // Source is part of project so we cover it.
                if path.starts_with(project) {
                    let force_test = path.starts_with(project.join("tests"));
                    if let Some(file) = ln_row.file(header) {
                        // If we can't map to line, we can't trace it.
                        let line = ln_row.line().unwrap_or(0);
                        let file = file.path_name();
                        // We now need to filter out lines which are meaningless to trace.

                        if let Ok(file) = String::from_utf8(file.to_bytes().to_vec()) {
                            path.push(file);
                            if !line_is_traceable(&path, line) {
                                continue;
                            }
                            let address = ln_row.address();

                            let desc = entries.iter()
                                              .filter(|&&(addr, _)| addr == address )
                                              .map(|&(_, t)| t)
                                              .nth(0)
                                              .unwrap_or(LineType::Unknown);
                            // TODO HACK: If in tests/ directory force it to a TestEntry
                            let desc = match desc {
                                LineType::FunctionEntry(e) if force_test => LineType::TestEntry(e),
                                x @ _ => x
                            };

                            result.push( TracerData {
                                path: path,
                                line: line,
                                address: address,
                                trace_type: desc,
                                hits: 0u64
                            });
                        }
                    }
                }
            }
        }
    }
    // Due to rust being a higher level language multiple instructions may map
    // to the same line. This prunes these to just the first instruction address
    let mut check: HashSet<(&Path, u64)> = HashSet::new();
    let result = result.iter()
                       .filter(|x| check.insert((x.path.as_path(), x.line)))
                       .map(|x| x.clone())
                       .collect::<Vec<TracerData>>();

    Ok(result)
}

/// Generates a list of lines we want to trace the coverage of. Used to instrument the
/// traces into the test executable
pub fn generate_tracer_data(manifest: &Path, test: &Path) -> io::Result<Vec<TracerData>> {
    let file = File::open(test)?;
    let file = Mmap::open(&file, Protection::Read)?;
    if let Ok(obj) = OFile::parse(unsafe {file.as_slice() }) {

        let data = if obj.is_little_endian() {
            get_line_addresses::<LittleEndian>(manifest, &obj)
        } else {
            get_line_addresses::<BigEndian>(manifest, &obj)
        };
        data.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Error while parsing"))
    } else {
        Err(io::Error::new(io::ErrorKind::InvalidData, "Unable to parse binary."))
    }
}
<commit_msg>Resolves #2<commit_after>use std::io;
use std::io::{BufRead, BufReader};
use std::path::{PathBuf, Path};
use std::ffi::CString;
use std::ops::Deref;
use std::fs::File;
use std::collections::HashSet;
use object::Object;
use object::File as OFile;
use memmap::{Mmap, Protection};
use gimli::*;
use regex::Regex;
use rustc_demangle::demangle;

/// Describes a function as low_pc, high_pc and bool representing is_test.
type FuncDesc = (u64, u64, bool);

#[derive(Debug, Clone, Copy)]
pub enum LineType {
    /// Entry of function known to be a test
    TestEntry(u64),
    /// Entry of function. May or may not be test
    FunctionEntry(u64),
    /// Standard statement
    Statement,
    /// Condition
    Condition,
    /// Unknown type
    Unknown,
}

#[derive(Debug, Clone)]
pub struct TracerData {
    pub path: PathBuf,
    pub line: u64,
    pub address: u64,
    pub trace_type: LineType,
    pub hits: u64,
}

fn line_is_traceable(file: &PathBuf, line: u64) -> bool {
    let mut result = false;
    if line > 0 {
        // Module imports are flagged as debuggable. But are always ran so meaningless!
        let reg: Regex = Regex::new(r"(:?^|\s)mod\s+\w+;").unwrap();
        if let Ok(f) = File::open(file) {
            let reader = BufReader::new(&f);
            if let Some(Ok(l)) = reader.lines().nth((line - 1) as usize) {
                result = !reg.is_match(l.as_ref());
            }
        }
    }
    result
}

fn generate_func_desc<T: Endianity>(die: &DebuggingInformationEntry<T>,
                                    debug_str: &DebugStr<T>) -> Result<FuncDesc> {
    let mut is_test = false;
    let low = die.attr_value(DW_AT_low_pc)?;
    let high = die.attr_value(DW_AT_high_pc)?;
    let linkage = die.attr_value(DW_AT_linkage_name)?;

    // Low is a program counter address so stored in an Addr
    let low = match low {
        Some(AttributeValue::Addr(x)) => x,
        _ => 0u64,
    };
    // High is an offset from the base pc, therefore is u64 data.
    let high = match high {
        Some(AttributeValue::Udata(x)) => x,
        _ => 0u64,
    };
    if let Some(AttributeValue::DebugStrRef(offset)) = linkage {
        let empty = CString::new("").unwrap();
        let name = debug_str.get_str(offset).unwrap_or(empty.deref());
        // go from CStr to Owned String
        let name = name.to_str().unwrap_or("");
        let name = demangle(name).to_string();
        // Simplest test is whether it's in tests namespace.
        // Rust guidelines recommend all tests are in a tests module.
        is_test = name.contains("tests::");
        // May need further tests in future for completeness.
    }

    Ok((low, high, is_test))
}

/// Finds all function entry points and returns a vector
/// This will identify definite tests, but may be prone to false negatives.
/// TODO Potential to trace all function calls from __test::main and find addresses of interest
fn get_entry_points<T: Endianity>(debug_info: &CompilationUnitHeader<T>,
                                  debug_abbrev: &Abbreviations,
                                  debug_str: &DebugStr<T>) -> Vec<FuncDesc> {
    let mut result:Vec<FuncDesc> = Vec::new();
    let mut cursor = debug_info.entries(debug_abbrev);
    // skip compilation unit root.
    let _ = cursor.next_entry();
    while let Ok(Some((_, node))) = cursor.next_dfs() {
        // Function DIE
        if node.tag() == DW_TAG_subprogram {

            if let Ok(fd) = generate_func_desc(&node, &debug_str) {
                result.push(fd);
            }
        }
    }
    result
}

fn get_addresses_from_program<T:Endianity>(prog: IncompleteLineNumberProgram<T>,
                                           entries: &Vec<(u64, LineType)>,
                                           project: &Path) -> Result<Vec<TracerData>> {
    let mut result: Vec<TracerData> = Vec::new();
    let ( cprog, seq) = prog.sequences()?;
    for s in seq {
        let mut sm = cprog.resume_from(&s);
         while let Ok(Some((ref header, &ln_row))) = sm.next_row() {
            if let Some(file) = ln_row.file(header) {
                let mut path = PathBuf::new();

                if let Some(dir) = file.directory(header) {
                    if let Ok(temp) = String::from_utf8(dir.to_bytes().to_vec()) {
                        path.push(temp);
                    }
                }
                // Source is part of project so we cover it.
                if path.starts_with(project) {
                    let force_test = path.starts_with(project.join("tests"));
                    if let Some(file) = ln_row.file(header) {
                        // If we can't map to line, we can't trace it.
                        let line = ln_row.line().unwrap_or(0);
                        let file = file.path_name();
                        // We now need to filter out lines which are meaningless to trace.

                        if let Ok(file) = String::from_utf8(file.to_bytes().to_vec()) {
                            path.push(file);
                            if !line_is_traceable(&path, line) {
                                continue;
                            }
                            let address = ln_row.address();

                            let desc = entries.iter()
                                              .filter(|&&(addr, _)| addr == address )
                                              .map(|&(_, t)| t)
                                              .nth(0)
                                              .unwrap_or(LineType::Unknown);
                            // TODO HACK: If in tests/ directory force it to a TestEntry
                            let desc = match desc {
                                LineType::FunctionEntry(e) if force_test => LineType::TestEntry(e),
                                x @ _ => x
                            };

                            result.push( TracerData {
                                path: path,
                                line: line,
                                address: address,
                                trace_type: desc,
                                hits: 0u64
                            });
                        }
                    }
                }
            }
        }
    }
    Ok(result)
}

fn get_line_addresses<Endian: Endianity>(project: &Path, obj: &OFile) -> Result<Vec<TracerData>>  {
    let mut result: Vec<TracerData> = Vec::new();
    let debug_info = obj.get_section(".debug_info").unwrap_or(&[]);
    let debug_info = DebugInfo::<Endian>::new(debug_info);
    let debug_abbrev = obj.get_section(".debug_abbrev").unwrap_or(&[]);
    let debug_abbrev = DebugAbbrev::<Endian>::new(debug_abbrev);
    let debug_strings = obj.get_section(".debug_str").unwrap_or(&[]);
    let debug_strings = DebugStr::<Endian>::new(debug_strings);

    let mut iter = debug_info.units();
    while let Ok(Some(cu)) = iter.next() {
        let addr_size = cu.address_size();
        let abbr = match cu.abbreviations(debug_abbrev) {
            Ok(a) => a,
            _ => continue,
        };
        let entries = get_entry_points(&cu, &abbr, &debug_strings)
            .iter()
            .map(|&(a, b, c)| {
                if c {
                    (a, LineType::TestEntry(b))
                } else {
                    (a, LineType::FunctionEntry(b))
                }
            }).collect();

        if let Ok(Some((_, root))) = cu.entries(&abbr).next_dfs() {
            let offset = match root.attr_value(DW_AT_stmt_list) {
                Ok(Some(AttributeValue::DebugLineRef(o))) => o,
                _ => continue,
            };
            let debug_line = obj.get_section(".debug_line").unwrap_or(&[]);
            let debug_line = DebugLine::<Endian>::new(debug_line);

            let prog = debug_line.program(offset, addr_size, None, None)?;
            if let Ok(mut addresses) = get_addresses_from_program(prog, &entries, &project) {
                result.append(&mut addresses);
            }
        }
    }
    // Due to rust being a higher level language multiple instructions may map
    // to the same line. This prunes these to just the first instruction address
    let mut check: HashSet<(&Path, u64)> = HashSet::new();
    let result = result.iter()
                       .filter(|x| check.insert((x.path.as_path(), x.line)))
                       .map(|x| x.clone())
                       .collect::<Vec<TracerData>>();

    Ok(result)
}

/// Generates a list of lines we want to trace the coverage of. Used to instrument the
/// traces into the test executable
pub fn generate_tracer_data(manifest: &Path, test: &Path) -> io::Result<Vec<TracerData>> {
    let file = File::open(test)?;
    let file = Mmap::open(&file, Protection::Read)?;
    if let Ok(obj) = OFile::parse(unsafe {file.as_slice() }) {

        let data = if obj.is_little_endian() {
            get_line_addresses::<LittleEndian>(manifest, &obj)
        } else {
            get_line_addresses::<BigEndian>(manifest, &obj)
        };
        data.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Error while parsing"))
    } else {
        Err(io::Error::new(io::ErrorKind::InvalidData, "Unable to parse binary."))
    }
}
<|endoftext|>


<commit_before>//! The traits defined here are implementation details of cell_gc.
//!
//! Application code does not need to use these traits. They are public only so
//! that `gc_heap_type!` can use it. Use the friendly macro!

use gcref::GCRef;

/// Trait for values that can be moved into a GC heap.
///
/// Cell-gc does not support GC allocation of arbitrary values: only values of
/// types that implement `IntoHeap`. This treat is **not** meant to be
/// implemented by hand: use the `gc_heap_type!` macro instead. All primitive
/// types and many standard types support `IntoHeap`.
///
/// GC types come in pairs: an *in-heap* type, which is stored physically inside
/// the heap, and may be packed with pointer fields and unsafe methods; and an
/// `IntoHeap` type, which is safe, is used in application code, and lives on
/// the stack. Both types have the same layout, bit for bit.
///
/// (The word "safe" above means that, as with everything generally in Rust, you
/// can hack without fear. `IntoHeap` types don't expose raw Rust pointers or
/// references to GC memory, they don't expose the unsafe in-heap types, and
/// they obey Rust's safety and aliasing rules.)
///
/// Users never get a direct `&` reference to any in-heap value. All access is
/// through safe `IntoHeap` types, like the `Ref` type that is automatically
/// declared for you when you use `gc_heap_type!` to declare a struct.
///
/// *   For primitive types, like `i32`, the two types are the same.
///
/// *   Macro-generated "Ref" types are `IntoHeap` types; the corresponding
///     in-heap types are effectively raw pointers.
///
/// *   For macro-generated structs and enums, the user specifies the names of
///     both types. Both types have identical fields or variants, but fields of
///     the in-heap type are in-heap, and fields of the `IntoHeap` type are
///     `IntoHeap`.
///
/// Implementation note: every type with `'static` lifetime should be safe to
/// store in the heap, because ref types are never `'static`. Unfortunately I
/// can't make rustc understand this. It would be *legal* to write this:
///
/// ```ignore
/// unsafe impl<'h, T: Clone + 'static> IntoHeap<'h> for T { ...trivial... }
/// ```
///
/// but alas, Rust thinks this impl conflicts with almost all others, making it
/// impossible to get the macro to work. So instead we `impl IntoHeap for` a
/// lot of individual types by hand.
///
/// **Liveness.** Some `IntoHeap` types ("Ref" types) are effectively smart
/// pointers to in-heap values. To preserve safety, if an `IntoHeap` value is
/// (or contains) a smart pointer to an in-heap value, then that value (and
/// everything reachable from it) is protected from GC.
///
/// Unsafe to implement: in-heap objects are full of pointers; if `into_heap`
/// puts garbage into them, GC will crash. `mark` must be implmented with care
/// in order to preserve the invariants of the GC graph-walking algorithm.
/// Bugs there are very likely to lead to dangling pointers and
/// hard-to-debug crashes down the road.
///
pub unsafe trait IntoHeap<'h>: Sized {
    /// The type of the value when it is physically stored in the heap.
    type In;

    /// Convert the value to the form it should have in the heap.
    /// This is for macro-generated code to call.
    ///
    /// This method must not be called while any direct Rust references to
    /// heap objects exist. (However, user code never runs while such
    /// references exist, so the method is not marked `unsafe`.)
    fn into_heap(self) -> Self::In;

    /// Extract the value from the heap. This turns any raw pointers in the
    /// in-heap value into safe references, so while it's an unsafe function,
    /// the result of a correct call can be safely handed out to user code.
    ///
    /// Unsafe to call: It is impossible for ordinary users to call this
    /// safely, because `self` must be a direct, unwrapped reference to a value
    /// stored in the GC heap, which ordinary users cannot obtain.
    unsafe fn from_heap(&Self::In) -> Self;

    /// Unsafe to call: It is impossible for ordinary users to call this
    /// safely, because `self` must be a direct, unwrapped reference to a value
    /// stored in the GC heap, which ordinary users cannot obtain.
    unsafe fn mark(&Self::In);
}

/// Relate an `IntoHeap` type to the corresponding safe reference type.
pub trait IntoHeapAllocation<'h>: IntoHeap<'h>
{
    type Ref: IntoHeap<'h>;

    fn wrap_gcref(gcref: GCRef<'h, Self>) -> Self::Ref;
}

// === Provided implmentations for primitive types

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        unsafe impl<'h> IntoHeap<'h> for $t {
            type In = $t;
            fn into_heap(self) -> $t { self }
            unsafe fn from_heap(storage: &$t) -> $t { storage.clone() }
            unsafe fn mark(_storage: &$t) {}
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

gc_trivial_impl!(String);

macro_rules! gc_generic_trivial_impl {
    (@as_item $it:item) => { $it };
    ([$($x:tt)*] $t:ty) => {
        gc_generic_trivial_impl! {
            @as_item
            unsafe impl<'h, $($x)*> IntoHeap<'h> for $t {
                type In = $t;
                fn into_heap(self) -> $t { self }
                unsafe fn from_heap(storage: &$t) -> $t { (*storage).clone() }
                unsafe fn mark(_storage: &$t) {}
            }
        }
    }
}

gc_generic_trivial_impl!([T: ?Sized] &'static T);
gc_generic_trivial_impl!([T: ?Sized] ::std::marker::PhantomData<T>);
gc_generic_trivial_impl!([T: Clone + 'static] ::GCLeaf<T>);
gc_generic_trivial_impl!([T: Clone + 'static] Box<T>);
gc_generic_trivial_impl!([T: Clone + 'static] ::std::rc::Rc<T>);

// Transitive implementations ("this particular kind of struct/enum is IntoHeap
// if all its fields are") are slightly less trivial.

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Option<T> {
    type In = Option<T::In>;

    fn into_heap(self) -> Option<T::In> {
        self.map(|t| t.into_heap())
    }

    unsafe fn mark(storage: &Option<T::In>) {
        match storage {
            &None => (),
            &Some(ref u) => T::mark(u)
        }
    }

    unsafe fn from_heap(storage: &Option<T::In>) -> Option<T> {
        match storage {
            &None => None,
            &Some(ref u) => Some(T::from_heap(u))
        }
    }
}

macro_rules! gc_trivial_tuple_impl {
    (@as_item $it:item) => { $it };
    ($($t:ident),*) => {
        gc_trivial_tuple_impl! {
            @as_item
            unsafe impl<'h, $($t: IntoHeap<'h>,)*> IntoHeap<'h> for ($($t,)*) {
                type In = ($($t::In,)*);

                #[allow(non_snake_case)]  // because we use the type names as variable names (!)
                fn into_heap(self) -> Self::In {
                    let ($($t,)*) = self;
                    ($($t.into_heap(),)*)
                }

                #[allow(non_snake_case)]
                unsafe fn mark(storage: &Self::In) {
                    let &($(ref $t,)*) = storage;
                    $( <$t as $crate::traits::IntoHeap>::mark($t); )*
                }

                #[allow(non_snake_case)]
                unsafe fn from_heap(storage: &Self::In) -> Self {
                    let &($(ref $t,)*) = storage;
                    ($( <$t as $crate::traits::IntoHeap>::from_heap($t), )*)
                }
            }
        }
    }
}

gc_trivial_tuple_impl!();
gc_trivial_tuple_impl!(T);
gc_trivial_tuple_impl!(T, U);
gc_trivial_tuple_impl!(T, U, V);
gc_trivial_tuple_impl!(T, U, V, W);
gc_trivial_tuple_impl!(T, U, V, W, X);
gc_trivial_tuple_impl!(T, U, V, W, X, Y);
gc_trivial_tuple_impl!(T, U, V, W, X, Y, Z);
<commit_msg>Fix typo: s/treat/trait/<commit_after>//! The traits defined here are implementation details of cell_gc.
//!
//! Application code does not need to use these traits. They are public only so
//! that `gc_heap_type!` can use it. Use the friendly macro!

use gcref::GCRef;

/// Trait for values that can be moved into a GC heap.
///
/// Cell-gc does not support GC allocation of arbitrary values: only values of
/// types that implement `IntoHeap`. This trait is **not** meant to be
/// implemented by hand: use the `gc_heap_type!` macro instead. All primitive
/// types and many standard types support `IntoHeap`.
///
/// GC types come in pairs: an *in-heap* type, which is stored physically inside
/// the heap, and may be packed with pointer fields and unsafe methods; and an
/// `IntoHeap` type, which is safe, is used in application code, and lives on
/// the stack. Both types have the same layout, bit for bit.
///
/// (The word "safe" above means that, as with everything generally in Rust, you
/// can hack without fear. `IntoHeap` types don't expose raw Rust pointers or
/// references to GC memory, they don't expose the unsafe in-heap types, and
/// they obey Rust's safety and aliasing rules.)
///
/// Users never get a direct `&` reference to any in-heap value. All access is
/// through safe `IntoHeap` types, like the `Ref` type that is automatically
/// declared for you when you use `gc_heap_type!` to declare a struct.
///
/// *   For primitive types, like `i32`, the two types are the same.
///
/// *   Macro-generated "Ref" types are `IntoHeap` types; the corresponding
///     in-heap types are effectively raw pointers.
///
/// *   For macro-generated structs and enums, the user specifies the names of
///     both types. Both types have identical fields or variants, but fields of
///     the in-heap type are in-heap, and fields of the `IntoHeap` type are
///     `IntoHeap`.
///
/// Implementation note: every type with `'static` lifetime should be safe to
/// store in the heap, because ref types are never `'static`. Unfortunately I
/// can't make rustc understand this. It would be *legal* to write this:
///
/// ```ignore
/// unsafe impl<'h, T: Clone + 'static> IntoHeap<'h> for T { ...trivial... }
/// ```
///
/// but alas, Rust thinks this impl conflicts with almost all others, making it
/// impossible to get the macro to work. So instead we `impl IntoHeap for` a
/// lot of individual types by hand.
///
/// **Liveness.** Some `IntoHeap` types ("Ref" types) are effectively smart
/// pointers to in-heap values. To preserve safety, if an `IntoHeap` value is
/// (or contains) a smart pointer to an in-heap value, then that value (and
/// everything reachable from it) is protected from GC.
///
/// Unsafe to implement: in-heap objects are full of pointers; if `into_heap`
/// puts garbage into them, GC will crash. `mark` must be implmented with care
/// in order to preserve the invariants of the GC graph-walking algorithm.
/// Bugs there are very likely to lead to dangling pointers and
/// hard-to-debug crashes down the road.
///
pub unsafe trait IntoHeap<'h>: Sized {
    /// The type of the value when it is physically stored in the heap.
    type In;

    /// Convert the value to the form it should have in the heap.
    /// This is for macro-generated code to call.
    ///
    /// This method must not be called while any direct Rust references to
    /// heap objects exist. (However, user code never runs while such
    /// references exist, so the method is not marked `unsafe`.)
    fn into_heap(self) -> Self::In;

    /// Extract the value from the heap. This turns any raw pointers in the
    /// in-heap value into safe references, so while it's an unsafe function,
    /// the result of a correct call can be safely handed out to user code.
    ///
    /// Unsafe to call: It is impossible for ordinary users to call this
    /// safely, because `self` must be a direct, unwrapped reference to a value
    /// stored in the GC heap, which ordinary users cannot obtain.
    unsafe fn from_heap(&Self::In) -> Self;

    /// Unsafe to call: It is impossible for ordinary users to call this
    /// safely, because `self` must be a direct, unwrapped reference to a value
    /// stored in the GC heap, which ordinary users cannot obtain.
    unsafe fn mark(&Self::In);
}

/// Relate an `IntoHeap` type to the corresponding safe reference type.
pub trait IntoHeapAllocation<'h>: IntoHeap<'h>
{
    type Ref: IntoHeap<'h>;

    fn wrap_gcref(gcref: GCRef<'h, Self>) -> Self::Ref;
}

// === Provided implmentations for primitive types

macro_rules! gc_trivial_impl {
    ($t:ty) => {
        unsafe impl<'h> IntoHeap<'h> for $t {
            type In = $t;
            fn into_heap(self) -> $t { self }
            unsafe fn from_heap(storage: &$t) -> $t { storage.clone() }
            unsafe fn mark(_storage: &$t) {}
        }
    }
}

gc_trivial_impl!(bool);
gc_trivial_impl!(char);
gc_trivial_impl!(i8);
gc_trivial_impl!(u8);
gc_trivial_impl!(i16);
gc_trivial_impl!(u16);
gc_trivial_impl!(i32);
gc_trivial_impl!(u32);
gc_trivial_impl!(i64);
gc_trivial_impl!(u64);
gc_trivial_impl!(isize);
gc_trivial_impl!(usize);
gc_trivial_impl!(f32);
gc_trivial_impl!(f64);

gc_trivial_impl!(String);

macro_rules! gc_generic_trivial_impl {
    (@as_item $it:item) => { $it };
    ([$($x:tt)*] $t:ty) => {
        gc_generic_trivial_impl! {
            @as_item
            unsafe impl<'h, $($x)*> IntoHeap<'h> for $t {
                type In = $t;
                fn into_heap(self) -> $t { self }
                unsafe fn from_heap(storage: &$t) -> $t { (*storage).clone() }
                unsafe fn mark(_storage: &$t) {}
            }
        }
    }
}

gc_generic_trivial_impl!([T: ?Sized] &'static T);
gc_generic_trivial_impl!([T: ?Sized] ::std::marker::PhantomData<T>);
gc_generic_trivial_impl!([T: Clone + 'static] ::GCLeaf<T>);
gc_generic_trivial_impl!([T: Clone + 'static] Box<T>);
gc_generic_trivial_impl!([T: Clone + 'static] ::std::rc::Rc<T>);

// Transitive implementations ("this particular kind of struct/enum is IntoHeap
// if all its fields are") are slightly less trivial.

unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Option<T> {
    type In = Option<T::In>;

    fn into_heap(self) -> Option<T::In> {
        self.map(|t| t.into_heap())
    }

    unsafe fn mark(storage: &Option<T::In>) {
        match storage {
            &None => (),
            &Some(ref u) => T::mark(u)
        }
    }

    unsafe fn from_heap(storage: &Option<T::In>) -> Option<T> {
        match storage {
            &None => None,
            &Some(ref u) => Some(T::from_heap(u))
        }
    }
}

macro_rules! gc_trivial_tuple_impl {
    (@as_item $it:item) => { $it };
    ($($t:ident),*) => {
        gc_trivial_tuple_impl! {
            @as_item
            unsafe impl<'h, $($t: IntoHeap<'h>,)*> IntoHeap<'h> for ($($t,)*) {
                type In = ($($t::In,)*);

                #[allow(non_snake_case)]  // because we use the type names as variable names (!)
                fn into_heap(self) -> Self::In {
                    let ($($t,)*) = self;
                    ($($t.into_heap(),)*)
                }

                #[allow(non_snake_case)]
                unsafe fn mark(storage: &Self::In) {
                    let &($(ref $t,)*) = storage;
                    $( <$t as $crate::traits::IntoHeap>::mark($t); )*
                }

                #[allow(non_snake_case)]
                unsafe fn from_heap(storage: &Self::In) -> Self {
                    let &($(ref $t,)*) = storage;
                    ($( <$t as $crate::traits::IntoHeap>::from_heap($t), )*)
                }
            }
        }
    }
}

gc_trivial_tuple_impl!();
gc_trivial_tuple_impl!(T);
gc_trivial_tuple_impl!(T, U);
gc_trivial_tuple_impl!(T, U, V);
gc_trivial_tuple_impl!(T, U, V, W);
gc_trivial_tuple_impl!(T, U, V, W, X);
gc_trivial_tuple_impl!(T, U, V, W, X, Y);
gc_trivial_tuple_impl!(T, U, V, W, X, Y, Z);
<|endoftext|>


<commit_before><commit_msg>[REM] Remove some files<commit_after><|endoftext|>