<commit_before>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(unused_imports))]

use std::env;
use std::thread;
use std::process::{self, Stdio};
use std::sync::mpsc;

use client;
use query::{Query, Response};

/// Manages the renderer process and all communication with it
#[cfg(not(any(feature = "test", test)))]
pub struct RendererProcess {
    process: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

#[cfg(not(any(feature = "test", test)))]
impl RendererProcess {
    /// Spawn the renderer process and also a thread for communicating with that process
    pub fn new() -> Self {
        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let (response_tx, response_rx) = mpsc::channel();
        let handle = thread::spawn(move || {
            client::run(renderer_stdout, response_tx);
        });

        Self {
            process: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Sends a query and automatically decides whether or not to wait for a response.
    ///
    /// If a query does not require a response, this function will return immediately after
    /// sending the query
    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        client::send_query(match self.process.stdin {
            Some(ref mut stdin) => stdin,
            None => unreachable!("bug: renderer process was not opened with stdin"),
        }, &query).unwrap_or_else(|_| {
            // Something went wrong while sending the query, check if the renderer process
            // panicked (exited with an error)
            match self.process.try_wait() {
                Ok(Some(status)) => {
                    if status.success() {
                        // The window/renderer process was closed normally
                        process::exit(0);
                    }
                    else {
                        // Something went wrong, likely the other thread panicked
                        process::exit(1);
                    }
                },
                Ok(None) => panic!("bug: failed to send query even though renderer process was still running"),
                Err(_) => panic!("bug: unable to check the exit status of the renderer process"),
            }
        });

        // Requests need responses
        if let Query::Request(_) = query {
            Some(self.wait_for_response())
        }
        else {
            None
        }
    }

    fn wait_for_response(&mut self) -> Response {
        match self.response_channel.recv() {
            Ok(response) => response,
            // The client thread has exited, that means that the renderer process has exited
            // and the window has closed
            Err(_) => self.exit_process(), // Quit
        }
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        let status = self.thread_handle.take().ok_or_else(|| {
            unreachable!("bug: the thread handle was used but the process did not end");
        }).and_then(|handle| {
            // First check if the other thread panicked before it quit
            handle.join().map_err(|_| ())
        }).and_then(|_| {
            // Then check if the renderer process ended normally
            self.process.wait()
                .map_err(|_| unreachable!("bug: renderer process never ran even though we exited"))
                .and_then(|status| if status.success() { Ok(()) } else { Err(()) })
        });

        match status {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(1),
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for RendererProcess {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }

        // Now that the thread has completed, the process likely has as well (or is about to).
        // Check its exit status to see if we need to quit with an error
        match self.process.wait() {
            Ok(status) => if !status.success() {
                process::exit(1);
            },
            Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
        }
    }
}

/// A special "renderer process" specifically for tests. Simulates the renderer process by
/// providing all of the same functionality and reusing internal parts of the
#[cfg(any(feature = "test", test))]
pub struct RendererProcess {
    app: ::app::TurtleApp,
    events: (mpsc::Sender<::Event>, mpsc::Receiver<::Event>),
    drawing: (mpsc::Sender<::query::DrawingCommand>, mpsc::Receiver<::query::DrawingCommand>),
}

#[cfg(any(feature = "test", test))]
impl RendererProcess {
    pub fn new() -> Self {
        Self {
            app: ::app::TurtleApp::new(),
            events: mpsc::channel(),
            drawing: mpsc::channel(),
        }
    }

    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        ::server::handle_query_for_test_use_only(query, &mut self.app, &self.events.1, &self.drawing.0)
            .expect("test bug: a query failed to be successful")
    }
}
<commit_msg>Replaced match with combinator<commit_after>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(unused_imports))]

use std::env;
use std::thread;
use std::process::{self, Stdio};
use std::sync::mpsc;

use client;
use query::{Query, Response};

