<commit_before>#![feature(collections)]
#![feature(io)]
#![feature(os)]
#![feature(rand)]

extern crate "rustc-serialize" as serialize;
extern crate getopts;
extern crate crc24;

use std::os;
use std::rand::{ Rng, OsRng };
use std::iter::repeat;
use std::old_io::{ stdio, IoError, IoErrorKind, IoResult, BufferedReader };

use serialize::base64::{ self, FromBase64, ToBase64 };
use getopts::Options;

use gf256::Gf256;

mod gf256;

fn new_vec<T: Clone>(n: usize, x: T) -> Vec<T> {
	repeat(x).take(n).collect()
}

fn other_io_err(s: &'static str) -> IoError {
	IoError {
		kind: IoErrorKind::OtherIoError,
		desc: s,
		detail: None
	}
}

/// evaluates a polynomial at x=1, 2, 3, ... n (inclusive)
fn encode<W: Writer>(src: &[u8], n: u8, w: &mut W) -> IoResult<()> {
	for raw_x in 1 .. ((n as u16) + 1) {
		let x = Gf256::from_byte(raw_x as u8);
		let mut fac = Gf256::one();
		let mut acc = Gf256::zero();
		for &coeff in src.iter() {
			acc = acc + fac * Gf256::from_byte(coeff);
			fac = fac * x;
		}
		try!(w.write_u8(acc.to_byte()));
	}
	Ok(())
}

/// evaluates an interpolated polynomial at `raw_x` where
/// the polynomial is determined using Lagrangian interpolation
/// based on the given x/y coordinates `src`.
fn lagrange_interpolate(src: &[(u8, u8)], raw_x: u8) -> u8 {
	let x = Gf256::from_byte(raw_x);
	let mut sum = Gf256::zero();
	for (i, &(raw_xi, raw_yi)) in src.iter().enumerate() {
		let xi = Gf256::from_byte(raw_xi);
		let yi = Gf256::from_byte(raw_yi);
		let mut lix = Gf256::one();
		for (j, &(raw_xj, _)) in src.iter().enumerate() {
			if i != j {
				let xj = Gf256::from_byte(raw_xj);
				let delta = xi - xj;
				assert!(delta.poly !=0, "Duplicate shares");
				lix = lix * (x - xj) / delta;
			}
		}
		sum = sum + lix * yi;
	}
	sum.to_byte()
}

fn secret_share(src: &[u8], k: u8, n: u8) -> IoResult<Vec<Vec<u8>>> {
	let mut result = Vec::with_capacity(n as usize);
	for _ in 0 .. (n as usize) {
		result.push(new_vec(src.len(), 0u8));
	}
	let mut col_in = new_vec(k as usize, 0u8);
	let mut col_out = Vec::with_capacity(n as usize);
	let mut osrng = try!(OsRng::new());
	for (c, &s) in src.iter().enumerate() {
		col_in[0] = s;
		osrng.fill_bytes(&mut col_in[1..]);
		col_out.clear();
		try!(encode(&*col_in, n, &mut col_out));
		for (&y, share) in col_out.iter().zip(result.iter_mut()) {
			share[c] = y;
		}
	}
	Ok(result)
}

enum Action {
	Encode(u8, u8), // k and n parameter
	Decode
}

fn parse_k_n(s: &str) -> Option<(u8, u8)> {
	let mut iter = s.split(',');
	let first = match iter.next() { Some(x) => x, None => return None };
	let second = match iter.next() { Some(x) => x, None => return None };
	let k = match first.parse() { Some(x) => x, None => return None };
	let n = match second.parse() { Some(x) => x, None => return None };
	Some((k, n))
}

/// tries to read everything but stops early if the input seems to be
/// larger than `max` bytes and returns an IoError in this case
fn read_no_more_than<R: Reader>(r: &mut R, max: usize) -> IoResult<Vec<u8>> {
	let mut data = Vec::new();
	loop {
		if let Err(e) = r.push(max + 1 - data.len(), &mut data) {
			if e.kind == IoErrorKind::EndOfFile {
				break; // EOF condition is actually OK
			} else {
				return Err(e);
			}
		}
		if data.len() > max {
			return Err(other_io_err("Input too long"));
		}
	}
	Ok(data)
}

fn crc24_as_bytes(octets: &[u8]) -> [u8; 3] {
	let v = crc24::hash_raw(octets);
	[((v >> 16) & 0xFF) as u8,
	 ((v >>  8) & 0xFF) as u8,
	 ( v        & 0xFF) as u8]
}

fn perform_encode(k: u8, n: u8, with_checksums: bool) -> IoResult<()> {
	let secret = try!(read_no_more_than(&mut stdio::stdin(), 0x10000));
	let shares = try!(secret_share(&*secret, k, n));
	let config = base64::Config {
		pad: false,
		..base64::STANDARD
	};
	for (index, share) in shares.iter().enumerate() {
		let salad = share.to_base64(config);
		if with_checksums {
			let crc_bytes = crc24_as_bytes(&**share);
			println!("{}-{}-{}-{}", k, index+1, salad, crc_bytes.to_base64(config));
		} else {
			println!("{}-{}-{}", k, index+1, salad);
		}
	}
	Ok(())
}

macro_rules! otry {
	($o:expr, $e:expr) => (
		match $o {
			Some(thing_) => thing_,
			None => return Err(other_io_err($e))
		}
	)
}

/// reads shares from stdin and returns Ok(k, shares) on success
/// where shares is a Vec<(u8, Vec<u8>)> representing x-coordinates
/// and share data.
fn read_shares() -> IoResult<(u8, Vec<(u8,Vec<u8>)>)> {
	let mut stdin = BufferedReader::new(stdio::stdin());
	let mut opt_k_l: Option<(u8, usize)> = None;
	let mut counter = 0u8;
	let mut shares: Vec<(u8,Vec<u8>)> = Vec::new();
	for line in stdin.lines() {
		let line = try!(line);
		let parts: Vec<_> = line.trim().split('-').collect();
		if parts.len() < 3 || parts.len() > 4 {
			return Err(other_io_err("Share parse error: Expected 3 or 4 \
			                         parts searated by a minus sign"));
		}
		let (k, n, p3, opt_p4) = {
			let mut iter = parts.into_iter();
			let msg = "Share parse error: Could not parse K,N parameters";
			let k = otry!(iter.next().unwrap().parse::<u8>(), msg);
			let n = otry!(iter.next().unwrap().parse::<u8>(), msg);
			let p3 = iter.next().unwrap();
			let opt_p4 = iter.next();
			(k, n, p3, opt_p4)
		};
		if k < 1 || n < 1 {
			return Err(other_io_err("Share parse error: Illegal K,N parameters"));
		}
		let data = try!(
			p3.from_base64().map_err(|_| other_io_err(
				"Share parse error: Base64 decoding of data block failed"))
		);
		if let Some(check) = opt_p4 {
			if check.len() != 4 {
				return Err(other_io_err("Share parse error: Checksum part is \
				                         expected to be four characters"));
			}
			let crc_bytes = try!(
				check.from_base64().map_err(|_| other_io_err(
					"Share parse error: Base64 decoding of checksum failed"))
			);
			let mychksum = crc24_as_bytes(&*data);
			if mychksum != crc_bytes {
				return Err(other_io_err("Share parse error: Checksum mismatch"));
			}
		}
		if let Some((ck, cl)) = opt_k_l {
			if ck != k || cl != data.len() {
				return Err(other_io_err("Incompatible shares"));
			}
		} else {
			opt_k_l = Some((k, data.len()));
		}
		if shares.iter().all(|s| s.0 != n) {
			shares.push((n, data));
			counter += 1;
			if counter == k {
				return Ok((k, shares));
			}
		}
	}
	Err(other_io_err("Not enough shares provided!"))
}

fn perform_decode() -> IoResult<()> {
	let (k, shares) = try!(read_shares());
	assert!(!shares.is_empty());
	let slen = shares[0].1.len();
	let mut col_in = Vec::with_capacity(k as usize);
	let mut secret = Vec::with_capacity(slen);
	for byteindex in 0 .. slen {
		col_in.clear();
		for s in shares.iter().take(k as usize) {
			col_in.push((s.0, s.1[byteindex]));
		}
		secret.push(lagrange_interpolate(&*col_in, 0u8));
	}
	let mut out = stdio::stdout_raw();
	try!(out.write_all(&*secret));
	out.flush()
}

fn main() {
	let mut stderr = stdio::stderr();
	let args = os::args();

	let mut opts = Options::new();
	opts.optflag("h", "help", "print this help text");
	opts.optflag("d", "decode", "for decoding");
	opts.optopt("e", "encode", "for encoding, K is the required number of \
	                            shares for decoding, N is the number of shares \
	                            to generate. 1 <= K <= N <= 255", "K,N");
	let opt_matches = match opts.parse(args.tail()) {
		Ok(m) => m,
		Err(f) => {
			drop(writeln!(&mut stderr, "Error: {}", f));
			os::set_exit_status(1);
			return;
		}
	};

	if args.len() < 2 || opt_matches.opt_present("h") {
		println!(
"The program secretshare is an implementation of Shamir's secret sharing scheme.\n\
 It is applied byte-wise within a finite field for arbitrarily long secrets.\n");
		println!("{}", opts.usage("Usage: secretshare [options]"));
		println!("Input is read from STDIN and output is written to STDOUT.");
 		return;
	}

	let action: Result<_,_> =
		match (opt_matches.opt_present("e"), opt_matches.opt_present("d")) {
			(false, false) => Err("Nothing to do! Use -e or -d"),
			(true, true) => Err("Use either -e or -d and not both"),
			(false, true) => Ok(Action::Decode),
			(true, false) => {
				if let Some(param) = opt_matches.opt_str("e") {
					if let Some((k,n)) = parse_k_n(&*param) {
						if 0 < k && k <= n {
							Ok(Action::Encode(k,n))
						} else {
							Err("Invalid encoding parameters K,N")
						}
					} else {
						Err("Could not parse K,N parameters")
					}
				} else {
					Err("No parameter for -e or -d provided")
				}
			}
		};

	let result =
		match action {
			Ok(Action::Encode(k,n)) => perform_encode(k, n, true),
			Ok(Action::Decode) => perform_decode(),
			Err(e) => Err(other_io_err(e))
		};

	if let Err(e) = result {
		drop(writeln!(&mut stderr, "{}", e));
		os::set_exit_status(1);
	}
}
<commit_msg>change hash to include coding parameters<commit_after>#![feature(collections)]
#![feature(core)]
#![feature(io)]
#![feature(os)]
#![feature(rand)]
#![feature(hash)]

extern crate "rustc-serialize" as serialize;
extern crate getopts;
extern crate crc24;

use std::default::Default;
use std::iter::repeat;
use std::old_io::{ stdio, IoError, IoErrorKind, IoResult, BufferedReader };
use std::os;
use std::rand::{ Rng, OsRng };

use getopts::Options;
use serialize::base64::{ self, FromBase64, ToBase64 };

use gf256::Gf256;

mod gf256;

fn new_vec<T: Clone>(n: usize, x: T) -> Vec<T> {
	repeat(x).take(n).collect()
}

fn other_io_err(s: &'static str) -> IoError {
	IoError {
		kind: IoErrorKind::OtherIoError,
		desc: s,
		detail: None
	}
}

/// evaluates a polynomial at x=1, 2, 3, ... n (inclusive)
fn encode<W: Writer>(src: &[u8], n: u8, w: &mut W) -> IoResult<()> {
	for raw_x in 1 .. ((n as u16) + 1) {
		let x = Gf256::from_byte(raw_x as u8);
		let mut fac = Gf256::one();
		let mut acc = Gf256::zero();
		for &coeff in src.iter() {
			acc = acc + fac * Gf256::from_byte(coeff);
			fac = fac * x;
		}
		try!(w.write_u8(acc.to_byte()));
	}
	Ok(())
}

