<commit_before>// Cooperative optimization solver
use std::collections::BinaryHeap;
use std::io::Write;
extern crate rand;

#[macro_use(max)]
extern crate gelpia_utils;
extern crate ga;
extern crate gr;

use ga::{ea, Individual};

use gelpia_utils::{Quple, INF, NINF, Flt, Parameters};

use gr::{GI, width_box, split_box, midpoint_box, eps_tol};

use std::sync::{Barrier, RwLock, Arc, RwLockWriteGuard};

use std::sync::atomic::{AtomicBool, Ordering};

use std::thread;

use std::time::Duration;

extern crate function;
use function::FuncObj;

extern crate args;
use args::{process_args};

extern crate time;

fn log_max(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
           f_best_low: f64,
           f_best_high: f64) {
    let mut max = f_best_high;
    let lq = q.clone();
    for qi in lq.iter() {
        max = max!{max, qi.fdata.upper()};
    }
    let _ = writeln!(&mut std::io::stderr(),
                     "lb: {}, possible ub: {}, guaranteed ub: {}",
                     f_best_low,
                     f_best_high,
                     max);
}

// Returns the upper bound, the domain where this bound occurs and a status
// flag indicating whether the answer is complete for the problem.
fn ibba(x_0: Vec<GI>, e_x: Flt, e_f: Flt,
        f_bestag: Arc<RwLock<Flt>>,
        f_best_shared: Arc<RwLock<Flt>>,
        x_bestbb: Arc<RwLock<Vec<GI>>>,
        b1: Arc<Barrier>, b2: Arc<Barrier>,
        q: Arc<RwLock<BinaryHeap<Quple>>>,
        sync: Arc<AtomicBool>, stop: Arc<AtomicBool>,
        f: FuncObj,
        logging: bool)
        -> (Flt, Vec<GI>, bool) {
    let mut f_best_high = NINF;
    let mut f_best_low  = NINF;
    let mut best_x = x_0.clone();

    let mut i: u32 = 0;//binade_presplit(&f, &q, &x_0, e_x);
    q.write().unwrap().push(Quple{p: INF, pf: 0, data: x_0.clone(),
                                  fdata: f.call(&x_0)});

    while q.read().unwrap().len() != 0 && !stop.load(Ordering::Acquire) {
        if sync.load(Ordering::Acquire) {
            // Ugly: Update the update thread's view of the best branch bound.
            *f_best_shared.write().unwrap() = f_best_low;
            b1.wait();
            b2.wait();
        }
        // Take q as writable during an iteration
        let mut q = q.write().unwrap();
        let fbl_orig = f_best_low;
        f_best_low = max!(f_best_low, *f_bestag.read().unwrap());
        if fbl_orig != f_best_low && logging {
            log_max(&q, f_best_low, f_best_high);
        }
        let (ref x, fx) =
            match q.pop() {
                Some(y) => (y.data, y.fdata),
                None    => unreachable!()
            };

        if fx.upper() < f_best_low ||
            width_box(x, e_x) ||
            eps_tol(fx, e_f) {
                if f_best_high < fx.upper() {
                    f_best_high = fx.upper();
                    best_x = x.clone();
                    if logging {
                        log_max(&q, f_best_low, f_best_high);
                    }
                }
                continue;
            }
        else {
            let x_s = split_box(&x);
            for sx in x_s {
                let mid = midpoint_box(&sx);
                let est_m = f.call(&mid);
                let fsx = f.call(&sx);
                let est_max = if est_m.lower() > fsx.lower() {est_m} else {fsx};
                if f_best_low < est_max.lower()  {
                    f_best_low = est_max.lower();
                    *x_bestbb.write().unwrap() = sx.clone();
                }
                i += 1;
                q.push(Quple{p: est_max.upper(),
                             pf: i,
                             data: sx,
                             fdata: fsx});
            }
        }
    }
    if !stop.load(Ordering::Acquire) {
        // Exiting normally
        // Tell GA thread to stop
        stop.store(true, Ordering::Release);
        (f_best_high, best_x, true)
    }
    else {
        // Exiting by some other condition
        (f_best_high, best_x, false)
    }
}

fn distance(p: &Vec<GI>, x: &Vec<GI>) -> Flt {
    let mut result = 0.0;
    for i in 0..x.len() {
        let dx = max!(x[i].lower() - p[i].lower(),
                      0.0, &p[i].lower() - x[i].upper());
        result += dx*dx;
    }
    result.sqrt()
}

fn update(q: Arc<RwLock<BinaryHeap<Quple>>>, population: Arc<RwLock<Vec<Individual>>>,
          f_best_shared: Arc<RwLock<Flt>>,
          stop: Arc<AtomicBool>, sync: Arc<AtomicBool>,
          b1: Arc<Barrier>, b2: Arc<Barrier>,
          f: FuncObj,
          upd_interval: u32,
          timeout: u32) {
    let start = time::get_time();

    'out: while !stop.load(Ordering::Acquire) {
        // Timer code...
        let last_update = time::get_time();
        while (time::get_time() - last_update).num_seconds() <= upd_interval as i64 {
            thread::sleep(Duration::new(1, 0));
            if timeout > 0 &&
                (time::get_time() - start).num_seconds() >= timeout as i64 {
                    let _ = writeln!(&mut std::io::stderr(), "Stopping early...");
                    stop.store(true, Ordering::Release);
                    break 'out;
                }
            if stop.load(Ordering::Acquire) { // Check if we've already stopped
                break 'out;
            }
        }
        // Signal EA and IBBA threads.
        sync.store(true, Ordering::Release);

        // Wait for EA and IBBA threads to stop.
        b1.wait();                                                               // Dead lock here?
        // Do update bizness.
        let mut q_u = q.write().unwrap();
        let mut q = Vec::new();
        for qi in q_u.iter() {
            q.push(qi.clone());
        }
        let mut pop = population.write().unwrap();
        let fbest = f_best_shared.read().unwrap();

        for i in 0..pop.len() {
            let mut d_min = INF;
            let mut j = 0;
            let mut x: Vec<GI>;
            let px: Flt = NINF;
            let mut x_c: Vec<GI> = q[0].data.clone();
            while j < q.len() && d_min != 0.0 {
                x = q[j].data.clone();
                if f.call(&x).upper() < *fbest {
                    q.remove(j);
                    continue;
                }
                let d = distance(&pop[i].solution, &x);

                if d == 0.0 {d_min = 0.0; continue;}
                else {
                    if d < d_min {d_min = d; x_c = x.clone();}
                }
                j += 1;
            }
            if d_min == 0.0 {
                let fp = f.call(&pop[i].solution).lower();
                if px < fp {
                    q[j] = Quple{p: fp, pf: q[j].pf,
                                 data: q[j].data.clone(),
                                 fdata: q[j].fdata }.clone();
                }
            }
            else {
                // Individual is outside the search region.
                // Project it back into the nearest current search space.
                project(&mut pop[i], &x_c, f.clone());
            }
        }
        // Restore the q for the ibba thread.
        (*q_u) = BinaryHeap::from(q);
        // Clear sync flag.
        sync.store(false, Ordering::SeqCst);

        // Resume EA and IBBA threads.
        b2.wait();                                                               // Dead lock here?
    }
}

/* Projects the box x into the box x_c */
fn project(p: &mut Individual, x_c: &Vec<GI>, f: FuncObj) {
    for i in 0..x_c.len() {
        if p.solution[i].lower() < x_c[i].lower()
            || p.solution[i].lower() > x_c[i].upper() {
                if x_c[i].upper() < p.solution[i].lower()
                {p.solution[i] = GI::new_d(x_c[i].upper(),
                                           x_c[i].upper());}
                else {p.solution[i] = GI::new_d(x_c[i].lower(),
                                                x_c[i].lower());}
            }
    }
    p.fitness = f.call(&p.solution).lower();
}

