<commit_before>//! This module specifies the type based interner for constants.
//!
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.

use rustc::ty::{Ty, self};
use rustc::mir::interpret::{InterpResult, ErrorHandled};
use rustc::hir;
use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;

use syntax::ast::Mutability;

use super::{
    ValueVisitor, MemoryKind, AllocId, MPlaceTy, Scalar,
};
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};

struct InternVisitor<'rt, 'mir, 'tcx> {
    /// The ectx from which we intern.
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    /// Previously encountered safe references.
    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
    /// A list of all encountered allocations. After type-based interning, we traverse this list to
    /// also intern allocations that are only referenced by a raw pointer or inside a union.
    leftover_allocations: &'rt mut FxHashSet<AllocId>,
    /// The root node of the value that we're looking at. This field is never mutated and only used
    /// for sanity assertions that will ICE when `const_qualif` screws up.
    mode: InternMode,
    /// This field stores the mutability of the value *currently* being checked.
    /// When encountering a mutable reference, we determine the pointee mutability
    /// taking into account the mutability of the context: `& &mut i32` is entirely immutable,
    /// despite the nested mutable reference!
    /// The field gets updated when an `UnsafeCell` is encountered.
    mutability: Mutability,
}

#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
    /// Mutable references must in fact be immutable due to their surrounding immutability in a
    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
    /// that will actually be treated as mutable.
    Static,
    /// UnsafeCell is OK in the value of a constant: `const FOO = Cell::new(0)` creates
    /// a new cell every time it is used.
    ConstBase,
    /// `UnsafeCell` ICEs.
    Const,
}

/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;

/// Intern an allocation without looking at its children.
/// `mode` is the mode of the environment where we found this pointer.
/// `mutablity` is the mutability of the place to be interned; even if that says
/// `immutable` things might become mutable if `ty` is not frozen.
fn intern_shallow<'rt, 'mir, 'tcx>(
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    leftover_allocations: &'rt mut FxHashSet<AllocId>,
    mode: InternMode,
    alloc_id: AllocId,
    mutability: Mutability,
    ty: Option<Ty<'tcx>>,
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
    trace!(
        "InternVisitor::intern {:?} with {:?}",
        alloc_id, mutability,
    );
    // remove allocation
    let tcx = ecx.tcx;
    let memory = ecx.memory_mut();
    let (kind, mut alloc) = match memory.alloc_map.remove(&alloc_id) {
        Some(entry) => entry,
        None => {
            // Pointer not found in local memory map. It is either a pointer to the global
            // map, or dangling.
            // If the pointer is dangling (neither in local nor global memory), we leave it
            // to validation to error. The `delay_span_bug` ensures that we don't forget such
            // a check in validation.
            if tcx.alloc_map.lock().get(alloc_id).is_none() {
                tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
            }
            // treat dangling pointers like other statics
            // just to stop trying to recurse into them
            return Ok(Some(IsStaticOrFn));
        },
    };
    // This match is just a canary for future changes to `MemoryKind`, which most likely need
    // changes in this function.
    match kind {
        MemoryKind::Stack | MemoryKind::Vtable => {},
    }
    // Set allocation mutability as appropriate. This is used by LLVM to put things into
    // read-only memory, and also by Miri when evluating other constants/statics that
    // access this one.
    if mode == InternMode::Static {
        let frozen = ty.map_or(true, |ty| ty.is_freeze(
            ecx.tcx.tcx,
            ecx.param_env,
            ecx.tcx.span,
        ));
        // For statics, allocation mutability is the combination of the place mutability and
        // the type mutability.
        // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
        if mutability == Mutability::Immutable && frozen {
            alloc.mutability = Mutability::Immutable;
        } else {
            // Just making sure we are not "upgrading" an immutable allocation to mutable.
            assert_eq!(alloc.mutability, Mutability::Mutable);
        }
    } else {
        // We *could* be non-frozen at `ConstBase`, for constants like `Cell::new(0)`.
        // But we still intern that as immutable as the memory cannot be changed once the
        // initial value was computed.
        // Constants are never mutable.
        alloc.mutability = Mutability::Immutable;
    };
    // link the alloc id to the actual allocation
    let alloc = tcx.intern_const_alloc(alloc);
    leftover_allocations.extend(alloc.relocations().iter().map(|&(_, ((), reloc))| reloc));
    tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
    Ok(None)
}

impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
    fn intern_shallow(
        &mut self,
        alloc_id: AllocId,
        mutability: Mutability,
        ty: Option<Ty<'tcx>>,
    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
        intern_shallow(
            self.ecx,
            self.leftover_allocations,
            self.mode,
            alloc_id,
            mutability,
            ty,
        )
    }
}

impl<'rt, 'mir, 'tcx>
    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
for
    InternVisitor<'rt, 'mir, 'tcx>
{
    type V = MPlaceTy<'tcx>;

    #[inline(always)]
    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
        &self.ecx
    }

    fn visit_aggregate(
        &mut self,
        mplace: MPlaceTy<'tcx>,
        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
    ) -> InterpResult<'tcx> {
        if let Some(def) = mplace.layout.ty.ty_adt_def() {
            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
                // We are crossing over an `UnsafeCell`, we can mutate again. This means that
                // References we encounter inside here are interned as pointing to mutable
                // allocations.
                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
                assert_ne!(
                    self.mode, InternMode::Const,
                    "UnsafeCells are not allowed behind references in constants. This should have \
                    been prevented statically by const qualification. If this were allowed one \
                    would be able to change a constant at one use site and other use sites could \
                    observe that mutation.",
                );
                let walked = self.walk_aggregate(mplace, fields);
                self.mutability = old;
                return walked;
            }
        }
        self.walk_aggregate(mplace, fields)
    }

    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
        // Handle Reference types, as these are the only relocations supported by const eval.
        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
        let ty = mplace.layout.ty;
        if let ty::Ref(_, referenced_ty, mutability) = ty.sty {
            let value = self.ecx.read_immediate(mplace.into())?;
            // Handle trait object vtables
            if let Ok(meta) = value.to_meta() {
                if let ty::Dynamic(..) =
                    self.ecx.tcx.struct_tail_erasing_lifetimes(
                        referenced_ty, self.ecx.param_env).sty
                {
                    if let Ok(vtable) = meta.unwrap().to_ptr() {
                        // explitly choose `Immutable` here, since vtables are immutable, even
                        // if the reference of the fat pointer is mutable
                        self.intern_shallow(vtable.alloc_id, Mutability::Immutable, None)?;
                    }
                }
            }
            let mplace = self.ecx.ref_to_mplace(value)?;
            // Check if we have encountered this pointer+layout combination before.
            // Only recurse for allocation-backed pointers.
            if let Scalar::Ptr(ptr) = mplace.ptr {
                // We do not have any `frozen` logic here, because it's essentially equivalent to
                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
                // and we check that in `visit_aggregate`.
                // This is not an inherent limitation, but one that we know to be true, because
                // const qualification enforces it. We can lift it in the future.
                match (self.mode, mutability) {
                    // immutable references are fine everywhere
                    (_, hir::Mutability::MutImmutable) => {},
                    // all is "good and well" in the unsoundness of `static mut`

                    // mutable references are ok in `static`. Either they are treated as immutable
                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
                    // and thus ok.
                    (InternMode::Static, hir::Mutability::MutMutable) => {},
                    // we statically prevent `&mut T` via `const_qualif` and double check this here
                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
                    (InternMode::Const, hir::Mutability::MutMutable) => {
                        match referenced_ty.sty {
                            ty::Array(_, n)
                                if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
                            ty::Slice(_)
                                if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
                            _ => bug!("const qualif failed to prevent mutable references"),
                        }
                    },
                }
                // Compute the mutability with which we'll start visiting the allocation. This is
                // what gets changed when we encounter an `UnsafeCell`
                let mutability = match (self.mutability, mutability) {
                    // The only way a mutable reference actually works as a mutable reference is
                    // by being in a `static mut` directly or behind another mutable reference.
                    // If there's an immutable reference or we are inside a static, then our
                    // mutable reference is equivalent to an immutable one. As an example:
                    // `&&mut Foo` is semantically equivalent to `&&Foo`
                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
                    _ => Mutability::Immutable,
                };
                // Recursing behind references changes the intern mode for constants in order to
                // cause assertions to trigger if we encounter any `UnsafeCell`s.
                let mode = match self.mode {
                    InternMode::ConstBase => InternMode::Const,
                    other => other,
                };
                match self.intern_shallow(ptr.alloc_id, mutability, Some(mplace.layout.ty))? {
                    // No need to recurse, these are interned already and statics may have
                    // cycles, so we don't want to recurse there
                    Some(IsStaticOrFn) => {},
                    // intern everything referenced by this value. The mutability is taken from the
                    // reference. It is checked above that mutable references only happen in
                    // `static mut`
                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
                }
            }
        }
        Ok(())
    }
}