/// evaluates an interpolated polynomial at `raw_x` where
/// the polynomial is determined using Lagrangian interpolation
/// based on the given x/y coordinates `src`.
fn lagrange_interpolate(src: &[(u8, u8)], raw_x: u8) -> u8 {
	let x = Gf256::from_byte(raw_x);
	let mut sum = Gf256::zero();
	for (i, &(raw_xi, raw_yi)) in src.iter().enumerate() {
		let xi = Gf256::from_byte(raw_xi);
		let yi = Gf256::from_byte(raw_yi);
		let mut lix = Gf256::one();
		for (j, &(raw_xj, _)) in src.iter().enumerate() {
			if i != j {
				let xj = Gf256::from_byte(raw_xj);
				let delta = xi - xj;
				assert!(delta.poly !=0, "Duplicate shares");
				lix = lix * (x - xj) / delta;
			}
		}
		sum = sum + lix * yi;
	}
	sum.to_byte()
}

fn secret_share(src: &[u8], k: u8, n: u8) -> IoResult<Vec<Vec<u8>>> {
	let mut result = Vec::with_capacity(n as usize);
	for _ in 0 .. (n as usize) {
		result.push(new_vec(src.len(), 0u8));
	}
	let mut col_in = new_vec(k as usize, 0u8);
	let mut col_out = Vec::with_capacity(n as usize);
	let mut osrng = try!(OsRng::new());
	for (c, &s) in src.iter().enumerate() {
		col_in[0] = s;
		osrng.fill_bytes(&mut col_in[1..]);
		col_out.clear();
		try!(encode(&*col_in, n, &mut col_out));
		for (&y, share) in col_out.iter().zip(result.iter_mut()) {
			share[c] = y;
		}
	}
	Ok(result)
}

enum Action {
	Encode(u8, u8), // k and n parameter
	Decode
}

fn parse_k_n(s: &str) -> Option<(u8, u8)> {
	let mut iter = s.split(',');
	let first = match iter.next() { Some(x) => x, None => return None };
	let second = match iter.next() { Some(x) => x, None => return None };
	let k = match first.parse() { Some(x) => x, None => return None };
	let n = match second.parse() { Some(x) => x, None => return None };
	Some((k, n))
}

/// tries to read everything but stops early if the input seems to be
/// larger than `max` bytes and returns an IoError in this case
fn read_no_more_than<R: Reader>(r: &mut R, max: usize) -> IoResult<Vec<u8>> {
	let mut data = Vec::new();
	loop {
		if let Err(e) = r.push(max + 1 - data.len(), &mut data) {
			if e.kind == IoErrorKind::EndOfFile {
				break; // EOF condition is actually OK
			} else {
				return Err(e);
			}
		}
		if data.len() > max {
			return Err(other_io_err("Input too long"));
		}
	}
	Ok(data)
}

/// computes a CRC-24 hash over the concatenated coding parameters k, n
/// and the raw share data
fn crc24_as_bytes(k: u8, n: u8, octets: &[u8]) -> [u8; 3] {
	use std::hash::{ Hasher, Writer };
	use std::slice::ref_slice;

	let mut h: crc24::Crc24Hasher = Default::default();
	h.write(ref_slice(&k));
	h.write(ref_slice(&n));
	h.write(octets);
	let v = h.finish();

	[((v >> 16) & 0xFF) as u8,
	 ((v >>  8) & 0xFF) as u8,
	 ( v        & 0xFF) as u8]
}

fn perform_encode(k: u8, n: u8, with_checksums: bool) -> IoResult<()> {
	let secret = try!(read_no_more_than(&mut stdio::stdin(), 0x10000));
	let shares = try!(secret_share(&*secret, k, n));
	let config = base64::Config {
		pad: false,
		..base64::STANDARD
	};
	for (index, share) in shares.iter().enumerate() {
		let salad = share.to_base64(config);
		if with_checksums {
			let crc_bytes = crc24_as_bytes(k, (index+1) as u8, &**share);
			println!("{}-{}-{}-{}", k, index+1, salad, crc_bytes.to_base64(config));
		} else {
			println!("{}-{}-{}", k, index+1, salad);
		}
	}
	Ok(())
}

macro_rules! otry {
	($o:expr, $e:expr) => (
		match $o {
			Some(thing_) => thing_,
			None => return Err(other_io_err($e))
		}
	)
}

/// reads shares from stdin and returns Ok(k, shares) on success
/// where shares is a Vec<(u8, Vec<u8>)> representing x-coordinates
/// and share data.
fn read_shares() -> IoResult<(u8, Vec<(u8,Vec<u8>)>)> {
	let mut stdin = BufferedReader::new(stdio::stdin());
	let mut opt_k_l: Option<(u8, usize)> = None;
	let mut counter = 0u8;
	let mut shares: Vec<(u8,Vec<u8>)> = Vec::new();
	for line in stdin.lines() {
		let line = try!(line);
		let parts: Vec<_> = line.trim().split('-').collect();
		if parts.len() < 3 || parts.len() > 4 {
			return Err(other_io_err("Share parse error: Expected 3 or 4 \
			                         parts searated by a minus sign"));
		}
		let (k, n, p3, opt_p4) = {
			let mut iter = parts.into_iter();
			let msg = "Share parse error: Could not parse K,N parameters";
			let k = otry!(iter.next().unwrap().parse::<u8>(), msg);
			let n = otry!(iter.next().unwrap().parse::<u8>(), msg);
			let p3 = iter.next().unwrap();
			let opt_p4 = iter.next();
			(k, n, p3, opt_p4)
		};
		if k < 1 || n < 1 {
			return Err(other_io_err("Share parse error: Illegal K,N parameters"));
		}
		let data = try!(
			p3.from_base64().map_err(|_| other_io_err(
				"Share parse error: Base64 decoding of data block failed"))
		);
		if let Some(check) = opt_p4 {
			if check.len() != 4 {
				return Err(other_io_err("Share parse error: Checksum part is \
				                         expected to be four characters"));
			}
			let crc_bytes = try!(
				check.from_base64().map_err(|_| other_io_err(
					"Share parse error: Base64 decoding of checksum failed"))
			);
			let mychksum = crc24_as_bytes(k, n, &*data);
			if mychksum != crc_bytes {
				return Err(other_io_err("Share parse error: Checksum mismatch"));
			}
		}
		if let Some((ck, cl)) = opt_k_l {
			if ck != k || cl != data.len() {
				return Err(other_io_err("Incompatible shares"));
			}
		} else {
			opt_k_l = Some((k, data.len()));
		}
		if shares.iter().all(|s| s.0 != n) {
			shares.push((n, data));
			counter += 1;
			if counter == k {
				return Ok((k, shares));
			}
		}
	}
	Err(other_io_err("Not enough shares provided!"))
}

fn perform_decode() -> IoResult<()> {
	let (k, shares) = try!(read_shares());
	assert!(!shares.is_empty());
	let slen = shares[0].1.len();
	let mut col_in = Vec::with_capacity(k as usize);
	let mut secret = Vec::with_capacity(slen);
	for byteindex in 0 .. slen {
		col_in.clear();
		for s in shares.iter().take(k as usize) {
			col_in.push((s.0, s.1[byteindex]));
		}
		secret.push(lagrange_interpolate(&*col_in, 0u8));
	}
	let mut out = stdio::stdout_raw();
	try!(out.write_all(&*secret));
	out.flush()
}

fn main() {
	let mut stderr = stdio::stderr();
	let args = os::args();

	let mut opts = Options::new();
	opts.optflag("h", "help", "print this help text");
	opts.optflag("d", "decode", "for decoding");
	opts.optopt("e", "encode", "for encoding, K is the required number of \
	                            shares for decoding, N is the number of shares \
	                            to generate. 1 <= K <= N <= 255", "K,N");
	let opt_matches = match opts.parse(args.tail()) {
		Ok(m) => m,
		Err(f) => {
			drop(writeln!(&mut stderr, "Error: {}", f));
			os::set_exit_status(1);
			return;
		}
	};

	if args.len() < 2 || opt_matches.opt_present("h") {
		println!(
"The program secretshare is an implementation of Shamir's secret sharing scheme.\n\
 It is applied byte-wise within a finite field for arbitrarily long secrets.\n");
		println!("{}", opts.usage("Usage: secretshare [options]"));
		println!("Input is read from STDIN and output is written to STDOUT.");
 		return;
	}

	let action: Result<_,_> =
		match (opt_matches.opt_present("e"), opt_matches.opt_present("d")) {
			(false, false) => Err("Nothing to do! Use -e or -d"),
			(true, true) => Err("Use either -e or -d and not both"),
			(false, true) => Ok(Action::Decode),
			(true, false) => {
				if let Some(param) = opt_matches.opt_str("e") {
					if let Some((k,n)) = parse_k_n(&*param) {
						if 0 < k && k <= n {
							Ok(Action::Encode(k,n))
						} else {
							Err("Invalid encoding parameters K,N")
						}
					} else {
						Err("Could not parse K,N parameters")
					}
				} else {
					Err("No parameter for -e or -d provided")
				}
			}
		};

	let result =
		match action {
			Ok(Action::Encode(k,n)) => perform_encode(k, n, true),
			Ok(Action::Decode) => perform_decode(),
			Err(e) => Err(other_io_err(e))
		};

	if let Err(e) = result {
		drop(writeln!(&mut stderr, "{}", e));
		os::set_exit_status(1);
	}
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate ipnetwork;
extern crate pnet;
extern crate rips;
extern crate tic;

use std::fmt;
use std::io::Write;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
use std::process;
use std::str::FromStr;
use std::sync::{Arc, Mutex};

use std::thread;

use tic::{Clocksource, Interest, Receiver, Sample, Sender};
use ipnetwork::Ipv4Network;
use pnet::datalink::{self, NetworkInterface};
use rips::udp::UdpSocket;

mod logging;
use logging::set_log_level;

lazy_static! {
    static ref DEFAULT_ROUTE: Ipv4Network = Ipv4Network::from_cidr("0.0.0.0/0").unwrap();
}

macro_rules! eprintln {
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
}

#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Metric {
    Ok,
}

impl fmt::Display for Metric {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Metric::Ok => write!(f, "ok"),
        }
    }
}

fn main() {
    set_log_level(0);
    let args = ArgumentParser::new();

    let (_, iface) = args.get_iface();
    let src_net = args.get_src_net();
    let gateway = args.get_gw();
    let src_port = args.get_src_port();
    let channel = args.create_channel();
    let src = SocketAddr::V4(SocketAddrV4::new(src_net.ip(), src_port));

    let mut stack = rips::NetworkStack::new();
    stack.add_interface(iface.clone(), channel).unwrap();
    stack.add_ipv4(&iface, src_net).unwrap();
    {
        let routing_table = stack.routing_table();
        routing_table.add_route(*DEFAULT_ROUTE, Some(gateway), iface);
    }

    // initialize a tic::Receiver to ingest stats
    let mut receiver = Receiver::configure()
        .windows(24*3600) // 1 day
        .duration(1)
        .capacity(1024)
        .http_listen("0.0.0.0:42024".to_owned())
        .build();

    receiver.add_interest(Interest::Count(Metric::Ok));

    let stack = Arc::new(Mutex::new(stack));
    let socket = UdpSocket::bind(stack, src).unwrap();
    let cs = receiver.get_clocksource();
    let sender = receiver.get_sender();
    thread::spawn(move || {
        handle(socket, cs, sender);
    });

    receiver.run();
}

fn handle(mut socket: UdpSocket, clocksource: Clocksource, stats: Sender<Metric>) {
    let response = "PONG\r\n".to_owned().into_bytes();
    let mut buffer = vec![0; 1024*2];
    loop {
        let (_, src) = socket.recv_from(&mut buffer).expect("Unable to read from socket");
        let t0 = clocksource.counter();
        let _ = socket.send_to(&response, src);
        let t1 = clocksource.counter();
        let _ = stats.send(Sample::new(t0, t1, Metric::Ok));
    }
}

struct ArgumentParser {
    app: clap::App<'static, 'static>,
    matches: clap::ArgMatches<'static>,
}

impl ArgumentParser {
    pub fn new() -> ArgumentParser {
        let app = Self::create_app();
        let matches = app.clone().get_matches();
        ArgumentParser {
            app: app,
            matches: matches,
        }
    }

    pub fn get_iface(&self) -> (NetworkInterface, rips::Interface) {
        let iface_name = self.matches.value_of("iface").unwrap();
        for iface in datalink::interfaces().into_iter() {
            if iface.name == iface_name {
                if let Ok(rips_iface) = rips::convert_interface(&iface) {
                    return (iface, rips_iface);
                } else {
                    self.print_error(&format!("Interface {} can't be used with rips", iface_name));
                }
            }
        }
        self.print_error(&format!("Found no interface named {}", iface_name));
    }

