<commit_before>//! Trait solving using Chalk.
use std::sync::{Arc, Mutex};

use chalk_ir::cast::Cast;
use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId};
use log::debug;
use ra_db::{impl_intern_key, salsa, CrateId};
use ra_prof::profile;
use rustc_hash::FxHashSet;

use crate::db::HirDatabase;

use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};

use self::chalk::{from_chalk, ToChalk, TypeFamily};

pub(crate) mod chalk;
mod builtin;

#[derive(Debug, Clone)]
pub struct TraitSolver {
    krate: CrateId,
    inner: Arc<Mutex<chalk_solve::Solver<TypeFamily>>>,
}

/// We need eq for salsa
impl PartialEq for TraitSolver {
    fn eq(&self, other: &TraitSolver) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for TraitSolver {}

impl TraitSolver {
    fn solve(
        &self,
        db: &impl HirDatabase,
        goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>,
    ) -> Option<chalk_solve::Solution<TypeFamily>> {
        let context = ChalkContext { db, krate: self.krate };
        debug!("solve goal: {:?}", goal);
        let mut solver = match self.inner.lock() {
            Ok(it) => it,
            // Our cancellation works via unwinding, but, as chalk is not
            // panic-safe, we need to make sure to propagate the cancellation.
            // Ideally, we should also make chalk panic-safe.
            Err(_) => ra_db::Canceled::throw(),
        };
        let solution = solver.solve(&context, goal);
        debug!("solve({:?}) => {:?}", goal, solution);
        solution
    }
}

/// This controls the maximum size of types Chalk considers. If we set this too
/// high, we can run into slow edge cases; if we set it too low, Chalk won't
/// find some solutions.
const CHALK_SOLVER_MAX_SIZE: usize = 4;

#[derive(Debug, Copy, Clone)]
struct ChalkContext<'a, DB> {
    db: &'a DB,
    krate: CrateId,
}

pub(crate) fn trait_solver_query(
    db: &(impl HirDatabase + salsa::Database),
    krate: CrateId,
) -> TraitSolver {
    db.salsa_runtime().report_untracked_read();
    // krate parameter is just so we cache a unique solver per crate
    let solver_choice = chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE };
    debug!("Creating new solver for crate {:?}", krate);
    TraitSolver { krate, inner: Arc::new(Mutex::new(solver_choice.into_solver())) }
}

/// Collects impls for the given trait in the whole dependency tree of `krate`.
pub(crate) fn impls_for_trait_query(
    db: &impl HirDatabase,
    krate: CrateId,
    trait_: TraitId,
) -> Arc<[ImplId]> {
    let mut impls = FxHashSet::default();
    // We call the query recursively here. On the one hand, this means we can
    // reuse results from queries for different crates; on the other hand, this
    // will only ever get called for a few crates near the root of the tree (the
    // ones the user is editing), so this may actually be a waste of memory. I'm
    // doing it like this mainly for simplicity for now.
    for dep in db.crate_graph().dependencies(krate) {
        impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter());
    }
    let crate_impl_blocks = db.impls_in_crate(krate);
    impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(trait_));
    impls.into_iter().collect()
}

/// A set of clauses that we assume to be true. E.g. if we are inside this function:
/// ```rust
/// fn foo<T: Default>(t: T) {}
/// ```
/// we assume that `T: Default`.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
    pub predicates: Vec<GenericPredicate>,
}

impl TraitEnvironment {
    /// Returns trait refs with the given self type which are supposed to hold
    /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will
    /// find that `T: SomeTrait` if we call it for `T`.
    pub(crate) fn trait_predicates_for_self_ty<'a>(
        &'a self,
        ty: &'a Ty,
    ) -> impl Iterator<Item = &'a TraitRef> + 'a {
        self.predicates.iter().filter_map(move |pred| match pred {
            GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr),
            _ => None,
        })
    }
}

/// Something (usually a goal), along with an environment.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InEnvironment<T> {
    pub environment: Arc<TraitEnvironment>,
    pub value: T,
}

impl<T> InEnvironment<T> {
    pub fn new(environment: Arc<TraitEnvironment>, value: T) -> InEnvironment<T> {
        InEnvironment { environment, value }
    }
}

/// Something that needs to be proven (by Chalk) during type checking, e.g. that
/// a certain type implements a certain trait. Proving the Obligation might
/// result in additional information about inference variables.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Obligation {
    /// Prove that a certain type implements a trait (the type is the `Self` type
    /// parameter to the `TraitRef`).
    Trait(TraitRef),
    Projection(ProjectionPredicate),
}

