<commit_before>extern crate timely;
extern crate alg3_dynamic;

use std::sync::{Arc, Mutex};
use std::io::BufReader;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use timely::dataflow::operators::*;

use alg3_dynamic::*;

type Node = u32;

fn main () {

    let start = std::time::Instant::now();

    let send = Arc::new(Mutex::new(0));
    let send2 = send.clone();

    let inspect = ::std::env::args().find(|x| x == "inspect").is_some();

    timely::execute_from_args(std::env::args(), move |root| {

        let send = send.clone();

        // used to partition graph loading
        let index = root.index();
        let peers = root.peers();

        let mut motif = vec![];
        let query_size: usize = std::env::args().nth(1).unwrap().parse().unwrap();
        for query in 0 .. query_size {
            let attr1: usize = std::env::args().nth(2 * (query + 1) + 0).unwrap().parse().unwrap();
            let attr2: usize = std::env::args().nth(2 * (query + 1) + 1).unwrap().parse().unwrap();
            motif.push((attr1, attr2));
        }

        // load fragment of input graph into memory to avoid io while running.
        let filename = std::env::args().nth(2 * (query_size) + 2).unwrap();
        let number_files: usize = std::env::args().nth(2 * (query_size) + 3).unwrap().parse().unwrap();
        let pre_load = std::env::args().nth(2 * (query_size) + 4).unwrap().parse().unwrap();
        let query_filename = std::env::args().nth(2 * (query_size) + 5).unwrap();
        let query_batch: usize = std::env::args().nth(2 * (query_size) + 6).unwrap().parse().unwrap();

        if index==0 {
            println!("motif:\t{:?}", motif);
            println!("filename:\t{:?} , {:?}", filename, number_files);
        }

        // handles to input and probe, but also both indices so we can compact them.
        let (mut input_graph1, mut input_graph2, mut input_delta, probe, load_probe1, load_probe2, handles) = root.dataflow::<Node,_,_>(move |builder| {

            // inputs for initial edges and changes to the edge set, respectively.
            let (graph_input1, graph1) = builder.new_input::<(Node, Node)>();
            let (graph_input2, graph2) = builder.new_input::<(Node, Node)>();
            let (delta_input, delta) = builder.new_input::<((Node, Node), i32)>();

            // // create indices and handles from the initial edges plus updates.
            let (graph_index, handles) = motif::GraphStreamIndex::from_separately(graph1, graph2, delta, |k| k as u64, |k| k as u64);

            // construct the motif dataflow subgraph.
            let motifs = graph_index.track_motif(&motif);

            // if "inspect", report motif counts.
            if inspect {
                motifs
                    .count()
                    .inspect_batch(|t,x| println!("{:?}: {:?}", t, x))
                    .inspect_batch(move |_,x| {
                        if let Ok(mut bound) = send.lock() {
                            *bound += x[0];
                        }
                    });
            }

            let load_probe1 = graph_index.forward.handle.clone();
            let load_probe2 = graph_index.reverse.handle.clone();

            (graph_input1, graph_input2, delta_input, motifs.probe(), load_probe1, load_probe2, handles)
        });

        // start the experiment!
        let start = ::std::time::Instant::now();
        let mut remaining = pre_load;
        let max_vertex = pre_load as Node;
        let prev_time = input_delta.time().clone();
        input_delta.advance_to(prev_time.inner + 1);

    if number_files == 1 {
        // Open the path in read-only mode, returns `io::Result<File>`
        let mut lines = match File::open(&Path::new(&filename)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&filename).display(),
                       Error::description(&why))
            },
        };
        // load up the graph, using the first `limit` lines in the file.
        for (counter, line) in lines.by_ref().take(pre_load).enumerate() {
            // each worker is responsible for a fraction of the queries
            if counter % peers == index {
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                  let mut elements = good_line[..].split_whitespace();
                  let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                  let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                  input_graph1.send((src, dst));
                }
            }
        }
          // synchronize with other workers before reporting data loaded.
        input_graph1.close();
        root.step_while(|| load_probe1.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tforward index loaded", start.elapsed(), index);
        //
        // REPEAT ABOVE
        // Open the path in read-only mode, returns `io::Result<File>`
        let mut lines = match File::open(&Path::new(&filename)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&filename).display(),
                       Error::description(&why))
            },
        };
        // load up the graph, using the first `limit` lines in the file.
        for (counter, line) in lines.by_ref().take(pre_load).enumerate() {
            // each worker is responsible for a fraction of the queries
            if counter % peers == index {
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                    let mut elements = good_line[..].split_whitespace();
                    let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                    let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                    input_graph2.send((src, dst));
                }
            }
        }

        // synchronize with other workers before reporting data loaded.
        input_graph2.close();
        root.step_while(|| load_probe2.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\treverse index loaded", start.elapsed(), index);

        // END REPEAT
    } else {

    println!("Multiple files...");

      for p in 0..number_files {
      if p % peers != index {
            // each partition will be handeled by one worker only.
            continue;
           }

        let mut p_str = filename.clone().to_string();
        if p / 10 == 0{
           p_str = p_str + "0000"+ &(p.to_string());
        }
        else if p / 100 == 0{
           p_str = p_str + "000"+ &(p.to_string());
        }
        else if p / 1000 == 0{
           p_str = p_str + "00"+ &(p.to_string());
        }
        else if p / 10000 == 0{
           p_str = p_str + "0"+ &(p.to_string());
        }
        else {
           p_str = p_str + &(p.to_string());
        }

        println!("worker{:?} --> filename: {:?} {:?}", index,p, p_str);

          let mut lines = match File::open(&Path::new(&p_str)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&p_str).display(),
                       Error::description(&why))
            },
        };

          remaining = 0;

        // load up all lines in the file.
        for (counter, line) in lines.by_ref().enumerate() {
          // count edges
          remaining = remaining +1 ;
           // each worker should load all available edges. Note that each partition is handled by one worker only.
           let good_line = line.ok().expect("EXCEPTION: read error");
           if !good_line.starts_with('#') && good_line.len() > 0 {
               let mut elements = good_line[..].split_whitespace();
               let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
               let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
               input_graph1.send((src, dst)); // send each edge to its responsible worker;
            }
        }

      } // end loop on files for forward

        // synchronize with other workers before reporting data loaded.
        input_graph1.close();
        root.step_while(|| load_probe1.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tforward index loaded", start.elapsed(), index);

        // REPEAT ABOVE

    for p in 0..number_files {
      if p % peers != index {
            // each partition will be handeled by one worker only.
            continue;
           }

        let mut p_str = filename.clone().to_string();
        if p / 10 == 0{
           p_str = p_str + "0000"+ &(p.to_string());
        }
        else if p / 100 == 0{
           p_str = p_str + "000"+ &(p.to_string());
        }
        else if p / 1000 == 0{
           p_str = p_str + "00"+ &(p.to_string());
        }
        else if p / 10000 == 0{
           p_str = p_str + "0"+ &(p.to_string());
        }
        else {
           p_str = p_str + &(p.to_string());
        }

        println!("worker{:?} --> filename: {:?} {:?}", index,p, p_str);

        let mut lines = match File::open(&Path::new(&p_str)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&p_str).display(),
                       Error::description(&why))
            },
        };

        remaining = 0;

        // Open the path in read-only mode, returns `io::Result<File>`
        let mut lines = match File::open(&Path::new(&p_str)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&p_str).display(),
                       Error::description(&why))
            },
        };

        // load up the graph, using the first `limit` lines in the file.
        for (counter, line) in lines.by_ref().enumerate() {
            // each worker is responsible for a fraction of the queries
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                   let mut elements = good_line[..].split_whitespace();
                   let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                   let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                   input_graph2.send((src, dst));
                }
        }

    }//end loop on files

    // synchronize with other workers before reporting data loaded.
    input_graph2.close();
    root.step_while(|| load_probe2.less_than(input_delta.time()));
    println!("{:?}\t[worker {}]\treverse index loaded", start.elapsed(), index);

    // END REPEAT

    }// end if there are multi files

        // loop { }

        // merge all of the indices the worker maintains.
        let prev_time = input_delta.time().clone();
        handles.merge_to(&prev_time);

        // synchronize with other workers before reporting indices merged.
        let prev_time = input_delta.time().clone();
        // input_graph.advance_to(prev_time.inner + 1);
        input_delta.advance_to(prev_time.inner + 1);
        root.step_while(|| probe.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tindices merged", start.elapsed(), index);

        let lines = match File::open(&Path::new(&query_filename)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&query_filename).display(),
                       Error::description(&why))
            },
        };

        // issue queries and updates, using the remaining lines in the file.
        for (query_counter, line) in lines.enumerate() {

            // each worker is responsible for a fraction of the queries
            if query_counter % peers == index {
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                    let mut elements = good_line[..].split_whitespace();
                    let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                    let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                    input_delta.send(((src, dst), 1));
                }
            }

            // synchronize and merge indices.
            if query_counter % query_batch == (query_batch - 1) {
                let prev_time = input_delta.time().clone();
                // input_graph.advance_to(prev_time.inner + 1);
                input_delta.advance_to(prev_time.inner + 1);
                root.step_while(|| probe.less_than(input_delta.time()));
                handles.merge_to(&prev_time);
            }
        }
    }).unwrap();

    let total = send2.lock().map(|x| *x).unwrap_or(0);
    println!("elapsed: {:?}\ttotal motifs at this process: {:?}", start.elapsed(), total);
}<commit_msg>pre-parse query edges<commit_after>extern crate timely;
extern crate alg3_dynamic;

use std::sync::{Arc, Mutex};
use std::io::BufReader;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use timely::dataflow::operators::*;

