<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.

//! Identify those variables whose entire value will eventually be
//! returned from the fn via the RETURN_PLACE. As an optimization, we
//! can skip computing liveness results for those variables. The idea
//! is that the return type of the fn only ever contains free
//! regions. Therefore, the types of those variables are going to
//! ultimately be contrained to outlive those free regions -- since
//! free regions are always live for the entire body, this implies
//! that the liveness results are not important for those regions.
//! This is most important in the "fns" that we create to represent static
//! values, since those are often really quite large, and all regions in them
//! will ultimately be constrained to be `'static`. Two examples:
//!
//! ```
//! fn foo() -> &'static [u32] { &[] }
//! static FOO: &[u32] = &[];
//! ```
//!
//! In both these cases, the return value will only have static lifetime.
//!
//! NB: The simple logic here relies on the fact that outlives
//! relations in our analysis don't have locations. Otherwise, we
//! would have to restrict ourselves to values that are
//! *unconditionally* returned (which would still cover the "big
//! static value" case).
//!
//! The way that this code works is to use union-find -- we iterate
//! over the MIR and union together two variables X and Y if all
//! regions in the value of Y are going to be stored into X -- that
//! is, if `typeof(X): 'a` requires that `typeof(Y): 'a`. This means
//! that e.g. we can union together `x` and `y` if we have something
//! like `x = (y, 22)`, but not something like `x = y.f` (since there
//! may be regions in the type of `y` that do not appear in the field
//! `f`).

use rustc::mir::visit::Visitor;
use rustc::mir::*;
use rustc::ty::TyCtxt;

use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::unify as ut;

crate struct EscapingLocals {
    unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
}

impl EscapingLocals {
    crate fn compute(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self {
        let mut visitor = GatherAssignedLocalsVisitor::new(tcx, mir);
        visitor.visit_mir(mir);

        EscapingLocals {
            unification_table: visitor.unification_table,
        }
    }

    /// True if `local` is known to escape into static
    /// memory.
    crate fn escapes_into_return(&mut self, local: Local) -> bool {
        let return_place = AssignedLocal::from(RETURN_PLACE);
        let other_place = AssignedLocal::from(local);
        self.unification_table.unioned(return_place, other_place)
    }
}

/// The MIR visitor gathering the union-find of the locals used in
/// assignments.
struct GatherAssignedLocalsVisitor<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
    unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
    mir: &'cx Mir<'tcx>,
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
struct AssignedLocal(u32);

impl ut::UnifyKey for AssignedLocal {
    type Value = ();

    fn index(&self) -> u32 {
        self.0
    }

    fn from_index(i: u32) -> AssignedLocal {
        AssignedLocal(i)
    }

    fn tag() -> &'static str {
        "AssignedLocal"
    }
}

impl From<Local> for AssignedLocal {
    fn from(item: Local) -> Self {
        // newtype_indexes use usize but are u32s.
        assert!(item.index() < ::std::u32::MAX as usize);
        AssignedLocal(item.index() as u32)
    }
}

impl GatherAssignedLocalsVisitor<'cx, 'gcx, 'tcx> {
    fn new(tcx: TyCtxt<'cx, 'gcx, 'tcx>, mir: &'cx Mir<'tcx>) -> Self {
        Self {
            unification_table: ut::UnificationTable::new(),
            tcx,
            mir,
        }
    }

    fn union_locals_if_needed(&mut self, lvalue: Option<Local>, rvalue: Option<Local>) {
        if let Some(lvalue) = lvalue {
            if let Some(rvalue) = rvalue {
                if lvalue != rvalue {
                    debug!("EscapingLocals: union {:?} and {:?}", lvalue, rvalue);
                    self.unification_table
                        .union(AssignedLocal::from(lvalue), AssignedLocal::from(rvalue));
                }
            }
        }
    }
}

// Returns the potential `Local` associated to this `Place` or `PlaceProjection`
fn find_local_in_place(place: &Place) -> Option<Local> {
    match place {
        Place::Local(local) => Some(*local),

        // If you do e.g. `x = a.f` then only *part* of the type of
        // `a` escapes into `x` (the part contained in `f`); if `a`'s
        // type has regions that don't appear in `f`, those might not
        // escape.
        Place::Projection(..) => None,

        Place::Static { .. } | Place::Promoted { .. } => None,
    }
}

// Returns the potential `Local` in this `Operand`.
fn find_local_in_operand(op: &Operand) -> Option<Local> {
    // Conservatively check a subset of `Operand`s we know our
    // benchmarks track, for example `html5ever`.
    match op {
        Operand::Copy(place) | Operand::Move(place) => find_local_in_place(place),
        Operand::Constant(_) => None,
    }
}

impl Visitor<'tcx> for GatherAssignedLocalsVisitor<'_, '_, 'tcx> {
    fn visit_mir(&mut self, mir: &Mir<'tcx>) {
        // We need as many union-find keys as there are locals
        for _ in 0..mir.local_decls.len() {
            self.unification_table.new_key(());
        }

        self.super_mir(mir);
    }

    fn visit_assign(
        &mut self,
        block: BasicBlock,
        place: &Place<'tcx>,
        rvalue: &Rvalue<'tcx>,
        location: Location,
    ) {
        let local = find_local_in_place(place);

        // Conservatively check a subset of `Rvalue`s we know our
        // benchmarks track, for example `html5ever`.
        match rvalue {
            Rvalue::Use(op) => self.union_locals_if_needed(local, find_local_in_operand(op)),
            Rvalue::Ref(_, _, place) => {
                // Special case: if you have `X = &*Y` (or `X = &**Y`
                // etc), then the outlives relationships will ensure
                // that all regions in `Y` are constrained by regions
                // in `X` -- this is because the lifetimes of the
                // references we deref through are required to outlive
                // the borrow lifetime (which appears in `X`).
                //
                // (We don't actually need to check the type of `Y`:
                // since `ProjectionElem::Deref` represents a built-in
                // deref and not an overloaded deref, if the thing we
                // deref through is not a reference, then it must be a
                // `Box` or `*const`, in which case it contains no
                // references.)
                let mut place_ref = place;
                while let Place::Projection(proj) = place_ref {
                    if let ProjectionElem::Deref = proj.elem {
                        place_ref = &proj.base;
                    } else {
                        break;
                    }
                }

                self.union_locals_if_needed(local, find_local_in_place(place_ref))
            }

            Rvalue::Cast(kind, op, _) => match kind {
                CastKind::Unsize => self.union_locals_if_needed(local, find_local_in_operand(op)),
                _ => (),
            },

            Rvalue::Aggregate(_, ops) => {
                for rvalue in ops.iter().map(find_local_in_operand) {
                    self.union_locals_if_needed(local, rvalue);
                }
            }

            _ => (),
        };

        self.super_assign(block, place, rvalue, location);
    }
}
<commit_msg>more comments and justify correctness<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.

//! Identify those variables whose entire value will eventually be
//! returned from the fn via the RETURN_PLACE. As an optimization, we
//! can skip computing liveness results for those variables. The idea
//! is that the return type of the fn only ever contains free
//! regions. Therefore, the types of those variables are going to
//! ultimately be contrained to outlive those free regions -- since
//! free regions are always live for the entire body, this implies
//! that the liveness results are not important for those regions.
//! This is most important in the "fns" that we create to represent static
//! values, since those are often really quite large, and all regions in them
//! will ultimately be constrained to be `'static`. Two examples:
//!
//! ```
//! fn foo() -> &'static [u32] { &[] }
//! static FOO: &[u32] = &[];
//! ```
//!
//! In both these cases, the return value will only have static lifetime.
//!
//! NB: The simple logic here relies on the fact that outlives
//! relations in our analysis don't have locations. Otherwise, we
//! would have to restrict ourselves to values that are
//! *unconditionally* returned (which would still cover the "big
//! static value" case).
//!
//! The way that this code works is to use union-find -- we iterate
//! over the MIR and union together two variables X and Y if all
//! regions in the value of Y are going to be stored into X -- that
//! is, if `typeof(X): 'a` requires that `typeof(Y): 'a`. This means
//! that e.g. we can union together `x` and `y` if we have something
//! like `x = (y, 22)`, but not something like `x = y.f` (since there
//! may be regions in the type of `y` that do not appear in the field
//! `f`).

use rustc::mir::visit::Visitor;
use rustc::mir::*;
use rustc::ty::TyCtxt;

use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::unify as ut;

crate struct EscapingLocals {
    unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
}

impl EscapingLocals {
    crate fn compute(tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) -> Self {
        let mut visitor = GatherAssignedLocalsVisitor::new(tcx, mir);
        visitor.visit_mir(mir);

        EscapingLocals {
            unification_table: visitor.unification_table,
        }
    }

    /// True if `local` is known to escape into static
    /// memory.
    crate fn escapes_into_return(&mut self, local: Local) -> bool {
        let return_place = AssignedLocal::from(RETURN_PLACE);
        let other_place = AssignedLocal::from(local);
        self.unification_table.unioned(return_place, other_place)
    }
}

/// The MIR visitor gathering the union-find of the locals used in
/// assignments.
struct GatherAssignedLocalsVisitor<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
    unification_table: ut::UnificationTable<ut::InPlace<AssignedLocal>>,
    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
    mir: &'cx Mir<'tcx>,
}

#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
struct AssignedLocal(u32);

impl ut::UnifyKey for AssignedLocal {
    type Value = ();

    fn index(&self) -> u32 {
        self.0
    }

    fn from_index(i: u32) -> AssignedLocal {
        AssignedLocal(i)
    }

    fn tag() -> &'static str {
        "AssignedLocal"
    }
}

impl From<Local> for AssignedLocal {
    fn from(item: Local) -> Self {
        // newtype_indexes use usize but are u32s.
        assert!(item.index() < ::std::u32::MAX as usize);
        AssignedLocal(item.index() as u32)
    }
}

impl GatherAssignedLocalsVisitor<'cx, 'gcx, 'tcx> {
    fn new(tcx: TyCtxt<'cx, 'gcx, 'tcx>, mir: &'cx Mir<'tcx>) -> Self {
        Self {
            unification_table: ut::UnificationTable::new(),
            tcx,
            mir,
        }
    }

    fn union_locals_if_needed(&mut self, lvalue: Option<Local>, rvalue: Option<Local>) {
        if let Some(lvalue) = lvalue {
            if let Some(rvalue) = rvalue {
                if lvalue != rvalue {
                    debug!("EscapingLocals: union {:?} and {:?}", lvalue, rvalue);
                    self.unification_table
                        .union(AssignedLocal::from(lvalue), AssignedLocal::from(rvalue));
                }
            }
        }
    }
}

// Returns the potential `Local` associated to this `Place` or `PlaceProjection`
fn find_local_in_place(place: &Place) -> Option<Local> {
    match place {
        Place::Local(local) => Some(*local),

        // If you do e.g. `x = a.f` then only *part* of the type of
        // `a` escapes into `x` (the part contained in `f`); if `a`'s
        // type has regions that don't appear in `f`, those might not
        // escape.
        Place::Projection(..) => None,

        Place::Static { .. } | Place::Promoted { .. } => None,
    }
}

