<commit_before>//! This crate tests using rocket_cors using Fairings

#![feature(plugin, custom_derive, test)]
#![plugin(rocket_codegen)]
extern crate hyper;
extern crate rocket;
extern crate rocket_cors;
extern crate test;

use std::str::FromStr;

use rocket::http::Method;
use rocket::http::{Header, Status};
use rocket::local::Client;
use rocket_cors::*;

#[get("/")]
fn cors<'a>() -> &'a str {
    "Hello CORS"
}

#[get("/panic")]
fn panicking_route() {
    panic!("This route will panic");
}

fn make_cors_options() -> Cors {
    let (allowed_origins, failed_origins) = AllOrSome::new_from_str_list(&["https://www.acme.com"]);
    assert!(failed_origins.is_empty());

    Cors {
        allowed_origins: allowed_origins,
        allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(),
        allowed_headers: AllOrSome::Some(
            ["Authorization"]
                .into_iter()
                .map(|s| s.to_string().into())
                .collect(),
        ),
        allow_credentials: true,
        ..Default::default()
    }
}

fn rocket() -> rocket::Rocket {
    rocket::ignite()
        .mount("/", routes![cors, panicking_route])
        .attach(make_cors_options())
}

#[test]
fn smoke_test() {
    let client = Client::new(rocket()).unwrap();

    // `Options` pre-flight checks
    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert!(response.status().class().is_success());

    // "Actual" request
    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(origin_header).header(authorization);

    let mut response = req.dispatch();
    assert!(response.status().class().is_success());
    let body_str = response.body().and_then(|body| body.into_string());
    assert_eq!(body_str, Some("Hello CORS".to_string()));

}

use test::Bencher;

#[bench]
fn bench_smoke_test(b: &mut Bencher) {
    b.iter(|| smoke_test());
}

#[test]
fn cors_options_check() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert!(response.status().class().is_success());
}

#[test]
fn cors_get_check() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(origin_header).header(authorization);

    let mut response = req.dispatch();
    println!("{:?}", response);
    assert!(response.status().class().is_success());
    let body_str = response.body().and_then(|body| body.into_string());
    assert_eq!(body_str, Some("Hello CORS".to_string()));
}

/// This test is to check that non CORS compliant requests to GET should still work. (i.e. curl)
#[test]
fn cors_get_no_origin() {
    let client = Client::new(rocket()).unwrap();

    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(authorization);

    let mut response = req.dispatch();
    assert!(response.status().class().is_success());
    let body_str = response.body().and_then(|body| body.into_string());
    assert_eq!(body_str, Some("Hello CORS".to_string()));
}

#[test]
fn cors_options_bad_origin() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.bad-origin.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

/// Unlike the "ad-hoc" mode, this should return 404 because we don't have such a route
#[test]
fn cors_options_missing_origin() {
    let client = Client::new(rocket()).unwrap();

    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client.options("/").header(method_header).header(
        request_headers,
    );

    let response = req.dispatch();
    assert_eq!(response.status(), Status::NotFound);
}

#[test]
fn cors_options_bad_request_method() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Post,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

#[test]
fn cors_options_bad_request_header() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers =
        hyper::header::AccessControlRequestHeaders(vec![FromStr::from_str("Foobar").unwrap()]);
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

#[test]
fn cors_get_bad_origin() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.bad-origin.com").unwrap(),
    );
    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(origin_header).header(authorization);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

/// This test ensures that on a failing CORS request, the route (along with its side effects)
/// should never be executed.
/// The route used will panic if executed
#[test]
fn routes_failing_checks_are_not_executed() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.bad-origin.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/panic")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}
<commit_msg>Remove benchmark<commit_after>//! This crate tests using rocket_cors using Fairings

#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]
extern crate hyper;
extern crate rocket;
extern crate rocket_cors;

use std::str::FromStr;

use rocket::http::Method;
use rocket::http::{Header, Status};
use rocket::local::Client;
use rocket_cors::*;

#[get("/")]
fn cors<'a>() -> &'a str {
    "Hello CORS"
}

#[get("/panic")]
fn panicking_route() {
    panic!("This route will panic");
}

fn make_cors_options() -> Cors {
    let (allowed_origins, failed_origins) = AllOrSome::new_from_str_list(&["https://www.acme.com"]);
    assert!(failed_origins.is_empty());

    Cors {
        allowed_origins: allowed_origins,
        allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(),
        allowed_headers: AllOrSome::Some(
            ["Authorization"]
                .into_iter()
                .map(|s| s.to_string().into())
                .collect(),
        ),
        allow_credentials: true,
        ..Default::default()
    }
}

fn rocket() -> rocket::Rocket {
    rocket::ignite()
        .mount("/", routes![cors, panicking_route])
        .attach(make_cors_options())
}

#[test]
fn smoke_test() {
    let client = Client::new(rocket()).unwrap();

    // `Options` pre-flight checks
    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert!(response.status().class().is_success());

    // "Actual" request
    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(origin_header).header(authorization);

    let mut response = req.dispatch();
    assert!(response.status().class().is_success());
    let body_str = response.body().and_then(|body| body.into_string());
    assert_eq!(body_str, Some("Hello CORS".to_string()));

}

#[test]
fn cors_options_check() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert!(response.status().class().is_success());
}

#[test]
fn cors_get_check() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(origin_header).header(authorization);

    let mut response = req.dispatch();
    println!("{:?}", response);
    assert!(response.status().class().is_success());
    let body_str = response.body().and_then(|body| body.into_string());
    assert_eq!(body_str, Some("Hello CORS".to_string()));
}

/// This test is to check that non CORS compliant requests to GET should still work. (i.e. curl)
#[test]
fn cors_get_no_origin() {
    let client = Client::new(rocket()).unwrap();

    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(authorization);

    let mut response = req.dispatch();
    assert!(response.status().class().is_success());
    let body_str = response.body().and_then(|body| body.into_string());
    assert_eq!(body_str, Some("Hello CORS".to_string()));
}

#[test]
fn cors_options_bad_origin() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.bad-origin.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

/// Unlike the "ad-hoc" mode, this should return 404 because we don't have such a route
#[test]
fn cors_options_missing_origin() {
    let client = Client::new(rocket()).unwrap();

    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client.options("/").header(method_header).header(
        request_headers,
    );

    let response = req.dispatch();
    assert_eq!(response.status(), Status::NotFound);
}

#[test]
fn cors_options_bad_request_method() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Post,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

#[test]
fn cors_options_bad_request_header() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.acme.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers =
        hyper::header::AccessControlRequestHeaders(vec![FromStr::from_str("Foobar").unwrap()]);
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

#[test]
fn cors_get_bad_origin() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.bad-origin.com").unwrap(),
    );
    let authorization = Header::new("Authorization", "let me in");
    let req = client.get("/").header(origin_header).header(authorization);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}

/// This test ensures that on a failing CORS request, the route (along with its side effects)
/// should never be executed.
/// The route used will panic if executed
#[test]
fn routes_failing_checks_are_not_executed() {
    let client = Client::new(rocket()).unwrap();

    let origin_header = Header::from(
        hyper::header::Origin::from_str("https://www.bad-origin.com").unwrap(),
    );
    let method_header = Header::from(hyper::header::AccessControlRequestMethod(
        hyper::method::Method::Get,
    ));
    let request_headers = hyper::header::AccessControlRequestHeaders(
        vec![FromStr::from_str("Authorization").unwrap()],
    );
    let request_headers = Header::from(request_headers);
    let req = client
        .options("/panic")
        .header(origin_header)
        .header(method_header)
        .header(request_headers);

    let response = req.dispatch();
    assert_eq!(response.status(), Status::Forbidden);
}
<|endoftext|>


<commit_before>use std::ops::{Add, Sub, Neg};

