<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::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

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

use libmount;
use nix::mount::{mount, umount, MsFlags};
use nix::sys::statvfs::statvfs;
use tempfile;

use stratis::{ErrorEnum, StratisError, StratisResult};

use super::super::super::engine::Filesystem;
use super::super::super::types::{FilesystemUuid, Name};

use super::super::cmd::{create_fs, set_uuid, xfs_growfs};
use super::super::dm::get_dm;
use super::super::serde_structs::FilesystemSave;

/// 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) -> StratisResult<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,
        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,
    ) -> StratisResult<StratFilesystem> {
        match self.thin_dev.snapshot(
            get_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 = tempfile::Builder::new().prefix("stratis_mp_").tempdir()?;
                // 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(StratisError::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) -> StratisResult<FilesystemStatus> {
        match self.thin_dev.status(get_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 mut table = self.thin_dev.table().table.clone();
                        table.length =
                            self.thin_dev.size() + self.extend_size(self.thin_dev.size());
                        if self.thin_dev.set_table(get_dm(), table).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) -> StratisResult<Option<PathBuf>> {
        let major = u64::from(self.thin_dev.device().major);
        let minor = u64::from(self.thin_dev.device().minor);

        let mut mount_data = String::new();
        File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
        let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());

        let mut last_error: Option<String> = None;
        for mp in parser {
            match mp {
                Ok(mount) => {
                    if mount.major as u64 == major && mount.minor as u64 == minor {
                        return Ok(Some(PathBuf::from(mount.mount_point.into_owned())));
                    }
                }
                Err(e) => {
                    last_error = Some(format!("Error during parsing {:?} {:?}", *self, e));
                }
            }
        }

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

    /// Tear down the filesystem.
    pub fn teardown(self) -> StratisResult<()> {
        self.thin_dev.teardown(get_dm())?;
        Ok(())
    }

    /// Destroy the filesystem.
    pub fn destroy(self, thin_pool: &ThinPoolDev) -> StratisResult<()> {
        self.thin_dev.destroy(get_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, flush: bool) -> StratisResult<()> {
        self.thin_dev.suspend(get_dm(), flush)?;
        Ok(())
    }

    pub fn resume(&mut self) -> StratisResult<()> {
        self.thin_dev.resume(get_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) -> StratisResult<(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)),
    ))
}
<commit_msg>Add mount check back in<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::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

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

use libmount;
use nix::mount::{mount, umount, MsFlags};
use nix::sys::statvfs::statvfs;
use tempfile;

use stratis::{ErrorEnum, StratisError, StratisResult};

use super::super::super::engine::Filesystem;
use super::super::super::types::{FilesystemUuid, Name};

use super::super::cmd::{create_fs, set_uuid, xfs_growfs};
use super::super::dm::get_dm;
use super::super::serde_structs::FilesystemSave;