impl Obligation {
    pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
        match predicate {
            GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
            GenericPredicate::Projection(projection_pred) => {
                Some(Obligation::Projection(projection_pred))
            }
            GenericPredicate::Error => None,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProjectionPredicate {
    pub projection_ty: ProjectionTy,
    pub ty: Ty,
}

impl TypeWalk for ProjectionPredicate {
    fn walk(&self, f: &mut impl FnMut(&Ty)) {
        self.projection_ty.walk(f);
        self.ty.walk(f);
    }

    fn walk_mut_binders(&mut self, f: &mut impl FnMut(&mut Ty, usize), binders: usize) {
        self.projection_ty.walk_mut_binders(f, binders);
        self.ty.walk_mut_binders(f, binders);
    }
}

/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
    db: &impl HirDatabase,
    krate: CrateId,
    goal: Canonical<InEnvironment<Obligation>>,
) -> Option<Solution> {
    let _p = profile("trait_solve_query");
    debug!("trait_solve_query({})", goal.value.value.display(db));

    if let Obligation::Projection(pred) = &goal.value.value {
        if let Ty::Bound(_) = &pred.projection_ty.parameters[0] {
            // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
            return Some(Solution::Ambig(Guidance::Unknown));
        }
    }

    let canonical = goal.to_chalk(db).cast();

    // We currently don't deal with universes (I think / hope they're not yet
    // relevant for our use cases?)
    let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
    let solution = db.trait_solver(krate).solve(db, &u_canonical);
    solution.map(|solution| solution_from_chalk(db, solution))
}

fn solution_from_chalk(
    db: &impl HirDatabase,
    solution: chalk_solve::Solution<TypeFamily>,
) -> Solution {
    let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<TypeFamily>>| {
        let value = subst
            .value
            .parameters
            .into_iter()
            .map(|p| {
                let ty = match p.ty() {
                    Some(ty) => from_chalk(db, ty.clone()),
                    None => unimplemented!(),
                };
                ty
            })
            .collect();
        let result = Canonical { value, num_vars: subst.binders.len() };
        SolutionVariables(result)
    };
    match solution {
        chalk_solve::Solution::Unique(constr_subst) => {
            let subst = chalk_ir::Canonical {
                value: constr_subst.value.subst,
                binders: constr_subst.binders,
            };
            Solution::Unique(convert_subst(subst))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
            Solution::Ambig(Guidance::Definite(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
            Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
            Solution::Ambig(Guidance::Unknown)
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);

#[derive(Clone, Debug, PartialEq, Eq)]
/// A (possible) solution for a proposed goal.
pub enum Solution {
    /// The goal indeed holds, and there is a unique value for all existential
    /// variables.
    Unique(SolutionVariables),

    /// The goal may be provable in multiple ways, but regardless we may have some guidance
    /// for type inference. In this case, we don't return any lifetime
    /// constraints, since we have not "committed" to any particular solution
    /// yet.
    Ambig(Guidance),
}

#[derive(Clone, Debug, PartialEq, Eq)]
/// When a goal holds ambiguously (e.g., because there are multiple possible
/// solutions), we issue a set of *guidance* back to type inference.
pub enum Guidance {
    /// The existential variables *must* have the given values if the goal is
    /// ever to hold, but that alone isn't enough to guarantee the goal will
    /// actually hold.
    Definite(SolutionVariables),

    /// There are multiple plausible values for the existentials, but the ones
    /// here are suggested as the preferred choice heuristically. These should
    /// be used for inference fallback only.
    Suggested(SolutionVariables),

    /// There's no useful information to feed back to type inference
    Unknown,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FnTrait {
    FnOnce,
    FnMut,
    Fn,
}

impl FnTrait {
    fn lang_item_name(self) -> &'static str {
        match self {
            FnTrait::FnOnce => "fn_once",
            FnTrait::FnMut => "fn_mut",
            FnTrait::Fn => "fn",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClosureFnTraitImplData {
    def: DefWithBodyId,
    expr: ExprId,
    fn_trait: FnTrait,
}

/// An impl. Usually this comes from an impl block, but some built-in types get
/// synthetic impls.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Impl {
    /// A normal impl from an impl block.
    ImplBlock(ImplId),
    /// Closure types implement the Fn traits synthetically.
    ClosureFnTraitImpl(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because our ImplIds are only unique per module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GlobalImplId(salsa::InternId);
impl_intern_key!(GlobalImplId);

/// An associated type value. Usually this comes from a `type` declaration
/// inside an impl block, but for built-in impls we have to synthesize it.
/// (We only need this because Chalk wants a unique ID for each of these.)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssocTyValue {
    /// A normal assoc type value from an impl block.
    TypeAlias(TypeAliasId),
    /// The output type of the Fn trait implementation.
    ClosureFnTraitImplOutput(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because it needs a unique ID for each associated
/// type value in an impl (even synthetic ones).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AssocTyValueId(salsa::InternId);
impl_intern_key!(AssocTyValueId);
<commit_msg>Don't panic if chalk panics<commit_after>//! Trait solving using Chalk.
use std::{
    panic,
    sync::{Arc, Mutex},
};

use chalk_ir::cast::Cast;
use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId};
use ra_db::{impl_intern_key, salsa, Canceled, CrateId};
use ra_prof::profile;
use rustc_hash::FxHashSet;

use crate::db::HirDatabase;

use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};

use self::chalk::{from_chalk, ToChalk, TypeFamily};

pub(crate) mod chalk;
mod builtin;

#[derive(Debug, Clone)]
pub struct TraitSolver {
    krate: CrateId,
    inner: Arc<Mutex<chalk_solve::Solver<TypeFamily>>>,
}

/// We need eq for salsa
impl PartialEq for TraitSolver {
    fn eq(&self, other: &TraitSolver) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for TraitSolver {}

impl TraitSolver {
    fn solve(
        &self,
        db: &impl HirDatabase,
        goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>,
    ) -> Option<chalk_solve::Solution<TypeFamily>> {
        let context = ChalkContext { db, krate: self.krate };
        log::debug!("solve goal: {:?}", goal);
        let mut solver = match self.inner.lock() {
            Ok(it) => it,
            // Our cancellation works via unwinding, but, as chalk is not
            // panic-safe, we need to make sure to propagate the cancellation.
            // Ideally, we should also make chalk panic-safe.
            Err(_) => ra_db::Canceled::throw(),
        };

        let solution = panic::catch_unwind({
            let solver = panic::AssertUnwindSafe(&mut solver);
            let context = panic::AssertUnwindSafe(&context);
            move || solver.0.solve(context.0, goal)
        });

        let solution = match solution {
            Ok(it) => it,
            Err(err) => {
                if err.downcast_ref::<Canceled>().is_some() {
                    panic::resume_unwind(err)
                } else {
                    log::error!("chalk panicked :-(");
                    // Reset the solver, as it is not panic-safe.
                    *solver = create_chalk_solver();
                    None
                }
            }
        };

        log::debug!("solve({:?}) => {:?}", goal, solution);
        solution
    }
}

/// This controls the maximum size of types Chalk considers. If we set this too
/// high, we can run into slow edge cases; if we set it too low, Chalk won't
/// find some solutions.
const CHALK_SOLVER_MAX_SIZE: usize = 4;

#[derive(Debug, Copy, Clone)]
struct ChalkContext<'a, DB> {
    db: &'a DB,
    krate: CrateId,
}

pub(crate) fn trait_solver_query(
    db: &(impl HirDatabase + salsa::Database),
    krate: CrateId,
) -> TraitSolver {
    db.salsa_runtime().report_untracked_read();
    // krate parameter is just so we cache a unique solver per crate
    log::debug!("Creating new solver for crate {:?}", krate);
    TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) }
}

fn create_chalk_solver() -> chalk_solve::Solver<TypeFamily> {
    let solver_choice = chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE };
    solver_choice.into_solver()
}

/// Collects impls for the given trait in the whole dependency tree of `krate`.
pub(crate) fn impls_for_trait_query(
    db: &impl HirDatabase,
    krate: CrateId,
    trait_: TraitId,
) -> Arc<[ImplId]> {
    let mut impls = FxHashSet::default();
    // We call the query recursively here. On the one hand, this means we can
    // reuse results from queries for different crates; on the other hand, this
    // will only ever get called for a few crates near the root of the tree (the
    // ones the user is editing), so this may actually be a waste of memory. I'm
    // doing it like this mainly for simplicity for now.
    for dep in db.crate_graph().dependencies(krate) {
        impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter());
    }
    let crate_impl_blocks = db.impls_in_crate(krate);
    impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(trait_));
    impls.into_iter().collect()
}

/// A set of clauses that we assume to be true. E.g. if we are inside this function:
/// ```rust
/// fn foo<T: Default>(t: T) {}
/// ```
/// we assume that `T: Default`.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
    pub predicates: Vec<GenericPredicate>,
}

impl TraitEnvironment {
    /// Returns trait refs with the given self type which are supposed to hold
    /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will
    /// find that `T: SomeTrait` if we call it for `T`.
    pub(crate) fn trait_predicates_for_self_ty<'a>(
        &'a self,
        ty: &'a Ty,
    ) -> impl Iterator<Item = &'a TraitRef> + 'a {
        self.predicates.iter().filter_map(move |pred| match pred {
            GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr),
            _ => None,
        })
    }
}

/// Something (usually a goal), along with an environment.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InEnvironment<T> {
    pub environment: Arc<TraitEnvironment>,
    pub value: T,
}

impl<T> InEnvironment<T> {
    pub fn new(environment: Arc<TraitEnvironment>, value: T) -> InEnvironment<T> {
        InEnvironment { environment, value }
    }
}

/// Something that needs to be proven (by Chalk) during type checking, e.g. that
/// a certain type implements a certain trait. Proving the Obligation might
/// result in additional information about inference variables.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Obligation {
    /// Prove that a certain type implements a trait (the type is the `Self` type
    /// parameter to the `TraitRef`).
    Trait(TraitRef),
    Projection(ProjectionPredicate),
}

