<commit_before>#![crate_name = "rboy"]
#![license    = "MIT" ]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;
#[phase(plugin)]       extern crate lazy_static;
                       extern crate spinlock;
                       extern crate native;
                       extern crate getopts;
                       extern crate sdl;
                       extern crate rboy;

use rboy::device::Device;
use std::time::Duration;
use std::sync::{Arc,RWLock};
use std::comm::{Sender,Receiver,Disconnected,Empty};
use std::task::TaskBuilder;
use std::collections::{Deque, DList};
use native::NativeTaskBuilder;
use spinlock::Spinlock;

static SCALE: uint = 2;
static EXITCODE_INCORRECTOPTIONS: int = 1;
static EXITCODE_CPULOADFAILS: int = 2;

lazy_static! {
	static ref SOUND_BUFFER: Arc<Spinlock<DList<u8>>> = Arc::new(Spinlock::new(DList::new()));
}

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free[0].clone()
	} else {
		println!("{}", getopts::usage(args[0].clone().append(" <filename>").as_slice(), opts));
		std::os::set_exit_status(EXITCODE_INCORRECTOPTIONS);
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdl_tx, cpu_rx) = std::comm::channel();
	let (cpu_tx, sdl_rx) = std::comm::channel();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();

	TaskBuilder::new().native().spawn(proc() cpuloop(&cpu_tx, &cpu_rx, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(Duration::milliseconds(8));

	'main : loop {
		periodic.recv();
		match sdl_rx.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdl_tx.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdl_tx.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdl_tx.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdl_tx.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<rboy::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(rboy::A),
		sdl::event::XKey => Some(rboy::B),
		sdl::event::UpKey => Some(rboy::Up),
		sdl::event::DownKey => Some(rboy::Down),
		sdl::event::LeftKey => Some(rboy::Left),
		sdl::event::RightKey => Some(rboy::Right),
		sdl::event::SpaceKey => Some(rboy::Select),
		sdl::event::ReturnKey => Some(rboy::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(rboy::KeypadKey),
	KeyDown(rboy::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(cpu_tx: &Sender<uint>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => Device::new(filename),
		false => Device::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); std::os::set_exit_status(EXITCODE_CPULOADFAILS); return; },
	};
	c.set_stdout(matches.opt_present("serial"));

	let soundbuffer = SOUND_BUFFER.clone();
	c.set_sound_buffer(soundbuffer);
	open_audio();

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(Duration::milliseconds(8));
	let mut limit_speed = true;

	let waitticks = (4194.304f32 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.check_and_reset_gpu_updated() {
				let mut data = arc.write();
				let gpu_data = c.get_gpu_data();
				for i in range(0, data.len()) { data[i] = gpu_data[i]; }
				if cpu_tx.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		if limit_speed { periodic.recv(); }

		match cpu_rx.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.keyup(key),
				KeyDown(key) => c.keydown(key),
				SpeedUp => limit_speed = false,
				SlowDown => limit_speed = true,
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
	close_audio();
}

fn sound_callback(sdlbuf: &mut [u8])
{
	let soundbuffer = SOUND_BUFFER.clone();
	let mut data = soundbuffer.lock();

	for i in range(0, sdlbuf.len())
	{
		sdlbuf[i] = data.pop_front().unwrap_or(0);
	}
}

fn open_audio()
{
	let spec = sdl::audio::DesiredAudioSpec
	{
		freq: 44100,
		format: sdl::audio::S8AudioFormat,
		channels: sdl::audio::Stereo,
		samples: 1024,
		callback: sound_callback,
	};
	match sdl::audio::open(spec)
	{
		Ok(_) => {},
		Err(()) => error!("Could not open audio"),
	}
	sdl::audio::pause(false);
}

fn close_audio()
{
	sdl::audio::close();
}
<commit_msg>Add dummy test to binary to ignore dead code<commit_after>#![crate_name = "rboy"]
#![license    = "MIT" ]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;
#[phase(plugin)]       extern crate lazy_static;
                       extern crate spinlock;
                       extern crate native;
                       extern crate getopts;
                       extern crate sdl;
                       extern crate rboy;

use rboy::device::Device;
use std::time::Duration;
use std::sync::{Arc,RWLock};
use std::comm::{Sender,Receiver,Disconnected,Empty};
use std::task::TaskBuilder;
use std::collections::{Deque, DList};
use native::NativeTaskBuilder;
use spinlock::Spinlock;

static SCALE: uint = 2;
static EXITCODE_INCORRECTOPTIONS: int = 1;
static EXITCODE_CPULOADFAILS: int = 2;

lazy_static! {
	static ref SOUND_BUFFER: Arc<Spinlock<DList<u8>>> = Arc::new(Spinlock::new(DList::new()));
}

#[cfg(test)]
fn set_exit_status(_exitcode: int)
{
    // Let the test-suite determine this
}

#[cfg(not(test))]
fn set_exit_status(exitcode: int)
{
    std::os::set_exit_status(exitcode);
}

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free[0].clone()
	} else {
		println!("{}", getopts::usage(args[0].clone().append(" <filename>").as_slice(), opts));
		set_exit_status(EXITCODE_INCORRECTOPTIONS);
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdl_tx, cpu_rx) = std::comm::channel();
	let (cpu_tx, sdl_rx) = std::comm::channel();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();

	TaskBuilder::new().native().spawn(proc() cpuloop(&cpu_tx, &cpu_rx, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(Duration::milliseconds(8));

	'main : loop {
		periodic.recv();
		match sdl_rx.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdl_tx.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdl_tx.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdl_tx.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdl_tx.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<rboy::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(rboy::A),
		sdl::event::XKey => Some(rboy::B),
		sdl::event::UpKey => Some(rboy::Up),
		sdl::event::DownKey => Some(rboy::Down),
		sdl::event::LeftKey => Some(rboy::Left),
		sdl::event::RightKey => Some(rboy::Right),
		sdl::event::SpaceKey => Some(rboy::Select),
		sdl::event::ReturnKey => Some(rboy::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(rboy::KeypadKey),
	KeyDown(rboy::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(cpu_tx: &Sender<uint>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => Device::new(filename),
		false => Device::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); set_exit_status(EXITCODE_CPULOADFAILS); return; },
	};
	c.set_stdout(matches.opt_present("serial"));

	let soundbuffer = SOUND_BUFFER.clone();
	c.set_sound_buffer(soundbuffer);
	open_audio();

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(Duration::milliseconds(8));
	let mut limit_speed = true;

	let waitticks = (4194.304f32 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.check_and_reset_gpu_updated() {
				let mut data = arc.write();
				let gpu_data = c.get_gpu_data();
				for i in range(0, data.len()) { data[i] = gpu_data[i]; }
				if cpu_tx.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		if limit_speed { periodic.recv(); }

		match cpu_rx.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.keyup(key),
				KeyDown(key) => c.keydown(key),
				SpeedUp => limit_speed = false,
				SlowDown => limit_speed = true,
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
	close_audio();
}

fn sound_callback(sdlbuf: &mut [u8])
{
	let soundbuffer = SOUND_BUFFER.clone();
	let mut data = soundbuffer.lock();

	for i in range(0, sdlbuf.len())
	{
		sdlbuf[i] = data.pop_front().unwrap_or(0);
	}
}

fn open_audio()
{
	let spec = sdl::audio::DesiredAudioSpec
	{
		freq: 44100,
		format: sdl::audio::S8AudioFormat,
		channels: sdl::audio::Stereo,
		samples: 1024,
		callback: sound_callback,
	};
	match sdl::audio::open(spec)
	{
		Ok(_) => {},
		Err(()) => error!("Could not open audio"),
	}
	sdl::audio::pause(false);
}

fn close_audio()
{
	sdl::audio::close();
}

#[cfg(test)]
mod test
{
    #[test]
    fn no_dead_code()
    {
        super::main();
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rboy"]

#![feature(exit_status)]

extern crate getopts;
extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,RwLock};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};

static SCALE: usize = 2;
static EXITCODE_INCORRECTOPTIONS: i32 = 1;
static EXITCODE_CPULOADFAILS: i32 = 2;

#[cfg(not(test))]
fn set_exit_status(exitcode: i32)
{
    std::env::set_exit_status(exitcode);
}

fn main() {
    let mut opts = getopts::Options::new();
    opts.optflag("s", "serial", "Output serial to stdout");
    opts.optflag("c", "classic", "Force Classic mode");

    let args: Vec<_> = std::env::args().collect();

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { println!("{}", f); return }
    };

    let filename = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let mut info_start = args[0].clone();
        info_start.push_str(" <filename>");
        println!("{}", opts.usage(&info_start));
        set_exit_status(EXITCODE_INCORRECTOPTIONS);
        return;
    };

    let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap();
    let window = match sdl2::video::Window::new("RBoy - A gameboy in Rust",
                                                sdl2::video::WindowPos::PosUndefined,
                                                sdl2::video::WindowPos::PosUndefined,
                                                160*SCALE as i32,
                                                144*SCALE as i32,
                                                sdl2::video::WindowFlags::empty()) {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };

    let (sdl_tx, cpu_rx) = std::sync::mpsc::channel();
    let (cpu_tx, sdl_rx) = std::sync::mpsc::channel();
    let rawscreen = ::std::iter::repeat(0u8).take(160*144*3).collect();
    let arc = Arc::new(RwLock::new(rawscreen));
    let arc2 = arc.clone();

    let cpuloop_thread = std::thread::scoped(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, &filename, &matches));

    let periodic = timer_periodic(8);

    let mut event_queue = sdl_context.event_pump();
    'main : loop {
        let _ = periodic.recv();
        match sdl_rx.try_recv() {
            Err(Disconnected) => { break 'main },
            Ok(_) => recalculate_screen(&renderer, &arc),
            Err(Empty) => {},
        }
        for event in event_queue.poll_iter() {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::Escape, .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) => { let _ = sdl_tx.send(GBEvent::KeyUp(key)); },
                        None => {},
                    }
                },
                _ => {}
            }
        }
    }

    drop(sdl_tx); // Disconnect such that the cpuloop will exit
    let _ = cpuloop_thread.join();
}

