<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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
//
//     http://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.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
#![feature(step_trait)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
#![feature(core_intrinsics)]

extern crate cgmath;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_yaml;
extern crate vte;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod macros;

mod renderer;
pub mod grid;
mod meter;
pub mod config;
mod input;
mod index;
mod event;
mod tty;
pub mod ansi;
mod term;
mod util;
mod sync;

use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicBool, Ordering};

use parking_lot::{Condvar, Mutex, MutexGuard};

use config::Config;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
use sync::PriorityMutex;
use term::Term;
use tty::process_should_exit;
use util::thread;

/// Channel used by resize handling on mac
static mut resize_sender: Option<mpsc::Sender<(u32, u32)>> = None;

#[derive(Clone)]
struct Flag(Arc<AtomicBool>);
impl Flag {
    pub fn new(initial_value: bool) -> Flag {
        Flag(Arc::new(AtomicBool::new(initial_value)))
    }

    #[inline]
    pub fn get(&self) -> bool {
        self.0.load(Ordering::Acquire)
    }

    #[inline]
    pub fn set(&self, value: bool) {
        self.0.store(value, Ordering::Release)
    }
}

/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
    unsafe {
        if let Some(ref tx) = resize_sender {
            let _ = tx.send((width, height));
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

mod gl {
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

fn main() {
    // Load configuration
    let config = match Config::load() {
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => Config::default(),
            // Exit when there's a problem with it
            _ => die!("{}", err),
        },
        Ok(config) => config,
    };

    let font = config.font();
    let dpi = config.dpi();
    let render_timer = config.render_timer();

    let mut window = glutin::WindowBuilder::new()
                                           .with_vsync()
                                           .with_title("Alacritty")
                                           .build().unwrap();

    window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));

    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    let (width, height) = window.get_inner_size_pixels().unwrap();
    let dpr = window.hidpi_factor();

    println!("device_pixel_ratio: {}", dpr);

    let _ = unsafe { window.make_current() };
    unsafe {
        gl::Viewport(0, 0, width as i32, height as i32);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
        gl::Enable(gl::MULTISAMPLE);
    }

    let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);

    // Create renderer
    let mut renderer = QuadRenderer::new(width, height);

    // Initialize glyph cache
    let glyph_cache = {
        println!("Initializing glyph cache");
        let init_start = ::std::time::Instant::now();

        let cache = renderer.with_loader(|mut api| {
            GlyphCache::new(rasterizer, &config, &mut api)
        });

        let stop = init_start.elapsed();
        let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
        println!("Finished initializing glyph cache in {}", stop_f);

        cache
    };

    let metrics = glyph_cache.font_metrics();
    let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
    let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;

    println!("Cell Size: ({} x {})", cell_width, cell_height);

    let terminal = Term::new(width as f32, height as f32, cell_width as f32, cell_height as f32);

    let reader = terminal.tty().reader();
    let mut writer = terminal.tty().writer();

    let (tx, rx) = mpsc::channel();
    unsafe {
        resize_sender = Some(tx.clone());
    }

    let signal_flag = Flag::new(false);

    let terminal = Arc::new(PriorityMutex::new(terminal));
    let window = Arc::new(window);

    let pty_reader = PtyReader::spawn(
        terminal.clone(),
        reader,
        window.create_window_proxy(),
        signal_flag.clone()
    );

    // Wraps a renderer and gives simple draw() api.
    let mut display = Display::new(
        window.clone(),
        terminal.clone(),
        renderer,
        glyph_cache,
        render_timer,
        rx
    );

    // Event processor
    let mut processor = event::Processor::new(&mut writer, terminal.clone(), tx);

    // Main loop
    loop {
        // Wait for something to happen
        processor.process_events(&window);

        signal_flag.set(false);

        // Maybe draw the terminal
        let terminal = terminal.lock_high();
        if terminal.dirty {
            display.draw(terminal);
        }

        if process_should_exit() {
            break;
        }
    }

    // shutdown
    pty_reader.join().ok();
    println!("Goodbye");
}

struct PtyReader;

impl PtyReader {
    pub fn spawn<R>(terminal: Arc<PriorityMutex<Term>>,
                    mut pty: R,
                    proxy: ::glutin::WindowProxy,
                    signal_flag: Flag)
                    -> std::thread::JoinHandle<()>
        where R: std::io::Read + Send + 'static
    {
        thread::spawn_named("pty reader", move || {
            let mut buf = [0u8; 4096];
            let mut pty_parser = ansi::Processor::new();

            loop {
                if let Ok(got) = pty.read(&mut buf[..]) {
                    let mut terminal = terminal.lock_high();

                    for byte in &buf[..got] {
                        pty_parser.advance(&mut *terminal, *byte);
                    }

                    terminal.dirty = true;

                    // Only wake up the event loop if it hasn't already been signaled. This is a
                    // really important optimization because waking up the event loop redundantly
                    // burns *a lot* of cycles.
                    if !signal_flag.get() {
                        proxy.wakeup_event_loop();
                        signal_flag.set(true);
                    }
                } else {
                    break;
                }
            }

            println!("pty reader stopped");
        })
    }
}

struct Display {
    window: Arc<glutin::Window>,
    terminal_mutex: Arc<PriorityMutex<Term>>,
    renderer: QuadRenderer,
    glyph_cache: GlyphCache,
    render_timer: bool,
    rx: mpsc::Receiver<(u32, u32)>,
    meter: Meter,
}

impl Display {
    pub fn new(window: Arc<glutin::Window>,
               terminal_mutex: Arc<PriorityMutex<Term>>,
               renderer: QuadRenderer,
               glyph_cache: GlyphCache,
               render_timer: bool,
               rx: mpsc::Receiver<(u32, u32)>)
               -> Display
    {
        Display {
            window: window,
            terminal_mutex: terminal_mutex,
            renderer: renderer,
            glyph_cache: glyph_cache,
            render_timer: render_timer,
            rx: rx,
            meter: Meter::new(),
        }
    }

    /// Draw the screen
    ///
    /// A reference to Term whose state is being drawn must be provided.
    ///
    /// This call may block if vsync is enabled
    pub fn draw(&mut self, mut terminal: MutexGuard<Term>) {
        terminal.dirty = false;

        // Resize events new_size and are handled outside the poll_events
        // iterator. This has the effect of coalescing multiple resize
        // events into one.
        let mut new_size = None;

        // TODO should be built into renderer
        unsafe {
            gl::ClearColor(0.0, 0.0, 0.00, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }

        // Check for any out-of-band resize events (mac only)
        while let Ok(sz) = self.rx.try_recv() {
            new_size = Some(sz);
        }

        // Receive any resize events; only call gl::Viewport on last
        // available
        if let Some((w, h)) = new_size.take() {
            terminal.resize(w as f32, h as f32);
            self.renderer.resize(w as i32, h as i32);
        }

        {
            let glyph_cache = &mut self.glyph_cache;
            // Draw grid
            {
                let _sampler = self.meter.sampler();

                let size_info = terminal.size_info().clone();
                self.renderer.with_api(&size_info, |mut api| {
                    // Draw the grid
                    api.render_grid(&terminal.render_grid(), glyph_cache);
                });
            }

            // Draw render timer
            if self.render_timer {
                let timing = format!("{:.3} usec", self.meter.average());
                let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
                self.renderer.with_api(terminal.size_info(), |mut api| {
                    api.render_string(&timing[..], glyph_cache, &color);
                });
            }
        }

        // Unlock the terminal mutex
        drop(terminal);
        self.window.swap_buffers().unwrap();
    }
}
<commit_msg>Delete an extra space<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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
//
//     http://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.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
#![feature(step_trait)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
#![feature(core_intrinsics)]

extern crate cgmath;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_yaml;
extern crate vte;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod macros;

mod renderer;
pub mod grid;
mod meter;
pub mod config;
mod input;
mod index;
mod event;
mod tty;
pub mod ansi;
mod term;
mod util;
mod sync;

use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicBool, Ordering};

use parking_lot::{Condvar, Mutex, MutexGuard};

use config::Config;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
use sync::PriorityMutex;
use term::Term;
use tty::process_should_exit;
use util::thread;

/// Channel used by resize handling on mac
static mut resize_sender: Option<mpsc::Sender<(u32, u32)>> = None;

#[derive(Clone)]
struct Flag(Arc<AtomicBool>);
impl Flag {
    pub fn new(initial_value: bool) -> Flag {
        Flag(Arc::new(AtomicBool::new(initial_value)))
    }

    #[inline]
    pub fn get(&self) -> bool {
        self.0.load(Ordering::Acquire)
    }

    #[inline]
    pub fn set(&self, value: bool) {
        self.0.store(value, Ordering::Release)
    }
}