impl Obligation {
    pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
        match predicate {
            GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
            GenericPredicate::Projection(projection_pred) => {
                Some(Obligation::Projection(projection_pred))
            }
            GenericPredicate::Error => None,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProjectionPredicate {
    pub projection_ty: ProjectionTy,
    pub ty: Ty,
}

impl TypeWalk for ProjectionPredicate {
    fn walk(&self, f: &mut impl FnMut(&Ty)) {
        self.projection_ty.walk(f);
        self.ty.walk(f);
    }

    fn walk_mut_binders(&mut self, f: &mut impl FnMut(&mut Ty, usize), binders: usize) {
        self.projection_ty.walk_mut_binders(f, binders);
        self.ty.walk_mut_binders(f, binders);
    }
}

/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
    db: &impl HirDatabase,
    krate: CrateId,
    goal: Canonical<InEnvironment<Obligation>>,
) -> Option<Solution> {
    let _p = profile("trait_solve_query");
    log::debug!("trait_solve_query({})", goal.value.value.display(db));

    if let Obligation::Projection(pred) = &goal.value.value {
        if let Ty::Bound(_) = &pred.projection_ty.parameters[0] {
            // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
            return Some(Solution::Ambig(Guidance::Unknown));
        }
    }

    let canonical = goal.to_chalk(db).cast();

    // We currently don't deal with universes (I think / hope they're not yet
    // relevant for our use cases?)
    let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
    let solution = db.trait_solver(krate).solve(db, &u_canonical);
    solution.map(|solution| solution_from_chalk(db, solution))
}

fn solution_from_chalk(
    db: &impl HirDatabase,
    solution: chalk_solve::Solution<TypeFamily>,
) -> Solution {
    let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<TypeFamily>>| {
        let value = subst
            .value
            .parameters
            .into_iter()
            .map(|p| {
                let ty = match p.ty() {
                    Some(ty) => from_chalk(db, ty.clone()),
                    None => unimplemented!(),
                };
                ty
            })
            .collect();
        let result = Canonical { value, num_vars: subst.binders.len() };
        SolutionVariables(result)
    };
    match solution {
        chalk_solve::Solution::Unique(constr_subst) => {
            let subst = chalk_ir::Canonical {
                value: constr_subst.value.subst,
                binders: constr_subst.binders,
            };
            Solution::Unique(convert_subst(subst))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
            Solution::Ambig(Guidance::Definite(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
            Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
            Solution::Ambig(Guidance::Unknown)
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);

#[derive(Clone, Debug, PartialEq, Eq)]
/// A (possible) solution for a proposed goal.
pub enum Solution {
    /// The goal indeed holds, and there is a unique value for all existential
    /// variables.
    Unique(SolutionVariables),

    /// The goal may be provable in multiple ways, but regardless we may have some guidance
    /// for type inference. In this case, we don't return any lifetime
    /// constraints, since we have not "committed" to any particular solution
    /// yet.
    Ambig(Guidance),
}

#[derive(Clone, Debug, PartialEq, Eq)]
/// When a goal holds ambiguously (e.g., because there are multiple possible
/// solutions), we issue a set of *guidance* back to type inference.
pub enum Guidance {
    /// The existential variables *must* have the given values if the goal is
    /// ever to hold, but that alone isn't enough to guarantee the goal will
    /// actually hold.
    Definite(SolutionVariables),

    /// There are multiple plausible values for the existentials, but the ones
    /// here are suggested as the preferred choice heuristically. These should
    /// be used for inference fallback only.
    Suggested(SolutionVariables),

    /// There's no useful information to feed back to type inference
    Unknown,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FnTrait {
    FnOnce,
    FnMut,
    Fn,
}

impl FnTrait {
    fn lang_item_name(self) -> &'static str {
        match self {
            FnTrait::FnOnce => "fn_once",
            FnTrait::FnMut => "fn_mut",
            FnTrait::Fn => "fn",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClosureFnTraitImplData {
    def: DefWithBodyId,
    expr: ExprId,
    fn_trait: FnTrait,
}

/// An impl. Usually this comes from an impl block, but some built-in types get
/// synthetic impls.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Impl {
    /// A normal impl from an impl block.
    ImplBlock(ImplId),
    /// Closure types implement the Fn traits synthetically.
    ClosureFnTraitImpl(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because our ImplIds are only unique per module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GlobalImplId(salsa::InternId);
impl_intern_key!(GlobalImplId);

/// An associated type value. Usually this comes from a `type` declaration
/// inside an impl block, but for built-in impls we have to synthesize it.
/// (We only need this because Chalk wants a unique ID for each of these.)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssocTyValue {
    /// A normal assoc type value from an impl block.
    TypeAlias(TypeAliasId),
    /// The output type of the Fn trait implementation.
    ClosureFnTraitImplOutput(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because it needs a unique ID for each associated
/// type value in an impl (even synthetic ones).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AssocTyValueId(salsa::InternId);
impl_intern_key!(AssocTyValueId);
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::error::Error;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;

use devicemapper::DM;
use devicemapper::{Bytes, Sectors};
use devicemapper::{ThinDev, ThinDevId, ThinStatus};
use devicemapper::ThinPoolDev;

use super::super::consts::IEC;
use super::super::engine::{Filesystem, HasName, HasUuid};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{FilesystemUuid, PoolUuid};

use super::dmdevice::{ThinRole, format_thin_name};
use super::serde_structs::{FilesystemSave, Recordable};

#[derive(Debug)]
pub struct StratFilesystem {
    fs_id: FilesystemUuid,
    name: String,
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    Failed,
}

impl StratFilesystem {
    pub fn initialize(pool_id: &PoolUuid,
                      fs_id: FilesystemUuid,
                      thin_id: ThinDevId,
                      name: &str,
                      dm: &DM,
                      thin_pool: &ThinPoolDev)
                      -> EngineResult<StratFilesystem> {
        let fs = try!(StratFilesystem::setup(pool_id,
                                             fs_id,
                                             thin_id,
                                             name,
                                             Bytes(IEC::Ti).sectors(),
                                             dm,
                                             thin_pool));
        try!(create_fs(try!(fs.devnode()).as_path()));
        Ok(fs)
    }

    /// Setup a filesystem, setting up the thin device as necessary.
    // FIXME: Check for still existing device mapper devices.
    pub fn setup(pool_uuid: &PoolUuid,
                 fs_id: FilesystemUuid,
                 thindev_id: ThinDevId,
                 name: &str,
                 size: Sectors,
                 dm: &DM,
                 thin_pool: &ThinPoolDev)
                 -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(ThinDev::setup(&device_name, dm, thin_pool, thindev_id, size));

        Ok(StratFilesystem {
               fs_id: fs_id,
               name: name.to_owned(),
               thin_dev: thin_dev,
           })
    }

    pub fn check(&self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match try!(self.thin_dev.status(dm)) {
            ThinStatus::Good((_mapped, _highest)) => {
                // TODO: check if filesystem is getting full and might need to
                // be extended (hint: use statfs(2))
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }
}

impl HasName for StratFilesystem {
    fn name(&self) -> &str {
        &self.name
    }
}

impl HasUuid for StratFilesystem {
    fn uuid(&self) -> &FilesystemUuid {
        &self.fs_id
    }
}

impl Filesystem for StratFilesystem {
    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        match self.thin_dev.teardown(&dm) {
            Ok(_) => Ok(()),
            Err(e) => Err(EngineError::Engine(ErrorEnum::Error, e.description().into())),
        }
    }

    fn devnode(&self) -> EngineResult<PathBuf> {
        Ok(try!(self.thin_dev.devnode()))
    }
}

impl Recordable<FilesystemSave> for StratFilesystem {
    fn record(&self) -> EngineResult<FilesystemSave> {
        Ok(FilesystemSave {
               name: self.name.clone(),
               uuid: self.fs_id,
               thin_id: self.thin_dev.id(),
               size: self.thin_dev.size(),
           })
    }
}

pub fn create_fs(dev_path: &Path) -> EngineResult<()> {

    debug!("Create filesystem for : {:?}", dev_path);
    let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_path)
                          .output());

    if output.status.success() {
        debug!("Created xfs filesystem on {:?}", dev_path)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}

pub fn mount_fs(dev_path: &Path, mount_point: &Path) -> EngineResult<()> {

    debug!("Mount filesystem {:?} on : {:?}", dev_path, mount_point);
    let output = try!(Command::new("mount")
                          .arg(&dev_path)
                          .arg(mount_point)
                          .output());

    if output.status.success() {
        debug!("Mounted filesystem on {:?}", mount_point)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}

pub fn unmount_fs<I, S>(mount_point: &Path, flags: I) -> EngineResult<()>
    where I: IntoIterator<Item = S>,
          S: AsRef<OsStr>
{
    debug!("Unmount filesystem {:?}", mount_point);

    let mut command = Command::new("umount");
    let output = try!(command.arg(mount_point).args(flags).output());

    if output.status.success() {
        debug!("Unmounted filesystem {:?}", mount_point)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}
<commit_msg>StratFilesystem::initialize should call ThinDev::new()<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::error::Error;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;

use devicemapper::DM;
use devicemapper::{Bytes, Sectors};
use devicemapper::{ThinDev, ThinDevId, ThinStatus};
use devicemapper::ThinPoolDev;

use super::super::consts::IEC;
use super::super::engine::{Filesystem, HasName, HasUuid};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{FilesystemUuid, PoolUuid};

use super::dmdevice::{ThinRole, format_thin_name};
use super::serde_structs::{FilesystemSave, Recordable};

#[derive(Debug)]
pub struct StratFilesystem {
    fs_id: FilesystemUuid,
    name: String,
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    Failed,
}

impl StratFilesystem {
    pub fn initialize(pool_uuid: &PoolUuid,
                      fs_id: FilesystemUuid,
                      thindev_id: ThinDevId,
                      name: &str,
                      dm: &DM,
                      thin_pool: &ThinPoolDev)
                      -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(ThinDev::new(&device_name,
                                         dm,
                                         thin_pool,
                                         thindev_id,
                                         Bytes(IEC::Ti).sectors()));
        let fs = StratFilesystem {
            fs_id: fs_id,
            name: name.to_owned(),
            thin_dev: thin_dev,
        };
        try!(create_fs(try!(fs.devnode()).as_path()));
        Ok(fs)
    }

    /// Setup a filesystem, setting up the thin device as necessary.
    // FIXME: Check for still existing device mapper devices.
    pub fn setup(pool_uuid: &PoolUuid,
                 fs_id: FilesystemUuid,
                 thindev_id: ThinDevId,
                 name: &str,
                 size: Sectors,
                 dm: &DM,
                 thin_pool: &ThinPoolDev)
                 -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(ThinDev::setup(&device_name, dm, thin_pool, thindev_id, size));

        Ok(StratFilesystem {
               fs_id: fs_id,
               name: name.to_owned(),
               thin_dev: thin_dev,
           })
    }

    pub fn check(&self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match try!(self.thin_dev.status(dm)) {
            ThinStatus::Good((_mapped, _highest)) => {
                // TODO: check if filesystem is getting full and might need to
                // be extended (hint: use statfs(2))
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }
}

impl HasName for StratFilesystem {
    fn name(&self) -> &str {
        &self.name
    }
}

impl HasUuid for StratFilesystem {
    fn uuid(&self) -> &FilesystemUuid {
        &self.fs_id
    }
}

impl Filesystem for StratFilesystem {
    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        match self.thin_dev.teardown(&dm) {
            Ok(_) => Ok(()),
            Err(e) => Err(EngineError::Engine(ErrorEnum::Error, e.description().into())),
        }
    }

    fn devnode(&self) -> EngineResult<PathBuf> {
        Ok(try!(self.thin_dev.devnode()))
    }
}

impl Recordable<FilesystemSave> for StratFilesystem {
    fn record(&self) -> EngineResult<FilesystemSave> {
        Ok(FilesystemSave {
               name: self.name.clone(),
               uuid: self.fs_id,
               thin_id: self.thin_dev.id(),
               size: self.thin_dev.size(),
           })
    }
}

pub fn create_fs(dev_path: &Path) -> EngineResult<()> {

    debug!("Create filesystem for : {:?}", dev_path);
    let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_path)
                          .output());

    if output.status.success() {
        debug!("Created xfs filesystem on {:?}", dev_path)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}

pub fn mount_fs(dev_path: &Path, mount_point: &Path) -> EngineResult<()> {

    debug!("Mount filesystem {:?} on : {:?}", dev_path, mount_point);
    let output = try!(Command::new("mount")
                          .arg(&dev_path)
                          .arg(mount_point)
                          .output());

    if output.status.success() {
        debug!("Mounted filesystem on {:?}", mount_point)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}

pub fn unmount_fs<I, S>(mount_point: &Path, flags: I) -> EngineResult<()>
    where I: IntoIterator<Item = S>,
          S: AsRef<OsStr>
{
    debug!("Unmount filesystem {:?}", mount_point);

    let mut command = Command::new("umount");
    let output = try!(command.arg(mount_point).args(flags).output());

    if output.status.success() {
        debug!("Unmounted filesystem {:?}", mount_point)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}
<|endoftext|>


<commit_before>//! Various C++-related types and functions needed for the `cpp_to_rust` project.

use std::fmt;
use std::ops::{Deref, DerefMut};
use std::ptr;

#[cfg(test)]
mod tests {
    use crate::{CppBox, CppDeletable, Ptr};
    use std::cell::RefCell;
    use std::rc::Rc;

    struct Struct1 {
        value: Rc<RefCell<i32>>,
    }

    unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
        (*this_ptr).value.borrow_mut().clone_from(&42);
    }

    impl CppDeletable for Struct1 {
        unsafe fn delete(&mut self) {
            struct1_delete(self);
        }
    }

    #[test]
    fn test_drop_calls_deleter() {
        let value1 = Rc::new(RefCell::new(10));
        let mut object1 = Struct1 {
            value: value1.clone(),
        };
        assert!(*value1.borrow() == 10);
        unsafe {
            // TODO: remove all "as *mut _" because it's automatic
            CppBox::new(Ptr::new(&mut object1));
        }
        assert!(*value1.borrow() == 42);
    }
}

/// Indicates that the type can be put into a CppBox.
///
/// Example of implementation:
/// ```
/// use cpp_utils::{CppDeletable};
///
/// struct Struct1;
///
/// unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
///     unimplemented!()
/// }
///
/// impl CppDeletable for Struct1 {
///     unsafe fn delete(&mut self) {
///         struct1_delete(self)
///     }
/// }
/// ```
pub trait CppDeletable: Sized {
    /// Calls C++ `delete x` on `self`.
    unsafe fn delete(&mut self);
}

/// A C++ pointer wrapper to manage deletion of objects.
///
/// Objects of CppBox should be created by calling into_box() for
/// types that implement CppDeletable trait. The object will
/// be deleted when corresponding CppBox is deleted.
pub struct CppBox<T: CppDeletable>(*mut T);

impl<T: CppDeletable> CppBox<T> {
    /// Returns constant raw pointer to the value in the box.
    pub unsafe fn as_ptr(&self) -> ConstPtr<T> {
        ConstPtr::new(self.0)
    }

    /// Returns mutable raw pointer to the value in the box.
    pub unsafe fn as_mut_ptr(&mut self) -> Ptr<T> {
        Ptr::new(self.0)
    }
    /// Returns the pointer that was used to create the object and destroys the box.
    /// The caller of the function becomes the owner of the object and should
    /// ensure that the object will be deleted at some point.
    pub fn into_raw(mut self) -> *mut T {
        let ptr = self.0;
        self.0 = ptr::null_mut();
        ptr
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_ref(&self) -> &T {
        self.0.as_ref().unwrap()
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_mut(&mut self) -> &mut T {
        self.0.as_mut().unwrap()
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T: CppDeletable> CppBox<T> {
    /// Encapsulates the object into a CppBox.
    ///
    /// You should use this function only for
    /// pointers that were created on C++ side and passed through
    /// a FFI boundary to Rust. An object created with C++ `new`
    /// must be deleted using C++ `delete`, which is executed by `CppBox`.
    ///
    /// Do not use this function for objects created in memory managed by Rust.
    /// Any wrapper constructor or function that returns an owned object
    /// is supposed to be deleted using Rust's ownage system and Drop trait.
    ///
    /// Do not use this function for objects that would be deleted by other means.
    /// If another C++ object is the owner of the passed object,
    /// it will attempt to delete it. If `CppBox` containing the object still exists,
    /// it would result in a double deletion, which should never happen.
    ///
    /// Use `CppBox::into_raw` to unwrap the pointer before passing it to
    /// a function that takes ownership of the object.
    ///
    /// It's permitted to put a null pointer into a `CppBox`. Deleter function
    /// will not be called for a null pointer. However, attempting to dereference
    /// a null pointer in a `CppBox`
    /// using `as_ref`, `as_mut`, `deref` or `deref_mut` will result in a panic.
    pub unsafe fn new(ptr: Ptr<T>) -> Self {
        CppBox(ptr.0)
    }

    pub unsafe fn from_raw(ptr: *mut T) -> Self {
        CppBox(ptr)
    }
}

impl<T: CppDeletable> Deref for CppBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { self.0.as_ref().unwrap() }
    }
}

impl<T: CppDeletable> DerefMut for CppBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { self.0.as_mut().unwrap() }
    }
}

impl<T: CppDeletable> Drop for CppBox<T> {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                T::delete(&mut *self.0);
            }
        }
    }
}

impl<T: CppDeletable> Default for CppBox<T> {
    fn default() -> CppBox<T> {
        CppBox(ptr::null_mut())
    }
}

/// Provides access to C++ `static_cast` conversion from derived class to base class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `StaticCast<T2>` is implemented for `T1`.
///
/// `StaticCast` allows to convert a reference to a class into
/// a reference to a base class.
///
/// `static_cast` and `static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticUpcast<T> {
    /// Convert type of a const reference.
    unsafe fn static_upcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_upcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast<R, T: StaticUpcast<R>>(value: &T) -> ConstPtr<R> {
    value.static_upcast()
}

/// Converts type of a mutable pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast_mut<R, T: StaticUpcast<R>>(value: &mut T) -> Ptr<R> {
    value.static_upcast_mut()
}

/// Provides access to C++ `static_cast` conversion from base class to derived class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `UnsafeStaticCast<T1>` is implemented for `T2`.
///
/// `UnsafeStaticCast` allows to convert a reference to a class into
/// a reference to a derived class without runtime check of the type.
/// Casting from base class type to a derived class type which is
/// not the actual type of the object will result in an invalid reference.
///
/// `unsafe_static_cast` and `unsafe_static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticDowncast<T> {
    /// Convert type of a const reference.
    unsafe fn static_downcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_downcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast<R, T: StaticDowncast<R>>(value: &T) -> ConstPtr<R> {
    value.static_downcast()
}

/// Converts type of a mutable pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast_mut<R, T: StaticDowncast<R>>(value: &mut T) -> Ptr<R> {
    value.static_downcast_mut()
}

/// Provides access to C++ `dynamic_cast` conversion.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `DynamicCast<T1>` is implemented for `T2`.
/// Use `StaticCast` to convert from `T1` to `T2`.
///
/// `DynamicCast` allows to convert a reference to a class into
/// a reference to a derived class with a runtime check of the type.
/// Conversion returns `None` if the object is actually not an instance of
/// the target type.
///
/// `dynamic_cast` and `dynamic_cast_mut` free functions can be used
/// to convert pointer types.
pub trait DynamicCast<T> {
    /// Convert type of a const reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast(&self) -> Option<ConstPtr<T>>;
    /// Convert type of a mutable reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast_mut(&mut self) -> Option<Ptr<T>>;
}

/// Converts type of a const pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R` or an instance of
/// a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.

pub unsafe fn dynamic_cast<R, T: DynamicCast<R>>(value: &T) -> Option<ConstPtr<R>> {
    value.dynamic_cast()
}

/// Converts type of a mutable pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn dynamic_cast_mut<R, T: DynamicCast<R>>(value: &mut T) -> Option<Ptr<R>> {
    value.dynamic_cast_mut()
}

#[derive(Debug, PartialEq, Eq)]
pub struct Ptr<T>(*mut T);

impl<T> Clone for Ptr<T> {
    fn clone(&self) -> Self {
        Ptr(self.0)
    }
}

impl<T> Copy for Ptr<T> {}

impl<T> Ptr<T> {
    pub unsafe fn new(ptr: *mut T) -> Self {
        Ptr(ptr)
    }

    pub unsafe fn new_option(ptr: *mut T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(Ptr(ptr))
        }
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(self) -> *const T {
        self.0
    }

    /// Returns mutable raw pointer to the value in the box.
    pub fn as_mut_ptr(self) -> *mut T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(self) -> bool {
        self.0.is_null()
    }
}

impl<T> Deref for Ptr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &(*self.0) }
    }
}

impl<T> DerefMut for Ptr<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut (*self.0) }
    }
}

