<commit_before>use gstreamer as gst;
use gstreamer_audio as gst_audio;

use log::{debug, info};

use smallvec::SmallVec;

use std::{
    mem,
    sync::{Arc, Mutex},
};

use super::{
    AudioBuffer, AudioChannel, Duration, SampleExtractor, SampleIndex, SampleIndexRange,
    INLINE_CHANNELS, QUEUE_SIZE,
};

const EXTRACTION_THRESHOLD: SampleIndexRange = SampleIndexRange::new(1024);

// The DoubleBuffer is reponsible for ensuring a thread safe double buffering
// mechanism that receives samples from GStreamer, prepares an extraction of
// these samples and presents the most recent extraction to an external
// mechanism (e.g. UI).
// The DoubleBuffer hosts two SampleExtractors and an AudioBuffer:
//   - The SampleExtractor trait is designed to allow the extraction of samples
//   depending on certains conditions as defined in its concrete implementation.
//   - The AudioBuffer manages the container for all the samples received.
// The DoubleBuffer prepares the extraction in the working_buffer and exposes
// the exposed_buffer during that time. When the extration is done, the buffers
// are swapped.
pub struct DoubleAudioBuffer {
    state: gst::State,
    audio_buffer: AudioBuffer,
    samples_since_last_extract: SampleIndex,
    exposed_buffer_mtx: Arc<Mutex<Box<dyn SampleExtractor>>>,
    working_buffer: Option<Box<dyn SampleExtractor>>,
    lower_to_keep: SampleIndex,
    sample_gauge: Option<SampleIndex>,
    sample_window: Option<SampleIndexRange>,
    max_sample_window: SampleIndexRange,
    can_handle_eos: bool, // accept / ignore eos (required for range seeks handling)
    has_new_position: bool,
}

impl DoubleAudioBuffer {
    // need 2 arguments for new as we can't clone buffers as they are known
    // as trait SampleExtractor
    pub fn new(
        buffer_duration: Duration,
        exposed_buffer: Box<dyn SampleExtractor>,
        working_buffer: Box<dyn SampleExtractor>,
    ) -> DoubleAudioBuffer {
        DoubleAudioBuffer {
            state: gst::State::Null,
            audio_buffer: AudioBuffer::new(buffer_duration),
            samples_since_last_extract: SampleIndex::default(),
            exposed_buffer_mtx: Arc::new(Mutex::new(exposed_buffer)),
            working_buffer: Some(working_buffer),
            lower_to_keep: SampleIndex::default(),
            sample_gauge: None,
            sample_window: None,
            max_sample_window: SampleIndexRange::default(),
            can_handle_eos: true,
            has_new_position: false,
        }
    }

    // Get a reference on the exposed buffer mutex.
    pub fn get_exposed_buffer_mtx(&self) -> Arc<Mutex<Box<dyn SampleExtractor>>> {
        Arc::clone(&self.exposed_buffer_mtx)
    }

    pub fn cleanup(&mut self) {
        self.reset();
        self.audio_buffer.cleanup();

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer.cleanup();
        }

        self.working_buffer.as_mut().unwrap().cleanup();
    }

    fn reset(&mut self) {
        self.state = gst::State::Null;
        self.samples_since_last_extract = SampleIndex::default();
        self.lower_to_keep = SampleIndex::default();
        self.sample_gauge = None;
        self.sample_window = None;
        self.max_sample_window = SampleIndexRange::default();
        self.can_handle_eos = true;
        self.has_new_position = false;
    }

    pub fn set_state(&mut self, state: gst::State) {
        debug!("changing state from {:?} to {:?}", self.state, state);
        self.state = state;
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.set_state(state);
        }
        self.working_buffer.as_mut().unwrap().set_state(state);
        if state == gst::State::Paused {
            self.refresh();
        }
    }

    pub fn clean_samples(&mut self) {
        self.audio_buffer.clean_samples();
        // Also reset basetime
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.reset_base_ts();
        }
        self.working_buffer.as_mut().unwrap().reset_base_ts();
    }

    pub fn set_caps(&mut self, caps: &gst::CapsRef) {
        info!("changing caps");
        let audio_info = gst_audio::AudioInfo::from_caps(caps).unwrap();

        self.reset();

        let rate = u64::from(audio_info.rate());
        let channels = audio_info.channels() as usize;

        let sample_duration = Duration::from_frequency(rate);
        self.max_sample_window = SampleIndexRange::from_duration(QUEUE_SIZE, sample_duration);
        let duration_per_1000_samples = Duration::from_nanos(1_000_000_000_000u64 / rate);

        let mut channels: SmallVec<[AudioChannel; INLINE_CHANNELS]> =
            SmallVec::with_capacity(channels);
        if let Some(positions) = audio_info.positions() {
            for position in positions.iter().take(INLINE_CHANNELS) {
                channels.push(AudioChannel::new(*position));
            }
        };

        self.audio_buffer.init(audio_info);

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer.set_sample_duration(sample_duration, duration_per_1000_samples);
            exposed_buffer.set_channels(&channels);
            exposed_buffer.new_segment();
        }

        let working_buffer = self.working_buffer.as_mut().unwrap();
        working_buffer.set_sample_duration(sample_duration, duration_per_1000_samples);
        working_buffer.set_channels(&channels);
        working_buffer.new_segment();
    }

    pub fn set_ref(&mut self, audio_ref: &gst::Element) {
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.set_time_ref(audio_ref);
        }
        self.working_buffer
            .as_mut()
            .unwrap()
            .set_time_ref(audio_ref);
    }

    pub fn ignore_eos(&mut self) {
        self.can_handle_eos = false;
    }

    pub fn accept_eos(&mut self) {
        self.can_handle_eos = true;
    }

    pub fn handle_eos(&mut self) {
        if self.can_handle_eos {
            self.audio_buffer.handle_eos();
            // extract last samples and swap
            self.extract_samples();
            // do it again to update second extractor too
            // this is required in case of a subsequent seek
            // in the extractors' range
            self.extract_samples();
        }
        self.sample_gauge = None
    }

    pub fn have_gst_segment(&mut self, segment: &gst::Segment) {
        self.audio_buffer
            .have_gst_segment(segment.get_start().get_value().into());
        self.sample_gauge = Some(SampleIndex::default());
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.new_segment();
        }
        self.working_buffer.as_mut().unwrap().new_segment();
    }

    pub fn push_gst_buffer(&mut self, buffer: &gst::Buffer) -> bool {
        // store incoming samples
        let sample_nb = self
            .audio_buffer
            .push_gst_buffer(buffer, self.lower_to_keep);
        self.samples_since_last_extract += sample_nb;

        let must_notify = if self.state != gst::State::Playing {
            if let Some(mut gauge) = self.sample_gauge.take() {
                gauge += sample_nb;
                let must_notify = self
                    .sample_window
                    .map_or(false, |sample_window| gauge >= sample_window);

                if !must_notify {
                    self.sample_gauge = Some(gauge);
                }
                // after the threshold is reached, the gauge is removed
                must_notify
            } else {
                false
            }
        } else {
            false
        };

        if must_notify || self.samples_since_last_extract >= EXTRACTION_THRESHOLD {
            // extract new samples and swap
            self.extract_samples();
            self.samples_since_last_extract = SampleIndex::default();

            if must_notify {
                self.sample_gauge = None;
            }
        }

        must_notify
    }

    // Update the working extractor with new samples and swap.
    fn extract_samples(&mut self) {
        let mut working_buffer = self.working_buffer.take().unwrap();
        working_buffer.extract_samples(&self.audio_buffer);

        // swap buffers
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            // get latest state from the previously exposed buffer
            // in order to smoothen rendering between frames
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();
        self.sample_window = working_buffer
            .get_requested_sample_window()
            .map(|req_sample_window| req_sample_window.min(self.max_sample_window));

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }

    pub fn refresh(&mut self) {
        // refresh with current conditions
        let mut working_buffer = self.working_buffer.take().unwrap();
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();

            if self.state != gst::State::Playing {
                exposed_buffer_box.switch_to_paused();
            }

            // get latest state from the previously exposed buffer
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());

            // refresh working buffer
            working_buffer.refresh(&self.audio_buffer);

            // swap buffers
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }
}
<commit_msg>DoubleAudioBuffer: optimize some more<commit_after>use gstreamer as gst;
use gstreamer_audio as gst_audio;