fn sdl_to_keypad(key: sdl2::keycode::KeyCode) -> Option<rboy::KeypadKey> {
    match key {
        sdl2::keycode::KeyCode::Z => Some(rboy::KeypadKey::A),
        sdl2::keycode::KeyCode::X => Some(rboy::KeypadKey::B),
        sdl2::keycode::KeyCode::Up => Some(rboy::KeypadKey::Up),
        sdl2::keycode::KeyCode::Down => Some(rboy::KeypadKey::Down),
        sdl2::keycode::KeyCode::Left => Some(rboy::KeypadKey::Left),
        sdl2::keycode::KeyCode::Right => Some(rboy::KeypadKey::Right),
        sdl2::keycode::KeyCode::Space => Some(rboy::KeypadKey::Select),
        sdl2::keycode::KeyCode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(screen: &sdl2::render::Renderer, arc: &Arc<RwLock<Vec<u8>>>) {
    let mut drawer = screen.drawer();
    drawer.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 0xFF, 0xFF));
    drawer.clear();

    let data =  arc.read().unwrap().clone();
    for y in 0..144 {
        for x in 0..160 {
            drawer.set_draw_color(sdl2::pixels::Color::RGB(data[y*160*3 + x*3 + 0],
                                                            data[y*160*3 + x*3 + 1],
                                                            data[y*160*3 + x*3 + 2]));
            drawer.draw_rect(sdl2::rect::Rect::new((x*SCALE) as i32, (y*SCALE) as i32, SCALE as i32, SCALE as i32));
        }
    }
    drawer.present();
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SlowDown,
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn cpuloop(cpu_tx: &Sender<u32>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, filename: &str, matches: &getopts::Matches) {
    let opt_c = match matches.opt_present("classic") {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); set_exit_status(EXITCODE_CPULOADFAILS); return; },
    };
    c.set_stdout(matches.opt_present("serial"));

    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194.304f32 * 4.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                let mut data = arc.write().unwrap();
                let gpu_data = c.get_gpu_data();
                for i in 0..data.len() { data[i] = gpu_data[i]; }
                if cpu_tx.send(0).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        match cpu_rx.try_recv() {
            Ok(event) => match event {
                GBEvent::KeyUp(key) => c.keyup(key),
                GBEvent::KeyDown(key) => c.keydown(key),
                GBEvent::SpeedUp => limit_speed = false,
                GBEvent::SlowDown => limit_speed = true,
            },
            Err(Empty) => {},
            Err(Disconnected) => { break },
        };
    }
}

fn timer_periodic(ms: u32) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep_ms(ms);
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}
<commit_msg>Remove exit_status feature<commit_after>#![crate_name = "rboy"]

extern crate getopts;
extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,RwLock};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};
use std::sync::atomic;
use std::sync::atomic::Ordering::Relaxed;

const SCALE : usize = 2;
const EXITCODE_SUCCESS : isize = 0;
const EXITCODE_INCORRECTOPTIONS : isize = 1;
const EXITCODE_CPULOADFAILS : isize = 2;

static EXITCODE : atomic::AtomicIsize = atomic::ATOMIC_ISIZE_INIT;

fn main() {
    set_exit_status(EXITCODE_SUCCESS);
    real_main();
    std::process::exit(EXITCODE.load(Relaxed) as i32);
}

fn set_exit_status(status: isize) {
    EXITCODE.store(status, Relaxed);
}

fn real_main() {
    let mut opts = getopts::Options::new();
    opts.optflag("s", "serial", "Output serial to stdout");
    opts.optflag("c", "classic", "Force Classic mode");

    let args: Vec<_> = std::env::args().collect();

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { println!("{}", f); set_exit_status(EXITCODE_INCORRECTOPTIONS); return }
    };

    let filename = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        let mut info_start = args[0].clone();
        info_start.push_str(" <filename>");
        println!("{}", opts.usage(&info_start));
        set_exit_status(EXITCODE_INCORRECTOPTIONS);
        return;
    };

    let sdl_context = sdl2::init(sdl2::INIT_VIDEO).unwrap();
    let window = match sdl2::video::Window::new("RBoy - A gameboy in Rust",
                                                sdl2::video::WindowPos::PosUndefined,
                                                sdl2::video::WindowPos::PosUndefined,
                                                160*SCALE as i32,
                                                144*SCALE as i32,
                                                sdl2::video::WindowFlags::empty()) {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };

    let (sdl_tx, cpu_rx) = std::sync::mpsc::channel();
    let (cpu_tx, sdl_rx) = std::sync::mpsc::channel();
    let rawscreen = ::std::iter::repeat(0u8).take(160*144*3).collect();
    let arc = Arc::new(RwLock::new(rawscreen));
    let arc2 = arc.clone();

    let cpuloop_thread = std::thread::scoped(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, &filename, &matches));

    let periodic = timer_periodic(8);

    let mut event_queue = sdl_context.event_pump();
    'main : loop {
        let _ = periodic.recv();
        match sdl_rx.try_recv() {
            Err(Disconnected) => { break 'main },
            Ok(_) => recalculate_screen(&renderer, &arc),
            Err(Empty) => {},
        }
        for event in event_queue.poll_iter() {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::Escape, .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: sdl2::keycode::KeyCode::LShift, .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: sdlkey, .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) => { let _ = sdl_tx.send(GBEvent::KeyUp(key)); },
                        None => {},
                    }
                },
                _ => {}
            }
        }
    }

    drop(sdl_tx); // Disconnect such that the cpuloop will exit
    let _ = cpuloop_thread.join();
}

fn sdl_to_keypad(key: sdl2::keycode::KeyCode) -> Option<rboy::KeypadKey> {
    match key {
        sdl2::keycode::KeyCode::Z => Some(rboy::KeypadKey::A),
        sdl2::keycode::KeyCode::X => Some(rboy::KeypadKey::B),
        sdl2::keycode::KeyCode::Up => Some(rboy::KeypadKey::Up),
        sdl2::keycode::KeyCode::Down => Some(rboy::KeypadKey::Down),
        sdl2::keycode::KeyCode::Left => Some(rboy::KeypadKey::Left),
        sdl2::keycode::KeyCode::Right => Some(rboy::KeypadKey::Right),
        sdl2::keycode::KeyCode::Space => Some(rboy::KeypadKey::Select),
        sdl2::keycode::KeyCode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(screen: &sdl2::render::Renderer, arc: &Arc<RwLock<Vec<u8>>>) {
    let mut drawer = screen.drawer();
    drawer.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 0xFF, 0xFF));
    drawer.clear();

    let data =  arc.read().unwrap().clone();
    for y in 0..144 {
        for x in 0..160 {
            drawer.set_draw_color(sdl2::pixels::Color::RGB(data[y*160*3 + x*3 + 0],
                                                            data[y*160*3 + x*3 + 1],
                                                            data[y*160*3 + x*3 + 2]));
            drawer.draw_rect(sdl2::rect::Rect::new((x*SCALE) as i32, (y*SCALE) as i32, SCALE as i32, SCALE as i32));
        }
    }
    drawer.present();
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SlowDown,
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn cpuloop(cpu_tx: &Sender<u32>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, filename: &str, matches: &getopts::Matches) {
    let opt_c = match matches.opt_present("classic") {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); set_exit_status(EXITCODE_CPULOADFAILS); return; },
    };
    c.set_stdout(matches.opt_present("serial"));

    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194.304f32 * 4.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                let mut data = arc.write().unwrap();
                let gpu_data = c.get_gpu_data();
                for i in 0..data.len() { data[i] = gpu_data[i]; }
                if cpu_tx.send(0).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        match cpu_rx.try_recv() {
            Ok(event) => match event {
                GBEvent::KeyUp(key) => c.keyup(key),
                GBEvent::KeyDown(key) => c.keydown(key),
                GBEvent::SpeedUp => limit_speed = false,
                GBEvent::SlowDown => limit_speed = true,
            },
            Err(Empty) => {},
            Err(Disconnected) => { break },
        };
    }
}

fn timer_periodic(ms: u32) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep_ms(ms);
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}
<|endoftext|>


<commit_before>use brotli::dictionary::{kBrotliDictionary, kBrotliDictionarySizeBitsByLength,
                         kBrotliDictionaryOffsetsByLength};
use brotli::transform::{TransformDictionaryWord};
use std::collections::BTreeMap;
use std::fmt;

struct HexSlice<'a>(&'a [u8]);

impl<'a> fmt::Display for HexSlice<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for byte in self.0 {
            write!(f, "{:02X}", byte)?;
        }
        Ok(())
    }
}
pub fn permute_dictionary() -> BTreeMap<Vec<u8>, ()> {
    let mut ret = BTreeMap::<Vec<u8>, ()>::new();
    let mut transformed = [0u8;38];
    for wordlen in 4..kBrotliDictionaryOffsetsByLength.len() {
        let offset = kBrotliDictionaryOffsetsByLength[wordlen] as usize;
        for index in 0..(1 << kBrotliDictionarySizeBitsByLength[wordlen]) {
            let word = &kBrotliDictionary[offset + index..offset + index + wordlen];
            for transform in 0..121 {
                TransformDictionaryWord(&mut transformed[..],
                                        word,
                                        wordlen as i32,
                                        transform as i32);
                let vec : Vec<u8> = transformed[..wordlen].to_vec();
                ret.insert(vec, ());
            }
        }
    }
    ret
}