/// 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) -> StratisResult<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,
        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,
    ) -> StratisResult<StratFilesystem> {
        match self.thin_dev.snapshot(
            get_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.
                if self.get_mount_point()?.is_some() {
                    let tmp_dir = tempfile::Builder::new().prefix("stratis_mp_").tempdir()?;
                    // 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(StratisError::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) -> StratisResult<FilesystemStatus> {
        match self.thin_dev.status(get_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 mut table = self.thin_dev.table().table.clone();
                        table.length =
                            self.thin_dev.size() + self.extend_size(self.thin_dev.size());
                        if self.thin_dev.set_table(get_dm(), table).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) -> StratisResult<Option<PathBuf>> {
        let major = u64::from(self.thin_dev.device().major);
        let minor = u64::from(self.thin_dev.device().minor);

        let mut mount_data = String::new();
        File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
        let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());

        let mut last_error: Option<String> = None;
        for mp in parser {
            match mp {
                Ok(mount) => {
                    if mount.major as u64 == major && mount.minor as u64 == minor {
                        return Ok(Some(PathBuf::from(mount.mount_point.into_owned())));
                    }
                }
                Err(e) => {
                    last_error = Some(format!("Error during parsing {:?} {:?}", *self, e));
                }
            }
        }

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

    /// Tear down the filesystem.
    pub fn teardown(self) -> StratisResult<()> {
        self.thin_dev.teardown(get_dm())?;
        Ok(())
    }

    /// Destroy the filesystem.
    pub fn destroy(self, thin_pool: &ThinPoolDev) -> StratisResult<()> {
        self.thin_dev.destroy(get_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, flush: bool) -> StratisResult<()> {
        self.thin_dev.suspend(get_dm(), flush)?;
        Ok(())
    }

    pub fn resume(&mut self) -> StratisResult<()> {
        self.thin_dev.resume(get_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) -> StratisResult<(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>use num::{PrimInt, Signed};

/// Right-shift an integer, but round to nearest, with ties rounding to even.
///
/// This minimizes error, and avoids a directional bias.
pub fn right_shift_nicely_rounded <T: PrimInt> (input: T, shift: usize)->T {
  if shift == 0 {return input}
  let half = T::one() << (shift - 1);
  let mask = (T::one() << shift) - T::one();
  if input & mask == half {
    let shifted = input >> shift;
    shifted + (shifted & T::one())
  } else {
    (input + half) >> shift
  }
}

/// Approximately evaluate an integer polynomial at an input in the range [-0.5, 0.5].
///
/// The input is represented as an integer combined with a right-shift size.
/// For a signed integer type with B bits:
/// Each coefficient, except the constant one, must obey coefficient.abs() <= T::max_value() >> shift.
/// The constant coefficient must obey coefficient.abs() <= T::max_value() - (T::max_value() >> shift),
/// i.e. be small enough that you can add one of the other coefficients to it without overflowing.
/// Given these conditions, the result is guaranteed to be strictly within 1 of the ideal result.
/// For instance, if the ideal result was 2.125, this function could return 2 or 3,
/// but if it was 2, this function can only return exactly 2.
pub fn evaluate_polynomial_at_small_input <Coefficient: Copy, T: PrimInt + Signed + From <Coefficient>> (coefficients: & [Coefficient], input: T, shift: usize)->T {
  if coefficients.len () == 0 {return T::zero()}
  let half = T::one() << (shift - 1);
  assert!(-half <= input && input <= half, "inputs to evaluate_polynomial_at_small_input must be in the range [-0.5, 0.5]");
  let mut result = T::zero();
  for coefficient in coefficients.iter().skip(1).rev() {
    result = right_shift_nicely_rounded ((result + (*coefficient).into())*input, shift);
  }
  result + coefficients [0].into()
}

#[cfg (test)]
mod tests {
  use super::*;
  use num::{Zero, One, FromPrimitive};
  use num::bigint::BigInt;
  use num::rational::{Ratio, BigRational};
  use rand::distributions::Distribution;
  use rand::distributions::range::{Range};
  use rand::{Rng, SeedableRng};

  fn evaluate_polynomial_exactly <Coefficient: Copy, T: PrimInt + Signed + From <Coefficient>> (coefficients: & [Coefficient], input: T, shift: usize)->BigRational
    where BigInt: From <Coefficient> + From <T> {
    let mut result: BigRational = Ratio::zero();
    let input = Ratio::new(BigInt::from (input), BigInt::one() << shift);
    for coefficient in coefficients.iter().rev() {
      result = result*&input + Ratio::from_integer (BigInt::from(*coefficient));
    }
    result
  }

  #[test]
  fn test_right_shift_nicely_rounded() {
    let inputs: Vec<(i64, usize, i64)> = vec![
      (0, 0, 0), (0, 5, 0), (1, 3, 0), (4, 3, 0), (5, 3, 1),
      (999, 1, 500), (998, 1, 499), (997, 1, 498)
    ];
    for (input, shift, result) in inputs {
      assert_eq!(right_shift_nicely_rounded (input, shift), result);
      assert_eq!(right_shift_nicely_rounded (-input, shift), -result);
    }
  }

  #[test]
  fn test_evaluate_polynomial_at_small_input() {
    let mut generator =::rand::chacha::ChaChaRng::from_seed ([33; 32]) ;
    for shift in 0..5 {
      let shift = 1 << shift;
      let half = 1i64 << (shift - 1);
      let maximum = i64::max_value() >> shift;
      let constant_maximum = i64::max_value() - maximum;
      let range = Range::new_inclusive (- maximum, maximum) ;
      let constant_range = Range::new_inclusive (- constant_maximum, constant_maximum);
      let mut test_inputs = vec![- half, - half + 1, - 1, 0, 1, half - 1, half];
      if half > 2 {for _ in 0..6 {test_inputs.push (generator.gen_range (- half + 2, half - 1));}}
      for _ in 0..40 {
        let mut coefficients = vec![constant_range.sample (&mut generator)];
        while generator.gen() {coefficients.push (range.sample (&mut generator));}
        for input in test_inputs.iter() {
          let result = evaluate_polynomial_at_small_input (& coefficients,*input, shift);

          let perfect_result = evaluate_polynomial_exactly (& coefficients,*input, shift);
          let difference = Ratio::from_integer (FromPrimitive::from_i64 (result).unwrap()) - perfect_result;
          assert!(difference < Ratio::from_integer (FromPrimitive::from_i64 (1).unwrap()));
        }
      }
    }
  }
}
<commit_msg>Cleaned up a comment<commit_after>use num::{PrimInt, Signed};

/// Right-shift an integer, but round to nearest, with ties rounding to even.
///
/// This minimizes error, and avoids a directional bias.
pub fn right_shift_nicely_rounded <T: PrimInt> (input: T, shift: usize)->T {
  if shift == 0 {return input}
  let half = T::one() << (shift - 1);
  let mask = (T::one() << shift) - T::one();
  if input & mask == half {
    let shifted = input >> shift;
    shifted + (shifted & T::one())
  } else {
    (input + half) >> shift
  }
}

/// Approximately evaluate an integer polynomial at an input in the range [-0.5, 0.5].
///
/// The input is represented as an integer combined with a right-shift size.
/// Each coefficient, except the constant one, must obey coefficient.abs() <= T::max_value() >> shift.
/// The constant coefficient must obey coefficient.abs() <= T::max_value() - (T::max_value() >> shift),
/// i.e. be small enough that you can add one of the other coefficients to it without overflowing.
/// Given these conditions, the result is guaranteed to be strictly within 1 of the ideal result.
/// For instance, if the ideal result was 2.125, this function could return 2 or 3,
/// but if it was 2, this function can only return exactly 2.
pub fn evaluate_polynomial_at_small_input <Coefficient: Copy, T: PrimInt + Signed + From <Coefficient>> (coefficients: & [Coefficient], input: T, shift: usize)->T {
  if coefficients.len () == 0 {return T::zero()}
  let half = T::one() << (shift - 1);
  assert!(-half <= input && input <= half, "inputs to evaluate_polynomial_at_small_input must be in the range [-0.5, 0.5]");
  let mut result = T::zero();
  for coefficient in coefficients.iter().skip(1).rev() {
    result = right_shift_nicely_rounded ((result + (*coefficient).into())*input, shift);
  }
  result + coefficients [0].into()
}

#[cfg (test)]
mod tests {
  use super::*;
  use num::{Zero, One, FromPrimitive};
  use num::bigint::BigInt;
  use num::rational::{Ratio, BigRational};
  use rand::distributions::Distribution;
  use rand::distributions::range::{Range};
  use rand::{Rng, SeedableRng};

  fn evaluate_polynomial_exactly <Coefficient: Copy, T: PrimInt + Signed + From <Coefficient>> (coefficients: & [Coefficient], input: T, shift: usize)->BigRational
    where BigInt: From <Coefficient> + From <T> {
    let mut result: BigRational = Ratio::zero();
    let input = Ratio::new(BigInt::from (input), BigInt::one() << shift);
    for coefficient in coefficients.iter().rev() {
      result = result*&input + Ratio::from_integer (BigInt::from(*coefficient));
    }
    result
  }

  #[test]
  fn test_right_shift_nicely_rounded() {
    let inputs: Vec<(i64, usize, i64)> = vec![
      (0, 0, 0), (0, 5, 0), (1, 3, 0), (4, 3, 0), (5, 3, 1),
      (999, 1, 500), (998, 1, 499), (997, 1, 498)
    ];
    for (input, shift, result) in inputs {
      assert_eq!(right_shift_nicely_rounded (input, shift), result);
      assert_eq!(right_shift_nicely_rounded (-input, shift), -result);
    }
  }

  #[test]
  fn test_evaluate_polynomial_at_small_input() {
    let mut generator =::rand::chacha::ChaChaRng::from_seed ([33; 32]) ;
    for shift in 0..5 {
      let shift = 1 << shift;
      let half = 1i64 << (shift - 1);
      let maximum = i64::max_value() >> shift;
      let constant_maximum = i64::max_value() - maximum;
      let range = Range::new_inclusive (- maximum, maximum) ;
      let constant_range = Range::new_inclusive (- constant_maximum, constant_maximum);
      let mut test_inputs = vec![- half, - half + 1, - 1, 0, 1, half - 1, half];
      if half > 2 {for _ in 0..6 {test_inputs.push (generator.gen_range (- half + 2, half - 1));}}
      for _ in 0..40 {
        let mut coefficients = vec![constant_range.sample (&mut generator)];
        while generator.gen() {coefficients.push (range.sample (&mut generator));}
        for input in test_inputs.iter() {
          let result = evaluate_polynomial_at_small_input (& coefficients,*input, shift);

          let perfect_result = evaluate_polynomial_exactly (& coefficients,*input, shift);
          let difference = Ratio::from_integer (FromPrimitive::from_i64 (result).unwrap()) - perfect_result;
          assert!(difference < Ratio::from_integer (FromPrimitive::from_i64 (1).unwrap()));
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>//! A shared ordered log.

use std::rc::Rc;
use std::cell::RefCell;
use std::time::{Instant, Duration};
use std::collections::VecDeque;

use ::{communication::Allocate, ExchangeData, PartialOrder};
use ::scheduling::Scheduler;
use worker::Worker;
use dataflow::channels::pact::Exchange;
use dataflow::operators::generic::operator::source;
use dataflow::operators::generic::operator::Operator;
use scheduling::activate::Activator;

// A Sequencer needs all operators firing with high frequency, because
// it uses the timer to gauge progress. If other workers cease
// advancing their own capabilities, although they might receive a
// record they may not actually tick forward their own source clocks,
// and no one will actually form the sequence.
//
// A CatchupActivator is an activator with an optional timestamp
// attached. This allows us to represent a special state, where after
// receiving an action from another worker, each of the other workers
// will keep scheduling its source operator, until its capability
// timestamp exceeds the greatest timestamp that the sink has
// received.
//
// This allows operators to go quiet again until a new requests shows
// up. The operators lose the ability to confirm that nothing is
// scheduled for a particular time (they could request this with a
// no-op event bearing a timestamp), but everyone still sees the same
// sequence.
struct CatchupActivator {
    pub catchup_until: Option<Duration>,
    activator: Activator,
}

impl CatchupActivator {
    pub fn activate(&self) {
        self.activator.activate();
    }
}

/// Orders elements inserted across all workers.
///
/// A Sequencer allows each worker to insert into a consistent ordered
/// sequence that is seen by all workers in the same order.
pub struct Sequencer<T> {
    activator: Rc<RefCell<Option<CatchupActivator>>>,
    send: Rc<RefCell<VecDeque<T>>>, // proposed items.
    recv: Rc<RefCell<VecDeque<T>>>, // sequenced items.
}

impl<T: ExchangeData> Sequencer<T> {

    /// Creates a new Sequencer.
    ///
    /// The `timer` instant is used to synchronize the workers, who use this
    /// elapsed time as their timestamp. Elements are ordered by this time,
    /// and cannot be made visible until all workers have reached the time.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use std::time::{Instant, Duration};
    ///
    /// use timely::Configuration;
    /// use timely::synchronization::Sequencer;
    ///
    /// timely::execute(Configuration::Process(4), |worker| {
    ///     let timer = Instant::now();
    ///     let mut sequencer = Sequencer::new(worker, timer);
    ///
    ///     for round in 0 .. 10 {
    ///
    ///         // Sleep, and then send an announcement on wake-up.
    ///         std::thread::sleep(Duration::from_millis(1 + worker.index() as u64));
    ///         sequencer.push(format!("worker {:?}, round {:?}", worker.index(), round));
    ///
    ///         // Ensures the pushed string is sent.
    ///         worker.step();
    ///
    ///         // Read out received announcements.
    ///         while let Some(element) = sequencer.next() {
    ///             println!("{:?}:\tWorker {:?}:\t recv'd: {:?}", timer.elapsed(), worker.index(), element);
    ///         }
    ///     }
    /// }).expect("Timely computation did not complete correctly.");
    /// ```
    pub fn new<A: Allocate>(worker: &mut Worker<A>, timer: Instant) -> Self {

        let send: Rc<RefCell<VecDeque<T>>> = Rc::new(RefCell::new(VecDeque::new()));
        let recv = Rc::new(RefCell::new(VecDeque::new()));
        let send_weak = Rc::downgrade(&send);
        let recv_weak = Rc::downgrade(&recv);

        // The SequenceInput activator will be held by the sequencer,
        // by the operator itself, and by the sink operator. We can
        // only initialize the activator once we obtain the operator
        // address.
        let activator = Rc::new(RefCell::new(None));
        let activator_source = activator.clone();
        let activator_sink = activator.clone();

        // build a dataflow used to serialize and circulate commands
        worker.dataflow::<Duration,_,_>(move |dataflow| {

            let scope = dataflow.clone();
            let peers = dataflow.peers();

            let mut recvd = Vec::new();
            let mut vector = Vec::new();

            // monotonic counter to maintain per-worker total order.
            let mut counter = 0;

            // a source that attempts to pull from `recv` and produce commands for everyone
            source(dataflow, "SequenceInput", move |capability, info| {

                // intialize activator, now that we have the address
                activator_source
                    .borrow_mut()
                    .replace(CatchupActivator {
                        activator: scope.activator_for(&info.address[..]),
                        catchup_until: None,
                    });

                // so we can drop, if input queue vanishes.
                let mut capability = Some(capability);

                // closure broadcasts any commands it grabs.
                move |output| {

                    if let Some(send_queue) = send_weak.upgrade() {

                        // capability *should* still be non-None.
                        let capability = capability.as_mut().expect("Capability unavailable");

                        // downgrade capability to current time.
                        capability.downgrade(&timer.elapsed());

                        // drain and broadcast `send`.
                        let mut session = output.session(&capability);
                        let mut borrow = send_queue.borrow_mut();
                        for element in borrow.drain(..) {
                            for worker_index in 0 .. peers {
                                session.give((worker_index, counter, element.clone()));
                            }
                            counter += 1;
                        }

                        let mut activator_borrow = activator_source.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        if let Some(t) = activator.catchup_until {
                            if capability.time().less_than(&t) {
                                activator.activate();
                            } else {
                                activator.catchup_until = None;
                            }
                        }
                    } else {
                        capability = None;
                    }
                }
            })
            .sink(
                Exchange::new(|x: &(usize, usize, T)| x.0 as u64),
                "SequenceOutput",
                move |input| {

                    // grab each command and queue it up
                    input.for_each(|time, data| {
                        data.swap(&mut vector);
                        for (worker, counter, element) in vector.drain(..) {
                            recvd.push(((time.time().clone(), worker, counter), element));
                        }
                    });

                    recvd.sort_by(|x,y| x.0.cmp(&y.0));

                    if let Some(last) = recvd.last() {
                        let mut activator_borrow = activator_sink.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        activator.catchup_until = Some((last.0).0);
                        activator.activate();
                    }

                    // determine how many (which) elements to read from `recvd`.
                    let count = recvd.iter().filter(|&((ref time, _, _), _)| !input.frontier().less_equal(time)).count();
                    let iter = recvd.drain(..count);

                    if let Some(recv_queue) = recv_weak.upgrade() {
                        recv_queue.borrow_mut().extend(iter.map(|(_,elem)| elem));
                    }
                }
            );
        });

        Sequencer { activator, send, recv, }
    }

    /// Adds an element to the shared log.
    pub fn push(&mut self, element: T) {
        self.send.borrow_mut().push_back(element);
        self.activator.borrow_mut().as_mut().unwrap().activate();
    }

    /// Reads the next element from the shared log.
    pub fn next(&mut self) -> Option<T> {
        self.recv.borrow_mut().pop_front()
    }
}
<commit_msg>implement Iterator<commit_after>//! A shared ordered log.

use std::rc::Rc;
use std::cell::RefCell;
use std::time::{Instant, Duration};
use std::collections::VecDeque;

use ::{communication::Allocate, ExchangeData, PartialOrder};
use ::scheduling::Scheduler;
use worker::Worker;
use dataflow::channels::pact::Exchange;
use dataflow::operators::generic::operator::source;
use dataflow::operators::generic::operator::Operator;
use scheduling::activate::Activator;

// A Sequencer needs all operators firing with high frequency, because
// it uses the timer to gauge progress. If other workers cease
// advancing their own capabilities, although they might receive a
// record they may not actually tick forward their own source clocks,
// and no one will actually form the sequence.
//
// A CatchupActivator is an activator with an optional timestamp
// attached. This allows us to represent a special state, where after
// receiving an action from another worker, each of the other workers
// will keep scheduling its source operator, until its capability
// timestamp exceeds the greatest timestamp that the sink has
// received.
//
// This allows operators to go quiet again until a new requests shows
// up. The operators lose the ability to confirm that nothing is
// scheduled for a particular time (they could request this with a
// no-op event bearing a timestamp), but everyone still sees the same
// sequence.
struct CatchupActivator {
    pub catchup_until: Option<Duration>,
    activator: Activator,
}

impl CatchupActivator {
    pub fn activate(&self) {
        self.activator.activate();
    }
}

/// Orders elements inserted across all workers.
///
/// A Sequencer allows each worker to insert into a consistent ordered
/// sequence that is seen by all workers in the same order.
pub struct Sequencer<T> {
    activator: Rc<RefCell<Option<CatchupActivator>>>,
    send: Rc<RefCell<VecDeque<T>>>, // proposed items.
    recv: Rc<RefCell<VecDeque<T>>>, // sequenced items.
}

impl<T: ExchangeData> Sequencer<T> {

    /// Creates a new Sequencer.
    ///
    /// The `timer` instant is used to synchronize the workers, who use this
    /// elapsed time as their timestamp. Elements are ordered by this time,
    /// and cannot be made visible until all workers have reached the time.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use std::time::{Instant, Duration};
    ///
    /// use timely::Configuration;
    /// use timely::synchronization::Sequencer;
    ///
    /// timely::execute(Configuration::Process(4), |worker| {
    ///     let timer = Instant::now();
    ///     let mut sequencer = Sequencer::new(worker, timer);
    ///
    ///     for round in 0 .. 10 {
    ///
    ///         // Sleep, and then send an announcement on wake-up.
    ///         std::thread::sleep(Duration::from_millis(1 + worker.index() as u64));
    ///         sequencer.push(format!("worker {:?}, round {:?}", worker.index(), round));
    ///
    ///         // Ensures the pushed string is sent.
    ///         worker.step();
    ///
    ///         // Read out received announcements.
    ///         while let Some(element) = sequencer.next() {
    ///             println!("{:?}:\tWorker {:?}:\t recv'd: {:?}", timer.elapsed(), worker.index(), element);
    ///         }
    ///     }
    /// }).expect("Timely computation did not complete correctly.");
    /// ```
    pub fn new<A: Allocate>(worker: &mut Worker<A>, timer: Instant) -> Self {

        let send: Rc<RefCell<VecDeque<T>>> = Rc::new(RefCell::new(VecDeque::new()));
        let recv = Rc::new(RefCell::new(VecDeque::new()));
        let send_weak = Rc::downgrade(&send);
        let recv_weak = Rc::downgrade(&recv);

        // The SequenceInput activator will be held by the sequencer,
        // by the operator itself, and by the sink operator. We can
        // only initialize the activator once we obtain the operator
        // address.
        let activator = Rc::new(RefCell::new(None));
        let activator_source = activator.clone();
        let activator_sink = activator.clone();

        // build a dataflow used to serialize and circulate commands
        worker.dataflow::<Duration,_,_>(move |dataflow| {

            let scope = dataflow.clone();
            let peers = dataflow.peers();

            let mut recvd = Vec::new();
            let mut vector = Vec::new();

            // monotonic counter to maintain per-worker total order.
            let mut counter = 0;

            // a source that attempts to pull from `recv` and produce commands for everyone
            source(dataflow, "SequenceInput", move |capability, info| {

                // intialize activator, now that we have the address
                activator_source
                    .borrow_mut()
                    .replace(CatchupActivator {
                        activator: scope.activator_for(&info.address[..]),
                        catchup_until: None,
                    });

                // so we can drop, if input queue vanishes.
                let mut capability = Some(capability);

                // closure broadcasts any commands it grabs.
                move |output| {

                    if let Some(send_queue) = send_weak.upgrade() {

                        // capability *should* still be non-None.
                        let capability = capability.as_mut().expect("Capability unavailable");

                        // downgrade capability to current time.
                        capability.downgrade(&timer.elapsed());

                        // drain and broadcast `send`.
                        let mut session = output.session(&capability);
                        let mut borrow = send_queue.borrow_mut();
                        for element in borrow.drain(..) {
                            for worker_index in 0 .. peers {
                                session.give((worker_index, counter, element.clone()));
                            }
                            counter += 1;
                        }

                        let mut activator_borrow = activator_source.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        if let Some(t) = activator.catchup_until {
                            if capability.time().less_than(&t) {
                                activator.activate();
                            } else {
                                activator.catchup_until = None;
                            }
                        }
                    } else {
                        capability = None;
                    }
                }
            })
            .sink(
                Exchange::new(|x: &(usize, usize, T)| x.0 as u64),
                "SequenceOutput",
                move |input| {

                    // grab each command and queue it up
                    input.for_each(|time, data| {
                        data.swap(&mut vector);
                        for (worker, counter, element) in vector.drain(..) {
                            recvd.push(((time.time().clone(), worker, counter), element));
                        }
                    });

                    recvd.sort_by(|x,y| x.0.cmp(&y.0));

                    if let Some(last) = recvd.last() {
                        let mut activator_borrow = activator_sink.borrow_mut();
                        let mut activator = activator_borrow.as_mut().unwrap();

                        activator.catchup_until = Some((last.0).0);
                        activator.activate();
                    }

                    // determine how many (which) elements to read from `recvd`.
                    let count = recvd.iter().filter(|&((ref time, _, _), _)| !input.frontier().less_equal(time)).count();
                    let iter = recvd.drain(..count);

                    if let Some(recv_queue) = recv_weak.upgrade() {
                        recv_queue.borrow_mut().extend(iter.map(|(_,elem)| elem));
                    }
                }
            );
        });

        Sequencer { activator, send, recv, }
    }

    /// Adds an element to the shared log.
    pub fn push(&mut self, element: T) {
        self.send.borrow_mut().push_back(element);
        self.activator.borrow_mut().as_mut().unwrap().activate();
    }
}

impl<T> Iterator for Sequencer<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        self.recv.borrow_mut().pop_front()
    }
}
<|endoftext|>


<commit_before>// compile-flags: --show-type-layout -Z unstable-options

// @has type_layout/struct.Foo.html 'Size: '
// @has - ' bytes'
pub struct Foo {
    pub a: usize,
    b: Vec<String>,
}

// @has type_layout/enum.Bar.html 'Size: '
// @has - ' bytes'
pub enum Bar<'a> {
    A(String),
    B(&'a str, (std::collections::HashMap<String, usize>, Foo)),
}

// @has type_layout/union.Baz.html 'Size: '
// @has - ' bytes'
pub union Baz {
    a: &'static str,
    b: usize,
    c: &'static [u8],
}

// @has type_layout/struct.X.html 'Size: '
// @has - ' bytes'
pub struct X(usize);

// @has type_layout/struct.Y.html 'Size: '
// @has - '1 byte'
// @!has - ' bytes'
pub struct Y(u8);

// @!has type_layout/struct.Generic.html 'Size: '
pub struct Generic<T>(T);

// @has type_layout/struct.Unsized.html 'Size: '
// @has - '(unsized)'
pub struct Unsized([u8]);

// @has type_layout/type.TypeAlias.html 'Size: '
// @has - ' bytes'
pub type TypeAlias = X;

// @!has type_layout/trait.MyTrait.html 'Size: '
pub trait MyTrait {}
<commit_msg>Add test case for zero-sized types<commit_after>// compile-flags: --show-type-layout -Z unstable-options

// @has type_layout/struct.Foo.html 'Size: '
// @has - ' bytes'
pub struct Foo {
    pub a: usize,
    b: Vec<String>,
}

// @has type_layout/enum.Bar.html 'Size: '
// @has - ' bytes'
pub enum Bar<'a> {
    A(String),
    B(&'a str, (std::collections::HashMap<String, usize>, Foo)),
}

// @has type_layout/union.Baz.html 'Size: '
// @has - ' bytes'
pub union Baz {
    a: &'static str,
    b: usize,
    c: &'static [u8],
}

// @has type_layout/struct.X.html 'Size: '
// @has - ' bytes'
pub struct X(usize);

// @has type_layout/struct.Y.html 'Size: '
// @has - '1 byte'
// @!has - ' bytes'
pub struct Y(u8);

// @has type_layout/struct.Z.html 'Size: '
// @has - '0 bytes'
pub struct Z;

// @!has type_layout/struct.Generic.html 'Size: '
pub struct Generic<T>(T);

// @has type_layout/struct.Unsized.html 'Size: '
// @has - '(unsized)'
pub struct Unsized([u8]);

// @has type_layout/type.TypeAlias.html 'Size: '
// @has - ' bytes'
pub type TypeAlias = X;

// @!has type_layout/trait.MyTrait.html 'Size: '
pub trait MyTrait {}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
#![feature(core)]

pub use svm::slist::List::{Cons,Nil};
use std::fmt;
use std::ops::Index;

/// Convenience macro for making lists.
///
/// # Example:
///
/// ```
/// # #[macro_use] extern crate seax_svm;
/// # use seax_svm::svm::slist;
/// # use seax_svm::svm::slist::List::{Cons, Nil};
/// # fn main () {
/// assert_eq!(
///     list!(1i32, 2i32, 3i32),
///     Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil))))))
///     );
/// # }
/// ```
#[macro_export]
macro_rules! list(
    ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
    ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
    () => ( @Empty )
);

/// Common functions for an immutable Stack abstract data type.
pub trait Stack<T> {

    /// Push an item to the top of the stack, returning a new stack
    fn push(self, item : T) -> Self;

    /// Pop the top element of the stack. Returns an Option on a T and
    /// a new Stack<T> to replace this.
    fn pop(self)            -> Option<(T, Self)>;

    /// Peek at the top item of the stack.
    ///
    /// Returns Some<T> if there is an item on top of the stack,
    /// and None if the stack is empty.
    fn peek(&self)          -> Option<&T>;

    /// Returns an empty stack.
    fn empty()              -> Self;
}

/// Stack implementation using a cons list
impl<T> Stack<T> for List<T> {

    /// Push an item to the top of the stack, returning a new stack.
    ///
    /// # Examples:
    /// ```
    /// use seax_svm::svm::slist::{List,Stack};
    ///
    /// let mut s: List<isize> = Stack::empty();
    /// assert_eq!(s.peek(), None);
    /// s = s.push(1);
    /// assert_eq!(s.peek(), Some(&1));
    /// s = s.push(6);
    /// assert_eq!(s.peek(), Some(&6));
    /// ```
    fn push(self, item: T) -> List<T> {
        Cons(item, box self)
    }

    /// Pop the top element of the stack.
    ///
    /// Pop the top element of the stack. Returns an
    /// `Option<(T,List<T>)>` containing the top element and a new
    /// `List<T>` with that item removed, or `None` if the stack is
    /// empty.
    ///
    /// # Examples:
    /// ```
    /// # use seax_svm::svm::slist::{List,Stack};
    ///
    /// let mut s: List<isize> = Stack::empty();
    /// s = s.push(2);
    /// s = s.push(1);
    /// let pop_result = s.pop().unwrap();
    /// s = pop_result.1;
    /// assert_eq!(s.peek(), Some(&2));
    /// assert_eq!(pop_result.0, 1);
    /// ```
    fn pop(self) -> Option<(T,List<T>)> {
        match self {
            Cons(item, new_self)    => Some((item, *new_self)),
            Nil                     => None
        }
    }

    fn empty() -> List<T> {
        Nil
    }

    /// Peek at the top element of the stack.
    ///
    /// Peek at the top element of the stack. Returns an `Option<&T>`
    /// with a borrowed pointer to the top element, or `None` if the
    /// stack is empty.
    ///
    /// # Examples:
    /// ```
    /// # use seax_svm::svm::slist::{List,Stack};
    ///
    /// let mut s: List<isize> = Stack::empty();
    /// s = s.push(2);
    /// s = s.push(1);
    /// let pop_result = s.pop().unwrap();
    /// s = pop_result.1;
    /// assert_eq!(s.peek(), Some(&2));
    /// assert_eq!(pop_result.0, 1);
    /// ```
    fn peek(&self) -> Option<&T> {
        match self {
            &Nil => None,
            &Cons(ref it,_) => Some(it)
        }
    }

}

/// Singly-linked cons list.
///
/// This is used internally to represent list primitives in the
/// machine.
#[derive(PartialEq,Clone,Debug)]
pub enum List<T> {
    /// Cons cell containing a `T` and a link to the tail
    Cons(T, Box<List<T>>),
    /// The empty list.
    Nil,
}

/// Public implementation for List.
impl<T> List<T> {

    /// Creates a new empty list
    pub fn new() -> List<T> {
        Nil
    }

    /// Prepends the given item to the list.
    ///
    /// Returns the list containing the new  head item.
    /// This is an O(1) operation.
    pub fn prepend(self, it: T) -> List<T> {
        Cons(it, box self)
    }

    /// Appends an item to the end of the list.
    ///
    /// This is an O(n) operation.
    pub fn append(self, it: T) {
        unimplemented!()
    }

    /// Returns the length of the list.
    pub fn length (&self) -> usize {
        match *self {
            Cons(_, ref tail) => 1 + tail.length(),
            Nil => 0
        }
    }

    /// Provide a forward iterator
    #[inline]
    pub fn iter<'a>(&'a self) -> ListIterator<'a, T> {
        ListIterator{current: self}
    }

    pub fn last(&self) -> &T {
        match *self {
            Cons(ref car, box Nil) => &car,
            Cons(_, ref cdr @ box Cons(_,_)) => cdr.last(),
            Nil => panic!("Last called on empty list")
        }
    }
}

impl<'a, T> fmt::Display for List<T> where T: fmt::Display{

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TODO: replace toString with this
        match *self {
            Cons(ref head, ref tail) => write!(f, "({}, {})", head, tail),
            Nil => write!(f,"nil")
        }
    }
}

/// Wraps a List<T> to allow it to be used as an Iterator<T>
pub struct ListIterator<'a, T:'a> {
    current: &'a List<T>
}

/// Implementation of Iterator for List. This allows iteration by
/// link hopping.
impl<'a, T> Iterator for ListIterator<'a, T> {
    type Item = &'a T;

    /// Get the next element from the list.
    ///
    /// Get the next element from the list. Returns a `Some<T>`, or `Nil`
    /// if at the end of the list.
    ///
    /// # Examples:
    /// ```
    /// # #[macro_use] extern crate seax_svm;
    /// # use seax_svm::svm::slist;
    /// # use seax_svm::svm::slist::List;
    /// # use seax_svm::svm::slist::List::{Cons, Nil};
    /// # fn main () {
    /// let list = list!(1,2,3);
    /// let mut iter = list.iter();
    /// assert_eq!(iter.next().unwrap(), &1);
    /// assert_eq!(iter.next().unwrap(), &2);
    /// assert_eq!(iter.next().unwrap(), &3);
    /// # }
    /// ```
    /// ```
    /// # #[macro_use] extern crate seax_svm;
    /// # use seax_svm::svm::slist;
    /// # use seax_svm::svm::slist::List;
    /// # use seax_svm::svm::slist::List::{Cons, Nil};
    /// # fn main () {
    /// let l: List<isize> = list!(1,2,3,4,5,6);
    /// let mut string = String::new();
    /// for item in l.iter() {
    ///     string.push_str((item.to_string() + ", ").as_slice());
    /// }
    /// assert_eq!(string.as_slice(), "1, 2, 3, 4, 5, 6, ")
    /// # }
    /// ```
    fn next(&mut self) -> Option<&'a T> {
        match self.current {
            &Cons(ref head, box ref tail) => { self.current = tail; Some(head) },
            &Nil => None
        }
    }
}

impl<'a, T> ExactSizeIterator for ListIterator<'a, T> {
    fn len(&self) -> usize {
        self.current.length()
    }
}
/// Implementation of indexing for `List<T>`.
///
/// # Examples:
/// ```
/// # #[macro_use] extern crate seax_svm;
/// # use seax_svm::svm::slist;
/// # use seax_svm::svm::slist::List;
/// # use seax_svm::svm::slist::List::{Cons, Nil};
/// # fn main () {
/// let list = list!(1,2,3,4,5,6);
/// assert_eq!(list[0us], 1);
/// # }
/// ```
impl<T> Index<usize> for List<T> {
    type Output = T;

    fn index<'a>(&'a self, _index: &usize) -> &'a T {
        match *_index {
            0us => match *self {
                Cons(ref car, _) => car,
                Nil => panic!("List index {} out of range", _index)
            },
            1us => match *self {
                Cons(_, box Cons(ref cdr, _)) => cdr,
                Cons(_, box Nil) => panic!("List index {} out of range", _index),
                Nil => panic!("List index {} out of range", _index)
            },
            i if i == self.length() => self.last(),
            i if i > self.length()  => panic!("List index {:?} out of range.", _index),
            i if i > 1us => {
                let mut it = self.iter();
                for _ in 0 .. i{
                    it.next();
                }
                it.next().unwrap()
            },
            _ => panic!("Expected an index i such that i >= 0, got {:?}.", _index)
        }
    }
}
/// Implementation of indexing for `List<T>`.
///
/// # Examples:
/// ```
/// # #[macro_use] extern crate seax_svm;
/// # use seax_svm::svm::slist;
/// # use seax_svm::svm::slist::List;
/// # use seax_svm::svm::slist::List::{Cons, Nil};
/// # fn main () {
/// let list = list!(1,2,3,4,5,6);
/// assert_eq!(list[0is], 1);
/// # }
/// ```
impl<T> Index<isize> for List<T> {
    type Output = T;

    fn index<'a>(&'a self, _index: &isize) -> &'a T {
        match *_index {
            0is => match *self {
                Cons(ref car, _) => car,
                Nil => panic!("List index {} out of range", _index)
            },
            1is => match *self {
                Cons(_, box Cons(ref cdr, _)) => cdr,
                Cons(_, box Nil) => panic!("List index {} out of range", _index),
                Nil => panic!("List index {} out of range", _index)
            },
            i if i == self.length() as isize => self.last(),
            i if i > self.length() as isize => panic!("List index {:?} out of range.", _index),
            i if i > 1is => {
                let mut it = self.iter();
                for _ in 0 .. i{
                    it.next();
                }
                it.next().unwrap()
            },
            _ => panic!("Expected an index i such that i >= 0, got {:?}.", _index)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{List, Stack};
    use super::List::{Cons,Nil};

    #[test]
    fn test_list_length() {
        let full_list: List<i32> = list!(1i32, 2i32, 3i32);
        let empty_list: List<i32> = List::new();
        assert_eq!(full_list.length(), 3);
        assert_eq!(empty_list.length(), 0);
    }

    #[test]
    fn test_list_to_string() {
        let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
        assert_eq!(l.to_string(), "(1, (2, (3, nil)))");
    }

    #[test]
    fn test_stack_length() {
        let full_stack: List<i32> = list!(1i32, 2i32, 3i32);
        let empty_stack: List<i32> = Stack::empty();
        assert_eq!(full_stack.length(), 3);
        assert_eq!(empty_stack.length(), 0);
    }

    #[test]
    fn test_stack_peek() {
        let full_stack: List<i32> = list!(1i32, 2i32, 3i32);
        let empty_stack: List<i32> = Stack::empty();
        assert_eq!(full_stack.peek(), Some(&1));
        assert_eq!(empty_stack.peek(), None);
    }

    #[test]
    fn test_stack_push() {
        let mut s: List<i32> = Stack::empty();
        assert_eq!(s.peek(), None);
        s = s.push(1);
        assert_eq!(s.peek(), Some(&1));
        s = s.push(6);
        assert_eq!(s.peek(), Some(&6));
    }

    #[test]
    fn test_stack_pop() {
        let mut s: List<i32> = Stack::empty();
        assert_eq!(s.peek(), None);
        s = s.push(1);
        assert_eq!(s.peek(), Some(&1));
        s = s.push(6);
        assert_eq!(s.peek(), Some(&6));
        let pop_result = s.pop().unwrap(); // should not break
        s = pop_result.1;
        assert_eq!(s.peek(), Some(&1));
        assert_eq!(pop_result.0, 6);
    }
/*
    #[test]
    fn test_list_usize_indexing() {
        let l: List<isize> = list!(1,2,3,4,5,6);
        assert_eq!(l[0us],1);
        assert_eq!(l[1us],2);
        assert_eq!(l[2us],3);
        assert_eq!(l[3us],4);
        assert_eq!(l[4us],5);
        assert_eq!(l[5us],6);
    }*/

    #[test]
    fn test_list_isize_indexing() {
        let l: List<isize> = list!(1,2,3,4,5,6);
        assert_eq!(l[0is],1);
        assert_eq!(l[1is],2);
        assert_eq!(l[2is],3);
        assert_eq!(l[3is],4);
        assert_eq!(l[4is],5);
        assert_eq!(l[5is],6);
    }

    #[test]
    fn test_list_macro() {
        let l: List<i32> = list!(1i32, 2i32, 3i32);
        assert_eq!(l.to_string(), "(1, (2, (3, nil)))")
    }

    #[test]
    fn test_list_iter() {
        let l: List<isize> = list!(1,2,3,4,5,6);
        let mut string = String::new();
        for item in l.iter() {
            string.push_str((item.to_string() + ", ").as_slice());
        }
        assert_eq!(string.as_slice(), "1, 2, 3, 4, 5, 6, ")
    }

}
<commit_msg>Use un-deprecated number suffixes<commit_after>#![feature(box_syntax)]
#![feature(core)]
#![feature(int_uint)]

pub use svm::slist::List::{Cons,Nil};
use std::fmt;
use std::ops::Index;

/// Convenience macro for making lists.
///
/// # Example:
///
/// ```
/// # #[macro_use] extern crate seax_svm;
/// # use seax_svm::svm::slist;
/// # use seax_svm::svm::slist::List::{Cons, Nil};
/// # fn main () {
/// assert_eq!(
///     list!(1i32, 2i32, 3i32),
///     Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil))))))
///     );
/// # }
/// ```
#[macro_export]
macro_rules! list(
    ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
    ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
    () => ( @Empty )
);

/// Common functions for an immutable Stack abstract data type.
pub trait Stack<T> {

    /// Push an item to the top of the stack, returning a new stack
    fn push(self, item : T) -> Self;

    /// Pop the top element of the stack. Returns an Option on a T and
    /// a new Stack<T> to replace this.
    fn pop(self)            -> Option<(T, Self)>;

    /// Peek at the top item of the stack.
    ///
    /// Returns Some<T> if there is an item on top of the stack,
    /// and None if the stack is empty.
    fn peek(&self)          -> Option<&T>;

    /// Returns an empty stack.
    fn empty()              -> Self;
}

/// Stack implementation using a cons list
impl<T> Stack<T> for List<T> {

    /// Push an item to the top of the stack, returning a new stack.
    ///
    /// # Examples:
    /// ```
    /// use seax_svm::svm::slist::{List,Stack};
    ///
    /// let mut s: List<isize> = Stack::empty();
    /// assert_eq!(s.peek(), None);
    /// s = s.push(1);
    /// assert_eq!(s.peek(), Some(&1));
    /// s = s.push(6);
    /// assert_eq!(s.peek(), Some(&6));
    /// ```
    fn push(self, item: T) -> List<T> {
        Cons(item, box self)
    }

    /// Pop the top element of the stack.
    ///
    /// Pop the top element of the stack. Returns an
    /// `Option<(T,List<T>)>` containing the top element and a new
    /// `List<T>` with that item removed, or `None` if the stack is
    /// empty.
    ///
    /// # Examples:
    /// ```
    /// # use seax_svm::svm::slist::{List,Stack};
    ///
    /// let mut s: List<isize> = Stack::empty();
    /// s = s.push(2);
    /// s = s.push(1);
    /// let pop_result = s.pop().unwrap();
    /// s = pop_result.1;
    /// assert_eq!(s.peek(), Some(&2));
    /// assert_eq!(pop_result.0, 1);
    /// ```
    fn pop(self) -> Option<(T,List<T>)> {
        match self {
            Cons(item, new_self)    => Some((item, *new_self)),
            Nil                     => None
        }
    }

    fn empty() -> List<T> {
        Nil
    }

    /// Peek at the top element of the stack.
    ///
    /// Peek at the top element of the stack. Returns an `Option<&T>`
    /// with a borrowed pointer to the top element, or `None` if the
    /// stack is empty.
    ///
    /// # Examples:
    /// ```
    /// # use seax_svm::svm::slist::{List,Stack};
    ///
    /// let mut s: List<isize> = Stack::empty();
    /// s = s.push(2);
    /// s = s.push(1);
    /// let pop_result = s.pop().unwrap();
    /// s = pop_result.1;
    /// assert_eq!(s.peek(), Some(&2));
    /// assert_eq!(pop_result.0, 1);
    /// ```
    fn peek(&self) -> Option<&T> {
        match self {
            &Nil => None,
            &Cons(ref it,_) => Some(it)
        }
    }

}

/// Singly-linked cons list.
///
/// This is used internally to represent list primitives in the
/// machine.
#[derive(PartialEq,Clone,Debug)]
pub enum List<T> {
    /// Cons cell containing a `T` and a link to the tail
    Cons(T, Box<List<T>>),
    /// The empty list.
    Nil,
}

/// Public implementation for List.
impl<T> List<T> {

    /// Creates a new empty list
    pub fn new() -> List<T> {
        Nil
    }

    /// Prepends the given item to the list.
    ///
    /// Returns the list containing the new  head item.
    /// This is an O(1) operation.
    pub fn prepend(self, it: T) -> List<T> {
        Cons(it, box self)
    }

    /// Appends an item to the end of the list.
    ///
    /// This is an O(n) operation.
    pub fn append(self, it: T) {
        unimplemented!()
    }

    /// Returns the length of the list.
    pub fn length (&self) -> usize {
        match *self {
            Cons(_, ref tail) => 1 + tail.length(),
            Nil => 0
        }
    }

    /// Provide a forward iterator
    #[inline]
    pub fn iter<'a>(&'a self) -> ListIterator<'a, T> {
        ListIterator{current: self}
    }

    pub fn last(&self) -> &T {
        match *self {
            Cons(ref car, box Nil) => &car,
            Cons(_, ref cdr @ box Cons(_,_)) => cdr.last(),
            Nil => panic!("Last called on empty list")
        }
    }
}