use log::{debug, info};

use smallvec::SmallVec;

use std::{
    mem,
    sync::{Arc, Mutex},
};

use super::{
    AudioBuffer, AudioChannel, Duration, SampleExtractor, SampleIndex, SampleIndexRange,
    INLINE_CHANNELS, QUEUE_SIZE,
};

const EXTRACTION_THRESHOLD: SampleIndexRange = SampleIndexRange::new(4096);

// The DoubleBuffer is reponsible for ensuring a thread safe double buffering
// mechanism that receives samples from GStreamer, prepares an extraction of
// these samples and presents the most recent extraction to an external
// mechanism (e.g. UI).
// The DoubleBuffer hosts two SampleExtractors and an AudioBuffer:
//   - The SampleExtractor trait is designed to allow the extraction of samples
//   depending on certains conditions as defined in its concrete implementation.
//   - The AudioBuffer manages the container for all the samples received.
// The DoubleBuffer prepares the extraction in the working_buffer and exposes
// the exposed_buffer during that time. When the extration is done, the buffers
// are swapped.
pub struct DoubleAudioBuffer {
    state: gst::State,
    audio_buffer: AudioBuffer,
    samples_since_last_extract: SampleIndex,
    exposed_buffer_mtx: Arc<Mutex<Box<dyn SampleExtractor>>>,
    working_buffer: Option<Box<dyn SampleExtractor>>,
    lower_to_keep: SampleIndex,
    sample_gauge: Option<SampleIndex>,
    sample_window: Option<SampleIndexRange>,
    max_sample_window: SampleIndexRange,
    can_handle_eos: bool, // accept / ignore eos (required for range seeks handling)
    has_new_position: bool,
}

impl DoubleAudioBuffer {
    // need 2 arguments for new as we can't clone buffers as they are known
    // as trait SampleExtractor
    pub fn new(
        buffer_duration: Duration,
        exposed_buffer: Box<dyn SampleExtractor>,
        working_buffer: Box<dyn SampleExtractor>,
    ) -> DoubleAudioBuffer {
        DoubleAudioBuffer {
            state: gst::State::Null,
            audio_buffer: AudioBuffer::new(buffer_duration),
            samples_since_last_extract: SampleIndex::default(),
            exposed_buffer_mtx: Arc::new(Mutex::new(exposed_buffer)),
            working_buffer: Some(working_buffer),
            lower_to_keep: SampleIndex::default(),
            sample_gauge: None,
            sample_window: None,
            max_sample_window: SampleIndexRange::default(),
            can_handle_eos: true,
            has_new_position: false,
        }
    }

    // Get a reference on the exposed buffer mutex.
    pub fn get_exposed_buffer_mtx(&self) -> Arc<Mutex<Box<dyn SampleExtractor>>> {
        Arc::clone(&self.exposed_buffer_mtx)
    }

    pub fn cleanup(&mut self) {
        self.reset();
        self.audio_buffer.cleanup();

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer.cleanup();
        }

        self.working_buffer.as_mut().unwrap().cleanup();
    }

    fn reset(&mut self) {
        self.state = gst::State::Null;
        self.samples_since_last_extract = SampleIndex::default();
        self.lower_to_keep = SampleIndex::default();
        self.sample_gauge = None;
        self.sample_window = None;
        self.max_sample_window = SampleIndexRange::default();
        self.can_handle_eos = true;
        self.has_new_position = false;
    }

    pub fn set_state(&mut self, state: gst::State) {
        debug!("changing state from {:?} to {:?}", self.state, state);
        self.state = state;
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.set_state(state);
        }
        self.working_buffer.as_mut().unwrap().set_state(state);
        if state == gst::State::Paused {
            self.refresh();
        }
    }

    pub fn clean_samples(&mut self) {
        self.audio_buffer.clean_samples();
        // Also reset basetime
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.reset_base_ts();
        }
        self.working_buffer.as_mut().unwrap().reset_base_ts();
    }

    pub fn set_caps(&mut self, caps: &gst::CapsRef) {
        info!("changing caps");
        let audio_info = gst_audio::AudioInfo::from_caps(caps).unwrap();

        self.reset();

        let rate = u64::from(audio_info.rate());
        let channels = audio_info.channels() as usize;

        let sample_duration = Duration::from_frequency(rate);
        self.max_sample_window = SampleIndexRange::from_duration(QUEUE_SIZE, sample_duration);
        let duration_per_1000_samples = Duration::from_nanos(1_000_000_000_000u64 / rate);

        let mut channels: SmallVec<[AudioChannel; INLINE_CHANNELS]> =
            SmallVec::with_capacity(channels);
        if let Some(positions) = audio_info.positions() {
            for position in positions.iter().take(INLINE_CHANNELS) {
                channels.push(AudioChannel::new(*position));
            }
        };

        self.audio_buffer.init(audio_info);

        {
            let exposed_buffer = &mut self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer.set_sample_duration(sample_duration, duration_per_1000_samples);
            exposed_buffer.set_channels(&channels);
            exposed_buffer.new_segment();
        }

        let working_buffer = self.working_buffer.as_mut().unwrap();
        working_buffer.set_sample_duration(sample_duration, duration_per_1000_samples);
        working_buffer.set_channels(&channels);
        working_buffer.new_segment();
    }

    pub fn set_ref(&mut self, audio_ref: &gst::Element) {
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.set_time_ref(audio_ref);
        }
        self.working_buffer
            .as_mut()
            .unwrap()
            .set_time_ref(audio_ref);
    }

    pub fn ignore_eos(&mut self) {
        self.can_handle_eos = false;
    }

    pub fn accept_eos(&mut self) {
        self.can_handle_eos = true;
    }

    pub fn handle_eos(&mut self) {
        if self.can_handle_eos {
            self.audio_buffer.handle_eos();
            // extract last samples and swap
            self.extract_samples();
            // do it again to update second extractor too
            // this is required in case of a subsequent seek
            // in the extractors' range
            self.extract_samples();
        }
        self.sample_gauge = None
    }

    pub fn have_gst_segment(&mut self, segment: &gst::Segment) {
        self.audio_buffer
            .have_gst_segment(segment.get_start().get_value().into());
        self.sample_gauge = Some(SampleIndex::default());
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            exposed_buffer_box.new_segment();
        }
        self.working_buffer.as_mut().unwrap().new_segment();
    }

    pub fn push_gst_buffer(&mut self, buffer: &gst::Buffer) -> bool {
        // store incoming samples
        let sample_nb = self
            .audio_buffer
            .push_gst_buffer(buffer, self.lower_to_keep);
        self.samples_since_last_extract += sample_nb;

        let mut must_notify = false;
        if self.state != gst::State::Playing {
            if let Some(gauge) = self.sample_gauge.as_mut() {
                *gauge += sample_nb;
                let gauge = *gauge; // let go the ref on self.sample_gauge
                must_notify = self
                    .sample_window
                    .map_or(false, |sample_window| gauge >= sample_window);

                if must_notify {
                    self.sample_gauge = None;
                }
            }
        }

        if must_notify || self.samples_since_last_extract >= EXTRACTION_THRESHOLD {
            // extract new samples and swap
            self.extract_samples();
            self.samples_since_last_extract = SampleIndex::default();
        }

        must_notify
    }

    // Update the working extractor with new samples and swap.
    fn extract_samples(&mut self) {
        let mut working_buffer = self.working_buffer.take().unwrap();
        working_buffer.extract_samples(&self.audio_buffer);

        // swap buffers
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();
            // get latest state from the previously exposed buffer
            // in order to smoothen rendering between frames
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();
        self.sample_window = working_buffer
            .get_requested_sample_window()
            .map(|req_sample_window| req_sample_window.min(self.max_sample_window));

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }

    pub fn refresh(&mut self) {
        // refresh with current conditions
        let mut working_buffer = self.working_buffer.take().unwrap();
        {
            let exposed_buffer_box = &mut *self.exposed_buffer_mtx.lock().unwrap();

            if self.state != gst::State::Playing {
                exposed_buffer_box.switch_to_paused();
            }

            // get latest state from the previously exposed buffer
            working_buffer.update_concrete_state(exposed_buffer_box.as_mut());

            // refresh working buffer
            working_buffer.refresh(&self.audio_buffer);

            // swap buffers
            mem::swap(exposed_buffer_box, &mut working_buffer);
        }

        self.lower_to_keep = working_buffer.get_lower();

        self.working_buffer = Some(working_buffer);
        // self.working_buffer is now the buffer previously in
        // self.exposed_buffer_mtx
    }
}
<|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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId};
use canvas_traits::webgl::{WebGLResult, WebGLError, webgl_channel};
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::inheritance::Castable;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::{Dom, DomRoot};
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webglrenderingcontext::WebGLRenderingContext;
use dom::webgltexture::WebGLTexture;
use dom_struct::dom_struct;
use std::cell::Cell;