fn main() {
    let args = process_args();

    let ref x_0 = args.domain;
    let ref fo = args.function;
    let x_err = args.x_error;
    let y_err = args.y_error;

    // Early out if there are no input variables...
    if x_0.len() == 0 {
        let result = fo.call(&x_0);
        println!("[{}, {{}}]", result.upper());
        return
    }

    let q_inner: BinaryHeap<Quple> = BinaryHeap::new();
    let q = Arc::new(RwLock::new(q_inner));

    let population_inner: Vec<Individual> = Vec::new();
    let population = Arc::new(RwLock::new(population_inner));

    let b1 = Arc::new(Barrier::new(3));
    let b2 = Arc::new(Barrier::new(3));

    let sync = Arc::new(AtomicBool::new(false));
    let stop = Arc::new(AtomicBool::new(false));

    let f_bestag: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));
    let f_best_shared: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));

    let x_e = x_0.clone();
    let x_i = x_0.clone();

    let x_bestbb = Arc::new(RwLock::new(x_0.clone()));

    let ibba_thread =
    {
        let q = q.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let f_bestag = f_bestag.clone();
        let f_best_shared = f_best_shared.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let fo_c = fo.clone();
        let logging = args.logging;
        thread::Builder::new().name("IBBA".to_string()).spawn(move || {
            ibba(x_i, x_err, y_err,
                 f_bestag, f_best_shared,
                 x_bestbb,
                 b1, b2, q, sync, stop, fo_c, logging)
        })};

    let _ea_thread =
    {
        let population = population.clone();
        let f_bestag = f_bestag.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        thread::Builder::new().name("EA".to_string()).spawn(move || {
            ea(x_e, Parameters{population: 2000, //1000,
                               selection: 8, //4,
                               elitism: 5, //2,
                               mutation: 0.4_f64,//0.3_f64,
                               crossover: 0.3_f64 // 0.5_f64
            },
               population,
               f_bestag,
               x_bestbb,
               b1, b2,
               stop, sync, fo_c)
        })};

    // pending finding out how to kill threads
    //let update_thread =
    {
        let q = q.clone();
        let population = population.clone();
        let f_best_shared = f_best_shared.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        let to = args.timeout.clone();
        let ui = args.update_interval.clone();
        let _ = thread::Builder::new().name("Update".to_string()).spawn(move || {
            update(q, population, f_best_shared,
                   stop, sync, b1, b2, fo_c, ui, to)
        });};

    let result = ibba_thread.unwrap().join();
    /*let ea_result = ea_thread.unwrap().join();
    if !ea_result.is_ok() {
        unreachable!();
    }*/

    // Join EA and Update here pending stop signaling.
    if result.is_ok() {
        let (mut max, mut interval, completed) = result.unwrap();
        if !completed {
            // Go through all remaining intervals from IBBA to find the true
            // max
            let mut lq = q.write().unwrap();
            while lq.len() != 0 {
                let v = lq.pop().unwrap();
                let ref l_data = &v.data;
                let ub = fo.call(l_data).upper();
                if ub > max {
                    max = ub;
                    interval =v.data.clone();
                }
            }
        }
        println!("[{}, {{", max);
        for i in 0..args.names.len() {
            println!("'{}' : {},", args.names[i], interval[i].to_string());
        }
        println!("}}]");

    }
    else {println!("error")}
}
<commit_msg>Terminate early if the upperbound is close enough to the guaranteed upperbound<commit_after>// Cooperative optimization solver
use std::collections::BinaryHeap;
use std::io::Write;
extern crate rand;

#[macro_use(max)]
extern crate gelpia_utils;
extern crate ga;
extern crate gr;

use ga::{ea, Individual};

use gelpia_utils::{Quple, INF, NINF, Flt, Parameters};

use gr::{GI, width_box, split_box, midpoint_box, eps_tol};

use std::sync::{Barrier, RwLock, Arc, RwLockWriteGuard};

use std::sync::atomic::{AtomicBool, Ordering};

use std::thread;

use std::time::Duration;

extern crate function;
use function::FuncObj;

extern crate args;
use args::{process_args};

extern crate time;

/// Returns the guaranteed upperbound for the algorithm
/// from the queue.
fn get_upper_bound(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
                   f_best_high: f64) -> f64{
    let mut max = f_best_high;
    for qi in q.iter() {
        max = max!{max, qi.fdata.upper()};
    }
    max
}

fn log_max(q: &RwLockWriteGuard<BinaryHeap<Quple>>,
           f_best_low: f64,
           f_best_high: f64) {
    let max = get_upper_bound(q, f_best_high);
    let _ = writeln!(&mut std::io::stderr(),
                     "lb: {}, possible ub: {}, guaranteed ub: {}",
                     f_best_low,
                     f_best_high,
                     max);
}

// Returns the upper bound, the domain where this bound occurs and a status
// flag indicating whether the answer is complete for the problem.
fn ibba(x_0: Vec<GI>, e_x: Flt, e_f: Flt,
        f_bestag: Arc<RwLock<Flt>>,
        f_best_shared: Arc<RwLock<Flt>>,
        x_bestbb: Arc<RwLock<Vec<GI>>>,
        b1: Arc<Barrier>, b2: Arc<Barrier>,
        q: Arc<RwLock<BinaryHeap<Quple>>>,
        sync: Arc<AtomicBool>, stop: Arc<AtomicBool>,
        f: FuncObj,
        logging: bool)
        -> (Flt, Vec<GI>, bool) {
    let mut f_best_high = NINF;
    let mut f_best_low  = NINF;
    let mut best_x = x_0.clone();

    let mut iters: u32 = 0;
    q.write().unwrap().push(Quple{p: INF, pf: 0, data: x_0.clone(),
                                  fdata: f.call(&x_0)});

    while q.read().unwrap().len() != 0 && !stop.load(Ordering::Acquire) {
        if sync.load(Ordering::Acquire) {
            // Ugly: Update the update thread's view of the best branch bound.
            *f_best_shared.write().unwrap() = f_best_low;
            b1.wait();
            b2.wait();
        }
        // Take q as writable during an iteration
        let mut q = q.write().unwrap();
        let fbl_orig = f_best_low;
        f_best_low = max!(f_best_low, *f_bestag.read().unwrap());
        if (iters % 1024 == 0) && iters != 0 {
            let guaranteed_bound = get_upper_bound(&q, f_best_high);
            if (guaranteed_bound - f_best_high).abs() < e_f {
                f_best_high = guaranteed_bound;
                break;
            }

        }

        if fbl_orig != f_best_low && logging {
            log_max(&q, f_best_low, f_best_high);
        }

        let (ref x, fx) =
            match q.pop() {
                Some(y) => (y.data, y.fdata),
                None    => unreachable!()
            };

        if fx.upper() < f_best_low ||
            width_box(x, e_x) ||
            eps_tol(fx, e_f) {
                if f_best_high < fx.upper() {
                    f_best_high = fx.upper();
                    best_x = x.clone();
                    if logging {
                        log_max(&q, f_best_low, f_best_high);
                    }
                }
                continue;
            }
        else {
            let x_s = split_box(&x);
            for sx in x_s {
                let mid = midpoint_box(&sx);
                let est_m = f.call(&mid);
                let fsx = f.call(&sx);
                let est_max = if est_m.lower() > fsx.lower() {est_m} else {fsx};
                if f_best_low < est_max.lower()  {
                    f_best_low = est_max.lower();
                    *x_bestbb.write().unwrap() = sx.clone();
                }
                iters += 1;
                q.push(Quple{p: est_max.upper(),
                             pf: iters,
                             data: sx,
                             fdata: fsx});
            }
        }
    }
    if !stop.load(Ordering::Acquire) {
        // Exiting normally
        // Tell GA thread to stop
        stop.store(true, Ordering::Release);
        (f_best_high, best_x, true)
    }
    else {
        // Exiting by some other condition
        (f_best_high, best_x, false)
    }
}

fn distance(p: &Vec<GI>, x: &Vec<GI>) -> Flt {
    let mut result = 0.0;
    for i in 0..x.len() {
        let dx = max!(x[i].lower() - p[i].lower(),
                      0.0, &p[i].lower() - x[i].upper());
        result += dx*dx;
    }
    result.sqrt()
}

fn update(q: Arc<RwLock<BinaryHeap<Quple>>>, population: Arc<RwLock<Vec<Individual>>>,
          f_best_shared: Arc<RwLock<Flt>>,
          stop: Arc<AtomicBool>, sync: Arc<AtomicBool>,
          b1: Arc<Barrier>, b2: Arc<Barrier>,
          f: FuncObj,
          upd_interval: u32,
          timeout: u32) {
    let start = time::get_time();

    'out: while !stop.load(Ordering::Acquire) {
        // Timer code...
        let last_update = time::get_time();
        while (time::get_time() - last_update).num_seconds() <= upd_interval as i64 {
            thread::sleep(Duration::new(1, 0));
            if timeout > 0 &&
                (time::get_time() - start).num_seconds() >= timeout as i64 {
                    let _ = writeln!(&mut std::io::stderr(), "Stopping early...");
                    stop.store(true, Ordering::Release);
                    break 'out;
                }
            if stop.load(Ordering::Acquire) { // Check if we've already stopped
                break 'out;
            }
        }
        // Signal EA and IBBA threads.
        sync.store(true, Ordering::Release);

        // Wait for EA and IBBA threads to stop.
        b1.wait();                                                               // Dead lock here?
        // Do update bizness.
        let mut q_u = q.write().unwrap();
        let mut q = Vec::new();
        for qi in q_u.iter() {
            q.push(qi.clone());
        }
        let mut pop = population.write().unwrap();
        let fbest = f_best_shared.read().unwrap();

        for i in 0..pop.len() {
            let mut d_min = INF;
            let mut j = 0;
            let mut x: Vec<GI>;
            let px: Flt = NINF;
            let mut x_c: Vec<GI> = q[0].data.clone();
            while j < q.len() && d_min != 0.0 {
                x = q[j].data.clone();
                if f.call(&x).upper() < *fbest {
                    q.remove(j);
                    continue;
                }
                let d = distance(&pop[i].solution, &x);

                if d == 0.0 {d_min = 0.0; continue;}
                else {
                    if d < d_min {d_min = d; x_c = x.clone();}
                }
                j += 1;
            }
            if d_min == 0.0 {
                let fp = f.call(&pop[i].solution).lower();
                if px < fp {
                    q[j] = Quple{p: fp, pf: q[j].pf,
                                 data: q[j].data.clone(),
                                 fdata: q[j].fdata }.clone();
                }
            }
            else {
                // Individual is outside the search region.
                // Project it back into the nearest current search space.
                project(&mut pop[i], &x_c, f.clone());
            }
        }
        // Restore the q for the ibba thread.
        (*q_u) = BinaryHeap::from(q);
        // Clear sync flag.
        sync.store(false, Ordering::SeqCst);

        // Resume EA and IBBA threads.
        b2.wait();                                                               // Dead lock here?
    }
}

