<commit_before>use std::fs;
use std::ops;
use std::path::{Path, PathBuf};

use anyhow::{anyhow, Context, Result};
use sha2::{Digest, Sha256};
use url::Url;

use crate::config::PgpPublicKey;
use crate::dist::notifications::*;
use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;

const UPDATE_HASH_LEN: usize = 20;

#[derive(Copy, Clone)]
pub struct DownloadCfg<'a> {
    pub dist_root: &'a str,
    pub temp_cfg: &'a temp::Cfg,
    pub download_dir: &'a PathBuf,
    pub notify_handler: &'a dyn Fn(Notification<'_>),
    pub pgp_keys: &'a [PgpPublicKey],
}

pub struct File {
    path: PathBuf,
}

impl ops::Deref for File {
    type Target = Path;

    fn deref(&self) -> &Path {
        self.path.as_path()
    }
}

impl<'a> DownloadCfg<'a> {
    /// Downloads a file, validating its hash, and resuming interrupted downloads
    /// Partial downloads are stored in `self.download_dir`, keyed by hash. If the
    /// target file already exists, then the hash is checked and it is returned
    /// immediately without re-downloading.
    pub fn download(&self, url: &Url, hash: &str) -> Result<File> {
        utils::ensure_dir_exists(
            "Download Directory",
            &self.download_dir,
            &self.notify_handler,
        )?;
        let target_file = self.download_dir.join(Path::new(hash));

        if target_file.exists() {
            let cached_result = file_hash(&target_file, self.notify_handler)?;
            if hash == cached_result {
                (self.notify_handler)(Notification::FileAlreadyDownloaded);
                (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));
                return Ok(File { path: target_file });
            } else {
                (self.notify_handler)(Notification::CachedFileChecksumFailed);
                fs::remove_file(&target_file).context("cleaning up previous download")?;
            }
        }

        let partial_file_path = target_file.with_file_name(
            target_file
                .file_name()
                .map(|s| s.to_str().unwrap_or("_"))
                .unwrap_or("_")
                .to_owned()
                + ".partial",
        );

        let partial_file_existed = partial_file_path.exists();

        let mut hasher = Sha256::new();

        if let Err(e) = utils::download_file_with_resume(
            &url,
            &partial_file_path,
            Some(&mut hasher),
            true,
            &|n| (self.notify_handler)(n.into()),
        ) {
            let err = Err(e);
            if partial_file_existed {
                return err.context(RustupError::BrokenPartialFile);
            } else {
                return err;
            }
        };

        let actual_hash = format!("{:x}", hasher.finalize());

        if hash != actual_hash {
            // Incorrect hash
            if partial_file_existed {
                self.clean(&[hash.to_string() + &".partial".to_string()])?;
                Err(anyhow!(RustupError::BrokenPartialFile))
            } else {
                Err(RustupError::ChecksumFailed {
                    url: url.to_string(),
                    expected: hash.to_string(),
                    calculated: actual_hash,
                }
                .into())
            }
        } else {
            (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));

            utils::rename_file(
                "downloaded",
                &partial_file_path,
                &target_file,
                self.notify_handler,
            )?;
            Ok(File { path: target_file })
        }
    }

    pub fn clean(&self, hashes: &[String]) -> Result<()> {
        for hash in hashes.iter() {
            let used_file = self.download_dir.join(hash);
            if self.download_dir.join(&used_file).exists() {
                fs::remove_file(used_file).context("cleaning up cached downloads")?;
            }
        }
        Ok(())
    }

    fn download_hash(&self, url: &str) -> Result<String> {
        let hash_url = utils::parse_url(&(url.to_owned() + ".sha256"))?;
        let hash_file = self.temp_cfg.new_file()?;

        utils::download_file(&hash_url, &hash_file, None, &|n| {
            (self.notify_handler)(n.into())
        })?;

        utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())
    }

    fn download_signature(&self, url: &str) -> Result<String> {
        let sig_url = utils::parse_url(&(url.to_owned() + ".asc"))?;
        let sig_file = self.temp_cfg.new_file()?;

        utils::download_file(&sig_url, &sig_file, None, &|n| {
            (self.notify_handler)(n.into())
        })?;

        utils::read_file("signature", &sig_file)
    }

    fn check_signature(&self, url: &str, file: &temp::File<'_>) -> Result<&PgpPublicKey> {
        assert!(
            !self.pgp_keys.is_empty(),
            "At least the builtin key must be present"
        );

        let signature = self
            .download_signature(url)
            .with_context(|| format!("failed to download signature file {}", url))?;

        let file_path: &Path = &file;
        let content = std::fs::File::open(file_path).with_context(|| RustupError::ReadingFile {
            name: "channel data",
            path: PathBuf::from(file_path),
        })?;

        let sig_result =
            crate::dist::signatures::verify_signature(content, &signature, &self.pgp_keys)?;
        if let Some(keyidx) = sig_result {
            let key = &self.pgp_keys[keyidx];
            Ok(key)
        } else {
            Err(anyhow!(format!(
                "signature verification failed for {}",
                url
            )))
        }
    }

    /// Downloads a file, sourcing its hash from the same url with a `.sha256` suffix.
    /// If `update_hash` is present, then that will be compared to the downloaded hash,
    /// and if they match, the download is skipped.
    /// Verifies the signature found at the same url with a `.asc` suffix, and prints a
    /// warning when the signature does not verify, or is not found.
    pub fn download_and_check(
        &self,
        url_str: &str,
        update_hash: Option<&Path>,
        ext: &str,
    ) -> Result<Option<(temp::File<'a>, String)>> {
        let hash = self.download_hash(url_str)?;
        let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();

        if let Some(hash_file) = update_hash {
            if utils::is_file(hash_file) {
                if let Ok(contents) = utils::read_file("update hash", hash_file) {
                    if contents == partial_hash {
                        // Skip download, update hash matches
                        return Ok(None);
                    }
                } else {
                    (self.notify_handler)(Notification::CantReadUpdateHash(hash_file));
                }
            } else {
                (self.notify_handler)(Notification::NoUpdateHash(hash_file));
            }
        }

        let url = utils::parse_url(url_str)?;
        let file = self.temp_cfg.new_file_with_ext("", ext)?;

        let mut hasher = Sha256::new();
        utils::download_file(&url, &file, Some(&mut hasher), &|n| {
            (self.notify_handler)(n.into())
        })?;
        let actual_hash = format!("{:x}", hasher.finalize());

        if hash != actual_hash {
            // Incorrect hash
            return Err(RustupError::ChecksumFailed {
                url: url_str.to_owned(),
                expected: hash,
                calculated: actual_hash,
            }
            .into());
        } else {
            (self.notify_handler)(Notification::ChecksumValid(url_str));
        }

        // No signatures for tarballs for now.
        if !url_str.ends_with(".tar.gz") && !url_str.ends_with(".tar.xz") {
            match self.check_signature(&url_str, &file) {
                Ok(key) => (self.notify_handler)(Notification::SignatureValid(url_str, key)),
                Err(_) => (self.notify_handler)(Notification::SignatureInvalid(url_str)),
            }
        }

        Ok(Some((file, partial_hash)))
    }
}

fn file_hash(path: &Path, notify_handler: &dyn Fn(Notification<'_>)) -> Result<String> {
    let mut hasher = Sha256::new();
    let notification_converter = |notification: crate::utils::Notification<'_>| {
        notify_handler(notification.into());
    };
    let mut downloaded = utils::FileReaderWithProgress::new_file(&path, &notification_converter)?;
    use std::io::Read;
    let mut buf = vec![0; 32768];
    while let Ok(n) = downloaded.read(&mut buf) {
        if n == 0 {
            break;
        }
        hasher.update(&buf[..n]);
    }

    Ok(format!("{:x}", hasher.finalize()))
}
<commit_msg>Edit `rustup::dist::download::DownloadCfg` docs<commit_after>use std::fs;
use std::ops;
use std::path::{Path, PathBuf};

use anyhow::{anyhow, Context, Result};
use sha2::{Digest, Sha256};
use url::Url;

use crate::config::PgpPublicKey;
use crate::dist::notifications::*;
use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;

const UPDATE_HASH_LEN: usize = 20;

#[derive(Copy, Clone)]
pub struct DownloadCfg<'a> {
    pub dist_root: &'a str,
    pub temp_cfg: &'a temp::Cfg,
    pub download_dir: &'a PathBuf,
    pub notify_handler: &'a dyn Fn(Notification<'_>),
    pub pgp_keys: &'a [PgpPublicKey],
}

pub struct File {
    path: PathBuf,
}

impl ops::Deref for File {
    type Target = Path;

    fn deref(&self) -> &Path {
        self.path.as_path()
    }
}

