<commit_before>//! Intel Instruction Set Architectures.

pub mod settings;
mod abi;
mod binemit;
mod enc_tables;
mod registers;

use binemit::{CodeSink, MemoryCodeSink, emit_function};
use super::super::settings as shared_settings;
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, RegClass, EncInfo, RegUnit};
use self::registers::RU;
use ir;
use regalloc;
use result;
use ir::{InstBuilder, InstructionData, Opcode};
use stack_layout::layout_stack;
use cursor::{Cursor, EncCursor};

#[allow(dead_code)]
struct Isa {
    shared_flags: shared_settings::Flags,
    isa_flags: settings::Flags,
    cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
}

/// Get an ISA builder for creating Intel targets.
pub fn isa_builder() -> IsaBuilder {
    IsaBuilder {
        setup: settings::builder(),
        constructor: isa_constructor,
    }
}

fn isa_constructor(
    shared_flags: shared_settings::Flags,
    builder: &shared_settings::Builder,
) -> Box<TargetIsa> {
    let level1 = if shared_flags.is_64bit() {
        &enc_tables::LEVEL1_I64[..]
    } else {
        &enc_tables::LEVEL1_I32[..]
    };
    Box::new(Isa {
        isa_flags: settings::Flags::new(&shared_flags, builder),
        shared_flags,
        cpumode: level1,
    })
}

impl TargetIsa for Isa {
    fn name(&self) -> &'static str {
        "intel"
    }

    fn flags(&self) -> &shared_settings::Flags {
        &self.shared_flags
    }

    fn register_info(&self) -> RegInfo {
        registers::INFO.clone()
    }

    fn encoding_info(&self) -> EncInfo {
        enc_tables::INFO.clone()
    }

    fn legal_encodings<'a>(
        &'a self,
        dfg: &'a ir::DataFlowGraph,
        inst: &'a ir::InstructionData,
        ctrl_typevar: ir::Type,
    ) -> Encodings<'a> {
        lookup_enclist(
            ctrl_typevar,
            inst,
            dfg,
            self.cpumode,
            &enc_tables::LEVEL2[..],
            &enc_tables::ENCLISTS[..],
            &enc_tables::LEGALIZE_ACTIONS[..],
            &enc_tables::RECIPE_PREDICATES[..],
            &enc_tables::INST_PREDICATES[..],
            self.isa_flags.predicate_view(),
        )
    }

    fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
        abi::legalize_signature(sig, &self.shared_flags, current)
    }

    fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass {
        abi::regclass_for_abi_type(ty)
    }

    fn allocatable_registers(&self, func: &ir::Function) -> regalloc::AllocatableSet {
        abi::allocatable_registers(func, &self.shared_flags)
    }

    fn emit_inst(
        &self,
        func: &ir::Function,
        inst: ir::Inst,
        divert: &mut regalloc::RegDiversions,
        sink: &mut CodeSink,
    ) {
        binemit::emit_inst(func, inst, divert, sink)
    }

    fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
        emit_function(func, binemit::emit_inst, sink)
    }

    fn reloc_names(&self) -> &'static [&'static str] {
        &binemit::RELOC_NAMES
    }

    fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult {
        let word_size = if self.flags().is_64bit() { 8 } else { 4 };

        // TODO: Insert stack slot for FP and CSRs we will push
        let meta_stack_size = word_size; // TODO: this needs to change depending on CSRs
        let stack_offset = -(meta_stack_size as i32);
        let slot = ir::StackSlotData {
            kind: ir::StackSlotKind::IncomingArg,
            size: meta_stack_size,
            offset: stack_offset,
        };
        func.create_stack_slot(slot);

        let total_stack_size = layout_stack(&mut func.stack_slots, word_size)?;
        let local_stack_size = total_stack_size - meta_stack_size;

        // Append frame pointer to function signature
        let rbp_arg = ir::AbiParam::special_reg(
            ir::types::I64,
            ir::ArgumentPurpose::FramePointer,
            RU::rbp as RegUnit,
        );
        func.signature.params.push(rbp_arg);
        func.signature.returns.push(rbp_arg);

        // Append param to entry EBB
        let entry_ebb = func.layout.entry_block().expect("missing entry block");
        func.dfg.append_ebb_param(entry_ebb, ir::types::I64);

        // Find our frame pointer parameter Value
        let fp = func.special_param(ir::ArgumentPurpose::FramePointer)
            .expect("missing frame pointer");

        // Assign it a location
        func.locations[fp] = ir::ValueLoc::Reg(RU::rbp as RegUnit);

        // Insert prologue
        {
            let mut pos = EncCursor::new(func, self).at_first_insertion_point(entry_ebb);
            pos.ins().x86_push(fp);
            pos.ins().copy_special(
                RU::rbp as RegUnit,
                RU::rsp as RegUnit,
            );
            pos.ins().adjust_sp_imm(-(local_stack_size as i32));
        }

        let mut return_insts = Vec::new();

        for ebb in func.layout.ebbs() {
            for inst in func.layout.ebb_insts(ebb) {
                if let InstructionData::MultiAry { opcode, .. } = func.dfg[inst] {
                    if opcode == Opcode::Return {
                        return_insts.push(inst);
                    }
                }

            }
        }

        for inst in return_insts {
            let fp_ret = self.insert_epilogue(inst, local_stack_size as i32, func);
            func.locations[fp_ret] = ir::ValueLoc::Reg(RU::rbp as RegUnit);
            func.dfg.append_inst_arg(inst, fp_ret);
        }

        Ok(())
    }
}

impl Isa {
    fn insert_epilogue(
        &self,
        inst: ir::Inst,
        stack_size: i32,
        func: &mut ir::Function,
    ) -> ir::Value {
        let mut pos = EncCursor::new(func, self).at_inst(inst);
        pos.ins().adjust_sp_imm(stack_size);
        pos.ins().x86_pop(ir::types::I64)
    }
}
<commit_msg>Only emit an adjust_sp_imm if the stack_size is non-zero.<commit_after>//! Intel Instruction Set Architectures.

pub mod settings;
mod abi;
mod binemit;
mod enc_tables;
mod registers;

use binemit::{CodeSink, MemoryCodeSink, emit_function};
use super::super::settings as shared_settings;
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, Encodings};
use isa::Builder as IsaBuilder;
use isa::{TargetIsa, RegInfo, RegClass, EncInfo, RegUnit};
use self::registers::RU;
use ir;
use regalloc;
use result;
use ir::{InstBuilder, InstructionData, Opcode};
use stack_layout::layout_stack;
use cursor::{Cursor, EncCursor};

#[allow(dead_code)]
struct Isa {
    shared_flags: shared_settings::Flags,
    isa_flags: settings::Flags,
    cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
}

/// Get an ISA builder for creating Intel targets.
pub fn isa_builder() -> IsaBuilder {
    IsaBuilder {
        setup: settings::builder(),
        constructor: isa_constructor,
    }
}

fn isa_constructor(
    shared_flags: shared_settings::Flags,
    builder: &shared_settings::Builder,
) -> Box<TargetIsa> {
    let level1 = if shared_flags.is_64bit() {
        &enc_tables::LEVEL1_I64[..]
    } else {
        &enc_tables::LEVEL1_I32[..]
    };
    Box::new(Isa {
        isa_flags: settings::Flags::new(&shared_flags, builder),
        shared_flags,
        cpumode: level1,
    })
}