pub struct ConstPtr<T>(*const T);

impl<T> Clone for ConstPtr<T> {
    fn clone(&self) -> Self {
        ConstPtr(self.0)
    }
}

impl<T> Copy for ConstPtr<T> {}

impl<T> fmt::Debug for ConstPtr<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ConstPtr({:?})", self.0)
    }
}

impl<T> ConstPtr<T> {
    pub unsafe fn new(ptr: *const T) -> Self {
        ConstPtr(ptr)
    }

    pub unsafe fn new_option(ptr: *const T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(ConstPtr(ptr))
        }
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(self) -> *const T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(self) -> bool {
        self.0.is_null()
    }
}

impl<T> Deref for ConstPtr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        if self.0.is_null() {
            panic!("attempted to deref a null ConstPtr<T>");
        }
        unsafe { &(*self.0) }
    }
}

impl<T> From<Ptr<T>> for ConstPtr<T> {
    fn from(value: Ptr<T>) -> Self {
        ConstPtr(value.0)
    }
}

#[test]
fn ptr_deref() {
    let mut i = 42;
    unsafe {
        let ptr: Ptr<i32> = Ptr::new(&mut i as *mut i32);
        assert_eq!(*ptr, 42);
    }
}
<commit_msg>add Ptr::null, add null deref check<commit_after>//! Various C++-related types and functions needed for the `cpp_to_rust` project.