// Returns the potential `Local` in this `Operand`.
fn find_local_in_operand(op: &Operand) -> Option<Local> {
    // Conservatively check a subset of `Operand`s we know our
    // benchmarks track, for example `html5ever`.
    match op {
        Operand::Copy(place) | Operand::Move(place) => find_local_in_place(place),
        Operand::Constant(_) => None,
    }
}

impl Visitor<'tcx> for GatherAssignedLocalsVisitor<'_, '_, 'tcx> {
    fn visit_mir(&mut self, mir: &Mir<'tcx>) {
        // We need as many union-find keys as there are locals
        for _ in 0..mir.local_decls.len() {
            self.unification_table.new_key(());
        }

        self.super_mir(mir);
    }

    fn visit_assign(
        &mut self,
        block: BasicBlock,
        place: &Place<'tcx>,
        rvalue: &Rvalue<'tcx>,
        location: Location,
    ) {
        let local = find_local_in_place(place);

        // Find those cases where there is a `Place` consumed by
        // `rvalue` and we know that all regions in its type will be
        // incorporated into `place`, the `Place` we are assigning to.
        match rvalue {
            // `x = y` is the simplest possible case.
            Rvalue::Use(op) => self.union_locals_if_needed(local, find_local_in_operand(op)),

            // `X = &'r P` -- the type of `X` will be `&'r T_P`, where
            // `T_P` is the type of `P`.
            Rvalue::Ref(_, _, place) => {
                // Special case: if you have `X = &*Y` (or `X = &**Y`
                // etc), then the outlives relationships will ensure
                // that all regions in `Y` are constrained by regions
                // in `X` -- this is because the lifetimes of the
                // references we deref through are required to outlive
                // the borrow lifetime `'r` (which appears in `X`).
                //
                // (We don't actually need to check the type of `Y`:
                // since `ProjectionElem::Deref` represents a built-in
                // deref and not an overloaded deref, if the thing we
                // deref through is not a reference, then it must be a
                // `Box` or `*const`, in which case it contains no
                // references.)
                let mut place_ref = place;
                while let Place::Projection(proj) = place_ref {
                    if let ProjectionElem::Deref = proj.elem {
                        place_ref = &proj.base;
                    } else {
                        break;
                    }
                }

                self.union_locals_if_needed(local, find_local_in_place(place_ref))
            }

            Rvalue::Cast(kind, op, _) => match kind {
                CastKind::Unsize => {
                    // Casting a `&[T; N]` to `&[T]` or `&Foo` to `&Trait` --
                    // in both cases, no regions are "lost".
                    self.union_locals_if_needed(local, find_local_in_operand(op))
                }
                _ => (),
            },

            // Constructing an aggregate like `(x,)` or `Foo { x }`
            // includes the full type of `x`.
            Rvalue::Aggregate(_, ops) => {
                for rvalue in ops.iter().map(find_local_in_operand) {
                    self.union_locals_if_needed(local, rvalue);
                }
            }

            // For other things, be conservative and do not union.
            _ => (),
        };

        self.super_assign(block, place, rvalue, location);
    }
}
<|endoftext|>


<commit_before>use std::cast;
use std::c_str::CString;
use std::i32;
use std::libc::*;
use std::num::Zero;
use std::ptr;

use internal::bitmap::*;
use internal::bitmap_like::*;
use internal::color::*;
use internal::core_drawing::*;
use internal::events::*;

use ffi::*;

pub use self::display_flag::*;
pub use rust_util::c_bool;

pub mod external
{
	pub use super::display_flag::*;
	pub use super::{Display, DisplayOption, DisplayOptionImportance, DisplayOrientation, DisplayOptions};
}

flag_type!(
	mod display_flag
	{
		DisplayFlags
		{
			WINDOWED                  = ALLEGRO_WINDOWED,
			FULLSCREEN                = ALLEGRO_FULLSCREEN,
			OPENGL                    = ALLEGRO_OPENGL,
			DIRECT3D                  = ALLEGRO_DIRECT3D_INTERNAL,
			RESIZABLE                 = ALLEGRO_RESIZABLE,
			FRAMELESS                 = ALLEGRO_FRAMELESS,
			GENERATE_EXPOSE_EVENTS    = ALLEGRO_GENERATE_EXPOSE_EVENTS,
			OPENGL_3_0                = ALLEGRO_OPENGL_3_0,
			OPENGL_FORWARD_COMPATIBLE = ALLEGRO_OPENGL_FORWARD_COMPATIBLE,
			FULLSCREEN_WINDOW         = ALLEGRO_FULLSCREEN_WINDOW,
			MINIMIZED                 = ALLEGRO_MINIMIZED
		}
	}
)

pub enum DisplayOption
{
	RedSize = ALLEGRO_RED_SIZE,
	GreenSize = ALLEGRO_GREEN_SIZE,
	BlueSize = ALLEGRO_BLUE_SIZE,
	AlphaSize = ALLEGRO_ALPHA_SIZE,
	RedShift = ALLEGRO_RED_SHIFT,
	GreenShift = ALLEGRO_GREEN_SHIFT,
	BlueShift = ALLEGRO_BLUE_SHIFT,
	AlphaShift = ALLEGRO_ALPHA_SHIFT,
	AccRedSize = ALLEGRO_ACC_RED_SIZE,
	AccGreenSize = ALLEGRO_ACC_GREEN_SIZE,
	AccBlueSize = ALLEGRO_ACC_BLUE_SIZE,
	AccAlphaSize = ALLEGRO_ACC_ALPHA_SIZE,
	Stereo = ALLEGRO_STEREO,
	AuxBuffers = ALLEGRO_AUX_BUFFERS,
	ColorSize = ALLEGRO_COLOR_SIZE,
	DepthSize = ALLEGRO_DEPTH_SIZE,
	StencilSize = ALLEGRO_STENCIL_SIZE,
	SampleBuffers = ALLEGRO_SAMPLE_BUFFERS,
	Samples = ALLEGRO_SAMPLES,
	RenderMethod = ALLEGRO_RENDER_METHOD,
	FloatColor = ALLEGRO_FLOAT_COLOR,
	FloatDepth = ALLEGRO_FLOAT_DEPTH,
	SingleBuffer = ALLEGRO_SINGLE_BUFFER,
	SwapMethod = ALLEGRO_SWAP_METHOD,
	CompatibleDisplay = ALLEGRO_COMPATIBLE_DISPLAY,
	UpdateDisplayRegion = ALLEGRO_UPDATE_DISPLAY_REGION,
	Vsync = ALLEGRO_VSYNC,
	MaxBitmapSize = ALLEGRO_MAX_BITMAP_SIZE,
	SupportNpotBitmap = ALLEGRO_SUPPORT_NPOT_BITMAP,
	CanDrawIntoBitmap = ALLEGRO_CAN_DRAW_INTO_BITMAP,
	SupportSeparateAlpha = ALLEGRO_SUPPORT_SEPARATE_ALPHA,
}

pub enum DisplayOptionImportance
{
	DontCare = ALLEGRO_DONTCARE,
	Require = ALLEGRO_REQUIRE,
	Suggest = ALLEGRO_SUGGEST,
}

pub enum DisplayOrientation
{
	DisplayOrientation0Degrees = ALLEGRO_DISPLAY_ORIENTATION_0_DEGREES,
	DisplayOrientation90Degrees = ALLEGRO_DISPLAY_ORIENTATION_90_DEGREES,
	DisplayOrientation180Degrees = ALLEGRO_DISPLAY_ORIENTATION_180_DEGREES,
	DisplayOrientation270Degrees = ALLEGRO_DISPLAY_ORIENTATION_270_DEGREES,
	DisplayOrientationFaceUp = ALLEGRO_DISPLAY_ORIENTATION_FACE_UP,
	DisplayOrientationFaceDown = ALLEGRO_DISPLAY_ORIENTATION_FACE_DOWN,
}

pub struct DisplayOptions<'m>
{
	flags: DisplayFlags,
	refresh_rate: Option<i32>,
	adapter: Option<i32>,
	window_position: Option<[i32, ..2]>,
	options: Option<&'m [(DisplayOption, i32, DisplayOptionImportance)]>
}

impl<'m> DisplayOptions<'m>
{
	pub fn new() -> DisplayOptions
	{
		DisplayOptions{ flags: Zero::zero(), refresh_rate: None, adapter: None, window_position: None, options: None }
	}
}

pub struct Display
{
	priv allegro_display: *mut ALLEGRO_DISPLAY,
	priv backbuffer: Bitmap,
	priv event_source: EventSource
}

impl Display
{
	fn select_this_display(&self)
	{
		unsafe
		{
			if self.allegro_display != al_get_current_display()
			{
				al_set_target_bitmap(al_get_backbuffer(self.allegro_display))
			}
		}
	}

	fn new(w: i32, h: i32) -> Option<Display>
	{
		unsafe
		{
			let d = al_create_display(w as c_int, h as c_int);
			if ptr::is_null(d)
			{
				None
			}
			else
			{
				Some(Display{ allegro_display: d, backbuffer: new_bitmap_ref(al_get_backbuffer(d)),
							  event_source: new_event_source_ref(al_get_display_event_source(d)) })
			}
		}
	}

	fn new_with_options(w: i32, h: i32, opt: &DisplayOptions) -> Option<Display>
	{
		unsafe
		{
			al_set_new_display_flags(opt.flags.get() as c_int);

			match opt.refresh_rate
			{
				Some(r) => al_set_new_display_refresh_rate(r as c_int),
				None => al_set_new_display_refresh_rate(0)
			}

			match opt.adapter
			{
				Some(a) => al_set_new_display_adapter(a as c_int),
				None => al_set_new_display_adapter(ALLEGRO_DEFAULT_DISPLAY_ADAPTER),
			}

			match opt.window_position
			{
				Some([x, y]) =>	al_set_new_window_position(x as c_int, y as c_int),
				None =>	al_set_new_window_position(i32::max_value, i32::max_value)
			}

			al_reset_new_display_options();

			match opt.options
			{
				Some(options) =>
				{
					for &(option, value, importance) in options.iter()
					{
						al_set_new_display_option(option as c_int, value as c_int, importance as c_int);
					}
				},
				None => ()
			}
		}

		Display::new(w, h)
	}

	pub fn get_width(&self) -> i32
	{
		unsafe
		{
			al_get_display_width(self.allegro_display) as i32
		}
	}

	pub fn get_height(&self) -> i32
	{
		unsafe
		{
			al_get_display_height(self.allegro_display) as i32
		}
	}

	pub fn get_format(&self) -> PixelFormat
	{
		unsafe
		{
			cast::transmute(al_get_display_format(self.allegro_display) as u32)
		}
	}

	pub fn get_refresh_rate(&self) -> i32
	{
		unsafe
		{
			al_get_display_refresh_rate(self.allegro_display) as i32
		}
	}

	pub fn get_flags(&self) -> DisplayFlags
	{
		unsafe
		{
			cast::transmute(al_get_display_flags(self.allegro_display))
		}
	}

	pub fn set_flag(&self, flag: DisplayFlags, onoff: bool) -> bool
	{
		unsafe
		{
			al_set_display_flag(self.allegro_display, flag.get(), onoff as u8) != 0
		}
	}