/// Manages the renderer process and all communication with it
#[cfg(not(any(feature = "test", test)))]
pub struct RendererProcess {
    process: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

#[cfg(not(any(feature = "test", test)))]
impl RendererProcess {
    /// Spawn the renderer process and also a thread for communicating with that process
    pub fn new() -> Self {
        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let (response_tx, response_rx) = mpsc::channel();
        let handle = thread::spawn(move || {
            client::run(renderer_stdout, response_tx);
        });

        Self {
            process: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Sends a query and automatically decides whether or not to wait for a response.
    ///
    /// If a query does not require a response, this function will return immediately after
    /// sending the query
    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        client::send_query(match self.process.stdin {
            Some(ref mut stdin) => stdin,
            None => unreachable!("bug: renderer process was not opened with stdin"),
        }, &query).unwrap_or_else(|_| {
            // Something went wrong while sending the query, check if the renderer process
            // panicked (exited with an error)
            match self.process.try_wait() {
                Ok(Some(status)) => {
                    if status.success() {
                        // The window/renderer process was closed normally
                        process::exit(0);
                    }
                    else {
                        // Something went wrong, likely the other thread panicked
                        process::exit(1);
                    }
                },
                Ok(None) => panic!("bug: failed to send query even though renderer process was still running"),
                Err(_) => panic!("bug: unable to check the exit status of the renderer process"),
            }
        });

        // Requests need responses
        if let Query::Request(_) = query {
            Some(self.wait_for_response())
        }
        else {
            None
        }
    }

    fn wait_for_response(&mut self) -> Response {
        self.response_channel.recv().unwrap_or_else(|_| {
            // The client thread has exited, that means that the renderer process has exited
            // and the window has closed
            self.exit_process()
        })
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        let status = self.thread_handle.take().ok_or_else(|| {
            unreachable!("bug: the thread handle was used but the process did not end");
        }).and_then(|handle| {
            // First check if the other thread panicked before it quit
            handle.join().map_err(|_| ())
        }).and_then(|_| {
            // Then check if the renderer process ended normally
            self.process.wait()
                .map_err(|_| unreachable!("bug: renderer process never ran even though we exited"))
                .and_then(|status| if status.success() { Ok(()) } else { Err(()) })
        });

        match status {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(1),
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for RendererProcess {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }

        // Now that the thread has completed, the process likely has as well (or is about to).
        // Check its exit status to see if we need to quit with an error
        match self.process.wait() {
            Ok(status) => if !status.success() {
                process::exit(1);
            },
            Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
        }
    }
}

/// A special "renderer process" specifically for tests. Simulates the renderer process by
/// providing all of the same functionality and reusing internal parts of the
#[cfg(any(feature = "test", test))]
pub struct RendererProcess {
    app: ::app::TurtleApp,
    events: (mpsc::Sender<::Event>, mpsc::Receiver<::Event>),
    drawing: (mpsc::Sender<::query::DrawingCommand>, mpsc::Receiver<::query::DrawingCommand>),
}

#[cfg(any(feature = "test", test))]
impl RendererProcess {
    pub fn new() -> Self {
        Self {
            app: ::app::TurtleApp::new(),
            events: mpsc::channel(),
            drawing: mpsc::channel(),
        }
    }

    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        ::server::handle_query_for_test_use_only(query, &mut self.app, &self.events.1, &self.drawing.0)
            .expect("test bug: a query failed to be successful")
    }
}
<|endoftext|>


<commit_before>use itertools::Itertools;

use crate::{
    compiler::hir::Id,
    module::Module,
    vm::{ChannelId, FiberId, Heap, Pointer},
};
use std::{collections::HashMap, fmt, time::Instant};

use super::{FiberEvent, Tracer, VmEvent};

/// A full tracer that saves all events that occur with timestamps.
#[derive(Clone, Default)]
pub struct FullTracer {
    pub events: Vec<TimedEvent>,
    pub heap: Heap,
    transferred_objects: HashMap<FiberId, HashMap<Pointer, Pointer>>,
}
#[derive(Clone)]
pub struct TimedEvent {
    pub when: Instant,
    pub event: StoredVmEvent,
}

#[derive(Clone)]
pub enum StoredVmEvent {
    FiberCreated {
        fiber: FiberId,
    },
    FiberDone {
        fiber: FiberId,
    },
    FiberPanicked {
        fiber: FiberId,
        panicked_child: Option<FiberId>,
    },
    FiberCanceled {
        fiber: FiberId,
    },
    FiberExecutionStarted {
        fiber: FiberId,
    },
    FiberExecutionEnded {
        fiber: FiberId,
    },
    ChannelCreated {
        channel: ChannelId,
    },
    InFiber {
        fiber: FiberId,
        event: StoredFiberEvent,
    },
}
#[derive(Clone)]
pub enum StoredFiberEvent {
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
    },
    ValueEvaluated {
        id: Id,
        value: Pointer,
    },
    FoundFuzzableClosure {
        id: Id,
        closure: Pointer,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    CallEnded {
        return_value: Pointer,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    NeedsEnded,
}

impl Tracer for FullTracer {
    fn add(&mut self, event: VmEvent) {
        let event = TimedEvent {
            when: Instant::now(),
            event: self.map_vm_event(event),
        };
        self.events.push(event);
    }
}
impl FullTracer {
    fn import_from_heap(
        &mut self,
        address: Pointer,
        heap: &Heap,
        fiber: Option<FiberId>,
    ) -> Pointer {
        if let Some(fiber) = fiber {
            let map = self
                .transferred_objects
                .entry(fiber)
                .or_insert_with(HashMap::new);
            heap.clone_single_to_other_heap_with_existing_mapping(&mut self.heap, address, map)
        } else {
            heap.clone_single_to_other_heap(&mut self.heap, address)
        }
    }

    fn map_vm_event(&mut self, event: VmEvent) -> StoredVmEvent {
        match event {
            VmEvent::FiberCreated { fiber } => StoredVmEvent::FiberCreated { fiber },
            VmEvent::FiberDone { fiber } => StoredVmEvent::FiberDone { fiber },
            VmEvent::FiberPanicked {
                fiber,
                panicked_child,
            } => StoredVmEvent::FiberPanicked {
                fiber,
                panicked_child,
            },
            VmEvent::FiberCanceled { fiber } => StoredVmEvent::FiberCanceled { fiber },
            VmEvent::FiberExecutionStarted { fiber } => {
                StoredVmEvent::FiberExecutionStarted { fiber }
            }
            VmEvent::FiberExecutionEnded { fiber } => StoredVmEvent::FiberExecutionEnded { fiber },
            VmEvent::ChannelCreated { channel } => StoredVmEvent::ChannelCreated { channel },
            VmEvent::InFiber { fiber, event } => StoredVmEvent::InFiber {
                fiber,
                event: self.map_fiber_event(event, fiber),
            },
        }
    }
    fn map_fiber_event(&mut self, event: FiberEvent, fiber: FiberId) -> StoredFiberEvent {
        match event {
            FiberEvent::ModuleStarted { module } => StoredFiberEvent::ModuleStarted { module },
            FiberEvent::ModuleEnded { export_map, heap } => {
                let export_map = self.import_from_heap(export_map, heap, Some(fiber));
                StoredFiberEvent::ModuleEnded { export_map }
            }
            FiberEvent::ValueEvaluated { id, value, heap } => {
                let value = self.import_from_heap(value, heap, Some(fiber));
                StoredFiberEvent::ValueEvaluated { id, value }
            }
            FiberEvent::FoundFuzzableClosure { id, closure, heap } => {
                let closure = self.import_from_heap(closure, heap, Some(fiber));
                StoredFiberEvent::FoundFuzzableClosure { id, closure }
            }
            FiberEvent::CallStarted {
                id,
                closure,
                args,
                heap,
            } => {
                let closure = self.import_from_heap(closure, heap, Some(fiber));
                let args = args
                    .into_iter()
                    .map(|arg| self.import_from_heap(arg, heap, Some(fiber)))
                    .collect();
                StoredFiberEvent::CallStarted { id, closure, args }
            }
            FiberEvent::CallEnded { return_value, heap } => {
                let return_value = self.import_from_heap(return_value, heap, Some(fiber));
                StoredFiberEvent::CallEnded { return_value }
            }
            FiberEvent::NeedsStarted {
                id,
                condition,
                reason,
                heap,
            } => {
                let condition = self.import_from_heap(condition, heap, Some(fiber));
                let reason = self.import_from_heap(reason, heap, Some(fiber));
                StoredFiberEvent::NeedsStarted {
                    id,
                    condition,
                    reason,
                }
            }
            FiberEvent::NeedsEnded => StoredFiberEvent::NeedsEnded,
        }
    }
}

impl fmt::Debug for FullTracer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let start = self.events.first().map(|event| event.when);
        for event in &self.events {
            writeln!(
                f,
                "{:?} us: {}",
                event.when.duration_since(start.unwrap()).as_micros(),
                match &event.event {
                    StoredVmEvent::FiberCreated { fiber } => format!("{fiber:?}: created"),
                    StoredVmEvent::FiberDone { fiber } => format!("{fiber:?}: done"),
                    StoredVmEvent::FiberPanicked {
                        fiber,
                        panicked_child,
                    } => format!(
                        "{fiber:?}: panicked{}",
                        if let Some(child) = panicked_child {
                            format!(" because child {child:?} panicked")
                        } else {
                            "".to_string()
                        }
                    ),
                    StoredVmEvent::FiberCanceled { fiber } => format!("{fiber:?}: canceled"),
                    StoredVmEvent::FiberExecutionStarted { fiber } =>
                        format!("{fiber:?}: execution started"),
                    StoredVmEvent::FiberExecutionEnded { fiber } =>
                        format!("{fiber:?}: execution ended"),
                    StoredVmEvent::ChannelCreated { channel } => format!("{channel:?}: created"),
                    StoredVmEvent::InFiber { fiber, event } => format!(
                        "{fiber:?}: {}",
                        match event {
                            StoredFiberEvent::ModuleStarted { module } =>
                                format!("module {module} started"),
                            StoredFiberEvent::ModuleEnded { export_map } => format!(
                                "module ended and exported {}",
                                export_map.format(&self.heap)
                            ),
                            StoredFiberEvent::ValueEvaluated { id, value } =>
                                format!("value {id} is {}", value.format(&self.heap)),
                            StoredFiberEvent::FoundFuzzableClosure { id, .. } =>
                                format!("found fuzzable closure {id}"),
                            StoredFiberEvent::CallStarted { id, closure, args } => format!(
                                "call {id} started: {} {}",
                                closure.format(&self.heap),
                                args.iter().map(|arg| arg.format(&self.heap)).join(" ")
                            ),
                            StoredFiberEvent::CallEnded { return_value } =>
                                format!("call ended: {}", return_value.format(&self.heap)),
                            StoredFiberEvent::NeedsStarted {
                                id,
                                condition,
                                reason,
                            } => format!(
                                "needs {id} started: needs {} {}",
                                condition.format(&self.heap),
                                reason.format(&self.heap)
                            ),
                            StoredFiberEvent::NeedsEnded => "needs ended".to_string(),
                        }
                    ),
                }
            )?;
        }
        Ok(())
    }
}
<commit_msg>Update compiler/src/vm/tracer/full.rs<commit_after>use itertools::Itertools;

use crate::{
    compiler::hir::Id,
    module::Module,
    vm::{ChannelId, FiberId, Heap, Pointer},
};
use std::{collections::HashMap, fmt, time::Instant};

use super::{FiberEvent, Tracer, VmEvent};

/// A full tracer that saves all events that occur with timestamps.
#[derive(Clone, Default)]
pub struct FullTracer {
    pub events: Vec<TimedEvent>,
    pub heap: Heap,
    transferred_objects: HashMap<FiberId, HashMap<Pointer, Pointer>>,
}
#[derive(Clone)]
pub struct TimedEvent {
    pub when: Instant,
    pub event: StoredVmEvent,
}

#[derive(Clone)]
pub enum StoredVmEvent {
    FiberCreated {
        fiber: FiberId,
    },
    FiberDone {
        fiber: FiberId,
    },
    FiberPanicked {
        fiber: FiberId,
        panicked_child: Option<FiberId>,
    },
    FiberCanceled {
        fiber: FiberId,
    },
    FiberExecutionStarted {
        fiber: FiberId,
    },
    FiberExecutionEnded {
        fiber: FiberId,
    },
    ChannelCreated {
        channel: ChannelId,
    },
    InFiber {
        fiber: FiberId,
        event: StoredFiberEvent,
    },
}
#[derive(Clone)]
pub enum StoredFiberEvent {
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
    },
    ValueEvaluated {
        id: Id,
        value: Pointer,
    },
    FoundFuzzableClosure {
        id: Id,
        closure: Pointer,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    CallEnded {
        return_value: Pointer,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    NeedsEnded,
}

impl Tracer for FullTracer {
    fn add(&mut self, event: VmEvent) {
        let event = TimedEvent {
            when: Instant::now(),
            event: self.map_vm_event(event),
        };
        self.events.push(event);
    }
}
impl FullTracer {
    fn import_from_heap(
        &mut self,
        address: Pointer,
        heap: &Heap,
        fiber: Option<FiberId>,
    ) -> Pointer {
        if let Some(fiber) = fiber {
            let map = self
                .transferred_objects
                .entry(fiber)
                .or_insert_with(HashMap::new);
            heap.clone_single_to_other_heap_with_existing_mapping(&mut self.heap, address, map)
        } else {
            heap.clone_single_to_other_heap(&mut self.heap, address)
        }
    }

    fn map_vm_event(&mut self, event: VmEvent) -> StoredVmEvent {
        match event {
            VmEvent::FiberCreated { fiber } => StoredVmEvent::FiberCreated { fiber },
            VmEvent::FiberDone { fiber } => StoredVmEvent::FiberDone { fiber },
            VmEvent::FiberPanicked {
                fiber,
                panicked_child,
            } => StoredVmEvent::FiberPanicked {
                fiber,
                panicked_child,
            },
            VmEvent::FiberCanceled { fiber } => StoredVmEvent::FiberCanceled { fiber },
            VmEvent::FiberExecutionStarted { fiber } => {
                StoredVmEvent::FiberExecutionStarted { fiber }
            }
            VmEvent::FiberExecutionEnded { fiber } => StoredVmEvent::FiberExecutionEnded { fiber },
            VmEvent::ChannelCreated { channel } => StoredVmEvent::ChannelCreated { channel },
            VmEvent::InFiber { fiber, event } => StoredVmEvent::InFiber {
                fiber,
                event: self.map_fiber_event(event, fiber),
            },
        }
    }
    fn map_fiber_event(&mut self, event: FiberEvent, fiber: FiberId) -> StoredFiberEvent {
        match event {
            FiberEvent::ModuleStarted { module } => StoredFiberEvent::ModuleStarted { module },
            FiberEvent::ModuleEnded { export_map, heap } => {
                let export_map = self.import_from_heap(export_map, heap, Some(fiber));
                StoredFiberEvent::ModuleEnded { export_map }
            }
            FiberEvent::ValueEvaluated { id, value, heap } => {
                let value = self.import_from_heap(value, heap, Some(fiber));
                StoredFiberEvent::ValueEvaluated { id, value }
            }
            FiberEvent::FoundFuzzableClosure { id, closure, heap } => {
                let closure = self.import_from_heap(closure, heap, Some(fiber));
                StoredFiberEvent::FoundFuzzableClosure { id, closure }
            }
            FiberEvent::CallStarted {
                id,
                closure,
                args,
                heap,
            } => {
                let closure = self.import_from_heap(closure, heap, Some(fiber));
                let args = args
                    .into_iter()
                    .map(|arg| self.import_from_heap(arg, heap, Some(fiber)))
                    .collect();
                StoredFiberEvent::CallStarted { id, closure, args }
            }
            FiberEvent::CallEnded { return_value, heap } => {
                let return_value = self.import_from_heap(return_value, heap, Some(fiber));
                StoredFiberEvent::CallEnded { return_value }
            }
            FiberEvent::NeedsStarted {
                id,
                condition,
                reason,
                heap,
            } => {
                let condition = self.import_from_heap(condition, heap, Some(fiber));
                let reason = self.import_from_heap(reason, heap, Some(fiber));
                StoredFiberEvent::NeedsStarted {
                    id,
                    condition,
                    reason,
                }
            }
            FiberEvent::NeedsEnded => StoredFiberEvent::NeedsEnded,
        }
    }
}

impl fmt::Debug for FullTracer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let start = self.events.first().map(|event| event.when);
        for event in &self.events {
            writeln!(
                f,
                "{:?} µs: {}",
                event.when.duration_since(start.unwrap()).as_micros(),
                match &event.event {
                    StoredVmEvent::FiberCreated { fiber } => format!("{fiber:?}: created"),
                    StoredVmEvent::FiberDone { fiber } => format!("{fiber:?}: done"),
                    StoredVmEvent::FiberPanicked {
                        fiber,
                        panicked_child,
                    } => format!(
                        "{fiber:?}: panicked{}",
                        if let Some(child) = panicked_child {
                            format!(" because child {child:?} panicked")
                        } else {
                            "".to_string()
                        }
                    ),
                    StoredVmEvent::FiberCanceled { fiber } => format!("{fiber:?}: canceled"),
                    StoredVmEvent::FiberExecutionStarted { fiber } =>
                        format!("{fiber:?}: execution started"),
                    StoredVmEvent::FiberExecutionEnded { fiber } =>
                        format!("{fiber:?}: execution ended"),
                    StoredVmEvent::ChannelCreated { channel } => format!("{channel:?}: created"),
                    StoredVmEvent::InFiber { fiber, event } => format!(
                        "{fiber:?}: {}",
                        match event {
                            StoredFiberEvent::ModuleStarted { module } =>
                                format!("module {module} started"),
                            StoredFiberEvent::ModuleEnded { export_map } => format!(
                                "module ended and exported {}",
                                export_map.format(&self.heap)
                            ),
                            StoredFiberEvent::ValueEvaluated { id, value } =>
                                format!("value {id} is {}", value.format(&self.heap)),
                            StoredFiberEvent::FoundFuzzableClosure { id, .. } =>
                                format!("found fuzzable closure {id}"),
                            StoredFiberEvent::CallStarted { id, closure, args } => format!(
                                "call {id} started: {} {}",
                                closure.format(&self.heap),
                                args.iter().map(|arg| arg.format(&self.heap)).join(" ")
                            ),
                            StoredFiberEvent::CallEnded { return_value } =>
                                format!("call ended: {}", return_value.format(&self.heap)),
                            StoredFiberEvent::NeedsStarted {
                                id,
                                condition,
                                reason,
                            } => format!(
                                "needs {id} started: needs {} {}",
                                condition.format(&self.heap),
                                reason.format(&self.heap)
                            ),
                            StoredFiberEvent::NeedsEnded => "needs ended".to_string(),
                        }
                    ),
                }
            )?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;
use std::mem;

fn pointer_size() -> usize {
    let tmp = 0u8;
    let boxed = Box::new(tmp);
    mem::size_of_val(&boxed)
}

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let mut file = File::open(inp).unwrap();
    // read 12 bytes from the Blender file
    let mut buf = [0u8; 12];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut header = String::new();
    for e in buf.iter() {
        header.push(*e as char);
    }
    println!("header = \"{}\"", header);
    // compare first 7 chars to "BLENDER"
    if header.len() >= 7 {
        // create copy of header without moving value
        let mut blender = String::new();
        blender = blender + &header;
        blender.truncate(7); // first 7 chars
        if blender == "BLENDER" {
            println!("starts with \"BLENDER\" ...");
            // switch to byte copy of String representation
            let bytes = header.into_bytes();
            // check 8th char of header
            println!("8th char of header = '{}'", bytes[7] as char);
            // check for 32-bit pointers vs. 64-bit pointers
            let ptr_size = pointer_size();
            println!("size of pointer in bytes: {}", ptr_size);
            let mut ptr_size_differs = false;
            if bytes[7] as char == '_' {
                // 32-bit pointers expected
                if ptr_size != 4 {
                    ptr_size_differs = true;
                }
                println!("32-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            } else {
                // 64-bit pointers expected
                if ptr_size != 8 {
                    ptr_size_differs = true;
                }
                println!("64-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            }
            // little endian or big endian?
            let mut l_endian;
            let endian_test = 1u16; // value one stored in two bytes
            if endian_test.to_le() != endian_test {
                l_endian = false;
            } else {
                // if we are on a little endian machine to_le() is a no-op
                l_endian = true;
            }
            println!("l_endian = {}", l_endian);
            let mut switch_endian;
            if bytes[8] as char == 'v' {
                // file stores little endian
                if !l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            } else {
                // file stores big endian
                if l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            }
            println!("switch_endian = {}", switch_endian);
            // get the version number
            let last3c = vec!(bytes[9], bytes[10], bytes[11]);
            let version = String::from_utf8(last3c).unwrap(); // last 3 chars
            println!("version = {}", version);
            if ptr_size == 8 && (ptr_size_differs | switch_endian) == false {
                // assumes 64-bit pointers (in file as well as on platform)
                // we also don't handle endian switching (yet)
                return read_remaining_blend_file(file);
            } else {
                println!("TODO: ptr_size = {}", ptr_size);
                println!("TODO: ptr_size_differs = {}", ptr_size_differs);
                println!("TODO: switch_endian = {}", switch_endian);
            }
        } else {
            println!("ERROR: FILE is not a Blender file");
        }
    } else {
        println!("ERROR: FILE is not a Blender file");
    }
    Ok(())
}

fn read_remaining_blend_file(mut file: File) -> io::Result<()> {
    // read_file_dna
    // 4 * int + 64-bit pointer
    let mut buf = [0u8; 24];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut bhead = String::new();
    for e in buf.iter() {
        bhead.push(*e as char);
    }
    let mut code = String::new();
    code.push_str(&bhead); // copy
    code.truncate(4); // first 4 chars
    println!("code = {}", code);
    // first int is 'len'
    let mut len: u32 = 0;
    len += (buf[4] as u32) <<  0;
    len += (buf[5] as u32) <<  8;
    len += (buf[6] as u32) << 16;
    len += (buf[7] as u32) << 24;
    println!("len = {}", len);
    // TODO: if code == "ENDB" && len == 0 { ... }
    if code == "ENDB" && len == 0 {
        println!("TODO: code == \"{}\"", code);
    } else if code == "DNA1" {
        println!("TODO: code == \"{}\"", code);
    } else {
        let mut tc = String::new();
        tc.push_str(&bhead); // copy
        tc.truncate(2); // first 4 chars
        if tc == "CA" {
            println!("TODO: tc == \"{}\"", tc);
        } else {
            let mut dummy: Vec<u8> = Vec::with_capacity(len as usize);
            for i in 0..len {
                dummy.push(i as u8);
            }
            let mut buf = &mut dummy;
            let bytes_read = file.read(buf).unwrap();
            if bytes_read != buf.len() {
                println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
                return Ok(());
            } else {
                println!("{} bytes read ...", bytes_read);
            }
        }
    }
    // WORK: read remaining file
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    match read_blend_file(inp) {
        Ok(_) => { ; }
        Err(f) => { panic!(f.to_string()) }
    };
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<commit_msg>Use a loop and break for all three TODO branches.<commit_after>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;
use std::mem;

fn pointer_size() -> usize {
    let tmp = 0u8;
    let boxed = Box::new(tmp);
    mem::size_of_val(&boxed)
}

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let mut file = File::open(inp).unwrap();
    // read 12 bytes from the Blender file
    let mut buf = [0u8; 12];
    let bytes_read = file.read(&mut buf).unwrap();
    if bytes_read != buf.len() {
        println!("{} bytes read, but {} expected ...", bytes_read, buf.len());
        return Ok(());
    }
    let mut header = String::new();
    for e in buf.iter() {
        header.push(*e as char);
    }
    println!("header = \"{}\"", header);
    // compare first 7 chars to "BLENDER"
    if header.len() >= 7 {
        // create copy of header without moving value
        let mut blender = String::new();
        blender = blender + &header;
        blender.truncate(7); // first 7 chars
        if blender == "BLENDER" {
            println!("starts with \"BLENDER\" ...");
            // switch to byte copy of String representation
            let bytes = header.into_bytes();
            // check 8th char of header
            println!("8th char of header = '{}'", bytes[7] as char);
            // check for 32-bit pointers vs. 64-bit pointers
            let ptr_size = pointer_size();
            println!("size of pointer in bytes: {}", ptr_size);
            let mut ptr_size_differs = false;
            if bytes[7] as char == '_' {
                // 32-bit pointers expected
                if ptr_size != 4 {
                    ptr_size_differs = true;
                }
                println!("32-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            } else {
                // 64-bit pointers expected
                if ptr_size != 8 {
                    ptr_size_differs = true;
                }
                println!("64-bit pointers in file, pointer size differs? {}",
                         ptr_size_differs);
            }
            // little endian or big endian?
            let mut l_endian;
            let endian_test = 1u16; // value one stored in two bytes
            if endian_test.to_le() != endian_test {
                l_endian = false;
            } else {
                // if we are on a little endian machine to_le() is a no-op
                l_endian = true;
            }
            println!("l_endian = {}", l_endian);
            let mut switch_endian;
            if bytes[8] as char == 'v' {
                // file stores little endian
                if !l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            } else {
                // file stores big endian
                if l_endian {
                    switch_endian = true;
                } else {
                    switch_endian = false;
                }
            }
            println!("switch_endian = {}", switch_endian);
            // get the version number
            let last3c = vec!(bytes[9], bytes[10], bytes[11]);
            let version = String::from_utf8(last3c).unwrap(); // last 3 chars
            println!("version = {}", version);
            if ptr_size == 8 && (ptr_size_differs | switch_endian) == false {
                // assumes 64-bit pointers (in file as well as on platform)
                // we also don't handle endian switching (yet)
                return read_remaining_blend_file(file);
            } else {
                println!("TODO: ptr_size = {}", ptr_size);
                println!("TODO: ptr_size_differs = {}", ptr_size_differs);
                println!("TODO: switch_endian = {}", switch_endian);
            }
        } else {
            println!("ERROR: FILE is not a Blender file");
        }
    } else {
        println!("ERROR: FILE is not a Blender file");
    }
    Ok(())
}

fn read_remaining_blend_file(mut file: File) -> io::Result<()> {
    loop {
        // read_file_dna
        // 4 * int + 64-bit pointer
        let mut buf = [0u8; 24];
        let bytes_read = file.read(&mut buf).unwrap();
        if bytes_read != buf.len() {
            println!("{} bytes read, but {} expected ...",
                     bytes_read, buf.len());
            return Ok(());
        }
        let mut bhead = String::new();
        for e in buf.iter() {
            bhead.push(*e as char);
        }
        let mut code = String::new();
        code.push_str(&bhead); // copy
        code.truncate(4); // first 4 chars
        println!("code = {}", code);
        // first int is 'len'
        let mut len: u32 = 0;
        len += (buf[4] as u32) <<  0;
        len += (buf[5] as u32) <<  8;
        len += (buf[6] as u32) << 16;
        len += (buf[7] as u32) << 24;
        println!("len = {}", len);
        // TODO: if code == "ENDB" && len == 0 { ... }
        if code == "ENDB" && len == 0 {
            println!("TODO: code == \"{}\"", code);
            break;
        } else if code == "DNA1" {
            println!("TODO: code == \"{}\"", code);
            break;
        } else {
            let mut tc = String::new();
            tc.push_str(&bhead); // copy
            tc.truncate(2); // first 4 chars
            if tc == "CA" {
                println!("TODO: tc == \"{}\"", tc);
                break;
            } else {
                let mut dummy: Vec<u8> = Vec::with_capacity(len as usize);
                for i in 0..len {
                    dummy.push(i as u8);
                }
                let mut buf = &mut dummy;
                let bytes_read = file.read(buf).unwrap();
                if bytes_read != buf.len() {
                    println!("{} bytes read, but {} expected ...",
                             bytes_read, buf.len());
                    return Ok(());
                } else {
                    println!("{} bytes read ...", bytes_read);
                }
            }
        }
    }
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    match read_blend_file(inp) {
        Ok(_) => { ; }
        Err(f) => { panic!(f.to_string()) }
    };
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<|endoftext|>


<commit_before>#[feature(globs)];
#[feature(struct_variant)];
extern mod std;
extern mod extra;
use extra::getopts;
use std::fmt;
use basics::*;
use policies::TimingPolicy;

mod basics;
mod policies;
mod raft;
mod sim;

fn main() {
    let args = std::os::args();
    let opts = [
        getopts::optflag("h"),
        getopts::optflag("help"),
        getopts::optopt("samples"),
        getopts::optopt("servers"),
        getopts::optopt("tasks"),
        getopts::optopt("timing"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m },
        Err(f) => { fail!(f.to_err_msg()) },
    };
    let usage = || {
        println!("Usage: {} [options]", args[0]);
        println!("-h, --help       Print this help message");
        println!("--samples=N      Number of simulations (default 10,000)");
        println!("--servers=N      Simulate cluster with N servers");
        println!("--tasks=N        Number of parallel jobs (default {})",
                 std::rt::default_sched_threads() - 1);
        println!("--timing=POLICY  Number of timing policy (default LAN)");
    };
    if matches.opt_present("h") || matches.opt_present("help") {
        usage();
        return;
    }
    if !matches.free.is_empty() {
        usage();
        fail!(format!("Extra arguments: {}", matches.free.len()));
    }
    let num_samples : uint = match matches.opt_str("samples") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of samples from '{}'", s));
            },
        }},
        None => { 10000 },
    };
    let num_servers : uint = match matches.opt_str("servers") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of servers from '{}'", s));
            },
        }},
        None => { 5 },
    };
    let num_tasks : uint = match matches.opt_str("tasks") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of tasks from '{}'", s));
            },
        }},
        None => { std::rt::default_sched_threads() - 1 },
    };
    let timing : ~str = match matches.opt_str("timing") {
        Some(s) => { s },
        None => { ~"LAN" },
    };

    println!("Servers: {}", num_servers)
    println!("Tasks:   {}", num_tasks)
    println!("Trials:  {}", num_samples)
    println!("Timing:  {}", timing)

    let start_ns = extra::time::precise_time_ns();

    let (port, chan): (Port<~[Time]>, Chan<~[Time]>) = stream();
    let chan = std::comm::SharedChan::new(chan);
    for tid in range(0, num_tasks) {
        let child_chan = chan.clone();
        let child_timing = timing.clone();
        do spawn || {
            let mut samples = ~[];
            let n = if tid == 0 {
                // get the odd one left over
                num_samples - (num_samples / num_tasks) * (num_tasks - 1)
            } else {
                num_samples / num_tasks
            };
            samples.reserve(n);
            do n.times {
                // TODO: may be better to reuse these timing policies.
                let p = policies::make_timing_policy(child_timing);
                let sample = sim::simulate(num_servers, p);
                samples.push(sample);
            }
            extra::sort::tim_sort(samples);
            child_chan.send(samples);
        }
    }

    let mut samples = ~[];
    samples.reserve(num_samples);
    do num_tasks.times {
        samples.push_all(port.recv());
    }
    extra::sort::tim_sort(samples);

    let end_ns = extra::time::precise_time_ns();
    let elapsed_ns = end_ns - start_ns;

    println!("Min:     {} ticks", samples[0]);
    println!("Median:  {} ticks", samples[samples.len()/2]);
    println!("Max:     {} ticks", samples[samples.len()-1]);
    println!("Wall:    {:.2f} s",     elapsed_ns as f64 / 1e9);
}
<commit_msg>Enable non-mt<commit_after>#[feature(globs)];
#[feature(struct_variant)];
extern mod std;
extern mod extra;
use extra::getopts;
use basics::*;
use policies::TimingPolicy;

