<commit_before>// Copyright 2014 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.

//! Scoped thread-local storage
//!
//! This module provides the ability to generate *scoped* thread-local
//! variables. In this sense, scoped indicates that thread local storage
//! actually stores a reference to a value, and this reference is only placed
//! in storage for a scoped amount of time.
//!
//! There are no restrictions on what types can be placed into a scoped
//! variable, but all scoped variables are initialized to the equivalent of
//! null. Scoped thread local storage is useful when a value is present for a known
//! period of time and it is not required to relinquish ownership of the
//! contents.
//!
//! # Example
//!
//! ```
//! scoped_thread_local!(static FOO: uint);
//!
//! // Initially each scoped slot is empty.
//! assert!(!FOO.is_set());
//!
//! // When inserting a value, the value is only in place for the duration
//! // of the closure specified.
//! FOO.set(&1, || {
//!     FOO.with(|slot| {
//!         assert_eq!(*slot, 1);
//!     });
//! });
//! ```

#![macro_escape]

use prelude::*;

// macro hygiene sure would be nice, wouldn't it?
#[doc(hidden)] pub use self::imp::KeyInner;
#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT;

/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
///
/// Keys are statically allocated and can contain a reference to an instance of
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
/// and `with`, both of which currently use closures to control the scope of
/// their contents.
pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> }

/// Declare a new scoped thread local storage key.
///
/// This macro declares a `static` item on which methods are used to get and
/// set the value stored within.
#[macro_export]
macro_rules! scoped_thread_local {
    (static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(static $name: $t)
    );
    (pub static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(pub static $name: $t)
    );
}

#[macro_export]
#[doc(hidden)]
macro_rules! __scoped_thread_local_inner {
    (static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")),
                   thread_local)]
        static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    (pub static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")),
                   thread_local)]
        pub static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    ($t:ty) => ({
        use std::thread_local::scoped::Key as __Key;

        #[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
        const INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::KeyInner {
                inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
            }
        };

        #[cfg(any(windows, target_os = "android", target_os = "ios"))]
        const INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::KeyInner {
                inner: ::std::thread_local::scoped::OS_INIT,
                marker: ::std::kinds::marker::InvariantType,
            }
        };

        INIT
    })
}

impl<T> Key<T> {
    /// Insert a value into this scoped thread local storage slot for a
    /// duration of a closure.
    ///
    /// While `cb` is running, the value `t` will be returned by `get` unless
    /// this function is called recursively inside of `cb`.
    ///
    /// Upon return, this function will restore the previous value, if any
    /// was available.
    ///
    /// # Example
    ///
    /// ```
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.set(&100, || {
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    ///
    ///     // set can be called recursively
    ///     FOO.set(&101, || {
    ///         // ...
    ///     });
    ///
    ///     // Recursive calls restore the previous value.
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    /// });
    /// ```
    pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where
        F: FnOnce() -> R,
    {
        struct Reset<'a, T: 'a> {
            key: &'a KeyInner<T>,
            val: *mut T,
        }
        #[unsafe_destructor]
        impl<'a, T> Drop for Reset<'a, T> {
            fn drop(&mut self) {
                unsafe { self.key.set(self.val) }
            }
        }

        let prev = unsafe {
            let prev = self.inner.get();
            self.inner.set(t as *const T as *mut T);
            prev
        };

        let _reset = Reset { key: &self.inner, val: prev };
        cb()
    }

    /// Get a value out of this scoped variable.
    ///
    /// This function takes a closure which receives the value of this
    /// variable.
    ///
    /// # Panics
    ///
    /// This function will panic if `set` has not previously been called.
    ///
    /// # Example
    ///
    /// ```no_run
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.with(|slot| {
    ///     // work with `slot`
    /// });
    /// ```
    pub fn with<R, F>(&'static self, cb: F) -> R where
        F: FnOnce(&T) -> R
    {
        unsafe {
            let ptr = self.inner.get();
            assert!(!ptr.is_null(), "cannot access a scoped thread local \
                                     variable without calling `set` first");
            cb(&*ptr)
        }
    }

    /// Test whether this TLS key has been `set` for the current thread.
    pub fn is_set(&'static self) -> bool {
        unsafe { !self.inner.get().is_null() }
    }
}

#[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
mod imp {
    use std::cell::UnsafeCell;

    #[doc(hidden)]
    pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> }

    unsafe impl<T> ::kinds::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { *self.inner.get() }
    }
}

#[cfg(any(windows, target_os = "android", target_os = "ios"))]
mod imp {
    use kinds::marker;
    use sys_common::thread_local::StaticKey as OsStaticKey;

    #[doc(hidden)]
    pub struct KeyInner<T> {
        pub inner: OsStaticKey,
        pub marker: marker::InvariantType<T>,
    }

    unsafe impl<T> ::kinds::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ }
    }
}

#[cfg(test)]
mod tests {
    use cell::Cell;
    use prelude::*;

    #[test]
    fn smoke() {
        scoped_thread_local!(static BAR: uint);

        assert!(!BAR.is_set());
        BAR.set(&1, || {
            assert!(BAR.is_set());
            BAR.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!BAR.is_set());
    }

    #[test]
    fn cell_allowed() {
        scoped_thread_local!(static BAR: Cell<uint>);

        BAR.set(&Cell::new(1), || {
            BAR.with(|slot| {
                assert_eq!(slot.get(), 1);
            });
        });
    }
}
<commit_msg>src/libstd/thread_local/scoped.rs: fixes scoped_thread_local! macro<commit_after>// Copyright 2014 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.

//! Scoped thread-local storage
//!
//! This module provides the ability to generate *scoped* thread-local
//! variables. In this sense, scoped indicates that thread local storage
//! actually stores a reference to a value, and this reference is only placed
//! in storage for a scoped amount of time.
//!
//! There are no restrictions on what types can be placed into a scoped
//! variable, but all scoped variables are initialized to the equivalent of
//! null. Scoped thread local storage is useful when a value is present for a known
//! period of time and it is not required to relinquish ownership of the
//! contents.
//!
//! # Example
//!
//! ```
//! scoped_thread_local!(static FOO: uint);
//!
//! // Initially each scoped slot is empty.
//! assert!(!FOO.is_set());
//!
//! // When inserting a value, the value is only in place for the duration
//! // of the closure specified.
//! FOO.set(&1, || {
//!     FOO.with(|slot| {
//!         assert_eq!(*slot, 1);
//!     });
//! });
//! ```

#![macro_escape]

use prelude::*;

// macro hygiene sure would be nice, wouldn't it?
#[doc(hidden)] pub use self::imp::KeyInner;
#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT;

/// Type representing a thread local storage key corresponding to a reference
/// to the type parameter `T`.
///
/// Keys are statically allocated and can contain a reference to an instance of
/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
/// and `with`, both of which currently use closures to control the scope of
/// their contents.
pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> }

/// Declare a new scoped thread local storage key.
///
/// This macro declares a `static` item on which methods are used to get and
/// set the value stored within.
#[macro_export]
macro_rules! scoped_thread_local {
    (static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(static $name: $t);
    );
    (pub static $name:ident: $t:ty) => (
        __scoped_thread_local_inner!(pub static $name: $t);
    );
}

#[macro_export]
#[doc(hidden)]
macro_rules! __scoped_thread_local_inner {
    (static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")),
                   thread_local)]
        static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    (pub static $name:ident: $t:ty) => (
        #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")),
                   thread_local)]
        pub static $name: ::std::thread_local::scoped::Key<$t> =
            __scoped_thread_local_inner!($t);
    );
    ($t:ty) => ({
        use std::thread_local::scoped::Key as __Key;

        #[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
        const INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::KeyInner {
                inner: ::std::cell::UnsafeCell { value: 0 as *mut _ },
            }
        };

        #[cfg(any(windows, target_os = "android", target_os = "ios"))]
        const INIT: __Key<$t> = __Key {
            inner: ::std::thread_local::scoped::KeyInner {
                inner: ::std::thread_local::scoped::OS_INIT,
                marker: ::std::kinds::marker::InvariantType,
            }
        };

        INIT
    })
}