    pub fn get_src_net(&self) -> Ipv4Network {
        if let Some(src_net) = self.matches.value_of("src_net") {
            match Ipv4Network::from_cidr(src_net) {
                Ok(src_net) => src_net,
                Err(_) => self.print_error("Invalid CIDR"),
            }
        } else {
            let (iface, _) = self.get_iface();
            if let Some(ips) = iface.ips.as_ref() {
                for ip in ips {
                    if let IpAddr::V4(ip) = *ip {
                        return Ipv4Network::new(ip, 24).unwrap();
                    }
                }
            }
            self.print_error("No IPv4 to use on given interface");
        }
    }

    pub fn get_src_port(&self) -> u16 {
        let matches = &self.matches;
        value_t!(matches, "src_port", u16).unwrap()
    }

    pub fn get_gw(&self) -> Ipv4Addr {
        if let Some(gw_str) = self.matches.value_of("gw") {
            if let Ok(gw) = Ipv4Addr::from_str(gw_str) {
                gw
            } else {
                self.print_error("Unable to parse gateway ip");
            }
        } else {
            let src_net = self.get_src_net();
            if let Some(gw) = src_net.nth(1) {
                gw
            } else {
                self.print_error(&format!("Could not guess a default gateway inside {}", src_net));
            }
        }
    }

    pub fn create_channel(&self) -> rips::EthernetChannel {
        let (iface, _) = self.get_iface();
        let mut config = datalink::Config::default();
        config.write_buffer_size = 1024 * 64;
        config.read_buffer_size = 1024 * 64;
        match datalink::channel(&iface, config) {
            Ok(datalink::Channel::Ethernet(tx, rx)) => rips::EthernetChannel(tx, rx),
            _ => self.print_error(&format!("Unable to open network channel on {}", iface.name)),
        }
    }

    fn create_app() -> clap::App<'static, 'static> {
        let src_net_arg = clap::Arg::with_name("src_net")
            .long("ip")
            .value_name("CIDR")
            .help("Local IP and prefix to send from, in CIDR format. Will default to first IP on \
                   given iface and prefix 24.")
            .takes_value(true);
        let src_port_arg = clap::Arg::with_name("src_port")
            .long("sport")
            .value_name("PORT")
            .help("Local port to bind to and send from.")
            .default_value("12221");
        let gw = clap::Arg::with_name("gw")
            .long("gateway")
            .short("gw")
            .value_name("IP")
            .help("The default gateway to use if the destination is not on the local network. \
                   Must be inside the network given to --ip. Defaults to the first address in \
                   the network given to --ip")
            .takes_value(true);
        let iface_arg = clap::Arg::with_name("iface")
            .help("Network interface to use")
            .required(true)
            .index(1);

        clap::App::new("UDP ping server")
            .version(crate_version!())
            .author(crate_authors!())
            .about("A simple UDP ping server with a userspace network stack")
            .arg(src_net_arg)
            .arg(src_port_arg)
            .arg(gw)
            .arg(iface_arg)
    }

    fn print_error(&self, error: &str) -> ! {
        eprintln!("ERROR: {}\n", error);
        self.app.write_help(&mut ::std::io::stderr()).unwrap();
        eprintln!("");
        process::exit(1);
    }
}
<commit_msg>clippy<commit_after>#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate ipnetwork;
extern crate pnet;
extern crate rips;
extern crate tic;

use std::fmt;
use std::io::Write;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
use std::process;
use std::str::FromStr;
use std::sync::{Arc, Mutex};

use std::thread;

use tic::{Clocksource, Interest, Receiver, Sample, Sender};
use ipnetwork::Ipv4Network;
use pnet::datalink::{self, NetworkInterface};
use rips::udp::UdpSocket;

mod logging;
use logging::set_log_level;

lazy_static! {
    static ref DEFAULT_ROUTE: Ipv4Network = Ipv4Network::from_cidr("0.0.0.0/0").unwrap();
}

macro_rules! eprintln {
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
    )
}

#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Metric {
    Ok,
}

impl fmt::Display for Metric {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Metric::Ok => write!(f, "ok"),
        }
    }
}

fn main() {
    set_log_level(0);
    let args = ArgumentParser::new();

    let (_, iface) = args.get_iface();
    let src_net = args.get_src_net();
    let gateway = args.get_gw();
    let src_port = args.get_src_port();
    let channel = args.create_channel();
    let src = SocketAddr::V4(SocketAddrV4::new(src_net.ip(), src_port));

    let mut stack = rips::NetworkStack::new();
    stack.add_interface(iface.clone(), channel).unwrap();
    stack.add_ipv4(&iface, src_net).unwrap();
    {
        let routing_table = stack.routing_table();
        routing_table.add_route(*DEFAULT_ROUTE, Some(gateway), iface);
    }

    // initialize a tic::Receiver to ingest stats
    let mut receiver = Receiver::configure()
        .windows(24*3600) // 1 day
        .duration(1)
        .capacity(1024)
        .http_listen("0.0.0.0:42024".to_owned())
        .build();

    receiver.add_interest(Interest::Count(Metric::Ok));

    let stack = Arc::new(Mutex::new(stack));
    let socket = UdpSocket::bind(stack, src).unwrap();
    let cs = receiver.get_clocksource();
    let sender = receiver.get_sender();
    thread::spawn(move || {
        handle(socket, cs, sender);
    });

    receiver.run();
}

fn handle(mut socket: UdpSocket, clocksource: Clocksource, stats: Sender<Metric>) {
    let response = "PONG\r\n".to_owned().into_bytes();
    let mut buffer = vec![0; 1024*2];
    loop {
        let (_, src) = socket.recv_from(&mut buffer).expect("Unable to read from socket");
        let t0 = clocksource.counter();
        let _ = socket.send_to(&response, src);
        let t1 = clocksource.counter();
        let _ = stats.send(Sample::new(t0, t1, Metric::Ok));
    }
}

struct ArgumentParser {
    app: clap::App<'static, 'static>,
    matches: clap::ArgMatches<'static>,
}

impl ArgumentParser {
    pub fn new() -> ArgumentParser {
        let app = Self::create_app();
        let matches = app.clone().get_matches();
        ArgumentParser {
            app: app,
            matches: matches,
        }
    }

    pub fn get_iface(&self) -> (NetworkInterface, rips::Interface) {
        let iface_name = self.matches.value_of("iface").unwrap();
        for iface in datalink::interfaces() {
            if iface.name == iface_name {
                if let Ok(rips_iface) = rips::convert_interface(&iface) {
                    return (iface, rips_iface);
                } else {
                    self.print_error(&format!("Interface {} can't be used with rips", iface_name));
                }
            }
        }
        self.print_error(&format!("Found no interface named {}", iface_name));
    }

    pub fn get_src_net(&self) -> Ipv4Network {
        if let Some(src_net) = self.matches.value_of("src_net") {
            match Ipv4Network::from_cidr(src_net) {
                Ok(src_net) => src_net,
                Err(_) => self.print_error("Invalid CIDR"),
            }
        } else {
            let (iface, _) = self.get_iface();
            if let Some(ips) = iface.ips.as_ref() {
                for ip in ips {
                    if let IpAddr::V4(ip) = *ip {
                        return Ipv4Network::new(ip, 24).unwrap();
                    }
                }
            }
            self.print_error("No IPv4 to use on given interface");
        }
    }

    pub fn get_src_port(&self) -> u16 {
        let matches = &self.matches;
        value_t!(matches, "src_port", u16).unwrap()
    }

    pub fn get_gw(&self) -> Ipv4Addr {
        if let Some(gw_str) = self.matches.value_of("gw") {
            if let Ok(gw) = Ipv4Addr::from_str(gw_str) {
                gw
            } else {
                self.print_error("Unable to parse gateway ip");
            }
        } else {
            let src_net = self.get_src_net();
            if let Some(gw) = src_net.nth(1) {
                gw
            } else {
                self.print_error(&format!("Could not guess a default gateway inside {}", src_net));
            }
        }
    }

    pub fn create_channel(&self) -> rips::EthernetChannel {
        let (iface, _) = self.get_iface();
        let mut config = datalink::Config::default();
        config.write_buffer_size = 1024 * 64;
        config.read_buffer_size = 1024 * 64;
        match datalink::channel(&iface, config) {
            Ok(datalink::Channel::Ethernet(tx, rx)) => rips::EthernetChannel(tx, rx),
            _ => self.print_error(&format!("Unable to open network channel on {}", iface.name)),
        }
    }

    fn create_app() -> clap::App<'static, 'static> {
        let src_net_arg = clap::Arg::with_name("src_net")
            .long("ip")
            .value_name("CIDR")
            .help("Local IP and prefix to send from, in CIDR format. Will default to first IP on \
                   given iface and prefix 24.")
            .takes_value(true);
        let src_port_arg = clap::Arg::with_name("src_port")
            .long("sport")
            .value_name("PORT")
            .help("Local port to bind to and send from.")
            .default_value("12221");
        let gw = clap::Arg::with_name("gw")
            .long("gateway")
            .short("gw")
            .value_name("IP")
            .help("The default gateway to use if the destination is not on the local network. \
                   Must be inside the network given to --ip. Defaults to the first address in \
                   the network given to --ip")
            .takes_value(true);
        let iface_arg = clap::Arg::with_name("iface")
            .help("Network interface to use")
            .required(true)
            .index(1);

        clap::App::new("UDP ping server")
            .version(crate_version!())
            .author(crate_authors!())
            .about("A simple UDP ping server with a userspace network stack")
            .arg(src_net_arg)
            .arg(src_port_arg)
            .arg(gw)
            .arg(iface_arg)
    }

    fn print_error(&self, error: &str) -> ! {
        eprintln!("ERROR: {}\n", error);
        self.app.write_help(&mut ::std::io::stderr()).unwrap();
        eprintln!("");
        process::exit(1);
    }
}
<|endoftext|>


<commit_before>use std::ops::{Fn, Range};

use cgmath;

pub type Vector = cgmath::Vector3<f32>;
pub type Point = Vector;
pub type UVCoords = (f32, f32);

pub const EPSILON: f32 = 10e-5;

pub trait VectorExt {
    fn map_element_wise<F: Fn(f32) -> f32>(&self, func: F) -> Self;
}

impl VectorExt for Vector {
    fn map_element_wise<F: Fn(f32) -> f32>(&self, func: F) -> Self {
        Vector {
            x: func(self.x),
            y: func(self.y),
            z: func(self.z),
        }
    }
}

pub fn clamp(value: f32, min: f32, max: f32) -> f32 {
    f32::max(min, f32::min(max, value))
}

pub fn map_range(value: f32, from: Range<f32>, to: Range<f32>) -> f32 {
    let slope = (to.end - to.start) / (from.end - from.start);

    to.start + slope * (value - from.start)
}

macro_rules! vec3 {
    ($v:expr) => ($crate::math::Vector::from_value($v));
    ($x:expr, $y:expr, $z:expr) => ($crate::math::Vector::new($x, $y, $z));
}

macro_rules! color {
    ($v:expr) => ($crate::color::Color::greyscale($v));
    ($r:expr, $g:expr, $b:expr) => ($crate::color::Color::rgb($r, $g, $b));
}

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

    #[test]
    fn test_clamp() {
        assert_eq!(clamp(1.0, 0.0, 2.0), 1.0);
        assert_eq!(clamp(10.0, 0.0, 2.0), 2.0);
        assert_eq!(clamp(-1.0, 0.0, 2.0), 0.0);
    }

    #[test]
    fn test_map_range() {
        assert_eq!(map_range(5.0, (0.0..10.0), (0.0..2.0)), 1.0);
        assert_eq!(map_range(0.0, (0.0..10.0), (0.0..2.0)), 0.0);
        assert_eq!(map_range(10.0, (0.0..10.0), (0.0..2.0)), 2.0);
    }
}
<commit_msg>Add `math::approx_fresnel()`<commit_after>use std::ops::{Fn, Range};

use cgmath;
use cgmath::prelude::*;

pub type Vector = cgmath::Vector3<f32>;
pub type Point = Vector;
pub type UVCoords = (f32, f32);

