<commit_before>#[macro_use]
extern crate wlroots;

use std::env;
use std::time::Instant;

use wlroots::{Compositor, CompositorBuilder, InputManagerHandler, KeyEvent, KeyboardHandler,
              OutputBuilder, OutputBuilderResult, OutputHandler, OutputManagerHandler};
use wlroots::render::Texture;
use wlroots::types::{KeyboardHandle, OutputHandle};
use wlroots::wlroots_sys::wl_output_transform;
use wlroots::xkbcommon::xkb::keysyms;

const CAT_STRIDE: i32 = 128;
const CAT_WIDTH: i32 = 128;
const CAT_HEIGHT: i32 = 128;
const CAT_DATA: &'static [u8] = include_bytes!("cat.data");

/// Helper step by iterator, because `step_by` on `Range` is unstable.
struct StepRange(i32, i32, i32);
impl Iterator for StepRange {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        if self.0 < self.1 {
            let v = self.0;
            self.0 = v + self.2;
            Some(v)
        } else {
            None
        }
    }
}

struct CompositorState {
    cat_texture: Option<Texture>,
    rotation: wl_output_transform,
    last_frame: Instant,
    x_offs: f32,
    y_offs: f32,
    x_vel: f32,
    y_vel: f32
}

compositor_data!(CompositorState);

impl CompositorState {
    fn new(rotation: wl_output_transform) -> Self {
        CompositorState {
            cat_texture: None,
            rotation,
            last_frame: Instant::now(),
            x_offs: 0.0,
            y_offs: 0.0,
            x_vel: 128.0,
            y_vel: 128.0
        }
    }
}

// TODO Basic rotation
// TODO Config reading
// TODO Arrow key velocity control

struct OutputManager;

struct Output;

struct InputManager;

struct KeyboardManager;

impl OutputManagerHandler for OutputManager {
    fn output_added<'output>(&mut self,
                             compositor: &mut Compositor,
                             builder: OutputBuilder<'output>)
                             -> Option<OutputBuilderResult<'output>> {
        let compositor_data: &mut CompositorState = compositor.into();
        let output = Output;
        let res = builder.build_best_mode(output);
        res.output.transform(compositor_data.rotation);
        Some(res)
    }
}

impl OutputHandler for Output {
    fn output_frame(&mut self, compositor: &mut Compositor, output: &mut OutputHandle) {
        let (width, height) = output.effective_resolution();
        let renderer = compositor
            .gles2_renderer
            .as_mut()
            .expect("Compositor was not loaded with gles2 renderer");
        let compositor_data: &mut CompositorState = (&mut compositor.data)
            .downcast_mut()
            .unwrap();
        let now = Instant::now();
        let delta = now.duration_since(compositor_data.last_frame);
        let seconds_delta = delta.as_secs() as f32;
        let nano_delta = delta.subsec_nanos() as u64;
        let ms = (seconds_delta * 1000.0) + nano_delta as f32 / 1000000.0;
        let seconds = ms / 1000.0;
        // TODO the method probably takes a different type, because you nede to call
        // start first. Will look into it.
        renderer.render(output, |renderer, output| {
            let cat_texture = compositor_data.cat_texture.as_ref().unwrap();
            for y in StepRange(-128 + compositor_data.y_offs as i32, height, 128) {
                for x in StepRange(-128 + compositor_data.x_offs as i32, width, 128) {
                    let matrix = cat_texture.get_matrix(&output.transform_matrix(), x, y);
                    // wlr_log!(L_ERROR, "x: {}, y: {}", x, y);
                    renderer.render_with_matrix(&cat_texture, &matrix);
                }
            }
        });
        compositor_data.x_offs += compositor_data.x_vel * seconds;
        compositor_data.y_offs += compositor_data.y_vel * seconds;
        if compositor_data.x_offs > 128.0 {
            compositor_data.x_offs = 0.0
        }
        if compositor_data.y_offs > 128.0 {
            compositor_data.y_offs = 0.0
        }
        compositor_data.last_frame = now;
    }
}

impl InputManagerHandler for InputManager {
    fn keyboard_added(&mut self,
                      _: &mut Compositor,
                      _: &mut KeyboardHandle)
                      -> Option<Box<KeyboardHandler>> {
        Some(Box::new(KeyboardManager))
    }
}

impl KeyboardHandler for KeyboardManager {
    fn on_key(&mut self,
              compositor: &mut Compositor,
              _: &mut KeyboardHandle,
              key_event: &mut KeyEvent) {
        let keys = key_event.input_keys();

        for key in keys {
            match key {
                keysyms::KEY_Escape => compositor.terminate(),
                keysyms::KEY_Left => update_velocities(compositor.into(), -16.0, 0.0),
                keysyms::KEY_Right => update_velocities(compositor.into(), 16.0, 0.0),
                keysyms::KEY_Up => update_velocities(compositor.into(), 0.0, -16.0),
                keysyms::KEY_Down => update_velocities(compositor.into(), 0.0, 16.0),
                _ => {}
            }
        }
    }
}

fn update_velocities(compositor: &mut CompositorState, x_diff: f32, y_diff: f32) {
    compositor.x_vel += x_diff;
    compositor.y_vel += y_diff;
}

fn main() {
    use wl_output_transform::*;
    let mut args = env::args();
    args.next();
    let rotation = if let Some(arg) = args.next() {
        match arg.as_str() {
            "90" => WL_OUTPUT_TRANSFORM_90,
            "180" => WL_OUTPUT_TRANSFORM_180,
            "270" => WL_OUTPUT_TRANSFORM_270,
            "flipped" => WL_OUTPUT_TRANSFORM_FLIPPED,
            "flipped_90" => WL_OUTPUT_TRANSFORM_FLIPPED_90,
            "flipped_180" => WL_OUTPUT_TRANSFORM_FLIPPED_180,
            "flipped_270" => WL_OUTPUT_TRANSFORM_FLIPPED_270,
            _ => WL_OUTPUT_TRANSFORM_NORMAL,
        }
    } else {
        WL_OUTPUT_TRANSFORM_NORMAL
    };
    let compositor_state = CompositorState::new(rotation);
    let input_manager = Box::new(InputManager);
    let output_manager = Box::new(OutputManager);
    let mut compositor = CompositorBuilder::new()
        .gles2_renderer(true)
        .build_auto(compositor_state, input_manager, output_manager);
    {
        let gles2_renderer = &mut compositor.gles2_renderer.as_mut().unwrap();
        let compositor_data: &mut CompositorState = (&mut compositor.data).downcast_mut().unwrap();
        compositor_data.cat_texture = gles2_renderer
            .create_texture()
            .map(|mut cat_texture| {
                     cat_texture.upload_pixels(CAT_STRIDE, CAT_WIDTH, CAT_HEIGHT, CAT_DATA);
                     cat_texture
                 })
    };
    compositor.run();
}
<commit_msg>Cleanup<commit_after>#[macro_use]
extern crate wlroots;

use std::env;
use std::time::Instant;

use wlroots::{Compositor, CompositorBuilder, InputManagerHandler, KeyEvent, KeyboardHandler,
              OutputBuilder, OutputBuilderResult, OutputHandler, OutputManagerHandler};
use wlroots::render::Texture;
use wlroots::types::{KeyboardHandle, OutputHandle};
use wlroots::wlroots_sys::wl_output_transform;
use wlroots::xkbcommon::xkb::keysyms;

const CAT_STRIDE: i32 = 128;
const CAT_WIDTH: i32 = 128;
const CAT_HEIGHT: i32 = 128;
const CAT_DATA: &'static [u8] = include_bytes!("cat.data");

/// Helper step by iterator, because `step_by` on `Range` is unstable.
struct StepRange(i32, i32, i32);
impl Iterator for StepRange {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        if self.0 < self.1 {
            let v = self.0;
            self.0 = v + self.2;
            Some(v)
        } else {
            None
        }
    }
}

struct CompositorState {
    cat_texture: Option<Texture>,
    rotation: wl_output_transform,
    last_frame: Instant,
    x_offs: f32,
    y_offs: f32,
    x_vel: f32,
    y_vel: f32
}

compositor_data!(CompositorState);

impl CompositorState {
    fn new(rotation: wl_output_transform) -> Self {
        CompositorState {
            cat_texture: None,
            rotation,
            last_frame: Instant::now(),
            x_offs: 0.0,
            y_offs: 0.0,
            x_vel: 128.0,
            y_vel: 128.0
        }
    }
}

struct OutputManager;

struct Output;

struct InputManager;

struct KeyboardManager;

impl OutputManagerHandler for OutputManager {
    fn output_added<'output>(&mut self,
                             compositor: &mut Compositor,
                             builder: OutputBuilder<'output>)
                             -> Option<OutputBuilderResult<'output>> {
        let compositor_data: &mut CompositorState = compositor.into();
        let output = Output;
        let res = builder.build_best_mode(output);
        res.output.transform(compositor_data.rotation);
        Some(res)
    }
}

impl OutputHandler for Output {
    fn output_frame(&mut self, compositor: &mut Compositor, output: &mut OutputHandle) {
        let (width, height) = output.effective_resolution();
        let renderer = compositor
            .gles2_renderer
            .as_mut()
            .expect("Compositor was not loaded with gles2 renderer");
        let compositor_data: &mut CompositorState = (&mut compositor.data)
            .downcast_mut()
            .unwrap();
        let now = Instant::now();
        let delta = now.duration_since(compositor_data.last_frame);
        let seconds_delta = delta.as_secs() as f32;
        let nano_delta = delta.subsec_nanos() as u64;
        let ms = (seconds_delta * 1000.0) + nano_delta as f32 / 1000000.0;
        let seconds = ms / 1000.0;
        // TODO the method probably takes a different type, because you nede to call
        // start first. Will look into it.
        renderer.render(output, |renderer, output| {
            let cat_texture = compositor_data.cat_texture.as_ref().unwrap();
            for y in StepRange(-128 + compositor_data.y_offs as i32, height, 128) {
                for x in StepRange(-128 + compositor_data.x_offs as i32, width, 128) {
                    let matrix = cat_texture.get_matrix(&output.transform_matrix(), x, y);
                    renderer.render_with_matrix(&cat_texture, &matrix);
                }
            }
        });
        compositor_data.x_offs += compositor_data.x_vel * seconds;
        compositor_data.y_offs += compositor_data.y_vel * seconds;
        if compositor_data.x_offs > 128.0 {
            compositor_data.x_offs = 0.0
        }
        if compositor_data.y_offs > 128.0 {
            compositor_data.y_offs = 0.0
        }
        compositor_data.last_frame = now;
    }
}

