<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   output path, default is "" (no output), "-" means stdout
//   depth (max iterations per pixel), defaults to 50_u
//
//  in the shootout, they use 16000 as image size, 50 as depth,
//   and write to stdout:
//
//  ./shootout_mandelbrot 16000 "-" 50 > /tmp/mandel.pbm
//
//  writes pbm image to output path

use io::WriterUtil;
use core::hashmap::linear::LinearMap;

struct cmplx {
    re: f64,
    im: f64
}

impl ops::Mul<cmplx,cmplx> for cmplx {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl ops::Add<cmplx,cmplx> for cmplx {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

struct Line {i: uint, b: ~[u8]}

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx, depth: uint) -> bool
{
    let mut z = x;
    let mut i = 0;
    while i < depth {
        if cabs(z) >= 4_f64 {
            return false;
        }
        z = z*z + x;
        i += 1;
    }
    true
}

fn fillbyte(x: cmplx, incr: f64, depth: uint) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z, depth) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, depth: uint) -> Line
{
    let bsize = size/8_u;
    let mut crv = vec::with_capacity(bsize);
    let incr = 2_f64/(size as f64);
    let y = incr*(i as f64) - 1_f64;
    let xincr = 8_f64*incr;
    for uint::range(0_u, bsize) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5_f64, im: y};
        crv.push(fillbyte(x, incr, depth));
    };
    Line {i:i, b:crv}
}

struct Devnull();

impl io::Writer for Devnull {
    fn write(&self, _b: &[const u8]) {}
    fn seek(&self, _i: int, _s: io::SeekStyle) {}
    fn tell(&self) -> uint {0_u}
    fn flush(&self) -> int {0}
    fn get_type(&self) -> io::WriterType { io::File }
}

fn writer(path: ~str, pport: pipes::Port<Line>, size: uint)
{
    let cout: io::Writer = match path {
        ~"" => {
            Devnull as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line("P4");
    cout.write_line(fmt!("%u %u", size, size));
    let mut lines: LinearMap<uint, Line> = LinearMap::new();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = pport.recv();
        if aline.i == done {
            debug!("W %u", done);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                match lines.pop(&prev) {
                    Some(pl) => {
                        debug!("WS %u", prev);
                        cout.write(pl.b);
                        done += 1_u;
                        prev += 1_u;
                    }
                    None => break
                };
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline);
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"50"]
    } else {
        args
    };

    let depth = if vec::len(args) < 4_u { 50_u }
    else { uint::from_str(args[3]).get() };

    let path = if vec::len(args) < 3_u { ~"" }
    else { copy args[2] };  // FIXME: bad for perf

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let (pport, pchan) = pipes::stream();
    let pchan = pipes::SharedChan(pchan);
    for uint::range(0_u, size) |j| {
        let cchan = pchan.clone();
        do task::spawn { cchan.send(chanmb(j, size, depth)) };
    };
    writer(path, pport, size);
}
<commit_msg>update copyright dates<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   output path, default is "" (no output), "-" means stdout
//   depth (max iterations per pixel), defaults to 50_u
//
//  in the shootout, they use 16000 as image size, 50 as depth,
//   and write to stdout:
//
//  ./shootout_mandelbrot 16000 "-" 50 > /tmp/mandel.pbm
//
//  writes pbm image to output path

use io::WriterUtil;
use core::hashmap::linear::LinearMap;

struct cmplx {
    re: f64,
    im: f64
}

impl ops::Mul<cmplx,cmplx> for cmplx {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl ops::Add<cmplx,cmplx> for cmplx {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

struct Line {i: uint, b: ~[u8]}

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx, depth: uint) -> bool
{
    let mut z = x;
    let mut i = 0;
    while i < depth {
        if cabs(z) >= 4_f64 {
            return false;
        }
        z = z*z + x;
        i += 1;
    }
    true
}

fn fillbyte(x: cmplx, incr: f64, depth: uint) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z, depth) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, depth: uint) -> Line
{
    let bsize = size/8_u;
    let mut crv = vec::with_capacity(bsize);
    let incr = 2_f64/(size as f64);
    let y = incr*(i as f64) - 1_f64;
    let xincr = 8_f64*incr;
    for uint::range(0_u, bsize) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5_f64, im: y};
        crv.push(fillbyte(x, incr, depth));
    };
    Line {i:i, b:crv}
}

struct Devnull();

impl io::Writer for Devnull {
    fn write(&self, _b: &[const u8]) {}
    fn seek(&self, _i: int, _s: io::SeekStyle) {}
    fn tell(&self) -> uint {0_u}
    fn flush(&self) -> int {0}
    fn get_type(&self) -> io::WriterType { io::File }
}

fn writer(path: ~str, pport: pipes::Port<Line>, size: uint)
{
    let cout: io::Writer = match path {
        ~"" => {
            Devnull as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line("P4");
    cout.write_line(fmt!("%u %u", size, size));
    let mut lines: LinearMap<uint, Line> = LinearMap::new();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = pport.recv();
        if aline.i == done {
            debug!("W %u", done);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                match lines.pop(&prev) {
                    Some(pl) => {
                        debug!("WS %u", prev);
                        cout.write(pl.b);
                        done += 1_u;
                        prev += 1_u;
                    }
                    None => break
                };
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline);
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"50"]
    } else {
        args
    };

    let depth = if vec::len(args) < 4_u { 50_u }
    else { uint::from_str(args[3]).get() };

    let path = if vec::len(args) < 3_u { ~"" }
    else { copy args[2] };  // FIXME: bad for perf

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let (pport, pchan) = pipes::stream();
    let pchan = pipes::SharedChan(pchan);
    for uint::range(0_u, size) |j| {
        let cchan = pchan.clone();
        do task::spawn { cchan.send(chanmb(j, size, depth)) };
    };
    writer(path, pport, size);
}
<|endoftext|>


<commit_before>use ToGlEnum;
use gl;

/// Function to use for out-of-bounds samples.
///
/// This is how GL must handle samples that are outside the texture.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum SamplerWrapFunction {
    /// Samples at coord `x + 1` map to coord `x`.
    Repeat,

    /// Samples at coord `x + 1` map to coord `1 - x`.
    Mirror,

    /// Samples at coord `x + 1` map to coord `1`.
    Clamp,

    /// Same as Mirror, but only for one repetition,
    MirrorClamp
}

impl ToGlEnum for SamplerWrapFunction {
    #[inline]
    fn to_glenum(&self) -> gl::types::GLenum {
        match *self {
            SamplerWrapFunction::Repeat => gl::REPEAT,
            SamplerWrapFunction::Mirror => gl::MIRRORED_REPEAT,
            SamplerWrapFunction::Clamp => gl::CLAMP_TO_EDGE,
            SamplerWrapFunction::MirrorClamp => gl::MIRROR_CLAMP_TO_EDGE,
        }
    }
}

/// The function that the GPU will use when loading the value of a texel.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum MagnifySamplerFilter {
    /// The nearest texel will be loaded.
    Nearest,

    /// All nearby texels will be loaded and their values will be merged.
    Linear,
}

impl ToGlEnum for MagnifySamplerFilter {
    #[inline]
    fn to_glenum(&self) -> gl::types::GLenum {
        match *self {
            MagnifySamplerFilter::Nearest => gl::NEAREST,
            MagnifySamplerFilter::Linear => gl::LINEAR,
        }
    }
}

/// The function that the GPU will use when loading the value of a texel.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum MinifySamplerFilter {
    /// The nearest texel will be loaded.
    ///
    /// Only uses the main texture, mipmaps are totally ignored.
    Nearest,

    /// All nearby texels will be loaded and their values will be merged.
    ///
    /// Only uses the main texture, mipmaps are totally ignored.
    Linear,

    /// The nearest texel of the nearest mipmap will be loaded.
    NearestMipmapNearest,

    /// Takes the nearest texel from the two nearest mipmaps, and merges them.
    LinearMipmapNearest,

    /// Same as `Linear`, but from the nearest mipmap.
    NearestMipmapLinear,

    /// Same as `Linear`, but from the two nearest mipmaps.
    LinearMipmapLinear,
}

impl ToGlEnum for MinifySamplerFilter {
    #[inline]
    fn to_glenum(&self) -> gl::types::GLenum {
        match *self {
            MinifySamplerFilter::Nearest => gl::NEAREST,
            MinifySamplerFilter::Linear => gl::LINEAR,
            MinifySamplerFilter::NearestMipmapNearest => gl::NEAREST_MIPMAP_NEAREST,
            MinifySamplerFilter::LinearMipmapNearest => gl::LINEAR_MIPMAP_NEAREST,
            MinifySamplerFilter::NearestMipmapLinear => gl::NEAREST_MIPMAP_LINEAR,
            MinifySamplerFilter::LinearMipmapLinear => gl::LINEAR_MIPMAP_LINEAR,
        }
    }
}

/// A sampler.
pub struct Sampler<'t, T: 't>(pub &'t T, pub SamplerBehavior);

impl<'t, T: 't> Sampler<'t, T> {
    /// Builds a new `Sampler` with default parameters.
    pub fn new(texture: &'t T) -> Sampler<'t, T> {
        Sampler(texture, Default::default())
    }

    /// Changes the wrap functions of all three coordinates.
    pub fn wrap_function(mut self, function: SamplerWrapFunction) -> Sampler<'t, T> {
        self.1.wrap_function = (function, function, function);
        self
    }

    /// Changes the minifying filter of the sampler.
    pub fn minify_filter(mut self, filter: MinifySamplerFilter) -> Sampler<'t, T> {
        self.1.minify_filter = filter;
        self
    }

    /// Changes the magnifying filter of the sampler.
    pub fn magnify_filter(mut self, filter: MagnifySamplerFilter) -> Sampler<'t, T> {
        self.1.magnify_filter = filter;
        self
    }

    /// Changes the magnifying filter of the sampler.
    pub fn anisotropy(mut self, level: u16) -> Sampler<'t, T> {
        self.1.max_anisotropy = level;
        self
    }
}

