<commit_before>extern crate rand;

use rand::{Rand, Rng};
use rand::Closed01 as RandClosed01;

/// Encapsulates a floating point number in the range [0, 1] including both endpoints.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Closed01<F>(F);

impl Closed01<f32> {
    #[inline(always)]
    pub fn new(f: f32) -> Closed01<f32> {
        assert!(f >= 0.0 && f <= 1.0);
        Closed01(f)
    }

    #[inline(always)]
    pub fn zero() -> Closed01<f32> {
        Closed01(0.0)
    }

    #[inline(always)]
    pub fn middle() -> Closed01<f32> {
        Closed01(0.5)
    }

    #[inline(always)]
    pub fn one() -> Closed01<f32> {
        Closed01(1.0)
    }

    #[inline(always)]
    /// Returns the smaller of the two.
    pub fn min(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 <= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the greater of the two.
    pub fn max(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 >= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the distance between the two numbers.
    pub fn distance(self, other: Closed01<f32>) -> Closed01<f32> {
        let d = (self.0 - other.0).abs();
        debug_assert!(d >= 0.0 && d <= 1.0);
        Closed01(d)
    }

    #[inline(always)]
    pub fn get(self) -> f32 {
        debug_assert!(self.0 >= 0.0 && self.0 <= 1.0);
        self.0
    }

    #[inline(always)]
    /// The average of two values.
    pub fn avg(a: Closed01<f32>, b: Closed01<f32>) -> Closed01<f32> {
        let avg = (a.get() + b.get()) / 2.0;
        debug_assert!(avg >= 0.0 && avg <= 1.0);
        Closed01(avg)
    }

    #[inline(always)]
    /// Saturating add
    pub fn saturating_add(self, other: Closed01<f32>) -> Closed01<f32> {
        let mut sum = self.0 + other.0;
        if sum > 1.0 {
            sum = 1.0;
        }
        debug_assert!(sum >= 0.0 && sum <= 1.0);
        Closed01(sum)
    }

    #[inline(always)]
    /// Saturating sub
    pub fn saturating_sub(self, other: Closed01<f32>) -> Closed01<f32> {
        let mut sub = self.0 - other.0;
        if sub < 0.0 {
            sub = 0.0;
        }
        debug_assert!(sub >= 0.0 && sub <= 1.0);
        Closed01(sub)
    }

    #[inline(always)]
    /// Multiplies both numbers
    pub fn mul(self, scalar: Closed01<f32>) -> Closed01<f32> {
        let s = self.get() * scalar.get();
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }

    #[inline(always)]
    pub fn approx_eq(self, other: Closed01<f32>, eps: Closed01<f32>) -> bool {
        self.distance(other) < eps
    }

    #[inline(always)]
    /// This scales `self` towards 1.0
    pub fn scale_up(self, other: Closed01<f32>) -> Closed01<f32> {
        let s = self.0 + (1.0 - self.0) * other.0;
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }

    #[inline(always)]
    /// This scales `self` towards 0.0
    pub fn scale_down(self, other: Closed01<f32>) -> Closed01<f32> {
        let s = self.0 - self.0 * other.0;
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }

    #[inline(always)]
    /// Mirror the number at 0.5.
    pub fn mirror(self) -> Closed01<f32> {
        let s = 1.0 - self.0;
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }
}

impl Into<f32> for Closed01<f32> {
    fn into(self) -> f32 {
        self.get()
    }
}

impl Rand for Closed01<f32> {
    fn rand<R: Rng>(rng: &mut R) -> Closed01<f32> {
        Closed01::new(RandClosed01::<f32>::rand(rng).0)
    }
}

#[test]
fn test_into() {
    assert_eq!(0.5f32, Closed01::new(0.5).into());
}

#[test]
fn test_minmax() {
    let a = Closed01::new(0.4);
    let b = Closed01::new(0.5);
    assert_eq!(a, a.min(b));
    assert_eq!(a, b.min(a));
    assert_eq!(b, a.max(b));
    assert_eq!(b, b.max(a));
}

#[test]
fn test_saturation() {
    let a = Closed01::new(0.4);
    let b = Closed01::new(0.5);
    let c = Closed01::new(0.6);

    assert_eq!(0.9, a.saturating_add(b).get());
    assert_eq!(1.0, a.saturating_add(c).get());
    assert_eq!(1.0, b.saturating_add(c).get());

    assert_eq!(0.0, a.saturating_sub(b).get());
    assert_eq!(0.0, a.saturating_sub(c).get());
    assert_eq!(0.0, b.saturating_sub(c).get());

    let eps = Closed01::new(0.001);
    assert!(c.saturating_sub(b).approx_eq(Closed01::new(0.1), eps));
    assert!(c.saturating_sub(a).approx_eq(Closed01::new(0.2), eps));
}

#[test]
fn test_scale_up() {
    let a = Closed01::new(0.0);
    let b = Closed01::new(1.0);
    let c = Closed01::new(0.5);

    assert_eq!(b, a.scale_up(b));
    assert_eq!(c, a.scale_up(c));

    assert_eq!(c, c.scale_up(a));
    assert_eq!(b, c.scale_up(b));
}

#[test]
fn test_scale_down() {
    let a = Closed01::new(0.0);
    let b = Closed01::new(1.0);
    let c = Closed01::new(0.5);

    assert_eq!(a, c.scale_down(b));
    assert_eq!(b, b.scale_down(a));
    assert_eq!(c, b.scale_down(c));
}

#[test]
fn test_mirror() {
    let a = Closed01::new(0.0);
    let b = Closed01::new(1.0);
    let c = Closed01::new(0.5);

    assert_eq!(a, a.mirror().mirror());
    assert_eq!(b, b.mirror().mirror());
    assert_eq!(c, c.mirror().mirror());

    assert_eq!(b, a.mirror());
    assert_eq!(a, b.mirror());
    assert_eq!(c, c.mirror());
}
<commit_msg>Rename mirror -> invert<commit_after>extern crate rand;

use rand::{Rand, Rng};
use rand::Closed01 as RandClosed01;

/// Encapsulates a floating point number in the range [0, 1] including both endpoints.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
pub struct Closed01<F>(F);

impl Closed01<f32> {
    #[inline(always)]
    pub fn new(f: f32) -> Closed01<f32> {
        assert!(f >= 0.0 && f <= 1.0);
        Closed01(f)
    }

    #[inline(always)]
    pub fn zero() -> Closed01<f32> {
        Closed01(0.0)
    }

    #[inline(always)]
    pub fn middle() -> Closed01<f32> {
        Closed01(0.5)
    }

    #[inline(always)]
    pub fn one() -> Closed01<f32> {
        Closed01(1.0)
    }

    #[inline(always)]
    /// Returns the smaller of the two.
    pub fn min(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 <= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the greater of the two.
    pub fn max(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 >= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the distance between the two numbers.
    pub fn distance(self, other: Closed01<f32>) -> Closed01<f32> {
        let d = (self.0 - other.0).abs();
        debug_assert!(d >= 0.0 && d <= 1.0);
        Closed01(d)
    }

    #[inline(always)]
    pub fn get(self) -> f32 {
        debug_assert!(self.0 >= 0.0 && self.0 <= 1.0);
        self.0
    }

    #[inline(always)]
    /// The average of two values.
    pub fn avg(a: Closed01<f32>, b: Closed01<f32>) -> Closed01<f32> {
        let avg = (a.get() + b.get()) / 2.0;
        debug_assert!(avg >= 0.0 && avg <= 1.0);
        Closed01(avg)
    }

    #[inline(always)]
    /// Saturating add
    pub fn saturating_add(self, other: Closed01<f32>) -> Closed01<f32> {
        let mut sum = self.0 + other.0;
        if sum > 1.0 {
            sum = 1.0;
        }
        debug_assert!(sum >= 0.0 && sum <= 1.0);
        Closed01(sum)
    }

    #[inline(always)]
    /// Saturating sub
    pub fn saturating_sub(self, other: Closed01<f32>) -> Closed01<f32> {
        let mut sub = self.0 - other.0;
        if sub < 0.0 {
            sub = 0.0;
        }
        debug_assert!(sub >= 0.0 && sub <= 1.0);
        Closed01(sub)
    }

    #[inline(always)]
    /// Multiplies both numbers
    pub fn mul(self, scalar: Closed01<f32>) -> Closed01<f32> {
        let s = self.get() * scalar.get();
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }

    #[inline(always)]
    pub fn approx_eq(self, other: Closed01<f32>, eps: Closed01<f32>) -> bool {
        self.distance(other) < eps
    }

    #[inline(always)]
    /// This scales `self` towards 1.0
    pub fn scale_up(self, other: Closed01<f32>) -> Closed01<f32> {
        let s = self.0 + (1.0 - self.0) * other.0;
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }

    #[inline(always)]
    /// This scales `self` towards 0.0
    pub fn scale_down(self, other: Closed01<f32>) -> Closed01<f32> {
        let s = self.0 - self.0 * other.0;
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }

    #[inline(always)]
    /// Invert the number (Mirror at 0.5).
    pub fn invert(self) -> Closed01<f32> {
        let s = 1.0 - self.0;
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }
}

impl Into<f32> for Closed01<f32> {
    fn into(self) -> f32 {
        self.get()
    }
}

impl Rand for Closed01<f32> {
    fn rand<R: Rng>(rng: &mut R) -> Closed01<f32> {
        Closed01::new(RandClosed01::<f32>::rand(rng).0)
    }
}

#[test]
fn test_into() {
    assert_eq!(0.5f32, Closed01::new(0.5).into());
}

#[test]
fn test_minmax() {
    let a = Closed01::new(0.4);
    let b = Closed01::new(0.5);
    assert_eq!(a, a.min(b));
    assert_eq!(a, b.min(a));
    assert_eq!(b, a.max(b));
    assert_eq!(b, b.max(a));
}

#[test]
fn test_saturation() {
    let a = Closed01::new(0.4);
    let b = Closed01::new(0.5);
    let c = Closed01::new(0.6);

    assert_eq!(0.9, a.saturating_add(b).get());
    assert_eq!(1.0, a.saturating_add(c).get());
    assert_eq!(1.0, b.saturating_add(c).get());

    assert_eq!(0.0, a.saturating_sub(b).get());
    assert_eq!(0.0, a.saturating_sub(c).get());
    assert_eq!(0.0, b.saturating_sub(c).get());

    let eps = Closed01::new(0.001);
    assert!(c.saturating_sub(b).approx_eq(Closed01::new(0.1), eps));
    assert!(c.saturating_sub(a).approx_eq(Closed01::new(0.2), eps));
}

#[test]
fn test_scale_up() {
    let a = Closed01::new(0.0);
    let b = Closed01::new(1.0);
    let c = Closed01::new(0.5);

    assert_eq!(b, a.scale_up(b));
    assert_eq!(c, a.scale_up(c));

    assert_eq!(c, c.scale_up(a));
    assert_eq!(b, c.scale_up(b));
}

#[test]
fn test_scale_down() {
    let a = Closed01::new(0.0);
    let b = Closed01::new(1.0);
    let c = Closed01::new(0.5);

    assert_eq!(a, c.scale_down(b));
    assert_eq!(b, b.scale_down(a));
    assert_eq!(c, b.scale_down(c));
}

#[test]
fn test_invert() {
    let a = Closed01::new(0.0);
    let b = Closed01::new(1.0);
    let c = Closed01::new(0.5);

    assert_eq!(a, a.invert().invert());
    assert_eq!(b, b.invert().invert());
    assert_eq!(c, c.invert().invert());

    assert_eq!(b, a.invert());
    assert_eq!(a, b.invert());
    assert_eq!(c, c.invert());
}
<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::io::{Write,Read};
use std::path::{PathBuf};
use std::process::Command;
use std::env::{set_current_dir};
#[macro_use]
extern crate json;

struct Config {
    class_name: String,
    master_repo: String,
    description: String
}

struct Student {
    name: String,
    repo: String
}

pub fn new(name: &str) {
    match fs::create_dir(name) {
        Ok(_) => init(name),
        Err(e) => println!("Couldn't create: {}",e)
    }
}

pub fn init(path: &str) {
    println!("initalizing at {}", path);
    let config: Config = get_init_options();
    let config_json = object!{
        "className" => config.class_name.trim(),
        "description" => config.description.trim(),
        "masterRepo" => config.master_repo.trim(),
        "students" => json::JsonValue::new_object()
    };
    write_config_json(json::stringify_pretty(config_json,4), path);
    clone_repo(config.master_repo.trim(), config.class_name.trim());
}

pub fn add() {
    let mut config_json = get_config_json().unwrap();
    let student = get_student_details();
    let student_json = object!(
        "name" => student.name.trim(),
        "repo" => student.repo.trim()
    );
    config_json["students"][student.name.trim()] = student_json;
    write_config_json(json::stringify_pretty(config_json,4), "./");
    clone_repo(student.repo.trim(), student.name.trim());
}

pub fn remove(name: &str) {
    let mut config_json = get_config_json().unwrap();
    if config_json["students"].has_key(name) {
        println!("Removing {}", name);
        config_json["students"].remove(name);
        match fs::remove_dir_all(name) {
            Ok(_) => println!("Removed {}",name),
            Err(e) => panic!("Couldn't remove!: {}",e)
        };
        write_config_json(json::stringify_pretty(config_json,4), "./");
    } else {
        println!("Student {} not known.",name);
    }
}

pub fn update() {
    println!("Updating student repos");
    let config_json = get_config_json().unwrap();
    let students = config_json["students"].entries();
    for s in students {
        println!("Updating {}",s.0);
        update_repo(s.1.clone());
    }
}

pub fn log(name: &str) {
    println!("Logging {}", name);
    let config_json = get_config_json().unwrap();
    if config_json["students"].has_key(name) {
        set_current_dir(name);
        let output = Command::new("sh")
            .arg("-c")
            .arg("git log")
            .output()
            .expect("failed to execute process");
        println!("{}", String::from_utf8_lossy(&output.stdout));
    } else {
        println!("Student {} not known.", name);
    }
}

fn write_config_json(data: String, path: &str) {
    let mut init_path: PathBuf = PathBuf::from(path);
    init_path.push("gitclass.json");
    let mut f = fs::File::create(init_path.as_path()).unwrap();
    // let out_str = json::stringify_pretty(obj, 4);
    println!("{}",data);
    match f.write(data.as_bytes()) {
        Ok(_) => println!("Config updated"),
        Err(e) => panic!("Config not updated: {}",e)
    };
}

fn get_config_json() -> Result<json::JsonValue, json::Error> {
    let mut f = fs::File::open("gitclass.json").unwrap();
    let mut config_str = String::new();
    match f.read_to_string(&mut config_str) {
        Ok(_) => println!("Config loaded"),
        Err(e) => panic!("Couldn't load config: {}",e)
    };
    json::parse(config_str.as_str())
}

fn get_init_options() -> Config {
    let mut class_name = String::new();
    println!("Enter your class's name: ");
    match io::stdin().read_line(&mut class_name) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read name: {}",e)
    };
    let mut master_repo = String::new();
    println!("Enter the master repo: ");
    match io::stdin().read_line(&mut master_repo) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read repo: {}",e)
    };
    println!("Enter the description: ");
    let mut description = String::new();
    match io::stdin().read_line(&mut description) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read description: {}",e)
    };
    return Config {
        class_name: class_name,
        master_repo: master_repo,
        description: description
    }
}

fn get_student_details() -> Student {
    let mut name = String::new();
    println!("Student name: ");
    match io::stdin().read_line(&mut name) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read name: {}",e)
    };
    let mut repo = String::new();
    println!("Student repo: ");
    match io::stdin().read_line(&mut repo) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read repo: {}",e)
    };
    return Student {
        name: name,
        repo: repo
    }
}