use std::fmt;
use std::ops::{Deref, DerefMut};
use std::ptr;

#[cfg(test)]
mod tests {
    use crate::{CppBox, CppDeletable, Ptr};
    use std::cell::RefCell;
    use std::rc::Rc;

    struct Struct1 {
        value: Rc<RefCell<i32>>,
    }

    unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
        (*this_ptr).value.borrow_mut().clone_from(&42);
    }

    impl CppDeletable for Struct1 {
        unsafe fn delete(&mut self) {
            struct1_delete(self);
        }
    }

    #[test]
    fn test_drop_calls_deleter() {
        let value1 = Rc::new(RefCell::new(10));
        let mut object1 = Struct1 {
            value: value1.clone(),
        };
        assert!(*value1.borrow() == 10);
        unsafe {
            // TODO: remove all "as *mut _" because it's automatic
            CppBox::new(Ptr::new(&mut object1));
        }
        assert!(*value1.borrow() == 42);
    }
}

/// Indicates that the type can be put into a CppBox.
///
/// Example of implementation:
/// ```
/// use cpp_utils::{CppDeletable};
///
/// struct Struct1;
///
/// unsafe extern "C" fn struct1_delete(this_ptr: *mut Struct1) {
///     unimplemented!()
/// }
///
/// impl CppDeletable for Struct1 {
///     unsafe fn delete(&mut self) {
///         struct1_delete(self)
///     }
/// }
/// ```
pub trait CppDeletable: Sized {
    /// Calls C++ `delete x` on `self`.
    unsafe fn delete(&mut self);
}

/// A C++ pointer wrapper to manage deletion of objects.
///
/// Objects of CppBox should be created by calling into_box() for
/// types that implement CppDeletable trait. The object will
/// be deleted when corresponding CppBox is deleted.
pub struct CppBox<T: CppDeletable>(*mut T);

impl<T: CppDeletable> CppBox<T> {
    /// Returns constant raw pointer to the value in the box.
    pub unsafe fn as_ptr(&self) -> ConstPtr<T> {
        ConstPtr::new(self.0)
    }

    /// Returns mutable raw pointer to the value in the box.
    pub unsafe fn as_mut_ptr(&mut self) -> Ptr<T> {
        Ptr::new(self.0)
    }
    /// Returns the pointer that was used to create the object and destroys the box.
    /// The caller of the function becomes the owner of the object and should
    /// ensure that the object will be deleted at some point.
    pub fn into_raw(mut self) -> *mut T {
        let ptr = self.0;
        self.0 = ptr::null_mut();
        ptr
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_ref(&self) -> &T {
        self.0.as_ref().unwrap()
    }

    #[allow(clippy::should_implement_trait)]
    pub unsafe fn as_mut(&mut self) -> &mut T {
        self.0.as_mut().unwrap()
    }

    /// Returns true if the pointer is null.
    pub fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<T: CppDeletable> CppBox<T> {
    /// Encapsulates the object into a CppBox.
    ///
    /// You should use this function only for
    /// pointers that were created on C++ side and passed through
    /// a FFI boundary to Rust. An object created with C++ `new`
    /// must be deleted using C++ `delete`, which is executed by `CppBox`.
    ///
    /// Do not use this function for objects created in memory managed by Rust.
    /// Any wrapper constructor or function that returns an owned object
    /// is supposed to be deleted using Rust's ownage system and Drop trait.
    ///
    /// Do not use this function for objects that would be deleted by other means.
    /// If another C++ object is the owner of the passed object,
    /// it will attempt to delete it. If `CppBox` containing the object still exists,
    /// it would result in a double deletion, which should never happen.
    ///
    /// Use `CppBox::into_raw` to unwrap the pointer before passing it to
    /// a function that takes ownership of the object.
    ///
    /// It's permitted to put a null pointer into a `CppBox`. Deleter function
    /// will not be called for a null pointer. However, attempting to dereference
    /// a null pointer in a `CppBox`
    /// using `as_ref`, `as_mut`, `deref` or `deref_mut` will result in a panic.
    pub unsafe fn new(ptr: Ptr<T>) -> Self {
        CppBox(ptr.0)
    }

    pub unsafe fn from_raw(ptr: *mut T) -> Self {
        CppBox(ptr)
    }
}

impl<T: CppDeletable> Deref for CppBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { self.0.as_ref().unwrap() }
    }
}

impl<T: CppDeletable> DerefMut for CppBox<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { self.0.as_mut().unwrap() }
    }
}

impl<T: CppDeletable> Drop for CppBox<T> {
    fn drop(&mut self) {
        if !self.0.is_null() {
            unsafe {
                T::delete(&mut *self.0);
            }
        }
    }
}

impl<T: CppDeletable> Default for CppBox<T> {
    fn default() -> CppBox<T> {
        CppBox(ptr::null_mut())
    }
}

/// Provides access to C++ `static_cast` conversion from derived class to base class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `StaticCast<T2>` is implemented for `T1`.
///
/// `StaticCast` allows to convert a reference to a class into
/// a reference to a base class.
///
/// `static_cast` and `static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticUpcast<T> {
    /// Convert type of a const reference.
    unsafe fn static_upcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_upcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast<R, T: StaticUpcast<R>>(value: &T) -> ConstPtr<R> {
    value.static_upcast()
}

/// Converts type of a mutable pointer using `StaticCast` implementation of the type.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_upcast_mut<R, T: StaticUpcast<R>>(value: &mut T) -> Ptr<R> {
    value.static_upcast_mut()
}