impl<'a> DownloadCfg<'a> {
    /// Downloads a file and validates its hash. Resumes interrupted downloads.
    /// Partial downloads are stored in `self.download_dir`, keyed by hash. If the
    /// target file already exists, then the hash is checked and it is returned
    /// immediately without re-downloading.
    pub fn download(&self, url: &Url, hash: &str) -> Result<File> {
        utils::ensure_dir_exists(
            "Download Directory",
            &self.download_dir,
            &self.notify_handler,
        )?;
        let target_file = self.download_dir.join(Path::new(hash));

        if target_file.exists() {
            let cached_result = file_hash(&target_file, self.notify_handler)?;
            if hash == cached_result {
                (self.notify_handler)(Notification::FileAlreadyDownloaded);
                (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));
                return Ok(File { path: target_file });
            } else {
                (self.notify_handler)(Notification::CachedFileChecksumFailed);
                fs::remove_file(&target_file).context("cleaning up previous download")?;
            }
        }

        let partial_file_path = target_file.with_file_name(
            target_file
                .file_name()
                .map(|s| s.to_str().unwrap_or("_"))
                .unwrap_or("_")
                .to_owned()
                + ".partial",
        );

        let partial_file_existed = partial_file_path.exists();

        let mut hasher = Sha256::new();

        if let Err(e) = utils::download_file_with_resume(
            &url,
            &partial_file_path,
            Some(&mut hasher),
            true,
            &|n| (self.notify_handler)(n.into()),
        ) {
            let err = Err(e);
            if partial_file_existed {
                return err.context(RustupError::BrokenPartialFile);
            } else {
                return err;
            }
        };

        let actual_hash = format!("{:x}", hasher.finalize());

        if hash != actual_hash {
            // Incorrect hash
            if partial_file_existed {
                self.clean(&[hash.to_string() + &".partial".to_string()])?;
                Err(anyhow!(RustupError::BrokenPartialFile))
            } else {
                Err(RustupError::ChecksumFailed {
                    url: url.to_string(),
                    expected: hash.to_string(),
                    calculated: actual_hash,
                }
                .into())
            }
        } else {
            (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));

            utils::rename_file(
                "downloaded",
                &partial_file_path,
                &target_file,
                self.notify_handler,
            )?;
            Ok(File { path: target_file })
        }
    }

    pub fn clean(&self, hashes: &[String]) -> Result<()> {
        for hash in hashes.iter() {
            let used_file = self.download_dir.join(hash);
            if self.download_dir.join(&used_file).exists() {
                fs::remove_file(used_file).context("cleaning up cached downloads")?;
            }
        }
        Ok(())
    }

    fn download_hash(&self, url: &str) -> Result<String> {
        let hash_url = utils::parse_url(&(url.to_owned() + ".sha256"))?;
        let hash_file = self.temp_cfg.new_file()?;

        utils::download_file(&hash_url, &hash_file, None, &|n| {
            (self.notify_handler)(n.into())
        })?;

        utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())
    }

    fn download_signature(&self, url: &str) -> Result<String> {
        let sig_url = utils::parse_url(&(url.to_owned() + ".asc"))?;
        let sig_file = self.temp_cfg.new_file()?;

        utils::download_file(&sig_url, &sig_file, None, &|n| {
            (self.notify_handler)(n.into())
        })?;

        utils::read_file("signature", &sig_file)
    }

    fn check_signature(&self, url: &str, file: &temp::File<'_>) -> Result<&PgpPublicKey> {
        assert!(
            !self.pgp_keys.is_empty(),
            "At least the builtin key must be present"
        );

        let signature = self
            .download_signature(url)
            .with_context(|| format!("failed to download signature file {}", url))?;

        let file_path: &Path = &file;
        let content = std::fs::File::open(file_path).with_context(|| RustupError::ReadingFile {
            name: "channel data",
            path: PathBuf::from(file_path),
        })?;

        let sig_result =
            crate::dist::signatures::verify_signature(content, &signature, &self.pgp_keys)?;
        if let Some(keyidx) = sig_result {
            let key = &self.pgp_keys[keyidx];
            Ok(key)
        } else {
            Err(anyhow!(format!(
                "signature verification failed for {}",
                url
            )))
        }
    }

    /// Downloads a file, sourcing its hash from the same url with a `.sha256` suffix.
    /// If `update_hash` is present, then that will be compared to the downloaded hash,
    /// and if they match, the download is skipped.
    /// Verifies the signature found at the same url with a `.asc` suffix, and prints a
    /// warning when the signature does not verify, or is not found.
    pub fn download_and_check(
        &self,
        url_str: &str,
        update_hash: Option<&Path>,
        ext: &str,
    ) -> Result<Option<(temp::File<'a>, String)>> {
        let hash = self.download_hash(url_str)?;
        let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();

        if let Some(hash_file) = update_hash {
            if utils::is_file(hash_file) {
                if let Ok(contents) = utils::read_file("update hash", hash_file) {
                    if contents == partial_hash {
                        // Skip download, update hash matches
                        return Ok(None);
                    }
                } else {
                    (self.notify_handler)(Notification::CantReadUpdateHash(hash_file));
                }
            } else {
                (self.notify_handler)(Notification::NoUpdateHash(hash_file));
            }
        }

        let url = utils::parse_url(url_str)?;
        let file = self.temp_cfg.new_file_with_ext("", ext)?;

        let mut hasher = Sha256::new();
        utils::download_file(&url, &file, Some(&mut hasher), &|n| {
            (self.notify_handler)(n.into())
        })?;
        let actual_hash = format!("{:x}", hasher.finalize());

        if hash != actual_hash {
            // Incorrect hash
            return Err(RustupError::ChecksumFailed {
                url: url_str.to_owned(),
                expected: hash,
                calculated: actual_hash,
            }
            .into());
        } else {
            (self.notify_handler)(Notification::ChecksumValid(url_str));
        }

        // No signatures for tarballs for now.
        if !url_str.ends_with(".tar.gz") && !url_str.ends_with(".tar.xz") {
            match self.check_signature(&url_str, &file) {
                Ok(key) => (self.notify_handler)(Notification::SignatureValid(url_str, key)),
                Err(_) => (self.notify_handler)(Notification::SignatureInvalid(url_str)),
            }
        }

        Ok(Some((file, partial_hash)))
    }
}

fn file_hash(path: &Path, notify_handler: &dyn Fn(Notification<'_>)) -> Result<String> {
    let mut hasher = Sha256::new();
    let notification_converter = |notification: crate::utils::Notification<'_>| {
        notify_handler(notification.into());
    };
    let mut downloaded = utils::FileReaderWithProgress::new_file(&path, &notification_converter)?;
    use std::io::Read;
    let mut buf = vec![0; 32768];
    while let Ok(n) = downloaded.read(&mut buf) {
        if n == 0 {
            break;
        }
        hasher.update(&buf[..n]);
    }

    Ok(format!("{:x}", hasher.finalize()))
}
<|endoftext|>


<commit_before>// aux-build:dep.rs

extern crate dep;

fn main() {
    dep::foo();
}
<commit_msg>re-disable aux_test -- it passes here, but not on Travis<commit_after>// aux-build:dep.rs
// ignore-cross-compile

extern crate dep;

fn main() {
    dep::foo();
}
<|endoftext|>


<commit_before>extern crate walkdir;
extern crate chrono;

use std::path;
use self::walkdir::{WalkDir, DirEntry, Error as WalkDirError};
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use self::chrono::{DateTime, UTC};
use std::error::Error;
use std::str::FromStr;
use std::net::SocketAddrV4;

#[derive(Debug)]
pub struct ELBRecord {
    timestamp: DateTime<UTC>,
    elb_name: String,
    client_address: SocketAddrV4,
    backend_address: SocketAddrV4,
    request_processing_time: f32,
    backend_processing_time: f32,
    response_processing_time: f32,
    elb_status_code: u16,
    backend_status_code: u16,
    received_bytes: u64,
    sent_bytes: u64,
    request_method: String,
    request_url: String,
    request_http_version: String
}

pub fn file_list(dir: &path::Path, filenames: &mut Vec<DirEntry>) -> Result<usize, WalkDirError> {
    for entry in WalkDir::new(dir).min_depth(1) {
        match entry {
            Err(err) => return Err(err),
            Ok(entry) => filenames.push(entry),
        }
    }
    Ok(filenames.len())
}

//TODO Reconsider logging based on the standard interfaces included with Rust.
//TODO We really want to accept a function to handle the parsed lines.
pub fn process_files(runtime_context: &::RuntimeContext, filenames: Vec<walkdir::DirEntry>) -> usize {
    let debug = runtime_context.debug;
    let mut record_count = 0;
    for filename in filenames {
        debug!(debug, "Processing file {}.", filename.path().display());
        match File::open(filename.path()) {
            Ok(file) => {
                let buffered_file = BufReader::new(&file);
                let recs: Vec<_> = buffered_file.lines()
                    .map(|possible_line| {
                        match possible_line {
                            Ok(record) => parse_record(record),

                            Err(_) => {
                                Err(ParsingErrors {
                                    record: "".to_string(),
                                    errors: vec![ELBRecordParsingErrors::LineReadError]
                                })
                            }
                        }
                    })
                    .collect();
                record_count += recs.len();
                debug!(debug, "Found {} records in file {}.", recs.len(), filename.path().display());
            },
            Err(e) => {
                println!("ERROR: {}", e);
            }
        }
    }

    record_count
}