/// Behavior of a sampler.
// TODO: GL_TEXTURE_BORDER_COLOR, GL_TEXTURE_MIN_LOD, GL_TEXTURE_MAX_LOD, GL_TEXTURE_LOD_BIAS,
//       GL_TEXTURE_COMPARE_MODE, GL_TEXTURE_COMPARE_FUNC
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct SamplerBehavior {
    /// Functions to use for the X, Y, and Z coordinates.
    pub wrap_function: (SamplerWrapFunction, SamplerWrapFunction, SamplerWrapFunction),

    /// Filter to use when minifying the texture.
    pub minify_filter: MinifySamplerFilter,

    /// Filter to use when magnifying the texture.
    pub magnify_filter: MagnifySamplerFilter,

    /// `1` means no anisotropic filtering, any value above `1` sets the max anisotropy.
    ///
    /// ## Compatibility
    ///
    /// This parameter is always available. However it is ignored on hardware that does
    /// not support anisotropic filtering.
    ///
    /// If you set the value to a value higher than what the hardware supports, it will
    /// be clamped.
    pub max_anisotropy: u16,
}

impl Default for SamplerBehavior {
    #[inline]
    fn default() -> SamplerBehavior {
        SamplerBehavior {
            wrap_function: (
                SamplerWrapFunction::Mirror,
                SamplerWrapFunction::Mirror,
                SamplerWrapFunction::Mirror
            ),
            minify_filter: MinifySamplerFilter::LinearMipmapLinear,
            magnify_filter: MagnifySamplerFilter::Linear,
            max_anisotropy: 1,
        }
    }
}
<commit_msg>Add trait implementations for Sampler<commit_after>use ToGlEnum;
use gl;

/// Function to use for out-of-bounds samples.
///
/// This is how GL must handle samples that are outside the texture.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum SamplerWrapFunction {
    /// Samples at coord `x + 1` map to coord `x`.
    Repeat,

    /// Samples at coord `x + 1` map to coord `1 - x`.
    Mirror,

    /// Samples at coord `x + 1` map to coord `1`.
    Clamp,

    /// Same as Mirror, but only for one repetition,
    MirrorClamp
}

impl ToGlEnum for SamplerWrapFunction {
    #[inline]
    fn to_glenum(&self) -> gl::types::GLenum {
        match *self {
            SamplerWrapFunction::Repeat => gl::REPEAT,
            SamplerWrapFunction::Mirror => gl::MIRRORED_REPEAT,
            SamplerWrapFunction::Clamp => gl::CLAMP_TO_EDGE,
            SamplerWrapFunction::MirrorClamp => gl::MIRROR_CLAMP_TO_EDGE,
        }
    }
}

/// The function that the GPU will use when loading the value of a texel.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum MagnifySamplerFilter {
    /// The nearest texel will be loaded.
    Nearest,

    /// All nearby texels will be loaded and their values will be merged.
    Linear,
}

impl ToGlEnum for MagnifySamplerFilter {
    #[inline]
    fn to_glenum(&self) -> gl::types::GLenum {
        match *self {
            MagnifySamplerFilter::Nearest => gl::NEAREST,
            MagnifySamplerFilter::Linear => gl::LINEAR,
        }
    }
}

/// The function that the GPU will use when loading the value of a texel.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum MinifySamplerFilter {
    /// The nearest texel will be loaded.
    ///
    /// Only uses the main texture, mipmaps are totally ignored.
    Nearest,

    /// All nearby texels will be loaded and their values will be merged.
    ///
    /// Only uses the main texture, mipmaps are totally ignored.
    Linear,

    /// The nearest texel of the nearest mipmap will be loaded.
    NearestMipmapNearest,

    /// Takes the nearest texel from the two nearest mipmaps, and merges them.
    LinearMipmapNearest,

    /// Same as `Linear`, but from the nearest mipmap.
    NearestMipmapLinear,

    /// Same as `Linear`, but from the two nearest mipmaps.
    LinearMipmapLinear,
}

impl ToGlEnum for MinifySamplerFilter {
    #[inline]
    fn to_glenum(&self) -> gl::types::GLenum {
        match *self {
            MinifySamplerFilter::Nearest => gl::NEAREST,
            MinifySamplerFilter::Linear => gl::LINEAR,
            MinifySamplerFilter::NearestMipmapNearest => gl::NEAREST_MIPMAP_NEAREST,
            MinifySamplerFilter::LinearMipmapNearest => gl::LINEAR_MIPMAP_NEAREST,
            MinifySamplerFilter::NearestMipmapLinear => gl::NEAREST_MIPMAP_LINEAR,
            MinifySamplerFilter::LinearMipmapLinear => gl::LINEAR_MIPMAP_LINEAR,
        }
    }
}

/// A sampler.
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct Sampler<'t, T: 't>(pub &'t T, pub SamplerBehavior);

impl<'t, T: 't> Sampler<'t, T> {
    /// Builds a new `Sampler` with default parameters.
    pub fn new(texture: &'t T) -> Sampler<'t, T> {
        Sampler(texture, Default::default())
    }

    /// Changes the wrap functions of all three coordinates.
    pub fn wrap_function(mut self, function: SamplerWrapFunction) -> Sampler<'t, T> {
        self.1.wrap_function = (function, function, function);
        self
    }

    /// Changes the minifying filter of the sampler.
    pub fn minify_filter(mut self, filter: MinifySamplerFilter) -> Sampler<'t, T> {
        self.1.minify_filter = filter;
        self
    }

    /// Changes the magnifying filter of the sampler.
    pub fn magnify_filter(mut self, filter: MagnifySamplerFilter) -> Sampler<'t, T> {
        self.1.magnify_filter = filter;
        self
    }

    /// Changes the magnifying filter of the sampler.
    pub fn anisotropy(mut self, level: u16) -> Sampler<'t, T> {
        self.1.max_anisotropy = level;
        self
    }
}

impl<'t, T: 't> Copy for Sampler<'t, T> {}

impl<'t, T: 't> Clone for Sampler<'t, T> {
    fn clone(&self) -> Self {
        *self
    }
}

/// Behavior of a sampler.
// TODO: GL_TEXTURE_BORDER_COLOR, GL_TEXTURE_MIN_LOD, GL_TEXTURE_MAX_LOD, GL_TEXTURE_LOD_BIAS,
//       GL_TEXTURE_COMPARE_MODE, GL_TEXTURE_COMPARE_FUNC
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct SamplerBehavior {
    /// Functions to use for the X, Y, and Z coordinates.
    pub wrap_function: (SamplerWrapFunction, SamplerWrapFunction, SamplerWrapFunction),

    /// Filter to use when minifying the texture.
    pub minify_filter: MinifySamplerFilter,

    /// Filter to use when magnifying the texture.
    pub magnify_filter: MagnifySamplerFilter,

    /// `1` means no anisotropic filtering, any value above `1` sets the max anisotropy.
    ///
    /// ## Compatibility
    ///
    /// This parameter is always available. However it is ignored on hardware that does
    /// not support anisotropic filtering.
    ///
    /// If you set the value to a value higher than what the hardware supports, it will
    /// be clamped.
    pub max_anisotropy: u16,
}

