<commit_before>//! "Dummy" environment for testing wasm translation.

use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment};
use translation_utils::{Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex,
                        FunctionIndex, MemoryIndex};
use func_translator::FuncTranslator;
use cretonne::ir::{self, InstBuilder};
use cretonne::ir::types::*;
use cretonne::cursor::FuncCursor;
use cretonne::settings;
use wasmparser;
use std::error::Error;

/// Compute a `ir::ExternalName` for a given wasm function index.
fn get_func_name(func_index: FunctionIndex) -> ir::ExternalName {
    ir::ExternalName::user(0, func_index as u32)
}

/// A collection of names under which a given entity is exported.
pub struct Exportable<T> {
    /// A wasm entity.
    pub entity: T,

    /// Names under which the entity is exported.
    pub export_names: Vec<String>,
}

impl<T> Exportable<T> {
    pub fn new(entity: T) -> Self {
        Self {
            entity,
            export_names: Vec::new(),
        }
    }
}

/// The main state belonging to a `DummyEnvironment`. This is split out from
/// `DummyEnvironment` to allow it to be borrowed separately from the
/// `FuncTranslator` field.
pub struct DummyModuleInfo {
    /// Compilation setting flags.
    pub flags: settings::Flags,

    /// Signatures as provided by `declare_signature`.
    pub signatures: Vec<ir::Signature>,

    /// Module and field names of imported functions as provided by `declare_func_import`.
    pub imported_funcs: Vec<(String, String)>,

    /// Functions, imported and local.
    pub functions: Vec<Exportable<SignatureIndex>>,

    /// Function bodies.
    pub function_bodies: Vec<ir::Function>,

    /// Tables as provided by `declare_table`.
    pub tables: Vec<Exportable<Table>>,

    /// Memories as provided by `declare_memory`.
    pub memories: Vec<Exportable<Memory>>,

    /// Globals as provided by `declare_global`.
    pub globals: Vec<Exportable<Global>>,

    /// The start function.
    pub start_func: Option<FunctionIndex>,
}

impl DummyModuleInfo {
    /// Allocates the data structures with the given flags.
    pub fn with_flags(flags: settings::Flags) -> Self {
        Self {
            flags,
            signatures: Vec::new(),
            imported_funcs: Vec::new(),
            functions: Vec::new(),
            function_bodies: Vec::new(),
            tables: Vec::new(),
            memories: Vec::new(),
            globals: Vec::new(),
            start_func: None,
        }
    }
}

/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
/// emitting placeholders when forced to. Don't try to execute code translated for this
/// environment, essentially here for translation debug purposes.
pub struct DummyEnvironment {
    /// Module information.
    pub info: DummyModuleInfo,

    /// Function translation.
    trans: FuncTranslator,
}

impl DummyEnvironment {
    /// Allocates the data structures with default flags.
    pub fn default() -> Self {
        Self::with_flags(settings::Flags::new(&settings::builder()))
    }

    /// Allocates the data structures with the given flags.
    pub fn with_flags(flags: settings::Flags) -> Self {
        Self {
            info: DummyModuleInfo::with_flags(flags),
            trans: FuncTranslator::new(),
        }
    }

    /// Return a `DummyFuncEnvironment` for translating functions within this
    /// `DummyEnvironment`.
    pub fn func_env(&self) -> DummyFuncEnvironment {
        DummyFuncEnvironment::new(&self.info)
    }
}

/// The FuncEnvironment implementation for use by the `DummyEnvironment`.
pub struct DummyFuncEnvironment<'dummy_environment> {
    pub mod_info: &'dummy_environment DummyModuleInfo,
}

impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
    pub fn new(mod_info: &'dummy_environment DummyModuleInfo) -> Self {
        Self { mod_info }
    }
}

impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
    fn flags(&self) -> &settings::Flags {
        &self.mod_info.flags
    }

    fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue {
        // Just create a dummy `vmctx` global.
        let offset = ((index * 8) as i32 + 8).into();
        let gv = func.create_global_var(ir::GlobalVarData::VmCtx { offset });
        GlobalValue::Memory {
            gv,
            ty: self.mod_info.globals[index].entity.ty,
        }
    }

    fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
        func.create_heap(ir::HeapData {
            base: ir::HeapBase::ReservedReg,
            min_size: 0.into(),
            guard_size: 0x8000_0000.into(),
            style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() },
        })
    }

    fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
        // A real implementation would probably change the calling convention and add `vmctx` and
        // signature index arguments.
        func.import_signature(self.mod_info.signatures[index].clone())
    }

    fn make_direct_func(&mut self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef {
        let sigidx = self.mod_info.functions[index].entity;
        // A real implementation would probably add a `vmctx` argument.
        // And maybe attempt some signature de-duplication.
        let signature = func.import_signature(self.mod_info.signatures[sigidx].clone());
        let name = get_func_name(index);
        func.import_function(ir::ExtFuncData { name, signature })
    }

    fn translate_call_indirect(
        &mut self,
        mut pos: FuncCursor,
        _table_index: TableIndex,
        _sig_index: SignatureIndex,
        sig_ref: ir::SigRef,
        callee: ir::Value,
        call_args: &[ir::Value],
    ) -> ir::Inst {
        pos.ins().call_indirect(sig_ref, callee, call_args)
    }

    fn translate_grow_memory(
        &mut self,
        mut pos: FuncCursor,
        _index: MemoryIndex,
        _heap: ir::Heap,
        _val: ir::Value,
    ) -> ir::Value {
        pos.ins().iconst(I32, -1)
    }

    fn translate_current_memory(
        &mut self,
        mut pos: FuncCursor,
        _index: MemoryIndex,
        _heap: ir::Heap,
    ) -> ir::Value {
        pos.ins().iconst(I32, -1)
    }
}

impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
    fn get_func_name(&self, func_index: FunctionIndex) -> ir::ExternalName {
        get_func_name(func_index)
    }

    fn declare_signature(&mut self, sig: &ir::Signature) {
        self.info.signatures.push(sig.clone());
    }

    fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature {
        &self.info.signatures[sig_index]
    }

    fn declare_func_import(
        &mut self,
        sig_index: SignatureIndex,
        module: &'data str,
        field: &'data str,
    ) {
        assert_eq!(
            self.info.functions.len(),
            self.info.imported_funcs.len(),
            "Imported functions must be declared first"
        );
        self.info.functions.push(Exportable::new(sig_index));
        self.info.imported_funcs.push((
            String::from(module),
            String::from(field),
        ));
    }

    fn get_num_func_imports(&self) -> usize {
        self.info.imported_funcs.len()
    }

    fn declare_func_type(&mut self, sig_index: SignatureIndex) {
        self.info.functions.push(Exportable::new(sig_index));
    }

    fn get_func_type(&self, func_index: FunctionIndex) -> SignatureIndex {
        self.info.functions[func_index].entity
    }

    fn declare_global(&mut self, global: Global) {
        self.info.globals.push(Exportable::new(global));
    }

    fn get_global(&self, global_index: GlobalIndex) -> &Global {
        &self.info.globals[global_index].entity
    }

    fn declare_table(&mut self, table: Table) {
        self.info.tables.push(Exportable::new(table));
    }
    fn declare_table_elements(
        &mut self,
        _table_index: TableIndex,
        _base: Option<GlobalIndex>,
        _offset: usize,
        _elements: Vec<FunctionIndex>,
    ) {
        // We do nothing
    }
    fn declare_memory(&mut self, memory: Memory) {
        self.info.memories.push(Exportable::new(memory));
    }
    fn declare_data_initialization(
        &mut self,
        _memory_index: MemoryIndex,
        _base: Option<GlobalIndex>,
        _offset: usize,
        _data: &'data [u8],
    ) {
        // We do nothing
    }

    fn declare_func_export(&mut self, func_index: FunctionIndex, name: &'data str) {
        self.info.functions[func_index].export_names.push(
            String::from(
                name,
            ),
        );
    }

    fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str) {
        self.info.tables[table_index].export_names.push(
            String::from(name),
        );
    }

    fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str) {
        self.info.memories[memory_index].export_names.push(
            String::from(
                name,
            ),
        );
    }

    fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str) {
        self.info.globals[global_index].export_names.push(
            String::from(
                name,
            ),
        );
    }

    fn declare_start_func(&mut self, func_index: FunctionIndex) {
        debug_assert!(self.info.start_func.is_none());
        self.info.start_func = Some(func_index);
    }

    fn define_function_body(&mut self, body_bytes: &'data [u8]) -> Result<(), String> {
        let function_index = self.get_num_func_imports() + self.info.function_bodies.len();
        let name = get_func_name(function_index);
        let sig = self.get_signature(self.get_func_type(function_index))
            .clone();
        let mut func = ir::Function::with_name_signature(name, sig);
        {
            let mut func_environ = DummyFuncEnvironment::new(&self.info);
            let reader = wasmparser::BinaryReader::new(body_bytes);
            self.trans
                .translate_from_reader(reader, &mut func, &mut func_environ)
                .map_err(|e| String::from(e.description()))?;
        }
        self.info.function_bodies.push(func);
        Ok(())
    }
}
<commit_msg>Stop generating reserved_reg heaps in DummyEnvironment.<commit_after>//! "Dummy" environment for testing wasm translation.