//TODO Take a look at the error handling once again.  This doesn't feel write given code you've read.

#[derive(Debug)]
pub struct ParsingErrors {
    record: String,
    errors: Vec<ELBRecordParsingErrors>,
}

#[derive(Debug, PartialEq)]
enum ELBRecordParsingErrors {
    MalformedRecord,
    ParsingError { property: &'static str, description: String },
    LineReadError
}

const ELB_RECORD_FIELD_COUNT: usize = 14;
const TIMESTAMP: &'static str = "timestamp";
const CLIENT_ADDRESS: &'static str = "client address";
const BACKEND_ADDRESS: &'static str = "backend address";
const REQUEST_PROCESSING_TIME: &'static str = "request processing time";
const BACKEND_PROCESSING_TIME: &'static str = "backend processing time";
const RESPONSE_PROCESSING_TIME: &'static str = "response processing time";
const ELB_STATUS_CODE: &'static str = "ELB status code";
const BE_STATUS_CODE: &'static str = "backend status code";
const RECEIVED_BYTES: &'static str = "received bytes";
const SENT_BYTES: &'static str = "sent bytes";

#[derive(Debug, PartialEq)]
enum ELBRecordFields {
    Timestamp = 0,
    ELBName,
    ClientAddress,
    BackendAddress,
    RequestProcessingTime,
    BackendProcessingTime,
    ResponseProcessingTime,
    ELBStatusCode,
    BackendStatusCode,
    ReceivedBytes,
    SentBytes,
    RequestMethod,
    RequestURL,
    RequestHTTPVersion
}

impl ELBRecordFields {