fn clone_repo(url: &str, path: &str) {
    let git_line = format!("git clone {} {}", url, path);
    println!("{}",git_line);
    let output = Command::new("sh")
        .arg("-c")
        .arg(git_line)
        .output()
        .expect("failed to execute process");
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

fn update_repo(student: json::JsonValue) {
    let path = student["name"].as_str().unwrap();
    set_current_dir(path);
    let output = Command::new("sh")
        .arg("-c")
        .arg("git pull")
        .output()
        .expect("failed to execute process");
    println!("{}", String::from_utf8_lossy(&output.stdout));
    set_current_dir("../");
}
<commit_msg>Use git2 for pull<commit_after>use std::fs;
use std::io;
use std::io::{Write,Read};
use std::path::{PathBuf};
use std::process::Command;
use std::env::{set_current_dir};
#[macro_use]
extern crate json;
extern crate git2;

struct Config {
    class_name: String,
    master_repo: String,
    description: String
}

struct Student {
    name: String,
    repo: String
}

pub fn new(name: &str) {
    match fs::create_dir(name) {
        Ok(_) => init(name),
        Err(e) => println!("Couldn't create: {}",e)
    }
}

pub fn init(path: &str) {
    println!("initalizing at {}", path);
    let config: Config = get_init_options();
    let config_json = object!{
        "className" => config.class_name.trim(),
        "description" => config.description.trim(),
        "masterRepo" => config.master_repo.trim(),
        "students" => json::JsonValue::new_object()
    };
    write_config_json(json::stringify_pretty(config_json,4), path);
    clone_repo(config.master_repo.trim(), config.class_name.trim());
}

pub fn add() {
    let mut config_json = get_config_json().unwrap();
    let student = get_student_details();
    let student_json = object!(
        "name" => student.name.trim(),
        "repo" => student.repo.trim()
    );
    config_json["students"][student.name.trim()] = student_json;
    write_config_json(json::stringify_pretty(config_json,4), "./");
    clone_repo(student.repo.trim(), student.name.trim());
}

pub fn remove(name: &str) {
    let mut config_json = get_config_json().unwrap();
    if config_json["students"].has_key(name) {
        println!("Removing {}", name);
        config_json["students"].remove(name);
        match fs::remove_dir_all(name) {
            Ok(_) => println!("Removed {}",name),
            Err(e) => panic!("Couldn't remove!: {}",e)
        };
        write_config_json(json::stringify_pretty(config_json,4), "./");
    } else {
        println!("Student {} not known.",name);
    }
}

pub fn update() {
    println!("Updating student repos");
    let config_json = get_config_json().unwrap();
    let students = config_json["students"].entries();
    for s in students {
        println!("Updating {}",s.0);
        update_repo(s.1.clone());
    }
}

pub fn log(name: &str) {
    println!("Logging {}", name);
    let config_json = get_config_json().unwrap();
    if config_json["students"].has_key(name) {
        set_current_dir(name);
        let output = Command::new("sh")
            .arg("-c")
            .arg("git log")
            .output()
            .expect("failed to execute process");
        println!("{}", String::from_utf8_lossy(&output.stdout));
    } else {
        println!("Student {} not known.", name);
    }
}

fn write_config_json(data: String, path: &str) {
    let mut init_path: PathBuf = PathBuf::from(path);
    init_path.push("gitclass.json");
    let mut f = fs::File::create(init_path.as_path()).unwrap();
    // let out_str = json::stringify_pretty(obj, 4);
    println!("{}",data);
    match f.write(data.as_bytes()) {
        Ok(_) => println!("Config updated"),
        Err(e) => panic!("Config not updated: {}",e)
    };
}

fn get_config_json() -> Result<json::JsonValue, json::Error> {
    let mut f = fs::File::open("gitclass.json").unwrap();
    let mut config_str = String::new();
    match f.read_to_string(&mut config_str) {
        Ok(_) => println!("Config loaded"),
        Err(e) => panic!("Couldn't load config: {}",e)
    };
    json::parse(config_str.as_str())
}

fn get_init_options() -> Config {
    let mut class_name = String::new();
    println!("Enter your class's name: ");
    match io::stdin().read_line(&mut class_name) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read name: {}",e)
    };
    let mut master_repo = String::new();
    println!("Enter the master repo: ");
    match io::stdin().read_line(&mut master_repo) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read repo: {}",e)
    };
    println!("Enter the description: ");
    let mut description = String::new();
    match io::stdin().read_line(&mut description) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read description: {}",e)
    };
    return Config {
        class_name: class_name,
        master_repo: master_repo,
        description: description
    }
}