/* Projects the box x into the box x_c */
fn project(p: &mut Individual, x_c: &Vec<GI>, f: FuncObj) {
    for i in 0..x_c.len() {
        if p.solution[i].lower() < x_c[i].lower()
            || p.solution[i].lower() > x_c[i].upper() {
                if x_c[i].upper() < p.solution[i].lower()
                {p.solution[i] = GI::new_d(x_c[i].upper(),
                                           x_c[i].upper());}
                else {p.solution[i] = GI::new_d(x_c[i].lower(),
                                                x_c[i].lower());}
            }
    }
    p.fitness = f.call(&p.solution).lower();
}

fn main() {
    let args = process_args();

    let ref x_0 = args.domain;
    let ref fo = args.function;
    let x_err = args.x_error;
    let y_err = args.y_error;

    // Early out if there are no input variables...
    if x_0.len() == 0 {
        let result = fo.call(&x_0);
        println!("[{}, {{}}]", result.upper());
        return
    }

    let q_inner: BinaryHeap<Quple> = BinaryHeap::new();
    let q = Arc::new(RwLock::new(q_inner));

    let population_inner: Vec<Individual> = Vec::new();
    let population = Arc::new(RwLock::new(population_inner));

    let b1 = Arc::new(Barrier::new(3));
    let b2 = Arc::new(Barrier::new(3));

    let sync = Arc::new(AtomicBool::new(false));
    let stop = Arc::new(AtomicBool::new(false));

    let f_bestag: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));
    let f_best_shared: Arc<RwLock<Flt>> = Arc::new(RwLock::new(NINF));

    let x_e = x_0.clone();
    let x_i = x_0.clone();

    let x_bestbb = Arc::new(RwLock::new(x_0.clone()));

    let ibba_thread =
    {
        let q = q.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let f_bestag = f_bestag.clone();
        let f_best_shared = f_best_shared.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let fo_c = fo.clone();
        let logging = args.logging;
        thread::Builder::new().name("IBBA".to_string()).spawn(move || {
            ibba(x_i, x_err, y_err,
                 f_bestag, f_best_shared,
                 x_bestbb,
                 b1, b2, q, sync, stop, fo_c, logging)
        })};

    let _ea_thread =
    {
        let population = population.clone();
        let f_bestag = f_bestag.clone();
        let x_bestbb = x_bestbb.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        thread::Builder::new().name("EA".to_string()).spawn(move || {
            ea(x_e, Parameters{population: 2000, //1000,
                               selection: 8, //4,
                               elitism: 5, //2,
                               mutation: 0.4_f64,//0.3_f64,
                               crossover: 0.3_f64 // 0.5_f64
            },
               population,
               f_bestag,
               x_bestbb,
               b1, b2,
               stop, sync, fo_c)
        })};

    // pending finding out how to kill threads
    //let update_thread =
    {
        let q = q.clone();
        let population = population.clone();
        let f_best_shared = f_best_shared.clone();
        let sync = sync.clone();
        let stop = stop.clone();
        let b1 = b1.clone();
        let b2 = b2.clone();
        let fo_c = fo.clone();
        let to = args.timeout.clone();
        let ui = args.update_interval.clone();
        let _ = thread::Builder::new().name("Update".to_string()).spawn(move || {
            update(q, population, f_best_shared,
                   stop, sync, b1, b2, fo_c, ui, to)
        });};

    let result = ibba_thread.unwrap().join();
    /*let ea_result = ea_thread.unwrap().join();
    if !ea_result.is_ok() {
        unreachable!();
    }*/

    // Join EA and Update here pending stop signaling.
    if result.is_ok() {
        let (mut max, mut interval, completed) = result.unwrap();
        if !completed {
            // Go through all remaining intervals from IBBA to find the true
            // max
            let mut lq = q.write().unwrap();
            while lq.len() != 0 {
                let v = lq.pop().unwrap();
                let ref l_data = &v.data;
                let ub = fo.call(l_data).upper();
                if ub > max {
                    max = ub;
                    interval =v.data.clone();
                }
            }
        }
        println!("[{}, {{", max);
        for i in 0..args.names.len() {
            println!("'{}' : {},", args.names[i], interval[i].to_string());
        }
        println!("}}]");

    }
    else {println!("error")}
}
<|endoftext|>


<commit_before>use crate::discord_client::CommandHandler;

pub struct StatusCommand;

impl StatusCommand {
    fn status(&self) -> &str {
        ""
    }
}

impl CommandHandler for StatusCommand {
    fn process_command(
        &self,
        _command: &str,
        send_message_callback: &Fn(&str) -> (),
    ) -> Result<(), &'static str> {
        send_message_callback(self.status());

        Ok(())
    }
}
<commit_msg>Adds current cargo version to status command<commit_after>use crate::discord_client::CommandHandler;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub struct StatusCommand;

impl StatusCommand {
    fn status(&self) -> String {
        format!("Version: {}", VERSION)
    }
}

