<commit_before>use std::cmp;
use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use filetime::FileTime;
use git2;
use glob::Pattern;

use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
use ops;
use util::{self, CargoResult, internal, internal_error, human, ChainError};
use util::Config;

pub struct PathSource<'cfg> {
    id: SourceId,
    path: PathBuf,
    updated: bool,
    packages: Vec<Package>,
    config: &'cfg Config,
}

// TODO: Figure out if packages should be discovered in new or self should be
// mut and packages are discovered in update
impl<'cfg> PathSource<'cfg> {
    pub fn for_path(path: &Path, config: &'cfg Config)
                    -> CargoResult<PathSource<'cfg>> {
        trace!("PathSource::for_path; path={}", path.display());
        Ok(PathSource::new(path, &try!(SourceId::for_path(path)), config))
    }

    /// Invoked with an absolute path to a directory that contains a Cargo.toml.
    /// The source will read the manifest and find any other packages contained
    /// in the directory structure reachable by the root manifest.
    pub fn new(path: &Path, id: &SourceId, config: &'cfg Config)
               -> PathSource<'cfg> {
        trace!("new; id={}", id);

        PathSource {
            id: id.clone(),
            path: path.to_path_buf(),
            updated: false,
            packages: Vec::new(),
            config: config,
        }
    }

    pub fn root_package(&self) -> CargoResult<Package> {
        trace!("root_package; source={:?}", self);

        if !self.updated {
            return Err(internal("source has not been updated"))
        }

        match self.packages.iter().find(|p| p.root() == &*self.path) {
            Some(pkg) => Ok(pkg.clone()),
            None => Err(internal("no package found in source"))
        }
    }

    fn read_packages(&self) -> CargoResult<Vec<Package>> {
        if self.updated {
            Ok(self.packages.clone())
        } else if self.id.is_path() && self.id.precise().is_some() {
            // If our source id is a path and it's listed with a precise
            // version, then it means that we're not allowed to have nested
            // dependencies (they've been rewritten to crates.io dependencies)
            // In this case we specifically read just one package, not a list of
            // packages.
            let path = self.path.join("Cargo.toml");
            let (pkg, _) = try!(ops::read_package(&path, &self.id,
                                                  self.config));
            Ok(vec![pkg])
        } else {
            ops::read_packages(&self.path, &self.id, self.config)
        }
    }

    /// List all files relevant to building this package inside this source.
    ///
    /// This function will use the appropriate methods to determine what is the
    /// set of files underneath this source's directory which are relevant for
    /// building `pkg`.
    ///
    /// The basic assumption of this method is that all files in the directory
    /// are relevant for building this package, but it also contains logic to
    /// use other methods like .gitignore to filter the list of files.
    pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
        let root = pkg.root();

        let parse = |p: &String| {
            Pattern::new(p).map_err(|e| {
                human(format!("could not parse pattern `{}`: {}", p, e))
            })
        };
        let exclude = try!(pkg.manifest().exclude().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());
        let include = try!(pkg.manifest().include().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());

        let mut filter = |p: &Path| {
            let relative_path = util::without_prefix(p, &root).unwrap();
            include.iter().any(|p| p.matches_path(&relative_path)) || {
                include.len() == 0 &&
                 !exclude.iter().any(|p| p.matches_path(&relative_path))
            }
        };

        // If this package is a git repository, then we really do want to query
        // the git repository as it takes into account items such as .gitignore.
        // We're not quite sure where the git repository is, however, so we do a
        // bit of a probe.
        //
        // We check all packages in this source that are ancestors of the
        // specified package (including the same package) to see if they're at
        // the root of the git repository. This isn't always true, but it'll get
        // us there most of the time!.
        let repo = self.packages.iter()
                       .map(|pkg| pkg.root())
                       .filter(|path| root.starts_with(path))
                       .filter_map(|path| git2::Repository::open(&path).ok())
                       .next();
        match repo {
            Some(repo) => self.list_files_git(pkg, repo, &mut filter),
            None => self.list_files_walk(pkg, &mut filter),
        }
    }

    fn list_files_git(&self, pkg: &Package, repo: git2::Repository,
                      filter: &mut FnMut(&Path) -> bool)
                      -> CargoResult<Vec<PathBuf>> {
        warn!("list_files_git {}", pkg.package_id());
        let index = try!(repo.index());
        let root = try!(repo.workdir().chain_error(|| {
            internal_error("Can't list files on a bare repository.", "")
        }));
        let pkg_path = pkg.root();

        let mut ret = Vec::new();

        // We use information from the git repository to guide use in traversing
        // its tree. The primary purpose of this is to take advantage of the
        // .gitignore and auto-ignore files that don't matter.
        //
        // Here we're also careful to look at both tracked an untracked files as
        // the untracked files are often part of a build and may become relevant
        // as part of a future commit.
        let index_files = index.iter().map(|entry| {
            use libgit2_sys::git_filemode_t::GIT_FILEMODE_COMMIT;
            let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
            (join(&root, &entry.path), Some(is_dir))
        });
        let mut opts = git2::StatusOptions::new();
        opts.include_untracked(true);
        let statuses = try!(repo.statuses(Some(&mut opts)));
        let untracked = statuses.iter().map(|entry| {
            (join(&root, entry.path_bytes()), None)
        });

        'outer: for (file_path, is_dir) in index_files.chain(untracked) {
            let file_path = try!(file_path);

            // Filter out files outside this package.
            if !file_path.starts_with(pkg_path) { continue }

            // Filter out Cargo.lock and target always
            {
                let fname = file_path.file_name().and_then(|s| s.to_str());
                if fname == Some("Cargo.lock") { continue }
                if fname == Some("target") { continue }
            }

            // Filter out sub-packages of this package
            for other_pkg in self.packages.iter().filter(|p| *p != pkg) {
                let other_path = other_pkg.root();
                if other_path.starts_with(pkg_path) &&
                   file_path.starts_with(other_path) {
                    continue 'outer;
                }
            }

            let is_dir = is_dir.or_else(|| {
                fs::metadata(&file_path).ok().map(|m| m.is_dir())
            }).unwrap_or(false);
            if is_dir {
                warn!("  found submodule {}", file_path.display());
                let rel = util::without_prefix(&file_path, &root).unwrap();
                let rel = try!(rel.to_str().chain_error(|| {
                    human(format!("invalid utf-8 filename: {}", rel.display()))
                }));
                // Git submodules are currently only named through `/` path
                // separators, explicitly not `\` which windows uses. Who knew?
                let rel = rel.replace(r"\", "/");
                match repo.find_submodule(&rel).and_then(|s| s.open()) {
                    Ok(repo) => {
                        let files = try!(self.list_files_git(pkg, repo, filter));
                        ret.extend(files.into_iter());
                    }
                    Err(..) => {
                        try!(PathSource::walk(&file_path, &mut ret, false,
                                              filter));
                    }
                }
            } else if (*filter)(&file_path) {
                // We found a file!
                warn!("  found {}", file_path.display());
                ret.push(file_path);
            }
        }
        return Ok(ret);

        #[cfg(unix)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::os::unix::prelude::*;
            use std::ffi::OsStr;
            Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
        }
        #[cfg(windows)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::str;
            match str::from_utf8(data) {
                Ok(s) => Ok(path.join(s)),
                Err(..) => Err(internal("cannot process path in git with a non \
                                         unicode filename")),
            }
        }
    }

    fn list_files_walk(&self, pkg: &Package, filter: &mut FnMut(&Path) -> bool)
                       -> CargoResult<Vec<PathBuf>> {
        let mut ret = Vec::new();
        for pkg in self.packages.iter().filter(|p| *p == pkg) {
            let loc = pkg.root();
            try!(PathSource::walk(loc, &mut ret, true, filter));
        }
        return Ok(ret);
    }

    fn walk(path: &Path, ret: &mut Vec<PathBuf>,
            is_root: bool, filter: &mut FnMut(&Path) -> bool) -> CargoResult<()>
    {
        if !fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
            if (*filter)(path) {
                ret.push(path.to_path_buf());
            }
            return Ok(())
        }
        // Don't recurse into any sub-packages that we have
        if !is_root && fs::metadata(&path.join("Cargo.toml")).is_ok() {
            return Ok(())
        }
        for dir in try!(fs::read_dir(path)) {
            let dir = try!(dir).path();
            match (is_root, dir.file_name().and_then(|s| s.to_str())) {
                (_,    Some(".git")) |
                (true, Some("target")) |
                (true, Some("Cargo.lock")) => continue,
                _ => {}
            }
            try!(PathSource::walk(&dir, ret, false, filter));
        }
        return Ok(())
    }
}

impl<'cfg> Debug for PathSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "the paths source")
    }
}