impl<'a, T> fmt::Display for List<T> where T: fmt::Display{

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // TODO: replace toString with this
        match *self {
            Cons(ref head, ref tail) => write!(f, "({}, {})", head, tail),
            Nil => write!(f,"nil")
        }
    }
}

/// Wraps a List<T> to allow it to be used as an Iterator<T>
pub struct ListIterator<'a, T:'a> {
    current: &'a List<T>
}

/// Implementation of Iterator for List. This allows iteration by
/// link hopping.
impl<'a, T> Iterator for ListIterator<'a, T> {
    type Item = &'a T;

    /// Get the next element from the list.
    ///
    /// Get the next element from the list. Returns a `Some<T>`, or `Nil`
    /// if at the end of the list.
    ///
    /// # Examples:
    /// ```
    /// # #[macro_use] extern crate seax_svm;
    /// # use seax_svm::svm::slist;
    /// # use seax_svm::svm::slist::List;
    /// # use seax_svm::svm::slist::List::{Cons, Nil};
    /// # fn main () {
    /// let list = list!(1,2,3);
    /// let mut iter = list.iter();
    /// assert_eq!(iter.next().unwrap(), &1);
    /// assert_eq!(iter.next().unwrap(), &2);
    /// assert_eq!(iter.next().unwrap(), &3);
    /// # }
    /// ```
    /// ```
    /// # #[macro_use] extern crate seax_svm;
    /// # use seax_svm::svm::slist;
    /// # use seax_svm::svm::slist::List;
    /// # use seax_svm::svm::slist::List::{Cons, Nil};
    /// # fn main () {
    /// let l: List<isize> = list!(1,2,3,4,5,6);
    /// let mut string = String::new();
    /// for item in l.iter() {
    ///     string.push_str((item.to_string() + ", ").as_slice());
    /// }
    /// assert_eq!(string.as_slice(), "1, 2, 3, 4, 5, 6, ")
    /// # }
    /// ```
    fn next(&mut self) -> Option<&'a T> {
        match self.current {
            &Cons(ref head, box ref tail) => { self.current = tail; Some(head) },
            &Nil => None
        }
    }
}