fn get_student_details() -> Student {
    let mut name = String::new();
    println!("Student name: ");
    match io::stdin().read_line(&mut name) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read name: {}",e)
    };
    let mut repo = String::new();
    println!("Student repo: ");
    match io::stdin().read_line(&mut repo) {
        Ok(_) => {},
        Err(e) => panic!("Couldn't read repo: {}",e)
    };
    return Student {
        name: name,
        repo: repo
    }
}

fn clone_repo(url: &str, path: &str) {
    let git_line = format!("git clone {} {}", url, path);
    println!("{}",git_line);
    let output = Command::new("sh")
        .arg("-c")
        .arg(git_line)
        .output()
        .expect("failed to execute process");
    println!("{}", String::from_utf8_lossy(&output.stdout));
}

fn update_repo(student: json::JsonValue) {
    let path = student["name"].as_str().unwrap();
    let repo = match git2::Repository::open(path) {
        Ok(repo) => repo,
        Err(e) => panic!("Couldn't open: {}", e),
    };
    let master = repo.find_branch("master",git2::BranchType::Local).unwrap();
    let upstream = match master.upstream() {
        Ok(branch) => branch,
        Err(e) => panic!("Couldn't open 'master' branch")
    };
    let upstream_name = upstream.name().unwrap().unwrap();
    println!("Upstream is {}", upstream_name);
    let mut ref_name = String::from("refs/remotes/");
    ref_name.push_str(upstream_name);
    let remotes = repo.remotes().unwrap();
    for r in remotes.into_iter() {
        let remote_name = r.unwrap();
        println!("Updating remote {}", remote_name);
        let mut remote = repo.find_remote(remote_name).unwrap();
        remote.fetch(&["master"], None, None).unwrap();
    }
    let latest_obj = repo.revparse_single("HEAD").ok();
    let remote_obj = match repo.revparse_single(ref_name.as_str()) {
        Ok(obj) => obj,
        Err(e) => panic!("Couldn't find the remote ref")
    };

    repo.reset(&remote_obj,
    git2::ResetType::Hard,
    Some(git2::build::CheckoutBuilder::new()
        .force()
        .remove_untracked(true)
    ));
}<|endoftext|>


<commit_before>#![doc(html_root_url = "https://docs.rs/rayon/1.2")]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(unreachable_pub)]

//! Data-parallelism library that makes it easy to convert sequential
//! computations into parallel
//!
//! Rayon is lightweight and convenient for introducing parallelism into existing
//! code. It guarantees data-race free executions and takes advantage of
//! parallelism when sensible, based on work-load at runtime.
//!
//! # How to use Rayon
//!
//! There are two ways to use Rayon:
//!
//! - **High-level parallel constructs** are the simplest way to use Rayon and also
//!   typically the most efficient.
//!   - [Parallel iterators][iter module] make it easy to convert a sequential iterator to
//!     execute in parallel.
//!   - The [`par_sort`] method sorts `&mut [T]` slices (or vectors) in parallel.
//!   - [`par_extend`] can be used to efficiently grow collections with items produced
//!     by a parallel iterator.
//! - **Custom tasks** let you divide your work into parallel tasks yourself.
//!   - [`join`] is used to subdivide a task into two pieces.
//!   - [`scope`] creates a scope within which you can create any number of parallel tasks.
//!   - [`ThreadPoolBuilder`] can be used to create your own thread pools or customize
//!     the global one.
//!
//! [iter module]: iter/index.html
//! [`join`]: fn.join.html
//! [`scope`]: fn.scope.html
//! [`par_sort`]: slice/trait.ParallelSliceMut.html#method.par_sort
//! [`par_extend`]: iter/trait.ParallelExtend.html#tymethod.par_extend
//! [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html
//!
//! # Basic usage and the Rayon prelude
//!
//! First, you will need to add `rayon` to your `Cargo.toml` and put
//! `extern crate rayon` in your main file (`lib.rs`, `main.rs`).
//!
//! Next, to use parallel iterators or the other high-level methods,
//! you need to import several traits. Those traits are bundled into
//! the module [`rayon::prelude`]. It is recommended that you import
//! all of these traits at once by adding `use rayon::prelude::*` at
//! the top of each module that uses Rayon methods.
//!
//! These traits give you access to the `par_iter` method which provides
//! parallel implementations of many iterative functions such as [`map`],
//! [`for_each`], [`filter`], [`fold`], and [more].
//!
//! [`rayon::prelude`]: prelude/index.html
//! [`map`]: iter/trait.ParallelIterator.html#method.map
//! [`for_each`]: iter/trait.ParallelIterator.html#method.for_each
//! [`filter`]: iter/trait.ParallelIterator.html#method.filter
//! [`fold`]: iter/trait.ParallelIterator.html#method.fold
//! [more]: iter/trait.ParallelIterator.html#provided-methods
//!
//! # Crate Layout
//!
//! Rayon extends many of the types found in the standard library with
//! parallel iterator implementations. The modules in the `rayon`
//! crate mirror [`std`] itself: so, e.g., the `option` module in
//! Rayon contains parallel iterators for the `Option` type, which is
//! found in [the `option` module of `std`]. Similarly, the
//! `collections` module in Rayon offers parallel iterator types for
//! [the `collections` from `std`]. You will rarely need to access
//! these submodules unless you need to name iterator types
//! explicitly.
//!
//! [the `option` module of `std`]: https://doc.rust-lang.org/std/option/index.html
//! [the `collections` from `std`]: https://doc.rust-lang.org/std/collections/index.html
//! [`std`]: https://doc.rust-lang.org/std/
//!
//! # Other questions?
//!
//! See [the Rayon FAQ][faq].
//!
//! [faq]: https://github.com/rayon-rs/rayon/blob/master/FAQ.md

extern crate crossbeam_deque;
extern crate either;
extern crate rayon_core;

#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate rand_xorshift;
#[cfg(test)]
#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

#[macro_use]
mod delegate;

#[macro_use]
mod private;

mod split_producer;

pub mod collections;
pub mod iter;
pub mod option;
pub mod prelude;
pub mod range;
pub mod range_inclusive;
pub mod result;
pub mod slice;
pub mod str;
pub mod vec;