impl<'cfg> Registry for PathSource<'cfg> {
    fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        self.packages.query(dep)
    }
}

impl<'cfg> Source for PathSource<'cfg> {
    fn update(&mut self) -> CargoResult<()> {
        if !self.updated {
            let packages = try!(self.read_packages());
            self.packages.extend(packages.into_iter());
            self.updated = true;
        }

        Ok(())
    }

    fn download(&mut self, _: &[PackageId])  -> CargoResult<()>{
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        trace!("getting packages; ids={:?}", ids);

        Ok(self.packages.iter()
           .filter(|pkg| ids.iter().any(|id| pkg.package_id() == id))
           .map(|pkg| pkg.clone())
           .collect())
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        if !self.updated {
            return Err(internal_error("BUG: source was not updated", ""));
        }

        let mut max = FileTime::zero();
        for file in try!(self.list_files(pkg)).iter() {
            // An fs::stat error here is either because path is a
            // broken symlink, a permissions error, or a race
            // condition where this path was rm'ed - either way,
            // we can ignore the error and treat the path's mtime
            // as 0.
            let mtime = fs::metadata(file).map(|meta| {
                FileTime::from_last_modification_time(&meta)
            }).unwrap_or(FileTime::zero());
            warn!("{} {}", mtime, file.display());
            max = cmp::max(max, mtime);
        }
        trace!("fingerprint {}: {}", self.path.display(), max);
        Ok(max.to_string())
    }
}
<commit_msg>Auto merge of #1746 - alexcrichton:less-git-operations, r=huonw<commit_after>use std::cmp;
use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use filetime::FileTime;
use git2;
use glob::Pattern;

use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
use ops;
use util::{self, CargoResult, internal, internal_error, human, ChainError};
use util::Config;

pub struct PathSource<'cfg> {
    id: SourceId,
    path: PathBuf,
    updated: bool,
    packages: Vec<Package>,
    config: &'cfg Config,
}

// TODO: Figure out if packages should be discovered in new or self should be
// mut and packages are discovered in update
impl<'cfg> PathSource<'cfg> {
    pub fn for_path(path: &Path, config: &'cfg Config)
                    -> CargoResult<PathSource<'cfg>> {
        trace!("PathSource::for_path; path={}", path.display());
        Ok(PathSource::new(path, &try!(SourceId::for_path(path)), config))
    }

    /// Invoked with an absolute path to a directory that contains a Cargo.toml.
    /// The source will read the manifest and find any other packages contained
    /// in the directory structure reachable by the root manifest.
    pub fn new(path: &Path, id: &SourceId, config: &'cfg Config)
               -> PathSource<'cfg> {
        trace!("new; id={}", id);

        PathSource {
            id: id.clone(),
            path: path.to_path_buf(),
            updated: false,
            packages: Vec::new(),
            config: config,
        }
    }

    pub fn root_package(&self) -> CargoResult<Package> {
        trace!("root_package; source={:?}", self);

        if !self.updated {
            return Err(internal("source has not been updated"))
        }

        match self.packages.iter().find(|p| p.root() == &*self.path) {
            Some(pkg) => Ok(pkg.clone()),
            None => Err(internal("no package found in source"))
        }
    }

    fn read_packages(&self) -> CargoResult<Vec<Package>> {
        if self.updated {
            Ok(self.packages.clone())
        } else if self.id.is_path() && self.id.precise().is_some() {
            // If our source id is a path and it's listed with a precise
            // version, then it means that we're not allowed to have nested
            // dependencies (they've been rewritten to crates.io dependencies)
            // In this case we specifically read just one package, not a list of
            // packages.
            let path = self.path.join("Cargo.toml");
            let (pkg, _) = try!(ops::read_package(&path, &self.id,
                                                  self.config));
            Ok(vec![pkg])
        } else {
            ops::read_packages(&self.path, &self.id, self.config)
        }
    }

    /// List all files relevant to building this package inside this source.
    ///
    /// This function will use the appropriate methods to determine what is the
    /// set of files underneath this source's directory which are relevant for
    /// building `pkg`.
    ///
    /// The basic assumption of this method is that all files in the directory
    /// are relevant for building this package, but it also contains logic to
    /// use other methods like .gitignore to filter the list of files.
    pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
        let root = pkg.root();

        let parse = |p: &String| {
            Pattern::new(p).map_err(|e| {
                human(format!("could not parse pattern `{}`: {}", p, e))
            })
        };
        let exclude = try!(pkg.manifest().exclude().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());
        let include = try!(pkg.manifest().include().iter()
                              .map(|p| parse(p)).collect::<Result<Vec<_>, _>>());

        let mut filter = |p: &Path| {
            let relative_path = util::without_prefix(p, &root).unwrap();
            include.iter().any(|p| p.matches_path(&relative_path)) || {
                include.len() == 0 &&
                 !exclude.iter().any(|p| p.matches_path(&relative_path))
            }
        };

        // If this package is a git repository, then we really do want to query
        // the git repository as it takes into account items such as .gitignore.
        // We're not quite sure where the git repository is, however, so we do a
        // bit of a probe.
        //
        // We check all packages in this source that are ancestors of the
        // specified package (including the same package) to see if they're at
        // the root of the git repository. This isn't always true, but it'll get
        // us there most of the time!.
        let repo = self.packages.iter()
                       .map(|pkg| pkg.root())
                       .filter(|path| root.starts_with(path))
                       .filter_map(|path| git2::Repository::open(&path).ok())
                       .next();
        match repo {
            Some(repo) => self.list_files_git(pkg, repo, &mut filter),
            None => self.list_files_walk(pkg, &mut filter),
        }
    }

    fn list_files_git(&self, pkg: &Package, repo: git2::Repository,
                      filter: &mut FnMut(&Path) -> bool)
                      -> CargoResult<Vec<PathBuf>> {
        warn!("list_files_git {}", pkg.package_id());
        let index = try!(repo.index());
        let root = try!(repo.workdir().chain_error(|| {
            internal_error("Can't list files on a bare repository.", "")
        }));
        let pkg_path = pkg.root();

        let mut ret = Vec::new();

        // We use information from the git repository to guide use in traversing
        // its tree. The primary purpose of this is to take advantage of the
        // .gitignore and auto-ignore files that don't matter.
        //
        // Here we're also careful to look at both tracked an untracked files as
        // the untracked files are often part of a build and may become relevant
        // as part of a future commit.
        let index_files = index.iter().map(|entry| {
            use libgit2_sys::git_filemode_t::GIT_FILEMODE_COMMIT;
            let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
            (join(&root, &entry.path), Some(is_dir))
        });
        let mut opts = git2::StatusOptions::new();
        opts.include_untracked(true);
        if let Some(suffix) = util::without_prefix(pkg_path, &root) {
            opts.pathspec(suffix);
        }
        let statuses = try!(repo.statuses(Some(&mut opts)));
        let untracked = statuses.iter().map(|entry| {
            (join(&root, entry.path_bytes()), None)
        });

        'outer: for (file_path, is_dir) in index_files.chain(untracked) {
            let file_path = try!(file_path);

            // Filter out files outside this package.
            if !file_path.starts_with(pkg_path) { continue }

            // Filter out Cargo.lock and target always
            {
                let fname = file_path.file_name().and_then(|s| s.to_str());
                if fname == Some("Cargo.lock") { continue }
                if fname == Some("target") { continue }
            }

            // Filter out sub-packages of this package
            for other_pkg in self.packages.iter().filter(|p| *p != pkg) {
                let other_path = other_pkg.root();
                if other_path.starts_with(pkg_path) &&
                   file_path.starts_with(other_path) {
                    continue 'outer;
                }
            }

            let is_dir = is_dir.or_else(|| {
                fs::metadata(&file_path).ok().map(|m| m.is_dir())
            }).unwrap_or(false);
            if is_dir {
                warn!("  found submodule {}", file_path.display());
                let rel = util::without_prefix(&file_path, &root).unwrap();
                let rel = try!(rel.to_str().chain_error(|| {
                    human(format!("invalid utf-8 filename: {}", rel.display()))
                }));
                // Git submodules are currently only named through `/` path
                // separators, explicitly not `\` which windows uses. Who knew?
                let rel = rel.replace(r"\", "/");
                match repo.find_submodule(&rel).and_then(|s| s.open()) {
                    Ok(repo) => {
                        let files = try!(self.list_files_git(pkg, repo, filter));
                        ret.extend(files.into_iter());
                    }
                    Err(..) => {
                        try!(PathSource::walk(&file_path, &mut ret, false,
                                              filter));
                    }
                }
            } else if (*filter)(&file_path) {
                // We found a file!
                warn!("  found {}", file_path.display());
                ret.push(file_path);
            }
        }
        return Ok(ret);

        #[cfg(unix)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::os::unix::prelude::*;
            use std::ffi::OsStr;
            Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
        }
        #[cfg(windows)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::str;
            match str::from_utf8(data) {
                Ok(s) => Ok(path.join(s)),
                Err(..) => Err(internal("cannot process path in git with a non \
                                         unicode filename")),
            }
        }
    }

    fn list_files_walk(&self, pkg: &Package, filter: &mut FnMut(&Path) -> bool)
                       -> CargoResult<Vec<PathBuf>> {
        let mut ret = Vec::new();
        for pkg in self.packages.iter().filter(|p| *p == pkg) {
            let loc = pkg.root();
            try!(PathSource::walk(loc, &mut ret, true, filter));
        }
        return Ok(ret);
    }

    fn walk(path: &Path, ret: &mut Vec<PathBuf>,
            is_root: bool, filter: &mut FnMut(&Path) -> bool) -> CargoResult<()>
    {
        if !fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
            if (*filter)(path) {
                ret.push(path.to_path_buf());
            }
            return Ok(())
        }
        // Don't recurse into any sub-packages that we have
        if !is_root && fs::metadata(&path.join("Cargo.toml")).is_ok() {
            return Ok(())
        }
        for dir in try!(fs::read_dir(path)) {
            let dir = try!(dir).path();
            match (is_root, dir.file_name().and_then(|s| s.to_str())) {
                (_,    Some(".git")) |
                (true, Some("target")) |
                (true, Some("Cargo.lock")) => continue,
                _ => {}
            }
            try!(PathSource::walk(&dir, ret, false, filter));
        }
        return Ok(())
    }
}