#[must_root]
#[derive(Clone, JSTraceable, MallocSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(Dom<WebGLRenderbuffer>),
    Texture { texture: Dom<WebGLTexture>, level: i32 },
}

#[derive(Clone, JSTraceable, MallocSizeOf)]
pub enum WebGLFramebufferAttachmentRoot {
    Renderbuffer(DomRoot<WebGLRenderbuffer>),
    Texture(DomRoot<WebGLTexture>),
}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    size: Cell<Option<(i32, i32)>>,
    status: Cell<u32>,
    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DomRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DomRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> Self {
        Self {
            webgl_object: WebGLObject::new_inherited(context),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            size: Cell::new(None),
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DomRefCell::new(None),
            depth: DomRefCell::new(None),
            stencil: DomRefCell::new(None),
            depthstencil: DomRefCell::new(None),
        }
    }

    pub fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
        let (sender, receiver) = webgl_channel().unwrap();
        context.send_command(WebGLCommand::CreateFramebuffer(sender));
        receiver.recv().unwrap().map(|id| WebGLFramebuffer::new(context, id))
    }

    pub fn new(
        context: &WebGLRenderingContext,
        id: WebGLFramebufferId,
    ) -> DomRoot<Self> {
        reflect_dom_object(
            Box::new(WebGLFramebuffer::new_inherited(context, id)),
            &*context.global(),
            WebGLFramebufferBinding::Wrap,
        )
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        self.upcast::<WebGLObject>().context().send_command(
            WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id)),
        );
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            self.upcast::<WebGLObject>()
                .context()
                .send_command(WebGLCommand::DeleteFramebuffer(self.id));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    pub fn size(&self) -> Option<(i32, i32)> {
        self.size.get()
    }

    fn update_status(&self) {
        let c = self.color.borrow();
        let z = self.depth.borrow();
        let s = self.stencil.borrow();
        let zs = self.depthstencil.borrow();
        let has_c = c.is_some();
        let has_z = z.is_some();
        let has_s = s.is_some();
        let has_zs = zs.is_some();
        let attachments = [&*c, &*z, &*s, &*zs];

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        let mut fb_size = None;
        for attachment in &attachments {
            // Get the size of this attachment.
            let size = match **attachment {
                Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
                    att_rb.size()
                }
                Some(WebGLFramebufferAttachment::Texture { texture: ref att_tex, level } ) => {
                    let info = att_tex.image_info_at_face(0, level as u32);
                    Some((info.width() as i32, info.height() as i32))
                }
                None => None,
            };

            // Make sure that, if we've found any other attachment,
            // that the size matches.
            if size.is_some() {
                if fb_size.is_some() && size != fb_size {
                    self.status.set(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
                    return;
                } else {
                    fb_size = size;
                }
            }
        }
        self.size.set(fb_size);

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(Dom::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.upcast::<WebGLObject>().context().send_command(
            WebGLCommand::FramebufferRenderbuffer(
                constants::FRAMEBUFFER,
                attachment,
                constants::RENDERBUFFER,
                rb_id,
            ),
        );

        self.update_status();
        Ok(())
    }

    pub fn attachment(&self, attachment: u32) -> Option<WebGLFramebufferAttachmentRoot> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return None,
        };

        binding.borrow().as_ref().map(|bin| {
            match bin {
                &WebGLFramebufferAttachment::Renderbuffer(ref rb) =>
                    WebGLFramebufferAttachmentRoot::Renderbuffer(DomRoot::from_ref(&rb)),
                &WebGLFramebufferAttachment::Texture { ref texture, .. } =>
                    WebGLFramebufferAttachmentRoot::Texture(DomRoot::from_ref(&texture)),
            }
        })
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
                    texture: Dom::from_ref(texture),
                    level: level }
                );

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.upcast::<WebGLObject>().context().send_command(
            WebGLCommand::FramebufferTexture2D(
                constants::FRAMEBUFFER,
                attachment,
                textarget,
                tex_id,
                level,
            ),
        );

        self.update_status();
        Ok(())
    }

    fn with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F)
        where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>)
    {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                closure(attachment);
            }
        }
    }

    fn with_matching_textures<F>(&self, texture: &WebGLTexture, mut closure: F)
        where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>)
    {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. })
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                closure(attachment);
            }
        }
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        self.with_matching_renderbuffers(rb, |att| {
            *att.borrow_mut() = None;
            self.update_status();
        });
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        self.with_matching_textures(texture, |att| {
            *att.borrow_mut() = None;
            self.update_status();
        });
    }

    pub fn invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        self.with_matching_renderbuffers(rb, |_att| {
            self.update_status();
        });
    }

    pub fn invalidate_texture(&self, texture: &WebGLTexture) {
        self.with_matching_textures(texture, |_att| {
            self.update_status();
        });
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<commit_msg>webgl: Mark zero-sized framebuffers as incomplete attachments.<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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::webgl::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId};
use canvas_traits::webgl::{WebGLResult, WebGLError, webgl_channel};
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::inheritance::Castable;
use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::root::{Dom, DomRoot};
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webglrenderingcontext::WebGLRenderingContext;
use dom::webgltexture::WebGLTexture;
use dom_struct::dom_struct;
use std::cell::Cell;

#[must_root]
#[derive(Clone, JSTraceable, MallocSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(Dom<WebGLRenderbuffer>),
    Texture { texture: Dom<WebGLTexture>, level: i32 },
}