impl<'a, T> ExactSizeIterator for ListIterator<'a, T> {
    fn len(&self) -> usize {
        self.current.length()
    }
}
/// Implementation of indexing for `List<T>`.
///
/// # Examples:
/// ```
/// # #[macro_use] extern crate seax_svm;
/// # use seax_svm::svm::slist;
/// # use seax_svm::svm::slist::List;
/// # use seax_svm::svm::slist::List::{Cons, Nil};
/// # fn main () {
/// let list = list!(1,2,3,4,5,6);
/// assert_eq!(list[0us], 1);
/// # }
/// ```
impl<T> Index<usize> for List<T> {
    type Output = T;

    fn index<'a>(&'a self, _index: &usize) -> &'a T {
        match *_index {
            0usize => match *self {
                Cons(ref car, _) => car,
                Nil => panic!("List index {} out of range", _index)
            },
            1usize => match *self {
                Cons(_, box Cons(ref cdr, _)) => cdr,
                Cons(_, box Nil) => panic!("List index {} out of range", _index),
                Nil => panic!("List index {} out of range", _index)
            },
            i if i == self.length() => self.last(),
            i if i > self.length()  => panic!("List index {:?} out of range.", _index),
            i if i > 1usize => {
                let mut it = self.iter();
                for _ in 0usize .. i{
                    it.next();
                }
                it.next().unwrap()
            },
            _ => panic!("Expected an index i such that i >= 0, got {:?}.", _index)
        }
    }
}
/// Implementation of indexing for `List<T>`.
///
/// # Examples:
/// ```
/// # #[macro_use] extern crate seax_svm;
/// # use seax_svm::svm::slist;
/// # use seax_svm::svm::slist::List;
/// # use seax_svm::svm::slist::List::{Cons, Nil};
/// # fn main () {
/// let list = list!(1,2,3,4,5,6);
/// assert_eq!(list[0is], 1);
/// # }
/// ```
impl<T> Index<isize> for List<T> {
    type Output = T;