impl<'cfg> Debug for PathSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "the paths source")
    }
}

impl<'cfg> Registry for PathSource<'cfg> {
    fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        self.packages.query(dep)
    }
}

impl<'cfg> Source for PathSource<'cfg> {
    fn update(&mut self) -> CargoResult<()> {
        if !self.updated {
            let packages = try!(self.read_packages());
            self.packages.extend(packages.into_iter());
            self.updated = true;
        }

        Ok(())
    }

    fn download(&mut self, _: &[PackageId])  -> CargoResult<()>{
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        trace!("getting packages; ids={:?}", ids);

        Ok(self.packages.iter()
           .filter(|pkg| ids.iter().any(|id| pkg.package_id() == id))
           .map(|pkg| pkg.clone())
           .collect())
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        if !self.updated {
            return Err(internal_error("BUG: source was not updated", ""));
        }

        let mut max = FileTime::zero();
        for file in try!(self.list_files(pkg)).iter() {
            // An fs::stat error here is either because path is a
            // broken symlink, a permissions error, or a race
            // condition where this path was rm'ed - either way,
            // we can ignore the error and treat the path's mtime
            // as 0.
            let mtime = fs::metadata(file).map(|meta| {
                FileTime::from_last_modification_time(&meta)
            }).unwrap_or(FileTime::zero());
            warn!("{} {}", mtime, file.display());
            max = cmp::max(max, mtime);
        }
        trace!("fingerprint {}: {}", self.path.display(), max);
        Ok(max.to_string())
    }
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use crate::util::file;
use anyhow::Result;
use log::info;
use regex::Regex;
use std::io::Read;
use std::process::{Child, ChildStdout, Command, Stdio};
use std::time::{Duration, Instant};

/// Verilator startup options.
pub struct Options {
    /// The verilator executable.
    pub executable: String,
    /// The ROM image used to boot the CPU.
    pub rom_image: String,
    /// The flash image stored in internal flash memory.
    pub flash_image: String,
    /// The OTP settings.
    pub otp_image: String,
    /// Any extra arguments to verilator.
    pub extra_args: Vec<String>,
}

pub struct Subprocess {
    child: Child,
    stdout: ChildStdout,
    accumulated_output: String,
}

impl Subprocess {
    /// Starts a verilator [`Subprocess`] based on [`Options`].
    pub fn from_options(options: Options) -> Result<Self> {
        let mut command = Command::new(&options.executable);
        let mut args = Vec::new();

        if !options.rom_image.is_empty() {
            args.push(format!("--meminit=rom,{}", options.rom_image));
        }
        if !options.flash_image.is_empty() {
            args.push(format!("--meminit=flash,{}", options.flash_image));
        }
        if !options.otp_image.is_empty() {
            args.push(format!("--meminit=otp,{}", options.otp_image));
        }
        args.extend_from_slice(&options.extra_args);
        command.args(&args[..]);

        info!("Spawning verilator: {:?} {:?}", options.executable, args);

        let mut child = command
            .stdin(Stdio::null())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()?;
        let stdout = child.stdout.take().unwrap();
        Ok(Subprocess {
            child,
            stdout,
            accumulated_output: String::default(),
        })
    }

    /// Accumulates output from verilator's `stdout`.
    pub fn accumulate(&mut self, timeout: Duration) -> Result<()> {
        let mut buf = [0u8; 256];
        file::wait_read_timeout(&self.stdout, timeout)?;
        let n = self.stdout.read(&mut buf)?;
        let s = String::from_utf8_lossy(&buf[..n]);
        self.accumulated_output.push_str(&s);
        Ok(())
    }

    /// Finds a string within the verilator output.
    /// It is assumed that the [`Regex`] `re` has exactly one capture.
    pub fn find(&mut self, re: &Regex, timeout: Duration) -> Result<String> {
        assert_eq!(re.captures_len(), 1);
        let deadline = Instant::now() + timeout;
        loop {
            if let Some(captures) = re.captures(&self.accumulated_output) {
                let val = captures.get(1).expect("expected a capture");
                return Ok(val.as_str().to_owned());
            } else {
                self.accumulate(deadline.saturating_duration_since(Instant::now()))?;
            }
        }
    }

    /// Kill the verilator subprocess.
    pub fn kill(&mut self) -> Result<()> {
        self.child.kill()?;
        Ok(())
    }
}
<commit_msg>[opentitantool] Correct an error in the verilator transport<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use crate::util::file;
use anyhow::Result;
use log::info;
use regex::Regex;
use std::io::{ErrorKind, Read};
use std::process::{Child, ChildStdout, Command, Stdio};
use std::time::{Duration, Instant};

/// Verilator startup options.
pub struct Options {
    /// The verilator executable.
    pub executable: String,
    /// The ROM image used to boot the CPU.
    pub rom_image: String,
    /// The flash image stored in internal flash memory.
    pub flash_image: String,
    /// The OTP settings.
    pub otp_image: String,
    /// Any extra arguments to verilator.
    pub extra_args: Vec<String>,
}

pub struct Subprocess {
    child: Child,
    stdout: ChildStdout,
    accumulated_output: String,
}

impl Subprocess {
    /// Starts a verilator [`Subprocess`] based on [`Options`].
    pub fn from_options(options: Options) -> Result<Self> {
        let mut command = Command::new(&options.executable);
        let mut args = Vec::new();

        if !options.rom_image.is_empty() {
            args.push(format!("--meminit=rom,{}", options.rom_image));
        }
        if !options.flash_image.is_empty() {
            args.push(format!("--meminit=flash,{}", options.flash_image));
        }
        if !options.otp_image.is_empty() {
            args.push(format!("--meminit=otp,{}", options.otp_image));
        }
        args.extend_from_slice(&options.extra_args);
        command.args(&args[..]);

        info!("Spawning verilator: {:?} {:?}", options.executable, args);

        let mut child = command
            .stdin(Stdio::null())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()?;
        let stdout = child.stdout.take().unwrap();
        Ok(Subprocess {
            child,
            stdout,
            accumulated_output: String::default(),
        })
    }

    /// Accumulates output from verilator's `stdout`.
    pub fn accumulate(&mut self, timeout: Duration) -> Result<()> {
        let mut buf = [0u8; 256];
        file::wait_read_timeout(&self.stdout, timeout)?;
        let n = self.stdout.read(&mut buf)?;
        let s = String::from_utf8_lossy(&buf[..n]);
        self.accumulated_output.push_str(&s);
        Ok(())
    }

    /// Finds a string within the verilator output.
    /// It is assumed that the [`Regex`] `re` has exactly one capture group.
    pub fn find(&mut self, re: &Regex, timeout: Duration) -> Result<String> {
        // Regex captures_len: Capture group 0 is the full match.  Subsequent
        // capture groups are the individual capture groups in the regex.
        // We expect only one user-specified capture group in the regex,
        // and thus expect a capture length of two.
        assert_eq!(re.captures_len(), 2);
        let deadline = Instant::now() + timeout;
        loop {
            if let Some(captures) = re.captures(&self.accumulated_output) {
                let val = captures.get(1).expect("expected a capture");
                return Ok(val.as_str().to_owned());
            } else {
                self.accumulate(deadline.saturating_duration_since(Instant::now()))?;
            }
        }
    }