impl<T> Key<T> {
    /// Insert a value into this scoped thread local storage slot for a
    /// duration of a closure.
    ///
    /// While `cb` is running, the value `t` will be returned by `get` unless
    /// this function is called recursively inside of `cb`.
    ///
    /// Upon return, this function will restore the previous value, if any
    /// was available.
    ///
    /// # Example
    ///
    /// ```
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.set(&100, || {
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    ///
    ///     // set can be called recursively
    ///     FOO.set(&101, || {
    ///         // ...
    ///     });
    ///
    ///     // Recursive calls restore the previous value.
    ///     let val = FOO.with(|v| *v);
    ///     assert_eq!(val, 100);
    /// });
    /// ```
    pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where
        F: FnOnce() -> R,
    {
        struct Reset<'a, T: 'a> {
            key: &'a KeyInner<T>,
            val: *mut T,
        }
        #[unsafe_destructor]
        impl<'a, T> Drop for Reset<'a, T> {
            fn drop(&mut self) {
                unsafe { self.key.set(self.val) }
            }
        }

        let prev = unsafe {
            let prev = self.inner.get();
            self.inner.set(t as *const T as *mut T);
            prev
        };

        let _reset = Reset { key: &self.inner, val: prev };
        cb()
    }

    /// Get a value out of this scoped variable.
    ///
    /// This function takes a closure which receives the value of this
    /// variable.
    ///
    /// # Panics
    ///
    /// This function will panic if `set` has not previously been called.
    ///
    /// # Example
    ///
    /// ```no_run
    /// scoped_thread_local!(static FOO: uint);
    ///
    /// FOO.with(|slot| {
    ///     // work with `slot`
    /// });
    /// ```
    pub fn with<R, F>(&'static self, cb: F) -> R where
        F: FnOnce(&T) -> R
    {
        unsafe {
            let ptr = self.inner.get();
            assert!(!ptr.is_null(), "cannot access a scoped thread local \
                                     variable without calling `set` first");
            cb(&*ptr)
        }
    }

    /// Test whether this TLS key has been `set` for the current thread.
    pub fn is_set(&'static self) -> bool {
        unsafe { !self.inner.get().is_null() }
    }
}

#[cfg(not(any(windows, target_os = "android", target_os = "ios")))]
mod imp {
    use std::cell::UnsafeCell;

    #[doc(hidden)]
    pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> }

    unsafe impl<T> ::kinds::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { *self.inner.get() }
    }
}

#[cfg(any(windows, target_os = "android", target_os = "ios"))]
mod imp {
    use kinds::marker;
    use sys_common::thread_local::StaticKey as OsStaticKey;

    #[doc(hidden)]
    pub struct KeyInner<T> {
        pub inner: OsStaticKey,
        pub marker: marker::InvariantType<T>,
    }

    unsafe impl<T> ::kinds::Sync for KeyInner<T> { }

    #[doc(hidden)]
    impl<T> KeyInner<T> {
        #[doc(hidden)]
        pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) }
        #[doc(hidden)]
        pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ }
    }
}

#[cfg(test)]
mod tests {
    use cell::Cell;
    use prelude::*;

    scoped_thread_local!(static FOO: uint);