    fn index<'a>(&'a self, _index: &isize) -> &'a T {
        match *_index {
            0isize => match *self {
                Cons(ref car, _) => car,
                Nil => panic!("List index {} out of range", _index)
            },
            1isize => match *self {
                Cons(_, box Cons(ref cdr, _)) => cdr,
                Cons(_, box Nil) => panic!("List index {} out of range", _index),
                Nil => panic!("List index {} out of range", _index)
            },
            i if i == self.length() as isize => self.last(),
            i if i > self.length() as isize => panic!("List index {:?} out of range.", _index),
            i if i > 1isize => {
                let mut it = self.iter();
                for _ in 0isize .. i{
                    it.next();
                }
                it.next().unwrap()
            },
            _ => panic!("Expected an index i such that i >= 0, got {:?}.", _index)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{List, Stack};
    use super::List::{Cons,Nil};

    #[test]
    fn test_list_length() {
        let full_list: List<i32> = list!(1i32, 2i32, 3i32);
        let empty_list: List<i32> = List::new();
        assert_eq!(full_list.length(), 3);
        assert_eq!(empty_list.length(), 0);
    }

    #[test]
    fn test_list_to_string() {
        let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
        assert_eq!(l.to_string(), "(1, (2, (3, nil)))");
    }

    #[test]
    fn test_stack_length() {
        let full_stack: List<i32> = list!(1i32, 2i32, 3i32);
        let empty_stack: List<i32> = Stack::empty();
        assert_eq!(full_stack.length(), 3);
        assert_eq!(empty_stack.length(), 0);
    }

    #[test]
    fn test_stack_peek() {
        let full_stack: List<i32> = list!(1i32, 2i32, 3i32);
        let empty_stack: List<i32> = Stack::empty();
        assert_eq!(full_stack.peek(), Some(&1));
        assert_eq!(empty_stack.peek(), None);
    }

    #[test]
    fn test_stack_push() {
        let mut s: List<i32> = Stack::empty();
        assert_eq!(s.peek(), None);
        s = s.push(1);
        assert_eq!(s.peek(), Some(&1));
        s = s.push(6);
        assert_eq!(s.peek(), Some(&6));
    }

    #[test]
    fn test_stack_pop() {
        let mut s: List<i32> = Stack::empty();
        assert_eq!(s.peek(), None);
        s = s.push(1);
        assert_eq!(s.peek(), Some(&1));
        s = s.push(6);
        assert_eq!(s.peek(), Some(&6));
        let pop_result = s.pop().unwrap(); // should not break
        s = pop_result.1;
        assert_eq!(s.peek(), Some(&1));
        assert_eq!(pop_result.0, 6);
    }
/*
    #[test]
    fn test_list_usize_indexing() {
        let l: List<isize> = list!(1,2,3,4,5,6);
        assert_eq!(l[0us],1);
        assert_eq!(l[1us],2);
        assert_eq!(l[2us],3);
        assert_eq!(l[3us],4);
        assert_eq!(l[4us],5);
        assert_eq!(l[5us],6);
    }*/

    #[test]
    fn test_list_isize_indexing() {
        let l: List<isize> = list!(1,2,3,4,5,6);
        assert_eq!(l[0is],1);
        assert_eq!(l[1is],2);
        assert_eq!(l[2is],3);
        assert_eq!(l[3is],4);
        assert_eq!(l[4is],5);
        assert_eq!(l[5is],6);
    }

    #[test]
    fn test_list_macro() {
        let l: List<i32> = list!(1i32, 2i32, 3i32);
        assert_eq!(l.to_string(), "(1, (2, (3, nil)))")
    }

    #[test]
    fn test_list_iter() {
        let l: List<isize> = list!(1,2,3,4,5,6);
        let mut string = String::new();
        for item in l.iter() {
            string.push_str((item.to_string() + ", ").as_slice());
        }
        assert_eq!(string.as_slice(), "1, 2, 3, 4, 5, 6, ")
    }

}
<|endoftext|>


