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

//! Thread-local random number generator

use std::cell::RefCell;
use std::rc::Rc;

use {RngCore, StdRng, SeedableRng, EntropyRng};
use {Distribution, Uniform, Rng, Error};
use reseeding::ReseedingRng;

// Number of generated bytes after which to reseed `TreadRng`.
//
// The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB.
// We pick a treshold here that is large enough to not reduce the average
// performance too much, but also small enough to not make reseeding something
// that basically never happens.
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB

/// The type returned by [`thread_rng`], essentially just a reference to the
/// PRNG in thread-local memory.
///
/// [`thread_rng`]: fn.thread_rng.html
#[derive(Clone, Debug)]
pub struct ThreadRng {
    rng: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>>,
}

thread_local!(
    static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>> = {
        let mut entropy_source = EntropyRng::new();
        let r = StdRng::from_rng(&mut entropy_source).unwrap_or_else(|err|
                panic!("could not initialize thread_rng: {}", err));
        let rng = ReseedingRng::new(r,
                                    THREAD_RNG_RESEED_THRESHOLD,
                                    entropy_source);
        Rc::new(RefCell::new(rng))
    }
);

/// Retrieve the lazily-initialized thread-local random number
/// generator, seeded by the system. Intended to be used in method
/// chaining style, e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g.
/// `let mut rng = thread_rng();`.
///
/// `ThreadRng` uses [`ReseedingRng`] wrapping a [`StdRng`] which is reseeded
/// after generating 32 MiB of random data. A single instance is cached per
/// thread and the returned `ThreadRng` is a reference to this instance — hence
/// `ThreadRng` is neither `Send` nor `Sync` but is safe to use within a single
/// thread. This RNG is seeded and reseeded via [`EntropyRng`] as required.
///
/// Note that the reseeding is done as an extra precaution against entropy
/// leaks and is in theory unnecessary — to predict `thread_rng`'s output, an
/// attacker would have to either determine most of the RNG's seed or internal
/// state, or crack the algorithm used.
///
/// Like [`StdRng`], `ThreadRng` is a cryptographically secure PRNG. The current
/// algorithm used is [HC-128], which is an array-based PRNG that trades memory
/// usage for better performance. This makes it similar to ISAAC, the algorithm
/// used in `ThreadRng` before rand 0.5.
///
/// [`ReseedingRng`]: reseeding/struct.ReseedingRng.html
/// [`StdRng`]: struct.StdRng.html
/// [`EntropyRng`]: struct.EntropyRng.html
/// [HC-128]: struct.Hc128Rng.html
pub fn thread_rng() -> ThreadRng {
    ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) }
}

impl RngCore for ThreadRng {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        self.rng.borrow_mut().next_u32()
    }

    #[inline]
    fn next_u64(&mut self) -> u64 {
        self.rng.borrow_mut().next_u64()
    }

    #[inline]
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        self.rng.borrow_mut().fill_bytes(bytes)
    }

    #[inline]
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        self.rng.borrow_mut().try_fill_bytes(dest)
    }
}

/// Generates a random value using the thread-local random number generator.
///
/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for
/// documentation of the entropy source and [`Rand`] for documentation of
/// distributions and type-specific generation.
///
/// # Examples
///
/// ```
/// let x = rand::random::<u8>();
/// println!("{}", x);
///
/// let y = rand::random::<f64>();
/// println!("{}", y);
///
/// if rand::random() { // generates a boolean
///     println!("Better lucky than good!");
/// }
/// ```
///
/// If you're calling `random()` in a loop, caching the generator as in the
/// following example can increase performance.
///
/// ```
/// use rand::Rng;
///
/// let mut v = vec![1, 2, 3];
///
/// for x in v.iter_mut() {
///     *x = rand::random()
/// }
///
/// // can be made faster by caching thread_rng
///
/// let mut rng = rand::thread_rng();
///
/// for x in v.iter_mut() {
///     *x = rng.gen();
/// }
/// ```
///
/// [`thread_rng`]: fn.thread_rng.html
/// [`Rand`]: trait.Rand.html
#[inline]
pub fn random<T>() -> T where Uniform: Distribution<T> {
    thread_rng().gen()
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[cfg(feature="std")]
    #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
    fn test_thread_rng() {
        let mut r = ::thread_rng();
        r.gen::<i32>();
        let mut v = [1, 1, 1];
        r.shuffle(&mut v);
        let b: &[_] = &[1, 1, 1];
        assert_eq!(v, b);
        assert_eq!(r.gen_range(0, 1), 0);
    }

    #[test]
    fn test_random() {
        // not sure how to test this aside from just getting some values
        let _n : usize = random();
        let _f : f32 = random();
        let _o : Option<Option<i8>> = random();
        let _many : ((),
                     (usize,
                      isize,
                      Option<(u32, (bool,))>),
                     (u8, i8, u16, i16, u32, i32, u64, i64),
                     (f32, (f64, (f64,)))) = random();
    }
}
<commit_msg>Implement CryptoRng for ThreadRng<commit_after>// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Thread-local random number generator

use std::cell::RefCell;
use std::rc::Rc;

use {RngCore, CryptoRng, StdRng, SeedableRng, EntropyRng};
use {Distribution, Uniform, Rng, Error};
use reseeding::ReseedingRng;

// Number of generated bytes after which to reseed `TreadRng`.
//
// The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB.
// We pick a treshold here that is large enough to not reduce the average
// performance too much, but also small enough to not make reseeding something
// that basically never happens.
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB

/// The type returned by [`thread_rng`], essentially just a reference to the
/// PRNG in thread-local memory.
///
/// [`thread_rng`]: fn.thread_rng.html
#[derive(Clone, Debug)]
pub struct ThreadRng {
    rng: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>>,
}

thread_local!(
    static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>> = {
        let mut entropy_source = EntropyRng::new();
        let r = StdRng::from_rng(&mut entropy_source).unwrap_or_else(|err|
                panic!("could not initialize thread_rng: {}", err));
        let rng = ReseedingRng::new(r,
                                    THREAD_RNG_RESEED_THRESHOLD,
                                    entropy_source);
        Rc::new(RefCell::new(rng))
    }
);

/// Retrieve the lazily-initialized thread-local random number
/// generator, seeded by the system. Intended to be used in method
/// chaining style, e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g.
/// `let mut rng = thread_rng();`.
///
/// `ThreadRng` uses [`ReseedingRng`] wrapping a [`StdRng`] which is reseeded
/// after generating 32 MiB of random data. A single instance is cached per
/// thread and the returned `ThreadRng` is a reference to this instance — hence
/// `ThreadRng` is neither `Send` nor `Sync` but is safe to use within a single
/// thread. This RNG is seeded and reseeded via [`EntropyRng`] as required.
///
/// Note that the reseeding is done as an extra precaution against entropy
/// leaks and is in theory unnecessary — to predict `thread_rng`'s output, an
/// attacker would have to either determine most of the RNG's seed or internal
/// state, or crack the algorithm used.
///
/// Like [`StdRng`], `ThreadRng` is a cryptographically secure PRNG. The current
/// algorithm used is [HC-128], which is an array-based PRNG that trades memory
/// usage for better performance. This makes it similar to ISAAC, the algorithm
/// used in `ThreadRng` before rand 0.5.
///
/// [`ReseedingRng`]: reseeding/struct.ReseedingRng.html
/// [`StdRng`]: struct.StdRng.html
/// [`EntropyRng`]: struct.EntropyRng.html
/// [HC-128]: struct.Hc128Rng.html
pub fn thread_rng() -> ThreadRng {
    ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) }
}

impl RngCore for ThreadRng {
    #[inline]
    fn next_u32(&mut self) -> u32 {
        self.rng.borrow_mut().next_u32()
    }

    #[inline]
    fn next_u64(&mut self) -> u64 {
        self.rng.borrow_mut().next_u64()
    }

    #[inline]
    fn fill_bytes(&mut self, bytes: &mut [u8]) {
        self.rng.borrow_mut().fill_bytes(bytes)
    }

    #[inline]
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        self.rng.borrow_mut().try_fill_bytes(dest)
    }
}

impl CryptoRng for ThreadRng {}