mod math;
mod par_either;

mod compile_fail;

pub use rayon_core::FnContext;
pub use rayon_core::ThreadBuilder;
pub use rayon_core::ThreadPool;
pub use rayon_core::ThreadPoolBuildError;
pub use rayon_core::ThreadPoolBuilder;
pub use rayon_core::{current_num_threads, current_thread_index};
pub use rayon_core::{join, join_context};
pub use rayon_core::{scope, Scope};
pub use rayon_core::{scope_fifo, ScopeFifo};
pub use rayon_core::{spawn, spawn_fifo};
<commit_msg>Link the iterator traits from top of the docs<commit_after>#![doc(html_root_url = "https://docs.rs/rayon/1.2")]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(unreachable_pub)]

//! Data-parallelism library that makes it easy to convert sequential
//! computations into parallel
//!
//! Rayon is lightweight and convenient for introducing parallelism into existing
//! code. It guarantees data-race free executions and takes advantage of
//! parallelism when sensible, based on work-load at runtime.
//!
//! # How to use Rayon
//!
//! There are two ways to use Rayon:
//!
//! - **High-level parallel constructs** are the simplest way to use Rayon and also
//!   typically the most efficient.
//!   - [Parallel iterators][iter module] make it easy to convert a sequential iterator to
//!     execute in parallel.
//!     - The [`ParallelIterator`] trait defines general methods for for all parallel iterators.
//!     - The [`IndexedParallelIterator`] trait adds methods for iterators that support random
//!       access.
//!   - The [`par_sort`] method sorts `&mut [T]` slices (or vectors) in parallel.
//!   - [`par_extend`] can be used to efficiently grow collections with items produced
//!     by a parallel iterator.
//! - **Custom tasks** let you divide your work into parallel tasks yourself.
//!   - [`join`] is used to subdivide a task into two pieces.
//!   - [`scope`] creates a scope within which you can create any number of parallel tasks.
//!   - [`ThreadPoolBuilder`] can be used to create your own thread pools or customize
//!     the global one.
//!
//! [iter module]: iter/index.html
//! [`join`]: fn.join.html
//! [`scope`]: fn.scope.html
//! [`par_sort`]: slice/trait.ParallelSliceMut.html#method.par_sort
//! [`par_extend`]: iter/trait.ParallelExtend.html#tymethod.par_extend
//! [`ThreadPoolBuilder`]: struct.ThreadPoolBuilder.html
//!
//! # Basic usage and the Rayon prelude
//!
//! First, you will need to add `rayon` to your `Cargo.toml` and put
//! `extern crate rayon` in your main file (`lib.rs`, `main.rs`).
//!
//! Next, to use parallel iterators or the other high-level methods,
//! you need to import several traits. Those traits are bundled into
//! the module [`rayon::prelude`]. It is recommended that you import
//! all of these traits at once by adding `use rayon::prelude::*` at
//! the top of each module that uses Rayon methods.
//!
//! These traits give you access to the `par_iter` method which provides
//! parallel implementations of many iterative functions such as [`map`],
//! [`for_each`], [`filter`], [`fold`], and [more].
//!
//! [`rayon::prelude`]: prelude/index.html
//! [`map`]: iter/trait.ParallelIterator.html#method.map
//! [`for_each`]: iter/trait.ParallelIterator.html#method.for_each
//! [`filter`]: iter/trait.ParallelIterator.html#method.filter
//! [`fold`]: iter/trait.ParallelIterator.html#method.fold
//! [more]: iter/trait.ParallelIterator.html#provided-methods
//! [`ParallelIterator`]: iter/trait.ParallelIterator.html
//! [`IndexedParallelIterator`]: iter/trait.IndexedParallelIterator.html
//!
//! # Crate Layout
//!
//! Rayon extends many of the types found in the standard library with
//! parallel iterator implementations. The modules in the `rayon`
//! crate mirror [`std`] itself: so, e.g., the `option` module in
//! Rayon contains parallel iterators for the `Option` type, which is
//! found in [the `option` module of `std`]. Similarly, the
//! `collections` module in Rayon offers parallel iterator types for
//! [the `collections` from `std`]. You will rarely need to access
//! these submodules unless you need to name iterator types
//! explicitly.
//!
//! [the `option` module of `std`]: https://doc.rust-lang.org/std/option/index.html
//! [the `collections` from `std`]: https://doc.rust-lang.org/std/collections/index.html
//! [`std`]: https://doc.rust-lang.org/std/
//!
//! # Other questions?
//!
//! See [the Rayon FAQ][faq].
//!
//! [faq]: https://github.com/rayon-rs/rayon/blob/master/FAQ.md

extern crate crossbeam_deque;
extern crate either;
extern crate rayon_core;

#[cfg(test)]
extern crate rand;
#[cfg(test)]
extern crate rand_xorshift;
#[cfg(test)]
#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

#[macro_use]
mod delegate;

#[macro_use]
mod private;

mod split_producer;

pub mod collections;
pub mod iter;
pub mod option;
pub mod prelude;
pub mod range;
pub mod range_inclusive;
pub mod result;
pub mod slice;
pub mod str;
pub mod vec;

mod math;
mod par_either;

mod compile_fail;

pub use rayon_core::FnContext;
pub use rayon_core::ThreadBuilder;
pub use rayon_core::ThreadPool;
pub use rayon_core::ThreadPoolBuildError;
pub use rayon_core::ThreadPoolBuilder;
pub use rayon_core::{current_num_threads, current_thread_index};
pub use rayon_core::{join, join_context};
pub use rayon_core::{scope, Scope};
pub use rayon_core::{scope_fifo, ScopeFifo};
pub use rayon_core::{spawn, spawn_fifo};
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate crc;
extern crate inflate;
#[macro_use]
extern crate slog;

use byteorder::ReadBytesExt;

use crc::crc32::Hasher32;

use std::io::{
    BufRead,
    Error,
    ErrorKind,
    Read,
    Seek,
    SeekFrom,
    Write
};

use std::str;

const GZIP_IDENTIFIER: [u8; 2] = [0x1f, 0x8b];
const BGZF_IDENTIFIER: [u8; 2] = [0x42, 0x43];

const DEFLATE: u8 = 8;

const FEXTRA: u8 = 1 << 2;

pub fn version() -> &'static str {
    return option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
}

pub trait Rescuable: BufRead + Seek {}
impl<T: BufRead + Seek> Rescuable for T {}

struct BGZFBlock {
    id: u64,
    header_bytes: Vec<u8>,
    deflated_payload_bytes: Vec<u8>,
    inflated_payload_crc32: u32,
    inflated_payload_size: u32,
}

pub struct Results {
    pub blocks_count: u64,
    pub blocks_size: u64,
    pub bad_blocks_count: u64,
    pub bad_blocks_size: u64,
    pub truncated_in_block: bool,
    pub truncated_between_blocks: bool,
}

fn check_payload(block: &Option<BGZFBlock>) -> Result<(), Error> {
    match *block {
        None => Ok(()),
        Some(ref block) => {
            let inflated_payload_bytes = match inflate::inflate_bytes(&block.deflated_payload_bytes) {
                Ok(inflated_payload_bytes) => inflated_payload_bytes,
                Err(error) => return Err(Error::new(ErrorKind::InvalidData, format!("Invalid bam file: unable to inflate payload of block {}: {}", block.id, error))),
            };

            let mut inflated_payload_digest = crc::crc32::Digest::new(crc::crc32::IEEE);
            inflated_payload_digest.write(&inflated_payload_bytes);
            let inflated_payload_crc32 = inflated_payload_digest.sum32();
            if inflated_payload_crc32 != block.inflated_payload_crc32 {
                return Err(Error::new(ErrorKind::InvalidData, format!("Invalid bam file: incorrect payload CRC32 of block {}", block.id)));
            }

            let inflated_payload_size = inflated_payload_bytes.len() as u32;
            if inflated_payload_size != block.inflated_payload_size {
                // TODO recoverable (wrong size is not a big issue if the CRC32 is correct)
                return Err(Error::new(ErrorKind::InvalidData, format!("Invalid bam file: incorrect payload size of block {}", block.id)));
            }

            Ok(())
        }
    }
}

macro_rules! fail {
    ($fail_fast: expr, $results: expr, $truncated_in_block: expr) => {
        $results.bad_blocks_count += 1;
        if $truncated_in_block {
            $results.truncated_in_block = true;
        }
        if $fail_fast {
            return $results;
        }
    }
}