impl TargetIsa for Isa {
    fn name(&self) -> &'static str {
        "intel"
    }

    fn flags(&self) -> &shared_settings::Flags {
        &self.shared_flags
    }

    fn register_info(&self) -> RegInfo {
        registers::INFO.clone()
    }

    fn encoding_info(&self) -> EncInfo {
        enc_tables::INFO.clone()
    }

    fn legal_encodings<'a>(
        &'a self,
        dfg: &'a ir::DataFlowGraph,
        inst: &'a ir::InstructionData,
        ctrl_typevar: ir::Type,
    ) -> Encodings<'a> {
        lookup_enclist(
            ctrl_typevar,
            inst,
            dfg,
            self.cpumode,
            &enc_tables::LEVEL2[..],
            &enc_tables::ENCLISTS[..],
            &enc_tables::LEGALIZE_ACTIONS[..],
            &enc_tables::RECIPE_PREDICATES[..],
            &enc_tables::INST_PREDICATES[..],
            self.isa_flags.predicate_view(),
        )
    }

    fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
        abi::legalize_signature(sig, &self.shared_flags, current)
    }

    fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass {
        abi::regclass_for_abi_type(ty)
    }

    fn allocatable_registers(&self, func: &ir::Function) -> regalloc::AllocatableSet {
        abi::allocatable_registers(func, &self.shared_flags)
    }

    fn emit_inst(
        &self,
        func: &ir::Function,
        inst: ir::Inst,
        divert: &mut regalloc::RegDiversions,
        sink: &mut CodeSink,
    ) {
        binemit::emit_inst(func, inst, divert, sink)
    }

    fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {
        emit_function(func, binemit::emit_inst, sink)
    }

    fn reloc_names(&self) -> &'static [&'static str] {
        &binemit::RELOC_NAMES
    }

    fn prologue_epilogue(&self, func: &mut ir::Function) -> result::CtonResult {
        let word_size = if self.flags().is_64bit() { 8 } else { 4 };

        // TODO: Insert stack slot for FP and CSRs we will push
        let meta_stack_size = word_size; // TODO: this needs to change depending on CSRs
        let stack_offset = -(meta_stack_size as i32);
        let slot = ir::StackSlotData {
            kind: ir::StackSlotKind::IncomingArg,
            size: meta_stack_size,
            offset: stack_offset,
        };
        func.create_stack_slot(slot);

        let total_stack_size = layout_stack(&mut func.stack_slots, word_size)?;
        let local_stack_size = total_stack_size - meta_stack_size;

        // Append frame pointer to function signature
        let rbp_arg = ir::AbiParam::special_reg(
            ir::types::I64,
            ir::ArgumentPurpose::FramePointer,
            RU::rbp as RegUnit,
        );
        func.signature.params.push(rbp_arg);
        func.signature.returns.push(rbp_arg);

        // Append param to entry EBB
        let entry_ebb = func.layout.entry_block().expect("missing entry block");
        func.dfg.append_ebb_param(entry_ebb, ir::types::I64);

        // Find our frame pointer parameter Value
        let fp = func.special_param(ir::ArgumentPurpose::FramePointer)
            .expect("missing frame pointer");

        // Assign it a location
        func.locations[fp] = ir::ValueLoc::Reg(RU::rbp as RegUnit);

        // Insert prologue
        {
            let mut pos = EncCursor::new(func, self).at_first_insertion_point(entry_ebb);
            pos.ins().x86_push(fp);
            pos.ins().copy_special(
                RU::rbp as RegUnit,
                RU::rsp as RegUnit,
            );
            if local_stack_size > 0 {
                pos.ins().adjust_sp_imm(-(local_stack_size as i32));
            }
        }

        // Find all 'return' instructions
        let mut return_insts = Vec::new();
        for ebb in func.layout.ebbs() {
            for inst in func.layout.ebb_insts(ebb) {
                if let InstructionData::MultiAry { opcode, .. } = func.dfg[inst] {
                    if opcode == Opcode::Return {
                        return_insts.push(inst);
                    }
                }

            }
        }

        // Insert an epilogue directly before every 'return'
        for inst in return_insts {
            let fp_ret = self.insert_epilogue(inst, local_stack_size as i32, func);
            func.locations[fp_ret] = ir::ValueLoc::Reg(RU::rbp as RegUnit);
            func.dfg.append_inst_arg(inst, fp_ret);
        }

        Ok(())
    }
}

impl Isa {
    fn insert_epilogue(
        &self,
        inst: ir::Inst,
        stack_size: i32,
        func: &mut ir::Function,
    ) -> ir::Value {
        let mut pos = EncCursor::new(func, self).at_inst(inst);
        if stack_size > 0 {
            pos.ins().adjust_sp_imm(stack_size);
        }
        pos.ins().x86_pop(ir::types::I64)
    }
}
<|endoftext|>


<commit_before>//! TODO Documentation

use std::panic;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};

use wlroots_sys::{wlr_cursor_attach_output_layout, wlr_output_effective_resolution,
                  wlr_output_layout, wlr_output_layout_add, wlr_output_layout_add_auto,
                  wlr_output_layout_create, wlr_output_layout_destroy, wlr_output_layout_move,
                  wlr_output_layout_output, wlr_output_layout_remove};

use errors::{UpgradeHandleErr, UpgradeHandleResult};

use {Cursor, CursorBuilder, Origin, Output, OutputHandle};

#[derive(Debug)]
pub struct OutputLayout {
    /// The structure that ensures weak handles to this structure are still alive.
    ///
    /// They contain weak handles, and will safely not use dead memory when this
    /// is freed by wlroots.
    ///
    /// If this is `None`, then this is from an upgraded `OutputHandle`, and
    /// the operations are **unchecked**.
    /// This is means safe operations might fail, but only if you use the unsafe
    /// marked function `upgrade` on a `OutputHandle`.
    liveliness: Option<Rc<AtomicBool>>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout,
    cursors: Vec<Cursor>
}

/// A handle to an `OutputLayout`.
///
/// Used internally by `Output` to gain access to the `OutputLayout`.
#[derive(Debug, Clone)]
pub(crate) struct OutputLayoutHandle {
    /// The Rc that ensures that this handle is still alive.
    ///
    /// When wlroots deallocates the pointer associated with this handle,
    /// this can no longer be used.
    handle: Weak<AtomicBool>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout
}

/// The coordinate information of an `Output` within an `OutputLayout`.
#[derive(Debug)]
// TODO Remove pub?
pub struct OutputLayoutOutput {
    layout_output: *mut wlr_output_layout_output
}

impl OutputLayout {
    /// Construct a new OuputLayout.
    pub fn new() -> Option<Self> {
        unsafe {
            let layout = wlr_output_layout_create();
            if layout.is_null() {
                None
            } else {
                Some(OutputLayout { liveliness: Some(Rc::new(AtomicBool::new(false))),
                                    layout,
                                    cursors: Vec::new() })
            }
        }
    }

    pub fn outputs(&mut self) -> Vec<(OutputHandle, Origin)> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link, (pos: wlr_output_layout_output) => {
                result.push((OutputHandle::from_ptr((*pos).output),
                             Origin::new((*pos).x, (*pos).y)))
            });
            result
        }
    }

    pub fn cursors(&mut self) -> &mut [Cursor] {
        self.cursors.as_mut_slice()
    }

    /// Attach a cursor to this OutputLayout.
    pub fn attach_cursor(&mut self, cursor: CursorBuilder) {
        unsafe {
            let cursor = cursor.build(self.weak_reference());
            wlr_cursor_attach_output_layout(cursor.as_ptr(), self.layout);
            self.cursors.push(cursor);
        }
    }

    /// Adds an output to the layout at the given coordinates.
    pub fn add(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_add(self.layout, output.as_ptr(), x, y) }
    }

    /// Adds an output to the layout, automatically positioning it with
    /// the others that are already there.
    pub fn add_auto(&mut self, output: &mut Output) {
        unsafe {
            let layout_handle = self.weak_reference();
            output.set_output_layout(Some(layout_handle));
            wlr_output_layout_add_auto(self.layout, output.as_ptr());
            wlr_log!(L_DEBUG, "Added {:?} to {:?}", output, self);
        }
    }

    /// Moves the output to the given coordinates.
    ///
    /// If the output is not part of this layout this does nothing.
    pub fn move_output(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_move(self.layout, output.as_ptr(), x, y) }
    }

    /// Remove an output from this layout.
    ///
    /// If the output was not in the layout, does nothing.
    pub fn remove(&mut self, output: &mut Output) {
        wlr_log!(L_DEBUG, "Removing {:?} from {:?}", output, self);
        unsafe {
            output.clear_output_layout_data();
            wlr_output_layout_remove(self.layout, output.as_ptr());
        };
    }

    /// Creates a weak reference to an `OutputLayout`.
    ///
    /// # Panics
    /// If this `OutputLayout` is a previously upgraded `OutputLayoutHandle`,
    /// then this function will panic.
    pub(crate) fn weak_reference(&self) -> OutputLayoutHandle {
        let arc = self.liveliness.as_ref()
                      .expect("Cannot downgrade a previously upgraded OutputLayoutHandle");
        OutputLayoutHandle { handle: Rc::downgrade(arc),
                             layout: self.layout }
    }

    unsafe fn from_handle(handle: &OutputLayoutHandle) -> Self {
        OutputLayout { liveliness: None,
                       layout: handle.as_ptr(),
                       cursors: Vec::new() }
    }
}