impl CommandHandler for StatusCommand {
    fn process_command(
        &self,
        _command: &str,
        send_message_callback: &Fn(&str) -> (),
    ) -> Result<(), &'static str> {
        send_message_callback(&self.status());

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate serde_json;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate task_hookrs;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagtodo;

use std::process::exit;
use std::process::{Command, Stdio};
use std::io::stdin;
use std::io::BufRead;

use task_hookrs::import::{import_task, import_tasks};
use task_hookrs::status::TaskStatus;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagtodo::task::IntoTask;
use libimagtodo::task::Task;
use libimagerror::trace::trace_error;

mod ui;

use ui::build_ui;
fn main() {
    let rt = generate_runtime_setup("imag-todo",
                                    &version!()[..],
                                    "Interface with taskwarrior",
                                    build_ui);

    match rt.cli().subcommand_name() {
        Some("tw-hook") => tw_hook(&rt),
        Some("list") => list(&rt),
        _ => unreachable!(),
    } // end match scmd
} // end main

fn tw_hook(rt: &Runtime) {
    let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
    if subcmd.is_present("add") {
        let stdin = stdin();
        let stdin = stdin.lock();

        match Task::import(rt.store(), stdin) {
            Ok((_, uuid)) => info!("Task {} stored in imag", uuid),
            Err(e) => {
                trace_error(&e);
                exit(1);
            }
        }
    } else if subcmd.is_present("delete") {
        // The used hook is "on-modify". This hook gives two json-objects
        // per usage und wants one (the second one) back.
        let stdin         = stdin();
        let stdin         = stdin.lock();

        match import_tasks(stdin) {
            Ok(ttasks) => for (counter, ttask) in ttasks.iter().enumerate() {
                if counter % 2 == 1 {
                    // Only every second task is needed, the first one is the
                    // task before the change, and the second one after
                    // the change. The (maybe modified) second one is
                    // expected by taskwarrior.
                    match serde_json::ser::to_string(&ttask) {
                        Ok(val) => println!("{}", val),
                        Err(e) => {
                            trace_error(&e);
                            exit(1);
                        }
                    }

                    if *ttask.status() == TaskStatus::Deleted {
                        match Task::delete_by_uuid(rt.store(), *ttask.uuid()) {
                            Ok(_) => println!("Deleted task {}", *ttask.uuid()),
                            Err(e) => {
                                trace_error(&e);
                                exit(1);
                            }
                        }
                    }
                } // end if c % 2
            },
            Err(e) => {
                trace_error(&e);
                exit(1);
            },
        }
    } else {
        // Should not be possible, as one argument is required via
        // ArgGroup
        unreachable!();
    }
}

fn list(rt: &Runtime) {
    let subcmd   = rt.cli().subcommand_matches("list").unwrap();
    let mut args = Vec::new();
    let verbose  = subcmd.is_present("verbose");
    let iter     = match Task::all(rt.store()) {
        Ok(iter) => iter,
        Err(e)   => {
            trace_error(&e);
            exit(1);
        },
    };

    for task in iter {
        match task {
            Ok(val) => {
                let uuid = match val.get_header().read("todo.uuid") {
                    Ok(Some(u)) => u,
                    Ok(None)    => continue,
                    Err(e)      => {
                        trace_error(&e);
                        continue;
                    }
                };

                if verbose {
                    args.clear();
                    args.push(format!("uuid:{} information", uuid));

                    let tw_process = Command::new("task")
                        .stdin(Stdio::null())
                        .args(&args)
                        .spawn()
                        .unwrap_or_else(|e| {
                            trace_error(&e);
                            panic!("failed");
                        });
                    let output = tw_process
                        .wait_with_output()
                        .unwrap_or_else(|e| panic!("failed to unwrap output: {}", e));
                    let outstring = String::from_utf8(output.stdout)
                        .unwrap_or_else(|e| panic!("failed to execute: {}", e));

                    println!("{}", outstring);
                } else {
                    println!("{}", match uuid {
                        toml::Value::String(s) => s,
                        _ => {
                            error!("Unexpected type for todo.uuid: {}", uuid);
                            continue;
                        },
                    });
                }
            }
            Err(e) => {
                trace_error(&e);
                continue;
            }
        } // end match task
    } // end for
}

<commit_msg>Remove unused imports<commit_after>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate serde_json;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate task_hookrs;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagerror;
extern crate libimagtodo;

use std::process::exit;
use std::process::{Command, Stdio};
use std::io::stdin;

use task_hookrs::import::import_tasks;
use task_hookrs::status::TaskStatus;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagtodo::task::Task;
use libimagerror::trace::trace_error;

mod ui;

use ui::build_ui;
fn main() {
    let rt = generate_runtime_setup("imag-todo",
                                    &version!()[..],
                                    "Interface with taskwarrior",
                                    build_ui);

    match rt.cli().subcommand_name() {
        Some("tw-hook") => tw_hook(&rt),
        Some("list") => list(&rt),
        _ => unreachable!(),
    } // end match scmd
} // end main

fn tw_hook(rt: &Runtime) {
    let subcmd = rt.cli().subcommand_matches("tw-hook").unwrap();
    if subcmd.is_present("add") {
        let stdin = stdin();
        let stdin = stdin.lock();

        match Task::import(rt.store(), stdin) {
            Ok((_, uuid)) => info!("Task {} stored in imag", uuid),
            Err(e) => {
                trace_error(&e);
                exit(1);
            }
        }
    } else if subcmd.is_present("delete") {
        // The used hook is "on-modify". This hook gives two json-objects
        // per usage und wants one (the second one) back.
        let stdin         = stdin();
        let stdin         = stdin.lock();

        match import_tasks(stdin) {
            Ok(ttasks) => for (counter, ttask) in ttasks.iter().enumerate() {
                if counter % 2 == 1 {
                    // Only every second task is needed, the first one is the
                    // task before the change, and the second one after
                    // the change. The (maybe modified) second one is
                    // expected by taskwarrior.
                    match serde_json::ser::to_string(&ttask) {
                        Ok(val) => println!("{}", val),
                        Err(e) => {
                            trace_error(&e);
                            exit(1);
                        }
                    }

                    if *ttask.status() == TaskStatus::Deleted {
                        match Task::delete_by_uuid(rt.store(), *ttask.uuid()) {
                            Ok(_) => println!("Deleted task {}", *ttask.uuid()),
                            Err(e) => {
                                trace_error(&e);
                                exit(1);
                            }
                        }
                    }
                } // end if c % 2
            },
            Err(e) => {
                trace_error(&e);
                exit(1);
            },
        }
    } else {
        // Should not be possible, as one argument is required via
        // ArgGroup
        unreachable!();
    }
}

fn list(rt: &Runtime) {
    let subcmd   = rt.cli().subcommand_matches("list").unwrap();
    let mut args = Vec::new();
    let verbose  = subcmd.is_present("verbose");
    let iter     = match Task::all(rt.store()) {
        Ok(iter) => iter,
        Err(e)   => {
            trace_error(&e);
            exit(1);
        },
    };

    for task in iter {
        match task {
            Ok(val) => {
                let uuid = match val.get_header().read("todo.uuid") {
                    Ok(Some(u)) => u,
                    Ok(None)    => continue,
                    Err(e)      => {
                        trace_error(&e);
                        continue;
                    }
                };

                if verbose {
                    args.clear();
                    args.push(format!("uuid:{} information", uuid));

                    let tw_process = Command::new("task")
                        .stdin(Stdio::null())
                        .args(&args)
                        .spawn()
                        .unwrap_or_else(|e| {
                            trace_error(&e);
                            panic!("failed");
                        });
                    let output = tw_process
                        .wait_with_output()
                        .unwrap_or_else(|e| panic!("failed to unwrap output: {}", e));
                    let outstring = String::from_utf8(output.stdout)
                        .unwrap_or_else(|e| panic!("failed to execute: {}", e));

                    println!("{}", outstring);
                } else {
                    println!("{}", match uuid {
                        toml::Value::String(s) => s,
                        _ => {
                            error!("Unexpected type for todo.uuid: {}", uuid);
                            continue;
                        },
                    });
                }
            }
            Err(e) => {
                trace_error(&e);
                continue;
            }
        } // end match task
    } // end for
}

<|endoftext|>


<commit_before>mod PartiallyPersistentNonindexedSet {
  use std::sync::{Arc, RwLock};
  use std::cmp::min;
  use std::collections::HashSet;
  use std::hash::Hash;
  use std::mem;
  use std::cell::{Cell, UnsafeCell};
  #[derive (Clone)]
  struct Entry <K: Clone + Eq> {
    key: K,
    insertion: bool,
  }
  //using Vec< in this buffer type has unnecessary indirection, but I was unable to find
  //a library that does the job safely without it.
  //Compare Arc<[Entry<K>]>, which could behave unsafely if I
  //didn't initialize it, but would waste time if I did.
  //
  //Even though I am technically using the RwLock type,
  //this is a lock-free data structure because we never
  //actually call RwLock::write(), only new() and read().
  //(TODO: is there a better primitive for this?)
  #[derive (Clone)]
  struct Buffer <K: Clone + Eq> {
    data: Arc <UnsafeCell <Vec<RwLock <Entry <K>>>>>,
    deletions: usize,
  }
  pub struct Snapshot <K: Clone + Eq> {
    buffer: Buffer <K>,
    used_length: usize,
  }
  pub struct SnapshotIter <'a, K: Clone + Eq + Hash + 'a> {
    snapshot: & 'a Snapshot <K>,
    position: usize,
    ignore: HashSet <K>,
  }

  pub struct Set <K: Clone + Eq> {
    live_buffer: Buffer <K>,
    next_buffer: Buffer <K>,
    next_transfer_index: usize,
    operating: bool,
  }
  const MAX_TRANSFER_SPEED: usize = 8;

  impl<K: Clone + Eq + Hash> Snapshot <K> {
    pub fn iter <'a> (& 'a self)->SnapshotIter <'a, K> {SnapshotIter {snapshot: self, position: self.used_length, ignore: HashSet::new(),}}
  }
  impl<'a, K: Clone + Eq + Hash> SnapshotIter <'a, K> {
    /// Do a single iteration step, which MAY return an iteration result,
    /// and is guaranteed to finish in O(1) worst-case time.
    pub fn step (&mut self)->Option <Option <K>> {
      if self.position == 0 {return Some (None);}
      self.position -= 1;
      let entry = unsafe {(*self.snapshot.buffer.data.get()) [self.position].read().unwrap()};
      if entry.insertion == false {
        self.ignore.insert (entry.key.clone());
      }
      else if self.ignore.get (& entry.key).is_none() {
        return Some (Some (entry.key.clone()));
      }
      None
    }
  }
  impl<'a, K: Clone + Eq + Hash > Iterator for SnapshotIter <'a, K> {
    type Item = K;
    fn next (&mut self)->Option <K> {
      loop {
        if let Some (result) = self.step() {return result;}
      }
    }
  }
  impl<K: Clone + Eq> Set <K> {
    pub fn new()->Set <K> {
      Set {
        live_buffer: Buffer {data: Arc::new (UnsafeCell::new (Vec::new())), deletions: 0},
        next_buffer: Buffer {data: Arc::new (UnsafeCell::new (Vec::new())), deletions: 0},
        next_transfer_index: 0,
        operating: false,
      }
    }
    /// Adds a value to the set.
    ///
    /// Complexity: O(1) worst-case if K does not implement Drop, O(1) amortized otherwise.
    pub fn insert <F: Fn (& K)->bool> (&mut self, key: K, currently_existent: & F) {
      assert!(!self.operating, "Attempt to call PartiallyPersistentNonindexedSet::insert() from inside its own existence checker callback; what ghastly code led to this situation?");
      self.operating = true;
      self.make_room (currently_existent);
      unsafe {(*self.live_buffer.data.get()).push (RwLock::new (Entry {key: key, insertion: true}));}
      self.operating = false;
    }
    /// Removes a value from the set.
    ///
    /// Complexity: O(1) worst-case if K does not implement Drop, O(1) amortized otherwise.
    pub fn remove <F: Fn (& K)->bool> (&mut self, key: K, currently_existent: & F) {
      assert!(!self.operating, "Attempt to call PartiallyPersistentNonindexedSet::insert() from inside its own existence checker callback; what ghastly code led to this situation?");
      self.operating = true;
      self.make_room (currently_existent);
      self.live_buffer.deletions += 1;
      unsafe {(*self.live_buffer.data.get()).push (RwLock::new (Entry {key: key.clone(), insertion: false}));}
      // the next buffer doesn't necessarily have an insertion
      // corresponding to this deletion, so this could be wasteful. However,
      // it would also be wasteful to look up whether the insertion is there
      // (we currently get away with not even being ABLE to do so)
      self.next_buffer.deletions += 1;
      unsafe {(*self.next_buffer.data.get()).push (RwLock::new (Entry {key: key, insertion: false}));}
      self.operating = false;
    }
    /// Takes out a snapshot to the current state of the set.
    ///
    /// These snapshots can safely be passed to other threads
    /// even as you continue to modify the set.
    ///
    /// Complexity: O(1) worst-case if K does not implement Drop, O(1) amortized otherwise.
    pub fn snapshot (&self)->Snapshot <K> {
      Snapshot {buffer: self.live_buffer.clone(), used_length: unsafe {(*self.live_buffer.data.get()).len()}}
    }