pub fn check(reader: &mut Rescuable, fail_fast: bool, logger: &slog::Logger) -> Results {
    info!(logger, "Checking integrity…");

    let mut results = Results {
        blocks_count: 0u64,
        blocks_size: 0u64,
        bad_blocks_count: 0u64,
        bad_blocks_size: 0u64,
        truncated_in_block: false,
        truncated_between_blocks: false,
    };

    let mut previous_block: Option<BGZFBlock> = None;
    loop {
        let mut header_bytes = vec![];
        {
            let mut header_reader = reader.take(16);
            match header_reader.read_to_end(&mut header_bytes) {
                Ok(header_size) => {
                    if header_size == 0 {
                        break;
                    }

                    if header_size < 16 {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        if header_bytes[0..2] != GZIP_IDENTIFIER {
            // TODO recoverable if only a bitflip or two
            fail!(fail_fast, results, false);
            // TODO seek right position, see below
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        if header_bytes[2] != DEFLATE {
            fail!(fail_fast, results, false);
            // TODO seek right position, see below
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        if header_bytes[3] != FEXTRA {
            fail!(fail_fast, results, false);
            // TODO seek right position, see below
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        // header_bytes[4..8] => modification time; can be anything
        // header_bytes[8] => extra flags; can be anything
        // header_bytes[9] => operating system; can be anything

        let mut bgzf_block_size = 0u16;

        let mut extra_field_size = 6u16;
        if header_bytes[10..16] != [
            0x06, 0x00, // extra field length (6 bytes)
            0x42, 0x43, // bgzf identifier
            0x02, 0x00  // extra subfield length (2 bytes)
        ] {
            // TODO recoverable if only a bitflip or two

            extra_field_size = match (&mut &header_bytes[10..12]).read_u16::<byteorder::LittleEndian>() {
                Ok(extra_field_size) => extra_field_size,
                Err(_) => {
                    fail!(fail_fast, results, false);
                    break;
                }
            };

            if header_bytes[12..16] == [
                0x42, 0x43, // bgzf identifier
                0x02, 0x00  // extra subfield length (2 bytes)
            ] {
                bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                    Ok(bgzf_block_size) => bgzf_block_size + 1,
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                };
                match reader.seek(SeekFrom::Current((extra_field_size - 6u16) as i64)) {
                    Ok(_) => (),
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                }
                // TODO the bgzf extra subfield is the first, but check the other subfields nonetheless
            } else {
                let first_extra_subfield_size = match (&mut &header_bytes[14..16]).read_u16::<byteorder::LittleEndian>() {
                    Ok(first_extra_subfield_size) => first_extra_subfield_size,
                    Err(_) => {
                        fail!(fail_fast, results, false);
                        break;
                    }
                };

                if first_extra_subfield_size > extra_field_size {
                    fail!(fail_fast, results, false);
                    break;
                }

                match reader.seek(SeekFrom::Current(first_extra_subfield_size as i64)) {
                    Ok(_) => (),
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                }

                let mut remaining_extra_field_size = extra_field_size - first_extra_subfield_size;
                while remaining_extra_field_size > 4 {
                    let mut extra_subfield_identifier = [0u8; 2];
                    match reader.read_exact(&mut extra_subfield_identifier) {
                        Ok(_) => (),
                        Err(_) => {
                            fail!(fail_fast, results, true);
                            break;
                        }
                    }

                    let extra_subfield_size = match reader.read_u16::<byteorder::LittleEndian>() {
                        Ok(extra_subfield_size) => extra_subfield_size,
                        Err(_) => {
                            fail!(fail_fast, results, true);
                            break;
                        }
                    };

                    if extra_subfield_identifier == BGZF_IDENTIFIER {
                        if extra_subfield_size != 2 {
                            fail!(fail_fast, results, false);
                            break;
                        }
                        bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                            Ok(bgzf_block_size) => bgzf_block_size + 1,
                            Err(_) => {
                                fail!(fail_fast, results, true);
                                break;
                            }
                        };
                    } else {
                        match reader.seek(SeekFrom::Current(extra_subfield_size as i64)) {
                            Ok(_) => (),
                            Err(_) => {
                                fail!(fail_fast, results, true);
                                break;
                            }
                        }
                    }

                    remaining_extra_field_size -= 4 + extra_subfield_size;
                }

                if bgzf_block_size == 0u16 {
                    fail!(fail_fast, results, false);
                    break;
                }
            }
        }

        // TODO if not at the right position for the next header, fix the previous header / payload or
        // the current header, seek to the right position and “continue”

        match check_payload(&previous_block) {
            Ok(_) => (),
            Err(_) => {
                results.bad_blocks_size += match previous_block {
                    None => 0,
                    Some(ref previous_block) => previous_block.inflated_payload_size as u64,
                };
                fail!(fail_fast, results, false);
            }
        }

        if bgzf_block_size == 0 {
            bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                Ok(bgzf_block_size) => bgzf_block_size + 1,
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            };
        }

        let mut deflated_payload_bytes = vec![];
        {
            let deflated_payload_size = bgzf_block_size - 20u16 - extra_field_size;
            let mut deflated_payload_reader = reader.take(deflated_payload_size as u64);
            match deflated_payload_reader.read_to_end(&mut deflated_payload_bytes) {
                Ok(deflated_payload_read_size) => {
                    if deflated_payload_read_size < deflated_payload_size as usize {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        let inflated_payload_crc32 = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_crc32) => inflated_payload_crc32,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };
        let inflated_payload_size = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_size) => inflated_payload_size,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };

        previous_block = Some(BGZFBlock {
            id: results.blocks_count,
            header_bytes: header_bytes,
            deflated_payload_bytes: deflated_payload_bytes,
            inflated_payload_crc32: inflated_payload_crc32,
            inflated_payload_size: inflated_payload_size,
        });

        results.blocks_count += 1;
        results.blocks_size += inflated_payload_size as u64;
    }

    match check_payload(&previous_block) {
        Ok(_) => (),
        Err(_) => {
            results.bad_blocks_size += match previous_block {
                None => 0,
                Some(ref previous_block) => previous_block.inflated_payload_size as u64,
            };
            fail!(fail_fast, results, false);
        }
    }

    match previous_block {
        None => (),
        Some(ref last_block) => {
            if last_block.inflated_payload_size != 0 {
                results.truncated_between_blocks = true;
                if fail_fast {
                    return results;
                }
            }
        }
    }

    results
}

pub fn rescue(reader: &mut Rescuable, writer: &mut Write, logger: &slog::Logger) -> Result<(), Error> {
    info!(logger, "Rescuing file…");

    error!(logger, "bamrescue::rescue() is not yet implemented");
    unimplemented!();
}
<commit_msg>Remove special case for single extra subfield gzip files<commit_after>extern crate byteorder;
extern crate crc;
extern crate inflate;
#[macro_use]
extern crate slog;

use byteorder::ReadBytesExt;

use crc::crc32::Hasher32;

use std::io::{
    BufRead,
    Error,
    ErrorKind,
    Read,
    Seek,
    SeekFrom,
    Write
};

use std::str;

const GZIP_IDENTIFIER: [u8; 2] = [0x1f, 0x8b];
const BGZF_IDENTIFIER: [u8; 2] = [0x42, 0x43];

const DEFLATE: u8 = 8;

const FEXTRA: u8 = 1 << 2;

pub fn version() -> &'static str {
    return option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
}

pub trait Rescuable: BufRead + Seek {}
impl<T: BufRead + Seek> Rescuable for T {}

struct BGZFBlock {
    id: u64,
    header_bytes: Vec<u8>,
    deflated_payload_bytes: Vec<u8>,
    inflated_payload_crc32: u32,
    inflated_payload_size: u32,
}

pub struct Results {
    pub blocks_count: u64,
    pub blocks_size: u64,
    pub bad_blocks_count: u64,
    pub bad_blocks_size: u64,
    pub truncated_in_block: bool,
    pub truncated_between_blocks: bool,
}

fn check_payload(block: &Option<BGZFBlock>) -> Result<(), Error> {
    match *block {
        None => Ok(()),
        Some(ref block) => {
            let inflated_payload_bytes = match inflate::inflate_bytes(&block.deflated_payload_bytes) {
                Ok(inflated_payload_bytes) => inflated_payload_bytes,
                Err(error) => return Err(Error::new(ErrorKind::InvalidData, format!("Invalid bam file: unable to inflate payload of block {}: {}", block.id, error))),
            };

            let mut inflated_payload_digest = crc::crc32::Digest::new(crc::crc32::IEEE);
            inflated_payload_digest.write(&inflated_payload_bytes);
            let inflated_payload_crc32 = inflated_payload_digest.sum32();
            if inflated_payload_crc32 != block.inflated_payload_crc32 {
                return Err(Error::new(ErrorKind::InvalidData, format!("Invalid bam file: incorrect payload CRC32 of block {}", block.id)));
            }

            let inflated_payload_size = inflated_payload_bytes.len() as u32;
            if inflated_payload_size != block.inflated_payload_size {
                // TODO recoverable (wrong size is not a big issue if the CRC32 is correct)
                return Err(Error::new(ErrorKind::InvalidData, format!("Invalid bam file: incorrect payload size of block {}", block.id)));
            }

            Ok(())
        }
    }
}

macro_rules! fail {
    ($fail_fast: expr, $results: expr, $truncated_in_block: expr) => {
        $results.bad_blocks_count += 1;
        if $truncated_in_block {
            $results.truncated_in_block = true;
        }
        if $fail_fast {
            return $results;
        }
    }
}

pub fn check(reader: &mut Rescuable, fail_fast: bool, logger: &slog::Logger) -> Results {
    info!(logger, "Checking integrity…");

    let mut results = Results {
        blocks_count: 0u64,
        blocks_size: 0u64,
        bad_blocks_count: 0u64,
        bad_blocks_size: 0u64,
        truncated_in_block: false,
        truncated_between_blocks: false,
    };

    let mut previous_block: Option<BGZFBlock> = None;
    loop {
        let mut header_bytes = vec![];
        {
            let mut header_reader = reader.take(12);
            match header_reader.read_to_end(&mut header_bytes) {
                Ok(header_size) => {
                    if header_size == 0 {
                        break;
                    }

                    if header_size < 12 {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        if header_bytes[0..2] != GZIP_IDENTIFIER {
            // TODO recoverable if only a bitflip or two
            fail!(fail_fast, results, false);
            // TODO seek right position, see below
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        if header_bytes[2] != DEFLATE {
            fail!(fail_fast, results, false);
            // TODO seek right position, see below
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        if header_bytes[3] != FEXTRA {
            fail!(fail_fast, results, false);
            // TODO seek right position, see below
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        // header_bytes[4..8] => modification time; can be anything
        // header_bytes[8] => extra flags; can be anything
        // header_bytes[9] => operating system; can be anything

        let extra_field_size = match (&mut &header_bytes[10..12]).read_u16::<byteorder::LittleEndian>() {
            Ok(extra_field_size) => extra_field_size,
            Err(_) => {
                fail!(fail_fast, results, false);
                break;
            }
        };

        // TODO add the next extra_field_size bytes to header_bytes for rescue

        let mut bgzf_block_size = 0u16;

        let mut remaining_extra_field_size = extra_field_size;
        while remaining_extra_field_size > 4 {
            let mut extra_subfield_identifier = [0u8; 2];
            match reader.read_exact(&mut extra_subfield_identifier) {
                Ok(_) => (),
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }

            let extra_subfield_size = match reader.read_u16::<byteorder::LittleEndian>() {
                Ok(extra_subfield_size) => extra_subfield_size,
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            };

            if extra_subfield_identifier == BGZF_IDENTIFIER {
                if extra_subfield_size != 2 {
                    fail!(fail_fast, results, false);
                    break;
                }
                bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                    Ok(bgzf_block_size) => bgzf_block_size + 1,
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                };
            } else {
                match reader.seek(SeekFrom::Current(extra_subfield_size as i64)) {
                    Ok(_) => (),
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                }
            }

            remaining_extra_field_size -= 4 + extra_subfield_size;
        }

        if bgzf_block_size == 0u16 {
            fail!(fail_fast, results, false);
            break;
        }

        // TODO if not at the right position for the next header, fix the previous header / payload or
        // the current header, seek to the right position and “continue”

        match check_payload(&previous_block) {
            Ok(_) => (),
            Err(_) => {
                results.bad_blocks_size += match previous_block {
                    None => 0,
                    Some(ref previous_block) => previous_block.inflated_payload_size as u64,
                };
                fail!(fail_fast, results, false);
            }
        }

        if bgzf_block_size == 0 {
            bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                Ok(bgzf_block_size) => bgzf_block_size + 1,
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            };
        }

        let mut deflated_payload_bytes = vec![];
        {
            let deflated_payload_size = bgzf_block_size - 20u16 - extra_field_size;
            let mut deflated_payload_reader = reader.take(deflated_payload_size as u64);
            match deflated_payload_reader.read_to_end(&mut deflated_payload_bytes) {
                Ok(deflated_payload_read_size) => {
                    if deflated_payload_read_size < deflated_payload_size as usize {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        let inflated_payload_crc32 = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_crc32) => inflated_payload_crc32,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };
        let inflated_payload_size = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_size) => inflated_payload_size,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };

        previous_block = Some(BGZFBlock {
            id: results.blocks_count,
            header_bytes: header_bytes,
            deflated_payload_bytes: deflated_payload_bytes,
            inflated_payload_crc32: inflated_payload_crc32,
            inflated_payload_size: inflated_payload_size,
        });

        results.blocks_count += 1;
        results.blocks_size += inflated_payload_size as u64;
    }

    match check_payload(&previous_block) {
        Ok(_) => (),
        Err(_) => {
            results.bad_blocks_size += match previous_block {
                None => 0,
                Some(ref previous_block) => previous_block.inflated_payload_size as u64,
            };
            fail!(fail_fast, results, false);
        }
    }

    match previous_block {
        None => (),
        Some(ref last_block) => {
            if last_block.inflated_payload_size != 0 {
                results.truncated_between_blocks = true;
                if fail_fast {
                    return results;
                }
            }
        }
    }

    results
}

pub fn rescue(reader: &mut Rescuable, writer: &mut Write, logger: &slog::Logger) -> Result<(), Error> {
    info!(logger, "Rescuing file…");

    error!(logger, "bamrescue::rescue() is not yet implemented");
    unimplemented!();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate exonum;
#[macro_use]
extern crate log;

extern crate serde;
extern crate serde_json;

use std::fmt;

use serde::{Serialize, Serializer};

use exonum::blockchain::{Service, Transaction, Schema, NodeState};
use exonum::node::State;
use exonum::crypto::{PublicKey, Hash, HASH_SIZE};
use exonum::messages::{RawMessage, Message, FromRaw, RawTransaction, Error as MessageError};
use exonum::storage::{StorageValue, Map, View, MapTable, MerklePatriciaTable,
                      Result as StorageResult};
use exonum::blockchain::StoredConfiguration;

pub const CONFIG_SERVICE: u16 = 1;
pub const CONFIG_PROPOSE_MESSAGE_ID: u16 = 0;
pub const CONFIG_VOTE_MESSAGE_ID: u16 = 1;

message! {
    TxConfigPropose {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_PROPOSE_MESSAGE_ID;
        const SIZE = 72;

        from:           &PublicKey  [00 => 32]
        prev_cfg_hash:  &Hash       [32 => 64]
        cfg:            &[u8]       [64 => 72] // serialized config bytes
    }
}

message! {
    TxConfigVote {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_VOTE_MESSAGE_ID;
        const SIZE = 64;

        from:           &PublicKey  [00 => 32]
        cfg_hash:       &Hash       [32 => 64] // hash of config we're voting for
    }
}

#[derive(Clone, PartialEq)]
pub enum ConfigTx {
    ConfigPropose(TxConfigPropose),
    ConfigVote(TxConfigVote),
}

impl Serialize for TxConfigPropose {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("config_propose", 4)?;
        ser.serialize_struct_elt(&mut state, "from", self.from())?;
        ser.serialize_struct_elt(&mut state, "previous_config_hash", self.prev_cfg_hash())?;
        if let Ok(cfg) = StoredConfiguration::deserialize_err(self.cfg()){
           ser.serialize_struct_elt(&mut state, "config", cfg)?;
        } else {
           ser.serialize_struct_elt(&mut state, "config", self.cfg())?;
        }
        ser.serialize_struct_end(state)
    }
}

impl Serialize for TxConfigVote {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("vote", 5)?;
        ser.serialize_struct_elt(&mut state, "from", self.from())?;
        ser.serialize_struct_elt(&mut state, "config_hash", self.cfg_hash())?;
        ser.serialize_struct_end(state)
    }
}

impl Serialize for ConfigTx {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match *self {
            ConfigTx::ConfigPropose(ref propose) => propose.serialize(ser),
            ConfigTx::ConfigVote(ref vote) => vote.serialize(ser),
        }
    }
}

#[derive(Default)]
pub struct ConfigurationService {}

pub struct ConfigurationSchema<'a> {
    view: &'a View,
}

impl ConfigTx {
    pub fn from(&self) -> &PublicKey {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.from(),
            ConfigTx::ConfigVote(ref msg) => msg.from(),
        }
    }
}

impl fmt::Debug for ConfigTx {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => write!(fmt, "{:?}", msg),
            ConfigTx::ConfigVote(ref msg) => write!(fmt, "{:?}", msg),
        }
    }
}

impl FromRaw for ConfigTx {
    fn from_raw(raw: RawMessage) -> Result<ConfigTx, MessageError> {
        match raw.message_type() {
            CONFIG_PROPOSE_MESSAGE_ID => {
                Ok(ConfigTx::ConfigPropose(TxConfigPropose::from_raw(raw)?))
            }
            CONFIG_VOTE_MESSAGE_ID => Ok(ConfigTx::ConfigVote(TxConfigVote::from_raw(raw)?)),
            _ => Err(MessageError::IncorrectMessageType { message_type: raw.message_type() }),
        }
    }
}

impl Message for ConfigTx {
    fn raw(&self) -> &RawMessage {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.raw(),
            ConfigTx::ConfigVote(ref msg) => msg.raw(),
        }
    }

    fn verify_signature(&self, public_key: &PublicKey) -> bool {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.verify_signature(public_key),
            ConfigTx::ConfigVote(ref msg) => msg.verify_signature(public_key),
        }
    }

    fn hash(&self) -> Hash {
        match *self {
            ConfigTx::ConfigPropose(ref msg) =>Message::hash(msg),
            ConfigTx::ConfigVote(ref msg) => Message::hash(msg),
        }
    }
}