/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
    unsafe {
        if let Some(ref tx) = resize_sender {
            let _ = tx.send((width, height));
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

mod gl {
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

fn main() {
    // Load configuration
    let config = match Config::load() {
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => Config::default(),
            // Exit when there's a problem with it
            _ => die!("{}", err),
        },
        Ok(config) => config,
    };

    let font = config.font();
    let dpi = config.dpi();
    let render_timer = config.render_timer();

    let mut window = glutin::WindowBuilder::new()
                                           .with_vsync()
                                           .with_title("Alacritty")
                                           .build().unwrap();

    window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));

    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    let (width, height) = window.get_inner_size_pixels().unwrap();
    let dpr = window.hidpi_factor();

    println!("device_pixel_ratio: {}", dpr);

    let _ = unsafe { window.make_current() };
    unsafe {
        gl::Viewport(0, 0, width as i32, height as i32);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
        gl::Enable(gl::MULTISAMPLE);
    }

    let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);

    // Create renderer
    let mut renderer = QuadRenderer::new(width, height);

    // Initialize glyph cache
    let glyph_cache = {
        println!("Initializing glyph cache");
        let init_start = ::std::time::Instant::now();

        let cache = renderer.with_loader(|mut api| {
            GlyphCache::new(rasterizer, &config, &mut api)
        });

        let stop = init_start.elapsed();
        let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
        println!("Finished initializing glyph cache in {}", stop_f);

        cache
    };

    let metrics = glyph_cache.font_metrics();
    let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
    let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;

    println!("Cell Size: ({} x {})", cell_width, cell_height);

    let terminal = Term::new(width as f32, height as f32, cell_width as f32, cell_height as f32);

    let reader = terminal.tty().reader();
    let mut writer = terminal.tty().writer();

    let (tx, rx) = mpsc::channel();
    unsafe {
        resize_sender = Some(tx.clone());
    }

    let signal_flag = Flag::new(false);

    let terminal = Arc::new(PriorityMutex::new(terminal));
    let window = Arc::new(window);

    let pty_reader = PtyReader::spawn(
        terminal.clone(),
        reader,
        window.create_window_proxy(),
        signal_flag.clone()
    );

    // Wraps a renderer and gives simple draw() api.
    let mut display = Display::new(
        window.clone(),
        terminal.clone(),
        renderer,
        glyph_cache,
        render_timer,
        rx
    );

    // Event processor
    let mut processor = event::Processor::new(&mut writer, terminal.clone(), tx);

    // Main loop
    loop {
        // Wait for something to happen
        processor.process_events(&window);

        signal_flag.set(false);

        // Maybe draw the terminal
        let terminal = terminal.lock_high();
        if terminal.dirty {
            display.draw(terminal);
        }

        if process_should_exit() {
            break;
        }
    }

    // shutdown
    pty_reader.join().ok();
    println!("Goodbye");
}

struct PtyReader;

impl PtyReader {
    pub fn spawn<R>(terminal: Arc<PriorityMutex<Term>>,
                    mut pty: R,
                    proxy: ::glutin::WindowProxy,
                    signal_flag: Flag)
                    -> std::thread::JoinHandle<()>
        where R: std::io::Read + Send + 'static
    {
        thread::spawn_named("pty reader", move || {
            let mut buf = [0u8; 4096];
            let mut pty_parser = ansi::Processor::new();

            loop {
                if let Ok(got) = pty.read(&mut buf[..]) {
                    let mut terminal = terminal.lock_high();

                    for byte in &buf[..got] {
                        pty_parser.advance(&mut *terminal, *byte);
                    }

                    terminal.dirty = true;

                    // Only wake up the event loop if it hasn't already been signaled. This is a
                    // really important optimization because waking up the event loop redundantly
                    // burns *a lot* of cycles.
                    if !signal_flag.get() {
                        proxy.wakeup_event_loop();
                        signal_flag.set(true);
                    }
                } else {
                    break;
                }
            }

            println!("pty reader stopped");
        })
    }
}

struct Display {
    window: Arc<glutin::Window>,
    terminal_mutex: Arc<PriorityMutex<Term>>,
    renderer: QuadRenderer,
    glyph_cache: GlyphCache,
    render_timer: bool,
    rx: mpsc::Receiver<(u32, u32)>,
    meter: Meter,
}

impl Display {
    pub fn new(window: Arc<glutin::Window>,
               terminal_mutex: Arc<PriorityMutex<Term>>,
               renderer: QuadRenderer,
               glyph_cache: GlyphCache,
               render_timer: bool,
               rx: mpsc::Receiver<(u32, u32)>)
               -> Display
    {
        Display {
            window: window,
            terminal_mutex: terminal_mutex,
            renderer: renderer,
            glyph_cache: glyph_cache,
            render_timer: render_timer,
            rx: rx,
            meter: Meter::new(),
        }
    }

    /// Draw the screen
    ///
    /// A reference to Term whose state is being drawn must be provided.
    ///
    /// This call may block if vsync is enabled
    pub fn draw(&mut self, mut terminal: MutexGuard<Term>) {
        terminal.dirty = false;

        // Resize events new_size and are handled outside the poll_events
        // iterator. This has the effect of coalescing multiple resize
        // events into one.
        let mut new_size = None;

        // TODO should be built into renderer
        unsafe {
            gl::ClearColor(0.0, 0.0, 0.00, 1.0);
            gl::Clear(gl::COLOR_BUFFER_BIT);
        }

        // Check for any out-of-band resize events (mac only)
        while let Ok(sz) = self.rx.try_recv() {
            new_size = Some(sz);
        }

        // Receive any resize events; only call gl::Viewport on last
        // available
        if let Some((w, h)) = new_size.take() {
            terminal.resize(w as f32, h as f32);
            self.renderer.resize(w as i32, h as i32);
        }

        {
            let glyph_cache = &mut self.glyph_cache;
            // Draw grid
            {
                let _sampler = self.meter.sampler();

                let size_info = terminal.size_info().clone();
                self.renderer.with_api(&size_info, |mut api| {
                    // Draw the grid
                    api.render_grid(&terminal.render_grid(), glyph_cache);
                });
            }

            // Draw render timer
            if self.render_timer {
                let timing = format!("{:.3} usec", self.meter.average());
                let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
                self.renderer.with_api(terminal.size_info(), |mut api| {
                    api.render_string(&timing[..], glyph_cache, &color);
                });
            }
        }

        // Unlock the terminal mutex
        drop(terminal);
        self.window.swap_buffers().unwrap();
    }
}
<|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/. */

extern crate "rust-md2" as md2;

use md2::{S, S2, md2_compress};
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};
use std::slice::bytes::copy_memory;

type Collision = Vec<Vec<u8>>;
type Collisions = Vec<Collision>;

