<commit_before>extern crate num_cpus;
extern crate itertools;
extern crate rand;
extern crate crossbeam;

use self::rand::{thread_rng, Rng};
use self::itertools::Itertools;
use self::crossbeam::{scope, ScopedJoinHandle};

use std::ops::Range;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::sync::{Mutex, RwLock, MutexGuard};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread::spawn;

use task::{TaskGenerator, Task};
use candidate::{WorkingCandidate, Candidate};
use solution::Solution;
use scaling::{ScalingFunction, proportionate};
use result::{Result as AbcResult, Error as AbcError};

/// Manages the parameters of the ABC algorithm.
pub struct HiveBuilder<S: Solution> {
    workers: usize,
    observers: usize,
    retries: usize,
    builder: Mutex<S::Builder>,
    threads: usize,
    scale: Box<ScalingFunction>,
}

impl<S: Solution> HiveBuilder<S> {
    /// Creates a new hive.
    ///
    /// * `builder` - Factory-like state that can be used while generating solutions.
    /// * `workers` - Number of working solution candidates to maintain at a time.
    pub fn new(builder: S::Builder, workers: usize) -> HiveBuilder<S> {
        if workers == 0 {
            panic!("HiveBuilder must have at least one worker.");
        }

        HiveBuilder {
            workers: workers,
            observers: workers,
            retries: workers,

            builder: Mutex::new(builder),
            threads: num_cpus::get(),
            scale: proportionate(),
        }
    }

    /// Sets the number of "bees" that will pick a candidate to work on at random.
    ///
    /// This defaults to the number of workers.
    pub fn set_observers(mut self, observers: usize) -> HiveBuilder<S> {
        self.observers = observers;
        self
    }

    /// Sets the number of times a candidate can go unimproved before being reinitialized.
    ///
    /// This defaults to the number of workers.
    pub fn set_retries(mut self, retries: usize) -> HiveBuilder<S> {
        self.retries = retries;
        self
    }

    /// Sets the number of worker threads to use while running.
    pub fn set_threads(mut self, threads: usize) -> HiveBuilder<S> {
        self.threads = threads;
        self
    }

    /// Sets the scaling function for observers to use.
    pub fn set_scaling(mut self, scale: Box<ScalingFunction>) -> HiveBuilder<S> {
        self.scale = scale;
        self
    }

    /// Activates the `HiveBuilder` to create a runnable object.
    pub fn build(self) -> AbcResult<Hive<S>> {
        Hive::new(self)
    }

    fn new_candidate(&self) -> AbcResult<Candidate<S>> {
        let mut builder = try!(self.builder.lock());
        let solution = S::make(&mut builder);
        drop(builder);
        Ok(Candidate::new(solution))
    }
}

/// Runs the ABC algorithm, maintaining any necessary state.
pub struct Hive<S: Solution> {
    hive: HiveBuilder<S>,

    working: Vec<RwLock<WorkingCandidate<S>>>,
    best: Mutex<Candidate<S>>,

    tasks: Mutex<Option<TaskGenerator>>,
    streaming: Option<Mutex<Sender<Candidate<S>>>>,
}

impl<S: Solution> Hive<S> {
    fn new(hive: HiveBuilder<S>) -> AbcResult<Hive<S>> {
        let tokens: Mutex<Range<usize>> = Mutex::new(0..hive.workers);
        let candidates = Mutex::new(Vec::with_capacity(hive.workers));
        let mut handles = Vec::<ScopedJoinHandle<AbcResult<()>>>::with_capacity(hive.threads);

        try!(crossbeam::scope(|scope| {
            for _ in 0..hive.threads {
                handles.push(scope.spawn(|| {
                    while let Some(_) = tokens.lock().unwrap().next() {
                        let candidate = try!(hive.new_candidate());
                        try!(candidates.lock()).push(candidate);
                    }
                    Ok(())
                }));
            }

            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
        }));

        let mut candidates = try!(candidates.into_inner());

        let best = {
            let best_candidate = candidates.iter()
                                           .fold1(|best, next| {
                                               if next.fitness > best.fitness {
                                                   next
                                               } else {
                                                   best
                                               }
                                           })
                                           .unwrap();
            Mutex::new(best_candidate.clone())
        };

        let working = candidates.drain(..)
                                .map(|c| RwLock::new(WorkingCandidate::new(c, hive.retries)))
                                .collect::<Vec<_>>();

        Ok(Hive {
            working: working,
            best: best,
            hive: hive,
            tasks: Mutex::new(None),
            streaming: None,
        })
    }

    fn current_working(&self) -> AbcResult<Vec<Candidate<S>>> {
        let mut current_working = Vec::with_capacity(self.working.len());
        for candidate_mutex in &self.working {
            let read_guard = try!(candidate_mutex.read());
            current_working.push(read_guard.candidate.clone())
        }
        Ok(current_working)
    }

    /// Returns a guard for the current best solution found by the hive.
    ///
    /// If the hive is running, you should drop the guard returned by this
    /// function as soon as convenient, since the logic of the hive can block
    /// on the availability of the associated mutex. If you plan on performing
    /// expensive computations, you should `drop` the guard as soon as
    /// possible, or acquire and clone it within a small block.
    pub fn get(&self) -> AbcResult<MutexGuard<Candidate<S>>> {
        self.best.lock().map_err(AbcError::from)
    }

    fn consider_improvement(&self, candidate: &Candidate<S>) -> AbcResult<()> {
        let mut best_guard = try!(self.best.lock());
        if candidate.fitness > best_guard.fitness {
            *best_guard = candidate.clone();
            if let Some(mutex) = self.streaming.as_ref() {
                // We're streaming, so we need to post the improved candidate.
                let sender_guard = try!(mutex.lock());
                // If this errors, the receiver was dropped, so we're done.
                if let Err(_) = sender_guard.send(candidate.clone()) {
                    try!(self.stop());
                }
            }
        }
        Ok(())
    }

    fn work_on(&self, current_working: &[Candidate<S>], n: usize) -> AbcResult<()> {
        let variant = Candidate::new(S::explore(current_working, n));
        let mut write_guard = try!(self.working[n].write());
        if variant.fitness > write_guard.candidate.fitness {
            *write_guard = WorkingCandidate::new(variant, self.hive.retries);
            try!(self.consider_improvement(&write_guard.candidate));
        } else {
            write_guard.deplete();
            // Scouting has been folded into the working process
            if write_guard.expired() {
                let candidate = try!(self.hive.new_candidate());
                *write_guard = WorkingCandidate::new(candidate, self.hive.retries);
                try!(self.consider_improvement(&write_guard.candidate));
            }
        }
        Ok(())
    }

    fn choose(&self, current_working: &[Candidate<S>]) -> usize {
        let fitnesses = (self.hive.scale)(current_working.iter()
                                                         .map(|candidate| candidate.fitness)
                                                         .collect::<Vec<f64>>());

        let running_totals = fitnesses.iter()
                                      .scan(0f64, |total, fitness| {
                                          *total += *fitness;
                                          Some(*total)
                                      })
                                      .collect::<Vec<f64>>();

        // Multiplying the choice point is equivalent to, and more efficient than, normalizing
        // all of the scaled fitnesses and having a choice point in [0,1)
        let total_fitness = running_totals.last().unwrap();
        let choice_point = thread_rng().next_f64() * total_fitness;

        for (i, total) in running_totals.iter().enumerate() {
            if *total > choice_point {
                return i;
            }
        }
        unreachable!();
    }

    fn execute(&self, task: &Task) -> AbcResult<()> {
        let current_working = try!(self.current_working());
        let index = match *task {
            Task::Worker(n) => n,
            Task::Observer(_) => self.choose(&current_working),
        };
        self.work_on(&current_working, index)
    }

    fn run(&self, tasks: TaskGenerator) -> AbcResult<()> {
        let mut guard = try!(self.tasks.lock());
        *guard = Some(tasks);
        drop(guard);

        let mut handles: Vec<ScopedJoinHandle<AbcResult<()>>> = Vec::new();

        scope(|scope| {
            for _ in 0..self.hive.threads {
                handles.push(scope.spawn(|| {
                    loop {
                        let mut guard = try!(self.tasks.lock());
                        let task = guard.as_mut().and_then(|gen| gen.next());
                        drop(guard);

                        match task {
                            Some(t) => try!(self.execute(&t)),
                            None => return Ok(()),
                        };
                    }
                }));
            }

            // Returns `Ok(())` only if all threads join cleanly, and the task
            // cycle is successfully cleared away.
            //
            // We avoid `try!` because we want all of the following logic to
            // execute unconditionally.
            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
                   .and(self.tasks
                            .lock()
                            .map(|mut tasks_guard| *tasks_guard = None)
                            .map_err(AbcError::from))
        })
    }

    /// Runs for a fixed number of rounds, then return the best solution found.
    ///
    /// If one of the worker threads panics while working, this will return
    /// `Err(abc::Error)`. Otherwise, it will return `Ok` with a `Candidate`.
    pub fn run_for_rounds(&self, rounds: usize) -> AbcResult<Candidate<S>> {
        let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers).max_rounds(rounds);
        try!(self.run(tasks));
        self.get().map(|guard| guard.clone())
    }

    /// Runs indefinitely in the background, providing a stream of results.
    ///
    /// This method consumes the hive, which will run until the `HiveBuilder` object
    /// is dropped. It returns an `mpsc::Receiver`, which receives a
    /// `Candidate` each time the hive improves on its best solution.
    pub fn stream(mut self) -> Receiver<Candidate<S>> {
        let (sender, receiver) = channel();
        spawn(move || {
            let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers);
            self.streaming = Some(Mutex::new(sender));
            self.run(tasks)
        });
        receiver
    }

    /// Stops a running hive.
    ///
    /// If a worker thread has panicked, this returns `Err(abc::Error)`.
    pub fn stop(&self) -> AbcResult<()> {
        let mut tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_mut().map_or((), |t| t.stop()))
    }

    /// Returns the current round of a running hive.
    ///
    /// If a worker thread has panicked and poisoned the task generator lock,
    /// `get_round` will return `Err(abc::Error)`.
    ///
    /// If the hive has not been run, `get_round` will return `Ok(None)`.
    pub fn get_round(&self) -> AbcResult<Option<usize>> {
        let tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_ref().map(|tasks| tasks.round))
    }
}