use alg3_dynamic::*;

type Node = u32;

fn main () {

    let start = std::time::Instant::now();

    let send = Arc::new(Mutex::new(0));
    let send2 = send.clone();

    let inspect = ::std::env::args().find(|x| x == "inspect").is_some();

    timely::execute_from_args(std::env::args(), move |root| {

        let send = send.clone();

        // used to partition graph loading
        let index = root.index();
        let peers = root.peers();

        let mut motif = vec![];
        let query_size: usize = std::env::args().nth(1).unwrap().parse().unwrap();
        for query in 0 .. query_size {
            let attr1: usize = std::env::args().nth(2 * (query + 1) + 0).unwrap().parse().unwrap();
            let attr2: usize = std::env::args().nth(2 * (query + 1) + 1).unwrap().parse().unwrap();
            motif.push((attr1, attr2));
        }

        // load fragment of input graph into memory to avoid io while running.
        let filename = std::env::args().nth(2 * (query_size) + 2).unwrap();
        let number_files: usize = std::env::args().nth(2 * (query_size) + 3).unwrap().parse().unwrap();
        let pre_load = std::env::args().nth(2 * (query_size) + 4).unwrap().parse().unwrap();
        let query_filename = std::env::args().nth(2 * (query_size) + 5).unwrap();
        let query_batch: usize = std::env::args().nth(2 * (query_size) + 6).unwrap().parse().unwrap();

        if index==0 {
            println!("motif:\t{:?}", motif);
            println!("filename:\t{:?} , {:?}", filename, number_files);
        }

        // handles to input and probe, but also both indices so we can compact them.
        let (mut input_graph1, mut input_graph2, mut input_delta, probe, load_probe1, load_probe2, handles) = root.dataflow::<Node,_,_>(move |builder| {

            // inputs for initial edges and changes to the edge set, respectively.
            let (graph_input1, graph1) = builder.new_input::<(Node, Node)>();
            let (graph_input2, graph2) = builder.new_input::<(Node, Node)>();
            let (delta_input, delta) = builder.new_input::<((Node, Node), i32)>();

            // // create indices and handles from the initial edges plus updates.
            let (graph_index, handles) = motif::GraphStreamIndex::from_separately(graph1, graph2, delta, |k| k as u64, |k| k as u64);

            // construct the motif dataflow subgraph.
            let motifs = graph_index.track_motif(&motif);

            // if "inspect", report motif counts.
            if inspect {
                motifs
                    .count()
                    .inspect_batch(|t,x| println!("{:?}: {:?}", t, x))
                    .inspect_batch(move |_,x| {
                        if let Ok(mut bound) = send.lock() {
                            *bound += x[0];
                        }
                    });
            }

            let load_probe1 = graph_index.forward.handle.clone();
            let load_probe2 = graph_index.reverse.handle.clone();

            (graph_input1, graph_input2, delta_input, motifs.probe(), load_probe1, load_probe2, handles)
        });

        // start the experiment!
        let start = ::std::time::Instant::now();
        let mut remaining = pre_load;
        let max_vertex = pre_load as Node;
        let prev_time = input_delta.time().clone();
        input_delta.advance_to(prev_time.inner + 1);

    if number_files == 1 {
        // Open the path in read-only mode, returns `io::Result<File>`
        let mut lines = match File::open(&Path::new(&filename)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&filename).display(),
                       Error::description(&why))
            },
        };
        // load up the graph, using the first `limit` lines in the file.
        for (counter, line) in lines.by_ref().take(pre_load).enumerate() {
            // each worker is responsible for a fraction of the queries
            if counter % peers == index {
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                  let mut elements = good_line[..].split_whitespace();
                  let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                  let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                  input_graph1.send((src, dst));
                }
            }
        }
          // synchronize with other workers before reporting data loaded.
        input_graph1.close();
        root.step_while(|| load_probe1.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tforward index loaded", start.elapsed(), index);
        //
        // REPEAT ABOVE
        // Open the path in read-only mode, returns `io::Result<File>`
        let mut lines = match File::open(&Path::new(&filename)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&filename).display(),
                       Error::description(&why))
            },
        };
        // load up the graph, using the first `limit` lines in the file.
        for (counter, line) in lines.by_ref().take(pre_load).enumerate() {
            // each worker is responsible for a fraction of the queries
            if counter % peers == index {
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                    let mut elements = good_line[..].split_whitespace();
                    let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                    let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                    input_graph2.send((src, dst));
                }
            }
        }

        // synchronize with other workers before reporting data loaded.
        input_graph2.close();
        root.step_while(|| load_probe2.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\treverse index loaded", start.elapsed(), index);

        // END REPEAT
    } else {

    println!("Multiple files...");

      for p in 0..number_files {
      if p % peers != index {
            // each partition will be handeled by one worker only.
            continue;
           }

        let mut p_str = filename.clone().to_string();
        if p / 10 == 0{
           p_str = p_str + "0000"+ &(p.to_string());
        }
        else if p / 100 == 0{
           p_str = p_str + "000"+ &(p.to_string());
        }
        else if p / 1000 == 0{
           p_str = p_str + "00"+ &(p.to_string());
        }
        else if p / 10000 == 0{
           p_str = p_str + "0"+ &(p.to_string());
        }
        else {
           p_str = p_str + &(p.to_string());
        }

        println!("worker{:?} --> filename: {:?} {:?}", index,p, p_str);

          let mut lines = match File::open(&Path::new(&p_str)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&p_str).display(),
                       Error::description(&why))
            },
        };

          remaining = 0;

        // load up all lines in the file.
        for (counter, line) in lines.by_ref().enumerate() {
          // count edges
          remaining = remaining +1 ;
           // each worker should load all available edges. Note that each partition is handled by one worker only.
           let good_line = line.ok().expect("EXCEPTION: read error");
           if !good_line.starts_with('#') && good_line.len() > 0 {
               let mut elements = good_line[..].split_whitespace();
               let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
               let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
               input_graph1.send((src, dst)); // send each edge to its responsible worker;
            }
        }

      } // end loop on files for forward

        // synchronize with other workers before reporting data loaded.
        input_graph1.close();
        root.step_while(|| load_probe1.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tforward index loaded", start.elapsed(), index);

        // REPEAT ABOVE

    for p in 0..number_files {
      if p % peers != index {
            // each partition will be handeled by one worker only.
            continue;
           }

        let mut p_str = filename.clone().to_string();
        if p / 10 == 0{
           p_str = p_str + "0000"+ &(p.to_string());
        }
        else if p / 100 == 0{
           p_str = p_str + "000"+ &(p.to_string());
        }
        else if p / 1000 == 0{
           p_str = p_str + "00"+ &(p.to_string());
        }
        else if p / 10000 == 0{
           p_str = p_str + "0"+ &(p.to_string());
        }
        else {
           p_str = p_str + &(p.to_string());
        }

        println!("worker{:?} --> filename: {:?} {:?}", index,p, p_str);

        let mut lines = match File::open(&Path::new(&p_str)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&p_str).display(),
                       Error::description(&why))
            },
        };

        remaining = 0;

        // Open the path in read-only mode, returns `io::Result<File>`
        let mut lines = match File::open(&Path::new(&p_str)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&p_str).display(),
                       Error::description(&why))
            },
        };

        // load up the graph, using the first `limit` lines in the file.
        for (counter, line) in lines.by_ref().enumerate() {
            // each worker is responsible for a fraction of the queries
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                   let mut elements = good_line[..].split_whitespace();
                   let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                   let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                   input_graph2.send((src, dst));
                }
        }

    }//end loop on files

    // synchronize with other workers before reporting data loaded.
    input_graph2.close();
    root.step_while(|| load_probe2.less_than(input_delta.time()));
    println!("{:?}\t[worker {}]\treverse index loaded", start.elapsed(), index);

    // END REPEAT

    }// end if there are multi files

        // loop { }

        // merge all of the indices the worker maintains.
        let prev_time = input_delta.time().clone();
        handles.merge_to(&prev_time);

        // synchronize with other workers before reporting indices merged.
        let prev_time = input_delta.time().clone();
        // input_graph.advance_to(prev_time.inner + 1);
        input_delta.advance_to(prev_time.inner + 1);
        root.step_while(|| probe.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tindices merged", start.elapsed(), index);

        let lines = match File::open(&Path::new(&query_filename)) {
            Ok(file) => BufReader::new(file).lines(),
            Err(why) => {
                panic!("EXCEPTION: couldn't open {}: {}",
                       Path::new(&query_filename).display(),
                       Error::description(&why))
            },
        };

        let mut batch = Vec::new();     // current accumulated batch.
        let mut batches = Vec::new();   // all batches listed out.

        // parse, filter queries this worker will introduce.
        for (query_counter, line) in lines.enumerate() {

            // each worker is responsible for a fraction of the queries
            if query_counter % peers == index {
                let good_line = line.ok().expect("EXCEPTION: read error");
                if !good_line.starts_with('#') && good_line.len() > 0 {
                    let mut elements = good_line[..].split_whitespace();
                    let src: Node = elements.next().unwrap().parse().ok().expect("malformed src");
                    let dst: Node = elements.next().unwrap().parse().ok().expect("malformed dst");
                    // input_delta.send(((src, dst), 1));
                    batch.push((src, dst));
                }
            }

            // synchronize and merge indices.
            if query_counter % query_batch == (query_batch - 1) {
                batches.push(batch);
                batch = Vec::new();
            }
        }
        if batch.len() > 0 { batches.push(batch); }

        let prev_time = input_delta.time().clone();
        input_delta.advance_to(prev_time.inner + 1);
        root.step_while(|| probe.less_than(input_delta.time()));
        println!("{:?}\t[worker {}]\tquery input batches parsed (number: {}).", start.elapsed(), index, batches.len());

        for round in 0 .. batches.len() {

            for (src, dst) in batches[round].drain(..) {
                input_delta.send(((src, dst), 1));
            }

            let prev_time = input_delta.time().clone();
            input_delta.advance_to(prev_time.inner + 1);
            root.step_while(|| probe.less_than(input_delta.time()));
            handles.merge_to(&prev_time);
            println!("{:?}\t[worker {}]\tquery input batch {} complete.", start.elapsed(), index, round);

        }

        println!("{:?}\t[worker {}]\tall query batches complete.", start.elapsed(), index);

    }).unwrap();

    let total = send2.lock().map(|x| *x).unwrap_or(0);
    println!("elapsed: {:?}\ttotal motifs at this process: {:?}", start.elapsed(), total);
}<|endoftext|>