pub fn intern_const_alloc_recursive(
    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
    def_id: DefId,
    ret: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
    let tcx = ecx.tcx;
    // this `mutability` is the mutability of the place, ignoring the type
    let (base_mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
        None => (Mutability::Immutable, InternMode::ConstBase),
        // `static mut` doesn't care about interior mutability, it's mutable anyway
        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
    };

    // Type based interning.
    // `ref_tracking` tracks typed references we have seen and still need to crawl for
    // more typed information inside them.
    // `leftover_allocations` collects *all* allocations we see, because some might not
    // be available in a typed way. They get interned at the end.
    let mut ref_tracking = RefTracking::new((ret, base_mutability, base_intern_mode));
    let leftover_allocations = &mut FxHashSet::default();

    // start with the outermost allocation
    intern_shallow(
        ecx,
        leftover_allocations,
        base_intern_mode,
        ret.ptr.to_ptr()?.alloc_id,
        base_mutability,
        Some(ret.layout.ty)
    )?;

    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
        let interned = InternVisitor {
            ref_tracking: &mut ref_tracking,
            ecx,
            mode,
            leftover_allocations,
            mutability,
        }.visit_value(mplace);
        if let Err(error) = interned {
            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
            // to read enum discriminants in order to find references in enum variant fields.
            if let err_unsup!(ValidationFailure(_)) = error.kind {
                let err = crate::const_eval::error_to_const_error(&ecx, error);
                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
                    Ok(mut diag) => {
                        diag.note(crate::const_eval::note_on_undefined_behavior_error());
                        diag.emit();
                    }
                    Err(ErrorHandled::TooGeneric) |
                    Err(ErrorHandled::Reported) => {},
                }
            }
        }
    }

    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
    // pointers, ... So we can't intern them according to their type rules

    let mut todo: Vec<_> = leftover_allocations.iter().cloned().collect();
    while let Some(alloc_id) = todo.pop() {
        if let Some((_, mut alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
            // We can't call the `intern_shallow` method here, as its logic is tailored to safe
            // references and a `leftover_allocations` set (where we only have a todo-list here).
            // So we hand-roll the interning logic here again.
            if base_intern_mode != InternMode::Static {
                // If it's not a static, it *must* be immutable.
                // We cannot have mutable memory inside a constant.
                // FIXME: ideally we would assert that they already are immutable, to double-
                // check our static checks.
                alloc.mutability = Mutability::Immutable;
            }
            let alloc = tcx.intern_const_alloc(alloc);
            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
            for &(_, ((), reloc)) in alloc.relocations().iter() {
                if leftover_allocations.insert(reloc) {
                    todo.push(reloc);
                }
            }
        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
            // dangling pointer
            throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into()))
        }
    }
    Ok(())
}
<commit_msg>assert that nobody asks for mutable constants<commit_after>//! This module specifies the type based interner for constants.
//!
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.

use rustc::ty::{Ty, self};
use rustc::mir::interpret::{InterpResult, ErrorHandled};
use rustc::hir;
use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;

use syntax::ast::Mutability;

use super::{
    ValueVisitor, MemoryKind, AllocId, MPlaceTy, Scalar,
};
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};

struct InternVisitor<'rt, 'mir, 'tcx> {
    /// The ectx from which we intern.
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    /// Previously encountered safe references.
    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
    /// A list of all encountered allocations. After type-based interning, we traverse this list to
    /// also intern allocations that are only referenced by a raw pointer or inside a union.
    leftover_allocations: &'rt mut FxHashSet<AllocId>,
    /// The root node of the value that we're looking at. This field is never mutated and only used
    /// for sanity assertions that will ICE when `const_qualif` screws up.
    mode: InternMode,
    /// This field stores the mutability of the value *currently* being checked.
    /// When encountering a mutable reference, we determine the pointee mutability
    /// taking into account the mutability of the context: `& &mut i32` is entirely immutable,
    /// despite the nested mutable reference!
    /// The field gets updated when an `UnsafeCell` is encountered.
    mutability: Mutability,
}

#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
    /// Mutable references must in fact be immutable due to their surrounding immutability in a
    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
    /// that will actually be treated as mutable.
    Static,
    /// UnsafeCell is OK in the value of a constant: `const FOO = Cell::new(0)` creates
    /// a new cell every time it is used.
    ConstBase,
    /// `UnsafeCell` ICEs.
    Const,
}

/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;

/// Intern an allocation without looking at its children.
/// `mode` is the mode of the environment where we found this pointer.
/// `mutablity` is the mutability of the place to be interned; even if that says
/// `immutable` things might become mutable if `ty` is not frozen.
fn intern_shallow<'rt, 'mir, 'tcx>(
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    leftover_allocations: &'rt mut FxHashSet<AllocId>,
    mode: InternMode,
    alloc_id: AllocId,
    mutability: Mutability,
    ty: Option<Ty<'tcx>>,
) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
    trace!(
        "InternVisitor::intern {:?} with {:?}",
        alloc_id, mutability,
    );
    // remove allocation
    let tcx = ecx.tcx;
    let memory = ecx.memory_mut();
    let (kind, mut alloc) = match memory.alloc_map.remove(&alloc_id) {
        Some(entry) => entry,
        None => {
            // Pointer not found in local memory map. It is either a pointer to the global
            // map, or dangling.
            // If the pointer is dangling (neither in local nor global memory), we leave it
            // to validation to error. The `delay_span_bug` ensures that we don't forget such
            // a check in validation.
            if tcx.alloc_map.lock().get(alloc_id).is_none() {
                tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
            }
            // treat dangling pointers like other statics
            // just to stop trying to recurse into them
            return Ok(Some(IsStaticOrFn));
        },
    };
    // This match is just a canary for future changes to `MemoryKind`, which most likely need
    // changes in this function.
    match kind {
        MemoryKind::Stack | MemoryKind::Vtable => {},
    }
    // Set allocation mutability as appropriate. This is used by LLVM to put things into
    // read-only memory, and also by Miri when evluating other constants/statics that
    // access this one.
    if mode == InternMode::Static {
        let frozen = ty.map_or(true, |ty| ty.is_freeze(
            ecx.tcx.tcx,
            ecx.param_env,
            ecx.tcx.span,
        ));
        // For statics, allocation mutability is the combination of the place mutability and
        // the type mutability.
        // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere.
        if mutability == Mutability::Immutable && frozen {
            alloc.mutability = Mutability::Immutable;
        } else {
            // Just making sure we are not "upgrading" an immutable allocation to mutable.
            assert_eq!(alloc.mutability, Mutability::Mutable);
        }
    } else {
        // We *could* be non-frozen at `ConstBase`, for constants like `Cell::new(0)`.
        // But we still intern that as immutable as the memory cannot be changed once the
        // initial value was computed.
        // Constants are never mutable.
        assert_eq!(
            mutability, Mutability::Immutable,
            "Something went very wrong: mutability requested for a constant"
        );
        alloc.mutability = Mutability::Immutable;
    };
    // link the alloc id to the actual allocation
    let alloc = tcx.intern_const_alloc(alloc);
    leftover_allocations.extend(alloc.relocations().iter().map(|&(_, ((), reloc))| reloc));
    tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
    Ok(None)
}

impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
    fn intern_shallow(
        &mut self,
        alloc_id: AllocId,
        mutability: Mutability,
        ty: Option<Ty<'tcx>>,
    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
        intern_shallow(
            self.ecx,
            self.leftover_allocations,
            self.mode,
            alloc_id,
            mutability,
            ty,
        )
    }
}

impl<'rt, 'mir, 'tcx>
    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
for
    InternVisitor<'rt, 'mir, 'tcx>
{
    type V = MPlaceTy<'tcx>;

    #[inline(always)]
    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
        &self.ecx
    }

    fn visit_aggregate(
        &mut self,
        mplace: MPlaceTy<'tcx>,
        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
    ) -> InterpResult<'tcx> {
        if let Some(def) = mplace.layout.ty.ty_adt_def() {
            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
                // We are crossing over an `UnsafeCell`, we can mutate again. This means that
                // References we encounter inside here are interned as pointing to mutable
                // allocations.
                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
                assert_ne!(
                    self.mode, InternMode::Const,
                    "UnsafeCells are not allowed behind references in constants. This should have \
                    been prevented statically by const qualification. If this were allowed one \
                    would be able to change a constant at one use site and other use sites could \
                    observe that mutation.",
                );
                let walked = self.walk_aggregate(mplace, fields);
                self.mutability = old;
                return walked;
            }
        }
        self.walk_aggregate(mplace, fields)
    }

    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
        // Handle Reference types, as these are the only relocations supported by const eval.
        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
        let ty = mplace.layout.ty;
        if let ty::Ref(_, referenced_ty, mutability) = ty.sty {
            let value = self.ecx.read_immediate(mplace.into())?;
            // Handle trait object vtables
            if let Ok(meta) = value.to_meta() {
                if let ty::Dynamic(..) =
                    self.ecx.tcx.struct_tail_erasing_lifetimes(
                        referenced_ty, self.ecx.param_env).sty
                {
                    if let Ok(vtable) = meta.unwrap().to_ptr() {
                        // explitly choose `Immutable` here, since vtables are immutable, even
                        // if the reference of the fat pointer is mutable
                        self.intern_shallow(vtable.alloc_id, Mutability::Immutable, None)?;
                    }
                }
            }
            let mplace = self.ecx.ref_to_mplace(value)?;
            // Check if we have encountered this pointer+layout combination before.
            // Only recurse for allocation-backed pointers.
            if let Scalar::Ptr(ptr) = mplace.ptr {
                // We do not have any `frozen` logic here, because it's essentially equivalent to
                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
                // and we check that in `visit_aggregate`.
                // This is not an inherent limitation, but one that we know to be true, because
                // const qualification enforces it. We can lift it in the future.
                match (self.mode, mutability) {
                    // immutable references are fine everywhere
                    (_, hir::Mutability::MutImmutable) => {},
                    // all is "good and well" in the unsoundness of `static mut`

                    // mutable references are ok in `static`. Either they are treated as immutable
                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
                    // and thus ok.
                    (InternMode::Static, hir::Mutability::MutMutable) => {},
                    // we statically prevent `&mut T` via `const_qualif` and double check this here
                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
                    (InternMode::Const, hir::Mutability::MutMutable) => {
                        match referenced_ty.sty {
                            ty::Array(_, n)
                                if n.eval_usize(self.ecx.tcx.tcx, self.ecx.param_env) == 0 => {}
                            ty::Slice(_)
                                if value.to_meta().unwrap().unwrap().to_usize(self.ecx)? == 0 => {}
                            _ => bug!("const qualif failed to prevent mutable references"),
                        }
                    },
                }
                // Compute the mutability with which we'll start visiting the allocation. This is
                // what gets changed when we encounter an `UnsafeCell`
                let mutability = match (self.mutability, mutability) {
                    // The only way a mutable reference actually works as a mutable reference is
                    // by being in a `static mut` directly or behind another mutable reference.
                    // If there's an immutable reference or we are inside a static, then our
                    // mutable reference is equivalent to an immutable one. As an example:
                    // `&&mut Foo` is semantically equivalent to `&&Foo`
                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
                    _ => Mutability::Immutable,
                };
                // Recursing behind references changes the intern mode for constants in order to
                // cause assertions to trigger if we encounter any `UnsafeCell`s.
                let mode = match self.mode {
                    InternMode::ConstBase => InternMode::Const,
                    other => other,
                };
                match self.intern_shallow(ptr.alloc_id, mutability, Some(mplace.layout.ty))? {
                    // No need to recurse, these are interned already and statics may have
                    // cycles, so we don't want to recurse there
                    Some(IsStaticOrFn) => {},
                    // intern everything referenced by this value. The mutability is taken from the
                    // reference. It is checked above that mutable references only happen in
                    // `static mut`
                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
                }
            }
        }
        Ok(())
    }
}