impl InputManagerHandler for InputManager {
    fn keyboard_added(&mut self,
                      _: &mut Compositor,
                      _: &mut KeyboardHandle)
                      -> Option<Box<KeyboardHandler>> {
        Some(Box::new(KeyboardManager))
    }
}

impl KeyboardHandler for KeyboardManager {
    fn on_key(&mut self,
              compositor: &mut Compositor,
              _: &mut KeyboardHandle,
              key_event: &mut KeyEvent) {
        let keys = key_event.input_keys();

        for key in keys {
            match key {
                keysyms::KEY_Escape => compositor.terminate(),
                keysyms::KEY_Left => update_velocities(compositor.into(), -16.0, 0.0),
                keysyms::KEY_Right => update_velocities(compositor.into(), 16.0, 0.0),
                keysyms::KEY_Up => update_velocities(compositor.into(), 0.0, -16.0),
                keysyms::KEY_Down => update_velocities(compositor.into(), 0.0, 16.0),
                _ => {}
            }
        }
    }
}

fn update_velocities(compositor: &mut CompositorState, x_diff: f32, y_diff: f32) {
    compositor.x_vel += x_diff;
    compositor.y_vel += y_diff;
}

fn main() {
    use wl_output_transform::*;
    let mut args = env::args();
    args.next();
    let rotation = if let Some(arg) = args.next() {
        match arg.as_str() {
            "90" => WL_OUTPUT_TRANSFORM_90,
            "180" => WL_OUTPUT_TRANSFORM_180,
            "270" => WL_OUTPUT_TRANSFORM_270,
            "flipped" => WL_OUTPUT_TRANSFORM_FLIPPED,
            "flipped_90" => WL_OUTPUT_TRANSFORM_FLIPPED_90,
            "flipped_180" => WL_OUTPUT_TRANSFORM_FLIPPED_180,
            "flipped_270" => WL_OUTPUT_TRANSFORM_FLIPPED_270,
            _ => WL_OUTPUT_TRANSFORM_NORMAL,
        }
    } else {
        WL_OUTPUT_TRANSFORM_NORMAL
    };
    let compositor_state = CompositorState::new(rotation);
    let input_manager = Box::new(InputManager);
    let output_manager = Box::new(OutputManager);
    let mut compositor = CompositorBuilder::new()
        .gles2_renderer(true)
        .build_auto(compositor_state, input_manager, output_manager);
    {
        let gles2_renderer = &mut compositor.gles2_renderer.as_mut().unwrap();
        let compositor_data: &mut CompositorState = (&mut compositor.data).downcast_mut().unwrap();
        compositor_data.cat_texture = gles2_renderer
            .create_texture()
            .map(|mut cat_texture| {
                     cat_texture.upload_pixels(CAT_STRIDE, CAT_WIDTH, CAT_HEIGHT, CAT_DATA);
                     cat_texture
                 })
    };
    compositor.run();
}
<|endoftext|>


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

use dom::bindings::callback::ExceptionHandling::Report;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::global::global_root_from_reflector;
use dom::bindings::reflector::Reflectable;
use dom::bindings::trace::JSTraceable;
use dom::window::ScriptHelpers;
use euclid::length::Length;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{HandleValue, Heap, RootedValue};
use js::jsval::{JSVal, UndefinedValue};
use num::traits::Saturating;
use script_traits::{MsDuration, precise_time_ms};
use script_traits::{TimerEvent, TimerEventId, TimerEventRequest, TimerSource};
use std::cell::Cell;
use std::cmp::{self, Ord, Ordering};
use std::default::Default;
use std::rc::Rc;
use util::mem::HeapSizeOf;
use util::str::DOMString;

#[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf, Hash, PartialOrd, Ord)]
pub struct TimerHandle(i32);

#[derive(JSTraceable, HeapSizeOf)]
#[privatize]
pub struct ActiveTimers {
    #[ignore_heap_size_of = "Defined in std"]
    timer_event_chan: IpcSender<TimerEvent>,
    #[ignore_heap_size_of = "Defined in std"]
    scheduler_chan: IpcSender<TimerEventRequest>,
    next_timer_handle: Cell<TimerHandle>,
    timers: DOMRefCell<Vec<Timer>>,
    suspended_since: Cell<Option<MsDuration>>,
    /// Initially 0, increased whenever the associated document is reactivated
    /// by the amount of ms the document was inactive. The current time can be
    /// offset back by this amount for a coherent time across document
    /// activations.
    suspension_offset: Cell<MsDuration>,
    /// Calls to `fire_timer` with a different argument than this get ignored.
    /// They were previously scheduled and got invalidated when
    ///  - timers were suspended,
    ///  - the timer it was scheduled for got canceled or
    ///  - a timer was added with an earlier callback time. In this case the
    ///    original timer is rescheduled when it is the next one to get called.
    expected_event_id: Cell<TimerEventId>,
    /// The nesting level of the currently executing timer task or 0.
    nesting_level: Cell<u32>,
}

// Holder for the various JS values associated with setTimeout
// (ie. function value to invoke and all arguments to pass
//      to the function when calling it)
// TODO: Handle rooting during fire_timer when movable GC is turned on
#[derive(JSTraceable, HeapSizeOf)]
#[privatize]
struct Timer {
    handle: TimerHandle,
    source: TimerSource,
    callback: InternalTimerCallback,
    is_interval: IsInterval,
    nesting_level: u32,
    duration: MsDuration,
    next_call: MsDuration,
}

// Enum allowing more descriptive values for the is_interval field
#[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf)]
pub enum IsInterval {
    Interval,
    NonInterval,
}

impl Ord for Timer {
    fn cmp(&self, other: &Timer) -> Ordering {
        match self.next_call.cmp(&other.next_call).reverse() {
            Ordering::Equal => self.handle.cmp(&other.handle).reverse(),
            res => res
        }
    }
}

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

impl Eq for Timer {}
impl PartialEq for Timer {
    fn eq(&self, other: &Timer) -> bool {
        self as *const Timer == other as *const Timer
    }
}

#[derive(Clone)]
pub enum TimerCallback {
    StringTimerCallback(DOMString),
    FunctionTimerCallback(Rc<Function>),
}

#[derive(JSTraceable, Clone)]
enum InternalTimerCallback {
    StringTimerCallback(DOMString),
    FunctionTimerCallback(Rc<Function>, Rc<Vec<Heap<JSVal>>>),
    InternalCallback(Box<ScheduledCallback>),
}

impl HeapSizeOf for InternalTimerCallback {
    fn heap_size_of_children(&self) -> usize {
        // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
        0
    }
}

pub trait ScheduledCallback: JSTraceable + HeapSizeOf {
    fn invoke(self: Box<Self>);

    fn box_clone(&self) -> Box<ScheduledCallback>;
}

impl Clone for Box<ScheduledCallback> {
    fn clone(&self) -> Box<ScheduledCallback> {
        self.box_clone()
    }
}

impl ActiveTimers {
    pub fn new(timer_event_chan: IpcSender<TimerEvent>,
               scheduler_chan: IpcSender<TimerEventRequest>)
               -> ActiveTimers {
        ActiveTimers {
            timer_event_chan: timer_event_chan,
            scheduler_chan: scheduler_chan,
            next_timer_handle: Cell::new(TimerHandle(1)),
            timers: DOMRefCell::new(Vec::new()),
            suspended_since: Cell::new(None),
            suspension_offset: Cell::new(Length::new(0)),
            expected_event_id: Cell::new(TimerEventId(0)),
            nesting_level: Cell::new(0),
        }
    }

    // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    pub fn set_timeout_or_interval(&self,
                               callback: TimerCallback,
                               arguments: Vec<HandleValue>,
                               timeout: i32,
                               is_interval: IsInterval,
                               source: TimerSource)
                               -> i32 {
        let callback = match callback {
            TimerCallback::StringTimerCallback(code_str) =>
                InternalTimerCallback::StringTimerCallback(code_str),
            TimerCallback::FunctionTimerCallback(function) => {
                // This is a bit complicated, but this ensures that the vector's
                // buffer isn't reallocated (and moved) after setting the Heap values
                let mut args = Vec::with_capacity(arguments.len());
                for _ in 0..arguments.len() {
                    args.push(Heap::default());
                }
                for (i, item) in arguments.iter().enumerate() {
                    args.get_mut(i).unwrap().set(item.get());
                }
                InternalTimerCallback::FunctionTimerCallback(function, Rc::new(args))
            }
        };

        let timeout = cmp::max(0, timeout);
        // step 7
        let duration = self.clamp_duration(Length::new(timeout as u64));

        let TimerHandle(handle) = self.schedule_internal_callback(callback, duration, is_interval, source);
        handle
    }

    pub fn schedule_callback(&self,
                             callback: Box<ScheduledCallback>,
                             duration: MsDuration,
                             source: TimerSource) -> TimerHandle {
        self.schedule_internal_callback(InternalTimerCallback::InternalCallback(callback),
                                        duration,
                                        IsInterval::NonInterval,
                                        source)
    }

    // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    fn schedule_internal_callback(&self,
                                  callback: InternalTimerCallback,
                                  duration: MsDuration,
                                  is_interval: IsInterval,
                                  source: TimerSource) -> TimerHandle {
        assert!(self.suspended_since.get().is_none());

        // step 3
        let TimerHandle(new_handle) = self.next_timer_handle.get();
        self.next_timer_handle.set(TimerHandle(new_handle + 1));

        let next_call = self.base_time() + duration;

        let timer = Timer {
            handle: TimerHandle(new_handle),
            source: source,
            callback: callback,
            is_interval: is_interval,
            duration: duration,
            // step 6
            nesting_level: self.nesting_level.get() + 1,
            next_call: next_call,
        };

        self.insert_timer(timer);

        let TimerHandle(max_handle) = self.timers.borrow().last().unwrap().handle;
        if max_handle == new_handle {
            self.schedule_timer_call();
        }

        // step 10
        TimerHandle(new_handle)
    }

    pub fn clear_timeout_or_interval(&self, handle: i32) {
        self.unschedule_callback(TimerHandle(handle));
    }

    pub fn unschedule_callback(&self, handle: TimerHandle) {
        let was_next = self.is_next_timer(handle);

        self.timers.borrow_mut().retain(|t| t.handle != handle);

        if was_next {
            self.invalidate_expected_event_id();
            self.schedule_timer_call();
        }
    }

    // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    #[allow(unsafe_code)]
    pub fn fire_timer<T: Reflectable>(&self, id: TimerEventId, this: &T) {
        let expected_id = self.expected_event_id.get();
        if expected_id != id {
            debug!("ignoring timer fire event {:?} (expected {:?}", id, expected_id);
            return;
        }

        assert!(self.suspended_since.get().is_none());

        let base_time = self.base_time();

        // Since the event id was the expected one, at least one timer should be due.
        assert!(base_time >= self.timers.borrow().last().unwrap().next_call);

        loop {
            let timer = {
                let mut timers = self.timers.borrow_mut();

                if timers.is_empty() || timers.last().unwrap().next_call > base_time {
                    break;
                }

                timers.pop().unwrap()
            };
            let callback = timer.callback.clone();

            // prep for step 6 in nested set_timeout_or_interval calls
            self.nesting_level.set(timer.nesting_level);

            // step 4.3
            if timer.is_interval == IsInterval::Interval {
                let mut timer = timer;

                // step 7
                timer.duration = self.clamp_duration(timer.duration);
                // step 8, 9
                timer.nesting_level += 1;
                timer.next_call = base_time + timer.duration;
                self.insert_timer(timer);
            }

            // step 14
            match callback {
                InternalTimerCallback::StringTimerCallback(code_str) => {
                    let cx = global_root_from_reflector(this).r().get_cx();
                    let mut rval = RootedValue::new(cx, UndefinedValue());

                    this.evaluate_js_on_global_with_result(&code_str, rval.handle_mut());
                },
                InternalTimerCallback::FunctionTimerCallback(function, arguments) => {
                    let arguments: Vec<JSVal> = arguments.iter().map(|arg| arg.get()).collect();
                    let arguments = arguments.iter().by_ref().map(|arg| unsafe {
                        HandleValue::from_marked_location(arg)
                    }).collect();

                    let _ = function.Call_(this, arguments, Report);
                },
                InternalTimerCallback::InternalCallback(callback) => {
                    callback.invoke();
                },
            };

            self.nesting_level.set(0);
        }

        self.schedule_timer_call();
    }

    fn insert_timer(&self, timer: Timer) {
        let mut timers = self.timers.borrow_mut();
        let insertion_index = timers.binary_search(&timer).err().unwrap();
        timers.insert(insertion_index, timer);
    }

    fn is_next_timer(&self, handle: TimerHandle) -> bool {
        match self.timers.borrow().last() {
            None => false,
            Some(ref max_timer) => max_timer.handle == handle
        }
    }

    fn schedule_timer_call(&self) {
        if self.suspended_since.get().is_some() {
            // The timer will be scheduled when the pipeline is thawed.
            return;
        }

        let timers = self.timers.borrow();

        if let Some(timer) = timers.last() {
            let expected_event_id = self.invalidate_expected_event_id();

            let delay = Length::new(timer.next_call.get().saturating_sub(precise_time_ms().get()));
            let request = TimerEventRequest(self.timer_event_chan.clone(), timer.source,
                                            expected_event_id, delay);
            self.scheduler_chan.send(request).unwrap();
        }
    }

    pub fn suspend(&self) {
        assert!(self.suspended_since.get().is_none());

        self.suspended_since.set(Some(precise_time_ms()));
        self.invalidate_expected_event_id();
    }

    pub fn resume(&self) {
        assert!(self.suspended_since.get().is_some());

        let additional_offset = match self.suspended_since.get() {
            Some(suspended_since) => precise_time_ms() - suspended_since,
            None => panic!("Timers are not suspended.")
        };

        self.suspension_offset.set(self.suspension_offset.get() + additional_offset);

        self.schedule_timer_call();
    }

    fn base_time(&self) -> MsDuration {
        let offset = self.suspension_offset.get();

        match self.suspended_since.get() {
            Some(time) => time - offset,
            None => precise_time_ms() - offset,
        }
    }

    // see step 7 of https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    fn clamp_duration(&self, unclamped: MsDuration) -> MsDuration {
        let ms = if self.nesting_level.get() > 5 {
            4
        } else {
            0
        };

        cmp::max(Length::new(ms), unclamped)
    }

    fn invalidate_expected_event_id(&self) -> TimerEventId {
        let TimerEventId(currently_expected) = self.expected_event_id.get();
        let next_id = TimerEventId(currently_expected + 1);
        debug!("invalidating expected timer (was {:?}, now {:?}", currently_expected, next_id);
        self.expected_event_id.set(next_id);
        next_id
    }
}
<commit_msg>Correct undisciplined rebase. (Closes #8583.)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::callback::ExceptionHandling::Report;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::global::global_root_from_reflector;
use dom::bindings::reflector::Reflectable;
use dom::bindings::trace::JSTraceable;
use dom::window::ScriptHelpers;
use euclid::length::Length;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{HandleValue, Heap, RootedValue};
use js::jsval::{JSVal, UndefinedValue};
use num::traits::Saturating;
use script_traits::{MsDuration, precise_time_ms};
use script_traits::{TimerEvent, TimerEventId, TimerEventRequest, TimerSource};
use std::cell::Cell;
use std::cmp::{self, Ord, Ordering};
use std::default::Default;
use std::rc::Rc;
use util::mem::HeapSizeOf;
use util::str::DOMString;

#[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf, Hash, PartialOrd, Ord)]
pub struct TimerHandle(i32);

#[derive(JSTraceable, HeapSizeOf)]
#[privatize]
pub struct ActiveTimers {
    #[ignore_heap_size_of = "Defined in std"]
    timer_event_chan: IpcSender<TimerEvent>,
    #[ignore_heap_size_of = "Defined in std"]
    scheduler_chan: IpcSender<TimerEventRequest>,
    next_timer_handle: Cell<TimerHandle>,
    timers: DOMRefCell<Vec<Timer>>,
    suspended_since: Cell<Option<MsDuration>>,
    /// Initially 0, increased whenever the associated document is reactivated
    /// by the amount of ms the document was inactive. The current time can be
    /// offset back by this amount for a coherent time across document
    /// activations.
    suspension_offset: Cell<MsDuration>,
    /// Calls to `fire_timer` with a different argument than this get ignored.
    /// They were previously scheduled and got invalidated when
    ///  - timers were suspended,
    ///  - the timer it was scheduled for got canceled or
    ///  - a timer was added with an earlier callback time. In this case the
    ///    original timer is rescheduled when it is the next one to get called.
    expected_event_id: Cell<TimerEventId>,
    /// The nesting level of the currently executing timer task or 0.
    nesting_level: Cell<u32>,
}

// Holder for the various JS values associated with setTimeout
// (ie. function value to invoke and all arguments to pass
//      to the function when calling it)
// TODO: Handle rooting during fire_timer when movable GC is turned on
#[derive(JSTraceable, HeapSizeOf)]
#[privatize]
struct Timer {
    handle: TimerHandle,
    source: TimerSource,
    callback: InternalTimerCallback,
    is_interval: IsInterval,
    nesting_level: u32,
    duration: MsDuration,
    next_call: MsDuration,
}

// Enum allowing more descriptive values for the is_interval field
#[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf)]
pub enum IsInterval {
    Interval,
    NonInterval,
}

impl Ord for Timer {
    fn cmp(&self, other: &Timer) -> Ordering {
        match self.next_call.cmp(&other.next_call).reverse() {
            Ordering::Equal => self.handle.cmp(&other.handle).reverse(),
            res => res
        }
    }
}

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

impl Eq for Timer {}
impl PartialEq for Timer {
    fn eq(&self, other: &Timer) -> bool {
        self as *const Timer == other as *const Timer
    }
}

#[derive(Clone)]
pub enum TimerCallback {
    StringTimerCallback(DOMString),
    FunctionTimerCallback(Rc<Function>),
}

#[derive(JSTraceable, Clone)]
enum InternalTimerCallback {
    StringTimerCallback(DOMString),
    FunctionTimerCallback(Rc<Function>, Rc<Vec<Heap<JSVal>>>),
    InternalCallback(Box<ScheduledCallback>),
}

impl HeapSizeOf for InternalTimerCallback {
    fn heap_size_of_children(&self) -> usize {
        // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
        0
    }
}

pub trait ScheduledCallback: JSTraceable + HeapSizeOf {
    fn invoke(self: Box<Self>);

    fn box_clone(&self) -> Box<ScheduledCallback>;
}

impl Clone for Box<ScheduledCallback> {
    fn clone(&self) -> Box<ScheduledCallback> {
        self.box_clone()
    }
}

impl ActiveTimers {
    pub fn new(timer_event_chan: IpcSender<TimerEvent>,
               scheduler_chan: IpcSender<TimerEventRequest>)
               -> ActiveTimers {
        ActiveTimers {
            timer_event_chan: timer_event_chan,
            scheduler_chan: scheduler_chan,
            next_timer_handle: Cell::new(TimerHandle(1)),
            timers: DOMRefCell::new(Vec::new()),
            suspended_since: Cell::new(None),
            suspension_offset: Cell::new(Length::new(0)),
            expected_event_id: Cell::new(TimerEventId(0)),
            nesting_level: Cell::new(0),
        }
    }

    // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    pub fn set_timeout_or_interval(&self,
                               callback: TimerCallback,
                               arguments: Vec<HandleValue>,
                               timeout: i32,
                               is_interval: IsInterval,
                               source: TimerSource)
                               -> i32 {
        let callback = match callback {
            TimerCallback::StringTimerCallback(code_str) =>
                InternalTimerCallback::StringTimerCallback(code_str),
            TimerCallback::FunctionTimerCallback(function) => {
                // This is a bit complicated, but this ensures that the vector's
                // buffer isn't reallocated (and moved) after setting the Heap values
                let mut args = Vec::with_capacity(arguments.len());
                for _ in 0..arguments.len() {
                    args.push(Heap::default());
                }
                for (i, item) in arguments.iter().enumerate() {
                    args.get_mut(i).unwrap().set(item.get());
                }
                InternalTimerCallback::FunctionTimerCallback(function, Rc::new(args))
            }
        };

        let timeout = cmp::max(0, timeout);
        // step 7
        let duration = self.clamp_duration(Length::new(timeout as u64));

        let TimerHandle(handle) = self.schedule_internal_callback(callback, duration, is_interval, source);
        handle
    }

    pub fn schedule_callback(&self,
                             callback: Box<ScheduledCallback>,
                             duration: MsDuration,
                             source: TimerSource) -> TimerHandle {
        self.schedule_internal_callback(InternalTimerCallback::InternalCallback(callback),
                                        duration,
                                        IsInterval::NonInterval,
                                        source)
    }

    // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    fn schedule_internal_callback(&self,
                                  callback: InternalTimerCallback,
                                  duration: MsDuration,
                                  is_interval: IsInterval,
                                  source: TimerSource) -> TimerHandle {
        // step 3
        let TimerHandle(new_handle) = self.next_timer_handle.get();
        self.next_timer_handle.set(TimerHandle(new_handle + 1));

        let next_call = self.base_time() + duration;

        let timer = Timer {
            handle: TimerHandle(new_handle),
            source: source,
            callback: callback,
            is_interval: is_interval,
            duration: duration,
            // step 6
            nesting_level: self.nesting_level.get() + 1,
            next_call: next_call,
        };

        self.insert_timer(timer);

        let TimerHandle(max_handle) = self.timers.borrow().last().unwrap().handle;
        if max_handle == new_handle {
            self.schedule_timer_call();
        }

        // step 10
        TimerHandle(new_handle)
    }

    pub fn clear_timeout_or_interval(&self, handle: i32) {
        self.unschedule_callback(TimerHandle(handle));
    }

    pub fn unschedule_callback(&self, handle: TimerHandle) {
        let was_next = self.is_next_timer(handle);

        self.timers.borrow_mut().retain(|t| t.handle != handle);

        if was_next {
            self.invalidate_expected_event_id();
            self.schedule_timer_call();
        }
    }

    // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    #[allow(unsafe_code)]
    pub fn fire_timer<T: Reflectable>(&self, id: TimerEventId, this: &T) {
        let expected_id = self.expected_event_id.get();
        if expected_id != id {
            debug!("ignoring timer fire event {:?} (expected {:?}", id, expected_id);
            return;
        }

        assert!(self.suspended_since.get().is_none());

        let base_time = self.base_time();

        // Since the event id was the expected one, at least one timer should be due.
        assert!(base_time >= self.timers.borrow().last().unwrap().next_call);

        loop {
            let timer = {
                let mut timers = self.timers.borrow_mut();

                if timers.is_empty() || timers.last().unwrap().next_call > base_time {
                    break;
                }

                timers.pop().unwrap()
            };
            let callback = timer.callback.clone();

            // prep for step 6 in nested set_timeout_or_interval calls
            self.nesting_level.set(timer.nesting_level);

            // step 4.3
            if timer.is_interval == IsInterval::Interval {
                let mut timer = timer;

                // step 7
                timer.duration = self.clamp_duration(timer.duration);
                // step 8, 9
                timer.nesting_level += 1;
                timer.next_call = base_time + timer.duration;
                self.insert_timer(timer);
            }

            // step 14
            match callback {
                InternalTimerCallback::StringTimerCallback(code_str) => {
                    let cx = global_root_from_reflector(this).r().get_cx();
                    let mut rval = RootedValue::new(cx, UndefinedValue());

                    this.evaluate_js_on_global_with_result(&code_str, rval.handle_mut());
                },
                InternalTimerCallback::FunctionTimerCallback(function, arguments) => {
                    let arguments: Vec<JSVal> = arguments.iter().map(|arg| arg.get()).collect();
                    let arguments = arguments.iter().by_ref().map(|arg| unsafe {
                        HandleValue::from_marked_location(arg)
                    }).collect();

                    let _ = function.Call_(this, arguments, Report);
                },
                InternalTimerCallback::InternalCallback(callback) => {
                    callback.invoke();
                },
            };

            self.nesting_level.set(0);
        }

        self.schedule_timer_call();
    }

    fn insert_timer(&self, timer: Timer) {
        let mut timers = self.timers.borrow_mut();
        let insertion_index = timers.binary_search(&timer).err().unwrap();
        timers.insert(insertion_index, timer);
    }

    fn is_next_timer(&self, handle: TimerHandle) -> bool {
        match self.timers.borrow().last() {
            None => false,
            Some(ref max_timer) => max_timer.handle == handle
        }
    }

    fn schedule_timer_call(&self) {
        if self.suspended_since.get().is_some() {
            // The timer will be scheduled when the pipeline is thawed.
            return;
        }

        let timers = self.timers.borrow();

        if let Some(timer) = timers.last() {
            let expected_event_id = self.invalidate_expected_event_id();

            let delay = Length::new(timer.next_call.get().saturating_sub(precise_time_ms().get()));
            let request = TimerEventRequest(self.timer_event_chan.clone(), timer.source,
                                            expected_event_id, delay);
            self.scheduler_chan.send(request).unwrap();
        }
    }

    pub fn suspend(&self) {
        assert!(self.suspended_since.get().is_none());

        self.suspended_since.set(Some(precise_time_ms()));
        self.invalidate_expected_event_id();
    }

    pub fn resume(&self) {
        assert!(self.suspended_since.get().is_some());

        let additional_offset = match self.suspended_since.get() {
            Some(suspended_since) => precise_time_ms() - suspended_since,
            None => panic!("Timers are not suspended.")
        };

        self.suspension_offset.set(self.suspension_offset.get() + additional_offset);

        self.schedule_timer_call();
    }

    fn base_time(&self) -> MsDuration {
        let offset = self.suspension_offset.get();

        match self.suspended_since.get() {
            Some(time) => time - offset,
            None => precise_time_ms() - offset,
        }
    }

    // see step 7 of https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
    fn clamp_duration(&self, unclamped: MsDuration) -> MsDuration {
        let ms = if self.nesting_level.get() > 5 {
            4
        } else {
            0
        };

        cmp::max(Length::new(ms), unclamped)
    }

    fn invalidate_expected_event_id(&self) -> TimerEventId {
        let TimerEventId(currently_expected) = self.expected_event_id.get();
        let next_id = TimerEventId(currently_expected + 1);
        debug!("invalidating expected timer (was {:?}, now {:?}", currently_expected, next_id);
        self.expected_event_id.set(next_id);
        next_id
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate timely;
extern crate differential_dataflow;

use rand::{Rng, SeedableRng, StdRng};

use timely::dataflow::operators::*;
use timely::order::Product;

use differential_dataflow::input::Input;
use differential_dataflow::AsCollection;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::reduce::Reduce;
use differential_dataflow::operators::join::JoinCore;
use differential_dataflow::operators::Iterate;
use differential_dataflow::operators::Consolidate;

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();
    let pre: usize = std::env::args().nth(4).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().nth(5).unwrap() == "inspect";

    // define a new timely dataflow computation.
    timely::execute_from_args(std::env::args().skip(6), move |worker| {

        let timer = ::std::time::Instant::now();

        let index = worker.index();
        let peers = worker.peers();

        let mut probe = timely::dataflow::operators::probe::Handle::new();

        // create a dataflow managing an ever-changing edge collection.
    	let mut graph = worker.dataflow::<Product<(),usize>,_,_>(|scope| {

            // create a source operator which will produce random edges and delete them.
            timely::dataflow::operators::generic::source(scope, "RandomGraph", |mut capability, _info| {

                let seed: &[_] = &[1, 2, 3, index];
                let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
                let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions

                let mut additions = 0;
                let mut deletions = 0;

                let handle = probe.clone();

                move |output| {

                    // do nothing if the probe is not caught up to us
                    if !handle.less_than(capability.time()) {

                        let mut time = capability.time().clone();
                        // println!("{:?}\tintroducing edges for batch starting {:?}", timer.elapsed(), time);

                        {   // scope to allow session to drop, un-borrow.
                            let mut session = output.session(&capability);

                            // we want to send at times.inner + (0 .. batch).
                            for _ in 0 .. batch {

                                while additions < time.inner + edges {
                                    if additions % peers == index {
                                        let src = rng1.gen_range(0, nodes);
                                        let dst = rng1.gen_range(0, nodes);
                                        session.give(((src, dst), time, 1));
                                    }
                                    additions += 1;
                                }
                                while deletions < time.inner {
                                    if deletions % peers == index {
                                        let src = rng2.gen_range(0, nodes);
                                        let dst = rng2.gen_range(0, nodes);
                                        session.give(((src, dst), time, -1));
                                    }
                                    deletions += 1;
                                }

                                time.inner += 1;
                            }
                        }

                        // println!("downgrading {:?} to {:?}", capability, time);
                        capability.downgrade(&time);
                    }
                }
            })
            .probe_with(&mut probe)
            .as_collection()
            .arrange_by_key()
            // .arrange::<OrdValSpineAbom>()
            .trace
        });

        println!("{:?}:\tloading edges", timer.elapsed());

        for _ in 0 .. pre {
            worker.step();
        }

        println!("{:?}\tedges loaded; building query dataflows", timer.elapsed());

        let mut roots = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, roots) = scope.new_collection();
            let roots = roots.map(|x| (x, 0));

            // repeatedly update minimal distances each node can be reached from each root
            roots.iterate(|dists| {

                let edges = edges.enter(&dists.scope());
                let roots = roots.enter(&dists.scope());

                dists.arrange_by_key()
                     .join_core(&edges, |_k,l,d| Some((*d, l+1)))
                     .concat(&roots)
                     .reduce(|_, s, t| t.push((*s[0].0, 1)))
            })
            .map(|(_node, dist)| dist)
            .consolidate()
            .inspect(|x| println!("distance update: {:?}", x))
            .probe_with(&mut probe);

            input
        });

        let mut query = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, query) = scope.new_collection();

            query.map(|x| (x, x))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .filter(move |_| inspect)
                 .map(|x| x.1)
                 .consolidate()
                 .inspect(|x| println!("{:?}", x))
                 .probe_with(&mut probe);

            input
        });

        println!("{:?}\tquery dataflows built; querying", timer.elapsed());

        // the trace will not compact unless we release capabilities.
        // we drop rather than continually downgrade them as we run.
        drop(graph);

        if batch > 0 {
            for round in 0 .. {

                let mut time = query.time().clone();

                // roots.insert(round % nodes);
                if index == 0 {
                    query.insert(round % nodes);
                }

                time.inner += batch;

                roots.advance_to(time); roots.flush();
                query.advance_to(time); query.flush();

                if index == 0 {
                    query.remove(round % nodes);
                }

                worker.step_while(|| probe.less_than(&time));

                println!("{:?}\tquery round {:?} complete", timer.elapsed(), round);
            }
        }
    }).unwrap();
}<commit_msg>Activate the source operator in the arrange example<commit_after>extern crate rand;
extern crate timely;
extern crate differential_dataflow;