#[derive(Clone, JSTraceable, MallocSizeOf)]
pub enum WebGLFramebufferAttachmentRoot {
    Renderbuffer(DomRoot<WebGLRenderbuffer>),
    Texture(DomRoot<WebGLTexture>),
}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    size: Cell<Option<(i32, i32)>>,
    status: Cell<u32>,
    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DomRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DomRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(context: &WebGLRenderingContext, id: WebGLFramebufferId) -> Self {
        Self {
            webgl_object: WebGLObject::new_inherited(context),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            size: Cell::new(None),
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DomRefCell::new(None),
            depth: DomRefCell::new(None),
            stencil: DomRefCell::new(None),
            depthstencil: DomRefCell::new(None),
        }
    }

    pub fn maybe_new(context: &WebGLRenderingContext) -> Option<DomRoot<Self>> {
        let (sender, receiver) = webgl_channel().unwrap();
        context.send_command(WebGLCommand::CreateFramebuffer(sender));
        receiver.recv().unwrap().map(|id| WebGLFramebuffer::new(context, id))
    }

    pub fn new(
        context: &WebGLRenderingContext,
        id: WebGLFramebufferId,
    ) -> DomRoot<Self> {
        reflect_dom_object(
            Box::new(WebGLFramebuffer::new_inherited(context, id)),
            &*context.global(),
            WebGLFramebufferBinding::Wrap,
        )
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        self.upcast::<WebGLObject>().context().send_command(
            WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id)),
        );
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            self.upcast::<WebGLObject>()
                .context()
                .send_command(WebGLCommand::DeleteFramebuffer(self.id));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    pub fn size(&self) -> Option<(i32, i32)> {
        self.size.get()
    }

    fn update_status(&self) {
        let c = self.color.borrow();
        let z = self.depth.borrow();
        let s = self.stencil.borrow();
        let zs = self.depthstencil.borrow();
        let has_c = c.is_some();
        let has_z = z.is_some();
        let has_s = s.is_some();
        let has_zs = zs.is_some();
        let attachments = [&*c, &*z, &*s, &*zs];

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        let mut fb_size = None;
        for attachment in &attachments {
            // Get the size of this attachment.
            let size = match **attachment {
                Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb)) => {
                    att_rb.size()
                }
                Some(WebGLFramebufferAttachment::Texture { texture: ref att_tex, level } ) => {
                    let info = att_tex.image_info_at_face(0, level as u32);
                    Some((info.width() as i32, info.height() as i32))
                }
                None => None,
            };

            // Make sure that, if we've found any other attachment,
            // that the size matches.
            if size.is_some() {
                if fb_size.is_some() && size != fb_size {
                    self.status.set(constants::FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
                    return;
                } else {
                    fb_size = size;
                }
            }
        }
        self.size.set(fb_size);

        if has_c || has_z || has_zs || has_s {
            if self.size.get().map_or(false, |(w, h)| w != 0 && h != 0) {
                self.status.set(constants::FRAMEBUFFER_COMPLETE);
            } else {
                self.status.set(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
            }
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(Dom::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.upcast::<WebGLObject>().context().send_command(
            WebGLCommand::FramebufferRenderbuffer(
                constants::FRAMEBUFFER,
                attachment,
                constants::RENDERBUFFER,
                rb_id,
            ),
        );

        self.update_status();
        Ok(())
    }

    pub fn attachment(&self, attachment: u32) -> Option<WebGLFramebufferAttachmentRoot> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return None,
        };

        binding.borrow().as_ref().map(|bin| {
            match bin {
                &WebGLFramebufferAttachment::Renderbuffer(ref rb) =>
                    WebGLFramebufferAttachmentRoot::Renderbuffer(DomRoot::from_ref(&rb)),
                &WebGLFramebufferAttachment::Texture { ref texture, .. } =>
                    WebGLFramebufferAttachmentRoot::Texture(DomRoot::from_ref(&texture)),
            }
        })
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
                    texture: Dom::from_ref(texture),
                    level: level }
                );

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.upcast::<WebGLObject>().context().send_command(
            WebGLCommand::FramebufferTexture2D(
                constants::FRAMEBUFFER,
                attachment,
                textarget,
                tex_id,
                level,
            ),
        );

        self.update_status();
        Ok(())
    }

    fn with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F)
        where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>)
    {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                closure(attachment);
            }
        }
    }

    fn with_matching_textures<F>(&self, texture: &WebGLTexture, mut closure: F)
        where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>)
    {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. })
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                closure(attachment);
            }
        }
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        self.with_matching_renderbuffers(rb, |att| {
            *att.borrow_mut() = None;
            self.update_status();
        });
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        self.with_matching_textures(texture, |att| {
            *att.borrow_mut() = None;
            self.update_status();
        });
    }

    pub fn invalidate_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        self.with_matching_renderbuffers(rb, |_att| {
            self.update_status();
        });
    }

    pub fn invalidate_texture(&self, texture: &WebGLTexture) {
        self.with_matching_textures(texture, |_att| {
            self.update_status();
        });
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<|endoftext|>


<commit_before><commit_msg>rt: combine context and entered thread-locals (#5168)<commit_after><|endoftext|>


<commit_before>//! Random access inspection of the results of a dataflow analysis.

use std::borrow::Borrow;

use rustc::mir::{self, BasicBlock, Location};
use rustc_index::bit_set::BitSet;

use super::{Analysis, Results};

/// A `ResultsCursor` that borrows the underlying `Results`.
pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;

/// Allows random access inspection of the results of a dataflow analysis.
///
/// This cursor only has linear performance within a basic block when its statements are visited in
/// order. In the worst case—when statements are visited in *reverse* order—performance will be
/// quadratic in the number of statements in the block. The order in which basic blocks are
/// inspected has no impact on performance.
///
/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
where
    A: Analysis<'tcx>,
{
    body: &'mir mir::Body<'tcx>,
    results: R,
    state: BitSet<A::Idx>,

    pos: CursorPosition,

    /// When this flag is set, the cursor is pointing at a `Call` terminator whose call return
    /// effect has been applied to `state`.
    ///
    /// This flag helps to ensure that multiple calls to `seek_after_assume_call_returns` with the
    /// same target will result in exactly one invocation of `apply_call_return_effect`. It is
    /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
    /// terminator will always require a cursor reset.
    call_return_effect_applied: bool,
}

impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
    A: Analysis<'tcx>,
    R: Borrow<Results<'tcx, A>>,
{
    /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
    pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
        ResultsCursor {
            body,
            pos: CursorPosition::BlockStart(mir::START_BLOCK),
            state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
            call_return_effect_applied: false,
            results,
        }
    }

    /// Returns the `Analysis` used to generate the underlying results.
    pub fn analysis(&self) -> &A {
        &self.results.borrow().analysis
    }

    /// Returns the dataflow state at the current location.
    pub fn get(&self) -> &BitSet<A::Idx> {
        &self.state
    }

    /// Resets the cursor to the start of the given basic block.
    pub fn seek_to_block_start(&mut self, block: BasicBlock) {
        self.state.overwrite(&self.results.borrow().entry_sets[block]);
        self.pos = CursorPosition::BlockStart(block);
        self.call_return_effect_applied = false;
    }

    /// Advances the cursor to hold all effects up to and including to the "before" effect of the
    /// statement (or terminator) at the given location.
    ///
    /// If you wish to observe the full effect of a statement or terminator, not just the "before"
    /// effect, use `seek_after` or `seek_after_assume_call_returns`.
    pub fn seek_before(&mut self, target: Location) {
        assert!(target <= self.body.terminator_loc(target.block));
        self._seek(target, false);
    }

    /// Advances the cursor to hold the full effect of all statements (and possibly closing
    /// terminators) up to and including the `target`.
    ///
    /// If the `target` is a `Call` terminator, any call return effect for that terminator will
    /// **not** be observed. Use `seek_after_assume_call_returns` if you wish to observe the call
    /// return effect.
    pub fn seek_after(&mut self, target: Location) {
        assert!(target <= self.body.terminator_loc(target.block));

        // If we have already applied the call return effect, we are currently pointing at a `Call`
        // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
        // the call return effect.
        if self.call_return_effect_applied {
            self.seek_to_block_start(target.block);
        }

        self._seek(target, true);
    }

    /// Advances the cursor to hold all effects up to and including of the statement (or
    /// terminator) at the given location.
    ///
    /// If the `target` is a `Call` terminator, any call return effect for that terminator will
    /// be observed. Use `seek_after` if you do **not** wish to observe the call return effect.
    pub fn seek_after_assume_call_returns(&mut self, target: Location) {
        let terminator_loc = self.body.terminator_loc(target.block);
        assert!(target.statement_index <= terminator_loc.statement_index);

        self._seek(target, true);

        if target != terminator_loc {
            return;
        }

        let terminator = self.body.basic_blocks()[target.block].terminator();
        if let mir::TerminatorKind::Call {
            destination: Some((return_place, _)), func, args, ..
        } = &terminator.kind
        {
            if !self.call_return_effect_applied {
                self.call_return_effect_applied = true;
                self.results.borrow().analysis.apply_call_return_effect(
                    &mut self.state,
                    target.block,
                    func,
                    args,
                    return_place,
                );
            }
        }
    }

    fn _seek(&mut self, target: Location, apply_after_effect_at_target: bool) {
        use CursorPosition::*;

        match self.pos {
            // Return early if we are already at the target location.
            Before(curr) if curr == target && !apply_after_effect_at_target => return,
            After(curr) if curr == target && apply_after_effect_at_target => return,

            // Otherwise, we must reset to the start of the target block if...

            // we are in a different block entirely.
            BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. })
                if block != target.block =>
            {
                self.seek_to_block_start(target.block)
            }

            // we are in the same block but have advanced past the target statement.
            Before(curr) | After(curr) if curr.statement_index > target.statement_index => {
                self.seek_to_block_start(target.block)
            }

            // we have already applied the entire effect of a statement but only wish to observe
            // its "before" effect.
            After(curr)
                if curr.statement_index == target.statement_index
                    && !apply_after_effect_at_target =>
            {
                self.seek_to_block_start(target.block)
            }

            // N.B., `call_return_effect_applied` is checked in `seek_after`, not here.
            _ => (),
        }

        let analysis = &self.results.borrow().analysis;
        let block_data = &self.body.basic_blocks()[target.block];

        // At this point, the cursor is in the same block as the target location at an earlier
        // statement.
        debug_assert_eq!(target.block, self.pos.block());

        // Find the first statement whose transfer function has not yet been applied.
        let first_unapplied_statement = match self.pos {
            BlockStart(_) => 0,
            After(Location { statement_index, .. }) => statement_index + 1,

            // If we have only applied the "before" effect for the current statement, apply the
            // remainder before continuing.
            Before(curr) => {
                if curr.statement_index == block_data.statements.len() {
                    let terminator = block_data.terminator();
                    analysis.apply_terminator_effect(&mut self.state, terminator, curr);
                } else {
                    let statement = &block_data.statements[curr.statement_index];
                    analysis.apply_statement_effect(&mut self.state, statement, curr);
                }

                // If all we needed to do was go from `Before` to `After` in the same statement,
                // we are now done.
                if curr.statement_index == target.statement_index {
                    debug_assert!(apply_after_effect_at_target);
                    self.pos = After(target);
                    return;
                }

                curr.statement_index + 1
            }
        };

        // We have now applied all effects prior to `first_unapplied_statement`.

        // Apply the effects of all statements before `target`.
        let mut location = Location { block: target.block, statement_index: 0 };
        for statement_index in first_unapplied_statement..target.statement_index {
            location.statement_index = statement_index;
            let statement = &block_data.statements[statement_index];
            analysis.apply_before_statement_effect(&mut self.state, statement, location);
            analysis.apply_statement_effect(&mut self.state, statement, location);
        }

        // Apply the effect of the statement (or terminator) at `target`.
        location.statement_index = target.statement_index;
        if target.statement_index == block_data.statements.len() {
            let terminator = &block_data.terminator();
            analysis.apply_before_terminator_effect(&mut self.state, terminator, location);

            if apply_after_effect_at_target {
                analysis.apply_terminator_effect(&mut self.state, terminator, location);
                self.pos = After(target);
            } else {
                self.pos = Before(target);
            }
        } else {
            let statement = &block_data.statements[target.statement_index];
            analysis.apply_before_statement_effect(&mut self.state, statement, location);

            if apply_after_effect_at_target {
                analysis.apply_statement_effect(&mut self.state, statement, location);
                self.pos = After(target)
            } else {
                self.pos = Before(target);
            }
        }
    }
}