impl<S: Solution + Debug> Debug for Hive<S> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        for mutex in (&self.working).iter() {
            let working = mutex.read().unwrap();
            try!(write!(f, "..{:?}..\n", working.candidate));
        }
        let best_candidate = self.get().unwrap();
        write!(f, ">>{:?}<<", *best_candidate)
    }
}

impl<S: Solution> Drop for Hive<S> {
    fn drop(&mut self) {
        self.stop().unwrap_or(())
    }
}
<commit_msg>Improve documentation for Hive.get_round<commit_after>extern crate num_cpus;
extern crate itertools;
extern crate rand;
extern crate crossbeam;

use self::rand::{thread_rng, Rng};
use self::itertools::Itertools;
use self::crossbeam::{scope, ScopedJoinHandle};

use std::ops::Range;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::sync::{Mutex, RwLock, MutexGuard};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread::spawn;

use task::{TaskGenerator, Task};
use candidate::{WorkingCandidate, Candidate};
use solution::Solution;
use scaling::{ScalingFunction, proportionate};
use result::{Result as AbcResult, Error as AbcError};

/// Manages the parameters of the ABC algorithm.
pub struct HiveBuilder<S: Solution> {
    workers: usize,
    observers: usize,
    retries: usize,
    builder: Mutex<S::Builder>,
    threads: usize,
    scale: Box<ScalingFunction>,
}

impl<S: Solution> HiveBuilder<S> {
    /// Creates a new hive.
    ///
    /// * `builder` - Factory-like state that can be used while generating solutions.
    /// * `workers` - Number of working solution candidates to maintain at a time.
    pub fn new(builder: S::Builder, workers: usize) -> HiveBuilder<S> {
        if workers == 0 {
            panic!("HiveBuilder must have at least one worker.");
        }

        HiveBuilder {
            workers: workers,
            observers: workers,
            retries: workers,

            builder: Mutex::new(builder),
            threads: num_cpus::get(),
            scale: proportionate(),
        }
    }

    /// Sets the number of "bees" that will pick a candidate to work on at random.
    ///
    /// This defaults to the number of workers.
    pub fn set_observers(mut self, observers: usize) -> HiveBuilder<S> {
        self.observers = observers;
        self
    }

    /// Sets the number of times a candidate can go unimproved before being reinitialized.
    ///
    /// This defaults to the number of workers.
    pub fn set_retries(mut self, retries: usize) -> HiveBuilder<S> {
        self.retries = retries;
        self
    }

    /// Sets the number of worker threads to use while running.
    pub fn set_threads(mut self, threads: usize) -> HiveBuilder<S> {
        self.threads = threads;
        self
    }

    /// Sets the scaling function for observers to use.
    pub fn set_scaling(mut self, scale: Box<ScalingFunction>) -> HiveBuilder<S> {
        self.scale = scale;
        self
    }

    /// Activates the `HiveBuilder` to create a runnable object.
    pub fn build(self) -> AbcResult<Hive<S>> {
        Hive::new(self)
    }

    fn new_candidate(&self) -> AbcResult<Candidate<S>> {
        let mut builder = try!(self.builder.lock());
        let solution = S::make(&mut builder);
        drop(builder);
        Ok(Candidate::new(solution))
    }
}

/// Runs the ABC algorithm, maintaining any necessary state.
pub struct Hive<S: Solution> {
    hive: HiveBuilder<S>,

    working: Vec<RwLock<WorkingCandidate<S>>>,
    best: Mutex<Candidate<S>>,

    tasks: Mutex<Option<TaskGenerator>>,
    streaming: Option<Mutex<Sender<Candidate<S>>>>,
}

impl<S: Solution> Hive<S> {
    fn new(hive: HiveBuilder<S>) -> AbcResult<Hive<S>> {
        let tokens: Mutex<Range<usize>> = Mutex::new(0..hive.workers);
        let candidates = Mutex::new(Vec::with_capacity(hive.workers));
        let mut handles = Vec::<ScopedJoinHandle<AbcResult<()>>>::with_capacity(hive.threads);

        try!(crossbeam::scope(|scope| {
            for _ in 0..hive.threads {
                handles.push(scope.spawn(|| {
                    while let Some(_) = tokens.lock().unwrap().next() {
                        let candidate = try!(hive.new_candidate());
                        try!(candidates.lock()).push(candidate);
                    }
                    Ok(())
                }));
            }

            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
        }));

        let mut candidates = try!(candidates.into_inner());

        let best = {
            let best_candidate = candidates.iter()
                                           .fold1(|best, next| {
                                               if next.fitness > best.fitness {
                                                   next
                                               } else {
                                                   best
                                               }
                                           })
                                           .unwrap();
            Mutex::new(best_candidate.clone())
        };

        let working = candidates.drain(..)
                                .map(|c| RwLock::new(WorkingCandidate::new(c, hive.retries)))
                                .collect::<Vec<_>>();

        Ok(Hive {
            working: working,
            best: best,
            hive: hive,
            tasks: Mutex::new(None),
            streaming: None,
        })
    }

    fn current_working(&self) -> AbcResult<Vec<Candidate<S>>> {
        let mut current_working = Vec::with_capacity(self.working.len());
        for candidate_mutex in &self.working {
            let read_guard = try!(candidate_mutex.read());
            current_working.push(read_guard.candidate.clone())
        }
        Ok(current_working)
    }

    /// Returns a guard for the current best solution found by the hive.
    ///
    /// If the hive is running, you should drop the guard returned by this
    /// function as soon as convenient, since the logic of the hive can block
    /// on the availability of the associated mutex. If you plan on performing
    /// expensive computations, you should `drop` the guard as soon as
    /// possible, or acquire and clone it within a small block.
    pub fn get(&self) -> AbcResult<MutexGuard<Candidate<S>>> {
        self.best.lock().map_err(AbcError::from)
    }

    fn consider_improvement(&self, candidate: &Candidate<S>) -> AbcResult<()> {
        let mut best_guard = try!(self.best.lock());
        if candidate.fitness > best_guard.fitness {
            *best_guard = candidate.clone();
            if let Some(mutex) = self.streaming.as_ref() {
                // We're streaming, so we need to post the improved candidate.
                let sender_guard = try!(mutex.lock());
                // If this errors, the receiver was dropped, so we're done.
                if let Err(_) = sender_guard.send(candidate.clone()) {
                    try!(self.stop());
                }
            }
        }
        Ok(())
    }

    fn work_on(&self, current_working: &[Candidate<S>], n: usize) -> AbcResult<()> {
        let variant = Candidate::new(S::explore(current_working, n));
        let mut write_guard = try!(self.working[n].write());
        if variant.fitness > write_guard.candidate.fitness {
            *write_guard = WorkingCandidate::new(variant, self.hive.retries);
            try!(self.consider_improvement(&write_guard.candidate));
        } else {
            write_guard.deplete();
            // Scouting has been folded into the working process
            if write_guard.expired() {
                let candidate = try!(self.hive.new_candidate());
                *write_guard = WorkingCandidate::new(candidate, self.hive.retries);
                try!(self.consider_improvement(&write_guard.candidate));
            }
        }
        Ok(())
    }

    fn choose(&self, current_working: &[Candidate<S>]) -> usize {
        let fitnesses = (self.hive.scale)(current_working.iter()
                                                         .map(|candidate| candidate.fitness)
                                                         .collect::<Vec<f64>>());

        let running_totals = fitnesses.iter()
                                      .scan(0f64, |total, fitness| {
                                          *total += *fitness;
                                          Some(*total)
                                      })
                                      .collect::<Vec<f64>>();

        // Multiplying the choice point is equivalent to, and more efficient than, normalizing
        // all of the scaled fitnesses and having a choice point in [0,1)
        let total_fitness = running_totals.last().unwrap();
        let choice_point = thread_rng().next_f64() * total_fitness;

        for (i, total) in running_totals.iter().enumerate() {
            if *total > choice_point {
                return i;
            }
        }
        unreachable!();
    }

    fn execute(&self, task: &Task) -> AbcResult<()> {
        let current_working = try!(self.current_working());
        let index = match *task {
            Task::Worker(n) => n,
            Task::Observer(_) => self.choose(&current_working),
        };
        self.work_on(&current_working, index)
    }

    fn run(&self, tasks: TaskGenerator) -> AbcResult<()> {
        let mut guard = try!(self.tasks.lock());
        *guard = Some(tasks);
        drop(guard);

        let mut handles: Vec<ScopedJoinHandle<AbcResult<()>>> = Vec::new();

        scope(|scope| {
            for _ in 0..self.hive.threads {
                handles.push(scope.spawn(|| {
                    loop {
                        let mut guard = try!(self.tasks.lock());
                        let task = guard.as_mut().and_then(|gen| gen.next());
                        drop(guard);

                        match task {
                            Some(t) => try!(self.execute(&t)),
                            None => return Ok(()),
                        };
                    }
                }));
            }

            // Returns `Ok(())` only if all threads join cleanly, and the task
            // cycle is successfully cleared away.
            //
            // We avoid `try!` because we want all of the following logic to
            // execute unconditionally.
            handles.drain(..)
                   .fold(Ok(()), |result, handle| result.and(handle.join()))
                   .and(self.tasks
                            .lock()
                            .map(|mut tasks_guard| *tasks_guard = None)
                            .map_err(AbcError::from))
        })
    }

    /// Runs for a fixed number of rounds, then return the best solution found.
    ///
    /// If one of the worker threads panics while working, this will return
    /// `Err(abc::Error)`. Otherwise, it will return `Ok` with a `Candidate`.
    pub fn run_for_rounds(&self, rounds: usize) -> AbcResult<Candidate<S>> {
        let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers).max_rounds(rounds);
        try!(self.run(tasks));
        self.get().map(|guard| guard.clone())
    }

    /// Runs indefinitely in the background, providing a stream of results.
    ///
    /// This method consumes the hive, which will run until the `HiveBuilder` object
    /// is dropped. It returns an `mpsc::Receiver`, which receives a
    /// `Candidate` each time the hive improves on its best solution.
    pub fn stream(mut self) -> Receiver<Candidate<S>> {
        let (sender, receiver) = channel();
        spawn(move || {
            let tasks = TaskGenerator::new(self.hive.workers, self.hive.observers);
            self.streaming = Some(Mutex::new(sender));
            self.run(tasks)
        });
        receiver
    }

    /// Stops a running hive.
    ///
    /// If a worker thread has panicked, this returns `Err(abc::Error)`.
    pub fn stop(&self) -> AbcResult<()> {
        let mut tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_mut().map_or((), |t| t.stop()))
    }

    /// Returns the current round of a running hive.
    ///
    /// If a worker thread has panicked and poisoned the task generator lock,
    /// `get_round` will return `Err(abc::Error)`. If the hive has not been
    /// run, `get_round` will return `Ok(None)`.
    ///
    /// If the hive is running, this will return `Ok(Some(n))`. `n` will start
    /// at 0, and increment each time every task in the round has been claimed
    /// (though not necessarily completed) by a worker thread.
    pub fn get_round(&self) -> AbcResult<Option<usize>> {
        let tasks_guard = try!(self.tasks.lock());
        Ok(tasks_guard.as_ref().map(|tasks| tasks.round))
    }
}