use rand::{Rng, SeedableRng, StdRng};

use timely::dataflow::operators::*;
use timely::order::Product;
use timely::scheduling::Scheduler;

use differential_dataflow::input::Input;
use differential_dataflow::AsCollection;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::operators::reduce::Reduce;
use differential_dataflow::operators::join::JoinCore;
use differential_dataflow::operators::Iterate;
use differential_dataflow::operators::Consolidate;

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();
    let pre: usize = std::env::args().nth(4).unwrap().parse().unwrap();
    let inspect: bool = std::env::args().nth(5).unwrap() == "inspect";

    // define a new timely dataflow computation.
    timely::execute_from_args(std::env::args().skip(6), move |worker| {

        let timer = ::std::time::Instant::now();

        let index = worker.index();
        let peers = worker.peers();

        let mut probe = timely::dataflow::operators::probe::Handle::new();

        // create a dataflow managing an ever-changing edge collection.
    	let mut graph = worker.dataflow::<Product<(),usize>,_,_>(|scope| {

            // create a source operator which will produce random edges and delete them.
            timely::dataflow::operators::generic::source(scope, "RandomGraph", |mut capability, info| {

                let activator = scope.activator_for(&info.address[..]);

                let seed: &[_] = &[1, 2, 3, index];
                let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
                let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge deletions

                let mut additions = 0;
                let mut deletions = 0;

                let handle = probe.clone();

                move |output| {

                    // do nothing if the probe is not caught up to us
                    if !handle.less_than(capability.time()) {

                        let mut time = capability.time().clone();
                        // println!("{:?}\tintroducing edges for batch starting {:?}", timer.elapsed(), time);

                        {   // scope to allow session to drop, un-borrow.
                            let mut session = output.session(&capability);

                            // we want to send at times.inner + (0 .. batch).
                            for _ in 0 .. batch {

                                while additions < time.inner + edges {
                                    if additions % peers == index {
                                        let src = rng1.gen_range(0, nodes);
                                        let dst = rng1.gen_range(0, nodes);
                                        session.give(((src, dst), time, 1));
                                    }
                                    additions += 1;
                                }
                                while deletions < time.inner {
                                    if deletions % peers == index {
                                        let src = rng2.gen_range(0, nodes);
                                        let dst = rng2.gen_range(0, nodes);
                                        session.give(((src, dst), time, -1));
                                    }
                                    deletions += 1;
                                }

                                time.inner += 1;
                            }
                        }

                        // println!("downgrading {:?} to {:?}", capability, time);
                        capability.downgrade(&time);
                    }
                    activator.activate();
                }
            })
            .probe_with(&mut probe)
            .as_collection()
            .arrange_by_key()
            // .arrange::<OrdValSpineAbom>()
            .trace
        });

        println!("{:?}:\tloading edges", timer.elapsed());

        for _ in 0 .. pre {
            worker.step();
        }

        println!("{:?}\tedges loaded; building query dataflows", timer.elapsed());

        let mut roots = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, roots) = scope.new_collection();
            let roots = roots.map(|x| (x, 0));

            // repeatedly update minimal distances each node can be reached from each root
            roots.iterate(|dists| {

                let edges = edges.enter(&dists.scope());
                let roots = roots.enter(&dists.scope());

                dists.arrange_by_key()
                     .join_core(&edges, |_k,l,d| Some((*d, l+1)))
                     .concat(&roots)
                     .reduce(|_, s, t| t.push((*s[0].0, 1)))
            })
            .map(|(_node, dist)| dist)
            .consolidate()
            .inspect(|x| println!("distance update: {:?}", x))
            .probe_with(&mut probe);

            input
        });

        let mut query = worker.dataflow(|scope| {

            let edges = graph.import(scope);
            let (input, query) = scope.new_collection();

            query.map(|x| (x, x))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .join_core(&edges, |_n, &q, &d| Some((d, q)))
                 .filter(move |_| inspect)
                 .map(|x| x.1)
                 .consolidate()
                 .inspect(|x| println!("{:?}", x))
                 .probe_with(&mut probe);

            input
        });

        println!("{:?}\tquery dataflows built; querying", timer.elapsed());

        // the trace will not compact unless we release capabilities.
        // we drop rather than continually downgrade them as we run.
        drop(graph);

        if batch > 0 {
            for round in 0 .. {

                let mut time = query.time().clone();

                // roots.insert(round % nodes);
                if index == 0 {
                    query.insert(round % nodes);
                }

                time.inner += batch;

                roots.advance_to(time); roots.flush();
                query.advance_to(time); query.flush();

                if index == 0 {
                    query.remove(round % nodes);
                }

                worker.step_while(|| probe.less_than(&time));

                println!("{:?}\tquery round {:?} complete", timer.elapsed(), round);
            }
        }
    }).unwrap();
}<|endoftext|>