#[derive(Clone, Copy, Debug)]
enum CursorPosition {
    /// No effects within this block have been applied.
    BlockStart(BasicBlock),

    /// Only the "before" effect of the statement (or terminator) at this location has been
    /// applied (along with the effects of all previous statements).
    Before(Location),

    /// The effects of all statements up to and including the one at this location have been
    /// applied.
    After(Location),
}

impl CursorPosition {
    fn block(&self) -> BasicBlock {
        match *self {
            Self::BlockStart(block) => block,
            Self::Before(loc) | Self::After(loc) => loc.block,
        }
    }
}
<commit_msg>Use trailing underscore for helper methods<commit_after>//! Random access inspection of the results of a dataflow analysis.

use std::borrow::Borrow;

use rustc::mir::{self, BasicBlock, Location};
use rustc_index::bit_set::BitSet;

use super::{Analysis, Results};

/// A `ResultsCursor` that borrows the underlying `Results`.
pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;

/// Allows random access inspection of the results of a dataflow analysis.
///
/// This cursor only has linear performance within a basic block when its statements are visited in
/// order. In the worst case—when statements are visited in *reverse* order—performance will be
/// quadratic in the number of statements in the block. The order in which basic blocks are
/// inspected has no impact on performance.
///
/// A `ResultsCursor` can either own (the default) or borrow the dataflow results it inspects. The
/// type of ownership is determined by `R` (see `ResultsRefCursor` above).
pub struct ResultsCursor<'mir, 'tcx, A, R = Results<'tcx, A>>
where
    A: Analysis<'tcx>,
{
    body: &'mir mir::Body<'tcx>,
    results: R,
    state: BitSet<A::Idx>,

    pos: CursorPosition,

    /// When this flag is set, the cursor is pointing at a `Call` terminator whose call return
    /// effect has been applied to `state`.
    ///
    /// This flag helps to ensure that multiple calls to `seek_after_assume_call_returns` with the
    /// same target will result in exactly one invocation of `apply_call_return_effect`. It is
    /// sufficient to clear this only in `seek_to_block_start`, since seeking away from a
    /// terminator will always require a cursor reset.
    call_return_effect_applied: bool,
}

impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
    A: Analysis<'tcx>,
    R: Borrow<Results<'tcx, A>>,
{
    /// Returns a new cursor for `results` that points to the start of the `START_BLOCK`.
    pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
        ResultsCursor {
            body,
            pos: CursorPosition::BlockStart(mir::START_BLOCK),
            state: results.borrow().entry_sets[mir::START_BLOCK].clone(),
            call_return_effect_applied: false,
            results,
        }
    }

    /// Returns the `Analysis` used to generate the underlying results.
    pub fn analysis(&self) -> &A {
        &self.results.borrow().analysis
    }

    /// Returns the dataflow state at the current location.
    pub fn get(&self) -> &BitSet<A::Idx> {
        &self.state
    }

    /// Resets the cursor to the start of the given basic block.
    pub fn seek_to_block_start(&mut self, block: BasicBlock) {
        self.state.overwrite(&self.results.borrow().entry_sets[block]);
        self.pos = CursorPosition::BlockStart(block);
        self.call_return_effect_applied = false;
    }

    /// Advances the cursor to hold all effects up to and including to the "before" effect of the
    /// statement (or terminator) at the given location.
    ///
    /// If you wish to observe the full effect of a statement or terminator, not just the "before"
    /// effect, use `seek_after` or `seek_after_assume_call_returns`.
    pub fn seek_before(&mut self, target: Location) {
        assert!(target <= self.body.terminator_loc(target.block));
        self.seek_(target, false);
    }

    /// Advances the cursor to hold the full effect of all statements (and possibly closing
    /// terminators) up to and including the `target`.
    ///
    /// If the `target` is a `Call` terminator, any call return effect for that terminator will
    /// **not** be observed. Use `seek_after_assume_call_returns` if you wish to observe the call
    /// return effect.
    pub fn seek_after(&mut self, target: Location) {
        assert!(target <= self.body.terminator_loc(target.block));

        // If we have already applied the call return effect, we are currently pointing at a `Call`
        // terminator. Unconditionally reset the dataflow cursor, since there is no way to "undo"
        // the call return effect.
        if self.call_return_effect_applied {
            self.seek_to_block_start(target.block);
        }

        self.seek_(target, true);
    }

    /// Advances the cursor to hold all effects up to and including of the statement (or
    /// terminator) at the given location.
    ///
    /// If the `target` is a `Call` terminator, any call return effect for that terminator will
    /// be observed. Use `seek_after` if you do **not** wish to observe the call return effect.
    pub fn seek_after_assume_call_returns(&mut self, target: Location) {
        let terminator_loc = self.body.terminator_loc(target.block);
        assert!(target.statement_index <= terminator_loc.statement_index);

        self.seek_(target, true);

        if target != terminator_loc {
            return;
        }

        let terminator = self.body.basic_blocks()[target.block].terminator();
        if let mir::TerminatorKind::Call {
            destination: Some((return_place, _)), func, args, ..
        } = &terminator.kind
        {
            if !self.call_return_effect_applied {
                self.call_return_effect_applied = true;
                self.results.borrow().analysis.apply_call_return_effect(
                    &mut self.state,
                    target.block,
                    func,
                    args,
                    return_place,
                );
            }
        }
    }

    fn seek_(&mut self, target: Location, apply_after_effect_at_target: bool) {
        use CursorPosition::*;

        match self.pos {
            // Return early if we are already at the target location.
            Before(curr) if curr == target && !apply_after_effect_at_target => return,
            After(curr) if curr == target && apply_after_effect_at_target => return,

            // Otherwise, we must reset to the start of the target block if...

            // we are in a different block entirely.
            BlockStart(block) | Before(Location { block, .. }) | After(Location { block, .. })
                if block != target.block =>
            {
                self.seek_to_block_start(target.block)
            }

            // we are in the same block but have advanced past the target statement.
            Before(curr) | After(curr) if curr.statement_index > target.statement_index => {
                self.seek_to_block_start(target.block)
            }

            // we have already applied the entire effect of a statement but only wish to observe
            // its "before" effect.
            After(curr)
                if curr.statement_index == target.statement_index
                    && !apply_after_effect_at_target =>
            {
                self.seek_to_block_start(target.block)
            }

            // N.B., `call_return_effect_applied` is checked in `seek_after`, not here.
            _ => (),
        }

        let analysis = &self.results.borrow().analysis;
        let block_data = &self.body.basic_blocks()[target.block];

        // At this point, the cursor is in the same block as the target location at an earlier
        // statement.
        debug_assert_eq!(target.block, self.pos.block());

        // Find the first statement whose transfer function has not yet been applied.
        let first_unapplied_statement = match self.pos {
            BlockStart(_) => 0,
            After(Location { statement_index, .. }) => statement_index + 1,

            // If we have only applied the "before" effect for the current statement, apply the
            // remainder before continuing.
            Before(curr) => {
                if curr.statement_index == block_data.statements.len() {
                    let terminator = block_data.terminator();
                    analysis.apply_terminator_effect(&mut self.state, terminator, curr);
                } else {
                    let statement = &block_data.statements[curr.statement_index];
                    analysis.apply_statement_effect(&mut self.state, statement, curr);
                }

                // If all we needed to do was go from `Before` to `After` in the same statement,
                // we are now done.
                if curr.statement_index == target.statement_index {
                    debug_assert!(apply_after_effect_at_target);
                    self.pos = After(target);
                    return;
                }

                curr.statement_index + 1
            }
        };

        // We have now applied all effects prior to `first_unapplied_statement`.

        // Apply the effects of all statements before `target`.
        let mut location = Location { block: target.block, statement_index: 0 };
        for statement_index in first_unapplied_statement..target.statement_index {
            location.statement_index = statement_index;
            let statement = &block_data.statements[statement_index];
            analysis.apply_before_statement_effect(&mut self.state, statement, location);
            analysis.apply_statement_effect(&mut self.state, statement, location);
        }

        // Apply the effect of the statement (or terminator) at `target`.
        location.statement_index = target.statement_index;
        if target.statement_index == block_data.statements.len() {
            let terminator = &block_data.terminator();
            analysis.apply_before_terminator_effect(&mut self.state, terminator, location);

            if apply_after_effect_at_target {
                analysis.apply_terminator_effect(&mut self.state, terminator, location);
                self.pos = After(target);
            } else {
                self.pos = Before(target);
            }
        } else {
            let statement = &block_data.statements[target.statement_index];
            analysis.apply_before_statement_effect(&mut self.state, statement, location);

            if apply_after_effect_at_target {
                analysis.apply_statement_effect(&mut self.state, statement, location);
                self.pos = After(target)
            } else {
                self.pos = Before(target);
            }
        }
    }
}