fn find_collisions(k: uint) -> Collisions {
    let mut values = create_initial_state(k);
    let mut bytes = Vec::from_elem(k, 0u8);
    let mut collisions: HashMap<Vec<u8>,Collision> = HashMap::new();

    let rows = 16 - k;

    loop {
        copy_memory(values[rows].slice_mut(17, 17 + k), bytes.as_slice());
        copy_memory(values[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

        for row in range(rows + 1, 18) {
            // Fill row.
            for i in range(1, 49) {
                values[row][i] = S[values[row][i - 1] as uint] ^ values[row - 1][i];
            }

            // Next t value.
            values[row + 1][0] = values[row][48] + (row as u8) - 1;
        }

        let key = Vec::from_fn(17 - rows, |row| values[rows + 2 + row][0]);

        match collisions.entry(key) {
            Vacant(entry) => { entry.set(vec!(bytes.clone())); },
            Occupied(mut entry) => { entry.get_mut().push(bytes.clone()); }
        };

        if !increase(bytes.as_mut_slice()) {
            break;
        }
    }

    // Compute original messages for each collision.
    collisions.values().filter(|x| x.len() > 1).map(|collision| {
        collision.iter().map(|bytes| {
            copy_memory(values[rows].slice_mut(17, 17 + k), bytes.as_slice());
            copy_memory(values[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

            // Fill upper rectangles.
            for row in range(1, rows + 1).rev() {
                for col in range(17, 32 - row + 2) {
                    values[row - 1][col] = S[values[row][col - 1] as uint] ^ values[row][col];
                }
            }

            values[0].slice(17, 33).to_vec()
        }).collect()
    }).collect()
}

fn create_initial_state(k: uint) -> [[u8, ..49], ..19] {
    let rows = 16 - k;
    let mut values = [[0u8, ..49], ..19];

    for row in range(1, rows + 1) {
        // Fill row of T1.
        for i in range(1, 17) {
            values[row][i] = S[values[row][i - 1] as uint] ^ values[row - 1][i];
        }

        // Last bytes are equal.
        values[row][32] = values[row][16];
        values[row][48] = values[row][16];

        // Next t value.
        values[row + 1][0] = values[row][48] + (row as u8) - 1;
    }

    // Compute triangles.
    for col in range(0, rows) {
        for row in range(2 + col, rows + 1).rev() {
            values[row][32 - col - 1] = S2[(values[row][32 - col] ^ values[row - 1][32 - col]) as uint];
            values[row][48 - col - 1] = S2[(values[row][48 - col] ^ values[row - 1][48 - col]) as uint];
        }
    }

    values
}

fn increase(num: &mut [u8]) -> bool {
    let len = num.len();

    for i in range(0, len).rev() {
        if num[i] == 255 {
            continue;
        }

        num[i] += 1;

        for i in range(i + 1, len) {
            num[i] = 0;
        }

        return true;
    }

    false
}

fn check_collisions(collisions: &Collisions) -> bool {
    let empty = [0u8, ..16];

    for collision in collisions.iter() {
        let mut first_hash: Option<[u8, ..16]> = None;

        for msg in collision.iter() {
            let md2 = md2_compress(&empty, msg.as_slice());

            match first_hash {
                Some(hash) => {
                    if hash != md2 {
                        return false;
                    }
                }
                None => first_hash = Some(md2)
            };
        }
    }

    true
}

fn main() {
    let collisions = find_collisions(2);
    if !check_collisions(&collisions) {
        panic!("invalid collision found :(");
    }

    println!("Found {} collisions.", collisions.iter().count());
}
<commit_msg>add some comments<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/. */

extern crate "rust-md2" as md2;

use md2::{S, S2, md2_compress};
use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};
use std::slice::bytes::copy_memory;

type Collision = Vec<Vec<u8>>;
type Collisions = Vec<Collision>;

fn find_collisions(k: uint) -> Collisions {
    let mut values = create_initial_state(k);
    let mut bytes = Vec::from_elem(k, 0u8);
    let mut collisions: HashMap<Vec<u8>,Collision> = HashMap::new();

    let rows = 16 - k;

    loop {
        copy_memory(values[rows].slice_mut(17, 17 + k), bytes.as_slice());
        copy_memory(values[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

        for row in range(rows + 1, 18) {
            // Fill row.
            for i in range(1, 49) {
                values[row][i] = S[values[row][i - 1] as uint] ^ values[row - 1][i];
            }

            // Next t value.
            values[row + 1][0] = values[row][48] + (row as u8) - 1;
        }

        let key = Vec::from_fn(17 - rows, |row| values[rows + 2 + row][0]);

        match collisions.entry(key) {
            Vacant(entry) => { entry.set(vec!(bytes.clone())); },
            Occupied(mut entry) => { entry.get_mut().push(bytes.clone()); }
        };

        if !increase(bytes.as_mut_slice()) {
            break;
        }
    }

    // Compute original messages for each collision.
    collisions.values().filter(|x| x.len() > 1).map(|collision| {
        collision.iter().map(|bytes| {
            copy_memory(values[rows].slice_mut(17, 17 + k), bytes.as_slice());
            copy_memory(values[rows].slice_mut(17 + 16, 17 + k + 16), bytes.as_slice());

            // Fill upper rectangles.
            for row in range(1, rows + 1).rev() {
                for col in range(17, 32 - row + 2) {
                    values[row - 1][col] = S[values[row][col - 1] as uint] ^ values[row][col];
                }
            }

            values[0].slice(17, 33).to_vec()
        }).collect()
    }).collect()
}

fn create_initial_state(k: uint) -> [[u8, ..49], ..19] {
    let rows = 16 - k;
    let mut values = [[0u8, ..49], ..19];

    for row in range(1, rows + 1) {
        // Fill row of T1.
        for i in range(1, 17) {
            values[row][i] = S[values[row][i - 1] as uint] ^ values[row - 1][i];
        }

        // Last bytes are equal.
        values[row][32] = values[row][16];
        values[row][48] = values[row][16];

        // Next t value.
        values[row + 1][0] = values[row][48] + (row as u8) - 1;
    }

    // Compute triangles.
    for col in range(0, rows) {
        for row in range(2 + col, rows + 1).rev() {
            values[row][32 - col - 1] = S2[(values[row][32 - col] ^ values[row - 1][32 - col]) as uint];
            values[row][48 - col - 1] = S2[(values[row][48 - col] ^ values[row - 1][48 - col]) as uint];
        }
    }

    values
}

fn increase(num: &mut [u8]) -> bool {
    let len = num.len();

    // Iterate bytes starting from the end.
    for i in range(0, len).rev() {
        if num[i] == 255 {
            continue;
        }

        // Increase.
        num[i] += 1;

        // Set all bytes right of the current index to zero.
        for i in range(i + 1, len) {
            num[i] = 0;
        }

        return true;
    }

    // Increasing any further would overflow.
    false
}

fn check_collisions(collisions: &Collisions) -> bool {
    let empty = [0u8, ..16];

    for collision in collisions.iter() {
        let mut first_hash: Option<[u8, ..16]> = None;

        for msg in collision.iter() {
            let md2 = md2_compress(&empty, msg.as_slice());

            match first_hash {
                Some(hash) => {
                    if hash != md2 {
                        return false;
                    }
                }
                None => first_hash = Some(md2)
            };
        }
    }

    true
}

fn main() {
    let collisions = find_collisions(2);
    if !check_collisions(&collisions) {
        panic!("invalid collision found :(");
    }

    println!("Found {} collisions.", collisions.iter().count());
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]

// SymbolExpr
// abstract over Argument

extern crate turtle;
extern crate lindenmayer_system;

use std::fmt;
use std::fs::File;
use turtle::{Canvas, Turtle};
use lindenmayer_system::{Alphabet, SymbolString, Symbol, Expr, System, NumType, ParameterizedSymbol, Condition, Rule, Argument};

#[derive(Eq, PartialEq, Clone)]
struct Character(char);
impl Alphabet for Character {}

impl fmt::Debug for Character {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

fn symstr_from_str(s: &str) -> SymbolString<Character> {
    SymbolString(s.chars()
                  .filter(|&c| !c.is_whitespace())
                  .map(|c| Symbol::new(Character(c)))
                  .collect())
}

fn production_from_str(s: &str) -> Vec<ParameterizedSymbol<Character>> {
    s.chars()
     .filter(|&c| !c.is_whitespace())
     .map(|c| ParameterizedSymbol::new(Character(c)))
     .collect()
}

/// A simple rule does not have any conditions nor parameters.
fn simple_rule(c: char, production: &str) -> Rule<Character> {
    Rule {
        character: Character(c),
        arity: 0,
        condition: Condition::True,
        production: production_from_str(production),
    }
}

fn psym(c: char, exprs: Vec<Expr>) -> ParameterizedSymbol<Character> {
    ParameterizedSymbol {
        character: Character(c),
        expressions: exprs
    }
}

fn draw(symstr: &SymbolString<Character>,
        init_direction: f32,
        angle: f32,
        distance: f32,
        filename: &str) {
    let mut t = Canvas::new();
    t.right(init_direction);
    for sym in symstr.0.iter() {
        match sym.character.0 {
            'F' => t.forward(distance),
            'f' => t.move_forward(distance),
            '+' => t.left(angle),
            '-' => t.right(angle),
            '[' => t.push(),
            ']' => t.pop(),
            _ => {}
        }
    }
    t.save_svg(&mut File::create(filename.to_string() + ".svg").unwrap()).unwrap();
    t.save_eps(&mut File::create(filename.to_string() + ".eps").unwrap()).unwrap();
}

fn draw_parametric(symstr: &SymbolString<Character>,
        default_angle: f32,
        filename: &str) {
    let mut t = Canvas::new();
    for sym in symstr.0.iter() {
        match (sym.character.0, sym.arguments.get(0).map(|a| a.0)) {
            ('F', Some(d)) => t.forward(d),
            ('f', Some(d)) => t.move_forward(d),
            ('+', Some(a)) => t.rotate(a),
            ('+', None) => t.rotate(default_angle),
            ('-', Some(a)) => t.rotate(-a),
            ('-', None) => t.rotate(-default_angle),
            ('[', None) => t.push(),
            (']', None) => t.pop(),
            _ => {}
        }
    }
    t.save_svg(&mut File::create(filename.to_string() + ".svg").unwrap()).unwrap();
    t.save_eps(&mut File::create(filename.to_string() + ".eps").unwrap()).unwrap();
}

fn koch_curve(maxiter: usize) {
    let axiom = symstr_from_str("F++F++F");

    let system = System { rules: vec![simple_rule('F', "F-F++F-F")] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, -90.0, 60.0, 10.0, &format!("koch_{:02}", iters));
}

fn dragon_curve(maxiter: usize) {
    let axiom = symstr_from_str("FX");

    let system = System { rules: vec![simple_rule('X', "X+YF+"), simple_rule('Y', "-FX-Y")] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 90.0, 10.0, &format!("dragon_{:02}", iters));
}

fn sierpinski_triangle(maxiter: usize) {
    let axiom = symstr_from_str("A");

    let system = System { rules: vec![simple_rule('A', "+B-A-B+"), simple_rule('B', "-A+B+A-")] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    // replace A and B with F
    let system = System {
        rules: vec![
        simple_rule('A', "F"),
        simple_rule('B', "F"),
        ],
    };
    let (after, _iters) = system.develop1(&after);

    draw(&after,
         -90.0,
         60.0,
         10.0,
         &format!("sierpinski_{:02}", iters));
}

fn fractal_plant(maxiter: usize) {
    let axiom = symstr_from_str("X");

    let system = System {
        rules: vec![
        simple_rule('X', "F-[[X]+X]+F[+FX]-X"),
        simple_rule('F', "FF"),
        ],
    };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 25.0, 10.0, &format!("plant_{:02}", iters));
}

fn branching_pattern_abop_1_9(maxiter: usize) {
    const R: NumType = 1.456;
    let axiom = SymbolString(vec![Symbol{character: Character('A'), arguments: vec![Argument(300.0)]}]);

    let system = System {
        rules: vec![
                Rule{
                        character: Character('A'),
                        arity: 1,
                        condition: Condition::True,
                        production: vec![
                                psym('F', vec![Expr::Arg(0)]),
                                psym('[', vec![]),
                                psym('+', vec![]),
                                psym('A', vec![Expr::Div(box Expr::Arg(0), box Expr::Const(R))]),
                                psym(']', vec![]),
                                psym('[', vec![]),
                                psym('-', vec![]),
                                psym('A', vec![Expr::Div(box Expr::Arg(0), box Expr::Const(R))]),
                                psym(']', vec![]),
                        ]
                }
        ]
    };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw_parametric(&after, 85.0, &format!("branching_abop_1_9_{:02}", iters));
}

fn main() {
    for i in 0..7 {
        koch_curve(i);
    }
    for i in 0..16 {
        dragon_curve(i);
    }
    for i in 0..10 {
        sierpinski_triangle(i);
    }
    for i in 4..8 {
        fractal_plant(i);
    }
    branching_pattern_abop_1_9(15);
}
<commit_msg>rustfmt<commit_after>#![feature(box_syntax)]

// SymbolExpr
// abstract over Argument

extern crate turtle;
extern crate lindenmayer_system;

use std::fmt;
use std::fs::File;
use turtle::{Canvas, Turtle};
use lindenmayer_system::{Alphabet, SymbolString, Symbol, Expr, System, NumType,
                         ParameterizedSymbol, Condition, Rule, Argument};

#[derive(Eq, PartialEq, Clone)]
struct Character(char);
impl Alphabet for Character {}

impl fmt::Debug for Character {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

fn symstr_from_str(s: &str) -> SymbolString<Character> {
    SymbolString(s.chars()
                  .filter(|&c| !c.is_whitespace())
                  .map(|c| Symbol::new(Character(c)))
                  .collect())
}

fn production_from_str(s: &str) -> Vec<ParameterizedSymbol<Character>> {
    s.chars()
     .filter(|&c| !c.is_whitespace())
     .map(|c| ParameterizedSymbol::new(Character(c)))
     .collect()
}

/// A simple rule does not have any conditions nor parameters.
fn simple_rule(c: char, production: &str) -> Rule<Character> {
    Rule {
        character: Character(c),
        arity: 0,
        condition: Condition::True,
        production: production_from_str(production),
    }
}

fn psym(c: char, exprs: Vec<Expr>) -> ParameterizedSymbol<Character> {
    ParameterizedSymbol {
        character: Character(c),
        expressions: exprs,
    }
}

fn draw(symstr: &SymbolString<Character>,
        init_direction: f32,
        angle: f32,
        distance: f32,
        filename: &str) {
    let mut t = Canvas::new();
    t.right(init_direction);
    for sym in symstr.0.iter() {
        match sym.character.0 {
            'F' => t.forward(distance),
            'f' => t.move_forward(distance),
            '+' => t.left(angle),
            '-' => t.right(angle),
            '[' => t.push(),
            ']' => t.pop(),
            _ => {}
        }
    }
    t.save_svg(&mut File::create(filename.to_string() + ".svg").unwrap()).unwrap();
    t.save_eps(&mut File::create(filename.to_string() + ".eps").unwrap()).unwrap();
}

fn draw_parametric(symstr: &SymbolString<Character>, default_angle: f32, filename: &str) {
    let mut t = Canvas::new();
    for sym in symstr.0.iter() {
        match (sym.character.0, sym.arguments.get(0).map(|a| a.0)) {
            ('F', Some(d)) => t.forward(d),
            ('f', Some(d)) => t.move_forward(d),
            ('+', Some(a)) => t.rotate(a),
            ('+', None) => t.rotate(default_angle),
            ('-', Some(a)) => t.rotate(-a),
            ('-', None) => t.rotate(-default_angle),
            ('[', None) => t.push(),
            (']', None) => t.pop(),
            _ => {}
        }
    }
    t.save_svg(&mut File::create(filename.to_string() + ".svg").unwrap()).unwrap();
    t.save_eps(&mut File::create(filename.to_string() + ".eps").unwrap()).unwrap();
}

fn koch_curve(maxiter: usize) {
    let axiom = symstr_from_str("F++F++F");

    let system = System { rules: vec![simple_rule('F', "F-F++F-F")] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, -90.0, 60.0, 10.0, &format!("koch_{:02}", iters));
}

fn dragon_curve(maxiter: usize) {
    let axiom = symstr_from_str("FX");

    let system = System { rules: vec![simple_rule('X', "X+YF+"), simple_rule('Y', "-FX-Y")] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 90.0, 10.0, &format!("dragon_{:02}", iters));
}

fn sierpinski_triangle(maxiter: usize) {
    let axiom = symstr_from_str("A");

    let system = System { rules: vec![simple_rule('A', "+B-A-B+"), simple_rule('B', "-A+B+A-")] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    // replace A and B with F
    let system = System {
        rules: vec![
        simple_rule('A', "F"),
        simple_rule('B', "F"),
        ],
    };
    let (after, _iters) = system.develop1(&after);

    draw(&after,
         -90.0,
         60.0,
         10.0,
         &format!("sierpinski_{:02}", iters));
}

fn fractal_plant(maxiter: usize) {
    let axiom = symstr_from_str("X");

    let system = System {
        rules: vec![
        simple_rule('X', "F-[[X]+X]+F[+FX]-X"),
        simple_rule('F', "FF"),
        ],
    };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 25.0, 10.0, &format!("plant_{:02}", iters));
}

fn branching_pattern_abop_1_9(maxiter: usize) {
    const R: NumType = 1.456;
    let axiom = SymbolString(vec![Symbol {
                                      character: Character('A'),
                                      arguments: vec![Argument(300.0)],
                                  }]);

    let system = System {
        rules: vec![Rule {
                        character: Character('A'),
                        arity: 1,
                        condition: Condition::True,
                        production: vec![
                                psym('F', vec![Expr::Arg(0)]),
                                psym('[', vec![]),
                                psym('+', vec![]),
                                psym('A', vec![Expr::Div(box Expr::Arg(0), box Expr::Const(R))]),
                                psym(']', vec![]),
                                psym('[', vec![]),
                                psym('-', vec![]),
                                psym('A', vec![Expr::Div(box Expr::Arg(0), box Expr::Const(R))]),
                                psym(']', vec![]),
                        ],
                    }],
    };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw_parametric(&after, 85.0, &format!("branching_abop_1_9_{:02}", iters));
}

fn main() {
    for i in 0..7 {
        koch_curve(i);
    }
    for i in 0..16 {
        dragon_curve(i);
    }
    for i in 0..10 {
        sierpinski_triangle(i);
    }
    for i in 4..8 {
        fractal_plant(i);
    }
    branching_pattern_abop_1_9(15);
}
<|endoftext|>


<commit_before>use std::{env, fs, io};
use tokio::sync::mpsc::{channel, Sender};

#[macro_use]
mod player_output;
#[macro_use]
mod serializable_flags;
#[macro_use]
mod game_object;

macro_rules! unwrap_or_continue {
    ($expr:expr) => {
        match $expr {
            Some(value) => value,
            None => continue,
        }
    };
}

macro_rules! unwrap_or_return_value {
    ($expr:expr, $ret:expr) => {
        match $expr {
            Some(value) => value,
            None => return $ret,
        }
    };
}

mod actions;
mod character_stats;
mod colors;
mod commands;
mod direction_utils;
mod events;
mod logs;
mod number_utils;
mod objects;
mod persistence_handler;
mod point3d;
mod relative_direction;
mod sessions;
mod telnet_server;
mod text_utils;
mod trigger_registry;
mod vector3d;
mod vector_utils;
mod vision_utils;
mod web_server;

use actions::{enter_room, look_at_object};
use commands::CommandExecutor;
use game_object::{hydrate, Character, GameObject, GameObjectId, GameObjectRef};
use logs::{log_command, log_session_event, LogMessage, LogSender};
use objects::Realm;
use persistence_handler::PersistenceRequest;
use player_output::PlayerOutput;
use sessions::{
    process_input, SessionEvent, SessionInputEvent, SessionOutput, SessionPromptInfo, SessionState,
    SignInState,
};
use trigger_registry::TriggerRegistry;

#[tokio::main]
async fn main() {
    let data_dir = env::var("PT_DATA_DIR").unwrap_or_else(|_| "data/".to_owned());
    let log_dir = env::var("PT_LOG_DIR").unwrap_or_else(|_| "logs/".to_owned());

    let telnet_port = env::var("PT_TELNET_PORT")
        .unwrap_or_else(|_| "".to_string())
        .parse()
        .unwrap_or(4801);
    let http_port = env::var("PT_HTTP_PORT")
        .unwrap_or_else(|_| "".to_string())
        .parse()
        .unwrap_or(8080);

    let mut realm = match load_data(&data_dir) {
        Ok(realm) => realm,
        Err(error) => panic!("Failed to load data from \"{}\": {}", data_dir, error),
    };

    let trigger_registry = TriggerRegistry::new();

    let (input_tx, mut input_rx) = channel::<SessionInputEvent>(64);
    let (persist_tx, persist_rx) = channel::<PersistenceRequest>(64);
    let (session_tx, session_rx) = channel::<SessionEvent>(64);
    let (log_tx, log_rx) = channel::<Box<dyn LogMessage>>(64);

    logs::create_log_handler(log_dir, log_rx);
    persistence_handler::create_persistence_handler(data_dir, persist_rx);
    sessions::create_sessions_handler(input_tx, log_tx.clone(), session_rx);

    telnet_server::serve(telnet_port, session_tx.clone());
    web_server::serve(realm.name().to_owned(), http_port, session_tx.clone());

    let command_executor = CommandExecutor::new();
    while let Some(SessionInputEvent {
        input,
        session_id,
        session_state,
        source,
    }) = input_rx.recv().await
    {
        match session_state {
            SessionState::SigningIn(state) => {
                let input_ev = (input, session_id, source, state);
                process_signing_in_input(&mut realm, &session_tx, &log_tx, input_ev).await;
            }
            SessionState::SignedIn(player_id) => {
                let input_ev = (input, session_id, source, player_id);
                process_signed_in_input(
                    &mut realm,
                    &command_executor,
                    &session_tx,
                    &trigger_registry,
                    &log_tx,
                    input_ev,
                )
                .await;
            }
            SessionState::SessionClosed(player_id) => {
                process_session_closed(&mut realm, &log_tx, player_id).await;
            }
        }

        let persistence_requests = realm.take_persistence_requests();
        for request in persistence_requests {
            if let Err(error) = persist_tx.send(request).await {
                panic!("Couldn't send persistence request: {:?}", error);
            }
        }
    }
}

fn load_data(data_dir: &str) -> Result<Realm, io::Error> {
    let content = fs::read_to_string(&format!("{}/realm.000000000", data_dir))?;
    let mut realm = Realm::hydrate(0, &content)
        .unwrap_or_else(|message| panic!("Failed to load realm: {}", message));

    let entries = fs::read_dir(&data_dir)?
        .map(|entry| entry.map(|entry| (entry.file_name(), entry.path())))
        .collect::<Result<Vec<_>, io::Error>>()?;
    let mut character_refs = Vec::new();
    for (file_name, path) in entries {
        if let Some(object_ref) = GameObjectRef::from_file_name(&file_name) {
            let content = fs::read_to_string(&path)?;
            match hydrate(object_ref, &content) {
                Ok(object) => {
                    if object.as_character().is_some() {
                        character_refs.push(object_ref);
                    }

                    realm.set(object_ref, object);
                }
                Err(message) => println!(
                    "error loading {} {}: {}",
                    object_ref.object_type(),
                    object_ref.id(),
                    message
                ),
            }
        }
    }

    inject_characters_into_rooms(&mut realm, character_refs);
    Ok(realm)
}

// Characters get injected on load, so that rooms don't need to be re-persisted every time a
// character moves from one room to another.
fn inject_characters_into_rooms(realm: &mut Realm, character_refs: Vec<GameObjectRef>) {
    for character_ref in character_refs {
        let maybe_current_room = realm
            .character(character_ref)
            .map(|character| character.current_room());
        if let Some(room) = maybe_current_room.and_then(|room_ref| realm.room_mut(room_ref)) {
            room.add_characters(vec![character_ref]);
        } else {
            println!(
                "Character {:?} has no room {:?}",
                character_ref, maybe_current_room
            );
        }
    }
}

async fn process_signing_in_input(
    realm: &mut Realm,
    session_tx: &Sender<SessionEvent>,
    log_tx: &LogSender,
    (input, session_id, source, state): (String, u64, String, SignInState),
) {
    let (new_state, output, log_messages) = process_input(&state, realm, &source, input);

    send_session_event(session_tx, SessionEvent::SessionOutput(session_id, output)).await;
    for message in log_messages {
        log_session_event(log_tx, source.clone(), message).await;
    }

    let session_state = if new_state.is_session_closed() {
        SessionState::SessionClosed(None)
    } else if let Some(user_name) = new_state.get_sign_in_user_name() {
        if let Some(sign_up_data) = new_state.get_sign_up_data() {
            realm.add_player(sign_up_data);
            log_session_event(
                log_tx,
                source.clone(),
                format!("Character created for player \"{}\"", user_name),
            )
            .await;
        }

        if let Some(player) = realm.player_by_name_mut(user_name) {
            if let Some(existing_session_id) = player.session_id() {
                send_session_event(
                    session_tx,
                    SessionEvent::SessionUpdate(
                        existing_session_id,
                        SessionState::SessionClosed(Some(player.id())),
                    ),
                )
                .await;
            }

            let player_ref = player.object_ref();
            let current_room = player.current_room();
            player.set_session_id(Some(session_id));

            log_command(log_tx, user_name.to_owned(), "(signed in)".to_owned()).await;

            let output = match enter_room(realm, player_ref, current_room) {
                Ok(mut output) => {
                    if let Ok(mut more) = look_at_object(realm, player_ref, current_room) {
                        output.append(&mut more);
                    }
                    output
                }
                Err(message) => vec![PlayerOutput::new_from_string(player_ref.id(), message)],
            };
            process_player_output(realm, session_tx, output).await;

            SessionState::SignedIn(player_ref.id())
        } else {
            log_session_event(
                log_tx,
                source,
                format!("Could not determine ID for player \"{}\"", user_name),
            )
            .await;
            SessionState::SessionClosed(None)
        }
    } else {
        SessionState::SigningIn(new_state)
    };

    send_session_event(
        session_tx,
        SessionEvent::SessionUpdate(session_id, session_state),
    )
    .await;
}

async fn process_signed_in_input(
    realm: &mut Realm,
    command_executor: &CommandExecutor,
    session_tx: &Sender<SessionEvent>,
    trigger_registry: &TriggerRegistry,
    log_tx: &LogSender,
    (input, session_id, source, player_id): (String, u64, String, GameObjectId),
) {
    if let Some(player) = realm.player_by_id(player_id) {
        log_command(log_tx, player.name().to_owned(), input.clone()).await;
        let player_ref = player.object_ref();
        let player_output =
            command_executor.execute_command(realm, trigger_registry, player_ref, log_tx, input);
        process_player_output(realm, session_tx, player_output).await;
    } else {
        log_session_event(log_tx, source, format!("Player {} deleted", player_id)).await;
        send_session_event(
            session_tx,
            SessionEvent::SessionUpdate(session_id, SessionState::SessionClosed(Some(player_id))),
        )
        .await;
    }
}

async fn process_player_output(
    realm: &Realm,
    session_tx: &Sender<SessionEvent>,
    player_output: Vec<PlayerOutput>,
) {
    for output in player_output {
        if let Some(player) = realm.player_by_id(output.player_id) {
            if let Some(session_id) = player.session_id() {
                send_session_event(
                    session_tx,
                    SessionEvent::SessionOutput(
                        session_id,
                        match output.output {
                            SessionOutput::Json(json) => SessionOutput::Json(json),
                            output => output.with(SessionOutput::Prompt(SessionPromptInfo {
                                name: player.name().to_owned(),
                                is_admin: player.is_admin(),
                                hp: player.hp(),
                                max_hp: player.stats().max_hp(),
                                mp: player.mp(),
                                max_mp: player.stats().max_mp(),
                            })),
                        },
                    ),
                )
                .await;
            }
        }
    }
}

async fn process_session_closed(
    realm: &mut Realm,
    log_tx: &LogSender,
    player_id: Option<GameObjectId>,
) {
    if let Some(player_id) = player_id {
        if let Some(player) = realm.player_by_id_mut(player_id) {
            player.set_session_id(None);

            let player_name = player.name().to_owned();
            log_command(log_tx, player_name, "(session closed)".to_owned()).await;
        }
    }
}

async fn send_session_event(session_tx: &Sender<SessionEvent>, event: SessionEvent) {
    if let Err(error) = session_tx.send(event).await {
        println!("Could not send session event: {:?}", error);
    }
}
<commit_msg>Aggregate session output before sending to avoid unnecessary prompts<commit_after>use std::{collections::BTreeMap, env, fs, io};
use tokio::sync::mpsc::{channel, Sender};

#[macro_use]
mod player_output;
#[macro_use]
mod serializable_flags;
#[macro_use]
mod game_object;

macro_rules! unwrap_or_continue {
    ($expr:expr) => {
        match $expr {
            Some(value) => value,
            None => continue,
        }
    };
}

macro_rules! unwrap_or_return_value {
    ($expr:expr, $ret:expr) => {
        match $expr {
            Some(value) => value,
            None => return $ret,
        }
    };
}

mod actions;
mod character_stats;
mod colors;
mod commands;
mod direction_utils;
mod events;
mod logs;
mod number_utils;
mod objects;
mod persistence_handler;
mod point3d;
mod relative_direction;
mod sessions;
mod telnet_server;
mod text_utils;
mod trigger_registry;
mod vector3d;
mod vector_utils;
mod vision_utils;
mod web_server;

use actions::{enter_room, look_at_object};
use commands::CommandExecutor;
use game_object::{hydrate, Character, GameObject, GameObjectId, GameObjectRef};
use logs::{log_command, log_session_event, LogMessage, LogSender};
use objects::Realm;
use persistence_handler::PersistenceRequest;
use player_output::PlayerOutput;
use sessions::{
    process_input, SessionEvent, SessionInputEvent, SessionOutput, SessionPromptInfo, SessionState,
    SignInState,
};
use trigger_registry::TriggerRegistry;

#[tokio::main]
async fn main() {
    let data_dir = env::var("PT_DATA_DIR").unwrap_or_else(|_| "data/".to_owned());
    let log_dir = env::var("PT_LOG_DIR").unwrap_or_else(|_| "logs/".to_owned());

    let telnet_port = env::var("PT_TELNET_PORT")
        .unwrap_or_else(|_| "".to_string())
        .parse()
        .unwrap_or(4801);
    let http_port = env::var("PT_HTTP_PORT")
        .unwrap_or_else(|_| "".to_string())
        .parse()
        .unwrap_or(8080);

    let mut realm = match load_data(&data_dir) {
        Ok(realm) => realm,
        Err(error) => panic!("Failed to load data from \"{}\": {}", data_dir, error),
    };

    let trigger_registry = TriggerRegistry::new();

    let (input_tx, mut input_rx) = channel::<SessionInputEvent>(64);
    let (persist_tx, persist_rx) = channel::<PersistenceRequest>(64);
    let (session_tx, session_rx) = channel::<SessionEvent>(64);
    let (log_tx, log_rx) = channel::<Box<dyn LogMessage>>(64);

    logs::create_log_handler(log_dir, log_rx);
    persistence_handler::create_persistence_handler(data_dir, persist_rx);
    sessions::create_sessions_handler(input_tx, log_tx.clone(), session_rx);

    telnet_server::serve(telnet_port, session_tx.clone());
    web_server::serve(realm.name().to_owned(), http_port, session_tx.clone());

    let command_executor = CommandExecutor::new();
    while let Some(SessionInputEvent {
        input,
        session_id,
        session_state,
        source,
    }) = input_rx.recv().await
    {
        match session_state {
            SessionState::SigningIn(state) => {
                let input_ev = (input, session_id, source, state);
                process_signing_in_input(&mut realm, &session_tx, &log_tx, input_ev).await;
            }
            SessionState::SignedIn(player_id) => {
                let input_ev = (input, session_id, source, player_id);
                process_signed_in_input(
                    &mut realm,
                    &command_executor,
                    &session_tx,
                    &trigger_registry,
                    &log_tx,
                    input_ev,
                )
                .await;
            }
            SessionState::SessionClosed(player_id) => {
                process_session_closed(&mut realm, &log_tx, player_id).await;
            }
        }

        let persistence_requests = realm.take_persistence_requests();
        for request in persistence_requests {
            if let Err(error) = persist_tx.send(request).await {
                panic!("Couldn't send persistence request: {:?}", error);
            }
        }
    }
}

fn load_data(data_dir: &str) -> Result<Realm, io::Error> {
    let content = fs::read_to_string(&format!("{}/realm.000000000", data_dir))?;
    let mut realm = Realm::hydrate(0, &content)
        .unwrap_or_else(|message| panic!("Failed to load realm: {}", message));

    let entries = fs::read_dir(&data_dir)?
        .map(|entry| entry.map(|entry| (entry.file_name(), entry.path())))
        .collect::<Result<Vec<_>, io::Error>>()?;
    let mut character_refs = Vec::new();
    for (file_name, path) in entries {
        if let Some(object_ref) = GameObjectRef::from_file_name(&file_name) {
            let content = fs::read_to_string(&path)?;
            match hydrate(object_ref, &content) {
                Ok(object) => {
                    if object.as_character().is_some() {
                        character_refs.push(object_ref);
                    }

                    realm.set(object_ref, object);
                }
                Err(message) => println!(
                    "error loading {} {}: {}",
                    object_ref.object_type(),
                    object_ref.id(),
                    message
                ),
            }
        }
    }

    inject_characters_into_rooms(&mut realm, character_refs);
    Ok(realm)
}

// Characters get injected on load, so that rooms don't need to be re-persisted every time a
// character moves from one room to another.
fn inject_characters_into_rooms(realm: &mut Realm, character_refs: Vec<GameObjectRef>) {
    for character_ref in character_refs {
        let maybe_current_room = realm
            .character(character_ref)
            .map(|character| character.current_room());
        if let Some(room) = maybe_current_room.and_then(|room_ref| realm.room_mut(room_ref)) {
            room.add_characters(vec![character_ref]);
        } else {
            println!(
                "Character {:?} has no room {:?}",
                character_ref, maybe_current_room
            );
        }
    }
}

async fn process_signing_in_input(
    realm: &mut Realm,
    session_tx: &Sender<SessionEvent>,
    log_tx: &LogSender,
    (input, session_id, source, state): (String, u64, String, SignInState),
) {
    let (new_state, output, log_messages) = process_input(&state, realm, &source, input);

    send_session_event(session_tx, SessionEvent::SessionOutput(session_id, output)).await;
    for message in log_messages {
        log_session_event(log_tx, source.clone(), message).await;
    }

    let session_state = if new_state.is_session_closed() {
        SessionState::SessionClosed(None)
    } else if let Some(user_name) = new_state.get_sign_in_user_name() {
        if let Some(sign_up_data) = new_state.get_sign_up_data() {
            realm.add_player(sign_up_data);
            log_session_event(
                log_tx,
                source.clone(),
                format!("Character created for player \"{}\"", user_name),
            )
            .await;
        }

        if let Some(player) = realm.player_by_name_mut(user_name) {
            if let Some(existing_session_id) = player.session_id() {
                send_session_event(
                    session_tx,
                    SessionEvent::SessionUpdate(
                        existing_session_id,
                        SessionState::SessionClosed(Some(player.id())),
                    ),
                )
                .await;
            }

            let player_ref = player.object_ref();
            let current_room = player.current_room();
            player.set_session_id(Some(session_id));

            log_command(log_tx, user_name.to_owned(), "(signed in)".to_owned()).await;

            let output = match enter_room(realm, player_ref, current_room) {
                Ok(mut output) => {
                    if let Ok(mut more) = look_at_object(realm, player_ref, current_room) {
                        output.append(&mut more);
                    }
                    output
                }
                Err(message) => vec![PlayerOutput::new_from_string(player_ref.id(), message)],
            };
            process_player_output(realm, session_tx, output).await;

            SessionState::SignedIn(player_ref.id())
        } else {
            log_session_event(
                log_tx,
                source,
                format!("Could not determine ID for player \"{}\"", user_name),
            )
            .await;
            SessionState::SessionClosed(None)
        }
    } else {
        SessionState::SigningIn(new_state)
    };

    send_session_event(
        session_tx,
        SessionEvent::SessionUpdate(session_id, session_state),
    )
    .await;
}

async fn process_signed_in_input(
    realm: &mut Realm,
    command_executor: &CommandExecutor,
    session_tx: &Sender<SessionEvent>,
    trigger_registry: &TriggerRegistry,
    log_tx: &LogSender,
    (input, session_id, source, player_id): (String, u64, String, GameObjectId),
) {
    if let Some(player) = realm.player_by_id(player_id) {
        log_command(log_tx, player.name().to_owned(), input.clone()).await;
        let player_ref = player.object_ref();
        let player_output =
            command_executor.execute_command(realm, trigger_registry, player_ref, log_tx, input);
        process_player_output(realm, session_tx, player_output).await;
    } else {
        log_session_event(log_tx, source, format!("Player {} deleted", player_id)).await;
        send_session_event(
            session_tx,
            SessionEvent::SessionUpdate(session_id, SessionState::SessionClosed(Some(player_id))),
        )
        .await;
    }
}

async fn process_player_output(
    realm: &Realm,
    session_tx: &Sender<SessionEvent>,
    player_output: Vec<PlayerOutput>,
) {
    let mut aggregated_output = BTreeMap::<u32, SessionOutput>::new();
    for PlayerOutput { player_id, output } in player_output {
        if let Some(existing_output) = aggregated_output.remove(&player_id) {
            aggregated_output.insert(player_id, existing_output.with(output));
        } else {
            aggregated_output.insert(player_id, output);
        }
    }

    for (player_id, output) in aggregated_output {
        let player = match realm.player_by_id(player_id) {
            Some(player) => player,
            None => continue,
        };

        if let Some(session_id) = player.session_id() {
            send_session_event(
                session_tx,
                SessionEvent::SessionOutput(
                    session_id,
                    match output {
                        SessionOutput::Json(json) => SessionOutput::Json(json),
                        output => output.with(SessionOutput::Prompt(SessionPromptInfo {
                            name: player.name().to_owned(),
                            is_admin: player.is_admin(),
                            hp: player.hp(),
                            max_hp: player.stats().max_hp(),
                            mp: player.mp(),
                            max_mp: player.stats().max_mp(),
                        })),
                    },
                ),
            )
            .await;
        }
    }
}

async fn process_session_closed(
    realm: &mut Realm,
    log_tx: &LogSender,
    player_id: Option<GameObjectId>,
) {
    if let Some(player_id) = player_id {
        if let Some(player) = realm.player_by_id_mut(player_id) {
            player.set_session_id(None);

            let player_name = player.name().to_owned();
            log_command(log_tx, player_name, "(session closed)".to_owned()).await;
        }
    }
}

async fn send_session_event(session_tx: &Sender<SessionEvent>, event: SessionEvent) {
    if let Err(error) = session_tx.send(event).await {
        println!("Could not send session event: {:?}", error);
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;
extern crate docopt;
extern crate core;
extern crate collections;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use docopt::FlagParser;
use std::io::BufferedReader;
use std::io::BufferedWriter;
use std::io::File;
use core::result::Result;
use collections::string::String;
use collections::vec::Vec;
use collections::PriorityQueue;

// Interleaver takes N files (e.g., i_1.txt ... i_N.txt) and transforms them into N output files
// named o_1.txt ... o_N.txt.  The lines of the input files must be prefixed with time stamps.  The
// output files contain the same content as the input files but only one file is allowed to make
// progress on a given line.  The other files are empty.

docopt!(Args, "
Usage:
  interleaver <file>...

Options:
  -h, --help    Show this screen.
", )

type BrType = BufferedReader<core::result::Result<std::io::fs::File,std::io::IoError>>;
type BwType = BufferedWriter<core::result::Result<std::io::fs::File,std::io::IoError>>;

#[deriving(Show,PartialEq,PartialOrd,Eq,Ord)]
enum Month { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }

fn string_to_month (s : &str) -> Option<Month> {
  match s {
    "Jan" => Some (Jan),
    "Feb" => Some (Feb),
    "Mar" => Some (Mar),
    "Apr" => Some (Apr),
    "May" => Some (May),
    "Jun" => Some (Jun),
    "Jul" => Some (Jul),
    "Aug" => Some (Aug),
    "Sep" => Some (Sep),
    "Oct" => Some (Oct),
    "Nov" => Some (Nov),
    "Dec" => Some (Dec),
    _     => None
  }
}

#[deriving(Show,PartialEq,Eq)]
struct TimedLine {
  l       : String,
  month   : Month,
  day     : u8,
  hour    : u8,
  minute  : u8,
  second  : u8,
  usecond : u32,
  target  : uint
}

impl TimedLine {
  fn new (l : &str, target : uint) -> TimedLine {
    let re = regex! (r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +(\d+) (\d+):(\d+):(\d+) (\d+):.*$");
    let caps = match re.captures (l) {
      None => fail! ("Could not analyze line: \"{}\"", l),
      Some (caps) => caps
    };
    let (month, day, hour, minute, second, usecond) =
      match (string_to_month (caps.at(1)), from_str::<u8>(caps.at(2)), from_str::<u8> (caps.at(3)), from_str::<u8> (caps.at(4)), from_str::<u8> (caps.at(5)), from_str::<u32> (caps.at(6))
      ) {
        (Some (month), Some (day), Some (hour), Some (minute), Some (second), Some (usecond)) => (month, day, hour, minute, second, usecond*100),
        _ => fail! ("Could not extract fields from line: \"{}\"", l)
      };

    TimedLine {
      l       : String::from_str (l),
      month   : month,
      day     : day,
      hour    : hour,
      minute  : minute,
      second  : second,
      usecond : usecond,
      target  : target
    }
  }
}

impl Ord for TimedLine {
  fn cmp(&self, other: &TimedLine) -> Ordering {
    let month_cmp = self.month.cmp (&other.month);
    if month_cmp == Equal {
      let day_cmp = self.day.cmp (&other.day);
      if day_cmp == Equal {
        let hour_cmp = self.hour.cmp (&other.hour);
        if hour_cmp == Equal {
          let minute_cmp = self.minute.cmp (&other.minute);
          if minute_cmp == Equal {
            let second_cmp = self.second.cmp (&other.second);
            if second_cmp == Equal {
              self.usecond.cmp (&other.usecond)
            }
            else { second_cmp }
          }
          else { minute_cmp }
        }
        else { hour_cmp }
      }
      else { day_cmp }
    }
    else { month_cmp }
  }
}

impl PartialOrd for TimedLine {
  fn partial_cmp(&self, other: &TimedLine) -> Option<Ordering> {
    Some (self.cmp (other))
  }
}

struct TimedLineQueue<'a> {
  q : PriorityQueue<TimedLine>,
  readers : &'a mut Vec<BrType>
}

impl<'a> TimedLineQueue<'a> {
  fn fill_que (&mut self, target : uint) {
    let reader = self.readers.get_mut (target);
    let mut lines = reader.lines ();
    let line_opt = lines.next ();
    match line_opt {
      Some (line) => {
        let line = line.unwrap();
        let line = line.as_slice();
        let line = line.trim_chars('\n');
        let timed_line = TimedLine::new (line, target);
        self.q.push (timed_line);
      }
      None => ()
    }
  }

  fn new (readers : & 'a mut Vec<BrType>) -> TimedLineQueue {
    let mut res = TimedLineQueue {
      q : PriorityQueue::new (),
      readers : readers
    };

    for (i, _) in res.readers.mut_iter().enumerate () {
      res.fill_que(i);
    }

    res
  }
}

impl<'a> Iterator<TimedLine> for TimedLineQueue<'a> {
  fn next (&mut self) -> Option<TimedLine> {
    let timed_line_opt = self.q.pop ();
    match timed_line_opt {
      None => (),
      Some (ref timed_line) => {
        self.fill_que(timed_line.target);
      }
    }

    timed_line_opt
  }
}

#[test]
fn test_timed_line_ordering () {
  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  assert! (tl_1.cmp (&tl_2) == Less);
  assert! (tl_2.cmp (&tl_1) == Greater);
  assert! (tl_1.cmp (&tl_1) == Equal);
  assert! (tl_2.cmp (&tl_2) == Equal);
}

fn main() {
  let args: Args = FlagParser::parse().unwrap_or_else(|e| e.exit());

  let file_names = args.arg_file;
  let file_count = file_names.len ();
  let paths_it = file_names.iter ().map (|n| Path::new(n.clone ()));
  let files_it = paths_it.map (|path| File::open(&path));
  let mut buffered_readers_it = files_it.map (|file| BufferedReader::new (file));
  let mut buffered_readers_vec : Vec<BrType> = buffered_readers_it.collect ();

  let paths_out_it = range (0u, file_names.len ()).map (|i| Path::new (format!("{}.txt", i)));
  let files_out_it = paths_out_it.map (|path_out| {
    let res = File::create(&path_out);
    match res {
      Ok(_)  => (),
      Err(e) => fail!("Could not open output file: {}", e)
    };
    res
  }
  );
  let mut buffered_writers_it = files_out_it.map (|file_out| BufferedWriter::new (file_out) );

  let mut buffered_writers_vec : Vec<BwType> = buffered_writers_it.collect ();

  let mut tlq = TimedLineQueue::new (& mut buffered_readers_vec);
  for timed_line in tlq {
    let target = timed_line.target;
    {
      let writer = buffered_writers_vec.get_mut (target);
      let res = writer.write_line (format!("{}", timed_line.l).as_slice ());
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[target];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }

    for i in range (0, file_count).filter (|i| *i != target) {
      let writer = buffered_writers_vec.get_mut (i);
      let res = writer.write_line ("");
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[i];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }
  }
}
<commit_msg>Added test for priority queue<commit_after>#![feature(phase)]
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;
extern crate docopt;
extern crate core;
extern crate collections;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use docopt::FlagParser;
use std::io::BufferedReader;
use std::io::BufferedWriter;
use std::io::File;
use core::result::Result;
use collections::string::String;
use collections::vec::Vec;
use collections::PriorityQueue;

// Interleaver takes N files (e.g., i_1.txt ... i_N.txt) and transforms them into N output files
// named o_1.txt ... o_N.txt.  The lines of the input files must be prefixed with time stamps.  The
// output files contain the same content as the input files but only one file is allowed to make
// progress on a given line.  The other files are empty.

docopt!(Args, "
Usage:
  interleaver <file>...

Options:
  -h, --help    Show this screen.
", )

type BrType = BufferedReader<core::result::Result<std::io::fs::File,std::io::IoError>>;
type BwType = BufferedWriter<core::result::Result<std::io::fs::File,std::io::IoError>>;

#[deriving(Show,PartialEq,PartialOrd,Eq,Ord)]
enum Month { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }

fn string_to_month (s : &str) -> Option<Month> {
  match s {
    "Jan" => Some (Jan),
    "Feb" => Some (Feb),
    "Mar" => Some (Mar),
    "Apr" => Some (Apr),
    "May" => Some (May),
    "Jun" => Some (Jun),
    "Jul" => Some (Jul),
    "Aug" => Some (Aug),
    "Sep" => Some (Sep),
    "Oct" => Some (Oct),
    "Nov" => Some (Nov),
    "Dec" => Some (Dec),
    _     => None
  }
}

#[deriving(Show,PartialEq,Eq)]
struct TimedLine {
  l       : String,
  month   : Month,
  day     : u8,
  hour    : u8,
  minute  : u8,
  second  : u8,
  usecond : u32,
  target  : uint
}

impl TimedLine {
  fn new (l : &str, target : uint) -> TimedLine {
    let re = regex! (r"^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +(\d+) (\d+):(\d+):(\d+) (\d+):.*$");
    let caps = match re.captures (l) {
      None => fail! ("Could not analyze line: \"{}\"", l),
      Some (caps) => caps
    };
    let (month, day, hour, minute, second, usecond) =
      match (string_to_month (caps.at(1)), from_str::<u8>(caps.at(2)), from_str::<u8> (caps.at(3)), from_str::<u8> (caps.at(4)), from_str::<u8> (caps.at(5)), from_str::<u32> (caps.at(6))
      ) {
        (Some (month), Some (day), Some (hour), Some (minute), Some (second), Some (usecond)) => (month, day, hour, minute, second, usecond*100),
        _ => fail! ("Could not extract fields from line: \"{}\"", l)
      };

    TimedLine {
      l       : String::from_str (l),
      month   : month,
      day     : day,
      hour    : hour,
      minute  : minute,
      second  : second,
      usecond : usecond,
      target  : target
    }
  }
}

impl Ord for TimedLine {
  fn cmp(&self, other: &TimedLine) -> Ordering {
    let month_cmp = self.month.cmp (&other.month);
    if month_cmp == Equal {
      let day_cmp = self.day.cmp (&other.day);
      if day_cmp == Equal {
        let hour_cmp = self.hour.cmp (&other.hour);
        if hour_cmp == Equal {
          let minute_cmp = self.minute.cmp (&other.minute);
          if minute_cmp == Equal {
            let second_cmp = self.second.cmp (&other.second);
            if second_cmp == Equal {
              self.usecond.cmp (&other.usecond)
            }
            else { second_cmp }
          }
          else { minute_cmp }
        }
        else { hour_cmp }
      }
      else { day_cmp }
    }
    else { month_cmp }
  }
}

impl PartialOrd for TimedLine {
  fn partial_cmp(&self, other: &TimedLine) -> Option<Ordering> {
    Some (self.cmp (other))
  }
}

struct TimedLineQueue<'a> {
  q : PriorityQueue<TimedLine>,
  readers : &'a mut Vec<BrType>
}

impl<'a> TimedLineQueue<'a> {
  fn fill_que (&mut self, target : uint) {
    let reader = self.readers.get_mut (target);
    let mut lines = reader.lines ();
    let line_opt = lines.next ();
    match line_opt {
      Some (line) => {
        let line = line.unwrap();
        let line = line.as_slice();
        let line = line.trim_chars('\n');
        let timed_line = TimedLine::new (line, target);
        self.q.push (timed_line);
      }
      None => ()
    }
  }

  fn new (readers : & 'a mut Vec<BrType>) -> TimedLineQueue {
    let mut res = TimedLineQueue {
      q : PriorityQueue::new (),
      readers : readers
    };

    for (i, _) in res.readers.mut_iter().enumerate () {
      res.fill_que(i);
    }

    res
  }
}

#[test]
fn test_priority_queue () {
  let mut q = PriorityQueue::new ();

  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  q.push (s1.clone ());
  q.push (s2.clone ());

  assert_eq! (q.pop(), Some(s2.clone ()));
  assert_eq! (q.pop(), Some(s1.clone ()));

  q.push (s2.clone ());
  q.push (s1.clone ());

  assert_eq! (q.pop(), Some(s2.clone ()));
  assert_eq! (q.pop(), Some(s1.clone ()));
}

impl<'a> Iterator<TimedLine> for TimedLineQueue<'a> {
  fn next (&mut self) -> Option<TimedLine> {
    let timed_line_opt = self.q.pop ();
    match timed_line_opt {
      None => (),
      Some (ref timed_line) => {
        self.fill_que(timed_line.target);
      }
    }

    timed_line_opt
  }
}

#[test]
fn test_timed_line_ordering () {
  let s1 = String::from_str ("Sep  2 14:25:02 8993: (main|info): --- NODE STARTED ---");
  let s2 = String::from_str ("Sep  2 14:25:02 9488: (main|info): --- NODE STARTED ---");

  let tl_1 = TimedLine::new (s1.as_slice(), 5u);
  let tl_2 = TimedLine::new (s2.as_slice(), 5u);

  assert! (tl_1.cmp (&tl_2) == Less);
  assert! (tl_2.cmp (&tl_1) == Greater);
  assert! (tl_1.cmp (&tl_1) == Equal);
  assert! (tl_2.cmp (&tl_2) == Equal);
}

fn main() {
  let args: Args = FlagParser::parse().unwrap_or_else(|e| e.exit());

  let file_names = args.arg_file;
  let file_count = file_names.len ();
  let paths_it = file_names.iter ().map (|n| Path::new(n.clone ()));
  let files_it = paths_it.map (|path| File::open(&path));
  let mut buffered_readers_it = files_it.map (|file| BufferedReader::new (file));
  let mut buffered_readers_vec : Vec<BrType> = buffered_readers_it.collect ();

  let paths_out_it = range (0u, file_names.len ()).map (|i| Path::new (format!("{}.txt", i)));
  let files_out_it = paths_out_it.map (|path_out| {
    let res = File::create(&path_out);
    match res {
      Ok(_)  => (),
      Err(e) => fail!("Could not open output file: {}", e)
    };
    res
  }
  );
  let mut buffered_writers_it = files_out_it.map (|file_out| BufferedWriter::new (file_out) );

  let mut buffered_writers_vec : Vec<BwType> = buffered_writers_it.collect ();

  let mut tlq = TimedLineQueue::new (& mut buffered_readers_vec);
  for timed_line in tlq {
    let target = timed_line.target;
    {
      let writer = buffered_writers_vec.get_mut (target);
      let res = writer.write_line (format!("{}", timed_line.l).as_slice ());
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[target];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }

    for i in range (0, file_count).filter (|i| *i != target) {
      let writer = buffered_writers_vec.get_mut (i);
      let res = writer.write_line ("");
      match res {
        Ok(_) => (),
        Err(e) => {
          let ref file_name = file_names[i];
          fail! ("Could not write to {}: {}", file_name, e);
        }
      };
    }
  }
}
<|endoftext|>


<commit_before>extern crate websocat;

extern crate futures;
extern crate tokio_core;
extern crate tokio_stdin_stdout;

use tokio_core::reactor::{Core};
use futures::future::Future;
use futures::Stream;
use websocat::{Session,peer_from_str,ProgramState,is_stdio_peer,is_stdioish_peer};

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

fn run() -> Result<()> {
    let mut ps : ProgramState = Default::default();

    let arg1 = std::env::args().nth(1).ok_or("Usage: websocat - ws[s]://...")?;
    let arg2 = std::env::args().nth(2).ok_or("no second arg")?;

    if is_stdio_peer(arg1.as_ref()) && is_stdio_peer(arg2.as_ref()) {
        // Degenerate mode: just copy stdin to stdout and call it a day
        ::std::io::copy(&mut ::std::io::stdin(), &mut ::std::io::stdout())?;
        return Ok(())
    }

    if is_stdioish_peer(arg1.as_ref()) && is_stdioish_peer(arg2.as_ref()) {
        Err("Too many usages of stdin/stdout")?;
    }

    let mut core = Core::new()?;

    let h1 = core.handle();
    let h2 = core.handle();

    use websocat::PeerConstructor::{ServeMultipleTimes, ServeOnce};

    let left = peer_from_str(&mut ps, &h1, arg1.as_ref());
    match left {
        ServeMultipleTimes(stream) => {
            let runner = stream
            .for_each(|peer1| {
                let h3 = h2.clone();
                let right = peer_from_str(&mut ps, &h2, arg2.as_ref());
                let fut = right.get_only_first_conn();
                fut.map(move |peer2| {
                    let s = Session::new(peer1,peer2);
                    h3.spawn(
                        s.run().map_err(|e| {
                            eprintln!("websocat: {}", e);
                        })
                    );
                })
            });
            core.run(runner)?;
        },
        ServeOnce(peer1c) => {
            let runner = peer1c
            .and_then(|peer1| {
                let right = peer_from_str(&mut ps, &h2, arg2.as_ref());
                let fut = right.get_only_first_conn();
                fut.and_then(move |peer2| {
                    let s = Session::new(peer1,peer2);
                    s.run()
                })
            });
            core.run(runner)?;
        },
    };

    Ok(())
}

fn main() {
    let r = run();

    if let Err(e) = r {
        eprintln!("websocat: {}", e);
        ::std::process::exit(1);
    }
}
<commit_msg>A bit better multiuse<commit_after>extern crate websocat;

extern crate futures;
extern crate tokio_core;
extern crate tokio_stdin_stdout;

use tokio_core::reactor::{Core};
use futures::future::Future;
use futures::Stream;
use websocat::{Session,peer_from_str,ProgramState,is_stdio_peer,is_stdioish_peer};

type Result<T> = std::result::Result<T, Box<std::error::Error>>;

fn run() -> Result<()> {
    let mut ps : ProgramState = Default::default();

    let arg1 = std::env::args().nth(1).ok_or("Usage: websocat - ws[s]://...")?;
    let arg2 = std::env::args().nth(2).ok_or("no second arg")?;

    if is_stdio_peer(arg1.as_ref()) && is_stdio_peer(arg2.as_ref()) {
        // Degenerate mode: just copy stdin to stdout and call it a day
        ::std::io::copy(&mut ::std::io::stdin(), &mut ::std::io::stdout())?;
        return Ok(())
    }

    if is_stdioish_peer(arg1.as_ref()) && is_stdioish_peer(arg2.as_ref()) {
        Err("Too many usages of stdin/stdout")?;
    }

    let mut core = Core::new()?;

    let h1 = core.handle();
    let h2 = core.handle();

    use websocat::PeerConstructor::{ServeMultipleTimes, ServeOnce};

    let left = peer_from_str(&mut ps, &h1, arg1.as_ref());
    match left {
        ServeMultipleTimes(stream) => {
            let runner = stream
            .map(|peer1| {
                h2.spawn(
                    peer_from_str(&mut ps, &h2, arg2.as_ref())
                    .get_only_first_conn()
                    .and_then(move |peer2| {
                        let s = Session::new(peer1,peer2);
                        s.run()
                    })
                    .map_err(|e| {
                        eprintln!("websocat: {}", e);
                    })
                )
            }).for_each(|()|futures::future::ok(()));
            core.run(runner)?;
        },
        ServeOnce(peer1c) => {
            let runner = peer1c
            .and_then(|peer1| {
                let right = peer_from_str(&mut ps, &h2, arg2.as_ref());
                let fut = right.get_only_first_conn();
                fut.and_then(move |peer2| {
                    let s = Session::new(peer1,peer2);
                    s.run()
                })
            });
            core.run(runner)?;
        },
    };

    Ok(())
}

fn main() {
    let r = run();

    if let Err(e) = r {
        eprintln!("websocat: {}", e);
        ::std::process::exit(1);
    }
}
<|endoftext|>