<commit_before>//! Implementation of panics via stack unwinding
//!
//! This crate is an implementation of panics in Rust using "most native" stack
//! unwinding mechanism of the platform this is being compiled for. This
//! essentially gets categorized into three buckets currently:
//!
//! 1. MSVC targets use SEH in the `seh.rs` file.
//! 2. The 64-bit MinGW target half-uses SEH and half-use gcc-like information
//!    in the `seh64_gnu.rs` module.
//! 3. All other targets use libunwind/libgcc in the `gcc/mod.rs` module.
//!
//! More documentation about each implementation can be found in the respective
//! module.

#![no_std]
#![unstable(feature = "panic_unwind", issue = "32837")]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/",
       issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]

#![deny(rust_2018_idioms)]

#![feature(allocator_api)]
#![feature(alloc)]
#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(libc)]
#![feature(nll)]
#![feature(panic_unwind)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(unwind_attributes)]

#![panic_runtime]
#![feature(panic_runtime)]

#[cfg(not(any(target_env = "msvc", all(windows, target_arch = "x86_64", target_env = "gnu"))))]
extern crate unwind;

use alloc::boxed::Box;
use core::intrinsics;
use core::mem;
use core::raw;
use core::panic::BoxMeUp;

#[macro_use]
mod macros;

cfg_if! {
    if #[cfg(target_os = "emscripten")] {
        #[path = "emcc.rs"]
        mod imp;
    } else if #[cfg(target_arch = "wasm32")] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(target_env = "msvc")] {
        #[path = "seh.rs"]
        mod imp;
    } else if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
        #[path = "seh64_gnu.rs"]
        mod imp;
    } else {
        // Rust runtime's startup objects depend on these symbols, so make them public.
        #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
        pub use imp::eh_frame_registry::*;
        #[path = "gcc.rs"]
        mod imp;
    }
}

mod dwarf;
mod windows;

// Entry point for catching an exception, implemented using the `try` intrinsic
// in the compiler.
//
// The interaction between the `payload` function and the compiler is pretty
// hairy and tightly coupled, for more information see the compiler's
// implementation of this.
#[no_mangle]
pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
                                                  data: *mut u8,
                                                  data_ptr: *mut usize,
                                                  vtable_ptr: *mut usize)
                                                  -> u32 {
    let mut payload = imp::payload();
    if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
        0
    } else {
        let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
        *data_ptr = obj.data as usize;
        *vtable_ptr = obj.vtable as usize;
        1
    }
}

// Entry point for raising an exception, just delegates to the platform-specific
// implementation.
#[no_mangle]
#[unwind(allowed)]
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
    let payload = payload as *mut &mut dyn BoxMeUp;
    imp::panic(Box::from_raw((*payload).box_me_up()))
}
<commit_msg>libpanic_unwind => 2018: remove unused extern crate.<commit_after>//! Implementation of panics via stack unwinding
//!
//! This crate is an implementation of panics in Rust using "most native" stack
//! unwinding mechanism of the platform this is being compiled for. This
//! essentially gets categorized into three buckets currently:
//!
//! 1. MSVC targets use SEH in the `seh.rs` file.
//! 2. The 64-bit MinGW target half-uses SEH and half-use gcc-like information
//!    in the `seh64_gnu.rs` module.
//! 3. All other targets use libunwind/libgcc in the `gcc/mod.rs` module.
//!
//! More documentation about each implementation can be found in the respective
//! module.

#![no_std]
#![unstable(feature = "panic_unwind", issue = "32837")]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/",
       issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]

#![deny(rust_2018_idioms)]

#![feature(allocator_api)]
#![feature(alloc)]
#![feature(core_intrinsics)]
#![feature(lang_items)]
#![feature(libc)]
#![feature(nll)]
#![feature(panic_unwind)]
#![feature(raw)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(unwind_attributes)]

#![panic_runtime]
#![feature(panic_runtime)]

use alloc::boxed::Box;
use core::intrinsics;
use core::mem;
use core::raw;
use core::panic::BoxMeUp;

#[macro_use]
mod macros;

cfg_if! {
    if #[cfg(target_os = "emscripten")] {
        #[path = "emcc.rs"]
        mod imp;
    } else if #[cfg(target_arch = "wasm32")] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
        #[path = "dummy.rs"]
        mod imp;
    } else if #[cfg(target_env = "msvc")] {
        #[path = "seh.rs"]
        mod imp;
    } else if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] {
        #[path = "seh64_gnu.rs"]
        mod imp;
    } else {
        // Rust runtime's startup objects depend on these symbols, so make them public.
        #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
        pub use imp::eh_frame_registry::*;
        #[path = "gcc.rs"]
        mod imp;
    }
}

mod dwarf;
mod windows;

// Entry point for catching an exception, implemented using the `try` intrinsic
// in the compiler.
//
// The interaction between the `payload` function and the compiler is pretty
// hairy and tightly coupled, for more information see the compiler's
// implementation of this.
#[no_mangle]
pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
                                                  data: *mut u8,
                                                  data_ptr: *mut usize,
                                                  vtable_ptr: *mut usize)
                                                  -> u32 {
    let mut payload = imp::payload();
    if intrinsics::r#try(f, data, &mut payload as *mut _ as *mut _) == 0 {
        0
    } else {
        let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
        *data_ptr = obj.data as usize;
        *vtable_ptr = obj.vtable as usize;
        1
    }
}

// Entry point for raising an exception, just delegates to the platform-specific
// implementation.
#[no_mangle]
#[unwind(allowed)]
pub unsafe extern "C" fn __rust_start_panic(payload: usize) -> u32 {
    let payload = payload as *mut &mut dyn BoxMeUp;
    imp::panic(Box::from_raw((*payload).box_me_up()))
}
<|endoftext|>


<commit_before>//! System bindings for the wasm/web platform
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
//! wasm, so we have no runtime here.
//!
//! This is all super highly experimental and not actually intended for
//! wide/production use yet, it's still all in the experimental category. This
//! will likely change over time.
//!
//! Currently all functions here are basically stubs that immediately return
//! errors. The hope is that with a portability lint we can turn actually just
//! remove all this and just omit parts of the standard library if we're
//! compiling for wasm. That way it's a compile time error for something that's
//! guaranteed to be a runtime error!

use libc;
use crate::io::{Error, ErrorKind};
use crate::mem;
use crate::os::raw::c_char;