  fn idx(self) -> usize {
    self as usize
  }
}

pub fn parse_record(record: String) -> Result<Box<ELBRecord>, ParsingErrors> {
    let mut errors: Vec<ELBRecordParsingErrors> = Vec::new();

    {
        //record is borrowed by the split method which means ownership of record cannot be
        //transferred to ParsingErrors until the borrow is complete.
        //Scoping this section of code seems more readable than creating a separate function
        //just to mitigate the borrow.
        let split_line: Vec<&str> = record.split(' ').collect();
        if split_line.len() != ELB_RECORD_FIELD_COUNT {
            errors.push(ELBRecordParsingErrors::MalformedRecord);
            None
        } else {
            let ts = parse_property::<DateTime<UTC>>(split_line[ELBRecordFields::Timestamp.idx()], TIMESTAMP, &mut errors);
            let clnt_addr = parse_property::<SocketAddrV4>(split_line[ELBRecordFields::ClientAddress.idx()], CLIENT_ADDRESS, &mut errors);
            let be_addr = parse_property::<SocketAddrV4>(split_line[ELBRecordFields::BackendAddress.idx()], BACKEND_ADDRESS, &mut errors);
            let req_proc_time = parse_property::<f32>(split_line[ELBRecordFields::RequestProcessingTime.idx()], REQUEST_PROCESSING_TIME, &mut errors);
            let be_proc_time = parse_property::<f32>(split_line[ELBRecordFields::BackendProcessingTime.idx()], BACKEND_PROCESSING_TIME, &mut errors);
            let res_proc_time = parse_property::<f32>(split_line[ELBRecordFields::ResponseProcessingTime.idx()], RESPONSE_PROCESSING_TIME, &mut errors);
            let elb_sc = parse_property::<u16>(split_line[ELBRecordFields::ELBStatusCode.idx()], ELB_STATUS_CODE, &mut errors);
            let be_sc = parse_property::<u16>(split_line[ELBRecordFields::BackendStatusCode.idx()], BE_STATUS_CODE, &mut errors);
            let bytes_received = parse_property::<u64>(split_line[ELBRecordFields::ReceivedBytes.idx()], RECEIVED_BYTES, &mut errors);
            let bytes_sent = parse_property::<u64>(split_line[ELBRecordFields::SentBytes.idx()], SENT_BYTES, &mut errors);

            if errors.is_empty() {
                //If errors is empty it is more than likely parsing was successful and unwrap is safe.
                Some(
                    ELBRecord {
                        timestamp: ts.unwrap(),
                        elb_name: split_line[ELBRecordFields::ELBName.idx()].to_string(),
                        client_address: clnt_addr.unwrap(),
                        backend_address: be_addr.unwrap(),
                        request_processing_time: req_proc_time.unwrap(),
                        backend_processing_time: be_proc_time.unwrap(),
                        response_processing_time: res_proc_time.unwrap(),
                        elb_status_code: elb_sc.unwrap(),
                        backend_status_code: be_sc.unwrap(),
                        received_bytes: bytes_received.unwrap(),
                        sent_bytes: bytes_sent.unwrap(),
                        request_method: split_line[ELBRecordFields::RequestMethod.idx()].trim_matches('"').to_string(),
                        request_url: split_line[ELBRecordFields::RequestURL.idx()].to_string(),
                        request_http_version: split_line[ELBRecordFields::RequestHTTPVersion.idx()].trim_matches('"').to_string()
                    }
                )
            } else {
                None
            }
        }
    }.map( |elb_rec|
        Ok(Box::new(elb_rec))
    ).unwrap_or_else( ||
        Err(ParsingErrors {
            record: record,
            errors: errors
        })
    )
}

//TODO Try to implement this on &str
fn parse_property<T>(raw_prop: &str, prop_name: &'static str, errors: &mut Vec<ELBRecordParsingErrors>) -> Option<T>
    where T: FromStr,
    T::Err: Error + 'static,
{
    match raw_prop.parse::<T>() {
        Ok(parsed) => Some(parsed),

        Err(e) => {
            errors.push(
                ELBRecordParsingErrors::ParsingError {
                    property: prop_name,
                    description: e.description().to_string(),
                }
            );
            None
        }
    }
}

//TODO test the error case.
#[cfg(test)]
mod tests {
    use super::parse_record;
    use super::ELBRecordParsingErrors;

    const TEST_RECORD: &'static str = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
    172.16.1.5:9000 0.000039 0.145507 0.00003 200 200 0 7582 \
    \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\"\
    ";

    #[test]
	fn parse_record_returns_a_malformed_record_error_for_records_short_on_values() {
        let short_record = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
        172.16.1.5:9000 0.000039 200 200 0 7582 \
        \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\" \
        ";

        let malformed_error = parse_record(short_record.to_string()).unwrap_err().errors.pop();

		assert_eq!(malformed_error, Some(ELBRecordParsingErrors::MalformedRecord))
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_http_version() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_http_version, "HTTP/1.1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_url() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_url, "http://some.domain.com:80/path0/path1?param0=p0&param1=p1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_method() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_method, "GET")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_sent_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.sent_bytes, 7582)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_received_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.received_bytes, 0)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_response_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.response_processing_time, 0.00003)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_processing_time, 0.145507)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_processing_time, 0.000039)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_address, "172.16.1.5:9000".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_client_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.client_address, "172.16.1.6:54814".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_timestamp() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(format!("{:?}", elb_record.timestamp), "2015-08-15T23:43:05.302180Z")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_name() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_name, "elb-name")
	}
}
<commit_msg>Created a method to convert the field enum to strings.<commit_after>extern crate walkdir;
extern crate chrono;

use std::path;
use self::walkdir::{WalkDir, DirEntry, Error as WalkDirError};
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use self::chrono::{DateTime, UTC};
use std::error::Error;
use std::str::FromStr;
use std::net::SocketAddrV4;

#[derive(Debug)]
pub struct ELBRecord {
    timestamp: DateTime<UTC>,
    elb_name: String,
    client_address: SocketAddrV4,
    backend_address: SocketAddrV4,
    request_processing_time: f32,
    backend_processing_time: f32,
    response_processing_time: f32,
    elb_status_code: u16,
    backend_status_code: u16,
    received_bytes: u64,
    sent_bytes: u64,
    request_method: String,
    request_url: String,
    request_http_version: String
}

pub fn file_list(dir: &path::Path, filenames: &mut Vec<DirEntry>) -> Result<usize, WalkDirError> {
    for entry in WalkDir::new(dir).min_depth(1) {
        match entry {
            Err(err) => return Err(err),
            Ok(entry) => filenames.push(entry),
        }
    }
    Ok(filenames.len())
}

//TODO Reconsider logging based on the standard interfaces included with Rust.
//TODO We really want to accept a function to handle the parsed lines.
pub fn process_files(runtime_context: &::RuntimeContext, filenames: Vec<walkdir::DirEntry>) -> usize {
    let debug = runtime_context.debug;
    let mut record_count = 0;
    for filename in filenames {
        debug!(debug, "Processing file {}.", filename.path().display());
        match File::open(filename.path()) {
            Ok(file) => {
                let buffered_file = BufReader::new(&file);
                let recs: Vec<_> = buffered_file.lines()
                    .map(|possible_line| {
                        match possible_line {
                            Ok(record) => parse_record(record),

                            Err(_) => {
                                Err(ParsingErrors {
                                    record: "".to_string(),
                                    errors: vec![ELBRecordParsingErrors::LineReadError]
                                })
                            }
                        }
                    })
                    .collect();
                record_count += recs.len();
                debug!(debug, "Found {} records in file {}.", recs.len(), filename.path().display());
            },
            Err(e) => {
                println!("ERROR: {}", e);
            }
        }
    }

    record_count
}

//TODO Take a look at the error handling once again.  This doesn't feel write given code you've read.

#[derive(Debug)]
pub struct ParsingErrors {
    record: String,
    errors: Vec<ELBRecordParsingErrors>,
}

#[derive(Debug, PartialEq)]
enum ELBRecordParsingErrors {
    MalformedRecord,
    ParsingError { property: &'static str, description: String },
    LineReadError
}

#[derive(Debug, PartialEq)]
enum ELBRecordFields {
    Timestamp = 0,
    ELBName,
    ClientAddress,
    BackendAddress,
    RequestProcessingTime,
    BackendProcessingTime,
    ResponseProcessingTime,
    ELBStatusCode,
    BackendStatusCode,
    ReceivedBytes,
    SentBytes,
    RequestMethod,
    RequestURL,
    RequestHTTPVersion
}

impl ELBRecordFields {

  fn idx(self) -> usize {
    self as usize
  }

  fn as_str(self) -> &'static str {
      match self {
          ELBRecordFields::Timestamp => "timestamp",
          ELBRecordFields::ELBName => "ELB name",
          ELBRecordFields::ClientAddress => "client address",
          ELBRecordFields::BackendAddress => "backend address",
          ELBRecordFields::RequestProcessingTime => "request processing time",
          ELBRecordFields::BackendProcessingTime => "backend processing time",
          ELBRecordFields::ResponseProcessingTime => "response processing time",
          ELBRecordFields::ELBStatusCode => "ELB status code",
          ELBRecordFields::BackendStatusCode => "backend status code",
          ELBRecordFields::ReceivedBytes => "received bytes",
          ELBRecordFields::SentBytes => "sent bytes",
          ELBRecordFields::RequestMethod => "request method",
          ELBRecordFields::RequestURL => "request URL",
          ELBRecordFields::RequestHTTPVersion => "request HTTP version"
      }
  }
}

const ELB_RECORD_FIELD_COUNT: usize = 14;
pub fn parse_record(record: String) -> Result<Box<ELBRecord>, ParsingErrors> {
    let mut errors: Vec<ELBRecordParsingErrors> = Vec::new();

    {
        //record is borrowed by the split method which means ownership of record cannot be
        //transferred to ParsingErrors until the borrow is complete.
        //Scoping this section of code seems more readable than creating a separate function
        //just to mitigate the borrow.
        let split_line: Vec<&str> = record.split(' ').collect();
        if split_line.len() != ELB_RECORD_FIELD_COUNT {
            errors.push(ELBRecordParsingErrors::MalformedRecord);
            None
        } else {
            let ts = parse_property::<DateTime<UTC>>(split_line[ELBRecordFields::Timestamp.idx()], ELBRecordFields::Timestamp.as_str(), &mut errors);
            let clnt_addr = parse_property::<SocketAddrV4>(split_line[ELBRecordFields::ClientAddress.idx()], ELBRecordFields::ClientAddress.as_str(), &mut errors);
            let be_addr = parse_property::<SocketAddrV4>(split_line[ELBRecordFields::BackendAddress.idx()], ELBRecordFields::BackendAddress.as_str(), &mut errors);
            let req_proc_time = parse_property::<f32>(split_line[ELBRecordFields::RequestProcessingTime.idx()], ELBRecordFields::RequestProcessingTime.as_str(), &mut errors);
            let be_proc_time = parse_property::<f32>(split_line[ELBRecordFields::BackendProcessingTime.idx()], ELBRecordFields::BackendProcessingTime.as_str(), &mut errors);
            let res_proc_time = parse_property::<f32>(split_line[ELBRecordFields::ResponseProcessingTime.idx()], ELBRecordFields::ResponseProcessingTime.as_str(), &mut errors);
            let elb_sc = parse_property::<u16>(split_line[ELBRecordFields::ELBStatusCode.idx()], ELBRecordFields::ELBStatusCode.as_str(), &mut errors);
            let be_sc = parse_property::<u16>(split_line[ELBRecordFields::BackendStatusCode.idx()], ELBRecordFields::BackendStatusCode.as_str(), &mut errors);
            let bytes_received = parse_property::<u64>(split_line[ELBRecordFields::ReceivedBytes.idx()], ELBRecordFields::ReceivedBytes.as_str(), &mut errors);
            let bytes_sent = parse_property::<u64>(split_line[ELBRecordFields::SentBytes.idx()], ELBRecordFields::SentBytes.as_str(), &mut errors);

            if errors.is_empty() {
                //If errors is empty it is more than likely parsing was successful and unwrap is safe.
                Some(
                    ELBRecord {
                        timestamp: ts.unwrap(),
                        elb_name: split_line[ELBRecordFields::ELBName.idx()].to_string(),
                        client_address: clnt_addr.unwrap(),
                        backend_address: be_addr.unwrap(),
                        request_processing_time: req_proc_time.unwrap(),
                        backend_processing_time: be_proc_time.unwrap(),
                        response_processing_time: res_proc_time.unwrap(),
                        elb_status_code: elb_sc.unwrap(),
                        backend_status_code: be_sc.unwrap(),
                        received_bytes: bytes_received.unwrap(),
                        sent_bytes: bytes_sent.unwrap(),
                        request_method: split_line[ELBRecordFields::RequestMethod.idx()].trim_matches('"').to_string(),
                        request_url: split_line[ELBRecordFields::RequestURL.idx()].to_string(),
                        request_http_version: split_line[ELBRecordFields::RequestHTTPVersion.idx()].trim_matches('"').to_string()
                    }
                )
            } else {
                None
            }
        }
    }.map( |elb_rec|
        Ok(Box::new(elb_rec))
    ).unwrap_or_else( ||
        Err(ParsingErrors {
            record: record,
            errors: errors
        })
    )
}

//TODO Try to implement this on &str, or better, on vec.  The vec gets passed in with the field enum and the rest is taken care of by the method.  Which will shrink the API.
fn parse_property<T>(raw_prop: &str, prop_name: &'static str, errors: &mut Vec<ELBRecordParsingErrors>) -> Option<T>
    where T: FromStr,
    T::Err: Error + 'static,
{
    match raw_prop.parse::<T>() {
        Ok(parsed) => Some(parsed),

        Err(e) => {
            errors.push(
                ELBRecordParsingErrors::ParsingError {
                    property: prop_name,
                    description: e.description().to_string(),
                }
            );
            None
        }
    }
}

//TODO test the error case.
#[cfg(test)]
mod tests {
    use super::parse_record;
    use super::ELBRecordParsingErrors;

    const TEST_RECORD: &'static str = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
    172.16.1.5:9000 0.000039 0.145507 0.00003 200 200 0 7582 \
    \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\"\
    ";

    #[test]
	fn parse_record_returns_a_malformed_record_error_for_records_short_on_values() {
        let short_record = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
        172.16.1.5:9000 0.000039 200 200 0 7582 \
        \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\" \
        ";

        let malformed_error = parse_record(short_record.to_string()).unwrap_err().errors.pop();

		assert_eq!(malformed_error, Some(ELBRecordParsingErrors::MalformedRecord))
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_http_version() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_http_version, "HTTP/1.1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_url() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_url, "http://some.domain.com:80/path0/path1?param0=p0&param1=p1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_method() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_method, "GET")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_sent_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.sent_bytes, 7582)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_received_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.received_bytes, 0)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_response_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.response_processing_time, 0.00003)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_processing_time, 0.145507)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_processing_time, 0.000039)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_address, "172.16.1.5:9000".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_client_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.client_address, "172.16.1.6:54814".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_timestamp() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(format!("{:?}", elb_record.timestamp), "2015-08-15T23:43:05.302180Z")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_name() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_name, "elb-name")
	}
}
<|endoftext|>


<commit_before>enum List<T> {
    Cons(T, ~List<T>),
    Nil
}

fn rev<T: Clone>(list: &List<T>) -> List<T> {
    fn rev_aux<T: Clone>(list: &List<T>, acc: List<T>) -> List<T> {
        match *list {
            Nil => acc,
            Cons(ref elem, ~ref rest) => rev_aux(rest, Cons((*elem).clone(), ~acc))
        }
    }
    rev_aux(list, Nil)
}

fn main() {
    let list: List<char> = Cons('a', ~Cons('b', ~Cons('c', ~Cons('d', ~Nil))));
    println!("{:?}", rev(&list));
}
<commit_msg>Redo Problem 05 with vectors<commit_after>fn rev<T: Clone>(list: &~[T]) -> ~[T] {
    let mut tsli = ~[];
    for elem in list.iter() {
        tsli.unshift(elem.clone());
    }
    tsli
}

fn main() {
    let list = ~['a', 'b', 'c', 'd', 'e'];
    println!("{:?}", rev(&list));
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::os::raw;
use std::os::raw::c_char;
use cose::SignatureAlgorithm;

/// An enum identifying supported signature algorithms. Currently only ECDSA with SHA256 (ES256) and
/// RSASSA-PSS with SHA-256 (PS256) are supported. Note that with PS256, the salt length is defined
/// to be 32 bytes.
#[derive(Debug)]
pub enum SignatureAlgorithm {
    ES256,
    PS256,
}

type SECItemType = raw::c_uint; // TODO: actually an enum - is this the right size?
const SI_BUFFER: SECItemType = 0; // called siBuffer in NSS

#[repr(C)]
struct SECItem {
    typ: SECItemType,
    data: *const u8,
    len: raw::c_uint,
}

impl SECItem {
    fn maybe_new(data: &[u8]) -> Result<SECItem, NSSError> {
        if data.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data.as_ptr(),
            len: data.len() as u32,
        })
    }

    fn maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError> {
        if len > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data,
            len: len as u32,
        })
    }
}