/// Provide access to the point's coordinate values
pub trait HasValues<T = i32>
  where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> + Copy {

  /// Return a tuple of (QRT)
  ///
  /// # Example
  ///
  /// ```
  /// use hex_math::{Point, HasValues};
  ///
  /// let point: Point = Point::new(1, 2, 5);
  ///
  /// assert_eq!((1, 2, 5), point.values());
  /// ```
  fn values(&self) -> (T, T, T);

  /// Return a tuple of (QR)
  ///
  /// # Example
  ///
  /// ```
  /// use hex_math::{Point, HasValues};
  ///
  /// let point: Point = Point::new_2d(1, 2);
  ///
  /// assert_eq!((1, 2), point.values_2d());
  /// ```
  fn values_2d(&self) -> (T, T) {
    let (q, r, _) = self.values();

    (q, r)
  }

  /// Return a tuple of (QRST)
  ///
  /// # Example
  ///
  /// ```
  /// use hex_math::{Point, HasValues};
  ///
  /// let point: Point = Point::new(1, 2, 5);
  ///
  /// assert_eq!((1, 2, -3, 5), point.values_cube());
  /// ```
  fn values_cube(&self) -> (T, T, T, T) {
    let (q, r, t) = self.values();
    let s = -q - r;

    (q, r, s, t)
  }

  /// Return a tuple of (QRS)
  ///
  /// ```
  /// use hex_math::{Point, HasValues};
  ///
  /// let point: Point = Point::new_2d(1, 2);
  ///
  /// assert_eq!((1, 2, -3), point.values_cube_2d());
  /// ```
  fn values_cube_2d(&self) -> (T, T, T) {
    let (q, r, s, _) = self.values_cube();

    (q, r, s)
  }

}

<commit_msg>Issue #33: Refactor HasValues<commit_after>use std::ops::{Add, Sub, Neg};

/// Provide access to the point's coordinate values
pub trait HasValues<T = i32>
  where T: Add<Output = T> + Sub<Output = T> + Neg<Output = T> + Copy {

  /// Return a tuple of (QRT)
  fn values(&self) -> (T, T, T);

  /// Return a tuple of (QR)
  fn values_2d(&self) -> (T, T) {
    let (q, r, _) = self.values();

    (q, r)
  }

  /// Return a tuple of (QRST)
  fn values_cube(&self) -> (T, T, T, T) {
    let (q, r, t) = self.values();
    let s = -q - r;

    (q, r, s, t)
  }

  /// Return a tuple of (QRS)
  fn values_cube_2d(&self) -> (T, T, T) {
    let (q, r, s, _) = self.values_cube();

    (q, r, s)
  }

}

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

  #[test]
  fn values_2d() {
    let (q, r) = Point::new(1, 2, 5).values_2d();

    assert!(1 == q);
    assert!(2 == r);
  }

  #[test]
  fn values_cube() {
    let (q, r, s, t) = Point::new(1, 2, 5).values_cube();

    assert!( 1 == q);
    assert!( 2 == r);
    assert!(-3 == s);
    assert!( 5 == t);
  }

  #[test]
  fn values_cube_2d() {
    let (q, r, s) = Point::new(1, 2, 5).values_cube_2d();

    assert!( 1 == q);
    assert!( 2 == r);
    assert!(-3 == s);
  }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use diem_config::config::RocksdbConfig;
use diem_global_constants::{
    CONSENSUS_KEY, FULLNODE_NETWORK_KEY, OPERATOR_ACCOUNT, OPERATOR_KEY, OWNER_ACCOUNT, OWNER_KEY,
    SAFETY_DATA, VALIDATOR_NETWORK_KEY, WAYPOINT,
};
use diem_management::{
    config::ConfigPath, error::Error, secure_backend::ValidatorBackend,
    storage::StorageWrapper as Storage,
};
use diem_temppath::TempPath;
use diem_types::{
    account_address::AccountAddress, account_config, account_state::AccountState,
    network_address::NetworkAddress, on_chain_config::ValidatorSet,
    validator_config::ValidatorConfig, waypoint::Waypoint,
};
use diem_vm::DiemVM;
use diemdb::DiemDB;
use executor::db_bootstrapper;
use std::{
    convert::TryFrom,
    fmt::Write,
    fs::File,
    io::Read,
    path::{Path, PathBuf},
    str::FromStr,
    sync::Arc,
};
use storage_interface::{DbReader, DbReaderWriter};
use structopt::StructOpt;

/// Prints the public information within a store
#[derive(Debug, StructOpt)]
pub struct Verify {
    #[structopt(flatten)]
    config: ConfigPath,
    #[structopt(flatten)]
    backend: ValidatorBackend,
    /// If specified, compares the internal state to that of a
    /// provided genesis. Note, that a waypont might diverge from
    /// the provided genesis after execution has begun.
    #[structopt(long, verbatim_doc_comment)]
    genesis_path: Option<PathBuf>,
}

impl Verify {
    pub fn execute(self) -> Result<String, Error> {
        let config = self
            .config
            .load()?
            .override_validator_backend(&self.backend.validator_backend)?;
        let validator_storage = config.validator_backend();
        let mut buffer = String::new();

        writeln!(buffer, "Data stored in SecureStorage:").unwrap();
        write_break(&mut buffer);
        writeln!(buffer, "Keys").unwrap();
        write_break(&mut buffer);

        write_ed25519_key(&validator_storage, &mut buffer, CONSENSUS_KEY);
        write_x25519_key(&validator_storage, &mut buffer, FULLNODE_NETWORK_KEY);
        write_ed25519_key(&validator_storage, &mut buffer, OWNER_KEY);
        write_ed25519_key(&validator_storage, &mut buffer, OPERATOR_KEY);
        write_ed25519_key(&validator_storage, &mut buffer, VALIDATOR_NETWORK_KEY);

        write_break(&mut buffer);
        writeln!(buffer, "Data").unwrap();
        write_break(&mut buffer);

        write_string(&validator_storage, &mut buffer, OPERATOR_ACCOUNT);
        write_string(&validator_storage, &mut buffer, OWNER_ACCOUNT);
        write_safety_data(&validator_storage, &mut buffer, SAFETY_DATA);
        write_waypoint(&validator_storage, &mut buffer, WAYPOINT);

        write_break(&mut buffer);

        if let Some(genesis_path) = self.genesis_path.as_ref() {
            compare_genesis(validator_storage, &mut buffer, genesis_path)?;
        }

        Ok(buffer)
    }
}

fn write_assert(buffer: &mut String, name: &str, value: bool) {
    let value = if value { "match" } else { "MISMATCH" };
    writeln!(buffer, "{} - {}", name, value).unwrap();
}

fn write_break(buffer: &mut String) {
    writeln!(
        buffer,
        "====================================================================================",
    )
    .unwrap();
}