mod basics;
mod policies;
mod raft;
mod sim;

fn main() {
    let args = std::os::args();
    let opts = [
        getopts::optflag("h"),
        getopts::optflag("help"),
        getopts::optopt("samples"),
        getopts::optopt("servers"),
        getopts::optopt("tasks"),
        getopts::optopt("timing"),
    ];
    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m },
        Err(f) => { fail!(f.to_err_msg()) },
    };
    let usage = || {
        println!("Usage: {} [options]", args[0]);
        println!("-h, --help       Print this help message");
        println!("--samples=N      Number of simulations (default 10,000)");
        println!("--servers=N      Simulate cluster with N servers");
        println!("--tasks=N        Number of parallel jobs (default {})",
                 std::rt::default_sched_threads() - 1);
        println!("--timing=POLICY  Number of timing policy (default LAN)");
    };
    if matches.opt_present("h") || matches.opt_present("help") {
        usage();
        return;
    }
    if !matches.free.is_empty() {
        usage();
        fail!(format!("Extra arguments: {}", matches.free.len()));
    }
    let num_samples : uint = match matches.opt_str("samples") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of samples from '{}'", s));
            },
        }},
        None => { 10000 },
    };
    let num_servers : uint = match matches.opt_str("servers") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of servers from '{}'", s));
            },
        }},
        None => { 5 },
    };
    let num_tasks : uint = match matches.opt_str("tasks") {
        Some(s) => { match std::from_str::from_str(s) {
            Some(i) => { i },
            None => {
                usage();
                fail!(format!("Couldn't parse number of tasks from '{}'", s));
            },
        }},
        None => { std::rt::default_sched_threads() - 1 },
    };
    let timing : ~str = match matches.opt_str("timing") {
        Some(s) => { s },
        None => { ~"LAN" },
    };

    println!("Servers: {}", num_servers)
    println!("Tasks:   {}", num_tasks)
    println!("Trials:  {}", num_samples)
    println!("Timing:  {}", timing)

    let start_ns = extra::time::precise_time_ns();

    let samples = if num_tasks <= 1 {
        run_task(num_samples, num_servers, timing)
    } else {
        let (port, chan): (Port<~[Time]>, Chan<~[Time]>) = stream();
        let chan = std::comm::SharedChan::new(chan);
        for tid in range(0, num_tasks) {
            let child_chan = chan.clone();
            let child_timing = timing.clone();
            do spawn || {
                let n = if tid == 0 {
                    // get the odd one left over
                    num_samples - (num_samples / num_tasks) * (num_tasks - 1)
                } else {
                    num_samples / num_tasks
                };
                child_chan.send(run_task(n, num_servers, child_timing));
            }
        }

        let mut samples = ~[];
        samples.reserve(num_samples);
        do num_tasks.times {
            samples.push_all(port.recv());
        }
        extra::sort::tim_sort(samples);
        samples
    };

    let end_ns = extra::time::precise_time_ns();
    let elapsed_ns = end_ns - start_ns;

    println!("Min:     {} ticks", samples[0]);
    println!("Median:  {} ticks", samples[samples.len()/2]);
    println!("Max:     {} ticks", samples[samples.len()-1]);
    println!("Wall:    {:.2f} s",     elapsed_ns as f64 / 1e9);
}