pub mod alloc;
pub mod args;
#[path = "../wasm/cmath.rs"]
pub mod cmath;
#[path = "../wasm/condvar.rs"]
pub mod condvar;
pub mod env;
pub mod fd;
pub mod fs;
#[path = "../wasm/memchr.rs"]
pub mod memchr;
#[path = "../wasm/mutex.rs"]
pub mod mutex;
pub mod net;
pub mod io;
pub mod os;
pub use crate::sys_common::os_str_bytes as os_str;
pub mod path;
pub mod pipe;
pub mod process;
#[path = "../wasm/rwlock.rs"]
pub mod rwlock;
#[path = "../wasm/stack_overflow.rs"]
pub mod stack_overflow;
pub mod stdio;
pub mod thread;
#[path = "../wasm/thread_local.rs"]
pub mod thread_local;
#[path = "../wasm/fast_thread_local.rs"]
pub mod fast_thread_local;
pub mod time;
pub mod ext;

#[cfg(not(test))]
pub fn init() {
}

pub fn unsupported<T>() -> crate::io::Result<T> {
    Err(unsupported_err())
}

pub fn unsupported_err() -> Error {
    Error::new(ErrorKind::Other, "operation not supported on wasm yet")
}

pub fn decode_error_kind(_code: i32) -> ErrorKind {
    ErrorKind::Other
}

// This enum is used as the storage for a bunch of types which can't actually
// exist.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum Void {}

pub unsafe fn strlen(mut s: *const c_char) -> usize {
    let mut n = 0;
    while *s != 0 {
        n += 1;
        s = s.offset(1);
    }
    return n
}

pub unsafe fn abort_internal() -> ! {
    libc::abort()
}

pub fn hashmap_random_keys() -> (u64, u64) {
    let mut ret = (0u64, 0u64);
    unsafe {
        let base = &mut ret as *mut (u64, u64) as *mut libc::c_void;
        let len = mem::size_of_val(&ret);
        cvt_wasi(libc::__wasi_random_get(base, len)).unwrap();
    }
    return ret
}

#[doc(hidden)]
pub trait IsMinusOne {
    fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
    if t.is_minus_one() {
        Err(Error::last_os_error())
    } else {
        Ok(t)
    }
}

pub fn cvt_wasi(r: u16) -> crate::io::Result<()> {
    if r != libc::__WASI_ESUCCESS {
        Err(Error::from_raw_os_error(r as i32))
    } else {
        Ok(())
    }
}
<commit_msg>Auto merge of #63814 - malbarbo:wasi-error-kind, r=alexcrichton<commit_after>//! System bindings for the wasm/web platform
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
//! wasm, so we have no runtime here.
//!
//! This is all super highly experimental and not actually intended for
//! wide/production use yet, it's still all in the experimental category. This
//! will likely change over time.
//!
//! Currently all functions here are basically stubs that immediately return
//! errors. The hope is that with a portability lint we can turn actually just
//! remove all this and just omit parts of the standard library if we're
//! compiling for wasm. That way it's a compile time error for something that's
//! guaranteed to be a runtime error!

use libc;
use crate::io::{Error, ErrorKind};
use crate::mem;
use crate::os::raw::c_char;

pub mod alloc;
pub mod args;
#[path = "../wasm/cmath.rs"]
pub mod cmath;
#[path = "../wasm/condvar.rs"]
pub mod condvar;
pub mod env;
pub mod fd;
pub mod fs;
#[path = "../wasm/memchr.rs"]
pub mod memchr;
#[path = "../wasm/mutex.rs"]
pub mod mutex;
pub mod net;
pub mod io;
pub mod os;
pub use crate::sys_common::os_str_bytes as os_str;
pub mod path;
pub mod pipe;
pub mod process;
#[path = "../wasm/rwlock.rs"]
pub mod rwlock;
#[path = "../wasm/stack_overflow.rs"]
pub mod stack_overflow;
pub mod stdio;
pub mod thread;
#[path = "../wasm/thread_local.rs"]
pub mod thread_local;
#[path = "../wasm/fast_thread_local.rs"]
pub mod fast_thread_local;
pub mod time;
pub mod ext;

#[cfg(not(test))]
pub fn init() {
}

pub fn unsupported<T>() -> crate::io::Result<T> {
    Err(unsupported_err())
}

pub fn unsupported_err() -> Error {
    Error::new(ErrorKind::Other, "operation not supported on wasm yet")
}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
    match errno as libc::c_int {
        libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
        libc::ECONNRESET => ErrorKind::ConnectionReset,
        libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
        libc::EPIPE => ErrorKind::BrokenPipe,
        libc::ENOTCONN => ErrorKind::NotConnected,
        libc::ECONNABORTED => ErrorKind::ConnectionAborted,
        libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
        libc::EADDRINUSE => ErrorKind::AddrInUse,
        libc::ENOENT => ErrorKind::NotFound,
        libc::EINTR => ErrorKind::Interrupted,
        libc::EINVAL => ErrorKind::InvalidInput,
        libc::ETIMEDOUT => ErrorKind::TimedOut,
        libc::EEXIST => ErrorKind::AlreadyExists,
        libc::EAGAIN => ErrorKind::WouldBlock,
        _ => ErrorKind::Other,
    }
}

// This enum is used as the storage for a bunch of types which can't actually
// exist.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum Void {}

pub unsafe fn strlen(mut s: *const c_char) -> usize {
    let mut n = 0;
    while *s != 0 {
        n += 1;
        s = s.offset(1);
    }
    return n
}

pub unsafe fn abort_internal() -> ! {
    libc::abort()
}

pub fn hashmap_random_keys() -> (u64, u64) {
    let mut ret = (0u64, 0u64);
    unsafe {
        let base = &mut ret as *mut (u64, u64) as *mut libc::c_void;
        let len = mem::size_of_val(&ret);
        cvt_wasi(libc::__wasi_random_get(base, len)).unwrap();
    }
    return ret
}

#[doc(hidden)]
pub trait IsMinusOne {
    fn is_minus_one(&self) -> bool;
}

macro_rules! impl_is_minus_one {
    ($($t:ident)*) => ($(impl IsMinusOne for $t {
        fn is_minus_one(&self) -> bool {
            *self == -1
        }
    })*)
}

impl_is_minus_one! { i8 i16 i32 i64 isize }

pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
    if t.is_minus_one() {
        Err(Error::last_os_error())
    } else {
        Ok(t)
    }
}

pub fn cvt_wasi(r: u16) -> crate::io::Result<()> {
    if r != libc::__WASI_ESUCCESS {
        Err(Error::from_raw_os_error(r as i32))
    } else {
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::mem::transmute;
use rand;

/// Generate a random frame mask.
#[inline]
pub fn generate_mask() -> [u8; 4] {
    rand::random()
}

/// Mask/unmask a frame.
#[inline]
pub fn apply_mask(buf: &mut [u8], mask: &[u8; 4]) {
    // Assume that the memory is 32-bytes aligned.
    // FIXME: this assumption is not correct.
    unsafe { apply_mask_aligned32(buf, mask) }
}

/// A safe unoptimized mask application.
#[inline]
fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
    for (i, byte) in buf.iter_mut().enumerate() {
        *byte ^= mask[i & 3];
    }
}

/// Faster version of `apply_mask()` which operates on 4-byte blocks.
///
/// Safety: `buf` must be at least 4-bytes aligned.
#[inline]
unsafe fn apply_mask_aligned32(buf: &mut [u8], mask: &[u8; 4]) {
    debug_assert_eq!(buf.as_ptr() as usize % 4, 0);

    let mask_u32 = transmute(*mask);

    let mut ptr = buf.as_mut_ptr() as *mut u32;
    for _ in 0..(buf.len() / 4) {
        *ptr ^= mask_u32;
        ptr = ptr.offset(1);
    }

    // Possible last block with less than 4 bytes.
    let last_block_start = buf.len() & !3;
    let last_block = &mut buf[last_block_start..];
    apply_mask_fallback(last_block, mask);
}

#[cfg(test)]
mod tests {

    use super::{apply_mask_fallback, apply_mask_aligned32};

    #[test]
    fn test_apply_mask() {
        let mask = [
            0x6d, 0xb6, 0xb2, 0x80,
        ];
        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00,
        ];

        let mut masked = unmasked.clone();
        apply_mask_fallback(&mut masked, &mask);

        let mut masked_aligned = unmasked.clone();
        unsafe { apply_mask_aligned32(&mut masked_aligned, &mask) };

        assert_eq!(masked, masked_aligned);
    }

}

<commit_msg>Fix fast masking of unaligned data<commit_after>use std::cmp::min;
use std::mem::uninitialized;
use std::ptr::copy_nonoverlapping;
use rand;

/// Generate a random frame mask.
#[inline]
pub fn generate_mask() -> [u8; 4] {
    rand::random()
}

/// Mask/unmask a frame.
#[inline]
pub fn apply_mask(buf: &mut [u8], mask: &[u8; 4]) {
    apply_mask_fast32(buf, mask)
}

/// A safe unoptimized mask application.
#[inline]
#[allow(dead_code)]
fn apply_mask_fallback(buf: &mut [u8], mask: &[u8; 4]) {
    for (i, byte) in buf.iter_mut().enumerate() {
        *byte ^= mask[i & 3];
    }
}

/// Faster version of `apply_mask()` which operates on 4-byte blocks.
#[inline]
#[allow(dead_code)]
fn apply_mask_fast32(buf: &mut [u8], mask: &[u8; 4]) {
    // TODO replace this with read_unaligned() as it stabilizes.
    let mask_u32 = unsafe {
        let mut m: u32 = uninitialized();
        #[allow(trivial_casts)]
        copy_nonoverlapping(mask.as_ptr(), &mut m as *mut _ as *mut u8, 4);
        m
    };

    let mut ptr = buf.as_mut_ptr();
    let mut len = buf.len();

    // Possible first unaligned block.
    let head = min(len, (4 - (ptr as usize & 3)) & 3);
    let mask_u32 = if head > 0 {
        unsafe {
            xor_mem(ptr, mask_u32, head);
            ptr = ptr.offset(head as isize);
        }
        len -= head;
        if cfg!(target_endian = "big") {
            mask_u32.rotate_left(8 * head as u32)
        } else {
            mask_u32.rotate_right(8 * head as u32)
        }
    } else {
        mask_u32
    };

    if len > 0 {
        debug_assert_eq!(ptr as usize % 4, 0);
    }

    // Properly aligned middle of the data.
    while len > 4 {
        unsafe {
            *(ptr as *mut u32) ^= mask_u32;
            ptr = ptr.offset(4);
            len -= 4;
        }
    }

    // Possible last block.
    if len > 0 {
        unsafe { xor_mem(ptr, mask_u32, len); }
    }
}