	pub fn get_backbuffer<'l>(&'l self) -> &'l Bitmap
	{
		&self.backbuffer
	}

	pub fn acknowledge_resize(&self) -> bool
	{
		unsafe
		{
			al_acknowledge_resize(self.allegro_display) != 0
		}
	}

	pub fn resize(&self, w: i32, h: i32) -> bool
	{
		unsafe
		{
			al_resize_display(self.allegro_display, w as c_int, h as c_int) != 0
		}
	}

	pub fn set_icon<T: BitmapLike>(&self, icon: &T)
	{
		unsafe
		{
			al_set_display_icon(self.allegro_display, icon.get_bitmap());
		}
	}

	pub fn set_icons<T: BitmapLike, U: Iterator<T>>(&self, icons: U)
	{
		let mut c_icons = icons.map(|ref b| b.get_bitmap()).to_owned_vec();
		unsafe
		{
			al_set_display_icons(self.allegro_display, c_icons.len() as c_int, c_icons.as_mut_ptr());
		}
	}

	pub fn set_window_position(&self, x: i32, y: i32)
	{
		unsafe
		{
			al_set_window_position(self.allegro_display, x as c_int, y as c_int);
		}
	}

	pub fn get_window_position(&self) -> (i32, i32)
	{
		unsafe
		{
			let mut x = 0 as c_int;
			let mut y = 0 as c_int;
			al_get_window_position(self.allegro_display, &mut x, &mut y);
			(x as i32, y as i32)
		}
	}

	pub fn set_window_title(&self, title: &CString)
	{
		unsafe
		{
			title.with_ref(|c_str|
			{
				al_set_window_title(self.allegro_display, c_str);
			});
		}
	}

	pub fn get_option(&self, option: DisplayOption) -> i32
	{
		unsafe
		{
			al_get_display_option(self.allegro_display, option as c_int) as i32
		}
	}

	pub fn clone_convert<T: BitmapLike>(&self, bmp: &T) -> Option<Bitmap>
	{
		clone_bitmap(bmp.get_bitmap())
	}

	pub fn get_event_source<'l>(&'l self) -> &'l EventSource
	{
		&'l self.event_source
	}

	pub fn get_allegro_display(&self) -> *mut ALLEGRO_DISPLAY
	{
		self.allegro_display
	}

	pub fn flip(&self)
	{
		self.select_this_display();
		unsafe
		{
			al_flip_display();
		}
	}

	pub fn update_region(&self, x: i32, y: i32, width: i32, height: i32)
	{
		self.select_this_display();
		unsafe
		{
			al_update_display_region(x as c_int, y as c_int, width as c_int, height as c_int);
		}
	}

	pub fn is_compatible_bitmap<T: BitmapLike>(&self, bitmap: &T) -> bool
	{
		self.select_this_display();
		unsafe
		{
			al_is_compatible_bitmap(bitmap.get_bitmap()) != 0
		}
	}

	pub fn wait_for_vsync(&self) -> bool
	{
		self.select_this_display();
		unsafe
		{
			al_wait_for_vsync() != 0
		}
	}

	pub fn hold_bitmap_drawing(&self, hold: bool)
	{
		self.select_this_display();
		unsafe
		{
			al_hold_bitmap_drawing(hold as c_bool);
		}
	}

	pub fn is_bitmap_drawing_held(&self) -> bool
	{
		self.select_this_display();
		unsafe
		{
			al_is_bitmap_drawing_held() != 0
		}
	}
}

impl Drop for Display
{
	fn drop(&mut self)
	{
		unsafe
		{
			al_destroy_display(self.allegro_display);
		}
	}
}

impl CoreDrawing for Display {}

impl DrawTarget for Display
{
	fn get_target_bitmap(&self) -> *mut ALLEGRO_BITMAP
	{
		unsafe
		{
			al_get_backbuffer(self.allegro_display)
		}
	}
}

impl ::internal::core::Core
{
	pub fn create_display(&self, w: i32, h: i32) -> Option<Display>
	{
		Display::new(w, h)
	}

	pub fn create_display_with_options(&self, w: i32, h: i32, opt: &DisplayOptions) -> Option<Display>
	{
		Display::new_with_options(w, h, opt)
	}
}
<commit_msg>Rename and fix clone_convert<commit_after>use std::cast;
use std::c_str::CString;
use std::i32;
use std::libc::*;
use std::num::Zero;
use std::ptr;

use internal::bitmap::*;
use internal::bitmap_like::*;
use internal::color::*;
use internal::core_drawing::*;
use internal::events::*;

use ffi::*;

pub use self::display_flag::*;
pub use rust_util::c_bool;

pub mod external
{
	pub use super::display_flag::*;
	pub use super::{Display, DisplayOption, DisplayOptionImportance, DisplayOrientation, DisplayOptions};
}

flag_type!(
	mod display_flag
	{
		DisplayFlags
		{
			WINDOWED                  = ALLEGRO_WINDOWED,
			FULLSCREEN                = ALLEGRO_FULLSCREEN,
			OPENGL                    = ALLEGRO_OPENGL,
			DIRECT3D                  = ALLEGRO_DIRECT3D_INTERNAL,
			RESIZABLE                 = ALLEGRO_RESIZABLE,
			FRAMELESS                 = ALLEGRO_FRAMELESS,
			GENERATE_EXPOSE_EVENTS    = ALLEGRO_GENERATE_EXPOSE_EVENTS,
			OPENGL_3_0                = ALLEGRO_OPENGL_3_0,
			OPENGL_FORWARD_COMPATIBLE = ALLEGRO_OPENGL_FORWARD_COMPATIBLE,
			FULLSCREEN_WINDOW         = ALLEGRO_FULLSCREEN_WINDOW,
			MINIMIZED                 = ALLEGRO_MINIMIZED
		}
	}
)

pub enum DisplayOption
{
	RedSize = ALLEGRO_RED_SIZE,
	GreenSize = ALLEGRO_GREEN_SIZE,
	BlueSize = ALLEGRO_BLUE_SIZE,
	AlphaSize = ALLEGRO_ALPHA_SIZE,
	RedShift = ALLEGRO_RED_SHIFT,
	GreenShift = ALLEGRO_GREEN_SHIFT,
	BlueShift = ALLEGRO_BLUE_SHIFT,
	AlphaShift = ALLEGRO_ALPHA_SHIFT,
	AccRedSize = ALLEGRO_ACC_RED_SIZE,
	AccGreenSize = ALLEGRO_ACC_GREEN_SIZE,
	AccBlueSize = ALLEGRO_ACC_BLUE_SIZE,
	AccAlphaSize = ALLEGRO_ACC_ALPHA_SIZE,
	Stereo = ALLEGRO_STEREO,
	AuxBuffers = ALLEGRO_AUX_BUFFERS,
	ColorSize = ALLEGRO_COLOR_SIZE,
	DepthSize = ALLEGRO_DEPTH_SIZE,
	StencilSize = ALLEGRO_STENCIL_SIZE,
	SampleBuffers = ALLEGRO_SAMPLE_BUFFERS,
	Samples = ALLEGRO_SAMPLES,
	RenderMethod = ALLEGRO_RENDER_METHOD,
	FloatColor = ALLEGRO_FLOAT_COLOR,
	FloatDepth = ALLEGRO_FLOAT_DEPTH,
	SingleBuffer = ALLEGRO_SINGLE_BUFFER,
	SwapMethod = ALLEGRO_SWAP_METHOD,
	CompatibleDisplay = ALLEGRO_COMPATIBLE_DISPLAY,
	UpdateDisplayRegion = ALLEGRO_UPDATE_DISPLAY_REGION,
	Vsync = ALLEGRO_VSYNC,
	MaxBitmapSize = ALLEGRO_MAX_BITMAP_SIZE,
	SupportNpotBitmap = ALLEGRO_SUPPORT_NPOT_BITMAP,
	CanDrawIntoBitmap = ALLEGRO_CAN_DRAW_INTO_BITMAP,
	SupportSeparateAlpha = ALLEGRO_SUPPORT_SEPARATE_ALPHA,
}

pub enum DisplayOptionImportance
{
	DontCare = ALLEGRO_DONTCARE,
	Require = ALLEGRO_REQUIRE,
	Suggest = ALLEGRO_SUGGEST,
}

pub enum DisplayOrientation
{
	DisplayOrientation0Degrees = ALLEGRO_DISPLAY_ORIENTATION_0_DEGREES,
	DisplayOrientation90Degrees = ALLEGRO_DISPLAY_ORIENTATION_90_DEGREES,
	DisplayOrientation180Degrees = ALLEGRO_DISPLAY_ORIENTATION_180_DEGREES,
	DisplayOrientation270Degrees = ALLEGRO_DISPLAY_ORIENTATION_270_DEGREES,
	DisplayOrientationFaceUp = ALLEGRO_DISPLAY_ORIENTATION_FACE_UP,
	DisplayOrientationFaceDown = ALLEGRO_DISPLAY_ORIENTATION_FACE_DOWN,
}

pub struct DisplayOptions<'m>
{
	flags: DisplayFlags,
	refresh_rate: Option<i32>,
	adapter: Option<i32>,
	window_position: Option<[i32, ..2]>,
	options: Option<&'m [(DisplayOption, i32, DisplayOptionImportance)]>
}

impl<'m> DisplayOptions<'m>
{
	pub fn new() -> DisplayOptions
	{
		DisplayOptions{ flags: Zero::zero(), refresh_rate: None, adapter: None, window_position: None, options: None }
	}
}

pub struct Display
{
	priv allegro_display: *mut ALLEGRO_DISPLAY,
	priv backbuffer: Bitmap,
	priv event_source: EventSource
}

impl Display
{
	fn select_this_display(&self)
	{
		unsafe
		{
			if self.allegro_display != al_get_current_display()
			{
				al_set_target_bitmap(al_get_backbuffer(self.allegro_display))
			}
		}
	}

	fn new(w: i32, h: i32) -> Option<Display>
	{
		unsafe
		{
			let d = al_create_display(w as c_int, h as c_int);
			if ptr::is_null(d)
			{
				None
			}
			else
			{
				Some(Display{ allegro_display: d, backbuffer: new_bitmap_ref(al_get_backbuffer(d)),
							  event_source: new_event_source_ref(al_get_display_event_source(d)) })
			}
		}
	}

	fn new_with_options(w: i32, h: i32, opt: &DisplayOptions) -> Option<Display>
	{
		unsafe
		{
			al_set_new_display_flags(opt.flags.get() as c_int);

			match opt.refresh_rate
			{
				Some(r) => al_set_new_display_refresh_rate(r as c_int),
				None => al_set_new_display_refresh_rate(0)
			}

			match opt.adapter
			{
				Some(a) => al_set_new_display_adapter(a as c_int),
				None => al_set_new_display_adapter(ALLEGRO_DEFAULT_DISPLAY_ADAPTER),
			}

			match opt.window_position
			{
				Some([x, y]) =>	al_set_new_window_position(x as c_int, y as c_int),
				None =>	al_set_new_window_position(i32::max_value, i32::max_value)
			}

			al_reset_new_display_options();

			match opt.options
			{
				Some(options) =>
				{
					for &(option, value, importance) in options.iter()
					{
						al_set_new_display_option(option as c_int, value as c_int, importance as c_int);
					}
				},
				None => ()
			}
		}

		Display::new(w, h)
	}

	pub fn get_width(&self) -> i32
	{
		unsafe
		{
			al_get_display_width(self.allegro_display) as i32
		}
	}

	pub fn get_height(&self) -> i32
	{
		unsafe
		{
			al_get_display_height(self.allegro_display) as i32
		}
	}