<commit_before>#[derive(Debug)]
pub struct Blueprint;
<commit_msg>Update `blueprint` entities and documentation<commit_after>//! Declarative API to build inference engines.

use fuzzy::membership::FuzzyMembershipFn;
use fuzzy::norm::{FuzzyNorm, Maximum, Minimum};
use util::{range, min_max};

/// Public representation of an inference engine.
#[derive(Debug)]
pub struct Blueprint<T: FuzzyNorm, S: FuzzyNorm, I: FuzzyNorm> {
  /// Optional engine name.
  pub name: Option<String>,
  /// Conjunction t-norm.
  pub conjunction: T,
  /// Disjunction s-norm.
  pub disjunction: S,
  /// Implication t-norm.
  pub implication: I,
  /// Set of fuzzy inputs.
  pub inputs: Vec<FuzzyInput>,
  /// Set of fuzzy outputs.
  pub outputs: Vec<FuzzyOutput>,
  /// Inference rules.
  pub rule_base: Vec<FuzzyRule>,
}

/// Blueprint of common Mamdani inference engine.
pub type MaxMin = Blueprint<Minimum, Maximum, Minimum>;

impl MaxMin {
  /// Generates a default instance of a `MaxMin` blueprint.
  pub fn default() -> Self {
    Blueprint {
      name: None,
      conjunction: Minimum {},
      disjunction: Maximum {},
      implication: Minimum {},
      inputs: Vec::new(),
      outputs: Vec::new(),
      rule_base: Vec::new(),
    }
  }
}

/// Input variable of inference engine.
#[derive(Debug)]
pub struct FuzzyInput {
  /// Unique input variable name.
  pub name: String,
  /// Universe of discourse.
  pub range: DiscreteRange,
  /// Set of linguistic values.
  pub terms: Vec<Term>,
}

/// Output variable of inference engine.
#[derive(Debug)]
pub struct FuzzyOutput/*<A, D> where A: FuzzyNorm*/ {
  /// Unique output variable name.
  pub name: String,
  /// Universe of discourse.
  pub range: DiscreteRange,
  // TODO: define aggregation and defuzzifier types
  //pub aggregation: A,
  //pub defuzzifier: D,
  /// Set of linguistic values.
  pub terms: Vec<Term>,
}

/// Inference rule representation.
#[derive(Debug)]
pub struct FuzzyRule {}

/// Iterator over a finite set of `f64` values.
#[derive(Debug, Clone)]
pub struct DiscreteRange {
  discrete_points: usize,
  point: usize,
  start: f64,
  end: f64,
  step: f64,
}

impl DiscreteRange {
  /// Creates a new `DiscreteRange` instance.
  pub fn from(a: f64, b: f64, pts: usize) -> Self {
    let (a, b) = min_max(a, b);

    DiscreteRange {
      discrete_points: pts,
      start: a,
      end: b,
      point: 0,
      step: range(a, b) / (pts - 1) as f64,
    }
  }
}

/// Fuzzy linguistic term.
#[derive(Debug)]
pub struct Term {
  /// Unique name.
  pub name: String,
  /// Membership function.
  pub mf: Box<FuzzyMembershipFn>,
}

impl Iterator for DiscreteRange {
  type Item = f64;

  fn next(&mut self) -> Option<f64> {
    self.point += 1;

    if self.point > self.discrete_points {
      None
    } else {
      Some(self.start + ((self.point - 1) as f64) * self.step)
    }
  }
}

#[cfg(test)]
mod tests {
  use super::{DiscreteRange, Term};
  use fuzzy::membership::Triangular;

  #[test]
  fn test() {
    let (start, end) = (1f64, 10f64);
    let total_points: usize = 20;

    let iter = DiscreteRange::from(start, end, total_points);

    assert_eq!(iter.clone().count(), total_points);

    println!("Range: {:?}",
             iter.enumerate().collect::<Vec<(usize, f64)>>());
  }

  #[test]
  fn debug_mf() {
    let name = String::from("test");
    let term = Term {
      name: name.clone(),
      mf: Box::new(Triangular(1., 2., 3.)),
    };

    assert_eq!(name, term.name);

    println!("{:?}", term);
  }
}
<|endoftext|>


<commit_before>use futures::{Future, future};
use futures::sync::oneshot;
use std::collections::HashMap;
use std::time::Instant;
use uuid::Uuid;

#[derive(Debug, PartialEq)]
pub enum Error {
    Invalid,
    NotFound,
    Dropped,
    NotReady,
    Eof,
    Other,
}

pub const MAX_SIZE: usize = 65536;

#[derive(Clone, Debug)]
pub enum Chunk {
    Data(Vec<u8>),
    Eof,
}

pub struct Flow {
    pub id: String,
    pub size: Option<u64>,
    active_stamp: Instant,
    next_index: u64,
    pub tail_index: u64,
    chunk_bucket: HashMap<u64, Chunk>,
    wait_list: HashMap<u64, Vec<oneshot::Sender<Chunk>>>,
    pub stat_pushed: u64,
    pub stat_dropped: u64,
}

type FlowFuture<T> = future::BoxFuture<T, Error>;

impl Flow {
    pub fn new(size: Option<u64>) -> Self {
        Flow {
            id: Uuid::new_v4().simple().to_string(),
            size: size,
            active_stamp: Instant::now(),
            next_index: 0,
            tail_index: 0,
            chunk_bucket: HashMap::new(),
            wait_list: HashMap::new(),
            stat_pushed: 0,
            stat_dropped: 0,
        }
    }

    fn push_chunk(&mut self, chunk: Chunk) -> FlowFuture<u64> {
        let chunk_index = self.next_index;
        self.next_index += 1;
        // Wake up the waiting fetch.
        if let Some(waits) = self.wait_list.remove(&chunk_index) {
            for wait in waits {
                wait.send(chunk.clone()).unwrap();
            }
        }
        // Insert the chunk.
        self.chunk_bucket.insert(chunk_index, chunk);
        future::ok(chunk_index).boxed()
    }

    pub fn close(&mut self) -> FlowFuture<()> {
        self.push_chunk(Chunk::Eof).map(|_| ()).boxed()
    }

    pub fn push(&mut self, data: &[u8]) -> FlowFuture<u64> {
        if data.len() > MAX_SIZE {
            return future::err(Error::Invalid).boxed();
        }
        if let Some(size) = self.size {
            if (data.len() as u64) + self.stat_pushed > size {
                return future::err(Error::Invalid).boxed();
            }
        }
        self.stat_pushed += data.len() as u64;
        self.push_chunk(Chunk::Data(data.to_vec()))
    }

    pub fn pull(&mut self, chunk_index: u64, timeout: Option<u64>) -> FlowFuture<Vec<u8>> {
        if let Some(chunk) = self.chunk_bucket.get(&chunk_index) {
            return match *chunk {
                           Chunk::Data(ref data) => future::ok(data.clone()),
                           Chunk::Eof => future::err(Error::Eof),
                       }
                       .boxed();
        } else if chunk_index < self.next_index {
            return future::err(Error::Dropped).boxed();
        } else if let Some(0) = timeout {
            return future::err(Error::NotReady).boxed();
        }
        let (tx, rx) = oneshot::channel();
        let waits = self.wait_list.entry(chunk_index).or_insert(Vec::new());
        waits.push(tx);
        rx.map_err(|_| Error::Other)
            .and_then(|chunk| match chunk {
                          Chunk::Data(ref data) => future::ok(data.clone()),
                          Chunk::Eof => future::err(Error::Eof),
                      })
            .boxed()
    }
}

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

    #[test]
    fn basic_flow_operations() {
        let mut flow = Flow::new(None);
        assert_eq!(flow.push(&[1u8; 1234]).wait(), Ok(0));
        assert_eq!(flow.push(&[2u8; MAX_SIZE]).wait(), Ok(1));
        assert_eq!(flow.push(b"hello").wait(), Ok(2));
        assert_eq!(flow.push(&[2u8; MAX_SIZE + 1]).wait(), Err(Error::Invalid));
        assert_eq!(flow.pull(2, Some(0)).wait(), Ok(Vec::from(b"hello" as &[u8])));
        assert_eq!(flow.pull(100, Some(0)).wait(), Err(Error::NotReady));
    }

    #[test]
    fn fixed_size_flow() {
        let mut flow = Flow::new(Some(10));
        assert_eq!(flow.push(b"hello").wait(), Ok(0));
        assert_eq!(flow.push(b"world").wait(), Ok(1));
        assert_eq!(flow.push(b"!").wait(), Err(Error::Invalid));
    }

    #[test]
    fn pull_chunk() {
        let mut flow = Flow::new(None);
        assert_eq!(flow.pull(0, Some(0)).wait(), Err(Error::NotReady));
        assert_eq!(flow.pull(1, None).join3(flow.push(b"ello"), flow.push(b"hello")).wait(),
                   Ok((Vec::from(b"hello" as &[u8]), 0, 1)));
    }

    #[test]
    fn close_flow() {
        let mut flow = Flow::new(None);
        assert_eq!(flow.push(b"hello").wait(), Ok(0));
        assert_eq!(flow.pull(0, Some(0)).wait(), Ok(Vec::from(b"hello" as &[u8])));
        assert_eq!(flow.close().wait(), Ok(()));
        assert_eq!(flow.pull(1, Some(0)).wait(), Err(Error::Eof));
    }
}
<commit_msg>Add chunk counter, use shared chunk<commit_after>use futures::{Future, future};
use futures::sync::oneshot;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::Instant;
use uuid::Uuid;