    /// Kill the verilator subprocess.
    pub fn kill(&mut self) -> Result<()> {
        match self.child.kill() {
            Ok(_) => Ok(()),
            Err(error) => {
                if error.kind() == ErrorKind::InvalidInput {
                    // Don't care if the child has already exited.
                    Ok(())
                } else {
                    Err(error.into())
                }
            }
        }
    }
}

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

    fn echo_subprocess() -> Result<Subprocess> {
        let options = Options {
            executable: "/bin/echo".to_owned(),
            rom_image: "".to_owned(),
            flash_image: "".to_owned(),
            otp_image: "".to_owned(),
            extra_args: vec!["abc 123 def 456".to_owned()],
        };
        Subprocess::from_options(options)
    }

    #[test]
    fn test_find_regex() -> Result<()> {
        let mut subprocess = echo_subprocess()?;
        let regex = Regex::new("abc (.*) def")?;
        let found = subprocess.find(&regex, Duration::from_millis(5000))?;
        assert_eq!(found, "123");
        Ok(())
    }

    #[test]
    fn test_kill() -> Result<()> {
        let mut subprocess = echo_subprocess()?;
        subprocess.kill()?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use parser::Identifier;
use operations::{Operation, Error};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_u8(scope: &mut ScopeStack) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let unit_type = scope.primitives().unit();

    let u8_type = scope.declare_type(
        Identifier::from("u8"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("u8", u8_type);

    scope.declare_builtin_function(
        // Special method for converting from literal
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::convert::From<{unsigned integer}>"),
        ItemType::Function {
            // This takes a single literal of the type specific within
            // the curly braces {} in the name
            args: vec![FuncArgType::Array {item: u8_type, size: None}],
            // Return type signifies which type we are declaring supports integer literals
            return_type: u8_type,
        },
        move |scope, args, target| {
            let value = args[0].numeric_literal_value();

            // 8 is the size of this numeric type in bytes
            // We use >= here because 0 is reserved for zero so we
            // have (2^size - 1) numbers available
            if value >= (1 << 8) {
                Err(Error::OverflowingLiteral {
                    typ: scope.get_type(u8_type).clone()
                })
            }
            else {
                Ok(vec![
                    Operation::Increment {
                        target: target.position(),
                        amount: value as usize,
                    }
                ])
            }
        }
    );

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_, args, _| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            Ok(vec![
                // Need to display numbers as numbers by first incrementing them to the proper
                // ascii code and then decrementing them again
                Operation::Increment {
                    target: mem.position(),
                    amount: b'0',
                },
                Operation::Write {
                    target: mem,
                },
                // Need to make sure we reverse the operation after or else there will be
                // unintended consequences
                Operation::Decrement {
                    target: mem.position(),
                    amount: b'0',
                },
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("increment"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Increment {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("decrement"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Decrement {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    // Method on [u8; _]
    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Array {item: u8_type, size: None}],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::Array {memory, ..} => memory,
                _ => unreachable!(),
            };

            Ok(vec![
                Operation::Write {
                    target: mem,
                }
            ])
        }
    );

    u8_type
}

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

    use operations::scope::{ScopeType};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_u8(&mut scope);

        let u8_type_id = match **scope.lookup_type(&Identifier::from("u8")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().u8(), u8_type_id);
    }
}
<commit_msg>Fixed implementation of [u8; N] print to support literals<commit_after>use parser::Identifier;
use operations::{Operation, Error};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_u8(scope: &mut ScopeStack) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let unit_type = scope.primitives().unit();

    let u8_type = scope.declare_type(
        Identifier::from("u8"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("u8", u8_type);

    scope.declare_builtin_function(
        // Special method for converting from literal
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::convert::From<{unsigned integer}>"),
        ItemType::Function {
            // This takes a single literal of the type specific within
            // the curly braces {} in the name
            args: vec![FuncArgType::Array {item: u8_type, size: None}],
            // Return type signifies which type we are declaring supports integer literals
            return_type: u8_type,
        },
        move |scope, args, target| {
            let value = args[0].numeric_literal_value();

            // 8 is the size of this numeric type in bytes
            // We use >= here because 0 is reserved for zero so we
            // have (2^size - 1) numbers available
            if value >= (1 << 8) {
                Err(Error::OverflowingLiteral {
                    typ: scope.get_type(u8_type).clone()
                })
            }
            else {
                Ok(vec![
                    Operation::Increment {
                        target: target.position(),
                        amount: value as u8,
                    }
                ])
            }
        }
    );

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_, args, _| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            Ok(vec![
                // Need to display numbers as numbers by first incrementing them to the proper
                // ascii code and then decrementing them again
                Operation::Increment {
                    target: mem.position(),
                    amount: b'0',
                },
                Operation::Write {
                    target: mem,
                },
                // Need to make sure we reverse the operation after or else there will be
                // unintended consequences
                Operation::Decrement {
                    target: mem.position(),
                    amount: b'0',
                },
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("increment"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Increment {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    scope.declare_builtin_function(
        Identifier::from("decrement"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(u8_type)],
            return_type: unit_type,
        },
        move |_scope, args, _target| {
            let mem = match args[0] {
                ScopeItem::TypedBlock {memory, ..} => memory,
                _ => unreachable!(),
            };

            debug_assert!(mem.size() == 1);

            Ok(vec![
                Operation::Decrement {
                    target: mem.position(),
                    amount: 1,
                }
            ])
        }
    );

    // Method on [u8; _]
    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Array {item: u8_type, size: None}],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::Array {memory, ..} => Ok(vec![
                    Operation::Write {
                        target: memory,
                    },
                ]),
                ScopeItem::ByteLiteral(ref bytes) => {
                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    Ok(vec![
                        Operation::TempAllocate {
                            temp: mem,
                            body: bytes.iter().flat_map(|&ch| vec![
                                Operation::Increment {
                                    target: mem.position(),
                                    amount: ch,
                                },
                                Operation::Write {
                                    target: mem,
                                },
                                Operation::Decrement {
                                    target: mem.position(),
                                    amount: ch,
                                },
                            ]).collect(),
                        },
                    ])
                },
                _ => unreachable!(),
            }
        }
    );

    u8_type
}

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

    use operations::scope::{ScopeType};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_u8(&mut scope);

        let u8_type_id = match **scope.lookup_type(&Identifier::from("u8")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().u8(), u8_type_id);
    }
}
<|endoftext|>


<commit_before>
<commit_msg>rna-transcription: Added template to stub file<commit_after>#[derive(Debug, PartialEq)]
pub struct DNA;

#[derive(Debug, PartialEq)]
pub struct RNA;

impl DNA {
    pub fn new(dna: &str) -> Result<DNA, usize> {
        unimplemented!("Construct new DNA from '{}' string. If string contains invalid nucleotides return index of first invalid nucleotide", dna);
    }

    pub fn to_rna(self) -> RNA {
        unimplemented!("Transform DNA {:?} into corresponding RNA", self);
    }
}

impl RNA {
    pub fn new(rna: &str) -> Result<RNA, usize> {
        unimplemented!("Construct new RNA from '{}' string. If string contains invalid nucleotides return index of first invalid nucleotide", rna);
    }
}
<|endoftext|>


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

#![forbid(unsafe_code)]

#[cfg(test)]
mod test;

use crate::logging::{LogEntry, LogSchema};
use anyhow::{anyhow, ensure, Result};
use consensus_types::block::Block as ConsensusBlock;
use diem_crypto::HashValue;
use diem_infallible::Mutex;
use diem_logger::{debug, info};
use diem_types::{ledger_info::LedgerInfo, proof::definition::LeafCount};
use executor_types::{Error, ExecutedChunk};
use std::{
    collections::{hash_map::Entry, HashMap},
    sync::{Arc, Weak},
};
use storage_interface::DbReader;

pub struct Block {
    pub id: HashValue,
    pub output: ExecutedChunk,
    children: Mutex<Vec<Arc<Block>>>,
    block_lookup: Arc<BlockLookup>,
}

impl Drop for Block {
    fn drop(&mut self) {
        self.block_lookup.remove(self.id);
        debug!(
            LogSchema::new(LogEntry::SpeculationCache).block_id(self.id),
            "Block dropped."
        );
    }
}

impl Block {
    fn add_child(&self, child: Arc<Self>) {
        self.children.lock().push(child)
    }

    pub fn num_persisted_transactions(&self) -> LeafCount {
        self.output.result_view.txn_accumulator().num_leaves()
    }