pub const EPSILON: f32 = 10e-5;

pub trait VectorExt {
    fn map_element_wise<F: Fn(f32) -> f32>(&self, func: F) -> Self;
}

impl VectorExt for Vector {
    fn map_element_wise<F: Fn(f32) -> f32>(&self, func: F) -> Self {
        Vector {
            x: func(self.x),
            y: func(self.y),
            z: func(self.z),
        }
    }
}

pub fn clamp(value: f32, min: f32, max: f32) -> f32 {
    f32::max(min, f32::min(max, value))
}

pub fn map_range(value: f32, from: Range<f32>, to: Range<f32>) -> f32 {
    let slope = (to.end - to.start) / (from.end - from.start);

    to.start + slope * (value - from.start)
}

pub fn approx_fresnel(direction: Vector, normal: Vector, ior_from: f32, ior_to: f32) -> f32 {
    let cos_in = direction.dot(normal);
    let parallel = ((ior_from - ior_to) / (ior_from + ior_to)).powi(2);

    parallel + (1.0 - parallel) * (1.0 - cos_in).powi(5)
}

macro_rules! vec3 {
    ($v:expr) => ($crate::math::Vector::from_value($v));
    ($x:expr, $y:expr, $z:expr) => ($crate::math::Vector::new($x, $y, $z));
}

macro_rules! color {
    ($v:expr) => ($crate::color::Color::greyscale($v));
    ($r:expr, $g:expr, $b:expr) => ($crate::color::Color::rgb($r, $g, $b));
}

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

    #[test]
    fn test_clamp() {
        assert_eq!(clamp(1.0, 0.0, 2.0), 1.0);
        assert_eq!(clamp(10.0, 0.0, 2.0), 2.0);
        assert_eq!(clamp(-1.0, 0.0, 2.0), 0.0);
    }

    #[test]
    fn test_map_range() {
        assert_eq!(map_range(5.0, (0.0..10.0), (0.0..2.0)), 1.0);
        assert_eq!(map_range(0.0, (0.0..10.0), (0.0..2.0)), 0.0);
        assert_eq!(map_range(10.0, (0.0..10.0), (0.0..2.0)), 2.0);
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CStr, CString, NulError};
use get_error;
use libc::c_char;

use sys::filesystem as ll;

pub fn base_path() -> Result<String, String> {
    let result = unsafe {
        let buf = ll::SDL_GetBasePath();
        CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
    };

    if result.len() == 0 {
        Err(get_error())
    } else {
        Ok(result)
    }
}

pub enum PrefPathError {
    InvalidOrganizationName(NulError),
    InvalidApplicationName(NulError),
    SdlError(String),
}

// TODO: Change to OsStr or something?
/// Return the preferred directory for the application to write files on this
/// system, based on the given organization and application name.
pub fn pref_path(org_name: &str, app_name: &str)
        -> Result<String, PrefPathError> {
    use self::PrefPathError::*;
    let result = unsafe {
        let org = match CString::new(org_name) {
            Ok(s) => s,
            Err(err) => return Err(InvalidOrganizationName(err)),
        };
        let app = match CString::new(app_name) {
            Ok(s) =>s,
            Err(err) => return Err(InvalidApplicationName(err)),
        };
        let buf = ll::SDL_GetPrefPath(org.as_ptr() as *const c_char, app.as_ptr() as *const c_char);
        CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
    };

    if result.len() == 0 {
        Err(SdlError(get_error()))
    } else {
        Ok(result)
    }
}
<commit_msg>Make sdl2::filesystem::PrefPathError derive Debug<commit_after>use std::ffi::{CStr, CString, NulError};
use get_error;
use libc::c_char;

use sys::filesystem as ll;

pub fn base_path() -> Result<String, String> {
    let result = unsafe {
        let buf = ll::SDL_GetBasePath();
        CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
    };

    if result.len() == 0 {
        Err(get_error())
    } else {
        Ok(result)
    }
}

#[derive(Debug)]
pub enum PrefPathError {
    InvalidOrganizationName(NulError),
    InvalidApplicationName(NulError),
    SdlError(String),
}

// TODO: Change to OsStr or something?
/// Return the preferred directory for the application to write files on this
/// system, based on the given organization and application name.
pub fn pref_path(org_name: &str, app_name: &str)
        -> Result<String, PrefPathError> {
    use self::PrefPathError::*;
    let result = unsafe {
        let org = match CString::new(org_name) {
            Ok(s) => s,
            Err(err) => return Err(InvalidOrganizationName(err)),
        };
        let app = match CString::new(app_name) {
            Ok(s) =>s,
            Err(err) => return Err(InvalidApplicationName(err)),
        };
        let buf = ll::SDL_GetPrefPath(org.as_ptr() as *const c_char, app.as_ptr() as *const c_char);
        CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
    };

    if result.len() == 0 {
        Err(SdlError(get_error()))
    } else {
        Ok(result)
    }
}
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::hash::{Hash, Hasher, SipHasher};
use std::collections::hash_map::DefaultHasher;
use std::cmp::min;

use adapton::bitstring::*;
use adapton::engine::{Art, Name, force, name_fork, name_of_str, name_unit, put};

/// Probablistically Balanced Trie
/// Rough implementation of probabilistic tries from OOPSLA 2015 paper.
///
/// See also: [Tries in OCaml](http://github.com/plum-umd/adapton.ocaml)
#[derive(Debug,Clone)]
pub enum Trie<X> {
    Nil(BS),
    Leaf(BS, X),
    Bin(BS, Box<Trie<X>>, Box<Trie<X>>),
    Root(Meta, Box<Trie<X>>),
    Name(Name, Box<Trie<X>>),
    Art(Art<Trie<X>>),
}

#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IFreq {
    Never,
    Depth(u64, u64),
    First(u64),
    Const(u64),
}

impl Hash for IFreq {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            IFreq::Never => {
                "Adapton.Trie.Meta.Freq#Never".hash(state);
            }
            IFreq::Depth(i, j) => {
                "Adapton.Trie.Meta.Freq#Depth".hash(state);
                i.hash(state);
                j.hash(state);
            }
            IFreq::First(n) => {
                "Adapton.Trie.Meta.Freq#First".hash(state);
                n.hash(state);
            }
            IFreq::Const(i) => {
                "Adapton.Trie.Meta.Freq#Const".hash(state);
                i.hash(state);
            }
        }
    }
}

/// Metadata held by the root node.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Meta {
    min_depth: i64,
    ifreq: IFreq,
}

pub trait MetaT {
    fn hash_seeded(&self, u64);
}

impl MetaT for Meta {
    fn hash_seeded(&self, seed: u64) {
        let mut hasher = SipHasher::new_with_keys(0, seed);
        "Adapton.Trie.Meta".hash(&mut hasher);
        self.min_depth.hash(&mut hasher);
        self.ifreq.hash(&mut hasher);
    }
}

pub trait TrieIntro<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn nil(BS) -> Self;
    fn leaf(BS, X) -> Self;
    fn bin(BS, Self, Self) -> Self;
    fn root(Meta, Self) -> Self;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name(Name, Self) -> Self;
    fn art(Art<Self>) -> Self;

    fn empty(Meta) -> Self;
    fn singleton(Meta, Name, X) -> Self;
    fn extend(Name, Self, X) -> Self;
}