#[derive(Debug, PartialEq)]
pub enum Error {
    Invalid,
    NotFound,
    Dropped,
    NotReady,
    Eof,
    Other,
}

pub const MAX_SIZE: usize = 65536;

#[derive(Clone, Debug)]
pub enum Chunk {
    Data(u64, Vec<u8>),
    Eof(u64),
}

type SharedChunk = Arc<RwLock<Chunk>>;

pub struct Flow {
    pub id: String,
    pub size: Option<u64>,
    active_stamp: Instant,
    next_index: u64,
    pub tail_index: u64,
    chunk_bucket: HashMap<u64, SharedChunk>,
    wait_list: HashMap<u64, Vec<oneshot::Sender<SharedChunk>>>,
    pub stat_pushed: u64,
    pub stat_dropped: u64,
}

type FlowFuture<T> = future::BoxFuture<T, Error>;

impl Flow {
    pub fn new(size: Option<u64>) -> Self {
        Flow {
            id: Uuid::new_v4().simple().to_string(),
            size: size,
            active_stamp: Instant::now(),
            next_index: 0,
            tail_index: 0,
            chunk_bucket: HashMap::new(),
            wait_list: HashMap::new(),
            stat_pushed: 0,
            stat_dropped: 0,
        }
    }

    fn acquire_chunk(&mut self, chunk: Chunk) -> FlowFuture<u64> {
        let shared_chunk = Arc::new(RwLock::new(chunk));
        let chunk_index = self.next_index;
        self.next_index += 1;
        // Wake up the waiting fetch.
        if let Some(waits) = self.wait_list.remove(&chunk_index) {
            for wait in waits {
                // Don't unwrap, since the receiver can early quit.
                wait.send(shared_chunk.clone()).is_ok();
            }
        }
        // Insert the chunk.
        self.chunk_bucket.insert(chunk_index, shared_chunk);
        future::ok(chunk_index).boxed()
    }

    pub fn close(&mut self) -> FlowFuture<()> {
        self.acquire_chunk(Chunk::Eof(1)).map(|_| ()).boxed()
    }

    pub fn push(&mut self, data: &[u8]) -> FlowFuture<u64> {
        if data.len() > MAX_SIZE {
            return future::err(Error::Invalid).boxed();
        }
        if let Some(size) = self.size {
            if (data.len() as u64) + self.stat_pushed > size {
                return future::err(Error::Invalid).boxed();
            }
        }
        self.stat_pushed += data.len() as u64;
        self.acquire_chunk(Chunk::Data(1, data.to_vec()))
    }

    fn deref_chunk(shared_chunk: &SharedChunk) -> Result<Vec<u8>, Error> {
        let mut chunk = shared_chunk.write().unwrap();
        match *chunk {
            Chunk::Data(ref mut count, ref data) => {
                *count += 1;
                Ok(data.to_vec())
            }
            Chunk::Eof(ref mut count) => {
                *count += 1;
                Err(Error::Eof)
            }
        }
    }

    pub fn pull(&mut self, chunk_index: u64, timeout: Option<u64>) -> FlowFuture<Vec<u8>> {
        if let Some(chunk) = self.chunk_bucket.get_mut(&chunk_index) {
            return future::result(Flow::deref_chunk(chunk)).boxed();
        } else if chunk_index < self.next_index {
            return future::err(Error::Dropped).boxed();
        } else if let Some(0) = timeout {
            return future::err(Error::NotReady).boxed();
        }
        let (tx, rx) = oneshot::channel();
        let waits = self.wait_list.entry(chunk_index).or_insert(Vec::new());
        waits.push(tx);
        rx.map_err(|_| Error::Other)
            .and_then(|chunk| future::result(Flow::deref_chunk(&chunk)))
            .boxed()
    }
}

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

    #[test]
    fn basic_flow_operations() {
        let mut flow = Flow::new(None);
        assert_eq!(flow.push(&[1u8; 1234]).wait(), Ok(0));
        assert_eq!(flow.push(&[2u8; MAX_SIZE]).wait(), Ok(1));
        assert_eq!(flow.push(b"hello").wait(), Ok(2));
        assert_eq!(flow.push(&[2u8; MAX_SIZE + 1]).wait(), Err(Error::Invalid));
        assert_eq!(flow.pull(2, Some(0)).wait(), Ok(Vec::from(b"hello" as &[u8])));
        assert_eq!(flow.pull(100, Some(0)).wait(), Err(Error::NotReady));
    }

    #[test]
    fn fixed_size_flow() {
        let mut flow = Flow::new(Some(10));
        assert_eq!(flow.push(b"hello").wait(), Ok(0));
        assert_eq!(flow.push(b"world").wait(), Ok(1));
        assert_eq!(flow.push(b"!").wait(), Err(Error::Invalid));
    }

    #[test]
    fn pull_chunk() {
        let mut flow = Flow::new(None);
        assert_eq!(flow.pull(0, Some(0)).wait(), Err(Error::NotReady));
        assert_eq!(flow.pull(1, None).join3(flow.push(b"ello"), flow.push(b"hello")).wait(),
                   Ok((Vec::from(b"hello" as &[u8]), 0, 1)));
    }

    #[test]
    fn close_flow() {
        let mut flow = Flow::new(None);
        assert_eq!(flow.push(b"hello").wait(), Ok(0));
        assert_eq!(flow.pull(0, Some(0)).wait(), Ok(Vec::from(b"hello" as &[u8])));
        assert_eq!(flow.close().wait(), Ok(()));
        assert_eq!(flow.pull(1, Some(0)).wait(), Err(Error::Eof));
    }
}
<|endoftext|>


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

use borrow_check::borrow_set::{BorrowSet, BorrowData};
use borrow_check::place_ext::PlaceExt;

use rustc;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::region;
use rustc::mir::{self, Location, Place, Mir};
use rustc::ty::TyCtxt;
use rustc::ty::{RegionKind, RegionVid};
use rustc::ty::RegionKind::ReScope;

use rustc_data_structures::bitslice::{BitwiseOperator, Word};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_set::IdxSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::sync::Lrc;

use dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use dataflow::indexes::BorrowIndex;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;

use std::rc::Rc;

/// `Borrows` stores the data used in the analyses that track the flow
/// of borrows.
///
/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
/// `BorrowIndex`, and maps each such index to a `BorrowData`
/// describing the borrow. These indexes are used for representing the
/// borrows in compact bitvectors.
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'gcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    scope_tree: Lrc<region::ScopeTree>,
    root_scope: Option<region::Scope>,

    borrow_set: Rc<BorrowSet<'tcx>>,
    borrows_out_of_scope_at_location: FxHashMap<Location, Vec<BorrowIndex>>,

    /// NLL region inference context with which NLL queries should be resolved
    _nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
}

fn precompute_borrows_out_of_scope<'a, 'tcx>(
    mir: &'a Mir<'tcx>,
    regioncx: &Rc<RegionInferenceContext<'tcx>>,
    borrows_out_of_scope_at_location: &mut FxHashMap<Location, Vec<BorrowIndex>>,
    borrow_index: BorrowIndex,
    borrow_region: RegionVid,
    location: Location,
) {
    // Keep track of places we've locations to check and locations that we have checked.
    let mut stack = vec![ location ];
    let mut visited = FxHashSet();
    visited.insert(location);

    debug!(
        "borrow {:?} has region {:?} with value {:?}",
        borrow_index,
        borrow_region,
        regioncx.region_value_str(borrow_region),
    );
    debug!("borrow {:?} starts at {:?}", borrow_index, location);
    while let Some(location) = stack.pop() {
        // If region does not contain a point at the location, then add to list and skip
        // successor locations.
        if !regioncx.region_contains(borrow_region, location) {
            debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
            borrows_out_of_scope_at_location
                .entry(location)
                .or_insert(vec![])
                .push(borrow_index);
            continue;
        }

        let bb_data = &mir[location.block];
        // If this is the last statement in the block, then add the
        // terminator successors next.
        if location.statement_index == bb_data.statements.len() {
            // Add successors to locations to visit, if not visited before.
            if let Some(ref terminator) = bb_data.terminator {
                for block in terminator.successors() {
                    let loc = block.start_location();
                    if visited.insert(loc) {
                        stack.push(loc);
                    }
                }
            }
        } else {
            // Visit next statement in block.
            let loc = location.successor_within_block();
            if visited.insert(loc) {
                stack.push(loc);
            }
        }
    }
}

impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
    crate fn new(
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        mir: &'a Mir<'tcx>,
        nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
        def_id: DefId,
        body_id: Option<hir::BodyId>,
        borrow_set: &Rc<BorrowSet<'tcx>>
    ) -> Self {
        let scope_tree = tcx.region_scope_tree(def_id);
        let root_scope = body_id.map(|body_id| {
            region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
        });

        let mut borrows_out_of_scope_at_location = FxHashMap();
        for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() {
            let borrow_region = borrow_data.region.to_region_vid();
            let location = borrow_set.borrows[borrow_index].reserve_location;

            precompute_borrows_out_of_scope(mir, &nonlexical_regioncx,
                                            &mut borrows_out_of_scope_at_location,
                                            borrow_index, borrow_region, location);
        }

        Borrows {
            tcx: tcx,
            mir: mir,
            borrow_set: borrow_set.clone(),
            borrows_out_of_scope_at_location,
            scope_tree,
            root_scope,
            _nonlexical_regioncx: nonlexical_regioncx,
        }
    }

    crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrow_set.borrows }
    pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree }

    pub fn location(&self, idx: BorrowIndex) -> &Location {
        &self.borrow_set.borrows[idx].reserve_location
    }

    /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
    /// That means either they went out of either a nonlexical scope, if we care about those
    /// at the moment, or the location represents a lexical EndRegion
    fn kill_loans_out_of_scope_at_location(&self,
                                           sets: &mut BlockSets<BorrowIndex>,
                                           location: Location) {
        // NOTE: The state associated with a given `location`
        // reflects the dataflow on entry to the statement.
        // Iterate over each of the borrows that we've precomputed
        // to have went out of scope at this location and kill them.
        //
        // We are careful always to call this function *before* we
        // set up the gen-bits for the statement or
        // termanator. That way, if the effect of the statement or
        // terminator *does* introduce a new loan of the same
        // region, then setting that gen-bit will override any
        // potential kill introduced here.
        if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
            for index in indices {
                sets.kill(&index);
            }
        }
    }

    fn kill_borrows_on_local(&self,
                             sets: &mut BlockSets<BorrowIndex>,
                             local: &rustc::mir::Local)
    {
        if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
            sets.kill_all(borrow_indexes);
        }
    }
}

impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
    type Idx = BorrowIndex;
    fn name() -> &'static str { "borrows" }
    fn bits_per_block(&self) -> usize {
        self.borrow_set.borrows.len() * 2
    }

    fn start_block_effect(&self, _entry_set: &mut IdxSet<BorrowIndex>) {
        // no borrows of code region_scopes have been taken prior to
        // function execution, so this method has no effect on
        // `_sets`.
    }

    fn before_statement_effect(&self,
                               sets: &mut BlockSets<BorrowIndex>,
                               location: Location) {
        debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });
        let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
            panic!("could not find statement at location {:?}");
        });

        match stmt.kind {
            mir::StatementKind::EndRegion(_) => {
            }

            mir::StatementKind::Assign(ref lhs, ref rhs) => {
                // Make sure there are no remaining borrows for variables
                // that are assigned over.
                if let Place::Local(ref local) = *lhs {
                    // FIXME: Handle the case in which we're assigning over
                    // a projection (`foo.bar`).
                    self.kill_borrows_on_local(sets, local);
                }

                // NOTE: if/when the Assign case is revised to inspect
                // the assigned_place here, make sure to also
                // re-consider the current implementations of the
                // propagate_call_return method.

                if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
                    if place.is_unsafe_place(self.tcx, self.mir) { return; }
                    let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
                        panic!("could not find BorrowIndex for location {:?}", location);
                    });

                    if let RegionKind::ReEmpty = region {
                        // If the borrowed value dies before the borrow is used, the region for
                        // the borrow can be empty. Don't track the borrow in that case.
                        debug!("Borrows::statement_effect_on_borrows \
                                location: {:?} stmt: {:?} has empty region, killing {:?}",
                               location, stmt.kind, index);
                        sets.kill(&index);
                        return
                    } else {
                        debug!("Borrows::statement_effect_on_borrows location: {:?} stmt: {:?}",
                               location, stmt.kind);
                    }

                    assert!(self.borrow_set.region_map.get(region).unwrap_or_else(|| {
                        panic!("could not find BorrowIndexs for region {:?}", region);
                    }).contains(&index));
                    sets.gen(&index);

                    // Issue #46746: Two-phase borrows handles
                    // stmts of form `Tmp = &mut Borrow` ...
                    match lhs {
                        Place::Local(..) | Place::Static(..) => {} // okay
                        Place::Projection(..) => {
                            // ... can assign into projections,
                            // e.g. `box (&mut _)`. Current
                            // conservative solution: force
                            // immediate activation here.
                            sets.gen(&index);
                        }
                    }
                }
            }

            mir::StatementKind::StorageDead(local) => {
                // Make sure there are no remaining borrows for locals that
                // are gone out of scope.
                self.kill_borrows_on_local(sets, &local)
            }

            mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
                for (output, kind) in outputs.iter().zip(&asm.outputs) {
                    if !kind.is_indirect && !kind.is_rw {
                        // Make sure there are no remaining borrows for direct
                        // output variables.
                        if let Place::Local(ref local) = *output {
                            // FIXME: Handle the case in which we're assigning over
                            // a projection (`foo.bar`).
                            self.kill_borrows_on_local(sets, local);
                        }
                    }
                }
            }

            mir::StatementKind::ReadForMatch(..) |
            mir::StatementKind::SetDiscriminant { .. } |
            mir::StatementKind::StorageLive(..) |
            mir::StatementKind::Validate(..) |
            mir::StatementKind::UserAssertTy(..) |
            mir::StatementKind::Nop => {}

        }
    }

    fn before_terminator_effect(&self,
                                sets: &mut BlockSets<BorrowIndex>,
                                location: Location) {
        debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn terminator_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });

        let term = block.terminator();
        match term.kind {
            mir::TerminatorKind::Resume |
            mir::TerminatorKind::Return |
            mir::TerminatorKind::GeneratorDrop => {
                // When we return from the function, then all `ReScope`-style regions
                // are guaranteed to have ended.
                // Normally, there would be `EndRegion` statements that come before,
                // and hence most of these loans will already be dead -- but, in some cases
                // like unwind paths, we do not always emit `EndRegion` statements, so we
                // add some kills here as a "backup" and to avoid spurious error messages.
                for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() {
                    if let ReScope(scope) = borrow_data.region {
                        // Check that the scope is not actually a scope from a function that is
                        // a parent of our closure. Note that the CallSite scope itself is
                        // *outside* of the closure, for some weird reason.
                        if let Some(root_scope) = self.root_scope {
                            if *scope != root_scope &&
                                self.scope_tree.is_subscope_of(*scope, root_scope)
                            {
                                sets.kill(&borrow_index);
                            }
                        }
                    }
                }
            }
            mir::TerminatorKind::Abort |
            mir::TerminatorKind::SwitchInt {..} |
            mir::TerminatorKind::Drop {..} |
            mir::TerminatorKind::DropAndReplace {..} |
            mir::TerminatorKind::Call {..} |
            mir::TerminatorKind::Assert {..} |
            mir::TerminatorKind::Yield {..} |
            mir::TerminatorKind::Goto {..} |
            mir::TerminatorKind::FalseEdges {..} |
            mir::TerminatorKind::FalseUnwind {..} |
            mir::TerminatorKind::Unreachable => {}
        }
    }

    fn propagate_call_return(&self,
                             _in_out: &mut IdxSet<BorrowIndex>,
                             _call_bb: mir::BasicBlock,
                             _dest_bb: mir::BasicBlock,
                             _dest_place: &mir::Place) {
        // there are no effects on borrows from method call return...
        //
        // ... but if overwriting a place can affect flow state, then
        // latter is not true; see NOTE on Assign case in
        // statement_effect_on_borrows.
    }
}

impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn join(&self, pred1: Word, pred2: Word) -> Word {
        pred1 | pred2 // union effects of preds when computing reservations
    }
}

impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn bottom_value() -> bool {
        false // bottom = nothing is reserved or activated yet
    }
}

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

use borrow_check::borrow_set::{BorrowSet, BorrowData};
use borrow_check::place_ext::PlaceExt;

use rustc;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::region;
use rustc::mir::{self, Location, Place, Mir};
use rustc::ty::TyCtxt;
use rustc::ty::{RegionKind, RegionVid};
use rustc::ty::RegionKind::ReScope;

use rustc_data_structures::bitslice::{BitwiseOperator, Word};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_set::IdxSet;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::sync::Lrc;

use dataflow::{BitDenotation, BlockSets, InitialFlow};
pub use dataflow::indexes::BorrowIndex;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;

use std::rc::Rc;

/// `Borrows` stores the data used in the analyses that track the flow
/// of borrows.
///
/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
/// `BorrowIndex`, and maps each such index to a `BorrowData`
/// describing the borrow. These indexes are used for representing the
/// borrows in compact bitvectors.
pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'gcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    scope_tree: Lrc<region::ScopeTree>,
    root_scope: Option<region::Scope>,

    borrow_set: Rc<BorrowSet<'tcx>>,
    borrows_out_of_scope_at_location: FxHashMap<Location, Vec<BorrowIndex>>,

    /// NLL region inference context with which NLL queries should be resolved
    _nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
}