use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment};
use translation_utils::{Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex,
                        FunctionIndex, MemoryIndex};
use func_translator::FuncTranslator;
use cretonne::ir::{self, InstBuilder};
use cretonne::ir::types::*;
use cretonne::cursor::FuncCursor;
use cretonne::settings;
use wasmparser;
use std::error::Error;

/// Compute a `ir::ExternalName` for a given wasm function index.
fn get_func_name(func_index: FunctionIndex) -> ir::ExternalName {
    ir::ExternalName::user(0, func_index as u32)
}

/// A collection of names under which a given entity is exported.
pub struct Exportable<T> {
    /// A wasm entity.
    pub entity: T,

    /// Names under which the entity is exported.
    pub export_names: Vec<String>,
}

impl<T> Exportable<T> {
    pub fn new(entity: T) -> Self {
        Self {
            entity,
            export_names: Vec::new(),
        }
    }
}

/// The main state belonging to a `DummyEnvironment`. This is split out from
/// `DummyEnvironment` to allow it to be borrowed separately from the
/// `FuncTranslator` field.
pub struct DummyModuleInfo {
    /// Compilation setting flags.
    pub flags: settings::Flags,

    /// Signatures as provided by `declare_signature`.
    pub signatures: Vec<ir::Signature>,

    /// Module and field names of imported functions as provided by `declare_func_import`.
    pub imported_funcs: Vec<(String, String)>,

    /// Functions, imported and local.
    pub functions: Vec<Exportable<SignatureIndex>>,

    /// Function bodies.
    pub function_bodies: Vec<ir::Function>,

    /// Tables as provided by `declare_table`.
    pub tables: Vec<Exportable<Table>>,

    /// Memories as provided by `declare_memory`.
    pub memories: Vec<Exportable<Memory>>,

    /// Globals as provided by `declare_global`.
    pub globals: Vec<Exportable<Global>>,

    /// The start function.
    pub start_func: Option<FunctionIndex>,
}

impl DummyModuleInfo {
    /// Allocates the data structures with the given flags.
    pub fn with_flags(flags: settings::Flags) -> Self {
        Self {
            flags,
            signatures: Vec::new(),
            imported_funcs: Vec::new(),
            functions: Vec::new(),
            function_bodies: Vec::new(),
            tables: Vec::new(),
            memories: Vec::new(),
            globals: Vec::new(),
            start_func: None,
        }
    }
}

/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
/// emitting placeholders when forced to. Don't try to execute code translated for this
/// environment, essentially here for translation debug purposes.
pub struct DummyEnvironment {
    /// Module information.
    pub info: DummyModuleInfo,

    /// Function translation.
    trans: FuncTranslator,
}

impl DummyEnvironment {
    /// Allocates the data structures with default flags.
    pub fn default() -> Self {
        Self::with_flags(settings::Flags::new(&settings::builder()))
    }

    /// Allocates the data structures with the given flags.
    pub fn with_flags(flags: settings::Flags) -> Self {
        Self {
            info: DummyModuleInfo::with_flags(flags),
            trans: FuncTranslator::new(),
        }
    }

    /// Return a `DummyFuncEnvironment` for translating functions within this
    /// `DummyEnvironment`.
    pub fn func_env(&self) -> DummyFuncEnvironment {
        DummyFuncEnvironment::new(&self.info)
    }
}

/// The FuncEnvironment implementation for use by the `DummyEnvironment`.
pub struct DummyFuncEnvironment<'dummy_environment> {
    pub mod_info: &'dummy_environment DummyModuleInfo,
}

impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
    pub fn new(mod_info: &'dummy_environment DummyModuleInfo) -> Self {
        Self { mod_info }
    }

    // Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
    // arguments.
    fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
        let mut sig = self.mod_info.signatures[sigidx].clone();
        sig.params.push(ir::AbiParam::special(
            self.native_pointer(),
            ir::ArgumentPurpose::VMContext,
        ));
        sig
    }
}

impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
    fn flags(&self) -> &settings::Flags {
        &self.mod_info.flags
    }

    fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue {
        // Just create a dummy `vmctx` global.
        let offset = ((index * 8) as i32 + 8).into();
        let gv = func.create_global_var(ir::GlobalVarData::VmCtx { offset });
        GlobalValue::Memory {
            gv,
            ty: self.mod_info.globals[index].entity.ty,
        }
    }

    fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
        // Create a static heap whose base address is stored at `vmctx+0`.
        let gv = func.create_global_var(ir::GlobalVarData::VmCtx { offset: 0.into() });

        func.create_heap(ir::HeapData {
            base: ir::HeapBase::GlobalVar(gv),
            min_size: 0.into(),
            guard_size: 0x8000_0000.into(),
            style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() },
        })
    }

    fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
        // A real implementation would probably change the calling convention and add `vmctx` and
        // signature index arguments.
        func.import_signature(self.vmctx_sig(index))
    }

    fn make_direct_func(&mut self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef {
        let sigidx = self.mod_info.functions[index].entity;
        // A real implementation would probably add a `vmctx` argument.
        // And maybe attempt some signature de-duplication.
        let signature = func.import_signature(self.vmctx_sig(sigidx));
        let name = get_func_name(index);
        func.import_function(ir::ExtFuncData { name, signature })
    }

    fn translate_call_indirect(
        &mut self,
        mut pos: FuncCursor,
        _table_index: TableIndex,
        _sig_index: SignatureIndex,
        sig_ref: ir::SigRef,
        callee: ir::Value,
        call_args: &[ir::Value],
    ) -> ir::Inst {
        // Pass the current function's vmctx parameter on to the callee.
        let vmctx = pos.func
            .special_param(ir::ArgumentPurpose::VMContext)
            .expect("Missing vmctx parameter");

        // The `callee` value is an index into a table of function pointers.
        // Apparently, that table is stored at absolute address 0 in this dummy environment.
        // TODO: Generate bounds checking code.
        let ptr = self.native_pointer();
        let callee_offset = if ptr == I32 {
            pos.ins().imul_imm(callee, 4)
        } else {
            let ext = pos.ins().uextend(I64, callee);
            pos.ins().imul_imm(ext, 4)
        };
        let func_ptr = pos.ins().load(ptr, ir::MemFlags::new(), callee_offset, 0);

        // Build a value list for the indirect call instruction containing the callee, call_args,
        // and the vmctx parameter.
        let mut args = ir::ValueList::default();
        args.push(func_ptr, &mut pos.func.dfg.value_lists);
        args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
        args.push(vmctx, &mut pos.func.dfg.value_lists);

        pos.ins()
            .IndirectCall(ir::Opcode::CallIndirect, ir::types::VOID, sig_ref, args)
            .0
    }

    fn translate_call(
        &mut self,
        mut pos: FuncCursor,
        _callee_index: FunctionIndex,
        callee: ir::FuncRef,
        call_args: &[ir::Value],
    ) -> ir::Inst {
        // Pass the current function's vmctx parameter on to the callee.
        let vmctx = pos.func
            .special_param(ir::ArgumentPurpose::VMContext)
            .expect("Missing vmctx parameter");

        // Build a value list for the call instruction containing the call_args and the vmctx
        // parameter.
        let mut args = ir::ValueList::default();
        args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
        args.push(vmctx, &mut pos.func.dfg.value_lists);

        pos.ins()
            .Call(ir::Opcode::Call, ir::types::VOID, callee, args)
            .0
    }

    fn translate_grow_memory(
        &mut self,
        mut pos: FuncCursor,
        _index: MemoryIndex,
        _heap: ir::Heap,
        _val: ir::Value,
    ) -> ir::Value {
        pos.ins().iconst(I32, -1)
    }

    fn translate_current_memory(
        &mut self,
        mut pos: FuncCursor,
        _index: MemoryIndex,
        _heap: ir::Heap,
    ) -> ir::Value {
        pos.ins().iconst(I32, -1)
    }
}

impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
    fn get_func_name(&self, func_index: FunctionIndex) -> ir::ExternalName {
        get_func_name(func_index)
    }

    fn declare_signature(&mut self, sig: &ir::Signature) {
        self.info.signatures.push(sig.clone());
    }

    fn get_signature(&self, sig_index: SignatureIndex) -> &ir::Signature {
        &self.info.signatures[sig_index]
    }

    fn declare_func_import(
        &mut self,
        sig_index: SignatureIndex,
        module: &'data str,
        field: &'data str,
    ) {
        assert_eq!(
            self.info.functions.len(),
            self.info.imported_funcs.len(),
            "Imported functions must be declared first"
        );
        self.info.functions.push(Exportable::new(sig_index));
        self.info.imported_funcs.push((
            String::from(module),
            String::from(field),
        ));
    }

    fn get_num_func_imports(&self) -> usize {
        self.info.imported_funcs.len()
    }

    fn declare_func_type(&mut self, sig_index: SignatureIndex) {
        self.info.functions.push(Exportable::new(sig_index));
    }

    fn get_func_type(&self, func_index: FunctionIndex) -> SignatureIndex {
        self.info.functions[func_index].entity
    }

    fn declare_global(&mut self, global: Global) {
        self.info.globals.push(Exportable::new(global));
    }

    fn get_global(&self, global_index: GlobalIndex) -> &Global {
        &self.info.globals[global_index].entity
    }

    fn declare_table(&mut self, table: Table) {
        self.info.tables.push(Exportable::new(table));
    }
    fn declare_table_elements(
        &mut self,
        _table_index: TableIndex,
        _base: Option<GlobalIndex>,
        _offset: usize,
        _elements: Vec<FunctionIndex>,
    ) {
        // We do nothing
    }
    fn declare_memory(&mut self, memory: Memory) {
        self.info.memories.push(Exportable::new(memory));
    }
    fn declare_data_initialization(
        &mut self,
        _memory_index: MemoryIndex,
        _base: Option<GlobalIndex>,
        _offset: usize,
        _data: &'data [u8],
    ) {
        // We do nothing
    }

    fn declare_func_export(&mut self, func_index: FunctionIndex, name: &'data str) {
        self.info.functions[func_index].export_names.push(
            String::from(
                name,
            ),
        );
    }

    fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str) {
        self.info.tables[table_index].export_names.push(
            String::from(name),
        );
    }

    fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str) {
        self.info.memories[memory_index].export_names.push(
            String::from(
                name,
            ),
        );
    }

    fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str) {
        self.info.globals[global_index].export_names.push(
            String::from(
                name,
            ),
        );
    }

    fn declare_start_func(&mut self, func_index: FunctionIndex) {
        debug_assert!(self.info.start_func.is_none());
        self.info.start_func = Some(func_index);
    }

    fn define_function_body(&mut self, body_bytes: &'data [u8]) -> Result<(), String> {
        let func = {
            let mut func_environ = DummyFuncEnvironment::new(&self.info);
            let function_index = self.get_num_func_imports() + self.info.function_bodies.len();
            let name = get_func_name(function_index);
            let sig = func_environ.vmctx_sig(self.get_func_type(function_index));
            let mut func = ir::Function::with_name_signature(name, sig);
            let reader = wasmparser::BinaryReader::new(body_bytes);
            self.trans
                .translate_from_reader(reader, &mut func, &mut func_environ)
                .map_err(|e| String::from(e.description()))?;
            func
        };
        self.info.function_bodies.push(func);
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{self, AtomicPtr};

use std::ptr::{self, null_mut};
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering::{Relaxed, Release, Acquire};

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    pub fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::new(null_mut()),
        }
    }

    pub fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, Release) == head {
                break
            }
        }
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

struct Atomic<T> {
    collecting: AtomicBool,
    inner: AtomicPtr<T>,
    readers: Stack<RawReader>,
    snapshots: Stack<Box<T>>,
}

impl<T> Atomic<T> {
    fn gc(&self) {
        if !self.collecting.swap(true, Ordering::Relaxed) {
            // Initially, every snapshot is marked unused.
            let mut unused = self.snapshots.collect();

            // Traverse the readers and update the reference counts.
            self.readers.take_each(|reader| {
                if reader.active.load() {
                    // The reader is not released yet, and is thus considered active.

                    // Remove the reader from the unused set and insert it back into the log, as
                    // the snapshot is active.
                    self.snapshots.insert(unused.remove(reader.ptr).unwrap());
                    // Put the reader back in the structure.
                    self.readers.insert(reader);
                }
            });

            self.collecting.store(true);
        }
    }
}
<commit_msg>Remove a potential use-after-free in the reclamation system.<commit_after>use std::sync::atomic::{self, AtomicPtr};

use std::ptr::{self, null_mut};
use std::sync::atomic::AtomicPtr;
use std::sync::atomic::Ordering::{Relaxed, Release, Acquire};

pub struct Stack<T> {
    head: AtomicPtr<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *mut Node<T>,
}

impl<T> Stack<T> {
    pub fn new() -> Stack<T> {
        Stack {
            head: AtomicPtr::new(null_mut()),
        }
    }

    pub fn push(&self, t: T) {
        // Allocate the node, and immediately turn it into a `*mut` pointer.
        let n = Box::into_raw(Box::new(Node {
            data: t,
            next: null_mut(),
        }));

        loop {
            // Snapshot current head.
            let head = self.head.load(Relaxed);

            // Update `next` pointer with snapshot.
            unsafe { (*n).next = head; }

            // If snapshot is still good, link in new node.
            if self.head.compare_and_swap(head, n, Release) == head {
                break
            }
        }
    }
}

struct RawReader {
    active: *const AtomicBool,
    ptr: *const T,
}

impl RawReader {
    unsafe fn destroy(self) {
        // Drop the atomic boolean stored on the heap.
        mem::drop_in_place(self.active);
    }
}

struct Reader<'a, T> {
    raw: RawReader,
    _marker: PhantomData<'a>,
}

impl<'a, T> Reader<'a, T> {
    fn drop(&mut self) {
        self.raw.active.store(true);
    }
}

struct Atomic<T> {
    inner: AtomicPtr<T>,
    snapshots: Stack<Box<T>>,
    readers: Stack<RawReader>,
    flags: AtomicUsize,
}

impl<T> Atomic<T> {
    fn gc(&self) {
        // Spin until no thread is currently modifying the stacks. This prevents premature frees in
        // the thread which is currently pushing to `self.readers`.
        loop {
            // Read the flags, and if no readers or garbage collectors, activate garbage
            // collection.
            let flags = self.flags.compare_and_swap(0, 1, atomic::Ordering::Relaxed);
            if flags == 0 {
                // Currently, noone accesses the readers stack and the CAS above means that the
                // lowest bitflag have been set, indicating that a garbage collection is now
                // active.
                break;
            } else if flags & 1 == 1 {
                // Another thread is currently garbage collection. No need for this thread doing
                // the same.
                return;
            }
        }

        // We've now set the lowest bit in `self.active` namely the flag defining if we are
        // collecting.

        // Initially, every snapshot is marked unused.
        let mut unused = self.snapshots.collect();

        // Traverse the readers and update the reference counts.
        self.readers.take_each(|reader| {
            if reader.active.load() {
                // The reader is not released yet, and is thus considered active.

                // Remove the reader from the unused set and insert it back into the log, as the
                // snapshot is active.
                self.snapshots.insert(unused.remove(reader.ptr).unwrap());
                // Put the reader back in the structure.
                self.readers.insert(reader);
            } else {
                // The reader was released. Destroy it.
                reader.destroy();
            }
        });

        // Unset the bitflag defining if currently garbage collecting.
        self.flags.fetch_and(!1, atomic::Ordering::Relaxed);
    }

    fn get(&self) -> Reader {
        // Increment the number of threads currently pushing to the readers stack. We add two to
        // account for the LSB being a separate bitflag. This ensures that the read snapshot isn't
        // freed while registering it in the reader stack.
        self.flags.fetch_add(2, atomic::Ordering::Relaxed);

        // Construct the raw reader.
        let reader = RawReader {
            // Load a snapshot of the pointer.
            ptr: self.inner.load(atomic::Ordering::Relaxed),
            // We allocate the atomic boolean on the heap as it is shared between the returned RAII
            // guard and the reader stack.
            released: Box::from_raw(Box::new(AtomicBool::new(false))),
        };

        // Register the reader through the reader stack, ensuring that it is not freed before the
        // RAII guard drops (`reader.release` is set to `true`).
        self.readers.push(reader);

        // Revert the original increment.
        self.flags.fetch_sub(2, atomic::Ordering::Relaxed);

        Reader {
            raw: reader,
            _marker: PhantomData,
        }
    }

    fn set(&self, new: Box<T>) {
        // Replace the inner by the new value.
        let old = self.inner.swap(Box::into_raw(new), atomic::Ordering::Relaxed);
        // Push the old pointer to the snapshot stack.
        self.snapshots.push(Box::from_raw(old));
    }
}
<|endoftext|>


<commit_before>use cube_shell::cube_shell;
use id_allocator::IdAllocator;
use terrain_block::BlockPosition;
use terrain_game_loader::{TerrainGameLoader, OwnerId};
use physics::Physics;
use state::EntityId;
use std::cmp::max;
use std::collections::RingBuf;
use std::num::Float;
use std::num::SignedInt;
use stopwatch::TimerSet;
use yaglw::gl_context::GLContext;

// values are approximately in microseconds, but they don't have to be.
pub const BLOCK_UPDATE_BUDGET: int = 20000;
pub const BLOCK_LOAD_COST: int = 600;
pub const BLOCK_UNLOAD_COST: int = 300;

fn radius_between(p1: &BlockPosition, p2: &BlockPosition) -> i32 {
  let dx = (p1.as_pnt().x - p2.as_pnt().x).abs();
  let dy = (p1.as_pnt().y - p2.as_pnt().y).abs();
  let dz = (p1.as_pnt().z - p2.as_pnt().z).abs();
  max(max(dx, dy), dz)
}

/// Keep surroundings loaded around a given world position.
pub struct SurroundingsLoader {
  pub id: OwnerId,
  pub last_position: Option<BlockPosition>,

  pub max_load_distance: i32,
  pub next_load_distance: i32,
  pub load_queue: RingBuf<BlockPosition>,

  pub loaded_vec: Vec<BlockPosition>,
  pub next_unload_index: uint,

  pub solid_boundary: Vec<BlockPosition>,
}