    fn make_room <F: Fn (& K)->bool> (&mut self, currently_existent: F) {
      let live_data = unsafe {self.live_buffer.data.get().as_ref().unwrap()};
      let next_data = unsafe {self.next_buffer.data.get().as_mut().unwrap()};
      let buffer_pushes_before_reset_needed = min (
        live_data.capacity() - live_data.len(),
        next_data.capacity()/2 - next_data.len(),
      );
      let current_insertions = live_data.len() - self.live_buffer.deletions;
      let tolerable_deletions = (current_insertions*5)/12;
      let deletions_before_reset_needed = tolerable_deletions - self.live_buffer.deletions;
      let operations_before_reset_possibly_needed = min (buffer_pushes_before_reset_needed, deletions_before_reset_needed);
      let transfer_steps_needed_before_reset_possible = live_data.len() + operations_before_reset_possibly_needed - self.next_transfer_index;
      assert!(transfer_steps_needed_before_reset_possible <= (operations_before_reset_possibly_needed + 1) * MAX_TRANSFER_SPEED);
      if transfer_steps_needed_before_reset_possible <= operations_before_reset_possibly_needed * MAX_TRANSFER_SPEED {
        for _ in 0..MAX_TRANSFER_SPEED {
          if let Some (item) = live_data.get (self.next_transfer_index) {
            let entry = item.read().unwrap();
            if entry.insertion && currently_existent (& entry.key) {
              next_data.push (RwLock::new ((*entry).clone()));
            }
            self.next_transfer_index += 1;
          }
          else {break;}
        }
      }
      if operations_before_reset_possibly_needed == 0 {
        mem::swap (&mut self.live_buffer, &mut self.next_buffer);
        self.next_buffer = Buffer {
          data: Arc::new (UnsafeCell::new (Vec::with_capacity ((next_data.len() + 1)*2))),
          deletions: 0,
        };
        self.next_transfer_index = 0;
      }
      unsafe {assert!((*self.live_buffer.data.get()).len() < (*self.live_buffer.data.get()).capacity());
      assert!((*self.next_buffer.data.get()).len() < (*self.next_buffer.data.get()).capacity());}
    }
  }
}
<commit_msg>impl Send, Sync so the new data structure can be used across threads (which I believe is safe)<commit_after>mod PartiallyPersistentNonindexedSet {
  use std::sync::{Arc, RwLock};
  use std::cmp::min;
  use std::collections::HashSet;
  use std::hash::Hash;
  use std::mem;
  use std::cell::{Cell, UnsafeCell};
  #[derive (Clone)]
  struct Entry <K: Clone + Eq> {
    key: K,
    insertion: bool,
  }
  //using Vec< in this buffer type has unnecessary indirection, but I was unable to find
  //a library that does the job safely without it.
  //Compare Arc<[Entry<K>]>, which could behave unsafely if I
  //didn't initialize it, but would waste time if I did.
  //
  //Even though I am technically using the RwLock type,
  //this is a lock-free data structure because we never
  //actually call RwLock::write(), only new() and read().
  //(TODO: is there a better primitive for this?)
  #[derive (Clone)]
  struct Buffer <K: Clone + Eq> {
    data: Arc <UnsafeCell <Vec<RwLock <Entry <K>>>>>,
    deletions: usize,
  }
  unsafe impl<K: Clone + Eq> Send for Buffer <K>{}
  unsafe impl<K: Clone + Eq> Sync for Buffer <K>{}

  pub struct Snapshot <K: Clone + Eq> {
    buffer: Buffer <K>,
    used_length: usize,
  }
  pub struct SnapshotIter <'a, K: Clone + Eq + Hash + 'a> {
    snapshot: & 'a Snapshot <K>,
    position: usize,
    ignore: HashSet <K>,
  }

  pub struct Set <K: Clone + Eq> {
    live_buffer: Buffer <K>,
    next_buffer: Buffer <K>,
    next_transfer_index: usize,
    operating: bool,
  }
  const MAX_TRANSFER_SPEED: usize = 8;

  impl<K: Clone + Eq + Hash> Snapshot <K> {
    pub fn iter <'a> (& 'a self)->SnapshotIter <'a, K> {SnapshotIter {snapshot: self, position: self.used_length, ignore: HashSet::new(),}}
  }
  impl<'a, K: Clone + Eq + Hash> SnapshotIter <'a, K> {
    /// Do a single iteration step, which MAY return an iteration result,
    /// and is guaranteed to finish in O(1) worst-case time.
    pub fn step (&mut self)->Option <Option <K>> {
      if self.position == 0 {return Some (None);}
      self.position -= 1;
      let entry = unsafe {(*self.snapshot.buffer.data.get()) [self.position].read().unwrap()};
      if entry.insertion == false {
        self.ignore.insert (entry.key.clone());
      }
      else if self.ignore.get (& entry.key).is_none() {
        return Some (Some (entry.key.clone()));
      }
      None
    }
  }
  impl<'a, K: Clone + Eq + Hash > Iterator for SnapshotIter <'a, K> {
    type Item = K;
    fn next (&mut self)->Option <K> {
      loop {
        if let Some (result) = self.step() {return result;}
      }
    }
  }
  impl<K: Clone + Eq> Set <K> {
    pub fn new()->Set <K> {
      Set {
        live_buffer: Buffer {data: Arc::new (UnsafeCell::new (Vec::new())), deletions: 0},
        next_buffer: Buffer {data: Arc::new (UnsafeCell::new (Vec::new())), deletions: 0},
        next_transfer_index: 0,
        operating: false,
      }
    }
    /// Adds a value to the set.
    ///
    /// Complexity: O(1) worst-case if K does not implement Drop, O(1) amortized otherwise.
    pub fn insert <F: Fn (& K)->bool> (&mut self, key: K, currently_existent: & F) {
      assert!(!self.operating, "Attempt to call PartiallyPersistentNonindexedSet::insert() from inside its own existence checker callback; what ghastly code led to this situation?");
      self.operating = true;
      self.make_room (currently_existent);
      unsafe {(*self.live_buffer.data.get()).push (RwLock::new (Entry {key: key, insertion: true}));}
      self.operating = false;
    }
    /// Removes a value from the set.
    ///
    /// Complexity: O(1) worst-case if K does not implement Drop, O(1) amortized otherwise.
    pub fn remove <F: Fn (& K)->bool> (&mut self, key: K, currently_existent: & F) {
      assert!(!self.operating, "Attempt to call PartiallyPersistentNonindexedSet::insert() from inside its own existence checker callback; what ghastly code led to this situation?");
      self.operating = true;
      self.make_room (currently_existent);
      self.live_buffer.deletions += 1;
      unsafe {(*self.live_buffer.data.get()).push (RwLock::new (Entry {key: key.clone(), insertion: false}));}
      // the next buffer doesn't necessarily have an insertion
      // corresponding to this deletion, so this could be wasteful. However,
      // it would also be wasteful to look up whether the insertion is there
      // (we currently get away with not even being ABLE to do so)
      self.next_buffer.deletions += 1;
      unsafe {(*self.next_buffer.data.get()).push (RwLock::new (Entry {key: key, insertion: false}));}
      self.operating = false;
    }
    /// Takes out a snapshot to the current state of the set.
    ///
    /// These snapshots can safely be passed to other threads
    /// even as you continue to modify the set.
    ///
    /// Complexity: O(1) worst-case if K does not implement Drop, O(1) amortized otherwise.
    pub fn snapshot (&self)->Snapshot <K> {
      Snapshot {buffer: self.live_buffer.clone(), used_length: unsafe {(*self.live_buffer.data.get()).len()}}
    }

    fn make_room <F: Fn (& K)->bool> (&mut self, currently_existent: F) {
      let live_data = unsafe {self.live_buffer.data.get().as_ref().unwrap()};
      let next_data = unsafe {self.next_buffer.data.get().as_mut().unwrap()};
      let buffer_pushes_before_reset_needed = min (
        live_data.capacity() - live_data.len(),
        next_data.capacity()/2 - next_data.len(),
      );
      let current_insertions = live_data.len() - self.live_buffer.deletions;
      let tolerable_deletions = (current_insertions*5)/12;
      let deletions_before_reset_needed = tolerable_deletions - self.live_buffer.deletions;
      let operations_before_reset_possibly_needed = min (buffer_pushes_before_reset_needed, deletions_before_reset_needed);
      let transfer_steps_needed_before_reset_possible = live_data.len() + operations_before_reset_possibly_needed - self.next_transfer_index;
      assert!(transfer_steps_needed_before_reset_possible <= (operations_before_reset_possibly_needed + 1) * MAX_TRANSFER_SPEED);
      if transfer_steps_needed_before_reset_possible <= operations_before_reset_possibly_needed * MAX_TRANSFER_SPEED {
        for _ in 0..MAX_TRANSFER_SPEED {
          if let Some (item) = live_data.get (self.next_transfer_index) {
            let entry = item.read().unwrap();
            if entry.insertion && currently_existent (& entry.key) {
              next_data.push (RwLock::new ((*entry).clone()));
            }
            self.next_transfer_index += 1;
          }
          else {break;}
        }
      }
      if operations_before_reset_possibly_needed == 0 {
        mem::swap (&mut self.live_buffer, &mut self.next_buffer);
        self.next_buffer = Buffer {
          data: Arc::new (UnsafeCell::new (Vec::with_capacity ((next_data.len() + 1)*2))),
          deletions: 0,
        };
        self.next_transfer_index = 0;
      }
      unsafe {assert!((*self.live_buffer.data.get()).len() < (*self.live_buffer.data.get()).capacity());
      assert!((*self.next_buffer.data.get()).len() < (*self.next_buffer.data.get()).capacity());}
    }
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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::u16;
use std::default::Default;

use super::{Frame, FrameBuilder};

bitflags! {
    flags OpCode: u8 {
        const CONTINUATION  = 0b0000_0000,
        const TEXT          = 0b0000_0001,
        const BINARY        = 0b0000_0010,
        const CLOSE         = 0b0000_1000,
        const PING          = 0b0000_1001,
        const PONG          = 0b0000_1010
    }
}

#[derive(Clone, PartialEq, Eq)]
pub enum FrameType {
    Control,
    Data
}

#[derive(Clone, PartialEq, Eq)]
pub enum OpType {
    Continuation,
    Text,
    Binary,
    Close,
    Ping,
    Pong
}

#[derive(Clone)]
struct Header {
    op_code: OpCode,
    mask: bool,
    payload_len: u64,
    masking_key: [u8; 4]
}

#[derive(Clone)]
struct Payload {
    data: Vec<u8>
}

#[derive(Clone)]
pub struct WebSocketFrame {
    frame_type: FrameType,
    header: Header,
    payload: Payload
}

#[derive(Clone)]
pub struct WebSocketFrameBuilder;
impl FrameBuilder for WebSocketFrameBuilder {
    fn from_bytes(buf: &mut Vec<u8>) -> Option<Box<Frame>> {
        if buf.len() < 5 {
            trace!("Buffer length is less than 5, not worth trying...");
            return None;
        }

        let mut frame: WebSocketFrame = Default::default();

        // OpCode and FrameType
        let fin_cleared = buf[0] & 0b0000_1111;
        match OpCode::from_bits(fin_cleared) {
            Some(op_code) => {
                if op_code.contains(CONTINUATION) {
                    frame.frame_type = FrameType::Data;
                    frame.header.op_code = CONTINUATION;
                } else if op_code.contains(TEXT) {
                    frame.frame_type = FrameType::Data;
                    frame.header.op_code = TEXT;
                } else if op_code.contains(BINARY) {
                    frame.frame_type = FrameType::Data;
                    frame.header.op_code = BINARY;
                } else if op_code.contains(CLOSE) {
                    frame.frame_type = FrameType::Control;
                    frame.header.op_code = CLOSE;
                } else if op_code.contains(PING) {
                    frame.frame_type = FrameType::Control;
                    frame.header.op_code = PING;
                } else if op_code.contains(PONG) {
                    frame.frame_type = FrameType::Control;
                    frame.header.op_code = PONG;
                }
            }
            None => {
                error!("Invalid OpCode bits: {:#b}", buf[0]);
                return None;
            }
        }

        // Payload masked (If from client, must always be true)
        let mask_bit = 0b1000_0000 & buf[1];
        frame.header.mask = mask_bit > 0;

        // Payload data length
        let mut next_offset: usize = 3;
        let payload_len = 0b0111_1111 & buf[2];
        if payload_len <= 125 {
            frame.header.payload_len = payload_len as u64;
        } else if payload_len == 126 {
            let mut len = (buf[3] as u16) << 8;
            len |= buf[4] as u16;
            frame.header.payload_len = len as u64;
            next_offset = 5;
        } else {
            // We don't want to cause a panic
            if buf.len() < 10 {
                return None;
            }

            let mut len = (buf[3] as u64) << 56;
            len |= (buf[3] as u64) << 48;
            len |= (buf[4] as u64) << 40;
            len |= (buf[5] as u64) << 32;
            len |= (buf[6] as u64) << 24;
            len |= (buf[7] as u64) << 16;
            len |= (buf[8] as u64) << 8;
            len |= buf[9] as u64;
            frame.header.payload_len = len;
            next_offset = 10;
        }

        // Optional masking key
        if frame.header.mask {
            if buf.len() <= next_offset + 4 {
                return None;
            }
            frame.header.masking_key[0] = buf[next_offset];
            frame.header.masking_key[1] = buf[next_offset + 1];
            frame.header.masking_key[2] = buf[next_offset + 2];
            frame.header.masking_key[3] = buf[next_offset + 3];
            next_offset += 4;
        }

        if buf.len() < next_offset + frame.header.payload_len as usize {
            return None;
        }

        // Payload data
        let len = frame.header.payload_len as usize;
        frame.payload.data.extend_from_slice(&buf[next_offset..len]);

        return Some(Box::new(frame));
    }
}

impl WebSocketFrame {
    pub fn new(buf: &[u8], frame_type: FrameType, op_type: OpType) -> WebSocketFrame {
        WebSocketFrame {
            frame_type: frame_type,
            header: Header {
                op_code: match op_type {
                    OpType::Continuation => CONTINUATION,
                    OpType::Text => TEXT,
                    OpType::Binary => BINARY,
                    OpType::Close => CLOSE,
                    OpType::Ping => PING,
                    OpType::Pong => PONG,
                },
                mask: false,
                payload_len: buf.len() as u64,
                masking_key: [0u8; 4]
            },
            payload: Payload {
                data: buf.to_vec()
            }
        }
    }

    pub fn op_type(&self) -> OpType {
        match self.header.op_code {
            CONTINUATION => OpType::Continuation,
            TEXT => OpType::Text,
            BINARY => OpType::Binary,
            CLOSE => OpType::Close,
            PING => OpType::Ping,
            PONG => OpType::Pong,
            _ => unreachable!()
        }
    }

    pub fn frame_type(&self) -> FrameType {
        self.frame_type.clone()
    }

    pub fn is_masked(&self) -> bool {
        self.header.mask
    }

    pub fn payload_unmasked(&self) -> Vec<u8> {
        let len = self.payload.data.len();
        let mut buf = Vec::<u8>::with_capacity(len);
        for x in 0..len {
            buf.push(self.payload.data[x] ^ self.header.masking_key[x % 4]);
        }

        buf
    }
}

impl Frame for WebSocketFrame {
    fn payload(&self) -> Vec<u8> {
        if self.header.mask {
            self.payload_unmasked()
        } else {
            self.payload.data.clone()
        }
    }

    fn to_bytes(&self) -> Vec<u8> {
        let mut buf = Vec::<u8>::with_capacity(self.len_as_vec());

        // OpCode
        buf.push(self.header.op_code.bits());

        // Mask and Payload len
        let mask_bit: u8 = if self.header.mask {
            0b1000_000
        } else {
            0b0000_000
        };
        let next_7_bits: u8 = if self.header.payload_len <= 125 {
            self.header.payload_len as u8
        } else if self.header.payload_len <= u16::MAX as u64 {
            126u8
        } else {
            127u8
        };
        let next_byte: u8 = mask_bit & next_7_bits;
        buf.push(next_byte);

        // Optional payload len
        if next_byte == 126 {
            buf.push(((self.header.payload_len as u16) >> 8) as u8);
            buf.push(self.header.payload_len as u8);
        } else if next_byte == 127 {
            buf.push((self.header.payload_len >> 56) as u8);
            buf.push((self.header.payload_len >> 48) as u8);
            buf.push((self.header.payload_len >> 40) as u8);
            buf.push((self.header.payload_len >> 32) as u8);
            buf.push((self.header.payload_len >> 24) as u8);
            buf.push((self.header.payload_len >> 16) as u8);
            buf.push((self.header.payload_len >> 8) as u8);
            buf.push(self.header.payload_len as u8);
        }

        // Optional masking key
        if self.header.mask {
            buf.push(self.header.masking_key[0]);
            buf.push(self.header.masking_key[1]);
            buf.push(self.header.masking_key[2]);
            buf.push(self.header.masking_key[3]);
        }

        // Payload data
        buf.extend_from_slice(&self.payload.data[..]);

        buf
    }

    fn len_as_vec(&self) -> usize {
        let mut len = 0usize;

        // OpCode
        len += 1;

        // Mask and paylaod length
        len += 1;

        // Extended Payload length
        if self.header.payload_len > 125 && self.header.payload_len < u16::MAX as u64 {
            len += 2;
        } else if self.header.payload_len > u16::MAX as u64 {
            len += 8;
        }

        // Optional masking key
        if self.header.mask {
            len += 4;
        }

        // Payload data
        len += self.header.payload_len as usize;

        len
    }

    fn as_mut_raw_erased(&self) -> *mut () {
        let dup = Box::new(self.clone());
        return Box::into_raw(dup) as *mut _ as *mut ();
    }
}

impl Default for WebSocketFrame {
    fn default() -> WebSocketFrame {
        WebSocketFrame {
            frame_type: FrameType::Control,
            header: Header {
                op_code: CONTINUATION,
                mask: false,
                payload_len: 0u64,
                masking_key: [0u8; 4]
            },
            payload: Payload {
                data: Vec::<u8>::new()
            }
        }
    }
}
<commit_msg>More logs<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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::u16;
use std::default::Default;

use super::{Frame, FrameBuilder};

bitflags! {
    flags OpCode: u8 {
        const CONTINUATION  = 0b0000_0000,
        const TEXT          = 0b0000_0001,
        const BINARY        = 0b0000_0010,
        const CLOSE         = 0b0000_1000,
        const PING          = 0b0000_1001,
        const PONG          = 0b0000_1010
    }
}

#[derive(Clone, PartialEq, Eq)]
pub enum FrameType {
    Control,
    Data
}

#[derive(Clone, PartialEq, Eq)]
pub enum OpType {
    Continuation,
    Text,
    Binary,
    Close,
    Ping,
    Pong
}

#[derive(Clone)]
struct Header {
    op_code: OpCode,
    mask: bool,
    payload_len: u64,
    masking_key: [u8; 4]
}

#[derive(Clone)]
struct Payload {
    data: Vec<u8>
}

#[derive(Clone)]
pub struct WebSocketFrame {
    frame_type: FrameType,
    header: Header,
    payload: Payload
}

#[derive(Clone)]
pub struct WebSocketFrameBuilder;
impl FrameBuilder for WebSocketFrameBuilder {
    fn from_bytes(buf: &mut Vec<u8>) -> Option<Box<Frame>> {
        if buf.len() < 5 {
            trace!("Buffer length is less than 5, not worth trying...");
            return None;
        }

        let mut frame: WebSocketFrame = Default::default();

        // OpCode and FrameType
        let fin_cleared = buf[0] & 0b0000_1111;
        match OpCode::from_bits(fin_cleared) {
            Some(op_code) => {
                if op_code.contains(CONTINUATION) {
                    frame.frame_type = FrameType::Data;
                    frame.header.op_code = CONTINUATION;
                } else if op_code.contains(TEXT) {
                    frame.frame_type = FrameType::Data;
                    frame.header.op_code = TEXT;
                } else if op_code.contains(BINARY) {
                    frame.frame_type = FrameType::Data;
                    frame.header.op_code = BINARY;
                } else if op_code.contains(CLOSE) {
                    frame.frame_type = FrameType::Control;
                    frame.header.op_code = CLOSE;
                } else if op_code.contains(PING) {
                    frame.frame_type = FrameType::Control;
                    frame.header.op_code = PING;
                } else if op_code.contains(PONG) {
                    frame.frame_type = FrameType::Control;
                    frame.header.op_code = PONG;
                }
            }
            None => {
                error!("Invalid OpCode bits: {:#b}", buf[0]);
                return None;
            }
        }

        // Payload masked (If from client, must always be true)
        let mask_bit = 0b1000_0000 & buf[1];
        frame.header.mask = mask_bit > 0;

        // Payload data length
        let mut next_offset: usize = 3;
        let payload_len = 0b0111_1111 & buf[2];
        if payload_len <= 125 {
            frame.header.payload_len = payload_len as u64;
        } else if payload_len == 126 {
            let mut len = (buf[3] as u16) << 8;
            len |= buf[4] as u16;
            frame.header.payload_len = len as u64;
            next_offset = 5;
        } else {
            // We don't want to cause a panic
            if buf.len() < 10 {
                trace!("Not enough payload to continue reading payload length");
                return None;
            }

            let mut len = (buf[3] as u64) << 56;
            len |= (buf[3] as u64) << 48;
            len |= (buf[4] as u64) << 40;
            len |= (buf[5] as u64) << 32;
            len |= (buf[6] as u64) << 24;
            len |= (buf[7] as u64) << 16;
            len |= (buf[8] as u64) << 8;
            len |= buf[9] as u64;
            frame.header.payload_len = len;
            next_offset = 10;
        }

        trace!("Payload length: {}", frame.header.payload_len);

        // Optional masking key
        if frame.header.mask {
            if buf.len() <= next_offset + 4 {
                trace!("Payload masked, not enough in buffer to read masking key");
                return None;
            }
            frame.header.masking_key[0] = buf[next_offset];
            frame.header.masking_key[1] = buf[next_offset + 1];
            frame.header.masking_key[2] = buf[next_offset + 2];
            frame.header.masking_key[3] = buf[next_offset + 3];
            next_offset += 4;
        }

        if buf.len() < next_offset + frame.header.payload_len as usize {
            trace!("Not enough in buffer to read payload");
            return None;
        }

        // Payload data
        let len = frame.header.payload_len as usize;
        frame.payload.data.extend_from_slice(&buf[next_offset..len]);

        trace!("Read complete");

        return Some(Box::new(frame));
    }
}

impl WebSocketFrame {
    pub fn new(buf: &[u8], frame_type: FrameType, op_type: OpType) -> WebSocketFrame {
        WebSocketFrame {
            frame_type: frame_type,
            header: Header {
                op_code: match op_type {
                    OpType::Continuation => CONTINUATION,
                    OpType::Text => TEXT,
                    OpType::Binary => BINARY,
                    OpType::Close => CLOSE,
                    OpType::Ping => PING,
                    OpType::Pong => PONG,
                },
                mask: false,
                payload_len: buf.len() as u64,
                masking_key: [0u8; 4]
            },
            payload: Payload {
                data: buf.to_vec()
            }
        }
    }

    pub fn op_type(&self) -> OpType {
        match self.header.op_code {
            CONTINUATION => OpType::Continuation,
            TEXT => OpType::Text,
            BINARY => OpType::Binary,
            CLOSE => OpType::Close,
            PING => OpType::Ping,
            PONG => OpType::Pong,
            _ => unreachable!()
        }
    }

    pub fn frame_type(&self) -> FrameType {
        self.frame_type.clone()
    }

    pub fn is_masked(&self) -> bool {
        self.header.mask
    }

    pub fn payload_unmasked(&self) -> Vec<u8> {
        let len = self.payload.data.len();
        let mut buf = Vec::<u8>::with_capacity(len);
        for x in 0..len {
            buf.push(self.payload.data[x] ^ self.header.masking_key[x % 4]);
        }

        buf
    }
}

impl Frame for WebSocketFrame {
    fn payload(&self) -> Vec<u8> {
        if self.header.mask {
            self.payload_unmasked()
        } else {
            self.payload.data.clone()
        }
    }

    fn to_bytes(&self) -> Vec<u8> {
        let mut buf = Vec::<u8>::with_capacity(self.len_as_vec());

        // OpCode
        buf.push(self.header.op_code.bits());

        // Mask and Payload len
        let mask_bit: u8 = if self.header.mask {
            0b1000_000
        } else {
            0b0000_000
        };
        let next_7_bits: u8 = if self.header.payload_len <= 125 {
            self.header.payload_len as u8
        } else if self.header.payload_len <= u16::MAX as u64 {
            126u8
        } else {
            127u8
        };
        let next_byte: u8 = mask_bit & next_7_bits;
        buf.push(next_byte);

        // Optional payload len
        if next_byte == 126 {
            buf.push(((self.header.payload_len as u16) >> 8) as u8);
            buf.push(self.header.payload_len as u8);
        } else if next_byte == 127 {
            buf.push((self.header.payload_len >> 56) as u8);
            buf.push((self.header.payload_len >> 48) as u8);
            buf.push((self.header.payload_len >> 40) as u8);
            buf.push((self.header.payload_len >> 32) as u8);
            buf.push((self.header.payload_len >> 24) as u8);
            buf.push((self.header.payload_len >> 16) as u8);
            buf.push((self.header.payload_len >> 8) as u8);
            buf.push(self.header.payload_len as u8);
        }

        // Optional masking key
        if self.header.mask {
            buf.push(self.header.masking_key[0]);
            buf.push(self.header.masking_key[1]);
            buf.push(self.header.masking_key[2]);
            buf.push(self.header.masking_key[3]);
        }

        // Payload data
        buf.extend_from_slice(&self.payload.data[..]);

        buf
    }

    fn len_as_vec(&self) -> usize {
        let mut len = 0usize;

        // OpCode
        len += 1;

        // Mask and paylaod length
        len += 1;

        // Extended Payload length
        if self.header.payload_len > 125 && self.header.payload_len < u16::MAX as u64 {
            len += 2;
        } else if self.header.payload_len > u16::MAX as u64 {
            len += 8;
        }

        // Optional masking key
        if self.header.mask {
            len += 4;
        }

        // Payload data
        len += self.header.payload_len as usize;

        len
    }

    fn as_mut_raw_erased(&self) -> *mut () {
        let dup = Box::new(self.clone());
        return Box::into_raw(dup) as *mut _ as *mut ();
    }
}

impl Default for WebSocketFrame {
    fn default() -> WebSocketFrame {
        WebSocketFrame {
            frame_type: FrameType::Control,
            header: Header {
                op_code: CONTINUATION,
                mask: false,
                payload_len: 0u64,
                masking_key: [0u8; 4]
            },
            payload: Payload {
                data: Vec::<u8>::new()
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Codegen `extern "platform-intrinsic"` intrinsics.

use super::*;
use crate::prelude::*;

pub(super) fn codegen_simd_intrinsic_call<'tcx>(
    fx: &mut FunctionCx<'_, '_, 'tcx>,
    instance: Instance<'tcx>,
    args: &[mir::Operand<'tcx>],
    ret: CPlace<'tcx>,
    span: Span,
) {
    let def_id = instance.def_id();
    let substs = instance.substs;

    let intrinsic = fx.tcx.item_name(def_id).as_str();
    let intrinsic = &intrinsic[..];

    intrinsic_match! {
        fx, intrinsic, substs, args,
        _ => {
            fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
        };

        simd_cast, (c a) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
                let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();

                let from_signed = type_sign(lane_layout.ty);
                let to_signed = type_sign(ret_lane_layout.ty);

                let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
                CValue::by_val(ret_lane, ret_lane_layout)
            });
        };

        simd_eq, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, Equal|Equal(x, y) -> ret);
        };
        simd_ne, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, NotEqual|NotEqual(x, y) -> ret);
        };
        simd_lt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThan|SignedLessThan|LessThan(x, y) -> ret);
        };
        simd_le, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual|LessThanOrEqual(x, y) -> ret);
        };
        simd_gt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan|GreaterThan(x, y) -> ret);
        };
        simd_ge, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(
                fx,
                UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual|GreaterThanOrEqual
                (x, y) -> ret
            );
        };

        // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
        _ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);

            let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap();

            assert_eq!(x.layout(), y.layout());
            let layout = x.layout();

            let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);

            assert_eq!(lane_ty, ret_lane_ty);
            assert_eq!(u64::from(n), ret_lane_count);

            let total_len = lane_count * 2;

            let indexes = {
                use rustc_middle::mir::interpret::*;
                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");

                let idx_bytes = match idx_const {
                    ConstValue::ByRef { alloc, offset } => {
                        let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
                        let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */);
                        alloc.get_bytes(fx, ptr, size).unwrap()
                    }
                    _ => unreachable!("{:?}", idx_const),
                };

                (0..ret_lane_count).map(|i| {
                    let i = usize::try_from(i).unwrap();
                    let idx = rustc_middle::mir::interpret::read_target_uint(
                        fx.tcx.data_layout.endian,
                        &idx_bytes[4*i.. 4*i + 4],
                    ).expect("read_target_uint");
                    u16::try_from(idx).expect("try_from u32")
                }).collect::<Vec<u16>>()
            };

            for &idx in &indexes {
                assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
            }

            for (out_idx, in_idx) in indexes.into_iter().enumerate() {
                let in_lane = if u64::from(in_idx) < lane_count {
                    x.value_field(fx, mir::Field::new(in_idx.into()))
                } else {
                    y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap()))
                };
                let out_lane = ret.place_field(fx, mir::Field::new(out_idx));
                out_lane.write_cvalue(fx, in_lane);
            }
        };

        simd_insert, (c base, o idx, c val) {
            // FIXME validate
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_fatal(
                    span,
                    "Index argument for `simd_insert` is not a constant",
                );
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
            }

            ret.write_cvalue(fx, base);
            let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret_lane.write_cvalue(fx, val);
        };

        simd_extract, (c v, o idx) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_warn(
                    span,
                    "Index argument for `simd_extract` is not a constant",
                );
                let res = crate::trap::trap_unimplemented_ret_value(
                    fx,
                    ret.layout(),
                    "Index argument for `simd_extract` is not a constant",
                );
                ret.write_cvalue(fx, res);
                return;
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
            }

            let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret.write_cvalue(fx, ret_lane);
        };

        simd_add, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
        };
        simd_sub, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
        };
        simd_mul, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
        };
        simd_div, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
        };
        simd_shl, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ishl(x, y) -> ret);
        };
        simd_shr, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
        };
        simd_and, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, band(x, y) -> ret);
        };
        simd_or, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bor(x, y) -> ret);
        };
        simd_xor, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bxor(x, y) -> ret);
        };

        simd_fma, (c a, c b, c c) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            assert_eq!(a.layout(), b.layout());
            assert_eq!(a.layout(), c.layout());
            let layout = a.layout();

            let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
            assert_eq!(lane_count, ret_lane_count);
            let ret_lane_layout = fx.layout_of(ret_lane_ty);

            for lane in 0..lane_count {
                let lane = mir::Field::new(lane.try_into().unwrap());
                let a_lane = a.value_field(fx, lane).load_scalar(fx);
                let b_lane = b.value_field(fx, lane).load_scalar(fx);
                let c_lane = c.value_field(fx, lane).load_scalar(fx);

                let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
                let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);

                ret.place_field(fx, lane).write_cvalue(fx, res_lane);
            }
        };

        simd_fmin, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmin(x, y) -> ret);
        };
        simd_fmax, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmax(x, y) -> ret);
        };

        simd_reduce_add_ordered | simd_reduce_add_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fadd(a, b)
                } else {
                    fx.bcx.ins().iadd(a, b)
                }
            });
        };

        simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fmul(a, b)
                } else {
                    fx.bcx.ins().imul(a, b)
                }
            });
        };

        simd_reduce_all, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().band(a, b));
        };

        simd_reduce_any, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b));
        };

        // simd_fabs
        // simd_saturating_add
        // simd_bitmask
        // simd_select
        // simd_rem
    }
}
<commit_msg>Add simd_neg platform intrinsic<commit_after>//! Codegen `extern "platform-intrinsic"` intrinsics.