impl Default for SamplerBehavior {
    #[inline]
    fn default() -> SamplerBehavior {
        SamplerBehavior {
            wrap_function: (
                SamplerWrapFunction::Mirror,
                SamplerWrapFunction::Mirror,
                SamplerWrapFunction::Mirror
            ),
            minify_filter: MinifySamplerFilter::LinearMipmapLinear,
            magnify_filter: MagnifySamplerFilter::Linear,
            max_anisotropy: 1,
        }
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate notify;

use self::notify::{Watcher, RecursiveMode, DebouncedEvent};
use std::sync::mpsc::channel;
use std::time::Duration;
use std::env;
use std::fs;
use std::thread;
use std::process::{Command,Child};

/// watches the debug build, restarts server on build
pub fn watcher (matches: &getopts::Matches) {
    if matches.opt_present("w") && matches.opt_present("d") {

        let stratis_path = env::var("STRATIS_DEBUG").expect("no \'STRATIS_DEBUG\' path found in environment vars");
        let stratis_dest = stratis_path.clone()+"\\PAN_stratis.exe";

        let (tx, rx) = channel();
        let mut w = self::notify::watcher(tx,Duration::from_secs(3)).expect("unable to create filesys watcher");

        w.watch(stratis_path.clone()+"\\stratis.exe",RecursiveMode::NonRecursive).expect("unable to watch directory");

        let mut spawn_handle: Option<Child> = None;

        for n in rx.iter() {
            let mut new_spawn_handle = None;
            println!("event: {:?}",n);

            match n {
                DebouncedEvent::Write(path) => {
                    if let Some(ref mut h) = spawn_handle {
                        if let Ok(_) = h.kill() {
                            println!("process-killed\n");
                            thread::sleep(Duration::from_secs(1));

                            if let Ok(r) = fs::copy(path,&stratis_dest) {
                                println!("watcher-copy:{:?}",r);
                                new_spawn_handle = spawn(&stratis_dest);
                            }
                        }
                    }
                    else { //no child process alive?
                        if let Ok(r) = fs::copy(path,&stratis_dest) {
                            println!("watcher-copy:{:?}",r);
                            new_spawn_handle = spawn(&stratis_dest);
                        }
                    }

                    spawn_handle = new_spawn_handle;
                    if let Some(ref h) = spawn_handle {
                        println!("process-spawned:{:?}\n",h.id());
                    }
                },
                _ => {  },
            }
        }

    }
}

fn spawn(path: &str) -> Option<Child> {
    let r = Command::new(path).spawn();
    match r {
        Ok(h) => {
            Some(h)
        },
        Err(e) => {
            println!("spawn-err:{:?}",e);
            None
        }
    }
}
<commit_msg>release and debug modes<commit_after>extern crate getopts;
extern crate notify;

use self::notify::{Watcher, RecursiveMode, DebouncedEvent};
use std::sync::mpsc::channel;
use std::time::Duration;
use std::env;
use std::fs;
use std::thread;
use std::process::{Command,Child};

/// watches the debug build, restarts server on build
pub fn watcher (matches: &getopts::Matches) {
    let stratis_path;
    let stratis_dest;

    if matches.opt_present("d") {
        stratis_path = env::var("STRATIS_DEBUG").expect("no \'STRATIS_DEBUG\' path found in environment vars");
        stratis_dest = stratis_path.clone()+"\\PAN_stratis.exe";
    }
    else {
        stratis_path = env::var("STRATIS_RELEASE").expect("no \'STRATIS_RELEASE\' path found in environment vars");
        stratis_dest = stratis_path.clone()+"\\PAN_stratis.exe";
    }

    if matches.opt_present("w") {
        let (tx, rx) = channel();
        let mut w = self::notify::watcher(tx,Duration::from_secs(3)).expect("unable to create filesys watcher");

        w.watch(stratis_path.clone()+"\\stratis.exe",RecursiveMode::NonRecursive).expect("unable to watch directory");

        let mut spawn_handle: Option<Child> = None;

        for n in rx.iter() {
            let mut new_spawn_handle = None;
            println!("event: {:?}",n);

            match n {
                DebouncedEvent::Write(path) => {
                    if let Some(ref mut h) = spawn_handle {
                        if let Ok(_) = h.kill() {
                            println!("process-killed\n");
                            thread::sleep(Duration::from_secs(1));

                            if let Ok(r) = fs::copy(path,&stratis_dest) {
                                println!("watcher-copy:{:?}",r);
                                new_spawn_handle = spawn(&stratis_dest);
                            }
                        }
                    }
                    else { //no child process alive?
                        if let Ok(r) = fs::copy(path,&stratis_dest) {
                            println!("watcher-copy:{:?}",r);
                            new_spawn_handle = spawn(&stratis_dest);
                        }
                    }

                    spawn_handle = new_spawn_handle;
                    if let Some(ref h) = spawn_handle {
                        println!("process-spawned:{:?}\n",h.id());
                    }
                },
                _ => {  },
            }
        }

    }
}

fn spawn(path: &str) -> Option<Child> {
    let r = Command::new(path).spawn();
    match r {
        Ok(h) => {
            Some(h)
        },
        Err(e) => {
            println!("spawn-err:{:?}",e);
            None
        }
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (ToDO) lflag ICANON tcgetattr tcsetattr TCSADRAIN

#[macro_use]
extern crate uucore;

use std::{
    convert::TryInto,
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screenful"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);
    let mut buff = String::new();
    if let Some(filenames) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = filenames.len();
        for (idx, fname) in filenames.clone().enumerate() {
            let fname = Path::new(fname);
            if fname.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", fname.display());
                return 1;
            }
            if !fname.exists() {
                terminal::disable_raw_mode().unwrap();
                eprintln!(
                    "{}: cannot open {}: No such file or directory",
                    executable!(),
                    fname.display()
                );
                return 1;
            }
            if filenames.len() > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", fname.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(fname).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            let last = idx + 1 == length;
            more(&buff, &mut stdout, last);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, true);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));
    let line_count: u16 = lines.len().try_into().unwrap();

    // Print everything and quit if line count is less than the availale rows
    if line_count < rows {
        println!("{}", buff);
        return;
    }

    let mut upper_mark = 0;
    // Number of lines left
    let mut lines_left = line_count - (upper_mark + rows);
    // Are we on the very last page and the next down arrow will return this function
    let mut to_be_done = false;
    draw(
        &mut upper_mark,
        rows,
        &mut stdout,
        lines.clone(),
        line_count,
    );

    loop {
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    // If this is the last page but some text
                    // is still left that does not require a page of its own
                    // then this event will print the left lines
                    if lines_left < rows && !to_be_done {
                        for l in lines.iter().skip((line_count - upper_mark - rows).into()) {
                            stdout.write_all(format!("\r{}\n", l).as_bytes()).unwrap();
                        }
                        make_prompt_and_flush(&mut stdout, line_count, line_count);
                        // If this is not the last input file
                        // do not return, but the next down arrow must return
                        // because we have printed everyhing
                        if !is_last {
                            to_be_done = true;
                        } else {
                            // Else quit
                            reset_term(&mut stdout);
                            return;
                        }
                        // This handles the next arrow key to quit
                    } else if lines_left < rows && to_be_done {
                        return;
                    } else {
                        // Print a normal page
                        upper_mark = upper_mark.saturating_add(rows.saturating_sub(1));
                        lines_left = line_count.saturating_sub(upper_mark + rows);
                        draw(
                            &mut upper_mark,
                            rows,
                            &mut stdout,
                            lines.clone(),
                            line_count,
                        );
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1));
                    draw(
                        &mut upper_mark,
                        rows,
                        &mut stdout,
                        lines.clone(),
                        line_count,
                    );
                }
                _ => continue,
            }
        }
    }
}

fn draw(
    upper_mark: &mut u16,
    rows: u16,
    mut stdout: &mut std::io::Stdout,
    lines: Vec<String>,
    lc: u16,
) {
    execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc);
    // Reduce the row by 1 for the prompt
    let displayed_lines = lines
        .iter()
        .skip(up_mark.into())
        .take(usize::from(rows.saturating_sub(1)));

    for line in displayed_lines {
        stdout
            .write_all(format!("\r{}\n", line).as_bytes())
            .unwrap();
    }
    make_prompt_and_flush(&mut stdout, lower_mark, lc);
    *upper_mark = up_mark;
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::new();

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(mut line: &str, cols: usize) -> Vec<String> {
    let breaks = (line.len() / cols).saturating_add(1);
    let mut lines = Vec::with_capacity(breaks);
    if line.len() < cols {
        lines.push(line.to_string());
        return lines;
    }

    for _ in 1..=breaks {
        let (line1, line2) = line.split_at(cols);
        lines.push(line1.to_string());
        if line2.len() < cols {
            lines.push(line2.to_string());
            break;
        }
        line = line2;
    }
    lines
}

// Calculate upper_mark based on certain parameters
fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
    let mut lower_mark = upper_mark.saturating_add(rows);

    if lower_mark >= line_count {
        upper_mark = line_count.saturating_sub(rows);
        lower_mark = line_count;
    } else {
        lower_mark = lower_mark.saturating_sub(1)
    }
    (upper_mark, lower_mark)
}

// Make a prompt similar to original more
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) {
    write!(
        stdout,
        "\r{}--More--({}%){}",
        Attribute::Reverse,
        ((lower_mark as f64 / lc as f64) * 100.0).round() as u16,
        Attribute::Reset
    )
    .unwrap();
    stdout.flush().unwrap();
}

#[cfg(test)]
mod tests {
    use super::{break_line, calc_range};

    // It is good to test the above functions
    #[test]
    fn test_calc_range() {
        assert_eq!((0, 24), calc_range(0, 25, 100));
        assert_eq!((50, 74), calc_range(50, 25, 100));
        assert_eq!((75, 100), calc_range(85, 25, 100));
    }
    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(
            (80, 80, 40),
            (lines[0].len(), lines[1].len(), lines[2].len())
        );
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }
}
<commit_msg>more: simplify and fix logic for multiple files<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (ToDO) lflag ICANON tcgetattr tcsetattr TCSADRAIN

#[macro_use]
extern crate uucore;

use std::{
    convert::TryInto,
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(env!("CARGO_PKG_VERSION"))
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screenful"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);
    let mut buff = String::new();
    if let Some(filenames) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = filenames.len();
        for (idx, fname) in filenames.enumerate() {
            let fname = Path::new(fname);
            if fname.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", fname.display());
                return 1;
            }
            if !fname.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!(
                    "cannot open {}: No such file or directory",
                    fname.display()
                );
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", fname.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(fname).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            let is_last = idx + 1 == length;
            more(&buff, &mut stdout, is_last);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, true);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, is_last: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));
    let line_count: u16 = lines.len().try_into().unwrap();

    let mut upper_mark = 0;
    let mut lines_left = line_count.saturating_sub(upper_mark + rows);

    draw(
        &mut upper_mark,
        rows,
        &mut stdout,
        lines.clone(),
        line_count,
    );

    let mut to_be_done = false;
    if lines_left == 0 && is_last {
        if is_last {
            return;
        } else {
            to_be_done = true;
        }
    }

    loop {
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_add(rows.saturating_sub(1));
                    lines_left = line_count.saturating_sub(upper_mark + rows);
                    draw(
                        &mut upper_mark,
                        rows,
                        &mut stdout,
                        lines.clone(),
                        line_count,
                    );

                    if lines_left == 0 {
                        if to_be_done || is_last {
                            return
                        }
                        to_be_done = true;
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    upper_mark = upper_mark.saturating_sub(rows.saturating_sub(1));
                    draw(
                        &mut upper_mark,
                        rows,
                        &mut stdout,
                        lines.clone(),
                        line_count,
                    );
                }
                _ => continue,
            }
        }
    }
}

