<commit_before>// this is just a test binary that can be hooked up to Instruments
extern crate lonlat_bng;
use lonlat_bng::{convert_to_bng_threaded, convert_to_osgb36_threaded, Array};

extern crate rand;
use rand::distributions::{IndependentSample, Range};

extern crate libc;

fn main() {
    let num_coords = 1000000;
    let between_lon = Range::new(-6.379880, 1.768960);
    let between_lat = Range::new(49.871159, 55.811741);
    let mut rng = rand::thread_rng();
    let lon_vec = vec![between_lon.ind_sample(&mut rng) as f32; num_coords];
    let lat_vec = vec![between_lat.ind_sample(&mut rng) as f32; num_coords];
    let lon_arr = Array {
        data: lon_vec.as_ptr() as *const libc::c_void,
        len: lon_vec.len() as libc::size_t,
    };
    let lat_arr = Array {
        data: lat_vec.as_ptr() as *const libc::c_void,
        len: lat_vec.len() as libc::size_t,
    };

    convert_to_bng_threaded(lon_arr, lat_arr);
    // convert_to_osgb36_threaded(lon_arr, lat_arr);
}
<commit_msg>Run 1MM conversions in binary test<commit_after>// this is just a test binary that can be hooked up to Instruments
extern crate lonlat_bng;
use lonlat_bng::{convert_to_bng_threaded, convert_to_osgb36_threaded, Array};

extern crate rand;
use rand::distributions::{IndependentSample, Range};

extern crate libc;

fn main() {
    let num_coords = 10000000;
    let between_lon = Range::new(-6.379880, 1.768960);
    let between_lat = Range::new(49.871159, 55.811741);
    let mut rng = rand::thread_rng();
    let lon_vec = vec![between_lon.ind_sample(&mut rng) as f32; num_coords];
    let lat_vec = vec![between_lat.ind_sample(&mut rng) as f32; num_coords];
    let lon_arr = Array {
        data: lon_vec.as_ptr() as *const libc::c_void,
        len: lon_vec.len() as libc::size_t,
    };
    let lat_arr = Array {
        data: lat_vec.as_ptr() as *const libc::c_void,
        len: lat_vec.len() as libc::size_t,
    };

    convert_to_bng_threaded(lon_arr, lat_arr);
    // convert_to_osgb36_threaded(lon_arr, lat_arr);
}
<|endoftext|>


<commit_before>extern crate twirc;

mod ui;

use std::env;
use std::io::BufRead;
use std::thread;
use twirc::irc::message::Message;
use twirc::irc::Connection;
use ui::UI;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::sync::Arc;

// Please note that all of this is temporary.

enum MsgSource {
    IRC(String),
    Terminal(char),
}

fn handle_irc(tx: &Sender<MsgSource>, conn: Connection) {
    let tx = tx.clone();

    thread::spawn(move || {
        for line in conn.reader.lines() {
            let line = line.expect("failed to read line");
            tx.send(MsgSource::IRC(line)).unwrap();
        }
    });
}

fn handle_terminal(tx: &Sender<MsgSource>, ui: &Arc<UI>) {
    let tx = tx.clone();
    let ui = ui.clone();

    thread::spawn(move || {
        loop {
            match ui.command_entry.read_input_char() {
                Some(ch) => tx.send(MsgSource::Terminal(ch)).unwrap(),
                None     => (),
            }
        }
    });
}