#[inline]
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so inefficient,
// it could be done better. The compiler does not see that len is limited to 3.
unsafe fn xor_mem(ptr: *mut u8, mask: u32, len: usize) {
    let mut b: u32 = uninitialized();
    #[allow(trivial_casts)]
    copy_nonoverlapping(ptr, &mut b as *mut _ as *mut u8, len);
    b ^= mask;
    #[allow(trivial_casts)]
    copy_nonoverlapping(&b as *const _ as *const u8, ptr, len);
}

#[cfg(test)]
mod tests {

    use super::{apply_mask_fallback, apply_mask_fast32};

    #[test]
    fn test_apply_mask() {
        let mask = [
            0x6d, 0xb6, 0xb2, 0x80,
        ];
        let unmasked = vec![
            0xf3, 0x00, 0x01, 0x02,  0x03, 0x80, 0x81, 0x82,
            0xff, 0xfe, 0x00, 0x17,  0x74, 0xf9, 0x12, 0x03,
        ];

        // Check masking with proper alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked, &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask_fast32(&mut masked_fast, &mask);

            assert_eq!(masked, masked_fast);
        }

        // Check masking without alignment.
        {
            let mut masked = unmasked.clone();
            apply_mask_fallback(&mut masked[1..], &mask);

            let mut masked_fast = unmasked.clone();
            apply_mask_fast32(&mut masked_fast[1..], &mask);

            assert_eq!(masked, masked_fast);
        }
    }

}

<|endoftext|>


<commit_before>// Compiles the shaders used internally by some commands

use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::fs;
use std::ffi::OsStr;

fn main() {
    let pd = env::var("CARGO_MANIFEST_DIR").unwrap();
    let target = env::var("TARGET").unwrap();
    let os = if target.ends_with("ios") {
        "ios"
    } else if target.ends_with("darwin") {
        "darwin"
    } else {
        panic!("unsupported target {}", target)
    };
    let arch = &target[..target.chars().position(|c| c == '-').unwrap()];

    let sdk_name = match (os, arch) {
        ("ios", "aarch64") => "iphoneos",
        | ("ios", "armv7s")
        | ("ios", "armv7") => panic!("32-bit iOS does not have metal support"),
        ("ios", "i386")
        | ("ios", "x86_64") => panic!("iOS simulator does not have metal support"),
        ("darwin", _) => "macosx",
        _ => panic!("unsupported target {}", target)
    };

    let project_dir = Path::new(&pd);
    let shader_dir = project_dir.join("shaders");
    println!("cargo:rerun-if-changed={}", shader_dir.to_str().unwrap());

    let od = env::var("OUT_DIR").unwrap();
    let out_dir = Path::new(&od);
    let out_lib = out_dir.join("gfx_shaders.metallib");

    // Find all .metal files _at the top level only_
    let shader_files = fs::read_dir(&shader_dir)
        .expect("could not open shader directory")
        .filter_map(|entry| {
            let entry = entry.expect("error reading shader directory entry");
            let path = entry.path();
            match path.extension().and_then(OsStr::to_str) {
                Some("metal") => Some(path),
                _ => None
            }
        });

    // Compile all the metal files into OUT_DIR
    let mut compiled_shader_files: Vec<PathBuf> = Vec::new();
    for shader_path in shader_files.into_iter() {
        println!("cargo:rerun-if-changed={}", shader_path.to_str().unwrap());

        let mut out_path = out_dir.join(shader_path.file_name().unwrap());
        out_path.set_extension("air");

        let status = Command::new("xcrun")
            .args(&["-sdk", sdk_name, "metal"])
            .arg(shader_path.as_os_str())
            .arg("-o")
            .arg(out_path.as_os_str())
            .status()
            .expect("failed to execute metal compiler");

        if !status.success() {
            // stdout is linked to parent, so more detailed message will have been output from `metal`
            panic!("shader compilation failed");
        }

        compiled_shader_files.push(out_path);
    }

    // Link all the compiled files into a single library
    let status = Command::new("xcrun")
        .args(&["-sdk", sdk_name, "metallib"])
        .args(compiled_shader_files.iter().map(|p| p.as_os_str()))
        .arg("-o")
        .arg(out_lib.as_os_str())
        .status()
        .expect("failed to execute metal library builder");

    if !status.success() {
        panic!("shader library build failed");
    }
}

<commit_msg>run rustfmt on build.rs<commit_after>// Compiles the shaders used internally by some commands

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    let pd = env::var("CARGO_MANIFEST_DIR").unwrap();
    let target = env::var("TARGET").unwrap();
    let os = if target.ends_with("ios") {
        "ios"
    } else if target.ends_with("darwin") {
        "darwin"
    } else {
        panic!("unsupported target {}", target)
    };
    let arch = &target[..target.chars().position(|c| c == '-').unwrap()];

    let sdk_name = match (os, arch) {
        ("ios", "aarch64") => "iphoneos",
        ("ios", "armv7s") | ("ios", "armv7") => panic!("32-bit iOS does not have metal support"),
        ("ios", "i386") | ("ios", "x86_64") => panic!("iOS simulator does not have metal support"),
        ("darwin", _) => "macosx",
        _ => panic!("unsupported target {}", target),
    };

    let project_dir = Path::new(&pd);
    let shader_dir = project_dir.join("shaders");
    println!("cargo:rerun-if-changed={}", shader_dir.to_str().unwrap());

    let od = env::var("OUT_DIR").unwrap();
    let out_dir = Path::new(&od);
    let out_lib = out_dir.join("gfx_shaders.metallib");

    // Find all .metal files _at the top level only_
    let shader_files = fs::read_dir(&shader_dir)
        .expect("could not open shader directory")
        .filter_map(|entry| {
            let entry = entry.expect("error reading shader directory entry");
            let path = entry.path();
            match path.extension().and_then(OsStr::to_str) {
                Some("metal") => Some(path),
                _ => None,
            }
        });

    // Compile all the metal files into OUT_DIR
    let mut compiled_shader_files: Vec<PathBuf> = Vec::new();
    for shader_path in shader_files.into_iter() {
        println!("cargo:rerun-if-changed={}", shader_path.to_str().unwrap());

        let mut out_path = out_dir.join(shader_path.file_name().unwrap());
        out_path.set_extension("air");

        let status = Command::new("xcrun")
            .args(&["-sdk", sdk_name, "metal"])
            .arg(shader_path.as_os_str())
            .arg("-o")
            .arg(out_path.as_os_str())
            .status()
            .expect("failed to execute metal compiler");

        if !status.success() {
            // stdout is linked to parent, so more detailed message will have been output from `metal`
            panic!("shader compilation failed");
        }

        compiled_shader_files.push(out_path);
    }

    // Link all the compiled files into a single library
    let status = Command::new("xcrun")
        .args(&["-sdk", sdk_name, "metallib"])
        .args(compiled_shader_files.iter().map(|p| p.as_os_str()))
        .arg("-o")
        .arg(out_lib.as_os_str())
        .status()
        .expect("failed to execute metal library builder");

    if !status.success() {
        panic!("shader library build failed");
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;

use walkdir::{DirEntry, WalkDir};

use ra_tools::project_root;

fn is_exclude_dir(p: &Path) -> bool {
    let exclude_dirs = ["tests", "test_data"];
    let mut cur_path = p;
    while let Some(path) = cur_path.parent() {
        if exclude_dirs.iter().any(|dir| path.ends_with(dir)) {
            return true;
        }
        cur_path = path;
    }

    false
}

fn is_exclude_file(d: &DirEntry) -> bool {
    let file_names = ["tests.rs"];

    d.file_name().to_str().map(|f_n| file_names.iter().any(|name| *name == f_n)).unwrap_or(false)
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false)
}

#[test]
fn no_docs_comments() {
    let crates = project_root().join("crates");
    let iter = WalkDir::new(crates);
    for f in iter.into_iter().filter_entry(|e| !is_hidden(e)) {
        let f = f.unwrap(); //dbg!(f.unwrap());
        if f.file_type().is_dir() {
            continue;
        }
        if f.path().extension().map(|it| it != "rs").unwrap_or(false) {
            //dbg!(f.path());
            continue;
        }
        if is_exclude_dir(f.path()) {
            //dbg!(f.path());
            continue;
        }
        if is_exclude_file(&f) {
            //dbg!(f.path());
            continue;
        }
        let mut reader = BufReader::new(fs::File::open(f.path()).unwrap());
        let mut line = String::new();
        reader.read_line(&mut line).unwrap();
        if !line.starts_with("//!") {
            //dbg!(line);
            panic!(
                "\nMissing docs strings\n\
                 module: {}\n\
                 Need add doc for module or this string \"//! FIXME: write short doc here\"\n",
                f.path().display()
            )
        }
    }
}
<commit_msg>Drop debug strings.<commit_after>use std::fs;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;

use walkdir::{DirEntry, WalkDir};

use ra_tools::project_root;

fn is_exclude_dir(p: &Path) -> bool {
    let exclude_dirs = ["tests", "test_data"];
    let mut cur_path = p;
    while let Some(path) = cur_path.parent() {
        if exclude_dirs.iter().any(|dir| path.ends_with(dir)) {
            return true;
        }
        cur_path = path;
    }

    false
}

fn is_exclude_file(d: &DirEntry) -> bool {
    let file_names = ["tests.rs"];

    d.file_name().to_str().map(|f_n| file_names.iter().any(|name| *name == f_n)).unwrap_or(false)
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name().to_str().map(|s| s.starts_with(".")).unwrap_or(false)
}

#[test]
fn no_docs_comments() {
    let crates = project_root().join("crates");
    let iter = WalkDir::new(crates);
    for f in iter.into_iter().filter_entry(|e| !is_hidden(e)) {
        let f = f.unwrap();
        if f.file_type().is_dir() {
            continue;
        }
        if f.path().extension().map(|it| it != "rs").unwrap_or(false) {
            continue;
        }
        if is_exclude_dir(f.path()) {
            continue;
        }
        if is_exclude_file(&f) {
            continue;
        }
        let mut reader = BufReader::new(fs::File::open(f.path()).unwrap());
        let mut line = String::new();
        reader.read_line(&mut line).unwrap();
        if !line.starts_with("//!") {
            panic!(
                "\nMissing docs strings\n\
                 module: {}\n\
                 Need add doc for module or this string \"//! FIXME: write short doc here\"\n",
                f.path().display()
            )
        }
    }
}
<|endoftext|>