	pub fn get_format(&self) -> PixelFormat
	{
		unsafe
		{
			cast::transmute(al_get_display_format(self.allegro_display) as u32)
		}
	}

	pub fn get_refresh_rate(&self) -> i32
	{
		unsafe
		{
			al_get_display_refresh_rate(self.allegro_display) as i32
		}
	}

	pub fn get_flags(&self) -> DisplayFlags
	{
		unsafe
		{
			cast::transmute(al_get_display_flags(self.allegro_display))
		}
	}

	pub fn set_flag(&self, flag: DisplayFlags, onoff: bool) -> bool
	{
		unsafe
		{
			al_set_display_flag(self.allegro_display, flag.get(), onoff as u8) != 0
		}
	}

	pub fn get_backbuffer<'l>(&'l self) -> &'l Bitmap
	{
		&self.backbuffer
	}

	pub fn acknowledge_resize(&self) -> bool
	{
		unsafe
		{
			al_acknowledge_resize(self.allegro_display) != 0
		}
	}

	pub fn resize(&self, w: i32, h: i32) -> bool
	{
		unsafe
		{
			al_resize_display(self.allegro_display, w as c_int, h as c_int) != 0
		}
	}

	pub fn set_icon<T: BitmapLike>(&self, icon: &T)
	{
		unsafe
		{
			al_set_display_icon(self.allegro_display, icon.get_bitmap());
		}
	}

	pub fn set_icons<T: BitmapLike, U: Iterator<T>>(&self, icons: U)
	{
		let mut c_icons = icons.map(|ref b| b.get_bitmap()).to_owned_vec();
		unsafe
		{
			al_set_display_icons(self.allegro_display, c_icons.len() as c_int, c_icons.as_mut_ptr());
		}
	}

	pub fn set_window_position(&self, x: i32, y: i32)
	{
		unsafe
		{
			al_set_window_position(self.allegro_display, x as c_int, y as c_int);
		}
	}

	pub fn get_window_position(&self) -> (i32, i32)
	{
		unsafe
		{
			let mut x = 0 as c_int;
			let mut y = 0 as c_int;
			al_get_window_position(self.allegro_display, &mut x, &mut y);
			(x as i32, y as i32)
		}
	}

	pub fn set_window_title(&self, title: &CString)
	{
		unsafe
		{
			title.with_ref(|c_str|
			{
				al_set_window_title(self.allegro_display, c_str);
			});
		}
	}

	pub fn get_option(&self, option: DisplayOption) -> i32
	{
		unsafe
		{
			al_get_display_option(self.allegro_display, option as c_int) as i32
		}
	}

	pub fn convert_bitmap<T: BitmapLike>(&self, bmp: &T) -> Option<Bitmap>
	{
		self.select_this_display();
		clone_bitmap(bmp.get_bitmap())
	}

	pub fn get_event_source<'l>(&'l self) -> &'l EventSource
	{
		&'l self.event_source
	}

	pub fn get_allegro_display(&self) -> *mut ALLEGRO_DISPLAY
	{
		self.allegro_display
	}

	pub fn flip(&self)
	{
		self.select_this_display();
		unsafe
		{
			al_flip_display();
		}
	}

	pub fn update_region(&self, x: i32, y: i32, width: i32, height: i32)
	{
		self.select_this_display();
		unsafe
		{
			al_update_display_region(x as c_int, y as c_int, width as c_int, height as c_int);
		}
	}

	pub fn is_compatible_bitmap<T: BitmapLike>(&self, bitmap: &T) -> bool
	{
		self.select_this_display();
		unsafe
		{
			al_is_compatible_bitmap(bitmap.get_bitmap()) != 0
		}
	}

	pub fn wait_for_vsync(&self) -> bool
	{
		self.select_this_display();
		unsafe
		{
			al_wait_for_vsync() != 0
		}
	}

	pub fn hold_bitmap_drawing(&self, hold: bool)
	{
		self.select_this_display();
		unsafe
		{
			al_hold_bitmap_drawing(hold as c_bool);
		}
	}

	pub fn is_bitmap_drawing_held(&self) -> bool
	{
		self.select_this_display();
		unsafe
		{
			al_is_bitmap_drawing_held() != 0
		}
	}
}

impl Drop for Display
{
	fn drop(&mut self)
	{
		unsafe
		{
			al_destroy_display(self.allegro_display);
		}
	}
}

impl CoreDrawing for Display {}

impl DrawTarget for Display
{
	fn get_target_bitmap(&self) -> *mut ALLEGRO_BITMAP
	{
		unsafe
		{
			al_get_backbuffer(self.allegro_display)
		}
	}
}

impl ::internal::core::Core
{
	pub fn create_display(&self, w: i32, h: i32) -> Option<Display>
	{
		Display::new(w, h)
	}

	pub fn create_display_with_options(&self, w: i32, h: i32, opt: &DisplayOptions) -> Option<Display>
	{
		Display::new_with_options(w, h, opt)
	}
}
<|endoftext|>


<commit_before>use std::rc::Rc;

use error::RuntimeError;

use sys::x11;
use sys::xcb;
use sys::egl;
use sys::gl;

use super::desktop::LinuxDesktop;
use super::super::common::GlInitializer;

pub struct XcbAtoms {
   pub protocols_atom: xcb::Atom,
   pub delete_window_atom: xcb::Atom,
}

pub struct LinuxInitializer {
   pub window: xcb::Window,
   pub atoms: XcbAtoms,
   pub egl: EglInitializer,
   pub gl: GlInitializer,
}

impl LinuxInitializer {
   pub fn new(
      desktop: &LinuxDesktop, title: &str, x: u32, y: u32, width: u32, height: u32
   ) -> Result<Self, RuntimeError> {

      let window = try!(Self::init_window(
         &desktop.connection, &desktop.screen,
         title, x, y, width, height
      ));

      let atoms = try!(Self::init_atoms(&window));

      let egl = try!(EglInitializer::new(&desktop.display, &window));

      let gl = try!(GlInitializer::new(width, height));

      Ok(LinuxInitializer {
         window: window,
         atoms: atoms,
         egl: egl,
         gl: gl,
      })
   }

   #[inline]
   pub fn init_window(
      connection: &Rc<xcb::Connection>, screen: &xcb::Screen,
      title: &str, x: u32, y: u32, width: u32, height: u32
   ) -> Result<xcb::Window, RuntimeError> {

      let window = try!(xcb::Window::create(
         connection, &screen, width, height,
      ));

      try!(window.set_title(title));

      try!(window.map());

      try!(window.position(x, y));

      Ok(window)
   }

   #[inline]
   pub fn init_atoms(window: &xcb::Window) -> Result<XcbAtoms, RuntimeError> {
      let (protocols_atom, delete_window_atom) = try!(window.register_close_event());

      Ok(XcbAtoms {
         protocols_atom: protocols_atom,
         delete_window_atom: delete_window_atom,
      })
   }
}

pub struct EglInitializer {
   pub display: egl::Display,
   pub version: egl::Version,
   pub config: egl::Config,
   pub context: egl::Context,
   pub surface: egl::Surface,
}

impl EglInitializer {
   pub fn new(x11_display: &x11::Display, window: &xcb::Window) -> Result<Self, RuntimeError> {
      try!(Self::bind());

      let display = try!(egl::Display::from_native(x11_display));

      let version = try!(egl::initialize(&display));

      let config = try!(egl::choose_config(&display));

      let context = try!(Self::init_context(&display, &config));

      let surface = try!(Self::init_surface(&display, &config, &context, window));

      Self::init_gl();

      Ok(EglInitializer {
         display: display,
         version: version,
         config: config,
         context: context,
         surface: surface,
      })
   }

   #[inline]
   pub fn bind() -> Result<(), RuntimeError> {
      egl::bind_api(egl::API::OpenGL)
   }

   #[inline]
   pub fn init_context(
      display: &egl::Display, config: &egl::Config
   ) -> Result<egl::Context, RuntimeError> {

      let context = try!(egl::create_context(&display, &config));

      try!(egl::query_context(&display, &context));

      Ok(context)
   }

   #[inline]
   pub fn init_surface(
      display: &egl::Display,
      config: &egl::Config,
      context: &egl::Context,
      window: &xcb::Window
   ) -> Result<egl::Surface, RuntimeError> {

      let surface = try!(egl::create_window_surface(
         display,
         config,
         &window.window_id.id
      ));

      try!(egl::make_current(
         display,
         &surface,
         &surface,
         context
      ));

      try!(egl::swap_interval(display, 0));

      Ok(surface)
   }
}
<commit_msg>Adding back missing init_gl function<commit_after>use std::rc::Rc;

use error::RuntimeError;

use sys::x11;
use sys::xcb;
use sys::egl;
use sys::gl;

use super::desktop::LinuxDesktop;
use super::super::common::GlInitializer;

pub struct XcbAtoms {
   pub protocols_atom: xcb::Atom,
   pub delete_window_atom: xcb::Atom,
}

pub struct LinuxInitializer {
   pub window: xcb::Window,
   pub atoms: XcbAtoms,
   pub egl: EglInitializer,
   pub gl: GlInitializer,
}

impl LinuxInitializer {
   pub fn new(
      desktop: &LinuxDesktop, title: &str, x: u32, y: u32, width: u32, height: u32
   ) -> Result<Self, RuntimeError> {

      let window = try!(Self::init_window(
         &desktop.connection, &desktop.screen,
         title, x, y, width, height
      ));

      let atoms = try!(Self::init_atoms(&window));

      let egl = try!(EglInitializer::new(&desktop.display, &window));

      let gl = try!(GlInitializer::new(width, height));

      Ok(LinuxInitializer {
         window: window,
         atoms: atoms,
         egl: egl,
         gl: gl,
      })
   }

   #[inline]
   pub fn init_window(
      connection: &Rc<xcb::Connection>, screen: &xcb::Screen,
      title: &str, x: u32, y: u32, width: u32, height: u32
   ) -> Result<xcb::Window, RuntimeError> {

      let window = try!(xcb::Window::create(
         connection, &screen, width, height,
      ));

      try!(window.set_title(title));

      try!(window.map());

      try!(window.position(x, y));

      Ok(window)
   }

   #[inline]
   pub fn init_atoms(window: &xcb::Window) -> Result<XcbAtoms, RuntimeError> {
      let (protocols_atom, delete_window_atom) = try!(window.register_close_event());

      Ok(XcbAtoms {
         protocols_atom: protocols_atom,
         delete_window_atom: delete_window_atom,
      })
   }
}

pub struct EglInitializer {
   pub display: egl::Display,
   pub version: egl::Version,
   pub config: egl::Config,
   pub context: egl::Context,
   pub surface: egl::Surface,
}

impl EglInitializer {
   pub fn new(x11_display: &x11::Display, window: &xcb::Window) -> Result<Self, RuntimeError> {
      try!(Self::bind());

      let display = try!(egl::Display::from_native(x11_display));

      let version = try!(egl::initialize(&display));

      let config = try!(egl::choose_config(&display));

      let context = try!(Self::init_context(&display, &config));

      let surface = try!(Self::init_surface(&display, &config, &context, window));

      Self::init_gl();

      Ok(EglInitializer {
         display: display,
         version: version,
         config: config,
         context: context,
         surface: surface,
      })
   }