fn draw(
    upper_mark: &mut u16,
    rows: u16,
    mut stdout: &mut std::io::Stdout,
    lines: Vec<String>,
    lc: u16,
) {
    execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    let (up_mark, lower_mark) = calc_range(*upper_mark, rows, lc);
    // Reduce the row by 1 for the prompt
    let displayed_lines = lines
        .iter()
        .skip(up_mark.into())
        .take(usize::from(rows.saturating_sub(1)));

    for line in displayed_lines {
        stdout
            .write_all(format!("\r{}\n", line).as_bytes())
            .unwrap();
    }
    make_prompt_and_flush(&mut stdout, lower_mark, lc);
    *upper_mark = up_mark;
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::new();

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(mut line: &str, cols: usize) -> Vec<String> {
    let breaks = (line.len() / cols).saturating_add(1);
    let mut lines = Vec::with_capacity(breaks);
    if line.len() < cols {
        lines.push(line.to_string());
        return lines;
    }

    for _ in 1..=breaks {
        let (line1, line2) = line.split_at(cols);
        lines.push(line1.to_string());
        if line2.len() < cols {
            lines.push(line2.to_string());
            break;
        }
        line = line2;
    }
    lines
}

// Calculate upper_mark based on certain parameters
fn calc_range(mut upper_mark: u16, rows: u16, line_count: u16) -> (u16, u16) {
    let mut lower_mark = upper_mark.saturating_add(rows);

    if lower_mark >= line_count {
        upper_mark = line_count.saturating_sub(rows);
        lower_mark = line_count;
    } else {
        lower_mark = lower_mark.saturating_sub(1)
    }
    (upper_mark, lower_mark)
}

// Make a prompt similar to original more
fn make_prompt_and_flush(stdout: &mut Stdout, lower_mark: u16, lc: u16) {
    write!(
        stdout,
        "\r{}--More--({}%){}",
        Attribute::Reverse,
        ((lower_mark as f64 / lc as f64) * 100.0).round() as u16,
        Attribute::Reset
    )
    .unwrap();
    stdout.flush().unwrap();
}

#[cfg(test)]
mod tests {
    use super::{break_line, calc_range};

    // It is good to test the above functions
    #[test]
    fn test_calc_range() {
        assert_eq!((0, 24), calc_range(0, 25, 100));
        assert_eq!((50, 74), calc_range(50, 25, 100));
        assert_eq!((75, 100), calc_range(85, 25, 100));
    }
    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(
            (80, 80, 40),
            (lines[0].len(), lines[1].len(), lines[2].len())
        );
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures)]

extern crate synthrs;

use synthrs::filter::{
    bandpass_filter, bandreject_filter, convolve, cutoff_from_frequency, highpass_filter,
    lowpass_filter,
};
use synthrs::synthesizer::{make_samples, quantize_samples};
use synthrs::wave::sine_wave;
use synthrs::writer::write_wav_file;

fn main() {
    // Lowpass/highpass filter convolution example
    let sample = make_samples(1.0, 44_100, |t: f64| -> f64 {
        0.33 * (sine_wave(6000.0)(t) + sine_wave(700.0)(t) + sine_wave(80.0)(t))
    });

    let lowpass = lowpass_filter(cutoff_from_frequency(400.0, 44_100), 0.01);
    let mut lowpass_samples = quantize_samples::<i16>(&sample);
    lowpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&lowpass, &sample)));
    write_wav_file("out/lowpass.wav", 44_100, &lowpass_samples).expect("failed");

    let highpass = highpass_filter(cutoff_from_frequency(2000.0, 44_100), 0.01);
    let mut highpass_samples = quantize_samples::<i16>(&sample);
    highpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&highpass, &sample)));
    write_wav_file("out/highpass.wav", 44_100, &highpass_samples).expect("failed");

    let bandpass = bandpass_filter(
        cutoff_from_frequency(500.0, 44_100),
        cutoff_from_frequency(3000.0, 44_100),
        0.01,
    );
    let mut bandpass_samples = quantize_samples::<i16>(&sample);
    bandpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandpass, &sample)));
    write_wav_file("out/bandpass.wav", 44_100, &bandpass_samples).expect("failed");

    let bandreject = bandreject_filter(
        cutoff_from_frequency(400.0, 44_100),
        cutoff_from_frequency(2000.0, 44_100),
        0.01,
    );
    let mut bandreject_samples = quantize_samples::<i16>(&sample);
    bandreject_samples
        .extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandreject, &sample)));
    write_wav_file("out/bandreject.wav", 44_100, &bandreject_samples).expect("failed");
}
<commit_msg>Add stateful filter examples<commit_after>#![feature(unboxed_closures)]

extern crate synthrs;

use synthrs::filter::*;
use synthrs::synthesizer::{make_samples, quantize_samples};
use synthrs::wave::sine_wave;
use synthrs::writer::write_wav_file;

fn main() {
    // Lowpass/highpass filter convolution example
    let sample = make_samples(1.0, 44_100, |t: f64| -> f64 {
        0.33 * (sine_wave(6000.0)(t) + sine_wave(700.0)(t) + sine_wave(80.0)(t))
    });

    let lowpass = lowpass_filter(cutoff_from_frequency(400.0, 44_100), 0.01);
    let mut lowpass_samples = quantize_samples::<i16>(&sample);
    lowpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&lowpass, &sample)));
    write_wav_file("out/lowpass.wav", 44_100, &lowpass_samples).expect("failed");

    let highpass = highpass_filter(cutoff_from_frequency(2000.0, 44_100), 0.01);
    let mut highpass_samples = quantize_samples::<i16>(&sample);
    highpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&highpass, &sample)));
    write_wav_file("out/highpass.wav", 44_100, &highpass_samples).expect("failed");

    let bandpass = bandpass_filter(
        cutoff_from_frequency(500.0, 44_100),
        cutoff_from_frequency(3000.0, 44_100),
        0.01,
    );
    let mut bandpass_samples = quantize_samples::<i16>(&sample);
    bandpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandpass, &sample)));
    write_wav_file("out/bandpass.wav", 44_100, &bandpass_samples).expect("failed");

    let bandreject = bandreject_filter(
        cutoff_from_frequency(400.0, 44_100),
        cutoff_from_frequency(2000.0, 44_100),
        0.01,
    );
    let mut bandreject_samples = quantize_samples::<i16>(&sample);
    bandreject_samples
        .extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandreject, &sample)));
    write_wav_file("out/bandreject.wav", 44_100, &bandreject_samples).expect("failed");

    // Stateful filters
    let mut comb = Comb::new(0.2, 44_100, 0.5, 0.5, 0.5);
    let comb_samples: Vec<f64> = sample.clone().into_iter().map(|s| comb.tick(s)).collect();
    write_wav_file(
        "out/comb.wav",
        44_100,
        &*quantize_samples::<i16>(comb_samples.as_slice()),
    ).expect("failed");

    let mut allpass = AllPass::new(1.0, 44_100, 0.5);
    let allpass_samples: Vec<f64> = sample
        .clone()
        .into_iter()
        .map(|s| allpass.tick(s))
        .collect();
    write_wav_file(
        "out/allpass.wav",
        44_100,
        &*quantize_samples::<i16>(allpass_samples.as_slice()),
    ).expect("failed");
}
<|endoftext|>


<commit_before>extern crate drm;
extern crate smithay;
#[macro_use]
extern crate slog;
extern crate slog_term;

use drm::{buffer::PixelFormat, control::dumbbuffer::DumbBuffer};
use slog::Drain;
use smithay::{
    backend::drm::{
        device_bind,
        legacy::{error::Error, LegacyDrmDevice, LegacyDrmSurface},
        Device, DeviceHandler, RawSurface, Surface,
    },
    reexports::{
        drm::control::{
            connector::{self, State as ConnectorState},
            crtc, encoder, framebuffer, Device as ControlDevice, ResourceInfo,
        },
        wayland_server::calloop::EventLoop,
    },
};
use std::{
    fs::{File, OpenOptions},
    io::Error as IoError,
    rc::Rc,
    sync::Mutex,
};

fn main() {
    let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!());

    /*
     * Initialize the drm backend
     */

    // "Find" a suitable drm device
    let mut options = OpenOptions::new();
    options.read(true);
    options.write(true);
    let mut device = LegacyDrmDevice::new(options.open("/dev/dri/card0").unwrap(), log.clone()).unwrap();

    // Get a set of all modesetting resource handles (excluding planes):
    let res_handles = Device::resource_handles(&device).unwrap();

    // Use first connected connector
    let connector_info = res_handles
        .connectors()
        .iter()
        .map(|conn| Device::resource_info::<connector::Info>(&device, *conn).unwrap())
        .find(|conn| conn.connection_state() == ConnectorState::Connected)
        .unwrap();

    // Use the first encoder
    let encoder_info = Device::resource_info::<encoder::Info>(&device, connector_info.encoders()[0]).unwrap();

    // use the connected crtc if any
    let crtc = encoder_info
        .current_crtc()
        // or use the first one that is compatible with the encoder
        .unwrap_or_else(|| {
            *res_handles
                .filter_crtcs(encoder_info.possible_crtcs())
                .iter()
                .next()
                .unwrap()
        });

    // Assuming we found a good connector and loaded the info into `connector_info`
    let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)

    // Initialize the hardware backend
    let surface = Rc::new(device.create_surface(crtc).unwrap());

    surface.use_mode(Some(mode)).unwrap();
    for conn in surface.current_connectors().into_iter() {
        if conn != connector_info.handle() {
            surface.remove_connector(conn).unwrap();
        }
    }
    surface.add_connector(connector_info.handle()).unwrap();

    /*
     * Lets create buffers and framebuffers.
     * We use drm-rs DumbBuffers, because they always work and require little to no setup.
     * But they are very slow, this is just for demonstration purposes.
     */
    let (w, h) = mode.size();
    let front_buffer =
        DumbBuffer::create_from_device(&device, (w as u32, h as u32), PixelFormat::XRGB8888).unwrap();
    let front_framebuffer = device.create_framebuffer(&front_buffer).unwrap();
    let back_buffer =
        DumbBuffer::create_from_device(&device, (w as u32, h as u32), PixelFormat::XRGB8888).unwrap();
    let back_framebuffer = device.create_framebuffer(&back_buffer).unwrap();

    device.set_handler(DrmHandlerImpl {
        current: front_framebuffer.handle(),
        front: (front_buffer, front_framebuffer.clone()),
        back: (back_buffer, back_framebuffer),
        surface: surface.clone(),
    });

    /*
     * Register the DrmDevice on the EventLoop
     */
    let mut event_loop = EventLoop::<()>::new().unwrap();
    let _source = device_bind(&event_loop.handle(), device)
        .map_err(|err| -> IoError { err.into() })
        .unwrap();

    // Start rendering
    if surface.commit_pending() {
        surface.commit(front_framebuffer.handle()).unwrap();
    }
    surface.page_flip(front_framebuffer.handle()).unwrap();

    // Run
    event_loop.run(None, &mut (), |_| {}).unwrap();
}