impl Drop for OutputLayout {
    fn drop(&mut self) {
        match self.liveliness {
            None => {}
            Some(ref liveliness) => {
                if Rc::strong_count(liveliness) == 1 {
                    unsafe { wlr_output_layout_destroy(self.layout) }
                    wlr_log!(L_DEBUG, "Dropped {:?}", self);
                    let weak_count = Rc::weak_count(liveliness);
                    if weak_count > 0 {
                        wlr_log!(L_DEBUG,
                                 "Still {} weak pointers to OutputLayout {:?}",
                                 weak_count,
                                 self.layout);
                    }
                }
            }
        }
    }
}

impl OutputLayoutHandle {
    /// Upgrades the `OutputLayoutHandle` to a reference
    /// to the backing `OutputLayout`.
    ///
    /// # Unsafety
    /// This function is unsafe, because it creates an unbound `OutputLayout`
    /// which may live forever..
    /// But the actual lifetime of `OutputLayout` is determined by the user.
    pub(crate) unsafe fn upgrade(&self) -> UpgradeHandleResult<OutputLayout> {
        self.handle.upgrade()
            .ok_or(UpgradeHandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let output_layout = OutputLayout::from_handle(self);
                if check.load(Ordering::Acquire) {
                    return Err(UpgradeHandleErr::AlreadyBorrowed)
                }
                check.store(true, Ordering::Release);
                Ok(output_layout)
            })
    }

    /// Run a function on the referenced OutputLayout, if it still exists
    ///
    /// Returns the result of the function, if successful.
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the OutputLayout
    /// to a short lived scope of an anonymous function,
    /// this function ensures the OutputLayout does not live longer
    /// than it exists (because the lifetime is controlled by the user).
    pub fn run<F, R>(&mut self, runner: F) -> UpgradeHandleResult<R>
        where F: FnOnce(&mut OutputLayout) -> R
    {
        let mut output_layout = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut output_layout)));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.load(Ordering::Acquire) {
                                          wlr_log!(L_ERROR,
                                                   "After running OutputLayout callback, mutable \
                                                    lock was false for: {:?}",
                                                   output_layout);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.store(false, Ordering::Release);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    unsafe fn as_ptr(&self) -> *mut wlr_output_layout {
        self.layout
    }
}

impl OutputLayoutOutput {
    /// Get the absolute top left edge coordinate of this output in the output
    /// layout.
    pub fn top_left_edge(&self) -> Origin {
        unsafe { Origin::new((*self.layout_output).x, (*self.layout_output).y) }
    }

    /// Get the absolute top right edge coordinate of this output in the output
    /// layout.
    pub fn top_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut _height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut _height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + width, y)
        }
    }

    pub fn bottom_left_edge(&self) -> Origin {
        unsafe {
            let (mut _width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut _width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x, y + height)
        }
    }

    pub fn bottom_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + height, y + height)
        }
    }
}

impl PartialEq for OutputLayoutHandle {
    fn eq(&self, other: &OutputLayoutHandle) -> bool {
        self.layout == other.layout
    }
}

impl Eq for OutputLayoutHandle {}
<commit_msg>Allow access to OutputLayoutOutput<commit_after>//! TODO Documentation

use std::panic;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::marker::PhantomData;

use wlroots_sys::{wlr_cursor_attach_output_layout, wlr_output_effective_resolution,
                  wlr_output_layout, wlr_output_layout_add, wlr_output_layout_add_auto,
                  wlr_output_layout_create, wlr_output_layout_destroy, wlr_output_layout_move,
                  wlr_output_layout_output, wlr_output_layout_remove};

use errors::{UpgradeHandleErr, UpgradeHandleResult};

use {Cursor, CursorBuilder, Origin, Output, OutputHandle};

#[derive(Debug)]
pub struct OutputLayout {
    /// The structure that ensures weak handles to this structure are still alive.
    ///
    /// They contain weak handles, and will safely not use dead memory when this
    /// is freed by wlroots.
    ///
    /// If this is `None`, then this is from an upgraded `OutputHandle`, and
    /// the operations are **unchecked**.
    /// This is means safe operations might fail, but only if you use the unsafe
    /// marked function `upgrade` on a `OutputHandle`.
    liveliness: Option<Rc<AtomicBool>>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout,
    cursors: Vec<Cursor>
}

/// A handle to an `OutputLayout`.
///
/// Used internally by `Output` to gain access to the `OutputLayout`.
#[derive(Debug, Clone)]
pub(crate) struct OutputLayoutHandle {
    /// The Rc that ensures that this handle is still alive.
    ///
    /// When wlroots deallocates the pointer associated with this handle,
    /// this can no longer be used.
    handle: Weak<AtomicBool>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout
}

/// The coordinate information of an `Output` within an `OutputLayout`.
#[derive(Debug)]
// TODO Remove pub?
pub struct OutputLayoutOutput<'output> {
    layout_output: *mut wlr_output_layout_output,
    phantom: PhantomData<&'output OutputLayout>
}

impl OutputLayout {
    /// Construct a new OuputLayout.
    pub fn new() -> Option<Self> {
        unsafe {
            let layout = wlr_output_layout_create();
            if layout.is_null() {
                None
            } else {
                Some(OutputLayout { liveliness: Some(Rc::new(AtomicBool::new(false))),
                                    layout,
                                    cursors: Vec::new() })
            }
        }
    }

    /// Get the outputs associated with this OutputLayout.
    ///
    /// Also returns their absolute position within the layout.
    pub fn outputs(&mut self) -> Vec<(OutputHandle, Origin)> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link, (pos: wlr_output_layout_output) => {
                result.push((OutputHandle::from_ptr((*pos).output),
                             Origin::new((*pos).x, (*pos).y)))
            });
            result
        }
    }

    /// Get the Outputs in the OutputLayout coupled with their output information.
    ///
    /// For a version that isn't bound by lifetimes, see `outputs`.
    pub fn outputs_layouts<'output>(&'output mut self) -> Vec<OutputLayoutOutput<'output>> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link,
                              (layout_output: wlr_output_layout_output) => {
                                  result.push(OutputLayoutOutput { layout_output,
                                                                   phantom: PhantomData
                                  })
                              });
            result
        }
    }

    pub fn cursors(&mut self) -> &mut [Cursor] {
        self.cursors.as_mut_slice()
    }

    /// Attach a cursor to this OutputLayout.
    pub fn attach_cursor(&mut self, cursor: CursorBuilder) {
        unsafe {
            let cursor = cursor.build(self.weak_reference());
            wlr_cursor_attach_output_layout(cursor.as_ptr(), self.layout);
            self.cursors.push(cursor);
        }
    }

    /// Adds an output to the layout at the given coordinates.
    pub fn add(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_add(self.layout, output.as_ptr(), x, y) }
    }

    /// Adds an output to the layout, automatically positioning it with
    /// the others that are already there.
    pub fn add_auto(&mut self, output: &mut Output) {
        unsafe {
            let layout_handle = self.weak_reference();
            output.set_output_layout(Some(layout_handle));
            wlr_output_layout_add_auto(self.layout, output.as_ptr());
            wlr_log!(L_DEBUG, "Added {:?} to {:?}", output, self);
        }
    }

    /// Moves the output to the given coordinates.
    ///
    /// If the output is not part of this layout this does nothing.
    pub fn move_output(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_move(self.layout, output.as_ptr(), x, y) }
    }

    /// Remove an output from this layout.
    ///
    /// If the output was not in the layout, does nothing.
    pub fn remove(&mut self, output: &mut Output) {
        wlr_log!(L_DEBUG, "Removing {:?} from {:?}", output, self);
        unsafe {
            output.clear_output_layout_data();
            wlr_output_layout_remove(self.layout, output.as_ptr());
        };
    }

    /// Creates a weak reference to an `OutputLayout`.
    ///
    /// # Panics
    /// If this `OutputLayout` is a previously upgraded `OutputLayoutHandle`,
    /// then this function will panic.
    pub(crate) fn weak_reference(&self) -> OutputLayoutHandle {
        let arc = self.liveliness.as_ref()
                      .expect("Cannot downgrade a previously upgraded OutputLayoutHandle");
        OutputLayoutHandle { handle: Rc::downgrade(arc),
                             layout: self.layout }
    }

    unsafe fn from_handle(handle: &OutputLayoutHandle) -> Self {
        OutputLayout { liveliness: None,
                       layout: handle.as_ptr(),
                       cursors: Vec::new() }
    }
}