/// Many NSS APIs take constant data input as SECItems. Some, however, output data as SECItems.
/// To represent this, we define another type of mutable SECItem.
#[repr(C)]
struct SECItemMut<'a> {
    typ: SECItemType,
    data: *mut u8,
    len: raw::c_uint,
    _marker: PhantomData<&'a mut Vec<u8>>,
}

impl<'a> SECItemMut<'a> {
    /// Given a mutable reference to a Vec<u8> that has a particular allocated size, create a
    /// SECItemMut that points to the vec and has the same capacity.
    /// The input vec is not expected to have any actual contents, and in any case is cleared.
    fn maybe_from_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError> {
        if vec.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        vec.clear();
        Ok(SECItemMut {
            typ: SI_BUFFER,
            data: vec.as_mut_ptr(),
            len: vec.capacity() as u32,
            _marker: PhantomData,
        })
    }
}

#[repr(C)]
struct CkRsaPkcsPssParams {
    // Called CK_RSA_PKCS_PSS_PARAMS in NSS
    hash_alg: CkMechanismType, // Called hashAlg in NSS
    mgf: CkRsaPkcsMgfType,
    s_len: raw::c_ulong, // Called sLen in NSS
}

impl CkRsaPkcsPssParams {
    fn new() -> CkRsaPkcsPssParams {
        CkRsaPkcsPssParams {
            hash_alg: CKM_SHA256,
            mgf: CKG_MGF1_SHA256,
            s_len: 32,
        }
    }

    fn get_params_item(&self) -> Result<SECItem, NSSError> {
        // This isn't entirely NSS' fault, but it mostly is.
        let params_ptr: *const CkRsaPkcsPssParams = self;
        let params_ptr: *const u8 = params_ptr as *const u8;
        let params_secitem =
            SECItem::maybe_from_parts(params_ptr, mem::size_of::<CkRsaPkcsPssParams>())?;
        Ok(params_secitem)
    }
}

type CkMechanismType = raw::c_ulong; // called CK_MECHANISM_TYPE in NSS
const CKM_ECDSA: CkMechanismType = 0x0000_1041;
const CKM_RSA_PKCS_PSS: CkMechanismType = 0x0000_000D;
const CKM_SHA256: CkMechanismType = 0x0000_0250;

type CkRsaPkcsMgfType = raw::c_ulong; // called CK_RSA_PKCS_MGF_TYPE in NSS
const CKG_MGF1_SHA256: CkRsaPkcsMgfType = 0x0000_0002;

type SECStatus = raw::c_int; // TODO: enum - right size?
const SEC_SUCCESS: SECStatus = 0; // Called SECSuccess in NSS
const SEC_FAILURE: SECStatus = -1; // Called SECFailure in NSS

enum SECKEYPublicKey {}
enum SECKEYPrivateKey {}
enum PK11SlotInfo {}
enum CERTCertificate {}
enum CERTCertDBHandle {}

const SHA256_LENGTH: usize = 32;
const SHA384_LENGTH: usize = 48;

// TODO: ugh this will probably have a platform-specific name...
#[link(name = "nss3")]
extern "C" {
    fn PK11_HashBuf(
        hashAlg: HashAlgorithm,
        out: *mut u8,
        data_in: *const u8, // called "in" in NSS
        len: raw::c_int,
    ) -> SECStatus;
    fn PK11_VerifyWithMechanism(
        key: *const SECKEYPublicKey,
        mechanism: CkMechanismType,
        param: *const SECItem,
        sig: *const SECItem,
        hash: *const SECItem,
        wincx: *const raw::c_void,
    ) -> SECStatus;

    fn SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey);

    fn CERT_GetDefaultCertDB() -> *const CERTCertDBHandle;
    fn CERT_DestroyCertificate(cert: *mut CERTCertificate);
    fn CERT_NewTempCertificate(
        handle: *const CERTCertDBHandle,
        derCert: *const SECItem,
        nickname: *const c_char,
        isperm: bool,
        copyDER: bool,
    ) -> *mut CERTCertificate;
    fn CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey;

    fn PK11_ImportDERPrivateKeyInfoAndReturnKey(
        slot: *mut PK11SlotInfo,
        derPKI: *const SECItem,
        nickname: *const SECItem,
        publicValue: *const SECItem,
        isPerm: bool,
        isPrivate: bool,
        keyUsage: u32,
        privk: *mut *mut SECKEYPrivateKey,
        wincx: *const u8,
    ) -> SECStatus;
    fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
    fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
    fn PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize;
    fn PK11_SignWithMechanism(
        key: *const SECKEYPrivateKey,
        mech: CkMechanismType,
        param: *const SECItem,
        sig: *mut SECItemMut,
        hash: *const SECItem,
    ) -> SECStatus;
}

/// An error type describing errors that may be encountered during verification.
#[derive(Debug, PartialEq)]
pub enum NSSError {
    ImportCertError,
    DecodingPKCS8Failed,
    InputTooLarge,
    LibraryFailure,
    SignatureVerificationFailed,
    SigningFailed,
    ExtractPublicKeyFailed,
}

// https://searchfox.org/nss/rev/990c2e793aa731cd66238c6c4f00b9473943bc66/lib/util/secoidt.h#274
#[derive(Debug, PartialEq, Clone)]
#[repr(C)]
enum HashAlgorithm {
    SHA256 = 191,
    SHA384 = 192,
}

fn hash(payload: &[u8], signature_algorithm: &SignatureAlgorithm) -> Result<Vec<u8>, NSSError> {
    if payload.len() > raw::c_int::max_value() as usize {
        return Err(NSSError::InputTooLarge);
    }
    let (hash_algorithm, digest_length) = match *signature_algorithm {
        SignatureAlgorithm::ES256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
        SignatureAlgorithm::ES384 => (HashAlgorithm::SHA384, SHA384_LENGTH),
        SignatureAlgorithm::PS256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
    };
    let mut hash_buf = vec![0; digest_length];
    let len: raw::c_int = payload.len() as raw::c_int;
    let hash_result =
        unsafe { PK11_HashBuf(hash_algorithm, hash_buf.as_mut_ptr(), payload.as_ptr(), len) };
    if hash_result != SEC_SUCCESS {
        return Err(NSSError::LibraryFailure);
    }
    Ok(hash_buf)
}

/// Main entrypoint for verification. Given a signature algorithm, the bytes of a subject public key
/// info, a payload, and a signature over the payload, returns a result based on the outcome of
/// decoding the subject public key info and running the signature verification algorithm on the
/// signed data.
pub fn verify_signature(
    signature_algorithm: &SignatureAlgorithm,
    cert: &[u8],
    payload: &[u8],
    signature: &[u8],
) -> Result<(), NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });

    let hash_buf = hash(payload, signature_algorithm).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;

    // Import DER cert into NSS.
    let der_cert = SECItem::maybe_new(cert)?;
    let db_handle = unsafe { CERT_GetDefaultCertDB() };
    if db_handle.is_null() {
        // TODO #28
        return Err(NSSError::LibraryFailure);
    }
    let nss_cert =
        unsafe { CERT_NewTempCertificate(db_handle, &der_cert, ptr::null(), false, true) };
    if nss_cert.is_null() {
        return Err(NSSError::ImportCertError);
    }
    defer!(unsafe {
        CERT_DestroyCertificate(nss_cert);
    });

    let key = unsafe { CERT_ExtractPublicKey(nss_cert) };
    if key.is_null() {
        return Err(NSSError::ExtractPublicKeyFailed);
    }
    defer!(unsafe {
        SECKEY_DestroyPublicKey(key);
    });
    let signature_item = SECItem::maybe_new(signature)?;
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::ES384 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let null_cx_ptr: *const raw::c_void = ptr::null();
    let result = unsafe {
        PK11_VerifyWithMechanism(
            key,
            mechanism,
            params_item,
            &signature_item,
            &hash_item,
            null_cx_ptr,
        )
    };
    match result {
        SEC_SUCCESS => Ok(()),
        SEC_FAILURE => Err(NSSError::SignatureVerificationFailed),
        _ => Err(NSSError::LibraryFailure),
    }
}