    #[test]
    fn smoke() {
        scoped_thread_local!(static BAR: uint);

        assert!(!BAR.is_set());
        BAR.set(&1, || {
            assert!(BAR.is_set());
            BAR.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!BAR.is_set());
    }

    #[test]
    fn cell_allowed() {
        scoped_thread_local!(static BAR: Cell<uint>);

        BAR.set(&Cell::new(1), || {
            BAR.with(|slot| {
                assert_eq!(slot.get(), 1);
            });
        });
    }

    #[test]
    fn scope_item_allowed() {
        assert!(!FOO.is_set());
        FOO.set(&1, || {
            assert!(FOO.is_set());
            FOO.with(|slot| {
                assert_eq!(*slot, 1);
            });
        });
        assert!(!FOO.is_set());
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 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.

//! The compiler code necessary to implement the `#[deriving]` extensions.
//!
//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
//! the standard library, and "std" is the core library.

use ast::{Item, MetaItem, MetaList, MetaNameValue, MetaWord};
use ext::base::ExtCtxt;
use codemap::Span;
use ptr::P;

pub mod bounds;
pub mod clone;
pub mod encodable;
pub mod decodable;
pub mod hash;
pub mod rand;
pub mod show;
pub mod zero;
pub mod default;
pub mod primitive;

#[path="cmp/eq.rs"]
pub mod eq;
#[path="cmp/totaleq.rs"]
pub mod totaleq;
#[path="cmp/ord.rs"]
pub mod ord;
#[path="cmp/totalord.rs"]
pub mod totalord;

pub mod generic;

pub fn expand_meta_deriving(cx: &mut ExtCtxt,
                            _span: Span,
                            mitem: &MetaItem,
                            item: &Item,
                            push: |P<Item>|) {
    match mitem.node {
        MetaNameValue(_, ref l) => {
            cx.span_err(l.span, "unexpected value in `deriving`");
        }
        MetaWord(_) => {
            cx.span_warn(mitem.span, "empty trait list in `deriving`");
        }
        MetaList(_, ref titems) if titems.len() == 0 => {
            cx.span_warn(mitem.span, "empty trait list in `deriving`");
        }
        MetaList(_, ref titems) => {
            for titem in titems.iter().rev() {
                match titem.node {
                    MetaNameValue(ref tname, _) |
                    MetaList(ref tname, _) |
                    MetaWord(ref tname) => {
                        macro_rules! expand(($func:path) => ($func(cx, titem.span,
                                                                   &**titem, item,
                                                                   |i| push(i))));
                        match tname.get() {
                            "Clone" => expand!(clone::expand_deriving_clone),

                            "Hash" => expand!(hash::expand_deriving_hash),

                            "Encodable" => expand!(encodable::expand_deriving_encodable),
                            "Decodable" => expand!(decodable::expand_deriving_decodable),

                            "PartialEq" => expand!(eq::expand_deriving_eq),
                            "Eq" => expand!(totaleq::expand_deriving_totaleq),
                            "PartialOrd" => expand!(ord::expand_deriving_ord),
                            "Ord" => expand!(totalord::expand_deriving_totalord),

                            "Rand" => expand!(rand::expand_deriving_rand),

                            "Show" => expand!(show::expand_deriving_show),

                            "Zero" => expand!(zero::expand_deriving_zero),
                            "Default" => expand!(default::expand_deriving_default),

                            "FromPrimitive" => expand!(primitive::expand_deriving_from_primitive),

                            "Send" => expand!(bounds::expand_deriving_bound),
                            "Sync" => expand!(bounds::expand_deriving_bound),
                            "Copy" => expand!(bounds::expand_deriving_bound),

                            ref tname => {
                                cx.span_err(titem.span,
                                            format!("unknown `deriving` \
                                                     trait: `{}`",
                                                    *tname).as_slice());
                            }
                        };
                    }
                }
            }
        }
    }
}
<commit_msg>rollup merge of #19755: alexcrichton/rust-serialize<commit_after>// Copyright 2012-2013 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.

//! The compiler code necessary to implement the `#[deriving]` extensions.
//!
//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
//! the standard library, and "std" is the core library.

use ast::{Item, MetaItem, MetaList, MetaNameValue, MetaWord};
use ext::base::ExtCtxt;
use codemap::Span;
use ptr::P;

pub mod bounds;
pub mod clone;
pub mod encodable;
pub mod decodable;
pub mod hash;
pub mod rand;
pub mod show;
pub mod zero;
pub mod default;
pub mod primitive;

#[path="cmp/eq.rs"]
pub mod eq;
#[path="cmp/totaleq.rs"]
pub mod totaleq;
#[path="cmp/ord.rs"]
pub mod ord;
#[path="cmp/totalord.rs"]
pub mod totalord;

pub mod generic;

pub fn expand_meta_deriving(cx: &mut ExtCtxt,
                            _span: Span,
                            mitem: &MetaItem,
                            item: &Item,
                            push: |P<Item>|) {
    match mitem.node {
        MetaNameValue(_, ref l) => {
            cx.span_err(l.span, "unexpected value in `deriving`");
        }
        MetaWord(_) => {
            cx.span_warn(mitem.span, "empty trait list in `deriving`");
        }
        MetaList(_, ref titems) if titems.len() == 0 => {
            cx.span_warn(mitem.span, "empty trait list in `deriving`");
        }
        MetaList(_, ref titems) => {
            for titem in titems.iter().rev() {
                match titem.node {
                    MetaNameValue(ref tname, _) |
                    MetaList(ref tname, _) |
                    MetaWord(ref tname) => {
                        macro_rules! expand(($func:path) => ($func(cx, titem.span,
                                                                   &**titem, item,
                                                                   |i| push(i))));
                        match tname.get() {
                            "Clone" => expand!(clone::expand_deriving_clone),

                            "Hash" => expand!(hash::expand_deriving_hash),

                            "RustcEncodable" => {
                                expand!(encodable::expand_deriving_encodable)
                            }
                            "RustcDecodable" => {
                                expand!(decodable::expand_deriving_decodable)
                            }
                            "Encodable" => {
                                // NOTE: uncomment after a stage0 snap
                                // cx.span_warn(titem.span,
                                //              "deriving(Encodable) is deprecated \
                                //               in favor of deriving(RustcEncodable)");

                                expand!(encodable::expand_deriving_encodable)
                            }
                            "Decodable" =>
                                // NOTE: uncomment after a stage0 snap
                                // cx.span_warn(titem.span,
                                //              "deriving(Decodable) is deprecated \
                                //               in favor of deriving(RustcDecodable)");

                                expand!(decodable::expand_deriving_decodable)
                            }

                            "PartialEq" => expand!(eq::expand_deriving_eq),
                            "Eq" => expand!(totaleq::expand_deriving_totaleq),
                            "PartialOrd" => expand!(ord::expand_deriving_ord),
                            "Ord" => expand!(totalord::expand_deriving_totalord),

                            "Rand" => expand!(rand::expand_deriving_rand),

                            "Show" => expand!(show::expand_deriving_show),

                            "Zero" => expand!(zero::expand_deriving_zero),
                            "Default" => expand!(default::expand_deriving_default),

                            "FromPrimitive" => expand!(primitive::expand_deriving_from_primitive),

                            "Send" => expand!(bounds::expand_deriving_bound),
                            "Sync" => expand!(bounds::expand_deriving_bound),
                            "Copy" => expand!(bounds::expand_deriving_bound),

                            ref tname => {
                                cx.span_err(titem.span,
                                            format!("unknown `deriving` \
                                                     trait: `{}`",
                                                    *tname).as_slice());
                            }
                        };
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Johannes Köster, Vadim Nazarov.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Various alignment algorithms.

pub mod pairwise;
pub mod distance;

/// Alignment operations (Match, Subst, Del and Ins).
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AlignmentOperation {
    Match,
    Subst,
    Del,
    Ins
}

/// An alignment, consisting of a score, a start in sequence y, a start in sequence x, a length
/// and its edit operations (see alignment::pairwise for meaning of x and y).
#[derive(Debug)]
pub struct Alignment {
    pub score: i32,
    pub ystart: usize,
    pub xstart: usize,
    pub xlen: usize,
    pub operations: Vec<AlignmentOperation>
}

impl Alignment {
    /// Calculate the cigar string.
    pub fn cigar(&self, hard_clip: bool) -> String {
        let add_op = |op, k, cigar: &mut String| {
            cigar.push_str(&format!("{}{}", k, match op {
                AlignmentOperation::Match => "=",
                AlignmentOperation::Subst => "X",
                AlignmentOperation::Del => "D",
                AlignmentOperation::Ins => "I",
            }));
        };

        let op_len = |op: AlignmentOperation| {
            (op == AlignmentOperation::Match || op == AlignmentOperation::Subst || op == AlignmentOperation::Ins) as usize
        };

        let clip_str = if hard_clip {"H"} else {"S"};

        let mut cigar = String::new();

        if !self.operations.is_empty() {
            if self.xstart > 0 {
                cigar.push_str(&format!("{}{}", self.xstart, clip_str));
            }

            let mut last = self.operations[0];
            let mut k = 1;
            let mut alen = op_len(last);
            for &op in self.operations[1..].iter() {
                if op == last {
                    k += 1;
                } else {
                    add_op(last, k, &mut cigar);
                    k = 1;
                }
                last = op;
                alen += op_len(op);
            }
            add_op(last, k, &mut cigar);

            let clip = self.xlen - alen;
            if clip > 0 {
                cigar.push_str(&format!("{}{}", clip, clip_str));
            }
        }
        else {
            cigar.push_str(&format!("{}{}", self.xlen, clip_str));
        }

        cigar
    }

    /// Return the pretty formatted alignment as a String.
    pub fn pretty(&self, x: &[u8], y: &[u8]) -> String {
        let mut x_pretty = String::new();
        let mut y_pretty = String::new();
        let mut inb_pretty = String::new();

        if !self.operations.is_empty() {
            let mut x_i : usize = self.xstart;
            let mut y_i : usize = self.ystart;

            // Add '-' before aligned subsequences and un-aligned 5' substrings of sequences.
            if x_i > y_i {
                let diff = x_i - y_i;
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                for _ in 0..diff {
                    y_pretty.push('-');
                }
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..y_i])));
            } else if x_i < y_i {
                let diff = y_i - x_i;
                for _ in 0..diff {
                    x_pretty.push('-');
                }
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&y[0..y_i])));
            } else {
                for i in 0..x_i {
                    x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
                    y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
                }
            }
            for _ in 0..x_pretty.len() {
                inb_pretty.push(' ');
            }

            // Process the alignment.
            for i in 0..self.operations.len() {
                match self.operations[i] {
                    AlignmentOperation::Match => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push_str("|");

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Subst => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Del => {
                        x_pretty.push('-');

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Ins => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push('-');
                    },
                }
            }

            // Add un-aligned 3' substrings of sequences.
            for i in x_i..x.len() {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
            }
            for i in y_i..y.len() {
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
            }

            // Add trailing '-'.
            if x_pretty.len() > y_pretty.len() {
                for _ in y_pretty.len()..x_pretty.len() {
                    y_pretty.push('-');
                }
            } else {
                for _ in x_pretty.len()..y_pretty.len() {
                    x_pretty.push('-');
                }
            }
        }

        format!("{}\n{}\n{}", x_pretty, inb_pretty, y_pretty)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::AlignmentOperation::*;
    use super::pairwise::*;

    #[test]
    fn test_cigar() {
        let alignment = Alignment { score: 5, xstart: 3, ystart: 0, xlen: 10, operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del] };
        assert_eq!(alignment.cigar(false), "3S3=1X2I2D4S");
    }

    #[test]
    fn test_pretty_alignment() {
        let y = b"TACCGTGGAC";
        let x = b"AAAAACCGTTGACGCAA";
        let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
        // alignment.operations: [Match, Match, Match, Match, Match, Subst, Match, Match, Match]
        let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
        let alignment = aligner.semiglobal(x, y);
        // let mut alignment = align_semiglobal!(x, y, -5, -1, score);
        // assert_eq!(alignment.pretty(x, y), align_semiglobal_pretty!(x, y, -5, -1, score));
        println!("semiglobal:\t{:?}", alignment);
        println!("SEMIGLOBAL: \n{}\n", alignment.pretty(x, y));
        // alignment.operations: [Match, Match, Match, Match, Match, Subst, Match, Match, Match]
        // alignment = align_local!(x, y, -5, -1, score);
        let alignment = aligner.local(x, y);
        println!("local:\t{:?}", alignment);
        println!("LOCAL: \n{}\n", alignment.pretty(x, y));
        // assert_eq!(alignment.pretty(x, y), align_local_pretty!(x, y, -5, -1, score));
        // alignment.operations: [Del, Del, Del, Del, Match, Match, Match, Match, Match, Subst, Match, Match, Match]
        // alignment = align_global!(x, y, -5, -1, score);
        let alignment = aligner.global(x, y);
        println!("global:\t{:?}", alignment);
        println!("GLOBAL: \n{}\n", alignment.pretty(x, y));

        // let x = b"AAAAAAA";
        // let y = b"TTTTTTTTTT";
        // let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
        // let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
        // let alignment = aligner.semiglobal(x, y);
        // println!("SEMIGLOBAL: \n{}\n", alignment.pretty(x, y));
        // let alignment = aligner.local(x, y);
        // println!("LOCAL: \n{}\n", alignment.pretty(x, y));
        // let alignment = aligner.global(x, y);
        // println!("GLOBAL: \n{}\n", alignment.pretty(x, y));
        // assert_eq!(alignment.pretty(x, y), align_global_pretty!(x, y, -5, -1, score));
    }

    // fn test_pretty_alignment() {
    //     let x = b"TACCGTGGAC";
    //     let y = b"AAAAACCGTTGACGCAA";
    //     let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
    //     let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
    //     let alignment = aligner.semiglobal(x, y);
    //     let alignment = aligner.local(x, y);
    //     let alignment = aligner.global(x, y);
    // }
}
<commit_msg>add unfinished docs for pretty()<commit_after>// Copyright 2014-2015 Johannes Köster, Vadim Nazarov.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Various alignment algorithms.

pub mod pairwise;
pub mod distance;

/// Alignment operations (Match, Subst, Del and Ins).
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AlignmentOperation {
    Match,
    Subst,
    Del,
    Ins
}

/// An alignment, consisting of a score, a start in sequence y, a start in sequence x, a length
/// and its edit operations (see alignment::pairwise for meaning of x and y).
#[derive(Debug)]
pub struct Alignment {
    pub score: i32,
    pub ystart: usize,
    pub xstart: usize,
    pub xlen: usize,
    pub operations: Vec<AlignmentOperation>
}

impl Alignment {
    /// Calculate the cigar string.
    pub fn cigar(&self, hard_clip: bool) -> String {
        let add_op = |op, k, cigar: &mut String| {
            cigar.push_str(&format!("{}{}", k, match op {
                AlignmentOperation::Match => "=",
                AlignmentOperation::Subst => "X",
                AlignmentOperation::Del => "D",
                AlignmentOperation::Ins => "I",
            }));
        };

        let op_len = |op: AlignmentOperation| {
            (op == AlignmentOperation::Match || op == AlignmentOperation::Subst || op == AlignmentOperation::Ins) as usize
        };

        let clip_str = if hard_clip {"H"} else {"S"};

        let mut cigar = String::new();

        if !self.operations.is_empty() {
            if self.xstart > 0 {
                cigar.push_str(&format!("{}{}", self.xstart, clip_str));
            }

            let mut last = self.operations[0];
            let mut k = 1;
            let mut alen = op_len(last);
            for &op in self.operations[1..].iter() {
                if op == last {
                    k += 1;
                } else {
                    add_op(last, k, &mut cigar);
                    k = 1;
                }
                last = op;
                alen += op_len(op);
            }
            add_op(last, k, &mut cigar);

            let clip = self.xlen - alen;
            if clip > 0 {
                cigar.push_str(&format!("{}{}", clip, clip_str));
            }
        }
        else {
            cigar.push_str(&format!("{}{}", self.xlen, clip_str));
        }

        cigar
    }