   #[inline]
   pub fn bind() -> Result<(), RuntimeError> {
      egl::bind_api(egl::API::OpenGL)
   }

   #[inline]
   pub fn init_context(
      display: &egl::Display, config: &egl::Config
   ) -> Result<egl::Context, RuntimeError> {

      let context = try!(egl::create_context(&display, &config));

      try!(egl::query_context(&display, &context));

      Ok(context)
   }

   #[inline]
   pub fn init_surface(
      display: &egl::Display,
      config: &egl::Config,
      context: &egl::Context,
      window: &xcb::Window
   ) -> Result<egl::Surface, RuntimeError> {

      let surface = try!(egl::create_window_surface(
         display,
         config,
         &window.window_id.id
      ));

      try!(egl::make_current(
         display,
         &surface,
         &surface,
         context
      ));

      try!(egl::swap_interval(display, 0));

      Ok(surface)
   }

   #[inline]
   pub fn init_gl() {
      gl::load(egl::Loader::new());
      gl::reset_pixelstore_alignment();
   }
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast;
use syntax::ast::P;
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build accessor functions for each register struct
pub struct BuildAccessors<'a, 'b, 'c> {
  builder: &'a mut Builder,
  cx: &'b ExtCtxt<'c>,
}

impl<'a, 'b, 'c> node::RegVisitor for BuildAccessors<'a, 'b, 'c> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    _width: node::RegWidth, fields: &Vec<node::Field>) {
    if fields.iter().any(|f| f.access != node::WriteOnly) {
      let item = build_get_fn(self.cx, path, reg);
      self.builder.push_item(item);
    }

    for field in fields.iter() {
      match build_field_accessors(self.cx, path, reg, field) {
        Some(item) => self.builder.push_item(item),
        None       => {}
      }
    }
  }
}

impl<'a, 'b, 'c> BuildAccessors<'a, 'b, 'c> {
  pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>)
             -> BuildAccessors<'a, 'b, 'c> {
    BuildAccessors {builder: builder, cx: cx}
  }
}

fn build_field_accessors<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
                             reg: &node::Reg, field: &node::Field)
                             -> Option<P<ast::Item>>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));

  let items = match field.access {
    node::ReadWrite => vec!(build_field_set_fn(cx, path, reg, field),
                            build_field_get_fn(cx, path, reg, field)),
    node::ReadOnly  => vec!(build_field_get_fn(cx, path, reg, field)),
    node::WriteOnly => vec!(build_field_set_fn(cx, path, reg, field)),
    node::SetToClear => vec!(build_field_clear_fn(cx, path, reg, field)),
  };

  let access_tag = match field.access {
    node::ReadWrite => "read/write",
    node::ReadOnly  => "read-only",
    node::WriteOnly => "write-only",
    node::SetToClear => "set-to-clear",
  };

  let field_doc = match field.docstring {
    Some(ref d) => {
      let s = token::get_ident(d.node);
      s.get().into_string()
    },
    None => "no documentation".into_string()
  };
  let docstring = format!("*[{}]* `{}` field: {}",
                          access_tag,
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  quote_item!(cx,
    $doc_attr
    impl $reg_ty {
      $items
    }
  )
}

fn build_get_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>, reg: &node::Reg)
                    -> P<ast::Item>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));
  let getter_ty = utils::getter_name(cx, path);

  let docstring = format!("Fetch the value of the `{}` register",
                          reg.name.node);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    impl $reg_ty {
      $doc_attr
      #[allow(dead_code)]
      pub fn get(&self) -> $getter_ty {
        $getter_ty::new(self)
      }
    }
    );
  item.unwrap()
}

fn build_field_set_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
                          reg: &node::Reg, field: &node::Field)
                          -> P<ast::Method>
{
  let fn_name =
    cx.ident_of((String::from_str("set_")+field.name.node).as_slice());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field), None);
  let setter_ty = utils::setter_name(cx, path);
  if field.count.node == 1 {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self, new_value: $field_ty) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name(new_value);
        setter
      }
    )
  } else {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self, idx: uint, new_value: $field_ty) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name(idx, new_value);
        setter
      }
    )
  }
}

fn build_field_get_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
                          reg: &node::Reg, field: &node::Field)
                          -> P<ast::Method>
{
  let fn_name = cx.ident_of(field.name.node.as_slice());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field), None);
  let getter_ty = utils::getter_name(cx, path);
  if field.count.node == 1 {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name(&self) -> $field_ty {
        $getter_ty::new(self).$fn_name()
      }
    )
  } else {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name(&self, idx: uint) -> $field_ty {
        $getter_ty::new(self).$fn_name(idx)
      }
    )
  }
}

fn build_field_clear_fn<'a>(cx: &'a ExtCtxt, path: &Vec<String>,
                            _reg: &node::Reg, field: &node::Field)
                            -> P<ast::Method>
{
  let fn_name =
    cx.ident_of((String::from_str("clear_")+field.name.node).as_slice());
  let setter_ty = utils::setter_name(cx, path);
  if field.count.node == 1 {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name();
        setter
      }
    )
  } else {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self, idx: uint) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name(idx);
        setter
      }
    )
  }
}
<commit_msg>ioreg/accessors: Clean up redundant lifetimes<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Ben Gamari <bgamari@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast;
use syntax::ast::P;
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::parse::token;

use super::Builder;
use super::utils;
use super::super::node;

/// A visitor to build accessor functions for each register struct
pub struct BuildAccessors<'a, 'b, 'c> {
  builder: &'a mut Builder,
  cx: &'b ExtCtxt<'c>,
}

impl<'a, 'b, 'c> node::RegVisitor for BuildAccessors<'a, 'b, 'c> {
  fn visit_prim_reg(&mut self, path: &Vec<String>, reg: &node::Reg,
                    _width: node::RegWidth, fields: &Vec<node::Field>) {
    if fields.iter().any(|f| f.access != node::WriteOnly) {
      let item = build_get_fn(self.cx, path, reg);
      self.builder.push_item(item);
    }

    for field in fields.iter() {
      match build_field_accessors(self.cx, path, reg, field) {
        Some(item) => self.builder.push_item(item),
        None       => {}
      }
    }
  }
}

impl<'a, 'b, 'c> BuildAccessors<'a, 'b, 'c> {
  pub fn new(builder: &'a mut Builder, cx: &'b ExtCtxt<'c>)
             -> BuildAccessors<'a, 'b, 'c> {
    BuildAccessors {builder: builder, cx: cx}
  }
}

fn build_field_accessors(cx: &ExtCtxt, path: &Vec<String>,
                         reg: &node::Reg, field: &node::Field)
                         -> Option<P<ast::Item>>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));

  let items = match field.access {
    node::ReadWrite => vec!(build_field_set_fn(cx, path, reg, field),
                            build_field_get_fn(cx, path, reg, field)),
    node::ReadOnly  => vec!(build_field_get_fn(cx, path, reg, field)),
    node::WriteOnly => vec!(build_field_set_fn(cx, path, reg, field)),
    node::SetToClear => vec!(build_field_clear_fn(cx, path, reg, field)),
  };

  let access_tag = match field.access {
    node::ReadWrite => "read/write",
    node::ReadOnly  => "read-only",
    node::WriteOnly => "write-only",
    node::SetToClear => "set-to-clear",
  };

  let field_doc = match field.docstring {
    Some(ref d) => {
      let s = token::get_ident(d.node);
      s.get().into_string()
    },
    None => "no documentation".into_string()
  };
  let docstring = format!("*[{}]* `{}` field: {}",
                          access_tag,
                          field.name.node,
                          field_doc);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  quote_item!(cx,
    $doc_attr
    impl $reg_ty {
      $items
    }
  )
}

fn build_get_fn(cx: &ExtCtxt, path: &Vec<String>, reg: &node::Reg)
                -> P<ast::Item>
{
  let reg_ty: P<ast::Ty> =
    cx.ty_ident(DUMMY_SP, utils::path_ident(cx, path));
  let getter_ty = utils::getter_name(cx, path);

  let docstring = format!("Fetch the value of the `{}` register",
                          reg.name.node);
  let doc_attr = utils::doc_attribute(cx, utils::intern_string(cx, docstring));

  let item = quote_item!(cx,
    impl $reg_ty {
      $doc_attr
      #[allow(dead_code)]
      pub fn get(&self) -> $getter_ty {
        $getter_ty::new(self)
      }
    }
    );
  item.unwrap()
}

fn build_field_set_fn(cx: &ExtCtxt, path: &Vec<String>,
                      reg: &node::Reg, field: &node::Field)
                      -> P<ast::Method>
{
  let fn_name =
    cx.ident_of((String::from_str("set_")+field.name.node).as_slice());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field), None);
  let setter_ty = utils::setter_name(cx, path);
  if field.count.node == 1 {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self, new_value: $field_ty) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name(new_value);
        setter
      }
    )
  } else {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self, idx: uint, new_value: $field_ty) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name(idx, new_value);
        setter
      }
    )
  }
}

fn build_field_get_fn(cx: &ExtCtxt, path: &Vec<String>,
                      reg: &node::Reg, field: &node::Field)
                      -> P<ast::Method>
{
  let fn_name = cx.ident_of(field.name.node.as_slice());
  let field_ty: P<ast::Ty> =
    cx.ty_path(utils::field_type_path(cx, path, reg, field), None);
  let getter_ty = utils::getter_name(cx, path);
  if field.count.node == 1 {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name(&self) -> $field_ty {
        $getter_ty::new(self).$fn_name()
      }
    )
  } else {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name(&self, idx: uint) -> $field_ty {
        $getter_ty::new(self).$fn_name(idx)
      }
    )
  }
}

fn build_field_clear_fn(cx: &ExtCtxt, path: &Vec<String>,
                        _reg: &node::Reg, field: &node::Field)
                        -> P<ast::Method>
{
  let fn_name =
    cx.ident_of((String::from_str("clear_")+field.name.node).as_slice());
  let setter_ty = utils::setter_name(cx, path);
  if field.count.node == 1 {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name();
        setter
      }
    )
  } else {
    quote_method!(cx,
      #[allow(dead_code, missing_doc)]
      pub fn $fn_name<'a>(&'a self, idx: uint) -> $setter_ty<'a> {
        let mut setter: $setter_ty = $setter_ty::new(self);
        setter.$fn_name(idx);
        setter
      }
    )
  }
}
<|endoftext|>


<commit_before>use std::mem;

use runtime::{Class, Message, Sel};