pub fn print_dictionary(dict :BTreeMap<Vec<u8>, ()>) {
    for (key, _) in dict {
        println!("{}", HexSlice(&key[..]));
    }
}
<commit_msg>Read transformed dictionary size<commit_after>use brotli::dictionary::{kBrotliDictionary, kBrotliDictionarySizeBitsByLength,
                         kBrotliDictionaryOffsetsByLength};
use brotli::transform::{TransformDictionaryWord};
use std::collections::BTreeMap;
use std::fmt;

struct HexSlice<'a>(&'a [u8]);

impl<'a> fmt::Display for HexSlice<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for byte in self.0 {
            write!(f, "{:02X}", byte)?;
        }
        Ok(())
    }
}
pub fn permute_dictionary() -> BTreeMap<Vec<u8>, ()> {
    let mut ret = BTreeMap::<Vec<u8>, ()>::new();
    let mut transformed = [0u8;38];
    for wordlen in 4..kBrotliDictionaryOffsetsByLength.len() {
        let offset = kBrotliDictionaryOffsetsByLength[wordlen] as usize;
        for index in 0..(1 << kBrotliDictionarySizeBitsByLength[wordlen]) {
            let word = &kBrotliDictionary[offset + index..offset + index + wordlen];
            for transform in 0..121 {
                let final_size = TransformDictionaryWord(&mut transformed[..],
                                        word,
                                        wordlen as i32,
                                        transform as i32);
                let vec : Vec<u8> = transformed[..final_size].to_vec();
                ret.insert(vec, ());
            }
        }
    }
    ret
}

pub fn print_dictionary(dict :BTreeMap<Vec<u8>, ()>) {
    for (key, _) in dict {
        println!("{}", HexSlice(&key[..]));
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

pub const DBUS_TIMEOUT: i32 = 20000; // millieconds

pub const STRATIS_VERSION: &'static str = "1";
pub const MANAGER_NAME: &'static str = "/Manager";
pub const STRATIS_BASE_PATH: &'static str = "/org/storage/stratis1";
pub const STRATIS_BASE_SERVICE: &'static str = "org.storage.stratis1";
pub const STRATIS_BASE_MANAGER: &'static str = "/org/storage/stratis1/Manager";
pub const STRATIS_MANAGER_INTERFACE: &'static str = "org.storage.stratis1.Manager";
pub const STRATIS_POOL_BASE_INTERFACE: &'static str = "org.storage.stratis1.pool";
pub const STRATIS_FILESYSTEM_BASE_INTERFACE: &'static str = "org.storage.stratis1.filesystem";
pub const STRATIS_DEV_BASE_INTERFACE: &'static str = "org.storage.stratis1.dev";
pub const STRATIS_CACHE_BASE_INTERFACE: &'static str = "org.storage.stratis1.cache";
pub const STRATIS_POOL_BASE_PATH: &'static str = "/org/storage/stratis/pool";

pub const DEFAULT_OBJECT_PATH: &'static str = "/";

// Manager Methods
pub const LIST_POOLS: &'static str = "ListPools";
pub const CREATE_POOL: &'static str = "CreatePool";
pub const DESTROY_POOL: &'static str = "DestroyPool";
pub const GET_POOL_OBJECT_PATH: &'static str = "GetPoolObjectPath";
pub const GET_FILESYSTEM_OBJECT_PATH: &'static str = "GetFilesystemObjectPath";
pub const GET_DEV_OBJECT_PATH: &'static str = "GetDevObjectPath";
pub const GET_CACHE_OBJECT_PATH: &'static str = "GetCacheObjectPath";
pub const GET_ERROR_CODES: &'static str = "GetErrorCodes";
pub const GET_RAID_LEVELS: &'static str = "GetRaidLevels";
pub const GET_DEV_TYPES: &'static str = "GetDevTypes";

// Pool Methods
pub const CREATE_FILESYSTEMS: &'static str = "CreateFilesystems";
pub const DESTROY_FILESYSTEMS: &'static str = "DestroyFilesystems";
pub const LIST_FILESYSTEMS: &'static str = "ListFilesystems";
pub const LIST_DEVS: &'static str = "ListDevs";
pub const LIST_CACHE_DEVS: &'static str = "ListCacheDevs";
pub const ADD_CACHE_DEVS: &'static str = "AddCacheDevs";
pub const REMOVE_CACHE_DEVS: &'static str = "RemoveCacheDevs";
pub const ADD_DEVS: &'static str = "AddDevs";
pub const REMOVE_DEVS: &'static str = "RemoveDevs";

pub trait HasCodes {
    /// Indicates that this enum can be converted to an int or described
    /// with a string.
    fn get_error_int(&self) -> u16;
    fn get_error_string(&self) -> &str;
}

custom_derive! {
    #[derive(Copy, Clone, EnumDisplay,
             IterVariants(StratisDBusErrorVariants),
             IterVariantNames(StratisDBusErrorVariantNames))]
    #[allow(non_camel_case_types)]
    pub enum StratisErrorEnum {
        STRATIS_OK,
        STRATIS_ERROR,
        STRATIS_NULL,
        STRATIS_NOTFOUND,
        STRATIS_POOL_NOTFOUND,
        STRATIS_FILESYSTEM_NOTFOUND,
        STRATIS_DEV_NOTFOUND,
        STRATIS_CACHE_NOTFOUND,
        STRATIS_BAD_PARAM,
        STRATIS_ALREADY_EXISTS,
        STRATIS_NULL_NAME,
        STRATIS_NO_POOLS,
        STRATIS_LIST_FAILURE,
    }
}

impl HasCodes for StratisErrorEnum {
    fn get_error_int(&self) -> u16 {
        *self as u16
    }

    fn get_error_string(&self) -> &str {
        match *self {
            StratisErrorEnum::STRATIS_OK => "Ok",
            StratisErrorEnum::STRATIS_ERROR => "A general error happened",
            StratisErrorEnum::STRATIS_NULL => "Null parameter was supplied",
            StratisErrorEnum::STRATIS_NOTFOUND => "Not found",
            StratisErrorEnum::STRATIS_POOL_NOTFOUND => "Pool not found",
            StratisErrorEnum::STRATIS_FILESYSTEM_NOTFOUND => "Filesystem not found",
            StratisErrorEnum::STRATIS_CACHE_NOTFOUND => "Cache not found",
            StratisErrorEnum::STRATIS_BAD_PARAM => "Bad parameter",
            StratisErrorEnum::STRATIS_DEV_NOTFOUND => "Dev not found",
            StratisErrorEnum::STRATIS_ALREADY_EXISTS => "Already exists",
            StratisErrorEnum::STRATIS_NULL_NAME => "Null name supplied",
            StratisErrorEnum::STRATIS_NO_POOLS => "No pools",
            StratisErrorEnum::STRATIS_LIST_FAILURE => "List operation failure.",
        }
    }
}

custom_derive! {
    #[derive(Copy, Clone, EnumDisplay,
             IterVariants(StratisDBusRaidTypeVariants),
             IterVariantNames(StratisDBusRaidTypeVariantNames))]
    #[allow(non_camel_case_types)]
    pub enum StratisRaidType {
        STRATIS_RAID_TYPE_UNKNOWN,
        STRATIS_RAID_TYPE_SINGLE,
        STRATIS_RAID_TYPE_RAID1,
        STRATIS_RAID_TYPE_RAID5,
        STRATIS_RAID_TYPE_RAID6,
    }
}

impl HasCodes for StratisRaidType {
    fn get_error_int(&self) -> u16 {
        *self as u16
    }

    fn get_error_string(&self) -> &str {
        match *self {
            StratisRaidType::STRATIS_RAID_TYPE_UNKNOWN => "Unknown",
            StratisRaidType::STRATIS_RAID_TYPE_SINGLE => "Single",
            StratisRaidType::STRATIS_RAID_TYPE_RAID1 => "Mirrored",
            StratisRaidType::STRATIS_RAID_TYPE_RAID5 => {
                "Block-level striping with distributed parity"
            }
            StratisRaidType::STRATIS_RAID_TYPE_RAID6 => {
                "Block-level striping with two distributed parities"
            }
        }
    }
}
<commit_msg>Alphabetize all the specific stratis error constants.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

pub const DBUS_TIMEOUT: i32 = 20000; // millieconds

pub const STRATIS_VERSION: &'static str = "1";
pub const MANAGER_NAME: &'static str = "/Manager";
pub const STRATIS_BASE_PATH: &'static str = "/org/storage/stratis1";
pub const STRATIS_BASE_SERVICE: &'static str = "org.storage.stratis1";
pub const STRATIS_BASE_MANAGER: &'static str = "/org/storage/stratis1/Manager";
pub const STRATIS_MANAGER_INTERFACE: &'static str = "org.storage.stratis1.Manager";
pub const STRATIS_POOL_BASE_INTERFACE: &'static str = "org.storage.stratis1.pool";
pub const STRATIS_FILESYSTEM_BASE_INTERFACE: &'static str = "org.storage.stratis1.filesystem";
pub const STRATIS_DEV_BASE_INTERFACE: &'static str = "org.storage.stratis1.dev";
pub const STRATIS_CACHE_BASE_INTERFACE: &'static str = "org.storage.stratis1.cache";
pub const STRATIS_POOL_BASE_PATH: &'static str = "/org/storage/stratis/pool";

pub const DEFAULT_OBJECT_PATH: &'static str = "/";