pub fn sign(
    signature_algorithm: &SignatureAlgorithm,
    pk8: &[u8],
    payload: &[u8],
) -> Result<Vec<u8>, NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });
    let pkcs8item = SECItem::maybe_new(pk8)?;
    let mut key: *mut SECKEYPrivateKey = ptr::null_mut();
    let ku_all = 0xFF;
    let rv = unsafe {
        PK11_ImportDERPrivateKeyInfoAndReturnKey(
            slot,
            &pkcs8item,
            ptr::null(),
            ptr::null(),
            false,
            false,
            ku_all,
            &mut key,
            ptr::null(),
        )
    };
    if rv != SEC_SUCCESS || key.is_null() {
        return Err(NSSError::DecodingPKCS8Failed);
    }
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::ES384 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let signature_len = unsafe { PK11_SignatureLen(key) };
    // Allocate enough space for the signature.
    let mut signature: Vec<u8> = Vec::with_capacity(signature_len);
    let hash_buf = hash(payload, signature_algorithm).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
    {
        // Get a mutable SECItem on the preallocated signature buffer. PK11_SignWithMechanism will
        // fill the SECItem's buf with the bytes of the signature.
        let mut signature_item = SECItemMut::maybe_from_vec(&mut signature)?;
        let rv = unsafe {
            PK11_SignWithMechanism(key, mechanism, params_item, &mut signature_item, &hash_item)
        };
        if rv != SEC_SUCCESS || signature_item.len as usize != signature_len {
            return Err(NSSError::SigningFailed);
        }
    }
    unsafe {
        // Now that the bytes of the signature have been filled out, set its length.
        signature.set_len(signature_len);
    }
    Ok(signature)
}
<commit_msg>fix merge errors<commit_after>use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::os::raw;
use std::os::raw::c_char;
use cose::SignatureAlgorithm;

type SECItemType = raw::c_uint; // TODO: actually an enum - is this the right size?
const SI_BUFFER: SECItemType = 0; // called siBuffer in NSS

#[repr(C)]
struct SECItem {
    typ: SECItemType,
    data: *const u8,
    len: raw::c_uint,
}

impl SECItem {
    fn maybe_new(data: &[u8]) -> Result<SECItem, NSSError> {
        if data.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data.as_ptr(),
            len: data.len() as u32,
        })
    }

    fn maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError> {
        if len > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data,
            len: len as u32,
        })
    }
}

/// Many NSS APIs take constant data input as SECItems. Some, however, output data as SECItems.
/// To represent this, we define another type of mutable SECItem.
#[repr(C)]
struct SECItemMut<'a> {
    typ: SECItemType,
    data: *mut u8,
    len: raw::c_uint,
    _marker: PhantomData<&'a mut Vec<u8>>,
}

impl<'a> SECItemMut<'a> {
    /// Given a mutable reference to a Vec<u8> that has a particular allocated size, create a
    /// SECItemMut that points to the vec and has the same capacity.
    /// The input vec is not expected to have any actual contents, and in any case is cleared.
    fn maybe_from_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError> {
        if vec.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        vec.clear();
        Ok(SECItemMut {
            typ: SI_BUFFER,
            data: vec.as_mut_ptr(),
            len: vec.capacity() as u32,
            _marker: PhantomData,
        })
    }
}

#[repr(C)]
struct CkRsaPkcsPssParams {
    // Called CK_RSA_PKCS_PSS_PARAMS in NSS
    hash_alg: CkMechanismType, // Called hashAlg in NSS
    mgf: CkRsaPkcsMgfType,
    s_len: raw::c_ulong, // Called sLen in NSS
}

impl CkRsaPkcsPssParams {
    fn new() -> CkRsaPkcsPssParams {
        CkRsaPkcsPssParams {
            hash_alg: CKM_SHA256,
            mgf: CKG_MGF1_SHA256,
            s_len: 32,
        }
    }

    fn get_params_item(&self) -> Result<SECItem, NSSError> {
        // This isn't entirely NSS' fault, but it mostly is.
        let params_ptr: *const CkRsaPkcsPssParams = self;
        let params_ptr: *const u8 = params_ptr as *const u8;
        let params_secitem =
            SECItem::maybe_from_parts(params_ptr, mem::size_of::<CkRsaPkcsPssParams>())?;
        Ok(params_secitem)
    }
}

type CkMechanismType = raw::c_ulong; // called CK_MECHANISM_TYPE in NSS
const CKM_ECDSA: CkMechanismType = 0x0000_1041;
const CKM_RSA_PKCS_PSS: CkMechanismType = 0x0000_000D;
const CKM_SHA256: CkMechanismType = 0x0000_0250;

type CkRsaPkcsMgfType = raw::c_ulong; // called CK_RSA_PKCS_MGF_TYPE in NSS
const CKG_MGF1_SHA256: CkRsaPkcsMgfType = 0x0000_0002;

type SECStatus = raw::c_int; // TODO: enum - right size?
const SEC_SUCCESS: SECStatus = 0; // Called SECSuccess in NSS
const SEC_FAILURE: SECStatus = -1; // Called SECFailure in NSS

enum SECKEYPublicKey {}
enum SECKEYPrivateKey {}
enum PK11SlotInfo {}
enum CERTCertificate {}
enum CERTCertDBHandle {}

const SHA256_LENGTH: usize = 32;
const SHA384_LENGTH: usize = 48;

// TODO: ugh this will probably have a platform-specific name...
#[link(name = "nss3")]
extern "C" {
    fn PK11_HashBuf(
        hashAlg: HashAlgorithm,
        out: *mut u8,
        data_in: *const u8, // called "in" in NSS
        len: raw::c_int,
    ) -> SECStatus;
    fn PK11_VerifyWithMechanism(
        key: *const SECKEYPublicKey,
        mechanism: CkMechanismType,
        param: *const SECItem,
        sig: *const SECItem,
        hash: *const SECItem,
        wincx: *const raw::c_void,
    ) -> SECStatus;

    fn SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey);

    fn CERT_GetDefaultCertDB() -> *const CERTCertDBHandle;
    fn CERT_DestroyCertificate(cert: *mut CERTCertificate);
    fn CERT_NewTempCertificate(
        handle: *const CERTCertDBHandle,
        derCert: *const SECItem,
        nickname: *const c_char,
        isperm: bool,
        copyDER: bool,
    ) -> *mut CERTCertificate;
    fn CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey;

    fn PK11_ImportDERPrivateKeyInfoAndReturnKey(
        slot: *mut PK11SlotInfo,
        derPKI: *const SECItem,
        nickname: *const SECItem,
        publicValue: *const SECItem,
        isPerm: bool,
        isPrivate: bool,
        keyUsage: u32,
        privk: *mut *mut SECKEYPrivateKey,
        wincx: *const u8,
    ) -> SECStatus;
    fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
    fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
    fn PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize;
    fn PK11_SignWithMechanism(
        key: *const SECKEYPrivateKey,
        mech: CkMechanismType,
        param: *const SECItem,
        sig: *mut SECItemMut,
        hash: *const SECItem,
    ) -> SECStatus;
}

/// An error type describing errors that may be encountered during verification.
#[derive(Debug, PartialEq)]
pub enum NSSError {
    ImportCertError,
    DecodingPKCS8Failed,
    InputTooLarge,
    LibraryFailure,
    SignatureVerificationFailed,
    SigningFailed,
    ExtractPublicKeyFailed,
}

// https://searchfox.org/nss/rev/990c2e793aa731cd66238c6c4f00b9473943bc66/lib/util/secoidt.h#274
#[derive(Debug, PartialEq, Clone)]
#[repr(C)]
enum HashAlgorithm {
    SHA256 = 191,
    SHA384 = 192,
}

fn hash(payload: &[u8], signature_algorithm: &SignatureAlgorithm) -> Result<Vec<u8>, NSSError> {
    if payload.len() > raw::c_int::max_value() as usize {
        return Err(NSSError::InputTooLarge);
    }
    let (hash_algorithm, digest_length) = match *signature_algorithm {
        SignatureAlgorithm::ES256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
        SignatureAlgorithm::ES384 => (HashAlgorithm::SHA384, SHA384_LENGTH),
        SignatureAlgorithm::PS256 => (HashAlgorithm::SHA256, SHA256_LENGTH),
    };
    let mut hash_buf = vec![0; digest_length];
    let len: raw::c_int = payload.len() as raw::c_int;
    let hash_result =
        unsafe { PK11_HashBuf(hash_algorithm, hash_buf.as_mut_ptr(), payload.as_ptr(), len) };
    if hash_result != SEC_SUCCESS {
        return Err(NSSError::LibraryFailure);
    }
    Ok(hash_buf)
}