/// Generates a random value using the thread-local random number generator.
///
/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for
/// documentation of the entropy source and [`Rand`] for documentation of
/// distributions and type-specific generation.
///
/// # Examples
///
/// ```
/// let x = rand::random::<u8>();
/// println!("{}", x);
///
/// let y = rand::random::<f64>();
/// println!("{}", y);
///
/// if rand::random() { // generates a boolean
///     println!("Better lucky than good!");
/// }
/// ```
///
/// If you're calling `random()` in a loop, caching the generator as in the
/// following example can increase performance.
///
/// ```
/// use rand::Rng;
///
/// let mut v = vec![1, 2, 3];
///
/// for x in v.iter_mut() {
///     *x = rand::random()
/// }
///
/// // can be made faster by caching thread_rng
///
/// let mut rng = rand::thread_rng();
///
/// for x in v.iter_mut() {
///     *x = rng.gen();
/// }
/// ```
///
/// [`thread_rng`]: fn.thread_rng.html
/// [`Rand`]: trait.Rand.html
#[inline]
pub fn random<T>() -> T where Uniform: Distribution<T> {
    thread_rng().gen()
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    #[cfg(feature="std")]
    #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
    fn test_thread_rng() {
        let mut r = ::thread_rng();
        r.gen::<i32>();
        let mut v = [1, 1, 1];
        r.shuffle(&mut v);
        let b: &[_] = &[1, 1, 1];
        assert_eq!(v, b);
        assert_eq!(r.gen_range(0, 1), 0);
    }

    #[test]
    fn test_random() {
        // not sure how to test this aside from just getting some values
        let _n : usize = random();
        let _f : f32 = random();
        let _o : Option<Option<i8>> = random();
        let _many : ((),
                     (usize,
                      isize,
                      Option<(u32, (bool,))>),
                     (u8, i8, u16, i16, u32, i32, u64, i64),
                     (f32, (f64, (f64,)))) = random();
    }
}
<|endoftext|>


<commit_before>//! Manage SBD messages stored on the fileystem.
//!
//! Messages are stored in a directory hierarchy under a single root directory.
//! Message storage and retrieval are managed by a `Storage` object, which is
//! configured for a single root directory.

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

use glob::{glob, Paths, PatternError};

use super::{Message, Result};

const SBD_EXTENSION: &'static str = ".sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Storage {
    root: PathBuf,
}

/// An interator over the messages in a `Storage`.
pub struct StorageIterator {
    paths: Option<Paths>,
}

/// The object yielded by a `StorageIterator`.
pub struct StorageEntry {
    pub message: Message,
    pub path_buf: PathBuf,
}

impl Iterator for StorageIterator {
    type Item = StorageEntry;

    fn next(&mut self) -> Option<Self::Item> {
        let mut paths = match self.paths {
            Some(ref mut paths) => paths,
            None => return None,
        };
        loop {
            let glob_result = match paths.next() {
                Some(result) => result,
                None => return None,
            };
            let path_buf = match glob_result {
                Ok(path_buf) => path_buf,
                Err(_) => continue,
            };
            match Message::from_path(&path_buf) {
                Ok(message) => return Some(StorageEntry {
                    message: message,
                    path_buf: path_buf,
                }),
                Err(_) => continue,
            }
        }
    }
}

impl StorageIterator {
    fn new(storage: &Storage) -> StorageIterator {
        let paths = match storage.glob() {
            Ok(paths) => Some(paths),
            Err(_) => None,
        };
        StorageIterator { paths: paths }
    }
}

impl<'a> IntoIterator for &'a Storage {
    type Item = StorageEntry;
    type IntoIter = StorageIterator;

    fn into_iter(self) -> Self::IntoIter {
        StorageIterator::new(self)
    }
}

impl Storage {
    /// Creates a new storage manager for a given root directory.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// let storage = Storage::new("/var/iridium");
    /// ```
    pub fn new<P: AsRef<Path>>(root: P) -> Storage {
        Storage { root: root.as_ref().to_path_buf() }
    }

    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// use sbd::Message;
    /// let message: Message = Default::default();
    /// let storage = Storage::new("/var/iridium");
    /// storage.store(&message);
    /// ```
    pub fn store(&self, message: &Message) -> Result<PathBuf> {
        let mut path_buf = self.root.clone();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%Y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session()
                             .format(&format!("%y%m%d_%H%M%S{}", SBD_EXTENSION))
                             .to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(path_buf)
    }

    fn glob(&self) -> result::Result<Paths, PatternError> {
        let mut path_buf = self.root.clone();
        path_buf.push("**");
        path_buf.push(format!("*{}", SBD_EXTENSION));
        glob(&path_buf.to_str().unwrap())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use tempdir::TempDir;

    use super::super::Message;

    #[test]
    fn new_storage() {
        Storage::new(TempDir::new("").unwrap().path());
    }

    #[test]
    fn store_message() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        let pathbuf = storage.store(&message).unwrap();
        Message::from_path(pathbuf).unwrap();
    }

    #[test]
    fn iterate_over_messages() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        storage.store(&message).unwrap();
        let messages = storage.into_iter().collect::<Vec<StorageEntry>>();
        assert_eq!(1, messages.len());
    }
}
<commit_msg>Mark StorageIterator as non-copy and non-debug<commit_after>//! Manage SBD messages stored on the fileystem.
//!
//! Messages are stored in a directory hierarchy under a single root directory.
//! Message storage and retrieval are managed by a `Storage` object, which is
//! configured for a single root directory.

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

use glob::{glob, Paths, PatternError};

use super::{Message, Result};

const SBD_EXTENSION: &'static str = ".sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Storage {
    root: PathBuf,
}

/// An interator over the messages in a `Storage`.
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub struct StorageIterator {
    paths: Option<Paths>,
}

/// The object yielded by a `StorageIterator`.
pub struct StorageEntry {
    pub message: Message,
    pub path_buf: PathBuf,
}

impl Iterator for StorageIterator {
    type Item = StorageEntry;

    fn next(&mut self) -> Option<Self::Item> {
        let mut paths = match self.paths {
            Some(ref mut paths) => paths,
            None => return None,
        };
        loop {
            let glob_result = match paths.next() {
                Some(result) => result,
                None => return None,
            };
            let path_buf = match glob_result {
                Ok(path_buf) => path_buf,
                Err(_) => continue,
            };
            match Message::from_path(&path_buf) {
                Ok(message) => return Some(StorageEntry {
                    message: message,
                    path_buf: path_buf,
                }),
                Err(_) => continue,
            }
        }
    }
}

impl StorageIterator {
    fn new(storage: &Storage) -> StorageIterator {
        let paths = match storage.glob() {
            Ok(paths) => Some(paths),
            Err(_) => None,
        };
        StorageIterator { paths: paths }
    }
}

impl<'a> IntoIterator for &'a Storage {
    type Item = StorageEntry;
    type IntoIter = StorageIterator;

    fn into_iter(self) -> Self::IntoIter {
        StorageIterator::new(self)
    }
}

impl Storage {
    /// Creates a new storage manager for a given root directory.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// let storage = Storage::new("/var/iridium");
    /// ```
    pub fn new<P: AsRef<Path>>(root: P) -> Storage {
        Storage { root: root.as_ref().to_path_buf() }
    }

    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// use sbd::Message;
    /// let message: Message = Default::default();
    /// let storage = Storage::new("/var/iridium");
    /// storage.store(&message);
    /// ```
    pub fn store(&self, message: &Message) -> Result<PathBuf> {
        let mut path_buf = self.root.clone();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%Y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session()
                             .format(&format!("%y%m%d_%H%M%S{}", SBD_EXTENSION))
                             .to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(path_buf)
    }

    fn glob(&self) -> result::Result<Paths, PatternError> {
        let mut path_buf = self.root.clone();
        path_buf.push("**");
        path_buf.push(format!("*{}", SBD_EXTENSION));
        glob(&path_buf.to_str().unwrap())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use tempdir::TempDir;

    use super::super::Message;

    #[test]
    fn new_storage() {
        Storage::new(TempDir::new("").unwrap().path());
    }

    #[test]
    fn store_message() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        let pathbuf = storage.store(&message).unwrap();
        Message::from_path(pathbuf).unwrap();
    }

    #[test]
    fn iterate_over_messages() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        storage.store(&message).unwrap();
        let messages = storage.into_iter().collect::<Vec<StorageEntry>>();
        assert_eq!(1, messages.len());
    }
}
<|endoftext|>


<commit_before>// parser/mod.rs

use std::collections::HashMap;
use std::iter::Peekable;
use std::str::Chars;
use utils::{Result, err_position, Position};
use super::compiler::abstract_tree::{AbstractTree, TokenType};

struct Parser<'a> {
    table: HashMap<char, fn(&mut Parser) -> Result<Option<AbstractTree>>>,
    stream: Peekable<Chars<'a>>,
    position: Position,
}