pub fn intern_const_alloc_recursive(
    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
    def_id: DefId,
    ret: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
    let tcx = ecx.tcx;
    // this `mutability` is the mutability of the place, ignoring the type
    let (base_mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
        None => (Mutability::Immutable, InternMode::ConstBase),
        // `static mut` doesn't care about interior mutability, it's mutable anyway
        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
    };

    // Type based interning.
    // `ref_tracking` tracks typed references we have seen and still need to crawl for
    // more typed information inside them.
    // `leftover_allocations` collects *all* allocations we see, because some might not
    // be available in a typed way. They get interned at the end.
    let mut ref_tracking = RefTracking::new((ret, base_mutability, base_intern_mode));
    let leftover_allocations = &mut FxHashSet::default();

    // start with the outermost allocation
    intern_shallow(
        ecx,
        leftover_allocations,
        base_intern_mode,
        ret.ptr.to_ptr()?.alloc_id,
        base_mutability,
        Some(ret.layout.ty)
    )?;

    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
        let interned = InternVisitor {
            ref_tracking: &mut ref_tracking,
            ecx,
            mode,
            leftover_allocations,
            mutability,
        }.visit_value(mplace);
        if let Err(error) = interned {
            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
            // to read enum discriminants in order to find references in enum variant fields.
            if let err_unsup!(ValidationFailure(_)) = error.kind {
                let err = crate::const_eval::error_to_const_error(&ecx, error);
                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
                    Ok(mut diag) => {
                        diag.note(crate::const_eval::note_on_undefined_behavior_error());
                        diag.emit();
                    }
                    Err(ErrorHandled::TooGeneric) |
                    Err(ErrorHandled::Reported) => {},
                }
            }
        }
    }

    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
    // pointers, ... So we can't intern them according to their type rules

    let mut todo: Vec<_> = leftover_allocations.iter().cloned().collect();
    while let Some(alloc_id) = todo.pop() {
        if let Some((_, mut alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
            // We can't call the `intern_shallow` method here, as its logic is tailored to safe
            // references and a `leftover_allocations` set (where we only have a todo-list here).
            // So we hand-roll the interning logic here again.
            if base_intern_mode != InternMode::Static {
                // If it's not a static, it *must* be immutable.
                // We cannot have mutable memory inside a constant.
                // FIXME: ideally we would assert that they already are immutable, to double-
                // check our static checks.
                alloc.mutability = Mutability::Immutable;
            }
            let alloc = tcx.intern_const_alloc(alloc);
            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
            for &(_, ((), reloc)) in alloc.relocations().iter() {
                if leftover_allocations.insert(reloc) {
                    todo.push(reloc);
                }
            }
        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
            // dangling pointer
            throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into()))
        }
    }
    Ok(())
}
<|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 dom::bindings::codegen::HTMLImageElementBinding;
use dom::bindings::codegen::InheritTypes::{NodeCast, HTMLImageElementDerived};
use dom::bindings::codegen::InheritTypes::{ElementCast};
use dom::bindings::js::JS;
use dom::bindings::utils::ErrorResult;
use dom::document::Document;
use dom::element::{Element, HTMLImageElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, NodeHelpers};
use extra::url::Url;
use servo_util::geometry::to_px;
use layout_interface::{ContentBoxQuery, ContentBoxResponse};
use servo_net::image_cache_task;
use servo_net::image_cache_task::ImageCacheTask;
use servo_util::url::parse_url;
use servo_util::namespace::Null;
use servo_util::str::DOMString;

use extra::serialize::{Encoder, Encodable};

#[deriving(Encodable)]
pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    extra: Untraceable,
}

struct Untraceable {
    image: Option<Url>,
}

impl<S: Encoder> Encodable<S> for Untraceable {
    fn encode(&self, _s: &mut S) {
    }
}

impl HTMLImageElementDerived for EventTarget {
    fn is_htmlimageelement(&self) -> bool {
        match self.type_id {
            NodeTargetTypeId(ElementNodeTypeId(HTMLImageElementTypeId)) => true,
            _ => false
        }
    }
}

impl HTMLImageElement {
    pub fn new_inherited(localName: DOMString, document: JS<Document>) -> HTMLImageElement {
        HTMLImageElement {
            htmlelement: HTMLElement::new_inherited(HTMLImageElementTypeId, localName, document),
            extra: Untraceable {
                image: None,
            }
        }
    }

    pub fn new(localName: DOMString, document: &JS<Document>) -> JS<HTMLImageElement> {
        let element = HTMLImageElement::new_inherited(localName, document.clone());
        Node::reflect_node(~element, document, HTMLImageElementBinding::Wrap)
    }
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    fn update_image(&mut self, image_cache: ImageCacheTask, url: Option<Url>) {
        let elem = &mut self.htmlelement.element;
        let src_opt = elem.get_attribute(Null, "src").map(|x| x.get().Value());
        match src_opt {
            None => {}
            Some(src) => {
                let img_url = parse_url(src, url);
                self.extra.image = Some(img_url.clone());

                // inform the image cache to load this, but don't store a
                // handle.
                //
                // TODO (Issue #84): don't prefetch if we are within a
                // <noscript> tag.
                image_cache.send(image_cache_task::Prefetch(img_url));
            }
        }
    }

    pub fn AfterSetAttr(&mut self, name: DOMString, _value: DOMString) {
        if "src" == name {
            let document = self.htmlelement.element.node.owner_doc();
            let window = document.get().window.get();
            let url = window.page.url.as_ref().map(|&(ref url, _)| url.clone());
            self.update_image(window.image_cache_task.clone(), url);
        }
    }

    pub fn AfterRemoveAttr(&mut self, name: DOMString) {
        // FIXME (#1469):
        // This might not handle remove src attribute actually since
        // `self.update_image()` will see the missing src attribute and return early.
        if "src" == name {
            let document = self.htmlelement.element.node.owner_doc();
            let window = document.get().window.get();
            self.update_image(window.image_cache_task.clone(), None);
        }
    }

    pub fn Alt(&self) -> DOMString {
        ~""
    }

    pub fn SetAlt(&mut self, _alt: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Src(&self, _abstract_self: &JS<HTMLImageElement>) -> DOMString {
        ~""
    }

    pub fn SetSrc(&mut self, abstract_self: &JS<HTMLImageElement>, src: DOMString) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(&ElementCast::from(abstract_self), ~"src", src.clone());
        Ok(())
    }

    pub fn CrossOrigin(&self) -> DOMString {
        ~""
    }

    pub fn SetCrossOrigin(&mut self, _cross_origin: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn UseMap(&self) -> DOMString {
        ~""
    }

    pub fn SetUseMap(&mut self, _use_map: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn IsMap(&self) -> bool {
        false
    }

    pub fn SetIsMap(&self, _is_map: bool) -> ErrorResult {
        Ok(())
    }

    pub fn Width(&self, abstract_self: &JS<HTMLImageElement>) -> u32 {
        let node: JS<Node> = NodeCast::from(abstract_self);
        let doc = node.get().owner_doc();
        let page = doc.get().window.get().page;
        let (port, chan) = Chan::new();
        let addr = node.to_trusted_node_address();
        match page.query_layout(ContentBoxQuery(addr, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.width) as u32
            }
        }
    }

    pub fn SetWidth(&mut self, abstract_self: &JS<HTMLImageElement>, width: u32) -> ErrorResult {
        let mut elem: JS<Element> = ElementCast::from(abstract_self);
        let mut elem_clone = elem.clone();
        elem.get_mut().set_attr(&mut elem_clone, ~"width", width.to_str());
        Ok(())
    }

    pub fn Height(&self, abstract_self: &JS<HTMLImageElement>) -> u32 {
        let node = &self.htmlelement.element.node;
        let doc = node.owner_doc();
        let page = doc.get().window.get().page;
        let (port, chan) = Chan::new();
        let this_node: JS<Node> = NodeCast::from(abstract_self);
        let addr = this_node.to_trusted_node_address();
        match page.query_layout(ContentBoxQuery(addr, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.height) as u32
            }
        }
    }