fn run_task(n: uint, num_servers: uint, timing: &str) -> ~[Time] {
    let mut samples = ~[];
    samples.reserve(n);
    do n.times {
        // TODO: may be better to reuse these timing policies.
        let p = policies::make_timing_policy(timing);
        let sample = sim::simulate(num_servers, p);
        samples.push(sample);
    }
    extra::sort::tim_sort(samples);
    return samples;
}
<|endoftext|>


<commit_before>use boxcars::crc::calc_crc;
use boxcars::*;
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};

fn bench_crc(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/rumble.replay");
    let mut group = c.benchmark_group("crc_throughput");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.bench_function("bench_crc", |b| {
        let data = include_bytes!("../assets/replays/good/rumble.replay");
        b.iter(|| {
            black_box(calc_crc(&data[..]));
        })
    });
    group.finish();
}

fn bench_json_serialization(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let json_data_bytes = 19416972_u64;

    let mut group = c.benchmark_group("json_throughput");
    group.sample_size(10);
    group.throughput(Throughput::Bytes(json_data_bytes));
    group.bench_function("bench_json_serialization", |b| {
        let mut bytes = Vec::new();
        let replay = ParserBuilder::new(data)
            .on_error_check_crc()
            .parse()
            .unwrap();
        serde_json::to_writer(&mut bytes, &replay).unwrap();
        assert!(json_data_bytes == bytes.len() as u64);
        unsafe {
            bytes.set_len(0);
        };

        b.iter(|| {
            black_box(serde_json::to_writer(&mut bytes, &replay).unwrap());
            unsafe {
                bytes.set_len(0);
            };
        })
    });
    group.finish();
}