// Manager Methods
pub const LIST_POOLS: &'static str = "ListPools";
pub const CREATE_POOL: &'static str = "CreatePool";
pub const DESTROY_POOL: &'static str = "DestroyPool";
pub const GET_POOL_OBJECT_PATH: &'static str = "GetPoolObjectPath";
pub const GET_FILESYSTEM_OBJECT_PATH: &'static str = "GetFilesystemObjectPath";
pub const GET_DEV_OBJECT_PATH: &'static str = "GetDevObjectPath";
pub const GET_CACHE_OBJECT_PATH: &'static str = "GetCacheObjectPath";
pub const GET_ERROR_CODES: &'static str = "GetErrorCodes";
pub const GET_RAID_LEVELS: &'static str = "GetRaidLevels";
pub const GET_DEV_TYPES: &'static str = "GetDevTypes";

// Pool Methods
pub const CREATE_FILESYSTEMS: &'static str = "CreateFilesystems";
pub const DESTROY_FILESYSTEMS: &'static str = "DestroyFilesystems";
pub const LIST_FILESYSTEMS: &'static str = "ListFilesystems";
pub const LIST_DEVS: &'static str = "ListDevs";
pub const LIST_CACHE_DEVS: &'static str = "ListCacheDevs";
pub const ADD_CACHE_DEVS: &'static str = "AddCacheDevs";
pub const REMOVE_CACHE_DEVS: &'static str = "RemoveCacheDevs";
pub const ADD_DEVS: &'static str = "AddDevs";
pub const REMOVE_DEVS: &'static str = "RemoveDevs";

pub trait HasCodes {
    /// Indicates that this enum can be converted to an int or described
    /// with a string.
    fn get_error_int(&self) -> u16;
    fn get_error_string(&self) -> &str;
}

custom_derive! {
    #[derive(Copy, Clone, EnumDisplay,
             IterVariants(StratisDBusErrorVariants),
             IterVariantNames(StratisDBusErrorVariantNames))]
    #[allow(non_camel_case_types)]
    pub enum StratisErrorEnum {
        STRATIS_OK,
        STRATIS_ERROR,

        STRATIS_ALREADY_EXISTS,
        STRATIS_BAD_PARAM,
        STRATIS_CACHE_NOTFOUND,
        STRATIS_DEV_NOTFOUND,
        STRATIS_FILESYSTEM_NOTFOUND,
        STRATIS_LIST_FAILURE,
        STRATIS_NO_POOLS,
        STRATIS_NOTFOUND,
        STRATIS_NULL,
        STRATIS_NULL_NAME,
        STRATIS_POOL_NOTFOUND,
    }
}

impl HasCodes for StratisErrorEnum {
    fn get_error_int(&self) -> u16 {
        *self as u16
    }

    fn get_error_string(&self) -> &str {
        match *self {
            StratisErrorEnum::STRATIS_OK => "Ok",
            StratisErrorEnum::STRATIS_ERROR => "A general error happened",
            StratisErrorEnum::STRATIS_NULL => "Null parameter was supplied",
            StratisErrorEnum::STRATIS_NOTFOUND => "Not found",
            StratisErrorEnum::STRATIS_POOL_NOTFOUND => "Pool not found",
            StratisErrorEnum::STRATIS_FILESYSTEM_NOTFOUND => "Filesystem not found",
            StratisErrorEnum::STRATIS_CACHE_NOTFOUND => "Cache not found",
            StratisErrorEnum::STRATIS_BAD_PARAM => "Bad parameter",
            StratisErrorEnum::STRATIS_DEV_NOTFOUND => "Dev not found",
            StratisErrorEnum::STRATIS_ALREADY_EXISTS => "Already exists",
            StratisErrorEnum::STRATIS_NULL_NAME => "Null name supplied",
            StratisErrorEnum::STRATIS_NO_POOLS => "No pools",
            StratisErrorEnum::STRATIS_LIST_FAILURE => "List operation failure.",
        }
    }
}

custom_derive! {
    #[derive(Copy, Clone, EnumDisplay,
             IterVariants(StratisDBusRaidTypeVariants),
             IterVariantNames(StratisDBusRaidTypeVariantNames))]
    #[allow(non_camel_case_types)]
    pub enum StratisRaidType {
        STRATIS_RAID_TYPE_UNKNOWN,
        STRATIS_RAID_TYPE_SINGLE,
        STRATIS_RAID_TYPE_RAID1,
        STRATIS_RAID_TYPE_RAID5,
        STRATIS_RAID_TYPE_RAID6,
    }
}

impl HasCodes for StratisRaidType {
    fn get_error_int(&self) -> u16 {
        *self as u16
    }