    fn ensure_has_child(&self, child_id: HashValue) -> Result<()> {
        ensure!(
            self.children.lock().iter().any(|c| c.id == child_id),
            "{:x} doesn't have child {:x}",
            self.id,
            child_id,
        );
        Ok(())
    }
}

fn epoch_genesis_block_id(ledger_info: &LedgerInfo) -> HashValue {
    ConsensusBlock::make_genesis_block_from_ledger_info(ledger_info).id()
}

struct BlockLookupInner(HashMap<HashValue, Weak<Block>>);

impl BlockLookupInner {
    fn multi_get(&self, ids: &[HashValue]) -> Result<Vec<Option<Arc<Block>>>> {
        let mut blocks = Vec::with_capacity(ids.len());
        for id in ids {
            let block = self
                .0
                .get(id)
                .map(|weak| {
                    weak.upgrade()
                        .ok_or_else(|| anyhow!("Block {:x} has been deallocated.", id))
                })
                .transpose()?;
            blocks.push(block)
        }
        Ok(blocks)
    }

    fn get(&self, id: HashValue) -> Result<Option<Arc<Block>>> {
        Ok(self.multi_get(&[id])?.pop().expect("Must exist."))
    }

    fn fetch_or_add_block(
        &mut self,
        id: HashValue,
        output: ExecutedChunk,
        parent_id: Option<HashValue>,
        block_lookup: &Arc<BlockLookup>,
    ) -> Result<(Arc<Block>, bool, Option<Arc<Block>>)> {
        let parent_block = parent_id
            .map(|id| {
                self.get(id)?
                    .ok_or_else(|| anyhow!("parent block {} doesn't exist."))
            })
            .transpose()?;

        match self.0.entry(id) {
            Entry::Occupied(entry) => {
                let existing = entry
                    .get()
                    .upgrade()
                    .ok_or_else(|| anyhow!("block dropped unexpected."))?;
                ensure!(
                    existing
                        .output
                        .result_view
                        .is_same_view(&output.result_view),
                    "Different block with same id {}",
                    id,
                );
                Ok((existing, true, parent_block))
            }
            Entry::Vacant(entry) => {
                let block = Arc::new(Block {
                    id,
                    output,
                    children: Mutex::new(Vec::new()),
                    block_lookup: block_lookup.clone(),
                });
                entry.insert(Arc::downgrade(&block));
                Ok((block, false, parent_block))
            }
        }
    }
}

struct BlockLookup {
    inner: Mutex<BlockLookupInner>,
}

impl BlockLookup {
    fn new() -> Self {
        Self {
            inner: Mutex::new(BlockLookupInner(HashMap::new())),
        }
    }

    fn multi_get(&self, ids: &[HashValue]) -> Result<Vec<Option<Arc<Block>>>> {
        self.inner.lock().multi_get(ids)
    }

    fn fetch_or_add_block(
        self: &Arc<Self>,
        id: HashValue,
        output: ExecutedChunk,
        parent_id: Option<HashValue>,
    ) -> Result<Arc<Block>> {
        let (block, existing, parent_block) = self
            .inner
            .lock()
            .fetch_or_add_block(id, output, parent_id, self)?;

        if let Some(parent_block) = parent_block {
            if existing {
                parent_block.ensure_has_child(id)?;
            } else {
                parent_block.add_child(block.clone());
            }
        }

        Ok(block)
    }

    fn remove(&self, id: HashValue) {
        self.inner.lock().0.remove(&id);
    }
}

pub struct BlockTree {
    root: Mutex<Arc<Block>>,
    block_lookup: Arc<BlockLookup>,
}

impl BlockTree {
    pub fn new(db: &Arc<dyn DbReader>) -> Result<Self> {
        let block_lookup = Arc::new(BlockLookup::new());
        let root = Mutex::new(Self::root_from_db(&block_lookup, db)?);

        Ok(Self { root, block_lookup })
    }

    pub fn reset(&self, db: &Arc<dyn DbReader>) -> Result<()> {
        *self.root.lock() = Self::root_from_db(&self.block_lookup, db)?;
        Ok(())
    }

    pub fn get_block(&self, id: HashValue) -> Result<Arc<Block>> {
        Ok(self.get_blocks(&[id])?.pop().expect("Must exist."))
    }

    pub fn get_blocks(&self, ids: &[HashValue]) -> Result<Vec<Arc<Block>>> {
        let lookup_result = self.block_lookup.multi_get(ids)?;

        itertools::zip_eq(ids, lookup_result)
            .map(|(id, res)| res.ok_or_else(|| Error::BlockNotFound(*id).into()))
            .collect()
    }

    pub fn get_blocks_opt(&self, ids: &[HashValue]) -> Result<Vec<Option<Arc<Block>>>> {
        self.block_lookup.multi_get(ids)
    }

    fn root_from_db(block_lookup: &Arc<BlockLookup>, db: &Arc<dyn DbReader>) -> Result<Arc<Block>> {
        let startup_info = db
            .get_startup_info()?
            .ok_or_else(|| anyhow!("DB not bootstrapped."))?;
        let ledger_info = startup_info.latest_ledger_info.ledger_info();

        let id = if ledger_info.ends_epoch() {
            epoch_genesis_block_id(ledger_info)
        } else {
            ledger_info.consensus_block_id()
        };

        let result_view = startup_info.committed_tree_state.clone().into();
        block_lookup.fetch_or_add_block(id, ExecutedChunk::new_empty(result_view), None)
    }

    pub fn prune(&self, ledger_info: &LedgerInfo) -> Result<()> {
        let committed_block_id = ledger_info.consensus_block_id();
        let last_committed_block = self.get_block(committed_block_id)?;

        let root = if ledger_info.ends_epoch() {
            let epoch_genesis_id = epoch_genesis_block_id(ledger_info);
            info!(
                LogSchema::new(LogEntry::SpeculationCache)
                    .root_block_id(epoch_genesis_id)
                    .original_reconfiguration_block_id(committed_block_id),
                "Updated with a new root block as a virtual block of reconfiguration block"
            );
            self.block_lookup.fetch_or_add_block(
                epoch_genesis_id,
                ExecutedChunk::new_empty(last_committed_block.output.result_view.clone()),
                None,
            )?
        } else {
            info!(
                LogSchema::new(LogEntry::SpeculationCache).root_block_id(committed_block_id),
                "Updated with a new root block",
            );
            last_committed_block
        };

        *self.root.lock() = root;
        Ok(())
    }

    pub fn add_block(
        &self,
        parent_block_id: HashValue,
        id: HashValue,
        output: ExecutedChunk,
    ) -> Result<Arc<Block>> {
        self.block_lookup
            .fetch_or_add_block(id, output, Some(parent_block_id))
    }

    pub fn root_block(&self) -> Arc<Block> {
        self.root.lock().clone()
    }
}
<commit_msg>[executor] fix anyhow error msg interpolation<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

#[cfg(test)]
mod test;

use crate::logging::{LogEntry, LogSchema};
use anyhow::{anyhow, ensure, Result};
use consensus_types::block::Block as ConsensusBlock;
use diem_crypto::HashValue;
use diem_infallible::Mutex;
use diem_logger::{debug, info};
use diem_types::{ledger_info::LedgerInfo, proof::definition::LeafCount};
use executor_types::{Error, ExecutedChunk};
use std::{
    collections::{hash_map::Entry, HashMap},
    sync::{Arc, Weak},
};
use storage_interface::DbReader;

pub struct Block {
    pub id: HashValue,
    pub output: ExecutedChunk,
    children: Mutex<Vec<Arc<Block>>>,
    block_lookup: Arc<BlockLookup>,
}

impl Drop for Block {
    fn drop(&mut self) {
        self.block_lookup.remove(self.id);
        debug!(
            LogSchema::new(LogEntry::SpeculationCache).block_id(self.id),
            "Block dropped."
        );
    }
}

impl Block {
    fn add_child(&self, child: Arc<Self>) {
        self.children.lock().push(child)
    }

    pub fn num_persisted_transactions(&self) -> LeafCount {
        self.output.result_view.txn_accumulator().num_leaves()
    }

    fn ensure_has_child(&self, child_id: HashValue) -> Result<()> {
        ensure!(
            self.children.lock().iter().any(|c| c.id == child_id),
            "{:x} doesn't have child {:x}",
            self.id,
            child_id,
        );
        Ok(())
    }
}

fn epoch_genesis_block_id(ledger_info: &LedgerInfo) -> HashValue {
    ConsensusBlock::make_genesis_block_from_ledger_info(ledger_info).id()
}

struct BlockLookupInner(HashMap<HashValue, Weak<Block>>);

impl BlockLookupInner {
    fn multi_get(&self, ids: &[HashValue]) -> Result<Vec<Option<Arc<Block>>>> {
        let mut blocks = Vec::with_capacity(ids.len());
        for id in ids {
            let block = self
                .0
                .get(id)
                .map(|weak| {
                    weak.upgrade()
                        .ok_or_else(|| anyhow!("Block {:x} has been deallocated.", id))
                })
                .transpose()?;
            blocks.push(block)
        }
        Ok(blocks)
    }