    /// Return the pretty formatted alignment as a String.
    ///
    /// ```
    /// use bio::alignment::Alignment;
    /// use bio::alignment::pairwise::Aligner;
    ///
    /// let x = b"CCGTCCGGCAA";
    /// let y = b"AAAAACCGTTGACGCAA";
    /// let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
    /// let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
    ///
    /// //
    /// //
    /// //
    /// let alignment = aligner.semiglobal(x, y);
    /// println!("SEMIGLOBAL: \n{}\n", alignment.pretty(x, y));
    ///
    /// //
    /// //
    /// //
    /// let alignment = aligner.local(x, y);
    /// println!("LOCAL: \n{}\n", alignment.pretty(x, y));
    ///
    /// //
    /// //
    /// //
    /// let alignment = aligner.global(x, y);
    /// println!("GLOBAL: \n{}\n", alignment.pretty(x, y));
    /// ```
    pub fn pretty(&self, x: &[u8], y: &[u8]) -> String {
        let mut x_pretty = String::new();
        let mut y_pretty = String::new();
        let mut inb_pretty = String::new();

        if !self.operations.is_empty() {
            let mut x_i : usize = self.xstart;
            let mut y_i : usize = self.ystart;

            // Add '-' before aligned subsequences and un-aligned 5' substrings of sequences.
            if x_i > y_i {
                let diff = x_i - y_i;
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                for _ in 0..diff {
                    y_pretty.push('-');
                }
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..y_i])));
            } else if x_i < y_i {
                let diff = y_i - x_i;
                for _ in 0..diff {
                    x_pretty.push('-');
                }
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&y[0..y_i])));
            } else {
                for i in 0..x_i {
                    x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
                    y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
                }
            }
            for _ in 0..x_pretty.len() {
                inb_pretty.push(' ');
            }

            // Process the alignment.
            for i in 0..self.operations.len() {
                match self.operations[i] {
                    AlignmentOperation::Match => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push_str("|");

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Subst => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Del => {
                        x_pretty.push('-');

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Ins => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push('-');
                    },
                }
            }

            // Add un-aligned 3' substrings of sequences.
            for i in x_i..x.len() {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
            }
            for i in y_i..y.len() {
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
            }

            // Add trailing '-'.
            if x_pretty.len() > y_pretty.len() {
                for _ in y_pretty.len()..x_pretty.len() {
                    y_pretty.push('-');
                }
            } else {
                for _ in x_pretty.len()..y_pretty.len() {
                    x_pretty.push('-');
                }
            }
        }

        format!("{}\n{}\n{}", x_pretty, inb_pretty, y_pretty)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::AlignmentOperation::*;
    use super::pairwise::*;

    #[test]
    fn test_cigar() {
        let alignment = Alignment { score: 5, xstart: 3, ystart: 0, xlen: 10, operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del] };
        assert_eq!(alignment.cigar(false), "3S3=1X2I2D4S");
    }

    #[test]
    fn test_pretty_alignment() {
        // let x = b"TACCGTGGAC";
        // let y = b"AAAAACCGTTGACGCAA";
        let y = b"CCGTCCGGCAA";
        let x = b"AAAAACCGTTGACGCAA";
        let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
        // alignment.operations: [Match, Match, Match, Match, Match, Subst, Match, Match, Match]
        let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
        let alignment = aligner.semiglobal(x, y);
        // let mut alignment = align_semiglobal!(x, y, -5, -1, score);
        // assert_eq!(alignment.pretty(x, y), align_semiglobal_pretty!(x, y, -5, -1, score));
        println!("semiglobal:\t{:?}", alignment);
        println!("SEMIGLOBAL: \n{}\n", alignment.pretty(x, y));
        // alignment.operations: [Match, Match, Match, Match, Match, Subst, Match, Match, Match]
        // alignment = align_local!(x, y, -5, -1, score);
        let alignment = aligner.local(x, y);
        println!("local:\t{:?}", alignment);
        println!("LOCAL: \n{}\n", alignment.pretty(x, y));
        // assert_eq!(alignment.pretty(x, y), align_local_pretty!(x, y, -5, -1, score));
        // alignment.operations: [Del, Del, Del, Del, Match, Match, Match, Match, Match, Subst, Match, Match, Match]
        // alignment = align_global!(x, y, -5, -1, score);
        let alignment = aligner.global(x, y);
        println!("global:\t{:?}", alignment);
        println!("GLOBAL: \n{}\n", alignment.pretty(x, y));

        // let x = b"AAAAAAA";
        // let y = b"TTTTTTTTTT";
        // let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
        // let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
        // let alignment = aligner.semiglobal(x, y);
        // println!("SEMIGLOBAL: \n{}\n", alignment.pretty(x, y));
        // let alignment = aligner.local(x, y);
        // println!("LOCAL: \n{}\n", alignment.pretty(x, y));
        // let alignment = aligner.global(x, y);
        // println!("GLOBAL: \n{}\n", alignment.pretty(x, y));
        // assert_eq!(alignment.pretty(x, y), align_global_pretty!(x, y, -5, -1, score));
    }

    // fn test_pretty_alignment() {
    //     let x = b"TACCGTGGAC";
    //     let y = b"AAAAACCGTTGACGCAA";
    //     let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
    //     let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
    //     let alignment = aligner.semiglobal(x, y);
    //     let alignment = aligner.local(x, y);
    //     let alignment = aligner.global(x, y);
    // }
}
<|endoftext|>


<commit_before>use crate::cpu::CpuDebugState;
use crate::ops::*;
use crate::ppu::PpuDebugState;
use crate::system::{System, SystemState};

use std::collections::VecDeque;

#[derive(Default)]
pub struct DebugState {
    trace_instrs: u32,
    color_dots: u32,
    log_once: bool,
    inst_queue: VecDeque<(CpuDebugState, PpuDebugState)>,
}

pub struct Debug {
    ops: &'static [Op; 0x100],
}

impl Debug {
    pub fn new() -> Debug {
        Debug { ops: Op::load() }
    }

    pub fn peek(&self, system: &System, state: &SystemState, addr: u16) -> u8 {
        system.cpu_bus.peek(system, state, addr)
    }

    pub fn frame(&self, state: &SystemState) -> u32 {
        state.ppu.frame
    }

    pub fn color_for(&self, state: &mut SystemState, count: u32) {
        state.debug.color_dots = count;
    }

    pub fn color(&self, state: &mut SystemState) -> bool {
        if state.debug.color_dots != 0 {
            state.debug.color_dots -= 1;
            true
        } else {
            false
        }
    }

    pub fn log_once_for(&self, state: &mut SystemState, count: u32) {
        if !state.debug.log_once {
            state.debug.log_once = true;
            self.log_for(state, count);
        }
    }
    pub fn log_for(&self, state: &mut SystemState, count: u32) {
        state.debug.trace_instrs = count;
    }

    pub fn log_history(&self, system: &System, state: &mut SystemState) {
        let queue = state.debug.inst_queue.clone();
        for &(cpu_state, ppu_state) in queue.iter() {
            let log = self.trace_instruction(system, state, cpu_state, ppu_state);
            eprintln!("{}", log);
        }
    }

    pub fn trace(
        &self,
        system: &System,
        state: &mut SystemState,
        cpu_state: CpuDebugState,
        ppu_state: PpuDebugState,
    ) {
        if let Some(inst_addr) = cpu_state.instruction_addr {
            if state.debug.inst_queue.len() == 100 {
                let _ = state.debug.inst_queue.pop_front();
            }
            state.debug.inst_queue.push_back((cpu_state, ppu_state));
            if state.debug.trace_instrs != 0 {
                let log = self.trace_instruction(system, state, cpu_state, ppu_state);
                eprintln!("{}", log);
                state.debug.trace_instrs -= 1;
            }

            let inst = self.ops[self.peek(system, state, inst_addr) as usize];
            if let Instruction::IllKil = inst.instruction {
                self.log_history(system, state);
                panic!("KIL");
            }
        }
    }