impl<'a> ConfigurationSchema<'a> {
    pub fn new(view: &'a View) -> ConfigurationSchema {
        ConfigurationSchema { view: view }
    }

    /// mapping Hash(config) -> TxConfigPropose
    pub fn config_proposes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, Hash, TxConfigPropose> {
        MerklePatriciaTable::new(MapTable::new(vec![04], self.view))
    }

    /// mapping Validator_public_key -> TxConfigVote
    pub fn config_votes
        (&self, config_hash: Hash)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, PublicKey, TxConfigVote> {
        let mut prefix = vec![05; 1 + HASH_SIZE];
        prefix[1..].copy_from_slice(config_hash.as_ref());
        MerklePatriciaTable::new(MapTable::new(prefix, self.view))
    }

    pub fn state_hash(&self) -> StorageResult<Vec<Hash>> {
        Ok(Vec::new())
    }
}

impl TxConfigPropose {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let following_config: Option<StoredConfiguration> = blockchain_schema.get_following_configuration()?;
        if let Some(foll_cfg) = following_config {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose: {} as there is an already scheduled next config: {:?} ", self_repr, foll_cfg);
            return Ok(());
        }

        let actual_config: StoredConfiguration = blockchain_schema.get_actual_configuration()?;
        let actual_config_hash = actual_config.hash();
        if *self.prev_cfg_hash() != actual_config_hash {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} which does not reference actual config: {:?}", self_repr, actual_config);
            return Ok(());
        }

        if !actual_config.validators.contains(self.from()) {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} from unknown validator. ", self_repr);
            return Ok(());
        }

        let config_candidate = StoredConfiguration::deserialize_err(self.cfg());
        if config_candidate.is_err() {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} which contains config, which cannot be parsed: {:?}", self_repr, config_candidate);
            return Ok(());
        }

        let config_candidate_body = config_candidate.unwrap();
        let current_height = blockchain_schema.last_height()? + 1;
        let actual_from = config_candidate_body.actual_from;
        if actual_from <= current_height {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} which has actual_from height less than or equal to current: {:?}", self_repr, current_height);
            return Ok(());
        }

        let config_hash = config_candidate_body.hash();

        if let Some(tx_propose) = config_schema.config_proposes().get(&config_hash)? {
            let self_repr = serde_json::to_string(self)?;
            let another_propose_repr = serde_json::to_string(&tx_propose)?;
            error!("Discarding TxConfigPropose:{} which contains an already posted config. Previous TxConfigPropose:{}",
                   self_repr, another_propose_repr);
            return Ok(());
        }

        config_schema.config_proposes().put(&config_hash, self.clone())?;

        let self_repr = serde_json::to_string(self)?;
        debug!("Put TxConfigPropose:{} to config_proposes table", self_repr);
        Ok(())
    }
}