fn bench_parse_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .always_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .unwrap()
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_no_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .unwrap()
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_no_body(c: &mut Criterion) {
    // Throughput not included in this benchmark as it is a bit confusing what the number reporting
    // represents. If parsing the header reports based on the entire size of the replay, it will be
    // some very large number (20 GB/s), else it may look deceptively small
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_no_body");
    group.bench_function("bench_parse_no_crc_no_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .never_parse_network_data()
                    .parse()
                    .unwrap()
            )
        });
    });
    group.finish();
}

fn bench_parse_crc_json(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_json");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(10);
    group.bench_function("bench_parse_crc_json", |b| {
        // allocate a buffer big enough to hold all of the serialized data
        let mut bytes = Vec::with_capacity(2_usize.pow(25));
        b.iter(|| {
            let data = ParserBuilder::new(data).always_check_crc().parse().unwrap();
            black_box(serde_json::to_writer(&mut bytes, &data).unwrap());
            unsafe {
                bytes.set_len(0);
            }
        });
    });
    group.finish();
}

criterion_group!(
    benches,
    bench_crc,
    bench_json_serialization,
    bench_parse_crc_body,
    bench_parse_no_crc_body,
    bench_parse_no_crc_no_body,
    bench_parse_crc_json,
);

criterion_main!(benches);
<commit_msg>JSON serialization benchmark panics with descriptive message<commit_after>use boxcars::crc::calc_crc;
use boxcars::*;
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};

fn bench_crc(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/rumble.replay");
    let mut group = c.benchmark_group("crc_throughput");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.bench_function("bench_crc", |b| {
        let data = include_bytes!("../assets/replays/good/rumble.replay");
        b.iter(|| {
            black_box(calc_crc(&data[..]));
        })
    });
    group.finish();
}

fn bench_json_serialization(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let json_data_bytes = 19416972_u64;

    let mut group = c.benchmark_group("json_throughput");
    group.sample_size(10);
    group.throughput(Throughput::Bytes(json_data_bytes));
    group.bench_function("bench_json_serialization", |b| {
        let mut bytes = Vec::new();
        let replay = ParserBuilder::new(data)
            .on_error_check_crc()
            .parse()
            .unwrap();
        serde_json::to_writer(&mut bytes, &replay).unwrap();
        assert!(
            json_data_bytes == bytes.len() as u64,
            "update json benchmark with latest throughput size: {}",
            bytes.len()
        );
        unsafe {
            bytes.set_len(0);
        };

        b.iter(|| {
            black_box(serde_json::to_writer(&mut bytes, &replay).unwrap());
            unsafe {
                bytes.set_len(0);
            };
        })
    });
    group.finish();
}

fn bench_parse_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .always_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .unwrap()
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_body(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_body");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(20);
    group.bench_function("bench_parse_no_crc_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .unwrap()
            )
        });
    });
    group.finish();
}

fn bench_parse_no_crc_no_body(c: &mut Criterion) {
    // Throughput not included in this benchmark as it is a bit confusing what the number reporting
    // represents. If parsing the header reports based on the entire size of the replay, it will be
    // some very large number (20 GB/s), else it may look deceptively small
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_no_crc_no_body");
    group.bench_function("bench_parse_no_crc_no_body", |b| {
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .never_parse_network_data()
                    .parse()
                    .unwrap()
            )
        });
    });
    group.finish();
}

fn bench_parse_crc_json(c: &mut Criterion) {
    let data = include_bytes!("../assets/replays/good/3381.replay");
    let mut group = c.benchmark_group("parse_crc_json");
    group.throughput(Throughput::Bytes(data.len() as u64));
    group.sample_size(10);
    group.bench_function("bench_parse_crc_json", |b| {
        // allocate a buffer big enough to hold all of the serialized data
        let mut bytes = Vec::with_capacity(2_usize.pow(25));
        b.iter(|| {
            let data = ParserBuilder::new(data).always_check_crc().parse().unwrap();
            black_box(serde_json::to_writer(&mut bytes, &data).unwrap());
            unsafe {
                bytes.set_len(0);
            }
        });
    });
    group.finish();
}

criterion_group!(
    benches,
    bench_crc,
    bench_json_serialization,
    bench_parse_crc_body,
    bench_parse_no_crc_body,
    bench_parse_no_crc_no_body,
    bench_parse_crc_json,
);

criterion_main!(benches);
<|endoftext|>


<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::DiskType;

use crate::sys::disk::{new_disk, Disk};
use crate::sys::processor::{self, Processor, Query};

use std::ffi::OsStr;
use std::mem::{size_of, zeroed};

use winapi::{ctypes::c_void, um::winbase::DRIVE_REMOVABLE};

use winapi::shared::minwindef::{DWORD, MAX_PATH, TRUE};
use winapi::um::fileapi::{
    CreateFileW, GetDriveTypeW, GetLogicalDrives, GetVolumeInformationW, OPEN_EXISTING,
};
use winapi::um::handleapi::CloseHandle;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO};
use winapi::um::winbase::DRIVE_FIXED;
use winapi::um::winioctl::{
    DEVICE_TRIM_DESCRIPTOR, IOCTL_DISK_GET_PARTITION_INFO_EX, IOCTL_STORAGE_QUERY_PROPERTY,
    PARTITION_INFORMATION_EX, STORAGE_PROPERTY_QUERY,
};
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE};

pub(crate) struct KeyHandler {
    pub unique_id: String,
}

impl KeyHandler {
    pub fn new(unique_id: String) -> KeyHandler {
        KeyHandler { unique_id }
    }
}

pub(crate) fn init_processors() -> (Vec<Processor>, String, String) {
    unsafe {
        let mut sys_info: SYSTEM_INFO = zeroed();
        GetSystemInfo(&mut sys_info);
        let (vendor_id, brand) = processor::get_vendor_id_and_brand(&sys_info);
        let frequencies = processor::get_frequencies(sys_info.dwNumberOfProcessors as usize);
        let mut ret = Vec::with_capacity(sys_info.dwNumberOfProcessors as usize + 1);
        for nb in 0..sys_info.dwNumberOfProcessors {
            ret.push(Processor::new_with_values(
                &format!("CPU {}", nb + 1),
                vendor_id.clone(),
                brand.clone(),
                frequencies[nb as usize],
            ));
        }
        (ret, vendor_id, brand)
    }
}

pub unsafe fn open_drive(drive_name: &[u16], open_rights: DWORD) -> HANDLE {
    CreateFileW(
        drive_name.as_ptr(),
        open_rights,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        std::ptr::null_mut(),
        OPEN_EXISTING,
        0,
        std::ptr::null_mut(),
    )
}