fn write_ed25519_key(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .ed25519_public_from_private(key)
        .map(|v| v.to_string())
        .unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_x25519_key(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .x25519_public_from_private(key)
        .map(|v| v.to_string())
        .unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_string(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage.string(key).unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_safety_data(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .value::<consensus_types::safety_data::SafetyData>(key)
        .map(|v| v.to_string())
        .unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_waypoint(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .string(key)
        .map(|value| {
            if value.is_empty() {
                "empty".into()
            } else {
                Waypoint::from_str(&value)
                    .map(|c| c.to_string())
                    .unwrap_or_else(|_| "Invalid waypoint".into())
            }
        })
        .unwrap_or_else(|e| e.to_string());

    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn compare_genesis(
    storage: Storage,
    buffer: &mut String,
    genesis_path: &Path,
) -> Result<(), Error> {
    // Compute genesis and waypoint and compare to given waypoint
    let db_path = TempPath::new();
    let (db_rw, expected_waypoint) = compute_genesis(genesis_path, db_path.path())?;

    let actual_waypoint = storage.waypoint(WAYPOINT)?;
    write_assert(buffer, WAYPOINT, actual_waypoint == expected_waypoint);

    // Fetch on-chain validator config and compare on-chain keys to local keys
    let validator_account = storage.account_address(OWNER_ACCOUNT)?;
    let validator_config = validator_config(validator_account, db_rw.reader.clone())?;

    let actual_consensus_key = storage.ed25519_public_from_private(CONSENSUS_KEY)?;
    let expected_consensus_key = &validator_config.consensus_public_key;
    write_assert(
        buffer,
        CONSENSUS_KEY,
        &actual_consensus_key == expected_consensus_key,
    );

    let actual_validator_key = storage.x25519_public_from_private(VALIDATOR_NETWORK_KEY)?;
    let actual_fullnode_key = storage.x25519_public_from_private(FULLNODE_NETWORK_KEY)?;
    let encryptor = storage.encryptor();

    let expected_validator_key = encryptor
        .decrypt(
            &validator_config.validator_network_addresses,
            validator_account,
        )
        .ok()
        .and_then(|addrs| {
            addrs
                .get(0)
                .and_then(|addr: &NetworkAddress| addr.find_noise_proto())
        });
    write_assert(
        buffer,
        VALIDATOR_NETWORK_KEY,
        Some(actual_validator_key) == expected_validator_key,
    );

    let expected_fullnode_key = validator_config.fullnode_network_addresses().ok().and_then(
        |addrs: Vec<NetworkAddress>| addrs.get(0).and_then(|addr| addr.find_noise_proto()),
    );
    write_assert(
        buffer,
        FULLNODE_NETWORK_KEY,
        Some(actual_fullnode_key) == expected_fullnode_key,
    );

    Ok(())
}

/// Compute the ledger given a genesis writeset transaction and return access to that ledger and
/// the waypoint for that state.
fn compute_genesis(
    genesis_path: &Path,
    db_path: &Path,
) -> Result<(DbReaderWriter, Waypoint), Error> {
    let diemdb = DiemDB::open(db_path, false, None, RocksdbConfig::default())
        .map_err(|e| Error::UnexpectedError(e.to_string()))?;
    let db_rw = DbReaderWriter::new(diemdb);

    let mut file = File::open(genesis_path)
        .map_err(|e| Error::UnexpectedError(format!("Unable to open genesis file: {}", e)))?;
    let mut buffer = vec![];
    file.read_to_end(&mut buffer)
        .map_err(|e| Error::UnexpectedError(format!("Unable to read genesis: {}", e)))?;
    let genesis = bcs::from_bytes(&buffer)
        .map_err(|e| Error::UnexpectedError(format!("Unable to parse genesis: {}", e)))?;

    let waypoint = db_bootstrapper::generate_waypoint::<DiemVM>(&db_rw, &genesis)
        .map_err(|e| Error::UnexpectedError(e.to_string()))?;
    db_bootstrapper::maybe_bootstrap::<DiemVM>(&db_rw, &genesis, waypoint)
        .map_err(|e| Error::UnexpectedError(format!("Unable to commit genesis: {}", e)))?;

    Ok((db_rw, waypoint))
}

/// Read from the ledger the validator config from the validator set for the specified account
fn validator_config(
    validator_account: AccountAddress,
    reader: Arc<dyn DbReader>,
) -> Result<ValidatorConfig, Error> {
    let blob = reader
        .get_latest_account_state(account_config::validator_set_address())
        .map_err(|e| {
            Error::UnexpectedError(format!("ValidatorSet Account issue {}", e.to_string()))
        })?
        .ok_or_else(|| Error::UnexpectedError("ValidatorSet Account not found".into()))?;
    let account_state = AccountState::try_from(&blob)
        .map_err(|e| Error::UnexpectedError(format!("Failed to parse blob: {}", e)))?;
    let validator_set: ValidatorSet = account_state
        .get_validator_set()
        .map_err(|e| Error::UnexpectedError(format!("ValidatorSet issue {}", e.to_string())))?
        .ok_or_else(|| Error::UnexpectedError("ValidatorSet does not exist".into()))?;
    let info = validator_set
        .payload()
        .iter()
        .find(|vi| vi.account_address() == &validator_account)
        .ok_or_else(|| {
            Error::UnexpectedError(format!(
                "Unable to find Validator account {:?}",
                &validator_account
            ))
        })?;
    Ok(info.config().clone())
}
<commit_msg>genesis: factor out a reusable verify_genesis function<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use diem_config::config::RocksdbConfig;
use diem_global_constants::{
    CONSENSUS_KEY, FULLNODE_NETWORK_KEY, OPERATOR_ACCOUNT, OPERATOR_KEY, OWNER_ACCOUNT, OWNER_KEY,
    SAFETY_DATA, VALIDATOR_NETWORK_KEY, WAYPOINT,
};
use diem_management::{
    config::ConfigPath, error::Error, secure_backend::ValidatorBackend,
    storage::StorageWrapper as Storage,
};
use diem_temppath::TempPath;
use diem_types::{
    account_address::AccountAddress, account_config, account_state::AccountState,
    network_address::NetworkAddress, on_chain_config::ValidatorSet,
    validator_config::ValidatorConfig, waypoint::Waypoint,
};
use diem_vm::DiemVM;
use diemdb::DiemDB;
use executor::db_bootstrapper;
use std::{
    convert::TryFrom,
    fmt::Write,
    fs::File,
    io::Read,
    path::{Path, PathBuf},
    str::FromStr,
    sync::Arc,
};
use storage_interface::{DbReader, DbReaderWriter};
use structopt::StructOpt;

/// Prints the public information within a store
#[derive(Debug, StructOpt)]
pub struct Verify {
    #[structopt(flatten)]
    config: ConfigPath,
    #[structopt(flatten)]
    backend: ValidatorBackend,
    /// If specified, compares the internal state to that of a
    /// provided genesis. Note, that a waypont might diverge from
    /// the provided genesis after execution has begun.
    #[structopt(long, verbatim_doc_comment)]
    genesis_path: Option<PathBuf>,
}

impl Verify {
    pub fn execute(self) -> Result<String, Error> {
        let config = self
            .config
            .load()?
            .override_validator_backend(&self.backend.validator_backend)?;
        let validator_storage = config.validator_backend();

        verify_genesis(validator_storage, self.genesis_path.as_deref())
    }
}

pub fn verify_genesis(
    validator_storage: Storage,
    genesis_path: Option<&Path>,
) -> Result<String, Error> {
    let mut buffer = String::new();

    writeln!(buffer, "Data stored in SecureStorage:").unwrap();
    write_break(&mut buffer);
    writeln!(buffer, "Keys").unwrap();
    write_break(&mut buffer);

    write_ed25519_key(&validator_storage, &mut buffer, CONSENSUS_KEY);
    write_x25519_key(&validator_storage, &mut buffer, FULLNODE_NETWORK_KEY);
    write_ed25519_key(&validator_storage, &mut buffer, OWNER_KEY);
    write_ed25519_key(&validator_storage, &mut buffer, OPERATOR_KEY);
    write_ed25519_key(&validator_storage, &mut buffer, VALIDATOR_NETWORK_KEY);

    write_break(&mut buffer);
    writeln!(buffer, "Data").unwrap();
    write_break(&mut buffer);

    write_string(&validator_storage, &mut buffer, OPERATOR_ACCOUNT);
    write_string(&validator_storage, &mut buffer, OWNER_ACCOUNT);
    write_safety_data(&validator_storage, &mut buffer, SAFETY_DATA);
    write_waypoint(&validator_storage, &mut buffer, WAYPOINT);

    write_break(&mut buffer);

    if let Some(genesis_path) = genesis_path {
        compare_genesis(validator_storage, &mut buffer, genesis_path)?;
    }

    Ok(buffer)
}

fn write_assert(buffer: &mut String, name: &str, value: bool) {
    let value = if value { "match" } else { "MISMATCH" };
    writeln!(buffer, "{} - {}", name, value).unwrap();
}

fn write_break(buffer: &mut String) {
    writeln!(
        buffer,
        "====================================================================================",
    )
    .unwrap();
}

fn write_ed25519_key(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .ed25519_public_from_private(key)
        .map(|v| v.to_string())
        .unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_x25519_key(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .x25519_public_from_private(key)
        .map(|v| v.to_string())
        .unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_string(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage.string(key).unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_safety_data(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .value::<consensus_types::safety_data::SafetyData>(key)
        .map(|v| v.to_string())
        .unwrap_or_else(|e| e.to_string());
    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn write_waypoint(storage: &Storage, buffer: &mut String, key: &'static str) {
    let value = storage
        .string(key)
        .map(|value| {
            if value.is_empty() {
                "empty".into()
            } else {
                Waypoint::from_str(&value)
                    .map(|c| c.to_string())
                    .unwrap_or_else(|_| "Invalid waypoint".into())
            }
        })
        .unwrap_or_else(|e| e.to_string());

    writeln!(buffer, "{} - {}", key, value).unwrap();
}

fn compare_genesis(
    storage: Storage,
    buffer: &mut String,
    genesis_path: &Path,
) -> Result<(), Error> {
    // Compute genesis and waypoint and compare to given waypoint
    let db_path = TempPath::new();
    let (db_rw, expected_waypoint) = compute_genesis(genesis_path, db_path.path())?;

    let actual_waypoint = storage.waypoint(WAYPOINT)?;
    write_assert(buffer, WAYPOINT, actual_waypoint == expected_waypoint);

    // Fetch on-chain validator config and compare on-chain keys to local keys
    let validator_account = storage.account_address(OWNER_ACCOUNT)?;
    let validator_config = validator_config(validator_account, db_rw.reader.clone())?;

    let actual_consensus_key = storage.ed25519_public_from_private(CONSENSUS_KEY)?;
    let expected_consensus_key = &validator_config.consensus_public_key;
    write_assert(
        buffer,
        CONSENSUS_KEY,
        &actual_consensus_key == expected_consensus_key,
    );

    let actual_validator_key = storage.x25519_public_from_private(VALIDATOR_NETWORK_KEY)?;
    let actual_fullnode_key = storage.x25519_public_from_private(FULLNODE_NETWORK_KEY)?;
    let encryptor = storage.encryptor();

    let expected_validator_key = encryptor
        .decrypt(
            &validator_config.validator_network_addresses,
            validator_account,
        )
        .ok()
        .and_then(|addrs| {
            addrs
                .get(0)
                .and_then(|addr: &NetworkAddress| addr.find_noise_proto())
        });
    write_assert(
        buffer,
        VALIDATOR_NETWORK_KEY,
        Some(actual_validator_key) == expected_validator_key,
    );

    let expected_fullnode_key = validator_config.fullnode_network_addresses().ok().and_then(
        |addrs: Vec<NetworkAddress>| addrs.get(0).and_then(|addr| addr.find_noise_proto()),
    );
    write_assert(
        buffer,
        FULLNODE_NETWORK_KEY,
        Some(actual_fullnode_key) == expected_fullnode_key,
    );

    Ok(())
}

/// Compute the ledger given a genesis writeset transaction and return access to that ledger and
/// the waypoint for that state.
fn compute_genesis(
    genesis_path: &Path,
    db_path: &Path,
) -> Result<(DbReaderWriter, Waypoint), Error> {
    let diemdb = DiemDB::open(db_path, false, None, RocksdbConfig::default())
        .map_err(|e| Error::UnexpectedError(e.to_string()))?;
    let db_rw = DbReaderWriter::new(diemdb);

    let mut file = File::open(genesis_path)
        .map_err(|e| Error::UnexpectedError(format!("Unable to open genesis file: {}", e)))?;
    let mut buffer = vec![];
    file.read_to_end(&mut buffer)
        .map_err(|e| Error::UnexpectedError(format!("Unable to read genesis: {}", e)))?;
    let genesis = bcs::from_bytes(&buffer)
        .map_err(|e| Error::UnexpectedError(format!("Unable to parse genesis: {}", e)))?;

    let waypoint = db_bootstrapper::generate_waypoint::<DiemVM>(&db_rw, &genesis)
        .map_err(|e| Error::UnexpectedError(e.to_string()))?;
    db_bootstrapper::maybe_bootstrap::<DiemVM>(&db_rw, &genesis, waypoint)
        .map_err(|e| Error::UnexpectedError(format!("Unable to commit genesis: {}", e)))?;

    Ok((db_rw, waypoint))
}

/// Read from the ledger the validator config from the validator set for the specified account
fn validator_config(
    validator_account: AccountAddress,
    reader: Arc<dyn DbReader>,
) -> Result<ValidatorConfig, Error> {
    let blob = reader
        .get_latest_account_state(account_config::validator_set_address())
        .map_err(|e| {
            Error::UnexpectedError(format!("ValidatorSet Account issue {}", e.to_string()))
        })?
        .ok_or_else(|| Error::UnexpectedError("ValidatorSet Account not found".into()))?;
    let account_state = AccountState::try_from(&blob)
        .map_err(|e| Error::UnexpectedError(format!("Failed to parse blob: {}", e)))?;
    let validator_set: ValidatorSet = account_state
        .get_validator_set()
        .map_err(|e| Error::UnexpectedError(format!("ValidatorSet issue {}", e.to_string())))?
        .ok_or_else(|| Error::UnexpectedError("ValidatorSet does not exist".into()))?;
    let info = validator_set
        .payload()
        .iter()
        .find(|vi| vi.account_address() == &validator_account)
        .ok_or_else(|| {
            Error::UnexpectedError(format!(
                "Unable to find Validator account {:?}",
                &validator_account
            ))
        })?;
    Ok(info.config().clone())
}
<|endoftext|>


<commit_before>macro_rules! extract_variant {
    ((ref $input: expr) -> $variant: ident) => ( match $input {
        &Element::$variant(ref x) => x,
        _ => unreachable!() });

    (($input: expr) -> $variant: ident) => ( match $input {
        Element::$variant(x) => x,
        _ => unreachable!() });
}

/// Helper for parsing a regex and returning the inner pattern type.
macro_rules! from_regex {
    ($s: tt -> Element<$T: ty>) => ( ::cripes::pattern::parse_regex::<$T>($s).unwrap() );
    ($s: tt -> $which: ident < $T: ty>) => (extract_variant!((::cripes::pattern::parse_regex::<$T>($s).unwrap()) -> $which));
}

mod sequence {
    use std::iter::{self, FromIterator};
    #[cfg(feature = "pattern_class")]
    use cripes::pattern::Class;
    use cripes::pattern::{Element, Sequence, Union};
    use cripes::pattern::Reduce;
    use cripes::util::set::IsSubsetOf;

    #[test]
    fn test_reduce() {
        let seq_of_seqs = Sequence::from_iter(["abc", "def", "ghi"].iter().map(|s| Sequence::from_iter(s.chars().map(|c| c.into())).into()));
        let flat_seq = Sequence::<char>::from_iter("abcdefghi".chars().map(|c| c.into()));

        assert!(seq_of_seqs != flat_seq);
        assert_eq!(Some(Element::Sequence(flat_seq)), seq_of_seqs.reduce());

        // Empty sequences should reduce to `None`
        assert_eq!(None, Sequence::<char>::from_iter(iter::empty()).reduce());

        // Unit-length sequences should reduce to their only element.
        assert_eq!(Some(Element::Wildcard), Sequence::<char>::from_iter(vec![Element::Wildcard]).reduce());
    }

    #[test]
    fn test_is_subset_of() {
        let unions = (&["abc", "def", "ghi"]).iter().map(|s| Union::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        #[cfg(feature = "pattern_class")]
        let classes = (&["abc", "def", "ghi"]).iter().map(|s| Class::<char>::from_iter(s.chars())).collect::<Vec<_>>();
        let seqs = (&["adg", "beh", "cfi"]).iter().map(|s| Sequence::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let useq = Sequence::from_iter(unions.iter().map(|u| Element::Union(u.clone())));
        #[cfg(feature = "pattern_class")]
        let cseq = Sequence::from_iter(classes.iter().map(|c| Element::Class(c.clone())));

        // Every sequence should be a subset of itself.
        assert!(useq.is_subset_of(&useq));
        #[cfg(feature = "pattern_class")] assert!(cseq.is_subset_of(&cseq));

        // `useq` and `cseq` should be subsets of each other.
        #[cfg(feature = "pattern_class")] {
            assert!(useq.is_subset_of(&cseq));
            assert!(cseq.is_subset_of(&useq));
        }

        // each sequence in `seqs` should be a subset of both `useq` and
        // `cseq`.
        for seq in seqs.iter() {
            for i in 0..3 {
                #[cfg(feature = "pattern_class")] assert!(seq[i].is_subset_of(&cseq[i]));
                assert!(seq[i].is_subset_of(&useq[i]));
            }

        #[cfg(feature = "pattern_class")] assert!(seq.is_subset_of(&cseq));
            assert!(seq.is_subset_of(&useq));
        }
    }
}

mod union {
    use std::iter::FromIterator;
    use cripes::pattern::{Element, Union, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf};

    #[test]
    fn test_contains() {
        let u = Union::from_iter("abc".chars().map(|c| c.into()));
        assert!(u.contains('a'));
        assert!(u.contains('b'));
        assert!(u.contains('c'));
    }

    #[test]
    fn test_is_subset_of_union() {
        let u1 = Union::<char>::from_iter("abc".chars().map(|c| c.into()));
        let u2 = Union::from_iter("abcdef".chars().map(|c| c.into()));
        assert!(u1.is_subset_of(&u2));
        assert!(! u2.is_subset_of(&u1));

        assert!(u1.is_subset_of(&Wildcard));
        assert!(u2.is_subset_of(&Wildcard));
    }
    #[test]
    fn test_is_subset_of_sequence() {
        let seq_of_classes = from_regex!("[abc][def][ghi]" -> Sequence<char>);
        let seq_of_unions = from_regex!("(a|b|c)(d|e|f)(g|h|i)" -> Sequence<char>);

        let union_of_seqs = from_regex!("adg|beh|cfi" -> Union<char>);
        println!("{:?}", union_of_seqs);
        assert_eq!(3, union_of_seqs.len());

        // check that the union would match each contained sequence.
        for seq in union_of_seqs.iter() {
            println!("{:?}", seq);
            assert!(seq.is_subset_of(&union_of_seqs));

            for i in 0..3 {
                let s = extract_variant!((ref seq) -> Sequence);
                println!("{:?} <=> {:?}", s[i], seq_of_unions[i]);
                assert!(s[i].is_subset_of(&seq_of_unions[i]));
                assert!(s[i].is_subset_of(&seq_of_classes[i]));
            }

            assert!(seq.is_subset_of(&seq_of_classes));
            assert!(seq.is_subset_of(&seq_of_unions));
        }

        assert!(union_of_seqs.is_subset_of(&seq_of_classes));
        assert!(union_of_seqs.is_subset_of(&seq_of_unions));

        assert!(! seq_of_classes.is_subset_of(&union_of_seqs));
        assert!(! seq_of_unions.is_subset_of(&union_of_seqs));

    }
}

mod wildcard {
    use std::{char, u8};
    use char_iter;
    #[cfg(feature = "pattern_class")] use std::iter::FromIterator;
    #[cfg(feature = "pattern_class")] use cripes::pattern::Class;
    use cripes::pattern::{ByteOrChar, Element, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_is_subset_of() {
        let wild = Wildcard::<char>;
        assert!(wild.is_superset_of(&'a'));
        assert!(wild.is_superset_of(&Element::Atom('a')));
        assert!(!wild.is_subset_of(&Element::Atom('a')));
        assert!(wild.is_subset_of(&Wildcard));

        assert!(ByteOrChar::Byte(b'H').is_subset_of(&Wildcard));

        #[cfg(feature = "pattern_class")] {
            let cls = Class::from_iter("xyz".chars());
            assert!(wild.is_superset_of(&cls));
        }
    }

    #[test]
    fn test_contains() {
        let wildc = Wildcard::<char>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildc.contains(c));
        }

        let wildbc = Wildcard::<ByteOrChar>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildbc.contains(c.into()));
        }
        for b in 0...u8::MAX {
            assert!(wildbc.contains(b.into()));
        }
    }
}

#[cfg(feature = "pattern_class")]
mod class {
    use std::char;
    use std::iter::FromIterator;

    use char_iter;
    use cripes::pattern::{Class, ClassMember, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf};

    #[test]
    fn test_contains() {
        let range = ClassMember::Range('a', 'z');
        panic_unless_eq!(26, range.len());
        assert!(range.contains('a'));
        assert!(range.contains('z'));
        assert!(! range.contains('A'));
        assert!(! range.contains('Z'));

        let atom = ClassMember::Atom('X');
        panic_unless_eq!(1, atom.len());
        assert!(atom.contains('X'));
        assert!(! atom.contains('x'));

        let class_from_members = Class::from_members([range, atom].into_iter().cloned());
        assert!(class_from_members.contains(&range));
        assert!(class_from_members.contains(&atom));
        panic_unless_eq!(27, class_from_members.len());
        for c in char_iter::new('a', 'z') {
            assert!(class_from_members.contains(c));
        }
        for c in char_iter::new('A', 'W') {
            assert!(! class_from_members.contains(c));
        }
        assert!(class_from_members.contains('X'));
        for c in char_iter::new('Y', 'Z') {
            assert!(! class_from_members.contains(c));
        }

        let cls = Class::from_iter(['a', 'b', 'c'].into_iter().cloned());
        assert_eq!(3, cls.len());

        // contains
        assert!(cls.contains('a'));
        assert!(cls.contains('b'));
        assert!(cls.contains('c'));
        for c in char_iter::new(0 as char, char::MAX) {
            if c == 'a' || c == 'b' || c == 'c' {
                assert!(cls.contains(c));
            } else {
                assert!(!cls.contains(c))
            }
        }
    }

    #[test]
    fn test_is_subset_of() {
        let cls1: Class<char> = FromIterator::from_iter(['x', 'y', 'z'].into_iter().cloned());
        assert!(cls1.is_subset_of(&Wildcard));

        let cls2: Class<char> = FromIterator::from_iter(['x', 'y'].into_iter().cloned());
        assert!(cls2.is_subset_of(&cls1));
        assert!(! cls1.is_subset_of(&cls2));
    }
}
<commit_msg>tests/pattern: check that `Union::new` panics on unit-length vector<commit_after>macro_rules! extract_variant {
    ((ref $input: expr) -> $variant: ident) => ( match $input {
        &Element::$variant(ref x) => x,
        _ => unreachable!() });

    (($input: expr) -> $variant: ident) => ( match $input {
        Element::$variant(x) => x,
        _ => unreachable!() });
}

/// Helper for parsing a regex and returning the inner pattern type.
macro_rules! from_regex {
    ($s: tt -> Element<$T: ty>) => ( ::cripes::pattern::parse_regex::<$T>($s).unwrap() );
    ($s: tt -> $which: ident < $T: ty>) => (extract_variant!((::cripes::pattern::parse_regex::<$T>($s).unwrap()) -> $which));
}

mod sequence {
    use std::iter::{self, FromIterator};
    #[cfg(feature = "pattern_class")]
    use cripes::pattern::Class;
    use cripes::pattern::{Element, Sequence, Union};
    use cripes::pattern::Reduce;
    use cripes::util::set::IsSubsetOf;

    #[test]
    fn test_reduce() {
        let seq_of_seqs = Sequence::from_iter(["abc", "def", "ghi"].iter().map(|s| Sequence::from_iter(s.chars().map(|c| c.into())).into()));
        let flat_seq = Sequence::<char>::from_iter("abcdefghi".chars().map(|c| c.into()));

        assert!(seq_of_seqs != flat_seq);
        assert_eq!(Some(Element::Sequence(flat_seq)), seq_of_seqs.reduce());

        // Empty sequences should reduce to `None`
        assert_eq!(None, Sequence::<char>::from_iter(iter::empty()).reduce());

        // Unit-length sequences should reduce to their only element.
        assert_eq!(Some(Element::Wildcard), Sequence::<char>::from_iter(vec![Element::Wildcard]).reduce());
    }

    #[test]
    fn test_is_subset_of() {
        let unions = (&["abc", "def", "ghi"]).iter().map(|s| Union::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        #[cfg(feature = "pattern_class")]
        let classes = (&["abc", "def", "ghi"]).iter().map(|s| Class::<char>::from_iter(s.chars())).collect::<Vec<_>>();
        let seqs = (&["adg", "beh", "cfi"]).iter().map(|s| Sequence::<char>::from_iter(s.chars().map(|c| c.into()))).collect::<Vec<_>>();
        let useq = Sequence::from_iter(unions.iter().map(|u| Element::Union(u.clone())));
        #[cfg(feature = "pattern_class")]
        let cseq = Sequence::from_iter(classes.iter().map(|c| Element::Class(c.clone())));

        // Every sequence should be a subset of itself.
        assert!(useq.is_subset_of(&useq));
        #[cfg(feature = "pattern_class")] assert!(cseq.is_subset_of(&cseq));

        // `useq` and `cseq` should be subsets of each other.
        #[cfg(feature = "pattern_class")] {
            assert!(useq.is_subset_of(&cseq));
            assert!(cseq.is_subset_of(&useq));
        }

        // each sequence in `seqs` should be a subset of both `useq` and
        // `cseq`.
        for seq in seqs.iter() {
            for i in 0..3 {
                #[cfg(feature = "pattern_class")] assert!(seq[i].is_subset_of(&cseq[i]));
                assert!(seq[i].is_subset_of(&useq[i]));
            }

        #[cfg(feature = "pattern_class")] assert!(seq.is_subset_of(&cseq));
            assert!(seq.is_subset_of(&useq));
        }
    }
}

mod union {
    use std::iter::FromIterator;
    use cripes::pattern::{Element, Union, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf};

    // Passing a single-element vector to `Union::new` should result in
    // a panic.
    #[test]
    #[should_panic]
    fn test_one_is_not_a_union() {
        Union::<char>::new(vec!['a'.into()]);
    }

    #[test]
    fn test_contains() {
        let u = Union::from_iter("abc".chars().map(|c| c.into()));
        assert!(u.contains('a'));
        assert!(u.contains('b'));
        assert!(u.contains('c'));
    }

    #[test]
    fn test_is_subset_of_union() {
        let u1 = Union::<char>::from_iter("abc".chars().map(|c| c.into()));
        let u2 = Union::from_iter("abcdef".chars().map(|c| c.into()));
        assert!(u1.is_subset_of(&u2));
        assert!(! u2.is_subset_of(&u1));

        assert!(u1.is_subset_of(&Wildcard));
        assert!(u2.is_subset_of(&Wildcard));
    }
    #[test]
    fn test_is_subset_of_sequence() {
        let seq_of_classes = from_regex!("[abc][def][ghi]" -> Sequence<char>);
        let seq_of_unions = from_regex!("(a|b|c)(d|e|f)(g|h|i)" -> Sequence<char>);

        let union_of_seqs = from_regex!("adg|beh|cfi" -> Union<char>);
        println!("{:?}", union_of_seqs);
        assert_eq!(3, union_of_seqs.len());

        // check that the union would match each contained sequence.
        for seq in union_of_seqs.iter() {
            println!("{:?}", seq);
            assert!(seq.is_subset_of(&union_of_seqs));

            for i in 0..3 {
                let s = extract_variant!((ref seq) -> Sequence);
                println!("{:?} <=> {:?}", s[i], seq_of_unions[i]);
                assert!(s[i].is_subset_of(&seq_of_unions[i]));
                assert!(s[i].is_subset_of(&seq_of_classes[i]));
            }

            assert!(seq.is_subset_of(&seq_of_classes));
            assert!(seq.is_subset_of(&seq_of_unions));
        }

        assert!(union_of_seqs.is_subset_of(&seq_of_classes));
        assert!(union_of_seqs.is_subset_of(&seq_of_unions));

        assert!(! seq_of_classes.is_subset_of(&union_of_seqs));
        assert!(! seq_of_unions.is_subset_of(&union_of_seqs));

    }
}

mod wildcard {
    use std::{char, u8};
    use char_iter;
    #[cfg(feature = "pattern_class")] use std::iter::FromIterator;
    #[cfg(feature = "pattern_class")] use cripes::pattern::Class;
    use cripes::pattern::{ByteOrChar, Element, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf, IsSupersetOf};

    #[test]
    fn test_is_subset_of() {
        let wild = Wildcard::<char>;
        assert!(wild.is_superset_of(&'a'));
        assert!(wild.is_superset_of(&Element::Atom('a')));
        assert!(!wild.is_subset_of(&Element::Atom('a')));
        assert!(wild.is_subset_of(&Wildcard));

        assert!(ByteOrChar::Byte(b'H').is_subset_of(&Wildcard));

        #[cfg(feature = "pattern_class")] {
            let cls = Class::from_iter("xyz".chars());
            assert!(wild.is_superset_of(&cls));
        }
    }

    #[test]
    fn test_contains() {
        let wildc = Wildcard::<char>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildc.contains(c));
        }

        let wildbc = Wildcard::<ByteOrChar>;
        for c in char_iter::new(0 as char, char::MAX) {
            assert!(wildbc.contains(c.into()));
        }
        for b in 0...u8::MAX {
            assert!(wildbc.contains(b.into()));
        }
    }
}

#[cfg(feature = "pattern_class")]
mod class {
    use std::char;
    use std::iter::FromIterator;

    use char_iter;
    use cripes::pattern::{Class, ClassMember, Wildcard};
    use cripes::util::set::{Contains, IsSubsetOf};

    #[test]
    fn test_contains() {
        let range = ClassMember::Range('a', 'z');
        panic_unless_eq!(26, range.len());
        assert!(range.contains('a'));
        assert!(range.contains('z'));
        assert!(! range.contains('A'));
        assert!(! range.contains('Z'));

        let atom = ClassMember::Atom('X');
        panic_unless_eq!(1, atom.len());
        assert!(atom.contains('X'));
        assert!(! atom.contains('x'));

        let class_from_members = Class::from_members([range, atom].into_iter().cloned());
        assert!(class_from_members.contains(&range));
        assert!(class_from_members.contains(&atom));
        panic_unless_eq!(27, class_from_members.len());
        for c in char_iter::new('a', 'z') {
            assert!(class_from_members.contains(c));
        }
        for c in char_iter::new('A', 'W') {
            assert!(! class_from_members.contains(c));
        }
        assert!(class_from_members.contains('X'));
        for c in char_iter::new('Y', 'Z') {
            assert!(! class_from_members.contains(c));
        }

        let cls = Class::from_iter(['a', 'b', 'c'].into_iter().cloned());
        assert_eq!(3, cls.len());

        // contains
        assert!(cls.contains('a'));
        assert!(cls.contains('b'));
        assert!(cls.contains('c'));
        for c in char_iter::new(0 as char, char::MAX) {
            if c == 'a' || c == 'b' || c == 'c' {
                assert!(cls.contains(c));
            } else {
                assert!(!cls.contains(c))
            }
        }
    }

    #[test]
    fn test_is_subset_of() {
        let cls1: Class<char> = FromIterator::from_iter(['x', 'y', 'z'].into_iter().cloned());
        assert!(cls1.is_subset_of(&Wildcard));

        let cls2: Class<char> = FromIterator::from_iter(['x', 'y'].into_iter().cloned());
        assert!(cls2.is_subset_of(&cls1));
        assert!(! cls1.is_subset_of(&cls2));
    }
}
<|endoftext|>


<commit_before>//! Various utilities for working with Cairo image surfaces.

use std::alloc;
use std::slice;

pub mod iterators;
pub mod shared_surface;
pub mod srgb;

// These two are for Cairo's platform-endian 0xaarrggbb pixels

#[cfg(target_endian = "little")]
use rgb::alt::BGRA8;
#[cfg(target_endian = "little")]
#[allow(clippy::upper_case_acronyms)]
pub type CairoARGB = BGRA8;

#[cfg(target_endian = "big")]
use rgb::alt::ARGB8;
#[cfg(target_endian = "big")]
#[allow(clippy::upper_case_acronyms)]
pub type CairoARGB = ARGB8;

/// GdkPixbuf's endian-independent RGBA8 pixel layout.
pub type GdkPixbufRGBA = rgb::RGBA8;

/// GdkPixbuf's packed RGB pixel layout.
pub type GdkPixbufRGB = rgb::RGB8;

/// Analogous to `rgb::FromSlice`, to convert from `[T]` to `[CairoARGB]`
#[allow(clippy::upper_case_acronyms)]
pub trait AsCairoARGB {
    /// Reinterpret slice as `CairoARGB` pixels.
    fn as_cairo_argb(&self) -> &[CairoARGB];

    /// Reinterpret mutable slice as `CairoARGB` pixels.
    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB];
}

// SAFETY: transmuting from u32 to CairoRGB is based on the following assumptions:
//  * there are no invalid bit representations for ARGB
//  * u32 and ARGB are the same size
//  * u32 is sufficiently aligned
impl AsCairoARGB for [u32] {
    fn as_cairo_argb(&self) -> &[CairoARGB] {
        const LAYOUT_U32: alloc::Layout = alloc::Layout::new::<u32>();
        const LAYOUT_ARGB: alloc::Layout = alloc::Layout::new::<CairoARGB>();
        let _: [(); LAYOUT_U32.size()] = [(); LAYOUT_ARGB.size()];
        let _: [(); 0] = [(); LAYOUT_U32.align() % LAYOUT_ARGB.align()];
        unsafe { slice::from_raw_parts(self.as_ptr() as *const _, self.len()) }
    }

    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB] {
        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len()) }
    }
}