<commit_before>//! Main interface for a stream of events the Supervisor can send out
//! in the course of its operations.
//!
//! Currently, the Supervisor is able to send events to a [NATS
//! Streaming][1] server. The `init_stream` function must be called
//! before sending events to initialize the publishing thread in the
//! background. Thereafter, you can pass "event" structs to the
//! `event` function, which will publish the event to the stream.
//!
//! All events are published under the "habitat" subject.
//!
//! [1]:https://github.com/nats-io/nats-streaming-server

mod types;

pub(crate) use self::types::ServiceMetadata;
use self::types::{EventMessage,
                  EventMetadata,
                  HealthCheckEvent,
                  ServiceStartedEvent,
                  ServiceStoppedEvent};
use crate::{error::Result,
            manager::{service::{HealthCheckResult,
                                Service},
                      sys::Sys}};
use clap::ArgMatches;
use futures::{sync::{mpsc as futures_mpsc,
                     mpsc::UnboundedSender},
              Future,
              Stream};
use habitat_common::types::{AutomateAuthToken,
                            EventStreamMetadata};
use nitox::{commands::ConnectCommand,
            streaming::client::NatsStreamingClient,
            NatsClient,
            NatsClientOptions};
use state::Container;
use std::{net::SocketAddr,
          sync::{mpsc as std_mpsc,
                 Once},
          thread,
          time::Duration};
use tokio::{executor,
            runtime::current_thread::Runtime as ThreadRuntime};

static INIT: Once = Once::new();
lazy_static! {
    // TODO (CM): When const fn support lands in stable, we can ditch
    // this lazy_static call.

    /// Reference to the event stream.
    static ref EVENT_STREAM: Container = Container::new();
    /// Core information that is shared between all events.
    static ref EVENT_CORE: Container = Container::new();
}

/// Starts a new thread for sending events to a NATS Streaming
/// server. Stashes the handle to the stream, as well as the core
/// event information that will be a part of all events, in a global
/// static reference for access later.
pub fn init_stream(config: EventStreamConfig, event_core: EventCore) {
    INIT.call_once(|| {
            let conn_info = EventConnectionInfo::new(config.token, config.url);
            let event_stream = init_nats_stream(conn_info).expect("Could not start NATS thread");
            EVENT_STREAM.set(event_stream);
            EVENT_CORE.set(event_core);
        });
}

/// Captures all event stream-related configuration options that would
/// be passed in by a user
#[derive(Clone, Debug)]
pub struct EventStreamConfig {
    environment: String,
    application: String,
    meta:        EventStreamMetadata,
    token:       AutomateAuthToken,
    url:         String,
}

impl EventStreamConfig {
    /// Create an instance from Clap arguments.
    pub fn from_matches(m: &ArgMatches) -> Result<EventStreamConfig> {
        Ok(EventStreamConfig { environment: m.value_of("EVENT_STREAM_ENVIRONMENT")
                                             .map(str::to_string)
                                             .expect("Required option for EventStream feature"),
                               application: m.value_of("EVENT_STREAM_APPLICATION")
                                             .map(str::to_string)
                                             .expect("Required option for EventStream feature"),
                               meta:        EventStreamMetadata::from_matches(m)?,
                               token:       AutomateAuthToken::from_matches(m)?,
                               url:         m.value_of("EVENT_STREAM_URL")
                                             .map(str::to_string)
                                             .expect("Required option for EventStream feature"), })
    }
}

/// All the information needed to establish a connection to a NATS
/// Streaming server.
// TODO: This will change as we firm up what the interaction between
// Habitat and A2 looks like.
pub struct EventConnectionInfo {
    pub name:        String,
    pub verbose:     bool,
    pub cluster_uri: String,
    pub cluster_id:  String,
    pub auth_token:  AutomateAuthToken,
}

impl EventConnectionInfo {
    pub fn new(auth_token: AutomateAuthToken, cluster_uri: String) -> Self {
        EventConnectionInfo { name: String::from("habitat"),
                              verbose: true,
                              cluster_uri,
                              cluster_id: String::from("event-service"),
                              auth_token }
    }
}