    pub fn SetHeight(&mut self, abstract_self: &JS<HTMLImageElement>, height: u32) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(&ElementCast::from(abstract_self), ~"height", height.to_str());
        Ok(())
    }

    pub fn NaturalWidth(&self) -> u32 {
        0
    }

    pub fn NaturalHeight(&self) -> u32 {
        0
    }

    pub fn Complete(&self) -> bool {
        false
    }

    pub fn Name(&self) -> DOMString {
        ~""
    }

    pub fn SetName(&mut self, _name: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Align(&self) -> DOMString {
        ~""
    }

    pub fn SetAlign(&mut self, _align: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Hspace(&self) -> u32 {
        0
    }

    pub fn SetHspace(&mut self, _hspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn Vspace(&self) -> u32 {
        0
    }

    pub fn SetVspace(&mut self, _vspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn LongDesc(&self) -> DOMString {
        ~""
    }

    pub fn SetLongDesc(&mut self, _longdesc: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Border(&self) -> DOMString {
        ~""
    }

    pub fn SetBorder(&mut self, _border: DOMString) -> ErrorResult {
        Ok(())
    }
}
<commit_msg>Fetch the image cache inside the update_image function.<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 dom::bindings::codegen::HTMLImageElementBinding;
use dom::bindings::codegen::InheritTypes::{NodeCast, HTMLImageElementDerived};
use dom::bindings::codegen::InheritTypes::{ElementCast};
use dom::bindings::js::JS;
use dom::bindings::utils::ErrorResult;
use dom::document::Document;
use dom::element::{Element, HTMLImageElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, NodeHelpers};
use extra::url::Url;
use servo_util::geometry::to_px;
use layout_interface::{ContentBoxQuery, ContentBoxResponse};
use servo_net::image_cache_task;
use servo_util::url::parse_url;
use servo_util::namespace::Null;
use servo_util::str::DOMString;

use extra::serialize::{Encoder, Encodable};

#[deriving(Encodable)]
pub struct HTMLImageElement {
    htmlelement: HTMLElement,
    extra: Untraceable,
}

struct Untraceable {
    image: Option<Url>,
}

impl<S: Encoder> Encodable<S> for Untraceable {
    fn encode(&self, _s: &mut S) {
    }
}

impl HTMLImageElementDerived for EventTarget {
    fn is_htmlimageelement(&self) -> bool {
        match self.type_id {
            NodeTargetTypeId(ElementNodeTypeId(HTMLImageElementTypeId)) => true,
            _ => false
        }
    }
}

impl HTMLImageElement {
    pub fn new_inherited(localName: DOMString, document: JS<Document>) -> HTMLImageElement {
        HTMLImageElement {
            htmlelement: HTMLElement::new_inherited(HTMLImageElementTypeId, localName, document),
            extra: Untraceable {
                image: None,
            }
        }
    }

    pub fn new(localName: DOMString, document: &JS<Document>) -> JS<HTMLImageElement> {
        let element = HTMLImageElement::new_inherited(localName, document.clone());
        Node::reflect_node(~element, document, HTMLImageElementBinding::Wrap)
    }
}

impl HTMLImageElement {
    /// Makes the local `image` member match the status of the `src` attribute and starts
    /// prefetching the image. This method must be called after `src` is changed.
    fn update_image(&mut self, url: Option<Url>) {
        let elem = &mut self.htmlelement.element;
        let document = elem.node.owner_doc();
        let window = document.get().window.get();
        let image_cache = &window.image_cache_task;
        let src_opt = elem.get_attribute(Null, "src").map(|x| x.get().Value());
        match src_opt {
            None => {}
            Some(src) => {
                let img_url = parse_url(src, url);
                self.extra.image = Some(img_url.clone());

                // inform the image cache to load this, but don't store a
                // handle.
                //
                // TODO (Issue #84): don't prefetch if we are within a
                // <noscript> tag.
                image_cache.send(image_cache_task::Prefetch(img_url));
            }
        }
    }

    pub fn AfterSetAttr(&mut self, name: DOMString, _value: DOMString) {
        if "src" == name {
            let document = self.htmlelement.element.node.owner_doc();
            let window = document.get().window.get();
            let url = window.page.url.as_ref().map(|&(ref url, _)| url.clone());
            self.update_image(url);
        }
    }

    pub fn AfterRemoveAttr(&mut self, name: DOMString) {
        // FIXME (#1469):
        // This might not handle remove src attribute actually since
        // `self.update_image()` will see the missing src attribute and return early.
        if "src" == name {
            self.update_image(None);
        }
    }

    pub fn Alt(&self) -> DOMString {
        ~""
    }

    pub fn SetAlt(&mut self, _alt: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Src(&self, _abstract_self: &JS<HTMLImageElement>) -> DOMString {
        ~""
    }

    pub fn SetSrc(&mut self, abstract_self: &JS<HTMLImageElement>, src: DOMString) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(&ElementCast::from(abstract_self), ~"src", src.clone());
        Ok(())
    }

    pub fn CrossOrigin(&self) -> DOMString {
        ~""
    }

    pub fn SetCrossOrigin(&mut self, _cross_origin: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn UseMap(&self) -> DOMString {
        ~""
    }

    pub fn SetUseMap(&mut self, _use_map: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn IsMap(&self) -> bool {
        false
    }

    pub fn SetIsMap(&self, _is_map: bool) -> ErrorResult {
        Ok(())
    }

    pub fn Width(&self, abstract_self: &JS<HTMLImageElement>) -> u32 {
        let node: JS<Node> = NodeCast::from(abstract_self);
        let doc = node.get().owner_doc();
        let page = doc.get().window.get().page;
        let (port, chan) = Chan::new();
        let addr = node.to_trusted_node_address();
        match page.query_layout(ContentBoxQuery(addr, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.width) as u32
            }
        }
    }

    pub fn SetWidth(&mut self, abstract_self: &JS<HTMLImageElement>, width: u32) -> ErrorResult {
        let mut elem: JS<Element> = ElementCast::from(abstract_self);
        let mut elem_clone = elem.clone();
        elem.get_mut().set_attr(&mut elem_clone, ~"width", width.to_str());
        Ok(())
    }

    pub fn Height(&self, abstract_self: &JS<HTMLImageElement>) -> u32 {
        let node = &self.htmlelement.element.node;
        let doc = node.owner_doc();
        let page = doc.get().window.get().page;
        let (port, chan) = Chan::new();
        let this_node: JS<Node> = NodeCast::from(abstract_self);
        let addr = this_node.to_trusted_node_address();
        match page.query_layout(ContentBoxQuery(addr, chan), port) {
            ContentBoxResponse(rect) => {
                to_px(rect.size.height) as u32
            }
        }
    }

    pub fn SetHeight(&mut self, abstract_self: &JS<HTMLImageElement>, height: u32) -> ErrorResult {
        let node = &mut self.htmlelement.element;
        node.set_attr(&ElementCast::from(abstract_self), ~"height", height.to_str());
        Ok(())
    }

    pub fn NaturalWidth(&self) -> u32 {
        0
    }

    pub fn NaturalHeight(&self) -> u32 {
        0
    }

    pub fn Complete(&self) -> bool {
        false
    }

    pub fn Name(&self) -> DOMString {
        ~""
    }

    pub fn SetName(&mut self, _name: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Align(&self) -> DOMString {
        ~""
    }

    pub fn SetAlign(&mut self, _align: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Hspace(&self) -> u32 {
        0
    }

    pub fn SetHspace(&mut self, _hspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn Vspace(&self) -> u32 {
        0
    }

    pub fn SetVspace(&mut self, _vspace: u32) -> ErrorResult {
        Ok(())
    }

    pub fn LongDesc(&self) -> DOMString {
        ~""
    }

    pub fn SetLongDesc(&mut self, _longdesc: DOMString) -> ErrorResult {
        Ok(())
    }

    pub fn Border(&self) -> DOMString {
        ~""
    }

    pub fn SetBorder(&mut self, _border: DOMString) -> ErrorResult {
        Ok(())
    }
}
<|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/.

// Discover or identify devices that may belong to Stratis.

use std::{
    collections::HashMap,
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::Device;

use crate::engine::{
    strat_engine::{
        backstore::metadata::device_identifiers,
        udev::{block_enumerator, decide_ownership, UdevOwnership},
    },
    types::{DevUuid, PoolUuid},
};

// A wrapper for obtaining the device number as a devicemapper Device
// which interprets absence of the value as an error, which it is in this
// context.
fn device_to_devno_wrapper(device: &libudev::Device) -> Result<Device, String> {
    device
        .devnum()
        .ok_or_else(|| "udev entry did not contain a device number".into())
        .map(Device::from)
}

// A wrapper around the metadata module's device_identifers method
// which also handles failure to open a device for reading.
// Returns an error if the device could not be opened for reading.
// Returns Ok(Err(...)) if there was an error while reading the
// Stratis identifiers from the device.
// Returns Ok(Ok(None)) if the identifers did not appear to be on
// the device.
fn device_identifiers_wrapper(
    devnode: &Path,
) -> Result<Result<Option<(PoolUuid, DevUuid)>, String>, String> {
    OpenOptions::new()
        .read(true)
        .open(devnode)
        .as_mut()
        .map_err(|err| {
            format!(
                "device {} could not be opened for reading: {}",
                devnode.display(),
                err
            )
        })
        .map(|f| {
            device_identifiers(f).map_err(|err| {
                format!(
                    "encountered an error while reading Stratis header for device {}: {}",
                    devnode.display(),
                    err
                )
            })
        })
}

/// Process a device which udev information indicates is a Stratis device.
fn process_stratis_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err))) => {
                    warn!("udev identified device {} as a Stratis device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                (_, Ok(Ok(None))) => {
                    warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, disregarding the device",
                          devnode.display());
                    None
                }
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, disregarding device");
            None
        }
    }
}

/// Process a device which udev information indicates is unowned.
fn process_unowned_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) => {
                    warn!("udev identified device {} as a block device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                // FIXME: Refine error return in StaticHeader::setup(),
                // so it can be used to distinguish between signficant
                // and insignficant errors and then use that ability to
                // distinguish here between different levels of
                // severity.
                (_, Ok(Err(err))) => {
                    debug!("udev identified device {} as a block device but {}, disregarding the device",
                           devnode.display(),
                           err);
                    None
                }
                (_, Ok(Ok(None))) => None,
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a block device, but the udev entry for the device had no device node, disregarding the device");
            None
        }
    }
}

// Use udev to identify all block devices and return the subset of those
// that have Stratis signatures.
fn find_all_block_devices_with_stratis_signatures(
) -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter_map(|dev| {
            decide_ownership(&dev)
                .map_err(|err| {
                    warn!("Could not determine ownership of a udev block device because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
            .map(|decision| match decision {
                UdevOwnership::Stratis => process_stratis_device(&dev),
                UdevOwnership::Unowned => process_unowned_device(&dev),
                _ => None,
            })
            .unwrap_or(None)
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, _, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });

    Ok(pool_map)
}

// Find all devices identified by udev as Stratis devices.
fn find_all_stratis_devices() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;
    enumerator.match_property("ID_FS_TYPE", "stratis")?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                warn!("Found a udev entry for a device identified as a Stratis device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter_map(|dev| {
            decide_ownership(&dev)
                .map_err(|err| {
                    warn!("Could not determine ownership of a block device identified as a Stratis device by udev because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
            .map(|decision| match decision {
                UdevOwnership::Stratis => process_stratis_device(&dev),
                UdevOwnership::MultipathMember => None,
                _ => {
                    warn!("udev enumeration identified this device as a Stratis block device but on further examination it appears not to belong to Stratis");
                    None
                }
            })
            .unwrap_or(None)
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, _, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });
    Ok(pool_map)
}

/// Identify a block device in the context where a udev event has been
/// captured for some block device. Return None if the device does not
/// appear to be a Stratis device. Log at an appropriate level on all errors.
pub fn identify_block_device(
    dev: &libudev::Device,
) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a udev block device because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => process_stratis_device(dev),
            UdevOwnership::Unowned => process_unowned_device(dev),
            _ => None,
        },
    }
}

/// Retrieve all block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that appear to be
/// multipath members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev rather than just all Stratis block devices.
///
/// Omits any device that appears problematic in some way.
///
/// Return an error only on a failure to construct or scan with a udev
/// enumerator.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    info!("Beginning initial search for Stratis block devices");
    let pool_map = find_all_stratis_devices()?;

    if pool_map.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and process only those that appear
        // unclaimed or appear to be claimed by Stratis (and not
        // multipath members).

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        find_all_block_devices_with_stratis_signatures()
    } else {
        Ok(pool_map)
    }
}
<commit_msg>Use identify_block_device in the body of a find method<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/.