impl SurroundingsLoader {
  pub fn new(id: OwnerId, max_load_distance: i32) -> SurroundingsLoader {
    assert!(max_load_distance >= 0);

    SurroundingsLoader {
      id: id,
      last_position: None,

      max_load_distance: max_load_distance,
      next_load_distance: 0,
      load_queue: RingBuf::new(),

      loaded_vec: Vec::new(),
      next_unload_index: 0,

      solid_boundary: {
        let mut b = Vec::new();
        b.push_all(cube_shell(&BlockPosition::new(0, 0, 0), 0).as_slice());
        b.push_all(cube_shell(&BlockPosition::new(0, 0, 0), 1).as_slice());
        b.push_all(cube_shell(&BlockPosition::new(0, 0, 0), 2).as_slice());
        b
      },
    }
  }

  pub fn update(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    terrain_game_loader: &mut TerrainGameLoader,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    position: BlockPosition,
  ) {
    if Some(position) != self.last_position {
      self.last_position.map(
        |last_position|
          for solid_block in
            self.solid_boundary.iter().map(|&dp| last_position + dp.as_pnt().to_vec()) {
              terrain_game_loader.remove_placeholder(physics, &solid_block, self.id);
            }
        );

      // There will make some of the remove_placeholders redundant. Fix?
      for solid_block in self.solid_boundary.iter().map(|&dp| position + dp.as_pnt().to_vec()) {
        terrain_game_loader.insert_placeholder(id_allocator, physics, &solid_block, self.id);
      }

      self.load_queue.clear();
      self.load_queue.push_back(position);

      self.next_unload_index = 0;
      self.next_load_distance = 0;
      self.last_position = Some(position);
    }

    let mut budget = BLOCK_UPDATE_BUDGET;
    while budget > 0 {
      if self.next_unload_index < self.loaded_vec.len() {
        let block_position = self.loaded_vec[self.next_unload_index];
        if radius_between(&position, &block_position) > self.max_load_distance {
          if terrain_game_loader.unload(timers, gl, physics, &block_position, self.id) {
            budget -= BLOCK_UNLOAD_COST;
          }

          self.loaded_vec.swap_remove(self.next_unload_index);
        } else {
          self.next_unload_index += 1;
        }
      } else {
        let block_position =
          match self.load_queue.pop_front() {
            None => break,
            Some(block_position) => block_position,
          };

        if terrain_game_loader.load(
          timers,
          gl,
          id_allocator,
          physics,
          &block_position,
          self.id,
        ) {
          budget -= BLOCK_LOAD_COST;
        }

        self.loaded_vec.push(block_position);

        if self.load_queue.is_empty() {
          self.next_load_distance += 1;
          if self.next_load_distance <= self.max_load_distance {
            self.load_queue.extend(cube_shell(&position, self.next_load_distance).into_iter());
          }
        }
      }
    }
  }
}
<commit_msg>Add debug logs for terrain load progress<commit_after>use cube_shell::cube_shell;
use id_allocator::IdAllocator;
use terrain_block::BlockPosition;
use terrain_game_loader::{TerrainGameLoader, OwnerId};
use physics::Physics;
use state::EntityId;
use std::cmp::max;
use std::collections::RingBuf;
use std::num::Float;
use std::num::SignedInt;
use stopwatch::TimerSet;
use yaglw::gl_context::GLContext;

// values are approximately in microseconds, but they don't have to be.
pub const BLOCK_UPDATE_BUDGET: int = 20000;
pub const BLOCK_LOAD_COST: int = 600;
pub const BLOCK_UNLOAD_COST: int = 300;

fn radius_between(p1: &BlockPosition, p2: &BlockPosition) -> i32 {
  let dx = (p1.as_pnt().x - p2.as_pnt().x).abs();
  let dy = (p1.as_pnt().y - p2.as_pnt().y).abs();
  let dz = (p1.as_pnt().z - p2.as_pnt().z).abs();
  max(max(dx, dy), dz)
}

/// Keep surroundings loaded around a given world position.
pub struct SurroundingsLoader {
  pub id: OwnerId,
  pub last_position: Option<BlockPosition>,

  pub max_load_distance: i32,
  pub next_load_distance: i32,
  pub load_queue: RingBuf<BlockPosition>,

  pub loaded_vec: Vec<BlockPosition>,
  pub next_unload_index: uint,

  pub solid_boundary: Vec<BlockPosition>,
}

impl SurroundingsLoader {
  pub fn new(id: OwnerId, max_load_distance: i32) -> SurroundingsLoader {
    assert!(max_load_distance >= 0);

    SurroundingsLoader {
      id: id,
      last_position: None,

      max_load_distance: max_load_distance,
      next_load_distance: 0,
      load_queue: RingBuf::new(),

      loaded_vec: Vec::new(),
      next_unload_index: 0,

      solid_boundary: {
        let mut b = Vec::new();
        b.push_all(cube_shell(&BlockPosition::new(0, 0, 0), 0).as_slice());
        b.push_all(cube_shell(&BlockPosition::new(0, 0, 0), 1).as_slice());
        b.push_all(cube_shell(&BlockPosition::new(0, 0, 0), 2).as_slice());
        b
      },
    }
  }