    fn get(&self, id: HashValue) -> Result<Option<Arc<Block>>> {
        Ok(self.multi_get(&[id])?.pop().expect("Must exist."))
    }

    fn fetch_or_add_block(
        &mut self,
        id: HashValue,
        output: ExecutedChunk,
        parent_id: Option<HashValue>,
        block_lookup: &Arc<BlockLookup>,
    ) -> Result<(Arc<Block>, bool, Option<Arc<Block>>)> {
        let parent_block = parent_id
            .map(|id| {
                self.get(id)?
                    .ok_or_else(|| anyhow!("parent block {:x} doesn't exist.", id))
            })
            .transpose()?;

        match self.0.entry(id) {
            Entry::Occupied(entry) => {
                let existing = entry
                    .get()
                    .upgrade()
                    .ok_or_else(|| anyhow!("block dropped unexpected."))?;
                ensure!(
                    existing
                        .output
                        .result_view
                        .is_same_view(&output.result_view),
                    "Different block with same id {:x}",
                    id,
                );
                Ok((existing, true, parent_block))
            }
            Entry::Vacant(entry) => {
                let block = Arc::new(Block {
                    id,
                    output,
                    children: Mutex::new(Vec::new()),
                    block_lookup: block_lookup.clone(),
                });
                entry.insert(Arc::downgrade(&block));
                Ok((block, false, parent_block))
            }
        }
    }
}

struct BlockLookup {
    inner: Mutex<BlockLookupInner>,
}

impl BlockLookup {
    fn new() -> Self {
        Self {
            inner: Mutex::new(BlockLookupInner(HashMap::new())),
        }
    }

    fn multi_get(&self, ids: &[HashValue]) -> Result<Vec<Option<Arc<Block>>>> {
        self.inner.lock().multi_get(ids)
    }

    fn fetch_or_add_block(
        self: &Arc<Self>,
        id: HashValue,
        output: ExecutedChunk,
        parent_id: Option<HashValue>,
    ) -> Result<Arc<Block>> {
        let (block, existing, parent_block) = self
            .inner
            .lock()
            .fetch_or_add_block(id, output, parent_id, self)?;

        if let Some(parent_block) = parent_block {
            if existing {
                parent_block.ensure_has_child(id)?;
            } else {
                parent_block.add_child(block.clone());
            }
        }

        Ok(block)
    }

    fn remove(&self, id: HashValue) {
        self.inner.lock().0.remove(&id);
    }
}

pub struct BlockTree {
    root: Mutex<Arc<Block>>,
    block_lookup: Arc<BlockLookup>,
}

impl BlockTree {
    pub fn new(db: &Arc<dyn DbReader>) -> Result<Self> {
        let block_lookup = Arc::new(BlockLookup::new());
        let root = Mutex::new(Self::root_from_db(&block_lookup, db)?);

        Ok(Self { root, block_lookup })
    }

    pub fn reset(&self, db: &Arc<dyn DbReader>) -> Result<()> {
        *self.root.lock() = Self::root_from_db(&self.block_lookup, db)?;
        Ok(())
    }

    pub fn get_block(&self, id: HashValue) -> Result<Arc<Block>> {
        Ok(self.get_blocks(&[id])?.pop().expect("Must exist."))
    }

    pub fn get_blocks(&self, ids: &[HashValue]) -> Result<Vec<Arc<Block>>> {
        let lookup_result = self.block_lookup.multi_get(ids)?;

        itertools::zip_eq(ids, lookup_result)
            .map(|(id, res)| res.ok_or_else(|| Error::BlockNotFound(*id).into()))
            .collect()
    }

    pub fn get_blocks_opt(&self, ids: &[HashValue]) -> Result<Vec<Option<Arc<Block>>>> {
        self.block_lookup.multi_get(ids)
    }

    fn root_from_db(block_lookup: &Arc<BlockLookup>, db: &Arc<dyn DbReader>) -> Result<Arc<Block>> {
        let startup_info = db
            .get_startup_info()?
            .ok_or_else(|| anyhow!("DB not bootstrapped."))?;
        let ledger_info = startup_info.latest_ledger_info.ledger_info();

        let id = if ledger_info.ends_epoch() {
            epoch_genesis_block_id(ledger_info)
        } else {
            ledger_info.consensus_block_id()
        };

        let result_view = startup_info.committed_tree_state.clone().into();
        block_lookup.fetch_or_add_block(id, ExecutedChunk::new_empty(result_view), None)
    }

    pub fn prune(&self, ledger_info: &LedgerInfo) -> Result<()> {
        let committed_block_id = ledger_info.consensus_block_id();
        let last_committed_block = self.get_block(committed_block_id)?;

        let root = if ledger_info.ends_epoch() {
            let epoch_genesis_id = epoch_genesis_block_id(ledger_info);
            info!(
                LogSchema::new(LogEntry::SpeculationCache)
                    .root_block_id(epoch_genesis_id)
                    .original_reconfiguration_block_id(committed_block_id),
                "Updated with a new root block as a virtual block of reconfiguration block"
            );
            self.block_lookup.fetch_or_add_block(
                epoch_genesis_id,
                ExecutedChunk::new_empty(last_committed_block.output.result_view.clone()),
                None,
            )?
        } else {
            info!(
                LogSchema::new(LogEntry::SpeculationCache).root_block_id(committed_block_id),
                "Updated with a new root block",
            );
            last_committed_block
        };

        *self.root.lock() = root;
        Ok(())
    }

    pub fn add_block(
        &self,
        parent_block_id: HashValue,
        id: HashValue,
        output: ExecutedChunk,
    ) -> Result<Arc<Block>> {
        self.block_lookup
            .fetch_or_add_block(id, output, Some(parent_block_id))
    }

    pub fn root_block(&self) -> Arc<Block> {
        self.root.lock().clone()
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
# ears

__ears__ is a simple library for play Audio in Rust.

__ears__ is build on the top of OpenAL and libsndfile.

* Provide an acces to the OpenAL spatialization functionnality in a simple way.
* Accept a lot of audio format thanks to libsndfile.

# A simple example

```Rust
extern mod ears;
use ears::Sound;

fn main() -> {
	// Create a new Sound.
	let snd = Sound::new(~"path/to/my/sound.ogg").unwrap();

	// Play the Sound
	snd.play();

	// Wait until the end of the sound
	while snd.is_playing() {}
}
```

# Functionnality

__ears__ provide two way for play audio files.

* The Sound class, which represent light sounds who can share a buffer of samples with another Sound.
* The Music class, which is a bigger sound and who can't share sample buffer.

# Use ears

As said before, __ears__ require OpenAL and libsndfile, you need to install these two libraries in your system.

__ears__ compile against the last Rust compiler, so if it doesn't work on your computer you may need to update your compiler.

__ears__ work fully with `rustpkg`, so just install __ears__ in your project workspace like this :

```Shell
> rustpkg install github.com/JeremyLetang/ears
```

then import stuff from __ears__ in your project :

```Rust
extern mod ears;
use ears::*;
```

*/

#[feature(globs)];

#[link(name = "ears",
       vers = "0.0.1",
       author = "letang.jeremy@gmail.com",
       uuid = "D0271EC6-5F27-4C72-BBEA-1341DC665F34",
       url = "https://github.com/JeremyLetang/ears")];

#[desc = "Easy Api in Rust for Sounds"];
#[license = "MIT"];
#[crate_type = "lib"];

extern mod extra;

pub use sound::Sound;
pub use music::Music;
pub use sound_data::{SoundData, SoundTags};
pub use states::{State, Initial, Playing, Paused, Stopped};
pub use audio_controller::AudioController;

#[doc(hidden)]
mod internal;

pub mod listener;
pub mod sound;
pub mod music;
pub mod sound_data;
pub mod states;
pub mod audio_controller;

#[doc(hidden)]
mod openal;
#[doc(hidden)]
mod sndfile;
<commit_msg>remove not implemented module<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
# ears

__ears__ is a simple library for play Audio in Rust.

__ears__ is build on the top of OpenAL and libsndfile.

* Provide an acces to the OpenAL spatialization functionnality in a simple way.
* Accept a lot of audio format thanks to libsndfile.

# A simple example

```Rust
extern mod ears;
use ears::Sound;

fn main() -> {
	// Create a new Sound.
	let snd = Sound::new(~"path/to/my/sound.ogg").unwrap();

	// Play the Sound
	snd.play();

	// Wait until the end of the sound
	while snd.is_playing() {}
}
```

# Functionnality

__ears__ provide two way for play audio files.

* The Sound class, which represent light sounds who can share a buffer of samples with another Sound.
* The Music class, which is a bigger sound and who can't share sample buffer.

# Use ears

As said before, __ears__ require OpenAL and libsndfile, you need to install these two libraries in your system.

__ears__ compile against the last Rust compiler, so if it doesn't work on your computer you may need to update your compiler.

__ears__ work fully with `rustpkg`, so just install __ears__ in your project workspace like this :

```Shell
> rustpkg install github.com/JeremyLetang/ears
```

then import stuff from __ears__ in your project :

```Rust
extern mod ears;
use ears::*;
```

*/

#[feature(globs)];

#[link(name = "ears",
       vers = "0.0.1",
       author = "letang.jeremy@gmail.com",
       uuid = "D0271EC6-5F27-4C72-BBEA-1341DC665F34",
       url = "https://github.com/JeremyLetang/ears")];