    fn get_error_string(&self) -> &str {
        match *self {
            StratisRaidType::STRATIS_RAID_TYPE_UNKNOWN => "Unknown",
            StratisRaidType::STRATIS_RAID_TYPE_SINGLE => "Single",
            StratisRaidType::STRATIS_RAID_TYPE_RAID1 => "Mirrored",
            StratisRaidType::STRATIS_RAID_TYPE_RAID5 => {
                "Block-level striping with distributed parity"
            }
            StratisRaidType::STRATIS_RAID_TYPE_RAID6 => {
                "Block-level striping with two distributed parities"
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod driver;
mod fake;
mod h1b_hw;
mod mock_alarm;
mod smart_program;
<commit_msg>Cut two tests in order for userspace binary to fit within 32kB.<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//mod driver;
mod fake;
mod h1b_hw;
mod mock_alarm;
//mod smart_program;
<|endoftext|>


<commit_before>mod cid;
mod authorize;
mod backend;
mod message;
mod tangle_auth;
mod processor;
mod error;
mod close_reason;
mod listener;
mod dispatcher;
mod connection_sender;
mod inactivity_handler;
mod replication;

pub use self::cid::Cid;
pub use self::tangle_auth::TangleAuth;
pub use self::authorize::start_authorize;
pub use self::message::{Meta, Args, Kwargs};
pub use self::error::MessageError;
pub use self::close_reason::CloseReason;
pub use self::listener::SessionPools;
pub use self::processor::{Processor, ConnectionMessage};
pub use self::dispatcher::Dispatcher;
pub use self::connection_sender::ConnectionSender;
pub use self::replication::ReplicationSession;

use metrics::{Counter, Integer, List, Metric};

lazy_static! {
    pub static ref CONNECTS: Counter = Counter::new();
    pub static ref CONNECTIONS: Integer = Integer::new();
}

pub struct Shutdown;

pub fn metrics() -> List {
    vec![
        (Metric("websockets.swindon_chat", "connects"), &*CONNECTS),
        (Metric("websockets.swindon_chat", "connections"), &*CONNECTS),
        (Metric("websockets.swindon_chat", "frames_received"),
            &*dispatcher::FRAMES_RECEIVED),
    ]
}
<commit_msg>Fixes metrics for swindon_chat<commit_after>mod cid;
mod authorize;
mod backend;
mod message;
mod tangle_auth;
mod processor;
mod error;
mod close_reason;
mod listener;
mod dispatcher;
mod connection_sender;
mod inactivity_handler;
mod replication;

pub use self::cid::Cid;
pub use self::tangle_auth::TangleAuth;
pub use self::authorize::start_authorize;
pub use self::message::{Meta, Args, Kwargs};
pub use self::error::MessageError;
pub use self::close_reason::CloseReason;
pub use self::listener::SessionPools;
pub use self::processor::{Processor, ConnectionMessage};
pub use self::dispatcher::Dispatcher;
pub use self::connection_sender::ConnectionSender;
pub use self::replication::ReplicationSession;

use metrics::{Counter, Integer, List, Metric};

lazy_static! {
    pub static ref CONNECTS: Counter = Counter::new();
    pub static ref CONNECTIONS: Integer = Integer::new();
}

pub struct Shutdown;

pub fn metrics() -> List {
    vec![
        (Metric("websockets.swindon_chat", "connects"), &*CONNECTS),
        (Metric("websockets.swindon_chat", "connections"), &*CONNECTIONS),
        (Metric("websockets.swindon_chat", "frames_received"),
            &*dispatcher::FRAMES_RECEIVED),
    ]
}
<|endoftext|>


<commit_before>use std::collections::hash_map::{HashMap, Vacant, Occupied};
use std::error::FromError;
use std::fmt;
use std::io;

use csv::{mod, ByteString};
use csv::index::Indexed;

use CliResult;
use config::{Config, Delimiter};
use select::{SelectColumns, Selection, NormalSelection};
use util;

static USAGE: &'static str = "
Joins two sets of CSV data on the specified columns.

The default join operation is an 'inner' join. This corresponds to the
intersection of rows on the keys specified.

The columns arguments specify the columns to join for each input. Columns can
be referenced by name or index, starting at 1. Specify multiple columns by
separating them with a comma. Specify a range of columns with `-`. Both
columns1 and columns2 must specify exactly the same number of columns.

Usage:
    xsv join [options] <columns1> <input1> <columns2> <input2>
    xsv join --help

join options:
    --left                 Do a 'left outer' join. This returns all rows in
                           first CSV data set, including rows with no
                           corresponding row in the second data set. When no
                           corresponding row exists, it is padded out with
                           empty fields.
    --right                Do a 'right outer' join. This returns all rows in
                           second CSV data set, including rows with no
                           corresponding row in the first data set. When no
                           corresponding row exists, it is padded out with
                           empty fields. (This is the reverse of 'outer left'.)
    --full                 Do a 'full outer' join. This returns all rows in
                           both data sets with matching records joined. If
                           there is no match, the missing side will be padded
                           out with empty fields. (This is the combination of
                           'outer left' and 'outer right'.)
    --cross                USE WITH CAUTION.
                           This returns the cartesian product of the CSV
                           data sets given. The number of rows return is
                           equal to N * M, where N and M correspond to the
                           number of rows in the given data sets, respectively.

Common options:
    -h, --help             Display this message
    -o, --output <file>    Write output to <file> instead of stdout.
    -n, --no-headers       When set, the first row will not be interpreted
                           as headers. (i.e., They are not searched, analyzed,
                           sliced, etc.)
    -d, --delimiter <arg>  The field delimiter for reading CSV data.
                           Must be a single character. [default: ,]
";

#[deriving(Decodable)]
struct Args {
    arg_columns1: SelectColumns,
    arg_input1: String,
    arg_columns2: SelectColumns,
    arg_input2: String,
    flag_left: bool,
    flag_right: bool,
    flag_full: bool,
    flag_cross: bool,
    flag_output: Option<String>,
    flag_no_headers: bool,
    flag_delimiter: Delimiter,
}

pub fn run(argv: &[&str]) -> CliResult<()> {
    let args: Args = try!(util::get_args(USAGE, argv));
    let mut state = try!(args.new_io_state());
    match (
        args.flag_left,
        args.flag_right,
        args.flag_full,
        args.flag_cross,
    ) {
        (true, false, false, false) => {
            try!(state.write_headers());
            state.outer_join(false)
        }
        (false, true, false, false) => {
            try!(state.write_headers());
            state.outer_join(true)
        }
        (false, false, true, false) => {
            try!(state.write_headers());
            state.full_outer_join()
        }
        (false, false, false, true) => {
            try!(state.write_headers());
            state.cross_join()
        }
        (false, false, false, false) => {
            try!(state.write_headers());
            state.inner_join()
        }
        _ => Err(FromError::from_error(
            "Please pick exactly one join operation."))
    }
}

struct IoState<R, W> {
    wtr: csv::Writer<W>,
    rdr1: csv::Reader<R>,
    sel1: Selection,
    rdr2: csv::Reader<R>,
    sel2: Selection,
    no_headers: bool,
}

impl<R: io::Reader + io::Seek, W: io::Writer> IoState<R, W> {
    fn write_headers(&mut self) -> CliResult<()> {
        let headers1 = try!(self.rdr1.byte_headers());
        let headers2 = try!(self.rdr2.byte_headers());
        if !self.no_headers {
            let mut headers = headers1.clone();
            headers.push_all(headers2[]);
            try!(self.wtr.write_bytes(headers.into_iter()));
        }
        Ok(())
    }

    fn inner_join(mut self) -> CliResult<()> {
        let mut validx = try!(ValueIndex::new(self.rdr2, &self.sel2.normal()));
        for row in self.rdr1.byte_records() {
            let row = try!(row);
            let val = self.sel1.select(row[])
                               .map(ByteString::from_bytes)
                               .collect::<Vec<ByteString>>();
            match validx.values.find(&val) {
                None => continue,
                Some(rows) => {
                    for &rowi in rows.iter() {
                        try!(validx.idx.seek(rowi as u64));

                        let mut row1 = row.iter().map(|f| Ok(f.as_slice()));
                        let row2 = validx.idx.csv().by_ref();
                        let combined = row1.by_ref().chain(row2);
                        try!(self.wtr.write_results(combined));
                    }
                }
            }
        }
        Ok(())
    }

    fn outer_join(mut self, right: bool) -> CliResult<()> {
        if right {
            ::std::mem::swap(&mut self.rdr1, &mut self.rdr2);
            ::std::mem::swap(&mut self.sel1, &mut self.sel2);
        }

        let (_, pad2) = try!(self.get_padding());
        let mut validx = try!(ValueIndex::new(self.rdr2, &self.sel2.normal()));
        for row in self.rdr1.byte_records() {
            let row = try!(row);
            let val = self.sel1.select(row[])
                               .map(ByteString::from_bytes)
                               .collect::<Vec<ByteString>>();
            match validx.values.find(&val) {
                None => {
                    let row1 = row.iter().map(|f| Ok(f[]));
                    let row2 = pad2.iter().map(|f| Ok(f[]));
                    if right {
                        try!(self.wtr.write_results(row2.chain(row1)));
                    } else {
                        try!(self.wtr.write_results(row1.chain(row2)));
                    }
                }
                Some(rows) => {
                    for &rowi in rows.iter() {
                        try!(validx.idx.seek(rowi as u64));
                        let row1 = row.iter().map(|f| Ok(f.as_slice()));
                        let row2 = validx.idx.csv().by_ref();
                        if right {
                            try!(self.wtr.write_results(row2.chain(row1)));
                        } else {
                            try!(self.wtr.write_results(row1.chain(row2)));
                        }
                    }
                }
            }
        }
        Ok(())
    }

    fn full_outer_join(mut self) -> CliResult<()> {
        let (pad1, pad2) = try!(self.get_padding());
        let mut validx = try!(ValueIndex::new(self.rdr2, &self.sel2.normal()));

        // Keep track of which rows we've written from rdr2.
        let mut rdr2_written = Vec::from_elem(validx.num_rows, false);
        for row1 in self.rdr1.byte_records() {
            let row1 = try!(row1);

            let val = self.sel1.select(row1[])
                               .map(ByteString::from_bytes)
                               .collect::<Vec<ByteString>>();
            match validx.values.find(&val) {
                None => {
                    let row1 = row1.iter().map(|f| Ok(f[]));
                    let row2 = pad2.iter().map(|f| Ok(f[]));
                    try!(self.wtr.write_results(row1.chain(row2)));
                }
                Some(rows) => {
                    for &rowi in rows.iter() {
                        rdr2_written[rowi] = true;

                        try!(validx.idx.seek(rowi as u64));
                        let row1 = row1.iter().map(|f| Ok(f[]));
                        let row2 = validx.idx.csv().by_ref();
                        try!(self.wtr.write_results(row1.chain(row2)));
                    }
                }
            }
        }

        // OK, now write any row from rdr2 that didn't get joined with a row
        // from rdr1.
        for (i, &written) in rdr2_written.iter().enumerate() {
            if !written {
                try!(validx.idx.seek(i as u64));
                let row1 = pad1.iter().map(|f| Ok(f[]));
                let row2 = validx.idx.csv().by_ref();
                try!(self.wtr.write_results(row1.chain(row2)));
            }
        }
        Ok(())
    }

    fn cross_join(mut self) -> CliResult<()> {
        for row1 in self.rdr1.byte_records() {
            let row1 = try!(row1);

            try!(self.rdr2.seek(0, io::SeekSet));
            let mut first = true;
            while !self.rdr2.done() {
                // Skip the header row. The raw byte interface won't
                // do it for us.
                if first {
                    for f in self.rdr2 { try!(f); }
                    first = false;
                }
                let row1 = row1.iter().map(|f| Ok(f[]));
                let row2 = self.rdr2.by_ref();
                try!(self.wtr.write_results(row1.chain(row2)));
            }
        }
        Ok(())
    }

    fn get_padding(&mut self) -> CliResult<(Vec<ByteString>, Vec<ByteString>)> {
        let len1 = try!(self.rdr1.byte_headers()).len();
        let len2 = try!(self.rdr2.byte_headers()).len();
        let (nada1, nada2) = (util::empty_field(), util::empty_field());
        Ok((Vec::from_elem(len1, nada1), Vec::from_elem(len2, nada2)))
    }
}

impl Args {
    fn new_io_state(&self)
        -> CliResult<IoState<io::File, Box<io::Writer+'static>>> {
        let rconf1 = Config::new(Some(self.arg_input1.clone()))
                            .delimiter(self.flag_delimiter)
                            .no_headers(self.flag_no_headers)
                            .select(self.arg_columns1.clone());
        let rconf2 = Config::new(Some(self.arg_input2.clone()))
                            .delimiter(self.flag_delimiter)
                            .no_headers(self.flag_no_headers)
                            .select(self.arg_columns2.clone());

        let mut rdr1 = try!(rconf1.reader_file());
        let mut rdr2 = try!(rconf2.reader_file());
        let (sel1, sel2) = try!(self.get_selections(&rconf1, &mut rdr1,
                                                    &rconf2, &mut rdr2));
        Ok(IoState {
            wtr: try!(Config::new(self.flag_output.clone()).writer()),
            rdr1: rdr1,
            sel1: sel1,
            rdr2: rdr2,
            sel2: sel2,
            no_headers: self.flag_no_headers,
        })
    }

    fn get_selections<R: Reader>
                     (&self,
                      rconf1: &Config, rdr1: &mut csv::Reader<R>,
                      rconf2: &Config, rdr2: &mut csv::Reader<R>)
                     -> CliResult<(Selection, Selection)> {
        let headers1 = try!(rdr1.byte_headers());
        let headers2 = try!(rdr2.byte_headers());
        let select1 = try!(rconf1.selection(headers1[]));
        let select2 = try!(rconf2.selection(headers2[]));
        if select1.len() != select2.len() {
            return Err(FromError::from_error(format!(
                "Column selections must have the same number of columns, \
                 but found column selections with {} and {} columns.",
                select1.len(), select2.len())));
        }
        Ok((select1, select2))
    }
}

struct ValueIndex<R> {
    // This maps tuples of values to corresponding rows.
    values: HashMap<Vec<ByteString>, Vec<uint>>,
    idx: Indexed<R, io::MemReader>,
    num_rows: uint,
}

impl<R: Reader + Seek> ValueIndex<R> {
    fn new(mut rdr: csv::Reader<R>, nsel: &NormalSelection)
          -> CliResult<ValueIndex<R>> {
        let mut val_idx = HashMap::with_capacity(10000);
        // let mut val_idx = BTreeMap::new();
        let mut rows = io::MemWriter::with_capacity(8 * 10000);
        let mut rowi = 0u;
        try!(rows.write_be_u64(0)); // offset to the first row, which
                                        // has already been read as a header.
        while !rdr.done() {
            // This is a bit hokey. We're doing this manually instead of
            // calling `csv::index::create` so we can create both indexes
            // in one pass.
            try!(rows.write_be_u64(rdr.byte_offset()));

            let fields = try!(nsel.select(unsafe { rdr.byte_fields() })
                                  .map(|v| v.map(ByteString::from_bytes))
                                  .collect::<Result<Vec<_>, _>>());
            match val_idx.entry(fields) {
                Vacant(v) => {
                    let mut rows = Vec::with_capacity(4);
                    rows.push(rowi);
                    v.set(rows);
                }
                Occupied(mut v) => { v.get_mut().push(rowi); }
            }
            rowi += 1;
        }
        Ok(ValueIndex {
            values: val_idx,
            idx: try!(Indexed::new(rdr, io::MemReader::new(rows.unwrap()))),
            num_rows: rowi,
        })
    }
}

impl<R> fmt::Show for ValueIndex<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Sort the values by order of first appearance.
        let mut kvs = self.values.iter().collect::<Vec<_>>();
        kvs.sort_by(|&(_, v1), &(_, v2)| v1[0].cmp(&v2[0]));
        for (keys, rows) in kvs.into_iter() {
            // This is just for debugging, so assume Unicode for now.
            let keys = keys.iter()
                           .map(|k| String::from_utf8(k[].to_vec()).unwrap())
                           .collect::<Vec<_>>();
            try!(writeln!(f, "({}) => {}", keys.connect(", "), rows))
        }
        Ok(())
    }
}
<commit_msg>Update to latest Rust.<commit_after>use std::collections::hash_map::{HashMap, Vacant, Occupied};
use std::error::FromError;
use std::fmt;
use std::io;

use csv::{mod, ByteString};
use csv::index::Indexed;

use CliResult;
use config::{Config, Delimiter};
use select::{SelectColumns, Selection, NormalSelection};
use util;

static USAGE: &'static str = "
Joins two sets of CSV data on the specified columns.

The default join operation is an 'inner' join. This corresponds to the
intersection of rows on the keys specified.

The columns arguments specify the columns to join for each input. Columns can
be referenced by name or index, starting at 1. Specify multiple columns by
separating them with a comma. Specify a range of columns with `-`. Both
columns1 and columns2 must specify exactly the same number of columns.

Usage:
    xsv join [options] <columns1> <input1> <columns2> <input2>
    xsv join --help

join options:
    --left                 Do a 'left outer' join. This returns all rows in
                           first CSV data set, including rows with no
                           corresponding row in the second data set. When no
                           corresponding row exists, it is padded out with
                           empty fields.
    --right                Do a 'right outer' join. This returns all rows in
                           second CSV data set, including rows with no
                           corresponding row in the first data set. When no
                           corresponding row exists, it is padded out with
                           empty fields. (This is the reverse of 'outer left'.)
    --full                 Do a 'full outer' join. This returns all rows in
                           both data sets with matching records joined. If
                           there is no match, the missing side will be padded
                           out with empty fields. (This is the combination of
                           'outer left' and 'outer right'.)
    --cross                USE WITH CAUTION.
                           This returns the cartesian product of the CSV
                           data sets given. The number of rows return is
                           equal to N * M, where N and M correspond to the
                           number of rows in the given data sets, respectively.

Common options:
    -h, --help             Display this message
    -o, --output <file>    Write output to <file> instead of stdout.
    -n, --no-headers       When set, the first row will not be interpreted
                           as headers. (i.e., They are not searched, analyzed,
                           sliced, etc.)
    -d, --delimiter <arg>  The field delimiter for reading CSV data.
                           Must be a single character. [default: ,]
";

#[deriving(Decodable)]
struct Args {
    arg_columns1: SelectColumns,
    arg_input1: String,
    arg_columns2: SelectColumns,
    arg_input2: String,
    flag_left: bool,
    flag_right: bool,
    flag_full: bool,
    flag_cross: bool,
    flag_output: Option<String>,
    flag_no_headers: bool,
    flag_delimiter: Delimiter,
}

pub fn run(argv: &[&str]) -> CliResult<()> {
    let args: Args = try!(util::get_args(USAGE, argv));
    let mut state = try!(args.new_io_state());
    match (
        args.flag_left,
        args.flag_right,
        args.flag_full,
        args.flag_cross,
    ) {
        (true, false, false, false) => {
            try!(state.write_headers());
            state.outer_join(false)
        }
        (false, true, false, false) => {
            try!(state.write_headers());
            state.outer_join(true)
        }
        (false, false, true, false) => {
            try!(state.write_headers());
            state.full_outer_join()
        }
        (false, false, false, true) => {
            try!(state.write_headers());
            state.cross_join()
        }
        (false, false, false, false) => {
            try!(state.write_headers());
            state.inner_join()
        }
        _ => Err(FromError::from_error(
            "Please pick exactly one join operation."))
    }
}

struct IoState<R, W> {
    wtr: csv::Writer<W>,
    rdr1: csv::Reader<R>,
    sel1: Selection,
    rdr2: csv::Reader<R>,
    sel2: Selection,
    no_headers: bool,
}

impl<R: io::Reader + io::Seek, W: io::Writer> IoState<R, W> {
    fn write_headers(&mut self) -> CliResult<()> {
        let headers1 = try!(self.rdr1.byte_headers());
        let headers2 = try!(self.rdr2.byte_headers());
        if !self.no_headers {
            let mut headers = headers1.clone();
            headers.push_all(headers2[]);
            try!(self.wtr.write_bytes(headers.into_iter()));
        }
        Ok(())
    }