  pub fn update(
    &mut self,
    timers: &TimerSet,
    gl: &mut GLContext,
    terrain_game_loader: &mut TerrainGameLoader,
    id_allocator: &mut IdAllocator<EntityId>,
    physics: &mut Physics,
    position: BlockPosition,
  ) {
    if Some(position) != self.last_position {
      self.last_position.map(
        |last_position|
          for solid_block in
            self.solid_boundary.iter().map(|&dp| last_position + dp.as_pnt().to_vec()) {
              terrain_game_loader.remove_placeholder(physics, &solid_block, self.id);
            }
        );

      // There will make some of the remove_placeholders redundant. Fix?
      for solid_block in self.solid_boundary.iter().map(|&dp| position + dp.as_pnt().to_vec()) {
        terrain_game_loader.insert_placeholder(id_allocator, physics, &solid_block, self.id);
      }

      self.load_queue.clear();
      self.load_queue.push_back(position);

      self.next_unload_index = 0;
      self.next_load_distance = 0;
      self.last_position = Some(position);
    }

    let mut budget = BLOCK_UPDATE_BUDGET;
    while budget > 0 {
      if self.next_unload_index < self.loaded_vec.len() {
        let block_position = self.loaded_vec[self.next_unload_index];
        if radius_between(&position, &block_position) > self.max_load_distance {
          if terrain_game_loader.unload(timers, gl, physics, &block_position, self.id) {
            budget -= BLOCK_UNLOAD_COST;
          }

          self.loaded_vec.swap_remove(self.next_unload_index);
        } else {
          self.next_unload_index += 1;
        }
      } else {
        let block_position =
          match self.load_queue.pop_front() {
            None => break,
            Some(block_position) => block_position,
          };

        if terrain_game_loader.load(
          timers,
          gl,
          id_allocator,
          physics,
          &block_position,
          self.id,
        ) {
          budget -= BLOCK_LOAD_COST;
        }

        self.loaded_vec.push(block_position);

        if self.load_queue.is_empty() {
          debug!("Done loading surroundings at distance {}", self.next_load_distance);
          self.next_load_distance += 1;
          if self.next_load_distance <= self.max_load_distance {
            self.load_queue.extend(cube_shell(&position, self.next_load_distance).into_iter());
          } else {
            debug!("Done loading surroundings");
          }
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>#![cfg(feature = "serde_impl")]

extern crate linked_hash_map;
use linked_hash_map::LinkedHashMap;

extern crate serde_test;
use serde_test::{Token, assert_tokens};

#[test]
fn test_ser_de_empty() {
    let map = LinkedHashMap::<String, u32>::new();

    assert_tokens(&map, &[
        Token::MapStart(Some(0)),
        Token::MapEnd,
    ]);
}

#[test]
fn test_ser_de() {
    let mut map = LinkedHashMap::new();
    map.insert("b".to_string(), 20);
    map.insert("a".to_string(), 10);
    map.insert("c".to_string(), 30);

    assert_tokens(&map, &[
        Token::MapStart(Some(3)),
            Token::MapSep,
            Token::Str("b"),
            Token::I32(20),

            Token::MapSep,
            Token::Str("a"),
            Token::I32(10),

            Token::MapSep,
            Token::Str("c"),
            Token::I32(30),
        Token::MapEnd,
    ]);
}
<commit_msg>Change serde test to use char instead of String<commit_after>#![cfg(feature = "serde_impl")]

extern crate linked_hash_map;
use linked_hash_map::LinkedHashMap;

extern crate serde_test;
use serde_test::{Token, assert_tokens};

#[test]
fn test_ser_de_empty() {
    let map = LinkedHashMap::<char, u32>::new();

    assert_tokens(&map, &[
        Token::MapStart(Some(0)),
        Token::MapEnd,
    ]);
}

#[test]
fn test_ser_de() {
    let mut map = LinkedHashMap::new();
    map.insert('b', 20);
    map.insert('a', 10);
    map.insert('c', 30);

    assert_tokens(&map, &[
        Token::MapStart(Some(3)),
            Token::MapSep,
            Token::Char('b'),
            Token::I32(20),

            Token::MapSep,
            Token::Char('a'),
            Token::I32(10),

            Token::MapSep,
            Token::Char('c'),
            Token::I32(30),
        Token::MapEnd,
    ]);
}
<|endoftext|>


<commit_before>extern crate obj_exporter as obj;

use obj::{ObjSet, Object, Vertex, TVertex, Geometry, Shape, Primitive};

#[test]
pub fn test_square() {
    let set = ObjSet {
      material_library: None,
      objects: vec![
        Object {
          name: "Square".to_owned(),
          vertices: vec![
            (-1, -1, 0),
            (1, -1, 0),
            (1, 1, 0),
            (-1, 1, 0)
          ].into_iter().map(|(x, y, z)| {
                Vertex {
                    x: x as f64,
                    y: y as f64,
                    z: z as f64,
                }
          }).collect(),
          tex_vertices: vec![],
          normals: vec![],
          geometry: vec![
            Geometry {
              material_name: None,
              shapes: vec![
                (0, 1, 2),
                (0, 2, 3)
              ].into_iter().map(|(x, y, z)| {
                Shape {
                  primitive: Primitive::Triangle(
                    (x, None, None),
                    (y, None, None),
                    (z, None, None)
                  ),
                  groups: vec![],
                  smoothing_groups: vec![],
                }
              }).collect(),
            }
          ],
        }
      ],
    };

    let expected = r#"o Square
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
f 1 2 3
f 1 3 4
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}

#[test]
pub fn test_square_with_uv() {
    let set = ObjSet {
        material_library: None,
        objects: vec![
        Object {
          name: "Square".to_owned(),
          vertices:
            vec![(-1, -1,  0),
                 (1, -1, 0),
                 (1, 1, 0),
                 (-1, 1,  0)]
            .into_iter()
            .map(|(x, y, z)|
              Vertex {
                x: x as f64,
                y: y as f64,
                z: z as f64
              })
            .collect(),
          tex_vertices: vec![
            (0, 0),
            (1, 0),
            (1, 1),
            (0, 1),
          ].into_iter().map(|(u, v)|
            TVertex {
              u: u as f64,
              v: v as f64,
              w: 0.0,
            }
          ).collect(),
          normals: vec!(),
          geometry: vec!(
            Geometry {
              material_name: None,
              shapes:
                vec![(0, 1, 2),
                     (0, 2, 3)]
                .into_iter()
                .map(|(x, y, z)|
                  Shape {
                    primitive:
                      Primitive::Triangle(
                        (x, Some(x), None),
                        (y, Some(y), None),
                        (z, Some(z), None)),
                    groups: vec!(),
                    smoothing_groups: vec!(),
                  })
                .collect()
            }
          )
        }
      ],
    };

    let expected = r#"o Square
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
f 1/1 2/2 3/3
f 1/1 3/3 4/4
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}

#[test]
pub fn test_square_with_uv_and_normals() {
    let set = ObjSet {
        material_library: None,
        objects: vec![
        Object {
          name: "Square".to_owned(),
          vertices:
            vec![(-1, -1,  0),
                 (1, -1, 0),
                 (1, 1, 0),
                 (-1, 1,  0)]
            .into_iter()
            .map(|(x, y, z)|
              Vertex {
                x: x as f64,
                y: y as f64,
                z: z as f64
              })
            .collect(),
          tex_vertices: vec![
            (0, 0),
            (1, 0),
            (1, 1),
            (0, 1),
          ].into_iter().map(|(u, v)|
            TVertex {
              u: u as f64,
              v: v as f64,
              w: 0.0,
            }
          ).collect(),
          normals: vec![
            Vertex {
              x: 0.0,
              y: 0.0,
              z: -1.0,
            }
          ],
          geometry: vec!(
            Geometry {
              material_name: None,
              shapes:
                vec![(0, 1, 2),
                     (0, 2, 3)]
                .into_iter()
                .map(|(x, y, z)|
                  Shape {
                    primitive:
                      Primitive::Triangle(
                        (x, Some(x), Some(0)),
                        (y, Some(y), Some(0)),
                        (z, Some(z), Some(0))),
                    groups: vec!(),
                    smoothing_groups: vec!(),
                  })
                .collect()
            }
          )
        }
      ],
    };

    let expected = r#"o Square
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vn 0.000000 0.000000 -1.000000
f 1/1/1 2/2/1 3/3/1
f 1/1/1 3/3/1 4/4/1
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}
<commit_msg>Test multiple objects<commit_after>extern crate obj_exporter as obj;

use obj::{ObjSet, Object, Vertex, TVertex, Geometry, Shape, Primitive};

#[test]
pub fn test_square() {
    let set = ObjSet {
      material_library: None,
      objects: vec![
        Object {
          name: "Square".to_owned(),
          vertices: vec![
            (-1, -1, 0),
            (1, -1, 0),
            (1, 1, 0),
            (-1, 1, 0)
          ].into_iter().map(|(x, y, z)| {
            Vertex {
              x: x as f64,
              y: y as f64,
              z: z as f64,
            }
          }).collect(),
          tex_vertices: vec![],
          normals: vec![],
          geometry: vec![
            Geometry {
              material_name: None,
              shapes: vec![
                (0, 1, 2),
                (0, 2, 3)
              ].into_iter().map(|(x, y, z)| {
                Shape {
                  primitive: Primitive::Triangle(
                    (x, None, None),
                    (y, None, None),
                    (z, None, None)
                  ),
                  groups: vec![],
                  smoothing_groups: vec![],
                }
              }).collect(),
            }
          ],
        }
      ],
    };

    let expected = r#"o Square
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
f 1 2 3
f 1 3 4
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}

#[test]
pub fn test_square_with_uv() {
    let set = ObjSet {
        material_library: None,
        objects: vec![
        Object {
          name: "Square".to_owned(),
          vertices:
            vec![(-1, -1,  0),
                 (1, -1, 0),
                 (1, 1, 0),
                 (-1, 1,  0)]
            .into_iter()
            .map(|(x, y, z)|
              Vertex {
                x: x as f64,
                y: y as f64,
                z: z as f64
              })
            .collect(),
          tex_vertices: vec![
            (0, 0),
            (1, 0),
            (1, 1),
            (0, 1),
          ].into_iter().map(|(u, v)|
            TVertex {
              u: u as f64,
              v: v as f64,
              w: 0.0,
            }
          ).collect(),
          normals: vec!(),
          geometry: vec!(
            Geometry {
              material_name: None,
              shapes:
                vec![(0, 1, 2),
                     (0, 2, 3)]
                .into_iter()
                .map(|(x, y, z)|
                  Shape {
                    primitive:
                      Primitive::Triangle(
                        (x, Some(x), None),
                        (y, Some(y), None),
                        (z, Some(z), None)),
                    groups: vec!(),
                    smoothing_groups: vec!(),
                  })
                .collect()
            }
          )
        }
      ],
    };

    let expected = r#"o Square
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
f 1/1 2/2 3/3
f 1/1 3/3 4/4
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}

#[test]
pub fn test_square_with_uv_and_normals() {
    let set = ObjSet {
        material_library: None,
        objects: vec![
        Object {
          name: "Square".to_owned(),
          vertices:
            vec![(-1, -1,  0),
                 (1, -1, 0),
                 (1, 1, 0),
                 (-1, 1,  0)]
            .into_iter()
            .map(|(x, y, z)|
              Vertex {
                x: x as f64,
                y: y as f64,
                z: z as f64
              })
            .collect(),
          tex_vertices: vec![
            (0, 0),
            (1, 0),
            (1, 1),
            (0, 1),
          ].into_iter().map(|(u, v)|
            TVertex {
              u: u as f64,
              v: v as f64,
              w: 0.0,
            }
          ).collect(),
          normals: vec![
            Vertex {
              x: 0.0,
              y: 0.0,
              z: -1.0,
            }
          ],
          geometry: vec!(
            Geometry {
              material_name: None,
              shapes:
                vec![(0, 1, 2),
                     (0, 2, 3)]
                .into_iter()
                .map(|(x, y, z)|
                  Shape {
                    primitive:
                      Primitive::Triangle(
                        (x, Some(x), Some(0)),
                        (y, Some(y), Some(0)),
                        (z, Some(z), Some(0))),
                    groups: vec!(),
                    smoothing_groups: vec!(),
                  })
                .collect()
            }
          )
        }
      ],
    };

    let expected = r#"o Square
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
vn 0.000000 0.000000 -1.000000
f 1/1/1 2/2/1 3/3/1
f 1/1/1 3/3/1 4/4/1
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}

#[test]
pub fn test_multiple_objects() {
    let set = ObjSet {
      material_library: None,
      objects: vec![
        Object {
          name: "Square1".to_owned(),
          vertices: vec![
            (-1, -1, 0),
            (1, -1, 0),
            (1, 1, 0),
            (-1, 1, 0)
          ].into_iter().map(|(x, y, z)| {
            Vertex {
              x: x as f64,
              y: y as f64,
              z: z as f64,
            }
          }).collect(),
          tex_vertices: vec![],
          normals: vec![],
          geometry: vec![
            Geometry {
              material_name: None,
              shapes: vec![
                (0, 1, 2),
                (0, 2, 3)
              ].into_iter().map(|(x, y, z)| {
                Shape {
                  primitive: Primitive::Triangle(
                    (x, None, None),
                    (y, None, None),
                    (z, None, None)
                  ),
                  groups: vec![],
                  smoothing_groups: vec![],
                }
              }).collect(),
            }
          ],
        },
        Object {
          name: "Square2".to_owned(),
          vertices: vec![
            (1, -1, 0),
            (2, -1, 0),
            (2, 1, 0),
            (1, 1, 0)
          ].into_iter().map(|(x, y, z)| {
            Vertex {
              x: x as f64,
              y: y as f64,
              z: z as f64,
            }
          }).collect(),
          tex_vertices: vec![],
          normals: vec![],
          geometry: vec![
            Geometry {
              material_name: None,
              shapes: vec![
                (0, 1, 2),
                (0, 2, 3)
              ].into_iter().map(|(x, y, z)| {
                Shape {
                  primitive: Primitive::Triangle(
                    (x, None, None),
                    (y, None, None),
                    (z, None, None)
                  ),
                  groups: vec![],
                  smoothing_groups: vec![],
                }
              }).collect(),
            }
          ],
        }
      ],
    };

    let expected = r#"o Square1
v -1.000000 -1.000000 0.000000
v 1.000000 -1.000000 0.000000
v 1.000000 1.000000 0.000000
v -1.000000 1.000000 0.000000
f 1 2 3
f 1 3 4
o Square2
v 1.000000 -1.000000 0.000000
v 2.000000 -1.000000 0.000000
v 2.000000 1.000000 0.000000
v 1.000000 1.000000 0.000000
f 1 2 3
f 1 3 4
"#;

    let result = obj::export(&set);
    assert_eq!(result, expected);
}
<|endoftext|>


<commit_before>extern crate cairo;

#[derive(Debug)]
#[derive(PartialEq)]
pub enum Segment {
    Degenerate {            // A single lone point
        x: f64,
        y: f64
    },

    LineOrCurve {
        x1: f64, y1: f64,
        x2: f64, y2: f64,
        x3: f64, y3: f64,
        x4: f64, y4: f64
    },
}

enum SegmentState {
    NewSubpath,
    InSubpath
}

/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
 *
 * 1. Segment::Degenerate => the segment is actually a single point (x, y)
 *
 * 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective lineto that results from a closepath).
 *    We have the following points:
 *       P1 = (x1, y1)
 *       P2 = (x2, y2)
 *       P3 = (x3, y3)
 *       P4 = (x4, y4)
 *
 *    The start and end points are P1 and P4, respectively.
 *    The tangent at the start point is given by the vector (P2 - P1).
 *    The tangent at the end point is given by the vector (P4 - P3).
 *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
 */

const EPSILON: f64 = 1e-10;

fn double_equals (a: f64, b: f64) -> bool {
    (a - b).abs () < EPSILON
}

fn make_degenerate (x: f64, y: f64) -> Segment {
    Segment::Degenerate { x: x, y: y}
}

fn make_curve (x1: f64, y1: f64,
               mut x2: f64, mut y2: f64,
               mut x3: f64, mut y3: f64,
               x4: f64, y4: f64) -> Segment {
    /* Fix the tangents for when the middle control points coincide with their respective endpoints */

    if double_equals (x2, x1) && double_equals (y2, y1) {
        x2 = x3;
        y2 = y3;
    }

    if double_equals (x3, x4) && double_equals (y3, y4) {
        x3 = x2;
        y3 = y2;
    }

    Segment::LineOrCurve {
        x1: x1, y1: y1,
        x2: x2, y2: y2,
        x3: x3, y3: y3,
        x4: x4, y4: y4
    }
}

fn make_line (x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
    make_curve (x1, y1, x2, y2, x1, y1, x2, y2)
}

pub fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
    let mut last_x: f64;
    let mut last_y: f64;
    let mut cur_x: f64;
    let mut cur_y: f64;
    let mut subpath_start_x: f64;
    let mut subpath_start_y: f64;
    let mut segments: Vec<Segment>;
    let mut state: SegmentState;

    cur_x = 0.0;
    cur_y = 0.0;
    subpath_start_x = 0.0;
    subpath_start_y = 0.0;

    segments = Vec::new ();
    state = SegmentState::InSubpath;

    for cairo_segment in path.iter () {
        last_x = cur_x;
        last_y = cur_y;

        let needs_new_segment: bool;
        let seg: Segment;

        match cairo_segment {
            cairo::PathSegment::MoveTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = make_degenerate (cur_x, cur_y);
                needs_new_segment = true;

                subpath_start_x = cur_x;
                subpath_start_y = cur_y;

                state = SegmentState::NewSubpath;
            },

            cairo::PathSegment::LineTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = make_line (last_x, last_y, cur_x, cur_y);

                match state {
                    SegmentState::NewSubpath => {
                        state = SegmentState::InSubpath;
                        needs_new_segment = false;
                    },

                    SegmentState::InSubpath => {
                        needs_new_segment = true;
                    }
                }
            },

            cairo::PathSegment::CurveTo ((x2, y2), (x3, y3), (x4, y4)) => {
                cur_x = x4;
                cur_y = y4;

                seg = make_curve (last_x, last_y, x2, y2, x3, y3, cur_x, cur_y);

                match state {
                    SegmentState::NewSubpath => {
                        state = SegmentState::InSubpath;
                        needs_new_segment = false;
                    },

                    SegmentState::InSubpath => {
                        needs_new_segment = true;
                    }
                }
            }

            cairo::PathSegment::ClosePath => {
                cur_x = subpath_start_x;
                cur_y = subpath_start_y;

                seg = make_line (last_x, last_y, cur_x, cur_y);

                match state {
                    SegmentState::NewSubpath => {
                        state = SegmentState::InSubpath;
                        needs_new_segment = false;
                    },

                    SegmentState::InSubpath => {
                        needs_new_segment = true;
                    }
                }
            }
        }

        if needs_new_segment {
            segments.push (seg);
        } else {
            let len = segments.len ();
            segments[len - 1] = seg;
        }
    }

    segments
}

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