pub struct DrmHandlerImpl {
    front: (DumbBuffer, framebuffer::Info),
    back: (DumbBuffer, framebuffer::Info),
    current: framebuffer::Handle,
    surface: Rc<LegacyDrmSurface<File>>,
}

impl DeviceHandler for DrmHandlerImpl {
    type Device = LegacyDrmDevice<File>;

    fn vblank(&mut self, _crtc: crtc::Handle) {
        {
            // Swap and map buffer
            let mut mapping = if self.current == self.front.1.handle() {
                self.current = self.back.1.handle();
                self.back.0.map(&*self.surface).unwrap()
            } else {
                self.current = self.front.1.handle();
                self.front.0.map(&*self.surface).unwrap()
            };

            // now we could render to the mapping via software rendering.
            // this example just sets some grey color

            for mut x in mapping.as_mut() {
                *x = 128;
            }
        }
        self.surface.page_flip(self.current).unwrap();
    }

    fn error(&mut self, error: Error) {
        panic!("{:?}", error);
    }
}
<commit_msg>Fix example crates<commit_after>extern crate smithay;
#[macro_use]
extern crate slog;
extern crate slog_term;

use slog::Drain;
use smithay::{
    backend::drm::{
        device_bind,
        legacy::{error::Error, LegacyDrmDevice, LegacyDrmSurface},
        Device, DeviceHandler, RawSurface, Surface,
    },
    reexports::{
        drm::{
            buffer::PixelFormat,
            control::{
                connector::{self, State as ConnectorState},
                crtc,
                dumbbuffer::DumbBuffer,
                encoder, framebuffer, Device as ControlDevice, ResourceInfo,
            },
        },
        wayland_server::calloop::EventLoop,
    },
};
use std::{
    fs::{File, OpenOptions},
    io::Error as IoError,
    rc::Rc,
    sync::Mutex,
};

fn main() {
    let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!());

    /*
     * Initialize the drm backend
     */

    // "Find" a suitable drm device
    let mut options = OpenOptions::new();
    options.read(true);
    options.write(true);
    let mut device = LegacyDrmDevice::new(options.open("/dev/dri/card0").unwrap(), log.clone()).unwrap();

    // Get a set of all modesetting resource handles (excluding planes):
    let res_handles = Device::resource_handles(&device).unwrap();

    // Use first connected connector
    let connector_info = res_handles
        .connectors()
        .iter()
        .map(|conn| Device::resource_info::<connector::Info>(&device, *conn).unwrap())
        .find(|conn| conn.connection_state() == ConnectorState::Connected)
        .unwrap();

    // Use the first encoder
    let encoder_info = Device::resource_info::<encoder::Info>(&device, connector_info.encoders()[0]).unwrap();

    // use the connected crtc if any
    let crtc = encoder_info
        .current_crtc()
        // or use the first one that is compatible with the encoder
        .unwrap_or_else(|| {
            *res_handles
                .filter_crtcs(encoder_info.possible_crtcs())
                .iter()
                .next()
                .unwrap()
        });

    // Assuming we found a good connector and loaded the info into `connector_info`
    let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)

    // Initialize the hardware backend
    let surface = Rc::new(device.create_surface(crtc).unwrap());

    surface.use_mode(Some(mode)).unwrap();
    for conn in surface.current_connectors().into_iter() {
        if conn != connector_info.handle() {
            surface.remove_connector(conn).unwrap();
        }
    }
    surface.add_connector(connector_info.handle()).unwrap();

    /*
     * Lets create buffers and framebuffers.
     * We use drm-rs DumbBuffers, because they always work and require little to no setup.
     * But they are very slow, this is just for demonstration purposes.
     */
    let (w, h) = mode.size();
    let front_buffer =
        DumbBuffer::create_from_device(&device, (w as u32, h as u32), PixelFormat::XRGB8888).unwrap();
    let front_framebuffer = device.create_framebuffer(&front_buffer).unwrap();
    let back_buffer =
        DumbBuffer::create_from_device(&device, (w as u32, h as u32), PixelFormat::XRGB8888).unwrap();
    let back_framebuffer = device.create_framebuffer(&back_buffer).unwrap();

    device.set_handler(DrmHandlerImpl {
        current: front_framebuffer.handle(),
        front: (front_buffer, front_framebuffer.clone()),
        back: (back_buffer, back_framebuffer),
        surface: surface.clone(),
    });

    /*
     * Register the DrmDevice on the EventLoop
     */
    let mut event_loop = EventLoop::<()>::new().unwrap();
    let _source = device_bind(&event_loop.handle(), device)
        .map_err(|err| -> IoError { err.into() })
        .unwrap();

    // Start rendering
    if surface.commit_pending() {
        surface.commit(front_framebuffer.handle()).unwrap();
    }
    surface.page_flip(front_framebuffer.handle()).unwrap();

    // Run
    event_loop.run(None, &mut (), |_| {}).unwrap();
}

pub struct DrmHandlerImpl {
    front: (DumbBuffer, framebuffer::Info),
    back: (DumbBuffer, framebuffer::Info),
    current: framebuffer::Handle,
    surface: Rc<LegacyDrmSurface<File>>,
}

impl DeviceHandler for DrmHandlerImpl {
    type Device = LegacyDrmDevice<File>;

    fn vblank(&mut self, _crtc: crtc::Handle) {
        {
            // Swap and map buffer
            let mut mapping = if self.current == self.front.1.handle() {
                self.current = self.back.1.handle();
                self.back.0.map(&*self.surface).unwrap()
            } else {
                self.current = self.front.1.handle();
                self.front.0.map(&*self.surface).unwrap()
            };

            // now we could render to the mapping via software rendering.
            // this example just sets some grey color

            for mut x in mapping.as_mut() {
                *x = 128;
            }
        }
        self.surface.page_flip(self.current).unwrap();
    }

    fn error(&mut self, error: Error) {
        panic!("{:?}", error);
    }
}
<|endoftext|>


<commit_before><commit_msg>chore: Remove this test, we have others for it<commit_after><|endoftext|>


<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::{
    cmp,
    fs::OpenOptions,
    panic,
    path::{Path, PathBuf},
};

use either::Either;
use serde_json::{from_reader, Value};
use uuid::Uuid;

use devicemapper::{
    devnode_to_devno, Bytes, Device, DmDevice, DmName, LinearDev, LinearDevTargetParams,
    LinearTargetParams, Sectors, TargetLine, IEC,
};

use crate::engine::strat_engine::{
    device::blkdev_size,
    dm::get_dm,
    tests::{logger::init_logger, util::clean_up},
    writing::wipe_sectors,
};

pub struct RealTestDev {
    dev: Either<PathBuf, LinearDev>,
}

impl RealTestDev {
    /// Construct a new test device for the given path.
    /// Wipe initial MiB to clear metadata.
    pub fn new(dev: Either<PathBuf, LinearDev>) -> RealTestDev {
        let test_dev = RealTestDev { dev };
        wipe_sectors(
            test_dev.as_path(),
            Sectors(0),
            Bytes::from(IEC::Mi).sectors(),
        )
        .unwrap();
        test_dev
    }

    /// Get the device node of the device.
    fn as_path(&self) -> PathBuf {
        self.dev.as_ref().either(|p| p.clone(), |l| l.devnode())
    }

    /// Teardown a real test dev
    fn teardown(self) {
        wipe_sectors(&self.as_path(), Sectors(0), Bytes::from(IEC::Mi).sectors()).unwrap();
        if let Some(mut ld) = self.dev.right() {
            ld.teardown(get_dm()).unwrap();
        }
    }
}

/// Shorthand for specifying devices to use for tests
/// For the minimum size, if None is given, the default 1 GiB is chosen.
pub enum DeviceLimits {
    /// Use exactly the number of devices specified
    /// The second argument is the minimum size for all devices.
    /// The third argument is the maximum size for all devices.
    Exactly(usize, Option<Sectors>, Option<Sectors>),
    /// Use at least the number of devices specified, but if there are more
    /// devices available, also use the maximum number of devices.
    /// The second argument is the minimum size for all devices.
    /// The third argument is the maximum size for all devices.
    AtLeast(usize, Option<Sectors>, Option<Sectors>),
    /// Use exactly the number of devices specified in the first argument,
    /// and the minimum of the number of devices specified and the number
    /// of devices available in the second argument.
    /// The third argument is the minimum size for all devices.
    /// The fourth argument is the maximum size for all devices.
    #[allow(dead_code)]
    Range(usize, usize, Option<Sectors>, Option<Sectors>),
}