/// A collection of data that will be present in all events. Rather
/// than baking this into the structure of each event, we represent it
/// once and merge the information into the final rendered form of the
/// event.
///
/// This prevents us from having to thread information throughout the
/// system, just to get it to the places where the events are
/// generated (e.g., not all code has direct access to the
/// Supervisor's ID).
#[derive(Clone, Debug)]
pub struct EventCore {
    /// The unique identifier of the Supervisor sending the event.
    supervisor_id: String,
    ip_address: SocketAddr,
    // TODO (CM): could add application and environment to the meta
    // map directly... hrmm
    application: String,
    environment: String,
    meta:        EventStreamMetadata,
}

impl EventCore {
    pub fn new(config: &EventStreamConfig, sys: &Sys) -> Self {
        EventCore { supervisor_id: sys.member_id.clone(),
                    ip_address:    sys.gossip_listen(),
                    environment:   config.environment.clone(),
                    application:   config.application.clone(),
                    meta:          config.meta.clone(), }
    }
}

/// Send an event for the start of a Service.
pub fn service_started(service: &Service) {
    if stream_initialized() {
        publish(ServiceStartedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

/// Send an event for the stop of a Service.
pub fn service_stopped(service: &Service) {
    if stream_initialized() {
        publish(ServiceStoppedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

// Takes metadata directly, rather than a `&Service` like other event
// functions, because of how the asynchronous health checking
// currently works. Revisit when async/await + Pin is all stabilized.
pub fn health_check(metadata: ServiceMetadata,
                    check_result: HealthCheckResult,
                    duration: Option<Duration>) {
    if stream_initialized() {
        publish(HealthCheckEvent { service_metadata: Some(metadata),
                                   event_metadata:   None,
                                   result:           Into::<types::HealthCheck>::into(check_result)
                                                     as i32,
                                   duration:         duration.map(Duration::into), });
    }
}

////////////////////////////////////////////////////////////////////////

/// Internal helper function to know whether or not to go to the trouble of
/// creating event structures. If the event stream hasn't been
/// initialized, then we shouldn't need to do anything.
fn stream_initialized() -> bool { EVENT_STREAM.try_get::<EventStream>().is_some() }

/// Publish an event. This is the main interface that client code will
/// use.
///
/// If `init_stream` has not been called already, this function will
/// be a no-op.
fn publish(mut event: impl EventMessage) {
    if let Some(e) = EVENT_STREAM.try_get::<EventStream>() {
        // TODO (CM): Yeah... this is looking pretty gross. The
        // intention is to be able to timestamp the events right as
        // they go out.
        //
        // We *could* set the time when we convert the EventCore to a
        // EventMetadata struct, but that seems odd.
        //
        // It probably means that this structure just isn't the right
        // one.
        //
        // The ugliness is at least contained, though.
        event.event_metadata(EventMetadata { timestamp:
                                                 Some(std::time::SystemTime::now().into()),
                                             ..EVENT_CORE.get::<EventCore>().to_event_metadata() });

        e.send(event.to_bytes());
    }
}

/// A lightweight handle for the event stream. All events get to the
/// event stream through this.
struct EventStream(UnboundedSender<Vec<u8>>);

impl EventStream {
    /// Queues an event to be sent out.
    fn send(&self, event: Vec<u8>) {
        trace!("About to queue an event: {:?}", event);
        if let Err(e) = self.0.unbounded_send(event) {
            error!("Failed to queue event: {:?}", e);
        }
    }
}

////////////////////////////////////////////////////////////////////////

/// All messages are published under this subject.
const HABITAT_SUBJECT: &str = "habitat";

fn init_nats_stream(conn_info: EventConnectionInfo) -> Result<EventStream> {
    // TODO (CM): Investigate back-pressure scenarios
    let (event_tx, event_rx) = futures_mpsc::unbounded();
    let (sync_tx, sync_rx) = std_mpsc::sync_channel(0); // rendezvous channel

    // TODO (CM): We could theoretically create this future and spawn
    // it in the Supervisor's Tokio runtime, but there's currently a
    // bug: https://github.com/YellowInnovation/nitox/issues/24

    thread::Builder::new().name("events".to_string())
                          .spawn(move || {
                              let EventConnectionInfo { name,
                                                        verbose,
                                                        cluster_uri,
                                                        cluster_id,
                                                        auth_token, } = conn_info;
                              let cc =
                                  ConnectCommand::builder().name(Some(name))
                                                           .verbose(verbose)
                                                           .auth_token(Some(auth_token.to_string()))
                                                           .tls_required(false)
                                                           .build()
                                                           .unwrap();
                              let opts =
                                  NatsClientOptions::builder().connect_command(cc)
                                                              .cluster_uri(cluster_uri.as_str())
                                                              .build()
                                                              .unwrap();

                              let publisher = NatsClient::from_options(opts)
                .map_err(|e| {
                    error!("Error creating Nats Client from options: {}", e);
                    e.into()
                })
                .and_then(|client| {
                    NatsStreamingClient::from(client)
                        .cluster_id(cluster_id)
                        .connect()
                })
                .map_err(|streaming_error| error!("{}", streaming_error))
                .and_then(move |client| {
                    sync_tx.send(()).expect("Couldn't synchronize!");
                    event_rx.for_each(move |event: Vec<u8>| {
                        let publish_event = client
                            .publish(HABITAT_SUBJECT.into(), event.into())
                            .map_err(|e| {
                                error!("Error publishing event: {:?}", e);
                            });
                        executor::spawn(publish_event);
                        Ok(())
                    })
                });

                              ThreadRuntime::new().expect("Couldn't create event stream runtime!")
                                                  .spawn(publisher)
                                                  .run()
                                                  .expect("something seriously wrong has occurred");
                          })
                          .expect("Couldn't start events thread!");

    sync_rx.recv()?; // TODO (CM): nicer error message
    Ok(EventStream(event_tx))
}
<commit_msg>fixes nats connection info username<commit_after>//! Main interface for a stream of events the Supervisor can send out
//! in the course of its operations.
//!
//! Currently, the Supervisor is able to send events to a [NATS
//! Streaming][1] server. The `init_stream` function must be called
//! before sending events to initialize the publishing thread in the
//! background. Thereafter, you can pass "event" structs to the
//! `event` function, which will publish the event to the stream.
//!
//! All events are published under the "habitat" subject.
//!
//! [1]:https://github.com/nats-io/nats-streaming-server

mod types;

pub(crate) use self::types::ServiceMetadata;
use self::types::{EventMessage,
                  EventMetadata,
                  HealthCheckEvent,
                  ServiceStartedEvent,
                  ServiceStoppedEvent};
use crate::{error::Result,
            manager::{service::{HealthCheckResult,
                                Service},
                      sys::Sys}};
use clap::ArgMatches;
use futures::{sync::{mpsc as futures_mpsc,
                     mpsc::UnboundedSender},
              Future,
              Stream};
use habitat_common::types::{AutomateAuthToken,
                            EventStreamMetadata};
use nitox::{commands::ConnectCommand,
            streaming::client::NatsStreamingClient,
            NatsClient,
            NatsClientOptions};
use state::Container;
use std::{net::SocketAddr,
          sync::{mpsc as std_mpsc,
                 Once},
          thread,
          time::Duration};
use tokio::{executor,
            runtime::current_thread::Runtime as ThreadRuntime};

static INIT: Once = Once::new();
lazy_static! {
    // TODO (CM): When const fn support lands in stable, we can ditch
    // this lazy_static call.

    /// Reference to the event stream.
    static ref EVENT_STREAM: Container = Container::new();
    /// Core information that is shared between all events.
    static ref EVENT_CORE: Container = Container::new();
}

/// Starts a new thread for sending events to a NATS Streaming
/// server. Stashes the handle to the stream, as well as the core
/// event information that will be a part of all events, in a global
/// static reference for access later.
pub fn init_stream(config: EventStreamConfig, event_core: EventCore) {
    INIT.call_once(|| {
            let conn_info = EventConnectionInfo::new(config.token, config.url);
            let event_stream = init_nats_stream(conn_info).expect("Could not start NATS thread");
            EVENT_STREAM.set(event_stream);
            EVENT_CORE.set(event_core);
        });
}

/// Captures all event stream-related configuration options that would
/// be passed in by a user
#[derive(Clone, Debug)]
pub struct EventStreamConfig {
    environment: String,
    application: String,
    meta:        EventStreamMetadata,
    token:       AutomateAuthToken,
    url:         String,
}

impl EventStreamConfig {
    /// Create an instance from Clap arguments.
    pub fn from_matches(m: &ArgMatches) -> Result<EventStreamConfig> {
        Ok(EventStreamConfig { environment: m.value_of("EVENT_STREAM_ENVIRONMENT")
                                             .map(str::to_string)
                                             .expect("Required option for EventStream feature"),
                               application: m.value_of("EVENT_STREAM_APPLICATION")
                                             .map(str::to_string)
                                             .expect("Required option for EventStream feature"),
                               meta:        EventStreamMetadata::from_matches(m)?,
                               token:       AutomateAuthToken::from_matches(m)?,
                               url:         m.value_of("EVENT_STREAM_URL")
                                             .map(str::to_string)
                                             .expect("Required option for EventStream feature"), })
    }
}

/// All the information needed to establish a connection to a NATS
/// Streaming server.
// TODO: This will change as we firm up what the interaction between
// Habitat and A2 looks like.
pub struct EventConnectionInfo {
    pub name:        String,
    pub verbose:     bool,
    pub cluster_uri: String,
    pub cluster_id:  String,
    pub auth_token:  AutomateAuthToken,
}

impl EventConnectionInfo {
    pub fn new(auth_token: AutomateAuthToken, cluster_uri: String) -> Self {
        EventConnectionInfo { name: String::from("hab_client"),
                              verbose: true,
                              cluster_uri,
                              cluster_id: String::from("event-service"),
                              auth_token }
    }
}

/// A collection of data that will be present in all events. Rather
/// than baking this into the structure of each event, we represent it
/// once and merge the information into the final rendered form of the
/// event.
///
/// This prevents us from having to thread information throughout the
/// system, just to get it to the places where the events are
/// generated (e.g., not all code has direct access to the
/// Supervisor's ID).
#[derive(Clone, Debug)]
pub struct EventCore {
    /// The unique identifier of the Supervisor sending the event.
    supervisor_id: String,
    ip_address: SocketAddr,
    // TODO (CM): could add application and environment to the meta
    // map directly... hrmm
    application: String,
    environment: String,
    meta:        EventStreamMetadata,
}

impl EventCore {
    pub fn new(config: &EventStreamConfig, sys: &Sys) -> Self {
        EventCore { supervisor_id: sys.member_id.clone(),
                    ip_address:    sys.gossip_listen(),
                    environment:   config.environment.clone(),
                    application:   config.application.clone(),
                    meta:          config.meta.clone(), }
    }
}

/// Send an event for the start of a Service.
pub fn service_started(service: &Service) {
    if stream_initialized() {
        publish(ServiceStartedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

/// Send an event for the stop of a Service.
pub fn service_stopped(service: &Service) {
    if stream_initialized() {
        publish(ServiceStoppedEvent { service_metadata: Some(service.to_service_metadata()),
                                      event_metadata:   None, });
    }
}

// Takes metadata directly, rather than a `&Service` like other event
// functions, because of how the asynchronous health checking
// currently works. Revisit when async/await + Pin is all stabilized.
pub fn health_check(metadata: ServiceMetadata,
                    check_result: HealthCheckResult,
                    duration: Option<Duration>) {
    if stream_initialized() {
        publish(HealthCheckEvent { service_metadata: Some(metadata),
                                   event_metadata:   None,
                                   result:           Into::<types::HealthCheck>::into(check_result)
                                                     as i32,
                                   duration:         duration.map(Duration::into), });
    }
}

////////////////////////////////////////////////////////////////////////

/// Internal helper function to know whether or not to go to the trouble of
/// creating event structures. If the event stream hasn't been
/// initialized, then we shouldn't need to do anything.
fn stream_initialized() -> bool { EVENT_STREAM.try_get::<EventStream>().is_some() }

/// Publish an event. This is the main interface that client code will
/// use.
///
/// If `init_stream` has not been called already, this function will
/// be a no-op.
fn publish(mut event: impl EventMessage) {
    if let Some(e) = EVENT_STREAM.try_get::<EventStream>() {
        // TODO (CM): Yeah... this is looking pretty gross. The
        // intention is to be able to timestamp the events right as
        // they go out.
        //
        // We *could* set the time when we convert the EventCore to a
        // EventMetadata struct, but that seems odd.
        //
        // It probably means that this structure just isn't the right
        // one.
        //
        // The ugliness is at least contained, though.
        event.event_metadata(EventMetadata { timestamp:
                                                 Some(std::time::SystemTime::now().into()),
                                             ..EVENT_CORE.get::<EventCore>().to_event_metadata() });

        e.send(event.to_bytes());
    }
}

/// A lightweight handle for the event stream. All events get to the
/// event stream through this.
struct EventStream(UnboundedSender<Vec<u8>>);

impl EventStream {
    /// Queues an event to be sent out.
    fn send(&self, event: Vec<u8>) {
        trace!("About to queue an event: {:?}", event);
        if let Err(e) = self.0.unbounded_send(event) {
            error!("Failed to queue event: {:?}", e);
        }
    }
}

////////////////////////////////////////////////////////////////////////

/// All messages are published under this subject.
const HABITAT_SUBJECT: &str = "habitat";

fn init_nats_stream(conn_info: EventConnectionInfo) -> Result<EventStream> {
    // TODO (CM): Investigate back-pressure scenarios
    let (event_tx, event_rx) = futures_mpsc::unbounded();
    let (sync_tx, sync_rx) = std_mpsc::sync_channel(0); // rendezvous channel

    // TODO (CM): We could theoretically create this future and spawn
    // it in the Supervisor's Tokio runtime, but there's currently a
    // bug: https://github.com/YellowInnovation/nitox/issues/24

    thread::Builder::new().name("events".to_string())
                          .spawn(move || {
                              let EventConnectionInfo { name,
                                                        verbose,
                                                        cluster_uri,
                                                        cluster_id,
                                                        auth_token, } = conn_info;
                              let cc =
                                  ConnectCommand::builder().name(Some(name))
                                                           .verbose(verbose)
                                                           .auth_token(Some(auth_token.to_string()))
                                                           .tls_required(false)
                                                           .build()
                                                           .unwrap();
                              let opts =
                                  NatsClientOptions::builder().connect_command(cc)
                                                              .cluster_uri(cluster_uri.as_str())
                                                              .build()
                                                              .unwrap();

                              let publisher = NatsClient::from_options(opts)
                .map_err(|e| {
                    error!("Error creating Nats Client from options: {}", e);
                    e.into()
                })
                .and_then(|client| {
                    NatsStreamingClient::from(client)
                        .cluster_id(cluster_id)
                        .connect()
                })
                .map_err(|streaming_error| error!("{}", streaming_error))
                .and_then(move |client| {
                    sync_tx.send(()).expect("Couldn't synchronize!");
                    event_rx.for_each(move |event: Vec<u8>| {
                        let publish_event = client
                            .publish(HABITAT_SUBJECT.into(), event.into())
                            .map_err(|e| {
                                error!("Error publishing event: {:?}", e);
                            });
                        executor::spawn(publish_event);
                        Ok(())
                    })
                });

                              ThreadRuntime::new().expect("Couldn't create event stream runtime!")
                                                  .spawn(publisher)
                                                  .run()
                                                  .expect("something seriously wrong has occurred");
                          })
                          .expect("Couldn't start events thread!");

    sync_rx.recv()?; // TODO (CM): nicer error message
    Ok(EventStream(event_tx))
}
<|endoftext|>


<commit_before>use crate::type_length;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{DataEnum, Fields, FieldsNamed, FieldsUnnamed, Ident, Variant};

pub fn generate(name: Ident, data_enum: DataEnum) -> TokenStream {
    let mut generator = EnumGenerator::empty();
    for variant in &data_enum.variants {
        generator.handle_variant(variant);
    }
    generator.finish(&name)
}

/// Total length is the sum of each variant's length. To represent a variant, its number is added to
/// the sum of previous variant lengths.
#[derive(Debug)]
struct EnumGenerator {
    length: TokenStream,
    from_usize_arms: TokenStream,
    into_usize_arms: TokenStream,
}

impl EnumGenerator {
    fn empty() -> Self {
        Self {
            length: quote! { 0usize },
            from_usize_arms: quote! {},
            into_usize_arms: quote! {},
        }
    }

    fn finish(&self, name: &Ident) -> TokenStream {
        let length = &self.length;
        let from_usize_arms = &self.from_usize_arms;
        let into_usize_arms = &self.into_usize_arms;

        quote! {
            #[automatically_derived]
            impl ::enum_map::Enum for #name {
                const LENGTH: ::enum_map::usize = #length;

                #[inline]
                fn from_usize(value: ::enum_map::usize) -> Self {
                    #from_usize_arms {
                        ::enum_map::unreachable!()
                    }
                }

                #[inline]
                fn into_usize(self) -> ::enum_map::usize {
                    match self {
                        #into_usize_arms
                    }
                }
            }

            #[automatically_derived]
            impl<V> ::enum_map::EnumArray<V> for #name {
                type Array = [V; #length];
            }
        }
    }

    fn handle_variant(&mut self, variant: &Variant) {
        match &variant.fields {
            Fields::Unit => self.handle_unit_variant(&variant.ident),
            Fields::Unnamed(fields) => self.handle_unnamed_variant(&variant.ident, fields),
            Fields::Named(fields) => self.handle_named_variant(&variant.ident, fields),
        }
    }

    /// Becomes simply `1` in counting, since this is the size of the unit.
    fn handle_unit_variant(&mut self, variant: &Ident) {
        let into_arms = &self.into_usize_arms;
        let length = &self.length;
        self.into_usize_arms = quote! { #into_arms Self::#variant => #length, };
        self.length = quote! { (#length + 1) };

        let from_arms = &self.from_usize_arms;
        let length = &self.length;
        self.from_usize_arms = quote! {
            #from_arms if value < #length {
                Self::#variant
            } else
        };
    }

    /// Its size is the product of the sizes of its members. To represent this variant, one can
    /// think of this as representing a little-endian number. First member is simply added, but
    /// next members are multiplied before being added.
    fn handle_unnamed_variant(&mut self, variant: &Ident, fields: &FieldsUnnamed) {
        let length = &self.length;
        let mut expr_into = quote! { #length };
        let mut fields_length = quote! { 1usize };
        let mut params_from = quote! {};
        for (i, field) in fields.unnamed.iter().enumerate() {
            let ident = format_ident!("p{}", i);
            let ty = &field.ty;
            let field_length = type_length(ty);

            expr_into = quote! {
                (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident))
            };

            params_from = quote! {
                #params_from <#ty as ::enum_map::Enum>::from_usize(
                    (value - #length) / #fields_length % #field_length
                ),
            };

            fields_length = quote! { (#fields_length * #field_length) };
        }

        self.length = quote! { (#length + #fields_length) };

        let length = &self.length;
        let from_arms = &self.from_usize_arms;
        self.from_usize_arms = quote! {
            #from_arms if value < #length {
                Self::#variant(#params_from)
            } else
        };

        let mut params_into = quote! {};
        for i in 0..fields.unnamed.len() {
            let ident = format_ident!("p{}", i);
            params_into = quote! { #params_into #ident, };
        }

        let into_arms = &self.into_usize_arms;
        self.into_usize_arms = quote! {
            #into_arms Self::#variant(#params_into) => #expr_into,
        };
    }

    /// Its size is the product of the sizes of its members. To represent this variant, one can
    /// think of this as representing a little-endian number. First member is simply added, but
    /// next members are multiplied before being added.
    fn handle_named_variant(&mut self, variant: &Ident, fields: &FieldsNamed) {
        let length = &self.length;
        let mut expr_into = quote! { #length };
        let mut fields_length = quote! { 1usize };
        let mut params_from = quote! {};

        for field in fields.named.iter() {
            let ident = field.ident.as_ref().unwrap();
            let ty = &field.ty;
            let field_length = type_length(ty);

            expr_into = quote! {
                (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident))
            };

            params_from = quote! {
                #params_from #ident: <#ty as ::enum_map::Enum>::from_usize(
                    (value - #length) / #fields_length % #field_length
                ),
            };

            fields_length = quote! { (#fields_length * #field_length) };
        }

        self.length = quote! { (#length + #fields_length) };

        let length = &self.length;
        let from_arms = &self.from_usize_arms;
        self.from_usize_arms = quote! {
            #from_arms if value < #length {
                Self::#variant { #params_from }
            } else
        };

        let mut params_into = quote! {};
        for field in fields.named.iter() {
            let ident = field.ident.as_ref().unwrap();
            params_into = quote! { #params_into #ident, };
        }

        let into_arms = &self.into_usize_arms;
        self.into_usize_arms = quote! {
            #into_arms Self::#variant { #params_into } => #expr_into,
        };
    }
}
<commit_msg>Optimize unit variant handling<commit_after>use crate::type_length;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{DataEnum, Fields, FieldsNamed, FieldsUnnamed, Ident, Variant};

pub fn generate(name: Ident, data_enum: DataEnum) -> TokenStream {
    let mut generator = EnumGenerator::empty();
    for variant in &data_enum.variants {
        generator.handle_variant(variant);
    }
    generator.finish(&name)
}

/// Total length is the sum of each variant's length. To represent a variant, its number is added to
/// the sum of previous variant lengths.
#[derive(Debug)]
struct EnumGenerator {
    length: TokenStream,
    from_usize_arms: TokenStream,
    into_usize_arms: TokenStream,
}

impl EnumGenerator {
    fn empty() -> Self {
        Self {
            length: quote! { 0usize },
            from_usize_arms: quote! {},
            into_usize_arms: quote! {},
        }
    }

    fn finish(&self, name: &Ident) -> TokenStream {
        let length = &self.length;
        let from_usize_arms = &self.from_usize_arms;
        let into_usize_arms = &self.into_usize_arms;

        quote! {
            #[automatically_derived]
            impl ::enum_map::Enum for #name {
                const LENGTH: ::enum_map::usize = #length;

                #[inline]
                fn from_usize(value: ::enum_map::usize) -> Self {
                    #from_usize_arms {
                        ::enum_map::unreachable!()
                    }
                }

                #[inline]
                fn into_usize(self) -> ::enum_map::usize {
                    match self {
                        #into_usize_arms
                    }
                }
            }

            #[automatically_derived]
            impl<V> ::enum_map::EnumArray<V> for #name {
                type Array = [V; #length];
            }
        }
    }

    fn handle_variant(&mut self, variant: &Variant) {
        match &variant.fields {
            Fields::Unit => self.handle_unit_variant(&variant.ident),
            Fields::Unnamed(fields) => self.handle_unnamed_variant(&variant.ident, fields),
            Fields::Named(fields) => self.handle_named_variant(&variant.ident, fields),
        }
    }

    /// Becomes simply `1` in counting, since this is the size of the unit.
    fn handle_unit_variant(&mut self, variant: &Ident) {
        let into_arms = &self.into_usize_arms;
        let length = &self.length;
        self.into_usize_arms = quote! { #into_arms Self::#variant => #length, };
        let from_arms = &self.from_usize_arms;
        self.from_usize_arms = quote! {
            #from_arms if value == #length {
                Self::#variant
            } else
        };
        self.length = quote! { (#length + 1) };
    }

    /// Its size is the product of the sizes of its members. To represent this variant, one can
    /// think of this as representing a little-endian number. First member is simply added, but
    /// next members are multiplied before being added.
    fn handle_unnamed_variant(&mut self, variant: &Ident, fields: &FieldsUnnamed) {
        let length = &self.length;
        let mut expr_into = quote! { #length };
        let mut fields_length = quote! { 1usize };
        let mut params_from = quote! {};
        for (i, field) in fields.unnamed.iter().enumerate() {
            let ident = format_ident!("p{}", i);
            let ty = &field.ty;
            let field_length = type_length(ty);

            expr_into = quote! {
                (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident))
            };

            params_from = quote! {
                #params_from <#ty as ::enum_map::Enum>::from_usize(
                    (value - #length) / #fields_length % #field_length
                ),
            };

            fields_length = quote! { (#fields_length * #field_length) };
        }

        self.length = quote! { (#length + #fields_length) };

        let length = &self.length;
        let from_arms = &self.from_usize_arms;
        self.from_usize_arms = quote! {
            #from_arms if value < #length {
                Self::#variant(#params_from)
            } else
        };

        let mut params_into = quote! {};
        for i in 0..fields.unnamed.len() {
            let ident = format_ident!("p{}", i);
            params_into = quote! { #params_into #ident, };
        }

        let into_arms = &self.into_usize_arms;
        self.into_usize_arms = quote! {
            #into_arms Self::#variant(#params_into) => #expr_into,
        };
    }

    /// Its size is the product of the sizes of its members. To represent this variant, one can
    /// think of this as representing a little-endian number. First member is simply added, but
    /// next members are multiplied before being added.
    fn handle_named_variant(&mut self, variant: &Ident, fields: &FieldsNamed) {
        let length = &self.length;
        let mut expr_into = quote! { #length };
        let mut fields_length = quote! { 1usize };
        let mut params_from = quote! {};

        for field in fields.named.iter() {
            let ident = field.ident.as_ref().unwrap();
            let ty = &field.ty;
            let field_length = type_length(ty);

            expr_into = quote! {
                (#expr_into + #fields_length * ::enum_map::Enum::into_usize(#ident))
            };

            params_from = quote! {
                #params_from #ident: <#ty as ::enum_map::Enum>::from_usize(
                    (value - #length) / #fields_length % #field_length
                ),
            };

            fields_length = quote! { (#fields_length * #field_length) };
        }

        self.length = quote! { (#length + #fields_length) };

        let length = &self.length;
        let from_arms = &self.from_usize_arms;
        self.from_usize_arms = quote! {
            #from_arms if value < #length {
                Self::#variant { #params_from }
            } else
        };

        let mut params_into = quote! {};
        for field in fields.named.iter() {
            let ident = field.ident.as_ref().unwrap();
            params_into = quote! { #params_into #ident, };
        }

        let into_arms = &self.into_usize_arms;
        self.into_usize_arms = quote! {
            #into_arms Self::#variant { #params_into } => #expr_into,
        };
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

pub fn get_template_source(tpl_path: &Path) -> String {
    let mut path = template_dir();
    path.push(tpl_path);
    let mut f = match File::open(&path) {
        Err(_) => {
            let msg = format!("unable to open template file '{}'", &path.to_str().unwrap());
            panic!(msg)
        },
        Ok(f) => f,
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    if s.ends_with('\n') {
        let _ = s.pop();
    }
    s
}

pub fn find_template_from_path(path: &str, start_at: Option<&Path>) -> PathBuf {
    let root = template_dir();
    if let Some(rel) = start_at {
        let mut fs_rel_path = root.clone();
        fs_rel_path.push(rel);
        fs_rel_path = fs_rel_path.with_file_name(path);
        if fs_rel_path.exists() {
            return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
        }
    }

    let mut fs_abs_path = root.clone();
    let path = Path::new(path);
    fs_abs_path.push(Path::new(path));
    if fs_abs_path.exists() {
        path.to_owned()
    } else {
        panic!(format!("template '{:?}' not found", path.to_str()));
    }
}

pub fn template_dir() -> PathBuf {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("templates");
    path
}

#[cfg(test)]
mod tests {
    use super::{find_template_from_path, get_template_source};
    use super::Path;

    #[test]
    fn get_source() {
        assert_eq!(get_template_source(Path::new("sub/b.html")), "bar");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some(Path::new("a.html")));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some(Path::new("a.html")));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<commit_msg>Improve error message when template is not found<commit_after>use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

pub fn get_template_source(tpl_path: &Path) -> String {
    let mut path = template_dir();
    path.push(tpl_path);
    let mut f = match File::open(&path) {
        Err(_) => {
            let msg = format!("unable to open template file '{}'", &path.to_str().unwrap());
            panic!(msg)
        },
        Ok(f) => f,
    };
    let mut s = String::new();
    f.read_to_string(&mut s).unwrap();
    if s.ends_with('\n') {
        let _ = s.pop();
    }
    s
}

pub fn find_template_from_path(path: &str, start_at: Option<&Path>) -> PathBuf {
    let root = template_dir();
    if let Some(rel) = start_at {
        let mut fs_rel_path = root.clone();
        fs_rel_path.push(rel);
        fs_rel_path = fs_rel_path.with_file_name(path);
        if fs_rel_path.exists() {
            return fs_rel_path.strip_prefix(&root).unwrap().to_owned();
        }
    }

    let mut fs_abs_path = root.clone();
    let path = Path::new(path);
    fs_abs_path.push(Path::new(path));
    if fs_abs_path.exists() {
        path.to_owned()
    } else {
        panic!(format!("template {:?} not found at {:?}", path.to_str().unwrap(), fs_abs_path));
    }
}

pub fn template_dir() -> PathBuf {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("templates");
    path
}

#[cfg(test)]
mod tests {
    use super::{find_template_from_path, get_template_source};
    use super::Path;

    #[test]
    fn get_source() {
        assert_eq!(get_template_source(Path::new("sub/b.html")), "bar");
    }

    #[test]
    fn find_absolute() {
        let path = find_template_from_path("sub/b.html", Some(Path::new("a.html")));
        assert_eq!(path, Path::new("sub/b.html"));
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        find_template_from_path("b.html", Some(Path::new("a.html")));
    }

    #[test]
    fn find_relative() {
        let path = find_template_from_path("c.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/c.html"));
    }

    #[test]
    fn find_relative_sub() {
        let path = find_template_from_path("sub1/d.html", Some(Path::new("sub/b.html")));
        assert_eq!(path, Path::new("sub/sub1/d.html"));
    }
}
<|endoftext|>