#[derive(Clone, Copy, Debug)]
enum CursorPosition {
    /// No effects within this block have been applied.
    BlockStart(BasicBlock),

    /// Only the "before" effect of the statement (or terminator) at this location has been
    /// applied (along with the effects of all previous statements).
    Before(Location),

    /// The effects of all statements up to and including the one at this location have been
    /// applied.
    After(Location),
}

impl CursorPosition {
    fn block(&self) -> BasicBlock {
        match *self {
            Self::BlockStart(block) => block,
            Self::Before(loc) | Self::After(loc) => loc.block,
        }
    }
}
<|endoftext|>


<commit_before>use kay::{Recipient, Actor, Fate};
use super::CurrentPlan;
use core::geometry::AnyShape;
use descartes::{N, P2};

#[derive(Default)]
pub struct Interaction {
    cmd_pressed: bool,
    shift_pressed: bool,
}

use super::InitInteractable;
use monet::{Renderer, AddEyeListener};
use core::ui::{UserInterface, Add, Focus};

impl Recipient<InitInteractable> for CurrentPlan {
    fn receive(&mut self, _msg: &InitInteractable) -> Fate {
        UserInterface::id() << Add::Interactable3d(CurrentPlan::id(), AnyShape::Everywhere, 0);
        UserInterface::id() << Focus(CurrentPlan::id());
        Renderer::id() <<
        AddEyeListener {
            scene_id: 0,
            listener: CurrentPlan::id(),
        };
        Fate::Live
    }
}

use monet::EyeMoved;

impl Recipient<EyeMoved> for CurrentPlan {
    fn receive(&mut self, msg: &EyeMoved) -> Fate {
        match *msg {
            EyeMoved { eye, .. } => {
                if eye.position.z < 100.0 {
                    self.settings.select_parallel = false;
                    self.settings.select_opposite = false;
                } else if eye.position.z < 130.0 {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = false;
                } else {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = true;
                }
                Fate::Live
            }
        }
    }
}

use core::ui::{Event3d, VirtualKeyCode};
use super::{Intent, ChangeIntent, IntentProgress, Materialize, Undo, Redo, SetNLanes,
            ToggleBothSides};
use super::stroke_canvas::{Stroke, StrokeState};