impl<'a> Parser<'a> {
    fn new(string: &'a str) -> Parser<'a> {
        Parser {
            table: HashMap::new(),
            stream: string.chars().peekable(),
            position: Position(0, 0),
        }
    }

    fn advance_char(&mut self) -> Option<char> {
        let current_char = self.stream.next();
        match current_char {
            Some(c) => {
                if c == '\n' {
                    // column set to 0
                    // line incremented
                    self.position.0 += 1;
                    self.position.1 = 0;
                } else {
                    // column incremented
                    self.position.1 += 1;
                }
            }
            None => {}
        }
        current_char
    }

    fn at_eof(&mut self) -> bool {
        self.current_char().is_none()
    }

    fn read_as(mut self, key: char, f: fn(&mut Parser) -> Result<Option<AbstractTree>>) -> Parser<'a> {
        self.table.insert(key, f);
        self
    }

    fn parse_expression(&mut self) -> Result<Option<AbstractTree>> {
        let current_reader = self.current_reader().unwrap_or(Parser::default_parse);
        current_reader(self)
    }

    fn current_reader(&mut self) -> Option<fn(&mut Parser) -> Result<Option<AbstractTree>>> {
        if self.current_char().is_none() {
            return None;
        }
        self.table.get(self.stream.peek().unwrap()).map(|f| *f)
    }

    fn current_char(&mut self) -> Option<&char> {
        self.stream.peek()
    }

    fn default_parse<'b>(parser: &mut Parser) -> Result<Option<AbstractTree>> {
        let mut chars = String::new();
        let starting_position = parser.position.clone();

        loop {
            // refactor this to a while - let
            // if parser.at_eof() { break; } // I don't think this is necessary
            let current_reader = parser.current_reader();
            match current_reader {
                Some(f) => {
                    break;
                }
                None => {
                    match parser.advance_char() {
                        Some(c) => chars.push(c),
                        None => {
                            break;
                        }
                    }
                }
            }
        }
        if chars.len() == 0 {
            return Ok(None);
        }
        Ok(Some(AbstractTree::Token(TokenType::Symbol, chars, starting_position)))
    }

}

fn no_op(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(None)
}

// consolidate these into one function.
fn close_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, ")".to_string(), Position(0, 0))))
}

fn close_curly(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, "}".to_string(), Position(0, 0))))
}

fn open_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    let starting_position = parser.position.clone();
    parser.advance_char();
    let mut accumulator = vec![];
    loop {
        let expression = parser.parse_expression();
        match expression {
            Ok(Some(AbstractTree::Token(TokenType::Flag, s, p))) => {
                if s == ")".to_string() {
                    break;
                } else {
                    // this way I can ensure ( } doesnt happen
                    return err_position(starting_position,
                                        format!("encountered incorrect flag {},\
                                                at position {:?}, while reading an \
                                                open paren", s, p))
                }
            }
            Ok(Some(a)) => accumulator.push(a),
            Ok(None) => {
                if parser.at_eof() {
                    return err_position(starting_position, "hit end of file while reading an open paren".to_string())
                }
            }
            error @ Err(_) => return error,
        }
    }
    Ok(Some(AbstractTree::Node(accumulator, starting_position)))
}

pub fn parse(string: &str) -> Result<Option<AbstractTree>> {
    Parser::new(string)
        .read_as(' ', no_op)
        .read_as(')', close_paren)
        .read_as('(', open_paren)
        .read_as('}', close_curly)
        // .read_as('{', open_curly)
        .parse_expression()
}

#[cfg(test)]
mod tests {
    use parser::parse;
    use compiler::abstract_tree::AbstractTree::*;
    use compiler::abstract_tree::TokenType::*;
    use utils::{err_position, Position, Error};

    #[test]
    fn test_parse_symbol() {
        assert_eq!(
            parse("symbol").unwrap().unwrap(),
            Token(Symbol, "symbol".to_string(), Position(0,0))
        )
    }

    #[test]
    fn test_parse_parentheses() {
        assert_eq!(
            parse("(hi there)").unwrap().unwrap(),
            Node(vec![Token(Symbol, "hi".to_string(), Position(0,1)), Token(Symbol, "there".to_string(), Position(0,4))], Position(0,0))
        );

        // Try with two levels
        assert_eq!(
            parse("(hi (one) there)").unwrap().unwrap(),
            Node(vec![Token(Symbol, "hi".to_string(), Position(0,1)), Node(vec![Token(Symbol, "one".to_string(), Position(0,5))], Position(0,4)), Token(Symbol, "there".to_string(), Position(0, 10))], Position(0,0))
        );
    }

    #[test]
    fn test_fail_parse_parentheses() {
        match parse("(hi there") {
            Ok(_) => panic!("I'm assertng this should not parse correctly"),
            Err(Error { description: description, position: _ }) => {
                assert_eq!("hit end of file while reading an open paren".to_string(), description);
            }
        }
    }
}
<commit_msg>now parsing multiple lines of code<commit_after>// parser/mod.rs

use std::collections::HashMap;
use std::iter::Peekable;
use std::str::Chars;
use utils::{Result, err_position, Position};
use super::compiler::abstract_tree::{AbstractTree, TokenType};

struct Parser<'a> {
    table: HashMap<char, fn(&mut Parser) -> Result<Option<AbstractTree>>>,
    stream: Peekable<Chars<'a>>,
    position: Position,
}

impl<'a> Parser<'a> {
    fn new(string: &'a str) -> Parser<'a> {
        Parser {
            table: HashMap::new(),
            stream: string.chars().peekable(),
            position: Position(0, 0),
        }
    }

    fn advance_char(&mut self) -> Option<char> {
        let current_char = self.stream.next();
        match current_char {
            Some(c) => {
                if c == '\n' {
                    // column set to 0
                    // line incremented
                    self.position.0 += 1;
                    self.position.1 = 0;
                } else {
                    // column incremented
                    self.position.1 += 1;
                }
            }
            None => {}
        }
        current_char
    }

    fn at_eof(&mut self) -> bool {
        self.current_char().is_none()
    }

    fn read_as(mut self,
               key: char,
               f: fn(&mut Parser) -> Result<Option<AbstractTree>>)
               -> Parser<'a> {
        self.table.insert(key, f);
        self
    }

    fn parse_expression(&mut self) -> Result<Option<AbstractTree>> {
        let current_reader = self.current_reader().unwrap_or(Parser::default_parse);
        current_reader(self)
    }

    fn current_reader(&mut self) -> Option<fn(&mut Parser) -> Result<Option<AbstractTree>>> {
        if self.current_char().is_none() {
            return None;
        }
        self.table.get(self.stream.peek().unwrap()).map(|f| *f)
    }

    fn current_char(&mut self) -> Option<&char> {
        self.stream.peek()
    }

    fn default_parse<'b>(parser: &mut Parser) -> Result<Option<AbstractTree>> {
        let mut chars = String::new();
        let starting_position = parser.position.clone();

        loop {
            // refactor this to a while - let
            // if parser.at_eof() { break; } // I don't think this is necessary
            let current_reader = parser.current_reader();
            match current_reader {
                Some(_) => {
                    break;
                }
                None => {
                    match parser.advance_char() {
                        Some(c) => chars.push(c),
                        None => {
                            break;
                        }
                    }
                }
            }
        }
        if chars.len() == 0 {
            return Ok(None);
        }
        Ok(Some(AbstractTree::Token(TokenType::Symbol, chars, starting_position)))
    }

    fn parse_complete(&mut self) -> Result<Option<AbstractTree>> {
        let starting_position = self.position.clone();
        let mut accumulator = vec![];
        loop {
            let expression = top_level_expression(self);
            match expression {
                Ok(Some(AbstractTree::Token(TokenType::Flag, s, p))) => {
                    // this way I can ensure ( } doesnt happen
                    return err_position(starting_position,
                                        format!("encountered incorrect flag {},\
                                                at position {:?}, while reading an \
                                                the top level",
                                                s,
                                                p));
                }
                Ok(Some(a)) => accumulator.push(a),
                Ok(None) => {
                    if self.at_eof() {
                        break;
                    }
                }
                error @ Err(_) => return error,
            }
        }
        Ok(Some(AbstractTree::Node(accumulator, starting_position)))
    }
}

fn no_op(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(None)
}

// consolidate these into one function.
fn close_paren(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, ")".to_string(), Position(0, 0))))
}

fn close_curly(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, "}".to_string(), Position(0, 0))))
}

fn newline(parser: &mut Parser) -> Result<Option<AbstractTree>> {
    parser.advance_char();
    Ok(Some(AbstractTree::Token(TokenType::Flag, "\n".to_string(), Position(0, 0))))
}