pub struct Encoding<T>(pub &'static str);

pub trait Encode {
	fn code() -> Encoding<Self>;
}

impl Encode for i8 {
	fn code() -> Encoding<i8> { Encoding("c") }
}

impl Encode for i16 {
	fn code() -> Encoding<i16> { Encoding("s") }
}

impl Encode for i32 {
	fn code() -> Encoding<i32> { Encoding("i") }
}

impl Encode for i64 {
	fn code() -> Encoding<i64> { Encoding("q") }
}

impl Encode for int {
	fn code() -> Encoding<int> {
		match mem::size_of::<int>() {
			1 => Encoding("c"),
			2 => Encoding("s"),
			4 => Encoding("i"),
			8 => Encoding("q"),
			_ => Encoding("?"),
		}
	}
}

impl Encode for u8 {
	fn code() -> Encoding<u8> { Encoding("C") }
}

impl Encode for u16 {
	fn code() -> Encoding<u16> { Encoding("S") }
}

impl Encode for u32 {
	fn code() -> Encoding<u32> { Encoding("I") }
}

impl Encode for u64 {
	fn code() -> Encoding<u64> { Encoding("Q") }
}

impl Encode for uint {
	fn code() -> Encoding<uint> {
		match mem::size_of::<uint>() {
			1 => Encoding("C"),
			2 => Encoding("S"),
			4 => Encoding("I"),
			8 => Encoding("Q"),
			_ => Encoding("?"),
		}
	}
}

impl Encode for f32 {
	fn code() -> Encoding<f32> { Encoding("f") }
}

impl Encode for f64 {
	fn code() -> Encoding<f64> { Encoding("d") }
}

impl Encode for bool {
	fn code() -> Encoding<bool> { Encoding("B") }
}

impl Encode for () {
	fn code() -> Encoding<()> { Encoding("v") }
}

impl<'a, T: Message> Encode for &'a T {
	fn code() -> Encoding<&'a T> { Encoding("@") }
}

impl<'a, T: Message> Encode for &'a mut T {
	fn code() -> Encoding<&'a mut T> { Encoding("@") }
}

impl<T: Message> Encode for *const T {
	fn code() -> Encoding<*const T> { Encoding("@") }
}

impl<T: Message> Encode for *mut T {
	fn code() -> Encoding<*mut T> { Encoding("@") }
}

impl Encode for Class {
	fn code() -> Encoding<Class> { Encoding("#") }
}

impl Encode for Sel {
	fn code() -> Encoding<Sel> { Encoding(":") }
}

pub fn encode<T: Encode>() -> &'static str {
	let Encoding(code): Encoding<T> = Encode::code();
	code
}

#[cfg(test)]
mod tests {
	use runtime::{Class, Object, Sel};
	use super::encode;

	#[test]
	fn test_encode() {
		assert!(encode::<u32>() == "I");
		assert!(encode::<()>() == "v");
		assert!(encode::<&Object>() == "@");
		assert!(encode::<*mut Object>() == "@");
		assert!(encode::<Class>() == "#");
		assert!(encode::<Sel>() == ":");
	}
}
<commit_msg>Removed incorrect Encode for Class.<commit_after>use std::mem;

use runtime::{Message, Sel};

pub struct Encoding<T>(pub &'static str);

pub trait Encode {
	fn code() -> Encoding<Self>;
}

impl Encode for i8 {
	fn code() -> Encoding<i8> { Encoding("c") }
}

impl Encode for i16 {
	fn code() -> Encoding<i16> { Encoding("s") }
}

impl Encode for i32 {
	fn code() -> Encoding<i32> { Encoding("i") }
}

impl Encode for i64 {
	fn code() -> Encoding<i64> { Encoding("q") }
}

impl Encode for int {
	fn code() -> Encoding<int> {
		match mem::size_of::<int>() {
			1 => Encoding("c"),
			2 => Encoding("s"),
			4 => Encoding("i"),
			8 => Encoding("q"),
			_ => Encoding("?"),
		}
	}
}

impl Encode for u8 {
	fn code() -> Encoding<u8> { Encoding("C") }
}

impl Encode for u16 {
	fn code() -> Encoding<u16> { Encoding("S") }
}

impl Encode for u32 {
	fn code() -> Encoding<u32> { Encoding("I") }
}

impl Encode for u64 {
	fn code() -> Encoding<u64> { Encoding("Q") }
}

impl Encode for uint {
	fn code() -> Encoding<uint> {
		match mem::size_of::<uint>() {
			1 => Encoding("C"),
			2 => Encoding("S"),
			4 => Encoding("I"),
			8 => Encoding("Q"),
			_ => Encoding("?"),
		}
	}
}

impl Encode for f32 {
	fn code() -> Encoding<f32> { Encoding("f") }
}

impl Encode for f64 {
	fn code() -> Encoding<f64> { Encoding("d") }
}

impl Encode for bool {
	fn code() -> Encoding<bool> { Encoding("B") }
}

impl Encode for () {
	fn code() -> Encoding<()> { Encoding("v") }
}

impl<'a, T: Message> Encode for &'a T {
	fn code() -> Encoding<&'a T> { Encoding("@") }
}

impl<'a, T: Message> Encode for &'a mut T {
	fn code() -> Encoding<&'a mut T> { Encoding("@") }
}

impl<T: Message> Encode for *const T {
	fn code() -> Encoding<*const T> { Encoding("@") }
}

impl<T: Message> Encode for *mut T {
	fn code() -> Encoding<*mut T> { Encoding("@") }
}

impl Encode for Sel {
	fn code() -> Encoding<Sel> { Encoding(":") }
}

pub fn encode<T: Encode>() -> &'static str {
	let Encoding(code): Encoding<T> = Encode::code();
	code
}

#[cfg(test)]
mod tests {
	use runtime::{Object, Sel};
	use super::encode;

	#[test]
	fn test_encode() {
		assert!(encode::<u32>() == "I");
		assert!(encode::<()>() == "v");
		assert!(encode::<&Object>() == "@");
		assert!(encode::<*mut Object>() == "@");
		assert!(encode::<Sel>() == ":");
	}
}
<|endoftext|>


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

use std::fmt;
use std::fs::File;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::{Duration, Instant};

use mio::event::Evented;
use mio::unix::{EventedFd, UnixReady};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel;

use gpio::sysfs;

pub use gpio::sysfs::Direction;
pub use gpio::{Level, Trigger};