impl Recipient<Event3d> for CurrentPlan {
    fn receive(&mut self, msg: &Event3d) -> Fate {
        match *msg {
            Event3d::KeyDown(VirtualKeyCode::Return) => {
                CurrentPlan::id() << Materialize;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LControl) |
            Event3d::KeyDown(VirtualKeyCode::RControl) |
            Event3d::KeyDown(VirtualKeyCode::LWin) |
            Event3d::KeyDown(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LControl) |
            Event3d::KeyUp(VirtualKeyCode::RControl) |
            Event3d::KeyUp(VirtualKeyCode::LWin) |
            Event3d::KeyUp(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LShift) |
            Event3d::KeyDown(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LShift) |
            Event3d::KeyUp(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Z) => {
                if self.interaction.cmd_pressed {
                    if self.interaction.shift_pressed {
                        CurrentPlan::id() << Redo;
                    } else {
                        CurrentPlan::id() << Undo;
                    }
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::C) => {
                // Swarm::<::game::lanes_and_cars::lane::Lane>::all() <<
                // ToRandom {
                //     n_recipients: 5000,
                //     message: Event3d::DragFinished {
                //         from: P3::new(0.0, 0.0, 0.0),
                //         to: P3::new(0.0, 0.0, 0.0),
                //     },
                // };
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::G) => {
                const GRID_SPACING: N = 1000.0;
                let grid_size = if self.interaction.shift_pressed {
                    15usize
                } else {
                    10usize
                };
                for x in 0..grid_size {
                    Self::id() <<
                    Stroke(vec![P2::new((x as f32 + 0.5) * GRID_SPACING, 0.0),
                                P2::new((x as f32 + 0.5) * GRID_SPACING,
                                        grid_size as f32 * GRID_SPACING)]
                               .into(),
                           StrokeState::Finished);
                }
                for y in 0..grid_size {
                    Self::id() <<
                    Stroke(vec![P2::new(0.0, (y as f32 + 0.5) * GRID_SPACING),
                                P2::new(grid_size as f32 * GRID_SPACING,
                                        (y as f32 + 0.5) * GRID_SPACING)]
                               .into(),
                           StrokeState::Finished);
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Back) => {
                Self::id() << ChangeIntent(Intent::DeleteSelection, IntentProgress::Finished);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key1) => {
                Self::id() << SetNLanes(1);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key2) => {
                Self::id() << SetNLanes(2);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key3) => {
                Self::id() << SetNLanes(3);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key4) => {
                Self::id() << SetNLanes(4);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key5) => {
                Self::id() << SetNLanes(5);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key6) => {
                Self::id() << SetNLanes(6);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key7) => {
                Self::id() << SetNLanes(7);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key8) => {
                Self::id() << SetNLanes(8);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key9) => {
                Self::id() << SetNLanes(9);
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::Key0) => {
                Self::id() << ToggleBothSides;
                Fate::Live
            }
            _ => Fate::Live,
        }
    }
}

pub fn setup() {
    CurrentPlan::handle::<InitInteractable>();
    CurrentPlan::handle::<EyeMoved>();
    CurrentPlan::handle::<Event3d>();
    CurrentPlan::id() << InitInteractable;
}<commit_msg>Spawn cars<commit_after>use kay::{Recipient, Actor, Fate};
use super::CurrentPlan;
use core::geometry::AnyShape;
use descartes::{N, P2};

#[derive(Default)]
pub struct Interaction {
    cmd_pressed: bool,
    shift_pressed: bool,
}

use super::InitInteractable;
use monet::{Renderer, AddEyeListener};
use core::ui::{UserInterface, Add, Focus};

impl Recipient<InitInteractable> for CurrentPlan {
    fn receive(&mut self, _msg: &InitInteractable) -> Fate {
        UserInterface::id() << Add::Interactable3d(CurrentPlan::id(), AnyShape::Everywhere, 0);
        UserInterface::id() << Focus(CurrentPlan::id());
        Renderer::id() <<
        AddEyeListener {
            scene_id: 0,
            listener: CurrentPlan::id(),
        };
        Fate::Live
    }
}

use monet::EyeMoved;

impl Recipient<EyeMoved> for CurrentPlan {
    fn receive(&mut self, msg: &EyeMoved) -> Fate {
        match *msg {
            EyeMoved { eye, .. } => {
                if eye.position.z < 100.0 {
                    self.settings.select_parallel = false;
                    self.settings.select_opposite = false;
                } else if eye.position.z < 130.0 {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = false;
                } else {
                    self.settings.select_parallel = true;
                    self.settings.select_opposite = true;
                }
                Fate::Live
            }
        }
    }
}

use core::ui::{Event3d, VirtualKeyCode};
use super::{Intent, ChangeIntent, IntentProgress, Materialize, Undo, Redo, SetNLanes,
            ToggleBothSides};
use super::stroke_canvas::{Stroke, StrokeState};

impl Recipient<Event3d> for CurrentPlan {
    fn receive(&mut self, msg: &Event3d) -> Fate {
        match *msg {
            Event3d::KeyDown(VirtualKeyCode::Return) => {
                CurrentPlan::id() << Materialize;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LControl) |
            Event3d::KeyDown(VirtualKeyCode::RControl) |
            Event3d::KeyDown(VirtualKeyCode::LWin) |
            Event3d::KeyDown(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LControl) |
            Event3d::KeyUp(VirtualKeyCode::RControl) |
            Event3d::KeyUp(VirtualKeyCode::LWin) |
            Event3d::KeyUp(VirtualKeyCode::RWin) => {
                self.interaction.cmd_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::LShift) |
            Event3d::KeyDown(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = true;
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::LShift) |
            Event3d::KeyUp(VirtualKeyCode::RShift) => {
                self.interaction.shift_pressed = false;
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Z) => {
                if self.interaction.cmd_pressed {
                    if self.interaction.shift_pressed {
                        CurrentPlan::id() << Redo;
                    } else {
                        CurrentPlan::id() << Undo;
                    }
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::C) => {
                // TODO: this is not supposed to be here!
                //       *but we have only one focusable!*
                //       WTF?! what's wrong with your UI model?
                //       *I uh.. I guess I should actually write a good one*
                //       When will you finally?!
                //       *Uh.. next week maybe?*
                use kay::swarm::{Swarm, ToRandom};
                use descartes::P3;
                Swarm::<::game::lanes_and_cars::lane::Lane>::all() <<
                ToRandom {
                    n_recipients: 5000,
                    message: Event3d::DragFinished {
                        from: P3::new(0.0, 0.0, 0.0),
                        to: P3::new(0.0, 0.0, 0.0),
                    },
                };
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::G) => {
                const GRID_SPACING: N = 1000.0;
                let grid_size = if self.interaction.shift_pressed {
                    15usize
                } else {
                    10usize
                };
                for x in 0..grid_size {
                    Self::id() <<
                    Stroke(vec![P2::new((x as f32 + 0.5) * GRID_SPACING, 0.0),
                                P2::new((x as f32 + 0.5) * GRID_SPACING,
                                        grid_size as f32 * GRID_SPACING)]
                               .into(),
                           StrokeState::Finished);
                }
                for y in 0..grid_size {
                    Self::id() <<
                    Stroke(vec![P2::new(0.0, (y as f32 + 0.5) * GRID_SPACING),
                                P2::new(grid_size as f32 * GRID_SPACING,
                                        (y as f32 + 0.5) * GRID_SPACING)]
                               .into(),
                           StrokeState::Finished);
                }
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Back) => {
                Self::id() << ChangeIntent(Intent::DeleteSelection, IntentProgress::Finished);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key1) => {
                Self::id() << SetNLanes(1);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key2) => {
                Self::id() << SetNLanes(2);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key3) => {
                Self::id() << SetNLanes(3);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key4) => {
                Self::id() << SetNLanes(4);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key5) => {
                Self::id() << SetNLanes(5);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key6) => {
                Self::id() << SetNLanes(6);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key7) => {
                Self::id() << SetNLanes(7);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key8) => {
                Self::id() << SetNLanes(8);
                Fate::Live
            }
            Event3d::KeyDown(VirtualKeyCode::Key9) => {
                Self::id() << SetNLanes(9);
                Fate::Live
            }
            Event3d::KeyUp(VirtualKeyCode::Key0) => {
                Self::id() << ToggleBothSides;
                Fate::Live
            }
            _ => Fate::Live,
        }
    }
}

pub fn setup() {
    CurrentPlan::handle::<InitInteractable>();
    CurrentPlan::handle::<EyeMoved>();
    CurrentPlan::handle::<Event3d>();
    CurrentPlan::id() << InitInteractable;
}<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// xfail-test

tag a_tag<A,B> {
    varA(A);
    varB(B);
}

type t_rec<A,B> = {
    chA: u8,
    tA: a_tag<A,B>,
    chB: u8,
    tB: a_tag<A,B>
};

fn mk_rec<A:copy,B:copy>(a: A, b: B) -> t_rec<A,B> {
    return { chA:0u8, tA:varA(a), chB:1u8, tB:varB(b) };
}

fn is_aligned<A>(amnt: uint, &&u: A) -> bool {
    let p = ptr::to_unsafe_ptr(u) as uint;
    return (p & (amnt-1u)) == 0u;
}

fn variant_data_is_aligned<A,B>(amnt: uint, &&u: a_tag<A,B>) -> bool {
    match u {
      varA(a) { is_aligned(amnt, a) }
      varB(b) { is_aligned(amnt, b) }
    }
}

pub fn main() {
    let x = mk_rec(22u64, 23u64);
    assert!(is_aligned(8u, x.tA));
    assert!(variant_data_is_aligned(8u, x.tA));
    assert!(is_aligned(8u, x.tB));
    assert!(variant_data_is_aligned(8u, x.tB));

    let x = mk_rec(22u64, 23u32);
    assert!(is_aligned(8u, x.tA));
    assert!(variant_data_is_aligned(8u, x.tA));
    assert!(is_aligned(8u, x.tB));
    assert!(variant_data_is_aligned(4u, x.tB));

    let x = mk_rec(22u32, 23u64);
    assert!(is_aligned(8u, x.tA));
    assert!(variant_data_is_aligned(4u, x.tA));
    assert!(is_aligned(8u, x.tB));
    assert!(variant_data_is_aligned(8u, x.tB));

    let x = mk_rec(22u32, 23u32);
    assert!(is_aligned(4u, x.tA));
    assert!(variant_data_is_aligned(4u, x.tA));
    assert!(is_aligned(4u, x.tB));
    assert!(variant_data_is_aligned(4u, x.tB));

    let x = mk_rec(22f64, 23f64);
    assert!(is_aligned(8u, x.tA));
    assert!(variant_data_is_aligned(8u, x.tA));
    assert!(is_aligned(8u, x.tB));
    assert!(variant_data_is_aligned(8u, x.tB));
}
<commit_msg>after syntax fixes, these tests appear to pass<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

enum a_tag<A,B> {
    varA(A),
    varB(B)
}

struct t_rec<A,B> {
    chA: u8,
    tA: a_tag<A,B>,
    chB: u8,
    tB: a_tag<A,B>
}

fn mk_rec<A:Copy,B:Copy>(a: A, b: B) -> t_rec<A,B> {
    return t_rec{ chA:0u8, tA:varA(a), chB:1u8, tB:varB(b) };
}

fn is_aligned<A>(amnt: uint, u: &A) -> bool {
    let p = ptr::to_unsafe_ptr(u) as uint;
    return (p & (amnt-1u)) == 0u;
}

fn variant_data_is_aligned<A,B>(amnt: uint, u: &a_tag<A,B>) -> bool {
    match u {
      &varA(ref a) => is_aligned(amnt, a),
      &varB(ref b) => is_aligned(amnt, b)
    }
}

pub fn main() {
    let x = mk_rec(22u64, 23u64);
    assert!(is_aligned(8u, &x.tA));
    assert!(variant_data_is_aligned(8u, &x.tA));
    assert!(is_aligned(8u, &x.tB));
    assert!(variant_data_is_aligned(8u, &x.tB));

    let x = mk_rec(22u64, 23u32);
    assert!(is_aligned(8u, &x.tA));
    assert!(variant_data_is_aligned(8u, &x.tA));
    assert!(is_aligned(8u, &x.tB));
    assert!(variant_data_is_aligned(4u, &x.tB));

    let x = mk_rec(22u32, 23u64);
    assert!(is_aligned(8u, &x.tA));
    assert!(variant_data_is_aligned(4u, &x.tA));
    assert!(is_aligned(8u, &x.tB));
    assert!(variant_data_is_aligned(8u, &x.tB));

    let x = mk_rec(22u32, 23u32);
    assert!(is_aligned(4u, &x.tA));
    assert!(variant_data_is_aligned(4u, &x.tA));
    assert!(is_aligned(4u, &x.tB));
    assert!(variant_data_is_aligned(4u, &x.tB));

    let x = mk_rec(22f64, 23f64);
    assert!(is_aligned(8u, &x.tA));
    assert!(variant_data_is_aligned(8u, &x.tA));
    assert!(is_aligned(8u, &x.tB));
    assert!(variant_data_is_aligned(8u, &x.tB));
}
<|endoftext|>


<commit_before>use rocksdb;
use kite::schema::FieldRef;
use byteorder::{ByteOrder, BigEndian};

use RocksDBIndexReader;
use key_builder::KeyBuilder;
use doc_id_set::DocIdSet;
use term_dictionary::TermRef;
use document_index::DocRef;

pub trait Segment {
    fn load_statistic(&self, stat_name: &[u8]) -> Result<Option<i64>, rocksdb::Error>;
    fn load_stored_field_value_raw(&self, doc_ord: u16, field_ref: FieldRef, value_type: &[u8]) -> Result<Option<rocksdb::DBVector>, rocksdb::Error>;
    fn load_term_directory(&self, field_ref: FieldRef, term_ref: TermRef) -> Result<Option<DocIdSet>, rocksdb::Error>;
    fn load_deletion_list(&self) -> Result<Option<DocIdSet>, rocksdb::Error>;
    fn id(&self) -> u32;

    fn doc_ref(&self, ord: u16) -> DocRef {
        DocRef::from_segment_ord(self.id(), ord)
    }
}

pub struct RocksDBSegment<'a> {
    reader: &'a RocksDBIndexReader<'a>,
    id: u32,
}

impl<'a> RocksDBSegment<'a> {
    pub fn new(reader: &'a RocksDBIndexReader, id: u32) -> RocksDBSegment<'a> {
        RocksDBSegment {
            reader: reader,
            id: id,
        }
    }
}

impl<'a> Segment for RocksDBSegment<'a> {
    fn id(&self) -> u32 {
        self.id
    }

    fn load_statistic(&self, stat_name: &[u8]) -> Result<Option<i64>, rocksdb::Error> {
        let kb = KeyBuilder::segment_stat(self.id, stat_name);
        let val = try!(self.reader.snapshot.get(&kb.key())).map(|val| BigEndian::read_i64(&val));
        Ok(val)
    }

    fn load_stored_field_value_raw(&self, doc_ord: u16, field_ref: FieldRef, value_type: &[u8]) -> Result<Option<rocksdb::DBVector>, rocksdb::Error> {
        let kb = KeyBuilder::stored_field_value(self.id, doc_ord, field_ref.ord(), value_type);
        let val = try!(self.reader.snapshot.get(&kb.key()));
        Ok(val)
    }

    fn load_term_directory(&self, field_ref: FieldRef, term_ref: TermRef) -> Result<Option<DocIdSet>, rocksdb::Error> {
        let kb = KeyBuilder::segment_dir_list(self.id, field_ref.ord(), term_ref.ord());
        let doc_id_set = try!(self.reader.snapshot.get(&kb.key())).map(|doc_id_set| DocIdSet::FromRDB(doc_id_set));
        Ok(doc_id_set)
    }

    fn load_deletion_list(&self) -> Result<Option<DocIdSet>, rocksdb::Error> {
        let kb = KeyBuilder::segment_del_list(self.id);
        let doc_id_set = try!(self.reader.snapshot.get(&kb.key())).map(|doc_id_set| DocIdSet::FromRDB(doc_id_set));
        Ok(doc_id_set)
    }
}
<commit_msg>Use Vec<u8> for stored field values<commit_after>use rocksdb;
use kite::schema::FieldRef;
use byteorder::{ByteOrder, BigEndian};

use RocksDBIndexReader;
use key_builder::KeyBuilder;
use doc_id_set::DocIdSet;
use term_dictionary::TermRef;
use document_index::DocRef;

pub trait Segment {
    fn load_statistic(&self, stat_name: &[u8]) -> Result<Option<i64>, rocksdb::Error>;
    fn load_stored_field_value_raw(&self, doc_ord: u16, field_ref: FieldRef, value_type: &[u8]) -> Result<Option<Vec<u8>>, rocksdb::Error>;
    fn load_term_directory(&self, field_ref: FieldRef, term_ref: TermRef) -> Result<Option<DocIdSet>, rocksdb::Error>;
    fn load_deletion_list(&self) -> Result<Option<DocIdSet>, rocksdb::Error>;
    fn id(&self) -> u32;

    fn doc_ref(&self, ord: u16) -> DocRef {
        DocRef::from_segment_ord(self.id(), ord)
    }
}

pub struct RocksDBSegment<'a> {
    reader: &'a RocksDBIndexReader<'a>,
    id: u32,
}

impl<'a> RocksDBSegment<'a> {
    pub fn new(reader: &'a RocksDBIndexReader, id: u32) -> RocksDBSegment<'a> {
        RocksDBSegment {
            reader: reader,
            id: id,
        }
    }
}

impl<'a> Segment for RocksDBSegment<'a> {
    fn id(&self) -> u32 {
        self.id
    }

    fn load_statistic(&self, stat_name: &[u8]) -> Result<Option<i64>, rocksdb::Error> {
        let kb = KeyBuilder::segment_stat(self.id, stat_name);
        let val = try!(self.reader.snapshot.get(&kb.key())).map(|val| BigEndian::read_i64(&val));
        Ok(val)
    }

    fn load_stored_field_value_raw(&self, doc_ord: u16, field_ref: FieldRef, value_type: &[u8]) -> Result<Option<Vec<u8>>, rocksdb::Error> {
        let kb = KeyBuilder::stored_field_value(self.id, doc_ord, field_ref.ord(), value_type);
        let val = try!(self.reader.snapshot.get(&kb.key()));
        Ok(val.map(|v| v.to_vec()))
    }

    fn load_term_directory(&self, field_ref: FieldRef, term_ref: TermRef) -> Result<Option<DocIdSet>, rocksdb::Error> {
        let kb = KeyBuilder::segment_dir_list(self.id, field_ref.ord(), term_ref.ord());
        let doc_id_set = try!(self.reader.snapshot.get(&kb.key())).map(|doc_id_set| DocIdSet::FromRDB(doc_id_set));
        Ok(doc_id_set)
    }

    fn load_deletion_list(&self) -> Result<Option<DocIdSet>, rocksdb::Error> {
        let kb = KeyBuilder::segment_del_list(self.id);
        let doc_id_set = try!(self.reader.snapshot.get(&kb.key())).map(|doc_id_set| DocIdSet::FromRDB(doc_id_set));
        Ok(doc_id_set)
    }
}
<|endoftext|>