impl Drop for OutputLayout {
    fn drop(&mut self) {
        match self.liveliness {
            None => {}
            Some(ref liveliness) => {
                if Rc::strong_count(liveliness) == 1 {
                    unsafe { wlr_output_layout_destroy(self.layout) }
                    wlr_log!(L_DEBUG, "Dropped {:?}", self);
                    let weak_count = Rc::weak_count(liveliness);
                    if weak_count > 0 {
                        wlr_log!(L_DEBUG,
                                 "Still {} weak pointers to OutputLayout {:?}",
                                 weak_count,
                                 self.layout);
                    }
                }
            }
        }
    }
}

impl OutputLayoutHandle {
    /// Upgrades the `OutputLayoutHandle` to a reference
    /// to the backing `OutputLayout`.
    ///
    /// # Unsafety
    /// This function is unsafe, because it creates an unbound `OutputLayout`
    /// which may live forever..
    /// But the actual lifetime of `OutputLayout` is determined by the user.
    pub(crate) unsafe fn upgrade(&self) -> UpgradeHandleResult<OutputLayout> {
        self.handle.upgrade()
            .ok_or(UpgradeHandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let output_layout = OutputLayout::from_handle(self);
                if check.load(Ordering::Acquire) {
                    return Err(UpgradeHandleErr::AlreadyBorrowed)
                }
                check.store(true, Ordering::Release);
                Ok(output_layout)
            })
    }

    /// Run a function on the referenced OutputLayout, if it still exists
    ///
    /// Returns the result of the function, if successful.
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the OutputLayout
    /// to a short lived scope of an anonymous function,
    /// this function ensures the OutputLayout does not live longer
    /// than it exists (because the lifetime is controlled by the user).
    pub fn run<F, R>(&mut self, runner: F) -> UpgradeHandleResult<R>
        where F: FnOnce(&mut OutputLayout) -> R
    {
        let mut output_layout = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut output_layout)));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.load(Ordering::Acquire) {
                                          wlr_log!(L_ERROR,
                                                   "After running OutputLayout callback, mutable \
                                                    lock was false for: {:?}",
                                                   output_layout);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.store(false, Ordering::Release);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    unsafe fn as_ptr(&self) -> *mut wlr_output_layout {
        self.layout
    }
}

impl <'output> OutputLayoutOutput<'output> {
    /// Get the absolute top left edge coordinate of this output in the output
    /// layout.
    pub fn top_left_edge(&self) -> Origin {
        unsafe { Origin::new((*self.layout_output).x, (*self.layout_output).y) }
    }

    /// Get the absolute top right edge coordinate of this output in the output
    /// layout.
    pub fn top_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut _height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut _height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + width, y)
        }
    }

    pub fn bottom_left_edge(&self) -> Origin {
        unsafe {
            let (mut _width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut _width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x, y + height)
        }
    }

    pub fn bottom_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + height, y + height)
        }
    }
}

impl PartialEq for OutputLayoutHandle {
    fn eq(&self, other: &OutputLayoutHandle) -> bool {
        self.layout == other.layout
    }
}

impl Eq for OutputLayoutHandle {}
<|endoftext|>


<commit_before>use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::f64;
use std::env;

use tabwriter::TabWriter;

use comment::Comment;
use config::{Config, Utf8Rule};
use count::Count;
use error::{CliError, CliResult};
use fsutil;
use fmt::{self, Format};
use language::Language;
use gitignore;

pub struct Counts<'c> {
    cfg: &'c Config<'c>,
    counts: Vec<Count>,
    tot: usize,
    tot_lines: u64,
    tot_comments: u64,
    tot_blanks: u64,
    tot_code: u64,
    tot_usafe: u64,
}

impl<'c> Counts<'c> {
    pub fn new(cfg: &'c Config) -> Self {
        Counts {
            cfg: cfg,
            counts: vec![],
            tot: 0,
            tot_lines: 0,
            tot_comments: 0,
            tot_blanks: 0,
            tot_code: 0,
            tot_usafe: 0,
        }
    }

    pub fn fill_from(&mut self) {
        debugln!("executing; fill_from; cfg={:?}", self.cfg);
        let cd;
        let gitignore = if self.cfg.all {
            None
        } else {
            cd = env::current_dir().unwrap().join(".gitignore");
            gitignore::File::new(&cd).ok()
        };
        for path in &self.cfg.to_count {
            debugln!("iter; path={:?};", path);
            let mut files = vec![];
            fsutil::get_all_files(&mut files,
                                  &path,
                                  &self.cfg.exclude,
                                  self.cfg.follow_links,
                                  &gitignore);

            for file in files {
                debugln!("iter; file={:?};", file);
                let extension = match Path::new(&file).extension() {
                    Some(result) => {
                        if let Some(ref exts) = self.cfg.exts {
                            if !exts.contains(&result.to_str().unwrap_or("")) {
                                continue;
                            }
                        }
                        result.to_str().unwrap()
                    }
                    None => continue,
                };

                debugln!("found extension: {:?}", extension);
                if let Some(pos_lang) = Language::from_ext(extension) {
                    debugln!("Extension is valid");
                    let mut found = false;
                    debugln!("Searching for previous entries of that type");
                    for l in self.counts.iter_mut() {
                        if l.lang == pos_lang {
                            debugln!("Found");
                            found = true;
                            l.add_file(PathBuf::from(&file));
                            break;
                        }
                    }
                    if !found {
                        debugln!("Not found, creating new");
                        let mut c = Count::new(pos_lang, self.cfg.thousands);
                        c.add_file(PathBuf::from(&file));
                        self.counts.push(c);
                    }
                } else {
                    debugln!("extension wasn't valid");
                }
            }
        }
    }