// Discover or identify devices that may belong to Stratis.

use std::{
    collections::HashMap,
    fs::OpenOptions,
    path::{Path, PathBuf},
};

use libudev;

use devicemapper::Device;

use crate::engine::{
    strat_engine::{
        backstore::metadata::device_identifiers,
        udev::{block_enumerator, decide_ownership, UdevOwnership},
    },
    types::{DevUuid, PoolUuid},
};

// A wrapper for obtaining the device number as a devicemapper Device
// which interprets absence of the value as an error, which it is in this
// context.
fn device_to_devno_wrapper(device: &libudev::Device) -> Result<Device, String> {
    device
        .devnum()
        .ok_or_else(|| "udev entry did not contain a device number".into())
        .map(Device::from)
}

// A wrapper around the metadata module's device_identifers method
// which also handles failure to open a device for reading.
// Returns an error if the device could not be opened for reading.
// Returns Ok(Err(...)) if there was an error while reading the
// Stratis identifiers from the device.
// Returns Ok(Ok(None)) if the identifers did not appear to be on
// the device.
fn device_identifiers_wrapper(
    devnode: &Path,
) -> Result<Result<Option<(PoolUuid, DevUuid)>, String>, String> {
    OpenOptions::new()
        .read(true)
        .open(devnode)
        .as_mut()
        .map_err(|err| {
            format!(
                "device {} could not be opened for reading: {}",
                devnode.display(),
                err
            )
        })
        .map(|f| {
            device_identifiers(f).map_err(|err| {
                format!(
                    "encountered an error while reading Stratis header for device {}: {}",
                    devnode.display(),
                    err
                )
            })
        })
}

/// Process a device which udev information indicates is a Stratis device.
fn process_stratis_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) | (_, Ok(Err(err))) => {
                    warn!("udev identified device {} as a Stratis device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                (_, Ok(Ok(None))) => {
                    warn!("udev identified device {} as a Stratis device but there appeared to be no Stratis metadata on the device, disregarding the device",
                          devnode.display());
                    None
                }
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a Stratis device, but the udev entry for the device had no device node, disregarding device");
            None
        }
    }
}

/// Process a device which udev information indicates is unowned.
fn process_unowned_device(dev: &libudev::Device) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    match dev.devnode() {
        Some(devnode) => {
            match (
                device_to_devno_wrapper(dev),
                device_identifiers_wrapper(devnode),
            ) {
                (Err(err), _) | (_, Err(err)) => {
                    warn!("udev identified device {} as a block device but {}, disregarding the device",
                          devnode.display(),
                          err);
                    None
                }
                // FIXME: Refine error return in StaticHeader::setup(),
                // so it can be used to distinguish between signficant
                // and insignficant errors and then use that ability to
                // distinguish here between different levels of
                // severity.
                (_, Ok(Err(err))) => {
                    debug!("udev identified device {} as a block device but {}, disregarding the device",
                           devnode.display(),
                           err);
                    None
                }
                (_, Ok(Ok(None))) => None,
                (Ok(devno), Ok(Ok(Some((pool_uuid, device_uuid))))) => {
                    Some((pool_uuid, device_uuid, devno, devnode.to_path_buf()))
                }
            }
        }
        None => {
            warn!("udev identified a device as a block device, but the udev entry for the device had no device node, disregarding the device");
            None
        }
    }
}

// Use udev to identify all block devices and return the subset of those
// that have Stratis signatures.
fn find_all_block_devices_with_stratis_signatures(
) -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;

    let pool_map = enumerator
        .scan_devices()?
        .filter_map(|dev| identify_block_device(&dev))
        .fold(
            HashMap::new(),
            |mut acc, (pool_uuid, _, device, devnode)| {
                acc.entry(pool_uuid)
                    .or_insert_with(HashMap::new)
                    .insert(device, devnode);
                acc
            },
        );

    Ok(pool_map)
}

// Find all devices identified by udev as Stratis devices.
fn find_all_stratis_devices() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let context = libudev::Context::new()?;
    let mut enumerator = block_enumerator(&context)?;
    enumerator.match_property("ID_FS_TYPE", "stratis")?;

    let pool_map = enumerator.scan_devices()?
        .filter(|dev| {
            let initialized = dev.is_initialized();
            if !initialized {
                warn!("Found a udev entry for a device identified as a Stratis device, but udev also identified it as uninitialized, omitting the device from the set of devices to process, for safety");
            };
            initialized
        })
        .filter_map(|dev| {
            decide_ownership(&dev)
                .map_err(|err| {
                    warn!("Could not determine ownership of a block device identified as a Stratis device by udev because of an error processing udev information, omitting the device from the set of devices to process, for safety: {}",
                          err);
                })
            .map(|decision| match decision {
                UdevOwnership::Stratis => process_stratis_device(&dev),
                UdevOwnership::MultipathMember => None,
                _ => {
                    warn!("udev enumeration identified this device as a Stratis block device but on further examination it appears not to belong to Stratis");
                    None
                }
            })
            .unwrap_or(None)
        })
        .fold(HashMap::new(), |mut acc, (pool_uuid, _, device, devnode)| {
            acc.entry(pool_uuid).or_insert_with(HashMap::new).insert(device, devnode);
            acc
        });
    Ok(pool_map)
}

/// Identify a block device in the context where a udev event has been
/// captured for some block device. Return None if the device does not
/// appear to be a Stratis device. Log at an appropriate level on all errors.
pub fn identify_block_device(
    dev: &libudev::Device,
) -> Option<(PoolUuid, DevUuid, Device, PathBuf)> {
    let initialized = dev.is_initialized();
    if !initialized {
        debug!("Found a udev entry for a device identified as a block device, but udev also identified it as uninitialized, disregarding the device");
        return None;
    };

    match decide_ownership(dev) {
        Err(err) => {
            warn!("Could not determine ownership of a udev block device because of an error processing udev information, disregarding the device: {}",
                  err);
            None
        }
        Ok(ownership) => match ownership {
            UdevOwnership::Stratis => process_stratis_device(dev),
            UdevOwnership::Unowned => process_unowned_device(dev),
            _ => None,
        },
    }
}