/// Modes which specify how the values of out of bounds pixels are computed.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EdgeMode {
    /// The nearest inbounds pixel value is returned.
    Duplicate,
    /// The image is extended by taking the color values from the opposite of the image.
    ///
    /// Imagine the image being tiled infinitely, with the original image at the origin.
    Wrap,
    /// Zero RGBA values are returned.
    None,
}

/// Trait to convert pixels in various formats to RGBA, for GdkPixbuf.
///
/// GdkPixbuf unconditionally uses RGBA ordering regardless of endianness,
/// but we need to convert to it from Cairo's endian-dependent 0xaarrggbb.
pub trait ToGdkPixbufRGBA {
    fn to_pixbuf_rgba(&self) -> GdkPixbufRGBA;
}

/// Trait to convert pixels in various formats to our own Pixel layout.
pub trait ToPixel {
    fn to_pixel(&self) -> Pixel;
}

/// Trait to convert pixels in various formats to Cairo's endian-dependent 0xaarrggbb.
pub trait ToCairoARGB {
    fn to_cairo_argb(&self) -> CairoARGB;
}

impl ToGdkPixbufRGBA for Pixel {
    #[inline]
    fn to_pixbuf_rgba(&self) -> GdkPixbufRGBA {
        GdkPixbufRGBA {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

impl ToPixel for CairoARGB {
    #[inline]
    fn to_pixel(&self) -> Pixel {
        Pixel {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

impl ToPixel for GdkPixbufRGBA {
    #[inline]
    fn to_pixel(&self) -> Pixel {
        Pixel {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

impl ToPixel for GdkPixbufRGB {
    #[inline]
    fn to_pixel(&self) -> Pixel {
        Pixel {
            r: self.r,
            g: self.g,
            b: self.b,
            a: 255,
        }
    }
}

impl ToCairoARGB for Pixel {
    #[inline]
    fn to_cairo_argb(&self) -> CairoARGB {
        CairoARGB {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

/// Extension methods for `cairo::ImageSurfaceData`.
pub trait ImageSurfaceDataExt {
    /// Sets the pixel at the given coordinates. Assumes the `ARgb32` format.
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32);
}

/// A pixel consisting of R, G, B and A values.
pub type Pixel = rgb::RGBA8;

pub trait PixelOps {
    fn premultiply(self) -> Self;
    fn unpremultiply(self) -> Self;
    fn diff(&self, other: &Self) -> Self;
    fn to_luminance_mask(&self) -> Self;
    fn to_u32(&self) -> u32;
    fn from_u32(x: u32) -> Self;
}

impl PixelOps for Pixel {
    /// Returns an unpremultiplied value of this pixel.
    ///
    /// For a fully transparent pixel, a transparent black pixel will be returned.
    #[inline]
    fn unpremultiply(self) -> Self {
        if self.a == 0 {
            Self {
                r: 0,
                g: 0,
                b: 0,
                a: 0,
            }
        } else {
            let alpha = f32::from(self.a) / 255.0;
            self.map_rgb(|x| ((f32::from(x) / alpha) + 0.5) as u8)
        }
    }

    /// Returns a premultiplied value of this pixel.
    #[inline]
    fn premultiply(self) -> Self {
        let a = self.a as u32;
        self.map_rgb(|x| (((x as u32) * a + 127) / 255) as u8)
    }

    #[inline]
    fn diff(&self, other: &Pixel) -> Pixel {
        self.iter()
            .zip(other.iter())
            .map(|(l, r)| (l as i32 - r as i32).unsigned_abs() as u8)
            .collect()
    }

    /// Returns a 'mask' pixel with only the alpha channel
    ///
    /// Assuming, the pixel is linear RGB (not sRGB)
    /// y = luminance
    /// Y = 0.2126 R + 0.7152 G + 0.0722 B
    /// 1.0 opacity = 255
    ///
    /// When Y = 1.0, pixel for mask should be 0xFFFFFFFF
    /// (you get 1.0 luminance from 255 from R, G and B)
    ///
    /// r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
    /// g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
    /// b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
    ///
    /// This allows for the following expected behaviour:
    ///    (we only care about the most significant byte)
    /// if pixel = 0x00FFFFFF, pixel' = 0xFF......
    /// if pixel = 0x00020202, pixel' = 0x02......

    /// if pixel = 0x00000000, pixel' = 0x00......
    #[inline]
    fn to_luminance_mask(&self) -> Self {
        let r = u32::from(self.r);
        let g = u32::from(self.g);
        let b = u32::from(self.b);

        Self {
            r: 0,
            g: 0,
            b: 0,
            a: (((r * 14042 + g * 47240 + b * 4769) * 255) >> 24) as u8,
        }
    }

    /// Returns the pixel value as a `u32`, in the same format as `cairo::Format::ARgb32`.
    #[inline]
    fn to_u32(&self) -> u32 {
        (u32::from(self.a) << 24)
            | (u32::from(self.r) << 16)
            | (u32::from(self.g) << 8)
            | u32::from(self.b)
    }

    /// Converts a `u32` in the same format as `cairo::Format::ARgb32` into a `Pixel`.
    #[inline]
    fn from_u32(x: u32) -> Self {
        Self {
            r: ((x >> 16) & 0xFF) as u8,
            g: ((x >> 8) & 0xFF) as u8,
            b: (x & 0xFF) as u8,
            a: ((x >> 24) & 0xFF) as u8,
        }
    }
}

impl<'a> ImageSurfaceDataExt for cairo::ImageSurfaceData<'a> {
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        let this: &mut [u8] = &mut *self;
        // SAFETY: this code assumes that cairo image surface data is correctly
        // aligned for u32. This assumption is justified by the Cairo docs,
        // which say this:
        //
        // https://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create-for-data
        //
        // > This pointer must be suitably aligned for any kind of variable,
        // > (for example, a pointer returned by malloc).
        #[allow(clippy::cast_ptr_alignment)]
        let this: &mut [u32] =
            unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut u32, this.len() / 4) };
        this.set_pixel(stride, pixel, x, y);
    }
}
impl<'a> ImageSurfaceDataExt for [u8] {
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        use byteorder::{NativeEndian, WriteBytesExt};
        let mut this = &mut self[y as usize * stride + x as usize * 4..];
        this.write_u32::<NativeEndian>(pixel.to_u32())
            .expect("out of bounds pixel access on [u8]");
    }
}
impl<'a> ImageSurfaceDataExt for [u32] {
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        self[(y as usize * stride + x as usize * 4) / 4] = pixel.to_u32();
    }
}

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

    #[test]
    fn pixel_diff() {
        let a = Pixel::new(0x10, 0x20, 0xf0, 0x40);
        assert_eq!(a, a.diff(&Pixel::default()));
        let b = Pixel::new(0x50, 0xff, 0x20, 0x10);
        assert_eq!(a.diff(&b), Pixel::new(0x40, 0xdf, 0xd0, 0x30));
    }

    // Floating-point reference implementation
    fn premultiply_float(pixel: Pixel) -> Pixel {
        let alpha = f64::from(pixel.a) / 255.0;
        pixel.map_rgb(|x| ((f64::from(x) * alpha) + 0.5) as u8)
    }

    prop_compose! {
        fn arbitrary_pixel()(a: u8, r: u8, g: u8, b: u8) -> Pixel {
            Pixel { r, g, b, a }
        }
    }

    proptest! {
        #[test]
        fn pixel_premultiply(pixel in arbitrary_pixel()) {
            prop_assert_eq!(pixel.premultiply(), premultiply_float(pixel));
        }

        #[test]
        fn pixel_unpremultiply(pixel in arbitrary_pixel()) {
            let roundtrip = pixel.premultiply().unpremultiply();
            if pixel.a == 0 {
                prop_assert_eq!(roundtrip, Pixel::default());
            } else {
                // roundtrip can't be perfect, the accepted error depends on alpha
                let tolerance = 0xff / pixel.a;
                let diff = roundtrip.diff(&pixel);
                prop_assert!(diff.r <= tolerance, "red component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.g <= tolerance, "green component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.b <= tolerance, "blue component value differs by more than {}: {:?}", tolerance, roundtrip);

                prop_assert_eq!(pixel.a, roundtrip.a);
            }
       }
    }
}
<commit_msg>clippy: remove unused lifetimes<commit_after>//! Various utilities for working with Cairo image surfaces.

use std::alloc;
use std::slice;

pub mod iterators;
pub mod shared_surface;
pub mod srgb;

// These two are for Cairo's platform-endian 0xaarrggbb pixels

#[cfg(target_endian = "little")]
use rgb::alt::BGRA8;
#[cfg(target_endian = "little")]
#[allow(clippy::upper_case_acronyms)]
pub type CairoARGB = BGRA8;

#[cfg(target_endian = "big")]
use rgb::alt::ARGB8;
#[cfg(target_endian = "big")]
#[allow(clippy::upper_case_acronyms)]
pub type CairoARGB = ARGB8;

/// GdkPixbuf's endian-independent RGBA8 pixel layout.
pub type GdkPixbufRGBA = rgb::RGBA8;

/// GdkPixbuf's packed RGB pixel layout.
pub type GdkPixbufRGB = rgb::RGB8;

/// Analogous to `rgb::FromSlice`, to convert from `[T]` to `[CairoARGB]`
#[allow(clippy::upper_case_acronyms)]
pub trait AsCairoARGB {
    /// Reinterpret slice as `CairoARGB` pixels.
    fn as_cairo_argb(&self) -> &[CairoARGB];

    /// Reinterpret mutable slice as `CairoARGB` pixels.
    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB];
}

// SAFETY: transmuting from u32 to CairoRGB is based on the following assumptions:
//  * there are no invalid bit representations for ARGB
//  * u32 and ARGB are the same size
//  * u32 is sufficiently aligned
impl AsCairoARGB for [u32] {
    fn as_cairo_argb(&self) -> &[CairoARGB] {
        const LAYOUT_U32: alloc::Layout = alloc::Layout::new::<u32>();
        const LAYOUT_ARGB: alloc::Layout = alloc::Layout::new::<CairoARGB>();
        let _: [(); LAYOUT_U32.size()] = [(); LAYOUT_ARGB.size()];
        let _: [(); 0] = [(); LAYOUT_U32.align() % LAYOUT_ARGB.align()];
        unsafe { slice::from_raw_parts(self.as_ptr() as *const _, self.len()) }
    }

    fn as_cairo_argb_mut(&mut self) -> &mut [CairoARGB] {
        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len()) }
    }
}