fn precompute_borrows_out_of_scope<'tcx>(
    mir: &Mir<'tcx>,
    regioncx: &Rc<RegionInferenceContext<'tcx>>,
    borrows_out_of_scope_at_location: &mut FxHashMap<Location, Vec<BorrowIndex>>,
    borrow_index: BorrowIndex,
    borrow_region: RegionVid,
    location: Location,
) {
    // Keep track of places we've locations to check and locations that we have checked.
    let mut stack = vec![ location ];
    let mut visited = FxHashSet();
    visited.insert(location);

    debug!(
        "borrow {:?} has region {:?} with value {:?}",
        borrow_index,
        borrow_region,
        regioncx.region_value_str(borrow_region),
    );
    debug!("borrow {:?} starts at {:?}", borrow_index, location);
    while let Some(location) = stack.pop() {
        // If region does not contain a point at the location, then add to list and skip
        // successor locations.
        if !regioncx.region_contains(borrow_region, location) {
            debug!("borrow {:?} gets killed at {:?}", borrow_index, location);
            borrows_out_of_scope_at_location
                .entry(location)
                .or_insert(vec![])
                .push(borrow_index);
            continue;
        }

        let bb_data = &mir[location.block];
        // If this is the last statement in the block, then add the
        // terminator successors next.
        if location.statement_index == bb_data.statements.len() {
            // Add successors to locations to visit, if not visited before.
            if let Some(ref terminator) = bb_data.terminator {
                for block in terminator.successors() {
                    let loc = block.start_location();
                    if visited.insert(loc) {
                        stack.push(loc);
                    }
                }
            }
        } else {
            // Visit next statement in block.
            let loc = location.successor_within_block();
            if visited.insert(loc) {
                stack.push(loc);
            }
        }
    }
}

impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
    crate fn new(
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        mir: &'a Mir<'tcx>,
        nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
        def_id: DefId,
        body_id: Option<hir::BodyId>,
        borrow_set: &Rc<BorrowSet<'tcx>>
    ) -> Self {
        let scope_tree = tcx.region_scope_tree(def_id);
        let root_scope = body_id.map(|body_id| {
            region::Scope::CallSite(tcx.hir.body(body_id).value.hir_id.local_id)
        });

        let mut borrows_out_of_scope_at_location = FxHashMap();
        for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() {
            let borrow_region = borrow_data.region.to_region_vid();
            let location = borrow_set.borrows[borrow_index].reserve_location;

            precompute_borrows_out_of_scope(mir, &nonlexical_regioncx,
                                            &mut borrows_out_of_scope_at_location,
                                            borrow_index, borrow_region, location);
        }

        Borrows {
            tcx: tcx,
            mir: mir,
            borrow_set: borrow_set.clone(),
            borrows_out_of_scope_at_location,
            scope_tree,
            root_scope,
            _nonlexical_regioncx: nonlexical_regioncx,
        }
    }

    crate fn borrows(&self) -> &IndexVec<BorrowIndex, BorrowData<'tcx>> { &self.borrow_set.borrows }
    pub fn scope_tree(&self) -> &Lrc<region::ScopeTree> { &self.scope_tree }

    pub fn location(&self, idx: BorrowIndex) -> &Location {
        &self.borrow_set.borrows[idx].reserve_location
    }

    /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
    /// That means either they went out of either a nonlexical scope, if we care about those
    /// at the moment, or the location represents a lexical EndRegion
    fn kill_loans_out_of_scope_at_location(&self,
                                           sets: &mut BlockSets<BorrowIndex>,
                                           location: Location) {
        // NOTE: The state associated with a given `location`
        // reflects the dataflow on entry to the statement.
        // Iterate over each of the borrows that we've precomputed
        // to have went out of scope at this location and kill them.
        //
        // We are careful always to call this function *before* we
        // set up the gen-bits for the statement or
        // termanator. That way, if the effect of the statement or
        // terminator *does* introduce a new loan of the same
        // region, then setting that gen-bit will override any
        // potential kill introduced here.
        if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
            for index in indices {
                sets.kill(&index);
            }
        }
    }

    fn kill_borrows_on_local(&self,
                             sets: &mut BlockSets<BorrowIndex>,
                             local: &rustc::mir::Local)
    {
        if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
            sets.kill_all(borrow_indexes);
        }
    }
}

impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
    type Idx = BorrowIndex;
    fn name() -> &'static str { "borrows" }
    fn bits_per_block(&self) -> usize {
        self.borrow_set.borrows.len() * 2
    }

    fn start_block_effect(&self, _entry_set: &mut IdxSet<BorrowIndex>) {
        // no borrows of code region_scopes have been taken prior to
        // function execution, so this method has no effect on
        // `_sets`.
    }

    fn before_statement_effect(&self,
                               sets: &mut BlockSets<BorrowIndex>,
                               location: Location) {
        debug!("Borrows::before_statement_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });
        let stmt = block.statements.get(location.statement_index).unwrap_or_else(|| {
            panic!("could not find statement at location {:?}");
        });

        match stmt.kind {
            mir::StatementKind::EndRegion(_) => {
            }

            mir::StatementKind::Assign(ref lhs, ref rhs) => {
                // Make sure there are no remaining borrows for variables
                // that are assigned over.
                if let Place::Local(ref local) = *lhs {
                    // FIXME: Handle the case in which we're assigning over
                    // a projection (`foo.bar`).
                    self.kill_borrows_on_local(sets, local);
                }

                // NOTE: if/when the Assign case is revised to inspect
                // the assigned_place here, make sure to also
                // re-consider the current implementations of the
                // propagate_call_return method.

                if let mir::Rvalue::Ref(region, _, ref place) = *rhs {
                    if place.is_unsafe_place(self.tcx, self.mir) { return; }
                    let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
                        panic!("could not find BorrowIndex for location {:?}", location);
                    });

                    if let RegionKind::ReEmpty = region {
                        // If the borrowed value dies before the borrow is used, the region for
                        // the borrow can be empty. Don't track the borrow in that case.
                        debug!("Borrows::statement_effect_on_borrows \
                                location: {:?} stmt: {:?} has empty region, killing {:?}",
                               location, stmt.kind, index);
                        sets.kill(&index);
                        return
                    } else {
                        debug!("Borrows::statement_effect_on_borrows location: {:?} stmt: {:?}",
                               location, stmt.kind);
                    }

                    assert!(self.borrow_set.region_map.get(region).unwrap_or_else(|| {
                        panic!("could not find BorrowIndexs for region {:?}", region);
                    }).contains(&index));
                    sets.gen(&index);

                    // Issue #46746: Two-phase borrows handles
                    // stmts of form `Tmp = &mut Borrow` ...
                    match lhs {
                        Place::Local(..) | Place::Static(..) => {} // okay
                        Place::Projection(..) => {
                            // ... can assign into projections,
                            // e.g. `box (&mut _)`. Current
                            // conservative solution: force
                            // immediate activation here.
                            sets.gen(&index);
                        }
                    }
                }
            }

            mir::StatementKind::StorageDead(local) => {
                // Make sure there are no remaining borrows for locals that
                // are gone out of scope.
                self.kill_borrows_on_local(sets, &local)
            }

            mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
                for (output, kind) in outputs.iter().zip(&asm.outputs) {
                    if !kind.is_indirect && !kind.is_rw {
                        // Make sure there are no remaining borrows for direct
                        // output variables.
                        if let Place::Local(ref local) = *output {
                            // FIXME: Handle the case in which we're assigning over
                            // a projection (`foo.bar`).
                            self.kill_borrows_on_local(sets, local);
                        }
                    }
                }
            }

            mir::StatementKind::ReadForMatch(..) |
            mir::StatementKind::SetDiscriminant { .. } |
            mir::StatementKind::StorageLive(..) |
            mir::StatementKind::Validate(..) |
            mir::StatementKind::UserAssertTy(..) |
            mir::StatementKind::Nop => {}

        }
    }

    fn before_terminator_effect(&self,
                                sets: &mut BlockSets<BorrowIndex>,
                                location: Location) {
        debug!("Borrows::before_terminator_effect sets: {:?} location: {:?}", sets, location);
        self.kill_loans_out_of_scope_at_location(sets, location);
    }

    fn terminator_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
        debug!("Borrows::terminator_effect sets: {:?} location: {:?}", sets, location);

        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
            panic!("could not find block at location {:?}", location);
        });

        let term = block.terminator();
        match term.kind {
            mir::TerminatorKind::Resume |
            mir::TerminatorKind::Return |
            mir::TerminatorKind::GeneratorDrop => {
                // When we return from the function, then all `ReScope`-style regions
                // are guaranteed to have ended.
                // Normally, there would be `EndRegion` statements that come before,
                // and hence most of these loans will already be dead -- but, in some cases
                // like unwind paths, we do not always emit `EndRegion` statements, so we
                // add some kills here as a "backup" and to avoid spurious error messages.
                for (borrow_index, borrow_data) in self.borrow_set.borrows.iter_enumerated() {
                    if let ReScope(scope) = borrow_data.region {
                        // Check that the scope is not actually a scope from a function that is
                        // a parent of our closure. Note that the CallSite scope itself is
                        // *outside* of the closure, for some weird reason.
                        if let Some(root_scope) = self.root_scope {
                            if *scope != root_scope &&
                                self.scope_tree.is_subscope_of(*scope, root_scope)
                            {
                                sets.kill(&borrow_index);
                            }
                        }
                    }
                }
            }
            mir::TerminatorKind::Abort |
            mir::TerminatorKind::SwitchInt {..} |
            mir::TerminatorKind::Drop {..} |
            mir::TerminatorKind::DropAndReplace {..} |
            mir::TerminatorKind::Call {..} |
            mir::TerminatorKind::Assert {..} |
            mir::TerminatorKind::Yield {..} |
            mir::TerminatorKind::Goto {..} |
            mir::TerminatorKind::FalseEdges {..} |
            mir::TerminatorKind::FalseUnwind {..} |
            mir::TerminatorKind::Unreachable => {}
        }
    }

    fn propagate_call_return(&self,
                             _in_out: &mut IdxSet<BorrowIndex>,
                             _call_bb: mir::BasicBlock,
                             _dest_bb: mir::BasicBlock,
                             _dest_place: &mir::Place) {
        // there are no effects on borrows from method call return...
        //
        // ... but if overwriting a place can affect flow state, then
        // latter is not true; see NOTE on Assign case in
        // statement_effect_on_borrows.
    }
}