    fn inner_join(mut self) -> CliResult<()> {
        let mut validx = try!(ValueIndex::new(self.rdr2, &self.sel2.normal()));
        for row in self.rdr1.byte_records() {
            let row = try!(row);
            let val = self.sel1.select(row[])
                               .map(ByteString::from_bytes)
                               .collect::<Vec<ByteString>>();
            match validx.values.get(&val) {
                None => continue,
                Some(rows) => {
                    for &rowi in rows.iter() {
                        try!(validx.idx.seek(rowi as u64));

                        let mut row1 = row.iter().map(|f| Ok(f.as_slice()));
                        let row2 = validx.idx.csv().by_ref();
                        let combined = row1.by_ref().chain(row2);
                        try!(self.wtr.write_results(combined));
                    }
                }
            }
        }
        Ok(())
    }

    fn outer_join(mut self, right: bool) -> CliResult<()> {
        if right {
            ::std::mem::swap(&mut self.rdr1, &mut self.rdr2);
            ::std::mem::swap(&mut self.sel1, &mut self.sel2);
        }

        let (_, pad2) = try!(self.get_padding());
        let mut validx = try!(ValueIndex::new(self.rdr2, &self.sel2.normal()));
        for row in self.rdr1.byte_records() {
            let row = try!(row);
            let val = self.sel1.select(row[])
                               .map(ByteString::from_bytes)
                               .collect::<Vec<ByteString>>();
            match validx.values.get(&val) {
                None => {
                    let row1 = row.iter().map(|f| Ok(f[]));
                    let row2 = pad2.iter().map(|f| Ok(f[]));
                    if right {
                        try!(self.wtr.write_results(row2.chain(row1)));
                    } else {
                        try!(self.wtr.write_results(row1.chain(row2)));
                    }
                }
                Some(rows) => {
                    for &rowi in rows.iter() {
                        try!(validx.idx.seek(rowi as u64));
                        let row1 = row.iter().map(|f| Ok(f.as_slice()));
                        let row2 = validx.idx.csv().by_ref();
                        if right {
                            try!(self.wtr.write_results(row2.chain(row1)));
                        } else {
                            try!(self.wtr.write_results(row1.chain(row2)));
                        }
                    }
                }
            }
        }
        Ok(())
    }

    fn full_outer_join(mut self) -> CliResult<()> {
        let (pad1, pad2) = try!(self.get_padding());
        let mut validx = try!(ValueIndex::new(self.rdr2, &self.sel2.normal()));

        // Keep track of which rows we've written from rdr2.
        let mut rdr2_written = Vec::from_elem(validx.num_rows, false);
        for row1 in self.rdr1.byte_records() {
            let row1 = try!(row1);

            let val = self.sel1.select(row1[])
                               .map(ByteString::from_bytes)
                               .collect::<Vec<ByteString>>();
            match validx.values.get(&val) {
                None => {
                    let row1 = row1.iter().map(|f| Ok(f[]));
                    let row2 = pad2.iter().map(|f| Ok(f[]));
                    try!(self.wtr.write_results(row1.chain(row2)));
                }
                Some(rows) => {
                    for &rowi in rows.iter() {
                        rdr2_written[rowi] = true;

                        try!(validx.idx.seek(rowi as u64));
                        let row1 = row1.iter().map(|f| Ok(f[]));
                        let row2 = validx.idx.csv().by_ref();
                        try!(self.wtr.write_results(row1.chain(row2)));
                    }
                }
            }
        }

        // OK, now write any row from rdr2 that didn't get joined with a row
        // from rdr1.
        for (i, &written) in rdr2_written.iter().enumerate() {
            if !written {
                try!(validx.idx.seek(i as u64));
                let row1 = pad1.iter().map(|f| Ok(f[]));
                let row2 = validx.idx.csv().by_ref();
                try!(self.wtr.write_results(row1.chain(row2)));
            }
        }
        Ok(())
    }

    fn cross_join(mut self) -> CliResult<()> {
        for row1 in self.rdr1.byte_records() {
            let row1 = try!(row1);

            try!(self.rdr2.seek(0, io::SeekSet));
            let mut first = true;
            while !self.rdr2.done() {
                // Skip the header row. The raw byte interface won't
                // do it for us.
                if first {
                    for f in self.rdr2 { try!(f); }
                    first = false;
                }
                let row1 = row1.iter().map(|f| Ok(f[]));
                let row2 = self.rdr2.by_ref();
                try!(self.wtr.write_results(row1.chain(row2)));
            }
        }
        Ok(())
    }