quick_error! {
/// Errors that can occur while working with interrupts.
    #[derive(Debug)]
    pub enum Error {
/// Synchronous interrupt isn't initialized.
        NotInitialized { description("not initialized") }
/// Time out.
        TimeOut { description("interrupt polling timed out while waiting for a trigger") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Disconnected while sending a control message to the interrupt polling thread.
        SendDisconnected { description("receiving half of the channel has disconnected") }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

impl<T> From<channel::SendError<T>> for Error {
    fn from(err: channel::SendError<T>) -> Error {
        match err {
            channel::SendError::Io(e) => Error::Io(e),
            channel::SendError::Disconnected(_) => Error::SendDisconnected,
        }
    }
}

/// Result type returned from methods that can have `rppal::gpio::interrupt::Error`s.
pub type Result<T> = result::Result<T, Error>;

const TOKEN_RX: usize = 0;
const TOKEN_PIN: usize = 1;

#[derive(Debug)]
struct Interrupt {
    pin: u8,
    trigger: Trigger,
    sysfs_value: File,
}

impl Interrupt {
    fn new(pin: u8, trigger: Trigger) -> Result<Interrupt> {
        // Export the GPIO pin so we can configure it through sysfs, set its mode to
        // input, and set the trigger type.
        sysfs::export(pin)?;
        sysfs::set_direction(pin, Direction::In)?;
        sysfs::set_edge(pin, trigger)?;

        Ok(Interrupt {
            pin,
            trigger,
            sysfs_value: sysfs::open_value(pin)?,
        })
    }

    fn trigger(&self) -> Trigger {
        self.trigger
    }

    fn set_trigger(&mut self, trigger: Trigger) -> Result<()> {
        self.trigger = trigger;
        sysfs::set_edge(self.pin, trigger)?;

        Ok(())
    }

    fn level(&mut self) -> Result<Level> {
        let mut buffer = [0; 1];
        self.sysfs_value.read_exact(&mut buffer)?;
        self.sysfs_value.seek(SeekFrom::Start(0))?;

        match &buffer {
            b"0" => Ok(Level::Low),
            _ => Ok(Level::High),
        }
    }
}

impl Drop for Interrupt {
    fn drop(&mut self) {
        sysfs::unexport(self.pin).ok();
    }
}

impl Evented for Interrupt {
    fn register(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).register(poll, token, interest, opts)
    }

    fn reregister(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).deregister(poll)
    }
}

#[derive(Debug)]
struct TriggerStatus {
    interrupt: Option<Interrupt>,
    triggered: bool,
    level: Level,
}

#[derive(Debug)]
pub struct EventLoop {
    poll: Poll,
    events: Events,
    trigger_status: Vec<TriggerStatus>,
}

impl EventLoop {
    pub fn new(capacity: usize) -> Result<EventLoop> {
        let mut trigger_status = Vec::with_capacity(capacity);

        // Initialize trigger_status while circumventing the Copy/Clone requirement
        for _ in 0..trigger_status.capacity() {
            trigger_status.push(TriggerStatus {
                interrupt: None,
                triggered: false,
                level: Level::Low,
            });
        }

        Ok(EventLoop {
            poll: Poll::new()?,
            events: Events::with_capacity(capacity),
            trigger_status,
        })
    }

    pub fn poll(
        &mut self,
        pins: &[u8],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<(u8, Level)> {
        for pin in pins {
            if *pin as usize >= self.trigger_status.capacity() {
                return Err(Error::NotInitialized);
            }

            // Did we cache any trigger events during the previous poll?
            if self.trigger_status[*pin as usize].triggered {
                self.trigger_status[*pin as usize].triggered = false;

                if !reset {
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // Read the logic level to reset any pending trigger events
            if let Some(ref mut interrupt) = self.trigger_status[*pin as usize].interrupt {
                if reset {
                    interrupt.level()?;
                }
            }
        }

        // Loop until we get any of the events we're waiting for, or a timeout occurs
        let now = Instant::now();
        loop {
            self.poll.poll(&mut self.events, timeout)?;

            // No events means a timeout occurred
            if self.events.is_empty() {
                return Err(Error::TimeOut);
            }

            for event in &self.events {
                if event.token().0 < self.trigger_status.capacity()
                    && event.readiness().is_readable()
                    && UnixReady::from(event.readiness()).is_error()
                {
                    self.trigger_status[event.token().0].triggered = true;
                    self.trigger_status[event.token().0].level = if let Some(ref mut interrupt) =
                        self.trigger_status[event.token().0].interrupt
                    {
                        interrupt.level()?
                    } else {
                        Level::Low
                    };
                }
            }

            // Were any interrupts triggered? If so, return one. The rest
            // will be saved for the next poll.
            for pin in pins {
                if self.trigger_status[*pin as usize].triggered {
                    self.trigger_status[*pin as usize].triggered = false;
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // It's possible a pin we're not waiting for continuously triggers
            // an interrupt, causing repeated loops with calls to poll() using a
            // reset timeout value. Make sure we haven't been looping longer than
            // the requested timeout.
            if let Some(t) = timeout {
                if now.elapsed() > t {
                    return Err(Error::TimeOut);
                }
            }
        }
    }

    pub fn set_interrupt(&mut self, pin: u8, trigger: Trigger) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        // Interrupt already exists. We just need to change the trigger.
        if let Some(ref mut interrupt) = self.trigger_status[pin as usize].interrupt {
            if interrupt.trigger != trigger {
                interrupt.set_trigger(trigger)?;
            }

            return Ok(());
        }

        // Register a new interrupt
        let mut base = Interrupt::new(pin, trigger)?;

        base.level()?;
        self.poll.register(
            &base,
            Token(pin as usize),
            Ready::readable() | UnixReady::error(),
            PollOpt::edge(),
        )?;

        self.trigger_status[pin as usize].interrupt = Some(base);

        Ok(())
    }

    pub fn clear_interrupt(&mut self, pin: u8) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        if let Some(interrupt) = self.trigger_status[pin as usize].interrupt.take() {
            self.poll.deregister(&interrupt)?;
        }

        Ok(())
    }
}

enum ControlMsg {
    Stop,
}

pub struct AsyncInterrupt {
    pin: u8,
    poll_thread: Option<thread::JoinHandle<Result<()>>>,
    tx: channel::Sender<ControlMsg>,
}

impl AsyncInterrupt {
    pub fn new<C>(pin: u8, trigger: Trigger, mut callback: C) -> Result<AsyncInterrupt>
    where
        C: FnMut(Level) + Send + 'static,
    {
        let (tx, rx) = channel::channel();

        let poll_thread = thread::spawn(move || -> Result<()> {
            let poll = Poll::new()?;
            let mut events = Events::with_capacity(2);

            poll.register(&rx, Token(TOKEN_RX), Ready::readable(), PollOpt::edge())?;

            let mut base = Interrupt::new(pin, trigger)?;
            base.level()?;
            poll.register(
                &base,
                Token(TOKEN_PIN),
                Ready::readable() | UnixReady::error(),
                PollOpt::edge(),
            )?;

            loop {
                poll.poll(&mut events, None)?;

                for event in &events {
                    if event.token() == Token(TOKEN_RX) {
                        match rx.try_recv() {
                            Ok(ControlMsg::Stop) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Disconnected) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Empty) => {
                                break;
                            }
                        }
                    } else if event.token() == Token(TOKEN_PIN)
                        && event.readiness().is_readable()
                        && UnixReady::from(event.readiness()).is_error()
                    {
                        let interrupt_value = base.level()?;

                        callback(interrupt_value);
                    }
                }
            }
        });

        Ok(AsyncInterrupt {
            pin,
            poll_thread: Some(poll_thread),
            tx,
        })
    }

    pub fn stop(&mut self) -> Result<()> {
        self.tx.send(ControlMsg::Stop)?;

        if let Some(poll_thread) = self.poll_thread.take() {
            match poll_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for AsyncInterrupt {
    fn drop(&mut self) {
        // Unexport the pin here, because we can't rely on the thread
        // living long enough to unexport it.
        sysfs::unexport(self.pin).ok();
    }
}

impl fmt::Debug for AsyncInterrupt {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("AsyncInterrupt")
            .field("pin", &self.pin)
            .field("poll_thread", &self.poll_thread)
            .field("tx", &"")
            .finish()
    }
}
<commit_msg>Update debug formatting<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

use std::fmt;
use std::fs::File;
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::{Duration, Instant};

use mio::event::Evented;
use mio::unix::{EventedFd, UnixReady};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio_extras::channel;

use gpio::sysfs;

pub use gpio::sysfs::Direction;
pub use gpio::{Level, Trigger};

quick_error! {
/// Errors that can occur while working with interrupts.
    #[derive(Debug)]
    pub enum Error {
/// Synchronous interrupt isn't initialized.
        NotInitialized { description("not initialized") }
/// Time out.
        TimeOut { description("interrupt polling timed out while waiting for a trigger") }
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// Disconnected while sending a control message to the interrupt polling thread.
        SendDisconnected { description("receiving half of the channel has disconnected") }
/// Interrupt polling thread panicked.
        ThreadPanic { description("interrupt polling thread panicked") }
    }
}

impl<T> From<channel::SendError<T>> for Error {
    fn from(err: channel::SendError<T>) -> Error {
        match err {
            channel::SendError::Io(e) => Error::Io(e),
            channel::SendError::Disconnected(_) => Error::SendDisconnected,
        }
    }
}

/// Result type returned from methods that can have `rppal::gpio::interrupt::Error`s.
pub type Result<T> = result::Result<T, Error>;

const TOKEN_RX: usize = 0;
const TOKEN_PIN: usize = 1;

#[derive(Debug)]
struct Interrupt {
    pin: u8,
    trigger: Trigger,
    sysfs_value: File,
}

impl Interrupt {
    fn new(pin: u8, trigger: Trigger) -> Result<Interrupt> {
        // Export the GPIO pin so we can configure it through sysfs, set its mode to
        // input, and set the trigger type.
        sysfs::export(pin)?;
        sysfs::set_direction(pin, Direction::In)?;
        sysfs::set_edge(pin, trigger)?;

        Ok(Interrupt {
            pin,
            trigger,
            sysfs_value: sysfs::open_value(pin)?,
        })
    }

    fn trigger(&self) -> Trigger {
        self.trigger
    }

    fn set_trigger(&mut self, trigger: Trigger) -> Result<()> {
        self.trigger = trigger;
        sysfs::set_edge(self.pin, trigger)?;

        Ok(())
    }

    fn level(&mut self) -> Result<Level> {
        let mut buffer = [0; 1];
        self.sysfs_value.read_exact(&mut buffer)?;
        self.sysfs_value.seek(SeekFrom::Start(0))?;

        match &buffer {
            b"0" => Ok(Level::Low),
            _ => Ok(Level::High),
        }
    }
}

impl Drop for Interrupt {
    fn drop(&mut self) {
        sysfs::unexport(self.pin).ok();
    }
}

impl Evented for Interrupt {
    fn register(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).register(poll, token, interest, opts)
    }

    fn reregister(
        &self,
        poll: &Poll,
        token: Token,
        interest: Ready,
        opts: PollOpt,
    ) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).reregister(poll, token, interest, opts)
    }

    fn deregister(&self, poll: &Poll) -> io::Result<()> {
        EventedFd(&self.sysfs_value.as_raw_fd()).deregister(poll)
    }
}

#[derive(Debug)]
struct TriggerStatus {
    interrupt: Option<Interrupt>,
    triggered: bool,
    level: Level,
}

#[derive(Debug)]
pub struct EventLoop {
    poll: Poll,
    events: Events,
    trigger_status: Vec<TriggerStatus>,
}

impl EventLoop {
    pub fn new(capacity: usize) -> Result<EventLoop> {
        let mut trigger_status = Vec::with_capacity(capacity);

        // Initialize trigger_status while circumventing the Copy/Clone requirement
        for _ in 0..trigger_status.capacity() {
            trigger_status.push(TriggerStatus {
                interrupt: None,
                triggered: false,
                level: Level::Low,
            });
        }

        Ok(EventLoop {
            poll: Poll::new()?,
            events: Events::with_capacity(capacity),
            trigger_status,
        })
    }

    pub fn poll(
        &mut self,
        pins: &[u8],
        reset: bool,
        timeout: Option<Duration>,
    ) -> Result<(u8, Level)> {
        for pin in pins {
            if *pin as usize >= self.trigger_status.capacity() {
                return Err(Error::NotInitialized);
            }

            // Did we cache any trigger events during the previous poll?
            if self.trigger_status[*pin as usize].triggered {
                self.trigger_status[*pin as usize].triggered = false;

                if !reset {
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // Read the logic level to reset any pending trigger events
            if let Some(ref mut interrupt) = self.trigger_status[*pin as usize].interrupt {
                if reset {
                    interrupt.level()?;
                }
            }
        }

        // Loop until we get any of the events we're waiting for, or a timeout occurs
        let now = Instant::now();
        loop {
            self.poll.poll(&mut self.events, timeout)?;

            // No events means a timeout occurred
            if self.events.is_empty() {
                return Err(Error::TimeOut);
            }

            for event in &self.events {
                if event.token().0 < self.trigger_status.capacity()
                    && event.readiness().is_readable()
                    && UnixReady::from(event.readiness()).is_error()
                {
                    self.trigger_status[event.token().0].triggered = true;
                    self.trigger_status[event.token().0].level = if let Some(ref mut interrupt) =
                        self.trigger_status[event.token().0].interrupt
                    {
                        interrupt.level()?
                    } else {
                        Level::Low
                    };
                }
            }

            // Were any interrupts triggered? If so, return one. The rest
            // will be saved for the next poll.
            for pin in pins {
                if self.trigger_status[*pin as usize].triggered {
                    self.trigger_status[*pin as usize].triggered = false;
                    return Ok((*pin, self.trigger_status[*pin as usize].level));
                }
            }

            // It's possible a pin we're not waiting for continuously triggers
            // an interrupt, causing repeated loops with calls to poll() using a
            // reset timeout value. Make sure we haven't been looping longer than
            // the requested timeout.
            if let Some(t) = timeout {
                if now.elapsed() > t {
                    return Err(Error::TimeOut);
                }
            }
        }
    }

    pub fn set_interrupt(&mut self, pin: u8, trigger: Trigger) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        // Interrupt already exists. We just need to change the trigger.
        if let Some(ref mut interrupt) = self.trigger_status[pin as usize].interrupt {
            if interrupt.trigger != trigger {
                interrupt.set_trigger(trigger)?;
            }

            return Ok(());
        }

        // Register a new interrupt
        let mut base = Interrupt::new(pin, trigger)?;

        base.level()?;
        self.poll.register(
            &base,
            Token(pin as usize),
            Ready::readable() | UnixReady::error(),
            PollOpt::edge(),
        )?;

        self.trigger_status[pin as usize].interrupt = Some(base);

        Ok(())
    }

    pub fn clear_interrupt(&mut self, pin: u8) -> Result<()> {
        self.trigger_status[pin as usize].triggered = false;

        if let Some(interrupt) = self.trigger_status[pin as usize].interrupt.take() {
            self.poll.deregister(&interrupt)?;
        }

        Ok(())
    }
}

enum ControlMsg {
    Stop,
}

pub struct AsyncInterrupt {
    pin: u8,
    poll_thread: Option<thread::JoinHandle<Result<()>>>,
    tx: channel::Sender<ControlMsg>,
}

impl AsyncInterrupt {
    pub fn new<C>(pin: u8, trigger: Trigger, mut callback: C) -> Result<AsyncInterrupt>
    where
        C: FnMut(Level) + Send + 'static,
    {
        let (tx, rx) = channel::channel();

        let poll_thread = thread::spawn(move || -> Result<()> {
            let poll = Poll::new()?;
            let mut events = Events::with_capacity(2);

            poll.register(&rx, Token(TOKEN_RX), Ready::readable(), PollOpt::edge())?;

            let mut base = Interrupt::new(pin, trigger)?;
            base.level()?;
            poll.register(
                &base,
                Token(TOKEN_PIN),
                Ready::readable() | UnixReady::error(),
                PollOpt::edge(),
            )?;

            loop {
                poll.poll(&mut events, None)?;

                for event in &events {
                    if event.token() == Token(TOKEN_RX) {
                        match rx.try_recv() {
                            Ok(ControlMsg::Stop) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Disconnected) => {
                                return Ok(());
                            }
                            Err(TryRecvError::Empty) => {
                                break;
                            }
                        }
                    } else if event.token() == Token(TOKEN_PIN)
                        && event.readiness().is_readable()
                        && UnixReady::from(event.readiness()).is_error()
                    {
                        let interrupt_value = base.level()?;

                        callback(interrupt_value);
                    }
                }
            }
        });

        Ok(AsyncInterrupt {
            pin,
            poll_thread: Some(poll_thread),
            tx,
        })
    }

    pub fn stop(&mut self) -> Result<()> {
        self.tx.send(ControlMsg::Stop)?;

        if let Some(poll_thread) = self.poll_thread.take() {
            match poll_thread.join() {
                Ok(r) => return r,
                Err(_) => return Err(Error::ThreadPanic),
            }
        }

        Ok(())
    }
}

impl Drop for AsyncInterrupt {
    fn drop(&mut self) {
        // Unexport the pin here, because we can't rely on the thread
        // living long enough to unexport it.
        sysfs::unexport(self.pin).ok();
    }
}

impl fmt::Debug for AsyncInterrupt {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("AsyncInterrupt")
            .field("pin", &self.pin)
            .field("poll_thread", &self.poll_thread)
            .field("tx", &())
            .finish()
    }
}
<|endoftext|>