impl<'a, 'gcx, 'tcx> BitwiseOperator for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn join(&self, pred1: Word, pred2: Word) -> Word {
        pred1 | pred2 // union effects of preds when computing reservations
    }
}

impl<'a, 'gcx, 'tcx> InitialFlow for Borrows<'a, 'gcx, 'tcx> {
    #[inline]
    fn bottom_value() -> bool {
        false // bottom = nothing is reserved or activated yet
    }
}

<|endoftext|>


<commit_before>// This file is part of rss.
//
// Copyright © 2015-2017 The rust-syndication Developers
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the MIT License and/or Apache 2.0 License.

use std::io::{BufRead, Write};

use quick_xml::errors::Error as XmlError;
use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText};
use quick_xml::events::attributes::Attributes;
use quick_xml::reader::Reader;
use quick_xml::writer::Writer;

use error::Error;
use fromxml::FromXml;
use toxml::ToXml;
use util::element_text;

/// Represents the GUID of an RSS item.
#[derive(Debug, Clone, PartialEq, Builder)]
#[builder(setter(into), default)]
pub struct Guid {
    /// The value of the GUID.
    value: String,
    /// Indicates if the GUID is a permalink.
    is_permalink: bool,
}

impl Guid {
    /// Return whether this GUID is a permalink.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_permalink(true);
    /// assert!(guid.is_permalink());
    /// ```
    pub fn is_permalink(&self) -> bool {
        self.is_permalink
    }

    /// Set whether this GUID is a permalink.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_permalink(true);
    /// ```
    pub fn set_permalink<V>(&mut self, is_permalink: V)
    where
        V: Into<bool>,
    {
        self.is_permalink = is_permalink.into()
    }

    /// Return the value of this GUID.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_value("00000000-0000-0000-0000-00000000000");
    /// assert_eq!(guid.value(), "00000000-0000-0000-0000-00000000000");
    /// ```
    pub fn value(&self) -> &str {
        self.value.as_str()
    }

    /// Set the value of this GUID.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_value("00000000-0000-0000-0000-00000000000");
    /// ```
    pub fn set_value<V>(&mut self, value: V)
    where
        V: Into<String>,
    {
        self.value = value.into();
    }
}

impl Default for Guid {
    #[inline]
    fn default() -> Self {
        Guid {
            value: Default::default(),
            is_permalink: true,
        }
    }
}

impl FromXml for Guid {
    fn from_xml<R: BufRead>(reader: &mut Reader<R>, mut atts: Attributes) -> Result<Self, Error> {
        let mut guid = Guid::default();

        for attr in atts.with_checks(false) {
            if let Ok(attr) = attr {
                if attr.key == b"isPermaLink" {
                    guid.is_permalink = attr.value != b"false";
                    break;
                }
            }
        }

        guid.value = element_text(reader)?.unwrap_or_default();
        Ok(guid)
    }
}

impl ToXml for Guid {
    fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
        let name = b"guid";
        let mut element = BytesStart::borrowed(name, name.len());
        if !self.is_permalink {
            element.push_attribute(("isPermaLink", "false"));
        }

        writer.write_event(Event::Start(element))?;

        writer
            .write_event(Event::Text(BytesText::borrowed(self.value.as_bytes())))?;

        writer.write_event(Event::End(BytesEnd::borrowed(name)))?;
        Ok(())
    }
}
<commit_msg>Changed is_permalink property to permalink<commit_after>// This file is part of rss.
//
// Copyright © 2015-2017 The rust-syndication Developers
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the MIT License and/or Apache 2.0 License.

use std::io::{BufRead, Write};

use quick_xml::errors::Error as XmlError;
use quick_xml::events::{Event, BytesStart, BytesEnd, BytesText};
use quick_xml::events::attributes::Attributes;
use quick_xml::reader::Reader;
use quick_xml::writer::Writer;

use error::Error;
use fromxml::FromXml;
use toxml::ToXml;
use util::element_text;

/// Represents the GUID of an RSS item.
#[derive(Debug, Clone, PartialEq, Builder)]
#[builder(setter(into), default)]
pub struct Guid {
    /// The value of the GUID.
    value: String,
    /// Indicates if the GUID is a permalink.
    permalink: bool,
}

impl Guid {
    /// Return whether this GUID is a permalink.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_permalink(true);
    /// assert!(guid.is_permalink());
    /// ```
    pub fn is_permalink(&self) -> bool {
        self.permalink
    }

    /// Set whether this GUID is a permalink.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_permalink(true);
    /// ```
    pub fn set_permalink<V>(&mut self, permalink: V)
    where
        V: Into<bool>,
    {
        self.permalink = permalink.into()
    }

    /// Return the value of this GUID.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_value("00000000-0000-0000-0000-00000000000");
    /// assert_eq!(guid.value(), "00000000-0000-0000-0000-00000000000");
    /// ```
    pub fn value(&self) -> &str {
        self.value.as_str()
    }

    /// Set the value of this GUID.
    ///
    /// # Examples
    ///
    /// ```
    /// use rss::Guid;
    ///
    /// let mut guid = Guid::default();
    /// guid.set_value("00000000-0000-0000-0000-00000000000");
    /// ```
    pub fn set_value<V>(&mut self, value: V)
    where
        V: Into<String>,
    {
        self.value = value.into();
    }
}

impl Default for Guid {
    #[inline]
    fn default() -> Self {
        Guid {
            value: Default::default(),
            permalink: true,
        }
    }
}

impl FromXml for Guid {
    fn from_xml<R: BufRead>(reader: &mut Reader<R>, mut atts: Attributes) -> Result<Self, Error> {
        let mut guid = Guid::default();

        for attr in atts.with_checks(false) {
            if let Ok(attr) = attr {
                if attr.key == b"isPermaLink" {
                    guid.permalink = attr.value != b"false";
                    break;
                }
            }
        }

        guid.value = element_text(reader)?.unwrap_or_default();
        Ok(guid)
    }
}

impl ToXml for Guid {
    fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
        let name = b"guid";
        let mut element = BytesStart::borrowed(name, name.len());
        if !self.permalink {
            element.push_attribute(("isPermaLink", "false"));
        }

        writer.write_event(Event::Start(element))?;

        writer
            .write_event(Event::Text(BytesText::borrowed(self.value.as_bytes())))?;

        writer.write_event(Event::End(BytesEnd::borrowed(name)))?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]
#![feature(phase)]

#![unstable]

#[phase(plugin)]
extern crate compile_msg;

extern crate libc;

use std::sync::Mutex;

use std::thread::Thread;

#[doc(hidden)]
pub mod ffi;

/// This static variable  will store the android_app* on creation, and set it back to 0 at
///  destruction.
/// Apart from this, the static is never written, so there is no risk of race condition.
static mut ANDROID_APP: *mut ffi::android_app = 0 as *mut ffi::android_app;

/// This is the structure that serves as user data in the android_app*
#[doc(hidden)]
struct Context {
    senders: Mutex<Vec<Sender<Event>>>,
}

/// An event triggered by the Android environment.
pub enum Event {
    EventUp,
    EventDown,
    EventMove(i32, i32),
}

impl Copy for Event {}

#[cfg(not(target_os = "android"))]
compile_note!("You are not compiling for Android");

#[macro_export]
macro_rules! android_start(
    ($main: ident) => (
        pub mod __android_start {
            extern crate android_glue;

            // this function is here because we are sure that it will be included by the linker
            // so we call app_dummy in it, in order to be sure that the native glue will be included
            #[start]
            pub fn start(_: int, _: *const *const u8) -> int {
                unsafe { android_glue::ffi::app_dummy() };
                1
            }

            #[no_mangle]
            #[inline(never)]
            #[allow(non_snake_case)]
            pub extern "C" fn android_main(app: *mut ()) {
                android_glue::android_main2(app, move|| super::$main());
            }
        }
    )
);

/// This is the function that must be called by `android_main`
#[doc(hidden)]
pub fn android_main2<F>(app: *mut (), main_function: F)
    where F: FnOnce(), F: Send
{
    use std::{mem, ptr};

    write_log("Entering android_main");

    unsafe { ANDROID_APP = std::mem::transmute(app) };
    let app: &mut ffi::android_app = unsafe { std::mem::transmute(app) };

    // creating the context that will be passed to the callback
    let context = Context { senders: Mutex::new(Vec::new()) };
    app.onAppCmd = commands_callback;
    app.onInputEvent = inputs_callback;
    app.userData = unsafe { std::mem::transmute(&context) };

    // executing the main function in parallel
    let g = Thread::spawn(move|| {
        std::io::stdio::set_stdout(box std::io::LineBufferedWriter::new(ToLogWriter));
        std::io::stdio::set_stderr(box std::io::LineBufferedWriter::new(ToLogWriter));
        main_function()
    });

    // polling for events forever
    // note that this must be done in the same thread as android_main because ALooper are
    //  thread-local
    unsafe {
        loop {
            let mut events = mem::uninitialized();
            let mut source = mem::uninitialized();

            // passing -1 means that we are blocking
            let ident = ffi::ALooper_pollAll(-1, ptr::null_mut(), &mut events,
                &mut source);

            // processing the event
            if !source.is_null() {
                let source: *mut ffi::android_poll_source = mem::transmute(source);
                ((*source).process)(ANDROID_APP, source);
            }
        }
    }

    // terminating the application
    unsafe { ANDROID_APP = 0 as *mut ffi::android_app };
}