macro_rules! define_expression_parser {
    (
        $a: ident
        name: $name: expr,
        close: $close: expr,
        advance: $should_advance: expr,
        top_level: $top_level: expr
    ) => {

        fn $a(parser: &mut Parser) -> Result<Option<AbstractTree>> {
            let starting_position = parser.position.clone();
            if $should_advance {
                parser.advance_char();
            }
            let mut accumulator = vec![];
            loop {
                let expression = parser.parse_expression();
                match expression {
                    Ok(Some(AbstractTree::Token(TokenType::Flag, s, p))) => {
                        if s == $close.to_string() {
                            break;
                        } else {
                            // this way I can ensure ( } doesnt happen
                            return err_position(starting_position,
                                                format!("encountered incorrect flag {},\
                                                        at position {:?}, while reading an \
                                                        {}", s, p, $name))
                        }
                    }
                    Ok(Some(a)) => accumulator.push(a),
                    Ok(None) => {
                        if parser.at_eof() {
                            if $top_level { break; }

                            return err_position(starting_position,
                                                format!("hit end of file \
                                                        while reading an {}", $name))
                        }
                    }
                    error @ Err(_) => return error,
                }
            }

            if accumulator.is_empty() { // This might be a bug // && parser.at_eof() {
                return Ok(None)
            }

            Ok(Some(AbstractTree::Node(accumulator, starting_position)))
        }

    }
}

define_expression_parser! { open_paren
    name: "open paren",
    close: ")",
    advance: true,
    top_level: false
}

define_expression_parser! { top_level_expression
    name: "top level expression",
    close: "\n",
    advance: false,
    top_level: true
}

pub fn parse(string: &str) -> Result<Option<AbstractTree>> {
    Parser::new(string)
        .read_as('\n', newline)
        .read_as(' ', no_op)
        .read_as(')', close_paren)
        .read_as('(', open_paren)
        .read_as('}', close_curly)
        // .read_as('{', open_curly)
        .parse_complete()
}

#[cfg(test)]
mod tests {
    use parser::parse;
    use compiler::abstract_tree::AbstractTree::*;
    use compiler::abstract_tree::TokenType::*;
    use utils::{Position, Error};

    #[test]
    fn test_parse_symbol() {
        assert_eq!(parse("symbol").unwrap().unwrap(),
                   Node(vec![Node(vec![Token(Symbol, "symbol".to_string(), Position(0, 0))],
                                  Position(0, 0))],
                        Position(0, 0)))
    }

    #[test]
    fn test_parse_parentheses() {
        assert_eq!(parse("(hi there)").unwrap().unwrap(),
                   Node(vec![Node(vec![Node(vec![Token(Symbol,
                                                       "hi".to_string(),
                                                       Position(0, 1)),
                                                 Token(Symbol,
                                                       "there".to_string(),
                                                       Position(0, 4))],
                                            Position(0, 0))],
                                  Position(0, 0))],
                        Position(0, 0)));

        // Try with two levels
        assert_eq!(parse("(hi (one) there)").unwrap().unwrap(),
                   Node(vec![Node(vec![Node(vec![Token(Symbol,
                                                       "hi".to_string(),
                                                       Position(0, 1)),
                                                 Node(vec![Token(Symbol,
                                                                 "one".to_string(),
                                                                 Position(0, 5))],
                                                      Position(0, 4)),
                                                 Token(Symbol,
                                                       "there".to_string(),
                                                       Position(0, 10))],
                                            Position(0, 0))],
                                  Position(0, 0))],
                        Position(0, 0)));
    }

    #[test]
    fn test_fail_parse_parentheses() {
        match parse("(hi there") {
            Ok(_) => panic!("I'm assertng this should not parse correctly"),
            Err(Error { description, position: _ }) => {
                assert_eq!("hit end of file while reading an open paren".to_string(),
                           description);
            }
        }
    }

    #[test]
    fn test_two_lines_of_code() {
        assert_eq!(parse("hi there\n(one two)").unwrap().unwrap(),
                   Node(vec![
                Node(vec![
                    Token(Symbol, "hi".to_string(), Position(0, 0)),
                    Token(Symbol, "there".to_string(), Position(0, 3))
                ], Position(0,0)),
                Node(vec![
                    Node(vec![
                        Token(Symbol, "one".to_string(), Position(1, 1)),
                        Token(Symbol, "two".to_string(), Position(1, 5))
                    ], Position(1,0)),
                ], Position(1,0)),
            ],
                        Position(0, 0)));
    }

}
<|endoftext|>


<commit_before>use command::{Command, Query};

#[derive(Clone, Debug)]
pub struct CommandLine {
    command: Command,
    arguments: Query,
}

impl CommandLine {
    pub fn new(command: Command, query: Query) -> CommandLine {
        CommandLine {
            command: command,
            arguments: query,
        }
    }

    /// Get command enum constant to construct url encoded query.
    pub fn get_command(&mut self) -> Command {
        self.command.clone()
    }

    /// Get vectorize `("key", "value")` pairs to construct url encoded query.
    pub fn get_argument(&mut self) -> Query {
        self.arguments.clone()
    }

    /// Set vectorize `("key", "value")` pairs to construct url encoded query.
    pub fn set_argument(&mut self, arguments: Query) {
        self.arguments = arguments
    }

    /// Create command line style command query.
    ///
    /// `vec![("key","value")]` interprets to `"--key value"`.
    /// And two or more value pair are concatinate with ` `(a space).
    pub fn make_command(&mut self) -> String {
        let mut output = String::new();
        for query in self.arguments.clone() {
            let (key, value) = query;
            if output.len() > 0 {
                output.push_str(" ");
            }
            let format_string = format!("--{} {}", &*key, &*value);
            output.push_str(format_string.as_str());
        }
        output
    }

    ///
    /// Create Groonga command line style command.
    pub fn encode(&mut self) -> String {
        format!("{} {}",
                self.get_command(),
                self.make_command())
            .trim() // Remove trailing white space(s).
            .to_owned()
    }
}

#[cfg(test)]
mod test {
    use super::CommandLine;
    use select::SelectCommand;
    use load::{LoadCommand, InputType};
    use status::StatusCommand;
    use dump::DumpCommand;
    use delete::DeleteCommand;
    use table_create::TableCreateCommand;
    use types::table_flag_type::TableFlagType;
    use column_create::ColumnCreateCommand;
    use types::data_type::DataType;

    #[test]
    fn test_select() {
        let (command, query) = SelectCommand::new("Test".to_string())
                                   .filter("output_column @ \"type_safe\"".to_string())
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "select --table Test --filter \'output_column @ \"type_safe\"\'";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_load() {
        let load_data: &'static str = r#"[
{"_key":"http://example.org/","title":"This is test record 1!"},
]"#;
        let (command, query, values) = LoadCommand::new("Test".to_string(), load_data.to_string())
                                           .input_type(InputType::Json)
                                           .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "load --table Test --input_type json";
        assert_eq!(cli_encoded.to_string(), command.encode());
        assert_eq!(load_data.to_string(), values);
    }