#[desc = "Easy Api in Rust for Sounds"];
#[license = "MIT"];
#[crate_type = "lib"];

extern mod extra;

pub use sound::Sound;
// pub use music::Music;
pub use sound_data::{SoundData, SoundTags};
pub use states::{State, Initial, Playing, Paused, Stopped};
pub use audio_controller::AudioController;

#[doc(hidden)]
mod internal;

pub mod listener;
pub mod sound;
// pub mod music;
pub mod sound_data;
pub mod states;
pub mod audio_controller;

#[doc(hidden)]
mod openal;
#[doc(hidden)]
mod sndfile;
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use libc;
use std::ffi::{CStr, CString};
use std::panic;
use std::{any, io, marker, mem, ptr};

use crate::conversion::ToPyObject;
use crate::err::{self, PyResult};
use crate::ffi;
use crate::objects::{exc, PyDict, PyObject, PyString, PyTuple};
use crate::python::{PyDrop, Python, PythonObject};

#[macro_export]
#[doc(hidden)]
macro_rules! py_method_def {
    ($name: expr, $flags: expr, $wrap: expr) => {{
        $crate::py_method_def!($name, $flags, $wrap, "")
    }};

    ($name: expr, $flags: expr, $wrap: expr, $doc: expr) => {{
        static mut METHOD_DEF: $crate::_detail::ffi::PyMethodDef =
            $crate::_detail::ffi::PyMethodDef {
                //ml_name: bytes!(stringify!($name), "\0"),
                ml_name: 0 as *const $crate::_detail::libc::c_char,
                ml_meth: None,
                ml_flags: $crate::_detail::ffi::METH_VARARGS
                    | $crate::_detail::ffi::METH_KEYWORDS
                    | $flags,
                ml_doc: 0 as *const $crate::_detail::libc::c_char,
            };
        METHOD_DEF.ml_name = $crate::strip_raw!(concat!($name, "\0")).as_ptr() as *const _;
        if !$doc.is_empty() {
            METHOD_DEF.ml_doc = concat!($doc, "\0").as_ptr() as *const _;
        }
        METHOD_DEF.ml_meth = Some(std::mem::transmute::<
            $crate::_detail::ffi::PyCFunctionWithKeywords,
            $crate::_detail::ffi::PyCFunction,
        >($wrap));
        &mut METHOD_DEF
    }};
}

/// Creates a Python callable object that invokes a Rust function.
///
/// There are two forms of this macro:
///
/// 1. `py_fn!(py, f(parameter_list))`
/// 2. `py_fn!(py, f(parameter_list) -> PyResult<T> { body })`
///
/// both forms return a value of type `PyObject`.
/// This python object is a callable object that invokes
/// the Rust function when called.
///
/// When called, the arguments are converted into
/// the Rust types specified in the parameter list.
/// See `py_argparse!()` for details on argument parsing.
///
/// Form 1:
///
///  * `py` must be an expression of type `Python`
///  * `f` must be the name of a function that is compatible with the specified
///    parameter list, except that a single parameter of type `Python` is prepended.
///    The function must return `PyResult<T>` for some `T` that implements `ToPyObject`.
///
/// Form 2:
///
///  * `py` must be an identifier that refers to a `Python` value.
///   The function body will also have access to a `Python` variable of this name.
///  * `f` must be an identifier.
///  * The function return type must be `PyResult<T>` for some `T` that
///   implements `ToPyObject`.
///
/// # Errors
///
/// * If argument parsing fails, the Rust function will not be called and an
///   appropriate Python exception is raised instead (usually `TypeError`
///   when the Python value does not match the expected type;
///   the implementation of `FromPyObject` for your type may document additional
///   errors).
/// * If the Rust function panics, a Python `SystemError` will be raised.
///
/// # Example
/// ```
/// use cpython::{Python, PyResult, PyErr, PyDict, py_fn};
/// use cpython::{exc};
///
/// fn multiply(py: Python, lhs: i32, rhs: i32) -> PyResult<i32> {
///     match lhs.checked_mul(rhs) {
///         Some(val) => Ok(val),
///         None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
///     }
/// }
///
/// fn main() {
///     let gil = Python::acquire_gil();
///     let py = gil.python();
///     let dict = PyDict::new(py);
///     dict.set_item(py, "multiply", py_fn!(py, multiply(lhs: i32, rhs: i32))).unwrap();
///     py.run("print(multiply(6, 7))", None, Some(&dict)).unwrap();
/// }
/// ```
#[macro_export]
macro_rules! py_fn {
    ($py:expr, $f:ident $plist:tt ) => {
        $crate::py_argparse_parse_plist! { py_fn_impl { $py, $f } $plist }
    };
    ($py:ident, $f:ident $plist:tt -> $ret:ty { $($body:tt)* } ) => {
        $crate::py_argparse_parse_plist! { py_fn_impl { $py, $f, $ret, { $($body)* } } $plist }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! py_fn_impl {
    // Form 1: reference existing function
    { $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        unsafe extern "C" fn wrap(
            _slf: *mut $crate::_detail::ffi::PyObject,
            args: *mut $crate::_detail::ffi::PyObject,
            kwargs: *mut $crate::_detail::ffi::PyObject)
        -> *mut $crate::_detail::ffi::PyObject
        {
            $crate::_detail::handle_callback(
                stringify!($f), $crate::_detail::PyObjectCallbackConverter,
                |py| {
                    $crate::py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
                        [ $( { $pname : $ptype = $detail } )* ]
                        {
                            $f(py $(, $pname )* )
                        })
                })
        }
        unsafe {
            $crate::_detail::py_fn_impl($py,
                $crate::py_method_def!(stringify!($f), 0, wrap))
        }
    }};
    // Form 2: inline function definition
    { $py:ident, $f:ident, $ret:ty, $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        fn $f($py: $crate::Python $( , $pname : $ptype )* ) -> $ret $body
        $crate::py_fn_impl!($py, $f [ $( { $pname : $ptype = $detail } )* ])
    }}
}

pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
    err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}

pub trait CallbackConverter<S> {
    type R;

    fn convert(val: S, py: Python) -> Self::R;
    fn error_value() -> Self::R;
}

pub struct PyObjectCallbackConverter;

impl<S> CallbackConverter<S> for PyObjectCallbackConverter
where
    S: ToPyObject,
{
    type R = *mut ffi::PyObject;

    fn convert(val: S, py: Python) -> *mut ffi::PyObject {
        val.into_py_object(py).into_object().steal_ptr()
    }

    #[inline]
    fn error_value() -> *mut ffi::PyObject {
        ptr::null_mut()
    }
}

pub struct PythonObjectCallbackConverter<T>(pub marker::PhantomData<T>);

impl<T, S> CallbackConverter<S> for PythonObjectCallbackConverter<T>
where
    T: PythonObject,
    S: ToPyObject<ObjectType = T>,
{
    type R = *mut ffi::PyObject;

    fn convert(val: S, py: Python) -> *mut ffi::PyObject {
        val.into_py_object(py).into_object().steal_ptr()
    }

    #[inline]
    fn error_value() -> *mut ffi::PyObject {
        ptr::null_mut()
    }
}

pub unsafe fn handle_callback<F, T, C>(_location: &str, _c: C, f: F) -> C::R
where
    F: FnOnce(Python) -> PyResult<T>,
    F: panic::UnwindSafe,
    C: CallbackConverter<T>,
{
    let ret = panic::catch_unwind(|| {
        let py = Python::assume_gil_acquired();
        match f(py) {
            Ok(val) => C::convert(val, py),
            Err(e) => {
                e.restore(py);
                C::error_value()
            }
        }
    });
    let ret = match ret {
        Ok(r) => r,
        Err(ref err) => {
            handle_panic(Python::assume_gil_acquired(), err);
            C::error_value()
        }
    };
    ret
}