/// Main entrypoint for verification. Given a signature algorithm, the bytes of a subject public key
/// info, a payload, and a signature over the payload, returns a result based on the outcome of
/// decoding the subject public key info and running the signature verification algorithm on the
/// signed data.
pub fn verify_signature(
    signature_algorithm: &SignatureAlgorithm,
    cert: &[u8],
    payload: &[u8],
    signature: &[u8],
) -> Result<(), NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });

    let hash_buf = hash(payload, signature_algorithm).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;

    // Import DER cert into NSS.
    let der_cert = SECItem::maybe_new(cert)?;
    let db_handle = unsafe { CERT_GetDefaultCertDB() };
    if db_handle.is_null() {
        // TODO #28
        return Err(NSSError::LibraryFailure);
    }
    let nss_cert =
        unsafe { CERT_NewTempCertificate(db_handle, &der_cert, ptr::null(), false, true) };
    if nss_cert.is_null() {
        return Err(NSSError::ImportCertError);
    }
    defer!(unsafe {
        CERT_DestroyCertificate(nss_cert);
    });

    let key = unsafe { CERT_ExtractPublicKey(nss_cert) };
    if key.is_null() {
        return Err(NSSError::ExtractPublicKeyFailed);
    }
    defer!(unsafe {
        SECKEY_DestroyPublicKey(key);
    });
    let signature_item = SECItem::maybe_new(signature)?;
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::ES384 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::ES384 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let null_cx_ptr: *const raw::c_void = ptr::null();
    let result = unsafe {
        PK11_VerifyWithMechanism(
            key,
            mechanism,
            params_item,
            &signature_item,
            &hash_item,
            null_cx_ptr,
        )
    };
    match result {
        SEC_SUCCESS => Ok(()),
        SEC_FAILURE => Err(NSSError::SignatureVerificationFailed),
        _ => Err(NSSError::LibraryFailure),
    }
}

pub fn sign(
    signature_algorithm: &SignatureAlgorithm,
    pk8: &[u8],
    payload: &[u8],
) -> Result<Vec<u8>, NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });
    let pkcs8item = SECItem::maybe_new(pk8)?;
    let mut key: *mut SECKEYPrivateKey = ptr::null_mut();
    let ku_all = 0xFF;
    let rv = unsafe {
        PK11_ImportDERPrivateKeyInfoAndReturnKey(
            slot,
            &pkcs8item,
            ptr::null(),
            ptr::null(),
            false,
            false,
            ku_all,
            &mut key,
            ptr::null(),
        )
    };
    if rv != SEC_SUCCESS || key.is_null() {
        return Err(NSSError::DecodingPKCS8Failed);
    }
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::ES384 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::ES384 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let signature_len = unsafe { PK11_SignatureLen(key) };
    // Allocate enough space for the signature.
    let mut signature: Vec<u8> = Vec::with_capacity(signature_len);
    let hash_buf = hash(payload, signature_algorithm).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
    {
        // Get a mutable SECItem on the preallocated signature buffer. PK11_SignWithMechanism will
        // fill the SECItem's buf with the bytes of the signature.
        let mut signature_item = SECItemMut::maybe_from_vec(&mut signature)?;
        let rv = unsafe {
            PK11_SignWithMechanism(key, mechanism, params_item, &mut signature_item, &hash_item)
        };
        if rv != SEC_SUCCESS || signature_item.len as usize != signature_len {
            return Err(NSSError::SigningFailed);
        }
    }
    unsafe {
        // Now that the bytes of the signature have been filled out, set its length.
        signature.set_len(signature_len);
    }
    Ok(signature)
}
<|endoftext|>


<commit_before>//! Aarch64 run-time features.