/// Retrieve all block devices that should be made use of by the
/// Stratis engine. This excludes Stratis block devices that appear to be
/// multipath members.
///
/// Includes a fallback path, which is used if no Stratis block devices are
/// found using the obvious udev property- and enumerator-based approach.
/// This fallback path is more expensive, because it must search all block
/// devices via udev rather than just all Stratis block devices.
///
/// Omits any device that appears problematic in some way.
///
/// Return an error only on a failure to construct or scan with a udev
/// enumerator.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> libudev::Result<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    info!("Beginning initial search for Stratis block devices");
    let pool_map = find_all_stratis_devices()?;

    if pool_map.is_empty() {
        // No Stratis devices have been found, possible reasons are:
        // 1. There are none
        // 2. There are some but libblkid version is less than 2.32, so
        // Stratis devices are not recognized by udev.
        // 3. There are many incomplete udev entries, because this code is
        // being run before the udev database is populated.
        //
        // Try again to find Stratis block devices, but this time enumerate
        // all block devices, not just all the ones that can be identified
        // as Stratis blockdevs by udev, and process only those that appear
        // unclaimed or appear to be claimed by Stratis (and not
        // multipath members).

        info!("Could not identify any Stratis devices by a udev search for devices with ID_FS_TYPE=\"stratis\"; using fallback search mechanism");

        find_all_block_devices_with_stratis_signatures()
    } else {
        Ok(pool_map)
    }
}
<|endoftext|>


<commit_before>use rand::distributions::{Exp, IndependentSample};

use {Distribution, Generator};

/// A continuous uniform distribution.
#[derive(Clone, Copy)]
pub struct Exponential {
    /// The rate parameter
    pub lambda: f64,
    sampler: Exp,
}

impl Exponential {
    /// Create an exponential distribution with `rate = lambda`.
    ///
    /// # Panics
    ///
    /// Panics if `lambda <= 0`.
    #[inline]
    pub fn new(lambda: f64) -> Exponential {
        assert!(lambda > 0.0, "Exponental::new() called with lambda <= 0");
        Exponential { lambda: lambda, sampler: Exp::new(lambda) }
    }
}

impl Distribution for Exponential {
    type Item = f64;

    #[inline]
    fn pdf(&self, x: f64) -> f64 {
        if x < 0.0 { 0.0 }
        else { self.lambda * (-self.lambda * x).exp() }
    }

    #[inline]
    fn cdf(&self, x: f64) -> f64 {
        if x <= 0.0 { 0.0 }
        else { -(-self.lambda * x).exp_m1() }
    }

    #[inline]
    fn inv_cdf(&self, p: f64) -> f64 {
        assert!(0.0 <= p && p <= 1.0,
                "Quantile functiona called with `p` out of scope");
        -(-p).ln_1p() / self.lambda
    }

    #[inline]
    fn sample<G: Generator>(&self, generator: &mut G) -> f64 {
        self.sampler.ind_sample(generator)
    }
}

#[cfg(test)]
mod tests {
    use assert;

    use Distribution;
    use distributions::Exponential;

    #[test]
    #[should_panic]
    #[allow(unused_variables)]
    fn negative_lambda() {
        let exponential = Exponential::new(-1.0);
    }

    #[test]
    fn pdf() {
        let exponential = Exponential::new(2.0);
        let x = vec![
            -1.0, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 12.0
        ];
        let p = vec![
            0.0, 2.0, 7.357588823428847e-1, 2.706705664732254e-1,
            9.957413673572789e-2, 3.663127777746836e-2, 1.3475893998170934e-2,
            4.957504353332717e-3, 6.709252558050237e-4,
            1.228842470665642e-5, 7.550269088558195e-11
        ];

        assert_eq!(&x.iter().map(|&x| exponential.pdf(x)).collect::<Vec<_>>(), &p);
    }

    #[test]
    fn cdf() {
        let exponential = Exponential::new(2.0);
        let x = vec![
            -1.0, 0.0, 0.01, 0.05, 0.1, 0.15,
            0.25, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0
        ];
        let p = vec![
            0.0, 0.0, 1.98013266932447e-2, 9.516258196404043e-2,
            0.18126924692201815, 0.2591817793182821, 0.3934693402873666,
            0.6321205588285577, 0.8646647167633873, 0.950212931632136,
            0.9816843611112658, 0.9975212478233336, 0.9996645373720975

        ];

        assert::within(&x.iter().map(|&x| exponential.cdf(x)).collect::<Vec<_>>(),
                       &p, 1e-18);
    }

    #[test]
    fn inv_cdf() {
        use std::f64::INFINITY;

        let exponential = Exponential::new(2.0);
        let x = vec![
            0.0, 0.01, 0.05, 0.1, 0.15, 0.25, 0.5, 1.0, 1.5, 2.0,
            3.0, 4.0, INFINITY
        ];
        let p = vec![
            0.0, 1.98013266932447e-2, 9.516258196404043e-2,
            0.18126924692201815, 0.2591817793182821, 0.3934693402873666,
            0.6321205588285577, 0.8646647167633873, 0.950212931632136,
            0.9816843611112658, 0.9975212478233336, 0.9996645373720975, 1.0
        ];

        assert::within(&p.iter().map(|&p| exponential.inv_cdf(p))
                                .collect::<Vec<_>>(),
                       &x, 1e-14);
    }

    #[test]
    #[should_panic]
    fn invalid_quantile() {
        let exponential = Exponential::new(2.0);
        exponential.inv_cdf(-0.2);
        exponential.inv_cdf(1.2);
    }
}
<commit_msg>Clean up<commit_after>use rand::distributions::{Exp, IndependentSample};

use {Distribution, Generator};

/// A continuous exponential distribution with the rate parameter
/// `lambda`.
#[derive(Clone, Copy)]
pub struct Exponential {
    /// The rate parameter
    pub lambda: f64,
    sampler: Exp,
}

impl Exponential {
    /// Create an exponential distribution with `rate = lambda`.
    ///
    /// # Panics
    ///
    /// Panics if `lambda <= 0`.
    #[inline]
    pub fn new(lambda: f64) -> Exponential {
        assert!(lambda > 0.0, "Exponental::new() called with lambda <= 0");
        Exponential { lambda: lambda, sampler: Exp::new(lambda) }
    }
}

impl Distribution for Exponential {
    type Item = f64;

    #[inline]
    fn pdf(&self, x: f64) -> f64 {
        if x < 0.0 { 0.0 }
        else { self.lambda * (-self.lambda * x).exp() }
    }

    #[inline]
    fn cdf(&self, x: f64) -> f64 {
        if x <= 0.0 { 0.0 }
        else { -(-self.lambda * x).exp_m1() }
    }

    #[inline]
    fn inv_cdf(&self, p: f64) -> f64 {
        debug_assert!(0.0 <= p && p <= 1.0,
                      "inv_cdf called with `p` not between 0 and 1");
        -(-p).ln_1p() / self.lambda
    }

    #[inline]
    fn sample<G: Generator>(&self, generator: &mut G) -> f64 {
        self.sampler.ind_sample(generator)
    }
}

#[cfg(test)]
mod tests {
    use assert;

    use Distribution;
    use distributions::Exponential;

    #[test]
    #[should_panic]
    #[allow(unused_variables)]
    fn negative_lambda() {
        let exponential = Exponential::new(-1.0);
    }

    #[test]
    fn pdf() {
        let exponential = Exponential::new(2.0);
        let x = vec![
            -1.0, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 6.0, 12.0
        ];
        let p = vec![
            0.0, 2.0, 7.357588823428847e-1, 2.706705664732254e-1,
            9.957413673572789e-2, 3.663127777746836e-2, 1.3475893998170934e-2,
            4.957504353332717e-3, 6.709252558050237e-4,
            1.228842470665642e-5, 7.550269088558195e-11
        ];

        assert_eq!(&x.iter().map(|&x| exponential.pdf(x)).collect::<Vec<_>>(), &p);
    }

    #[test]
    fn cdf() {
        let exponential = Exponential::new(2.0);
        let x = vec![
            -1.0, 0.0, 0.01, 0.05, 0.1, 0.15,
            0.25, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0
        ];
        let p = vec![
            0.0, 0.0, 1.98013266932447e-2, 9.516258196404043e-2,
            0.18126924692201815, 0.2591817793182821, 0.3934693402873666,
            0.6321205588285577, 0.8646647167633873, 0.950212931632136,
            0.9816843611112658, 0.9975212478233336, 0.9996645373720975
        ];

        assert::within(&x.iter().map(|&x| exponential.cdf(x)).collect::<Vec<_>>(),
                       &p, 1e-18);
    }

    #[test]
    fn inv_cdf() {
        use std::f64::INFINITY;

        let exponential = Exponential::new(2.0);
        let x = vec![
            0.0, 0.01, 0.05, 0.1, 0.15, 0.25, 0.5, 1.0, 1.5, 2.0,
            3.0, 4.0, INFINITY
        ];
        let p = vec![
            0.0, 1.98013266932447e-2, 9.516258196404043e-2,
            0.18126924692201815, 0.2591817793182821, 0.3934693402873666,
            0.6321205588285577, 0.8646647167633873, 0.950212931632136,
            0.9816843611112658, 0.9975212478233336, 0.9996645373720975, 1.0
        ];

        assert::within(&p.iter().map(|&p| exponential.inv_cdf(p))
                                .collect::<Vec<_>>(), &x, 1e-14);
    }

    #[test]
    #[should_panic]
    fn invalid_quantile() {
        let exponential = Exponential::new(2.0);
        exponential.inv_cdf(-0.2);
        exponential.inv_cdf(1.2);
    }
}
<|endoftext|>