/// Writer that will redirect what is written to it to the logs.
struct ToLogWriter;

impl Writer for ToLogWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::IoResult<()> {
        buf.with_c_str(|message| {
            b"RustAndroidGlueStdouterr".with_c_str(|tag| {
                unsafe { ffi::__android_log_write(3, tag, message) };
            });
        });
        Ok(())
    }
}

/// The callback for inputs.
pub extern fn inputs_callback(_: *mut ffi::android_app, event: *const ffi::AInputEvent)
    -> libc::int32_t
{
    fn send_event(event: Event) {
        for sender in get_context().senders.lock().iter() {
            sender.send(event);
        }
    }
    let action = unsafe { ffi::AMotionEvent_getAction(event) };
    let action_code = action & ffi::AMOTION_EVENT_ACTION_MASK;
    match action_code {
        ffi::AMOTION_EVENT_ACTION_UP
            | ffi::AMOTION_EVENT_ACTION_OUTSIDE
            | ffi::AMOTION_EVENT_ACTION_CANCEL
            | ffi::AMOTION_EVENT_ACTION_POINTER_UP =>
        {
            send_event(Event::EventUp);
        },
        ffi::AMOTION_EVENT_ACTION_DOWN
            | ffi::AMOTION_EVENT_ACTION_POINTER_DOWN =>
        {
            send_event(Event::EventDown);
        },
        _ => {
            let x = unsafe { ffi::AMotionEvent_getX(event, 0) };
            let y = unsafe { ffi::AMotionEvent_getY(event, 0) };
            send_event(Event::EventMove(x as i32, y as i32));
        },
    }
    0
}

/// The callback for commands.
#[doc(hidden)]
pub extern fn commands_callback(_: *mut ffi::android_app, command: libc::int32_t) {
    let context = get_context();

    match command {
        ffi::APP_CMD_INIT_WINDOW => {

        },

        ffi::APP_CMD_SAVE_STATE => {

        },

        ffi::APP_CMD_TERM_WINDOW => {

        },

        ffi::APP_CMD_GAINED_FOCUS => {

        },

        ffi::APP_CMD_LOST_FOCUS => {

        },

        _ => ()
    }
}

/// Returns the current Context.
fn get_context() -> &'static Context {
    let context = unsafe { (*ANDROID_APP).userData };
    unsafe { std::mem::transmute(context) }
}

/// Adds a sender where events will be sent to.
pub fn add_sender(sender: Sender<Event>) {
    get_context().senders.lock().push(sender);
}

/// Returns a handle to the native window.
pub unsafe fn get_native_window() -> ffi::NativeWindowType {
    if ANDROID_APP.is_null() {
        panic!("The application was not initialized from android_main");
    }

    loop {
        let value = (*ANDROID_APP).window;
        if !value.is_null() {
            return value;
        }

        // spin-locking
        std::io::timer::sleep(std::time::Duration::milliseconds(10));
    }
}

///
pub fn write_log(message: &str) {
    message.with_c_str(|message| {
        b"RustAndroidGlue".with_c_str(|tag| {
            unsafe { ffi::__android_log_write(3, tag, message) };
        });
    });
}
<commit_msg>Updated for rust fc2ba1393 2015-01-03<commit_after>#![feature(macro_rules)]
#![feature(phase)]

#![unstable]

#[phase(plugin)]
extern crate compile_msg;

extern crate libc;

use std::c_str::ToCStr;
use std::sync::mpsc::{Sender};
use std::sync::Mutex;
use std::thread::Thread;

#[doc(hidden)]
pub mod ffi;

/// This static variable  will store the android_app* on creation, and set it back to 0 at
///  destruction.
/// Apart from this, the static is never written, so there is no risk of race condition.
static mut ANDROID_APP: *mut ffi::android_app = 0 as *mut ffi::android_app;

/// This is the structure that serves as user data in the android_app*
#[doc(hidden)]
struct Context {
    senders: Mutex<Vec<Sender<Event>>>,
}

/// An event triggered by the Android environment.
pub enum Event {
    EventUp,
    EventDown,
    EventMove(i32, i32),
}

impl Copy for Event {}

#[cfg(not(target_os = "android"))]
compile_note!("You are not compiling for Android");

#[macro_export]
macro_rules! android_start(
    ($main: ident) => (
        pub mod __android_start {
            extern crate android_glue;

            // this function is here because we are sure that it will be included by the linker
            // so we call app_dummy in it, in order to be sure that the native glue will be included
            #[start]
            pub fn start(_: int, _: *const *const u8) -> int {
                unsafe { android_glue::ffi::app_dummy() };
                1
            }

            #[no_mangle]
            #[inline(never)]
            #[allow(non_snake_case)]
            pub extern "C" fn android_main(app: *mut ()) {
                android_glue::android_main2(app, move|| super::$main());
            }
        }
    )
);

/// This is the function that must be called by `android_main`
#[doc(hidden)]
pub fn android_main2<F>(app: *mut (), main_function: F)
    where F: FnOnce(), F: Send
{
    use std::{mem, ptr};

    write_log("Entering android_main");

    unsafe { ANDROID_APP = std::mem::transmute(app) };
    let app: &mut ffi::android_app = unsafe { std::mem::transmute(app) };

    // creating the context that will be passed to the callback
    let context = Context { senders: Mutex::new(Vec::new()) };
    app.onAppCmd = commands_callback;
    app.onInputEvent = inputs_callback;
    app.userData = unsafe { std::mem::transmute(&context) };

    // executing the main function in parallel
    let g = Thread::spawn(move|| {
        std::io::stdio::set_stdout(box std::io::LineBufferedWriter::new(ToLogWriter));
        std::io::stdio::set_stderr(box std::io::LineBufferedWriter::new(ToLogWriter));
        main_function()
    });

    // polling for events forever
    // note that this must be done in the same thread as android_main because ALooper are
    //  thread-local
    unsafe {
        loop {
            let mut events = mem::uninitialized();
            let mut source = mem::uninitialized();

            // passing -1 means that we are blocking
            let ident = ffi::ALooper_pollAll(-1, ptr::null_mut(), &mut events,
                &mut source);

            // processing the event
            if !source.is_null() {
                let source: *mut ffi::android_poll_source = mem::transmute(source);
                ((*source).process)(ANDROID_APP, source);
            }
        }
    }

    // terminating the application
    unsafe { ANDROID_APP = 0 as *mut ffi::android_app };
}

/// Writer that will redirect what is written to it to the logs.
struct ToLogWriter;

impl Writer for ToLogWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::IoResult<()> {
        buf.with_c_str(|message| {
            b"RustAndroidGlueStdouterr".with_c_str(|tag| {
                unsafe { ffi::__android_log_write(3, tag, message) };
            });
        });
        Ok(())
    }
}

/// The callback for inputs.
pub extern fn inputs_callback(_: *mut ffi::android_app, event: *const ffi::AInputEvent)
    -> libc::int32_t
{
    fn send_event(event: Event) {
        let senders = get_context().senders.lock().ok().unwrap();
        for sender in senders.iter() {
            sender.send(event);
        }
    }
    let action = unsafe { ffi::AMotionEvent_getAction(event) };
    let action_code = action & ffi::AMOTION_EVENT_ACTION_MASK;
    match action_code {
        ffi::AMOTION_EVENT_ACTION_UP
            | ffi::AMOTION_EVENT_ACTION_OUTSIDE
            | ffi::AMOTION_EVENT_ACTION_CANCEL
            | ffi::AMOTION_EVENT_ACTION_POINTER_UP =>
        {
            send_event(Event::EventUp);
        },
        ffi::AMOTION_EVENT_ACTION_DOWN
            | ffi::AMOTION_EVENT_ACTION_POINTER_DOWN =>
        {
            send_event(Event::EventDown);
        },
        _ => {
            let x = unsafe { ffi::AMotionEvent_getX(event, 0) };
            let y = unsafe { ffi::AMotionEvent_getY(event, 0) };
            send_event(Event::EventMove(x as i32, y as i32));
        },
    }
    0
}

/// The callback for commands.
#[doc(hidden)]
pub extern fn commands_callback(_: *mut ffi::android_app, command: libc::int32_t) {
    let context = get_context();

    match command {
        ffi::APP_CMD_INIT_WINDOW => {

        },

        ffi::APP_CMD_SAVE_STATE => {

        },

        ffi::APP_CMD_TERM_WINDOW => {

        },

        ffi::APP_CMD_GAINED_FOCUS => {

        },

        ffi::APP_CMD_LOST_FOCUS => {

        },

        _ => ()
    }
}

/// Returns the current Context.
fn get_context() -> &'static Context {
    let context = unsafe { (*ANDROID_APP).userData };
    unsafe { std::mem::transmute(context) }
}

/// Adds a sender where events will be sent to.
pub fn add_sender(sender: Sender<Event>) {
    get_context().senders.lock().ok().unwrap().push(sender);
}

/// Returns a handle to the native window.
pub unsafe fn get_native_window() -> ffi::NativeWindowType {
    if ANDROID_APP.is_null() {
        panic!("The application was not initialized from android_main");
    }

    loop {
        let value = (*ANDROID_APP).window;
        if !value.is_null() {
            return value;
        }

        // spin-locking
        std::io::timer::sleep(std::time::Duration::milliseconds(10));
    }
}

///
pub fn write_log(message: &str) {
    message.with_c_str(|message| {
        b"RustAndroidGlue".with_c_str(|tag| {
            unsafe { ffi::__android_log_write(3, tag, message) };
        });
    });
}
<|endoftext|>