    pub fn trace_instruction(
        &self,
        system: &System,
        state: &mut SystemState,
        cpu_state: CpuDebugState,
        ppu_state: PpuDebugState,
    ) -> String {
        let addr = cpu_state.instruction_addr.unwrap();
        let instr = self.peek(system, state, addr);

        let op = self.ops[instr as usize];
        let name = op.instruction.name();
        let len = op.addressing.length() as u16;

        let mut addr_inc = addr + 1;
        let read = |state, addr| -> u8 { self.peek(system, state, addr) };

        let mut read_pc = |state| -> u8 {
            let val = self.peek(system, state, addr_inc);
            addr_inc = addr_inc.wrapping_add(1);
            val
        };

        let pc_string = format!("{:04X}", addr);

        let instr_bytes_string = {
            let mut buf = String::new();
            for x in 0..len {
                buf.push_str(&*format!(" {:02X}", read(state, x.wrapping_add(addr))));
            }

            buf
        };

        let addr_string = match op.addressing {
            Addressing::None => format!(""),
            Addressing::Accumulator => format!("A = {:02X}", cpu_state.reg_a),
            Addressing::Immediate => {
                let r = read_pc(state);
                format!("#${:02X}", r)
            }
            Addressing::ZeroPage(..) => {
                let a = read_pc(state);
                let v = read(state, a as u16);
                format!("${:02X} = {:02X}", a, v)
            }
            Addressing::ZeroPageOffset(Reg::X, ..) => {
                let a1 = read_pc(state) as u16;
                let a2 = a1.wrapping_add(cpu_state.reg_x as u16) & 0xff;
                let v = read(state, a2);
                format!("${:02X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::ZeroPageOffset(Reg::Y, ..) => {
                let a1 = read_pc(state) as u16;
                let a2 = a1.wrapping_add(cpu_state.reg_y as u16) & 0xff;
                let v = read(state, a2);
                format!("${:02X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::Absolute(..) => {
                let a_low = read_pc(state) as u16;
                let a_high = read_pc(state);
                let a = ((a_high as u16) << 8) | a_low;
                let v = read(state, a);
                format!("${:04X} = ${:02X}", a, v)
            }
            Addressing::AbsoluteOffset(Reg::X, ..) => {
                let a_low = read_pc(state) as u16;
                let a_high = read_pc(state);
                let a1 = ((a_high as u16) << 8) | a_low;
                let a2 = a1.wrapping_add(cpu_state.reg_x as u16);
                let v = read(state, a2);
                format!("${:04X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::AbsoluteOffset(Reg::Y, ..) => {
                let a_low = read_pc(state) as u16;
                let a_high = read_pc(state);
                let a1 = ((a_high as u16) << 8) | a_low;
                let a2 = a1.wrapping_add(cpu_state.reg_y as u16);
                let v = read(state, a2);
                format!("${:04X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::IndirectAbsolute(..) => {
                let a1_low = read_pc(state) as u16;
                let a1_high = read_pc(state);
                let a1 = ((a1_high as u16) << 8) | a1_low;
                let a2_low = read(state, a1) as u16;
                let a2_high = read(state, (a1 & 0xff00) | (a1.wrapping_add(1) & 0xff));
                let a2 = ((a2_high as u16) << 8) | a2_low;
                format!("(${:04X}) @ {:04X}", a1, a2)
            }
            Addressing::Relative(..) => {
                let rel = read_pc(state);
                let a = if rel < 0x080 {
                    addr_inc.wrapping_add(rel as u16)
                } else {
                    addr_inc.wrapping_sub(256).wrapping_add(rel as u16)
                };
                format!("${:04X}", a)
            }
            Addressing::IndirectX(..) => {
                let a1 = read_pc(state) as u16;
                let a2 = a1.wrapping_add(cpu_state.reg_x as u16) & 0xff;
                let a3_low = read(state, a2) as u16;
                let a3_high = read(state, (a2 & 0xff00) | (a2.wrapping_add(1) & 0xff)) as u16;
                let a3 = (a3_high << 8) | a3_low;
                let v = read(state, a3);
                format!("(${:02X},X) @ {:02X} = {:04X} = {:02X}", a1, a2, a3, v)
            }
            Addressing::IndirectY(..) => {
                let a1 = read_pc(state) as u16;
                let a2_low = read(state, a1);
                let a2_high = read(state, (a1 & 0xff00) | (a1.wrapping_add(1) & 0xff));
                let a2 = ((a2_high as u16) << 8) | a2_low as u16;
                let a3 = a2.wrapping_add((cpu_state.reg_y & 0xff) as u16);
                let v = read(state, a3);
                format!("(${:02X}),Y = {:04X} @ {:04X} = {:02X}", a1, a2, a3, v)
            }
        };

        let reg_string = {
            format!(
                "A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{:9}",
                cpu_state.reg_a,
                cpu_state.reg_x,
                cpu_state.reg_y,
                cpu_state.reg_p,
                cpu_state.reg_sp,
                cpu_state.cycle,
            )
        };

        let ppu_string = {
            format!(
                "DOT: {:3} SL: {:3} TICK: {:9}",
                ppu_state.dot, ppu_state.scanline, ppu_state.tick
            )
        };

        format!(
            "{}{: <10.10}{: >4.4} {: <30.30} {} {}",
            pc_string, instr_bytes_string, name, addr_string, reg_string, ppu_string
        )
    }
}
<commit_msg>Store inst log in ring buffer<commit_after>use crate::cpu::CpuDebugState;
use crate::ops::*;
use crate::ppu::PpuDebugState;
use crate::system::{System, SystemState};

const INST_HISTORY_BUF_SIZE: usize = 100;

pub struct DebugState {
    trace_instrs: u32,
    color_dots: u32,
    log_once: bool,
    inst_ring: RingBuffer<(CpuDebugState, PpuDebugState)>,
}

impl Default for DebugState {
    fn default() -> DebugState {
        DebugState {
            trace_instrs: 0,
            color_dots: 0,
            log_once: false,
            inst_ring: RingBuffer::new(INST_HISTORY_BUF_SIZE),
        }
    }
}

impl DebugState {
    fn log_inst(&mut self, inst: (CpuDebugState, PpuDebugState)) {
        self.inst_ring.push(inst);
    }

    fn log_iter<'a>(&'a self) -> RingIter<'a, (CpuDebugState, PpuDebugState)> {
        self.inst_ring.iter()
    }
}

struct RingBuffer<T> {
    ring: Box<[Option<T>]>,
    ring_index: usize,
}

impl<T> RingBuffer<T> {
    fn new(capacity: usize) -> RingBuffer<T> {
        let mut ring = Vec::with_capacity(capacity);
        for _ in 0..capacity {
            ring.push(None);
        }

        RingBuffer {
            ring: ring.into_boxed_slice(),
            ring_index: 0,
        }
    }

    fn push(&mut self, item: T) {
        self.ring_index += 1;
        if self.ring_index >= self.ring.len() {
            self.ring_index = 0;
        }

        self.ring[self.ring_index] = Some(item);
    }

    fn iter<'a>(&'a self) -> RingIter<'a, T> {
        RingIter {
            ring: &self.ring[..],
            ring_end_index: self.ring_index,
            ring_index: self.ring_index,
            first: true,
        }
    }
}

struct RingIter<'a, T> {
    ring: &'a [Option<T>],
    ring_end_index: usize,
    ring_index: usize,
    first: bool,
}

impl<'a, T> Iterator for RingIter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        if !self.first && self.ring_index == self.ring_end_index {
            None
        } else {
            let res = self.ring[self.ring_index].as_ref();
            if self.ring_index == 0 {
                self.ring_index = self.ring.len() - 1;
            } else {
                self.ring_index -= 1;
            }

            self.first = false;
            res
        }
    }
}

impl<'a, T> DoubleEndedIterator for RingIter<'a, T> {
    fn next_back(&mut self) -> Option<Self::Item> {
        loop {
            if !self.first && self.ring_end_index == self.ring_index {
                return None;
            } else {
                self.ring_end_index += 1;
                if self.ring_end_index >= self.ring.len() {
                    self.ring_end_index = 0;
                }

                let res = self.ring[self.ring_end_index].as_ref();

                if res.is_some() {
                    self.first = false;
                    return res;
                }
            }
        }
    }
}

pub struct Debug {
    ops: &'static [Op; 0x100],
}

impl Debug {
    pub fn new() -> Debug {
        Debug { ops: Op::load() }
    }

    pub fn peek(&self, system: &System, state: &SystemState, addr: u16) -> u8 {
        system.cpu_bus.peek(system, state, addr)
    }

    pub fn frame(&self, state: &SystemState) -> u32 {
        state.ppu.frame
    }

    pub fn color_for(&self, state: &mut SystemState, count: u32) {
        state.debug.color_dots = count;
    }