    #[test]
    fn test_status() {
        let (command, query) = StatusCommand::new().build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "status";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_dump() {
        let (command, query) = DumpCommand::new()
                                   .tables(vec!["Books".to_string(), "Categories".to_string()])
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "dump --tables Books,Categories";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_delete() {
        let (command, query) = DeleteCommand::new("Books".to_string())
                                   .filter("author == unknown".to_string())
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "delete --table Books --filter \'author == unknown\'";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_table_create() {
        let (command, query) = TableCreateCommand::new("Test".to_string())
                                   .flags(vec![(TableFlagType::PatKey),
                                               (TableFlagType::KeyWithSIS)])
                                   .build();
        let mut command = CommandLine::new(command, query);
        let url_encoded = "table_create --name Test --flags TABLE_PAT_KEY|TABLE_KEY_WITH_SIS";
        assert_eq!(url_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_column_create() {
        let (command, query) = ColumnCreateCommand::new("Test".to_string(), "element".to_string())
                                   .column_type(DataType::LongText)
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "column_create --table Test --name element --type LongText";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }
}
<commit_msg>command_line: rustfmt<commit_after>use command::{Command, Query};

#[derive(Clone, Debug)]
pub struct CommandLine {
    command: Command,
    arguments: Query,
}

impl CommandLine {
    pub fn new(command: Command, query: Query) -> CommandLine {
        CommandLine {
            command: command,
            arguments: query,
        }
    }

    /// Get command enum constant to construct url encoded query.
    pub fn get_command(&mut self) -> Command {
        self.command.clone()
    }

    /// Get vectorize `("key", "value")` pairs to construct url encoded query.
    pub fn get_argument(&mut self) -> Query {
        self.arguments.clone()
    }

    /// Set vectorize `("key", "value")` pairs to construct url encoded query.
    pub fn set_argument(&mut self, arguments: Query) {
        self.arguments = arguments
    }

    /// Create command line style command query.
    ///
    /// `vec![("key","value")]` interprets to `"--key value"`.
    /// And two or more value pair are concatinate with ` `(a space).
    pub fn make_command(&mut self) -> String {
        let mut output = String::new();
        for query in self.arguments.clone() {
            let (key, value) = query;
            if output.len() > 0 {
                output.push_str(" ");
            }
            let format_string = format!("--{} {}", &*key, &*value);
            output.push_str(format_string.as_str());
        }
        output
    }

    ///
    /// Create Groonga command line style command.
    pub fn encode(&mut self) -> String {
        format!("{} {}", self.get_command(), self.make_command())
            .trim()
            .to_owned()
    }
}

#[cfg(test)]
mod test {
    use super::CommandLine;
    use select::SelectCommand;
    use load::{LoadCommand, InputType};
    use status::StatusCommand;
    use dump::DumpCommand;
    use delete::DeleteCommand;
    use table_create::TableCreateCommand;
    use types::table_flag_type::TableFlagType;
    use column_create::ColumnCreateCommand;
    use types::data_type::DataType;

    #[test]
    fn test_select() {
        let (command, query) = SelectCommand::new("Test".to_string())
                                   .filter("output_column @ \"type_safe\"".to_string())
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "select --table Test --filter \'output_column @ \"type_safe\"\'";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_load() {
        let load_data: &'static str = r#"[
{"_key":"http://example.org/","title":"This is test record 1!"},
]"#;
        let (command, query, values) = LoadCommand::new("Test".to_string(), load_data.to_string())
                                           .input_type(InputType::Json)
                                           .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "load --table Test --input_type json";
        assert_eq!(cli_encoded.to_string(), command.encode());
        assert_eq!(load_data.to_string(), values);
    }

    #[test]
    fn test_status() {
        let (command, query) = StatusCommand::new().build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "status";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_dump() {
        let (command, query) = DumpCommand::new()
                                   .tables(vec!["Books".to_string(), "Categories".to_string()])
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "dump --tables Books,Categories";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_delete() {
        let (command, query) = DeleteCommand::new("Books".to_string())
                                   .filter("author == unknown".to_string())
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "delete --table Books --filter \'author == unknown\'";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_table_create() {
        let (command, query) = TableCreateCommand::new("Test".to_string())
                                   .flags(vec![(TableFlagType::PatKey),
                                               (TableFlagType::KeyWithSIS)])
                                   .build();
        let mut command = CommandLine::new(command, query);
        let url_encoded = "table_create --name Test --flags TABLE_PAT_KEY|TABLE_KEY_WITH_SIS";
        assert_eq!(url_encoded.to_string(), command.encode());
    }

    #[test]
    fn test_column_create() {
        let (command, query) = ColumnCreateCommand::new("Test".to_string(), "element".to_string())
                                   .column_type(DataType::LongText)
                                   .build();
        let mut command = CommandLine::new(command, query);
        let cli_encoded = "column_create --table Test --name element --type LongText";
        assert_eq!(cli_encoded.to_string(), command.encode());
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::INTERNAL_METADATA_DIRECTIVE;
use common::{Diagnostic, DiagnosticsResult, NamedItem, WithLocation};
use graphql_ir::{
    Argument, ConstantArgument, ConstantValue, Directive, OperationDefinition, Program,
    Transformed, Transformer, ValidationMessage, Value,
};
use graphql_syntax::OperationKind;
use interner::{Intern, StringKey};
use lazy_static::lazy_static;

lazy_static! {
    static ref LIVE_QUERY_DIRECTIVE_NAME: StringKey = "live_query".intern();
    static ref LIVE_METADATA_KEY: StringKey = "live".intern();
    static ref POLLING_INTERVAL_ARG: StringKey = "polling_interval".intern();
    static ref CONFIG_ID_ARG: StringKey = "config_id".intern();
}

pub fn generate_live_query_metadata(program: &Program) -> DiagnosticsResult<Program> {
    let mut transformer = GenerateLiveQueryMetadata::new(program);
    let next_program = transformer
        .transform_program(program)
        .replace_or_else(|| program.clone());

    if transformer.errors.is_empty() {
        Ok(next_program)
    } else {
        Err(transformer.errors)
    }
}

struct GenerateLiveQueryMetadata<'s> {
    pub program: &'s Program,
    pub errors: Vec<Diagnostic>,
}

impl<'s> GenerateLiveQueryMetadata<'s> {
    fn new(program: &'s Program) -> Self {
        GenerateLiveQueryMetadata {
            program,
            errors: vec![],
        }
    }
}

impl<'s> Transformer for GenerateLiveQueryMetadata<'s> {
    const NAME: &'static str = "GenerateLiveQueryMetadata";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        match operation.kind {
            OperationKind::Query => {
                let live_query_directive = operation.directives.named(*LIVE_QUERY_DIRECTIVE_NAME);

                if let Some(live_query_directive) = live_query_directive {
                    let polling_interval =
                        live_query_directive.arguments.named(*POLLING_INTERVAL_ARG);
                    let config_id = live_query_directive.arguments.named(*CONFIG_ID_ARG);

                    if polling_interval.is_none() && config_id.is_none() {
                        self.errors.push(Diagnostic::error(
                            ValidationMessage::LiveQueryTransformMissingConfig {
                                query_name: operation.name.item,
                            },
                            live_query_directive.name.location,
                        ));
                        return Transformed::Keep;
                    }

                    let mut next_directives = operation.directives.clone();

                    // I'm porting JS logic here. Not sure if the case where
                    // polling_interval and config_id both provided handled correctly
                    if let Some(polling_interval) = polling_interval {
                        let poll_interval_value = match polling_interval.value.item {
                            Value::Constant(ConstantValue::Int(value)) => value,
                            _ => {
                                self.errors.push(Diagnostic::error(
                                    ValidationMessage::LiveQueryTransformInvalidPollingInterval {
                                        query_name: operation.name.item,
                                    },
                                    polling_interval.value.location,
                                ));
                                return Transformed::Keep;
                            }
                        };
                        next_directives.push(Directive {
                            name: WithLocation::new(
                                operation.name.location,
                                *INTERNAL_METADATA_DIRECTIVE,
                            ),
                            arguments: vec![Argument {
                                name: WithLocation::new(
                                    operation.name.location,
                                    *LIVE_METADATA_KEY,
                                ),
                                value: WithLocation::new(
                                    operation.name.location,
                                    Value::Constant(ConstantValue::Object(vec![
                                        ConstantArgument {
                                            name: WithLocation::new(
                                                operation.name.location,
                                                *POLLING_INTERVAL_ARG,
                                            ),
                                            value: WithLocation::new(
                                                operation.name.location,
                                                ConstantValue::Int(poll_interval_value),
                                            ),
                                        },
                                    ])),
                                ),
                            }],
                        });
                    } else if let Some(config_id) = config_id {
                        let config_id_value = match config_id.value.item {
                            Value::Constant(ConstantValue::String(value)) => value,
                            _ => {
                                self.errors.push(Diagnostic::error(
                                    ValidationMessage::LiveQueryTransformInvalidConfigId {
                                        query_name: operation.name.item,
                                    },
                                    config_id.value.location,
                                ));
                                return Transformed::Keep;
                            }
                        };
                        next_directives.push(Directive {
                            name: WithLocation::new(
                                operation.name.location,
                                *INTERNAL_METADATA_DIRECTIVE,
                            ),
                            arguments: vec![Argument {
                                name: WithLocation::new(
                                    operation.name.location,
                                    *LIVE_METADATA_KEY,
                                ),
                                value: WithLocation::new(
                                    operation.name.location,
                                    Value::Constant(ConstantValue::Object(vec![
                                        ConstantArgument {
                                            name: WithLocation::new(
                                                operation.name.location,
                                                *CONFIG_ID_ARG,
                                            ),
                                            value: WithLocation::new(
                                                operation.name.location,
                                                ConstantValue::String(config_id_value),
                                            ),
                                        },
                                    ])),
                                ),
                            }],
                        });
                    }

                    Transformed::Replace(OperationDefinition {
                        directives: next_directives,
                        ..operation.clone()
                    })
                } else {
                    Transformed::Keep
                }
            }
            _ => Transformed::Keep,
        }
    }
}
<commit_msg>Clean up generate_live_query_metadata<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::INTERNAL_METADATA_DIRECTIVE;
use common::{Diagnostic, DiagnosticsResult, NamedItem, WithLocation};
use graphql_ir::{
    Argument, ConstantArgument, ConstantValue, Directive, OperationDefinition, Program,
    Transformed, Transformer, ValidationMessage, Value,
};
use graphql_syntax::OperationKind;
use interner::{Intern, StringKey};
use lazy_static::lazy_static;

lazy_static! {
    static ref LIVE_QUERY_DIRECTIVE_NAME: StringKey = "live_query".intern();
    static ref LIVE_METADATA_KEY: StringKey = "live".intern();
    static ref POLLING_INTERVAL_ARG: StringKey = "polling_interval".intern();
    static ref CONFIG_ID_ARG: StringKey = "config_id".intern();
}

pub fn generate_live_query_metadata(program: &Program) -> DiagnosticsResult<Program> {
    let mut transformer = GenerateLiveQueryMetadata::default();
    let next_program = transformer
        .transform_program(program)
        .replace_or_else(|| program.clone());

    if transformer.errors.is_empty() {
        Ok(next_program)
    } else {
        Err(transformer.errors)
    }
}

#[derive(Default)]
struct GenerateLiveQueryMetadata {
    errors: Vec<Diagnostic>,
}

impl Transformer for GenerateLiveQueryMetadata {
    const NAME: &'static str = "GenerateLiveQueryMetadata";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        match operation.kind {
            OperationKind::Query => {
                let live_query_directive = operation.directives.named(*LIVE_QUERY_DIRECTIVE_NAME);

                if let Some(live_query_directive) = live_query_directive {
                    let polling_interval =
                        live_query_directive.arguments.named(*POLLING_INTERVAL_ARG);
                    let config_id = live_query_directive.arguments.named(*CONFIG_ID_ARG);

                    if polling_interval.is_none() && config_id.is_none() {
                        self.errors.push(Diagnostic::error(
                            ValidationMessage::LiveQueryTransformMissingConfig {
                                query_name: operation.name.item,
                            },
                            live_query_directive.name.location,
                        ));
                        return Transformed::Keep;
                    }

                    let mut next_directives = operation.directives.clone();

                    // I'm porting JS logic here. Not sure if the case where
                    // polling_interval and config_id both provided handled correctly
                    if let Some(polling_interval) = polling_interval {
                        let poll_interval_value = match polling_interval.value.item {
                            Value::Constant(ConstantValue::Int(value)) => value,
                            _ => {
                                self.errors.push(Diagnostic::error(
                                    ValidationMessage::LiveQueryTransformInvalidPollingInterval {
                                        query_name: operation.name.item,
                                    },
                                    polling_interval.value.location,
                                ));
                                return Transformed::Keep;
                            }
                        };
                        next_directives.push(Directive {
                            name: WithLocation::new(
                                operation.name.location,
                                *INTERNAL_METADATA_DIRECTIVE,
                            ),
                            arguments: vec![Argument {
                                name: WithLocation::new(
                                    operation.name.location,
                                    *LIVE_METADATA_KEY,
                                ),
                                value: WithLocation::new(
                                    operation.name.location,
                                    Value::Constant(ConstantValue::Object(vec![
                                        ConstantArgument {
                                            name: WithLocation::new(
                                                operation.name.location,
                                                *POLLING_INTERVAL_ARG,
                                            ),
                                            value: WithLocation::new(
                                                operation.name.location,
                                                ConstantValue::Int(poll_interval_value),
                                            ),
                                        },
                                    ])),
                                ),
                            }],
                        });
                    } else if let Some(config_id) = config_id {
                        let config_id_value = match config_id.value.item {
                            Value::Constant(ConstantValue::String(value)) => value,
                            _ => {
                                self.errors.push(Diagnostic::error(
                                    ValidationMessage::LiveQueryTransformInvalidConfigId {
                                        query_name: operation.name.item,
                                    },
                                    config_id.value.location,
                                ));
                                return Transformed::Keep;
                            }
                        };
                        next_directives.push(Directive {
                            name: WithLocation::new(
                                operation.name.location,
                                *INTERNAL_METADATA_DIRECTIVE,
                            ),
                            arguments: vec![Argument {
                                name: WithLocation::new(
                                    operation.name.location,
                                    *LIVE_METADATA_KEY,
                                ),
                                value: WithLocation::new(
                                    operation.name.location,
                                    Value::Constant(ConstantValue::Object(vec![
                                        ConstantArgument {
                                            name: WithLocation::new(
                                                operation.name.location,
                                                *CONFIG_ID_ARG,
                                            ),
                                            value: WithLocation::new(
                                                operation.name.location,
                                                ConstantValue::String(config_id_value),
                                            ),
                                        },
                                    ])),
                                ),
                            }],
                        });
                    }

                    Transformed::Replace(OperationDefinition {
                        directives: next_directives,
                        ..operation.clone()
                    })
                } else {
                    Transformed::Keep
                }
            }
            _ => Transformed::Keep,
        }
    }
}
<|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/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::fs::OpenOptions;
use std::path::PathBuf;

use serde_json;

use devicemapper::{devnode_to_devno, Device};

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

use super::super::super::types::PoolUuid;

use super::super::serde_structs::{BackstoreSave, PoolSave};

use super::blockdev::StratBlockDev;
use super::device::{blkdev_size, is_stratis_device};
use super::metadata::BDA;
use super::util::get_stratis_block_devices;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                is_stratis_device(&devnode)?.and_then(|pool_uuid| {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode)
                });
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<PoolSave>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas.iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in bdas.iter()
        .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time))
    {
        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data successfully read";
    Err(StratisError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
#[allow(implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<_, _> = backstore_save
        .data_devs
        .iter()
        .map(|bds| (bds.uuid, bds))
        .collect();

    let recorded_cache_map: HashMap<_, _> = match backstore_save.cache_devs {
        Some(ref cache_devs) => cache_devs.iter().map(|bds| (bds.uuid, bds)).collect(),
        None => HashMap::new(),
    };

    let mut segment_table = HashMap::new();
    for seg in &backstore_save.data_segments {
        segment_table
            .entry(seg.0)
            .or_insert_with(Vec::default)
            .push((seg.1, seg.2))
    }
    if let Some(ref segs) = backstore_save.cache_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }
    if let Some(ref segs) = backstore_save.meta_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }

    let (mut datadevs, mut cachedevs) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                let actual_size =
                    blkdev_size(&OpenOptions::new().read(true).open(devnode)?)?.sectors();

                if actual_size < bda.dev_size() {
                    let err_msg = format!(
                        "actual blockdev size ({}) < recorded size ({})",
                        actual_size,
                        bda.dev_size()
                    );

                    return Err(StratisError::Engine(ErrorEnum::Error, err_msg));
                }

                let dev_uuid = bda.dev_uuid();

                let (dev_vec, bd_save) = match recorded_data_map.get(&dev_uuid) {
                    Some(bd_save) => (&mut datadevs, bd_save),
                    None => match recorded_cache_map.get(&dev_uuid) {
                        Some(bd_save) => (&mut cachedevs, bd_save),
                        None => {
                            let err_msg =
                                format!("Blockdev {} not found in metadata", bda.dev_uuid());
                            return Err(StratisError::Engine(ErrorEnum::NotFound, err_msg));
                        }
                    },
                };

                // This should always succeed since the actual size is at
                // least the recorded size, so all segments should be
                // available to be allocated. If this fails, the most likely
                // conclusion is metadata corruption.
                let segments = segment_table.get(&dev_uuid);
                dev_vec.push(StratBlockDev::new(
                    *device,
                    devnode.to_owned(),
                    bda,
                    segments.unwrap_or(&vec![]),
                    bd_save.user_info.clone(),
                    bd_save.hardware_info.clone(),
                )?);
            }
        }
    }

    // Verify that datadevs found match datadevs recorded.
    let current_data_uuids: HashSet<_> = datadevs.iter().map(|b| b.uuid()).collect();
    let recorded_data_uuids: HashSet<_> = recorded_data_map.keys().cloned().collect();
    if current_data_uuids != recorded_data_uuids {
        let err_msg = "Recorded data dev UUIDs != discovered datadev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if datadevs.len() != current_data_uuids.len() {
        let err_msg = "Duplicate data devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    // Verify that cachedevs found match cachedevs recorded.
    let current_cache_uuids: HashSet<_> = cachedevs.iter().map(|b| b.uuid()).collect();
    let recorded_cache_uuids: HashSet<_> = recorded_cache_map.keys().cloned().collect();
    if current_cache_uuids != recorded_cache_uuids {
        let err_msg = "Recorded cache dev UUIDs != discovered cachedev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if cachedevs.len() != current_cache_uuids.len() {
        let err_msg = "Duplicate cache devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok((datadevs, cachedevs))
}
<commit_msg>Assert a precondition on get_blockdevs<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/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::fs::OpenOptions;
use std::path::PathBuf;

use serde_json;

use devicemapper::{devnode_to_devno, Device};

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

use super::super::super::types::PoolUuid;

use super::super::serde_structs::{BackstoreSave, PoolSave};

use super::blockdev::StratBlockDev;
use super::device::{blkdev_size, is_stratis_device};
use super::metadata::BDA;
use super::util::get_stratis_block_devices;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                is_stratis_device(&devnode)?.and_then(|pool_uuid| {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode)
                });
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<PoolSave>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas.iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in bdas.iter()
        .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time))
    {
        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data successfully read";
    Err(StratisError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
#[allow(implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<_, _> = backstore_save
        .data_devs
        .iter()
        .map(|bds| (bds.uuid, bds))
        .collect();

    let recorded_cache_map: HashMap<_, _> = match backstore_save.cache_devs {
        Some(ref cache_devs) => cache_devs.iter().map(|bds| (bds.uuid, bds)).collect(),
        None => HashMap::new(),
    };

    let mut segment_table = HashMap::new();
    for seg in &backstore_save.data_segments {
        segment_table
            .entry(seg.0)
            .or_insert_with(Vec::default)
            .push((seg.1, seg.2))
    }
    if let Some(ref segs) = backstore_save.cache_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }
    if let Some(ref segs) = backstore_save.meta_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }

    let (mut datadevs, mut cachedevs) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        let actual_size = blkdev_size(&OpenOptions::new().read(true).open(devnode)?)?.sectors();

        if actual_size < bda.dev_size() {
            let err_msg = format!(
                "actual blockdev size ({}) < recorded size ({})",
                actual_size,
                bda.dev_size()
            );

            return Err(StratisError::Engine(ErrorEnum::Error, err_msg));
        }

        let dev_uuid = bda.dev_uuid();

        let (dev_vec, bd_save) = match recorded_data_map.get(&dev_uuid) {
            Some(bd_save) => (&mut datadevs, bd_save),
            None => match recorded_cache_map.get(&dev_uuid) {
                Some(bd_save) => (&mut cachedevs, bd_save),
                None => {
                    let err_msg = format!("Blockdev {} not found in metadata", bda.dev_uuid());
                    return Err(StratisError::Engine(ErrorEnum::NotFound, err_msg));
                }
            },
        };

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        dev_vec.push(StratBlockDev::new(
            *device,
            devnode.to_owned(),
            bda,
            segments.unwrap_or(&vec![]),
            bd_save.user_info.clone(),
            bd_save.hardware_info.clone(),
        )?);
    }

    // Verify that datadevs found match datadevs recorded.
    let current_data_uuids: HashSet<_> = datadevs.iter().map(|b| b.uuid()).collect();
    let recorded_data_uuids: HashSet<_> = recorded_data_map.keys().cloned().collect();
    if current_data_uuids != recorded_data_uuids {
        let err_msg = "Recorded data dev UUIDs != discovered datadev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if datadevs.len() != current_data_uuids.len() {
        let err_msg = "Duplicate data devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    // Verify that cachedevs found match cachedevs recorded.
    let current_cache_uuids: HashSet<_> = cachedevs.iter().map(|b| b.uuid()).collect();
    let recorded_cache_uuids: HashSet<_> = recorded_cache_map.keys().cloned().collect();
    if current_cache_uuids != recorded_cache_uuids {
        let err_msg = "Recorded cache dev UUIDs != discovered cachedev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if cachedevs.len() != current_cache_uuids.len() {
        let err_msg = "Duplicate cache devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok((datadevs, cachedevs))
}
<|endoftext|>


<commit_before>/*

  This struct essentially encapsulates the "instance variables"

*/
pub struct SimpleHdrHistogram {
    pub leading_zeros_count_base: usize,
    pub sub_bucket_mask: u64,
    pub unit_magnitude: u32,
    pub sub_bucket_count: usize,
    pub sub_bucket_half_count: usize,
    pub sub_bucket_half_count_magnitude: u32,
    pub counts: Vec<u64>,
    pub counts_array_length: usize,
    pub normalizing_index_offset: usize,
}

/*

  Implementing this trait (Default) for our struct gives us a nice way to
  initialize an instance using default args instead of having to provide all of them

 */
impl Default for SimpleHdrHistogram {
    fn default () -> SimpleHdrHistogram {
        SimpleHdrHistogram {
            leading_zeros_count_base: 0,
            sub_bucket_mask: 0,
            unit_magnitude: 0,
            sub_bucket_count: 0,
            sub_bucket_half_count: 0,
            sub_bucket_half_count_magnitude: 0,
            counts: Vec::new(),
            counts_array_length: 0,
            normalizing_index_offset: 0,
        }
    }
}

pub trait HistogramBase {

    //FIXME this stuff could be mostly unsigned

    //TODO should be default impl of this trait
    fn record_single_value(&mut self, value: u64) -> Result<(), String>;

    //TODO should be default impl of this trait
    fn counts_array_index(&self, value: u64) -> Result<usize, String>;

    //TODO should be default impl of this trait
    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize;

    //TODO should be default impl of this trait
    fn get_bucket_index(&self, value: u64) -> usize;

    //TODO should be default impl of this trait
    fn get_sub_bucket_index(&self, value: u64, bucket_index: usize) -> usize;

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String>;
    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
        Result<usize, String>;

}

impl HistogramBase for SimpleHdrHistogram {

    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
Result<usize, String> {

        match normalizing_index_offset {
            0 => Ok(index),
            _ =>
                if ((index > array_length) || (index < 0)) {
                    Err(String::from("index out of covered range"))
                } else {
                    let mut normalized_index: usize = index - normalizing_index_offset;
                    if (normalized_index < 0) {
                        normalized_index += array_length;
                    } else if (normalized_index >= array_length) {
                        normalized_index -=array_length;
                    }
                    Ok(normalized_index)
                }
        }

    }

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String> {
        let normalized_index =
            self.normalize_index(index, self.normalizing_index_offset, self.counts_array_length);
        match normalized_index {
            Ok(the_index) => {
                self.counts[the_index] += 1;
                Ok(())
            }
            Err(err) =>
                Err(err)
        }
    }

    fn get_sub_bucket_index(&self, value: u64, bucket_index: usize) -> usize {
        let sum = bucket_index + self.unit_magnitude as usize;
        value.rotate_right(sum as u32) as usize
    }

    fn get_bucket_index(&self, value: u64) -> usize {
        let valueOrred = value | self.sub_bucket_mask;
        self.leading_zeros_count_base - (valueOrred.leading_zeros() as usize)
    }

    fn record_single_value(&mut self, value: u64) -> Result<(), String> {

        match self.counts_array_index(value) {
            Ok(counts_index) =>
                match self.increment_count_at_index(counts_index) {
                    Ok(_) =>
                        if true {
                            Ok(())
                        } else {
                            Err(String::from("Could not record single value"))
                        },
                    Err(err) => Err(String::from("Could not increment stuff"))
                },
            Err(err) => Err(String::from("Could not get index"))
        }

    }

    fn counts_array_index(&self, value: u64) -> Result<usize, String> {
        if value < 0 {
            Err(String::from("Histogram recorded values cannot be negative."))
        } else {
            let bucket_index = self.get_bucket_index(value);
            let sub_bucket_index = self.get_sub_bucket_index(value, bucket_index);
            let result = self.counts_array_index_sub(bucket_index, sub_bucket_index);
            Ok(result)
        }
    }

    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize {
        assert!(sub_bucket_index < self.sub_bucket_count);
        assert!(bucket_index == 0 || (sub_bucket_index >= self.sub_bucket_half_count));

        let bucket_base_index = (bucket_index + 1) << self.sub_bucket_half_count_magnitude;
        let offset_in_bucket = sub_bucket_index - self.sub_bucket_half_count;

        bucket_base_index + offset_in_bucket
    }
}

#[test]
fn can_record_single_value() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.record_single_value(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not add single record to histogram because error: {}", err))
    }
}