/// Get a list of lists of devices to use for tests.
/// May return an empty list if the request is not satisfiable.
#[allow(clippy::type_complexity)]
fn get_device_runs<'a>(
    limits: &DeviceLimits,
    dev_sizes: &[(&'a Path, Sectors)],
) -> Vec<Vec<(&'a Path, Option<(Sectors, Sectors)>)>> {
    let (lower, maybe_upper, min_size, max_size) = match limits {
        DeviceLimits::Exactly(num, min_size, max_size) => (*num, Some(*num), *min_size, *max_size),
        DeviceLimits::AtLeast(num, min_size, max_size) => (*num, None, *min_size, *max_size),
        DeviceLimits::Range(lower, upper, min_size, max_size) => {
            assert!(lower < upper);
            (*lower, Some(*upper), *min_size, *max_size)
        }
    };

    let min_size = min_size.unwrap_or_else(|| Bytes::from(IEC::Gi).sectors());

    assert!(max_size.is_none() || Some(min_size) <= max_size);

    // Retain only devices that are larger than min_size
    let (matches, _): (Vec<(&Path, Sectors)>, Vec<(&Path, Sectors)>) =
        dev_sizes.iter().partition(|&&(_, s)| s >= min_size);

    // If there is not a sufficient number of devices large enough to match
    // the lower bound, return an empty vec. TODO: It would be possible to try
    // harder, by merging several devices into a single linear device. If this
    // turns out to be necessary, this is the correct place to do it.
    if lower > matches.len() {
        return vec![];
    }

    // Retain only devices that are less than max_size
    let (mut matches, mut too_large): (Vec<(&Path, Sectors)>, Vec<(&Path, Sectors)>) =
        if let Some(max_size) = max_size {
            matches.iter().partition(|&&(_, s)| s <= max_size)
        } else {
            (matches, vec![])
        };

    let avail = matches.len();
    let needed = maybe_upper.unwrap_or(lower);
    let must_generate = if avail > needed { 0 } else { needed - avail };
    let avail_specs = {
        let mut avail_specs = vec![];
        while avail_specs.len() < must_generate && (!too_large.is_empty() || !matches.is_empty()) {
            let (path, size) = if too_large.is_empty() {
                matches.pop().expect("!matches.is_empty()")
            } else {
                too_large.pop().expect("!too_large.is_empty()")
            };
            let mut new_pairs = vec![];
            for i in 0..size / min_size {
                new_pairs.push((path, Some((i * min_size, min_size))))
            }
            avail_specs.extend(new_pairs);
        }
        avail_specs.extend(matches.iter().map(|&(p, _)| (p, None)));
        avail_specs
    };

    let avail = avail_specs.len();

    // If there is not a sufficient number of devices small enough to match
    // the lower bound, return an empty vec.
    if lower > avail {
        return vec![];
    }

    let mut device_lists = vec![avail_specs.iter().take(lower).cloned().collect::<Vec<_>>()];

    if lower != avail {
        match maybe_upper {
            None => device_lists.push(avail_specs.iter().take(avail).cloned().collect::<Vec<_>>()),
            Some(upper) => {
                if lower < upper {
                    device_lists.push(
                        avail_specs
                            .iter()
                            .take(cmp::min(upper, avail))
                            .cloned()
                            .collect::<Vec<_>>(),
                    )
                }
            }
        }
    }

    device_lists
}

/// Make a new LinearDev according to specs
fn make_linear_test_dev(devnode: &Path, start: Sectors, length: Sectors) -> LinearDev {
    let params = LinearTargetParams::new(
        Device::from(devnode_to_devno(devnode).unwrap().unwrap()),
        start,
    );
    let table = vec![TargetLine::new(
        Sectors(0),
        length,
        LinearDevTargetParams::Linear(params),
    )];
    LinearDev::setup(
        get_dm(),
        DmName::new(&format!("stratis_test_{}", Uuid::new_v4())).expect("valid format"),
        None,
        table,
    )
    .unwrap()
}

/// Run test on real devices, using given constraints. Constraints may result
/// in multiple invocations of the test, with differing numbers of block
/// devices.
pub fn test_with_spec<F>(limits: &DeviceLimits, test: F)
where
    F: Fn(&[&Path]) + panic::RefUnwindSafe,
{
    let file = OpenOptions::new()
        .read(true)
        .open("tests/test_config.json")
        .unwrap();
    let config: Value = from_reader(&file).unwrap();
    let devpaths: Vec<_> = config
        .get("ok_to_destroy_dev_array_key")
        .unwrap()
        .as_array()
        .unwrap()
        .iter()
        .map(|x| Path::new(x.as_str().unwrap()))
        .collect();

    let dev_sizes: Vec<(&Path, Sectors)> = devpaths
        .iter()
        .map(|p| {
            (
                *p,
                blkdev_size(&OpenOptions::new().read(true).open(p).unwrap())
                    .unwrap()
                    .sectors(),
            )
        })
        .collect();

    let runs = get_device_runs(limits, &dev_sizes);

    assert!(!runs.is_empty());

    init_logger();

    for run_paths in runs {
        clean_up().unwrap();

        let mut devices: Vec<_> = run_paths
            .iter()
            .map(|&(path, spec)| match spec {
                Some((start, length)) => {
                    RealTestDev::new(Either::Right(make_linear_test_dev(path, start, length)))
                }
                None => RealTestDev::new(Either::Left(path.to_path_buf())),
            })
            .collect();

        let paths: Vec<PathBuf> = devices.iter().map(|x| x.as_path()).collect();
        let paths: Vec<&Path> = paths.iter().map(|x| x.as_path()).collect();
        let result = panic::catch_unwind(|| test(&paths));
        let tear_down = clean_up();

        result.unwrap();
        tear_down.unwrap();

        for dev in devices.drain(..) {
            dev.teardown();
        }
    }
}
<commit_msg>Remove obsolete dead code allow<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::{
    cmp,
    fs::OpenOptions,
    panic,
    path::{Path, PathBuf},
};

use either::Either;
use serde_json::{from_reader, Value};
use uuid::Uuid;

use devicemapper::{
    devnode_to_devno, Bytes, Device, DmDevice, DmName, LinearDev, LinearDevTargetParams,
    LinearTargetParams, Sectors, TargetLine, IEC,
};

use crate::engine::strat_engine::{
    device::blkdev_size,
    dm::get_dm,
    tests::{logger::init_logger, util::clean_up},
    writing::wipe_sectors,
};

pub struct RealTestDev {
    dev: Either<PathBuf, LinearDev>,
}

impl RealTestDev {
    /// Construct a new test device for the given path.
    /// Wipe initial MiB to clear metadata.
    pub fn new(dev: Either<PathBuf, LinearDev>) -> RealTestDev {
        let test_dev = RealTestDev { dev };
        wipe_sectors(
            test_dev.as_path(),
            Sectors(0),
            Bytes::from(IEC::Mi).sectors(),
        )
        .unwrap();
        test_dev
    }

    /// Get the device node of the device.
    fn as_path(&self) -> PathBuf {
        self.dev.as_ref().either(|p| p.clone(), |l| l.devnode())
    }

    /// Teardown a real test dev
    fn teardown(self) {
        wipe_sectors(&self.as_path(), Sectors(0), Bytes::from(IEC::Mi).sectors()).unwrap();
        if let Some(mut ld) = self.dev.right() {
            ld.teardown(get_dm()).unwrap();
        }
    }
}

/// Shorthand for specifying devices to use for tests
/// For the minimum size, if None is given, the default 1 GiB is chosen.
pub enum DeviceLimits {
    /// Use exactly the number of devices specified
    /// The second argument is the minimum size for all devices.
    /// The third argument is the maximum size for all devices.
    Exactly(usize, Option<Sectors>, Option<Sectors>),
    /// Use at least the number of devices specified, but if there are more
    /// devices available, also use the maximum number of devices.
    /// The second argument is the minimum size for all devices.
    /// The third argument is the maximum size for all devices.
    AtLeast(usize, Option<Sectors>, Option<Sectors>),
    /// Use exactly the number of devices specified in the first argument,
    /// and the minimum of the number of devices specified and the number
    /// of devices available in the second argument.
    /// The third argument is the minimum size for all devices.
    /// The fourth argument is the maximum size for all devices.
    Range(usize, usize, Option<Sectors>, Option<Sectors>),
}

/// Get a list of lists of devices to use for tests.
/// May return an empty list if the request is not satisfiable.
#[allow(clippy::type_complexity)]
fn get_device_runs<'a>(
    limits: &DeviceLimits,
    dev_sizes: &[(&'a Path, Sectors)],
) -> Vec<Vec<(&'a Path, Option<(Sectors, Sectors)>)>> {
    let (lower, maybe_upper, min_size, max_size) = match limits {
        DeviceLimits::Exactly(num, min_size, max_size) => (*num, Some(*num), *min_size, *max_size),
        DeviceLimits::AtLeast(num, min_size, max_size) => (*num, None, *min_size, *max_size),
        DeviceLimits::Range(lower, upper, min_size, max_size) => {
            assert!(lower < upper);
            (*lower, Some(*upper), *min_size, *max_size)
        }
    };

    let min_size = min_size.unwrap_or_else(|| Bytes::from(IEC::Gi).sectors());

    assert!(max_size.is_none() || Some(min_size) <= max_size);

    // Retain only devices that are larger than min_size
    let (matches, _): (Vec<(&Path, Sectors)>, Vec<(&Path, Sectors)>) =
        dev_sizes.iter().partition(|&&(_, s)| s >= min_size);

    // If there is not a sufficient number of devices large enough to match
    // the lower bound, return an empty vec. TODO: It would be possible to try
    // harder, by merging several devices into a single linear device. If this
    // turns out to be necessary, this is the correct place to do it.
    if lower > matches.len() {
        return vec![];
    }

    // Retain only devices that are less than max_size
    let (mut matches, mut too_large): (Vec<(&Path, Sectors)>, Vec<(&Path, Sectors)>) =
        if let Some(max_size) = max_size {
            matches.iter().partition(|&&(_, s)| s <= max_size)
        } else {
            (matches, vec![])
        };

    let avail = matches.len();
    let needed = maybe_upper.unwrap_or(lower);
    let must_generate = if avail > needed { 0 } else { needed - avail };
    let avail_specs = {
        let mut avail_specs = vec![];
        while avail_specs.len() < must_generate && (!too_large.is_empty() || !matches.is_empty()) {
            let (path, size) = if too_large.is_empty() {
                matches.pop().expect("!matches.is_empty()")
            } else {
                too_large.pop().expect("!too_large.is_empty()")
            };
            let mut new_pairs = vec![];
            for i in 0..size / min_size {
                new_pairs.push((path, Some((i * min_size, min_size))))
            }
            avail_specs.extend(new_pairs);
        }
        avail_specs.extend(matches.iter().map(|&(p, _)| (p, None)));
        avail_specs
    };

    let avail = avail_specs.len();

    // If there is not a sufficient number of devices small enough to match
    // the lower bound, return an empty vec.
    if lower > avail {
        return vec![];
    }

    let mut device_lists = vec![avail_specs.iter().take(lower).cloned().collect::<Vec<_>>()];

    if lower != avail {
        match maybe_upper {
            None => device_lists.push(avail_specs.iter().take(avail).cloned().collect::<Vec<_>>()),
            Some(upper) => {
                if lower < upper {
                    device_lists.push(
                        avail_specs
                            .iter()
                            .take(cmp::min(upper, avail))
                            .cloned()
                            .collect::<Vec<_>>(),
                    )
                }
            }
        }
    }

    device_lists
}

/// Make a new LinearDev according to specs
fn make_linear_test_dev(devnode: &Path, start: Sectors, length: Sectors) -> LinearDev {
    let params = LinearTargetParams::new(
        Device::from(devnode_to_devno(devnode).unwrap().unwrap()),
        start,
    );
    let table = vec![TargetLine::new(
        Sectors(0),
        length,
        LinearDevTargetParams::Linear(params),
    )];
    LinearDev::setup(
        get_dm(),
        DmName::new(&format!("stratis_test_{}", Uuid::new_v4())).expect("valid format"),
        None,
        table,
    )
    .unwrap()
}

/// Run test on real devices, using given constraints. Constraints may result
/// in multiple invocations of the test, with differing numbers of block
/// devices.
pub fn test_with_spec<F>(limits: &DeviceLimits, test: F)
where
    F: Fn(&[&Path]) + panic::RefUnwindSafe,
{
    let file = OpenOptions::new()
        .read(true)
        .open("tests/test_config.json")
        .unwrap();
    let config: Value = from_reader(&file).unwrap();
    let devpaths: Vec<_> = config
        .get("ok_to_destroy_dev_array_key")
        .unwrap()
        .as_array()
        .unwrap()
        .iter()
        .map(|x| Path::new(x.as_str().unwrap()))
        .collect();

    let dev_sizes: Vec<(&Path, Sectors)> = devpaths
        .iter()
        .map(|p| {
            (
                *p,
                blkdev_size(&OpenOptions::new().read(true).open(p).unwrap())
                    .unwrap()
                    .sectors(),
            )
        })
        .collect();

    let runs = get_device_runs(limits, &dev_sizes);

    assert!(!runs.is_empty());

    init_logger();

    for run_paths in runs {
        clean_up().unwrap();

        let mut devices: Vec<_> = run_paths
            .iter()
            .map(|&(path, spec)| match spec {
                Some((start, length)) => {
                    RealTestDev::new(Either::Right(make_linear_test_dev(path, start, length)))
                }
                None => RealTestDev::new(Either::Left(path.to_path_buf())),
            })
            .collect();

        let paths: Vec<PathBuf> = devices.iter().map(|x| x.as_path()).collect();
        let paths: Vec<&Path> = paths.iter().map(|x| x.as_path()).collect();
        let result = panic::catch_unwind(|| test(&paths));
        let tear_down = clean_up();

        result.unwrap();
        tear_down.unwrap();

        for dev in devices.drain(..) {
            dev.teardown();
        }
    }
}
<|endoftext|>


<commit_before>//! A suite of non-cryptographic hash functions for Rust.
//!
//! # Example
//!
//! ```rust
//! use std::hash::{Hash, Hasher};
//!
//! use fasthash::{metro, MetroHasher};
//!
//! fn hash<T: Hash>(t: &T) -> u64 {
//!     let mut s: MetroHasher = Default::default();
//!     t.hash(&mut s);
//!     s.finish()
//! }
//!
//! let h = metro::hash64(b"hello world\xff");
//!
//! assert_eq!(h, hash(&"hello world"));
//! ```
//!
//! By default, `HashMap` uses a hashing algorithm selected to
//! provide resistance against `HashDoS` attacks.
//! The hashing algorithm can be replaced on a per-`HashMap` basis
//! using the `HashMap::with_hasher` or
//! `HashMap::with_capacity_and_hasher` methods.
//!
//! It also cowork with `HashMap` or `HashSet`, act as a hash function
//!
//! ```rust
//! use std::collections::HashSet;
//!
//! use fasthash::spooky::Hash128;
//!
//! let mut set = HashSet::with_hasher(Hash128);
//! set.insert(2);
//! ```
//!
//! Or use `RandomState<CityHash64>` with a random seed.
//!
//! ```rust
//! use std::collections::HashMap;
//!
//! use fasthash::{city, RandomState};
//!
//! let s = RandomState::<city::Hash64>::new();
//! let mut map = HashMap::with_hasher(s);
//!
//! assert_eq!(map.insert(37, "a"), None);
//! assert_eq!(map.is_empty(), false);
//!
//! map.insert(37, "b");
//! assert_eq!(map.insert(37, "c"), Some("b"));
//! assert_eq!(map[&37], "c");
//! ```
#![warn(missing_docs)]

#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate lazy_static;
extern crate fasthash_sys as ffi;

#[cfg(feature = "digest")]
pub extern crate digest;

#[macro_use]
mod hasher;
pub mod city;
pub mod farm;
pub mod highway;
pub mod lookup3;
pub mod metro;
pub mod mum;
pub mod murmur;
pub mod murmur2;
pub mod murmur3;
pub mod sea;
pub mod spooky;
pub mod t1ha;
pub mod xx;
pub mod xxh3;

pub use crate::hasher::{
    BufHasher, FastHash, FastHasher, Fingerprint, HasherExt, Output, RandomState, Seed,
    StreamHasher,
};

pub use crate::farm::{Hasher128 as FarmHasherExt, Hasher64 as FarmHasher};
pub use crate::lookup3::Hasher32 as Lookup3Hasher;
pub use crate::mum::Hasher64 as MumHasher;
pub use crate::murmur::Hasher32 as MurmurHasher;
pub use crate::murmur3::Hasher32 as Murmur3Hasher;
#[doc(no_inline)]
pub use crate::sea::Hasher64 as SeaHasher;
pub use crate::spooky::{Hasher128 as SpookyHasherExt, Hasher64 as SpookyHasher};
pub use crate::t1ha::{t1ha0, t1ha1, t1ha2};
pub use crate::t1ha2::{Hasher128 as T1haHasherExt, Hasher128 as T1haHasher};
pub use crate::xx::Hasher64 as XXHasher;
cfg_if! {
    if #[cfg(target_pointer_width = "64")] {
        pub use crate::murmur2::Hasher64_x64 as Murmur2Hasher;
        pub use crate::murmur3::Hasher128_x64 as Murmur3HasherExt;
    } else {
        pub use murmur2::Hasher64_x86 as Murmur2Hasher;
        pub use murmur3::Hasher128_x86 as Murmur3HasherExt;
    }
}
cfg_if! {
    if #[cfg(any(feature = "sse42", target_feature = "sse4.2"))] {
        pub use crate::city::{Hasher64 as CityHasher, crc::Hasher128 as CityHasherExt};
        pub use crate::metro::{crc::Hasher128_1 as MetroHasherExt, crc::Hasher64_1 as MetroHasher};
    } else {
        pub use city::{Hasher128 as CityHasherExt, Hasher64 as CityHasher};
        pub use metro::{Hasher128_1 as MetroHasherExt, Hasher64_1 as MetroHasher};
    }
}
<commit_msg>only define Output when digest feature enabled<commit_after>//! A suite of non-cryptographic hash functions for Rust.
//!
//! # Example
//!
//! ```rust
//! use std::hash::{Hash, Hasher};
//!
//! use fasthash::{metro, MetroHasher};
//!
//! fn hash<T: Hash>(t: &T) -> u64 {
//!     let mut s: MetroHasher = Default::default();
//!     t.hash(&mut s);
//!     s.finish()
//! }
//!
//! let h = metro::hash64(b"hello world\xff");
//!
//! assert_eq!(h, hash(&"hello world"));
//! ```
//!
//! By default, `HashMap` uses a hashing algorithm selected to
//! provide resistance against `HashDoS` attacks.
//! The hashing algorithm can be replaced on a per-`HashMap` basis
//! using the `HashMap::with_hasher` or
//! `HashMap::with_capacity_and_hasher` methods.
//!
//! It also cowork with `HashMap` or `HashSet`, act as a hash function
//!
//! ```rust
//! use std::collections::HashSet;
//!
//! use fasthash::spooky::Hash128;
//!
//! let mut set = HashSet::with_hasher(Hash128);
//! set.insert(2);
//! ```
//!
//! Or use `RandomState<CityHash64>` with a random seed.
//!
//! ```rust
//! use std::collections::HashMap;
//!
//! use fasthash::{city, RandomState};
//!
//! let s = RandomState::<city::Hash64>::new();
//! let mut map = HashMap::with_hasher(s);
//!
//! assert_eq!(map.insert(37, "a"), None);
//! assert_eq!(map.is_empty(), false);
//!
//! map.insert(37, "b");
//! assert_eq!(map.insert(37, "c"), Some("b"));
//! assert_eq!(map[&37], "c");
//! ```
#![warn(missing_docs)]