/// Provides access to C++ `static_cast` conversion from base class to derived class.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `UnsafeStaticCast<T1>` is implemented for `T2`.
///
/// `UnsafeStaticCast` allows to convert a reference to a class into
/// a reference to a derived class without runtime check of the type.
/// Casting from base class type to a derived class type which is
/// not the actual type of the object will result in an invalid reference.
///
/// `unsafe_static_cast` and `unsafe_static_cast_mut` free functions can be used
/// to convert pointer types.
///
/// Note that Rust functions associated with this trait have runtime overhead.
/// In C++, `static_cast` is usually a no-op if there is no multiple inheritance,
/// and multiple inheritance requires pointer adjustment. However, Rust compiler
/// and `cpp_to_rust` do not have any information about these implementation details,
/// so all calls of `static_cast` are wrapper in FFI functions.
/// Still, `static_cast` is faster than casts with runtime checks on C++ side
/// because runtime overhead of Rust wrapper functions is the same for all cast types.
pub trait StaticDowncast<T> {
    /// Convert type of a const reference.
    unsafe fn static_downcast(&self) -> ConstPtr<T>;
    /// Convert type of a mutable reference.
    unsafe fn static_downcast_mut(&mut self) -> Ptr<T>;
}

/// Converts type of a const pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast<R, T: StaticDowncast<R>>(value: &T) -> ConstPtr<R> {
    value.static_downcast()
}

/// Converts type of a mutable pointer using `UnsafeStaticCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `R` class
/// or a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn static_downcast_mut<R, T: StaticDowncast<R>>(value: &mut T) -> Ptr<R> {
    value.static_downcast_mut()
}

/// Provides access to C++ `dynamic_cast` conversion.
///
/// This trait is automatically implemented by `cpp_to_rust`.
/// If `T1` class is derived (in C++) from `T2` class,
/// `DynamicCast<T1>` is implemented for `T2`.
/// Use `StaticCast` to convert from `T1` to `T2`.
///
/// `DynamicCast` allows to convert a reference to a class into
/// a reference to a derived class with a runtime check of the type.
/// Conversion returns `None` if the object is actually not an instance of
/// the target type.
///
/// `dynamic_cast` and `dynamic_cast_mut` free functions can be used
/// to convert pointer types.
pub trait DynamicCast<T> {
    /// Convert type of a const reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast(&self) -> Option<ConstPtr<T>>;
    /// Convert type of a mutable reference.
    /// Returns `None` if `self` is not an instance of `T`.
    unsafe fn dynamic_cast_mut(&mut self) -> Option<Ptr<T>>;
}

/// Converts type of a const pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R` or an instance of
/// a class derived from `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.

pub unsafe fn dynamic_cast<R, T: DynamicCast<R>>(value: &T) -> Option<ConstPtr<R>> {
    value.dynamic_cast()
}

/// Converts type of a mutable pointer using `DynamicCast` implementation of the type.
/// `ptr` must be either a null pointer or a valid pointer to an instance of `T` class
/// or a class derived from `T`.
/// Returns null pointer if `ptr` does not point to an instance of `R`.
/// If `ptr` is null, this function does nothing and returns null pointer.
pub unsafe fn dynamic_cast_mut<R, T: DynamicCast<R>>(value: &mut T) -> Option<Ptr<R>> {
    value.dynamic_cast_mut()
}

#[derive(Debug, PartialEq, Eq)]
pub struct Ptr<T>(*mut T);

impl<T> Clone for Ptr<T> {
    fn clone(&self) -> Self {
        Ptr(self.0)
    }
}

impl<T> Copy for Ptr<T> {}

impl<T> Ptr<T> {
    pub unsafe fn new(ptr: *mut T) -> Self {
        Ptr(ptr)
    }

    pub unsafe fn new_option(ptr: *mut T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(Ptr(ptr))
        }
    }

    pub unsafe fn null() -> Self {
        Ptr(std::ptr::null_mut())
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(self) -> *const T {
        self.0
    }

    /// Returns mutable raw pointer to the value in the box.
    pub fn as_mut_ptr(self) -> *mut T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(self) -> bool {
        self.0.is_null()
    }
}

impl<T> Deref for Ptr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        if self.0.is_null() {
            panic!("attempted to deref a null Ptr<T>");
        }
        unsafe { &(*self.0) }
    }
}

impl<T> DerefMut for Ptr<T> {
    fn deref_mut(&mut self) -> &mut T {
        if self.0.is_null() {
            panic!("attempted to deref a null Ptr<T>");
        }
        unsafe { &mut (*self.0) }
    }
}

pub struct ConstPtr<T>(*const T);

impl<T> Clone for ConstPtr<T> {
    fn clone(&self) -> Self {
        ConstPtr(self.0)
    }
}

impl<T> Copy for ConstPtr<T> {}

impl<T> fmt::Debug for ConstPtr<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ConstPtr({:?})", self.0)
    }
}

impl<T> ConstPtr<T> {
    pub unsafe fn new(ptr: *const T) -> Self {
        ConstPtr(ptr)
    }

    pub unsafe fn new_option(ptr: *const T) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(ConstPtr(ptr))
        }
    }

    pub unsafe fn null() -> Self {
        ConstPtr(std::ptr::null())
    }

    /// Returns constant raw pointer to the value in the box.
    pub fn as_ptr(self) -> *const T {
        self.0
    }

    /// Returns true if the pointer is null.
    pub fn is_null(self) -> bool {
        self.0.is_null()
    }
}

impl<T> Deref for ConstPtr<T> {
    type Target = T;

    fn deref(&self) -> &T {
        if self.0.is_null() {
            panic!("attempted to deref a null ConstPtr<T>");
        }
        unsafe { &(*self.0) }
    }
}

impl<T> From<Ptr<T>> for ConstPtr<T> {
    fn from(value: Ptr<T>) -> Self {
        ConstPtr(value.0)
    }
}

#[test]
fn ptr_deref() {
    let mut i = 42;
    unsafe {
        let ptr: Ptr<i32> = Ptr::new(&mut i as *mut i32);
        assert_eq!(*ptr, 42);
    }
}
<|endoftext|>


<commit_before>extern crate bindgen;
extern crate cc;
extern crate glob;

use std::env;
use std::fs;
use std::path::PathBuf;

fn link(name: &str, bundled: bool) {
    use std::env::var;
    let target = var("TARGET").unwrap();
    let target: Vec<_> = target.split('-').collect();
    if target.get(2) == Some(&"windows") {
        println!("cargo:rustc-link-lib=dylib={}", name);
        if bundled && target.get(3) == Some(&"gnu") {
            let dir = var("CARGO_MANIFEST_DIR").unwrap();
            println!("cargo:rustc-link-search=native={}/{}", dir, target[0]);
        }
    }
}

fn fail_on_empty_directory(name: &str) {
    if fs::read_dir(name).unwrap().count() == 0 {
        println!(
            "The `{}` directory is empty, did you forget to pull the submodules?",
            name
        );
        println!("Try `git submodule update --init --recursive`");
        panic!();
    }
}

