<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]

use std::borrow::Cow;
use std::collections::BTreeSet;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, TokenStreamExt};

struct X;

impl quote::ToTokens for X {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("X", Span::call_site()));
    }
}

#[test]
fn test_quote_impl() {
    let tokens = quote! {
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens (& self , tokens : & mut TokenStream) { ",
        "(* * self) . to_tokens (tokens) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_spanned_impl() {
    let span = Span::call_site();
    let tokens = quote_spanned! {span=>
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens (& self , tokens : & mut TokenStream) { ",
        "(* * self) . to_tokens (tokens) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_substitution() {
    let x = X;
    let tokens = quote!(#x <#x> (#x) [#x] {#x});

    let expected = "X < X > (X) [X] { X }";

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_iter() {
    let primes = &[X, X, X, X];

    assert_eq!("X X X X", quote!(#(#primes)*).to_string());

    assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

    assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_array() {
    let array: [u8; 40] = [0; 40];
    let _ = quote!(#(#array #array)*);

    let ref_array: &[u8; 40] = &[0; 40];
    let _ = quote!(#(#ref_array #ref_array)*);

    let ref_slice: &[u8] = &[0; 40];
    let _ = quote!(#(#ref_slice #ref_slice)*);
}

#[test]
fn test_advanced() {
    let generics = quote!( <'a, T> );

    let where_clause = quote!( where T: Serialize );

    let field_ty = quote!(String);

    let item_ty = quote!(Cow<'a, str>);

    let path = quote!(SomeTrait::serialize_with);

    let value = quote!(self.x);

    let tokens = quote! {
        struct SerializeWith #generics #where_clause {
            value: &'a #field_ty,
            phantom: ::std::marker::PhantomData<#item_ty>,
        }

        impl #generics ::serde::Serialize for SerializeWith #generics #where_clause {
            fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: ::serde::Serializer
            {
                #path(self.value, s)
            }
        }

        SerializeWith {
            value: #value,
            phantom: ::std::marker::PhantomData::<#item_ty>,
        }
    };

    let expected = concat!(
        "struct SerializeWith < 'a , T > where T : Serialize { ",
        "value : & 'a String , ",
        "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , ",
        "} ",
        "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { ",
        "fn serialize < S > (& self , s : & mut S) -> Result < () , S :: Error > ",
        "where S : :: serde :: Serializer ",
        "{ ",
        "SomeTrait :: serialize_with (self . value , s) ",
        "} ",
        "} ",
        "SerializeWith { ",
        "value : self . x , ",
        "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_integer() {
    let ii8 = -1i8;
    let ii16 = -1i16;
    let ii32 = -1i32;
    let ii64 = -1i64;
    let ii128 = -1i128;
    let iisize = -1isize;
    let uu8 = 1u8;
    let uu16 = 1u16;
    let uu32 = 1u32;
    let uu64 = 1u64;
    let uu128 = 1u128;
    let uusize = 1usize;

    let tokens = quote! {
        #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
        #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
    };
    let expected = "- 1i8 - 1i16 - 1i32 - 1i64 - 1i128 - 1isize 1u8 1u16 1u32 1u64 1u128 1usize";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_floating() {
    let e32 = 2.345f32;

    let e64 = 2.345f64;

    let tokens = quote! {
        #e32
        #e64
    };
    let expected = concat!("2.345f32 2.345f64");
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_char() {
    let zero = '\0';
    let pound = '#';
    let quote = '"';
    let apost = '\'';
    let newline = '\n';
    let heart = '\u{2764}';

    let tokens = quote! {
        #zero #pound #quote #apost #newline #heart
    };
    let expected = "'\\u{0}' '#' '\"' '\\'' '\\n' '\u{2764}'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_str() {
    let s = "\0 a 'b \" c";
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_string() {
    let s = "\0 a 'b \" c".to_string();
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_ident() {
    let foo = Ident::new("Foo", Span::call_site());
    let bar = Ident::new(&format!("Bar{}", 7), Span::call_site());
    let tokens = quote!(struct #foo; enum #bar {});
    let expected = "struct Foo ; enum Bar7 { }";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate() {
    let ch = 'x';

    let tokens = quote!(#ch #ch);

    let expected = "'x' 'x'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_fancy_repetition() {
    let foo = vec!["a", "b"];
    let bar = vec![true, false];

    let tokens = quote! {
        #(#foo: #bar),*
    };

    let expected = r#""a" : true , "b" : false"#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nested_fancy_repetition() {
    let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

    let tokens = quote! {
        #(
            #(#nested)*
        ),*
    };

    let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition() {
    let foo = &["a", "b"];

    let tokens = quote! {
        #(#foo: #foo),*
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition_no_copy() {
    let foo = vec!["a".to_owned(), "b".to_owned()];

    let tokens = quote! {
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_btreeset_repetition() {
    let mut set = BTreeSet::new();
    set.insert("a".to_owned());
    set.insert("b".to_owned());

    let tokens = quote! {
        #(#set: #set),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_variable_name_conflict() {
    // The implementation of `#(...),*` uses the variable `_i` but it should be
    // fine, if a little confusing when debugging.
    let _i = vec!['a', 'b'];
    let tokens = quote! { #(#_i),* };
    let expected = "'a' , 'b'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nonrep_in_repetition() {
    let rep = vec!["a", "b"];
    let nonrep = "c";

    let tokens = quote! {
        #(#rep #rep : #nonrep #nonrep),*
    };

    let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_empty_quote() {
    let tokens = quote!();
    assert_eq!("", tokens.to_string());
}

#[test]
fn test_box_str() {
    let b = "str".to_owned().into_boxed_str();
    let tokens = quote! { #b };
    assert_eq!("\"str\"", tokens.to_string());
}

#[test]
fn test_cow() {
    let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site()));

    let ident = Ident::new("borrowed", Span::call_site());
    let borrowed = Cow::Borrowed(&ident);

    let tokens = quote! { #owned #borrowed };
    assert_eq!("owned borrowed", tokens.to_string());
}

#[test]
fn test_closure() {
    fn field_i(i: usize) -> Ident {
        format_ident!("__field{}", i)
    }

    let fields = (0usize..3)
        .map(field_i as fn(_) -> _)
        .map(|var| quote! { #var });

    let tokens = quote! { #(#fields)* };
    assert_eq!("__field0 __field1 __field2", tokens.to_string());
}

#[test]
fn test_append_tokens() {
    let mut a = quote!(a);
    let b = quote!(b);
    a.append_all(b);
    assert_eq!("a b", a.to_string());
}

#[test]
fn test_format_ident() {
    let id0 = format_ident!("Aa");
    let id1 = format_ident!("Hello{x}", x = id0);
    let id2 = format_ident!("Hello{x}", x = 5usize);
    let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
    let id4 = format_ident!("Aa", span = Span::call_site());
    let id5 = format_ident!("Hello{}", Cow::Borrowed("World"));

    assert_eq!(id0, "Aa");
    assert_eq!(id1, "HelloAa");
    assert_eq!(id2, "Hello5");
    assert_eq!(id3, "HelloAa_10");
    assert_eq!(id4, "Aa");
    assert_eq!(id5, "HelloWorld");
}

#[test]
fn test_format_ident_strip_raw() {
    let id = format_ident!("r#struct");
    let my_id = format_ident!("MyId{}", id);
    let raw_my_id = format_ident!("r#MyId{}", id);

    assert_eq!(id, "r#struct");
    assert_eq!(my_id, "MyIdstruct");
    assert_eq!(raw_my_id, "r#MyIdstruct");
}

#[test]
fn test_outer_line_comment() {
    let tokens = quote! {
        /// doc
    };
    let expected = "# [doc = r\" doc\"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_line_comment() {
    let tokens = quote! {
        //! doc
    };
    let expected = "# ! [doc = r\" doc\"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_block_comment() {
    let tokens = quote! {
        /** doc */
    };
    let expected = "# [doc = r\" doc \"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_block_comment() {
    let tokens = quote! {
        /*! doc */
    };
    let expected = "# ! [doc = r\" doc \"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_attr() {
    let tokens = quote! {
        #[inline]
    };
    let expected = "# [inline]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_attr() {
    let tokens = quote! {
        #![no_std]
    };
    let expected = "# ! [no_std]";
    assert_eq!(expected, tokens.to_string());
}

// https://github.com/dtolnay/quote/issues/130
#[test]
fn test_star_after_repetition() {
    let c = vec!['0', '1'];
    let tokens = quote! {
        #(
            f(#c);
        )*
        *out = None;
    };
    let expected = "f ('0') ; f ('1') ; * out = None ;";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_raw_id() {
    let id = quote!(r#raw_id);
    assert_eq!(id.to_string(), "r#raw_id");
}
<commit_msg>Test arrays containing non-Copy element too<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))]

use std::borrow::Cow;
use std::collections::BTreeSet;

use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, TokenStreamExt};

struct X;

impl quote::ToTokens for X {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append(Ident::new("X", Span::call_site()));
    }
}

#[test]
fn test_quote_impl() {
    let tokens = quote! {
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens (& self , tokens : & mut TokenStream) { ",
        "(* * self) . to_tokens (tokens) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_spanned_impl() {
    let span = Span::call_site();
    let tokens = quote_spanned! {span=>
        impl<'a, T: ToTokens> ToTokens for &'a T {
            fn to_tokens(&self, tokens: &mut TokenStream) {
                (**self).to_tokens(tokens)
            }
        }
    };

    let expected = concat!(
        "impl < 'a , T : ToTokens > ToTokens for & 'a T { ",
        "fn to_tokens (& self , tokens : & mut TokenStream) { ",
        "(* * self) . to_tokens (tokens) ",
        "} ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_substitution() {
    let x = X;
    let tokens = quote!(#x <#x> (#x) [#x] {#x});

    let expected = "X < X > (X) [X] { X }";

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_iter() {
    let primes = &[X, X, X, X];

    assert_eq!("X X X X", quote!(#(#primes)*).to_string());

    assert_eq!("X , X , X , X ,", quote!(#(#primes,)*).to_string());

    assert_eq!("X , X , X , X", quote!(#(#primes),*).to_string());
}

#[test]
fn test_array() {
    let array: [u8; 40] = [0; 40];
    let _ = quote!(#(#array #array)*);

    let ref_array: &[u8; 40] = &[0; 40];
    let _ = quote!(#(#ref_array #ref_array)*);

    let ref_slice: &[u8] = &[0; 40];
    let _ = quote!(#(#ref_slice #ref_slice)*);

    let array: [X; 2] = [X, X]; // !Copy
    let _ = quote!(#(#array #array)*);

    let ref_array: &[X; 2] = &[X, X];
    let _ = quote!(#(#ref_array #ref_array)*);

    let ref_slice: &[X] = &[X, X];
    let _ = quote!(#(#ref_slice #ref_slice)*);
}

#[test]
fn test_advanced() {
    let generics = quote!( <'a, T> );

    let where_clause = quote!( where T: Serialize );

    let field_ty = quote!(String);

    let item_ty = quote!(Cow<'a, str>);

    let path = quote!(SomeTrait::serialize_with);

    let value = quote!(self.x);

    let tokens = quote! {
        struct SerializeWith #generics #where_clause {
            value: &'a #field_ty,
            phantom: ::std::marker::PhantomData<#item_ty>,
        }

        impl #generics ::serde::Serialize for SerializeWith #generics #where_clause {
            fn serialize<S>(&self, s: &mut S) -> Result<(), S::Error>
                where S: ::serde::Serializer
            {
                #path(self.value, s)
            }
        }

        SerializeWith {
            value: #value,
            phantom: ::std::marker::PhantomData::<#item_ty>,
        }
    };

    let expected = concat!(
        "struct SerializeWith < 'a , T > where T : Serialize { ",
        "value : & 'a String , ",
        "phantom : :: std :: marker :: PhantomData < Cow < 'a , str > > , ",
        "} ",
        "impl < 'a , T > :: serde :: Serialize for SerializeWith < 'a , T > where T : Serialize { ",
        "fn serialize < S > (& self , s : & mut S) -> Result < () , S :: Error > ",
        "where S : :: serde :: Serializer ",
        "{ ",
        "SomeTrait :: serialize_with (self . value , s) ",
        "} ",
        "} ",
        "SerializeWith { ",
        "value : self . x , ",
        "phantom : :: std :: marker :: PhantomData :: < Cow < 'a , str > > , ",
        "}"
    );

    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_integer() {
    let ii8 = -1i8;
    let ii16 = -1i16;
    let ii32 = -1i32;
    let ii64 = -1i64;
    let ii128 = -1i128;
    let iisize = -1isize;
    let uu8 = 1u8;
    let uu16 = 1u16;
    let uu32 = 1u32;
    let uu64 = 1u64;
    let uu128 = 1u128;
    let uusize = 1usize;

    let tokens = quote! {
        #ii8 #ii16 #ii32 #ii64 #ii128 #iisize
        #uu8 #uu16 #uu32 #uu64 #uu128 #uusize
    };
    let expected = "- 1i8 - 1i16 - 1i32 - 1i64 - 1i128 - 1isize 1u8 1u16 1u32 1u64 1u128 1usize";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_floating() {
    let e32 = 2.345f32;

    let e64 = 2.345f64;

    let tokens = quote! {
        #e32
        #e64
    };
    let expected = concat!("2.345f32 2.345f64");
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_char() {
    let zero = '\0';
    let pound = '#';
    let quote = '"';
    let apost = '\'';
    let newline = '\n';
    let heart = '\u{2764}';

    let tokens = quote! {
        #zero #pound #quote #apost #newline #heart
    };
    let expected = "'\\u{0}' '#' '\"' '\\'' '\\n' '\u{2764}'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_str() {
    let s = "\0 a 'b \" c";
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_string() {
    let s = "\0 a 'b \" c".to_string();
    let tokens = quote!(#s);
    let expected = "\"\\u{0} a 'b \\\" c\"";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_ident() {
    let foo = Ident::new("Foo", Span::call_site());
    let bar = Ident::new(&format!("Bar{}", 7), Span::call_site());
    let tokens = quote!(struct #foo; enum #bar {});
    let expected = "struct Foo ; enum Bar7 { }";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate() {
    let ch = 'x';

    let tokens = quote!(#ch #ch);

    let expected = "'x' 'x'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_fancy_repetition() {
    let foo = vec!["a", "b"];
    let bar = vec![true, false];

    let tokens = quote! {
        #(#foo: #bar),*
    };

    let expected = r#""a" : true , "b" : false"#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nested_fancy_repetition() {
    let nested = vec![vec!['a', 'b', 'c'], vec!['x', 'y', 'z']];

    let tokens = quote! {
        #(
            #(#nested)*
        ),*
    };

    let expected = "'a' 'b' 'c' , 'x' 'y' 'z'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition() {
    let foo = &["a", "b"];

    let tokens = quote! {
        #(#foo: #foo),*
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_duplicate_name_repetition_no_copy() {
    let foo = vec!["a".to_owned(), "b".to_owned()];

    let tokens = quote! {
        #(#foo: #foo),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_btreeset_repetition() {
    let mut set = BTreeSet::new();
    set.insert("a".to_owned());
    set.insert("b".to_owned());

    let tokens = quote! {
        #(#set: #set),*
    };

    let expected = r#""a" : "a" , "b" : "b""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_variable_name_conflict() {
    // The implementation of `#(...),*` uses the variable `_i` but it should be
    // fine, if a little confusing when debugging.
    let _i = vec!['a', 'b'];
    let tokens = quote! { #(#_i),* };
    let expected = "'a' , 'b'";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_nonrep_in_repetition() {
    let rep = vec!["a", "b"];
    let nonrep = "c";

    let tokens = quote! {
        #(#rep #rep : #nonrep #nonrep),*
    };

    let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_empty_quote() {
    let tokens = quote!();
    assert_eq!("", tokens.to_string());
}

#[test]
fn test_box_str() {
    let b = "str".to_owned().into_boxed_str();
    let tokens = quote! { #b };
    assert_eq!("\"str\"", tokens.to_string());
}

#[test]
fn test_cow() {
    let owned: Cow<Ident> = Cow::Owned(Ident::new("owned", Span::call_site()));

    let ident = Ident::new("borrowed", Span::call_site());
    let borrowed = Cow::Borrowed(&ident);

    let tokens = quote! { #owned #borrowed };
    assert_eq!("owned borrowed", tokens.to_string());
}

#[test]
fn test_closure() {
    fn field_i(i: usize) -> Ident {
        format_ident!("__field{}", i)
    }

    let fields = (0usize..3)
        .map(field_i as fn(_) -> _)
        .map(|var| quote! { #var });

    let tokens = quote! { #(#fields)* };
    assert_eq!("__field0 __field1 __field2", tokens.to_string());
}

#[test]
fn test_append_tokens() {
    let mut a = quote!(a);
    let b = quote!(b);
    a.append_all(b);
    assert_eq!("a b", a.to_string());
}

#[test]
fn test_format_ident() {
    let id0 = format_ident!("Aa");
    let id1 = format_ident!("Hello{x}", x = id0);
    let id2 = format_ident!("Hello{x}", x = 5usize);
    let id3 = format_ident!("Hello{}_{x}", id0, x = 10usize);
    let id4 = format_ident!("Aa", span = Span::call_site());
    let id5 = format_ident!("Hello{}", Cow::Borrowed("World"));

    assert_eq!(id0, "Aa");
    assert_eq!(id1, "HelloAa");
    assert_eq!(id2, "Hello5");
    assert_eq!(id3, "HelloAa_10");
    assert_eq!(id4, "Aa");
    assert_eq!(id5, "HelloWorld");
}

#[test]
fn test_format_ident_strip_raw() {
    let id = format_ident!("r#struct");
    let my_id = format_ident!("MyId{}", id);
    let raw_my_id = format_ident!("r#MyId{}", id);

    assert_eq!(id, "r#struct");
    assert_eq!(my_id, "MyIdstruct");
    assert_eq!(raw_my_id, "r#MyIdstruct");
}

#[test]
fn test_outer_line_comment() {
    let tokens = quote! {
        /// doc
    };
    let expected = "# [doc = r\" doc\"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_line_comment() {
    let tokens = quote! {
        //! doc
    };
    let expected = "# ! [doc = r\" doc\"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_block_comment() {
    let tokens = quote! {
        /** doc */
    };
    let expected = "# [doc = r\" doc \"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_block_comment() {
    let tokens = quote! {
        /*! doc */
    };
    let expected = "# ! [doc = r\" doc \"]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_outer_attr() {
    let tokens = quote! {
        #[inline]
    };
    let expected = "# [inline]";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_inner_attr() {
    let tokens = quote! {
        #![no_std]
    };
    let expected = "# ! [no_std]";
    assert_eq!(expected, tokens.to_string());
}

// https://github.com/dtolnay/quote/issues/130
#[test]
fn test_star_after_repetition() {
    let c = vec!['0', '1'];
    let tokens = quote! {
        #(
            f(#c);
        )*
        *out = None;
    };
    let expected = "f ('0') ; f ('1') ; * out = None ;";
    assert_eq!(expected, tokens.to_string());
}

#[test]
fn test_quote_raw_id() {
    let id = quote!(r#raw_id);
    assert_eq!(id.to_string(), "r#raw_id");
}
<|endoftext|>


<commit_before>extern crate rustboy_core;
extern crate image;

use std::fs::{read_dir, create_dir, File};
use std::io::{Read, Error, Write};
use std::path::Path;

use std::vec::Vec;

const TEST_FRAMES: usize = 60 * 15;

pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>, Error> {
	let mut file = try!(File::open(path));
	let mut buffer = Vec::new();
	let result = file.read_to_end(&mut buffer);
	match result {
		Ok(_) => Ok(buffer.into_boxed_slice()),
		Err(err) => Err(err),
	}
}

///Runs a test rom and saves a sceenshot in tests/results after a specified ammount of cycles
///If there are no errors, it returns a vec of u32's that represent an rgba screenshot
fn run_test_rom(path: String) -> Result<Vec<u32>,String> {
	let rom = read_file(path.clone());
	if let Err(_) = rom {
		return Err(format!("Failed to open file {}.", path));
	}

	let gameboy = rustboy_core::init(rom.unwrap(), None);
	if let Err(ref msg) = gameboy {
		return Err(format!("Test rom {} failed with error {}.", path.clone(), msg));
	}
	let mut gameboy = gameboy.unwrap();

	for frame in 0..TEST_FRAMES {
		gameboy.step_frame();
	}

	let framebuffer = gameboy.get_framebuffer();
	let mut buffer = std::vec::Vec::with_capacity(framebuffer.len() * 4);
	buffer.extend_from_slice(framebuffer);

	//return screenshot
	Ok((buffer))
}

fn save_screenshot(path: String, raw: Vec<u32>) -> Result<(), std::io::Error> {
	let file = try!(File::create(path));

	//Convert the u32 pixels into rgba structs for the image library
	let mut buffer: Vec<u8> = Vec::with_capacity(raw.len() * 4);
	for val in raw {
		buffer.push((val >> 24) as u8);
		buffer.push((val >> 16) as u8);
		buffer.push((val >> 8) as u8);
		buffer.push((val & 0xFF) as u8);
	}
	let encoder = image::png::PNGEncoder::new(file);
	encoder.encode(buffer.as_slice(), rustboy_core::WIDTH as u32, rustboy_core::HEIGHT as u32, image::ColorType::RGBA(8))
}

#[test]
#[allow(unused_must_use)]
fn test_rom_runner() {
	let mut log = File::create("tests/results/test_log.txt").expect("Failed to create log file.");

	create_dir("tests/results");	//Create a directory for screenshots

	let dir = read_dir("tests/test_roms").expect("Test rom directory doesn't exist. Place test roms in tests/test_roms to run them.");

	for item in dir {
		if let Ok(ref entry) = item {
			writeln!(&mut log, "Running rom file {:?}", entry.file_name()).unwrap();

			let file_path = entry.path().into_os_string().into_string().unwrap();
			let gb_result = run_test_rom(file_path);
			if let Ok(screenshot) = gb_result {
				let screenshot_path = format!("tests/results/{}.png", entry.file_name().into_string().unwrap());
				let sc_result = save_screenshot(screenshot_path, screenshot);
				match sc_result {
					Ok(()) => {},
					Err(error) => writeln!(&mut log, "{}", error).unwrap(), /* Error saving screenshot */
				};
			}
			else if let Err(error) = gb_result {
				writeln!(&mut log, "Running rom {:?} failed with error: {}", entry.file_name(), error).unwrap();
			}
		}
	}
}
<commit_msg>Fixed a bug where the log was created before the directory it was supposed to exist in.<commit_after>extern crate rustboy_core;
extern crate image;

use std::fs::{read_dir, create_dir, File};
use std::io::{Read, Error, Write};
use std::path::Path;

use std::vec::Vec;

const TEST_FRAMES: usize = 60 * 15;

pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>, Error> {
	let mut file = try!(File::open(path));
	let mut buffer = Vec::new();
	let result = file.read_to_end(&mut buffer);
	match result {
		Ok(_) => Ok(buffer.into_boxed_slice()),
		Err(err) => Err(err),
	}
}

///Runs a test rom and saves a sceenshot in tests/results after a specified ammount of cycles
///If there are no errors, it returns a vec of u32's that represent an rgba screenshot
fn run_test_rom(path: String) -> Result<Vec<u32>,String> {
	let rom = read_file(path.clone());
	if let Err(_) = rom {
		return Err(format!("Failed to open file {}.", path));
	}

	let gameboy = rustboy_core::init(rom.unwrap(), None);
	if let Err(ref msg) = gameboy {
		return Err(format!("Test rom {} failed with error {}.", path.clone(), msg));
	}
	let mut gameboy = gameboy.unwrap();

	for frame in 0..TEST_FRAMES {
		gameboy.step_frame();
	}

	let framebuffer = gameboy.get_framebuffer();
	let mut buffer = std::vec::Vec::with_capacity(framebuffer.len() * 4);
	buffer.extend_from_slice(framebuffer);

	//return screenshot
	Ok((buffer))
}

fn save_screenshot(path: String, raw: Vec<u32>) -> Result<(), std::io::Error> {
	let file = try!(File::create(path));

	//Convert the u32 pixels into rgba structs for the image library
	let mut buffer: Vec<u8> = Vec::with_capacity(raw.len() * 4);
	for val in raw {
		buffer.push((val >> 24) as u8);
		buffer.push((val >> 16) as u8);
		buffer.push((val >> 8) as u8);
		buffer.push((val & 0xFF) as u8);
	}
	let encoder = image::png::PNGEncoder::new(file);
	encoder.encode(buffer.as_slice(), rustboy_core::WIDTH as u32, rustboy_core::HEIGHT as u32, image::ColorType::RGBA(8))
}

#[test]
#[allow(unused_must_use)]
fn test_rom_runner() {
	create_dir("tests/results");	//Create a directory for screenshots
	let mut log = File::create("tests/results/test_log.txt").expect("Failed to create log file.");

	let dir = read_dir("tests/test_roms").expect("Test rom directory doesn't exist. Place test roms in tests/test_roms to run them.");

	for item in dir {
		if let Ok(ref entry) = item {
			writeln!(&mut log, "Running rom file {:?}", entry.file_name()).unwrap();

			let file_path = entry.path().into_os_string().into_string().unwrap();
			let gb_result = run_test_rom(file_path);
			if let Ok(screenshot) = gb_result {
				let screenshot_path = format!("tests/results/{}.png", entry.file_name().into_string().unwrap());
				let sc_result = save_screenshot(screenshot_path, screenshot);
				match sc_result {
					Ok(()) => {},
					Err(error) => writeln!(&mut log, "{}", error).unwrap(), /* Error saving screenshot */
				};
			}
			else if let Err(error) = gb_result {
				writeln!(&mut log, "Running rom {:?} failed with error: {}", entry.file_name(), error).unwrap();
			}
		}
	}
}
<|endoftext|>


<commit_before>use ::inflector::Inflector;
use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::query;
use infer::response;
use infer::TokensResult;

const METHODS: &'static [&'static str] = &[
    "DELETE",
    "GET",
    "HEAD",
    "PATCH",
    "POST",
    "PUT",
];

pub(super) fn valid(method: &str) -> bool {
    METHODS.contains(&method.to_uppercase().as_str())
}

fn fn_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

fn mod_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

pub(super) fn infer_v3(method: &str, schema: &Yaml) -> TokensResult {
    let method_fn = fn_ident(&method);
    let method_mod = mod_ident(&method);

    let mut structs: Vec<Tokens> = Vec::new();
    let mut bounds: Vec<Tokens> = Vec::new();
    let mut args: Vec<Tokens> = Vec::new();
    let mut transformations: Vec<Tokens> = Vec::new();

    let query_parameters: Vec<Yaml>;
    if let Some(parameters) = schema["parameters"].as_vec() {
        query_parameters = parameters
            .iter().cloned()
            .filter(|p| p["in"] == Yaml::from_str("query")).collect();

        if query_parameters.len() > 0 {
            let (s, b, a, t) = query::infer_v3(
                &method_mod,
                &Yaml::Array(query_parameters)
            )?;
            structs.push(s);
            bounds.push(b);
            args.push(a);
            transformations.push(t);
        }
    }

    structs.push(response::infer_v3(&schema["responses"])?);

    Ok(quote! {
        pub mod #method_mod {
            use super::schema_ref;
            #(#structs)*
        }

        #[allow(dead_code)]
        pub fn #method_fn<#(#bounds),*>(#(#args),*) -> #method_mod::ResponseResult {
            let mut url = Url::parse(self::API_URL)
                .expect("Malformed server URL or path.");
            url.set_path(self::API_PATH);

            #(#transformations)*

            let client = Client::new().unwrap();
            let request = client.#method_fn(url)
                .header(header::Accept::json());

            let mut response = request.send().ok();
            <#method_mod::ResponseResult as Response>::from(&mut response.as_mut())
        }
    })
}
<commit_msg>Fix path fragment from base API URL being dropped<commit_after>use ::inflector::Inflector;
use ::quote::Tokens;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::query;
use infer::response;
use infer::TokensResult;

const METHODS: &'static [&'static str] = &[
    "DELETE",
    "GET",
    "HEAD",
    "PATCH",
    "POST",
    "PUT",
];

pub(super) fn valid(method: &str) -> bool {
    METHODS.contains(&method.to_uppercase().as_str())
}

fn fn_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

fn mod_ident(method: &str) -> Ident {
    assert!(valid(method), "Invalid method: {}", method);
    Ident::new(method.to_snake_case())
}

pub(super) fn infer_v3(method: &str, schema: &Yaml) -> TokensResult {
    let method_fn = fn_ident(&method);
    let method_mod = mod_ident(&method);

    let mut structs: Vec<Tokens> = Vec::new();
    let mut bounds: Vec<Tokens> = Vec::new();
    let mut args: Vec<Tokens> = Vec::new();
    let mut transformations: Vec<Tokens> = Vec::new();

    let query_parameters: Vec<Yaml>;
    if let Some(parameters) = schema["parameters"].as_vec() {
        query_parameters = parameters
            .iter().cloned()
            .filter(|p| p["in"] == Yaml::from_str("query")).collect();

        if query_parameters.len() > 0 {
            let (s, b, a, t) = query::infer_v3(
                &method_mod,
                &Yaml::Array(query_parameters)
            )?;
            structs.push(s);
            bounds.push(b);
            args.push(a);
            transformations.push(t);
        }
    }

    structs.push(response::infer_v3(&schema["responses"])?);

    Ok(quote! {
        pub mod #method_mod {
            use super::schema_ref;
            #(#structs)*
        }

        #[allow(dead_code)]
        pub fn #method_fn<#(#bounds),*>(#(#args),*) -> #method_mod::ResponseResult {
            let mut url = Url::parse(
                format!("{}{}", self::API_URL, self::API_PATH).as_str()
            ).expect("Malformed server URL or path.");

            #(#transformations)*

            let client = Client::new().unwrap();
            let request = client.#method_fn(url)
                .header(header::Accept::json());

            let mut response = request.send().ok();
            <#method_mod::ResponseResult as Response>::from(&mut response.as_mut())
        }
    })
}
<|endoftext|>


<commit_before>// Reader unit reads lines from input.txt, send lines one at a time to printer
// unit, which then prints lines, keeping track of lines printed. At the end of
// the file, the reader unit requests number of lines printed from the printer
// unit, and then prints them.
//
// http://rosettacode.org/wiki/Synchronous_concurrency

use std::io::File;
use std::io::BufferedReader;
use std::comm::{DuplexStream, duplex};

static FILENAME: &'static str = "resources/input.txt";

enum Message {
    Line(String),
    End
}

fn printer(chan: DuplexStream<int, Message>) {
    let mut count = 0;
    loop {
        match chan.recv() {
            Line(line) => {
                print!("{}", line);
                count += 1;
            }
            End => {break;}
        }
    }
    chan.send(count);
}

fn reader(chan: DuplexStream<Message, int>) {
    let mut file = BufferedReader::new(File::open(&Path::new(FILENAME)));
    for line in file.lines() {
        chan.send(Line(line.unwrap()));
    }
    chan.send(End);
    println!("Total Lines: {}", chan.recv());
}

// not_tested
fn main() {
    let (to_reader, to_printer) = duplex();
    spawn(proc() printer(to_reader));
    spawn(proc() reader(to_printer));
}
<commit_msg>Fixed some comments<commit_after>// http://rosettacode.org/wiki/Synchronous_concurrency
// not_tested
//
// Reader unit reads lines from input.txt, send lines one at a time to printer
// unit, which then prints lines, keeping track of lines printed. At the end of
// the file, the reader unit requests number of lines printed from the printer
// unit, and then prints them.

use std::io::File;
use std::io::BufferedReader;
use std::comm::{DuplexStream, duplex};

static FILENAME: &'static str = "resources/input.txt";

enum Message {
    Line(String),
    End
}

fn printer(chan: DuplexStream<int, Message>) {
    let mut count = 0;
    loop {
        match chan.recv() {
            Line(line) => {
                print!("{}", line);
                count += 1;
            }
            End => {break;}
        }
    }
    chan.send(count);
}

fn reader(chan: DuplexStream<Message, int>) {
    let mut file = BufferedReader::new(File::open(&Path::new(FILENAME)));
    for line in file.lines() {
        chan.send(Line(line.unwrap()));
    }
    chan.send(End);
    println!("Total Lines: {}", chan.recv());
}

fn main() {
    let (to_reader, to_printer) = duplex();
    spawn(proc() printer(to_reader));
    spawn(proc() reader(to_printer));
}
<|endoftext|>


<commit_before>use EffectClass::*;

#[derive(Debug, Copy, Clone)]
struct Character {
    hp: i32,
    mana: i32,
    damage: i32
}

impl Character {
    fn new(hp: i32) -> Character {
        Character {hp, mana: 0, damage: 0}
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum EffectClass {
    Shield,
    Poison,
    Recharge
}

#[derive(Debug, Copy, Clone)]
struct Effect {
    turns: i32,
    intensity: i32,
    class: EffectClass
}

#[derive(Debug, Copy, Clone)]
struct Spell {
    cost: i32,
    damage: i32,
    healing: i32,
    effect: Option<Effect>
}

#[derive(Debug, Clone)]
struct State {
    player: Character,
    boss: Character,
    effects: Vec<Effect>
}

fn main() {
    let spells = vec![
        Spell {
            cost: 53,
            damage: 4,
            healing: 0,
            effect: None
        },
        Spell {
            cost: 73,
            damage: 2,
            healing: 2,
            effect: None
        },
        Spell {
            cost: 113,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 7, class: Shield})
        },
        Spell {
            cost: 173,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 3, class: Poison})
        },
        Spell {
            cost: 229,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 5, intensity: 101, class: Recharge})
        }
    ];

    let boss = Character {
        hp: 51,
        mana: 0,
        damage: 9
    };

    let player = Character {
        hp: 50,
        mana: 500,
        damage: 0
    };
}
<commit_msg>Naive implementation of 2015 day 22 part 1<commit_after>use std::cmp;
use AttackResult::*;
use EffectClass::*;

#[derive(Debug, Copy, Clone)]
struct Character {
    hp: i32,
    mana: i32,
    damage: i32,
    armor: i32
}

#[derive(Debug, Clone)]
enum AttackResult {
    Win,
    Lose,
    Continue(State)
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum EffectClass {
    Shield,
    Poison,
    Recharge
}

#[derive(Debug, Copy, Clone)]
struct Effect {
    turns: i32,
    intensity: i32,
    class: EffectClass
}

#[derive(Debug, Copy, Clone)]
struct Spell {
    cost: i32,
    damage: i32,
    healing: i32,
    effect: Option<Effect>
}

#[derive(Debug, Clone)]
struct State {
    player: Character,
    boss: Character,
    effects: Vec<Effect>
}

fn process_effects(state: &State) -> State {
    let mut result = State {player: state.player, boss: state.boss, effects: Vec::new()};
    let mut armor = 0;

    for mut effect in state.effects.iter().filter(|x| x.turns > 0).cloned() {
        effect.turns -= 1;

        if effect.turns > 0 {
            result.effects.push(effect);
        }

        match effect.class {
            Shield => armor = effect.intensity,
            Poison => result.boss.hp -= effect.intensity,
            Recharge => result.player.mana += effect.intensity
        }
    }

    result.player.armor = armor;
    result
}

fn cast_spell(state: &State, spell: Spell) -> Option<AttackResult> {
    let mut result = process_effects(state);

    // Cast spell

    result.player.mana -= spell.cost;
    result.player.hp += spell.healing;
    result.boss.hp -= spell.damage;

    if result.player.mana <= 0 {
        return Some(Lose);
    }

    // Deal with effects

    if let Some(effect) = spell.effect {
        if result.effects.iter().all(|x| x.class != effect.class) {
            result.effects.push(effect)
        } else {
            return None;
        }
    }

    // Counter attack

    let mut result = process_effects(&result);

    if result.boss.hp > 0 {
        let damage = cmp::max(result.boss.damage - result.player.armor, 1);
        result.player.hp = cmp::max(result.player.hp - damage, 0);

        if result.player.hp <= 0 {
            return Some(Lose);
        }
    } else {
        return Some(Win);
    }

    Some(Continue(result))
}

fn list_strategies(spells: &Vec<Spell>, state: &State) -> Vec<Vec<Spell>> {
    spells.iter().flat_map(|&spell| match cast_spell(state, spell) {
        Some(Continue(next_state)) => {
            list_strategies(spells, &next_state)
            .into_iter()
            .map(|mut strategy| {
                strategy.push(spell);
                strategy
            })
            .collect()
        },
        Some(Win) => vec![vec![spell]],
        _ => vec![]
    }).collect()
}

fn main() {
    let spells = vec![
        Spell {
            cost: 53,
            damage: 4,
            healing: 0,
            effect: None
        },
        Spell {
            cost: 73,
            damage: 2,
            healing: 2,
            effect: None
        },
        Spell {
            cost: 113,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 7, class: Shield})
        },
        Spell {
            cost: 173,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 6, intensity: 3, class: Poison})
        },
        Spell {
            cost: 229,
            damage: 0,
            healing: 0,
            effect: Some(Effect {turns: 5, intensity: 101, class: Recharge})
        }
    ];

    let boss = Character {
        hp: 51,
        mana: 0,
        damage: 9,
        armor: 0
    };

    let player = Character {
        hp: 50,
        mana: 500,
        damage: 0,
        armor: 0
    };

    let start = State {player, boss, effects: Vec::new()};
    let strategies = list_strategies(&spells, &start);

    println!("{:?}", strategies.len());
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("01.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn get_fuel(mass: i32) -> i32 {
  *[mass / 3 - 2, 0].into_iter().max().unwrap()
}

fn get_fuel_recursive(mass: i32) -> i32 {
  let mut result = get_fuel(mass);
  let mut residual_fuel = result;

  loop {
    residual_fuel = get_fuel(residual_fuel);

    if residual_fuel <= 0 {
      break;
    }

    result += residual_fuel;
  }

  result
}

fn main() {
  let input = get_input().unwrap();
  let fuel = input.lines()
    .filter_map(|line| line.parse::<i32>().ok())
    .map(get_fuel)
    .sum::<i32>();

  println!("Part 1: {}", fuel);

  let fuel = input.lines()
    .filter_map(|line| line.parse::<i32>().ok())
    .map(get_fuel_recursive)
    .sum::<i32>();

  println!("Part 2: {}", fuel);
}
<commit_msg>Have elegant recursive algorithm<commit_after>use std::fs::File;
use std::io::prelude::*;

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("01.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn get_fuel(mass: u64) -> u64 {
  (mass / 3).checked_sub(2).unwrap_or(0)
}

fn get_fuel_recursive(mass: u64) -> u64 {
  let initial_fuel = get_fuel(mass);

  if initial_fuel <= 0 {
    0
  } else {
    initial_fuel + get_fuel_recursive(initial_fuel)
  }
}

fn main() {
  let input = get_input().unwrap();
  let fuel = input.lines()
    .filter_map(|line| line.parse::<u64>().ok())
    .map(get_fuel)
    .sum::<u64>();

  println!("Part 1: {}", fuel);

  let fuel = input.lines()
    .filter_map(|line| line.parse::<u64>().ok())
    .map(get_fuel_recursive)
    .sum::<u64>();

  println!("Part 2: {}", fuel);
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate git2;
#[macro_use]
extern crate log;
extern crate tools;
extern crate unicode_normalization;

use std::borrow::Borrow;
use std::collections::HashMap;
use std::env;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};

use git2::{DiffFindOptions, ObjectType, Oid, Patch, Repository, Sort};
use tools::config::index_blame;
use unicode_normalization::UnicodeNormalization;

fn get_hg_rev(helper: &mut Child, git_oid: &Oid) -> Option<String> {
    write!(helper.stdin.as_mut().unwrap(), "{}\n", git_oid).unwrap();
    let mut reader = BufReader::new(helper.stdout.as_mut().unwrap());
    let mut result = String::new();
    reader.read_line(&mut result).unwrap();
    let hgrev = result.trim();
    if hgrev.chars().all(|c| c == '0') {
        return None;
    }
    return Some(hgrev.to_string());
}

fn start_cinnabar_helper(git_repo: &Repository) -> Child {
    Command::new("git")
        .arg("cinnabar")
        .arg("git2hg")
        .arg("--batch")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(git_repo.path())
        .spawn()
        .unwrap()
}

fn count_lines(blob: &git2::Blob) -> usize {
    let data = blob.content();
    if data.is_empty() {
        return 0;
    }
    let mut linecount = 0;
    for b in data {
        if *b == b'\n' {
            linecount += 1;
        }
    }
    if data[data.len() - 1] != b'\n' {
        linecount += 1;
    }
    linecount
}

fn unmodified_lines(
    blob: &git2::Blob,
    parent_blob: &git2::Blob,
) -> Result<Vec<(usize, usize)>, git2::Error> {
    let mut unchanged = Vec::new();

    let patch = Patch::from_blobs(parent_blob, None, blob, None, None)?;

    if patch.delta().flags().is_binary() {
        return Ok(unchanged);
    }

    fn add_delta(lineno: usize, delta: i32) -> usize {
        ((lineno as i32) + delta) as usize
    }

    let mut latest_line: usize = 0;
    let mut delta: i32 = 0;

    for hunk_index in 0..patch.num_hunks() {
        for line_index in 0..patch.num_lines_in_hunk(hunk_index)? {
            let line = patch.line_in_hunk(hunk_index, line_index)?;

            if let Some(lineno) = line.new_lineno() {
                let lineno = lineno as usize;
                for i in latest_line..lineno - 1 {
                    unchanged.push((i, add_delta(i, delta)));
                }
                latest_line = (lineno - 1) + 1;
            }

            match line.origin() {
                '+' => delta -= 1,
                '-' => delta += 1,
                ' ' => {
                    assert_eq!(
                        line.old_lineno().unwrap() as usize,
                        add_delta(line.new_lineno().unwrap() as usize, delta)
                    );
                    unchanged.push((
                        (line.new_lineno().unwrap() - 1) as usize,
                        (line.old_lineno().unwrap() - 1) as usize,
                    ));
                }
                _ => (),
            };
        }
    }

    let linecount = count_lines(blob);
    for i in latest_line..linecount {
        unchanged.push((i, add_delta(i, delta)));
    }
    Ok(unchanged)
}

fn blame_for_path(
    file_movement: &HashMap<Oid, HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    path: &Path,
) -> Result<String, git2::Error> {
    let blob = commit
        .tree()?
        .get_path(path)?
        .to_object(git_repo)?
        .peel_to_blob()?;
    let linecount = count_lines(&blob);
    // TODO: drop this author field entirely, I don't think it gets consumed by anything
    let commit_id = commit.id();
    let author = commit
        .author()
        .name()
        .map(|n| n.nfkd().filter(|c| c.is_ascii()).collect::<String>())
        .unwrap_or_default();
    let mut blame = Vec::with_capacity(linecount);
    for line in 1..=linecount {
        blame.push(format!("{}:%:{}:{}", commit_id, line, author));
    }

    for (parent, blame_parent) in commit.parents().zip(blame_parents.iter()).rev() {
        let parent_path = file_movement
            .get(&parent.id())
            .and_then(|m| m.get(&blob.id()))
            .map(|p| p.borrow())
            .unwrap_or(path);
        let parent_blob = match parent.tree()?.get_path(parent_path) {
            Ok(t) if t.kind() == Some(ObjectType::Blob) => t.to_object(git_repo)?.peel_to_blob()?,
            _ => {
                continue;
            }
        };
        let parent_blame_blob = match blame_parent.tree()?.get_path(parent_path) {
            Ok(entry) => entry.to_object(blame_repo)?.peel_to_blob()?,
            _ => continue,
        };
        let parent_blame = std::str::from_utf8(parent_blame_blob.content())
            .unwrap() // We only ever put ascii in the blame blob (for now)
            .lines()
            .collect::<Vec<&str>>();

        let path_unchanged = path == parent_path;
        for (lineno, parent_lineno) in unmodified_lines(&blob, &parent_blob)? {
            if path_unchanged {
                blame[lineno] = String::from(parent_blame[parent_lineno]);
                continue;
            }
            let mut pieces = parent_blame[parent_lineno].splitn(4, ':');
            let p_rev = pieces.next().unwrap();
            let mut p_fname = pieces.next().unwrap();
            let p_lineno = pieces.next().unwrap();
            let p_author = pieces.next().unwrap();
            if p_fname == "%" {
                p_fname = parent_path.to_str().unwrap();
            }
            blame[lineno] = format!("{}:{}:{}:{}", p_rev, p_fname, p_lineno, p_author);
        }
    }
    // Extra entry so the `join` call after adds a trailing newline
    blame.push(String::new());
    Ok(blame.join("\n"))
}

fn build_blame_tree(
    builder: &mut git2::TreeBuilder,
    file_movement: &HashMap<Oid, HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    mut path: PathBuf,
) -> Result<(), git2::Error> {
    let tree_at_path = if path == PathBuf::new() {
        commit.tree()?
    } else {
        commit
            .tree()?
            .get_path(&path)?
            .to_object(git_repo)?
            .peel_to_tree()?
    };
    'outer: for entry in tree_at_path.iter() {
        path.push(entry.name().unwrap());
        for (i, parent) in commit.parents().enumerate() {
            if let Ok(parent_entry) = parent.tree()?.get_path(&path) {
                if parent_entry.id() == entry.id() {
                    // Item at `path` is the same in the tree for `commit` as in the tree
                    // for `parent`, so the blame must be the same too
                    let blame_parent_entry = blame_parents[i].tree()?.get_path(&path)?;
                    builder.insert(
                        entry.name().unwrap(),
                        blame_parent_entry.id(),
                        entry.filemode(),
                    )?;
                    path.pop();
                    continue 'outer;
                }
            }
        }

        match entry.kind() {
            Some(ObjectType::Blob) => {
                let blame_text = blame_for_path(
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    &path,
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    blame_repo.blob(&blame_text.as_bytes())?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Commit) => {
                // This is a submodule, just treat it as an empty dir. We could
                // probably also skip over it entirely.
                let entry_builder = blame_repo.treebuilder(None)?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Tree) => {
                let mut entry_builder = blame_repo.treebuilder(None)?;
                build_blame_tree(
                    &mut entry_builder,
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    path.clone(),
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            _ => {
                panic!(
                    "Unexpected entry kind {:?} found in tree for commit {:?} at path {:?}",
                    entry.kind(),
                    commit.id(),
                    path
                );
            }
        };

        path.pop();
    }

    Ok(())
}

fn main() {
    env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init();

    let args: Vec<_> = env::args().collect();
    let git_repo = Repository::open(&args[1]).unwrap();
    let blame_repo = Repository::open(&args[2]).unwrap();
    let use_cinnabar = env::var("CINNABAR").map_or(true, |v| v != "0");
    let mut hg_helper = if use_cinnabar {
        Some(start_cinnabar_helper(&git_repo))
    } else {
        None
    };
    let blame_ref = env::var("BLAME_REF").ok().unwrap_or("HEAD".to_string());

    info!("Reading existing blame map of ref {}...", blame_ref);
    let mut blame_map = if let Ok(oid) = blame_repo.refname_to_id(&blame_ref) {
        let (blame_map, _) = index_blame(&blame_repo, Some(oid));
        blame_map
    } else {
        HashMap::new()
    };

    let mut refobj = blame_repo
        .find_reference(&blame_ref)
        .ok()
        .and_then(|r| r.resolve().ok());

    let mut walk = git_repo.revwalk().unwrap();
    walk.set_sorting(Sort::TOPOLOGICAL | Sort::REVERSE).unwrap();
    walk.push(git_repo.refname_to_id(&blame_ref).unwrap())
        .unwrap();
    let revs_to_process = walk
        .map(|r| r.unwrap()) // walk produces Result<git2::Oid> so we unwrap to just the Oid
        .filter(|git_oid| !blame_map.contains_key(git_oid))
        .collect::<Vec<_>>();
    let rev_count = revs_to_process.len();

    info!("Transforming new commits...");

    let mut rev_done = 0;
    for git_oid in revs_to_process {
        rev_done += 1;

        let hg_rev = match hg_helper {
            Some(ref mut helper) => get_hg_rev(helper, &git_oid),
            None => None, // we don't support mapfiles any more.
        };

        info!(
            "Transforming {} (hg {:?}) progress {}/{}",
            git_oid, hg_rev, rev_done, rev_count
        );
        let commit = git_repo.find_commit(git_oid).unwrap();
        let blame_parents = commit
            .parent_ids()
            .map(|pid| blame_repo.find_commit(blame_map[&pid]).unwrap())
            .collect::<Vec<_>>();

        let mut file_movement = HashMap::new();
        if commit.parent_count() == 1 {
            let mut movement = HashMap::new();
            let mut diff = git_repo
                .diff_tree_to_tree(
                    Some(&commit.parent(0).unwrap().tree().unwrap()),
                    Some(&commit.tree().unwrap()),
                    None,
                )
                .unwrap();
            diff.find_similar(Some(
                DiffFindOptions::new().copies(true).rename_limit(1000000),
            ))
            .unwrap();
            for delta in diff.deltas() {
                if !delta.old_file().id().is_zero()
                    && !delta.new_file().id().is_zero()
                    && delta.old_file().path() != delta.new_file().path()
                {
                    movement.insert(
                        delta.new_file().id(),
                        delta.old_file().path().unwrap().to_path_buf(),
                    );
                }
            }
            file_movement.insert(commit.parent_id(0).unwrap(), movement);
        }

        let mut builder = blame_repo.treebuilder(None).unwrap();
        build_blame_tree(
            &mut builder,
            &file_movement,
            &git_repo,
            &commit,
            &blame_repo,
            &blame_parents,
            PathBuf::new(),
        )
        .unwrap();
        let tree_oid = builder.write().unwrap();

        let commit_msg = if let Some(hg_rev) = hg_rev {
            format!("git {}\nhg {}\n", git_oid, hg_rev)
        } else {
            format!("git {}\n", git_oid)
        };

        let commit_ref = if refobj.is_some() {
            None
        } else {
            // This should only happen on the first commit, if the blame_ref
            // doesn't exist yet in the destination repo
            Some(blame_ref.as_str())
        };
        let blame_oid = blame_repo
            .commit(
                commit_ref,
                &commit.author(),
                &commit.committer(),
                &commit_msg,
                &blame_repo.find_tree(tree_oid).unwrap(),
                &blame_parents.iter().collect::<Vec<_>>(),
            )
            .unwrap();

        if let Some(ref mut refobj) = refobj {
            *refobj = refobj.set_target(blame_oid, "").unwrap();
        } else {
            refobj = blame_repo
                .find_reference(&blame_ref)
                .ok()
                .and_then(|r| r.resolve().ok());
            assert!(refobj.is_some());
        }

        blame_map.insert(git_oid, blame_oid);
        info!("  -> {}", blame_oid);
    }

    if let Some(mut helper) = hg_helper {
        helper.kill().unwrap();
    }
}
<commit_msg>Bug 1634770 - Remove outer hashmap for file movement<commit_after>extern crate env_logger;
extern crate git2;
#[macro_use]
extern crate log;
extern crate tools;
extern crate unicode_normalization;

use std::borrow::Borrow;
use std::collections::HashMap;
use std::env;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, Command, Stdio};

use git2::{DiffFindOptions, ObjectType, Oid, Patch, Repository, Sort};
use tools::config::index_blame;
use unicode_normalization::UnicodeNormalization;

fn get_hg_rev(helper: &mut Child, git_oid: &Oid) -> Option<String> {
    write!(helper.stdin.as_mut().unwrap(), "{}\n", git_oid).unwrap();
    let mut reader = BufReader::new(helper.stdout.as_mut().unwrap());
    let mut result = String::new();
    reader.read_line(&mut result).unwrap();
    let hgrev = result.trim();
    if hgrev.chars().all(|c| c == '0') {
        return None;
    }
    return Some(hgrev.to_string());
}

fn start_cinnabar_helper(git_repo: &Repository) -> Child {
    Command::new("git")
        .arg("cinnabar")
        .arg("git2hg")
        .arg("--batch")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .current_dir(git_repo.path())
        .spawn()
        .unwrap()
}

fn count_lines(blob: &git2::Blob) -> usize {
    let data = blob.content();
    if data.is_empty() {
        return 0;
    }
    let mut linecount = 0;
    for b in data {
        if *b == b'\n' {
            linecount += 1;
        }
    }
    if data[data.len() - 1] != b'\n' {
        linecount += 1;
    }
    linecount
}

fn unmodified_lines(
    blob: &git2::Blob,
    parent_blob: &git2::Blob,
) -> Result<Vec<(usize, usize)>, git2::Error> {
    let mut unchanged = Vec::new();

    let patch = Patch::from_blobs(parent_blob, None, blob, None, None)?;

    if patch.delta().flags().is_binary() {
        return Ok(unchanged);
    }

    fn add_delta(lineno: usize, delta: i32) -> usize {
        ((lineno as i32) + delta) as usize
    }

    let mut latest_line: usize = 0;
    let mut delta: i32 = 0;

    for hunk_index in 0..patch.num_hunks() {
        for line_index in 0..patch.num_lines_in_hunk(hunk_index)? {
            let line = patch.line_in_hunk(hunk_index, line_index)?;

            if let Some(lineno) = line.new_lineno() {
                let lineno = lineno as usize;
                for i in latest_line..lineno - 1 {
                    unchanged.push((i, add_delta(i, delta)));
                }
                latest_line = (lineno - 1) + 1;
            }

            match line.origin() {
                '+' => delta -= 1,
                '-' => delta += 1,
                ' ' => {
                    assert_eq!(
                        line.old_lineno().unwrap() as usize,
                        add_delta(line.new_lineno().unwrap() as usize, delta)
                    );
                    unchanged.push((
                        (line.new_lineno().unwrap() - 1) as usize,
                        (line.old_lineno().unwrap() - 1) as usize,
                    ));
                }
                _ => (),
            };
        }
    }

    let linecount = count_lines(blob);
    for i in latest_line..linecount {
        unchanged.push((i, add_delta(i, delta)));
    }
    Ok(unchanged)
}

fn blame_for_path(
    file_movement: &Option<HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    path: &Path,
) -> Result<String, git2::Error> {
    let blob = commit
        .tree()?
        .get_path(path)?
        .to_object(git_repo)?
        .peel_to_blob()?;
    let linecount = count_lines(&blob);
    // TODO: drop this author field entirely, I don't think it gets consumed by anything
    let commit_id = commit.id();
    let author = commit
        .author()
        .name()
        .map(|n| n.nfkd().filter(|c| c.is_ascii()).collect::<String>())
        .unwrap_or_default();
    let mut blame = Vec::with_capacity(linecount);
    for line in 1..=linecount {
        blame.push(format!("{}:%:{}:{}", commit_id, line, author));
    }

    for (parent, blame_parent) in commit.parents().zip(blame_parents.iter()).rev() {
        let parent_path = file_movement
            .as_ref()
            .and_then(|m| m.get(&blob.id()))
            .map(|p| p.borrow())
            .unwrap_or(path);
        let parent_blob = match parent.tree()?.get_path(parent_path) {
            Ok(t) if t.kind() == Some(ObjectType::Blob) => t.to_object(git_repo)?.peel_to_blob()?,
            _ => {
                continue;
            }
        };
        let parent_blame_blob = match blame_parent.tree()?.get_path(parent_path) {
            Ok(entry) => entry.to_object(blame_repo)?.peel_to_blob()?,
            _ => continue,
        };
        let parent_blame = std::str::from_utf8(parent_blame_blob.content())
            .unwrap() // We only ever put ascii in the blame blob (for now)
            .lines()
            .collect::<Vec<&str>>();

        let path_unchanged = path == parent_path;
        for (lineno, parent_lineno) in unmodified_lines(&blob, &parent_blob)? {
            if path_unchanged {
                blame[lineno] = String::from(parent_blame[parent_lineno]);
                continue;
            }
            let mut pieces = parent_blame[parent_lineno].splitn(4, ':');
            let p_rev = pieces.next().unwrap();
            let mut p_fname = pieces.next().unwrap();
            let p_lineno = pieces.next().unwrap();
            let p_author = pieces.next().unwrap();
            if p_fname == "%" {
                p_fname = parent_path.to_str().unwrap();
            }
            blame[lineno] = format!("{}:{}:{}:{}", p_rev, p_fname, p_lineno, p_author);
        }
    }
    // Extra entry so the `join` call after adds a trailing newline
    blame.push(String::new());
    Ok(blame.join("\n"))
}

fn build_blame_tree(
    builder: &mut git2::TreeBuilder,
    file_movement: &Option<HashMap<Oid, PathBuf>>,
    git_repo: &git2::Repository,
    commit: &git2::Commit,
    blame_repo: &git2::Repository,
    blame_parents: &[git2::Commit],
    mut path: PathBuf,
) -> Result<(), git2::Error> {
    let tree_at_path = if path == PathBuf::new() {
        commit.tree()?
    } else {
        commit
            .tree()?
            .get_path(&path)?
            .to_object(git_repo)?
            .peel_to_tree()?
    };
    'outer: for entry in tree_at_path.iter() {
        path.push(entry.name().unwrap());
        for (i, parent) in commit.parents().enumerate() {
            if let Ok(parent_entry) = parent.tree()?.get_path(&path) {
                if parent_entry.id() == entry.id() {
                    // Item at `path` is the same in the tree for `commit` as in the tree
                    // for `parent`, so the blame must be the same too
                    let blame_parent_entry = blame_parents[i].tree()?.get_path(&path)?;
                    builder.insert(
                        entry.name().unwrap(),
                        blame_parent_entry.id(),
                        entry.filemode(),
                    )?;
                    path.pop();
                    continue 'outer;
                }
            }
        }

        match entry.kind() {
            Some(ObjectType::Blob) => {
                let blame_text = blame_for_path(
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    &path,
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    blame_repo.blob(&blame_text.as_bytes())?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Commit) => {
                // This is a submodule, just treat it as an empty dir. We could
                // probably also skip over it entirely.
                let entry_builder = blame_repo.treebuilder(None)?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            Some(ObjectType::Tree) => {
                let mut entry_builder = blame_repo.treebuilder(None)?;
                build_blame_tree(
                    &mut entry_builder,
                    file_movement,
                    git_repo,
                    commit,
                    blame_repo,
                    blame_parents,
                    path.clone(),
                )?;
                builder.insert(
                    entry.name().unwrap(),
                    entry_builder.write()?,
                    entry.filemode(),
                )?;
            }
            _ => {
                panic!(
                    "Unexpected entry kind {:?} found in tree for commit {:?} at path {:?}",
                    entry.kind(),
                    commit.id(),
                    path
                );
            }
        };

        path.pop();
    }

    Ok(())
}

fn main() {
    env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init();

    let args: Vec<_> = env::args().collect();
    let git_repo = Repository::open(&args[1]).unwrap();
    let blame_repo = Repository::open(&args[2]).unwrap();
    let use_cinnabar = env::var("CINNABAR").map_or(true, |v| v != "0");
    let mut hg_helper = if use_cinnabar {
        Some(start_cinnabar_helper(&git_repo))
    } else {
        None
    };
    let blame_ref = env::var("BLAME_REF").ok().unwrap_or("HEAD".to_string());

    info!("Reading existing blame map of ref {}...", blame_ref);
    let mut blame_map = if let Ok(oid) = blame_repo.refname_to_id(&blame_ref) {
        let (blame_map, _) = index_blame(&blame_repo, Some(oid));
        blame_map
    } else {
        HashMap::new()
    };

    let mut refobj = blame_repo
        .find_reference(&blame_ref)
        .ok()
        .and_then(|r| r.resolve().ok());

    let mut walk = git_repo.revwalk().unwrap();
    walk.set_sorting(Sort::TOPOLOGICAL | Sort::REVERSE).unwrap();
    walk.push(git_repo.refname_to_id(&blame_ref).unwrap())
        .unwrap();
    let revs_to_process = walk
        .map(|r| r.unwrap()) // walk produces Result<git2::Oid> so we unwrap to just the Oid
        .filter(|git_oid| !blame_map.contains_key(git_oid))
        .collect::<Vec<_>>();
    let rev_count = revs_to_process.len();

    info!("Transforming new commits...");

    let mut rev_done = 0;
    for git_oid in revs_to_process {
        rev_done += 1;

        let hg_rev = match hg_helper {
            Some(ref mut helper) => get_hg_rev(helper, &git_oid),
            None => None, // we don't support mapfiles any more.
        };

        info!(
            "Transforming {} (hg {:?}) progress {}/{}",
            git_oid, hg_rev, rev_done, rev_count
        );
        let commit = git_repo.find_commit(git_oid).unwrap();
        let blame_parents = commit
            .parent_ids()
            .map(|pid| blame_repo.find_commit(blame_map[&pid]).unwrap())
            .collect::<Vec<_>>();

        let file_movement = if commit.parent_count() == 1 {
            let mut movement = HashMap::new();
            let mut diff = git_repo
                .diff_tree_to_tree(
                    Some(&commit.parent(0).unwrap().tree().unwrap()),
                    Some(&commit.tree().unwrap()),
                    None,
                )
                .unwrap();
            diff.find_similar(Some(
                DiffFindOptions::new().copies(true).rename_limit(1000000),
            ))
            .unwrap();
            for delta in diff.deltas() {
                if !delta.old_file().id().is_zero()
                    && !delta.new_file().id().is_zero()
                    && delta.old_file().path() != delta.new_file().path()
                {
                    movement.insert(
                        delta.new_file().id(),
                        delta.old_file().path().unwrap().to_path_buf(),
                    );
                }
            }
            Some(movement)
        } else {
            None
        };

        let mut builder = blame_repo.treebuilder(None).unwrap();
        build_blame_tree(
            &mut builder,
            &file_movement,
            &git_repo,
            &commit,
            &blame_repo,
            &blame_parents,
            PathBuf::new(),
        )
        .unwrap();
        let tree_oid = builder.write().unwrap();

        let commit_msg = if let Some(hg_rev) = hg_rev {
            format!("git {}\nhg {}\n", git_oid, hg_rev)
        } else {
            format!("git {}\n", git_oid)
        };

        let commit_ref = if refobj.is_some() {
            None
        } else {
            // This should only happen on the first commit, if the blame_ref
            // doesn't exist yet in the destination repo
            Some(blame_ref.as_str())
        };
        let blame_oid = blame_repo
            .commit(
                commit_ref,
                &commit.author(),
                &commit.committer(),
                &commit_msg,
                &blame_repo.find_tree(tree_oid).unwrap(),
                &blame_parents.iter().collect::<Vec<_>>(),
            )
            .unwrap();

        if let Some(ref mut refobj) = refobj {
            *refobj = refobj.set_target(blame_oid, "").unwrap();
        } else {
            refobj = blame_repo
                .find_reference(&blame_ref)
                .ok()
                .and_then(|r| r.resolve().ok());
            assert!(refobj.is_some());
        }

        blame_map.insert(git_oid, blame_oid);
        info!("  -> {}", blame_oid);
    }

    if let Some(mut helper) = hg_helper {
        helper.kill().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate dot;

use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashSet};

use super::common::{Node, Weight};

#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Arc(pub Node, pub Node);

pub type Nodes = BTreeSet<Node>;
pub type Arcs = BTreeMap<Arc, Weight>;

#[derive(Clone, Debug)]
pub struct Graph {
    nodes: Nodes,
    arcs: Arcs,
}

impl Graph {
    pub fn new() -> Graph {
        Graph {
            nodes: Nodes::new(),
            arcs: Arcs::new(),
        }
    }

    pub fn nodes(&self) -> &Nodes {
        &self.nodes
    }

    pub fn arcs(&self) -> &Arcs {
        &self.arcs
    }

    pub fn arc_weight(&self, arc: &Arc) -> Weight {
        self.arcs[arc]
    }

    pub fn add_node(&mut self, id: Node) {
        self.nodes.insert(id);
    }

    pub fn rm_node(&mut self, id: Node) {
        self.nodes.remove(&id);
    }

    pub fn add_arc(&mut self, src: Node, dst: Node, weight: Weight) -> Arc {
        assert!(self.nodes.contains(&src));
        assert!(self.nodes.contains(&dst));
        let arc = Arc(src, dst);
        self.arcs.insert(arc.clone(), weight);
        arc
    }

    pub fn rm_arc(&mut self, arc: &Arc) {
        self.arcs.remove(arc);
    }

    pub fn union(&self, other: &Graph) -> Graph {
        let nodes = self.nodes.union(&other.nodes).cloned().collect();
        let self_arcs = self.arcs.keys().cloned().collect::<HashSet<_>>();
        let other_arcs = other.arcs.keys().cloned().collect::<HashSet<_>>();
        let arcs = self_arcs.union(&other_arcs).cloned().into_iter()
            .map(|arc| {
                let self_w = self.arcs.get(&arc);
                let other_w = other.arcs.get(&arc);
                let n = self_w.is_some() as i64 + other_w.is_some() as i64;
                let weight = (self_w.unwrap_or(&0.0) + other_w.unwrap_or(&0.0)) / n as Weight;
                (arc, weight)
            }).collect();
        Graph {nodes: nodes, arcs: arcs}
    }
}

impl<'a> dot::Labeller<'a, Node, Arc> for Graph {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("neural_network").unwrap()
    }

    fn node_id(&'a self, node: &Node) -> dot::Id<'a> {
        let &Node(id) = node;
        dot::Id::new(format!("node_{}", id)).unwrap()
    }

    fn node_label<'b>(&'b self, node: &Node) -> dot::LabelText<'b> {
        let &Node(id) = node;
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", id)))
    }

    fn edge_label<'b>(&'b self, arc: &Arc) -> dot::LabelText<'b> {
        let weight = self.arc_weight(arc);
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", weight)))
    }
}

impl<'a> dot::GraphWalk<'a, Node, Arc> for Graph {
    fn nodes(&self) -> dot::Nodes<'a, Node> {
        Cow::Owned(self.nodes().iter().cloned().collect::<Vec<Node>>())
    }

    fn edges(&'a self) -> dot::Edges<'a, Arc> {
        Cow::Owned(self.arcs().keys().cloned().collect::<Vec<Arc>>())
    }

    fn source(&self, arc: &Arc) -> Node {
        let &Arc(src, _) = arc;
        src
    }

    fn target(&self, arc: &Arc) -> Node {
        let &Arc(_, dst) = arc;
        dst
    }
}

#[test]
fn test_create_should_succeed() {
    Graph::new();
}

#[test]
fn test_add_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    assert!(graph.nodes().contains(&Node(42)));
}

#[test]
fn test_rm_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    graph.rm_node(Node(42));
    assert!(graph.nodes().is_empty());
}

#[test]
fn test_add_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    assert_eq!(arc, Arc(src, dst));
    assert_eq!(graph.arcs()[&arc], weight);
}

#[test]
fn test_rm_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    graph.rm_arc(&arc);
    assert!(graph.arcs().is_empty());
}

#[test]
#[should_panic]
fn test_add_arc_with_nonexistent_nodes_should_panic() {
    Graph::new().add_arc(Node(1), Node(2), 0.3);
}

#[test]
fn test_union_should_succeed() {
    let node1 = Node(1);
    let node2 = Node(2);
    let node3 = Node(3);
    let node4 = Node(4);
    let mut graph1 = Graph::new();
    graph1.add_node(node1);
    graph1.add_node(node2);
    graph1.add_node(node3);
    graph1.add_arc(node1, node2, 1.0);
    graph1.add_arc(node2, node3, 1.0);
    let mut graph2 = Graph::new();
    graph2.add_node(node1);
    graph2.add_node(node2);
    graph2.add_node(node4);
    graph2.add_arc(node1, node2, 0.5);
    graph2.add_arc(node1, node4, 1.0);
    let union = graph1.union(&graph2);
    assert_eq!(union.nodes(), &[node1, node2, node3, node4].iter().cloned().collect::<Nodes>());
    assert_eq!(union.arcs(), &[
        (Arc(node1, node2), 0.75),
        (Arc(node2, node3), 1.0),
        (Arc(node1, node4), 1.0),
    ].iter().cloned().collect::<Arcs>());
}
<commit_msg>Check node arcs count when remove<commit_after>extern crate dot;

use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};

use super::common::{Node, Weight};

#[derive(Clone, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct Arc(pub Node, pub Node);

pub type Nodes = BTreeSet<Node>;
pub type Arcs = BTreeMap<Arc, Weight>;
type ArcsCounter = HashMap<Node, usize>;

#[derive(Clone, Debug)]
pub struct Graph {
    nodes: Nodes,
    arcs: Arcs,
    arcs_counter: ArcsCounter,
}

impl Graph {
    pub fn new() -> Graph {
        Graph {
            nodes: Nodes::new(),
            arcs: Arcs::new(),
            arcs_counter: ArcsCounter::new(),
        }
    }

    pub fn nodes(&self) -> &Nodes {
        &self.nodes
    }

    pub fn arcs(&self) -> &Arcs {
        &self.arcs
    }

    pub fn node_arcs_count(&self, id: Node) -> usize {
        if let Some(value) = self.arcs_counter.get(&id) {
            *value
        } else {
            0
        }
    }

    pub fn arc_weight(&self, arc: &Arc) -> Weight {
        self.arcs[arc]
    }

    pub fn add_node(&mut self, id: Node) {
        self.nodes.insert(id);
    }

    pub fn rm_node(&mut self, id: Node) {
        assert_eq!(self.node_arcs_count(id), 0);
        self.nodes.remove(&id);
    }

    pub fn add_arc(&mut self, src: Node, dst: Node, weight: Weight) -> Arc {
        assert!(self.nodes.contains(&src));
        assert!(self.nodes.contains(&dst));
        let arc = Arc(src, dst);
        self.arcs.insert(arc.clone(), weight);
        Graph::inc_arcs_counter(&mut self.arcs_counter, src);
        Graph::inc_arcs_counter(&mut self.arcs_counter, dst);
        arc
    }

    pub fn rm_arc(&mut self, arc: &Arc) {
        self.arcs.remove(arc);
        let &Arc(src, dst) = arc;
        self.dec_arcs_counter(src);
        self.dec_arcs_counter(dst);
    }

    pub fn union(&self, other: &Graph) -> Graph {
        let nodes = self.nodes.union(&other.nodes).cloned().collect();
        let self_arcs = self.arcs.keys().cloned().collect::<HashSet<_>>();
        let other_arcs = other.arcs.keys().cloned().collect::<HashSet<_>>();
        let arcs: Arcs = self_arcs.union(&other_arcs).cloned().into_iter()
            .map(|arc| {
                let self_w = self.arcs.get(&arc);
                let other_w = other.arcs.get(&arc);
                let n = self_w.is_some() as i64 + other_w.is_some() as i64;
                let weight = (self_w.unwrap_or(&0.0) + other_w.unwrap_or(&0.0)) / n as Weight;
                (arc, weight)
            }).collect();
        let mut arcs_counter = ArcsCounter::new();
        for &Arc(src, dst) in arcs.keys() {
            Graph::inc_arcs_counter(&mut arcs_counter, src);
            Graph::inc_arcs_counter(&mut arcs_counter, dst);
        }
        Graph {nodes: nodes, arcs: arcs, arcs_counter: arcs_counter}
    }

    fn inc_arcs_counter(arcs_counter: &mut ArcsCounter, id: Node) {
        let value = arcs_counter.entry(id).or_insert(0);
        *value += 1;
    }

    fn dec_arcs_counter(&mut self, id: Node) {
        let value = self.arcs_counter.get_mut(&id).unwrap();
        assert!(*value > 0);
        *value -= 1;
    }
}

impl<'a> dot::Labeller<'a, Node, Arc> for Graph {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("neural_network").unwrap()
    }

    fn node_id(&'a self, node: &Node) -> dot::Id<'a> {
        let &Node(id) = node;
        dot::Id::new(format!("node_{}", id)).unwrap()
    }

    fn node_label<'b>(&'b self, node: &Node) -> dot::LabelText<'b> {
        let &Node(id) = node;
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", id)))
    }

    fn edge_label<'b>(&'b self, arc: &Arc) -> dot::LabelText<'b> {
        let weight = self.arc_weight(arc);
        dot::LabelText::LabelStr(Cow::Owned(format!("{}", weight)))
    }
}

impl<'a> dot::GraphWalk<'a, Node, Arc> for Graph {
    fn nodes(&self) -> dot::Nodes<'a, Node> {
        Cow::Owned(self.nodes().iter().cloned().collect::<Vec<Node>>())
    }

    fn edges(&'a self) -> dot::Edges<'a, Arc> {
        Cow::Owned(self.arcs().keys().cloned().collect::<Vec<Arc>>())
    }

    fn source(&self, arc: &Arc) -> Node {
        let &Arc(src, _) = arc;
        src
    }

    fn target(&self, arc: &Arc) -> Node {
        let &Arc(_, dst) = arc;
        dst
    }
}

#[test]
fn test_create_should_succeed() {
    Graph::new();
}

#[test]
fn test_add_node_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    assert!(graph.nodes().contains(&Node(42)));
}

#[test]
fn test_rm_node_without_arcs_should_succeed() {
    let mut graph = Graph::new();
    graph.add_node(Node(42));
    graph.rm_node(Node(42));
    assert!(graph.nodes().is_empty());
}

#[test]
#[should_panic]
fn test_rm_node_with_arcs_should_panic() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    graph.add_node(src);
    graph.add_node(dst);
    graph.add_arc(src, dst, 1.0);
    graph.rm_node(src);
}

#[test]
fn test_add_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    assert_eq!(arc, Arc(src, dst));
    assert_eq!(graph.arcs()[&arc], weight);
    assert_eq!(graph.node_arcs_count(src), 1);
    assert_eq!(graph.node_arcs_count(dst), 1);
}

#[test]
fn test_rm_arc_should_succeed() {
    let mut graph = Graph::new();
    let src = Node(1);
    let dst = Node(2);
    let weight = 0.3;
    graph.add_node(src);
    graph.add_node(dst);
    let arc = graph.add_arc(src, dst, weight);
    graph.rm_arc(&arc);
    assert!(graph.arcs().is_empty());
}

#[test]
#[should_panic]
fn test_add_arc_with_nonexistent_nodes_should_panic() {
    Graph::new().add_arc(Node(1), Node(2), 0.3);
}

#[test]
fn test_union_should_succeed() {
    let node1 = Node(1);
    let node2 = Node(2);
    let node3 = Node(3);
    let node4 = Node(4);
    let mut graph1 = Graph::new();
    graph1.add_node(node1);
    graph1.add_node(node2);
    graph1.add_node(node3);
    graph1.add_arc(node1, node2, 1.0);
    graph1.add_arc(node2, node3, 1.0);
    let mut graph2 = Graph::new();
    graph2.add_node(node1);
    graph2.add_node(node2);
    graph2.add_node(node4);
    graph2.add_arc(node1, node2, 0.5);
    graph2.add_arc(node1, node4, 1.0);
    let union = graph1.union(&graph2);
    assert_eq!(union.nodes(), &[node1, node2, node3, node4].iter().cloned().collect::<Nodes>());
    assert_eq!(union.arcs(), &[
        (Arc(node1, node2), 0.75),
        (Arc(node2, node3), 1.0),
        (Arc(node1, node4), 1.0),
    ].iter().cloned().collect::<Arcs>());
}
<|endoftext|>


<commit_before>
use std::sync::Mutex;
use rand;
use rand::Rng;
use std::ptr;

static GRID_WDH : i32 = 256;

lazy_static! {
    static ref GRID: Mutex<Vec<u8>> = {
        let mut v = Vec::new();
        v.resize((GRID_WDH * GRID_WDH) as usize, 0);
        Mutex::new(v)
    };
}

#[no_mangle]
pub extern fn gol_randomize() -> () {
    let mut grid = GRID.lock().unwrap();
    let mut rng = rand::thread_rng();

    for y in 0..GRID_WDH {
        for x in 0..GRID_WDH {
            let idx = (x + y * GRID_WDH) as usize;
            grid[idx] = if rng.gen() { 1 } else { 0 };
        }
    }
}

#[no_mangle]
pub extern fn gol_step() -> () {
    // Allocate new grid without wasting time setting it to any value
    let grid_size = (GRID_WDH * GRID_WDH) as usize;
    let mut new_grid = Vec::with_capacity(grid_size);
    unsafe { new_grid.set_len(grid_size); }
    let new_grid_ptr = new_grid.as_mut_ptr();

    let mut grid = GRID.lock().unwrap();
    let grid_ptr = grid.as_mut_ptr();

    // Compute the four borders
    for side in 0..4 {
        let mut x;
        let mut y;
        let xincr;
        let yincr;
        match side {
            0 => { x = 0           ; y = 0           ; xincr = 1; yincr = 0; },
            1 => { x = 0           ; y = GRID_WDH - 1; xincr = 1; yincr = 0; },
            2 => { x = 0           ; y = 0           ; xincr = 0; yincr = 1; },
            3 => { x = GRID_WDH - 1; y = 0           ; xincr = 0; yincr = 1; },
            _ => panic!("invalid side")
        }

        while x < GRID_WDH && y < GRID_WDH {
            // We're at the boundary, wrap
            let torus_idx = |x: i32, y: i32| -> u8 {
                let wrapx = if x < 0 { GRID_WDH - 1 } else { if x > GRID_WDH - 1 { 0 } else { x } };
                let wrapy = if y < 0 { GRID_WDH - 1 } else { if y > GRID_WDH - 1 { 0 } else { y } };
                let idx = wrapx + wrapy * GRID_WDH;
                unsafe { * grid_ptr.offset(idx as isize) }
            };

            // 2D indexing, faster to wrap
            let idx = (x + y * GRID_WDH) as isize;
            let alive = unsafe { * grid_ptr.offset(idx as isize) };
            let alive_nb = torus_idx(x + 1, y    ) +
                           torus_idx(x    , y + 1) +
                           torus_idx(x - 1, y    ) +
                           torus_idx(x    , y - 1) +
                           torus_idx(x + 1, y + 1) +
                           torus_idx(x - 1, y - 1) +
                           torus_idx(x + 1, y - 1) +
                           torus_idx(x - 1, y + 1);

            unsafe {
                * new_grid_ptr.offset(idx as isize) =
                    if alive_nb == 3 || (alive == 1 && alive_nb == 2) { 1 } else { 0 };
            }

            x += xincr;
            y += yincr;
        }
    }

    // Compute interior
    for y in 1..GRID_WDH - 1 {
        for x in 1..GRID_WDH - 1 {
            // 1D indexing, no wrapping needed
            let idx = x + y * GRID_WDH;
            let alive = unsafe { * grid_ptr.offset(idx as isize) };
            let alive_nb = unsafe {
                * grid_ptr.offset((idx + 1           ) as isize) +
                * grid_ptr.offset((idx - 1           ) as isize) +
                * grid_ptr.offset((idx     + GRID_WDH) as isize) +
                * grid_ptr.offset((idx     - GRID_WDH) as isize) +
                * grid_ptr.offset((idx + 1 + GRID_WDH) as isize) +
                * grid_ptr.offset((idx + 1 - GRID_WDH) as isize) +
                * grid_ptr.offset((idx - 1 + GRID_WDH) as isize) +
                * grid_ptr.offset((idx - 1 - GRID_WDH) as isize)
            };

            unsafe {
                * new_grid_ptr.offset(idx as isize) =
                    if alive_nb == 3 || (alive == 1 && alive_nb == 2) { 1 } else { 0 };
            }
        }
    }

    * grid = new_grid;
}

#[no_mangle]
pub extern fn gol_draw(w: i32, h: i32, fb: *mut u32) -> () {
    // Clear background
    unsafe { ptr::write_bytes(fb, 64, (w * h) as usize); }

    let grid = GRID.lock().unwrap();

    // Center
    let xoffs = w / 2 - GRID_WDH / 2;
    let yoffs = h / 2 - GRID_WDH / 2;

    for y in 0..GRID_WDH {
        for x in 0..GRID_WDH {
            let idx_fb = (xoffs + x) + (yoffs + y) * w;

            // Out of bounds check for the FB
            if idx_fb < 0 || idx_fb > w * h - 1 { continue; }

            let idx_grid = x + y * GRID_WDH;

            unsafe {
                * fb.offset(idx_fb as isize) =
                    if grid[idx_grid as usize] == 1 { 0x00FFFFFF } else { 0 };
            }
        }
    }
}

#[no_mangle]
pub extern fn gol_set_pattern(w: i32, h: i32, pat: *mut u8) -> () {
    let mut grid = GRID.lock().unwrap();

    // Clear grid
    let grid_size = GRID_WDH * GRID_WDH;
    grid.clear();
    grid.resize(grid_size as usize, 0);

    // Center
    let xoffs = GRID_WDH / 2 - w / 2;
    let yoffs = GRID_WDH / 2 - h / 2;

    for y in 0..h {
        for x in 0..w {
            let idx_grid = (xoffs + x) + (yoffs + y) * GRID_WDH;

            // Out of bounds check for the grid
            if idx_grid < 0 || idx_grid > grid_size - 1 { continue; }

            let idx_pat = x + y * w;
            unsafe { grid[idx_grid as usize] = * pat.offset(idx_pat as isize); }
        }
    }
}

<commit_msg>Initial multithreaded implementation of GoL, currently disabled<commit_after>
use std::sync::Mutex;
use rand;
use rand::Rng;
use std::ptr;
use std::thread;

static GRID_WDH : i32 = 256;

lazy_static! {
    static ref GRID: Mutex<Vec<u8>> = {
        let mut v = Vec::new();
        v.resize((GRID_WDH * GRID_WDH) as usize, 0);
        Mutex::new(v)
    };
}

#[no_mangle]
pub extern fn gol_randomize() -> () {
    let mut grid = GRID.lock().unwrap();
    let mut rng = rand::thread_rng();

    for y in 0..GRID_WDH {
        for x in 0..GRID_WDH {
            let idx = (x + y * GRID_WDH) as usize;
            grid[idx] = if rng.gen() { 1 } else { 0 };
        }
    }
}

#[no_mangle]
pub extern fn gol_step() -> () {
    // Allocate new grid without wasting time setting it to any value
    let grid_size = (GRID_WDH * GRID_WDH) as usize;
    let mut new_grid = Vec::with_capacity(grid_size);
    unsafe { new_grid.set_len(grid_size); }
    let new_grid_ptr = new_grid.as_mut_ptr();

    let mut grid = GRID.lock().unwrap();
    let grid_ptr = grid.as_mut_ptr();

    // Compute the four borders
    for side in 0..4 {
        let mut x;
        let mut y;
        let xincr;
        let yincr;
        match side {
            0 => { x = 0           ; y = 0           ; xincr = 1; yincr = 0; },
            1 => { x = 0           ; y = GRID_WDH - 1; xincr = 1; yincr = 0; },
            2 => { x = 0           ; y = 0           ; xincr = 0; yincr = 1; },
            3 => { x = GRID_WDH - 1; y = 0           ; xincr = 0; yincr = 1; },
            _ => panic!("invalid side")
        }

        while x < GRID_WDH && y < GRID_WDH {
            // We're at the boundary, wrap
            let torus_idx = |x: i32, y: i32| -> u8 {
                let wrapx = if x < 0 { GRID_WDH - 1 } else { if x > GRID_WDH - 1 { 0 } else { x } };
                let wrapy = if y < 0 { GRID_WDH - 1 } else { if y > GRID_WDH - 1 { 0 } else { y } };
                let idx = wrapx + wrapy * GRID_WDH;
                unsafe { * grid_ptr.offset(idx as isize) }
            };

            // 2D indexing, faster to wrap
            let idx = (x + y * GRID_WDH) as isize;
            let alive = unsafe { * grid_ptr.offset(idx as isize) };
            let alive_nb = torus_idx(x + 1, y    ) +
                           torus_idx(x    , y + 1) +
                           torus_idx(x - 1, y    ) +
                           torus_idx(x    , y - 1) +
                           torus_idx(x + 1, y + 1) +
                           torus_idx(x - 1, y - 1) +
                           torus_idx(x + 1, y - 1) +
                           torus_idx(x - 1, y + 1);

            unsafe {
                * new_grid_ptr.offset(idx as isize) =
                    if alive_nb == 3 || (alive == 1 && alive_nb == 2) { 1 } else { 0 };
            }

            x += xincr;
            y += yincr;
        }
    }

    /*
    let nthreads = 2;
    let threads: Vec<_> = (0..nthreads).map(|i| {
        let range = (GRID_WDH - 2) / nthreads;
        let seg_low = 1 + range * i;
        let seg_high = if i == nthreads - 1 { GRID_WDH - 1 } else { 1 + range * (i + 1) };

        // Allow sharing of pointers with our threads
        struct SendPtr {
            ptr : *mut u8
        }
        unsafe impl Send for SendPtr { }
        let grid_ptr_send     = SendPtr { ptr: grid_ptr     };
        let new_grid_ptr_send = SendPtr { ptr: new_grid_ptr };

        thread::spawn(move || {
            let grid_ptr     = grid_ptr_send.ptr;
            let new_grid_ptr = new_grid_ptr_send.ptr;
            // Compute interior
            for y in seg_low..seg_high {
                for x in 1..GRID_WDH - 1 {
                    // 1D indexing, no wrapping needed
                    let idx = x + y * GRID_WDH;
                    let alive = unsafe { * grid_ptr.offset(idx as isize) };
                    let alive_nb = unsafe {
                        * grid_ptr.offset((idx + 1           ) as isize) +
                        * grid_ptr.offset((idx - 1           ) as isize) +
                        * grid_ptr.offset((idx     + GRID_WDH) as isize) +
                        * grid_ptr.offset((idx     - GRID_WDH) as isize) +
                        * grid_ptr.offset((idx + 1 + GRID_WDH) as isize) +
                        * grid_ptr.offset((idx + 1 - GRID_WDH) as isize) +
                        * grid_ptr.offset((idx - 1 + GRID_WDH) as isize) +
                        * grid_ptr.offset((idx - 1 - GRID_WDH) as isize)
                    };

                    unsafe {
                        * new_grid_ptr.offset(idx as isize) =
                            if alive_nb == 3 || (alive == 1 && alive_nb == 2) { 1 } else { 0 };
                    }
                }
            }
        })
    }).collect();

    for thread in threads {
        thread.join().unwrap();
    }
    */

    // Compute interior
    for y in 1..GRID_WDH - 1 {
        for x in 1..GRID_WDH - 1 {
            // 1D indexing, no wrapping needed
            let idx = x + y * GRID_WDH;
            let alive = unsafe { * grid_ptr.offset(idx as isize) };
            let alive_nb = unsafe {
                * grid_ptr.offset((idx + 1           ) as isize) +
                * grid_ptr.offset((idx - 1           ) as isize) +
                * grid_ptr.offset((idx     + GRID_WDH) as isize) +
                * grid_ptr.offset((idx     - GRID_WDH) as isize) +
                * grid_ptr.offset((idx + 1 + GRID_WDH) as isize) +
                * grid_ptr.offset((idx + 1 - GRID_WDH) as isize) +
                * grid_ptr.offset((idx - 1 + GRID_WDH) as isize) +
                * grid_ptr.offset((idx - 1 - GRID_WDH) as isize)
            };

            unsafe {
                * new_grid_ptr.offset(idx as isize) =
                    if alive_nb == 3 || (alive == 1 && alive_nb == 2) { 1 } else { 0 };
            }
        }
    }

    * grid = new_grid;
}

#[no_mangle]
pub extern fn gol_draw(w: i32, h: i32, fb: *mut u32) -> () {
    // Clear background
    unsafe { ptr::write_bytes(fb, 64, (w * h) as usize); }

    let grid = GRID.lock().unwrap();

    // Center
    let xoffs = w / 2 - GRID_WDH / 2;
    let yoffs = h / 2 - GRID_WDH / 2;

    for y in 0..GRID_WDH {
        for x in 0..GRID_WDH {
            let idx_fb = (xoffs + x) + (yoffs + y) * w;

            // Out of bounds check for the FB
            if idx_fb < 0 || idx_fb > w * h - 1 { continue; }

            let idx_grid = x + y * GRID_WDH;

            unsafe {
                * fb.offset(idx_fb as isize) =
                    if grid[idx_grid as usize] == 1 { 0x00FFFFFF } else { 0 };
            }
        }
    }
}

#[no_mangle]
pub extern fn gol_set_pattern(w: i32, h: i32, pat: *mut u8) -> () {
    let mut grid = GRID.lock().unwrap();

    // Clear grid
    let grid_size = GRID_WDH * GRID_WDH;
    grid.clear();
    grid.resize(grid_size as usize, 0);

    // Center
    let xoffs = GRID_WDH / 2 - w / 2;
    let yoffs = GRID_WDH / 2 - h / 2;

    for y in 0..h {
        for x in 0..w {
            let idx_grid = (xoffs + x) + (yoffs + y) * GRID_WDH;

            // Out of bounds check for the grid
            if idx_grid < 0 || idx_grid > grid_size - 1 { continue; }

            let idx_pat = x + y * w;
            unsafe { grid[idx_grid as usize] = * pat.offset(idx_pat as isize); }
        }
    }
}

<|endoftext|>