pub unsafe fn get_drive_size(handle: HANDLE) -> u64 {
    let mut pdg: PARTITION_INFORMATION_EX = std::mem::zeroed();
    let mut junk = 0;
    let result = DeviceIoControl(
        handle,
        IOCTL_DISK_GET_PARTITION_INFO_EX,
        std::ptr::null_mut(),
        0,
        &mut pdg as *mut PARTITION_INFORMATION_EX as *mut c_void,
        size_of::<PARTITION_INFORMATION_EX>() as DWORD,
        &mut junk,
        std::ptr::null_mut(),
    );
    if result == TRUE {
        *pdg.PartitionLength.QuadPart() as u64
    } else {
        0
    }
}

pub unsafe fn get_disks() -> Vec<Disk> {
    let drives = GetLogicalDrives();
    if drives == 0 {
        return Vec::new();
    }

    #[cfg(feature = "multithread")]
    use rayon::iter::ParallelIterator;

    crate::utils::into_iter(0..DWORD::BITS)
        .filter_map(|x| {
            if (drives >> x) & 1 == 0 {
                return None;
            }
            let mount_point = [b'A' as u16 + x as u16, b':' as u16, b'\\' as u16, 0];

            let drive_type = GetDriveTypeW(mount_point.as_ptr());

            let is_removable = drive_type == DRIVE_REMOVABLE;

            if drive_type != DRIVE_FIXED && drive_type != DRIVE_REMOVABLE {
                return None;
            }
            let mut name = [0u16; MAX_PATH + 1];
            let mut file_system = [0u16; 32];
            if GetVolumeInformationW(
                mount_point.as_ptr(),
                name.as_mut_ptr(),
                name.len() as DWORD,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                file_system.as_mut_ptr(),
                file_system.len() as DWORD,
            ) == 0
            {
                return None;
            }
            let mut pos = 0;
            for x in name.iter() {
                if *x == 0 {
                    break;
                }
                pos += 1;
            }
            let name = String::from_utf16_lossy(&name[..pos]);
            let name = OsStr::new(&name);

            pos = 0;
            for x in file_system.iter() {
                if *x == 0 {
                    break;
                }
                pos += 1;
            }
            let file_system: Vec<u8> = file_system[..pos].iter().map(|x| *x as u8).collect();

            let drive_name = [
                b'\\' as u16,
                b'\\' as u16,
                b'.' as u16,
                b'\\' as u16,
                b'A' as u16 + x as u16,
                b':' as u16,
                0,
            ];
            let handle = open_drive(&drive_name, 0);
            if handle == INVALID_HANDLE_VALUE {
                CloseHandle(handle);
                return new_disk(
                    name,
                    &mount_point,
                    &file_system,
                    DiskType::Unknown(-1),
                    0,
                    is_removable,
                );
            }
            let disk_size = get_drive_size(handle);
            /*let mut spq_trim: STORAGE_PROPERTY_QUERY = std::mem::zeroed();
            spq_trim.PropertyId = StorageDeviceTrimProperty;
            spq_trim.QueryType = PropertyStandardQuery;
            let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();*/
            let mut spq_trim = STORAGE_PROPERTY_QUERY {
                PropertyId: 8,
                QueryType: 0,
                AdditionalParameters: [0],
            };
            let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();

            let mut dw_size = 0;
            if DeviceIoControl(
                handle,
                IOCTL_STORAGE_QUERY_PROPERTY,
                &mut spq_trim as *mut STORAGE_PROPERTY_QUERY as *mut c_void,
                size_of::<STORAGE_PROPERTY_QUERY>() as DWORD,
                &mut dtd as *mut DEVICE_TRIM_DESCRIPTOR as *mut c_void,
                size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD,
                &mut dw_size,
                std::ptr::null_mut(),
            ) == 0
                || dw_size != size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD
            {
                CloseHandle(handle);
                return new_disk(
                    name,
                    &mount_point,
                    &file_system,
                    DiskType::Unknown(-1),
                    disk_size,
                    is_removable,
                );
            }
            let is_ssd = dtd.TrimEnabled != 0;
            CloseHandle(handle);
            new_disk(
                name,
                &mount_point,
                &file_system,
                if is_ssd { DiskType::SSD } else { DiskType::HDD },
                disk_size,
                is_removable,
            )
        })
        .collect::<Vec<_>>()
}

pub(crate) fn add_english_counter(
    s: String,
    query: &mut Query,
    keys: &mut Option<KeyHandler>,
    counter_name: String,
) {
    let mut full = s.encode_utf16().collect::<Vec<_>>();
    full.push(0);
    if query.add_english_counter(&counter_name, full) {
        *keys = Some(KeyHandler::new(counter_name));
    }
}
<commit_msg>Add a fallback to get_drive_size on Windows<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::DiskType;

use crate::sys::disk::{new_disk, Disk};
use crate::sys::processor::{self, Processor, Query};

use std::ffi::OsStr;
use std::mem::{size_of, zeroed};

use winapi::shared::ntdef::ULARGE_INTEGER;
use winapi::{ctypes::c_void, um::winbase::DRIVE_REMOVABLE};

use winapi::shared::minwindef::{DWORD, MAX_PATH, TRUE};
use winapi::um::fileapi::{
    CreateFileW, GetDiskFreeSpaceExW, GetDriveTypeW, GetLogicalDrives, GetVolumeInformationW,
    OPEN_EXISTING,
};
use winapi::um::handleapi::CloseHandle;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO};
use winapi::um::winbase::DRIVE_FIXED;
use winapi::um::winioctl::{
    DEVICE_TRIM_DESCRIPTOR, IOCTL_DISK_GET_PARTITION_INFO_EX, IOCTL_STORAGE_QUERY_PROPERTY,
    PARTITION_INFORMATION_EX, STORAGE_PROPERTY_QUERY,
};
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE};

pub(crate) struct KeyHandler {
    pub unique_id: String,
}

impl KeyHandler {
    pub fn new(unique_id: String) -> KeyHandler {
        KeyHandler { unique_id }
    }
}

pub(crate) fn init_processors() -> (Vec<Processor>, String, String) {
    unsafe {
        let mut sys_info: SYSTEM_INFO = zeroed();
        GetSystemInfo(&mut sys_info);
        let (vendor_id, brand) = processor::get_vendor_id_and_brand(&sys_info);
        let frequencies = processor::get_frequencies(sys_info.dwNumberOfProcessors as usize);
        let mut ret = Vec::with_capacity(sys_info.dwNumberOfProcessors as usize + 1);
        for nb in 0..sys_info.dwNumberOfProcessors {
            ret.push(Processor::new_with_values(
                &format!("CPU {}", nb + 1),
                vendor_id.clone(),
                brand.clone(),
                frequencies[nb as usize],
            ));
        }
        (ret, vendor_id, brand)
    }
}

pub unsafe fn open_drive(drive_name: &[u16], open_rights: DWORD) -> HANDLE {
    CreateFileW(
        drive_name.as_ptr(),
        open_rights,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        std::ptr::null_mut(),
        OPEN_EXISTING,
        0,
        std::ptr::null_mut(),
    )
}

pub unsafe fn get_drive_size(handle: HANDLE, mount_point: &[u16]) -> u64 {
    let mut pdg: PARTITION_INFORMATION_EX = std::mem::zeroed();
    let mut junk = 0;
    let result = DeviceIoControl(
        handle,
        IOCTL_DISK_GET_PARTITION_INFO_EX,
        std::ptr::null_mut(),
        0,
        &mut pdg as *mut PARTITION_INFORMATION_EX as *mut c_void,
        size_of::<PARTITION_INFORMATION_EX>() as DWORD,
        &mut junk,
        std::ptr::null_mut(),
    );
    if result == TRUE {
        *pdg.PartitionLength.QuadPart() as u64
    } else {
        // Sometimes DeviceIoControl can fail, so try using GetDiskFreeSpaceEx as a backup
        let mut tmp: ULARGE_INTEGER = std::mem::zeroed();
        if GetDiskFreeSpaceExW(
            mount_point.as_ptr(),
            std::ptr::null_mut(),
            &mut tmp,
            std::ptr::null_mut(),
        ) != 0
        {
            *tmp.QuadPart() as u64
        } else {
            0
        }
    }
}