impl<S: Solution + Debug> Debug for Hive<S> {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        for mutex in (&self.working).iter() {
            let working = mutex.read().unwrap();
            try!(write!(f, "..{:?}..\n", working.candidate));
        }
        let best_candidate = self.get().unwrap();
        write!(f, ">>{:?}<<", *best_candidate)
    }
}

impl<S: Solution> Drop for Hive<S> {
    fn drop(&mut self) {
        self.stop().unwrap_or(())
    }
}
<|endoftext|>


<commit_before>//! Two-step verfication of HOTP algorithm.
//!
//! # Example
//!
//! ```
//! extern crate otpauth;
//!
//! fn main() {
//!     let auth = otpauth::HOTP::new("python");
//!     let code = auth.generate(4);
//!     assert!(auth.verify(code, 0, 100));
//! }
//! ```
//!
use std::io::Cursor;

use crypto::digest::Digest;
use crypto::mac::Mac;
use crypto::hmac::Hmac;
use crypto::sha1::Sha1;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};

/// Two-step verfication of HOTP algorithm.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct HOTP {
    /// A secret token for the authentication
    pub secret: String,
}

impl HOTP {
    /// Constructs a new `HOTP`
    pub fn new<S: Into<String>>(secret: S) -> HOTP {
        HOTP { secret: secret.into() }
    }

    /// Generate a HOTP code.
    ///
    /// ``counter``: HOTP is a counter based algorithm.
    pub fn generate(&self, counter: usize) -> u32 {
        // Init Hmac
        let key = self.secret.clone().into_bytes();
        let mut hmac = Hmac::new(Sha1::new(), &key[..]);
        // Calc msg
        let mut wtr = vec![];
        // supress warning about unused_must_use
        let _ = wtr.write_u64::<BigEndian>(counter as u64);
        hmac.input(&wtr[..]);
        // Get digest
        let result = hmac.result();
        let digest = result.code();
        let ob = digest[19];
        let pos: usize = (ob & 15) as usize;
        let mut rdr = Cursor::new(digest[pos..pos + 4].to_vec());
        let base = rdr.read_u32::<BigEndian>().unwrap() & 0x7fffffff;
        base % 1000000
    }

    /// Valid a HOTP code.
    ///
    /// ``code``: A number that is less than 6 characters.
    ///
    /// ``last``: Guess HOTP code from ``last + 1`` range.
    ///
    /// ``trials``: Guess HOTP code end at ``last + trials + 1``.
    pub fn verify(&self, code: u32, last: usize, trials: usize) -> bool {
        let code_str = code.to_string();
        let code_bytes = code_str.as_bytes();
        if code_bytes.len() > 6 {
            return false;
        }
        for i in last + 1..last + trials + 1 {
            let valid_code = self.generate(i).to_string();
            let valid_bytes = valid_code.as_bytes();
            if code_bytes.len() != valid_code.len() {
                continue;
            }
            let mut rv = 0;
            for (a, b) in code_bytes.iter().zip(valid_bytes.iter()) {
                rv |= a ^ b;
            }
            if rv == 0 {
                return true;
            }
        }
        false
    }

    /// Generate the otpauth protocal string.
    ///
    /// ``label``: Label of the identifier.
    ///
    /// ``issuer``: The company, the organization or something else.
    ///
    /// ``counter``: Counter of the HOTP algorithm.
    pub fn to_uri<S: AsRef<str>>(&self, label: S, issuer: S, counter: usize) -> String {
        use base32::encode;
        use base32::Alphabet::RFC4648;

        let encoded_secret = encode(RFC4648 { padding: false }, self.secret.as_bytes());
        format!("otpauth://hotp/{}?secret={}&issuer={}&counter={}",
                label.as_ref(),
                encoded_secret,
                issuer.as_ref(),
                counter)
    }
}
<commit_msg>Remove unused import `crypto::digest::Digest`<commit_after>//! Two-step verfication of HOTP algorithm.
//!
//! # Example
//!
//! ```
//! extern crate otpauth;
//!
//! fn main() {
//!     let auth = otpauth::HOTP::new("python");
//!     let code = auth.generate(4);
//!     assert!(auth.verify(code, 0, 100));
//! }
//! ```
//!
use std::io::Cursor;

use crypto::mac::Mac;
use crypto::hmac::Hmac;
use crypto::sha1::Sha1;
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};

/// Two-step verfication of HOTP algorithm.
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct HOTP {
    /// A secret token for the authentication
    pub secret: String,
}

impl HOTP {
    /// Constructs a new `HOTP`
    pub fn new<S: Into<String>>(secret: S) -> HOTP {
        HOTP { secret: secret.into() }
    }

    /// Generate a HOTP code.
    ///
    /// ``counter``: HOTP is a counter based algorithm.
    pub fn generate(&self, counter: usize) -> u32 {
        // Init Hmac
        let key = self.secret.clone().into_bytes();
        let mut hmac = Hmac::new(Sha1::new(), &key[..]);
        // Calc msg
        let mut wtr = vec![];
        // supress warning about unused_must_use
        let _ = wtr.write_u64::<BigEndian>(counter as u64);
        hmac.input(&wtr[..]);
        // Get digest
        let result = hmac.result();
        let digest = result.code();
        let ob = digest[19];
        let pos: usize = (ob & 15) as usize;
        let mut rdr = Cursor::new(digest[pos..pos + 4].to_vec());
        let base = rdr.read_u32::<BigEndian>().unwrap() & 0x7fffffff;
        base % 1000000
    }

    /// Valid a HOTP code.
    ///
    /// ``code``: A number that is less than 6 characters.
    ///
    /// ``last``: Guess HOTP code from ``last + 1`` range.
    ///
    /// ``trials``: Guess HOTP code end at ``last + trials + 1``.
    pub fn verify(&self, code: u32, last: usize, trials: usize) -> bool {
        let code_str = code.to_string();
        let code_bytes = code_str.as_bytes();
        if code_bytes.len() > 6 {
            return false;
        }
        for i in last + 1..last + trials + 1 {
            let valid_code = self.generate(i).to_string();
            let valid_bytes = valid_code.as_bytes();
            if code_bytes.len() != valid_code.len() {
                continue;
            }
            let mut rv = 0;
            for (a, b) in code_bytes.iter().zip(valid_bytes.iter()) {
                rv |= a ^ b;
            }
            if rv == 0 {
                return true;
            }
        }
        false
    }

    /// Generate the otpauth protocal string.
    ///
    /// ``label``: Label of the identifier.
    ///
    /// ``issuer``: The company, the organization or something else.
    ///
    /// ``counter``: Counter of the HOTP algorithm.
    pub fn to_uri<S: AsRef<str>>(&self, label: S, issuer: S, counter: usize) -> String {
        use base32::encode;
        use base32::Alphabet::RFC4648;

        let encoded_secret = encode(RFC4648 { padding: false }, self.secret.as_bytes());
        format!("otpauth://hotp/{}?secret={}&issuer={}&counter={}",
                label.as_ref(),
                encoded_secret,
                issuer.as_ref(),
                counter)
    }
}
<|endoftext|>


<commit_before>use super::{Array1dView, Array1dViewMut, Array2dView, Array2dViewMut};

use openblas::ffi::*;

#[derive(Clone, Copy)]
pub enum Transpose {
  N,
  T,
}

impl<'a> Array1dView<'a, f32> {
  pub fn l2_norm(&'a self) -> f32 {
    let n = self.dim();
    let incx = self.stride();
    unsafe { openblas_sequential_cblas_snrm2(
        n as _,
        self.buf.as_ptr(),
        incx as _,
    ) }
  }

  pub fn elem_sum(&'a self) -> f32 {
    let x_n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    let mut x_sum = 0.0;
    for _ in 0 .. x_n {
      let x_i = self.buf[p];
      x_sum += x_i;
      p += incx;
    }
    x_sum
  }

  pub fn inner_prod(&'a self, alpha: f32, y: Array1dView<'a, f32>) -> f32 {
    let x_n = self.dim();
    let y_n = y.dim();
    assert_eq!(x_n, y_n);
    let incx = self.stride();
    let incy = y.stride();
    unsafe { openblas_sequential_cblas_sdot(
        x_n as _,
        alpha,
        self.buf.as_ptr(),
        incx as _,
        y.as_ptr(),
        incy as _,
    ) }
  }
}

impl<'a> Array1dViewMut<'a, f32> {
  pub fn add_scalar(&'a mut self, c: f32) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      self.buf[p] += c;
      p += incx;
    }
  }

  pub fn vector_add_scalar(&'a mut self, c: f32) {
    self.add_scalar(c);
  }

  pub fn square(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i * x_i;
      p += incx;
    }
  }

  pub fn vector_square(&'a mut self) {
    self.square();
  }

  pub fn sqrt(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i.sqrt();
      p += incx;
    }
  }

  pub fn vector_sqrt(&'a mut self) {
    self.sqrt();
  }

  pub fn reciprocal(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = 1.0 / x_i;
      p += incx;
    }
  }

  pub fn vector_recip(&'a mut self) {
    self.reciprocal();
  }

  pub fn exp(&mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i.exp();
      p += incx;
    }
  }

  pub fn scale(&'a mut self, alpha: f32) {
    let n = self.dim();
    let incx = self.stride();
    unsafe { openblas_sequential_cblas_sscal(
        n as _,
        alpha,
        self.buf.as_mut_ptr(),
        incx as _,
    ) }
  }

  pub fn vector_scale(&'a mut self, alpha: f32) {
    self.scale(alpha);
  }

  pub fn add(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    unsafe { openblas_sequential_cblas_saxpy(
        x_n as _,
        alpha,
        x.buf.as_ptr(),
        incx as _,
        self.buf.as_mut_ptr(),
        incy as _,
    ) };
  }

  pub fn vector_add(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    self.add(alpha, x);
  }

  pub fn average(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * (x_i - y_i);
      p += incx;
      q += incy;
    }
  }

  pub fn elem_mult(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * x_i * y_i;
      p += incx;
      q += incy;
    }
  }

  pub fn vector_elem_mult(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    self.elem_mult(alpha, x);
  }

  pub fn elem_div(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * y_i / x_i;
      p += incx;
      q += incy;
    }
  }
}