#[test]
fn can_compute_counts_array_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.counts_array_index(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not compute counts array index because error: {}", err))
    }
}

#[test]
fn can_get_bucket_index() {
    let the_hist = init_histo();
    let result = the_hist.get_bucket_index(99);
    assert_eq!(result, 0)
}

#[test]
fn can_get_sub_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_sub_bucket_index(99, 1);
    assert_eq!(result, 0)
}<commit_msg>add histogram init helper<commit_after>/*

  This struct essentially encapsulates the "instance variables"

*/
#[derive(Debug)]
pub struct SimpleHdrHistogram {
    pub leading_zeros_count_base: usize,
    pub sub_bucket_mask: u64,
    pub unit_magnitude: u32,
    pub sub_bucket_count: usize,
    pub sub_bucket_half_count: usize,
    pub sub_bucket_half_count_magnitude: u32,
    pub counts: Vec<u64>,
    pub counts_array_length: usize,
    pub normalizing_index_offset: usize,
}

/*

  Implementing this trait (Default) for our struct gives us a nice way to
  initialize an instance using default args instead of having to provide all of them

 */
impl Default for SimpleHdrHistogram {
    fn default () -> SimpleHdrHistogram {
        SimpleHdrHistogram {
            leading_zeros_count_base: 0,
            sub_bucket_mask: 0,
            unit_magnitude: 0,
            sub_bucket_count: 0,
            sub_bucket_half_count: 0,
            sub_bucket_half_count_magnitude: 0,
            counts: Vec::new(),
            counts_array_length: 0,
            normalizing_index_offset: 0,
        }
    }
}