    fn create_cr () -> cairo::Context {
        let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 1, 1);
        let cr = cairo::Context::new (&surf);

        cr
    }

    fn degenerate (x: f64, y: f64) -> Segment {
        Segment::Degenerate { x: x, y: y }
    }

    fn curve (x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
        Segment::LineOrCurve {
            x1: x1, y1: y1, x2: x2, y2: y2, x3: x3, y3: y3, x4: x4, y4: y4
        }
    }

    fn line (x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
        curve (x1, y1, x2, y2, x1, y1, x2, y2)
    }

    fn test_path_to_segments (path: cairo::Path, expected_segments: Vec<Segment>) {
        let segments = path_to_segments (path);
        assert_eq! (expected_segments, segments);
    }

    /* Single open path */

    fn setup_open_path () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_open_path () {
        let expected_segments: Vec<Segment> = vec![
            line (10.0, 10.0, 20.0, 10.0),
            line (20.0, 10.0, 20.0, 20.0)
        ];

        test_path_to_segments (setup_open_path(), expected_segments);
    }

    /* Multiple open subpaths */

    fn setup_multiple_open_subpaths () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        cr.move_to (30.0, 30.0);
        cr.line_to (40.0, 30.0);
        cr.curve_to (50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
        cr.line_to (80.0, 90.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_multiple_open_subpaths () {
        let expected_segments: Vec<Segment> = vec![
            line  (10.0, 10.0, 20.0, 10.0),
            line  (20.0, 10.0, 20.0, 20.0),

            line  (30.0, 30.0, 40.0, 30.0),
            curve (40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
            line  (70.0, 70.0, 80.0, 90.0)
        ];

        test_path_to_segments (setup_multiple_open_subpaths (), expected_segments);
    }

    fn setup_closed_subpath () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);
        cr.close_path ();

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_closed_subpath () {
        let expected_segments: Vec<Segment> = vec![
            line (10.0, 10.0, 20.0, 10.0),
            line (20.0, 10.0, 20.0, 20.0),
            line (20.0, 20.0, 10.0, 10.0)
        ];

        test_path_to_segments (setup_closed_subpath (), expected_segments);
    }
}
<commit_msg>marker.rs: Handle Cairo's artificial moveto after a closepath<commit_after>extern crate cairo;

#[derive(Debug)]
#[derive(PartialEq)]
pub enum Segment {
    Degenerate {            // A single lone point
        x: f64,
        y: f64
    },

    LineOrCurve {
        x1: f64, y1: f64,
        x2: f64, y2: f64,
        x3: f64, y3: f64,
        x4: f64, y4: f64
    },
}

enum SegmentState {
    Initial,
    NewSubpath,
    InSubpath,
    ClosedSubpath
}

/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
 *
 * 1. Segment::Degenerate => the segment is actually a single point (x, y)
 *
 * 2. Segment::LineOrCurve => either a lineto or a curveto (or the effective lineto that results from a closepath).
 *    We have the following points:
 *       P1 = (x1, y1)
 *       P2 = (x2, y2)
 *       P3 = (x3, y3)
 *       P4 = (x4, y4)
 *
 *    The start and end points are P1 and P4, respectively.
 *    The tangent at the start point is given by the vector (P2 - P1).
 *    The tangent at the end point is given by the vector (P4 - P3).
 *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
 */

const EPSILON: f64 = 1e-10;

fn double_equals (a: f64, b: f64) -> bool {
    (a - b).abs () < EPSILON
}

fn make_degenerate (x: f64, y: f64) -> Segment {
    Segment::Degenerate { x: x, y: y}
}

fn make_curve (x1: f64, y1: f64,
               mut x2: f64, mut y2: f64,
               mut x3: f64, mut y3: f64,
               x4: f64, y4: f64) -> Segment {
    /* Fix the tangents for when the middle control points coincide with their respective endpoints */

    if double_equals (x2, x1) && double_equals (y2, y1) {
        x2 = x3;
        y2 = y3;
    }

    if double_equals (x3, x4) && double_equals (y3, y4) {
        x3 = x2;
        y3 = y2;
    }

    Segment::LineOrCurve {
        x1: x1, y1: y1,
        x2: x2, y2: y2,
        x3: x3, y3: y3,
        x4: x4, y4: y4
    }
}

fn make_line (x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
    make_curve (x1, y1, x2, y2, x1, y1, x2, y2)
}

pub fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
    let mut last_x: f64;
    let mut last_y: f64;
    let mut cur_x: f64;
    let mut cur_y: f64;
    let mut subpath_start_x: f64;
    let mut subpath_start_y: f64;
    let mut segments: Vec<Segment>;
    let mut state: SegmentState;

    cur_x = 0.0;
    cur_y = 0.0;
    subpath_start_x = 0.0;
    subpath_start_y = 0.0;

    segments = Vec::new ();
    state = SegmentState::Initial;

    for cairo_segment in path.iter () {
        last_x = cur_x;
        last_y = cur_y;

        let seg: Segment;

        match cairo_segment {
            cairo::PathSegment::MoveTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                subpath_start_x = cur_x;
                subpath_start_y = cur_y;

                match state {
                    SegmentState::Initial |
                    SegmentState::InSubpath => {
                        /* Ignore the very first moveto in a sequence (Initial state), or if we were
                         * already drawing within a subpath, start a new subpath.
                         */
                        state = SegmentState::NewSubpath;
                    },

                    SegmentState::NewSubpath => {
                        /* We had just begun a new subpath (i.e. from a moveto) and we got another
                         * moveto?  Output a stray point for the previous moveto.
                         */
                        segments.push (make_degenerate (last_x, last_y));
                        state = SegmentState::NewSubpath;
                    }

                    SegmentState::ClosedSubpath => {
                        /* Cairo outputs a moveto after every closepath, so that subsequent
                         * lineto/curveto commands will start at the closed vertex.
                         *
                         * We don't want to actually emit a point (a degenerate segment) in that
                         * artificial-moveto case.
                         *
                         * We'll reset to the Initial state so that a subsequent "real" moveto will
                         * be handled as the beginning of a new subpath, or a degenerate point, as
                         * usual.
                         */
                        state = SegmentState::Initial;
                    }
                }
            },

            cairo::PathSegment::LineTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                seg = make_line (last_x, last_y, cur_x, cur_y);

                state = SegmentState::InSubpath;
                segments.push (seg);
            },

            cairo::PathSegment::CurveTo ((x2, y2), (x3, y3), (x4, y4)) => {
                cur_x = x4;
                cur_y = y4;

                seg = make_curve (last_x, last_y, x2, y2, x3, y3, cur_x, cur_y);

                state = SegmentState::InSubpath;
                segments.push (seg);
            }

            cairo::PathSegment::ClosePath => {
                cur_x = subpath_start_x;
                cur_y = subpath_start_y;

                seg = make_line (last_x, last_y, cur_x, cur_y);

                state = SegmentState::ClosedSubpath;
                segments.push (seg);
            }
        }
    }

    match state {
        SegmentState::NewSubpath => {
            /* Output a lone point if we started a subpath with a
             * moveto command, but there are no subsequent commands.
             */
            let seg = make_degenerate (cur_x, cur_y);
            segments.push (seg);
        },

        _ => {
        }
    }

    segments
}

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

    fn create_cr () -> cairo::Context {
        let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 1, 1);
        let cr = cairo::Context::new (&surf);

        cr
    }

    fn degenerate (x: f64, y: f64) -> Segment {
        Segment::Degenerate { x: x, y: y }
    }

    fn curve (x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> Segment {
        Segment::LineOrCurve {
            x1: x1, y1: y1, x2: x2, y2: y2, x3: x3, y3: y3, x4: x4, y4: y4
        }
    }

    fn line (x1: f64, y1: f64, x2: f64, y2: f64) -> Segment {
        curve (x1, y1, x2, y2, x1, y1, x2, y2)
    }

    fn test_path_to_segments (path: cairo::Path, expected_segments: Vec<Segment>) {
        let segments = path_to_segments (path);
        assert_eq! (expected_segments, segments);
    }

    /* Single open path */

    fn setup_open_path () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_open_path () {
        let expected_segments: Vec<Segment> = vec![
            line (10.0, 10.0, 20.0, 10.0),
            line (20.0, 10.0, 20.0, 20.0)
        ];

        test_path_to_segments (setup_open_path(), expected_segments);
    }

    /* Multiple open subpaths */

    fn setup_multiple_open_subpaths () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        cr.move_to (30.0, 30.0);
        cr.line_to (40.0, 30.0);
        cr.curve_to (50.0, 35.0, 60.0, 60.0, 70.0, 70.0);
        cr.line_to (80.0, 90.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_multiple_open_subpaths () {
        let expected_segments: Vec<Segment> = vec![
            line  (10.0, 10.0, 20.0, 10.0),
            line  (20.0, 10.0, 20.0, 20.0),

            line  (30.0, 30.0, 40.0, 30.0),
            curve (40.0, 30.0, 50.0, 35.0, 60.0, 60.0, 70.0, 70.0),
            line  (70.0, 70.0, 80.0, 90.0)
        ];

        test_path_to_segments (setup_multiple_open_subpaths (), expected_segments);
    }

    fn setup_closed_subpath () -> cairo::Path {
        let cr = create_cr ();

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);
        cr.close_path ();

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_closed_subpath () {
        let expected_segments: Vec<Segment> = vec![
            line (10.0, 10.0, 20.0, 10.0),
            line (20.0, 10.0, 20.0, 20.0),
            line (20.0, 20.0, 10.0, 10.0)
        ];

        test_path_to_segments (setup_closed_subpath (), expected_segments);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Check license of third-party deps by inspecting src/vendor

use std::collections::{BTreeSet, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Command;

use serde_json;

static LICENSES: &'static [&'static str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std / test).
static EXCEPTIONS: &'static [&'static str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0 rls
];

/// Which crates to check against the whitelist?
static WHITELIST_CRATES: &'static [Crate] = &[Crate("rustc"), Crate("rustc_trans")];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
static WHITELIST: &'static [Crate] = &[
    Crate("ar "),
    Crate("arena "),
    Crate("backtrace "),
    Crate("backtrace-sys "),
    Crate("bitflags "),
    Crate("build_helper "),
    Crate("byteorder "),
    Crate("cc "),
    Crate("cfg-if "),
    Crate("cmake "),
    Crate("filetime "),
    Crate("flate2 "),
    Crate("fmt_macros "),
    Crate("fuchsia-zircon "),
    Crate("fuchsia-zircon-sys "),
    Crate("graphviz "),
    Crate("jobserver "),
    Crate("kernel32-sys "),
    Crate("lazy_static "),
    Crate("libc "),
    Crate("log "),
    Crate("log_settings "),
    Crate("miniz-sys "),
    Crate("num_cpus "),
    Crate("owning_ref "),
    Crate("parking_lot "),
    Crate("parking_lot_core "),
    Crate("rand "),
    Crate("redox_syscall "),
    Crate("rustc "),
    Crate("rustc-demangle "),
    Crate("rustc_allocator "),
    Crate("rustc_apfloat "),
    Crate("rustc_back "),
    Crate("rustc_binaryen "),
    Crate("rustc_const_eval "),
    Crate("rustc_const_math "),
    Crate("rustc_cratesio_shim "),
    Crate("rustc_data_structures "),
    Crate("rustc_errors "),
    Crate("rustc_incremental "),
    Crate("rustc_llvm "),
    Crate("rustc_mir "),
    Crate("rustc_platform_intrinsics "),
    Crate("rustc_trans "),
    Crate("rustc_trans_utils "),
    Crate("serialize "),
    Crate("smallvec "),
    Crate("stable_deref_trait "),
    Crate("syntax "),
    Crate("syntax_pos "),
    Crate("tempdir "),
    Crate("unicode-width "),
    Crate("winapi "),
    Crate("winapi-build"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to...

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name,)

impl<'a> Crate<'a> {
    pub fn from_str(s: &'a str) -> Self {
        let mut parts = s.split(" ");
        let name = parts.next().unwrap();

        Crate(name)
    }

    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences
    let path = path.join("vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // skip our exceptions
        if EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("src/vendor/{}", exception))
        }) {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of WHITELIST_CRATES at the given path. Changes `bad` to `true` if a check
/// failed.
///
/// Specifically, this checks that the dependencies are on the WHITELIST.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from cargo metadata
    let resolve = get_deps(path, cargo);

    // Get the whitelist into a convenient form
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate);
        unapproved.append(&mut bad);
    }

    if unapproved.len() > 0 {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Get the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a, 'b>(
    whitelist: &'a HashSet<Crate>,
    resolve: &'a Resolve,
    visited: &'b mut BTreeSet<Crate<'a>>,
    krate: Crate<'a>,
) -> BTreeSet<Crate<'a>> {
    // Will contain bad deps
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this dependency is not on the WHITELIST, add to bad set
    if !whitelist.contains(&krate) {
        unapproved.insert(krate);
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!)
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let krate = Crate::from_str(dep);
        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate);

        unapproved.append(&mut bad);
    }

    unapproved
}
<commit_msg>Remove spurious whitespace<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Check license of third-party deps by inspecting src/vendor