fn bindgen_rocksdb() {
    let bindings = bindgen::Builder::default()
        .header("rocksdb/include/rocksdb/c.h")
        .blacklist_type("max_align_t") // https://github.com/rust-lang-nursery/rust-bindgen/issues/550
        .ctypes_prefix("libc")
        .generate()
        .expect("unable to generate rocksdb bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("unable to write rocksdb bindings");
}

fn build_rocksdb() {
    let mut config = cc::Build::new();
    config.include("rocksdb/include/");
    config.include("rocksdb/");
    config.include("rocksdb/third-party/gtest-1.7.0/fused-src/");

    if cfg!(feature = "snappy") {
        config.define("SNAPPY", Some("1"));
        config.include("snappy/");
    }

    if cfg!(feature = "lz4") {
        config.define("LZ4", Some("1"));
        config.include("lz4/lib/");
    }

    if cfg!(feature = "zstd") {
        config.define("ZSTD", Some("1"));
        config.include("zstd/lib/");
        config.include("zstd/lib/dictBuilder/");
    }

    if cfg!(feature = "zlib") {
        config.define("ZLIB", Some("1"));
        config.include("zlib/");
    }

    if cfg!(feature = "bzip2") {
        config.define("BZIP2", Some("1"));
        config.include("bzip2/");
    }

    config.include(".");
    config.define("NDEBUG", Some("1"));

    let mut lib_sources = include_str!("rocksdb_lib_sources.txt")
        .split(" ")
        .collect::<Vec<&'static str>>();

    // We have a pregenerated a version of build_version.cc in the local directory
    lib_sources = lib_sources
        .iter()
        .cloned()
        .filter(|file| *file != "util/build_version.cc")
        .collect::<Vec<&'static str>>();

    if cfg!(target_arch = "x86_64") {
        // This is needed to enable hardware CRC32C. Technically, SSE 4.2 is
        // only available since Intel Nehalem (about 2010) and AMD Bulldozer
        // (about 2011).
        config.define("HAVE_PCLMUL", Some("1"));
        config.define("HAVE_SSE42", Some("1"));
        config.flag("-msse2");
        config.flag("-msse4.1");
        config.flag("-msse4.2");
        config.flag("-mpclmul");
    }

    if cfg!(target_os = "macos") {
        config.define("OS_MACOSX", Some("1"));
        config.define("ROCKSDB_PLATFORM_POSIX", Some("1"));
        config.define("ROCKSDB_LIB_IO_POSIX", Some("1"));
    }
    if cfg!(target_os = "linux") {
        config.define("OS_LINUX", Some("1"));
        config.define("ROCKSDB_PLATFORM_POSIX", Some("1"));
        config.define("ROCKSDB_LIB_IO_POSIX", Some("1"));
        // COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
    }
    if cfg!(target_os = "freebsd") {
        config.define("OS_FREEBSD", Some("1"));
        config.define("ROCKSDB_PLATFORM_POSIX", Some("1"));
        config.define("ROCKSDB_LIB_IO_POSIX", Some("1"));
    }

    if cfg!(target_os = "windows") {
        link("rpcrt4", false);
        link("shlwapi", false);
        config.define("OS_WIN", Some("1"));

        // Remove POSIX-specific sources
        lib_sources = lib_sources
            .iter()
            .cloned()
            .filter(|file| match *file {
                "port/port_posix.cc" | "env/env_posix.cc" | "env/io_posix.cc" => false,
                _ => true,
            })
            .collect::<Vec<&'static str>>();

        // Add Windows-specific sources
        lib_sources.push("port/win/port_win.cc");
        lib_sources.push("port/win/env_win.cc");
        lib_sources.push("port/win/env_default.cc");
        lib_sources.push("port/win/win_logger.cc");
        lib_sources.push("port/win/io_win.cc");
        lib_sources.push("port/win/win_thread.cc");
    }

    if cfg!(target_env = "msvc") {
        config.flag("-EHsc");
    } else {
        config.flag("-std=c++11");
        // this was breaking the build on travis due to
        // > 4mb of warnings emitted.
        config.flag("-Wno-unused-parameter");
    }

    for file in lib_sources {
        let file = "rocksdb/".to_string() + file;
        config.file(&file);
    }

    config.file("build_version.cc");

    config.cpp(true);
    config.compile("librocksdb.a");
}

fn build_snappy() {
    let mut config = cc::Build::new();
    config.include("snappy/");
    config.include(".");

    config.define("NDEBUG", Some("1"));

    if cfg!(target_env = "msvc") {
        config.flag("-EHsc");
    } else {
        config.flag("-std=c++11");
    }

    config.file("snappy/snappy.cc");
    config.file("snappy/snappy-sinksource.cc");
    config.file("snappy/snappy-c.cc");
    config.cpp(true);
    config.compile("libsnappy.a");
}

fn build_lz4() {
    let mut compiler = cc::Build::new();

    compiler
        .file("lz4/lib/lz4.c")
        .file("lz4/lib/lz4frame.c")
        .file("lz4/lib/lz4hc.c")
        .file("lz4/lib/xxhash.c");

    compiler.opt_level(3);

    match env::var("TARGET").unwrap().as_str() {
        "i686-pc-windows-gnu" => {
            compiler.flag("-fno-tree-vectorize");
        }
        _ => {}
    }

    compiler.compile("liblz4.a");
}

fn build_zstd() {
    let mut compiler = cc::Build::new();

    compiler.include("zstd/lib/");
    compiler.include("zstd/lib/common");
    compiler.include("zstd/lib/legacy");

    let globs = &[
        "zstd/lib/common/*.c",
        "zstd/lib/compress/*.c",
        "zstd/lib/decompress/*.c",
        "zstd/lib/dictBuilder/*.c",
        "zstd/lib/legacy/*.c",
    ];

    for pattern in globs {
        for path in glob::glob(pattern).unwrap() {
            let path = path.unwrap();
            compiler.file(path);
        }
    }

    compiler.opt_level(3);

    compiler.define("ZSTD_LIB_DEPRECATED", Some("0"));
    compiler.compile("libzstd.a");
}

fn build_zlib() {
    let mut compiler = cc::Build::new();

    let globs = &["zlib/*.c"];

    for pattern in globs {
        for path in glob::glob(pattern).unwrap() {
            let path = path.unwrap();
            compiler.file(path);
        }
    }

    compiler.flag_if_supported("-Wno-implicit-function-declaration");
    compiler.opt_level(3);
    compiler.compile("libz.a");
}

fn build_bzip2() {
    let mut compiler = cc::Build::new();

    compiler
        .file("bzip2/blocksort.c")
        .file("bzip2/bzlib.c")
        .file("bzip2/compress.c")
        .file("bzip2/crctable.c")
        .file("bzip2/decompress.c")
        .file("bzip2/huffman.c")
        .file("bzip2/randtable.c");

    compiler
        .define("_FILE_OFFSET_BITS", Some("64"))
        .define("BZ_NO_STDIO", None);

    compiler.extra_warnings(false);
    compiler.opt_level(3);
    compiler.compile("libbz2.a");
}

fn try_to_find_and_link_lib(lib_name: &str) -> bool {
    if let Ok(lib_dir) = env::var(&format!("{}_LIB_DIR", lib_name)) {
        println!("cargo:rustc-link-search=native={}", lib_dir);
        let mode = match env::var_os(&format!("{}_STATIC", lib_name)) {
            Some(_) => "static",
            None => "dylib",
        };
        println!("cargo:rustc-link-lib={}={}", mode, lib_name.to_lowercase());
        return true;
    }
    false
}

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=rocksdb/");
    println!("cargo:rerun-if-changed=snappy/");
    println!("cargo:rerun-if-changed=lz4/");
    println!("cargo:rerun-if-changed=zstd/");
    println!("cargo:rerun-if-changed=zlib/");
    println!("cargo:rerun-if-changed=bzip2/");

    fail_on_empty_directory("rocksdb");
    fail_on_empty_directory("snappy");
    fail_on_empty_directory("lz4");
    fail_on_empty_directory("zstd");
    fail_on_empty_directory("zlib");
    fail_on_empty_directory("bzip2");

    bindgen_rocksdb();

    if !try_to_find_and_link_lib("ROCKSDB") {
        build_rocksdb();
    }
    if cfg!(feature = "snappy") && !try_to_find_and_link_lib("SNAPPY") {
        build_snappy();
    }
    if cfg!(feature = "lz4") && !try_to_find_and_link_lib("LZ4") {
        build_lz4();
    }
    if cfg!(feature = "zstd") && !try_to_find_and_link_lib("ZSTD") {
        build_zstd();
    }
    if cfg!(feature = "zlib") && !try_to_find_and_link_lib("ZLIB") {
        build_zlib();
    }
    if cfg!(feature = "bzip2") && !try_to_find_and_link_lib("BZIP2") {
        build_bzip2();
    }
}
<commit_msg>Build RocksDB with support for unicode filenames on Windows<commit_after>extern crate bindgen;
extern crate cc;
extern crate glob;

use std::env;
use std::fs;
use std::path::PathBuf;

fn link(name: &str, bundled: bool) {
    use std::env::var;
    let target = var("TARGET").unwrap();
    let target: Vec<_> = target.split('-').collect();
    if target.get(2) == Some(&"windows") {
        println!("cargo:rustc-link-lib=dylib={}", name);
        if bundled && target.get(3) == Some(&"gnu") {
            let dir = var("CARGO_MANIFEST_DIR").unwrap();
            println!("cargo:rustc-link-search=native={}/{}", dir, target[0]);
        }
    }
}

fn fail_on_empty_directory(name: &str) {
    if fs::read_dir(name).unwrap().count() == 0 {
        println!(
            "The `{}` directory is empty, did you forget to pull the submodules?",
            name
        );
        println!("Try `git submodule update --init --recursive`");
        panic!();
    }
}