pub trait HistogramBase {

    //FIXME this stuff could be mostly unsigned

    //TODO should be default impl of this trait
    fn record_single_value(&mut self, value: u64) -> Result<(), String>;

    //TODO should be default impl of this trait
    fn counts_array_index(&self, value: u64) -> Result<usize, String>;

    //TODO should be default impl of this trait
    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize;

    //TODO should be default impl of this trait
    fn get_bucket_index(&self, value: u64) -> usize;

    //TODO should be default impl of this trait
    fn get_sub_bucket_index(&self, value: u64, bucket_index: usize) -> usize;

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String>;
    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
        Result<usize, String>;

}

impl HistogramBase for SimpleHdrHistogram {

    fn normalize_index(&self, index: usize, normalizing_index_offset: usize, array_length: usize) ->
Result<usize, String> {

        match normalizing_index_offset {
            0 => Ok(index),
            _ =>
                if ((index > array_length) || (index < 0)) {
                    Err(String::from("index out of covered range"))
                } else {
                    let mut normalized_index: usize = index - normalizing_index_offset;
                    if (normalized_index < 0) {
                        normalized_index += array_length;
                    } else if (normalized_index >= array_length) {
                        normalized_index -=array_length;
                    }
                    Ok(normalized_index)
                }
        }

    }