impl<'a> Array2dView<'a, f32> {
  pub fn matrix_diag(&'a self, y: Array1dViewMut<'a, f32>) {
    let (a_m, a_n) = self.dim();
    let y_m = y.dim();
    assert_eq!(a_m, a_n);
    assert_eq!(a_m, y_m);
    let (a_inc, lda) = self.stride();
    let incy = y.stride();
    assert_eq!(1, a_inc);
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. y_m {
      y.buf[q] = self.buf[p];
      p += a_inc + lda;
      q += incy;
    }
  }
}

impl<'a> Array2dViewMut<'a, f32> {
  pub fn matrix_add(&'a mut self, alpha: f32, x: Array2dView<'a, f32>) {
    let (x_m, x_n) = x.dim();
    let (y_m, y_n) = self.dim();
    assert_eq!(x_m, y_m);
    assert_eq!(x_n, y_n);
    let (incx, ldx) = x.stride();
    let (incy, ldy) = self.stride();
    if x_n == 1 {
      unsafe { openblas_sequential_cblas_saxpy(
          x_m as _,
          alpha,
          x.buf.as_ptr(),
          incx as _,
          self.buf.as_mut_ptr(),
          incy as _,
      ) };
    } else if x_m == 1 {
      unimplemented!();
    } else {
      unimplemented!();
    }
  }

  pub fn matrix_prod(&'a mut self, alpha: f32, a: Array2dView<'a, f32>, a_trans: Transpose, b: Array2dView<'a, f32>, b_trans: Transpose, beta: f32) {
    let (a_m, a_n) = a.dim();
    let (b_m, b_n) = b.dim();
    let (c_m, c_n) = self.dim();
    let (at_m, at_n) = match a_trans {
      Transpose::N => (a_m, a_n),
      Transpose::T => (a_n, a_m),
    };
    let (bt_m, bt_n) = match b_trans {
      Transpose::N => (b_m, b_n),
      Transpose::T => (b_n, b_m),
    };
    assert_eq!(c_m, at_m);
    assert_eq!(c_n, bt_n);
    assert_eq!(at_n, bt_m);
    let k = at_n;
    let (a_inc, lda) = a.stride();
    let (b_inc, ldb) = b.stride();
    let (c_inc, ldc) = self.stride();
    assert_eq!(1, a_inc);
    assert_eq!(1, b_inc);
    assert_eq!(1, c_inc);
    unsafe { openblas_sequential_cblas_sgemm(
        CblasOrder::ColMajor,
        match a_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        match b_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        c_m as _, c_n as _, k as _,
        alpha,
        a.buf.as_ptr(), lda as _,
        b.buf.as_ptr(), ldb as _,
        beta,
        self.buf.as_mut_ptr(), ldc as _,
    ) };
  }
}

impl<'a> Array2dViewMut<'a, f64> {
  pub fn matrix_prod(&'a mut self) {
    unimplemented!();
  }
}
<commit_msg>Bugfix: online averaging of 2 vectors was broken.<commit_after>use super::{Array1dView, Array1dViewMut, Array2dView, Array2dViewMut};

use openblas::ffi::*;

#[derive(Clone, Copy)]
pub enum Transpose {
  N,
  T,
}

impl<'a> Array1dView<'a, f32> {
  pub fn l2_norm(&'a self) -> f32 {
    let n = self.dim();
    let incx = self.stride();
    unsafe { openblas_sequential_cblas_snrm2(
        n as _,
        self.buf.as_ptr(),
        incx as _,
    ) }
  }

  pub fn elem_sum(&'a self) -> f32 {
    let x_n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    let mut x_sum = 0.0;
    for _ in 0 .. x_n {
      let x_i = self.buf[p];
      x_sum += x_i;
      p += incx;
    }
    x_sum
  }

  pub fn inner_prod(&'a self, alpha: f32, y: Array1dView<'a, f32>) -> f32 {
    let x_n = self.dim();
    let y_n = y.dim();
    assert_eq!(x_n, y_n);
    let incx = self.stride();
    let incy = y.stride();
    unsafe { openblas_sequential_cblas_sdot(
        x_n as _,
        alpha,
        self.buf.as_ptr(),
        incx as _,
        y.as_ptr(),
        incy as _,
    ) }
  }
}

impl<'a> Array1dViewMut<'a, f32> {
  pub fn add_scalar(&'a mut self, c: f32) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      self.buf[p] += c;
      p += incx;
    }
  }

  pub fn vector_add_scalar(&'a mut self, c: f32) {
    self.add_scalar(c);
  }

  pub fn square(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i * x_i;
      p += incx;
    }
  }

  pub fn vector_square(&'a mut self) {
    self.square();
  }

  pub fn sqrt(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i.sqrt();
      p += incx;
    }
  }

  pub fn vector_sqrt(&'a mut self) {
    self.sqrt();
  }

  pub fn reciprocal(&'a mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = 1.0 / x_i;
      p += incx;
    }
  }

  pub fn vector_recip(&'a mut self) {
    self.reciprocal();
  }

  pub fn exp(&mut self) {
    let n = self.dim();
    let incx = self.stride();
    let mut p = 0;
    for _ in 0 .. n {
      let x_i = self.buf[p];
      self.buf[p] = x_i.exp();
      p += incx;
    }
  }

  pub fn scale(&'a mut self, alpha: f32) {
    let n = self.dim();
    let incx = self.stride();
    unsafe { openblas_sequential_cblas_sscal(
        n as _,
        alpha,
        self.buf.as_mut_ptr(),
        incx as _,
    ) }
  }

  pub fn vector_scale(&'a mut self, alpha: f32) {
    self.scale(alpha);
  }

  pub fn add(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    unsafe { openblas_sequential_cblas_saxpy(
        x_n as _,
        alpha,
        x.buf.as_ptr(),
        incx as _,
        self.buf.as_mut_ptr(),
        incy as _,
    ) };
  }

  pub fn vector_add(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    self.add(alpha, x);
  }

  pub fn average(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = y_i + alpha * (x_i - y_i);
      p += incx;
      q += incy;
    }
  }

  pub fn elem_mult(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * x_i * y_i;
      p += incx;
      q += incy;
    }
  }

  pub fn vector_elem_mult(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    self.elem_mult(alpha, x);
  }

  pub fn elem_div(&'a mut self, alpha: f32, x: Array1dView<'a, f32>) {
    let x_n = x.dim();
    let y_n = self.dim();
    assert_eq!(x_n, y_n);
    let incx = x.stride();
    let incy = self.stride();
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. x_n {
      let x_i = x.buf[p];
      let y_i = self.buf[q];
      self.buf[q] = alpha * y_i / x_i;
      p += incx;
      q += incy;
    }
  }
}

impl<'a> Array2dView<'a, f32> {
  pub fn matrix_diag(&'a self, y: Array1dViewMut<'a, f32>) {
    let (a_m, a_n) = self.dim();
    let y_m = y.dim();
    assert_eq!(a_m, a_n);
    assert_eq!(a_m, y_m);
    let (a_inc, lda) = self.stride();
    let incy = y.stride();
    assert_eq!(1, a_inc);
    let mut p = 0;
    let mut q = 0;
    for _ in 0 .. y_m {
      y.buf[q] = self.buf[p];
      p += a_inc + lda;
      q += incy;
    }
  }
}

impl<'a> Array2dViewMut<'a, f32> {
  pub fn matrix_add(&'a mut self, alpha: f32, x: Array2dView<'a, f32>) {
    let (x_m, x_n) = x.dim();
    let (y_m, y_n) = self.dim();
    assert_eq!(x_m, y_m);
    assert_eq!(x_n, y_n);
    let (incx, ldx) = x.stride();
    let (incy, ldy) = self.stride();
    if x_n == 1 {
      unsafe { openblas_sequential_cblas_saxpy(
          x_m as _,
          alpha,
          x.buf.as_ptr(),
          incx as _,
          self.buf.as_mut_ptr(),
          incy as _,
      ) };
    } else if x_m == 1 {
      unimplemented!();
    } else {
      unimplemented!();
    }
  }

  pub fn matrix_prod(&'a mut self, alpha: f32, a: Array2dView<'a, f32>, a_trans: Transpose, b: Array2dView<'a, f32>, b_trans: Transpose, beta: f32) {
    let (a_m, a_n) = a.dim();
    let (b_m, b_n) = b.dim();
    let (c_m, c_n) = self.dim();
    let (at_m, at_n) = match a_trans {
      Transpose::N => (a_m, a_n),
      Transpose::T => (a_n, a_m),
    };
    let (bt_m, bt_n) = match b_trans {
      Transpose::N => (b_m, b_n),
      Transpose::T => (b_n, b_m),
    };
    assert_eq!(c_m, at_m);
    assert_eq!(c_n, bt_n);
    assert_eq!(at_n, bt_m);
    let k = at_n;
    let (a_inc, lda) = a.stride();
    let (b_inc, ldb) = b.stride();
    let (c_inc, ldc) = self.stride();
    assert_eq!(1, a_inc);
    assert_eq!(1, b_inc);
    assert_eq!(1, c_inc);
    unsafe { openblas_sequential_cblas_sgemm(
        CblasOrder::ColMajor,
        match a_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        match b_trans {
          Transpose::N => CblasTranspose::NoTrans,
          Transpose::T => CblasTranspose::Trans,
        },
        c_m as _, c_n as _, k as _,
        alpha,
        a.buf.as_ptr(), lda as _,
        b.buf.as_ptr(), ldb as _,
        beta,
        self.buf.as_mut_ptr(), ldc as _,
    ) };
  }
}

impl<'a> Array2dViewMut<'a, f64> {
  pub fn matrix_prod(&'a mut self) {
    unimplemented!();
  }
}
<|endoftext|>