fn handle_events(rx: Receiver<MsgSource>, ui: &Arc<UI>) {
    use MsgSource::*;

    loop {
        match rx.recv().unwrap() {
            IRC(line)    => ui.chat.add_message(&format!("{:?}", Message::parse(&line))),
            Terminal(ch) => ui.command_entry.process_char(ch),
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);

    let nick  = args.next().unwrap();
    let oauth = args.next().unwrap();
    let channels = args.collect::<Vec<_>>();

    let mut conn = Connection::new("irc.chat.twitch.tv:6667").unwrap();
    conn.login(&nick, &oauth).unwrap();

    for channel in channels {
        conn.write_line(&format!("JOIN #{}", channel)).unwrap();
    }

    let ui = Arc::new(UI::create());
    let (tx, rx) = channel();

    handle_irc(&tx, conn);
    handle_terminal(&tx, &ui);

    handle_events(rx, &ui);
}<commit_msg>Respond to PING messages<commit_after>extern crate twirc;

mod ui;

use std::env;
use std::io::BufRead;
use std::thread;
use twirc::irc::message::Message;
use twirc::irc::Connection;
use ui::UI;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::sync::Arc;

// Please note that all of this is temporary.

enum MsgSource {
    IRC(Message),
    Terminal(char),
    Error(String),
}

fn handle_irc(tx: &Sender<MsgSource>, conn: Connection) {
    let tx = tx.clone();
    let mut conn = conn;

    thread::spawn(move || {
        loop {
            let mut line = String::new();
            conn.reader.read_line(&mut line).unwrap();

            let msg = Message::parse(&line);

            match msg {
                Ok(Message::Ping(reply)) => conn.write_line(&format!("PONG {}", reply)).unwrap(),
                Ok(msg)  => tx.send(MsgSource::IRC(msg)).unwrap(),
                Err(err) => tx.send(MsgSource::Error(format!("{:?}", err))).unwrap(),
            }
        }
    });
}

fn handle_terminal(tx: &Sender<MsgSource>, ui: &Arc<UI>) {
    let tx = tx.clone();
    let ui = ui.clone();

    thread::spawn(move || {
        loop {
            match ui.command_entry.read_input_char() {
                Some(ch) => tx.send(MsgSource::Terminal(ch)).unwrap(),
                None     => (),
            }
        }
    });
}

fn handle_events(rx: Receiver<MsgSource>, ui: &Arc<UI>) {
    use MsgSource::*;

    loop {
        match rx.recv().unwrap() {
            IRC(msg)     => ui.chat.add_message(&format!("{:?}", msg)),
            Terminal(ch) => ui.command_entry.process_char(ch),
            Error(msg)   => ui.chat.add_message(&format!("ERROR: {}", msg)),
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);

    let nick  = args.next().unwrap();
    let oauth = args.next().unwrap();
    let channels = args.collect::<Vec<_>>();

    let mut conn = Connection::new("irc.chat.twitch.tv:6667").unwrap();
    conn.login(&nick, &oauth).unwrap();

    for channel in channels {
        conn.write_line(&format!("JOIN #{}", channel)).unwrap();
    }

    let ui = Arc::new(UI::create());
    let (tx, rx) = channel();

    handle_irc(&tx, conn);
    handle_terminal(&tx, &ui);

    handle_events(rx, &ui);
}<|endoftext|>


<commit_before>use libc::{c_char, c_int, c_short, c_uchar, int32_t, int64_t, uint16_t, c_void};

// Opaque Pointer of hdfsFS
pub enum hdfsFS {}

// Opaque Pointer of hdfsFile
pub enum hdfsFile {}

// Opaque Pointer of hdfsBuilder
pub enum hdfsBuilder {}

#[repr(C)]
#[allow(non_snake_case)]
pub struct HdfsReadStatistics {
  pub totalBytesRead : u64,
  pub totalLocalBytesRead : u64,
  pub totalShortCircuitBytesRead : u64,
  pub totalZeroCopyBytesRead : u64
}

pub type tSize = int32_t;

#[link(name="hdfs")]
extern "C" {

  /// Determine if a file is open for read.
  ///
  /// Return 1 if the file is open for read; 0 otherwise
  pub fn hdfsFileIsOpenForRead(fs: *mut hdfsFile) -> c_int;

  /// Determine if a file is open for write.
  ///
  /// Return 1 if the file is open for write; 0 otherwise.
  pub fn hdfsFileIsOpenForWrite(file: *mut hdfsFile) -> c_int;

  /// Get read statistics about a file.  This is only applicable to files
  /// opened for reading.
  ///
  /// # Return Value
  ///
  /// * 0 if the statistics were successfully returned,
  /// * -1 otherwise.  On a failure, please check errno against
  /// * ENOTSUP. webhdfs, LocalFilesystem, and so forth may
  /// not support read statistics.
  pub fn hdfsFileGetReadStatistics(file: *mut hdfsFile,
                   stats: &mut *mut HdfsReadStatistics) -> c_int;

  /// HDFS read statistics for a file,
  ///
  /// Return the number of remote bytes read.
  pub fn hdfsReadStatisticsGetRemoteBytesRead(
    stats: *const HdfsReadStatistics) -> int64_t;

  /// Free some HDFS read statistics.
  pub fn hdfsFileFreeReadStatistics(stats: *mut HdfsReadStatistics);

  /// hdfsConnectAsUser - Connect to a hdfs file system as a specific user.
  ///
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnectAsUser(host: *const c_char,
                       uint16_t: u16, user:
                       *const c_char) -> *mut hdfsFS;

  /// hdfsConnect - Connect to a hdfs file system.
  ///
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnect(host: *const c_char, uint16_t: u16) -> *mut hdfsFS;

  /// hdfsConnect - Connect to an hdfs file system.
  ///
  /// Forces a new instance to be created
  ///
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnectAsUserNewInstance(host: *const c_char,
                    uint16_t: u16,
                    user: *const c_char) -> *mut hdfsFS;

  pub fn hdfsConnectNewInstance(host: *const c_char,
                            uint16_t: u16) -> *mut hdfsFS;

  pub fn hdfsNewBuilder() -> *mut hdfsBuilder;

  pub fn hdfsFreeBuilder(bld: *mut hdfsBuilder);

  pub fn hdfsBuilderSetNameNode(bld: *mut hdfsBuilder, host: *const c_char);

  pub fn hdfsBuilderSetNameNodePort(bld: *mut hdfsBuilder, port : uint16_t);

  pub fn hdfsBuilderSetUserName(bld: *mut hdfsBuilder, userName: *const c_char);

  pub fn hdfsBuilderSetKerbTicketCachePath(bld: *mut hdfsBuilder,
                       kerbTicketCachePath: *const c_char);

  pub fn hdfsBuilderConfSetStr(bld: *mut hdfsBuilder,
                           key: *const c_char , value: *const c_char) -> c_int;

//  fn hdfsConfGetStr(value : *const c_char, **c_char val) -> c_int

  pub fn hdfsConfGetInt(key: *const c_char, val: *mut int32_t) -> c_int;

  pub fn hdfsConfStrFree(val: *const c_char);

  ///
  /// Try to connect a specific HDFS namenode.
  ///
  ///
  pub fn hdfsBuilderConnect(bld : *mut hdfsBuilder) -> *mut hdfsFS;

  pub fn hdfsDisconnect(fs: *mut hdfsFS) -> c_int;

  /// Open a hdfs file in given mode.
  ///
  /// #### Params
  /// * fs The configured filesystem handle.
  /// * file The file handle.
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error. On error, errno will be set appropriately.
  /// If the hdfs file was valid, the memory associated with it will
  /// be freed at the end of this call, even if there was an I/O error.
  pub fn hdfsOpenFile(fs: *mut hdfsFS, path: *const c_char, flags: c_int,
                      bufferSize: c_int, replication: c_short,
                      blocksize: int32_t) -> *mut hdfsFile;

  /// Close an open file.
  /// #### Params
  /// * fs The configured filesystem handle.
  /// * file The file handle.
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error.  On error, errno will be set appropriately.
  /// If the hdfs file was valid, the memory associated with it will
  /// be freed at the end of this call, even if there was an I/O error.
  pub fn hdfsCloseFile(fs: *mut hdfsFS, file: *mut hdfsFile) -> c_int;

  /// Checks if a given path exsits on the filesystem
  /// #### Params
  /// * fs The configured filesystem handle.
  /// * path The path to look for
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error.
  pub fn hdfsExists(fs: *mut hdfsFS, path: *const c_char) -> c_int;
}

pub enum NativeMiniDfsCluster {}

#[repr(C)]
#[allow(non_snake_case)]
pub struct MiniDfsConf {
  do_format: c_uchar,
  webhdfs_enabled: c_uchar,
  namenode_http_port: c_int,
  short_circuit_enabled: c_uchar
}

impl MiniDfsConf {
  pub fn new() -> MiniDfsConf {
    MiniDfsConf {
      do_format: 1,
      webhdfs_enabled: 0,
      namenode_http_port: 0,
      short_circuit_enabled: 0
    }
  }

  pub fn set_do_format(&mut self, on: bool) -> &mut MiniDfsConf {
    self.do_format = if on { 1 } else { 0 };
    self
  }

  pub fn do_format(&self) -> bool {
    if self.do_format != 0 { true } else { false }
  }

  pub fn set_web_hdfs(&mut self, enable: bool) -> &mut MiniDfsConf {
    self.webhdfs_enabled = if enable { 1 } else { 0 };
    self
  }

  pub fn web_hdfs_enabled(&self) -> bool {
    if self.webhdfs_enabled != 0 { true } else { false }
  }

  pub fn set_http_port(&mut self, port: i32) -> &mut MiniDfsConf {
    self.namenode_http_port = port as c_int;
    self
  }

  pub fn http_port(&self) -> i32 {
    self.namenode_http_port
  }

  pub fn set_short_circuit(&mut self, enable: bool) -> &mut MiniDfsConf {
    self.short_circuit_enabled = if enable { 1 } else { 0 };
    self
  }

  pub fn short_circuit_enabled(&self) -> bool {
    if self.short_circuit_enabled != 0 { true } else { false }
  }
}

#[link(name="hdfs")]
extern "C" {
  pub fn nmdCreate(conf: *const MiniDfsConf) -> *mut NativeMiniDfsCluster;

  pub fn nmdWaitClusterUp(cl: *mut NativeMiniDfsCluster) -> c_int;

  pub fn nmdShutdown(cl: *mut NativeMiniDfsCluster) -> c_int;

  pub fn nmdFree(cl: *mut NativeMiniDfsCluster) -> c_void;

  pub fn nmdGetNameNodePort(cl: *const NativeMiniDfsCluster) -> c_int;

  pub fn nmdGetNameNodeHttpAddress(cl: *const NativeMiniDfsCluster,
                               port: *mut c_int, hostName: *mut *mut c_char) -> c_int;

  pub fn nmdConfigureHdfsBuilder(cl: *mut NativeMiniDfsCluster, bld: *mut hdfsBuilder) -> c_int;
}<commit_msg>Add more comments to binding APIs.<commit_after>use libc::{c_char, c_int, c_short, c_uchar, c_void, int32_t, int64_t, uint16_t, time_t};

// Opaque Pointer of hdfsFS
pub enum hdfsFS {}

// Opaque Pointer of hdfsFile
pub enum hdfsFile {}

// Opaque Pointer of hdfsBuilder
pub enum hdfsBuilder {}

#[repr(C)]
#[allow(non_snake_case)]
pub struct HdfsReadStatistics {
  pub totalBytesRead : u64,
  pub totalLocalBytesRead : u64,
  pub totalShortCircuitBytesRead : u64,
  pub totalZeroCopyBytesRead : u64
}

pub type tSize = int32_t;
pub type tTime = time_t;
pub type tOffset = int64_t;
pub type tPort = uint16_t;

#[link(name="hdfs")]
extern "C" {

  /// Determine if a file is open for read.
  ///
  /// #### Params
  /// * file - the HDFS file
  ///
  /// #### Return
  /// Return 1 if the file is open for read; 0 otherwise
  pub fn hdfsFileIsOpenForRead(fs: *mut hdfsFile) -> c_int;

  /// Determine if a file is open for write.
  ///
  /// #### Params
  /// * file - the HDFS file
  ///
  /// #### Return
  /// Return 1 if the file is open for write; 0 otherwise.
  pub fn hdfsFileIsOpenForWrite(file: *mut hdfsFile) -> c_int;

  /// Get read statistics about a file.  This is only applicable to files
  /// opened for reading.
  ///
  /// #### Params
  /// * file - The HDFS file
  /// * stats - (out parameter) on a successful return, the read statistics.
  /// Unchanged otherwise. You must free the returned statistics with
  /// hdfsFileFreeReadStatistics.
  ///
  /// #### Return
  /// * 0 if the statistics were successfully returned,
  /// * -1 otherwise.  On a failure, please check errno against
  /// * ENOTSUP. webhdfs, LocalFilesystem, and so forth may
  /// not support read statistics.
  pub fn hdfsFileGetReadStatistics(file: *mut hdfsFile,
                   stats: &mut *mut HdfsReadStatistics) -> c_int;

  /// HDFS read statistics for a file,
  ///
  /// #### Params
  /// * stats - HDFS read statistics for a file.
  ///
  /// #### Return
  /// Return the number of remote bytes read.
  pub fn hdfsReadStatisticsGetRemoteBytesRead(
    stats: *const HdfsReadStatistics) -> int64_t;

  /// Free some HDFS read statistics.
  ///
  /// #### Params
  /// * stats - The HDFS read statistics to free.
  pub fn hdfsFileFreeReadStatistics(stats: *mut HdfsReadStatistics);

  /// Connect to a hdfs file system as a specific user.
  ///
  /// #### Params
  /// * nn - The NameNode.  See hdfsBuilderSetNameNode for details.
  /// * port - The port on which the server is listening.
  /// * param - user the user name (this is hadoop domain user).
  /// Or NULL is equivelant to hhdfsConnect(host, port)
  ///
  /// #### Return
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnectAsUser(host: *const c_char,
                       uint16_t: u16, user:
                       *const c_char) -> *mut hdfsFS;

  /// Connect to a hdfs file system.
  ///
  /// This API is deprecated. Use hdfsBuilderConnect instead.
  ///
  /// #### Params
  /// * nn - The NameNode.  See hdfsBuilderSetNameNode for details.
  /// * port - The port on which the server is listening.
  ///
  /// #### Return
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnect(host: *const c_char, uint16_t: u16) -> *mut hdfsFS;

  /// Connect to an hdfs file system.
  ///
  /// Forces a new instance to be created. This API is deprecated.
  /// Use hdfsBuilderConnect instead.
  ///
  /// #### Params
  /// * nn - The NameNode.  See hdfsBuilderSetNameNode for details.
  /// * port - The port on which the server is listening.
  /// * user - The user name to use when connecting
  ///
  /// #### Return
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnectAsUserNewInstance(host: *const c_char,
                    uint16_t: u16,
                    user: *const c_char) -> *mut hdfsFS;

  /// Connect to an hdfs file system.
  ///
  /// Forces a new instance to be created. This API is deprecated.
  /// Use hdfsBuilderConnect instead.
  ///
  /// #### Params
  /// * nn - The NameNode.  See hdfsBuilderSetNameNode for details.
  /// * port - The port on which the server is listening.
  ///
  /// #### Return
  /// Returns a handle to the filesystem or NULL on error.
  pub fn hdfsConnectNewInstance(host: *const c_char,
                            uint16_t: u16) -> *mut hdfsFS;

  /// Connect to HDFS using the parameters defined by the builder.
  ///
  /// The HDFS builder will be freed, whether or not the connection was successful.
  ///
  /// Every successful call to hdfsBuilderConnect should be matched with a call
  /// to hdfsDisconnect, when the hdfsFS is no longer needed.
  ///
  /// #### Params
  /// * bld - The HDFS builder
  ///
  /// #### Return
  /// Returns a handle to the filesystem, or NULL on error.
  pub fn hdfsBuilderConnect(bld : *mut hdfsBuilder) -> *mut hdfsFS;

  /// Create an HDFS builder.
  ///
  /// #### Return
  /// The HDFS builder, or NULL on error.
  pub fn hdfsNewBuilder() -> *mut hdfsBuilder;

  /// Force the builder to always create a new instance of the FileSystem,
  /// rather than possibly finding one in the cache.
  ///
  /// #### Params
  /// * bld - The HDFS builder
  pub fn hdfsBuilderSetForceNewInstance(bld: *mut hdfsBuilder);

  /// Set the HDFS NameNode to connect to.
  ///
  /// #### Params
  /// * bld - The HDFS builder
  /// * nn - The NameNode to use. If the string given is 'default', the default NameNode
  /// configuration will be used (from the XML configuration files).
  /// If NULL is given, a LocalFileSystem will be created.
  /// If the string starts with a protocol type such as ```file://``` or
  /// ```hdfs://```, this protocol type will be used.  If not, the
  /// ```hdfs://``` protocol type will be used.
  /// You may specify a NameNode port in the usual way by
  /// passing a string of the format ```hdfs://<hostname>:<port>```.
  /// Alternately, you may set the port with hdfsBuilderSetNameNodePort.
  /// However, you must not pass the port in two different ways.
  pub fn hdfsBuilderSetNameNode(bld: *mut hdfsBuilder, host: *const c_char);

  /// Set the port of the HDFS NameNode to connect to.
  ///
  /// #### Params
  /// * bld - The HDFS builder
  /// * port - The port.
  pub fn hdfsBuilderSetNameNodePort(bld: *mut hdfsBuilder, port : uint16_t);

  /// Set the username to use when connecting to the HDFS cluster.
  ///
  /// #### Params
  /// * bld - The HDFS builder
  /// * userName - The user name.  The string will be shallow-copied.
  pub fn hdfsBuilderSetUserName(bld: *mut hdfsBuilder, userName: *const c_char);

  /// Set the path to the Kerberos ticket cache to use when connecting to
  /// the HDFS cluster.
  ///
  /// #### Params
  /// * ```bld``` - The HDFS builder
  /// * ```kerbTicketCachePath``` - The Kerberos ticket cache path.  The string
  /// will be shallow-copied.
  pub fn hdfsBuilderSetKerbTicketCachePath(bld: *mut hdfsBuilder,
      kerbTicketCachePath: *const c_char);

  /// Free an HDFS builder.
  ///
  /// It is normally not necessary to call this function since
  /// hdfsBuilderConnect frees the builder.
  ///
  /// #### Params
  /// * ```bld``` - The HDFS builder
  pub fn hdfsFreeBuilder(bld: *mut hdfsBuilder);

  /// Set a configuration string for an HdfsBuilder.
  ///
  /// #### Params
  /// * ```key``` - The key to set.
  /// * ```val``` - The value, or NULL to set no value.
  /// This will be shallow-copied.  You are responsible for
  /// ensuring that it remains valid until the builder is freed.
  ///
  /// #### Return
  /// 0 on success; nonzero error code otherwise.
  pub fn hdfsBuilderConfSetStr(bld: *mut hdfsBuilder,
                           key: *const c_char , value: *const c_char) -> c_int;

  /// Get a configuration string.
  ///
  /// #### Params
  /// * ```key``` - The key to find
  /// * ```val``` - (out param) The value.  This will be set to NULL if the
  /// key isn't found.  You must free this string with
  /// ```hdfsConfStrFree```.
  ///
  /// #### Return
  /// 0 on success; nonzero error code otherwise.
  /// Failure to find the key is not an error.
  pub fn hdfsConfGetStr(value : *const c_char, val: *mut *mut c_char) -> c_int;

  /// Get a configuration integer.
  ///
  /// #### Params
  /// * ```key``` - The key to find
  /// * ```val``` - (out param) The value.  This will NOT be changed if the
  /// key isn't found.
  ///
  /// #### Return
  /// 0 on success; nonzero error code otherwise.
  /// Failure to find the key is not an error.
  pub fn hdfsConfGetInt(key: *const c_char, val: *mut int32_t) -> c_int;

  /// Free a configuration string found with hdfsConfGetStr.
  ///
  /// #### Params
  /// * ```val``` - A configuration string obtained from hdfsConfGetStr
  pub fn hdfsConfStrFree(val: *const c_char);

  /// hdfsDisconnect - Disconnect from the hdfs file system.
  /// Disconnect from hdfs.
  ///
  /// #### Params
  /// * ```fs``` - The configured filesystem handle.
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error.
  /// Even if there is an error, the resources associated with the
  /// hdfsFS will be freed.
  pub fn hdfsDisconnect(fs: *mut hdfsFS) -> c_int;

  /// Open a hdfs file in given mode.
  ///
  /// #### Params
  /// * ```fs``` - The configured filesystem handle.
  /// * ```file``` - The file handle.
  /// * ```flags``` - an ```|``` of ```bits/fcntl.h``` file flags -
  /// supported flags are O_RDONLY, O_WRONLY (meaning create or overwrite
  /// i.e., implies O_TRUNCAT), O_WRONLY|O_APPEND. Other flags are generally
  /// ignored other than (O_RDWR || (O_EXCL & O_CREAT)) which return NULL and
  /// set errno equal ENOTSUP.
  /// * ```bufferSize``` - Size of buffer for read/write - pass 0 if you want
  /// to use the default configured values.
  /// * ```replication``` Block replication - pass 0 if you want to use
  /// the default configured values.
  /// * ```blocksize``` - Size of block - pass 0 if you want to use the
  /// default configured values.
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error. On error, errno will be set appropriately.
  /// If the hdfs file was valid, the memory associated with it will
  /// be freed at the end of this call, even if there was an I/O error.
  pub fn hdfsOpenFile(fs: *mut hdfsFS, path: *const c_char, flags: c_int,
                      bufferSize: c_int, replication: c_short,
                      blocksize: int32_t) -> *mut hdfsFile;

  /// Close an open file.
  ///
  /// #### Params
  /// * ```fs``` - The configured filesystem handle.
  /// * ```file``` - The file handle.
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error.  On error, errno will be set appropriately.
  /// If the hdfs file was valid, the memory associated with it will
  /// be freed at the end of this call, even if there was an I/O error.
  pub fn hdfsCloseFile(fs: *mut hdfsFS, file: *mut hdfsFile) -> c_int;

  /// Checks if a given path exsits on the filesystem
  ///
  /// #### Params
  /// * ```fs``` - The configured filesystem handle.
  /// * ```path``` - The path to look for
  ///
  /// #### Return
  /// Returns 0 on success, -1 on error.
  pub fn hdfsExists(fs: *mut hdfsFS, path: *const c_char) -> c_int;
}

pub enum NativeMiniDfsCluster {}

#[repr(C)]
#[allow(non_snake_case)]
pub struct MiniDfsConf {
  do_format: c_uchar,
  webhdfs_enabled: c_uchar,
  namenode_http_port: c_int,
  short_circuit_enabled: c_uchar
}

impl MiniDfsConf {
  pub fn new() -> MiniDfsConf {
    MiniDfsConf {
      do_format: 1,
      webhdfs_enabled: 0,
      namenode_http_port: 0,
      short_circuit_enabled: 0
    }
  }

  pub fn set_do_format(&mut self, on: bool) -> &mut MiniDfsConf {
    self.do_format = if on { 1 } else { 0 };
    self
  }

  pub fn do_format(&self) -> bool {
    if self.do_format != 0 { true } else { false }
  }

  pub fn set_web_hdfs(&mut self, enable: bool) -> &mut MiniDfsConf {
    self.webhdfs_enabled = if enable { 1 } else { 0 };
    self
  }

  pub fn web_hdfs_enabled(&self) -> bool {
    if self.webhdfs_enabled != 0 { true } else { false }
  }

  pub fn set_http_port(&mut self, port: i32) -> &mut MiniDfsConf {
    self.namenode_http_port = port as c_int;
    self
  }

  pub fn http_port(&self) -> i32 {
    self.namenode_http_port
  }

  pub fn set_short_circuit(&mut self, enable: bool) -> &mut MiniDfsConf {
    self.short_circuit_enabled = if enable { 1 } else { 0 };
    self
  }

  pub fn short_circuit_enabled(&self) -> bool {
    if self.short_circuit_enabled != 0 { true } else { false }
  }
}

#[link(name="hdfs")]
extern "C" {
  pub fn nmdCreate(conf: *const MiniDfsConf) -> *mut NativeMiniDfsCluster;

  pub fn nmdWaitClusterUp(cl: *mut NativeMiniDfsCluster) -> c_int;

  pub fn nmdShutdown(cl: *mut NativeMiniDfsCluster) -> c_int;

  pub fn nmdFree(cl: *mut NativeMiniDfsCluster) -> c_void;

  pub fn nmdGetNameNodePort(cl: *const NativeMiniDfsCluster) -> c_int;

  pub fn nmdGetNameNodeHttpAddress(cl: *const NativeMiniDfsCluster,
                               port: *mut c_int, hostName: *mut *mut c_char) -> c_int;

  pub fn nmdConfigureHdfsBuilder(cl: *mut NativeMiniDfsCluster, bld: *mut hdfsBuilder) -> c_int;
}<|endoftext|>


<commit_before>// Copy of https://github.com/tamird/bitflags/blob/8bb7e6252f3b09b0317359c3042b9d4095948bf8/src/lib.rs
// (associated-constants branch)

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

//! A typesafe bitmask flag generator (tamird's associated-constants branch)

macro_rules! bitflags {
    ($(#[$attr:meta])* pub flags $BitFlags:ident: $T:ty {
        $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+
    }) => {
        #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
        $(#[$attr])*
        pub struct $BitFlags {
            bits: $T,
        }

        bitflags! {
            @_impl flags $BitFlags: $T {
                $($(#[$Flag_attr])* const $Flag = $value),+
            }
        }
    };
    ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty {
        $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+
    }) => {
        #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
        $(#[$attr])*
        struct $BitFlags {
            bits: $T,
        }

        bitflags! {
            @_impl flags $BitFlags: $T {
                $($(#[$Flag_attr])* const $Flag = $value),+
            }
        }
    };
    (@_impl flags $BitFlags:ident: $T:ty {
        $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+
    }) => {
        impl $crate::core::fmt::Debug for $BitFlags {
            #[allow(unused_assignments)]
            fn fmt(&self, f: &mut $crate::core::fmt::Formatter) -> $crate::core::fmt::Result {
                // This convoluted approach is to handle #[cfg]-based flag
                // omission correctly. Some of the $Flag variants may not be
                // defined in this module so we create a trait which defines
                // *all* flags to the value of 0. Afterwards we define a struct
                // which overrides the definitions in the trait for all defined
                // variants, leaving only the undefined ones with the bit value
                // of 0.
                trait DummyTrait {
                    // Now we define the "undefined" versions of the flags.
                    // This way, all the names exist, even if some are #[cfg]ed
                    // out.
                    $(const $Flag: $BitFlags = $BitFlags { bits: 0 };)+
                };
                struct DummyStruct;
                impl DummyTrait for DummyStruct {
                    // Then we override the "undefined" flags with flags that
                    // have their proper value. Flags that are #[cfg]ed out
                    // retain the default value of 0 defined by the trait.
                    $($(#[$Flag_attr])* const $Flag: $BitFlags = $BitFlags { bits: $value };)+
                };
                let mut first = true;
                $(
                    // $Flag.bits == 0 means that $Flag doesn't exist
                    if DummyStruct::$Flag.bits != 0 && self.contains(DummyStruct::$Flag) {
                        if !first {
                            try!(f.write_str(" | "));
                        }
                        first = false;
                        try!(f.write_str(stringify!($Flag)));
                    }
                )+
                Ok(())
            }
        }

        #[allow(dead_code)]
        impl $BitFlags {
            $($(#[$Flag_attr])* pub const $Flag: $BitFlags = $BitFlags { bits: $value };)+

            /// Returns an empty set of flags.
            #[inline]
            pub fn empty() -> $BitFlags {
                $BitFlags { bits: 0 }
            }

            /// Returns the set containing all flags.
            #[inline]
            pub fn all() -> $BitFlags {
                // See above for why this approach is taken.
                trait DummyTrait {
                    $(const $Flag: $BitFlags = $BitFlags { bits: 0 };)+
                };
                struct DummyStruct;
                impl DummyTrait for DummyStruct {
                    $($(#[$Flag_attr])* const $Flag: $BitFlags = $BitFlags { bits: $value };)+
                };
                $BitFlags { bits: $(DummyStruct::$Flag.bits)|+ }
            }

            /// Returns the raw value of the flags currently stored.
            #[inline]
            pub fn bits(&self) -> $T {
                self.bits
            }

            /// Convert from underlying bit representation, unless that
            /// representation contains bits that do not correspond to a flag.
            #[inline]
            pub fn from_bits(bits: $T) -> $crate::core::option::Option<$BitFlags> {
                if (bits & !$BitFlags::all().bits()) == 0 {
                    $crate::core::option::Option::Some($BitFlags { bits: bits })
                } else {
                    $crate::core::option::Option::None
                }
            }

            /// Convert from underlying bit representation, dropping any bits
            /// that do not correspond to flags.
            #[inline]
            pub fn from_bits_truncate(bits: $T) -> $BitFlags {
                $BitFlags { bits: bits } & $BitFlags::all()
            }

            /// Returns `true` if no flags are currently stored.
            #[inline]
            pub fn is_empty(&self) -> bool {
                *self == $BitFlags::empty()
            }

            /// Returns `true` if all flags are currently set.
            #[inline]
            pub fn is_all(&self) -> bool {
                *self == $BitFlags::all()
            }

            /// Returns `true` if there are flags common to both `self` and `other`.
            #[inline]
            pub fn intersects(&self, other: $BitFlags) -> bool {
                !(*self & other).is_empty()
            }

            /// Returns `true` all of the flags in `other` are contained within `self`.
            #[inline]
            pub fn contains(&self, other: $BitFlags) -> bool {
                (*self & other) == other
            }

            /// Inserts the specified flags in-place.
            #[inline]
            pub fn insert(&mut self, other: $BitFlags) {
                self.bits |= other.bits;
            }

            /// Removes the specified flags in-place.
            #[inline]
            pub fn remove(&mut self, other: $BitFlags) {
                self.bits &= !other.bits;
            }

            /// Toggles the specified flags in-place.
            #[inline]
            pub fn toggle(&mut self, other: $BitFlags) {
                self.bits ^= other.bits;
            }
        }

        impl $crate::core::ops::BitOr for $BitFlags {
            type Output = $BitFlags;

            /// Returns the union of the two sets of flags.
            #[inline]
            fn bitor(self, other: $BitFlags) -> $BitFlags {
                $BitFlags { bits: self.bits | other.bits }
            }
        }

        impl $crate::core::ops::BitOrAssign for $BitFlags {

            /// Adds the set of flags.
            #[inline]
            fn bitor_assign(&mut self, other: $BitFlags) {
                self.bits |= other.bits;
            }
        }

        impl $crate::core::ops::BitXor for $BitFlags {
            type Output = $BitFlags;

            /// Returns the left flags, but with all the right flags toggled.
            #[inline]
            fn bitxor(self, other: $BitFlags) -> $BitFlags {
                $BitFlags { bits: self.bits ^ other.bits }
            }
        }

        impl $crate::core::ops::BitXorAssign for $BitFlags {

            /// Toggles the set of flags.
            #[inline]
            fn bitxor_assign(&mut self, other: $BitFlags) {
                self.bits ^= other.bits;
            }
        }

        impl $crate::core::ops::BitAnd for $BitFlags {
            type Output = $BitFlags;

            /// Returns the intersection between the two sets of flags.
            #[inline]
            fn bitand(self, other: $BitFlags) -> $BitFlags {
                $BitFlags { bits: self.bits & other.bits }
            }
        }

        impl $crate::core::ops::BitAndAssign for $BitFlags {

            /// Disables all flags disabled in the set.
            #[inline]
            fn bitand_assign(&mut self, other: $BitFlags) {
                self.bits &= other.bits;
            }
        }

        impl $crate::core::ops::Sub for $BitFlags {
            type Output = $BitFlags;

            /// Returns the set difference of the two sets of flags.
            #[inline]
            fn sub(self, other: $BitFlags) -> $BitFlags {
                $BitFlags { bits: self.bits & !other.bits }
            }
        }

        impl $crate::core::ops::SubAssign for $BitFlags {

            /// Disables all flags enabled in the set.
            #[inline]
            fn sub_assign(&mut self, other: $BitFlags) {
                self.bits &= !other.bits;
            }
        }

        impl $crate::core::ops::Not for $BitFlags {
            type Output = $BitFlags;

            /// Returns the complement of this set of flags.
            #[inline]
            fn not(self) -> $BitFlags {
                $BitFlags { bits: !self.bits } & $BitFlags::all()
            }
        }

        impl $crate::core::iter::Extend<$BitFlags> for $BitFlags {
            fn extend<T: $crate::core::iter::IntoIterator<Item=$BitFlags>>(&mut self, iterator: T) {
                for item in iterator {
                    self.insert(item)
                }
            }
        }

        impl $crate::core::iter::FromIterator<$BitFlags> for $BitFlags {
            fn from_iter<T: $crate::core::iter::IntoIterator<Item=$BitFlags>>(iterator: T) -> $BitFlags {
                let mut result = Self::empty();
                result.extend(iterator);
                result
            }
        }
    };
    ($(#[$attr:meta])* pub flags $BitFlags:ident: $T:ty {
        $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+,
    }) => {
        bitflags! {
            $(#[$attr])*
            pub flags $BitFlags: $T {
                $($(#[$Flag_attr])* const $Flag = $value),+
            }
        }
    };
    ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty {
        $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+,
    }) => {
        bitflags! {
            $(#[$attr])*
            flags $BitFlags: $T {
                $($(#[$Flag_attr])* const $Flag = $value),+
            }
        }
    };
}
<commit_msg>Remove old unused bitflags module<commit_after><|endoftext|>


<commit_before>/// All types that implement this trait can be converted to Gerber Code.
pub trait GerberCode {
    fn to_code(&self) -> String;
}

/// Automatically implement GerberCode trait for struct types
/// that are based on x and y attributes.
macro_rules! impl_xy_gerbercode {
    ($class:ty) => {
        impl GerberCode for $class {
            fn to_code(&self) -> String {
                let mut code = String::new();
                if let Some(x) = self.x {
                    code = format!("X{}", x);
                }
                if let Some(y) = self.y {
                    code.push_str(&format!("Y{}", y));
                }
                code
            }
        }
    }
}

/// Coordinates are part of an operation.
///
/// Coordinates are modal. If an X is omitted, the X coordinate of the
/// current point is used. Similar for Y.
#[derive(Debug)]
pub struct Coordinates {
    pub x: Option<i32>,
    pub y: Option<i32>,
}

impl_xy_gerbercode!(Coordinates);

/// Coordinate offsets can be used for interpolate operations in circular
/// interpolation mode.
#[derive(Debug)]
pub struct CoordinateOffset {
    pub x: Option<i32>,
    pub y: Option<i32>,
}

impl_xy_gerbercode!(CoordinateOffset);

#[derive(Debug)]
pub enum Command {
    FunctionCode(FunctionCode),
    ExtendedCode(ExtendedCode),
}

#[derive(Debug)]
pub enum FunctionCode {
    DCode(DCode),
    GCode(GCode),
    MCode(MCode),
}

#[derive(Debug)]
pub enum ExtendedCode {
    FS,
    MO,
    AD,
    AM,
    SR,
    LP,
    TF,
    TA,
    TD,
}

#[derive(Debug)]
pub enum DCode {
    Operation,
    SelectAperture,
}

#[derive(Debug)]
pub enum InterpolationMode {
    Linear,
    ClockwiseCircular,
    CounterclockwiseCircular,
}

impl GerberCode for InterpolationMode {
    fn to_code(&self) -> String {
        match *self {
            InterpolationMode::Linear => "G01*",
            InterpolationMode::ClockwiseCircular => "G02*",
            InterpolationMode::CounterclockwiseCircular => "G03*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum QuadrantMode {
    Single,
    Multi,
}

impl GerberCode for QuadrantMode {
    fn to_code(&self) -> String {
        match *self {
            QuadrantMode::Single => "G74*",
            QuadrantMode::Multi => "G75*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum GCode {
    InterpolationMode(InterpolationMode),
    RegionMode(bool),
    QuadrantMode(QuadrantMode),
    Comment(String),
}

impl GerberCode for GCode {
    fn to_code(&self) -> String {
        match *self {
            GCode::InterpolationMode(ref mode) => mode.to_code(),
            GCode::RegionMode(enabled) => if enabled { "G36*".to_string() } else { "G37*".to_string() },
            GCode::QuadrantMode(ref mode) => mode.to_code(),
            GCode::Comment(ref comment) => format!("G04 {} *", comment),
        }
    }
}

#[derive(Debug)]
pub enum MCode {
    EndOfFile,
}

impl GerberCode for MCode {
    fn to_code(&self) -> String {
        match *self {
            MCode::EndOfFile => "M02*",
        }.to_string()
    }
}

/// Implement GerberCode for Vectors of types that implement GerberCode.
impl<T: GerberCode> GerberCode for Vec<T> {
    fn to_code(&self) -> String {
        self.iter()
            .map(|g| g.to_code())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

#[cfg(test)]
mod test {
    use super::{Command, FunctionCode};
    use super::{GCode, InterpolationMode, QuadrantMode};
    use super::{MCode};
    use super::{DCode, Coordinates, CoordinateOffset};
    use super::GerberCode;

    #[test]
    fn test_debug() {
        //! The debug representation should work properly.
        let c = Command::FunctionCode(FunctionCode::GCode(GCode::Comment("test".to_string())));
        let debug = format!("{:?}", c);
        assert_eq!(debug, "FunctionCode(GCode(Comment(\"test\")))");
    }

    #[test]
    fn test_to_code() {
        //! The to_code method of the GerberCode trait should generate strings.
        let comment = GCode::Comment("testcomment".to_string());
        assert_eq!(comment.to_code(), "G04 testcomment *".to_string());
    }

    #[test]
    fn test_vec_to_code() {
        //! A `Vec<T: GerberCode>` should also implement `GerberCode`.
        let mut v = Vec::new();
        v.push(GCode::Comment("comment 1".to_string()));
        v.push(GCode::Comment("another one".to_string()));
        assert_eq!(v.to_code(), "G04 comment 1 *\nG04 another one *".to_string());
    }

    #[test]
    fn test_interpolation_mode() {
        let mut commands = Vec::new();
        let c1 = GCode::InterpolationMode(InterpolationMode::Linear);
        let c2 = GCode::InterpolationMode(InterpolationMode::ClockwiseCircular);
        let c3 = GCode::InterpolationMode(InterpolationMode::CounterclockwiseCircular);
        commands.push(c1);
        commands.push(c2);
        commands.push(c3);
        assert_eq!(commands.to_code(), "G01*\nG02*\nG03*".to_string());
    }

    #[test]
    fn test_region_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::RegionMode(true));
        commands.push(GCode::RegionMode(false));
        assert_eq!(commands.to_code(), "G36*\nG37*".to_string());
    }

    #[test]
    fn test_quadrant_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::QuadrantMode(QuadrantMode::Single));
        commands.push(GCode::QuadrantMode(QuadrantMode::Multi));
        assert_eq!(commands.to_code(), "G74*\nG75*".to_string());
    }

    #[test]
    fn test_end_of_file() {
        let c = MCode::EndOfFile;
        assert_eq!(c.to_code(), "M02*".to_string());
    }

    #[test]
    fn test_coordinates() {
        macro_rules! assert_coords {
            ($x:expr, $y:expr, $result:expr) => {{
                assert_eq!(Coordinates { x: $x, y: $y }.to_code(), $result.to_string());
            }}
        }
        assert_coords!(Some(10), Some(20), "X10Y20");
        assert_coords!(None, None, ""); // TODO should we catch this?
        assert_coords!(Some(10), None, "X10");
        assert_coords!(None, Some(20), "Y20");
        assert_coords!(Some(0), Some(-400), "X0Y-400");
    }

    #[test]
    fn test_offset() {
        macro_rules! assert_coords {
            ($x:expr, $y:expr, $result:expr) => {{
                assert_eq!(CoordinateOffset { x: $x, y: $y }.to_code(), $result.to_string());
            }}
        }
        assert_coords!(Some(10), Some(20), "X10Y20");
        assert_coords!(None, None, ""); // TODO should we catch this?
        assert_coords!(Some(10), None, "X10");
        assert_coords!(None, Some(20), "Y20");
        assert_coords!(Some(0), Some(-400), "X0Y-400");
    }

}
<commit_msg>Make X/Y values in impl_xy_gerbercode! customizable<commit_after>/// All types that implement this trait can be converted to Gerber Code.
pub trait GerberCode {
    fn to_code(&self) -> String;
}

/// Automatically implement GerberCode trait for struct types
/// that are based on x and y attributes.
macro_rules! impl_xy_gerbercode {
    ($class:ty, $x:expr, $y: expr) => {
        impl GerberCode for $class {
            fn to_code(&self) -> String {
                let mut code = String::new();
                if let Some(x) = self.x {
                    code = format!("{}{}", $x, x);
                }
                if let Some(y) = self.y {
                    code.push_str(&format!("{}{}", $y, y));
                }
                code
            }
        }
    }
}

/// Coordinates are part of an operation.
///
/// Coordinates are modal. If an X is omitted, the X coordinate of the
/// current point is used. Similar for Y.
#[derive(Debug)]
pub struct Coordinates {
    pub x: Option<i32>,
    pub y: Option<i32>,
}

impl_xy_gerbercode!(Coordinates, "X", "Y");

/// Coordinate offsets can be used for interpolate operations in circular
/// interpolation mode.
#[derive(Debug)]
pub struct CoordinateOffset {
    pub x: Option<i32>,
    pub y: Option<i32>,
}

impl_xy_gerbercode!(CoordinateOffset, "I", "J");

#[derive(Debug)]
pub enum Command {
    FunctionCode(FunctionCode),
    ExtendedCode(ExtendedCode),
}

#[derive(Debug)]
pub enum FunctionCode {
    DCode(DCode),
    GCode(GCode),
    MCode(MCode),
}

#[derive(Debug)]
pub enum ExtendedCode {
    FS,
    MO,
    AD,
    AM,
    SR,
    LP,
    TF,
    TA,
    TD,
}

#[derive(Debug)]
pub enum DCode {
    Operation,
    SelectAperture,
}

#[derive(Debug)]
pub enum InterpolationMode {
    Linear,
    ClockwiseCircular,
    CounterclockwiseCircular,
}

impl GerberCode for InterpolationMode {
    fn to_code(&self) -> String {
        match *self {
            InterpolationMode::Linear => "G01*",
            InterpolationMode::ClockwiseCircular => "G02*",
            InterpolationMode::CounterclockwiseCircular => "G03*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum QuadrantMode {
    Single,
    Multi,
}

impl GerberCode for QuadrantMode {
    fn to_code(&self) -> String {
        match *self {
            QuadrantMode::Single => "G74*",
            QuadrantMode::Multi => "G75*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum GCode {
    InterpolationMode(InterpolationMode),
    RegionMode(bool),
    QuadrantMode(QuadrantMode),
    Comment(String),
}

impl GerberCode for GCode {
    fn to_code(&self) -> String {
        match *self {
            GCode::InterpolationMode(ref mode) => mode.to_code(),
            GCode::RegionMode(enabled) => if enabled { "G36*".to_string() } else { "G37*".to_string() },
            GCode::QuadrantMode(ref mode) => mode.to_code(),
            GCode::Comment(ref comment) => format!("G04 {} *", comment),
        }
    }
}

#[derive(Debug)]
pub enum MCode {
    EndOfFile,
}

impl GerberCode for MCode {
    fn to_code(&self) -> String {
        match *self {
            MCode::EndOfFile => "M02*",
        }.to_string()
    }
}

/// Implement GerberCode for Vectors of types that implement GerberCode.
impl<T: GerberCode> GerberCode for Vec<T> {
    fn to_code(&self) -> String {
        self.iter()
            .map(|g| g.to_code())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

#[cfg(test)]
mod test {
    use super::{Command, FunctionCode};
    use super::{GCode, InterpolationMode, QuadrantMode};
    use super::{MCode};
    use super::{DCode, Coordinates, CoordinateOffset};
    use super::GerberCode;

    #[test]
    fn test_debug() {
        //! The debug representation should work properly.
        let c = Command::FunctionCode(FunctionCode::GCode(GCode::Comment("test".to_string())));
        let debug = format!("{:?}", c);
        assert_eq!(debug, "FunctionCode(GCode(Comment(\"test\")))");
    }

    #[test]
    fn test_to_code() {
        //! The to_code method of the GerberCode trait should generate strings.
        let comment = GCode::Comment("testcomment".to_string());
        assert_eq!(comment.to_code(), "G04 testcomment *".to_string());
    }

    #[test]
    fn test_vec_to_code() {
        //! A `Vec<T: GerberCode>` should also implement `GerberCode`.
        let mut v = Vec::new();
        v.push(GCode::Comment("comment 1".to_string()));
        v.push(GCode::Comment("another one".to_string()));
        assert_eq!(v.to_code(), "G04 comment 1 *\nG04 another one *".to_string());
    }

    #[test]
    fn test_interpolation_mode() {
        let mut commands = Vec::new();
        let c1 = GCode::InterpolationMode(InterpolationMode::Linear);
        let c2 = GCode::InterpolationMode(InterpolationMode::ClockwiseCircular);
        let c3 = GCode::InterpolationMode(InterpolationMode::CounterclockwiseCircular);
        commands.push(c1);
        commands.push(c2);
        commands.push(c3);
        assert_eq!(commands.to_code(), "G01*\nG02*\nG03*".to_string());
    }

    #[test]
    fn test_region_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::RegionMode(true));
        commands.push(GCode::RegionMode(false));
        assert_eq!(commands.to_code(), "G36*\nG37*".to_string());
    }

    #[test]
    fn test_quadrant_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::QuadrantMode(QuadrantMode::Single));
        commands.push(GCode::QuadrantMode(QuadrantMode::Multi));
        assert_eq!(commands.to_code(), "G74*\nG75*".to_string());
    }

    #[test]
    fn test_end_of_file() {
        let c = MCode::EndOfFile;
        assert_eq!(c.to_code(), "M02*".to_string());
    }

    #[test]
    fn test_coordinates() {
        macro_rules! assert_coords {
            ($x:expr, $y:expr, $result:expr) => {{
                assert_eq!(Coordinates { x: $x, y: $y }.to_code(), $result.to_string());
            }}
        }
        assert_coords!(Some(10), Some(20), "X10Y20");
        assert_coords!(None, None, ""); // TODO should we catch this?
        assert_coords!(Some(10), None, "X10");
        assert_coords!(None, Some(20), "Y20");
        assert_coords!(Some(0), Some(-400), "X0Y-400");
    }

    #[test]
    fn test_offset() {
        macro_rules! assert_coords {
            ($x:expr, $y:expr, $result:expr) => {{
                assert_eq!(CoordinateOffset { x: $x, y: $y }.to_code(), $result.to_string());
            }}
        }
        assert_coords!(Some(10), Some(20), "I10J20");
        assert_coords!(None, None, ""); // TODO should we catch this?
        assert_coords!(Some(10), None, "I10");
        assert_coords!(None, Some(20), "J20");
        assert_coords!(Some(0), Some(-400), "I0J-400");
    }

}
<|endoftext|>


<commit_before>//! Game world display utilities.

use std::sync::Arc;
use calx_resource::Resource;
use calx_grid::{Dir12, Dir6};
use world::{Brush, Location, World};
use world::{Query, terrain};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
/// Draw layer for visual map elements.
pub enum Layer {
    /// Floor sprites, below other map forms.
    Floor,
    /// Blood splatters etc. on floor.
    Decal,
    /// Small items on floor,.
    Items,
    /// Large map objects, walls, most entities etc.
    Object,
    /// Transient effects shown on top of other map content.
    Effect,
    /// Text captions for map elements, on top of everything else.
    Text,
}

pub fn draw_terrain_sprites<F>(w: &World, loc: Location, mut draw: F)
    where F: FnMut(Layer, Angle, &Resource<Brush>, usize)
{
    use self::Angle::*;

    let terrain = w.terrain(loc);
    let kernel = Kernel::new(w, loc);

    match terrain.form {
        terrain::Form::Void | terrain::Form::Floor => {
            draw(Layer::Floor, Up, &terrain.brush, 0);
        }
        terrain::Form::Gate => {
            if let Some(d12) = Dir12::away_from(&kernel.void_mask()) {
                draw(Layer::Floor, Up, &terrain.brush, d12 as usize + 1);
            } else {
                draw(Layer::Floor, Up, &terrain.brush, 0);
            }
        }
        terrain::Form::Prop => {
            draw(Layer::Object, South, &terrain.brush, 0);
        }
        terrain::Form::Blob => {
            // This part gets a little tricky. Basic idea is that there's an inner pointy-top
            // hex core and the blob hull will snap to that instead of the outer flat-top hex
            // edge if neither adjacent face to the outer hex vertex is connected to another
            // blob.
            //
            // Based on how the sprites split up, the processing is done in four vertical
            // segments.

            let faces = kernel.blob_faces();

            // Do we snap to the outer vertices?
            let ne_vertex = !faces[0] || !faces[1];
            let e_vertex = !faces[1] || !faces[2];
            let se_vertex = !faces[2] || !faces[3];
            let sw_vertex = !faces[3] || !faces[4];
            let w_vertex = !faces[4] || !faces[5];
            let nw_vertex = !faces[5] || !faces[0];

            // Segment 2, middle left
            {
                if faces[0] {
                    if nw_vertex && ne_vertex {
                        draw(Layer::Object, North, &terrain.brush, 7);
                    } else if nw_vertex {
                        draw(Layer::Object, XWallBack, &terrain.brush, 15);
                    } else {
                        draw(Layer::Object, YWallBack, &terrain.brush, 11);
                    }
                }
                if faces[3] {
                    if sw_vertex && se_vertex {
                        draw(Layer::Object, South, &terrain.brush, 19);
                    } else if sw_vertex {
                        draw(Layer::Object, YWall, &terrain.brush, 23);
                    } else {
                        draw(Layer::Object, XWall, &terrain.brush, 27);
                    }
                }
            }

            // Segment 3, middle right
            {
                if faces[0] {
                    if ne_vertex && nw_vertex {
                        draw(Layer::Object, North, &terrain.brush, 8);
                    } else if ne_vertex {
                        draw(Layer::Object, YWallBack, &terrain.brush, 12);
                    } else {
                        draw(Layer::Object, XWallBack, &terrain.brush, 16);
                    }
                }
                if faces[3] {
                    if se_vertex && sw_vertex {
                        draw(Layer::Object, South, &terrain.brush, 20);
                    } else if se_vertex {
                        draw(Layer::Object, XWall, &terrain.brush, 28);
                    } else {
                        draw(Layer::Object, YWall, &terrain.brush, 24);
                    }
                }
            }

            // The side segments need to come after the middle
            // segments so that the vertical edges can overwrite the
            // middle segment pixels.

            // Segment 1, left edge
            {
                if w_vertex {
                    if faces[5] {
                        if nw_vertex {
                            draw(Layer::Object, Northwest, &terrain.brush, 6);
                        } else {
                            draw(Layer::Object, YWallBack, &terrain.brush, 10);
                        }
                    }

                    if faces[4] {
                        if sw_vertex {
                            draw(Layer::Object, Southwest, &terrain.brush, 18);
                        } else {
                            draw(Layer::Object, XWall, &terrain.brush, 26);
                        }
                    }
                } else {
                    // Draw the left vertical line.
                    draw(Layer::Object, West, &terrain.brush, 2);
                    if !faces[0] {
                        draw(Layer::Object, West, &terrain.brush, 0);
                    }
                    if !faces[3] {
                        draw(Layer::Object, West, &terrain.brush, 4);
                    }
                }
            }

            // Segment 4, right edge
            {
                if e_vertex {
                    if faces[1] {
                        if ne_vertex {
                            draw(Layer::Object, Northeast, &terrain.brush, 9);
                        } else {
                            draw(Layer::Object, XWallBack, &terrain.brush, 17);
                        }
                    }

                    if faces[2] {
                        if se_vertex {
                            draw(Layer::Object, Southeast, &terrain.brush, 21);
                        } else {
                            draw(Layer::Object, YWall, &terrain.brush, 25);
                        }
                    }
                } else {
                    // Draw the right vertical line.
                    draw(Layer::Object, East, &terrain.brush, 3);
                    if !faces[0] {
                        draw(Layer::Object, East, &terrain.brush, 1);
                    }
                    if !faces[3] {
                        draw(Layer::Object, East, &terrain.brush, 5);
                    }
                }
            }
        }
        terrain::Form::Wall => {
            let extends = kernel.wall_extends();
            if extends[0] {
                draw(Layer::Object, XWall, &terrain.brush, 2);
            } else {
                draw(Layer::Object, XWall, &terrain.brush, 0);
            }
            if extends[1] {
                draw(Layer::Object, YWall, &terrain.brush, 3);
            } else {
                draw(Layer::Object, YWall, &terrain.brush, 1);
            }
        }
    }
    // TODO: Generate sprites for entities, with tweening state projection.
    // TODO: Generate special effect sprites grounded on this location.
}

#[derive(Clone)]
pub struct Kernel {
    pub n: Arc<terrain::Tile>,
    pub ne: Arc<terrain::Tile>,
    pub nw: Arc<terrain::Tile>,
    pub center: Arc<terrain::Tile>,
    pub se: Arc<terrain::Tile>,
    pub sw: Arc<terrain::Tile>,
    pub s: Arc<terrain::Tile>,
}

fn neighbor(w: &World, loc: Location, dir: Dir6) -> Arc<terrain::Tile> {
    let loc = w.visible_portal(loc + dir.to_v2()).unwrap_or(loc + dir.to_v2());
    w.terrain(loc)
}

impl Kernel {
    pub fn new(w: &World, loc: Location) -> Kernel {
        Kernel {
            n: neighbor(w, loc, Dir6::North),
            ne: neighbor(w, loc, Dir6::NorthEast),
            nw: neighbor(w, loc, Dir6::NorthWest),
            center: w.terrain(loc),
            se: neighbor(w, loc, Dir6::SouthEast),
            sw: neighbor(w, loc, Dir6::SouthWest),
            s: neighbor(w, loc, Dir6::South),
        }
    }

    /// Bool is true if left/right half of wall should be extended.
    pub fn wall_extends(&self) -> [bool; 2] { [self.nw.is_wall(), self.ne.is_wall()] }

    /// Bool is true if n/ne/se/s/sw/nw face of block is facing open air.
    pub fn blob_faces(&self) -> [bool; 6] {
        // Because they work a bit differently visually, back-side faces
        // are not drawn if there is any hull touching, front is only
        // not drawn if there's another blob.
        [!self.n.is_hull(),
         !self.ne.is_hull(),
         !self.se.is_blob(),
         !self.s.is_blob(),
         !self.sw.is_blob(),
         !self.nw.is_hull()]
    }

    /// Mark neighbors that are not void terrain as true.
    pub fn void_mask(&self) -> [bool; 6] {
        [self.n.form != terrain::Form::Void,
         self.ne.form != terrain::Form::Void,
         self.se.form != terrain::Form::Void,
         self.s.form != terrain::Form::Void,
         self.sw.form != terrain::Form::Void,
         self.nw.form != terrain::Form::Void]
    }
}
<commit_msg>Fix compass direction capitalization<commit_after>//! Game world display utilities.

use std::sync::Arc;
use calx_resource::Resource;
use calx_grid::{Dir12, Dir6};
use world::{Brush, Location, World};
use world::{Query, terrain};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
/// Draw layer for visual map elements.
pub enum Layer {
    /// Floor sprites, below other map forms.
    Floor,
    /// Blood splatters etc. on floor.
    Decal,
    /// Small items on floor,.
    Items,
    /// Large map objects, walls, most entities etc.
    Object,
    /// Transient effects shown on top of other map content.
    Effect,
    /// Text captions for map elements, on top of everything else.
    Text,
}

pub fn draw_terrain_sprites<F>(w: &World, loc: Location, mut draw: F)
    where F: FnMut(Layer, Angle, &Resource<Brush>, usize)
{
    use self::Angle::*;

    let terrain = w.terrain(loc);
    let kernel = Kernel::new(w, loc);

    match terrain.form {
        terrain::Form::Void | terrain::Form::Floor => {
            draw(Layer::Floor, Up, &terrain.brush, 0);
        }
        terrain::Form::Gate => {
            if let Some(d12) = Dir12::away_from(&kernel.void_mask()) {
                draw(Layer::Floor, Up, &terrain.brush, d12 as usize + 1);
            } else {
                draw(Layer::Floor, Up, &terrain.brush, 0);
            }
        }
        terrain::Form::Prop => {
            draw(Layer::Object, South, &terrain.brush, 0);
        }
        terrain::Form::Blob => {
            // This part gets a little tricky. Basic idea is that there's an inner pointy-top
            // hex core and the blob hull will snap to that instead of the outer flat-top hex
            // edge if neither adjacent face to the outer hex vertex is connected to another
            // blob.
            //
            // Based on how the sprites split up, the processing is done in four vertical
            // segments.

            let faces = kernel.blob_faces();

            // Do we snap to the outer vertices?
            let ne_vertex = !faces[0] || !faces[1];
            let e_vertex = !faces[1] || !faces[2];
            let se_vertex = !faces[2] || !faces[3];
            let sw_vertex = !faces[3] || !faces[4];
            let w_vertex = !faces[4] || !faces[5];
            let nw_vertex = !faces[5] || !faces[0];

            // Segment 2, middle left
            {
                if faces[0] {
                    if nw_vertex && ne_vertex {
                        draw(Layer::Object, North, &terrain.brush, 7);
                    } else if nw_vertex {
                        draw(Layer::Object, XWallBack, &terrain.brush, 15);
                    } else {
                        draw(Layer::Object, YWallBack, &terrain.brush, 11);
                    }
                }
                if faces[3] {
                    if sw_vertex && se_vertex {
                        draw(Layer::Object, South, &terrain.brush, 19);
                    } else if sw_vertex {
                        draw(Layer::Object, YWall, &terrain.brush, 23);
                    } else {
                        draw(Layer::Object, XWall, &terrain.brush, 27);
                    }
                }
            }

            // Segment 3, middle right
            {
                if faces[0] {
                    if ne_vertex && nw_vertex {
                        draw(Layer::Object, North, &terrain.brush, 8);
                    } else if ne_vertex {
                        draw(Layer::Object, YWallBack, &terrain.brush, 12);
                    } else {
                        draw(Layer::Object, XWallBack, &terrain.brush, 16);
                    }
                }
                if faces[3] {
                    if se_vertex && sw_vertex {
                        draw(Layer::Object, South, &terrain.brush, 20);
                    } else if se_vertex {
                        draw(Layer::Object, XWall, &terrain.brush, 28);
                    } else {
                        draw(Layer::Object, YWall, &terrain.brush, 24);
                    }
                }
            }

            // The side segments need to come after the middle
            // segments so that the vertical edges can overwrite the
            // middle segment pixels.

            // Segment 1, left edge
            {
                if w_vertex {
                    if faces[5] {
                        if nw_vertex {
                            draw(Layer::Object, Northwest, &terrain.brush, 6);
                        } else {
                            draw(Layer::Object, YWallBack, &terrain.brush, 10);
                        }
                    }

                    if faces[4] {
                        if sw_vertex {
                            draw(Layer::Object, Southwest, &terrain.brush, 18);
                        } else {
                            draw(Layer::Object, XWall, &terrain.brush, 26);
                        }
                    }
                } else {
                    // Draw the left vertical line.
                    draw(Layer::Object, West, &terrain.brush, 2);
                    if !faces[0] {
                        draw(Layer::Object, West, &terrain.brush, 0);
                    }
                    if !faces[3] {
                        draw(Layer::Object, West, &terrain.brush, 4);
                    }
                }
            }

            // Segment 4, right edge
            {
                if e_vertex {
                    if faces[1] {
                        if ne_vertex {
                            draw(Layer::Object, Northeast, &terrain.brush, 9);
                        } else {
                            draw(Layer::Object, XWallBack, &terrain.brush, 17);
                        }
                    }

                    if faces[2] {
                        if se_vertex {
                            draw(Layer::Object, Southeast, &terrain.brush, 21);
                        } else {
                            draw(Layer::Object, YWall, &terrain.brush, 25);
                        }
                    }
                } else {
                    // Draw the right vertical line.
                    draw(Layer::Object, East, &terrain.brush, 3);
                    if !faces[0] {
                        draw(Layer::Object, East, &terrain.brush, 1);
                    }
                    if !faces[3] {
                        draw(Layer::Object, East, &terrain.brush, 5);
                    }
                }
            }
        }
        terrain::Form::Wall => {
            let extends = kernel.wall_extends();
            if extends[0] {
                draw(Layer::Object, XWall, &terrain.brush, 2);
            } else {
                draw(Layer::Object, XWall, &terrain.brush, 0);
            }
            if extends[1] {
                draw(Layer::Object, YWall, &terrain.brush, 3);
            } else {
                draw(Layer::Object, YWall, &terrain.brush, 1);
            }
        }
    }
    // TODO: Generate sprites for entities, with tweening state projection.
    // TODO: Generate special effect sprites grounded on this location.
}

#[derive(Clone)]
pub struct Kernel {
    pub n: Arc<terrain::Tile>,
    pub ne: Arc<terrain::Tile>,
    pub nw: Arc<terrain::Tile>,
    pub center: Arc<terrain::Tile>,
    pub se: Arc<terrain::Tile>,
    pub sw: Arc<terrain::Tile>,
    pub s: Arc<terrain::Tile>,
}

fn neighbor(w: &World, loc: Location, dir: Dir6) -> Arc<terrain::Tile> {
    let loc = w.visible_portal(loc + dir.to_v2()).unwrap_or(loc + dir.to_v2());
    w.terrain(loc)
}

impl Kernel {
    pub fn new(w: &World, loc: Location) -> Kernel {
        Kernel {
            n: neighbor(w, loc, Dir6::North),
            ne: neighbor(w, loc, Dir6::Northeast),
            nw: neighbor(w, loc, Dir6::Northwest),
            center: w.terrain(loc),
            se: neighbor(w, loc, Dir6::Southeast),
            sw: neighbor(w, loc, Dir6::Southwest),
            s: neighbor(w, loc, Dir6::South),
        }
    }

    /// Bool is true if left/right half of wall should be extended.
    pub fn wall_extends(&self) -> [bool; 2] { [self.nw.is_wall(), self.ne.is_wall()] }

    /// Bool is true if n/ne/se/s/sw/nw face of block is facing open air.
    pub fn blob_faces(&self) -> [bool; 6] {
        // Because they work a bit differently visually, back-side faces
        // are not drawn if there is any hull touching, front is only
        // not drawn if there's another blob.
        [!self.n.is_hull(),
         !self.ne.is_hull(),
         !self.se.is_blob(),
         !self.s.is_blob(),
         !self.sw.is_blob(),
         !self.nw.is_hull()]
    }

    /// Mark neighbors that are not void terrain as true.
    pub fn void_mask(&self) -> [bool; 6] {
        [self.n.form != terrain::Form::Void,
         self.ne.form != terrain::Form::Void,
         self.se.form != terrain::Form::Void,
         self.s.form != terrain::Form::Void,
         self.sw.form != terrain::Form::Void,
         self.nw.form != terrain::Form::Void]
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    function_data_builder::{FunctionDataBuilder, FunctionDataBuilderOption},
    function_target::{FunctionData, FunctionTarget},
    function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder},
    graph::Graph,
    stackless_bytecode::{AttrId, BorrowNode, Bytecode, Label, Operation, PropKind},
    stackless_control_flow_graph::{BlockContent, BlockId, StacklessControlFlowGraph},
};
use move_model::{
    ast::{self, TempIndex},
    model::FunctionEnv,
};
use std::collections::{BTreeMap, BTreeSet};
use vm::file_format::CodeOffset;

#[derive(Debug, Default, Clone)]
pub struct LoopInfo {
    pub invariants: BTreeMap<CodeOffset, (AttrId, ast::Exp)>,
    pub targets: BTreeSet<TempIndex>,
    pub back_edge_location: CodeOffset,
}

#[derive(Debug, Default, Clone)]
pub struct LoopAnnotation {
    pub loops: BTreeMap<Label, LoopInfo>,
}

impl LoopAnnotation {
    fn back_edges_locations(&self) -> BTreeSet<CodeOffset> {
        self.loops.values().map(|l| l.back_edge_location).collect()
    }

    fn invariants_locations(&self) -> BTreeSet<CodeOffset> {
        self.loops
            .values()
            .map(|l| l.invariants.keys())
            .flatten()
            .copied()
            .collect()
    }
}

pub struct LoopAnalysisProcessor {}

impl LoopAnalysisProcessor {
    pub fn new() -> Box<Self> {
        Box::new(LoopAnalysisProcessor {})
    }
}

impl FunctionTargetProcessor for LoopAnalysisProcessor {
    fn process(
        &self,
        _targets: &mut FunctionTargetsHolder,
        func_env: &FunctionEnv<'_>,
        data: FunctionData,
    ) -> FunctionData {
        if func_env.is_native() {
            return data;
        }
        let loop_annotation = Self::build_loop_annotation(func_env, &data);
        Self::transform(func_env, data, &loop_annotation)
    }

    fn name(&self) -> String {
        "loop_analysis".to_string()
    }
}

impl LoopAnalysisProcessor {
    fn transform(
        func_env: &FunctionEnv<'_>,
        data: FunctionData,
        loop_annotation: &LoopAnnotation,
    ) -> FunctionData {
        let back_edge_locs = loop_annotation.back_edges_locations();
        let invariant_locs = loop_annotation.invariants_locations();
        let mut builder = FunctionDataBuilder::new_with_option(
            func_env,
            data,
            FunctionDataBuilderOption {
                no_fallthrough_jump_removal: true,
            },
        );
        let mut goto_fixes = vec![];
        let code = std::mem::take(&mut builder.data.code);
        for (offset, bytecode) in code.into_iter().enumerate() {
            match bytecode {
                Bytecode::Label(attr_id, label) => {
                    builder.emit(bytecode);
                    builder.set_loc_from_attr(attr_id);
                    if let Some(loop_info) = loop_annotation.loops.get(&label) {
                        // assert loop invariants -> this is the base case
                        for (_, exp) in loop_info.invariants.values() {
                            builder.emit_with(|attr_id| {
                                Bytecode::Prop(attr_id, PropKind::Assert, exp.clone())
                            });
                        }

                        // havoc all loop targets
                        for idx in &loop_info.targets {
                            builder.emit_with(|attr_id| {
                                Bytecode::Call(attr_id, vec![], Operation::Havoc, vec![*idx], None)
                            });
                        }

                        // add additional assumptions
                        let exp =
                            builder.mk_not(builder.mk_bool_call(ast::Operation::AbortFlag, vec![]));
                        builder.emit_with(|attr_id| Bytecode::Prop(attr_id, PropKind::Assume, exp));
                        for idx in &loop_info.targets {
                            let exp = builder.mk_bool_call(
                                ast::Operation::WellFormed,
                                vec![builder.mk_temporary(*idx)],
                            );
                            builder.emit_with(|attr_id| {
                                Bytecode::Prop(attr_id, PropKind::Assume, exp)
                            });
                        }

                        // re-assume loop invariants
                        for (attr_id, exp) in loop_info.invariants.values() {
                            builder.emit(Bytecode::Prop(*attr_id, PropKind::Assume, exp.clone()));
                        }
                    }
                }
                Bytecode::Prop(_, PropKind::Assert, _)
                    if invariant_locs.contains(&(offset as CodeOffset)) =>
                {
                    // skip it, as the invariant should have been added as an assert after the label
                }
                _ => {
                    builder.emit(bytecode);
                }
            }
            // mark that the goto labels in this bytecode needs to be updated to a new label
            // representing the invariant-checking block for the loop.
            if back_edge_locs.contains(&(offset as CodeOffset)) {
                goto_fixes.push(builder.data.code.len() - 1);
            }
        }

        // create one invariant-checking block for each loop
        let invariant_checker_labels: BTreeMap<_, _> = loop_annotation
            .loops
            .keys()
            .map(|label| (*label, builder.new_label()))
            .collect();

        for (label, loop_info) in &loop_annotation.loops {
            let checker_label = invariant_checker_labels.get(label).unwrap();
            builder.set_next_debug_comment(format!(
                "Loop invariant checking block for the loop started with header: L{}",
                label.as_usize()
            ));
            builder.emit_with(|attr_id| Bytecode::Label(attr_id, *checker_label));
            builder.clear_next_debug_comment();

            // add instrumentations to assert loop invariants -> this is the induction case
            for (_, exp) in loop_info.invariants.values() {
                builder.emit_with(|attr_id| Bytecode::Prop(attr_id, PropKind::Assert, exp.clone()));
            }

            // stop the checking
            builder.emit_with(|attr_id| {
                Bytecode::Call(attr_id, vec![], Operation::Stop, vec![], None)
            });
        }

        // fix the goto statements in the loop latch blocks
        for code_offset in goto_fixes {
            let updated_goto = match &builder.data.code[code_offset] {
                Bytecode::Jump(attr_id, old_label) => {
                    Bytecode::Jump(*attr_id, *invariant_checker_labels.get(old_label).unwrap())
                }
                Bytecode::Branch(attr_id, if_label, else_label, idx) => {
                    let new_if_label = *invariant_checker_labels.get(if_label).unwrap_or(if_label);
                    let new_else_label = *invariant_checker_labels
                        .get(else_label)
                        .unwrap_or(else_label);
                    Bytecode::Branch(*attr_id, new_if_label, new_else_label, *idx)
                }
                _ => panic!("Expect a branch statement"),
            };
            builder.data.code[code_offset] = updated_goto;
        }

        builder.data
    }

    fn build_loop_annotation(func_env: &FunctionEnv<'_>, data: &FunctionData) -> LoopAnnotation {
        // build for natural loops
        let func_target = FunctionTarget::new(func_env, data);
        let code = func_target.get_bytecode();
        let cfg = StacklessControlFlowGraph::new_forward(code);
        let entry = cfg.entry_block();
        let nodes = cfg.blocks();
        let edges: Vec<(BlockId, BlockId)> = nodes
            .iter()
            .map(|x| {
                cfg.successors(*x)
                    .iter()
                    .map(|y| (*x, *y))
                    .collect::<Vec<(BlockId, BlockId)>>()
            })
            .flatten()
            .collect();
        let graph = Graph::new(entry, nodes, edges);
        let natural_loops = graph.compute_reducible().expect(
            "A well-formed Move function is expected to have a reducible control-flow graph",
        );

        // collect labels where loop invariant instrumentations are expected to be placed at
        let loop_header_to_label: BTreeMap<BlockId, Label> = natural_loops
            .iter()
            .filter_map(|l| match cfg.content(l.loop_header) {
                BlockContent::Dummy => None,
                BlockContent::Basic { lower, upper: _ } => {
                    if let Bytecode::Label(_, label) = code[*lower as usize] {
                        Some((l.loop_header, label))
                    } else {
                        None
                    }
                }
            })
            .collect();
        if loop_header_to_label.len() != natural_loops.len() {
            panic!(
                "Natural loops in a well-formed Move function are expected to have unique headers \
                and each loop header is expected to start with a Label bytecode"
            );
        }

        // find loop targets and invariants per loop (also means, per label)
        let mut loops = BTreeMap::new();
        for single_loop in &natural_loops {
            let loop_label = loop_header_to_label[&single_loop.loop_header];

            let targets = single_loop
                .loop_body
                .iter()
                .map(|block_id| {
                    cfg.instr_indexes(*block_id)
                        .expect("A loop body should never contain a dummy block")
                })
                .flatten()
                .map(|code_offset| Self::targets(&code[code_offset as usize]))
                .flatten()
                .collect();

            // Loop invariants are defined as the longest sequence of consecutive 'assert'
            // statements in the loop header block, immediately after the Label statement.
            //
            // In other words, for the loop header block:
            // - the first statement must be a 'label',
            // - followed by N 'assert' statements, N >= 0
            // - and N + 1 must not be an 'assert' statement.
            let mut invariants = BTreeMap::new();
            for (index, code_offset) in cfg
                .instr_indexes(single_loop.loop_header)
                .unwrap()
                .enumerate()
            {
                let bytecode = &code[code_offset as usize];
                if index == 0 {
                    assert!(matches!(bytecode, Bytecode::Label(_, _)));
                } else {
                    match bytecode {
                        Bytecode::Prop(attr_id, PropKind::Assert, exp) => {
                            invariants.insert(code_offset, (*attr_id, exp.clone()));
                        }
                        _ => break,
                    }
                }
            }

            let back_edge_location = match cfg.content(single_loop.loop_latch) {
                BlockContent::Dummy => panic!("A loop body should never contain a dummy block"),
                BlockContent::Basic { upper, .. } => *upper,
            };
            match &code[back_edge_location as usize] {
                Bytecode::Jump(_, goto_label) if *goto_label == loop_label => {}
                Bytecode::Branch(_, if_label, else_label, _)
                    if *if_label == loop_label || *else_label == loop_label => {}
                _ => panic!("The latch bytecode of a loop does not branch into the header"),
            }

            loops.insert(
                loop_label,
                LoopInfo {
                    invariants,
                    targets,
                    back_edge_location,
                },
            );
        }

        LoopAnnotation { loops }
    }

    fn targets(bytecode: &Bytecode) -> Vec<TempIndex> {
        use BorrowNode::*;
        match bytecode {
            Bytecode::Assign(_, dest, _, _) => vec![*dest],
            Bytecode::Load(_, dest, _) => vec![*dest],
            Bytecode::Call(_, _, Operation::WriteBack(LocalRoot(dest), ..), ..) => vec![*dest],
            Bytecode::Call(_, _, Operation::WriteBack(Reference(dest), ..), ..) => vec![*dest],
            Bytecode::Call(_, dests, ..) => dests.clone(),
            _ => vec![],
        }
    }
}
<commit_msg>[move-prover] capture loop with multiple backedges as fat-loop<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    function_data_builder::{FunctionDataBuilder, FunctionDataBuilderOption},
    function_target::{FunctionData, FunctionTarget},
    function_target_pipeline::{FunctionTargetProcessor, FunctionTargetsHolder},
    graph::Graph,
    stackless_bytecode::{AttrId, BorrowNode, Bytecode, Label, Operation, PropKind},
    stackless_control_flow_graph::{BlockContent, BlockId, StacklessControlFlowGraph},
};
use move_model::{
    ast::{self, TempIndex},
    model::FunctionEnv,
};
use std::collections::{BTreeMap, BTreeSet};
use vm::file_format::CodeOffset;

/// A fat-loop captures the information of one or more natural loops that share the same loop
/// header. This shared header is called the header of the fat-loop.
///
/// Conceptually, every back edge defines a unique natural loop and different back edges may points
/// to the same loop header (e.g., when there are two "continue" statements in the loop body).
///
/// However, since these natural loops share the same loop header, they share the same loop
/// invariants too and the fat-loop targets (i.e., variables that may be changed in any sub-loop)
/// is the union of loop targets per each natural loop that share the header.
#[derive(Debug, Clone)]
pub struct FatLoop {
    pub invariants: BTreeMap<CodeOffset, (AttrId, ast::Exp)>,
    pub targets: BTreeSet<TempIndex>,
    pub back_edges: BTreeSet<CodeOffset>,
}

#[derive(Debug, Clone)]
pub struct LoopAnnotation {
    pub fat_loops: BTreeMap<Label, FatLoop>,
}

impl LoopAnnotation {
    fn back_edges_locations(&self) -> BTreeSet<CodeOffset> {
        self.fat_loops
            .values()
            .map(|l| l.back_edges.iter())
            .flatten()
            .copied()
            .collect()
    }

    fn invariants_locations(&self) -> BTreeSet<CodeOffset> {
        self.fat_loops
            .values()
            .map(|l| l.invariants.keys())
            .flatten()
            .copied()
            .collect()
    }
}

pub struct LoopAnalysisProcessor {}

impl LoopAnalysisProcessor {
    pub fn new() -> Box<Self> {
        Box::new(LoopAnalysisProcessor {})
    }
}

impl FunctionTargetProcessor for LoopAnalysisProcessor {
    fn process(
        &self,
        _targets: &mut FunctionTargetsHolder,
        func_env: &FunctionEnv<'_>,
        data: FunctionData,
    ) -> FunctionData {
        if func_env.is_native() {
            return data;
        }
        let loop_annotation = Self::build_loop_annotation(func_env, &data);
        Self::transform(func_env, data, &loop_annotation)
    }

    fn name(&self) -> String {
        "loop_analysis".to_string()
    }
}

impl LoopAnalysisProcessor {
    fn transform(
        func_env: &FunctionEnv<'_>,
        data: FunctionData,
        loop_annotation: &LoopAnnotation,
    ) -> FunctionData {
        let back_edge_locs = loop_annotation.back_edges_locations();
        let invariant_locs = loop_annotation.invariants_locations();
        let mut builder = FunctionDataBuilder::new_with_option(
            func_env,
            data,
            FunctionDataBuilderOption {
                no_fallthrough_jump_removal: true,
            },
        );
        let mut goto_fixes = vec![];
        let code = std::mem::take(&mut builder.data.code);
        for (offset, bytecode) in code.into_iter().enumerate() {
            match bytecode {
                Bytecode::Label(attr_id, label) => {
                    builder.emit(bytecode);
                    builder.set_loc_from_attr(attr_id);
                    if let Some(loop_info) = loop_annotation.fat_loops.get(&label) {
                        // assert loop invariants -> this is the base case
                        for (_, exp) in loop_info.invariants.values() {
                            builder.emit_with(|attr_id| {
                                Bytecode::Prop(attr_id, PropKind::Assert, exp.clone())
                            });
                        }

                        // havoc all loop targets
                        for idx in &loop_info.targets {
                            builder.emit_with(|attr_id| {
                                Bytecode::Call(attr_id, vec![], Operation::Havoc, vec![*idx], None)
                            });
                        }

                        // add additional assumptions
                        let exp =
                            builder.mk_not(builder.mk_bool_call(ast::Operation::AbortFlag, vec![]));
                        builder.emit_with(|attr_id| Bytecode::Prop(attr_id, PropKind::Assume, exp));
                        for idx in &loop_info.targets {
                            let exp = builder.mk_bool_call(
                                ast::Operation::WellFormed,
                                vec![builder.mk_temporary(*idx)],
                            );
                            builder.emit_with(|attr_id| {
                                Bytecode::Prop(attr_id, PropKind::Assume, exp)
                            });
                        }

                        // re-assume loop invariants
                        for (attr_id, exp) in loop_info.invariants.values() {
                            builder.emit(Bytecode::Prop(*attr_id, PropKind::Assume, exp.clone()));
                        }
                    }
                }
                Bytecode::Prop(_, PropKind::Assert, _)
                    if invariant_locs.contains(&(offset as CodeOffset)) =>
                {
                    // skip it, as the invariant should have been added as an assert after the label
                }
                _ => {
                    builder.emit(bytecode);
                }
            }
            // mark that the goto labels in this bytecode needs to be updated to a new label
            // representing the invariant-checking block for the loop.
            if back_edge_locs.contains(&(offset as CodeOffset)) {
                goto_fixes.push(builder.data.code.len() - 1);
            }
        }

        // create one invariant-checking block for each fat loop
        let invariant_checker_labels: BTreeMap<_, _> = loop_annotation
            .fat_loops
            .keys()
            .map(|label| (*label, builder.new_label()))
            .collect();

        for (label, loop_info) in &loop_annotation.fat_loops {
            let checker_label = invariant_checker_labels.get(label).unwrap();
            builder.set_next_debug_comment(format!(
                "Loop invariant checking block for the loop started with header: L{}",
                label.as_usize()
            ));
            builder.emit_with(|attr_id| Bytecode::Label(attr_id, *checker_label));
            builder.clear_next_debug_comment();

            // add instrumentations to assert loop invariants -> this is the induction case
            for (_, exp) in loop_info.invariants.values() {
                builder.emit_with(|attr_id| Bytecode::Prop(attr_id, PropKind::Assert, exp.clone()));
            }

            // stop the checking
            builder.emit_with(|attr_id| {
                Bytecode::Call(attr_id, vec![], Operation::Stop, vec![], None)
            });
        }

        // fix the goto statements in the loop latch blocks
        for code_offset in goto_fixes {
            let updated_goto = match &builder.data.code[code_offset] {
                Bytecode::Jump(attr_id, old_label) => {
                    Bytecode::Jump(*attr_id, *invariant_checker_labels.get(old_label).unwrap())
                }
                Bytecode::Branch(attr_id, if_label, else_label, idx) => {
                    let new_if_label = *invariant_checker_labels.get(if_label).unwrap_or(if_label);
                    let new_else_label = *invariant_checker_labels
                        .get(else_label)
                        .unwrap_or(else_label);
                    Bytecode::Branch(*attr_id, new_if_label, new_else_label, *idx)
                }
                _ => panic!("Expect a branch statement"),
            };
            builder.data.code[code_offset] = updated_goto;
        }

        builder.data
    }

    fn build_loop_annotation(func_env: &FunctionEnv<'_>, data: &FunctionData) -> LoopAnnotation {
        // build for natural loops
        let func_target = FunctionTarget::new(func_env, data);
        let code = func_target.get_bytecode();
        let cfg = StacklessControlFlowGraph::new_forward(code);
        let entry = cfg.entry_block();
        let nodes = cfg.blocks();
        let edges: Vec<(BlockId, BlockId)> = nodes
            .iter()
            .map(|x| {
                cfg.successors(*x)
                    .iter()
                    .map(|y| (*x, *y))
                    .collect::<Vec<(BlockId, BlockId)>>()
            })
            .flatten()
            .collect();
        let graph = Graph::new(entry, nodes, edges);
        let natural_loops = graph.compute_reducible().expect(
            "A well-formed Move function is expected to have a reducible control-flow graph",
        );

        // collect shared headers from loops
        let mut fat_headers = BTreeMap::new();
        for single_loop in natural_loops {
            fat_headers
                .entry(single_loop.loop_header)
                .or_insert_with(Vec::new)
                .push(single_loop);
        }

        // build fat loops by label
        let mut fat_loops = BTreeMap::new();
        for (fat_root, sub_loops) in fat_headers {
            // get the label of the scc root
            let label = match cfg.content(fat_root) {
                BlockContent::Dummy => panic!("A loop header should never be a dummy block"),
                BlockContent::Basic { lower, upper: _ } => match code[*lower as usize] {
                    Bytecode::Label(_, label) => label,
                    _ => panic!("A loop header block is expected to start with a Label bytecode"),
                },
            };

            // collect invariants
            //
            // Loop invariants are defined as the longest sequence of consecutive 'assert'
            // statements in the loop header block, immediately after the Label statement.
            //
            // In other words, for the loop header block:
            // - the first statement must be a 'label',
            // - followed by N 'assert' statements, N >= 0
            // - and N + 1 must not be an 'assert' statement.
            let mut invariants = BTreeMap::new();
            for (index, code_offset) in cfg.instr_indexes(fat_root).unwrap().enumerate() {
                let bytecode = &code[code_offset as usize];
                if index == 0 {
                    assert!(matches!(bytecode, Bytecode::Label(_, _)));
                } else {
                    match bytecode {
                        Bytecode::Prop(attr_id, PropKind::Assert, exp) => {
                            invariants.insert(code_offset, (*attr_id, exp.clone()));
                        }
                        _ => break,
                    }
                }
            }

            // collect fat loop targets
            let fat_loop_body: BTreeSet<_> = sub_loops
                .iter()
                .map(|l| l.loop_body.iter())
                .flatten()
                .copied()
                .collect();
            let targets = fat_loop_body
                .iter()
                .map(|block_id| {
                    cfg.instr_indexes(*block_id)
                        .expect("A loop body should never contain a dummy block")
                })
                .flatten()
                .map(|code_offset| Self::targets(&code[code_offset as usize]))
                .flatten()
                .collect();

            // collect back edge locations
            let back_edges = sub_loops
                .iter()
                .map(|l| {
                    let code_offset = match cfg.content(l.loop_latch) {
                        BlockContent::Dummy => {
                            panic!("A loop body should never contain a dummy block")
                        }
                        BlockContent::Basic { upper, .. } => *upper,
                    };
                    match &code[code_offset as usize] {
                        Bytecode::Jump(_, goto_label) if *goto_label == label => {}
                        Bytecode::Branch(_, if_label, else_label, _)
                            if *if_label == label || *else_label == label => {}
                        _ => panic!("The latch bytecode of a loop does not branch into the header"),
                    };
                    code_offset
                })
                .collect();

            // done with all information collection
            fat_loops.insert(
                label,
                FatLoop {
                    invariants,
                    targets,
                    back_edges,
                },
            );
        }

        LoopAnnotation { fat_loops }
    }

    fn targets(bytecode: &Bytecode) -> Vec<TempIndex> {
        use BorrowNode::*;
        match bytecode {
            Bytecode::Assign(_, dest, _, _) => vec![*dest],
            Bytecode::Load(_, dest, _) => vec![*dest],
            Bytecode::Call(_, _, Operation::WriteBack(LocalRoot(dest), ..), ..) => vec![*dest],
            Bytecode::Call(_, _, Operation::WriteBack(Reference(dest), ..), ..) => vec![*dest],
            Bytecode::Call(_, dests, ..) => dests.clone(),
            _ => vec![],
        }
    }
}
<|endoftext|>