use std::collections::{BTreeSet, HashSet};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::Command;

use serde_json;

static LICENSES: &'static [&'static str] = &[
    "MIT/Apache-2.0",
    "MIT / Apache-2.0",
    "Apache-2.0/MIT",
    "Apache-2.0 / MIT",
    "MIT OR Apache-2.0",
    "MIT",
    "Unlicense/MIT",
];

/// These are exceptions to Rust's permissive licensing policy, and
/// should be considered bugs. Exceptions are only allowed in Rust
/// tooling. It is _crucial_ that no exception crates be dependencies
/// of the Rust runtime (std / test).
static EXCEPTIONS: &'static [&'static str] = &[
    "mdbook",             // MPL2, mdbook
    "openssl",            // BSD+advertising clause, cargo, mdbook
    "pest",               // MPL2, mdbook via handlebars
    "thread-id",          // Apache-2.0, mdbook
    "toml-query",         // MPL-2.0, mdbook
    "is-match",           // MPL-2.0, mdbook
    "cssparser",          // MPL-2.0, rustdoc
    "smallvec",           // MPL-2.0, rustdoc
    "fuchsia-zircon-sys", // BSD-3-Clause, rustdoc, rustc, cargo
    "fuchsia-zircon",     // BSD-3-Clause, rustdoc, rustc, cargo (jobserver & tempdir)
    "cssparser-macros",   // MPL-2.0, rustdoc
    "selectors",          // MPL-2.0, rustdoc
    "clippy_lints",       // MPL-2.0 rls
];