/// Modes which specify how the values of out of bounds pixels are computed.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EdgeMode {
    /// The nearest inbounds pixel value is returned.
    Duplicate,
    /// The image is extended by taking the color values from the opposite of the image.
    ///
    /// Imagine the image being tiled infinitely, with the original image at the origin.
    Wrap,
    /// Zero RGBA values are returned.
    None,
}

/// Trait to convert pixels in various formats to RGBA, for GdkPixbuf.
///
/// GdkPixbuf unconditionally uses RGBA ordering regardless of endianness,
/// but we need to convert to it from Cairo's endian-dependent 0xaarrggbb.
pub trait ToGdkPixbufRGBA {
    fn to_pixbuf_rgba(&self) -> GdkPixbufRGBA;
}

/// Trait to convert pixels in various formats to our own Pixel layout.
pub trait ToPixel {
    fn to_pixel(&self) -> Pixel;
}

/// Trait to convert pixels in various formats to Cairo's endian-dependent 0xaarrggbb.
pub trait ToCairoARGB {
    fn to_cairo_argb(&self) -> CairoARGB;
}

impl ToGdkPixbufRGBA for Pixel {
    #[inline]
    fn to_pixbuf_rgba(&self) -> GdkPixbufRGBA {
        GdkPixbufRGBA {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

impl ToPixel for CairoARGB {
    #[inline]
    fn to_pixel(&self) -> Pixel {
        Pixel {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

impl ToPixel for GdkPixbufRGBA {
    #[inline]
    fn to_pixel(&self) -> Pixel {
        Pixel {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

impl ToPixel for GdkPixbufRGB {
    #[inline]
    fn to_pixel(&self) -> Pixel {
        Pixel {
            r: self.r,
            g: self.g,
            b: self.b,
            a: 255,
        }
    }
}

impl ToCairoARGB for Pixel {
    #[inline]
    fn to_cairo_argb(&self) -> CairoARGB {
        CairoARGB {
            r: self.r,
            g: self.g,
            b: self.b,
            a: self.a,
        }
    }
}

/// Extension methods for `cairo::ImageSurfaceData`.
pub trait ImageSurfaceDataExt {
    /// Sets the pixel at the given coordinates. Assumes the `ARgb32` format.
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32);
}

/// A pixel consisting of R, G, B and A values.
pub type Pixel = rgb::RGBA8;

pub trait PixelOps {
    fn premultiply(self) -> Self;
    fn unpremultiply(self) -> Self;
    fn diff(&self, other: &Self) -> Self;
    fn to_luminance_mask(&self) -> Self;
    fn to_u32(&self) -> u32;
    fn from_u32(x: u32) -> Self;
}

impl PixelOps for Pixel {
    /// Returns an unpremultiplied value of this pixel.
    ///
    /// For a fully transparent pixel, a transparent black pixel will be returned.
    #[inline]
    fn unpremultiply(self) -> Self {
        if self.a == 0 {
            Self {
                r: 0,
                g: 0,
                b: 0,
                a: 0,
            }
        } else {
            let alpha = f32::from(self.a) / 255.0;
            self.map_rgb(|x| ((f32::from(x) / alpha) + 0.5) as u8)
        }
    }

    /// Returns a premultiplied value of this pixel.
    #[inline]
    fn premultiply(self) -> Self {
        let a = self.a as u32;
        self.map_rgb(|x| (((x as u32) * a + 127) / 255) as u8)
    }

    #[inline]
    fn diff(&self, other: &Pixel) -> Pixel {
        self.iter()
            .zip(other.iter())
            .map(|(l, r)| (l as i32 - r as i32).unsigned_abs() as u8)
            .collect()
    }

    /// Returns a 'mask' pixel with only the alpha channel
    ///
    /// Assuming, the pixel is linear RGB (not sRGB)
    /// y = luminance
    /// Y = 0.2126 R + 0.7152 G + 0.0722 B
    /// 1.0 opacity = 255
    ///
    /// When Y = 1.0, pixel for mask should be 0xFFFFFFFF
    /// (you get 1.0 luminance from 255 from R, G and B)
    ///
    /// r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
    /// g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
    /// b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
    ///
    /// This allows for the following expected behaviour:
    ///    (we only care about the most significant byte)
    /// if pixel = 0x00FFFFFF, pixel' = 0xFF......
    /// if pixel = 0x00020202, pixel' = 0x02......

    /// if pixel = 0x00000000, pixel' = 0x00......
    #[inline]
    fn to_luminance_mask(&self) -> Self {
        let r = u32::from(self.r);
        let g = u32::from(self.g);
        let b = u32::from(self.b);

        Self {
            r: 0,
            g: 0,
            b: 0,
            a: (((r * 14042 + g * 47240 + b * 4769) * 255) >> 24) as u8,
        }
    }

    /// Returns the pixel value as a `u32`, in the same format as `cairo::Format::ARgb32`.
    #[inline]
    fn to_u32(&self) -> u32 {
        (u32::from(self.a) << 24)
            | (u32::from(self.r) << 16)
            | (u32::from(self.g) << 8)
            | u32::from(self.b)
    }

    /// Converts a `u32` in the same format as `cairo::Format::ARgb32` into a `Pixel`.
    #[inline]
    fn from_u32(x: u32) -> Self {
        Self {
            r: ((x >> 16) & 0xFF) as u8,
            g: ((x >> 8) & 0xFF) as u8,
            b: (x & 0xFF) as u8,
            a: ((x >> 24) & 0xFF) as u8,
        }
    }
}

impl<'a> ImageSurfaceDataExt for cairo::ImageSurfaceData<'a> {
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        let this: &mut [u8] = &mut *self;
        // SAFETY: this code assumes that cairo image surface data is correctly
        // aligned for u32. This assumption is justified by the Cairo docs,
        // which say this:
        //
        // https://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-image-surface-create-for-data
        //
        // > This pointer must be suitably aligned for any kind of variable,
        // > (for example, a pointer returned by malloc).
        #[allow(clippy::cast_ptr_alignment)]
        let this: &mut [u32] =
            unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut u32, this.len() / 4) };
        this.set_pixel(stride, pixel, x, y);
    }
}
impl ImageSurfaceDataExt for [u8] {
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        use byteorder::{NativeEndian, WriteBytesExt};
        let mut this = &mut self[y as usize * stride + x as usize * 4..];
        this.write_u32::<NativeEndian>(pixel.to_u32())
            .expect("out of bounds pixel access on [u8]");
    }
}
impl ImageSurfaceDataExt for [u32] {
    #[inline]
    fn set_pixel(&mut self, stride: usize, pixel: Pixel, x: u32, y: u32) {
        self[(y as usize * stride + x as usize * 4) / 4] = pixel.to_u32();
    }
}

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

    #[test]
    fn pixel_diff() {
        let a = Pixel::new(0x10, 0x20, 0xf0, 0x40);
        assert_eq!(a, a.diff(&Pixel::default()));
        let b = Pixel::new(0x50, 0xff, 0x20, 0x10);
        assert_eq!(a.diff(&b), Pixel::new(0x40, 0xdf, 0xd0, 0x30));
    }

    // Floating-point reference implementation
    fn premultiply_float(pixel: Pixel) -> Pixel {
        let alpha = f64::from(pixel.a) / 255.0;
        pixel.map_rgb(|x| ((f64::from(x) * alpha) + 0.5) as u8)
    }

    prop_compose! {
        fn arbitrary_pixel()(a: u8, r: u8, g: u8, b: u8) -> Pixel {
            Pixel { r, g, b, a }
        }
    }

    proptest! {
        #[test]
        fn pixel_premultiply(pixel in arbitrary_pixel()) {
            prop_assert_eq!(pixel.premultiply(), premultiply_float(pixel));
        }

        #[test]
        fn pixel_unpremultiply(pixel in arbitrary_pixel()) {
            let roundtrip = pixel.premultiply().unpremultiply();
            if pixel.a == 0 {
                prop_assert_eq!(roundtrip, Pixel::default());
            } else {
                // roundtrip can't be perfect, the accepted error depends on alpha
                let tolerance = 0xff / pixel.a;
                let diff = roundtrip.diff(&pixel);
                prop_assert!(diff.r <= tolerance, "red component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.g <= tolerance, "green component value differs by more than {}: {:?}", tolerance, roundtrip);
                prop_assert!(diff.b <= tolerance, "blue component value differs by more than {}: {:?}", tolerance, roundtrip);

                prop_assert_eq!(pixel.a, roundtrip.a);
            }
       }
    }
}
<|endoftext|>