    pub fn color(&self, state: &mut SystemState) -> bool {
        if state.debug.color_dots != 0 {
            state.debug.color_dots -= 1;
            true
        } else {
            false
        }
    }

    pub fn log_once_for(&self, state: &mut SystemState, count: u32) {
        if !state.debug.log_once {
            state.debug.log_once = true;
            self.log_for(state, count);
        }
    }
    pub fn log_for(&self, state: &mut SystemState, count: u32) {
        state.debug.trace_instrs = count;
    }

    pub fn log_history(&self, system: &System, state: &SystemState) {
        for (cpu_state, ppu_state) in state.debug.log_iter().rev() {
            let log = self.trace_instruction(system, state, cpu_state, ppu_state);
            eprintln!("{}", log);
        }
    }

    pub fn trace(
        &self,
        system: &System,
        state: &mut SystemState,
        cpu_state: CpuDebugState,
        ppu_state: PpuDebugState,
    ) {
        if let Some(inst_addr) = cpu_state.instruction_addr {
            if state.debug.trace_instrs != 0 {
                let log = self.trace_instruction(system, state, &cpu_state, &ppu_state);
                eprintln!("{}", log);
                state.debug.trace_instrs -= 1;
            }

            state.debug.log_inst((cpu_state, ppu_state));

            let inst = self.ops[self.peek(system, state, inst_addr) as usize];
            if let Instruction::IllKil = inst.instruction {
                self.log_history(system, state);
                panic!("KIL");
            }
        }
    }

    pub fn trace_instruction(
        &self,
        system: &System,
        state: &SystemState,
        cpu_state: &CpuDebugState,
        ppu_state: &PpuDebugState,
    ) -> String {
        let addr = cpu_state.instruction_addr.unwrap();
        let instr = self.peek(system, state, addr);

        let op = self.ops[instr as usize];
        let name = op.instruction.name();
        let len = op.addressing.length() as u16;

        let mut addr_inc = addr + 1;
        let read = |state, addr| -> u8 { self.peek(system, state, addr) };

        let mut read_pc = |state| -> u8 {
            let val = self.peek(system, state, addr_inc);
            addr_inc = addr_inc.wrapping_add(1);
            val
        };

        let pc_string = format!("{:04X}", addr);

        let instr_bytes_string = {
            let mut buf = String::new();
            for x in 0..len {
                buf.push_str(&*format!(" {:02X}", read(state, x.wrapping_add(addr))));
            }

            buf
        };

        let addr_string = match op.addressing {
            Addressing::None => format!(""),
            Addressing::Accumulator => format!("A = {:02X}", cpu_state.reg_a),
            Addressing::Immediate => {
                let r = read_pc(state);
                format!("#${:02X}", r)
            }
            Addressing::ZeroPage(..) => {
                let a = read_pc(state);
                let v = read(state, a as u16);
                format!("${:02X} = {:02X}", a, v)
            }
            Addressing::ZeroPageOffset(Reg::X, ..) => {
                let a1 = read_pc(state) as u16;
                let a2 = a1.wrapping_add(cpu_state.reg_x as u16) & 0xff;
                let v = read(state, a2);
                format!("${:02X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::ZeroPageOffset(Reg::Y, ..) => {
                let a1 = read_pc(state) as u16;
                let a2 = a1.wrapping_add(cpu_state.reg_y as u16) & 0xff;
                let v = read(state, a2);
                format!("${:02X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::Absolute(..) => {
                let a_low = read_pc(state) as u16;
                let a_high = read_pc(state);
                let a = ((a_high as u16) << 8) | a_low;
                let v = read(state, a);
                format!("${:04X} = ${:02X}", a, v)
            }
            Addressing::AbsoluteOffset(Reg::X, ..) => {
                let a_low = read_pc(state) as u16;
                let a_high = read_pc(state);
                let a1 = ((a_high as u16) << 8) | a_low;
                let a2 = a1.wrapping_add(cpu_state.reg_x as u16);
                let v = read(state, a2);
                format!("${:04X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::AbsoluteOffset(Reg::Y, ..) => {
                let a_low = read_pc(state) as u16;
                let a_high = read_pc(state);
                let a1 = ((a_high as u16) << 8) | a_low;
                let a2 = a1.wrapping_add(cpu_state.reg_y as u16);
                let v = read(state, a2);
                format!("${:04X},X @ {:04X} = {:02X}", a1, a2, v)
            }
            Addressing::IndirectAbsolute(..) => {
                let a1_low = read_pc(state) as u16;
                let a1_high = read_pc(state);
                let a1 = ((a1_high as u16) << 8) | a1_low;
                let a2_low = read(state, a1) as u16;
                let a2_high = read(state, (a1 & 0xff00) | (a1.wrapping_add(1) & 0xff));
                let a2 = ((a2_high as u16) << 8) | a2_low;
                format!("(${:04X}) @ {:04X}", a1, a2)
            }
            Addressing::Relative(..) => {
                let rel = read_pc(state);
                let a = if rel < 0x080 {
                    addr_inc.wrapping_add(rel as u16)
                } else {
                    addr_inc.wrapping_sub(256).wrapping_add(rel as u16)
                };
                format!("${:04X}", a)
            }
            Addressing::IndirectX(..) => {
                let a1 = read_pc(state) as u16;
                let a2 = a1.wrapping_add(cpu_state.reg_x as u16) & 0xff;
                let a3_low = read(state, a2) as u16;
                let a3_high = read(state, (a2 & 0xff00) | (a2.wrapping_add(1) & 0xff)) as u16;
                let a3 = (a3_high << 8) | a3_low;
                let v = read(state, a3);
                format!("(${:02X},X) @ {:02X} = {:04X} = {:02X}", a1, a2, a3, v)
            }
            Addressing::IndirectY(..) => {
                let a1 = read_pc(state) as u16;
                let a2_low = read(state, a1);
                let a2_high = read(state, (a1 & 0xff00) | (a1.wrapping_add(1) & 0xff));
                let a2 = ((a2_high as u16) << 8) | a2_low as u16;
                let a3 = a2.wrapping_add((cpu_state.reg_y & 0xff) as u16);
                let v = read(state, a3);
                format!("(${:02X}),Y = {:04X} @ {:04X} = {:02X}", a1, a2, a3, v)
            }
        };

        let reg_string = {
            format!(
                "A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{:9}",
                cpu_state.reg_a,
                cpu_state.reg_x,
                cpu_state.reg_y,
                cpu_state.reg_p,
                cpu_state.reg_sp,
                cpu_state.cycle,
            )
        };

        let ppu_string = {
            format!(
                "DOT: {:3} SL: {:3} TICK: {:9}",
                ppu_state.dot, ppu_state.scanline, ppu_state.tick
            )
        };

        format!(
            "{}{: <10.10}{: >4.4} {: <30.30} {} {}",
            pc_string, instr_bytes_string, name, addr_string, reg_string, ppu_string
        )
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 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.

#![feature(nll)]

enum Either {
    One(X),
    Two(X),
}

struct X(Y);

struct Y;

pub fn main() {
    let e = Either::One(X(Y));
    let mut em = Either::One(X(Y));

    let r = &e;
    let rm = &mut Either::One(X(Y));

    let x = X(Y);
    let mut xm = X(Y);

    let s = &x;
    let sm = &mut X(Y);

    // --------

    let X(_t) = *s;
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION s
    if let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION r
    while let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION r
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION r
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION r
        // (invalid but acceptable)
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
    }

    let X(_t) = *sm;
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION sm
    if let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION rm
    while let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION rm
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION rm
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION rm
        // (invalid but acceptable)
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION rm
        // (invalid but acceptable)
        Either::One(_t) => (),
        Either::Two(ref mut _t) => (),
    }

    // --------

    let &X(_t) = s;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match r {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f1(&X(_t): &X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)

    let &mut X(_t) = sm;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::Two(_t)
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f2(&mut X(_t): &mut X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)

    // --------

    let &X(_t) = &x;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }

    let &mut X(_t) = &mut xm;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t)
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        | &mut Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
}
<commit_msg>Add more TODOs<commit_after>// Copyright 2018 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.

#![feature(nll)]

enum Either {
    One(X),
    Two(X),
}

struct X(Y);

struct Y;

pub fn main() {
    let e = Either::One(X(Y));
    let mut em = Either::One(X(Y));

    let r = &e;
    let rm = &mut Either::One(X(Y));

    let x = X(Y);
    let mut xm = X(Y);

    let s = &x;
    let sm = &mut X(Y);

    // --------

    let X(_t) = *s;
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION s
    if let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION r
    while let Either::One(_t) = *r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION r
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION r
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *r {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION r
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // TODO: should suggest removing `ref` too
    }

    let X(_t) = *sm;
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION sm
    if let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION rm
    while let Either::One(_t) = *rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this dereference operator
    //~| SUGGESTION rm
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION rm
        Either::One(_t)
        | Either::Two(_t) => (),
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION rm
        Either::One(_t) => (),
        Either::Two(ref _t) => (),
        // TODO: should suggest removing `ref` too
    }
    match *rm {
        //~^ ERROR cannot move
        //~| HELP consider removing this dereference operator
        //~| SUGGESTION rm
        Either::One(_t) => (),
        Either::Two(ref mut _t) => (),
        // TODO: should suggest removing `ref mut` too
    }

    // --------

    let &X(_t) = s;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = r { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match r {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match r {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f1(&X(_t): &X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)

    let &mut X(_t) = sm;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = rm { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::Two(_t)
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match rm {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
    fn f2(&mut X(_t): &mut X) { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)

    // --------

    let &X(_t) = &x;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &Either::One(_t) = &e { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t)
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        | &Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &Either::Two(ref _t) => (),
    }
    match &e {
        //~^ ERROR cannot move
        &Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }

    let &mut X(_t) = &mut xm;
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION X(_t)
    if let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    while let &mut Either::One(_t) = &mut em { }
    //~^ ERROR cannot move
    //~| HELP consider removing this borrow operator
    //~| SUGGESTION Either::One(_t)
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t)
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        | &mut Either::Two(_t) => (),
        // TODO: would really like a suggestion here too
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        &mut Either::Two(ref mut _t) => (),
    }
    match &mut em {
        //~^ ERROR cannot move
        &mut Either::One(_t) => (),
        //~^ HELP consider removing this borrow operator
        //~| SUGGESTION Either::One(_t)
        Either::Two(_t) => (),
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

mod convert;

pub use convert::{FromLossy, IntoLossy};

use log::error;

pub fn convert<T, U>(item: T) -> U
where
    T: IntoLossy<U>,
{
    item.into_lossy()
}

include!(concat!(env!("OUT_DIR"), "/grr.rs"));

impl From<bool> for DataBlob {

    fn from(value: bool) -> DataBlob {
        DataBlob {
            boolean: Some(value),
            ..Default::default()
        }
    }
}

impl From<i64> for DataBlob {

    fn from(value: i64) -> DataBlob {
        DataBlob {
            integer: Some(value),
            ..Default::default()
        }
    }
}

impl From<f32> for DataBlob {

    fn from(value: f32) -> DataBlob {
        DataBlob {
            float: Some(value),
            ..Default::default()
        }
    }
}

impl From<Vec<u8>> for DataBlob {

    fn from(value: Vec<u8>) -> DataBlob {
        DataBlob {
            data: Some(value),
            ..Default::default()
        }
    }
}

impl From<String> for DataBlob {

    fn from(value: String) -> DataBlob  {
        DataBlob {
            string: Some(value),
            ..Default::default()
        }
    }
}

impl KeyValue {

    /// Creates an empty key-value.
    ///
    /// Both the key and the value are going to be equal to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::empty();
    /// assert_eq!(entry.k, None);
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn empty() -> KeyValue {
        KeyValue {
            k: None,
            v: None,
        }
    }

    /// Creates a key-value pair.
    ///
    /// Both the key and the value are going to be equal to the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::pair(String::from("foo"), 42i64);
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("foo")));
    /// assert_eq!(entry.v.unwrap().integer, Some(42));
    /// ```
    pub fn pair<K, V>(key: K, value: V) -> KeyValue
    where
        K: Into<DataBlob>,
        V: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: Some(value.into()),
        }
    }

    /// Creates a key-only key-value.
    ///
    /// The key is going to be equal to the given value and the key will be
    /// `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::key(String::from("quux"));
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("quux")));
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn key<K>(key: K) -> KeyValue
    where
        K: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: None,
        }
    }
}