<commit_before>
/* vvv SHOULD LOOK LIKE THIS vvv

qregister_wire_fns!{

    fn load_stores |cfg: &Cfg| => {

        single PositionStore;

        single physics::LinearVelocityStore{ name: "LinearVelocityStore" }

        single ScoreStore{ init: ScoreStore::from_file(cfg.get_string("score_file.txt")) }

    }

    fn load_services => {

        single Timing;

        option Physics;

        alternative(Physics) nphysics::Physics{ name: "NPhysicsPhysics" }

        alternative(Physics) LegacyPhysics{ init: LegacyPhysics::new("0.1.0") }

    }
}

pub fn load_module(register: &mut Register<MyBase>, cfg: &Cfg){

    load_stores(register, cfg);
    load_services(register);

    register.wire_alternative("Physics", "LegacyPhysics");
}

*/

#[macro_export]
macro_rules! qregister_wire_fns {
    () => {};
    ($col:ident <- single $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            impl super::OptionReflect for $ty {
                fn option_name() -> &'static str { $name }
            }

            $col.singles.push(({$name}.to_string(), box {$init}));

            qregister_wire_fns!($($ff)*);
        }
    };
    ($col:ident <- single $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: $name, init: $init }
            $($ff)*
        }
    };
    ($col:ident <- single $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };
    ($col:ident <- single $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: $name, init: <$ty as Default>::default() }
            $($ff)*
        }
    };
    ($col:ident <- single $ty:ty; $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: stringify!($ty) }
            $($ff)*
        }
    };
    ($col:ident <- option $ty:ty { name: $name:expr } $($ff:tt)*) => {
        {
            impl super::OptionReflect for $ty {
                fn option_name() -> &'static str { $name }
            }

            $col.options.push({$name}.to_string());

            qregister_wire_fns!($($ff)*);
        }
    };
    ($col:ident <- option $ty:ty; $($ff:tt)*) => {
        qregister_wire_fns!($col <- option $ty { name: stringify!($ty) });
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            $col.alternatives.push((<$opt as super::OptionReflect>::option_name().to_string(),
                                    {$name}.to_string(),
                                    box {$init}));

            qregister_wire_fns!($($ff)*);
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: $name, init: $init }
            $($ff)*
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: $name, init: box <$ty as Default>::default() as Box<$opt> }
            $($ff)*
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty; $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty) }
            $($ff)*
        }
    };
}

use std::any::Any;
pub trait Base: Any + 'static {}
impl<B: ?Sized + Base> Base for Box<B> {}

pub trait SecondBase: Base {}

#[derive(Default)]
pub struct Foo;
impl Base for Foo {}

#[derive(Default)]
pub struct Bar;
impl Base for Bar {}
impl SecondBase for Bar {}

#[test]
pub fn test(){
    struct Collect {
        singles: Vec<(String, Box<Base>)>,
        options: Vec<String>,
        alternatives: Vec<(String, String, Box<Base>)>,
    }

    let mut col = Collect{ singles: vec![], options: vec![], alternatives: vec![] };

    qregister_wire_fns!{
        col <- single Foo{ init: Foo }
        col <- option SecondBase{ name: "2ndBase" }
        col <- alternative(SecondBase) Bar;
    }
}

<commit_msg>Update macros.rs<commit_after>
/* vvv SHOULD LOOK LIKE THIS vvv

qregister_load_fns!{

    fn load_stores |cfg: &Cfg| => {

        single PositionStore;

        single physics::LinearVelocityStore{ name: "LinearVelocityStore" }

        single ScoreStore{ init: ScoreStore::from_file(cfg.get_string("score_file.txt")) }

    }

    fn load_services => {

        single Timing;

        option Physics;

        alternative(Physics) nphysics::Physics{ name: "NPhysicsPhysics" }

        alternative(Physics) LegacyPhysics{ init: LegacyPhysics::new("0.1.0") }

    }
}

pub fn load_module(register: &mut RegisterModifier<MyBase>, cfg: &Cfg){

    load_stores(register, cfg);
    load_services(register);

    register.wire_alternative("Physics", Some("LegacyPhysics"));
}

*/

#[macro_export]
macro_rules! qregister_wire_fns {
    () => {};
    ($col:ident <- single $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            impl super::OptionReflect for $ty {
                fn option_name() -> &'static str { $name }
            }

            $col.singles.push(({$name}.to_string(), box {$init}));

            qregister_wire_fns!($($ff)*);
        }
    };
    ($col:ident <- single $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: $name, init: $init }
            $($ff)*
        }
    };
    ($col:ident <- single $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };
    ($col:ident <- single $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: $name, init: <$ty as Default>::default() }
            $($ff)*
        }
    };
    ($col:ident <- single $ty:ty; $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- single $ty { name: stringify!($ty) }
            $($ff)*
        }
    };
    ($col:ident <- option $ty:ty { name: $name:expr } $($ff:tt)*) => {
        {
            impl super::OptionReflect for $ty {
                fn option_name() -> &'static str { $name }
            }

            $col.options.push({$name}.to_string());

            qregister_wire_fns!($($ff)*);
        }
    };
    ($col:ident <- option $ty:ty; $($ff:tt)*) => {
        qregister_wire_fns!($col <- option $ty { name: stringify!($ty) });
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            $col.alternatives.push((<$opt as super::OptionReflect>::option_name().to_string(),
                                    {$name}.to_string(),
                                    box {$init}));

            qregister_wire_fns!($($ff)*);
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: $name, init: $init }
            $($ff)*
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: $name, init: box <$ty as Default>::default() as Box<$opt> }
            $($ff)*
        }
    };
    ($col:ident <- alternative($opt:ty) $ty:ty; $($ff:tt)*) => {
        qregister_wire_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty) }
            $($ff)*
        }
    };
}

use std::any::Any;
pub trait Base: Any + 'static {}
impl<B: ?Sized + Base> Base for Box<B> {}

pub trait SecondBase: Base {}

#[derive(Default)]
pub struct Foo;
impl Base for Foo {}

#[derive(Default)]
pub struct Bar;
impl Base for Bar {}
impl SecondBase for Bar {}

#[test]
pub fn test(){
    struct Collect {
        singles: Vec<(String, Box<Base>)>,
        options: Vec<String>,
        alternatives: Vec<(String, String, Box<Base>)>,
    }

    let mut col = Collect{ singles: vec![], options: vec![], alternatives: vec![] };

    qregister_wire_fns!{
        col <- single Foo{ init: Foo }
        col <- option SecondBase{ name: "2ndBase" }
        col <- alternative(SecondBase) Bar;
    }
}

<|endoftext|>


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

use std::mem::MaybeUninit;

// See docs of the `procedural-masquerade` crate.
define_invoke_proc_macro!(cssparser_internal__invoke_proc_macro);

/// Expands to a `match` expression with string patterns,
/// matching case-insensitively in the ASCII range.
///
/// The patterns must not contain ASCII upper case letters. (They must be already be lower-cased.)
///
/// # Example
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everythig in its own main
/// # fn dummy(function_name: &String) { let _ =
/// match_ignore_ascii_case! { &function_name,
///     "rgb" => parse_rgb(..),
///     "rgba" => parse_rgba(..),
///     "hsl" => parse_hsl(..),
///     "hsla" => parse_hsla(..),
///     _ => Err(format!("unknown function: {}", function_name))
/// }
/// # ;}
/// # use std::ops::RangeFull;
/// # fn parse_rgb(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_rgba(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsl(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsla(_: RangeFull) -> Result<(), String> { Ok(()) }
/// ```
#[macro_export]
macro_rules! match_ignore_ascii_case {
    ( $input:expr, $( $match_body:tt )* ) => {
        {
            cssparser_internal__invoke_proc_macro! {
                cssparser_internal__assert_ascii_lowercase__max_len!( $( $match_body )* )
            }

            {
                // MAX_LENGTH is generated by cssparser_internal__assert_ascii_lowercase__max_len
                cssparser_internal__to_lowercase!($input, MAX_LENGTH => lowercase);
                // "A" is a short string that we know is different for every string pattern,
                // since we’ve verified that none of them include ASCII upper case letters.
                match lowercase.unwrap_or("A") {
                    $( $match_body )*
                }
            }
        }
    };
}

/// Define a function `$name(&str) -> Option<&'static $ValueType>`
///
/// The function finds a match for the input string
/// in a [`phf` map](https://github.com/sfackler/rust-phf)
/// and returns a reference to the corresponding value.
/// Matching is case-insensitive in the ASCII range.
///
/// ## Example:
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everything in its own main
///
/// fn color_rgb(input: &str) -> Option<(u8, u8, u8)> {
///     ascii_case_insensitive_phf_map! {
///         keyword -> (u8, u8, u8) = {
///             "red" => (255, 0, 0),
///             "green" => (0, 255, 0),
///             "blue" => (0, 0, 255),
///         }
///     }
///     keyword(input).cloned()
/// }
#[macro_export]
macro_rules! ascii_case_insensitive_phf_map {
    ($name: ident -> $ValueType: ty = { $( $key: expr => $value: expr ),* }) => {
        ascii_case_insensitive_phf_map!($name -> $ValueType = { $( $key => $value, )* })
    };
    ($name: ident -> $ValueType: ty = { $( $key: expr => $value: expr, )* }) => {
        pub fn $name(input: &str) -> Option<&'static $ValueType> {
            cssparser_internal__invoke_proc_macro! {
                cssparser_internal__phf_map!( ($ValueType) $( $key ($value) )+ )
            }

            {
                cssparser_internal__invoke_proc_macro! {
                    cssparser_internal__max_len!( $( $key )+ )
                }
                // MAX_LENGTH is generated by cssparser_internal__max_len
                cssparser_internal__to_lowercase!(input, MAX_LENGTH => lowercase);
                lowercase.and_then(|s| MAP.get(s))
            }
        }
    }
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This macro is not part of the public API. It can change or be removed between any versions.**
///
/// Define a local variable named `$output`
/// and assign it the result of calling `_internal__to_lowercase`
/// with a stack-allocated buffer of length `$BUFFER_SIZE`.
#[macro_export]
#[doc(hidden)]
macro_rules! cssparser_internal__to_lowercase {
    ($input: expr, $BUFFER_SIZE: expr => $output: ident) => {
        #[allow(unsafe_code)]
        let mut buffer = unsafe {
            ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit<u8>; $BUFFER_SIZE]>::uninit().assume_init()
        };
        let input: &str = $input;
        let $output = $crate::_internal__to_lowercase(&mut buffer, input);
    };
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This function is not part of the public API. It can change or be removed between any verisons.**
///
/// If `input` is larger than buffer, return `None`.
/// Otherwise, return `input` ASCII-lowercased, using `buffer` as temporary space if necessary.
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn _internal__to_lowercase<'a>(buffer: &'a mut [MaybeUninit<u8>], input: &'a str) -> Option<&'a str> {
    if let Some(buffer) = buffer.get_mut(..input.len()) {
        if let Some(first_uppercase) = input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) {
            let buffer = unsafe {
                let ptr = buffer.as_mut_ptr() as *mut u8;
                std::ptr::copy_nonoverlapping(input.as_bytes().as_ptr(), ptr, input.len());
                &mut *(buffer as *mut [MaybeUninit<u8>] as *mut [u8])
            };
            buffer[first_uppercase..].make_ascii_lowercase();
            // `buffer` was initialized to a copy of `input` (which is &str so well-formed UTF-8)
            // then lowercased (which preserves UTF-8 well-formedness)
            unsafe { Some(::std::str::from_utf8_unchecked(buffer)) }
        } else {
            // Input is already lower-case
            Some(input)
        }
    } else {
        // Input is longer than buffer, which has the length of the longest expected string:
        // none of the expected strings would match.
        None
    }
}