<commit_before>use crate::gc::swiper::CardIdx;
use crate::gc::swiper::{CARD_REFS, CARD_SIZE, CARD_SIZE_BITS};
use crate::gc::{Address, Region};
use crate::mem;
use crate::object::offset_of_array_data;

// see GC Handbook 11.8: Crossing Maps
// meaning of byte value
//
// 0 <= v < 64: [0-63] FirstObject
//     first object starts v words after card start
//     no references before first object
//
// 64: NoRefs
//     no references in this card
//
// 64 < v <= 128: [1-64] LeadingRefs
//     there are (v-64) references before first object
//
// 128 < v <= 130: [1-2] ArrayStart
//     object starts (v-128) words before this card
//     used only for object arrays when array starts in the card before
//     and some non-reference content is in the next card

#[derive(Clone)]
pub struct CrossingMap {
    // boundaries for crossing map
    start: Address,
    end: Address,
    cards_in_heap: usize,
}

impl CrossingMap {
    pub fn new(start: Address, end: Address, heap_size: usize) -> CrossingMap {
        CrossingMap {
            start,
            end,
            cards_in_heap: heap_size / CARD_SIZE,
        }
    }

    pub fn set_no_references(&self, card: CardIdx) {
        self.set(card, 64);
    }

    pub fn set_first_object(&self, card: CardIdx, words: usize) {
        assert!(words < 64);
        self.set(card, words as u8);
    }

    pub fn set_array_start(&self, card: CardIdx, words: usize) {
        assert!(words == 1 || words == 2);
        self.set(card, (128 + words) as u8);
    }

    pub fn set_references_at_start(&self, card: CardIdx, refs: usize) {
        assert!(refs > 0 && refs <= 64);
        self.set(card, 64 + (refs as u8));
    }

    fn set(&self, card: CardIdx, val: u8) {
        let card = self.index_from_card_idx(card);

        unsafe {
            *self.start.offset(card).to_mut_ptr::<u8>() = val;
        }
    }

    pub fn get(&self, card: CardIdx) -> CrossingEntry {
        let card = self.index_from_card_idx(card);

        let val = unsafe { *self.start.offset(card).to_ptr::<u8>() };

        if val < 64 {
            CrossingEntry::FirstObject(val)
        } else if val == 64 {
            CrossingEntry::NoRefs
        } else if val <= 128 {
            CrossingEntry::LeadingRefs(val - 64)
        } else {
            assert!(val == 129 || val == 130);
            CrossingEntry::ArrayStart(val - 128)
        }
    }

    #[inline(always)]
    fn index_from_card_idx(&self, card: CardIdx) -> usize {
        let card = card.to_usize();
        assert!(card < self.cards_in_heap);
        card
    }

    pub fn update(
        &self,
        old_total: Region,
        object_start: Address,
        object_end: Address,
        array_ref: bool,
    ) {
        debug_assert!(old_total.valid_top(object_start) && old_total.valid_top(object_end));

        if (object_start.to_usize() >> CARD_SIZE_BITS) == (object_end.to_usize() >> CARD_SIZE_BITS)
        {
            // object does not span multiple cards
        } else if array_ref {
            let end_card_idx = card_idx(object_end, old_total.start);
            let end_card_addr = card_address(end_card_idx, old_total.start);

            let start_card_idx = card_idx(object_start, old_total.start);
            let start_card_end = card_address(start_card_idx, old_total.start).offset(CARD_SIZE);

            let mut loop_card_start = start_card_idx + 1;

            // If you allocate an object array just before the card end,
            // it could happen that the card starts with part of the header
            // or the length-field.
            if object_start.offset(offset_of_array_data() as usize) > start_card_end {
                let diff = start_card_end.offset_from(object_start) / mem::ptr_width_usize();
                self.set_array_start(loop_card_start.into(), diff);

                loop_card_start += 1;
            }

            // all cards between ]start_card; end_card[ are full with references
            for c in loop_card_start..end_card_idx {
                self.set_references_at_start(c.into(), CARD_REFS);
            }

            // end_card starts with x references, then next object
            if end_card_idx >= loop_card_start && object_end < old_total.end {
                if object_end == end_card_addr {
                    self.set_first_object(end_card_idx.into(), 0);
                } else {
                    let refs_dist = object_end.offset_from(end_card_addr) / mem::ptr_width_usize();
                    self.set_references_at_start(end_card_idx.into(), refs_dist);
                }
            }
        } else {
            let end_card_idx = card_idx(object_end, old_total.start);
            let end_card_addr = card_address(end_card_idx, old_total.start);

            let start_card_idx = card_idx(object_start, old_total.start);

            // all cards between ]start_card; end_card[ are set to NoRefs
            for c in start_card_idx + 1..end_card_idx {
                self.set_no_references(c.into());
            }

            // end_card stores x words of object, then next object
            if object_end < old_total.end {
                self.set_first_object(
                    end_card_idx.into(),
                    object_end.offset_from(end_card_addr) / mem::ptr_width_usize(),
                );
            }
        }
    }
}

fn card_idx(address: Address, start: Address) -> usize {
    address.offset_from(start) >> CARD_SIZE_BITS
}

fn card_address(card: usize, start: Address) -> Address {
    start.offset(card << CARD_SIZE_BITS)
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CrossingEntry {
    NoRefs,
    LeadingRefs(u8),
    FirstObject(u8),
    ArrayStart(u8),
}
<commit_msg>swiper: make most crossing functions private<commit_after>use crate::gc::swiper::CardIdx;
use crate::gc::swiper::{CARD_REFS, CARD_SIZE, CARD_SIZE_BITS};
use crate::gc::{Address, Region};
use crate::mem;
use crate::object::offset_of_array_data;

// see GC Handbook 11.8: Crossing Maps
// meaning of byte value
//
// 0 <= v < 64: [0-63] FirstObject
//     first object starts v words after card start
//     no references before first object
//
// 64: NoRefs
//     no references in this card
//
// 64 < v <= 128: [1-64] LeadingRefs
//     there are (v-64) references before first object
//
// 128 < v <= 130: [1-2] ArrayStart
//     object starts (v-128) words before this card
//     used only for object arrays when array starts in the card before
//     and some non-reference content is in the next card

#[derive(Clone)]
pub struct CrossingMap {
    // boundaries for crossing map
    start: Address,
    end: Address,
    cards_in_heap: usize,
}

impl CrossingMap {
    pub fn new(start: Address, end: Address, heap_size: usize) -> CrossingMap {
        CrossingMap {
            start,
            end,
            cards_in_heap: heap_size / CARD_SIZE,
        }
    }

    fn set_no_references(&self, card: CardIdx) {
        self.set(card, 64);
    }

    pub fn set_first_object(&self, card: CardIdx, words: usize) {
        assert!(words < 64);
        self.set(card, words as u8);
    }

    fn set_array_start(&self, card: CardIdx, words: usize) {
        assert!(words == 1 || words == 2);
        self.set(card, (128 + words) as u8);
    }

    fn set_references_at_start(&self, card: CardIdx, refs: usize) {
        assert!(refs > 0 && refs <= 64);
        self.set(card, 64 + (refs as u8));
    }

    fn set(&self, card: CardIdx, val: u8) {
        let card = self.index_from_card_idx(card);

        unsafe {
            *self.start.offset(card).to_mut_ptr::<u8>() = val;
        }
    }

    pub fn get(&self, card: CardIdx) -> CrossingEntry {
        let card = self.index_from_card_idx(card);

        let val = unsafe { *self.start.offset(card).to_ptr::<u8>() };

        if val < 64 {
            CrossingEntry::FirstObject(val)
        } else if val == 64 {
            CrossingEntry::NoRefs
        } else if val <= 128 {
            CrossingEntry::LeadingRefs(val - 64)
        } else {
            assert!(val == 129 || val == 130);
            CrossingEntry::ArrayStart(val - 128)
        }
    }

    #[inline(always)]
    fn index_from_card_idx(&self, card: CardIdx) -> usize {
        let card = card.to_usize();
        assert!(card < self.cards_in_heap);
        card
    }

    pub fn update(
        &self,
        old_total: Region,
        object_start: Address,
        object_end: Address,
        array_ref: bool,
    ) {
        debug_assert!(old_total.valid_top(object_start) && old_total.valid_top(object_end));

        if (object_start.to_usize() >> CARD_SIZE_BITS) == (object_end.to_usize() >> CARD_SIZE_BITS)
        {
            // object does not span multiple cards
        } else if array_ref {
            let end_card_idx = card_idx(object_end, old_total.start);
            let end_card_addr = card_address(end_card_idx, old_total.start);

            let start_card_idx = card_idx(object_start, old_total.start);
            let start_card_end = card_address(start_card_idx, old_total.start).offset(CARD_SIZE);

            let mut loop_card_start = start_card_idx + 1;

            // If you allocate an object array just before the card end,
            // it could happen that the card starts with part of the header
            // or the length-field.
            if object_start.offset(offset_of_array_data() as usize) > start_card_end {
                let diff = start_card_end.offset_from(object_start) / mem::ptr_width_usize();
                self.set_array_start(loop_card_start.into(), diff);

                loop_card_start += 1;
            }

            // all cards between ]start_card; end_card[ are full with references
            for c in loop_card_start..end_card_idx {
                self.set_references_at_start(c.into(), CARD_REFS);
            }

            // end_card starts with x references, then next object
            if end_card_idx >= loop_card_start && object_end < old_total.end {
                if object_end == end_card_addr {
                    self.set_first_object(end_card_idx.into(), 0);
                } else {
                    let refs_dist = object_end.offset_from(end_card_addr) / mem::ptr_width_usize();
                    self.set_references_at_start(end_card_idx.into(), refs_dist);
                }
            }
        } else {
            let end_card_idx = card_idx(object_end, old_total.start);
            let end_card_addr = card_address(end_card_idx, old_total.start);

            let start_card_idx = card_idx(object_start, old_total.start);

            // all cards between ]start_card; end_card[ are set to NoRefs
            for c in start_card_idx + 1..end_card_idx {
                self.set_no_references(c.into());
            }

            // end_card stores x words of object, then next object
            if object_end < old_total.end {
                self.set_first_object(
                    end_card_idx.into(),
                    object_end.offset_from(end_card_addr) / mem::ptr_width_usize(),
                );
            }
        }
    }
}

fn card_idx(address: Address, start: Address) -> usize {
    address.offset_from(start) >> CARD_SIZE_BITS
}

fn card_address(card: usize, start: Address) -> Address {
    start.offset(card << CARD_SIZE_BITS)
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CrossingEntry {
    NoRefs,
    LeadingRefs(u8),
    FirstObject(u8),
    ArrayStart(u8),
}
<|endoftext|>