#[macro_use]
extern crate cfg_if;
#[macro_use]
extern crate lazy_static;
extern crate fasthash_sys as ffi;

cfg_if! {
    if #[cfg(feature = "digest")] {
        pub extern crate digest;

        pub use crate::hasher::Output;
    }
}

#[macro_use]
mod hasher;
pub mod city;
pub mod farm;
pub mod highway;
pub mod lookup3;
pub mod metro;
pub mod mum;
pub mod murmur;
pub mod murmur2;
pub mod murmur3;
pub mod sea;
pub mod spooky;
pub mod t1ha;
pub mod xx;
pub mod xxh3;

pub use crate::hasher::{
    BufHasher, FastHash, FastHasher, Fingerprint, HasherExt, RandomState, Seed, StreamHasher,
};

pub use crate::farm::{Hasher128 as FarmHasherExt, Hasher64 as FarmHasher};
pub use crate::lookup3::Hasher32 as Lookup3Hasher;
pub use crate::mum::Hasher64 as MumHasher;
pub use crate::murmur::Hasher32 as MurmurHasher;
pub use crate::murmur3::Hasher32 as Murmur3Hasher;
#[doc(no_inline)]
pub use crate::sea::Hasher64 as SeaHasher;
pub use crate::spooky::{Hasher128 as SpookyHasherExt, Hasher64 as SpookyHasher};
pub use crate::t1ha::{t1ha0, t1ha1, t1ha2};
pub use crate::t1ha2::{Hasher128 as T1haHasherExt, Hasher128 as T1haHasher};
pub use crate::xx::Hasher64 as XXHasher;
cfg_if! {
    if #[cfg(target_pointer_width = "64")] {
        pub use crate::murmur2::Hasher64_x64 as Murmur2Hasher;
        pub use crate::murmur3::Hasher128_x64 as Murmur3HasherExt;
    } else {
        pub use murmur2::Hasher64_x86 as Murmur2Hasher;
        pub use murmur3::Hasher128_x86 as Murmur3HasherExt;
    }
}
cfg_if! {
    if #[cfg(any(feature = "sse42", target_feature = "sse4.2"))] {
        pub use crate::city::{Hasher64 as CityHasher, crc::Hasher128 as CityHasherExt};
        pub use crate::metro::{crc::Hasher128_1 as MetroHasherExt, crc::Hasher64_1 as MetroHasher};
    } else {
        pub use city::{Hasher128 as CityHasherExt, Hasher64 as CityHasher};
        pub use metro::{Hasher128_1 as MetroHasherExt, Hasher64_1 as MetroHasher};
    }
}
<|endoftext|>