fn handle_panic(_py: Python, _panic: &dyn any::Any) {
    let msg = cstr!("Rust panic");
    unsafe {
        ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
    }
}

pub struct AbortOnDrop<'a>(pub &'a str);

impl<'a> Drop for AbortOnDrop<'a> {
    fn drop(&mut self) {
        use std::io::Write;
        let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
        unsafe { libc::abort() }
    }
}

// Tests for this file are in tests/test_function.rs
<commit_msg>Protect against panics in C::error_value()<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use libc;
use std::ffi::{CStr, CString};
use std::panic;
use std::{any, io, marker, mem, ptr};

use crate::conversion::ToPyObject;
use crate::err::{self, PyResult};
use crate::ffi;
use crate::objects::{exc, PyDict, PyObject, PyString, PyTuple};
use crate::python::{PyDrop, Python, PythonObject};

#[macro_export]
#[doc(hidden)]
macro_rules! py_method_def {
    ($name: expr, $flags: expr, $wrap: expr) => {{
        $crate::py_method_def!($name, $flags, $wrap, "")
    }};

    ($name: expr, $flags: expr, $wrap: expr, $doc: expr) => {{
        static mut METHOD_DEF: $crate::_detail::ffi::PyMethodDef =
            $crate::_detail::ffi::PyMethodDef {
                //ml_name: bytes!(stringify!($name), "\0"),
                ml_name: 0 as *const $crate::_detail::libc::c_char,
                ml_meth: None,
                ml_flags: $crate::_detail::ffi::METH_VARARGS
                    | $crate::_detail::ffi::METH_KEYWORDS
                    | $flags,
                ml_doc: 0 as *const $crate::_detail::libc::c_char,
            };
        METHOD_DEF.ml_name = $crate::strip_raw!(concat!($name, "\0")).as_ptr() as *const _;
        if !$doc.is_empty() {
            METHOD_DEF.ml_doc = concat!($doc, "\0").as_ptr() as *const _;
        }
        METHOD_DEF.ml_meth = Some(std::mem::transmute::<
            $crate::_detail::ffi::PyCFunctionWithKeywords,
            $crate::_detail::ffi::PyCFunction,
        >($wrap));
        &mut METHOD_DEF
    }};
}

/// Creates a Python callable object that invokes a Rust function.
///
/// There are two forms of this macro:
///
/// 1. `py_fn!(py, f(parameter_list))`
/// 2. `py_fn!(py, f(parameter_list) -> PyResult<T> { body })`
///
/// both forms return a value of type `PyObject`.
/// This python object is a callable object that invokes
/// the Rust function when called.
///
/// When called, the arguments are converted into
/// the Rust types specified in the parameter list.
/// See `py_argparse!()` for details on argument parsing.
///
/// Form 1:
///
///  * `py` must be an expression of type `Python`
///  * `f` must be the name of a function that is compatible with the specified
///    parameter list, except that a single parameter of type `Python` is prepended.
///    The function must return `PyResult<T>` for some `T` that implements `ToPyObject`.
///
/// Form 2:
///
///  * `py` must be an identifier that refers to a `Python` value.
///   The function body will also have access to a `Python` variable of this name.
///  * `f` must be an identifier.
///  * The function return type must be `PyResult<T>` for some `T` that
///   implements `ToPyObject`.
///
/// # Errors
///
/// * If argument parsing fails, the Rust function will not be called and an
///   appropriate Python exception is raised instead (usually `TypeError`
///   when the Python value does not match the expected type;
///   the implementation of `FromPyObject` for your type may document additional
///   errors).
/// * If the Rust function panics, a Python `SystemError` will be raised.
///
/// # Example
/// ```
/// use cpython::{Python, PyResult, PyErr, PyDict, py_fn};
/// use cpython::{exc};
///
/// fn multiply(py: Python, lhs: i32, rhs: i32) -> PyResult<i32> {
///     match lhs.checked_mul(rhs) {
///         Some(val) => Ok(val),
///         None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
///     }
/// }
///
/// fn main() {
///     let gil = Python::acquire_gil();
///     let py = gil.python();
///     let dict = PyDict::new(py);
///     dict.set_item(py, "multiply", py_fn!(py, multiply(lhs: i32, rhs: i32))).unwrap();
///     py.run("print(multiply(6, 7))", None, Some(&dict)).unwrap();
/// }
/// ```
#[macro_export]
macro_rules! py_fn {
    ($py:expr, $f:ident $plist:tt ) => {
        $crate::py_argparse_parse_plist! { py_fn_impl { $py, $f } $plist }
    };
    ($py:ident, $f:ident $plist:tt -> $ret:ty { $($body:tt)* } ) => {
        $crate::py_argparse_parse_plist! { py_fn_impl { $py, $f, $ret, { $($body)* } } $plist }
    };
}

#[macro_export]
#[doc(hidden)]
macro_rules! py_fn_impl {
    // Form 1: reference existing function
    { $py:expr, $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        unsafe extern "C" fn wrap(
            _slf: *mut $crate::_detail::ffi::PyObject,
            args: *mut $crate::_detail::ffi::PyObject,
            kwargs: *mut $crate::_detail::ffi::PyObject)
        -> *mut $crate::_detail::ffi::PyObject
        {
            $crate::_detail::handle_callback(
                stringify!($f), $crate::_detail::PyObjectCallbackConverter,
                |py| {
                    $crate::py_argparse_raw!(py, Some(stringify!($f)), args, kwargs,
                        [ $( { $pname : $ptype = $detail } )* ]
                        {
                            $f(py $(, $pname )* )
                        })
                })
        }
        unsafe {
            $crate::_detail::py_fn_impl($py,
                $crate::py_method_def!(stringify!($f), 0, wrap))
        }
    }};
    // Form 2: inline function definition
    { $py:ident, $f:ident, $ret:ty, $body:block [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ] } => {{
        fn $f($py: $crate::Python $( , $pname : $ptype )* ) -> $ret $body
        $crate::py_fn_impl!($py, $f [ $( { $pname : $ptype = $detail } )* ])
    }}
}

pub unsafe fn py_fn_impl(py: Python, method_def: *mut ffi::PyMethodDef) -> PyObject {
    err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(method_def, ptr::null_mut()))
}

pub trait CallbackConverter<S> {
    type R;

    fn convert(val: S, py: Python) -> Self::R;
    fn error_value() -> Self::R;
}

pub struct PyObjectCallbackConverter;

impl<S> CallbackConverter<S> for PyObjectCallbackConverter
where
    S: ToPyObject,
{
    type R = *mut ffi::PyObject;

    fn convert(val: S, py: Python) -> *mut ffi::PyObject {
        val.into_py_object(py).into_object().steal_ptr()
    }

    #[inline]
    fn error_value() -> *mut ffi::PyObject {
        ptr::null_mut()
    }
}

pub struct PythonObjectCallbackConverter<T>(pub marker::PhantomData<T>);

impl<T, S> CallbackConverter<S> for PythonObjectCallbackConverter<T>
where
    T: PythonObject,
    S: ToPyObject<ObjectType = T>,
{
    type R = *mut ffi::PyObject;

    fn convert(val: S, py: Python) -> *mut ffi::PyObject {
        val.into_py_object(py).into_object().steal_ptr()
    }

    #[inline]
    fn error_value() -> *mut ffi::PyObject {
        ptr::null_mut()
    }
}

pub unsafe fn handle_callback<F, T, C>(_location: &str, _c: C, f: F) -> C::R
where
    F: FnOnce(Python) -> PyResult<T>,
    F: panic::UnwindSafe,
    C: CallbackConverter<T>,
{
    let ret = panic::catch_unwind(|| {
        let py = Python::assume_gil_acquired();
        match f(py) {
            Ok(val) => C::convert(val, py),
            Err(e) => {
                e.restore(py);
                C::error_value()
            }
        }
    });
    match ret {
        Ok(r) => r,
        Err(ref err) => {
            // Protect against panics in C::error_value() causing UB
            let guard = AbortOnDrop("handle_panic() / C::error_value()");
            handle_panic(Python::assume_gil_acquired(), err);
            let errval = C::error_value();
            mem::forget(guard);
            errval
        }
    }
}

fn handle_panic(_py: Python, _panic: &dyn any::Any) {
    let msg = cstr!("Rust panic");
    unsafe {
        ffi::PyErr_SetString(ffi::PyExc_SystemError, msg.as_ptr());
    }
}

pub struct AbortOnDrop<'a>(pub &'a str);

impl<'a> Drop for AbortOnDrop<'a> {
    fn drop(&mut self) {
        use std::io::Write;
        let _ = writeln!(&mut io::stderr(), "Cannot unwind out of {}", self.0);
        unsafe { libc::abort() }
    }
}

// Tests for this file are in tests/test_function.rs
<|endoftext|>