impl std::iter::FromIterator<KeyValue> for AttributedDict {

    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = KeyValue>,
    {
        AttributedDict {
            dat: iter.into_iter().collect(),
        }
    }
}

impl<K, V> std::iter::FromIterator<(K, V)> for AttributedDict
where
    K: Into<DataBlob>,
    V: Into<DataBlob>,
{
    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let pair = |(key, value)| KeyValue::pair(key, value);
        iter.into_iter().map(pair).collect()
    }
}

impl FromLossy<std::fs::Metadata> for StatEntry {

    fn from_lossy(metadata: std::fs::Metadata) -> StatEntry {
        use std::convert::{TryFrom, TryInto};
        #[cfg(target_family = "unix")]
        use std::os::unix::fs::MetadataExt;

        // TODO: Fix definition of `StatEntry`.
        // `StatEntry` defines insufficient integer width for some fields. For
        // now we just ignore errors, but the definition should be improved.
        let some = |value: u64| Some(value.try_into().unwrap_or(0));

        // TODO: Move this into some utility module.
        // TODO: Add support for other log types.
        macro_rules! ack {
            { $expr:expr, error: $message:literal } => {
                match $expr {
                    Ok(value) => Some(value),
                    Err(err) => {
                        ::log::error!(concat!($message, ": {}"), err);
                        None
                    },
                }
            };
        }

        let atime_secs = ack! {
            metadata.accessed(),
            error: "failed to obtain file access time"
        }.and_then(|atime| ack! {
            secs(atime),
            error: "failed to convert access time to seconds"
        });

        let mtime_secs = ack! {
            metadata.modified(),
            error: "failed to obtain file modification time"
        }.and_then(|mtime| ack! {
            secs(mtime),
            error: "failed to convert modification time to seconds"
        });

        let crtime_secs = ack! {
            metadata.created(),
            error: "failed to obtain file creation time"
        }.and_then(|crtime| ack! {
            secs(crtime),
            error: "failed to convert creation time to seconds"
        });

        #[cfg(target_family = "unix")]
        let ctime_secs = match u64::try_from(metadata.ctime()) {
            Ok(ctime_secs) => Some(ctime_secs),
            Err(err) => {
                error!("negative inode change time: {}", err);
                None
            },
        };

        StatEntry {
            #[cfg(target_family = "unix")]
            st_mode: Some(metadata.mode().into()),
            #[cfg(target_family = "unix")]
            st_ino: some(metadata.ino()),
            #[cfg(target_family = "unix")]
            st_dev: some(metadata.dev()),
            #[cfg(target_family = "unix")]
            st_rdev: some(metadata.rdev()),
            #[cfg(target_family = "unix")]
            st_nlink: some(metadata.nlink()),
            #[cfg(target_family = "unix")]
            st_uid: Some(metadata.uid()),
            #[cfg(target_family = "unix")]
            st_gid: Some(metadata.gid()),
            st_size: Some(metadata.len()),
            st_atime: atime_secs,
            st_mtime: mtime_secs,
            #[cfg(target_family = "unix")]
            st_ctime: ctime_secs,
            st_crtime: crtime_secs,
            #[cfg(target_family = "unix")]
            st_blocks: some(metadata.blocks()),
            #[cfg(target_family = "unix")]
            st_blksize: some(metadata.blksize()),
            ..Default::default()
        }
    }
}

/// An error type for failures of converting timestamps to microseconds.
#[derive(Clone, Debug)]
pub enum MicrosError {
    /// Attempted to convert pre-epoch system time.
    Epoch(std::time::SystemTimeError),
    /// Attempted to convert a value outside of 64-bit unsigned integer range.
    Overflow(std::num::TryFromIntError),
}

impl std::fmt::Display for MicrosError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => {
                write!(fmt, "pre-epoch system time: {}", error)
            }
            Overflow(ref error) => {
                write!(fmt, "system time value too big: {}", error)
            }
        }
    }
}

impl std::error::Error for MicrosError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => Some(error),
            Overflow(ref error) => Some(error),
        }
    }
}

impl From<std::time::SystemTimeError> for MicrosError {

    fn from(error: std::time::SystemTimeError) -> MicrosError {
        MicrosError::Epoch(error)
    }
}

impl From<std::num::TryFromIntError> for MicrosError {

    fn from(error: std::num::TryFromIntError) -> MicrosError {
        MicrosError::Overflow(error)
    }
}

/// An error type for failures of converting timestamps to seconds.
#[derive(Clone, Debug)]
pub struct SecsError {
    error: MicrosError,
}

impl std::fmt::Display for SecsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.error.fmt(fmt)
    }
}

impl From<MicrosError> for SecsError {

    fn from(error: MicrosError) -> SecsError {
        SecsError {
            error: error,
        }
    }
}

impl From<SecsError> for MicrosError {

    fn from(error: SecsError) -> MicrosError {
        error.error
    }
}

/// Converts system time into epoch microseconds.
///
/// Because most GRR messages use epoch microseconds for representing timestamps
/// this function be be useful to convert from more idiomatic representations.
///
/// # Examples
///
/// ```
/// use rrg_proto::micros;
///
/// assert_eq!(micros(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn micros(time: std::time::SystemTime) -> Result<u64, MicrosError> {
    let time_micros = time.duration_since(std::time::UNIX_EPOCH)?.as_micros();
    Ok(std::convert::TryInto::try_into(time_micros)?)
}