    #[cfg_attr(feature = "lints", allow(cyclomatic_complexity, trivial_regex))]
    pub fn count(&mut self) -> CliResult<()> {
        for count in self.counts.iter_mut() {
            debugln!("iter; count={:?};", count);
            let re = if let Some(kw) = count.lang.unsafe_keyword() {
                regex!(&*format!("(.*?)([:^word:]{}[:^word:])(.*)", kw))
            } else {
                regex!("")
            };
            for file in count.files.iter() {
                debugln!("iter; file={:?};", file);
                let mut buffer = String::new();

                let mut file_ref = cli_try!(File::open(&file));

                match self.cfg.utf8_rule {
                    Utf8Rule::Ignore => {
                        if let Err(..) = file_ref.read_to_string(&mut buffer) {
                            continue;
                        }
                    }
                    Utf8Rule::Lossy => {
                        let mut vec_buf = vec![];
                        cli_try!(file_ref.read_to_end(&mut vec_buf));
                        buffer = String::from_utf8_lossy(&vec_buf).into_owned();
                    }
                    Utf8Rule::Strict => {
                        cli_try!(file_ref.read_to_string(&mut buffer));
                    }
                }
                let mut is_in_comments = false;
                let mut is_in_unsafe = false;
                let mut bracket_count: i64 = 0;

                'new_line: for line in buffer.lines() {
                    let line = line.trim();
                    debugln!("iter; line={:?};", line);
                    count.lines += 1;

                    if is_in_comments {
                        debugln!("still in comments");
                        if line.contains(count.multi_end().unwrap()) {
                            debugln!("line contained ending comment, stopping comments");
                            is_in_comments = false;
                        }
                        count.comments += 1;
                        continue;
                    }
                    debugln!("not in comments");

                    if line.trim().is_empty() {
                        debugln!("line was empty");
                        count.blanks += 1;
                        continue;
                    }
                    debugln!("Line isn't empty");

                    if let Some(ms) = count.multi_start() {
                        debugln!("This file type has a multi start of: {:?}", ms);
                        if line.starts_with(ms) {
                            debugln!("line starts with multi comment");
                            count.comments += 1;
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            continue;
                        } else if line.contains(ms) {
                            debugln!("line contains a multi start");
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            if is_in_comments {
                                continue;
                            }
                        }
                    } else {
                        debugln!("No multi line comments for this type");
                    }
                    debugln!("No multi line comments for this line");

                    if let Some(single_comments) = count.single() {
                        debugln!("This type has single line comments: {:?}", single_comments);
                        for single in single_comments {
                            if line.starts_with(single) {
                                debugln!("Line started with a comment");
                                count.comments += 1;
                                continue 'new_line;
                            } else {
                                debugln!("Line dind't start with a comment");
                            }
                        }
                    } else {
                        debugln!("No single line comments for this type");
                    }

                    if self.cfg.usafe && count.lang.is_unsafe() {
                        debugln!("Calculating --unsafe-statistics");
                        debugln!("The language is not safe");
                        if let Some(..) = count.lang.unsafe_keyword() {
                            debugln!("There is a keyword");
                            debugln!("line={:?}", line);
                            if is_in_unsafe {
                                debugln!("It didn't contain the keyword, but we are still in \
                                          unsafe");
                                count.usafe += 1;
                                bracket_count = Counts::count_brackets(line, Some(bracket_count));
                                is_in_unsafe = bracket_count > 0;
                                debugln!("after counting brackets; is_in_unsafe={:?}; \
                                          bracket_count={:?}",
                                         is_in_unsafe,
                                         bracket_count);
                            } else if let Some(caps) = re.captures(line) {
                                let mut should_count = true;
                                if let Some(before) = caps.at(1) {
                                    if let Some(single_v) = count.lang.single() {
                                        for s in single_v {
                                            if before.contains(s) {
                                                should_count = false;
                                                break;
                                            }
                                        }
                                    }
                                    if let Some(multi) = count.lang.multi_start() {
                                        if before.contains(multi) &&
                                           !before.contains(count.lang.multi_end().unwrap()) {
                                            should_count = false;
                                        }
                                    }
                                }
                                if should_count {
                                    debugln!("It contained the keyword; usafe_line={:?}", line);
                                    count.usafe += 1;
                                    if let Some(after) = caps.at(3) {
                                        debugln!("after_usafe={:?}", after);
                                        bracket_count = Counts::count_brackets(after, None);
                                        is_in_unsafe = bracket_count > 0;
                                        debugln!("after counting brackets; is_in_unsafe={:?}; \
                                                  bracket_count={:?}",
                                                 is_in_unsafe,
                                                 bracket_count);
                                    }
                                }
                            } else {
                                debugln!("It didn't contain the keyword, and we are not in unsafe");
                            }

                            if bracket_count < 0 {
                                debugln!("bracket_count < 0; resetting");
                                bracket_count = 0
                            }
                        } else {
                            debugln!("Language is unsafe, incing the count");
                            count.usafe += 1;
                        }
                    }
                    count.code += 1;
                }
            }
            self.tot += count.files.len();
            self.tot_lines += count.lines;
            self.tot_comments += count.comments;
            self.tot_blanks += count.blanks;
            self.tot_code += count.code;
            self.tot_usafe += count.usafe;
        }

        Ok(())
    }

    pub fn write_results(&mut self) -> CliResult<()> {
        let mut w = TabWriter::new(vec![]);
        cli_try!(write!(w,
                        "\tLanguage\tFiles\tLines\tBlanks\tComments\tCode{}\n",
                        if self.cfg.usafe {
                            "\tUnsafe (%)"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        for count in &self.counts {
            if self.cfg.usafe {
                let usafe_per = (count.usafe as f64 / count.code as f64) * 100.00f64;
                cli_try!(write!(w,
                                "\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
                                count.lang.name(),
                                count.total_files(),
                                count.lines(),
                                count.blanks(),
                                count.comments(),
                                count.code(),
                                if (usafe_per - 00f64).abs() < f64::EPSILON {
                                    "".to_owned()
                                } else {
                                    format!("{} ({:.2}%)", count.usafe(), usafe_per)
                                }));
            } else {
                cli_try!(write!(w, "\t{}\n", count));
            }
        }
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "{}\t\t{}\t{}\t{}\t{}\t{}{}\n",
                        "Totals:",
                        fmt::format_number(self.tot as u64, self.cfg.thousands),
                        fmt::format_number(self.tot_lines, self.cfg.thousands),
                        fmt::format_number(self.tot_blanks, self.cfg.thousands),
                        fmt::format_number(self.tot_comments, self.cfg.thousands),
                        fmt::format_number(self.tot_code, self.cfg.thousands),
                        if self.cfg.usafe {
                            format!("\t{} ({:.2}%)",
                                    fmt::format_number(self.tot_usafe, self.cfg.thousands),
                                    (self.tot_usafe as f64 / self.tot_code as f64) * 100.00f64)
                        } else {
                            "".to_owned()
                        }));

        cli_try!(w.flush());

        verboseln!(self.cfg,
                   "{} {}",
                   Format::Good("Displaying"),
                   "the results:");
        if self.tot > 0 {
            write!(io::stdout(),
                   "{}",
                   String::from_utf8(w.unwrap()).ok().expect("failed to get valid UTF-8 String"))
                .expect("failed to write output");
        } else {
            println!("\n\tNo source files were found matching the specified criteria");
        }
        Ok(())
    }

    fn count_brackets(line: &str, count: Option<i64>) -> i64 {
        let mut b: i64 = count.unwrap_or(0);
        for c in line.chars() {
            match c {
                '{' => b += 1,
                '}' => b -= 1,
                _ => (),
            }
        }
        b
    }
}
<commit_msg>fix: fix division by zero<commit_after>use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::f64;
use std::env;

use tabwriter::TabWriter;

use comment::Comment;
use config::{Config, Utf8Rule};
use count::Count;
use error::{CliError, CliResult};
use fsutil;
use fmt::{self, Format};
use language::Language;
use gitignore;

pub struct Counts<'c> {
    cfg: &'c Config<'c>,
    counts: Vec<Count>,
    tot: usize,
    tot_lines: u64,
    tot_comments: u64,
    tot_blanks: u64,
    tot_code: u64,
    tot_usafe: u64,
}

impl<'c> Counts<'c> {
    pub fn new(cfg: &'c Config) -> Self {
        Counts {
            cfg: cfg,
            counts: vec![],
            tot: 0,
            tot_lines: 0,
            tot_comments: 0,
            tot_blanks: 0,
            tot_code: 0,
            tot_usafe: 0,
        }
    }