use super::*;
use crate::prelude::*;

pub(super) fn codegen_simd_intrinsic_call<'tcx>(
    fx: &mut FunctionCx<'_, '_, 'tcx>,
    instance: Instance<'tcx>,
    args: &[mir::Operand<'tcx>],
    ret: CPlace<'tcx>,
    span: Span,
) {
    let def_id = instance.def_id();
    let substs = instance.substs;

    let intrinsic = fx.tcx.item_name(def_id).as_str();
    let intrinsic = &intrinsic[..];

    intrinsic_match! {
        fx, intrinsic, substs, args,
        _ => {
            fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
        };

        simd_cast, (c a) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| {
                let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap();

                let from_signed = type_sign(lane_layout.ty);
                let to_signed = type_sign(ret_lane_layout.ty);

                let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed);
                CValue::by_val(ret_lane, ret_lane_layout)
            });
        };

        simd_eq, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, Equal|Equal(x, y) -> ret);
        };
        simd_ne, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, NotEqual|NotEqual(x, y) -> ret);
        };
        simd_lt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThan|SignedLessThan|LessThan(x, y) -> ret);
        };
        simd_le, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual|LessThanOrEqual(x, y) -> ret);
        };
        simd_gt, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan|GreaterThan(x, y) -> ret);
        };
        simd_ge, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_cmp!(
                fx,
                UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual|GreaterThanOrEqual
                (x, y) -> ret
            );
        };

        // simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
        _ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);

            let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap();

            assert_eq!(x.layout(), y.layout());
            let layout = x.layout();

            let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);

            assert_eq!(lane_ty, ret_lane_ty);
            assert_eq!(u64::from(n), ret_lane_count);

            let total_len = lane_count * 2;

            let indexes = {
                use rustc_middle::mir::interpret::*;
                let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");

                let idx_bytes = match idx_const {
                    ConstValue::ByRef { alloc, offset } => {
                        let ptr = Pointer::new(AllocId(0 /* dummy */), offset);
                        let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */);
                        alloc.get_bytes(fx, ptr, size).unwrap()
                    }
                    _ => unreachable!("{:?}", idx_const),
                };

                (0..ret_lane_count).map(|i| {
                    let i = usize::try_from(i).unwrap();
                    let idx = rustc_middle::mir::interpret::read_target_uint(
                        fx.tcx.data_layout.endian,
                        &idx_bytes[4*i.. 4*i + 4],
                    ).expect("read_target_uint");
                    u16::try_from(idx).expect("try_from u32")
                }).collect::<Vec<u16>>()
            };

            for &idx in &indexes {
                assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
            }

            for (out_idx, in_idx) in indexes.into_iter().enumerate() {
                let in_lane = if u64::from(in_idx) < lane_count {
                    x.value_field(fx, mir::Field::new(in_idx.into()))
                } else {
                    y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap()))
                };
                let out_lane = ret.place_field(fx, mir::Field::new(out_idx));
                out_lane.write_cvalue(fx, in_lane);
            }
        };

        simd_insert, (c base, o idx, c val) {
            // FIXME validate
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_fatal(
                    span,
                    "Index argument for `simd_insert` is not a constant",
                );
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
            }

            ret.write_cvalue(fx, base);
            let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret_lane.write_cvalue(fx, val);
        };

        simd_extract, (c v, o idx) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
                idx_const
            } else {
                fx.tcx.sess.span_warn(
                    span,
                    "Index argument for `simd_extract` is not a constant",
                );
                let res = crate::trap::trap_unimplemented_ret_value(
                    fx,
                    ret.layout(),
                    "Index argument for `simd_extract` is not a constant",
                );
                ret.write_cvalue(fx, res);
                return;
            };

            let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
            let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
            if idx >= lane_count.into() {
                fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
            }

            let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap()));
            ret.write_cvalue(fx, ret_lane);
        };

        simd_add, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret);
        };
        simd_sub, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret);
        };
        simd_mul, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret);
        };
        simd_div, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret);
        };
        simd_shl, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ishl(x, y) -> ret);
        };
        simd_shr, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, ushr|sshr(x, y) -> ret);
        };
        simd_and, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, band(x, y) -> ret);
        };
        simd_or, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bor(x, y) -> ret);
        };
        simd_xor, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_int_binop!(fx, bxor(x, y) -> ret);
        };

        simd_fma, (c a, c b, c c) {
            validate_simd_type!(fx, intrinsic, span, a.layout().ty);
            assert_eq!(a.layout(), b.layout());
            assert_eq!(a.layout(), c.layout());
            let layout = a.layout();

            let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
            let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
            assert_eq!(lane_count, ret_lane_count);
            let ret_lane_layout = fx.layout_of(ret_lane_ty);

            for lane in 0..lane_count {
                let lane = mir::Field::new(lane.try_into().unwrap());
                let a_lane = a.value_field(fx, lane).load_scalar(fx);
                let b_lane = b.value_field(fx, lane).load_scalar(fx);
                let c_lane = c.value_field(fx, lane).load_scalar(fx);

                let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
                let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);

                ret.place_field(fx, lane).write_cvalue(fx, res_lane);
            }
        };

        simd_fmin, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmin(x, y) -> ret);
        };
        simd_fmax, (c x, c y) {
            validate_simd_type!(fx, intrinsic, span, x.layout().ty);
            simd_flt_binop!(fx, fmax(x, y) -> ret);
        };

        simd_reduce_add_ordered | simd_reduce_add_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fadd(a, b)
                } else {
                    fx.bcx.ins().iadd(a, b)
                }
            });
        };

        simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce(fx, v, ret, |fx, lane_layout, a, b| {
                if lane_layout.ty.is_floating_point() {
                    fx.bcx.ins().fmul(a, b)
                } else {
                    fx.bcx.ins().imul(a, b)
                }
            });
        };

        simd_reduce_all, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().band(a, b));
        };

        simd_reduce_any, (c v) {
            validate_simd_type!(fx, intrinsic, span, v.layout().ty);
            simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b));
        };

        // simd_fabs
        // simd_saturating_add
        // simd_bitmask
        // simd_select
        // simd_rem
        // simd_neg
    }
}
<|endoftext|>