#[cfg(feature = "dummy_match_byte")]
macro_rules! match_byte {
    ($value:expr, $($rest:tt)* ) => {
        match $value {
            $(
                $rest
            )+
        }
    };
}
<commit_msg>Use [T]::copy_from_slice instead of ptr::copy_nonoverlapping<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::mem::MaybeUninit;

// See docs of the `procedural-masquerade` crate.
define_invoke_proc_macro!(cssparser_internal__invoke_proc_macro);

/// Expands to a `match` expression with string patterns,
/// matching case-insensitively in the ASCII range.
///
/// The patterns must not contain ASCII upper case letters. (They must be already be lower-cased.)
///
/// # Example
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everythig in its own main
/// # fn dummy(function_name: &String) { let _ =
/// match_ignore_ascii_case! { &function_name,
///     "rgb" => parse_rgb(..),
///     "rgba" => parse_rgba(..),
///     "hsl" => parse_hsl(..),
///     "hsla" => parse_hsla(..),
///     _ => Err(format!("unknown function: {}", function_name))
/// }
/// # ;}
/// # use std::ops::RangeFull;
/// # fn parse_rgb(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_rgba(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsl(_: RangeFull) -> Result<(), String> { Ok(()) }
/// # fn parse_hsla(_: RangeFull) -> Result<(), String> { Ok(()) }
/// ```
#[macro_export]
macro_rules! match_ignore_ascii_case {
    ( $input:expr, $( $match_body:tt )* ) => {
        {
            cssparser_internal__invoke_proc_macro! {
                cssparser_internal__assert_ascii_lowercase__max_len!( $( $match_body )* )
            }

            {
                // MAX_LENGTH is generated by cssparser_internal__assert_ascii_lowercase__max_len
                cssparser_internal__to_lowercase!($input, MAX_LENGTH => lowercase);
                // "A" is a short string that we know is different for every string pattern,
                // since we’ve verified that none of them include ASCII upper case letters.
                match lowercase.unwrap_or("A") {
                    $( $match_body )*
                }
            }
        }
    };
}

/// Define a function `$name(&str) -> Option<&'static $ValueType>`
///
/// The function finds a match for the input string
/// in a [`phf` map](https://github.com/sfackler/rust-phf)
/// and returns a reference to the corresponding value.
/// Matching is case-insensitive in the ASCII range.
///
/// ## Example:
///
/// ```rust
/// #[macro_use] extern crate cssparser;
///
/// # fn main() {}  // Make doctest not wrap everything in its own main
///
/// fn color_rgb(input: &str) -> Option<(u8, u8, u8)> {
///     ascii_case_insensitive_phf_map! {
///         keyword -> (u8, u8, u8) = {
///             "red" => (255, 0, 0),
///             "green" => (0, 255, 0),
///             "blue" => (0, 0, 255),
///         }
///     }
///     keyword(input).cloned()
/// }
#[macro_export]
macro_rules! ascii_case_insensitive_phf_map {
    ($name: ident -> $ValueType: ty = { $( $key: expr => $value: expr ),* }) => {
        ascii_case_insensitive_phf_map!($name -> $ValueType = { $( $key => $value, )* })
    };
    ($name: ident -> $ValueType: ty = { $( $key: expr => $value: expr, )* }) => {
        pub fn $name(input: &str) -> Option<&'static $ValueType> {
            cssparser_internal__invoke_proc_macro! {
                cssparser_internal__phf_map!( ($ValueType) $( $key ($value) )+ )
            }

            {
                cssparser_internal__invoke_proc_macro! {
                    cssparser_internal__max_len!( $( $key )+ )
                }
                // MAX_LENGTH is generated by cssparser_internal__max_len
                cssparser_internal__to_lowercase!(input, MAX_LENGTH => lowercase);
                lowercase.and_then(|s| MAP.get(s))
            }
        }
    }
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This macro is not part of the public API. It can change or be removed between any versions.**
///
/// Define a local variable named `$output`
/// and assign it the result of calling `_internal__to_lowercase`
/// with a stack-allocated buffer of length `$BUFFER_SIZE`.
#[macro_export]
#[doc(hidden)]
macro_rules! cssparser_internal__to_lowercase {
    ($input: expr, $BUFFER_SIZE: expr => $output: ident) => {
        #[allow(unsafe_code)]
        let mut buffer = unsafe {
            ::std::mem::MaybeUninit::<[::std::mem::MaybeUninit<u8>; $BUFFER_SIZE]>::uninit()
                .assume_init()
        };
        let input: &str = $input;
        let $output = $crate::_internal__to_lowercase(&mut buffer, input);
    };
}

/// Implementation detail of match_ignore_ascii_case! and ascii_case_insensitive_phf_map! macros.
///
/// **This function is not part of the public API. It can change or be removed between any verisons.**
///
/// If `input` is larger than buffer, return `None`.
/// Otherwise, return `input` ASCII-lowercased, using `buffer` as temporary space if necessary.
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn _internal__to_lowercase<'a>(
    buffer: &'a mut [MaybeUninit<u8>],
    input: &'a str,
) -> Option<&'a str> {
    if let Some(buffer) = buffer.get_mut(..input.len()) {
        if let Some(first_uppercase) = input.bytes().position(|byte| matches!(byte, b'A'..=b'Z')) {
            unsafe {
                // This cast doesn’t change the pointer’s validity
                // since `u8` has the same layout as `MaybeUninit<u8>`:
                let input_bytes = &*(input.as_bytes() as *const [u8] as *const [MaybeUninit<u8>]);

                buffer.copy_from_slice(&*input_bytes);

                // Same as above re layout, plus these bytes have been initialized:
                let buffer = &mut *(buffer as *mut [MaybeUninit<u8>] as *mut [u8]);

                buffer[first_uppercase..].make_ascii_lowercase();
                // `buffer` was initialized to a copy of `input`
                // (which is `&str` so well-formed UTF-8)
                // then ASCII-lowercased (which preserves UTF-8 well-formedness):
                Some(::std::str::from_utf8_unchecked(buffer))
            }
        } else {
            // Input is already lower-case
            Some(input)
        }
    } else {
        // Input is longer than buffer, which has the length of the longest expected string:
        // none of the expected strings would match.
        None
    }
}

#[cfg(feature = "dummy_match_byte")]
macro_rules! match_byte {
    ($value:expr, $($rest:tt)* ) => {
        match $value {
            $(
                $rest
            )+
        }
    };
}
<|endoftext|>


<commit_before>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![allow(dead_code)]

//! Most transactions can mutate the Mentat metadata by transacting assertions:
//!
//! - they can add (and, eventually, retract and alter) recognized idents using the `:db/ident`
//!   attribute;
//!
//! - they can add (and, eventually, retract and alter) schema attributes using various `:db/*`
//!   attributes;
//!
//! - eventually, they will be able to add (and possibly retract) entid partitions using a Mentat
//!   equivalent (perhaps :db/partition or :db.partition/start) to Datomic's `:db.install/partition`
//!   attribute.
//!
//! This module recognizes, validates, applies, and reports on these mutations.

use failure::ResultExt;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;

use add_retract_alter_set::{
    AddRetractAlterSet,
};
use edn::symbols;
use entids;
use errors::{
    DbErrorKind,
    Result,
};
use mentat_core::{
    attribute,
    Entid,
    Schema,
    AttributeMap,
    TypedValue,
    ValueType,
};

use schema::{
    AttributeBuilder,
    AttributeValidation,
};

/// An alteration to an attribute.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum AttributeAlteration {
    /// From http://blog.datomic.com/2014/01/schema-alteration.html:
    /// - rename attributes
    /// - rename your own programmatic identities (uses of :db/ident)
    /// - add or remove indexes
    Index,
    /// - add or remove uniqueness constraints
    Unique,
    /// - change attribute cardinality
    Cardinality,
    /// - change whether history is retained for an attribute
    NoHistory,
    /// - change whether an attribute is treated as a component
    IsComponent,
}

/// An alteration to an ident.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum IdentAlteration {
    Ident(symbols::Keyword),
}

/// Summarizes changes to metadata such as a a `Schema` and (in the future) a `PartitionMap`.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct MetadataReport {
    // Entids that were not present in the original `AttributeMap` that was mutated.
    pub attributes_installed: BTreeSet<Entid>,

    // Entids that were present in the original `AttributeMap` that was mutated, together with a
    // representation of the mutations that were applied.
    pub attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>>,

    // Idents that were installed into the `AttributeMap`.
    pub idents_altered: BTreeMap<Entid, IdentAlteration>,
}

impl MetadataReport {
    pub fn attributes_did_change(&self) -> bool {
        !(self.attributes_installed.is_empty() &&
          self.attributes_altered.is_empty())
    }
}