/// Converts system time into epoch seconds.
///
/// Some GRR messages use epoch seconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::secs;
///
/// assert_eq!(secs(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn secs(time: std::time::SystemTime) -> Result<u64, SecsError> {
    Ok(micros(time)? / 1_000_000)
}
<commit_msg>Use Linux-oriented names for timestamp fields.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

mod convert;

pub use convert::{FromLossy, IntoLossy};

use log::error;

pub fn convert<T, U>(item: T) -> U
where
    T: IntoLossy<U>,
{
    item.into_lossy()
}

include!(concat!(env!("OUT_DIR"), "/grr.rs"));

impl From<bool> for DataBlob {

    fn from(value: bool) -> DataBlob {
        DataBlob {
            boolean: Some(value),
            ..Default::default()
        }
    }
}

impl From<i64> for DataBlob {

    fn from(value: i64) -> DataBlob {
        DataBlob {
            integer: Some(value),
            ..Default::default()
        }
    }
}

impl From<f32> for DataBlob {

    fn from(value: f32) -> DataBlob {
        DataBlob {
            float: Some(value),
            ..Default::default()
        }
    }
}

impl From<Vec<u8>> for DataBlob {

    fn from(value: Vec<u8>) -> DataBlob {
        DataBlob {
            data: Some(value),
            ..Default::default()
        }
    }
}

impl From<String> for DataBlob {

    fn from(value: String) -> DataBlob  {
        DataBlob {
            string: Some(value),
            ..Default::default()
        }
    }
}

impl KeyValue {

    /// Creates an empty key-value.
    ///
    /// Both the key and the value are going to be equal to `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::empty();
    /// assert_eq!(entry.k, None);
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn empty() -> KeyValue {
        KeyValue {
            k: None,
            v: None,
        }
    }

    /// Creates a key-value pair.
    ///
    /// Both the key and the value are going to be equal to the given values.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::pair(String::from("foo"), 42i64);
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("foo")));
    /// assert_eq!(entry.v.unwrap().integer, Some(42));
    /// ```
    pub fn pair<K, V>(key: K, value: V) -> KeyValue
    where
        K: Into<DataBlob>,
        V: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: Some(value.into()),
        }
    }

    /// Creates a key-only key-value.
    ///
    /// The key is going to be equal to the given value and the key will be
    /// `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rrg_proto::KeyValue;
    ///
    /// let entry = KeyValue::key(String::from("quux"));
    /// assert_eq!(entry.k.unwrap().string, Some(String::from("quux")));
    /// assert_eq!(entry.v, None);
    /// ```
    pub fn key<K>(key: K) -> KeyValue
    where
        K: Into<DataBlob>,
    {
        KeyValue {
            k: Some(key.into()),
            v: None,
        }
    }
}

impl std::iter::FromIterator<KeyValue> for AttributedDict {

    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = KeyValue>,
    {
        AttributedDict {
            dat: iter.into_iter().collect(),
        }
    }
}

impl<K, V> std::iter::FromIterator<(K, V)> for AttributedDict
where
    K: Into<DataBlob>,
    V: Into<DataBlob>,
{
    fn from_iter<I>(iter: I) -> AttributedDict
    where
        I: IntoIterator<Item = (K, V)>,
    {
        let pair = |(key, value)| KeyValue::pair(key, value);
        iter.into_iter().map(pair).collect()
    }
}

impl FromLossy<std::fs::Metadata> for StatEntry {

    fn from_lossy(metadata: std::fs::Metadata) -> StatEntry {
        use std::convert::{TryFrom, TryInto};
        #[cfg(target_family = "unix")]
        use std::os::unix::fs::MetadataExt;

        // TODO: Fix definition of `StatEntry`.
        // `StatEntry` defines insufficient integer width for some fields. For
        // now we just ignore errors, but the definition should be improved.
        let some = |value: u64| Some(value.try_into().unwrap_or(0));

        // TODO: Move this into some utility module.
        // TODO: Add support for other log types.
        macro_rules! ack {
            { $expr:expr, error: $message:literal } => {
                match $expr {
                    Ok(value) => Some(value),
                    Err(err) => {
                        ::log::error!(concat!($message, ": {}"), err);
                        None
                    },
                }
            };
        }

        let atime_secs = ack! {
            metadata.accessed(),
            error: "failed to obtain file access time"
        }.and_then(|atime| ack! {
            secs(atime),
            error: "failed to convert access time to seconds"
        });

        let mtime_secs = ack! {
            metadata.modified(),
            error: "failed to obtain file modification time"
        }.and_then(|mtime| ack! {
            secs(mtime),
            error: "failed to convert modification time to seconds"
        });

        let btime_secs = ack! {
            metadata.created(),
            error: "failed to obtain file creation time"
        }.and_then(|crtime| ack! {
            secs(crtime),
            error: "failed to convert creation time to seconds"
        });

        #[cfg(target_family = "unix")]
        let ctime_secs = match u64::try_from(metadata.ctime()) {
            Ok(ctime_secs) => Some(ctime_secs),
            Err(err) => {
                error!("negative inode change time: {}", err);
                None
            },
        };

        StatEntry {
            #[cfg(target_family = "unix")]
            st_mode: Some(metadata.mode().into()),
            #[cfg(target_family = "unix")]
            st_ino: some(metadata.ino()),
            #[cfg(target_family = "unix")]
            st_dev: some(metadata.dev()),
            #[cfg(target_family = "unix")]
            st_rdev: some(metadata.rdev()),
            #[cfg(target_family = "unix")]
            st_nlink: some(metadata.nlink()),
            #[cfg(target_family = "unix")]
            st_uid: Some(metadata.uid()),
            #[cfg(target_family = "unix")]
            st_gid: Some(metadata.gid()),
            st_size: Some(metadata.len()),
            st_atime: atime_secs,
            st_mtime: mtime_secs,
            #[cfg(target_family = "unix")]
            st_ctime: ctime_secs,
            st_crtime: btime_secs,
            #[cfg(target_family = "unix")]
            st_blocks: some(metadata.blocks()),
            #[cfg(target_family = "unix")]
            st_blksize: some(metadata.blksize()),
            ..Default::default()
        }
    }
}

/// An error type for failures of converting timestamps to microseconds.
#[derive(Clone, Debug)]
pub enum MicrosError {
    /// Attempted to convert pre-epoch system time.
    Epoch(std::time::SystemTimeError),
    /// Attempted to convert a value outside of 64-bit unsigned integer range.
    Overflow(std::num::TryFromIntError),
}

impl std::fmt::Display for MicrosError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => {
                write!(fmt, "pre-epoch system time: {}", error)
            }
            Overflow(ref error) => {
                write!(fmt, "system time value too big: {}", error)
            }
        }
    }
}

impl std::error::Error for MicrosError {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use MicrosError::*;

        match *self {
            Epoch(ref error) => Some(error),
            Overflow(ref error) => Some(error),
        }
    }
}

impl From<std::time::SystemTimeError> for MicrosError {

    fn from(error: std::time::SystemTimeError) -> MicrosError {
        MicrosError::Epoch(error)
    }
}

impl From<std::num::TryFromIntError> for MicrosError {

    fn from(error: std::num::TryFromIntError) -> MicrosError {
        MicrosError::Overflow(error)
    }
}

/// An error type for failures of converting timestamps to seconds.
#[derive(Clone, Debug)]
pub struct SecsError {
    error: MicrosError,
}

impl std::fmt::Display for SecsError {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        self.error.fmt(fmt)
    }
}

impl From<MicrosError> for SecsError {

    fn from(error: MicrosError) -> SecsError {
        SecsError {
            error: error,
        }
    }
}

impl From<SecsError> for MicrosError {

    fn from(error: SecsError) -> MicrosError {
        error.error
    }
}

/// Converts system time into epoch microseconds.
///
/// Because most GRR messages use epoch microseconds for representing timestamps
/// this function be be useful to convert from more idiomatic representations.
///
/// # Examples
///
/// ```
/// use rrg_proto::micros;
///
/// assert_eq!(micros(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn micros(time: std::time::SystemTime) -> Result<u64, MicrosError> {
    let time_micros = time.duration_since(std::time::UNIX_EPOCH)?.as_micros();
    Ok(std::convert::TryInto::try_into(time_micros)?)
}

/// Converts system time into epoch seconds.
///
/// Some GRR messages use epoch seconds for representing timestamps. In such
/// cases this function can be useful to convert from more idiomatic types.
///
/// # Examples
///
/// ```
/// use rrg_proto::secs;
///
/// assert_eq!(secs(std::time::UNIX_EPOCH).unwrap(), 0);
/// ```
pub fn secs(time: std::time::SystemTime) -> Result<u64, SecsError> {
    Ok(micros(time)? / 1_000_000)
}
<|endoftext|>