    fn increment_count_at_index(&mut self, index: usize) -> Result<(), String> {
        let normalized_index =
            self.normalize_index(index, self.normalizing_index_offset, self.counts_array_length);
        match normalized_index {
            Ok(the_index) => {
                self.counts[the_index] += 1;
                Ok(())
            }
            Err(err) =>
                Err(err)
        }
    }

    fn get_sub_bucket_index(&self, value: u64, bucket_index: usize) -> usize {
        let sum = bucket_index + self.unit_magnitude as usize;
        value.rotate_right(sum as u32) as usize
    }

    fn get_bucket_index(&self, value: u64) -> usize {
        let valueOrred = value | self.sub_bucket_mask;
        self.leading_zeros_count_base - (valueOrred.leading_zeros() as usize)
    }

    fn record_single_value(&mut self, value: u64) -> Result<(), String> {

        match self.counts_array_index(value) {
            Ok(counts_index) =>
                match self.increment_count_at_index(counts_index) {
                    Ok(_) =>
                        if true {
                            Ok(())
                        } else {
                            Err(String::from("Could not record single value"))
                        },
                    Err(err) => Err(String::from("Could not increment stuff"))
                },
            Err(err) => Err(String::from("Could not get index"))
        }

    }

    fn counts_array_index(&self, value: u64) -> Result<usize, String> {
        if value < 0 {
            Err(String::from("Histogram recorded values cannot be negative."))
        } else {
            let bucket_index = self.get_bucket_index(value);
            let sub_bucket_index = self.get_sub_bucket_index(value, bucket_index);
            let result = self.counts_array_index_sub(bucket_index, sub_bucket_index);
            Ok(result)
        }
    }

    fn counts_array_index_sub(&self, bucket_index: usize, sub_bucket_index: usize) -> usize {
        assert!(sub_bucket_index < self.sub_bucket_count);
        assert!(bucket_index == 0 || (sub_bucket_index >= self.sub_bucket_half_count));

        let bucket_base_index = (bucket_index + 1) << self.sub_bucket_half_count_magnitude;
        let offset_in_bucket = sub_bucket_index - self.sub_bucket_half_count;

        bucket_base_index + offset_in_bucket
    }
}

#[test]
fn can_record_single_value() {
    let mut the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.record_single_value(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not add single record to histogram because error: {}", err))
    }
}

#[test]
fn can_compute_counts_array_index() {
    let mut the_hist = init_histo(1, 1000, 5);
    let result = the_hist.counts_array_index(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not compute counts array index because error: {}", err))
    }
}

#[test]
fn can_get_bucket_index() {
    let mut the_hist = init_histo(1, 1000, 5);
    let result = the_hist.get_bucket_index(99);
    assert_eq!(result, 0)
}

#[test]
fn can_get_sub_bucket_index() {
    let mut the_hist = init_histo(1, 1000, 5);
    let result = the_hist.get_sub_bucket_index(99, 1);
    assert_eq!(result, 0)
}

/// lowest_discernible_value: must be >= 1
/// highest_trackable_value: must be >= 2 * lowest_discernible_value
/// num_significant_digits: must be <= 5
fn init_histo(lowest_discernible_value: u64, highest_trackable_value: u64, num_significant_digits: u32) -> SimpleHdrHistogram {

    assert!(lowest_discernible_value >= 1);
    assert!(highest_trackable_value >= 2 * lowest_discernible_value);
    assert!(num_significant_digits <= 5);

    let mut hist =  SimpleHdrHistogram { ..Default::default() };

    let largest_value_with_single_unit_resolution = 2_u64 * 10_u64.pow(num_significant_digits);

    let unit_magnitude = ((lowest_discernible_value as f64).ln() / 2_f64.ln()) as u32;
    let unit_magnitude_mask: u64  = 1_u64.rotate_left(unit_magnitude) - 1;

    // find nearest power of 2 to largest_value_with_single_unit_resolution
    let sub_bucket_count_magnitude: u32 =
        ((largest_value_with_single_unit_resolution as f64).ln() / 2_f64.ln()).ceil() as u32;

    // ugly looking... how should ternaries be done?
    let sub_bucket_half_count_magnitude: u32 = (if sub_bucket_count_magnitude > 1 { sub_bucket_count_magnitude } else { 1 }) - 1;
    let sub_bucket_count: usize = 2_usize.pow(sub_bucket_half_count_magnitude + 1);
    let sub_bucket_half_count: usize = sub_bucket_count / 2;
    // TODO is this cast OK?
    let sub_bucket_mask = (sub_bucket_count - 1).rotate_left(unit_magnitude) as u64;

    let counts_arr_len = counts_arr_len(highest_trackable_value, sub_bucket_count, unit_magnitude);
    let bucket_count = buckets_needed_for_value(highest_trackable_value, sub_bucket_count, unit_magnitude);

    let leading_zero_count_base: usize = (64_u32 - unit_magnitude - sub_bucket_half_count_magnitude - 1) as usize;

    hist.leading_zeros_count_base = leading_zero_count_base;
    hist.sub_bucket_mask = sub_bucket_mask;
    hist.unit_magnitude = unit_magnitude;
    hist.sub_bucket_count = sub_bucket_count;
    hist.sub_bucket_half_count = sub_bucket_half_count;
    hist.sub_bucket_half_count_magnitude = sub_bucket_half_count_magnitude;
    hist.counts = Vec::with_capacity(counts_arr_len);
    hist.counts_array_length = counts_arr_len;
    hist.normalizing_index_offset = 0_usize; // 0 for normal Histogram ctor in Java impl

    println!("hist: {:?}", hist);

    hist
}

fn buckets_needed_for_value(value: u64, sub_bucket_count: usize, unit_magnitude: u32) -> usize {

    // is this cast ok?
    let mut smallest_untrackable_value: u64 = sub_bucket_count.rotate_left(unit_magnitude) as u64;
    let mut buckets_needed: usize = 1;

    while smallest_untrackable_value <= value {
        if (smallest_untrackable_value > u64::max_value() / 2) {
            return buckets_needed + 1;
        }

        smallest_untrackable_value = smallest_untrackable_value.rotate_left(1);
        buckets_needed += 1;
    }

    return buckets_needed;
}

fn counts_arr_len_for_buckets(buckets: usize, sub_bucket_count: usize) -> usize {
    (buckets + 1) * (sub_bucket_count / 2)
}

fn counts_arr_len(value: u64, sub_bucket_count: usize, unit_magnitude: u32) -> usize {
    counts_arr_len_for_buckets(buckets_needed_for_value(value, sub_bucket_count, unit_magnitude), sub_bucket_count)
}<|endoftext|>