impl TxConfigVote {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let propose_option = config_schema.config_proposes().get(self.cfg_hash())?;
        if propose_option.is_none() {
            error!("Discarding TxConfigVote:{:?} which references unknown config hash", self);
            return Ok(());
        }

        let following_config: Option<StoredConfiguration> = blockchain_schema.get_following_configuration()?;
        if let Some(foll_cfg) = following_config {
            error!("Discarding TxConfigVote: {:?} as there is an already scheduled next config: {:?} ", self, foll_cfg);
            return Ok(());
        }

        let referenced_tx_propose = propose_option.unwrap();
        let actual_config: StoredConfiguration = blockchain_schema.get_actual_configuration()?;
        let actual_config_hash = actual_config.hash();
        if *referenced_tx_propose.prev_cfg_hash() != actual_config_hash {
            let propose_repr = serde_json::to_string(&referenced_tx_propose)?;
            error!("Discarding TxConfigVote:{:?}, whose corresponding TxConfigPropose:{} does not reference actual config: {:?}", self, propose_repr, actual_config);
            return Ok(());
        }

        if !actual_config.validators.contains(self.from()) {
            error!("Discarding TxConfigVote:{:?} from unknown validator. ", self.from());
            return Ok(());
        }

        let current_height = blockchain_schema.last_height()? + 1;
        let parsed_config =StoredConfiguration::deserialize_err(referenced_tx_propose.cfg()).unwrap();
        let actual_from = parsed_config.actual_from;
        if actual_from <= current_height {
            let propose_repr = serde_json::to_string(&referenced_tx_propose)?;
            error!("Discarding TxConfigVote:{:?}, whose corresponding TxConfigPropose:{} has actual_from height less than or equal to current: {:?}", self, propose_repr,  current_height);
            return Ok(());
        }

        let votes_for_cfg = config_schema.config_votes(*self.cfg_hash());
        votes_for_cfg.put(self.from(), self.clone())?;
        debug!("Put TxConfigVote:{:?} to corresponding cfg config_votes table", self);

        let mut votes_count = 0;
        for pub_key in &actual_config.validators {
            if let Some(_) = votes_for_cfg.get(&pub_key)? {
                    votes_count += 1;
            }
        }

        if votes_count >= State::byzantine_majority_count(actual_config.validators.len()) {
            blockchain_schema.commit_actual_configuration(parsed_config)?;
        }
        Ok(())
    }
}

impl Transaction for ConfigTx {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, view: &View) -> StorageResult<()> {
        match *self {
            ConfigTx::ConfigPropose(ref tx) => tx.execute(view),
            ConfigTx::ConfigVote(ref tx) => tx.execute(view),
        }
    }
}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_id(&self) -> u16 {
        CONFIG_SERVICE
    }

    fn state_hash(&self, view: &View) -> StorageResult<Vec<Hash>> {
        let schema = ConfigurationSchema::new(view);
        schema.state_hash()
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError> {
        ConfigTx::from_raw(raw).map(|tx| Box::new(tx) as Box<Transaction>)
    }

    fn handle_commit(&self, state: &mut NodeState) -> StorageResult<()> {
        let old_cfg = state.actual_config().clone();

        let new_cfg = {
            let schema = Schema::new(state.view());
            schema.get_actual_configuration()?
        };

        if new_cfg != old_cfg {
            info!("Updated node config={:#?}", new_cfg);
            state.update_config(new_cfg);
        }
        Ok(())
    }
}
<commit_msg>rustfmt<commit_after>#[macro_use]
extern crate exonum;
#[macro_use]
extern crate log;

extern crate serde;
extern crate serde_json;

use std::fmt;

use serde::{Serialize, Serializer};

use exonum::blockchain::{Service, Transaction, Schema, NodeState};
use exonum::node::State;
use exonum::crypto::{PublicKey, Hash, HASH_SIZE};
use exonum::messages::{RawMessage, Message, FromRaw, RawTransaction, Error as MessageError};
use exonum::storage::{StorageValue, Map, View, MapTable, MerklePatriciaTable,
                      Result as StorageResult};
use exonum::blockchain::StoredConfiguration;

pub const CONFIG_SERVICE: u16 = 1;
pub const CONFIG_PROPOSE_MESSAGE_ID: u16 = 0;
pub const CONFIG_VOTE_MESSAGE_ID: u16 = 1;

message! {
    TxConfigPropose {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_PROPOSE_MESSAGE_ID;
        const SIZE = 72;

        from:           &PublicKey  [00 => 32]
        prev_cfg_hash:  &Hash       [32 => 64]
        cfg:            &[u8]       [64 => 72] // serialized config bytes
    }
}

message! {
    TxConfigVote {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_VOTE_MESSAGE_ID;
        const SIZE = 64;

        from:           &PublicKey  [00 => 32]
        cfg_hash:       &Hash       [32 => 64] // hash of config we're voting for
    }
}

#[derive(Clone, PartialEq)]
pub enum ConfigTx {
    ConfigPropose(TxConfigPropose),
    ConfigVote(TxConfigVote),
}

impl Serialize for TxConfigPropose {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("config_propose", 4)?;
        ser.serialize_struct_elt(&mut state, "from", self.from())?;
        ser.serialize_struct_elt(&mut state, "previous_config_hash", self.prev_cfg_hash())?;
        if let Ok(cfg) = StoredConfiguration::deserialize_err(self.cfg()) {
            ser.serialize_struct_elt(&mut state, "config", cfg)?;
        } else {
            ser.serialize_struct_elt(&mut state, "config", self.cfg())?;
        }
        ser.serialize_struct_end(state)
    }
}