/// Which crates to check against the whitelist?
static WHITELIST_CRATES: &'static [Crate] = &[Crate("rustc"), Crate("rustc_trans")];

/// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible.
static WHITELIST: &'static [Crate] = &[
    Crate("ar"),
    Crate("arena"),
    Crate("backtrace"),
    Crate("backtrace-sys"),
    Crate("bitflags"),
    Crate("build_helper"),
    Crate("byteorder"),
    Crate("cc"),
    Crate("cfg-if"),
    Crate("cmake"),
    Crate("filetime"),
    Crate("flate2"),
    Crate("fmt_macros"),
    Crate("fuchsia-zircon"),
    Crate("fuchsia-zircon-sys"),
    Crate("graphviz"),
    Crate("jobserver"),
    Crate("kernel32-sys"),
    Crate("lazy_static"),
    Crate("libc"),
    Crate("log"),
    Crate("log_settings"),
    Crate("miniz-sys"),
    Crate("num_cpus"),
    Crate("owning_ref"),
    Crate("parking_lot"),
    Crate("parking_lot_core"),
    Crate("rand"),
    Crate("redox_syscall"),
    Crate("rustc"),
    Crate("rustc-demangle"),
    Crate("rustc_allocator"),
    Crate("rustc_apfloat"),
    Crate("rustc_back"),
    Crate("rustc_binaryen"),
    Crate("rustc_const_eval"),
    Crate("rustc_const_math"),
    Crate("rustc_cratesio_shim"),
    Crate("rustc_data_structures"),
    Crate("rustc_errors"),
    Crate("rustc_incremental"),
    Crate("rustc_llvm"),
    Crate("rustc_mir"),
    Crate("rustc_platform_intrinsics"),
    Crate("rustc_trans"),
    Crate("rustc_trans_utils"),
    Crate("serialize"),
    Crate("smallvec"),
    Crate("stable_deref_trait"),
    Crate("syntax"),
    Crate("syntax_pos"),
    Crate("tempdir"),
    Crate("unicode-width"),
    Crate("winapi"),
    Crate("winapi-build"),
];

// Some types for Serde to deserialize the output of `cargo metadata` to...

#[derive(Deserialize)]
struct Output {
    resolve: Resolve,
}

#[derive(Deserialize)]
struct Resolve {
    nodes: Vec<ResolveNode>,
}

#[derive(Deserialize)]
struct ResolveNode {
    id: String,
    dependencies: Vec<String>,
}

/// A unique identifier for a crate
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
struct Crate<'a>(&'a str); // (name,)

impl<'a> Crate<'a> {
    pub fn from_str(s: &'a str) -> Self {
        let mut parts = s.split(" ");
        let name = parts.next().unwrap();

        Crate(name)
    }

    pub fn id_str(&self) -> String {
        format!("{} ", self.0)
    }
}

/// Checks the dependency at the given path. Changes `bad` to `true` if a check failed.
///
/// Specifically, this checks that the license is correct.
pub fn check(path: &Path, bad: &mut bool) {
    // Check licences
    let path = path.join("vendor");
    assert!(path.exists(), "vendor directory missing");
    let mut saw_dir = false;
    for dir in t!(path.read_dir()) {
        saw_dir = true;
        let dir = t!(dir);

        // skip our exceptions
        if EXCEPTIONS.iter().any(|exception| {
            dir.path()
                .to_str()
                .unwrap()
                .contains(&format!("src/vendor/{}", exception))
        }) {
            continue;
        }

        let toml = dir.path().join("Cargo.toml");
        *bad = *bad || !check_license(&toml);
    }
    assert!(saw_dir, "no vendored source");
}

/// Checks the dependency of WHITELIST_CRATES at the given path. Changes `bad` to `true` if a check
/// failed.
///
/// Specifically, this checks that the dependencies are on the WHITELIST.
pub fn check_whitelist(path: &Path, cargo: &Path, bad: &mut bool) {
    // Get dependencies from cargo metadata
    let resolve = get_deps(path, cargo);

    // Get the whitelist into a convenient form
    let whitelist: HashSet<_> = WHITELIST.iter().cloned().collect();

    // Check dependencies
    let mut visited = BTreeSet::new();
    let mut unapproved = BTreeSet::new();
    for &krate in WHITELIST_CRATES.iter() {
        let mut bad = check_crate_whitelist(&whitelist, &resolve, &mut visited, krate);
        unapproved.append(&mut bad);
    }

    if unapproved.len() > 0 {
        println!("Dependencies not on the whitelist:");
        for dep in unapproved {
            println!("* {}", dep.id_str());
        }
        *bad = true;
    }
}

fn check_license(path: &Path) -> bool {
    if !path.exists() {
        panic!("{} does not exist", path.display());
    }
    let mut contents = String::new();
    t!(t!(File::open(path)).read_to_string(&mut contents));

    let mut found_license = false;
    for line in contents.lines() {
        if !line.starts_with("license") {
            continue;
        }
        let license = extract_license(line);
        if !LICENSES.contains(&&*license) {
            println!("invalid license {} in {}", license, path.display());
            return false;
        }
        found_license = true;
        break;
    }
    if !found_license {
        println!("no license in {}", path.display());
        return false;
    }

    true
}

fn extract_license(line: &str) -> String {
    let first_quote = line.find('"');
    let last_quote = line.rfind('"');
    if let (Some(f), Some(l)) = (first_quote, last_quote) {
        let license = &line[f + 1..l];
        license.into()
    } else {
        "bad-license-parse".into()
    }
}

/// Get the dependencies of the crate at the given path using `cargo metadata`.
fn get_deps(path: &Path, cargo: &Path) -> Resolve {
    // Run `cargo metadata` to get the set of dependencies
    let output = Command::new(cargo)
        .arg("metadata")
        .arg("--format-version")
        .arg("1")
        .arg("--manifest-path")
        .arg(path.join("Cargo.toml"))
        .output()
        .expect("Unable to run `cargo metadata`")
        .stdout;
    let output = String::from_utf8_lossy(&output);
    let output: Output = serde_json::from_str(&output).unwrap();

    output.resolve
}

/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
/// the whitelist. Returns a list of illegal dependencies.
fn check_crate_whitelist<'a, 'b>(
    whitelist: &'a HashSet<Crate>,
    resolve: &'a Resolve,
    visited: &'b mut BTreeSet<Crate<'a>>,
    krate: Crate<'a>,
) -> BTreeSet<Crate<'a>> {
    // Will contain bad deps
    let mut unapproved = BTreeSet::new();

    // Check if we have already visited this crate
    if visited.contains(&krate) {
        return unapproved;
    }

    visited.insert(krate);

    // If this dependency is not on the WHITELIST, add to bad set
    if !whitelist.contains(&krate) {
        unapproved.insert(krate);
    }

    // Do a DFS in the crate graph (it's a DAG, so we know we have no cycles!)
    let to_check = resolve
        .nodes
        .iter()
        .find(|n| n.id.starts_with(&krate.id_str()))
        .expect("crate does not exist");

    for dep in to_check.dependencies.iter() {
        let krate = Crate::from_str(dep);
        let mut bad = check_crate_whitelist(whitelist, resolve, visited, krate);

        unapproved.append(&mut bad);
    }

    unapproved
}
<|endoftext|>