features! {
    @TARGET: aarch64;
    @MACRO_NAME: is_aarch64_feature_detected;
    @MACRO_ATTRS:
    /// This macro tests, at runtime, whether an `aarch64` feature is enabled on aarch64 platforms.
    /// Currently most features are only supported on linux-based platforms.
    ///
    /// This macro takes one argument which is a string literal of the feature being tested for.
    /// The feature names are mostly taken from their FEAT_* definitions in the [ARM Architecture
    /// Reference Manual][docs].
    ///
    /// ## Supported arguments
    ///
    /// * `"asimd"` or "neon" - FEAT_AdvSIMD
    /// * `"pmull"` - FEAT_PMULL
    /// * `"fp"` - FEAT_FP
    /// * `"fp16"` - FEAT_FP16
    /// * `"sve"` - FEAT_SVE
    /// * `"crc"` - FEAT_CRC
    /// * `"lse"` - FEAT_LSE
    /// * `"lse2"` - FEAT_LSE2
    /// * `"rdm"` - FEAT_RDM
    /// * `"rcpc"` - FEAT_LRCPC
    /// * `"rcpc2"` - FEAT_LRCPC2
    /// * `"dotprod"` - FEAT_DotProd
    /// * `"tme"` - FEAT_TME
    /// * `"fhm"` - FEAT_FHM
    /// * `"dit"` - FEAT_DIT
    /// * `"flagm"` - FEAT_FLAGM
    /// * `"ssbs"` - FEAT_SSBS
    /// * `"sb"` - FEAT_SB
    /// * `"pauth"` - FEAT_PAuth
    /// * `"dpb"` - FEAT_DPB
    /// * `"dpb2"` - FEAT_DPB2
    /// * `"sve2"` - FEAT_SVE2
    /// * `"sve2-aes"` - FEAT_SVE2_AES
    /// * `"sve2-sm4"` - FEAT_SVE2_SM4
    /// * `"sve2-sha3"` - FEAT_SVE2_SHA3
    /// * `"sve2-bitperm"` - FEAT_SVE2_BitPerm
    /// * `"frintts"` - FEAT_FRINTTS
    /// * `"i8mm"` - FEAT_I8MM
    /// * `"f32mm"` - FEAT_F32MM
    /// * `"f64mm"` - FEAT_F64MM
    /// * `"bf16"` - FEAT_BF16
    /// * `"rand"` - FEAT_RNG
    /// * `"bti"` - FEAT_BTI
    /// * `"mte"` - FEAT_MTE
    /// * `"jsconv"` - FEAT_JSCVT
    /// * `"fcma"` - FEAT_FCMA
    /// * `"aes"` - FEAT_AES
    /// * `"sha2"` - FEAT_SHA1 & FEAT_SHA256
    /// * `"sha3"` - FEAT_SHA512 & FEAT_SHA3
    /// * `"sm4"` - FEAT_SM3 & FEAT_SM4
    ///
    /// [docs]: https://developer.arm.com/documentation/ddi0487/latest
    #[unstable(feature = "stdsimd", issue = "27731")]
    @BIND_FEATURE_NAME: "asimd"; "neon";
    @NO_RUNTIME_DETECTION: "ras";
    @NO_RUNTIME_DETECTION: "v8.1a";
    @NO_RUNTIME_DETECTION: "v8.2a";
    @NO_RUNTIME_DETECTION: "v8.3a";
    @NO_RUNTIME_DETECTION: "v8.4a";
    @NO_RUNTIME_DETECTION: "v8.5a";
    @NO_RUNTIME_DETECTION: "v8.6a";
    @NO_RUNTIME_DETECTION: "v8.7a";
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] asimd: "neon";
    /// FEAT_AdvSIMD (Advanced SIMD/NEON)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] pmull: "pmull";
    /// FEAT_PMULL (Polynomial Multiply)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fp: "fp";
    /// FEAT_FP (Floating point support)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fp16: "fp16";
    /// FEAT_FP16 (Half-float support)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve: "sve";
    /// FEAT_SVE (Scalable Vector Extension)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] crc: "crc";
    /// FEAT_CRC32 (Cyclic Redundancy Check)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] lse: "lse";
    /// FEAT_LSE (Large System Extension - atomics)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] lse2: "lse2";
    /// FEAT_LSE2 (unaligned and register-pair atomics)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rdm: "rdm";
    /// FEAT_RDM (Rounding Doubling Multiply - ASIMDRDM)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rcpc: "rcpc";
    /// FEAT_LRCPC (Release consistent Processor consistent)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rcpc2: "rcpc2";
    /// FEAT_LRCPC2 (RCPC with immediate offsets)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dotprod: "dotprod";
    /// FEAT_DotProd (Vector Dot-Product - ASIMDDP)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] tme: "tme";
    /// FEAT_TME (Transactional Memory Extensions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fhm: "fhm";
    /// FEAT_FHM (fp16 multiplication instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dit: "dit";
    /// FEAT_DIT (Data Independent Timing instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] flagm: "flagm";
    /// FEAT_FLAGM (flag manipulation instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] ssbs: "ssbs";
    /// FEAT_SSBS (speculative store bypass safe)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sb: "sb";
    /// FEAT_SB (speculation barrier)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] pauth: "pauth";
    /// FEAT_PAuth (pointer authentication)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dpb: "dpb";
    /// FEAT_DPB (aka dcpop - data cache clean to point of persistence)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] dpb2: "dpb2";
    /// FEAT_DPB2 (aka dcpodp - data cache clean to point of deep persistence)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2: "sve2";
    /// FEAT_SVE2 (Scalable Vector Extension 2)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_aes: "sve2-aes";
    /// FEAT_SVE_AES (SVE2 AES crypto)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_sm4: "sve2-sm4";
    /// FEAT_SVE_SM4 (SVE2 SM4 crypto)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_sha3: "sve2-sha3";
    /// FEAT_SVE_SHA3 (SVE2 SHA3 crypto)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sve2_bitperm: "sve2-bitperm";
    /// FEAT_SVE_BitPerm (SVE2 bit permutation instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] frintts: "frintts";
    /// FEAT_FRINTTS (float to integer rounding instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] i8mm: "i8mm";
    /// FEAT_I8MM (integer matrix multiplication, plus ASIMD support)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] f32mm: "f32mm";
    /// FEAT_F32MM (single-precision matrix multiplication)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] f64mm: "f64mm";
    /// FEAT_F64MM (double-precision matrix multiplication)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] bf16: "bf16";
    /// FEAT_BF16 (BFloat16 type, plus MM instructions, plus ASIMD support)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] rand: "rand";
    /// FEAT_RNG (Random Number Generator)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] bti: "bti";
    /// FEAT_BTI (Branch Target Identification)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] mte: "mte";
    /// FEAT_MTE (Memory Tagging Extension)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] jsconv: "jsconv";
    /// FEAT_JSCVT (JavaScript float conversion instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] fcma: "fcma";
    /// FEAT_FCMA (float complex number operations)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] aes: "aes";
    /// FEAT_AES (AES instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sha2: "sha2";
    /// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sha3: "sha3";
    /// FEAT_SHA512 & FEAT_SHA3 (SHA2-512 & SHA3 instructions)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] sm4: "sm4";
    /// FEAT_SM3 & FEAT_SM4 (SM3 & SM4 instructions)
}
<commit_msg>Stabilise `is_aarch64_feature_detected!` under `simd_aarch64` feature (#1239)<commit_after>//! Aarch64 run-time features.

features! {
    @TARGET: aarch64;
    @MACRO_NAME: is_aarch64_feature_detected;
    @MACRO_ATTRS:
    /// This macro tests, at runtime, whether an `aarch64` feature is enabled on aarch64 platforms.
    /// Currently most features are only supported on linux-based platforms.
    ///
    /// This macro takes one argument which is a string literal of the feature being tested for.
    /// The feature names are mostly taken from their FEAT_* definitions in the [ARM Architecture
    /// Reference Manual][docs].
    ///
    /// ## Supported arguments
    ///
    /// * `"asimd"` or "neon" - FEAT_AdvSIMD
    /// * `"pmull"` - FEAT_PMULL
    /// * `"fp"` - FEAT_FP
    /// * `"fp16"` - FEAT_FP16
    /// * `"sve"` - FEAT_SVE
    /// * `"crc"` - FEAT_CRC
    /// * `"lse"` - FEAT_LSE
    /// * `"lse2"` - FEAT_LSE2
    /// * `"rdm"` - FEAT_RDM
    /// * `"rcpc"` - FEAT_LRCPC
    /// * `"rcpc2"` - FEAT_LRCPC2
    /// * `"dotprod"` - FEAT_DotProd
    /// * `"tme"` - FEAT_TME
    /// * `"fhm"` - FEAT_FHM
    /// * `"dit"` - FEAT_DIT
    /// * `"flagm"` - FEAT_FLAGM
    /// * `"ssbs"` - FEAT_SSBS
    /// * `"sb"` - FEAT_SB
    /// * `"pauth"` - FEAT_PAuth
    /// * `"dpb"` - FEAT_DPB
    /// * `"dpb2"` - FEAT_DPB2
    /// * `"sve2"` - FEAT_SVE2
    /// * `"sve2-aes"` - FEAT_SVE2_AES
    /// * `"sve2-sm4"` - FEAT_SVE2_SM4
    /// * `"sve2-sha3"` - FEAT_SVE2_SHA3
    /// * `"sve2-bitperm"` - FEAT_SVE2_BitPerm
    /// * `"frintts"` - FEAT_FRINTTS
    /// * `"i8mm"` - FEAT_I8MM
    /// * `"f32mm"` - FEAT_F32MM
    /// * `"f64mm"` - FEAT_F64MM
    /// * `"bf16"` - FEAT_BF16
    /// * `"rand"` - FEAT_RNG
    /// * `"bti"` - FEAT_BTI
    /// * `"mte"` - FEAT_MTE
    /// * `"jsconv"` - FEAT_JSCVT
    /// * `"fcma"` - FEAT_FCMA
    /// * `"aes"` - FEAT_AES
    /// * `"sha2"` - FEAT_SHA1 & FEAT_SHA256
    /// * `"sha3"` - FEAT_SHA512 & FEAT_SHA3
    /// * `"sm4"` - FEAT_SM3 & FEAT_SM4
    ///
    /// [docs]: https://developer.arm.com/documentation/ddi0487/latest
    #[stable(feature = "simd_aarch64", since = "1.60.0")]
    @BIND_FEATURE_NAME: "asimd"; "neon";
    @NO_RUNTIME_DETECTION: "ras";
    @NO_RUNTIME_DETECTION: "v8.1a";
    @NO_RUNTIME_DETECTION: "v8.2a";
    @NO_RUNTIME_DETECTION: "v8.3a";
    @NO_RUNTIME_DETECTION: "v8.4a";
    @NO_RUNTIME_DETECTION: "v8.5a";
    @NO_RUNTIME_DETECTION: "v8.6a";
    @NO_RUNTIME_DETECTION: "v8.7a";
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] asimd: "neon";
    /// FEAT_AdvSIMD (Advanced SIMD/NEON)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] pmull: "pmull";
    /// FEAT_PMULL (Polynomial Multiply)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp: "fp";
    /// FEAT_FP (Floating point support)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fp16: "fp16";
    /// FEAT_FP16 (Half-float support)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve: "sve";
    /// FEAT_SVE (Scalable Vector Extension)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] crc: "crc";
    /// FEAT_CRC32 (Cyclic Redundancy Check)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse: "lse";
    /// FEAT_LSE (Large System Extension - atomics)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] lse2: "lse2";
    /// FEAT_LSE2 (unaligned and register-pair atomics)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rdm: "rdm";
    /// FEAT_RDM (Rounding Doubling Multiply - ASIMDRDM)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc: "rcpc";
    /// FEAT_LRCPC (Release consistent Processor consistent)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rcpc2: "rcpc2";
    /// FEAT_LRCPC2 (RCPC with immediate offsets)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dotprod: "dotprod";
    /// FEAT_DotProd (Vector Dot-Product - ASIMDDP)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] tme: "tme";
    /// FEAT_TME (Transactional Memory Extensions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fhm: "fhm";
    /// FEAT_FHM (fp16 multiplication instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dit: "dit";
    /// FEAT_DIT (Data Independent Timing instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] flagm: "flagm";
    /// FEAT_FLAGM (flag manipulation instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] ssbs: "ssbs";
    /// FEAT_SSBS (speculative store bypass safe)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sb: "sb";
    /// FEAT_SB (speculation barrier)
    @FEATURE: #[unstable(feature = "stdsimd", issue = "27731")] pauth: "pauth";
    /// FEAT_PAuth (pointer authentication)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb: "dpb";
    /// FEAT_DPB (aka dcpop - data cache clean to point of persistence)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] dpb2: "dpb2";
    /// FEAT_DPB2 (aka dcpodp - data cache clean to point of deep persistence)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2: "sve2";
    /// FEAT_SVE2 (Scalable Vector Extension 2)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_aes: "sve2-aes";
    /// FEAT_SVE_AES (SVE2 AES crypto)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sm4: "sve2-sm4";
    /// FEAT_SVE_SM4 (SVE2 SM4 crypto)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_sha3: "sve2-sha3";
    /// FEAT_SVE_SHA3 (SVE2 SHA3 crypto)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sve2_bitperm: "sve2-bitperm";
    /// FEAT_SVE_BitPerm (SVE2 bit permutation instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] frintts: "frintts";
    /// FEAT_FRINTTS (float to integer rounding instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] i8mm: "i8mm";
    /// FEAT_I8MM (integer matrix multiplication, plus ASIMD support)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f32mm: "f32mm";
    /// FEAT_F32MM (single-precision matrix multiplication)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] f64mm: "f64mm";
    /// FEAT_F64MM (double-precision matrix multiplication)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bf16: "bf16";
    /// FEAT_BF16 (BFloat16 type, plus MM instructions, plus ASIMD support)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] rand: "rand";
    /// FEAT_RNG (Random Number Generator)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] bti: "bti";
    /// FEAT_BTI (Branch Target Identification)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] mte: "mte";
    /// FEAT_MTE (Memory Tagging Extension)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] jsconv: "jsconv";
    /// FEAT_JSCVT (JavaScript float conversion instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] fcma: "fcma";
    /// FEAT_FCMA (float complex number operations)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] aes: "aes";
    /// FEAT_AES (AES instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha2: "sha2";
    /// FEAT_SHA1 & FEAT_SHA256 (SHA1 & SHA2-256 instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sha3: "sha3";
    /// FEAT_SHA512 & FEAT_SHA3 (SHA2-512 & SHA3 instructions)
    @FEATURE: #[stable(feature = "simd_aarch64", since = "1.60.0")] sm4: "sm4";
    /// FEAT_SM3 & FEAT_SM4 (SM3 & SM4 instructions)
}
<|endoftext|>