<commit_before>//! GLTF package loader for gfx-rs

#![crate_name = "gltf"]
#![feature(phase)]

extern crate serialize;
extern crate cgmath;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate serde;
#[phase(plugin)]
extern crate serde_macros;

use std::cmp;
use std::collections::HashMap;
use std::io::File;
use serde::{de, json};

mod types;

fn load_map<T: de::Deserializable<json::JsonDeserializer, json::ParserError>, R>(
            json: &json::Json, var: &str, fun: |T| -> R) -> HashMap<String, R> {
    match json.find(&var.to_string()) {
        Some(&json::Object(ref map)) => map.iter().map(|(id, sub)| {
            let v: T = json::from_json(sub.clone()).unwrap();
            (id.clone(), fun(v))
        }).collect(),
        _ => HashMap::new(),
    }
}

fn attrib_to_slice(attrib: &gfx::Attribute)
                   -> Result<gfx::Slice, ()> {
    use gfx::attrib as a;
    match (attrib.format.elem_count, attrib.format.elem_type) {
        (1, a::Int(a::IntRaw, a::U8, a::Unsigned)) => Ok(gfx::IndexSlice8(
            gfx::TriangleList,
            gfx::BufferHandle::from_raw(attrib.buffer),
            0,
            attrib.format.offset >> 0
        )),
        (1, a::Int(a::IntRaw, a::U16, a::Unsigned)) => Ok(gfx::IndexSlice16(
            gfx::TriangleList,
            gfx::BufferHandle::from_raw(attrib.buffer),
            0,
            attrib.format.offset >> 1
        )),
        (1, a::Int(a::IntRaw, a::U32, a::Unsigned)) => Ok(gfx::IndexSlice32(
            gfx::TriangleList,
            gfx::BufferHandle::from_raw(attrib.buffer),
            0,
            attrib.format.offset >> 2
        )),
        _ => Err(())
    }
}

#[deriving(Clone, PartialEq, Show)]
pub enum AccessorError {
    AccessorMatrix(u8),
    AccessorUnknown(String),
}

fn parse_accessor_type(ty: &str) -> Result<(gfx::attrib::Count,
                       gfx::attrib::Type), AccessorError> {
    use gfx::attrib as a;
    match ty {
        "SCALAR" => Ok((1, a::Float(a::FloatDefault, a::F32))),
        "VEC2"   => Ok((2, a::Float(a::FloatDefault, a::F32))),
        "VEC3"   => Ok((3, a::Float(a::FloatDefault, a::F32))),
        "VEC4"   => Ok((4, a::Float(a::FloatDefault, a::F32))),
        "MAT2"   => Err(AccessorMatrix(2)),
        "MAT3"   => Err(AccessorMatrix(3)),
        "MAT4"   => Err(AccessorMatrix(4)),
        _        => Err(AccessorUnknown(ty.to_string())),
    }
}

pub enum LoadError {
    ErrorString,
    ErrorJson,
}

pub struct SubMesh {
    mesh: gfx::Mesh,
    slice: gfx::Slice,
    material: String,
}

pub struct Package {
    pub buffers: HashMap<String, gfx::RawBufferHandle>,
    pub attributes: HashMap<String, (gfx::Attribute, uint)>,
    pub models: HashMap<String, Vec<SubMesh>>,
    pub shaders: HashMap<String, gfx::ShaderHandle>,
}

impl Package {
    fn load<C: gfx::CommandBuffer, D: gfx::Device<C>>(input: &str, device: &mut D)
            -> Result<Package, LoadError> {
        let json = match json::from_str(input) {
            Ok(j) => j,
            Err(_e) => return Err(ErrorString),
        };
        let buffers = load_map(&json, "buffers", |b: types::Buffer| {
            let data = File::open(&Path::new(b.uri)).read_to_end().unwrap();
            debug_assert_eq!(data.len(), b.byteLength);
            device.create_buffer_static(&data).raw()
        });
        let attributes = load_map(&json, "accessors", |a: types::Accessor| {
            let (el_count, el_ty) = parse_accessor_type(a.ty.as_slice()).unwrap();
            (gfx::Attribute {
                name: a.name,
                buffer: *buffers.find(&a.bufferView).unwrap(),
                format: gfx::attrib::Format {
                    elem_count: el_count,
                    elem_type: el_ty,
                    offset: a.byteOffset as gfx::attrib::Offset,
                    stride: a.byteStride as gfx::attrib::Stride,
                    instance_rate: 0,
                },
            }, a.count)
        });
        let models = load_map(&json, "meshes", |m: types::Mesh| {
            //(m.name.clone(), gfx::Mesh::new(10), gfx::VertexSlice(gfx::Point, 0, 0))
            m.primitives.iter().map(|prim| SubMesh {
                mesh: gfx::Mesh {
                    num_vertices: prim.attributes.values().fold(-1u, |min, id| {
                        let &(_, count) = attributes.find(id).unwrap();
                        cmp::min(min, count)
                    }) as gfx::VertexCount,
                    attributes: prim.attributes.iter().map(|(name, id)| {
                        let (mut at, _) = attributes.find(id).unwrap().clone();
                        at.name = name.clone();
                        at
                    }).collect(),
                },
                slice: attrib_to_slice(attributes.find(&prim.indices)
                    .unwrap().ref0()).unwrap(),
                material: prim.material.clone(),
            }).collect()
        });
        let shaders = load_map(&json, "shaders", |s: types::Shader| {
            let kind = gfx::shade::Vertex; //TODO s.type
            let data = File::open(&Path::new(s.uri)).read_to_end().unwrap();
            let source = gfx::ShaderSource {
                glsl_120: None,
                glsl_150: Some(gfx::OwnedBytes(data)),
            };
            device.create_shader(kind, source).unwrap()
        });
        Ok(Package {
            buffers: buffers,
            attributes: attributes,
            models: models,
            shaders: shaders,
        })
    }
}
<commit_msg>gltf - shader stage parsing<commit_after>//! GLTF package loader for gfx-rs

#![crate_name = "gltf"]
#![feature(phase)]

extern crate serialize;
extern crate cgmath;
extern crate gfx;
#[phase(plugin)]
extern crate gfx_macros;
extern crate serde;
#[phase(plugin)]
extern crate serde_macros;

use std::cmp;
use std::collections::HashMap;
use std::io::File;
use serde::{de, json};

mod types;

fn load_map<T: de::Deserializable<json::JsonDeserializer, json::ParserError>, R>(
            json: &json::Json, var: &str, fun: |T| -> R) -> HashMap<String, R> {
    match json.find(&var.to_string()) {
        Some(&json::Object(ref map)) => map.iter().map(|(id, sub)| {
            let v: T = json::from_json(sub.clone()).unwrap();
            (id.clone(), fun(v))
        }).collect(),
        _ => HashMap::new(),
    }
}

fn attrib_to_slice(attrib: &gfx::Attribute)
                   -> Result<gfx::Slice, ()> {
    use gfx::attrib as a;
    match (attrib.format.elem_count, attrib.format.elem_type) {
        (1, a::Int(a::IntRaw, a::U8, a::Unsigned)) => Ok(gfx::IndexSlice8(
            gfx::TriangleList,
            gfx::BufferHandle::from_raw(attrib.buffer),
            0,
            attrib.format.offset >> 0
        )),
        (1, a::Int(a::IntRaw, a::U16, a::Unsigned)) => Ok(gfx::IndexSlice16(
            gfx::TriangleList,
            gfx::BufferHandle::from_raw(attrib.buffer),
            0,
            attrib.format.offset >> 1
        )),
        (1, a::Int(a::IntRaw, a::U32, a::Unsigned)) => Ok(gfx::IndexSlice32(
            gfx::TriangleList,
            gfx::BufferHandle::from_raw(attrib.buffer),
            0,
            attrib.format.offset >> 2
        )),
        _ => Err(())
    }
}

#[deriving(Clone, PartialEq, Show)]
pub enum AccessorError {
    AccessorMatrix(u8),
    AccessorUnknown(String),
}

fn parse_accessor_type(ty: &str) -> Result<(gfx::attrib::Count,
                       gfx::attrib::Type), AccessorError> {
    use gfx::attrib as a;
    match ty {
        "SCALAR" => Ok((1, a::Float(a::FloatDefault, a::F32))),
        "VEC2"   => Ok((2, a::Float(a::FloatDefault, a::F32))),
        "VEC3"   => Ok((3, a::Float(a::FloatDefault, a::F32))),
        "VEC4"   => Ok((4, a::Float(a::FloatDefault, a::F32))),
        "MAT2"   => Err(AccessorMatrix(2)),
        "MAT3"   => Err(AccessorMatrix(3)),
        "MAT4"   => Err(AccessorMatrix(4)),
        _        => Err(AccessorUnknown(ty.to_string())),
    }
}

#[deriving(Clone, PartialEq, Show)]
pub struct ShaderError(uint);

fn parse_shader_type(ty: uint) -> Result<gfx::shade::Stage, ShaderError> {
    match ty {
        35633 => Ok(gfx::shade::Vertex),
        35632 => Ok(gfx::shade::Fragment),
        _ => Err(ShaderError(ty)),
    }
}

pub enum LoadError {
    ErrorString,
    ErrorJson,
}

pub struct SubMesh {
    mesh: gfx::Mesh,
    slice: gfx::Slice,
    material: String,
}