pub unsafe fn get_disks() -> Vec<Disk> {
    let drives = GetLogicalDrives();
    if drives == 0 {
        return Vec::new();
    }

    #[cfg(feature = "multithread")]
    use rayon::iter::ParallelIterator;

    crate::utils::into_iter(0..DWORD::BITS)
        .filter_map(|x| {
            if (drives >> x) & 1 == 0 {
                return None;
            }
            let mount_point = [b'A' as u16 + x as u16, b':' as u16, b'\\' as u16, 0];

            let drive_type = GetDriveTypeW(mount_point.as_ptr());

            let is_removable = drive_type == DRIVE_REMOVABLE;

            if drive_type != DRIVE_FIXED && drive_type != DRIVE_REMOVABLE {
                return None;
            }
            let mut name = [0u16; MAX_PATH + 1];
            let mut file_system = [0u16; 32];
            if GetVolumeInformationW(
                mount_point.as_ptr(),
                name.as_mut_ptr(),
                name.len() as DWORD,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                file_system.as_mut_ptr(),
                file_system.len() as DWORD,
            ) == 0
            {
                return None;
            }
            let mut pos = 0;
            for x in name.iter() {
                if *x == 0 {
                    break;
                }
                pos += 1;
            }
            let name = String::from_utf16_lossy(&name[..pos]);
            let name = OsStr::new(&name);

            pos = 0;
            for x in file_system.iter() {
                if *x == 0 {
                    break;
                }
                pos += 1;
            }
            let file_system: Vec<u8> = file_system[..pos].iter().map(|x| *x as u8).collect();

            let drive_name = [
                b'\\' as u16,
                b'\\' as u16,
                b'.' as u16,
                b'\\' as u16,
                b'A' as u16 + x as u16,
                b':' as u16,
                0,
            ];
            let handle = open_drive(&drive_name, 0);
            if handle == INVALID_HANDLE_VALUE {
                CloseHandle(handle);
                return new_disk(
                    name,
                    &mount_point,
                    &file_system,
                    DiskType::Unknown(-1),
                    0,
                    is_removable,
                );
            }
            let disk_size = get_drive_size(handle, &mount_point);
            /*let mut spq_trim: STORAGE_PROPERTY_QUERY = std::mem::zeroed();
            spq_trim.PropertyId = StorageDeviceTrimProperty;
            spq_trim.QueryType = PropertyStandardQuery;
            let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();*/
            let mut spq_trim = STORAGE_PROPERTY_QUERY {
                PropertyId: 8,
                QueryType: 0,
                AdditionalParameters: [0],
            };
            let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();

            let mut dw_size = 0;
            if DeviceIoControl(
                handle,
                IOCTL_STORAGE_QUERY_PROPERTY,
                &mut spq_trim as *mut STORAGE_PROPERTY_QUERY as *mut c_void,
                size_of::<STORAGE_PROPERTY_QUERY>() as DWORD,
                &mut dtd as *mut DEVICE_TRIM_DESCRIPTOR as *mut c_void,
                size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD,
                &mut dw_size,
                std::ptr::null_mut(),
            ) == 0
                || dw_size != size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD
            {
                CloseHandle(handle);
                return new_disk(
                    name,
                    &mount_point,
                    &file_system,
                    DiskType::Unknown(-1),
                    disk_size,
                    is_removable,
                );
            }
            let is_ssd = dtd.TrimEnabled != 0;
            CloseHandle(handle);
            new_disk(
                name,
                &mount_point,
                &file_system,
                if is_ssd { DiskType::SSD } else { DiskType::HDD },
                disk_size,
                is_removable,
            )
        })
        .collect::<Vec<_>>()
}

pub(crate) fn add_english_counter(
    s: String,
    query: &mut Query,
    keys: &mut Option<KeyHandler>,
    counter_name: String,
) {
    let mut full = s.encode_utf16().collect::<Vec<_>>();
    full.push(0);
    if query.add_english_counter(&counter_name, full) {
        *keys = Some(KeyHandler::new(counter_name));
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;

use std::io;

use actix::prelude::Addr;
use actix_files::{Files, NamedFile};
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer, Result};
use dotenv::dotenv;
use futures::future;
use futures::future::Future;
use serde::Serialize;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct SecretBody {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: usize,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ApiError<T: Serialize> {
    error: T,
}

impl<T: Serialize> ApiError<T> {
    pub fn from(err: T) -> ApiError<T> {
        ApiError { error: err }
    }
}

#[derive(Debug, Clone)]
struct AppConfig {
    redis_key_prefix: String,
    max_secret_length: usize,
    max_secret_expiration: usize,
}

struct AppState {
    redis: Addr<RedisActor>,
    config: AppConfig,
}

impl AppState {
    fn format_key(self: &AppState, id: &str) -> String {
        format!("{}.{}", self.config.redis_key_prefix, id)
    }

    fn create_secret(
        self: &AppState,
        id: Uuid,
        body: &str,
        expiration_seconds: usize,
    ) -> impl Future<Item = bool, Error = actix_web::Error> {
        let redis_key = self.format_key(&id.to_string());

        self.redis
            .send(Command(resp_array![
                "SETEX",
                redis_key,
                expiration_seconds.to_string(),
                body
            ]))
            .map_err(actix_web::Error::from)
            .and_then(|res: Result<RespValue, actix_redis::Error>| match res {
                Ok(RespValue::SimpleString(_)) => Ok(true),

                err => {
                    println!("unexpected response to creating secret: {:?}", err);
                    Err(actix_web::error::ErrorInternalServerError(
                        "failed to create secret",
                    ))
                }
            })
    }

    fn fetch_secret(
        self: &AppState,
        id: &str,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let redis_key = self.format_key(id);

        self.redis
            .send(Command(resp_array!["GET", redis_key]))
            .map_err(actix_web::Error::from)
            .map(|res| match res {
                Ok(RespValue::BulkString(bytes)) => {
                    let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                    Some(str)
                }

                _ => None,
            })
    }

    fn consume_secret(
        self: &AppState,
        id: &str,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let redis_key = self.format_key(id);

        let fetch_secret = self.fetch_secret(&redis_key);
        let delete_secret = self
            .redis
            .send(Command(resp_array!["DEL", redis_key]))
            .map_err(actix_web::Error::from);

        fetch_secret.and_then(|res| delete_secret.map(|_| res))
    }
}

fn create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validate = if body.content.len() > state.config.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > state.config.max_secret_expiration {
        Err("expiration too long")
    } else {
        Ok(())
    };

    future::result(validate)
        .map_err(|err| HttpResponse::BadRequest().json(ApiError::from(err)))
        .map_err(actix_web::Error::from)
        .and_then(move |_| {
            println!("Writing: {:?}: {:?}", id.to_string(), expiration);

            // TODO: Possibly re-encrypt secret here? libsodium or something.

            state
                .create_secret(id, body.content.as_ref(), body.expiration_seconds)
                .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }))
        })
}

fn get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.into_inner();
    println!("Fetching: {:?}", &secret_id);
    state
        .consume_secret(&secret_id)
        .map(|res: Option<String>| match res {
            Some(secret) => HttpResponse::Ok().json(SecretBody { content: secret }),
            None => HttpResponse::NotFound().json(ApiError::from("not found")),
        })
}

fn view_secret_page(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = NamedFile, Error = actix_web::Error> {
    let secret_id = path.into_inner();

    state.fetch_secret(&secret_id).and_then(|res| match res {
        Some(_) => Ok(NamedFile::open("./static/view.html")?),

        None => {
            let file = NamedFile::open("./static/404.html")?
                // TODO: (requires HTTP crate).set_status_code()
                ;

            Ok(file)
        }
    })
}

fn config_from_env() -> AppConfig {
    AppConfig {
        max_secret_length: std::env::var("MAX_SECRET_LENGTH")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        max_secret_expiration: std::env::var("MAX_SECRET_EXPIRATION")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        redis_key_prefix: std::env::var("REDIS_KEY_PREFIX")
            .unwrap_or("tofu:".to_string()),
    }
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    let bind_addr = std::env::var("BIND_ADDR").unwrap_or("127.0.0.1:8080".to_string());

    HttpServer::new(move || {
        let config = config_from_env();
        println!("Booting with config: {:?}", config);

        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            config: config,
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(
                web::resource("/api/secret").route(web::post().to_async(create_secret)),
            )
            .service(
                web::resource("/api/secret/{secret_id}")
                    .route(web::get().to_async(get_secret)),
            )
            .service(
                web::resource("/view/{secret_id}")
                    .route(web::get().to_async(view_secret_page)),
            )
            .service(Files::new("/", "./static/").index_file("index.html"))
    })
    .bind(bind_addr)?
    .run()
}
<commit_msg>Fix double formatting bug<commit_after>#[macro_use]
extern crate redis_async;
#[macro_use]
extern crate serde_derive;

use std::io;

use actix::prelude::Addr;
use actix_files::{Files, NamedFile};
use actix_redis::{Command, RedisActor, RespValue};
use actix_web::{middleware, web, App, HttpResponse, HttpServer, Result};
use dotenv::dotenv;
use futures::future;
use futures::future::Future;
use serde::Serialize;
use uuid::Uuid;

#[derive(Serialize, Deserialize)]
pub struct SecretBody {
    content: String,
}

#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretPostBody {
    content: String,
    expiration_seconds: usize,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct CreateSecretResponseBody {
    id: Uuid,
}

#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ApiError<T: Serialize> {
    error: T,
}

impl<T: Serialize> ApiError<T> {
    pub fn from(err: T) -> ApiError<T> {
        ApiError { error: err }
    }
}

#[derive(Debug, Clone)]
struct AppConfig {
    redis_key_prefix: String,
    max_secret_length: usize,
    max_secret_expiration: usize,
}

struct AppState {
    redis: Addr<RedisActor>,
    config: AppConfig,
}

impl AppState {
    fn format_key(self: &AppState, id: &str) -> String {
        format!("{}.{}", self.config.redis_key_prefix, id)
    }

    fn create_secret(
        self: &AppState,
        id: Uuid,
        body: &str,
        expiration_seconds: usize,
    ) -> impl Future<Item = bool, Error = actix_web::Error> {
        let redis_key = self.format_key(&id.to_string());

        self.redis
            .send(Command(resp_array![
                "SETEX",
                redis_key,
                expiration_seconds.to_string(),
                body
            ]))
            .map_err(actix_web::Error::from)
            .and_then(|res: Result<RespValue, actix_redis::Error>| match res {
                Ok(RespValue::SimpleString(_)) => Ok(true),

                err => {
                    println!("unexpected response to creating secret: {:?}", err);
                    Err(actix_web::error::ErrorInternalServerError(
                        "failed to create secret",
                    ))
                }
            })
    }