    pub fn fill_from(&mut self) {
        debugln!("executing; fill_from; cfg={:?}", self.cfg);
        let cd;
        let gitignore = if self.cfg.all {
            None
        } else {
            cd = env::current_dir().unwrap().join(".gitignore");
            gitignore::File::new(&cd).ok()
        };
        for path in &self.cfg.to_count {
            debugln!("iter; path={:?};", path);
            let mut files = vec![];
            fsutil::get_all_files(&mut files,
                                  &path,
                                  &self.cfg.exclude,
                                  self.cfg.follow_links,
                                  &gitignore);

            for file in files {
                debugln!("iter; file={:?};", file);
                let extension = match Path::new(&file).extension() {
                    Some(result) => {
                        if let Some(ref exts) = self.cfg.exts {
                            if !exts.contains(&result.to_str().unwrap_or("")) {
                                continue;
                            }
                        }
                        result.to_str().unwrap()
                    }
                    None => continue,
                };

                debugln!("found extension: {:?}", extension);
                if let Some(pos_lang) = Language::from_ext(extension) {
                    debugln!("Extension is valid");
                    let mut found = false;
                    debugln!("Searching for previous entries of that type");
                    for l in self.counts.iter_mut() {
                        if l.lang == pos_lang {
                            debugln!("Found");
                            found = true;
                            l.add_file(PathBuf::from(&file));
                            break;
                        }
                    }
                    if !found {
                        debugln!("Not found, creating new");
                        let mut c = Count::new(pos_lang, self.cfg.thousands);
                        c.add_file(PathBuf::from(&file));
                        self.counts.push(c);
                    }
                } else {
                    debugln!("extension wasn't valid");
                }
            }
        }
    }

    #[cfg_attr(feature = "lints", allow(cyclomatic_complexity, trivial_regex))]
    pub fn count(&mut self) -> CliResult<()> {
        for count in self.counts.iter_mut() {
            debugln!("iter; count={:?};", count);
            let re = if let Some(kw) = count.lang.unsafe_keyword() {
                regex!(&*format!("(.*?)([:^word:]{}[:^word:])(.*)", kw))
            } else {
                regex!("")
            };
            for file in count.files.iter() {
                debugln!("iter; file={:?};", file);
                let mut buffer = String::new();

                let mut file_ref = cli_try!(File::open(&file));

                match self.cfg.utf8_rule {
                    Utf8Rule::Ignore => {
                        if let Err(..) = file_ref.read_to_string(&mut buffer) {
                            continue;
                        }
                    }
                    Utf8Rule::Lossy => {
                        let mut vec_buf = vec![];
                        cli_try!(file_ref.read_to_end(&mut vec_buf));
                        buffer = String::from_utf8_lossy(&vec_buf).into_owned();
                    }
                    Utf8Rule::Strict => {
                        cli_try!(file_ref.read_to_string(&mut buffer));
                    }
                }
                let mut is_in_comments = false;
                let mut is_in_unsafe = false;
                let mut bracket_count: i64 = 0;

                'new_line: for line in buffer.lines() {
                    let line = line.trim();
                    debugln!("iter; line={:?};", line);
                    count.lines += 1;

                    if is_in_comments {
                        debugln!("still in comments");
                        if line.contains(count.multi_end().unwrap()) {
                            debugln!("line contained ending comment, stopping comments");
                            is_in_comments = false;
                        }
                        count.comments += 1;
                        continue;
                    }
                    debugln!("not in comments");

                    if line.trim().is_empty() {
                        debugln!("line was empty");
                        count.blanks += 1;
                        continue;
                    }
                    debugln!("Line isn't empty");

                    if let Some(ms) = count.multi_start() {
                        debugln!("This file type has a multi start of: {:?}", ms);
                        if line.starts_with(ms) {
                            debugln!("line starts with multi comment");
                            count.comments += 1;
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            continue;
                        } else if line.contains(ms) {
                            debugln!("line contains a multi start");
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            if is_in_comments {
                                continue;
                            }
                        }
                    } else {
                        debugln!("No multi line comments for this type");
                    }
                    debugln!("No multi line comments for this line");

                    if let Some(single_comments) = count.single() {
                        debugln!("This type has single line comments: {:?}", single_comments);
                        for single in single_comments {
                            if line.starts_with(single) {
                                debugln!("Line started with a comment");
                                count.comments += 1;
                                continue 'new_line;
                            } else {
                                debugln!("Line dind't start with a comment");
                            }
                        }
                    } else {
                        debugln!("No single line comments for this type");
                    }

                    if self.cfg.usafe && count.lang.is_unsafe() {
                        debugln!("Calculating --unsafe-statistics");
                        debugln!("The language is not safe");
                        if let Some(..) = count.lang.unsafe_keyword() {
                            debugln!("There is a keyword");
                            debugln!("line={:?}", line);
                            if is_in_unsafe {
                                debugln!("It didn't contain the keyword, but we are still in \
                                          unsafe");
                                count.usafe += 1;
                                bracket_count = Counts::count_brackets(line, Some(bracket_count));
                                is_in_unsafe = bracket_count > 0;
                                debugln!("after counting brackets; is_in_unsafe={:?}; \
                                          bracket_count={:?}",
                                         is_in_unsafe,
                                         bracket_count);
                            } else if let Some(caps) = re.captures(line) {
                                let mut should_count = true;
                                if let Some(before) = caps.at(1) {
                                    if let Some(single_v) = count.lang.single() {
                                        for s in single_v {
                                            if before.contains(s) {
                                                should_count = false;
                                                break;
                                            }
                                        }
                                    }
                                    if let Some(multi) = count.lang.multi_start() {
                                        if before.contains(multi) &&
                                           !before.contains(count.lang.multi_end().unwrap()) {
                                            should_count = false;
                                        }
                                    }
                                }
                                if should_count {
                                    debugln!("It contained the keyword; usafe_line={:?}", line);
                                    count.usafe += 1;
                                    if let Some(after) = caps.at(3) {
                                        debugln!("after_usafe={:?}", after);
                                        bracket_count = Counts::count_brackets(after, None);
                                        is_in_unsafe = bracket_count > 0;
                                        debugln!("after counting brackets; is_in_unsafe={:?}; \
                                                  bracket_count={:?}",
                                                 is_in_unsafe,
                                                 bracket_count);
                                    }
                                }
                            } else {
                                debugln!("It didn't contain the keyword, and we are not in unsafe");
                            }

                            if bracket_count < 0 {
                                debugln!("bracket_count < 0; resetting");
                                bracket_count = 0
                            }
                        } else {
                            debugln!("Language is unsafe, incing the count");
                            count.usafe += 1;
                        }
                    }
                    count.code += 1;
                }
            }
            self.tot += count.files.len();
            self.tot_lines += count.lines;
            self.tot_comments += count.comments;
            self.tot_blanks += count.blanks;
            self.tot_code += count.code;
            self.tot_usafe += count.usafe;
        }