pub struct Package {
    pub buffers: HashMap<String, gfx::RawBufferHandle>,
    pub attributes: HashMap<String, (gfx::Attribute, uint)>,
    pub models: HashMap<String, Vec<SubMesh>>,
    pub shaders: HashMap<String, gfx::ShaderHandle>,
}

impl Package {
    fn load<C: gfx::CommandBuffer, D: gfx::Device<C>>(input: &str, device: &mut D)
            -> Result<Package, LoadError> {
        let json = match json::from_str(input) {
            Ok(j) => j,
            Err(_e) => return Err(ErrorString),
        };
        let buffers = load_map(&json, "buffers", |b: types::Buffer| {
            let data = File::open(&Path::new(b.uri)).read_to_end().unwrap();
            debug_assert_eq!(data.len(), b.byteLength);
            device.create_buffer_static(&data).raw()
        });
        let attributes = load_map(&json, "accessors", |a: types::Accessor| {
            let (el_count, el_ty) = parse_accessor_type(a.ty.as_slice()).unwrap();
            (gfx::Attribute {
                name: a.name,
                buffer: *buffers.find(&a.bufferView).unwrap(),
                format: gfx::attrib::Format {
                    elem_count: el_count,
                    elem_type: el_ty,
                    offset: a.byteOffset as gfx::attrib::Offset,
                    stride: a.byteStride as gfx::attrib::Stride,
                    instance_rate: 0,
                },
            }, a.count)
        });
        let models = load_map(&json, "meshes", |m: types::Mesh| {
            //(m.name.clone(), gfx::Mesh::new(10), gfx::VertexSlice(gfx::Point, 0, 0))
            m.primitives.iter().map(|prim| SubMesh {
                mesh: gfx::Mesh {
                    num_vertices: prim.attributes.values().fold(-1u, |min, id| {
                        let &(_, count) = attributes.find(id).unwrap();
                        cmp::min(min, count)
                    }) as gfx::VertexCount,
                    attributes: prim.attributes.iter().map(|(name, id)| {
                        let (mut at, _) = attributes.find(id).unwrap().clone();
                        at.name = name.clone();
                        at
                    }).collect(),
                },
                slice: attrib_to_slice(attributes.find(&prim.indices)
                    .unwrap().ref0()).unwrap(),
                material: prim.material.clone(),
            }).collect()
        });
        let shaders = load_map(&json, "shaders", |s: types::Shader| {
            let stage = parse_shader_type(s.ty).unwrap();
            let data = File::open(&Path::new(s.uri)).read_to_end().unwrap();
            let source = gfx::ShaderSource {
                glsl_120: None,
                glsl_150: Some(gfx::OwnedBytes(data)),
            };
            device.create_shader(stage, source).unwrap()
        });
        Ok(Package {
            buffers: buffers,
            attributes: attributes,
            models: models,
            shaders: shaders,
        })
    }
}
<|endoftext|>


<commit_before>use draw_state::DrawState;
use types::{ self, Matrix2d, Scalar };
use deform::DeformGrid;
use {
    CircleArc,
    Ellipse,
    Image,
    ImageSize,
    Line,
    Polygon,
    Rectangle,
};

/// Implemented by all graphics back-ends.
///
/// Default trait methods can be overridden for better performance or higher
/// quality.
///
/// When drawing, use this trait as generic constraint:
///
/// ```ignore
/// fn draw<G: Graphics>(c: &Context, g: &mut G) { ... }
/// ```
pub trait Graphics: Sized {
    /// The texture type associated with the back-end.
    ///
    /// In generic code, this type is often unknown.
    /// This might lead to more boilerplate code:
    ///
    /// ```ignore
    /// fn draw_texture<G, T>(c: &Context, g: &mut G)
    ///     where G: Graphcis<Texture = T>, T: ImageSize { ... }
    /// ```
    ///
    /// Code written specifically for one back-end can be easier to write.
    /// Later, when the code is done, it can be refactored into generic code.
    type Texture: ImageSize;

    /// Clears background with a color.
    fn clear_color(&mut self, color: types::Color);

    /// Clears stencil buffer with a value.
    fn clear_stencil(&mut self, value: u8);

    /// Renders list of 2d triangles.
    fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], f: F)
        where F: FnMut(&mut FnMut(&[f32]));

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        f: F
    ) where F: FnMut(&mut FnMut(&[f32], &[f32]));

    /// Draws a rectangle.
    #[inline(always)]
    fn rectangle<R: Into<types::Rectangle>>(
        &mut self,
        r: &Rectangle,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        r.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws a polygon.
    #[inline(always)]
    fn polygon(
        &mut self,
        p: &Polygon,
        polygon: types::Polygon,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tri(polygon, draw_state, transform, self);
    }

    /// Draws a tweened polygon using linear interpolation.
    #[inline(always)]
    fn polygon_tween_lerp(
        &mut self,
        p: &Polygon,
        polygons: types::Polygons,
        tween_factor: Scalar,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tween_lerp_tri(polygons, tween_factor,
            draw_state, transform, self);
    }

    /// Draws image.
    #[inline(always)]
    fn image(
        &mut self,
        image: &Image,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        image.draw_tri(texture, draw_state, transform, self);
    }

    /// Draws ellipse.
    #[inline(always)]
    fn ellipse<R: Into<types::Rectangle>>(
        &mut self,
        e: &Ellipse,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        e.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws line.
    #[inline(always)]
    fn line<L: Into<types::Line>>(
        &mut self,
        l: &Line,
        line: L,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        l.draw_tri(line, draw_state, transform, self);
    }

    /// Draws circle arc.
    #[inline(always)]
    fn circle_arc<R: Into<types::Rectangle>>(
        &mut self,
        c: &CircleArc,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        c.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws deformed image.
    #[inline(always)]
    fn deform_image(
        &mut self,
        d: &DeformGrid,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        d.draw_image_tri(texture, draw_state, transform, self);
    }
}
<commit_msg>Improved docs for `Graphics::clear_stencil`<commit_after>use draw_state::DrawState;
use types::{ self, Matrix2d, Scalar };
use deform::DeformGrid;
use {
    CircleArc,
    Ellipse,
    Image,
    ImageSize,
    Line,
    Polygon,
    Rectangle,
};

/// Implemented by all graphics back-ends.
///
/// Default trait methods can be overridden for better performance or higher
/// quality.
///
/// When drawing, use this trait as generic constraint:
///
/// ```ignore
/// fn draw<G: Graphics>(c: &Context, g: &mut G) { ... }
/// ```
pub trait Graphics: Sized {
    /// The texture type associated with the back-end.
    ///
    /// In generic code, this type is often unknown.
    /// This might lead to more boilerplate code:
    ///
    /// ```ignore
    /// fn draw_texture<G, T>(c: &Context, g: &mut G)
    ///     where G: Graphcis<Texture = T>, T: ImageSize { ... }
    /// ```
    ///
    /// Code written specifically for one back-end can be easier to write.
    /// Later, when the code is done, it can be refactored into generic code.
    type Texture: ImageSize;

    /// Clears background with a color.
    ///
    /// The color should replace the values in the buffer.
    fn clear_color(&mut self, color: types::Color);

    /// Clears stencil buffer with a value, usually 0.
    ///
    /// A stencil buffer contains values that are not visible on the screen.
    /// These values are used to test against the pixel to paint.
    ///
    /// If you are drawing a shape for clipping and forgot to clear the
    /// stencil buffer, then the clipping shape will carry over in next frame
    /// and cause artifacts.
    fn clear_stencil(&mut self, value: u8);

    /// Renders list of 2d triangles.
    fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], f: F)
        where F: FnMut(&mut FnMut(&[f32]));

    /// Renders list of 2d triangles.
    ///
    /// A texture coordinate is assigned per vertex.
    /// The texture coordinates refers to the current texture.
    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        f: F
    ) where F: FnMut(&mut FnMut(&[f32], &[f32]));

    /// Draws a rectangle.
    #[inline(always)]
    fn rectangle<R: Into<types::Rectangle>>(
        &mut self,
        r: &Rectangle,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        r.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws a polygon.
    #[inline(always)]
    fn polygon(
        &mut self,
        p: &Polygon,
        polygon: types::Polygon,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tri(polygon, draw_state, transform, self);
    }

    /// Draws a tweened polygon using linear interpolation.
    #[inline(always)]
    fn polygon_tween_lerp(
        &mut self,
        p: &Polygon,
        polygons: types::Polygons,
        tween_factor: Scalar,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        p.draw_tween_lerp_tri(polygons, tween_factor,
            draw_state, transform, self);
    }

    /// Draws image.
    #[inline(always)]
    fn image(
        &mut self,
        image: &Image,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        image.draw_tri(texture, draw_state, transform, self);
    }

    /// Draws ellipse.
    #[inline(always)]
    fn ellipse<R: Into<types::Rectangle>>(
        &mut self,
        e: &Ellipse,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        e.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws line.
    #[inline(always)]
    fn line<L: Into<types::Line>>(
        &mut self,
        l: &Line,
        line: L,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        l.draw_tri(line, draw_state, transform, self);
    }

    /// Draws circle arc.
    #[inline(always)]
    fn circle_arc<R: Into<types::Rectangle>>(
        &mut self,
        c: &CircleArc,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        c.draw_tri(rectangle, draw_state, transform, self);
    }

    /// Draws deformed image.
    #[inline(always)]
    fn deform_image(
        &mut self,
        d: &DeformGrid,
        texture: &Self::Texture,
        draw_state: &DrawState,
        transform: Matrix2d
    ) {
        d.draw_image_tri(texture, draw_state, transform, self);
    }
}
<|endoftext|>