    fn get_padding(&mut self) -> CliResult<(Vec<ByteString>, Vec<ByteString>)> {
        let len1 = try!(self.rdr1.byte_headers()).len();
        let len2 = try!(self.rdr2.byte_headers()).len();
        let (nada1, nada2) = (util::empty_field(), util::empty_field());
        Ok((Vec::from_elem(len1, nada1), Vec::from_elem(len2, nada2)))
    }
}

impl Args {
    fn new_io_state(&self)
        -> CliResult<IoState<io::File, Box<io::Writer+'static>>> {
        let rconf1 = Config::new(Some(self.arg_input1.clone()))
                            .delimiter(self.flag_delimiter)
                            .no_headers(self.flag_no_headers)
                            .select(self.arg_columns1.clone());
        let rconf2 = Config::new(Some(self.arg_input2.clone()))
                            .delimiter(self.flag_delimiter)
                            .no_headers(self.flag_no_headers)
                            .select(self.arg_columns2.clone());

        let mut rdr1 = try!(rconf1.reader_file());
        let mut rdr2 = try!(rconf2.reader_file());
        let (sel1, sel2) = try!(self.get_selections(&rconf1, &mut rdr1,
                                                    &rconf2, &mut rdr2));
        Ok(IoState {
            wtr: try!(Config::new(self.flag_output.clone()).writer()),
            rdr1: rdr1,
            sel1: sel1,
            rdr2: rdr2,
            sel2: sel2,
            no_headers: self.flag_no_headers,
        })
    }

    fn get_selections<R: Reader>
                     (&self,
                      rconf1: &Config, rdr1: &mut csv::Reader<R>,
                      rconf2: &Config, rdr2: &mut csv::Reader<R>)
                     -> CliResult<(Selection, Selection)> {
        let headers1 = try!(rdr1.byte_headers());
        let headers2 = try!(rdr2.byte_headers());
        let select1 = try!(rconf1.selection(headers1[]));
        let select2 = try!(rconf2.selection(headers2[]));
        if select1.len() != select2.len() {
            return Err(FromError::from_error(format!(
                "Column selections must have the same number of columns, \
                 but found column selections with {} and {} columns.",
                select1.len(), select2.len())));
        }
        Ok((select1, select2))
    }
}

struct ValueIndex<R> {
    // This maps tuples of values to corresponding rows.
    values: HashMap<Vec<ByteString>, Vec<uint>>,
    idx: Indexed<R, io::MemReader>,
    num_rows: uint,
}

impl<R: Reader + Seek> ValueIndex<R> {
    fn new(mut rdr: csv::Reader<R>, nsel: &NormalSelection)
          -> CliResult<ValueIndex<R>> {
        let mut val_idx = HashMap::with_capacity(10000);
        // let mut val_idx = BTreeMap::new();
        let mut rows = io::MemWriter::with_capacity(8 * 10000);
        let mut rowi = 0u;
        try!(rows.write_be_u64(0)); // offset to the first row, which
                                        // has already been read as a header.
        while !rdr.done() {
            // This is a bit hokey. We're doing this manually instead of
            // calling `csv::index::create` so we can create both indexes
            // in one pass.
            try!(rows.write_be_u64(rdr.byte_offset()));

            let fields = try!(nsel.select(unsafe { rdr.byte_fields() })
                                  .map(|v| v.map(ByteString::from_bytes))
                                  .collect::<Result<Vec<_>, _>>());
            match val_idx.entry(fields) {
                Vacant(v) => {
                    let mut rows = Vec::with_capacity(4);
                    rows.push(rowi);
                    v.set(rows);
                }
                Occupied(mut v) => { v.get_mut().push(rowi); }
            }
            rowi += 1;
        }
        Ok(ValueIndex {
            values: val_idx,
            idx: try!(Indexed::new(rdr, io::MemReader::new(rows.unwrap()))),
            num_rows: rowi,
        })
    }
}

impl<R> fmt::Show for ValueIndex<R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Sort the values by order of first appearance.
        let mut kvs = self.values.iter().collect::<Vec<_>>();
        kvs.sort_by(|&(_, v1), &(_, v2)| v1[0].cmp(&v2[0]));
        for (keys, rows) in kvs.into_iter() {
            // This is just for debugging, so assume Unicode for now.
            let keys = keys.iter()
                           .map(|k| String::from_utf8(k[].to_vec()).unwrap())
                           .collect::<Vec<_>>();
            try!(writeln!(f, "({}) => {}", keys.connect(", "), rows))
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::{sys, Token};

use std::fmt;

/// A readiness event.
///
/// `Event` is a readiness state paired with a [`Token`]. It is returned by
/// [`Poll::poll`].
///
/// For more documentation on polling and events, see [`Poll`].
///
/// [`Poll::poll`]: ../struct.Poll.html#method.poll
/// [`Poll`]: ../struct.Poll.html
/// [`Token`]: ../struct.Token.html
#[repr(transparent)]
pub struct Event {
    inner: sys::Event,
}

impl Event {
    /// Returns the event's token.
    pub fn token(&self) -> Token {
        sys::event::token(&self.inner)
    }

    /// Returns true if the event contains readable readiness.
    pub fn is_readable(&self) -> bool {
        sys::event::is_readable(&self.inner)
    }

    /// Returns true if the event contains writable readiness.
    pub fn is_writable(&self) -> bool {
        sys::event::is_writable(&self.inner)
    }

    /// Returns true if the event contains error readiness.
    ///
    /// Error events occur when the socket enters an error state. In this case,
    /// the socket will also receive a readable or writable event. Reading or
    /// writing to the socket will result in an error.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms trigger the
    /// error event.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLERR`      |
    /// | [kqueue]      | `EV_ERROR` and `EV_EOF` with `fflags` set to `0`. |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_error(&self) -> bool {
        sys::event::is_error(&self.inner)
    }

    /// Returns true if the event contains read closed readiness.
    ///
    /// # Notes
    ///
    /// Read closed readiness can be expected after any of the following have
    /// occurred:
    /// * The local stream has shutdown the read half of its socket
    /// * The local stream has shtudown both the read half and the write half
    ///   of its socket
    /// * The peer stream has shtudown the write half its socket; this sends a
    ///   `FIN` packet that has been received by the local stream
    ///
    /// Method is a best effort implementation. While some platforms may not
    /// return readiness when read half is closed, it is guaranteed that
    /// false-positives will not occur.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLHUP`, or  |
    /// |               | `EPOLLIN` and `EPOLLRDHUP` |
    /// | [kqueue]      | `EV_EOF`        |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_read_closed(&self) -> bool {
        sys::event::is_read_closed(&self.inner)
    }

    /// Returns true if the event contains write closed readiness.
    ///
    /// # Notes
    ///
    /// On [epoll] this is essentially a check for `EPOLLHUP` flag as the
    /// local stream shutting down its write half does not trigger this event.
    ///
    /// On [kqueue] the local stream shutting down the write half of its
    /// socket will trigger this event.
    ///
    /// Method is a best effort implementation. While some platforms may not
    /// return readiness when write half is closed, it is guaranteed that
    /// false-positives will not occur.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLHUP`, or  |
    /// |               | `EPOLLOUT` and `EPOLLERR` |
    /// | [kqueue]      | `EV_EOF`        |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_write_closed(&self) -> bool {
        sys::event::is_write_closed(&self.inner)
    }

    /// Returns true if the event contains priority readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms trigger the
    /// priority event.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLPRI`      |
    /// | [kqueue]      | *Not supported* |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    #[inline]
    pub fn is_priority(&self) -> bool {
        sys::event::is_priority(&self.inner)
    }

    /// Returns true if the event contains AIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms support AIO.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | *Not supported* |
    /// | [kqueue]1 | `EVFILT_AIO` |
    ///
    /// 1: Only supported on DragonFly BSD, FreeBSD, iOS and macOS.
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_aio(&self) -> bool {
        sys::event::is_aio(&self.inner)
    }

    /// Returns true if the event contains LIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but only FreeBSD supports LIO. On
    /// FreeBSD this method checks the `EVFILT_LIO` flag.
    pub fn is_lio(&self) -> bool {
        sys::event::is_lio(&self.inner)
    }

    /// Create a reference to an `Event` from a platform specific event.
    pub(crate) fn from_sys_event_ref(sys_event: &sys::Event) -> &Event {
        unsafe {
            // This is safe because the memory layout of `Event` is
            // the same as `sys::Event` due to the `repr(transparent)` attribute.
            &*(sys_event as *const sys::Event as *const Event)
        }
    }
}

/// When the [alternate] flag is enabled this will print platform specific
/// details, for example the fields of the `kevent` structure on platforms that
/// use `kqueue(2)`. Note however that the output of this implementation is
/// **not** consider a part of the stable API.
///
/// [alternate]: fmt::Formatter::alternate
impl fmt::Debug for Event {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let alternate = f.alternate();
        let mut d = f.debug_struct("Event");
        d.field("token", &self.token())
            .field("readable", &self.is_readable())
            .field("writable", &self.is_writable())
            .field("error", &self.is_error())
            .field("read_closed", &self.is_read_closed())
            .field("write_closed", &self.is_write_closed())
            .field("priority", &self.is_priority())
            .field("aio", &self.is_aio())
            .field("lio", &self.is_lio());

        if alternate {
            struct EventDetails<'a>(&'a sys::Event);

            impl<'a> fmt::Debug for EventDetails<'a> {
                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                    sys::event::debug_details(f, self.0)
                }
            }

            d.field("details", &EventDetails(&self.inner)).finish()
        } else {
            d.finish()
        }
    }
}
<commit_msg>Doc that EPOLLERR is chcked in Event::is_write_closed docs<commit_after>use crate::{sys, Token};