    fn fetch_secret(
        self: &AppState,
        id: &str,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let redis_key = self.format_key(id);

        self.redis
            .send(Command(resp_array!["GET", redis_key]))
            .map_err(actix_web::Error::from)
            .map(|res| match res {
                Ok(RespValue::BulkString(bytes)) => {
                    let str = String::from_utf8(bytes).expect("non-utf8 string in redis");
                    Some(str)
                }

                _ => None,
            })
    }

    fn consume_secret(
        self: &AppState,
        id: &str,
    ) -> impl Future<Item = Option<String>, Error = actix_web::Error> {
        let fetch_secret = self.fetch_secret(id);
        let delete_secret = self
            .redis
            .send(Command(resp_array!["DEL", self.format_key(id)]))
            .map_err(actix_web::Error::from);

        fetch_secret.and_then(|res| delete_secret.map(|_| res))
    }
}

fn api_create_secret(
    body: web::Json<CreateSecretPostBody>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let body = body.into_inner();

    let id = Uuid::new_v4();
    let expiration = body.expiration_seconds.to_string();

    let validate = if body.content.len() > state.config.max_secret_length {
        Err("content too large")
    } else if body.expiration_seconds > state.config.max_secret_expiration {
        Err("expiration too long")
    } else {
        Ok(())
    };

    future::result(validate)
        .map_err(|err| HttpResponse::BadRequest().json(ApiError::from(err)))
        .map_err(actix_web::Error::from)
        .and_then(move |_| {
            println!("Writing: {:?}: {:?}", id.to_string(), expiration);

            // TODO: Possibly re-encrypt secret here? libsodium or something.

            state
                .create_secret(id, body.content.as_ref(), body.expiration_seconds)
                .map(move |_| HttpResponse::Ok().json(CreateSecretResponseBody { id }))
        })
}

fn api_get_secret(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = HttpResponse, Error = actix_web::Error> {
    let secret_id = path.into_inner();
    println!("Fetching: {:?}", &secret_id);
    state
        .consume_secret(&secret_id)
        .map(|res: Option<String>| match res {
            Some(secret) => HttpResponse::Ok().json(SecretBody { content: secret }),
            None => HttpResponse::NotFound().json(ApiError::from("not found")),
        })
}

fn view_secret_page(
    path: web::Path<String>,
    state: web::Data<AppState>,
) -> impl Future<Item = NamedFile, Error = actix_web::Error> {
    let secret_id = path.into_inner();

    state.fetch_secret(&secret_id).and_then(|res| match res {
        Some(_) => Ok(NamedFile::open("./static/view.html")?),

        None => {
            let file = NamedFile::open("./static/404.html")?
                // TODO: (requires HTTP crate).set_status_code()
                ;

            Ok(file)
        }
    })
}

fn config_from_env() -> AppConfig {
    AppConfig {
        max_secret_length: std::env::var("MAX_SECRET_LENGTH")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        max_secret_expiration: std::env::var("MAX_SECRET_EXPIRATION")
            .unwrap_or("4096".to_string())
            .parse()
            .expect("unvalid integer value"),
        redis_key_prefix: std::env::var("REDIS_KEY_PREFIX")
            .unwrap_or("tofu:".to_string()),
    }
}

fn main() -> io::Result<()> {
    env_logger::init();
    dotenv().ok();

    let bind_addr = std::env::var("BIND_ADDR").unwrap_or("127.0.0.1:8080".to_string());

    HttpServer::new(move || {
        let config = config_from_env();
        println!("Booting with config: {:?}", config);

        let state = AppState {
            redis: RedisActor::start("localhost:6379"),
            config: config,
        };

        App::new()
            .data(state)
            .wrap(middleware::Logger::default())
            .service(
                web::resource("/api/secret")
                    .route(web::post().to_async(api_create_secret)),
            )
            .service(
                web::resource("/api/secret/{secret_id}")
                    .route(web::get().to_async(api_get_secret)),
            )
            .service(
                web::resource("/view/{secret_id}")
                    .route(web::get().to_async(view_secret_page)),
            )
            .service(Files::new("/", "./static/").index_file("index.html"))
    })
    .bind(bind_addr)?
    .run()
}
<|endoftext|>


<commit_before>use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use decorum::R64;
use nalgebra::Point3;
use plexus::geometry::AsPositionMut;
use plexus::graph::{EdgeMidpoint, FaceView, GraphData, MeshGraph};
use plexus::prelude::*;
use plexus::primitive::Tetragon;
use smallvec::SmallVec;

const DEPTH: usize = 8;

type E3 = Point3<R64>;

pub trait Circumscribe<G> {
    fn circumscribe(self) -> Self;
}

impl<'a, G> Circumscribe<G> for FaceView<&'a mut MeshGraph<G>>
where
    G: EdgeMidpoint + GraphData,
    G::Vertex: AsPositionMut,
{
    // Subdivide the face such that a similar polygon is formed within its
    // perimeter.
    fn circumscribe(self) -> Self {
        // Split each edge, stashing the vertex key and moving to the next arc.
        let arity = self.arity();
        let mut arc = self.into_arc();
        let mut splits = SmallVec::<[_; 4]>::with_capacity(arity);
        for _ in 0..arity {
            let vertex = arc.split_at_midpoint();
            splits.push(vertex.key());
            arc = vertex.into_outgoing_arc().into_next_arc();
        }
        // Split faces along the vertices from each arc split.
        let mut face = arc.into_face().unwrap();
        for (a, b) in splits.into_iter().perimeter() {
            face = face.split(ByKey(a), ByKey(b)).unwrap().into_face().unwrap();
        }
        // Return the face forming the similar polygon.
        face
    }
}

fn tetragon() -> MeshGraph<E3> {
    // Create a graph from a tetragon.
    MeshGraph::from(Tetragon::from([
        (1.0, 0.0, -1.0),
        (-1.0, 0.0, -1.0),
        (-1.0, 0.0, 1.0),
        (1.0, 0.0, 1.0),
    ]))
}

fn subdivide(mut graph: MeshGraph<E3>) {
    // Get the face of the tetragon.
    let key = graph.faces().nth(0).unwrap().key();
    let mut face = graph.face_mut(key).unwrap();

    // Circumscribe and extrude the face recursively.
    for _ in 0..DEPTH {
        face = face.circumscribe().extrude_with_offset(0.5).unwrap();
    }
}

#[allow(unused)]
fn benchmark(criterion: &mut Criterion) {
    criterion.bench_function("subdivide", move |bencher| {
        bencher.iter_batched(tetragon, subdivide, BatchSize::SmallInput)
    });
}

criterion_group!(benches, benchmark);
criterion_main!(benches);
<commit_msg>Allow `clippy::iter_nth_zero`.<commit_after>#![allow(clippy::iter_nth_zero)]

use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
use decorum::R64;
use nalgebra::Point3;
use plexus::geometry::AsPositionMut;
use plexus::graph::{EdgeMidpoint, FaceView, GraphData, MeshGraph};
use plexus::prelude::*;
use plexus::primitive::Tetragon;
use smallvec::SmallVec;

const DEPTH: usize = 8;

type E3 = Point3<R64>;

pub trait Circumscribe<G> {
    fn circumscribe(self) -> Self;
}

impl<'a, G> Circumscribe<G> for FaceView<&'a mut MeshGraph<G>>
where
    G: EdgeMidpoint + GraphData,
    G::Vertex: AsPositionMut,
{
    // Subdivide the face such that a similar polygon is formed within its
    // perimeter.
    fn circumscribe(self) -> Self {
        // Split each edge, stashing the vertex key and moving to the next arc.
        let arity = self.arity();
        let mut arc = self.into_arc();
        let mut splits = SmallVec::<[_; 4]>::with_capacity(arity);
        for _ in 0..arity {
            let vertex = arc.split_at_midpoint();
            splits.push(vertex.key());
            arc = vertex.into_outgoing_arc().into_next_arc();
        }
        // Split faces along the vertices from each arc split.
        let mut face = arc.into_face().unwrap();
        for (a, b) in splits.into_iter().perimeter() {
            face = face.split(ByKey(a), ByKey(b)).unwrap().into_face().unwrap();
        }
        // Return the face forming the similar polygon.
        face
    }
}

fn tetragon() -> MeshGraph<E3> {
    // Create a graph from a tetragon.
    MeshGraph::from(Tetragon::from([
        (1.0, 0.0, -1.0),
        (-1.0, 0.0, -1.0),
        (-1.0, 0.0, 1.0),
        (1.0, 0.0, 1.0),
    ]))
}

fn subdivide(mut graph: MeshGraph<E3>) {
    // Get the face of the tetragon.
    let key = graph.faces().nth(0).unwrap().key();
    let mut face = graph.face_mut(key).unwrap();

    // Circumscribe and extrude the face recursively.
    for _ in 0..DEPTH {
        face = face.circumscribe().extrude_with_offset(0.5).unwrap();
    }
}

#[allow(unused)]
fn benchmark(criterion: &mut Criterion) {
    criterion.bench_function("subdivide", move |bencher| {
        bencher.iter_batched(tetragon, subdivide, BatchSize::SmallInput)
    });
}

criterion_group!(benches, benchmark);
criterion_main!(benches);
<|endoftext|>


<commit_before>fn main() {
    let s1 = String::from("abc");
    let s2 = "defg";

    let result = longest(s1.as_str(), s2);
    println!("Longest result is: {}", result);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
<commit_msg>Example of struct that holds reference with specified lifetime<commit_after>fn main() {
    let s1 = String::from("abc");
    let s2 = "defg";

    let result = longest(s1.as_str(), s2);
    println!("Longest result is: {}", result);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

struct VeryImportant<'a> {
    part: &'a str,
}<|endoftext|>