        Ok(())
    }

    pub fn write_results(&mut self) -> CliResult<()> {
        let mut w = TabWriter::new(vec![]);
        cli_try!(write!(w,
                        "\tLanguage\tFiles\tLines\tBlanks\tComments\tCode{}\n",
                        if self.cfg.usafe {
                            "\tUnsafe (%)"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        for count in &self.counts {
            if self.cfg.usafe {
                let usafe_per = if count.code != 0 {
                    (count.usafe as f64 / count.code as f64) * 100.00f64
                } else {
                    0f64
                };
                cli_try!(write!(w,
                                "\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
                                count.lang.name(),
                                count.total_files(),
                                count.lines(),
                                count.blanks(),
                                count.comments(),
                                count.code(),
                                if (usafe_per - 00f64).abs() < f64::EPSILON {
                                    "".to_owned()
                                } else {
                                    format!("{} ({:.2}%)", count.usafe(), usafe_per)
                                }));
            } else {
                cli_try!(write!(w, "\t{}\n", count));
            }
        }
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "{}\t\t{}\t{}\t{}\t{}\t{}{}\n",
                        "Totals:",
                        fmt::format_number(self.tot as u64, self.cfg.thousands),
                        fmt::format_number(self.tot_lines, self.cfg.thousands),
                        fmt::format_number(self.tot_blanks, self.cfg.thousands),
                        fmt::format_number(self.tot_comments, self.cfg.thousands),
                        fmt::format_number(self.tot_code, self.cfg.thousands),
                        if self.cfg.usafe {
                            format!("\t{} ({:.2}%)",
                                    fmt::format_number(self.tot_usafe, self.cfg.thousands),
                                    (self.tot_usafe as f64 / self.tot_code as f64) * 100.00f64)
                        } else {
                            "".to_owned()
                        }));

        cli_try!(w.flush());

        verboseln!(self.cfg,
                   "{} {}",
                   Format::Good("Displaying"),
                   "the results:");
        if self.tot > 0 {
            write!(io::stdout(),
                   "{}",
                   String::from_utf8(w.unwrap()).ok().expect("failed to get valid UTF-8 String"))
                .expect("failed to write output");
        } else {
            println!("\n\tNo source files were found matching the specified criteria");
        }
        Ok(())
    }

    fn count_brackets(line: &str, count: Option<i64>) -> i64 {
        let mut b: i64 = count.unwrap_or(0);
        for c in line.chars() {
            match c {
                '{' => b += 1,
                '}' => b -= 1,
                _ => (),
            }
        }
        b
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::{ContextOf, AContext};
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, BufferBinding, ContextBufferExt,
             ArrayBufferBinding, ArrayBufferContext,
             ElementArrayBufferBinding, ElementArrayBufferContext};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    pub fn enable<V, C>(&self, gl: &mut C) -> Result<(), AttribError>
        where V: VertexData, C: AContext
    {
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V: VertexData>(&self, gl_buffer: &ArrayBufferBinding)
        -> Result<(), AttribError>
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                gl_buffer.vertex_attrib_pointer(
                    program_attrib,
                    vertex_attrib.ty.components,
                    vertex_attrib.ty.data,
                    vertex_attrib.ty.normalize,
                    vertex_attrib.stride,
                    vertex_attrib.offset
                );
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const VertexBuffer<T>>
}

pub trait ContextVertexBufferExt: AContext {
    fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }

    fn buffer_vertices<T>(&self,
                          gl_vbo: &mut VertexBufferBinding<T>,
                          vertices: &[T],
                          usage: super::BufferDataUsage)
        where T: VertexData, [T]: VertexBytes
    {

        *gl_vbo.count = vertices.len();
        self.buffer_bytes(&mut gl_vbo.gl_buffer,
                          vertices.vertex_bytes(),
                          usage);
    }

    fn buffer_indices<T>(&self,
                         gl_ibo: &mut IndexBufferBinding<T>,
                         indices: &[T],
                         usage: super::BufferDataUsage)
        where T: IndexDatum, [T]: IndexData
    {
        *gl_ibo.count = indices.len();
        self.buffer_bytes(&mut gl_ibo.gl_buffer, indices.index_bytes(), usage);
    }

    fn draw_arrays_range_vbo<V>(&self,
                                gl_vbo: &VertexBufferBinding<V>,
                                mode: DrawingMode,
                                start: u32,
                                length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= *gl_vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    fn draw_arrays_vbo<V>(&self,
                          gl_vbo: &VertexBufferBinding<V>,
                          mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   *gl_vbo.count);
        }
    }

    fn draw_n_elements_buffered_vbo<V, I>(&self,
                                          gl_vbo: &VertexBufferBinding<V>,
                                          gl_ibo: &IndexBufferBinding<I>,
                                          mode: DrawingMode,
                                          length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= *gl_ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    fn draw_elements_buffered_vbo<V, I>(&self,
                                        gl_vbo: &VertexBufferBinding<V>,
                                        gl_ibo: &IndexBufferBinding<I>,
                                        mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          *gl_ibo.count,
                                          I::index_datum_type());
        }
    }

    fn draw_n_elements_vbo<V, I>(&self,
                                 gl_vbo: &VertexBufferBinding<V>,
                                 mode: DrawingMode,
                                 count: usize,
                                 indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    fn draw_elements_vbo<V, I>(&mut self,
                               gl_vbo: &VertexBufferBinding<V>,
                               mode: DrawingMode,
                               indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<C: AContext> ContextVertexBufferExt for C {

}

pub trait VertexBufferContext: ArrayBufferContext + Sized {
    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        // TODO: Cleanup error handling
        let (gl_array_buffer, rest) = match vbo.attrib_binder {
            Some(ref binder) => {
                let buf = &mut vbo.buffer;
                let (gl_buffer, mut rest) = self.bind_array_buffer(buf);
                binder.enable::<V, _>(&mut rest).unwrap();
                binder.bind::<V>(&gl_buffer).unwrap();
                (gl_buffer, rest)
            },
            None => {
                panic!("No attribute bindings provided for vertex buffer");
            }
        };

        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

impl<C: ArrayBufferContext> VertexBufferContext for C {

}

pub trait IndexBufferContext: ElementArrayBufferContext + Sized {
    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum
    {
        let (gl_be, rest) = self.bind_element_array_buffer(&mut ibo.buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                count: &mut ibo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

impl<C: ElementArrayBufferContext> IndexBufferContext for C {

}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const IndexBuffer<T>>
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<commit_msg>Refactor `AttribBinder.bind()`<commit_after>use std::marker::PhantomData;
use std::collections::{HashMap, HashSet};
use context::{ContextOf, AContext};
use program::ProgramAttrib;
use vertex_data::{VertexData, VertexBytes, VertexAttribute};
use index_data::{IndexData, IndexDatum};
use buffer::{Buffer, ContextBufferExt,
             ArrayBufferBinding, ArrayBufferContext,
             ElementArrayBufferBinding, ElementArrayBufferContext};
use types::DrawingMode;

#[derive(Debug)]
pub enum AttribAddError {
    DuplicateAttrib(String)
}

#[derive(Debug)]
pub struct AttribError {
    missing_attribs: Vec<String>,
    unknown_attribs: Vec<String>
}

pub struct AttribBinder {
    attribs: HashMap<String, ProgramAttrib>
}

impl AttribBinder {
    pub fn new() -> Self {
        AttribBinder {
            attribs: HashMap::new()
        }
    }

    pub fn add(&mut self, name: &str, attrib: ProgramAttrib)
        -> Result<(), AttribAddError>
    {
        match self.attribs.insert(name.into(), attrib) {
            None => Ok(()),
            Some(_) => Err(AttribAddError::DuplicateAttrib(name.into()))
        }
    }

    fn for_each<T, F>(&self, mut f: F) -> Result<(), AttribError>
        where T: VertexData, F: FnMut(VertexAttribute, ProgramAttrib)
    {
        // TODO: Avoid heap allocations
        // TODO: Avoid redundant calls to T::visit_attributes
        let mut attribs =
            HashMap::<String, (VertexAttribute, ProgramAttrib)>::new();
        let mut missing = Vec::<String>::new();

        T::visit_attributes(|vertex_attrib| {
            match self.attribs.get(&vertex_attrib.name) {
                Some(program_attrib) => {
                    let pair = (vertex_attrib.clone(), *program_attrib);
                    attribs.insert(vertex_attrib.name, pair);
                },
                None => {
                    missing.push(vertex_attrib.name);
                }
            }
        });

        let unknown: Vec<_> = {
            let expected: HashSet<_> = self.attribs.keys().collect();
            let actual: HashSet<_> = attribs.keys().collect();
            expected.difference(&actual).cloned().cloned().collect()
        };

        if missing.is_empty() && unknown.is_empty() {
            for (_, (vertex_attrib, program_attrib)) in attribs.into_iter() {
                f(vertex_attrib, program_attrib);
            }
            Ok(())
        }
        else {
            Err(AttribError {
                missing_attribs: missing,
                unknown_attribs: unknown
            })
        }
    }

    pub fn enable<V, C>(&self, gl: &mut C) -> Result<(), AttribError>
        where V: VertexData, C: AContext
    {
        self.for_each::<V, _>(|_, program_attrib| {
            gl.enable_vertex_attrib_array(program_attrib);
        })
    }

    pub fn bind<V, C>(&self, gl: &C) -> Result<(), AttribError>
        where V: VertexData, C: AContext
    {
        self.for_each::<V, _>(|vertex_attrib, program_attrib| {
            unsafe {
                // TODO: Refactor!
                // (Make vertex_attrib_pointer take vertex_attrib)
                gl.vertex_attrib_pointer(program_attrib,
                                         vertex_attrib.ty.components,
                                         vertex_attrib.ty.data,
                                         vertex_attrib.ty.normalize,
                                         vertex_attrib.stride,
                                         vertex_attrib.offset);
            }
        })
    }
}

#[derive(Debug)]
pub enum VertexBindError {
    BindingError(AttribError),
    NoAttributeBindings
}

impl From<AttribError> for VertexBindError {
    fn from(attrib_error: AttribError) -> VertexBindError {
        VertexBindError::BindingError(attrib_error)
    }
}

pub struct VertexBuffer<T: VertexData> {
    attrib_binder: Option<AttribBinder>,
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<V: VertexData> VertexBuffer<V> {
    pub fn bind_attrib_pointers(&mut self, binder: AttribBinder) {
        self.attrib_binder = Some(binder);
    }

    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct VertexBufferBinding<'a, T: VertexData + 'a> {
    gl_buffer: ArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const VertexBuffer<T>>
}

pub trait ContextVertexBufferExt: AContext {
    fn new_vertex_buffer<V: VertexData>(&self) -> VertexBuffer<V> {
        VertexBuffer {
            attrib_binder: None,
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }

    fn buffer_vertices<T>(&self,
                          gl_vbo: &mut VertexBufferBinding<T>,
                          vertices: &[T],
                          usage: super::BufferDataUsage)
        where T: VertexData, [T]: VertexBytes
    {

        *gl_vbo.count = vertices.len();
        self.buffer_bytes(&mut gl_vbo.gl_buffer,
                          vertices.vertex_bytes(),
                          usage);
    }

    fn buffer_indices<T>(&self,
                         gl_ibo: &mut IndexBufferBinding<T>,
                         indices: &[T],
                         usage: super::BufferDataUsage)
        where T: IndexDatum, [T]: IndexData
    {
        *gl_ibo.count = indices.len();
        self.buffer_bytes(&mut gl_ibo.gl_buffer, indices.index_bytes(), usage);
    }

    fn draw_arrays_range_vbo<V>(&self,
                                gl_vbo: &VertexBufferBinding<V>,
                                mode: DrawingMode,
                                start: u32,
                                length: usize)
        where V: VertexData
    {
        debug_assert!((start as usize) + length <= *gl_vbo.count);

        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer, mode, start, length);
        }
    }

    fn draw_arrays_vbo<V>(&self,
                          gl_vbo: &VertexBufferBinding<V>,
                          mode: DrawingMode)
        where V: VertexData
    {
        unsafe {
            self.draw_arrays_range(&gl_vbo.gl_buffer,
                                   mode,
                                   0,
                                   *gl_vbo.count);
        }
    }

    fn draw_n_elements_buffered_vbo<V, I>(&self,
                                          gl_vbo: &VertexBufferBinding<V>,
                                          gl_ibo: &IndexBufferBinding<I>,
                                          mode: DrawingMode,
                                          length: usize)
        where V: VertexData, I: IndexDatum
    {
        debug_assert!(length <= *gl_ibo.count);

        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          length,
                                          I::index_datum_type());
        }
    }

    fn draw_elements_buffered_vbo<V, I>(&self,
                                        gl_vbo: &VertexBufferBinding<V>,
                                        gl_ibo: &IndexBufferBinding<I>,
                                        mode: DrawingMode)
        where V: VertexData, I: IndexDatum
    {
        unsafe {
            self.draw_n_elements_buffered(&gl_vbo.gl_buffer,
                                          &gl_ibo.gl_buffer,
                                          mode,
                                          *gl_ibo.count,
                                          I::index_datum_type());
        }
    }

    fn draw_n_elements_vbo<V, I>(&self,
                                 gl_vbo: &VertexBufferBinding<V>,
                                 mode: DrawingMode,
                                 count: usize,
                                 indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_n_elements(&gl_vbo.gl_buffer, mode, count, indices);
        }
    }

    fn draw_elements_vbo<V, I>(&mut self,
                               gl_vbo: &VertexBufferBinding<V>,
                               mode: DrawingMode,
                               indices: &[I])
        where V: VertexData, I: IndexDatum, [I]: IndexData
    {
        unsafe {
            self.draw_elements(&gl_vbo.gl_buffer, mode, indices);
        }
    }
}

impl<C: AContext> ContextVertexBufferExt for C {

}

pub trait VertexBufferContext: ArrayBufferContext + Sized {
    fn bind_vertex_buffer<'a, V>(self, vbo: &'a mut VertexBuffer<V>)
        -> (VertexBufferBinding<V>, Self::Rest)
        where V: VertexData
    {
        // TODO: Cleanup error handling
        let (gl_array_buffer, rest) = match vbo.attrib_binder {
            Some(ref binder) => {
                let buf = &mut vbo.buffer;
                let (gl_buffer, mut rest) = self.bind_array_buffer(buf);
                binder.enable::<V, _>(&mut rest).unwrap();
                binder.bind::<V, _>(&rest).unwrap();
                (gl_buffer, rest)
            },
            None => {
                panic!("No attribute bindings provided for vertex buffer");
            }
        };

        (
            VertexBufferBinding {
                gl_buffer: gl_array_buffer,
                count: &mut vbo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

impl<C: ArrayBufferContext> VertexBufferContext for C {

}

pub trait IndexBufferContext: ElementArrayBufferContext + Sized {
    fn bind_index_buffer<'a, I>(self, ibo: &'a mut IndexBuffer<I>)
        -> (IndexBufferBinding<I>, Self::Rest)
        where I: IndexDatum
    {
        let (gl_be, rest) = self.bind_element_array_buffer(&mut ibo.buffer);
        (
            IndexBufferBinding {
                gl_buffer: gl_be,
                count: &mut ibo.count,
                _phantom: PhantomData
            },
            rest
        )
    }
}

impl<C: ElementArrayBufferContext> IndexBufferContext for C {

}

pub struct IndexBuffer<T: IndexDatum> {
    buffer: Buffer,
    count: usize,
    phantom: PhantomData<*const T>
}

impl<T: IndexDatum> IndexBuffer<T> {
    pub fn buffer(&self) -> &Buffer {
        &self.buffer
    }

    pub fn buffer_mut(&mut self) -> &mut Buffer {
        &mut self.buffer
    }
}

pub struct IndexBufferBinding<'a, T: IndexDatum + 'a> {
    gl_buffer: ElementArrayBufferBinding<'a>,
    count: &'a mut usize,
    _phantom: PhantomData<*const IndexBuffer<T>>
}

impl<B, F, P, R, T> ContextOf<B, F, P, R, T> {
    pub fn new_index_buffer<I: IndexDatum>(&self) -> IndexBuffer<I> {
        IndexBuffer {
            buffer: self.gen_buffer(),
            count: 0,
            phantom: PhantomData
        }
    }
}

#[macro_export]
macro_rules! attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let mut binder = $crate::AttribBinder::new();
            $(binder.add(stringify!($field_name), $field_attrib).unwrap());*;
            binder
        }
    }
}

#[macro_export]
macro_rules! bind_attrib_pointers {
    ($gl:expr, $vbo:expr, {
        $($field_name:ident => $field_attrib:expr),*
    }) => {
        {
            let vbo = $vbo;
            let binder = {
                attrib_pointers!($gl, vbo, {
                    $($field_name => $field_attrib),*
                })
            };
            vbo.bind_attrib_pointers(binder);
        }
    }
}

#[macro_export]
macro_rules! bind_vertex_buffer {
    ($gl:expr, $vbo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut vbo = $vbo;

            let (gl_vbo, _) = $gl.bind_vertex_buffer(vbo.borrow_mut());
            gl_vbo
        }
    }
}

#[macro_export]
macro_rules! bind_index_buffer {
    ($gl:expr, $ibo:expr) => {
        {
            use std::borrow::BorrowMut;
            let mut ibo = $ibo;

            let (gl_ibo, _) = $gl.bind_index_buffer(ibo.borrow_mut());
            gl_ibo
        }
    }
}
<|endoftext|>