use std::fmt;

/// A readiness event.
///
/// `Event` is a readiness state paired with a [`Token`]. It is returned by
/// [`Poll::poll`].
///
/// For more documentation on polling and events, see [`Poll`].
///
/// [`Poll::poll`]: ../struct.Poll.html#method.poll
/// [`Poll`]: ../struct.Poll.html
/// [`Token`]: ../struct.Token.html
#[repr(transparent)]
pub struct Event {
    inner: sys::Event,
}

impl Event {
    /// Returns the event's token.
    pub fn token(&self) -> Token {
        sys::event::token(&self.inner)
    }

    /// Returns true if the event contains readable readiness.
    pub fn is_readable(&self) -> bool {
        sys::event::is_readable(&self.inner)
    }

    /// Returns true if the event contains writable readiness.
    pub fn is_writable(&self) -> bool {
        sys::event::is_writable(&self.inner)
    }

    /// Returns true if the event contains error readiness.
    ///
    /// Error events occur when the socket enters an error state. In this case,
    /// the socket will also receive a readable or writable event. Reading or
    /// writing to the socket will result in an error.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms trigger the
    /// error event.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLERR`      |
    /// | [kqueue]      | `EV_ERROR` and `EV_EOF` with `fflags` set to `0`. |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_error(&self) -> bool {
        sys::event::is_error(&self.inner)
    }

    /// Returns true if the event contains read closed readiness.
    ///
    /// # Notes
    ///
    /// Read closed readiness can be expected after any of the following have
    /// occurred:
    /// * The local stream has shutdown the read half of its socket
    /// * The local stream has shtudown both the read half and the write half
    ///   of its socket
    /// * The peer stream has shtudown the write half its socket; this sends a
    ///   `FIN` packet that has been received by the local stream
    ///
    /// Method is a best effort implementation. While some platforms may not
    /// return readiness when read half is closed, it is guaranteed that
    /// false-positives will not occur.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLHUP`, or  |
    /// |               | `EPOLLIN` and `EPOLLRDHUP` |
    /// | [kqueue]      | `EV_EOF`        |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_read_closed(&self) -> bool {
        sys::event::is_read_closed(&self.inner)
    }

    /// Returns true if the event contains write closed readiness.
    ///
    /// # Notes
    ///
    /// On [epoll] this is essentially a check for `EPOLLHUP` flag as the
    /// local stream shutting down its write half does not trigger this event.
    ///
    /// On [kqueue] the local stream shutting down the write half of its
    /// socket will trigger this event.
    ///
    /// Method is a best effort implementation. While some platforms may not
    /// return readiness when write half is closed, it is guaranteed that
    /// false-positives will not occur.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLHUP`, or  |
    /// |               | only `EPOLLERR`, or |
    /// |               | `EPOLLOUT` and `EPOLLERR` |
    /// | [kqueue]      | `EV_EOF`        |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_write_closed(&self) -> bool {
        sys::event::is_write_closed(&self.inner)
    }

    /// Returns true if the event contains priority readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms trigger the
    /// priority event.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | `EPOLLPRI`      |
    /// | [kqueue]      | *Not supported* |
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    #[inline]
    pub fn is_priority(&self) -> bool {
        sys::event::is_priority(&self.inner)
    }

    /// Returns true if the event contains AIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but not all platforms support AIO.
    ///
    /// The table below shows what flags are checked on what OS.
    ///
    /// | [OS selector] | Flag(s) checked |
    /// |---------------|-----------------|
    /// | [epoll]       | *Not supported* |
    /// | [kqueue]1 | `EVFILT_AIO` |
    ///
    /// 1: Only supported on DragonFly BSD, FreeBSD, iOS and macOS.
    ///
    /// [OS selector]: ../struct.Poll.html#implementation-notes
    /// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
    /// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
    pub fn is_aio(&self) -> bool {
        sys::event::is_aio(&self.inner)
    }

    /// Returns true if the event contains LIO readiness.
    ///
    /// # Notes
    ///
    /// Method is available on all platforms, but only FreeBSD supports LIO. On
    /// FreeBSD this method checks the `EVFILT_LIO` flag.
    pub fn is_lio(&self) -> bool {
        sys::event::is_lio(&self.inner)
    }

    /// Create a reference to an `Event` from a platform specific event.
    pub(crate) fn from_sys_event_ref(sys_event: &sys::Event) -> &Event {
        unsafe {
            // This is safe because the memory layout of `Event` is
            // the same as `sys::Event` due to the `repr(transparent)` attribute.
            &*(sys_event as *const sys::Event as *const Event)
        }
    }
}

/// When the [alternate] flag is enabled this will print platform specific
/// details, for example the fields of the `kevent` structure on platforms that
/// use `kqueue(2)`. Note however that the output of this implementation is
/// **not** consider a part of the stable API.
///
/// [alternate]: fmt::Formatter::alternate
impl fmt::Debug for Event {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let alternate = f.alternate();
        let mut d = f.debug_struct("Event");
        d.field("token", &self.token())
            .field("readable", &self.is_readable())
            .field("writable", &self.is_writable())
            .field("error", &self.is_error())
            .field("read_closed", &self.is_read_closed())
            .field("write_closed", &self.is_write_closed())
            .field("priority", &self.is_priority())
            .field("aio", &self.is_aio())
            .field("lio", &self.is_lio());

        if alternate {
            struct EventDetails<'a>(&'a sys::Event);

            impl<'a> fmt::Debug for EventDetails<'a> {
                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
                    sys::event::debug_details(f, self.0)
                }
            }

            d.field("details", &EventDetails(&self.inner)).finish()
        } else {
            d.finish()
        }
    }
}
<|endoftext|>


<commit_before>use {NeuronId, Num, Timestep};
use std::collections::BinaryHeap;
use std::cmp::Ordering;

#[derive(Debug, Copy, Clone)]
pub struct Event {
    pub at: Timestep,
    pub neuron: NeuronId,
    pub weight: Num,
}

impl PartialEq for Event {
    fn eq(&self, other: &Self) -> bool {
        self.at == other.at && self.neuron == other.neuron
    }
}
impl Eq for Event {}

impl PartialOrd for Event {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.at.partial_cmp(&other.at).map(|o| o.reverse())
    }
}

impl Ord for Event {
    fn cmp(&self, other: &Self) -> Ordering {
        self.at.cmp(&other.at).reverse()
    }
}

pub struct EventQueue {
    heap: BinaryHeap<Event>,
}

impl EventQueue {
    pub fn new() -> Self {
        EventQueue {
            heap: BinaryHeap::new(),
        }
    }

    pub fn push(&mut self, ev: Event) {
        self.heap.push(ev);
    }

    pub fn pop_next_event_at(&mut self, at: Timestep) -> Option<Event> {
        match self.heap.peek() {
            Some(ev) if at >= ev.at => {}
            _ => {
                return None;
            }
        }
        let ev = self.heap.pop().unwrap();
        if at > ev.at {
            panic!("old event found");
        }
        debug_assert!(ev.at == at);
        return Some(ev);
    }
}
<commit_msg>Improve some comments<commit_after>use {NeuronId, Num, Timestep};
use std::collections::BinaryHeap;
use std::cmp::Ordering;

#[derive(Debug, Copy, Clone)]
pub struct Event {
    pub at: Timestep,
    pub neuron: NeuronId,
    pub weight: Num,
}

impl PartialEq for Event {
    fn eq(&self, other: &Self) -> bool {
        self.at == other.at && self.neuron == other.neuron
    }
}
impl Eq for Event {}

impl PartialOrd for Event {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.at.partial_cmp(&other.at).map(|o| o.reverse())
    }
}

impl Ord for Event {
    // The `cmp` function is required for the sort order in the
    // BinaryHeap. As `std::collections::BinaryHeap` implements
    // a max-heap, but we require a min-heap, we have to use the
    // `reverse` ordering here.
    fn cmp(&self, other: &Self) -> Ordering {
        self.at.cmp(&other.at).reverse()
    }
}

pub struct EventQueue {
    heap: BinaryHeap<Event>,
}

impl EventQueue {
    pub fn new() -> Self {
        EventQueue {
            heap: BinaryHeap::new(),
        }
    }

    pub fn push(&mut self, ev: Event) {
        self.heap.push(ev);
    }

    // Returns the next event that has a timestamp of `at`. Returns
    // None, if the event queue is either empty, or if there is no
    // event for timestamp `at`. Panics if the top-most event in the
    // event queue is before `at`.
    pub fn pop_next_event_at(&mut self, at: Timestep) -> Option<Event> {
        match self.heap.peek() {
            Some(ev) if ev.at <= at => {
                // fall-through, because of the borrow checker
            }
            _ => {
                return None;
            }
        }
        let ev = self.heap.pop().unwrap();
        assert!(ev.at == at, "old event found");
        return Some(ev);
    }
}
<|endoftext|>


<commit_before>/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

pub struct Frame {
  pub header: Header,
  pub footer: Footer,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  Independent = 0,
  LeftSide    = 1,
  RightSide   = 2,
  MiddleSide  = 3,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  Frame(u32),
  Sample(u64),
}

#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  pub block_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub channel_assignment: ChannelAssignment,
  pub bits_per_sample: usize,
  pub number: NumberType,
  pub crc: u8,
}

#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<commit_msg>Add documentation for Frame and data types in it<commit_after>/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  pub header: Header,
  pub footer: Footer,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  Independent = 0,
  LeftSide    = 1,
  RightSide   = 2,
  MiddleSide  = 3,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  Frame(u32),
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  pub block_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub channel_assignment: ChannelAssignment,
  pub bits_per_sample: usize,
  pub number: NumberType,
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a CRC-16 value that represents the CRC-16 of everything inside
/// the frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<|endoftext|>