pub trait TrieElim<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn find(&Self, i64) -> Option<X>;
    fn is_empty(&Self) -> bool;
    fn split_atomic(Self) -> Self;

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(Self, NilC, LeafC, BinC, RootC, NameC) -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res;

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(&Self,
                                                      NilC,
                                                      LeafC,
                                                      BinC,
                                                      RootC,
                                                      NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res;
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Trie<X> {
    fn mfn(nm: Name, meta: Meta, trie: Self, bs: BS, elt: X, hash: u64) -> Self {
        match trie {
            Trie::Nil(_) if BS::length(bs) < meta.min_depth => {
                let h_ = hash << 1;
                let bs0 = BS::prepend(0, bs);
                let bs1 = BS::prepend(1, bs);
                let mt0 = Self::nil(bs0);
                let mt1 = Self::nil(bs1);
                if BS::is_set(0, bs.value) {
                    Self::bin(bs, mt0, Self::mfn(nm, meta, mt1, bs1, elt, h_))
                } else {
                    Self::bin(bs, Self::mfn(nm, meta, mt0, bs0, elt, h_), mt1)
                }
            }
            Trie::Nil(_) => Trie::Leaf(bs, elt),
            Trie::Leaf(bs_, e) => {
                let depth = BS::length(bs);
                if depth >= BS::MAX_LEN || e == elt {
                    Self::leaf(bs, elt)
                } else if depth < BS::MAX_LEN {
                    Self::mfn(nm,
                              meta,
                              Self::split_atomic(Self::leaf(bs_, e)),
                              bs,
                              elt,
                              hash)
                } else {
                    panic!("Bad value found in nadd:\nLeaf(bs, e)\n");
                }
            }
            Trie::Bin(bs, left, right) => {
                let h_ = hash << 1;
                if BS::is_set(0, bs.value) {
                    let r = Self::mfn(nm, meta, *right, BS::prepend(1, bs), elt, h_);
                    Self::bin(bs, *left, r)
                } else {
                    let l = Self::mfn(nm, meta, *left, BS::prepend(0, bs), elt, h_);
                    Self::bin(bs, l, *right)
                }
            }
            Trie::Name(_, box Trie::Art(a)) => Self::mfn(nm, meta, force(&a), bs, elt, hash),
            t => panic!("Bad value found in nadd:\n{:?}\n", t),
        }
    }

    fn root_mfn(_: Name, nm: Name, trie: Self, elt: X) -> Self {
        match trie {
            Trie::Name(_, box Trie::Art(a)) => {
                match force(&a) {
                    Trie::Root(meta, t) => {
                        let (nm, nm_) = name_fork(nm);
                        let mut hasher = DefaultHasher::new();
                        elt.hash(&mut hasher);
                        let a = Self::mfn(nm_,
                                          meta.clone(),
                                          *t,
                                          BS {
                                              length: 0,
                                              value: 0,
                                          },
                                          elt,
                                          hasher.finish());
                        Self::root(meta, Self::name(nm, Self::art(put(a))))
                    }
                    _ => panic!("Non-root node entry to `Trie.extend'"),
                }
            }
            _ => panic!("None-name node at entry to `Trie.extend'"),
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieIntro<X> for Trie<X> {
    fn nil(bs: BS) -> Self {
        Trie::Nil(bs)
    }
    fn leaf(bs: BS, x: X) -> Self {
        Trie::Leaf(bs, x)
    }
    fn bin(bs: BS, l: Self, r: Self) -> Self {
        Trie::Bin(bs, Box::new(l), Box::new(r))
    }
    fn root(meta: Meta, trie: Self) -> Self {
        Trie::Root(meta, Box::new(trie))
    }
    fn name(nm: Name, trie: Self) -> Self {
        Trie::Name(nm, Box::new(trie))
    }
    fn art(art: Art<Self>) -> Self {
        Trie::Art(art)
    }

    fn empty(meta: Meta) -> Self {
        if meta.min_depth > BS::MAX_LEN {
            println!("Cannot make Adapton.Trie with min_depth > {} (given {})",
                     BS::MAX_LEN,
                     meta.min_depth);
        }
        let min = min(meta.min_depth, BS::MAX_LEN);
        let meta = Meta {
            min_depth: min,
            ifreq: meta.ifreq,
        };
        let nm = name_of_str("empty");
        let (nm1, nm2) = name_fork(nm);
        let mtbs = BS {
            length: 0,
            value: 0,
        };
        Self::name(nm1,
                   Self::art(put(Self::root(meta,
                                            Self::name(nm2, Self::art(put(Self::nil(mtbs))))))))
    }

    fn singleton(meta: Meta, nm: Name, elt: X) -> Self {
        Self::extend(nm, Self::empty(meta), elt)
    }

    fn extend(nm: Name, trie: Self, elt: X) -> Self {
        let (nm, nm_) = name_fork(nm);
        let a = Self::root_mfn(nm.clone(), nm_, trie, elt);
        Self::name(nm, Self::art(put(a)))
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> PartialEq for Trie<X> {
    fn eq(&self, other: &Trie<X>) -> bool {
        match (self, other) {
            (&Trie::Nil(bs_self), &Trie::Nil(bs_other)) => bs_self == bs_other,
            (&Trie::Leaf(bs_self, ref e_self), &Trie::Leaf(bs_other, ref e_other)) => {
                let bs_equal = bs_self == bs_other;
                bs_equal && e_self == e_other
            }
            (&Trie::Bin(bs, ref left, ref right),
             &Trie::Bin(bs_other, ref left_other, ref right_other)) => {
                bs == bs_other && left == left_other && right == right_other
            }
            (&Trie::Root(ref md, ref t), &Trie::Root(ref md_other, ref t_other)) => {
                md == md_other && t == t_other
            }
            (&Trie::Name(ref nm, ref t), &Trie::Name(ref nm_other, ref t_other)) => {
                nm == nm_other && t == t_other
            }
            _ => false,
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Eq for Trie<X> {}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Hash for Trie<X> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Trie::Nil(bs) => bs.hash(state),
            Trie::Leaf(bs, ref x) => {
                x.hash(state);
                bs.hash(state)
            }
            Trie::Bin(bs, ref left, ref right) => {
                right.hash(state);
                left.hash(state);
                bs.hash(state)
            }
            Trie::Root(ref md, ref t) => {
                t.hash(state);
                md.hash_seeded(state.finish());
            }
            Trie::Name(ref nm, ref t) => {
                t.hash(state);
                nm.hash(state)
            }
            Trie::Art(ref art_t) => art_t.hash(state),
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieElim<X> for Trie<X> {
    fn find(trie: &Self, i: i64) -> Option<X> {
        Self::elim_ref(trie,
                       |_| None,
                       |_, x| Some(x.clone()),
                       |_, left, right| if i % 2 == 0 {
                           Self::find(left, i >> 1)
                       } else {
                           Self::find(right, i >> 1)
                       },
                       |_, t| Self::find(t, i),
                       |_, t| Self::find(t, i))
    }

    fn is_empty(trie: &Self) -> bool {
        Self::elim_ref(trie,
                       |_| true,
                       |_, _| false,
                       |_, _, _| false,
                       |_, t| Self::is_empty(t),
                       |_, t| Self::is_empty(t))
    }

    fn split_atomic(trie: Self) -> Self {
        fn suffix(bs: BS, k: i64) -> bool {
            bs.value & k == bs.value
        }
        match trie {
            t @ Trie::Nil(_) |
            t @ Trie::Bin(_, _, _) => t,
            Trie::Leaf(bs, e) => {
                let bs0 = BS::prepend(0, bs);
                let bs1 = BS::prepend(1, bs);
                let mut hasher = DefaultHasher::new();
                e.hash(&mut hasher);
                if suffix(bs1, hasher.finish() as i64) {
                    Self::bin(bs, Self::nil(bs0), Self::leaf(bs1, e))
                } else {
                    Self::bin(bs, Self::leaf(bs0, e), Self::nil(bs1))
                }
            }
            _ => panic!("Bad split_atomic(t)"),
        }
    }

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(trie: Self,
                                                  nil: NilC,
                                                  leaf: LeafC,
                                                  bin: BinC,
                                                  root: RootC,
                                                  name: NameC)
                                                  -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res
    {
        match trie {
            Trie::Nil(bs) => nil(bs),
            Trie::Leaf(bs, x) => leaf(bs, x),
            Trie::Bin(bs, l, r) => bin(bs, *l, *r),
            Trie::Name(nm, t) => name(nm, *t),
            Trie::Root(meta, t) => root(meta, *t),
            Trie::Art(art) => {
                let trie = force(&art);
                Self::elim(trie, nil, leaf, bin, root, name)
            }
        }
    }

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(trie: &Self,
                                                      nil: NilC,
                                                      leaf: LeafC,
                                                      bin: BinC,
                                                      root: RootC,
                                                      name: NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res
    {
        match *trie {
            Trie::Nil(ref bs) => nil(bs),
            Trie::Leaf(ref bs, ref x) => leaf(bs, x),
            Trie::Bin(ref bs, ref l, ref r) => bin(bs, &*l, &*r),
            Trie::Name(ref nm, ref t) => name(nm, &*t),
            Trie::Root(ref meta, ref t) => root(meta, &*t),
            Trie::Art(ref art) => {
                let trie = force(art);
                Self::elim_ref(&trie, nil, leaf, bin, root, name)
            }
        }
    }
}

#[test]
fn test_is_empty() {
    let meta = Meta {
        min_depth: 1,
        ifreq: IFreq::Const(1),
    };
    let empty = Trie::<usize>::empty(meta.clone());
    let singleton = Trie::<usize>::singleton(meta.clone(), name_unit(), 7);
    assert!(Trie::is_empty(&Trie::<usize>::nil(BS {
        length: 0,
        value: 0,
    })));
    assert!(Trie::is_empty(&empty));

    assert!(!Trie::is_empty(&Trie::<usize>::leaf(BS {
                                                     length: 0,
                                                     value: 0,
                                                 },
                                                 0)));
    assert!(!Trie::is_empty(&singleton));
}
<commit_msg>Derive eq and add basic tests<commit_after>use std::fmt::Debug;
use std::hash::{Hash, Hasher, SipHasher};
use std::collections::hash_map::DefaultHasher;
use std::cmp::min;

use adapton::bitstring::*;
use adapton::engine::{Art, Name, force, name_fork, name_of_str, name_unit, put};

/// Probablistically Balanced Trie
/// Rough implementation of probabilistic tries from OOPSLA 2015 paper.
///
/// See also: [Tries in OCaml](http://github.com/plum-umd/adapton.ocaml)
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Trie<X> {
    Nil(BS),
    Leaf(BS, X),
    Bin(BS, Box<Trie<X>>, Box<Trie<X>>),
    Root(Meta, Box<Trie<X>>),
    Name(Name, Box<Trie<X>>),
    Art(Art<Trie<X>>),
}

#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IFreq {
    Never,
    Depth(u64, u64),
    First(u64),
    Const(u64),
}

impl Hash for IFreq {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            IFreq::Never => {
                "Adapton.Trie.Meta.Freq#Never".hash(state);
            }
            IFreq::Depth(i, j) => {
                "Adapton.Trie.Meta.Freq#Depth".hash(state);
                i.hash(state);
                j.hash(state);
            }
            IFreq::First(n) => {
                "Adapton.Trie.Meta.Freq#First".hash(state);
                n.hash(state);
            }
            IFreq::Const(i) => {
                "Adapton.Trie.Meta.Freq#Const".hash(state);
                i.hash(state);
            }
        }
    }
}

/// Metadata held by the root node.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Meta {
    min_depth: i64,
    ifreq: IFreq,
}

pub trait MetaT {
    fn hash_seeded(&self, u64);
}

impl MetaT for Meta {
    fn hash_seeded(&self, seed: u64) {
        let mut hasher = SipHasher::new_with_keys(0, seed);
        "Adapton.Trie.Meta".hash(&mut hasher);
        self.min_depth.hash(&mut hasher);
        self.ifreq.hash(&mut hasher);
    }
}

pub trait TrieIntro<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn nil(BS) -> Self;
    fn leaf(BS, X) -> Self;
    fn bin(BS, Self, Self) -> Self;
    fn root(Meta, Self) -> Self;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name(Name, Self) -> Self;
    fn art(Art<Self>) -> Self;

    fn empty(Meta) -> Self;
    fn singleton(Meta, Name, X) -> Self;
    fn extend(Name, Self, X) -> Self;
}

pub trait TrieElim<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn find(&Self, i64) -> Option<X>;
    fn is_empty(&Self) -> bool;
    fn split_atomic(Self) -> Self;

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(Self, NilC, LeafC, BinC, RootC, NameC) -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res;

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(&Self,
                                                      NilC,
                                                      LeafC,
                                                      BinC,
                                                      RootC,
                                                      NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res;
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Trie<X> {
    fn mfn(nm: Name, meta: Meta, trie: Self, bs: BS, elt: X, hash: u64) -> Self {
        match trie {
            Trie::Nil(_) if BS::length(bs) < meta.min_depth => {
                let h_ = hash << 1;
                let bs0 = BS::prepend(0, bs);
                let bs1 = BS::prepend(1, bs);
                let mt0 = Self::nil(bs0);
                let mt1 = Self::nil(bs1);
                if BS::is_set(0, bs.value) {
                    Self::bin(bs, mt0, Self::mfn(nm, meta, mt1, bs1, elt, h_))
                } else {
                    Self::bin(bs, Self::mfn(nm, meta, mt0, bs0, elt, h_), mt1)
                }
            }
            Trie::Nil(_) => Trie::Leaf(bs, elt),
            Trie::Leaf(bs_, e) => {
                let depth = BS::length(bs);
                if depth >= BS::MAX_LEN || e == elt {
                    Self::leaf(bs, elt)
                } else if depth < BS::MAX_LEN {
                    Self::mfn(nm,
                              meta,
                              Self::split_atomic(Self::leaf(bs_, e)),
                              bs,
                              elt,
                              hash)
                } else {
                    panic!("Bad value found in nadd:\nLeaf(bs, e)\n");
                }
            }
            Trie::Bin(bs, left, right) => {
                let h_ = hash << 1;
                if BS::is_set(0, bs.value) {
                    let r = Self::mfn(nm, meta, *right, BS::prepend(1, bs), elt, h_);
                    Self::bin(bs, *left, r)
                } else {
                    let l = Self::mfn(nm, meta, *left, BS::prepend(0, bs), elt, h_);
                    Self::bin(bs, l, *right)
                }
            }
            Trie::Name(_, box Trie::Art(a)) => Self::mfn(nm, meta, force(&a), bs, elt, hash),
            t => panic!("Bad value found in nadd:\n{:?}\n", t),
        }
    }

    fn root_mfn(_: Name, nm: Name, trie: Self, elt: X) -> Self {
        match trie {
            Trie::Name(_, box Trie::Art(a)) => {
                match force(&a) {
                    Trie::Root(meta, t) => {
                        let (nm, nm_) = name_fork(nm);
                        let mut hasher = DefaultHasher::new();
                        elt.hash(&mut hasher);
                        let a = Self::mfn(nm_,
                                          meta.clone(),
                                          *t,
                                          BS {
                                              length: 0,
                                              value: 0,
                                          },
                                          elt,
                                          hasher.finish());
                        Self::root(meta, Self::name(nm, Self::art(put(a))))
                    }
                    _ => panic!("Non-root node entry to `Trie.extend'"),
                }
            }
            _ => panic!("None-name node at entry to `Trie.extend'"),
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieIntro<X> for Trie<X> {
    fn nil(bs: BS) -> Self {
        Trie::Nil(bs)
    }
    fn leaf(bs: BS, x: X) -> Self {
        Trie::Leaf(bs, x)
    }
    fn bin(bs: BS, l: Self, r: Self) -> Self {
        Trie::Bin(bs, Box::new(l), Box::new(r))
    }
    fn root(meta: Meta, trie: Self) -> Self {
        Trie::Root(meta, Box::new(trie))
    }
    fn name(nm: Name, trie: Self) -> Self {
        Trie::Name(nm, Box::new(trie))
    }
    fn art(art: Art<Self>) -> Self {
        Trie::Art(art)
    }

    fn empty(meta: Meta) -> Self {
        if meta.min_depth > BS::MAX_LEN {
            println!("Cannot make Adapton.Trie with min_depth > {} (given {})",
                     BS::MAX_LEN,
                     meta.min_depth);
        }
        let min = min(meta.min_depth, BS::MAX_LEN);
        let meta = Meta {
            min_depth: min,
            ifreq: meta.ifreq,
        };
        let nm = name_of_str("empty");
        let (nm1, nm2) = name_fork(nm);
        let mtbs = BS {
            length: 0,
            value: 0,
        };
        Self::name(nm1,
                   Self::art(put(Self::root(meta,
                                            Self::name(nm2, Self::art(put(Self::nil(mtbs))))))))
    }

    fn singleton(meta: Meta, nm: Name, elt: X) -> Self {
        Self::extend(nm, Self::empty(meta), elt)
    }

    fn extend(nm: Name, trie: Self, elt: X) -> Self {
        let (nm, nm_) = name_fork(nm);
        let a = Self::root_mfn(nm.clone(), nm_, trie, elt);
        Self::name(nm, Self::art(put(a)))
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Hash for Trie<X> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Trie::Nil(bs) => bs.hash(state),
            Trie::Leaf(bs, ref x) => {
                x.hash(state);
                bs.hash(state)
            }
            Trie::Bin(bs, ref left, ref right) => {
                right.hash(state);
                left.hash(state);
                bs.hash(state)
            }
            Trie::Root(ref md, ref t) => {
                t.hash(state);
                md.hash_seeded(state.finish());
            }
            Trie::Name(ref nm, ref t) => {
                t.hash(state);
                nm.hash(state)
            }
            Trie::Art(ref art_t) => art_t.hash(state),
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieElim<X> for Trie<X> {
    fn find(trie: &Self, i: i64) -> Option<X> {
        Self::elim_ref(trie,
                       |_| None,
                       |_, x| Some(x.clone()),
                       |_, left, right| if i % 2 == 0 {
                           Self::find(left, i >> 1)
                       } else {
                           Self::find(right, i >> 1)
                       },
                       |_, t| Self::find(t, i),
                       |_, t| Self::find(t, i))
    }

    fn is_empty(trie: &Self) -> bool {
        Self::elim_ref(trie,
                       |_| true,
                       |_, _| false,
                       |_, _, _| false,
                       |_, t| Self::is_empty(t),
                       |_, t| Self::is_empty(t))
    }

    fn split_atomic(trie: Self) -> Self {
        fn suffix(bs: BS, k: i64) -> bool {
            bs.value & k == bs.value
        }
        match trie {
            t @ Trie::Nil(_) |
            t @ Trie::Bin(_, _, _) => t,
            Trie::Leaf(bs, e) => {
                let bs0 = BS::prepend(0, bs);
                let bs1 = BS::prepend(1, bs);
                let mut hasher = DefaultHasher::new();
                e.hash(&mut hasher);
                if suffix(bs1, hasher.finish() as i64) {
                    Self::bin(bs, Self::nil(bs0), Self::leaf(bs1, e))
                } else {
                    Self::bin(bs, Self::leaf(bs0, e), Self::nil(bs1))
                }
            }
            _ => panic!("Bad split_atomic(t)"),
        }
    }

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(trie: Self,
                                                  nil: NilC,
                                                  leaf: LeafC,
                                                  bin: BinC,
                                                  root: RootC,
                                                  name: NameC)
                                                  -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res
    {
        match trie {
            Trie::Nil(bs) => nil(bs),
            Trie::Leaf(bs, x) => leaf(bs, x),
            Trie::Bin(bs, l, r) => bin(bs, *l, *r),
            Trie::Name(nm, t) => name(nm, *t),
            Trie::Root(meta, t) => root(meta, *t),
            Trie::Art(art) => {
                let trie = force(&art);
                Self::elim(trie, nil, leaf, bin, root, name)
            }
        }
    }

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(trie: &Self,
                                                      nil: NilC,
                                                      leaf: LeafC,
                                                      bin: BinC,
                                                      root: RootC,
                                                      name: NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res
    {
        match *trie {
            Trie::Nil(ref bs) => nil(bs),
            Trie::Leaf(ref bs, ref x) => leaf(bs, x),
            Trie::Bin(ref bs, ref l, ref r) => bin(bs, &*l, &*r),
            Trie::Name(ref nm, ref t) => name(nm, &*t),
            Trie::Root(ref meta, ref t) => root(meta, &*t),
            Trie::Art(ref art) => {
                let trie = force(art);
                Self::elim_ref(&trie, nil, leaf, bin, root, name)
            }
        }
    }
}

#[test]
fn test_is_empty() {
    let meta = Meta {
        min_depth: 1,
        ifreq: IFreq::Const(1),
    };
    let empty = Trie::<usize>::empty(meta.clone());
    let singleton = Trie::<usize>::singleton(meta.clone(), name_unit(), 7);
    assert!(Trie::is_empty(&Trie::<usize>::nil(BS {
        length: 0,
        value: 0,
    })));
    assert!(Trie::is_empty(&empty));

    assert!(!Trie::is_empty(&Trie::<usize>::leaf(BS {
                                                     length: 0,
                                                     value: 0,
                                                 },
                                                 0)));
    assert!(!Trie::is_empty(&singleton));
}

#[test]
fn test_equal() {
    let meta = Meta {
        min_depth: 1,
        ifreq: IFreq::Const(1),
    };
    let empty = Trie::<usize>::empty(meta.clone());
    let singleton_7 = Trie::<usize>::singleton(meta.clone(), name_unit(), 7);
    let singleton_7_ = Trie::<usize>::singleton(meta.clone(), name_unit(), 7);
    let singleton_8 = Trie::<usize>::singleton(meta.clone(), name_unit(), 8);
    assert_eq!(empty, empty);
    assert_eq!(singleton_7, singleton_7);
    assert_eq!(singleton_7, singleton_7_);
    assert_eq!(singleton_8, singleton_8);

    assert_ne!(empty, singleton_7);
    assert_ne!(empty, singleton_8);
    assert_ne!(singleton_7, singleton_8);
}
<|endoftext|>


<commit_before>use cargo::core::{Workspace, Source};
use cargo::sources::PathSource;
use syntex_syntax::attr;
use syntex_syntax::visit::{self, Visitor, FnKind};
use syntex_syntax::codemap::{CodeMap, Span, FilePathMapping};
use syntex_syntax::ast::{NodeId, Mac, Attribute, Stmt, StmtKind, FnDecl, Mod,
    StructField, Block, Item, ItemKind};
use syntex_syntax::parse::{self, ParseSess};
use syntex_syntax::errors::Handler;
use syntex_syntax::errors::emitter::ColorConfig;
use std::path::PathBuf;
use std::ffi::OsStr;
use std::rc::Rc;
use std::ops::Deref;
use config::Config;

struct IgnoredLines<'a> {
    lines: Vec<usize>,
    codemap: &'a CodeMap,
    config: &'a Config,
    started: bool,
}

/// Returns a list of files and line numbers to ignore (not indexes!)
pub fn get_lines_to_ignore(project: &Workspace, config: &Config) -> Vec<(PathBuf, usize)> {
    let mut result: Vec<(PathBuf, usize)> = Vec::new();

    let pkg = project.current().unwrap();
    let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), project.config());
    // If this fails we should just iterate over no files. No need to care.
    let _ = src.update();

    let codemap = Rc::new(CodeMap::new(FilePathMapping::empty()));
    let handler = Handler::with_tty_emitter(ColorConfig::Auto, false, false, Some(codemap.clone()));
    let parse_session = ParseSess::with_span_handler(handler, codemap.clone());

    for file in src.list_files(&pkg).unwrap().iter() {
        if !(config.ignore_tests && file.starts_with(project.root().join("tests"))) {
            if file.extension() == Some(OsStr::new("rs")) {
                // Rust source file
                let mut parser = parse::new_parser_from_file(&parse_session, &file);
                if let Ok(krate) = parser.parse_crate_mod() {
                    let mut visitor = IgnoredLines::from_session(&parse_session, config);
                    visitor.visit_mod(&krate.module, krate.span, &krate.attrs, NodeId::new(0));
                    result.append(&mut visitor.lines.iter()
                                                    .map(|x| (file.to_path_buf(), *x))
                                                    .collect::<Vec<_>>());
                }
            }
        }
    }
    result
}

impl<'a> IgnoredLines<'a> {
    /// Construct a new ignored lines object for the given project
    fn from_session(session: &'a ParseSess, config: &'a Config) -> IgnoredLines<'a> {
        IgnoredLines {
            lines: vec![],
            codemap: session.codemap(),
            config: config,
            started: false
        }
    }

    fn ignore_lines(&mut self, span: Span) {
        if let Ok(s) = self.codemap.span_to_lines(span) {
            for line in &s.lines {
                // Line number is index+1
                self.lines.push(line.line_index + 1);
            }
        }
    }

    fn contains_cfg_test(&mut self, attrs: &[Attribute]) -> bool {
        let mut result = false;
        'outer: for attr in attrs.iter() {
            if attr.path == "cfg" {
                if let Some(items) = attr.meta_item_list() {
                    for item in items {
                        if let Some(word) = item.word() {
                            if word.name().as_str() == "test" {
                                result = true;
                                break 'outer;
                            }
                        }
                    }
                }
            }
        }
        result
    }

}

impl<'v, 'a> Visitor<'v> for IgnoredLines<'a> {

    fn visit_item(&mut self, i: &'v Item) {
        match i.node {
            ItemKind::ExternCrate(..) => self.ignore_lines(i.span),
            ItemKind::Fn(_, _, _, _, _, ref block) => {
                if attr::contains_name(&i.attrs, "test") && self.config.ignore_tests {
                    self.ignore_lines(i.span);
                    self.ignore_lines(block.deref().span);
                }
            },
            _ => {},
        }
        visit::walk_item(self, i);
    }

    fn visit_mod(&mut self, m: &'v Mod, s: Span, _attrs: &[Attribute], _n: NodeId) {
        // We want to limit ourselves to only this source file.
        // This avoids repeated hits and issues where there are multiple compilation targets
        if self.started == false {
            self.started = true;
            visit::walk_mod(self, m);
        } else {
            // Needs to be more advanced - cope with mod { .. } in file.
            // Also if mod is cfg(test) and --ignore-tests ignore contents!
            if let Ok(fl) = self.codemap.span_to_lines(s) {
                if fl.lines.len() > 1 {
                    if self.config.ignore_tests && self.contains_cfg_test(_attrs) {
                        self.ignore_lines(s);
                    }
                    visit::walk_mod(self, m);
                } else {
                    // one line mod, so either referencing another file or something
                    // not worth covering.
                    self.ignore_lines(s);
                }
            }
        }
    }

    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, s: Span, _: NodeId) {
        visit::walk_fn(self, fk, fd, s);
    }

    fn visit_stmt(&mut self, s: &Stmt) {
        match s.node {
            StmtKind::Mac(_) => visit::walk_stmt(self, s),
            _ => {},
        }
    }

    fn visit_mac(&mut self, mac: &Mac) {
        // Use this to ignore unreachable lines
        let mac_text = &format!("{}", mac.node.path)[..];
        // TODO unimplemented should have extra logic to exclude the
        // function from coverage
        match mac_text {
            "unimplemented" => self.ignore_lines(mac.span),
            "unreachable" => self.ignore_lines(mac.span),
            _ => {},
        }
        visit::walk_mac(self, mac);
    }

    /// Ignores attributes which may get identified as coverable lines.
    fn visit_attribute(&mut self, attr: &Attribute) {
        if attr.check_name("derive") {
            self.ignore_lines(attr.span);
        }
    }

    /// Struct fields are mistakenly identified as instructions and uncoverable.
    fn visit_struct_field(&mut self, s: &'v StructField) {
        self.ignore_lines(s.span);
        visit::walk_struct_field(self, s);
    }

    fn visit_block(&mut self, b: &'v Block) {

        visit::walk_block(self, b);
    }
}

<commit_msg>Cleaned up contains_cfg_test<commit_after>use cargo::core::{Workspace, Source};
use cargo::sources::PathSource;
use syntex_syntax::attr;
use syntex_syntax::visit::{self, Visitor, FnKind};
use syntex_syntax::codemap::{CodeMap, Span, FilePathMapping};
use syntex_syntax::ast::{NodeId, Mac, Attribute, Stmt, StmtKind, FnDecl, Mod,
    StructField, Block, Item, ItemKind};
use syntex_syntax::parse::{self, ParseSess};
use syntex_syntax::errors::Handler;
use syntex_syntax::errors::emitter::ColorConfig;
use std::path::PathBuf;
use std::ffi::OsStr;
use std::rc::Rc;
use std::ops::Deref;
use config::Config;

struct IgnoredLines<'a> {
    lines: Vec<usize>,
    codemap: &'a CodeMap,
    config: &'a Config,
    started: bool,
}

/// Returns a list of files and line numbers to ignore (not indexes!)
pub fn get_lines_to_ignore(project: &Workspace, config: &Config) -> Vec<(PathBuf, usize)> {
    let mut result: Vec<(PathBuf, usize)> = Vec::new();

    let pkg = project.current().unwrap();
    let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), project.config());
    // If this fails we should just iterate over no files. No need to care.
    let _ = src.update();

    let codemap = Rc::new(CodeMap::new(FilePathMapping::empty()));
    let handler = Handler::with_tty_emitter(ColorConfig::Auto, false, false, Some(codemap.clone()));
    let parse_session = ParseSess::with_span_handler(handler, codemap.clone());

    for file in src.list_files(&pkg).unwrap().iter() {
        if !(config.ignore_tests && file.starts_with(project.root().join("tests"))) {
            if file.extension() == Some(OsStr::new("rs")) {
                // Rust source file
                let mut parser = parse::new_parser_from_file(&parse_session, &file);
                if let Ok(krate) = parser.parse_crate_mod() {
                    let mut visitor = IgnoredLines::from_session(&parse_session, config);
                    visitor.visit_mod(&krate.module, krate.span, &krate.attrs, NodeId::new(0));
                    result.append(&mut visitor.lines.iter()
                                                    .map(|x| (file.to_path_buf(), *x))
                                                    .collect::<Vec<_>>());
                }
            }
        }
    }
    result
}

impl<'a> IgnoredLines<'a> {
    /// Construct a new ignored lines object for the given project
    fn from_session(session: &'a ParseSess, config: &'a Config) -> IgnoredLines<'a> {
        IgnoredLines {
            lines: vec![],
            codemap: session.codemap(),
            config: config,
            started: false
        }
    }

    fn ignore_lines(&mut self, span: Span) {
        if let Ok(s) = self.codemap.span_to_lines(span) {
            for line in &s.lines {
                // Line number is index+1
                self.lines.push(line.line_index + 1);
            }
        }
    }

    fn contains_cfg_test(&mut self, attrs: &[Attribute]) -> bool {
        attrs.iter()
             .filter(|x| x.path == "cfg")
             .filter_map(|x| x.meta_item_list())
             .flat_map(|x| x)
             .any(|x| {
                 if let Some(w) = x.word() {
                    w.name().as_str() == "test"
                 } else {
                     false
                 }
             })
      }

}

impl<'v, 'a> Visitor<'v> for IgnoredLines<'a> {

    fn visit_item(&mut self, i: &'v Item) {
        match i.node {
            ItemKind::ExternCrate(..) => self.ignore_lines(i.span),
            ItemKind::Fn(_, _, _, _, _, ref block) => {
                if attr::contains_name(&i.attrs, "test") && self.config.ignore_tests {
                    self.ignore_lines(i.span);
                    self.ignore_lines(block.deref().span);
                }
            },
            _ => {},
        }
        visit::walk_item(self, i);
    }

    fn visit_mod(&mut self, m: &'v Mod, s: Span, _attrs: &[Attribute], _n: NodeId) {
        // We want to limit ourselves to only this source file.
        // This avoids repeated hits and issues where there are multiple compilation targets
        if self.started == false {
            self.started = true;
            visit::walk_mod(self, m);
        } else {
            // Needs to be more advanced - cope with mod { .. } in file.
            // Also if mod is cfg(test) and --ignore-tests ignore contents!
            if let Ok(fl) = self.codemap.span_to_lines(s) {
                if fl.lines.len() > 1 {
                    if self.config.ignore_tests && self.contains_cfg_test(_attrs) {
                        self.ignore_lines(s);
                    }
                    visit::walk_mod(self, m);
                } else {
                    // one line mod, so either referencing another file or something
                    // not worth covering.
                    self.ignore_lines(s);
                }
            }
        }
    }

    fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl, s: Span, _: NodeId) {
        visit::walk_fn(self, fk, fd, s);
    }

    fn visit_stmt(&mut self, s: &Stmt) {
        match s.node {
            StmtKind::Mac(_) => visit::walk_stmt(self, s),
            _ => {},
        }
    }

    fn visit_mac(&mut self, mac: &Mac) {
        // Use this to ignore unreachable lines
        let mac_text = &format!("{}", mac.node.path)[..];
        // TODO unimplemented should have extra logic to exclude the
        // function from coverage
        match mac_text {
            "unimplemented" => self.ignore_lines(mac.span),
            "unreachable" => self.ignore_lines(mac.span),
            _ => {},
        }
        visit::walk_mac(self, mac);
    }

    /// Ignores attributes which may get identified as coverable lines.
    fn visit_attribute(&mut self, attr: &Attribute) {
        if attr.check_name("derive") {
            self.ignore_lines(attr.span);
        }
    }

    /// Struct fields are mistakenly identified as instructions and uncoverable.
    fn visit_struct_field(&mut self, s: &'v StructField) {
        self.ignore_lines(s.span);
        visit::walk_struct_field(self, s);
    }

    fn visit_block(&mut self, b: &'v Block) {

        visit::walk_block(self, b);
    }
}

<|endoftext|>


<commit_before>
const VERTICAL_TIMING:          u32 = 0x00;
const DISPLAY_CONFIG:           u32 = 0x02;
const HORIZONTAL_TIMING_0_HI:   u32 = 0x04;
const HORIZONTAL_TIMING_0_LO:   u32 = 0x06;
const HORIZONTAL_TIMING_1_HI:   u32 = 0x08;
const HORIZONTAL_TIMING_1_LO:   u32 = 0x0A;
const VERTICAL_TIMING_ODD_HI:   u32 = 0x0C;
const VERTICAL_TIMING_ODD_LO:   u32 = 0x0E;
const VERTICAL_TIMING_EVEN_HI:  u32 = 0x10;
const VERTICAL_TIMING_EVEN_LO:  u32 = 0x12;
const BURST_BLANKING_ODD_HI:    u32 = 0x14;
const BURST_BLANKING_ODD_LO:    u32 = 0x16;
const BURST_BLANKING_EVEN_HI:   u32 = 0x18;
const BURST_BLANKING_EVEN_LO:   u32 = 0x1A;
const FB_TOP_LEFT_HI:           u32 = 0x1C;
const FB_TOP_LEFT_LO:           u32 = 0x1E;
const FB_TOP_RIGHT_HI:          u32 = 0x20;
const FB_TOP_RIGHT_LO:          u32 = 0x22;
const FB_BOTTOM_LEFT_HI:        u32 = 0x24;
const FB_BOTTOM_LEFT_LO:        u32 = 0x26;
const FB_BOTTOM_RIGHT_HI:       u32 = 0x28;
const FB_BOTTOM_RIGHT_LO:       u32 = 0x2A;
const BEAM_POSITION_VERTICAL:   u32 = 0x2C;
const BEAM_POSITION_HORIZONTAL: u32 = 0x2E;
const DISPLAY_INTERRUPT_0_HI:   u32 = 0x30;
const DISPLAY_INTERRUPT_0_LO:   u32 = 0x32;
const DISPLAY_INTERRUPT_1_HI:   u32 = 0x34;
const DISPLAY_INTERRUPT_1_LO:   u32 = 0x36;
const DISPLAY_INTERRUPT_2_HI:   u32 = 0x38;
const DISPLAY_INTERRUPT_2_LO:   u32 = 0x3A;
const DISPLAY_INTERRUPT_3_HI:   u32 = 0x3C;
const DISPLAY_INTERRUPT_3_LO:   u32 = 0x3E;
const DISPLAY_LATCH_0_LO:       u32 = 0x40;
const DISPLAY_LATCH_0_HI:       u32 = 0x42;
const DISPLAY_LATCH_1_LO:       u32 = 0x44;
const DISPLAY_LATCH_1_HI:       u32 = 0x46;
const SCALING_WIDTH:            u32 = 0x48;
const HORIZONTAL_SCALING:       u32 = 0x4A;
const FILTER_COEFFICIENT_0_HI:  u32 = 0x4C;
const FILTER_COEFFICIENT_0_LO:  u32 = 0x4E;
const FILTER_COEFFICIENT_1_HI:  u32 = 0x50;
const FILTER_COEFFICIENT_1_LO:  u32 = 0x52;
const FILTER_COEFFICIENT_2_HI:  u32 = 0x54;
const FILTER_COEFFICIENT_2_LO:  u32 = 0x56;
const FILTER_COEFFICIENT_3_HI:  u32 = 0x58;
const FILTER_COEFFICIENT_3_LO:  u32 = 0x5A;
const FILTER_COEFFICIENT_4_HI:  u32 = 0x5C;
const FILTER_COEFFICIENT_4_LO:  u32 = 0x5E;
const FILTER_COEFFICIENT_5_HI:  u32 = 0x60;
const FILTER_COEFFICIENT_5_LO:  u32 = 0x62;
const FILTER_COEFFICIENT_6_HI:  u32 = 0x64;
const FILTER_COEFFICIENT_6_LO:  u32 = 0x66;
const UNKOWN_AA_HI:             u32 = 0x68;
const UNKOWN_AA_LO:             u32 = 0x6A;
const CLOCK_SELECT:             u32 = 0x6C;
const DTV_STATUS:               u32 = 0x6E;
const UNKNOWN:                  u32 = 0x70;
const BORDER_BLANK_END:         u32 = 0x72;
const BORDER_BLANK_START:       u32 = 0x74;

pub struct VideoInterface {
    display_config: u16,
    vertical_beam_position: u16
}

impl VideoInterface {

    pub fn new() -> VideoInterface {
        VideoInterface {
            display_config: 0,
            vertical_beam_position: 0
        }
    }

    pub fn update(&mut self) {
        self.vertical_beam_position += 1;
    }

    pub fn read_u16(&self, register: u32) -> u16 {
        println!("READ VI reg {:#x}", register);

        match register {
            DISPLAY_CONFIG => self.display_config,
            BEAM_POSITION_VERTICAL => self.vertical_beam_position,
            DISPLAY_INTERRUPT_0_HI => 0,
            DISPLAY_INTERRUPT_1_HI => 0,
            _ => panic!("VI: unhandled register ({:#x})", register)
        }
    }

    pub fn write_u16(&mut self, register: u32, val: u16) {
        println!("WRITE VI reg {:#x} {}", register, val);

        match register {
            VERTICAL_TIMING => {},
            DISPLAY_CONFIG => self.display_config = val,
            HORIZONTAL_TIMING_0_HI => {},
            HORIZONTAL_TIMING_0_LO => {},
            HORIZONTAL_TIMING_1_HI => {},
            HORIZONTAL_TIMING_1_LO => {},
            VERTICAL_TIMING_ODD_HI => {},
            VERTICAL_TIMING_ODD_LO => {},
            VERTICAL_TIMING_EVEN_HI => {},
            VERTICAL_TIMING_EVEN_LO => {},
            BURST_BLANKING_ODD_HI => {},
            BURST_BLANKING_ODD_LO => {},
            BURST_BLANKING_EVEN_HI => {},
            BURST_BLANKING_EVEN_LO => {},
            DISPLAY_INTERRUPT_0_HI => {},
            DISPLAY_INTERRUPT_0_LO => {},
            DISPLAY_INTERRUPT_1_HI => {},
            DISPLAY_INTERRUPT_1_LO => {},
            FILTER_COEFFICIENT_0_HI => {},
            FILTER_COEFFICIENT_0_LO => {},
            FILTER_COEFFICIENT_1_HI => {},
            FILTER_COEFFICIENT_1_LO => {},
            FILTER_COEFFICIENT_2_HI => {},
            FILTER_COEFFICIENT_2_LO => {},
            FILTER_COEFFICIENT_3_HI => {},
            FILTER_COEFFICIENT_3_LO => {},
            FILTER_COEFFICIENT_4_HI => {},
            FILTER_COEFFICIENT_4_LO => {},
            FILTER_COEFFICIENT_5_HI => {},
            FILTER_COEFFICIENT_5_LO => {},
            FILTER_COEFFICIENT_6_HI => {},
            FILTER_COEFFICIENT_6_LO => {},
            SCALING_WIDTH => {},
            CLOCK_SELECT => {},
            UNKNOWN => {},
            _ => panic!("VI: unhandled register ({:#x})", register)
        }
    }

}
<commit_msg>improve video interface<commit_after>
// NTSC  640x480 60Hz
// PAL   640x574 50Hz
// MPAL  640x490 60Hz
// PAL60 640x490 60Hz

const VERTICAL_TIMING:          u32 = 0x00;
const DISPLAY_CONFIG:           u32 = 0x02;
const HORIZONTAL_TIMING_0_HI:   u32 = 0x04;
const HORIZONTAL_TIMING_0_LO:   u32 = 0x06;
const HORIZONTAL_TIMING_1_HI:   u32 = 0x08; // Setting bit 0 seems to blackout screen.
const HORIZONTAL_TIMING_1_LO:   u32 = 0x0A;
const VERTICAL_TIMING_ODD_HI:   u32 = 0x0C; // Sets up the pre-blanking and post-blanking interval of odd fields.
const VERTICAL_TIMING_ODD_LO:   u32 = 0x0E;
const VERTICAL_TIMING_EVEN_HI:  u32 = 0x10; // Sets up the pre-blanking and post-blanking intervals of even fields.
const VERTICAL_TIMING_EVEN_LO:  u32 = 0x12;
const BURST_BLANKING_ODD_HI:    u32 = 0x14;
const BURST_BLANKING_ODD_LO:    u32 = 0x16;
const BURST_BLANKING_EVEN_HI:   u32 = 0x18;
const BURST_BLANKING_EVEN_LO:   u32 = 0x1A;
const FB_TOP_LEFT_HI:           u32 = 0x1C; // Display origin of the top field of a picture in 2D mode or left picture in 3D.
const FB_TOP_LEFT_LO:           u32 = 0x1E;
const FB_TOP_RIGHT_HI:          u32 = 0x20; // Base address of the top field for the right picture in 3D mode.
const FB_TOP_RIGHT_LO:          u32 = 0x22;
const FB_BOTTOM_LEFT_HI:        u32 = 0x24; // Display origin of the the bottom field of a picture in 2D mode or left picture in 3D.
const FB_BOTTOM_LEFT_LO:        u32 = 0x26;
const FB_BOTTOM_RIGHT_HI:       u32 = 0x28; // Base address of the bottom field for the right picture in 3D mode.
const FB_BOTTOM_RIGHT_LO:       u32 = 0x2A;
const BEAM_POSITION_VERTICAL:   u32 = 0x2C; // Count in lines (on frame basis), runs from 1 to # lines per frame. NTSC vcount ranges from 1-263.
const BEAM_POSITION_HORIZONTAL: u32 = 0x2E; // Count in pixels, runs from 1 to # pixels per line.
const DISPLAY_INTERRUPT_0_HI:   u32 = 0x30; // There are 4 display interrupts(0-3). They generate interrupts to the cpu at different positions within field.
const DISPLAY_INTERRUPT_0_LO:   u32 = 0x32;
const DISPLAY_INTERRUPT_1_HI:   u32 = 0x34;
const DISPLAY_INTERRUPT_1_LO:   u32 = 0x36;
const DISPLAY_INTERRUPT_2_HI:   u32 = 0x38;
const DISPLAY_INTERRUPT_2_LO:   u32 = 0x3A;
const DISPLAY_INTERRUPT_3_HI:   u32 = 0x3C;
const DISPLAY_INTERRUPT_3_LO:   u32 = 0x3E;
const DISPLAY_LATCH_0_LO:       u32 = 0x40; // Latches the value of the display position register at the rising edge of the gt0 signal. The trigger is set if a gun trigger is detected.
const DISPLAY_LATCH_0_HI:       u32 = 0x42;
const DISPLAY_LATCH_1_LO:       u32 = 0x44;
const DISPLAY_LATCH_1_HI:       u32 = 0x46;
const SCALING_WIDTH:            u32 = 0x48; // Number of source pixels to be scaled. Only used when horizontal scaler is enabled.
const HORIZONTAL_SCALING:       u32 = 0x4A; // Step size of horizontal stepper.
const FILTER_COEFFICIENT_0_HI:  u32 = 0x4C;
const FILTER_COEFFICIENT_0_LO:  u32 = 0x4E;
const FILTER_COEFFICIENT_1_HI:  u32 = 0x50;
const FILTER_COEFFICIENT_1_LO:  u32 = 0x52;
const FILTER_COEFFICIENT_2_HI:  u32 = 0x54;
const FILTER_COEFFICIENT_2_LO:  u32 = 0x56;
const FILTER_COEFFICIENT_3_HI:  u32 = 0x58;
const FILTER_COEFFICIENT_3_LO:  u32 = 0x5A;
const FILTER_COEFFICIENT_4_HI:  u32 = 0x5C;
const FILTER_COEFFICIENT_4_LO:  u32 = 0x5E;
const FILTER_COEFFICIENT_5_HI:  u32 = 0x60;
const FILTER_COEFFICIENT_5_LO:  u32 = 0x62;
const FILTER_COEFFICIENT_6_HI:  u32 = 0x64;
const FILTER_COEFFICIENT_6_LO:  u32 = 0x66;
const UNKOWN_AA_HI:             u32 = 0x68;
const UNKOWN_AA_LO:             u32 = 0x6A;
const CLOCK_SELECT:             u32 = 0x6C;
const DTV_STATUS:               u32 = 0x6E; // Read status of 2 io pins.
const UNKNOWN:                  u32 = 0x70; // Horizontal stepping ??? progressive scanning ???
const BORDER_BLANK_END:         u32 = 0x72; // Sets up black border around active pixels in debug mode.
const BORDER_BLANK_START:       u32 = 0x74;

#[derive(Debug)]
pub struct VideoInterface {
    vertical_timing: VerticalTiming,
    horizontal_timing: HorizontalTiming,
    display_config: DisplayConfig,
    vertical_beam_position: u16,
    vertical_timing_odd: VBlankTiming,
    vertical_timing_even: VBlankTiming,

    clock_select: bool, // 0: 27MHz, 1: 54 MHz (used in progressive)
}

impl VideoInterface {

    pub fn new() -> VideoInterface {
        VideoInterface {
            vertical_timing: VerticalTiming::default(),
            horizontal_timing: HorizontalTiming::default(),
            display_config: DisplayConfig::default(),
            vertical_beam_position: 1, // default location
            vertical_timing_odd: VBlankTiming::default(),
            vertical_timing_even: VBlankTiming::default(),

            clock_select: false
        }
    }

    pub fn update(&mut self) {
        if self.display_config.enable {

            self.vertical_beam_position += 1;

            if self.display_config.format == 0 && self.vertical_beam_position > 525 { // ntsc
                self.vertical_beam_position = 1;
            }
        }
    }

    pub fn read_u16(&mut self, register: u32) -> u16 {
        //println!("READ VI reg {:#x}", register);

        self.update();

        match register {
            DISPLAY_CONFIG => self.display_config.as_u16(),
            BEAM_POSITION_VERTICAL => self.vertical_beam_position,
            DISPLAY_INTERRUPT_0_HI => 0,
            DISPLAY_INTERRUPT_1_HI => 0,
            _ => panic!("VI: unhandled register ({:#x})", register)
        }
    }

    pub fn write_u16(&mut self, register: u32, val: u16) {
        //println!("WRITE VI reg {:#x} {:#x}", register, val);

        match register {
            VERTICAL_TIMING => self.vertical_timing = val.into(),
            DISPLAY_CONFIG => self.display_config = val.into(),
            HORIZONTAL_TIMING_0_HI => self.horizontal_timing.from_hi(val),
            HORIZONTAL_TIMING_0_LO => self.horizontal_timing.from_lo(val),
            HORIZONTAL_TIMING_1_HI => self.horizontal_timing.from_hi_1(val),
            HORIZONTAL_TIMING_1_LO => self.horizontal_timing.from_lo_1(val),
            VERTICAL_TIMING_ODD_HI => self.vertical_timing_odd.from_hi(val),
            VERTICAL_TIMING_ODD_LO => self.vertical_timing_odd.from_lo(val),
            VERTICAL_TIMING_EVEN_HI => self.vertical_timing_even.from_hi(val),
            VERTICAL_TIMING_EVEN_LO => self.vertical_timing_even.from_lo(val),
            BURST_BLANKING_ODD_HI => {},
            BURST_BLANKING_ODD_LO => {},
            BURST_BLANKING_EVEN_HI => {},
            BURST_BLANKING_EVEN_LO => {},
            DISPLAY_INTERRUPT_0_HI => {},
            DISPLAY_INTERRUPT_0_LO => {},
            DISPLAY_INTERRUPT_1_HI => {},
            DISPLAY_INTERRUPT_1_LO => {},
            FILTER_COEFFICIENT_0_HI => {},
            FILTER_COEFFICIENT_0_LO => {},
            FILTER_COEFFICIENT_1_HI => {},
            FILTER_COEFFICIENT_1_LO => {},
            FILTER_COEFFICIENT_2_HI => {},
            FILTER_COEFFICIENT_2_LO => {},
            FILTER_COEFFICIENT_3_HI => {},
            FILTER_COEFFICIENT_3_LO => {},
            FILTER_COEFFICIENT_4_HI => {},
            FILTER_COEFFICIENT_4_LO => {},
            FILTER_COEFFICIENT_5_HI => {},
            FILTER_COEFFICIENT_5_LO => {},
            FILTER_COEFFICIENT_6_HI => {},
            FILTER_COEFFICIENT_6_LO => {},
            SCALING_WIDTH => {},
            CLOCK_SELECT => self.clock_select = val != 0,
            UNKNOWN => {},
            _ => panic!("VI: unhandled register ({:#x})", register)
        }
    }

}

#[derive(Debug, Default)]
struct DisplayConfig {
    format:          u8,   // 0: NTSC, 1: PAL, 2: MPAL, 3: DEBUG // pal50/pal60/ntsc: 0x0101, 0x0001, 0x0001
    display_latch_0: u8,
    display_latch_1: u8,
    display_mode_3d: bool,
    interlaced:      bool,
    reset:           bool,
    enable:          bool
}

impl From<u16> for DisplayConfig {
    fn from(value: u16) -> Self {
        DisplayConfig {
            format:          ((value >> 8) & 3) as u8,
            display_latch_0: ((value >> 6) & 3) as u8,
            display_latch_1: ((value >> 4) & 3) as u8,
            display_mode_3d: (value & (1 << 3)) != 0,
            interlaced:      (value & (1 << 2)) != 0,
            reset:           (value & (1 << 1)) != 0,
            enable:          (value & 1) != 0
        }
    }
}

impl DisplayConfig {
    pub fn as_u16(&self) -> u16 {
        let mut value = 0;

        value |= (self.format as u16)          << 8;
        value |= (self.display_latch_0 as u16) << 6;
        value |= (self.display_latch_1 as u16) << 4;
        value |= (self.display_mode_3d as u16) << 3;
        value |= (self.interlaced as u16)      << 2;
        value |= (self.reset as u16)           << 1;
        value |=  self.enable as u16;

        value
    }
}

#[derive(Debug, Default)]
struct VerticalTiming {
    active_video: u16,
    equalization: u8 // equalization pulse in half lines
}

impl From<u16> for VerticalTiming {
    fn from(value: u16) -> Self {
        VerticalTiming {
            active_video: ((value >> 4) & 0x3FF) as u16,
            equalization: (value & 0xF) as u8
        }
    }
}

#[derive(Debug, Default)]
struct HorizontalTiming {
    horizontal_sync_start: u8,
    horizontal_sync_end: u8,
    halfline_width: u16,
    halfline_blank_start: u16,
    horizontal_blank_end: u16,
    horizontal_sync_width: u8,
}

impl HorizontalTiming {
    pub fn from_hi(&mut self, val: u16) {
        self.horizontal_sync_start = ((val >> 8) & 0x7F) as u8;
        self.horizontal_sync_end = (val & 0x7F) as u8
    }

    pub fn from_lo(&mut self, val: u16) {
        self.halfline_width = val & 0x1FF;
    }

    pub fn from_hi_1(&mut self, val: u16) {
        self.halfline_blank_start = (val >> 1) & 0x3FF;
        self.horizontal_blank_end |= (val & 1) << 9;
    }

    pub fn from_lo_1(&mut self, val: u16) {
        self.horizontal_blank_end = val >> 7;
        self.horizontal_sync_width = (val & 0x7F) as u8;
    }
}

#[derive(Debug, Default)]
struct VBlankTiming {
    post_blanking: u16,
    pre_blanking: u16
}

impl VBlankTiming {
    pub fn from_hi(&mut self, val: u16) {
        self.post_blanking = val;
    }

    pub fn from_lo(&mut self, val: u16) {
        self.pre_blanking = val;
    }
}
<|endoftext|>