fn bindgen_rocksdb() {
    let bindings = bindgen::Builder::default()
        .header("rocksdb/include/rocksdb/c.h")
        .blacklist_type("max_align_t") // https://github.com/rust-lang-nursery/rust-bindgen/issues/550
        .ctypes_prefix("libc")
        .generate()
        .expect("unable to generate rocksdb bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("unable to write rocksdb bindings");
}

fn build_rocksdb() {
    let mut config = cc::Build::new();
    config.include("rocksdb/include/");
    config.include("rocksdb/");
    config.include("rocksdb/third-party/gtest-1.7.0/fused-src/");

    if cfg!(feature = "snappy") {
        config.define("SNAPPY", Some("1"));
        config.include("snappy/");
    }

    if cfg!(feature = "lz4") {
        config.define("LZ4", Some("1"));
        config.include("lz4/lib/");
    }

    if cfg!(feature = "zstd") {
        config.define("ZSTD", Some("1"));
        config.include("zstd/lib/");
        config.include("zstd/lib/dictBuilder/");
    }

    if cfg!(feature = "zlib") {
        config.define("ZLIB", Some("1"));
        config.include("zlib/");
    }

    if cfg!(feature = "bzip2") {
        config.define("BZIP2", Some("1"));
        config.include("bzip2/");
    }

    config.include(".");
    config.define("NDEBUG", Some("1"));

    let mut lib_sources = include_str!("rocksdb_lib_sources.txt")
        .split(" ")
        .collect::<Vec<&'static str>>();

    // We have a pregenerated a version of build_version.cc in the local directory
    lib_sources = lib_sources
        .iter()
        .cloned()
        .filter(|file| *file != "util/build_version.cc")
        .collect::<Vec<&'static str>>();

    if cfg!(target_arch = "x86_64") {
        // This is needed to enable hardware CRC32C. Technically, SSE 4.2 is
        // only available since Intel Nehalem (about 2010) and AMD Bulldozer
        // (about 2011).
        config.define("HAVE_PCLMUL", Some("1"));
        config.define("HAVE_SSE42", Some("1"));
        config.flag("-msse2");
        config.flag("-msse4.1");
        config.flag("-msse4.2");
        config.flag("-mpclmul");
    }

    if cfg!(target_os = "macos") {
        config.define("OS_MACOSX", Some("1"));
        config.define("ROCKSDB_PLATFORM_POSIX", Some("1"));
        config.define("ROCKSDB_LIB_IO_POSIX", Some("1"));
    }
    if cfg!(target_os = "linux") {
        config.define("OS_LINUX", Some("1"));
        config.define("ROCKSDB_PLATFORM_POSIX", Some("1"));
        config.define("ROCKSDB_LIB_IO_POSIX", Some("1"));
        // COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
    }
    if cfg!(target_os = "freebsd") {
        config.define("OS_FREEBSD", Some("1"));
        config.define("ROCKSDB_PLATFORM_POSIX", Some("1"));
        config.define("ROCKSDB_LIB_IO_POSIX", Some("1"));
    }

    if cfg!(target_os = "windows") {
        link("rpcrt4", false);
        link("shlwapi", false);
        config.define("OS_WIN", Some("1"));
        config.define("ROCKSDB_WINDOWS_UTF8_FILENAMES", Some("1"));

        // Remove POSIX-specific sources
        lib_sources = lib_sources
            .iter()
            .cloned()
            .filter(|file| match *file {
                "port/port_posix.cc" | "env/env_posix.cc" | "env/io_posix.cc" => false,
                _ => true,
            })
            .collect::<Vec<&'static str>>();

        // Add Windows-specific sources
        lib_sources.push("port/win/port_win.cc");
        lib_sources.push("port/win/env_win.cc");
        lib_sources.push("port/win/env_default.cc");
        lib_sources.push("port/win/win_logger.cc");
        lib_sources.push("port/win/io_win.cc");
        lib_sources.push("port/win/win_thread.cc");
    }

    if cfg!(target_env = "msvc") {
        config.flag("-EHsc");
    } else {
        config.flag("-std=c++11");
        // this was breaking the build on travis due to
        // > 4mb of warnings emitted.
        config.flag("-Wno-unused-parameter");
    }

    for file in lib_sources {
        let file = "rocksdb/".to_string() + file;
        config.file(&file);
    }

    config.file("build_version.cc");

    config.cpp(true);
    config.compile("librocksdb.a");
}

fn build_snappy() {
    let mut config = cc::Build::new();
    config.include("snappy/");
    config.include(".");

    config.define("NDEBUG", Some("1"));

    if cfg!(target_env = "msvc") {
        config.flag("-EHsc");
    } else {
        config.flag("-std=c++11");
    }

    config.file("snappy/snappy.cc");
    config.file("snappy/snappy-sinksource.cc");
    config.file("snappy/snappy-c.cc");
    config.cpp(true);
    config.compile("libsnappy.a");
}

fn build_lz4() {
    let mut compiler = cc::Build::new();

    compiler
        .file("lz4/lib/lz4.c")
        .file("lz4/lib/lz4frame.c")
        .file("lz4/lib/lz4hc.c")
        .file("lz4/lib/xxhash.c");

    compiler.opt_level(3);

    match env::var("TARGET").unwrap().as_str() {
        "i686-pc-windows-gnu" => {
            compiler.flag("-fno-tree-vectorize");
        }
        _ => {}
    }

    compiler.compile("liblz4.a");
}

fn build_zstd() {
    let mut compiler = cc::Build::new();

    compiler.include("zstd/lib/");
    compiler.include("zstd/lib/common");
    compiler.include("zstd/lib/legacy");

    let globs = &[
        "zstd/lib/common/*.c",
        "zstd/lib/compress/*.c",
        "zstd/lib/decompress/*.c",
        "zstd/lib/dictBuilder/*.c",
        "zstd/lib/legacy/*.c",
    ];

    for pattern in globs {
        for path in glob::glob(pattern).unwrap() {
            let path = path.unwrap();
            compiler.file(path);
        }
    }

    compiler.opt_level(3);

    compiler.define("ZSTD_LIB_DEPRECATED", Some("0"));
    compiler.compile("libzstd.a");
}

fn build_zlib() {
    let mut compiler = cc::Build::new();

    let globs = &["zlib/*.c"];

    for pattern in globs {
        for path in glob::glob(pattern).unwrap() {
            let path = path.unwrap();
            compiler.file(path);
        }
    }

    compiler.flag_if_supported("-Wno-implicit-function-declaration");
    compiler.opt_level(3);
    compiler.compile("libz.a");
}

fn build_bzip2() {
    let mut compiler = cc::Build::new();

    compiler
        .file("bzip2/blocksort.c")
        .file("bzip2/bzlib.c")
        .file("bzip2/compress.c")
        .file("bzip2/crctable.c")
        .file("bzip2/decompress.c")
        .file("bzip2/huffman.c")
        .file("bzip2/randtable.c");

    compiler
        .define("_FILE_OFFSET_BITS", Some("64"))
        .define("BZ_NO_STDIO", None);

    compiler.extra_warnings(false);
    compiler.opt_level(3);
    compiler.compile("libbz2.a");
}

fn try_to_find_and_link_lib(lib_name: &str) -> bool {
    if let Ok(lib_dir) = env::var(&format!("{}_LIB_DIR", lib_name)) {
        println!("cargo:rustc-link-search=native={}", lib_dir);
        let mode = match env::var_os(&format!("{}_STATIC", lib_name)) {
            Some(_) => "static",
            None => "dylib",
        };
        println!("cargo:rustc-link-lib={}={}", mode, lib_name.to_lowercase());
        return true;
    }
    false
}

fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=rocksdb/");
    println!("cargo:rerun-if-changed=snappy/");
    println!("cargo:rerun-if-changed=lz4/");
    println!("cargo:rerun-if-changed=zstd/");
    println!("cargo:rerun-if-changed=zlib/");
    println!("cargo:rerun-if-changed=bzip2/");

    fail_on_empty_directory("rocksdb");
    fail_on_empty_directory("snappy");
    fail_on_empty_directory("lz4");
    fail_on_empty_directory("zstd");
    fail_on_empty_directory("zlib");
    fail_on_empty_directory("bzip2");

    bindgen_rocksdb();

    if !try_to_find_and_link_lib("ROCKSDB") {
        build_rocksdb();
    }
    if cfg!(feature = "snappy") && !try_to_find_and_link_lib("SNAPPY") {
        build_snappy();
    }
    if cfg!(feature = "lz4") && !try_to_find_and_link_lib("LZ4") {
        build_lz4();
    }
    if cfg!(feature = "zstd") && !try_to_find_and_link_lib("ZSTD") {
        build_zstd();
    }
    if cfg!(feature = "zlib") && !try_to_find_and_link_lib("ZLIB") {
        build_zlib();
    }
    if cfg!(feature = "bzip2") && !try_to_find_and_link_lib("BZIP2") {
        build_bzip2();
    }
}
<|endoftext|>