impl Serialize for TxConfigVote {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("vote", 5)?;
        ser.serialize_struct_elt(&mut state, "from", self.from())?;
        ser.serialize_struct_elt(&mut state, "config_hash", self.cfg_hash())?;
        ser.serialize_struct_end(state)
    }
}

impl Serialize for ConfigTx {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match *self {
            ConfigTx::ConfigPropose(ref propose) => propose.serialize(ser),
            ConfigTx::ConfigVote(ref vote) => vote.serialize(ser),
        }
    }
}

#[derive(Default)]
pub struct ConfigurationService {}

pub struct ConfigurationSchema<'a> {
    view: &'a View,
}

impl ConfigTx {
    pub fn from(&self) -> &PublicKey {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.from(),
            ConfigTx::ConfigVote(ref msg) => msg.from(),
        }
    }
}

impl fmt::Debug for ConfigTx {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => write!(fmt, "{:?}", msg),
            ConfigTx::ConfigVote(ref msg) => write!(fmt, "{:?}", msg),
        }
    }
}

impl FromRaw for ConfigTx {
    fn from_raw(raw: RawMessage) -> Result<ConfigTx, MessageError> {
        match raw.message_type() {
            CONFIG_PROPOSE_MESSAGE_ID => {
                Ok(ConfigTx::ConfigPropose(TxConfigPropose::from_raw(raw)?))
            }
            CONFIG_VOTE_MESSAGE_ID => Ok(ConfigTx::ConfigVote(TxConfigVote::from_raw(raw)?)),
            _ => Err(MessageError::IncorrectMessageType { message_type: raw.message_type() }),
        }
    }
}

impl Message for ConfigTx {
    fn raw(&self) -> &RawMessage {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.raw(),
            ConfigTx::ConfigVote(ref msg) => msg.raw(),
        }
    }

    fn verify_signature(&self, public_key: &PublicKey) -> bool {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.verify_signature(public_key),
            ConfigTx::ConfigVote(ref msg) => msg.verify_signature(public_key),
        }
    }

    fn hash(&self) -> Hash {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => Message::hash(msg),
            ConfigTx::ConfigVote(ref msg) => Message::hash(msg),
        }
    }
}

impl<'a> ConfigurationSchema<'a> {
    pub fn new(view: &'a View) -> ConfigurationSchema {
        ConfigurationSchema { view: view }
    }

    /// mapping Hash(config) -> TxConfigPropose
    pub fn config_proposes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, Hash, TxConfigPropose> {
        MerklePatriciaTable::new(MapTable::new(vec![04], self.view))
    }

    /// mapping Validator_public_key -> TxConfigVote
    pub fn config_votes
        (&self,
         config_hash: Hash)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, PublicKey, TxConfigVote> {
        let mut prefix = vec![05; 1 + HASH_SIZE];
        prefix[1..].copy_from_slice(config_hash.as_ref());
        MerklePatriciaTable::new(MapTable::new(prefix, self.view))
    }

    pub fn state_hash(&self) -> StorageResult<Vec<Hash>> {
        Ok(Vec::new())
    }
}

impl TxConfigPropose {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let following_config: Option<StoredConfiguration> =
            blockchain_schema.get_following_configuration()?;
        if let Some(foll_cfg) = following_config {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose: {} as there is an already scheduled next config: \
                    {:?} ",
                   self_repr,
                   foll_cfg);
            return Ok(());
        }

        let actual_config: StoredConfiguration = blockchain_schema.get_actual_configuration()?;
        let actual_config_hash = actual_config.hash();
        if *self.prev_cfg_hash() != actual_config_hash {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} which does not reference actual config: {:?}",
                   self_repr,
                   actual_config);
            return Ok(());
        }

        if !actual_config.validators.contains(self.from()) {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} from unknown validator. ",
                   self_repr);
            return Ok(());
        }

        let config_candidate = StoredConfiguration::deserialize_err(self.cfg());
        if config_candidate.is_err() {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} which contains config, which cannot be parsed: \
                    {:?}",
                   self_repr,
                   config_candidate);
            return Ok(());
        }

        let config_candidate_body = config_candidate.unwrap();
        let current_height = blockchain_schema.last_height()? + 1;
        let actual_from = config_candidate_body.actual_from;
        if actual_from <= current_height {
            let self_repr = serde_json::to_string(self)?;
            error!("Discarding TxConfigPropose:{} which has actual_from height less than or \
                    equal to current: {:?}",
                   self_repr,
                   current_height);
            return Ok(());
        }

        let config_hash = config_candidate_body.hash();

        if let Some(tx_propose) = config_schema.config_proposes().get(&config_hash)? {
            let self_repr = serde_json::to_string(self)?;
            let another_propose_repr = serde_json::to_string(&tx_propose)?;
            error!("Discarding TxConfigPropose:{} which contains an already posted config. \
                    Previous TxConfigPropose:{}",
                   self_repr,
                   another_propose_repr);
            return Ok(());
        }

        config_schema.config_proposes().put(&config_hash, self.clone())?;

        let self_repr = serde_json::to_string(self)?;
        debug!("Put TxConfigPropose:{} to config_proposes table", self_repr);
        Ok(())
    }
}

impl TxConfigVote {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let propose_option = config_schema.config_proposes().get(self.cfg_hash())?;
        if propose_option.is_none() {
            error!("Discarding TxConfigVote:{:?} which references unknown config hash",
                   self);
            return Ok(());
        }

        let following_config: Option<StoredConfiguration> =
            blockchain_schema.get_following_configuration()?;
        if let Some(foll_cfg) = following_config {
            error!("Discarding TxConfigVote: {:?} as there is an already scheduled next config: \
                    {:?} ",
                   self,
                   foll_cfg);
            return Ok(());
        }

        let referenced_tx_propose = propose_option.unwrap();
        let actual_config: StoredConfiguration = blockchain_schema.get_actual_configuration()?;
        let actual_config_hash = actual_config.hash();
        if *referenced_tx_propose.prev_cfg_hash() != actual_config_hash {
            let propose_repr = serde_json::to_string(&referenced_tx_propose)?;
            error!("Discarding TxConfigVote:{:?}, whose corresponding TxConfigPropose:{} does \
                    not reference actual config: {:?}",
                   self,
                   propose_repr,
                   actual_config);
            return Ok(());
        }

        if !actual_config.validators.contains(self.from()) {
            error!("Discarding TxConfigVote:{:?} from unknown validator. ",
                   self.from());
            return Ok(());
        }

        let current_height = blockchain_schema.last_height()? + 1;
        let parsed_config = StoredConfiguration::deserialize_err(referenced_tx_propose.cfg())
            .unwrap();
        let actual_from = parsed_config.actual_from;
        if actual_from <= current_height {
            let propose_repr = serde_json::to_string(&referenced_tx_propose)?;
            error!("Discarding TxConfigVote:{:?}, whose corresponding TxConfigPropose:{} has \
                    actual_from height less than or equal to current: {:?}",
                   self,
                   propose_repr,
                   current_height);
            return Ok(());
        }

        let votes_for_cfg = config_schema.config_votes(*self.cfg_hash());
        votes_for_cfg.put(self.from(), self.clone())?;
        debug!("Put TxConfigVote:{:?} to corresponding cfg config_votes table",
               self);

        let mut votes_count = 0;
        for pub_key in &actual_config.validators {
            if let Some(_) = votes_for_cfg.get(&pub_key)? {
                votes_count += 1;
            }
        }

        if votes_count >= State::byzantine_majority_count(actual_config.validators.len()) {
            blockchain_schema.commit_actual_configuration(parsed_config)?;
        }
        Ok(())
    }
}

impl Transaction for ConfigTx {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, view: &View) -> StorageResult<()> {
        match *self {
            ConfigTx::ConfigPropose(ref tx) => tx.execute(view),
            ConfigTx::ConfigVote(ref tx) => tx.execute(view),
        }
    }
}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_id(&self) -> u16 {
        CONFIG_SERVICE
    }

    fn state_hash(&self, view: &View) -> StorageResult<Vec<Hash>> {
        let schema = ConfigurationSchema::new(view);
        schema.state_hash()
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError> {
        ConfigTx::from_raw(raw).map(|tx| Box::new(tx) as Box<Transaction>)
    }

    fn handle_commit(&self, state: &mut NodeState) -> StorageResult<()> {
        let old_cfg = state.actual_config().clone();

        let new_cfg = {
            let schema = Schema::new(state.view());
            schema.get_actual_configuration()?
        };

        if new_cfg != old_cfg {
            info!("Updated node config={:#?}", new_cfg);
            state.update_config(new_cfg);
        }
        Ok(())
    }
}
<|endoftext|>