/// Update a `AttributeMap` in place from the given `[e a typed_value]` triples.
///
/// This is suitable for producing a `AttributeMap` from the `schema` materialized view, which does not
/// contain install and alter markers.
///
/// Returns a report summarizing the mutations that were applied.
pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut AttributeMap, assertions: A, retractions: R) -> Result<MetadataReport>
    where A: IntoIterator<Item=(Entid, Entid, TypedValue)>,
          R: IntoIterator<Item=(Entid, Entid, TypedValue)> {

    fn attribute_builder_to_modify(attribute_id: Entid, existing: &AttributeMap) -> AttributeBuilder {
        existing.get(&attribute_id)
                .map(AttributeBuilder::to_modify_attribute)
                .unwrap_or_else(AttributeBuilder::default)
    }

    // Group mutations by impacted entid.
    let mut builders: BTreeMap<Entid, AttributeBuilder> = BTreeMap::new();

    // For retractions, we start with an attribute builder that's pre-populated with the existing
    // attribute values. That allows us to check existing values and unset them.
    for (entid, attr, ref value) in retractions.into_iter() {
        let builder = builders.entry(entid).or_insert_with(|| attribute_builder_to_modify(entid, attribute_map));
        match attr {
            // You can only retract :db/unique, :db/isComponent; all others must be altered instead
            // of retracted, or are not allowed to change.
            entids::DB_IS_COMPONENT => {
                match value {
                    &TypedValue::Boolean(v) if builder.component == Some(v) => {
                        builder.component(false);
                    },
                    v => {
                        bail!(DbErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
                    },
                }
            },
            entids::DB_UNIQUE => {
                match *value {
                    TypedValue::Ref(u) => {
                        match u {
                            entids::DB_UNIQUE_VALUE if builder.unique == Some(Some(attribute::Unique::Value)) => {
                                builder.non_unique();
                            },
                            entids::DB_UNIQUE_IDENTITY if builder.unique == Some(Some(attribute::Unique::Identity)) => {
                                builder.non_unique();
                            },
                            v => {
                                bail!(DbErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
                            },
                        }
                    },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
                }
            },
            _ => {
                bail!(DbErrorKind::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
            },
        }
    }

    for (entid, attr, ref value) in assertions.into_iter() {
        // For assertions, we can start with an empty attribute builder.
        let builder = builders.entry(entid).or_insert_with(Default::default);

        // TODO: improve error messages throughout.
        match attr {
            entids::DB_VALUE_TYPE => {
                match *value {
                    TypedValue::Ref(entids::DB_TYPE_BOOLEAN) => { builder.value_type(ValueType::Boolean); },
                    TypedValue::Ref(entids::DB_TYPE_DOUBLE)  => { builder.value_type(ValueType::Double); },
                    TypedValue::Ref(entids::DB_TYPE_INSTANT) => { builder.value_type(ValueType::Instant); },
                    TypedValue::Ref(entids::DB_TYPE_KEYWORD) => { builder.value_type(ValueType::Keyword); },
                    TypedValue::Ref(entids::DB_TYPE_LONG)    => { builder.value_type(ValueType::Long); },
                    TypedValue::Ref(entids::DB_TYPE_REF)     => { builder.value_type(ValueType::Ref); },
                    TypedValue::Ref(entids::DB_TYPE_STRING)  => { builder.value_type(ValueType::String); },
                    TypedValue::Ref(entids::DB_TYPE_UUID)    => { builder.value_type(ValueType::Uuid); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
                }
            },

            entids::DB_CARDINALITY => {
                match *value {
                    TypedValue::Ref(entids::DB_CARDINALITY_MANY) => { builder.multival(true); },
                    TypedValue::Ref(entids::DB_CARDINALITY_ONE) => { builder.multival(false); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
                }
            },

            entids::DB_UNIQUE => {
                match *value {
                    TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(attribute::Unique::Value); },
                    TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(attribute::Unique::Identity); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
                }
            },

            entids::DB_INDEX => {
                match *value {
                    TypedValue::Boolean(x) => { builder.index(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
                }
            },

            entids::DB_FULLTEXT => {
                match *value {
                    TypedValue::Boolean(x) => { builder.fulltext(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
                }
            },

            entids::DB_IS_COMPONENT => {
                match *value {
                    TypedValue::Boolean(x) => { builder.component(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
                }
            },

            entids::DB_NO_HISTORY => {
                match *value {
                    TypedValue::Boolean(x) => { builder.no_history(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
                }
            },

            _ => {
                bail!(DbErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
            }
        }
    };

    let mut attributes_installed: BTreeSet<Entid> = BTreeSet::default();
    let mut attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>> = BTreeMap::default();

    for (entid, builder) in builders.into_iter() {
        match attribute_map.entry(entid) {
            Entry::Vacant(entry) => {
                // Validate once…
                builder.validate_install_attribute().context(DbErrorKind::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;

                // … and twice, now we have the Attribute.
                let a = builder.build();
                a.validate(|| entid.to_string())?;
                entry.insert(builder.build());
                attributes_installed.insert(entid);
            },

            Entry::Occupied(mut entry) => {
                builder.validate_alter_attribute().context(DbErrorKind::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
                let mutations = builder.mutate(entry.get_mut());
                attributes_altered.insert(entid, mutations);
            },
        }
    }

    Ok(MetadataReport {
        attributes_installed: attributes_installed,
        attributes_altered: attributes_altered,
        idents_altered: BTreeMap::default(),
    })
}

/// Update a `Schema` in place from the given `[e a typed_value added]` quadruples.
///
/// This layer enforces that ident assertions of the form [entid :db/ident ...] (as distinct from
/// attribute assertions) are present and correct.
///
/// This is suitable for mutating a `Schema` from an applied transaction.
///
/// Returns a report summarizing the mutations that were applied.
pub fn update_schema_from_entid_quadruples<U>(schema: &mut Schema, assertions: U) -> Result<MetadataReport>
    where U: IntoIterator<Item=(Entid, Entid, TypedValue, bool)> {

    // Group attribute assertions into asserted, retracted, and updated.  We assume all our
    // attribute assertions are :db/cardinality :db.cardinality/one (so they'll only be added or
    // retracted at most once), which means all attribute alterations are simple changes from an old
    // value to a new value.
    let mut attribute_set: AddRetractAlterSet<(Entid, Entid), TypedValue> = AddRetractAlterSet::default();
    let mut ident_set: AddRetractAlterSet<Entid, symbols::Keyword> = AddRetractAlterSet::default();

    for (e, a, typed_value, added) in assertions.into_iter() {
        // Here we handle :db/ident assertions.
        if a == entids::DB_IDENT {
            if let TypedValue::Keyword(ref keyword) = typed_value {
                ident_set.witness(e, keyword.as_ref().clone(), added);
                continue
            } else {
                // Something is terribly wrong: the schema ensures we have a keyword.
                unreachable!();
            }
        }

        attribute_set.witness((e, a), typed_value, added);
    }

    // Collect triples.
    let retracted_triples = attribute_set.retracted.into_iter().map(|((e, a), typed_value)| (e, a, typed_value));
    let asserted_triples = attribute_set.asserted.into_iter().map(|((e, a), typed_value)| (e, a, typed_value));
    let altered_triples = attribute_set.altered.into_iter().map(|((e, a), (_old_value, new_value))| (e, a, new_value));

    let report = update_attribute_map_from_entid_triples(&mut schema.attribute_map,
                                                         asserted_triples.chain(altered_triples),
                                                         retracted_triples)?;

    let mut idents_altered: BTreeMap<Entid, IdentAlteration> = BTreeMap::new();

    // Asserted, altered, or retracted :db/idents update the relevant entids.
    for (entid, ident) in ident_set.asserted {
        schema.entid_map.insert(entid, ident.clone());
        schema.ident_map.insert(ident.clone(), entid);
        idents_altered.insert(entid, IdentAlteration::Ident(ident.clone()));
    }

    for (entid, (old_ident, new_ident)) in ident_set.altered {
        schema.entid_map.insert(entid, new_ident.clone()); // Overwrite existing.
        schema.ident_map.remove(&old_ident); // Remove old.
        schema.ident_map.insert(new_ident.clone(), entid); // Insert new.
        idents_altered.insert(entid, IdentAlteration::Ident(new_ident.clone()));
    }

    for (entid, ident) in ident_set.retracted {
        schema.entid_map.remove(&entid);
        schema.ident_map.remove(&ident);
        idents_altered.insert(entid, IdentAlteration::Ident(ident.clone()));
    }

    if report.attributes_did_change() {
        schema.update_component_attributes();
    }

    Ok(MetadataReport {
        idents_altered: idents_altered,
        .. report
    })
}
<commit_msg>Pre: Differentiate bad attribute retractions from unrecognized retractions.<commit_after>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![allow(dead_code)]

//! Most transactions can mutate the Mentat metadata by transacting assertions:
//!
//! - they can add (and, eventually, retract and alter) recognized idents using the `:db/ident`
//!   attribute;
//!
//! - they can add (and, eventually, retract and alter) schema attributes using various `:db/*`
//!   attributes;
//!
//! - eventually, they will be able to add (and possibly retract) entid partitions using a Mentat
//!   equivalent (perhaps :db/partition or :db.partition/start) to Datomic's `:db.install/partition`
//!   attribute.
//!
//! This module recognizes, validates, applies, and reports on these mutations.

use failure::ResultExt;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;

use add_retract_alter_set::{
    AddRetractAlterSet,
};
use edn::symbols;
use entids;
use errors::{
    DbErrorKind,
    Result,
};
use mentat_core::{
    attribute,
    Entid,
    Schema,
    AttributeMap,
    TypedValue,
    ValueType,
};

use schema::{
    AttributeBuilder,
    AttributeValidation,
};

/// An alteration to an attribute.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum AttributeAlteration {
    /// From http://blog.datomic.com/2014/01/schema-alteration.html:
    /// - rename attributes
    /// - rename your own programmatic identities (uses of :db/ident)
    /// - add or remove indexes
    Index,
    /// - add or remove uniqueness constraints
    Unique,
    /// - change attribute cardinality
    Cardinality,
    /// - change whether history is retained for an attribute
    NoHistory,
    /// - change whether an attribute is treated as a component
    IsComponent,
}

/// An alteration to an ident.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum IdentAlteration {
    Ident(symbols::Keyword),
}

/// Summarizes changes to metadata such as a a `Schema` and (in the future) a `PartitionMap`.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct MetadataReport {
    // Entids that were not present in the original `AttributeMap` that was mutated.
    pub attributes_installed: BTreeSet<Entid>,

    // Entids that were present in the original `AttributeMap` that was mutated, together with a
    // representation of the mutations that were applied.
    pub attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>>,

    // Idents that were installed into the `AttributeMap`.
    pub idents_altered: BTreeMap<Entid, IdentAlteration>,
}

impl MetadataReport {
    pub fn attributes_did_change(&self) -> bool {
        !(self.attributes_installed.is_empty() &&
          self.attributes_altered.is_empty())
    }
}

/// Update a `AttributeMap` in place from the given `[e a typed_value]` triples.
///
/// This is suitable for producing a `AttributeMap` from the `schema` materialized view, which does not
/// contain install and alter markers.
///
/// Returns a report summarizing the mutations that were applied.
pub fn update_attribute_map_from_entid_triples<A, R>(attribute_map: &mut AttributeMap, assertions: A, retractions: R) -> Result<MetadataReport>
    where A: IntoIterator<Item=(Entid, Entid, TypedValue)>,
          R: IntoIterator<Item=(Entid, Entid, TypedValue)> {

    fn attribute_builder_to_modify(attribute_id: Entid, existing: &AttributeMap) -> AttributeBuilder {
        existing.get(&attribute_id)
                .map(AttributeBuilder::to_modify_attribute)
                .unwrap_or_else(AttributeBuilder::default)
    }

    // Group mutations by impacted entid.
    let mut builders: BTreeMap<Entid, AttributeBuilder> = BTreeMap::new();

    // For retractions, we start with an attribute builder that's pre-populated with the existing
    // attribute values. That allows us to check existing values and unset them.
    for (entid, attr, ref value) in retractions.into_iter() {
        let builder = builders.entry(entid).or_insert_with(|| attribute_builder_to_modify(entid, attribute_map));
        match attr {
            // You can only retract :db/unique, :db/isComponent; all others must be altered instead
            // of retracted, or are not allowed to change.
            entids::DB_IS_COMPONENT => {
                match value {
                    &TypedValue::Boolean(v) if builder.component == Some(v) => {
                        builder.component(false);
                    },
                    v => {
                        bail!(DbErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/isComponent with the wrong value {:?}.", v)));
                    },
                }
            },

            entids::DB_UNIQUE => {
                match *value {
                    TypedValue::Ref(u) => {
                        match u {
                            entids::DB_UNIQUE_VALUE if builder.unique == Some(Some(attribute::Unique::Value)) => {
                                builder.non_unique();
                            },
                            entids::DB_UNIQUE_IDENTITY if builder.unique == Some(Some(attribute::Unique::Identity)) => {
                                builder.non_unique();
                            },
                            v => {
                                bail!(DbErrorKind::BadSchemaAssertion(format!("Attempted to retract :db/unique with the wrong value {}.", v)));
                            },
                        }
                    },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [:db/retract _ :db/unique :db.unique/_] but got [:db/retract {} :db/unique {:?}]", entid, value)))
                }
            },

            entids::DB_VALUE_TYPE |
            entids::DB_CARDINALITY |
            entids::DB_INDEX |
            entids::DB_FULLTEXT |
            entids::DB_NO_HISTORY => {
                bail!(DbErrorKind::BadSchemaAssertion(format!("Retracting attribute {} for entity {} not permitted.", attr, entid)));
            },

            _ => {
                bail!(DbErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
            }
        }
    }

    for (entid, attr, ref value) in assertions.into_iter() {
        // For assertions, we can start with an empty attribute builder.
        let builder = builders.entry(entid).or_insert_with(Default::default);

        // TODO: improve error messages throughout.
        match attr {
            entids::DB_VALUE_TYPE => {
                match *value {
                    TypedValue::Ref(entids::DB_TYPE_BOOLEAN) => { builder.value_type(ValueType::Boolean); },
                    TypedValue::Ref(entids::DB_TYPE_DOUBLE)  => { builder.value_type(ValueType::Double); },
                    TypedValue::Ref(entids::DB_TYPE_INSTANT) => { builder.value_type(ValueType::Instant); },
                    TypedValue::Ref(entids::DB_TYPE_KEYWORD) => { builder.value_type(ValueType::Keyword); },
                    TypedValue::Ref(entids::DB_TYPE_LONG)    => { builder.value_type(ValueType::Long); },
                    TypedValue::Ref(entids::DB_TYPE_REF)     => { builder.value_type(ValueType::Ref); },
                    TypedValue::Ref(entids::DB_TYPE_STRING)  => { builder.value_type(ValueType::String); },
                    TypedValue::Ref(entids::DB_TYPE_UUID)    => { builder.value_type(ValueType::Uuid); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/valueType :db.type/*] but got [... :db/valueType {:?}] for entid {} and attribute {}", value, entid, attr)))
                }
            },

            entids::DB_CARDINALITY => {
                match *value {
                    TypedValue::Ref(entids::DB_CARDINALITY_MANY) => { builder.multival(true); },
                    TypedValue::Ref(entids::DB_CARDINALITY_ONE) => { builder.multival(false); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/cardinality :db.cardinality/many|:db.cardinality/one] but got [... :db/cardinality {:?}]", value)))
                }
            },

            entids::DB_UNIQUE => {
                match *value {
                    TypedValue::Ref(entids::DB_UNIQUE_VALUE) => { builder.unique(attribute::Unique::Value); },
                    TypedValue::Ref(entids::DB_UNIQUE_IDENTITY) => { builder.unique(attribute::Unique::Identity); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/unique :db.unique/value|:db.unique/identity] but got [... :db/unique {:?}]", value)))
                }
            },

            entids::DB_INDEX => {
                match *value {
                    TypedValue::Boolean(x) => { builder.index(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/index true|false] but got [... :db/index {:?}]", value)))
                }
            },

            entids::DB_FULLTEXT => {
                match *value {
                    TypedValue::Boolean(x) => { builder.fulltext(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/fulltext true|false] but got [... :db/fulltext {:?}]", value)))
                }
            },

            entids::DB_IS_COMPONENT => {
                match *value {
                    TypedValue::Boolean(x) => { builder.component(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/isComponent true|false] but got [... :db/isComponent {:?}]", value)))
                }
            },

            entids::DB_NO_HISTORY => {
                match *value {
                    TypedValue::Boolean(x) => { builder.no_history(x); },
                    _ => bail!(DbErrorKind::BadSchemaAssertion(format!("Expected [... :db/noHistory true|false] but got [... :db/noHistory {:?}]", value)))
                }
            },

            _ => {
                bail!(DbErrorKind::BadSchemaAssertion(format!("Do not recognize attribute {} for entid {}", attr, entid)))
            }
        }
    };

    let mut attributes_installed: BTreeSet<Entid> = BTreeSet::default();
    let mut attributes_altered: BTreeMap<Entid, Vec<AttributeAlteration>> = BTreeMap::default();

    for (entid, builder) in builders.into_iter() {
        match attribute_map.entry(entid) {
            Entry::Vacant(entry) => {
                // Validate once…
                builder.validate_install_attribute().context(DbErrorKind::BadSchemaAssertion(format!("Schema alteration for new attribute with entid {} is not valid", entid)))?;

                // … and twice, now we have the Attribute.
                let a = builder.build();
                a.validate(|| entid.to_string())?;
                entry.insert(builder.build());
                attributes_installed.insert(entid);
            },

            Entry::Occupied(mut entry) => {
                builder.validate_alter_attribute().context(DbErrorKind::BadSchemaAssertion(format!("Schema alteration for existing attribute with entid {} is not valid", entid)))?;
                let mutations = builder.mutate(entry.get_mut());
                attributes_altered.insert(entid, mutations);
            },
        }
    }

    Ok(MetadataReport {
        attributes_installed: attributes_installed,
        attributes_altered: attributes_altered,
        idents_altered: BTreeMap::default(),
    })
}

/// Update a `Schema` in place from the given `[e a typed_value added]` quadruples.
///
/// This layer enforces that ident assertions of the form [entid :db/ident ...] (as distinct from
/// attribute assertions) are present and correct.
///
/// This is suitable for mutating a `Schema` from an applied transaction.
///
/// Returns a report summarizing the mutations that were applied.
pub fn update_schema_from_entid_quadruples<U>(schema: &mut Schema, assertions: U) -> Result<MetadataReport>
    where U: IntoIterator<Item=(Entid, Entid, TypedValue, bool)> {

    // Group attribute assertions into asserted, retracted, and updated.  We assume all our
    // attribute assertions are :db/cardinality :db.cardinality/one (so they'll only be added or
    // retracted at most once), which means all attribute alterations are simple changes from an old
    // value to a new value.
    let mut attribute_set: AddRetractAlterSet<(Entid, Entid), TypedValue> = AddRetractAlterSet::default();
    let mut ident_set: AddRetractAlterSet<Entid, symbols::Keyword> = AddRetractAlterSet::default();

    for (e, a, typed_value, added) in assertions.into_iter() {
        // Here we handle :db/ident assertions.
        if a == entids::DB_IDENT {
            if let TypedValue::Keyword(ref keyword) = typed_value {
                ident_set.witness(e, keyword.as_ref().clone(), added);
                continue
            } else {
                // Something is terribly wrong: the schema ensures we have a keyword.
                unreachable!();
            }
        }

        attribute_set.witness((e, a), typed_value, added);
    }

    // Collect triples.
    let retracted_triples = attribute_set.retracted.into_iter().map(|((e, a), typed_value)| (e, a, typed_value));
    let asserted_triples = attribute_set.asserted.into_iter().map(|((e, a), typed_value)| (e, a, typed_value));
    let altered_triples = attribute_set.altered.into_iter().map(|((e, a), (_old_value, new_value))| (e, a, new_value));

    let report = update_attribute_map_from_entid_triples(&mut schema.attribute_map,
                                                         asserted_triples.chain(altered_triples),
                                                         retracted_triples)?;

    let mut idents_altered: BTreeMap<Entid, IdentAlteration> = BTreeMap::new();

    // Asserted, altered, or retracted :db/idents update the relevant entids.
    for (entid, ident) in ident_set.asserted {
        schema.entid_map.insert(entid, ident.clone());
        schema.ident_map.insert(ident.clone(), entid);
        idents_altered.insert(entid, IdentAlteration::Ident(ident.clone()));
    }

    for (entid, (old_ident, new_ident)) in ident_set.altered {
        schema.entid_map.insert(entid, new_ident.clone()); // Overwrite existing.
        schema.ident_map.remove(&old_ident); // Remove old.
        schema.ident_map.insert(new_ident.clone(), entid); // Insert new.
        idents_altered.insert(entid, IdentAlteration::Ident(new_ident.clone()));
    }

    for (entid, ident) in ident_set.retracted {
        schema.entid_map.remove(&entid);
        schema.ident_map.remove(&ident);
        idents_altered.insert(entid, IdentAlteration::Ident(ident.clone()));
    }

    if report.attributes_did_change() {
        schema.update_component_attributes();
    }

    Ok(MetadataReport {
        idents_altered: idents_altered,
        .. report
    })
}
<|endoftext|>