<commit_before>use std::iter::repeat;
use integer::Integer;
use traits::{Zero, One};

use biguint::BigUint;

struct MontyReducer<'a> {
    p: &'a BigUint,
    n: Vec<u32>,
    n0inv: u32
}

// Calculate the modular inverse of `num`, using Extended GCD.
//
// Reference:
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 1.20
fn inv_mod_u32(num: u32) -> u32 {
    // num needs to be relatively prime to 2**32 -- i.e. it must be odd.
    assert!(num % 2 != 0);

    let mut a: i64 = num as i64;
    let mut b: i64 = (u32::max_value() as i64) + 1;

    // ExtendedGcd
    // Input: positive integers a and b
    // Output: integers (g, u, v) such that g = gcd(a, b) = ua + vb
    // As we don't need v for modular inverse, we don't calculate it.

    // 1: (u, w) <- (1, 0)
    let mut u = 1;
    let mut w = 0;
    // 3: while b != 0
    while b != 0 {
        // 4: (q, r) <- DivRem(a, b)
        let q = a / b;
        let r = a % b;
        // 5: (a, b) <- (b, r)
        a = b; b = r;
        // 6: (u, w) <- (w, u - qw)
        let m = u - w*q;
        u = w; w = m;
    }

    assert!(a == 1);
    // Downcasting acts like a mod 2^32 too.
    u as u32
}

impl<'a> MontyReducer<'a> {
    fn new(p: &'a BigUint) -> Self {
        let n : Vec<u32> = p.data.clone();
        let n0inv = inv_mod_u32(n[0]);
        MontyReducer { p: p, n: n, n0inv: n0inv }
    }
}

// Montgomery Reduction
//
// Reference:
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 2.6
fn monty_redc(a: BigUint, mr: &MontyReducer) -> BigUint {
    let mut c = a.data;
    let n = &mr.n;
    let n_size = n.len();
    let old_size = c.len();

    // Allocate sufficient work space
    c.reserve(2*n_size+2-old_size);
    c.extend(repeat(0).take(2*n_size+2-old_size));

    // β is the size of a word, in this case 32 bits. So "a mod β" is
    // equivalent to masking a to 32 bits.
    let beta_mask = u32::max_value() as u64;
    // mu <- -N^(-1) mod β
    let mu = (beta_mask-mr.n0inv as u64)+1;

    // 1: for i = 0 to (n-1)
    for i in 0..n_size {
        // Carry storage
        let mut carry = 0;

        // 2: q_i <- mu*c_i mod β
        let q_i = ((c[i] as u64) * mu) & beta_mask;

        // 3: C <- C + q_i * N * β^i
        // When iterating over each word, this becomes:
        for j in 0..n_size {
            // c_(i+j) <- c_(i+j) + q_i * n_j
            let x = (c[i+j] as u64) + q_i * (n[j] as u64) + carry;
            c[i+j] = (x & beta_mask) as u32;
            carry = x >> 32;
        }

        // Apply the remaining carry to the rest of the work space
        for j in n_size..2*n_size-i+2 {
            let x = (c[i+j] as u64) + carry;
            c[i+j] = (x & beta_mask) as u32;
            carry = x >> 32;
        }
    }

    // 4: R <- C * β^(-n)
    // This is an n-word bitshift, equivalent to skipping n words.
    let r : Vec<u32> = c.iter().skip(n_size).cloned().collect();
    let ret = BigUint::new(r);

    // 5: if R >= β^n then return R-N else return R.
    if &ret < mr.p {
        ret
    } else {
        &ret-mr.p
    }
}

// Montgomery Multiplication
fn monty_mult(a: BigUint, b: &BigUint, mr: &MontyReducer) -> BigUint {
    monty_redc(a * b, mr)
}

// Montgomery Squaring
fn monty_sqr(a: BigUint, mr: &MontyReducer) -> BigUint {
    // TODO: Replace with an optimised squaring function
    monty_redc(&a * &a, mr)
}

pub fn monty_modpow(a: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint{
    let mr = MontyReducer::new(modulus);

    // Calculate the Montgomery parameter
    let mut r : BigUint = One::one();
    while &r < mr.p {
        r = r << 32;
    }

    // Map the base to the Montgomery domain
    let mut apri = a * &r % mr.p;

    // Binary exponentiation
    let mut ans = &r % mr.p;
    let mut e = exp.clone();
    let zero = Zero::zero();
    while e > zero {
        if e.is_odd() {
            ans = monty_mult(ans, &apri, &mr);
        }
        apri = monty_sqr(apri, &mr);
        e = e >> 1;
    }

    // Map the result back to the residues domain
    monty_redc(ans, &mr)
}
<commit_msg>bigint::monty: simplify work space allocation<commit_after>use integer::Integer;
use traits::{Zero, One};

use biguint::BigUint;

struct MontyReducer<'a> {
    p: &'a BigUint,
    n: Vec<u32>,
    n0inv: u32
}

// Calculate the modular inverse of `num`, using Extended GCD.
//
// Reference:
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 1.20
fn inv_mod_u32(num: u32) -> u32 {
    // num needs to be relatively prime to 2**32 -- i.e. it must be odd.
    assert!(num % 2 != 0);

    let mut a: i64 = num as i64;
    let mut b: i64 = (u32::max_value() as i64) + 1;

    // ExtendedGcd
    // Input: positive integers a and b
    // Output: integers (g, u, v) such that g = gcd(a, b) = ua + vb
    // As we don't need v for modular inverse, we don't calculate it.

    // 1: (u, w) <- (1, 0)
    let mut u = 1;
    let mut w = 0;
    // 3: while b != 0
    while b != 0 {
        // 4: (q, r) <- DivRem(a, b)
        let q = a / b;
        let r = a % b;
        // 5: (a, b) <- (b, r)
        a = b; b = r;
        // 6: (u, w) <- (w, u - qw)
        let m = u - w*q;
        u = w; w = m;
    }

    assert!(a == 1);
    // Downcasting acts like a mod 2^32 too.
    u as u32
}

impl<'a> MontyReducer<'a> {
    fn new(p: &'a BigUint) -> Self {
        let n : Vec<u32> = p.data.clone();
        let n0inv = inv_mod_u32(n[0]);
        MontyReducer { p: p, n: n, n0inv: n0inv }
    }
}

// Montgomery Reduction
//
// Reference:
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 2.6
fn monty_redc(a: BigUint, mr: &MontyReducer) -> BigUint {
    let mut c = a.data;
    let n = &mr.n;
    let n_size = n.len();

    // Allocate sufficient work space
    c.resize(2 * n_size + 2, 0);

    // β is the size of a word, in this case 32 bits. So "a mod β" is
    // equivalent to masking a to 32 bits.
    let beta_mask = u32::max_value() as u64;
    // mu <- -N^(-1) mod β
    let mu = (beta_mask-mr.n0inv as u64)+1;

    // 1: for i = 0 to (n-1)
    for i in 0..n_size {
        // Carry storage
        let mut carry = 0;

        // 2: q_i <- mu*c_i mod β
        let q_i = ((c[i] as u64) * mu) & beta_mask;

        // 3: C <- C + q_i * N * β^i
        // When iterating over each word, this becomes:
        for j in 0..n_size {
            // c_(i+j) <- c_(i+j) + q_i * n_j
            let x = (c[i+j] as u64) + q_i * (n[j] as u64) + carry;
            c[i+j] = (x & beta_mask) as u32;
            carry = x >> 32;
        }

        // Apply the remaining carry to the rest of the work space
        for j in n_size..2*n_size-i+2 {
            let x = (c[i+j] as u64) + carry;
            c[i+j] = (x & beta_mask) as u32;
            carry = x >> 32;
        }
    }

    // 4: R <- C * β^(-n)
    // This is an n-word bitshift, equivalent to skipping n words.
    let r : Vec<u32> = c.iter().skip(n_size).cloned().collect();
    let ret = BigUint::new(r);

    // 5: if R >= β^n then return R-N else return R.
    if &ret < mr.p {
        ret
    } else {
        &ret-mr.p
    }
}

// Montgomery Multiplication
fn monty_mult(a: BigUint, b: &BigUint, mr: &MontyReducer) -> BigUint {
    monty_redc(a * b, mr)
}

// Montgomery Squaring
fn monty_sqr(a: BigUint, mr: &MontyReducer) -> BigUint {
    // TODO: Replace with an optimised squaring function
    monty_redc(&a * &a, mr)
}

pub fn monty_modpow(a: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint{
    let mr = MontyReducer::new(modulus);

    // Calculate the Montgomery parameter
    let mut r : BigUint = One::one();
    while &r < mr.p {
        r = r << 32;
    }

    // Map the base to the Montgomery domain
    let mut apri = a * &r % mr.p;

    // Binary exponentiation
    let mut ans = &r % mr.p;
    let mut e = exp.clone();
    let zero = Zero::zero();
    while e > zero {
        if e.is_odd() {
            ans = monty_mult(ans, &apri, &mr);
        }
        apri = monty_sqr(apri, &mr);
        e = e >> 1;
    }

    // Map the result back to the residues domain
    monty_redc(ans, &mr)
}
<|endoftext|>