<commit_before>use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::ops::IndexMut;

use ::{SMF,Event,SMFFormat,MetaEvent,MidiMessage,Track,TrackEvent};

/// An AbsoluteEvent is an event that has an absolute time
/// This is useful for apps that want to store events internally
/// with absolute times and then quickly build an SMF file for saving etc...
pub struct AbsoluteEvent {
    time: u64,
    event: Event,
}

impl AbsoluteEvent {
    pub fn new_midi(time: u64, midi: MidiMessage) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Midi(midi),
        }
    }
    pub fn new_meta(time: u64, meta: MetaEvent) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Meta(meta),
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a midi
    /// event, false if it's a meta event
    pub fn is_midi(&self) -> bool {
        match self.event {
            Event::Midi(_) => true,
            Event::Meta(_) => false,
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a meta
    /// event, false if it's a midi event
    pub fn is_meta(&self) -> bool {
        match self.event {
            Event::Midi(_) => false,
            Event::Meta(_) => true,
        }
    }

    pub fn get_event(&self) -> &Event {
        &self.event
    }

    pub fn get_time(&self) -> u64 {
        self.time
    }
}

impl Eq for AbsoluteEvent {}

impl PartialEq for AbsoluteEvent {
    fn eq(&self, other: &AbsoluteEvent) -> bool {
        if self.time == other.time {
            match (&self.event,&other.event) {
                (&Event::Midi(_),&Event::Meta(_)) => false,
                (&Event::Meta(_),&Event::Midi(_)) => false,
                (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                    me.command == you.command
                },
                (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                    me.data(0) == you.data(0)
                        &&
                    me.data(1) == me.data(1)
                },
            }
        } else {
            false
        }
    }

    fn ne(&self, other: &AbsoluteEvent) -> bool {
        !(self.eq(other))
    }
}

// Implement `Ord` and sort messages by time
impl Ord for AbsoluteEvent {
    fn cmp(&self, other: &AbsoluteEvent) -> Ordering {
        let res = self.time.cmp(&other.time);
        match res {
            // vtime takes priority
            Ordering::Less | Ordering::Greater => res,
            // if vtime is the same, check types and make meta events
            // sort before standard events
            Ordering::Equal => {
                match (&self.event,&other.event) {
                    // I'm midi, other is meta, so I'm greater
                    (&Event::Midi(_),&Event::Meta(_)) => Ordering::Greater,
                    // I'm meta, other is midi, so I'm less
                    (&Event::Meta(_),&Event::Midi(_)) => Ordering::Less,
                    (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                        me.command.cmp(&you.command)
                    },
                    (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                        if      me.data(0) < you.data(0) { Ordering::Less }
                        else if me.data(0) > you.data(0) { Ordering::Greater }
                        else {
                            if me.data(1) < you.data(1) {
                                Ordering::Less
                            } else if me.data(1) > you.data(1) {
                                Ordering::Greater
                            } else {
                                res
                            }
                        }
                    },
                }
            }
        }
    }
}

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

enum EventContainer {
    Heap(BinaryHeap<AbsoluteEvent>),
    Static(Vec<TrackEvent>),
}

struct TrackBuilder {
    copyright: Option<String>,
    name: Option<String>,
    events: EventContainer,
}

impl TrackBuilder {

    fn result(self) -> Track {
        Track {
            copyright: self.copyright,
            name: self.name,
            events: match self.events {
                EventContainer::Heap(mut heap) => {
                    let mut events = Vec::new();
                    events.reserve(heap.len());
                    let mut cur_time: u64 = 0;
                    loop {
                        match heap.pop() {
                            Some(bev) => {
                                let vtime = bev.time - cur_time;
                                cur_time = vtime;
                                events.push(TrackEvent {
                                    vtime: vtime,
                                    event: bev.event,
                                });
                            }
                            None => { break; }
                        }
                    }
                    events
                },
                EventContainer::Static(vec) => vec,
            },
        }
    }

    fn abs_time_from_delta(&self,delta: u64) -> u64 {
        match self.events {
            EventContainer::Heap(ref heap) => {
                match heap.peek() {
                    Some(e) => { e.time + delta }
                    None => { delta }
                }
            }
            _ => { panic!("Can't call abs_time_from_delta on non-heap builder") }
        }
    }
}

/// An SMFBuilder can be used to create an SMF file.  This is done by
/// adding tracks to the builder via `add_track` and then adding
/// events to each track.
pub struct SMFBuilder {
    tracks:Vec<TrackBuilder>
}

impl SMFBuilder {
    /// Create a new SMFBuilder.  Initially the builder will have no tracks
    pub fn new() -> SMFBuilder {
        SMFBuilder {
            tracks: Vec::new(),
        }
    }

    /// Get the number of tracks currenly in the builder
    pub fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    /// Add new a track to this builder
    pub fn add_track(&mut self) {
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Heap(BinaryHeap::new()),
        });
    }

    /// Add a static track to the builder (note this will clone all events in the passed iterator)
    pub fn add_static_track<'a,I>(&mut self, track: I) where I: Iterator<Item=&'a AbsoluteEvent> {
        let mut cur_time: u64 = 0;
        let vec = track.map(|bev| {
            assert!(bev.time >= cur_time);
            let vtime = bev.time - cur_time;
            cur_time = vtime;
            TrackEvent {
                vtime: vtime,
                event: bev.event.clone(),
            }
        }).collect();
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Static(vec),
        });
    }

    /// Set the copyright for the track at index `track`.  This will
    /// also cause a copyright meta event to be inserted.
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a copyright set.
    pub fn set_copyright(&mut self, track: usize, copyright: String) {
        assert!(self.tracks.len() < track);
        assert!(self.tracks[track].copyright.is_none());
        // let event = AbsoluteEvent {
        //     time: 0,
        //     event: Event::Meta(MetaEvent::copyright_notice(copyright.clone())),
        // };
        self.tracks[track].copyright = Some(copyright);
    }

    /// Set the name for the track at index `track`.  This will
    /// also cause a name meta event to be inserted.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a name set.
    pub fn set_name(&mut self, track: usize, name: String) {
        assert!(self.tracks.len() < track);
        assert!(self.tracks[track].name.is_none());
        // let event = AbsoluteEvent{
        //     time: 0,
        //     event: Event::Meta(MetaEvent::sequence_or_track_name(name.clone())),
        // };
        self.tracks[track].name = Some(name);
    }

    /// Add a midi message to track at index `track` at absolute time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_abs(&mut self, track: usize, time: u64, msg: MidiMessage) {
        assert!(self.tracks.len() < track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Midi(msg),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a midi message to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_rel(&mut self, track: usize, delta: u64, msg: MidiMessage) {
        assert!(self.tracks.len() < track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_midi_abs(track,time,msg);
    }

    /// Add a meta event to track at index `track` at absolute  time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_abs(&mut self, track: usize, time: u64, event: MetaEvent) {
        assert!(self.tracks.len() < track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Meta(event),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a meta event to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_rel(&mut self, track: usize, delta: u64, event: MetaEvent) {
        assert!(self.tracks.len() < track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_meta_abs(track,time,event);
    }

    /// Add a TrackEvent to the track at index `track`.  The event
    /// will be added at `event.vtime` after the last event currently
    /// in the builder for the track.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_event(&mut self, track: usize, event: TrackEvent) {
        assert!(self.tracks.len() < track);
        let bevent = AbsoluteEvent {
            time: self.tracks[track].abs_time_from_delta(event.vtime),
            event: event.event,
        };
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(bevent);
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Generate an SMF file with the events that have been added to
    /// the builder
    pub fn result(self) -> SMF {
        SMF {
            format: SMFFormat::MultiTrack,
            tracks: self.tracks.into_iter().map(|tb| tb.result()).collect(),
            division: 0,
        }
    }
}
<commit_msg>assertion bug fix<commit_after>use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::ops::IndexMut;

use ::{SMF,Event,SMFFormat,MetaEvent,MidiMessage,Track,TrackEvent};

/// An AbsoluteEvent is an event that has an absolute time
/// This is useful for apps that want to store events internally
/// with absolute times and then quickly build an SMF file for saving etc...
pub struct AbsoluteEvent {
    time: u64,
    event: Event,
}

impl AbsoluteEvent {
    pub fn new_midi(time: u64, midi: MidiMessage) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Midi(midi),
        }
    }
    pub fn new_meta(time: u64, meta: MetaEvent) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Meta(meta),
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a midi
    /// event, false if it's a meta event
    pub fn is_midi(&self) -> bool {
        match self.event {
            Event::Midi(_) => true,
            Event::Meta(_) => false,
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a meta
    /// event, false if it's a midi event
    pub fn is_meta(&self) -> bool {
        match self.event {
            Event::Midi(_) => false,
            Event::Meta(_) => true,
        }
    }

    pub fn get_event(&self) -> &Event {
        &self.event
    }

    pub fn get_time(&self) -> u64 {
        self.time
    }
}

impl Eq for AbsoluteEvent {}

impl PartialEq for AbsoluteEvent {
    fn eq(&self, other: &AbsoluteEvent) -> bool {
        if self.time == other.time {
            match (&self.event,&other.event) {
                (&Event::Midi(_),&Event::Meta(_)) => false,
                (&Event::Meta(_),&Event::Midi(_)) => false,
                (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                    me.command == you.command
                },
                (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                    me.data(0) == you.data(0)
                        &&
                    me.data(1) == me.data(1)
                },
            }
        } else {
            false
        }
    }

    fn ne(&self, other: &AbsoluteEvent) -> bool {
        !(self.eq(other))
    }
}

// Implement `Ord` and sort messages by time
impl Ord for AbsoluteEvent {
    fn cmp(&self, other: &AbsoluteEvent) -> Ordering {
        let res = self.time.cmp(&other.time);
        match res {
            // vtime takes priority
            Ordering::Less | Ordering::Greater => res,
            // if vtime is the same, check types and make meta events
            // sort before standard events
            Ordering::Equal => {
                match (&self.event,&other.event) {
                    // I'm midi, other is meta, so I'm greater
                    (&Event::Midi(_),&Event::Meta(_)) => Ordering::Greater,
                    // I'm meta, other is midi, so I'm less
                    (&Event::Meta(_),&Event::Midi(_)) => Ordering::Less,
                    (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                        me.command.cmp(&you.command)
                    },
                    (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                        if      me.data(0) < you.data(0) { Ordering::Less }
                        else if me.data(0) > you.data(0) { Ordering::Greater }
                        else {
                            if me.data(1) < you.data(1) {
                                Ordering::Less
                            } else if me.data(1) > you.data(1) {
                                Ordering::Greater
                            } else {
                                res
                            }
                        }
                    },
                }
            }
        }
    }
}

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

enum EventContainer {
    Heap(BinaryHeap<AbsoluteEvent>),
    Static(Vec<TrackEvent>),
}

struct TrackBuilder {
    copyright: Option<String>,
    name: Option<String>,
    events: EventContainer,
}

impl TrackBuilder {

    fn result(self) -> Track {
        Track {
            copyright: self.copyright,
            name: self.name,
            events: match self.events {
                EventContainer::Heap(mut heap) => {
                    let mut events = Vec::new();
                    events.reserve(heap.len());
                    let mut cur_time: u64 = 0;
                    loop {
                        match heap.pop() {
                            Some(bev) => {
                                let vtime = bev.time - cur_time;
                                cur_time = vtime;
                                events.push(TrackEvent {
                                    vtime: vtime,
                                    event: bev.event,
                                });
                            }
                            None => { break; }
                        }
                    }
                    events
                },
                EventContainer::Static(vec) => vec,
            },
        }
    }

    fn abs_time_from_delta(&self,delta: u64) -> u64 {
        match self.events {
            EventContainer::Heap(ref heap) => {
                match heap.peek() {
                    Some(e) => { e.time + delta }
                    None => { delta }
                }
            }
            _ => { panic!("Can't call abs_time_from_delta on non-heap builder") }
        }
    }
}

/// An SMFBuilder can be used to create an SMF file.  This is done by
/// adding tracks to the builder via `add_track` and then adding
/// events to each track.
pub struct SMFBuilder {
    tracks:Vec<TrackBuilder>
}

impl SMFBuilder {
    /// Create a new SMFBuilder.  Initially the builder will have no tracks
    pub fn new() -> SMFBuilder {
        SMFBuilder {
            tracks: Vec::new(),
        }
    }

    /// Get the number of tracks currenly in the builder
    pub fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    /// Add new a track to this builder
    pub fn add_track(&mut self) {
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Heap(BinaryHeap::new()),
        });
    }

    /// Add a static track to the builder (note this will clone all events in the passed iterator)
    pub fn add_static_track<'a,I>(&mut self, track: I) where I: Iterator<Item=&'a AbsoluteEvent> {
        let mut cur_time: u64 = 0;
        let vec = track.map(|bev| {
            assert!(bev.time >= cur_time);
            let vtime = bev.time - cur_time;
            cur_time = vtime;
            TrackEvent {
                vtime: vtime,
                event: bev.event.clone(),
            }
        }).collect();
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Static(vec),
        });
    }

    /// Set the copyright for the track at index `track`.  This will
    /// also cause a copyright meta event to be inserted.
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a copyright set.
    pub fn set_copyright(&mut self, track: usize, copyright: String) {
        assert!(self.tracks.len() > track);
        assert!(self.tracks[track].copyright.is_none());
        // let event = AbsoluteEvent {
        //     time: 0,
        //     event: Event::Meta(MetaEvent::copyright_notice(copyright.clone())),
        // };
        self.tracks[track].copyright = Some(copyright);
    }

    /// Set the name for the track at index `track`.  This will
    /// also cause a name meta event to be inserted.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a name set.
    pub fn set_name(&mut self, track: usize, name: String) {
        assert!(self.tracks.len() > track);
        assert!(self.tracks[track].name.is_none());
        // let event = AbsoluteEvent{
        //     time: 0,
        //     event: Event::Meta(MetaEvent::sequence_or_track_name(name.clone())),
        // };
        self.tracks[track].name = Some(name);
    }

    /// Add a midi message to track at index `track` at absolute time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_abs(&mut self, track: usize, time: u64, msg: MidiMessage) {
        assert!(self.tracks.len() > track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Midi(msg),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a midi message to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_rel(&mut self, track: usize, delta: u64, msg: MidiMessage) {
        assert!(self.tracks.len() > track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_midi_abs(track,time,msg);
    }

    /// Add a meta event to track at index `track` at absolute  time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_abs(&mut self, track: usize, time: u64, event: MetaEvent) {
        assert!(self.tracks.len() > track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Meta(event),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a meta event to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_rel(&mut self, track: usize, delta: u64, event: MetaEvent) {
        assert!(self.tracks.len() > track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_meta_abs(track,time,event);
    }

    /// Add a TrackEvent to the track at index `track`.  The event
    /// will be added at `event.vtime` after the last event currently
    /// in the builder for the track.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_event(&mut self, track: usize, event: TrackEvent) {
        assert!(self.tracks.len() > track);
        let bevent = AbsoluteEvent {
            time: self.tracks[track].abs_time_from_delta(event.vtime),
            event: event.event,
        };
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(bevent);
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Generate an SMF file with the events that have been added to
    /// the builder
    pub fn result(self) -> SMF {
        SMF {
            format: SMFFormat::MultiTrack,
            tracks: self.tracks.into_iter().map(|tb| tb.result()).collect(),
            division: 0,
        }
    }
}
<|endoftext|>


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

use app_units::Au;
use device::TextureFilter;
use frame::FrameId;
use platform::font::{FontContext, RasterizedGlyph};
use profiler::TextureCacheProfileCounters;
use rayon::ThreadPool;
use rayon::prelude::*;
use resource_cache::ResourceClassCache;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::collections::HashSet;
use std::mem;
use texture_cache::{TextureCacheItemId, TextureCache};
use internal_types::FontTemplate;
use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};

pub type GlyphCache = ResourceClassCache<GlyphRequest, Option<TextureCacheItemId>>;

pub struct FontContexts {
    // These worker are mostly accessed from their corresponding worker threads.
    // The goal is that there should be no noticeable contention on the muteces.
    worker_contexts: Vec<Mutex<FontContext>>,

    // This worker should be accessed by threads that don't belong to thre thread pool
    // (in theory that's only the render backend thread so no contention expected either).
    shared_context: Mutex<FontContext>,

    // Stored here as a convenience to get the current thread index.
    workers: Arc<ThreadPool>,
}

impl FontContexts {
    /// Get access to the font context associated to the current thread.
    pub fn lock_current_context(&self) -> MutexGuard<FontContext> {
        let id = self.current_worker_id();
        self.lock_context(id)
    }

    /// Get access to any particular font context.
    ///
    /// The id is ```Some(i)``` where i is an index between 0 and num_worker_contexts
    /// for font contexts associated to the thread pool, and None for the shared
    /// global font context for use outside of the thread pool.
    pub fn lock_context(&self, id: Option<usize>) -> MutexGuard<FontContext> {
        match id {
            Some(index) => self.worker_contexts[index].lock().unwrap(),
            None => self.shared_context.lock().unwrap(),
        }
    }

    /// Get access to the font context usable outside of the thread pool.
    pub fn lock_shared_context(&self) -> MutexGuard<FontContext> {
        self.shared_context.lock().unwrap()
    }

    // number of contexts associated to workers
    pub fn num_worker_contexts(&self) -> usize {
        self.worker_contexts.len()
    }

    fn current_worker_id(&self) -> Option<usize> {
        self.workers.current_thread_index()
    }
}

pub struct GlyphRasterizer {
    workers: Arc<ThreadPool>,
    font_contexts: Arc<FontContexts>,

    // Receives the rendered glyphs.
    glyph_rx: Receiver<Vec<GlyphRasterJob>>,
    glyph_tx: Sender<Vec<GlyphRasterJob>>,

    // Maintain a set of glyphs that have been requested this
    // frame. This ensures the glyph thread won't rasterize
    // the same glyph more than once in a frame. This is required
    // because the glyph cache hash table is not updated
    // until the end of the frame when we wait for glyph requests
    // to be resolved.
    pending_glyphs: HashSet<GlyphRequest>,

    // We defer removing fonts to the end of the frame so that:
    // - this work is done outside of the critical path,
    // - we don't have to worry about the ordering of events if a font is used on
    //   a frame where it is used (although it seems unlikely).
    fonts_to_remove: Vec<FontKey>,
}

impl GlyphRasterizer {
    pub fn new(workers: Arc<ThreadPool>) -> Self {
        let (glyph_tx, glyph_rx) = channel();

        let num_workers = workers.current_num_threads();
        let mut contexts = Vec::with_capacity(num_workers);

        for _ in 0..num_workers {
            contexts.push(Mutex::new(FontContext::new()));
        }

        GlyphRasterizer {
            font_contexts: Arc::new(
                FontContexts {
                    worker_contexts: contexts,
                    shared_context: Mutex::new(FontContext::new()),
                    workers: Arc::clone(&workers),
                }
            ),
            glyph_rx: glyph_rx,
            glyph_tx: glyph_tx,
            pending_glyphs: HashSet::new(),
            workers: workers,
            fonts_to_remove: Vec::new(),
        }
    }

    pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
        let font_contexts = Arc::clone(&self.font_contexts);
        // It's important to synchronously add the font for the shared context because
        // we use it to check that fonts have been properly added when requesting glyphs.
        font_contexts.lock_shared_context().add_font(&font_key, &template);

        // TODO: this locks each font context while adding the font data, probably not a big deal,
        // but if there is contention on this lock we could easily have a queue of per-context
        // operations to add and delete fonts, and have these queues lazily processed by each worker
        // before rendering a glyph.
        // We can also move this into a worker to free up some cycles in the calling (render backend)
        // thread.
        for i in 0..font_contexts.num_worker_contexts() {
            font_contexts.lock_context(Some(i)).add_font(&font_key, &template);
        }
    }

    pub fn delete_font(&mut self, font_key: FontKey) {
        self.fonts_to_remove.push(font_key);
    }

    pub fn request_glyphs(
        &mut self,
        glyph_cache: &mut GlyphCache,
        current_frame_id: FrameId,
        font_key: FontKey,
        size: Au,
        color: ColorF,
        glyph_instances: &[GlyphInstance],
        render_mode: FontRenderMode,
        glyph_options: Option<GlyphOptions>,
    ) {
        assert!(self.font_contexts.lock_shared_context().has_font(&font_key));

        let mut glyphs = Vec::with_capacity(glyph_instances.len());

        {
            // TODO: If this takes too long we can resurect a dedicated glyph
            // dispatch thread, hopefully not.
            profile_scope!("glyph-requests");

            // select glyphs that have not been requested yet.
            for glyph in glyph_instances {
                let glyph_request = GlyphRequest::new(
                    font_key,
                    size,
                    color,
                    glyph.index,
                    glyph.point,
                    render_mode,
                    glyph_options,
                );

                glyph_cache.mark_as_needed(&glyph_request, current_frame_id);
                if !glyph_cache.contains_key(&glyph_request) && !self.pending_glyphs.contains(&glyph_request) {
                    self.pending_glyphs.insert(glyph_request.clone());
                    glyphs.push(glyph_request);
                }
            }
        }

        if glyphs.is_empty() {
            return;
        }

        let font_contexts = Arc::clone(&self.font_contexts);
        let glyph_tx = self.glyph_tx.clone();
        // spawn an async task to get off of the render backend thread as early as
        // possible and in that task use rayon's fork join dispatch to rasterize the
        // glyphs in the thread pool.
        self.workers.spawn_async(move || {
            let jobs = glyphs.par_iter().map(|request: &GlyphRequest| {
                profile_scope!("glyph-raster");
                let mut context = font_contexts.lock_current_context();
                let job = GlyphRasterJob {
                    request: request.clone(),
                    result: context.rasterize_glyph(
                        &request.key,
                        request.render_mode,
                        request.glyph_options
                    ),
                };

                // Sanity check.
                if let Some(ref glyph) = job.result {
                    let bpp = 4; // We always render glyphs in 32 bits RGBA format.
                    assert_eq!(glyph.bytes.len(), bpp * (glyph.width * glyph.height) as usize);
                }

                job
            }).collect();

            glyph_tx.send(jobs).unwrap();
        });
    }

    pub fn get_glyph_dimensions(&mut self, glyph_key: &GlyphKey) -> Option<GlyphDimensions> {
        self.font_contexts.lock_shared_context().get_glyph_dimensions(glyph_key)
    }

    pub fn resolve_glyphs(
        &mut self,
        current_frame_id: FrameId,
        glyph_cache: &mut GlyphCache,
        texture_cache: &mut TextureCache,
        texture_cache_profile: &mut TextureCacheProfileCounters,
    ) {
        let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len());

        // Pull rasterized glyphs from the queue.
        while !self.pending_glyphs.is_empty() {
            // TODO: rather than blocking until all pending glyphs are available
            // we could try_recv and steal work from the thread pool to take advantage
            // of the fact that this thread is alive and we avoid the added latency
            // of blocking it.
            let raster_jobs = self.glyph_rx.recv().expect("BUG: Should be glyphs pending!");
            for job in raster_jobs {
                debug_assert!(self.pending_glyphs.contains(&job.request));
                self.pending_glyphs.remove(&job.request);

                rasterized_glyphs.push(job);
            }
        }

        // Ensure that the glyphs are always processed in the same
        // order for a given text run (since iterating a hash set doesn't
        // guarantee order). This can show up as very small float inaccuacry
        // differences in rasterizers due to the different coordinates
        // that text runs get associated with by the texture cache allocator.
        rasterized_glyphs.sort_by(|a, b| a.request.cmp(&b.request));

        // Update the caches.
        for job in rasterized_glyphs {
            let image_id = job.result.and_then(
                |glyph| if glyph.width > 0 && glyph.height > 0 {
                    let image_id = texture_cache.new_item_id();
                    texture_cache.insert(
                        image_id,
                        ImageDescriptor {
                            width: glyph.width,
                            height: glyph.height,
                            stride: None,
                            format: ImageFormat::RGBA8,
                            is_opaque: false,
                            offset: 0,
                        },
                        TextureFilter::Linear,
                        ImageData::Raw(Arc::new(glyph.bytes)),
                        texture_cache_profile,
                    );
                    Some(image_id)
                } else {
                    None
                }
            );

            glyph_cache.insert(job.request, image_id, current_frame_id);
        }

        // Now that we are done with the critical path (rendering the glyphs),
        // we can schedule removing the fonts if needed.
        if !self.fonts_to_remove.is_empty() {
            let font_contexts = Arc::clone(&self.font_contexts);
            let fonts_to_remove = mem::replace(&mut self.fonts_to_remove, Vec::new());
            self.workers.spawn_async(move || {
                for font_key in &fonts_to_remove {
                    font_contexts.lock_shared_context().delete_font(font_key);
                }
                for i in 0..font_contexts.num_worker_contexts() {
                    let mut context = font_contexts.lock_context(Some(i));
                    for font_key in &fonts_to_remove {
                        context.delete_font(font_key);
                    }
                }
            });
        }
    }
}

impl FontContext {
    fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
        match template {
            &FontTemplate::Raw(ref bytes, index) => {
                self.add_raw_font(&font_key, &**bytes, index);
            }
            &FontTemplate::Native(ref native_font_handle) => {
                self.add_native_font(&font_key, (*native_font_handle).clone());
            }
        }
    }
}

#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct GlyphRequest {
    pub key: GlyphKey,
    pub render_mode: FontRenderMode,
    pub glyph_options: Option<GlyphOptions>,
}

impl GlyphRequest {
    pub fn new(
        font_key: FontKey,
        size: Au,
        color: ColorF,
        index: u32,
        point: LayoutPoint,
        render_mode: FontRenderMode,
        glyph_options: Option<GlyphOptions>,
    ) -> GlyphRequest {
        GlyphRequest {
            key: GlyphKey::new(font_key, size, color, index, point, render_mode),
            render_mode: render_mode,
            glyph_options: glyph_options,
        }
    }
}

struct GlyphRasterJob {
    request: GlyphRequest,
    result: Option<RasterizedGlyph>,
}

#[test]
fn raterize_200_glyphs() {
    // This test loads a font from disc, the renders 4 requests containing
    // 50 glyphs each, deletes the font and waits for the result.

    use rayon::Configuration;
    use std::fs::File;
    use std::io::Read;

    let workers = Arc::new(ThreadPool::new(Configuration::new()).unwrap());
    let mut glyph_rasterizer = GlyphRasterizer::new(workers);
    let mut glyph_cache = GlyphCache::new();

    let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
    let mut font_data = vec![];
    font_file.read_to_end(&mut font_data).expect("failed to read font file");

    let font_key = FontKey::new(0, 0);
    glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));

    let frame_id = FrameId(1);

    let mut glyph_instances = Vec::with_capacity(200);
    for i in 0..200 {
        glyph_instances.push(GlyphInstance {
            index: i, // It doesn't matter which glyphs we are actually rendering.
            point: LayoutPoint::new(0.0, 0.0),
        });
    }

    for i in 0..4 {
        glyph_rasterizer.request_glyphs(
            &mut glyph_cache,
            frame_id,
            font_key,
            Au::from_px(32),
            ColorF::new(0.0, 0.0, 0.0, 1.0),
            &glyph_instances[(50 * i)..(50 * (i + 1))],
            FontRenderMode::Subpixel,
            None,
        );
    }

    glyph_rasterizer.delete_font(font_key);

    glyph_rasterizer.resolve_glyphs(
        frame_id,
        &mut glyph_cache,
        &mut TextureCache::new(4096),
        &mut TextureCacheProfileCounters::new(),
    );
}
<commit_msg>Reomve the glyph-requests profiler probe.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use device::TextureFilter;
use frame::FrameId;
use platform::font::{FontContext, RasterizedGlyph};
use profiler::TextureCacheProfileCounters;
use rayon::ThreadPool;
use rayon::prelude::*;
use resource_cache::ResourceClassCache;
use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::collections::HashSet;
use std::mem;
use texture_cache::{TextureCacheItemId, TextureCache};
use internal_types::FontTemplate;
use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};

pub type GlyphCache = ResourceClassCache<GlyphRequest, Option<TextureCacheItemId>>;

pub struct FontContexts {
    // These worker are mostly accessed from their corresponding worker threads.
    // The goal is that there should be no noticeable contention on the muteces.
    worker_contexts: Vec<Mutex<FontContext>>,

    // This worker should be accessed by threads that don't belong to thre thread pool
    // (in theory that's only the render backend thread so no contention expected either).
    shared_context: Mutex<FontContext>,

    // Stored here as a convenience to get the current thread index.
    workers: Arc<ThreadPool>,
}

impl FontContexts {
    /// Get access to the font context associated to the current thread.
    pub fn lock_current_context(&self) -> MutexGuard<FontContext> {
        let id = self.current_worker_id();
        self.lock_context(id)
    }

    /// Get access to any particular font context.
    ///
    /// The id is ```Some(i)``` where i is an index between 0 and num_worker_contexts
    /// for font contexts associated to the thread pool, and None for the shared
    /// global font context for use outside of the thread pool.
    pub fn lock_context(&self, id: Option<usize>) -> MutexGuard<FontContext> {
        match id {
            Some(index) => self.worker_contexts[index].lock().unwrap(),
            None => self.shared_context.lock().unwrap(),
        }
    }

    /// Get access to the font context usable outside of the thread pool.
    pub fn lock_shared_context(&self) -> MutexGuard<FontContext> {
        self.shared_context.lock().unwrap()
    }

    // number of contexts associated to workers
    pub fn num_worker_contexts(&self) -> usize {
        self.worker_contexts.len()
    }

    fn current_worker_id(&self) -> Option<usize> {
        self.workers.current_thread_index()
    }
}

pub struct GlyphRasterizer {
    workers: Arc<ThreadPool>,
    font_contexts: Arc<FontContexts>,

    // Receives the rendered glyphs.
    glyph_rx: Receiver<Vec<GlyphRasterJob>>,
    glyph_tx: Sender<Vec<GlyphRasterJob>>,

    // Maintain a set of glyphs that have been requested this
    // frame. This ensures the glyph thread won't rasterize
    // the same glyph more than once in a frame. This is required
    // because the glyph cache hash table is not updated
    // until the end of the frame when we wait for glyph requests
    // to be resolved.
    pending_glyphs: HashSet<GlyphRequest>,

    // We defer removing fonts to the end of the frame so that:
    // - this work is done outside of the critical path,
    // - we don't have to worry about the ordering of events if a font is used on
    //   a frame where it is used (although it seems unlikely).
    fonts_to_remove: Vec<FontKey>,
}

impl GlyphRasterizer {
    pub fn new(workers: Arc<ThreadPool>) -> Self {
        let (glyph_tx, glyph_rx) = channel();

        let num_workers = workers.current_num_threads();
        let mut contexts = Vec::with_capacity(num_workers);

        for _ in 0..num_workers {
            contexts.push(Mutex::new(FontContext::new()));
        }

        GlyphRasterizer {
            font_contexts: Arc::new(
                FontContexts {
                    worker_contexts: contexts,
                    shared_context: Mutex::new(FontContext::new()),
                    workers: Arc::clone(&workers),
                }
            ),
            glyph_rx: glyph_rx,
            glyph_tx: glyph_tx,
            pending_glyphs: HashSet::new(),
            workers: workers,
            fonts_to_remove: Vec::new(),
        }
    }

    pub fn add_font(&mut self, font_key: FontKey, template: FontTemplate) {
        let font_contexts = Arc::clone(&self.font_contexts);
        // It's important to synchronously add the font for the shared context because
        // we use it to check that fonts have been properly added when requesting glyphs.
        font_contexts.lock_shared_context().add_font(&font_key, &template);

        // TODO: this locks each font context while adding the font data, probably not a big deal,
        // but if there is contention on this lock we could easily have a queue of per-context
        // operations to add and delete fonts, and have these queues lazily processed by each worker
        // before rendering a glyph.
        // We can also move this into a worker to free up some cycles in the calling (render backend)
        // thread.
        for i in 0..font_contexts.num_worker_contexts() {
            font_contexts.lock_context(Some(i)).add_font(&font_key, &template);
        }
    }

    pub fn delete_font(&mut self, font_key: FontKey) {
        self.fonts_to_remove.push(font_key);
    }

    pub fn request_glyphs(
        &mut self,
        glyph_cache: &mut GlyphCache,
        current_frame_id: FrameId,
        font_key: FontKey,
        size: Au,
        color: ColorF,
        glyph_instances: &[GlyphInstance],
        render_mode: FontRenderMode,
        glyph_options: Option<GlyphOptions>,
    ) {
        assert!(self.font_contexts.lock_shared_context().has_font(&font_key));

        let mut glyphs = Vec::with_capacity(glyph_instances.len());

        // select glyphs that have not been requested yet.
        for glyph in glyph_instances {
            let glyph_request = GlyphRequest::new(
                font_key,
                size,
                color,
                glyph.index,
                glyph.point,
                render_mode,
                glyph_options,
            );

            glyph_cache.mark_as_needed(&glyph_request, current_frame_id);
            if !glyph_cache.contains_key(&glyph_request) && !self.pending_glyphs.contains(&glyph_request) {
                self.pending_glyphs.insert(glyph_request.clone());
                glyphs.push(glyph_request);
            }
        }

        if glyphs.is_empty() {
            return;
        }

        let font_contexts = Arc::clone(&self.font_contexts);
        let glyph_tx = self.glyph_tx.clone();
        // spawn an async task to get off of the render backend thread as early as
        // possible and in that task use rayon's fork join dispatch to rasterize the
        // glyphs in the thread pool.
        self.workers.spawn_async(move || {
            let jobs = glyphs.par_iter().map(|request: &GlyphRequest| {
                profile_scope!("glyph-raster");
                let mut context = font_contexts.lock_current_context();
                let job = GlyphRasterJob {
                    request: request.clone(),
                    result: context.rasterize_glyph(
                        &request.key,
                        request.render_mode,
                        request.glyph_options
                    ),
                };

                // Sanity check.
                if let Some(ref glyph) = job.result {
                    let bpp = 4; // We always render glyphs in 32 bits RGBA format.
                    assert_eq!(glyph.bytes.len(), bpp * (glyph.width * glyph.height) as usize);
                }

                job
            }).collect();

            glyph_tx.send(jobs).unwrap();
        });
    }

    pub fn get_glyph_dimensions(&mut self, glyph_key: &GlyphKey) -> Option<GlyphDimensions> {
        self.font_contexts.lock_shared_context().get_glyph_dimensions(glyph_key)
    }

    pub fn resolve_glyphs(
        &mut self,
        current_frame_id: FrameId,
        glyph_cache: &mut GlyphCache,
        texture_cache: &mut TextureCache,
        texture_cache_profile: &mut TextureCacheProfileCounters,
    ) {
        let mut rasterized_glyphs = Vec::with_capacity(self.pending_glyphs.len());

        // Pull rasterized glyphs from the queue.
        while !self.pending_glyphs.is_empty() {
            // TODO: rather than blocking until all pending glyphs are available
            // we could try_recv and steal work from the thread pool to take advantage
            // of the fact that this thread is alive and we avoid the added latency
            // of blocking it.
            let raster_jobs = self.glyph_rx.recv().expect("BUG: Should be glyphs pending!");
            for job in raster_jobs {
                debug_assert!(self.pending_glyphs.contains(&job.request));
                self.pending_glyphs.remove(&job.request);

                rasterized_glyphs.push(job);
            }
        }

        // Ensure that the glyphs are always processed in the same
        // order for a given text run (since iterating a hash set doesn't
        // guarantee order). This can show up as very small float inaccuacry
        // differences in rasterizers due to the different coordinates
        // that text runs get associated with by the texture cache allocator.
        rasterized_glyphs.sort_by(|a, b| a.request.cmp(&b.request));

        // Update the caches.
        for job in rasterized_glyphs {
            let image_id = job.result.and_then(
                |glyph| if glyph.width > 0 && glyph.height > 0 {
                    let image_id = texture_cache.new_item_id();
                    texture_cache.insert(
                        image_id,
                        ImageDescriptor {
                            width: glyph.width,
                            height: glyph.height,
                            stride: None,
                            format: ImageFormat::RGBA8,
                            is_opaque: false,
                            offset: 0,
                        },
                        TextureFilter::Linear,
                        ImageData::Raw(Arc::new(glyph.bytes)),
                        texture_cache_profile,
                    );
                    Some(image_id)
                } else {
                    None
                }
            );

            glyph_cache.insert(job.request, image_id, current_frame_id);
        }

        // Now that we are done with the critical path (rendering the glyphs),
        // we can schedule removing the fonts if needed.
        if !self.fonts_to_remove.is_empty() {
            let font_contexts = Arc::clone(&self.font_contexts);
            let fonts_to_remove = mem::replace(&mut self.fonts_to_remove, Vec::new());
            self.workers.spawn_async(move || {
                for font_key in &fonts_to_remove {
                    font_contexts.lock_shared_context().delete_font(font_key);
                }
                for i in 0..font_contexts.num_worker_contexts() {
                    let mut context = font_contexts.lock_context(Some(i));
                    for font_key in &fonts_to_remove {
                        context.delete_font(font_key);
                    }
                }
            });
        }
    }
}

impl FontContext {
    fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
        match template {
            &FontTemplate::Raw(ref bytes, index) => {
                self.add_raw_font(&font_key, &**bytes, index);
            }
            &FontTemplate::Native(ref native_font_handle) => {
                self.add_native_font(&font_key, (*native_font_handle).clone());
            }
        }
    }
}

#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub struct GlyphRequest {
    pub key: GlyphKey,
    pub render_mode: FontRenderMode,
    pub glyph_options: Option<GlyphOptions>,
}

impl GlyphRequest {
    pub fn new(
        font_key: FontKey,
        size: Au,
        color: ColorF,
        index: u32,
        point: LayoutPoint,
        render_mode: FontRenderMode,
        glyph_options: Option<GlyphOptions>,
    ) -> GlyphRequest {
        GlyphRequest {
            key: GlyphKey::new(font_key, size, color, index, point, render_mode),
            render_mode: render_mode,
            glyph_options: glyph_options,
        }
    }
}

struct GlyphRasterJob {
    request: GlyphRequest,
    result: Option<RasterizedGlyph>,
}

#[test]
fn raterize_200_glyphs() {
    // This test loads a font from disc, the renders 4 requests containing
    // 50 glyphs each, deletes the font and waits for the result.

    use rayon::Configuration;
    use std::fs::File;
    use std::io::Read;

    let workers = Arc::new(ThreadPool::new(Configuration::new()).unwrap());
    let mut glyph_rasterizer = GlyphRasterizer::new(workers);
    let mut glyph_cache = GlyphCache::new();

    let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file");
    let mut font_data = vec![];
    font_file.read_to_end(&mut font_data).expect("failed to read font file");

    let font_key = FontKey::new(0, 0);
    glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0));

    let frame_id = FrameId(1);

    let mut glyph_instances = Vec::with_capacity(200);
    for i in 0..200 {
        glyph_instances.push(GlyphInstance {
            index: i, // It doesn't matter which glyphs we are actually rendering.
            point: LayoutPoint::new(0.0, 0.0),
        });
    }

    for i in 0..4 {
        glyph_rasterizer.request_glyphs(
            &mut glyph_cache,
            frame_id,
            font_key,
            Au::from_px(32),
            ColorF::new(0.0, 0.0, 0.0, 1.0),
            &glyph_instances[(50 * i)..(50 * (i + 1))],
            FontRenderMode::Subpixel,
            None,
        );
    }

    glyph_rasterizer.delete_font(font_key);

    glyph_rasterizer.resolve_glyphs(
        frame_id,
        &mut glyph_cache,
        &mut TextureCache::new(4096),
        &mut TextureCacheProfileCounters::new(),
    );
}
<|endoftext|>


<commit_before>
use gfx;
use gfx_core;
use gfx::traits::*;
use gfx::format::*;
use vecmath;

use heightmap::Heightmap;

type RenderTarget = gfx::RenderTarget<Srgba8>;
type DepthTarget = gfx::DepthTarget<DepthStencil>;

gfx_pipeline!( pipe {
    model_view_projection: gfx::Global<[[f32; 4]; 4]> = "modelViewProjection",
    resolution: gfx::Global<i32> = "resolution",
    heights: gfx::TextureSampler<f32> = "heights",
    normals: gfx::TextureSampler<[f32; 4]> = "normals",
    shadows: gfx::TextureSampler<f32> = "shadows",
    out_color: RenderTarget = "OutColor",
    out_depth: DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE,
});

gfx_pipeline!( generate_textures {
    y_scale: gfx::Global<f32> = "yScale",
    heights: gfx::TextureSampler<f32> = "heights",
    normals: gfx::RenderTarget<Rgba8> = "normals",
    shadows: gfx::RenderTarget<(R16, Unorm)> = "shadows",
});

pub struct Terrain <R, F> where R: gfx::Resources, F: gfx::Factory<R>{
    factory: F,
    pso: gfx::PipelineState<R, pipe::Meta>,
    slice: gfx::Slice<R>,
    data: pipe::Data<R>,

    normals: (gfx_core::handle::Texture<R, gfx_core::format::R8_G8_B8_A8>,
              gfx_core::handle::ShaderResourceView<R, [f32; 4]>,
              gfx_core::handle::RenderTargetView<R, Rgba8>),

    shadows: (gfx_core::handle::Texture<R, gfx_core::format::R16>,
              gfx_core::handle::ShaderResourceView<R, f32>,
              gfx_core::handle::RenderTargetView<R, (R16, Unorm)>),
}

impl<R, F> Terrain <R, F> where R: gfx::Resources, F: gfx::Factory<R> {
    pub fn new(heightmap: Heightmap<u16>,
               mut factory: F,
               out_color: <RenderTarget as gfx::pso::DataBind<R>>::Data,
               out_stencil: <DepthTarget as gfx::pso::DataBind<R>>::Data) -> Self
    {
        let resolution = 32;

        let slice = gfx::Slice {
            start: 0,
            end: resolution * resolution * 4,
            base_vertex: 0,
            instances: None,
            buffer: gfx::IndexBuffer::Auto,
        };

        let w = heightmap.width;
        let h = heightmap.height;
        let (_, texture_view) = factory.create_texture_immutable::<(R16, Unorm)>(
            gfx::texture::Kind::D2(w, h, gfx::texture::AaMode::Single),
            &[&heightmap.heights[..]]).unwrap();

        let sinfo = gfx::texture::SamplerInfo::new(
            gfx::texture::FilterMethod::Bilinear,
            gfx::texture::WrapMode::Clamp);
        let sampler = factory.create_sampler(sinfo);

        let normals = factory.create_render_target::<Rgba8>(w, h).unwrap();
        let shadows = factory.create_render_target::<(R16, Unorm)>(w, h).unwrap();

        let data = pipe::Data {
            model_view_projection: [[0.0; 4]; 4],
            resolution: resolution as i32,
            heights: (texture_view, sampler.clone()),
            normals: (normals.1.clone(), sampler.clone()),
            shadows: (shadows.1.clone(), sampler),
            out_color: out_color,
            out_depth: out_stencil,
        };

        let shaders = gfx::ShaderSet::Tessellated(
            factory.create_shader_vertex(
                include_str!("../assets/clipmap.glslv").as_bytes()).unwrap(),
            factory.create_shader_hull(
                include_str!("../assets/clipmap.glslh").as_bytes()).unwrap(),
            factory.create_shader_domain(
                include_str!("../assets/clipmap.glsld").as_bytes()).unwrap(),
            factory.create_shader_pixel(
                include_str!("../assets/clipmap.glslf").as_bytes()).unwrap()
        );
        let rasterizer = gfx::state::Rasterizer {
            front_face: gfx::state::FrontFace::Clockwise,
            cull_face: gfx::state::CullFace::Nothing,
            method: gfx::state::RasterMethod::Fill,
            offset: None,
            samples: None,
        };
        let pso = factory.create_pipeline_state(
            &shaders,
            gfx::Primitive::PatchList(4),
            rasterizer,
            pipe::new()
        ).unwrap();

        Terrain {
            factory: factory,
            pso: pso,
            slice: slice,
            data: data,
            normals: normals,
            shadows: shadows,
        }
    }

    pub fn generate_textures<C>(&mut self, encoder: &mut gfx::Encoder<R, C>)
        where C: gfx_core::command::Buffer<R>
    {
        let slice = gfx::Slice {
            start: 0,
            end: 6,
            base_vertex: 0,
            instances: None,
            buffer: gfx::IndexBuffer::Auto,
        };

        let data = generate_textures::Data {
            heights: self.data.heights.clone(),
            normals: self.normals.2.clone(),
            shadows: self.shadows.2.clone(),
            y_scale: 320.0,
        };

        let pso = self.factory.create_pipeline_simple(
            include_str!("../assets/generate_textures.glslv").as_bytes(),
            include_str!("../assets/generate_textures.glslf").as_bytes(),
            generate_textures::new()
        ).unwrap();

        encoder.draw(&slice, &pso, &data);
    }

    pub fn update(&mut self, mvp_mat: vecmath::Matrix4<f32>) {
        self.data.model_view_projection = mvp_mat;
    }

    pub fn render<C>(&self, encoder: &mut gfx::Encoder<R, C>)
        where C: gfx_core::command::Buffer<R>
    {
        encoder.draw(&self.slice, &self.pso, &self.data);
    }
}
<commit_msg>Start refactoring to be an actual clipmap<commit_after>
use gfx;
use gfx_core;
use gfx::traits::*;
use gfx::format::*;
use vecmath;

use heightmap::Heightmap;

type RenderTarget = gfx::RenderTarget<Srgba8>;
type DepthTarget = gfx::DepthTarget<DepthStencil>;

gfx_pipeline!( pipe {
    model_view_projection: gfx::Global<[[f32; 4]; 4]> = "modelViewProjection",
    resolution: gfx::Global<i32> = "resolution",
    heights: gfx::TextureSampler<f32> = "heights",
    normals: gfx::TextureSampler<[f32; 4]> = "normals",
    shadows: gfx::TextureSampler<f32> = "shadows",
    out_color: RenderTarget = "OutColor",
    out_depth: DepthTarget = gfx::preset::depth::LESS_EQUAL_WRITE,
});

gfx_pipeline!( generate_textures {
    y_scale: gfx::Global<f32> = "yScale",
    heights: gfx::TextureSampler<f32> = "heights",
    normals: gfx::RenderTarget<Rgba8> = "normals",
    shadows: gfx::RenderTarget<(R16, Unorm)> = "shadows",
});

type HeightMap<R> = (gfx_core::handle::Texture<R, gfx_core::format::R16>,
                  gfx_core::handle::ShaderResourceView<R, f32>,
                  gfx_core::handle::RenderTargetView<R, (R16, Unorm)>);

type ColorMap<R> = (gfx_core::handle::Texture<R, gfx_core::format::R8_G8_B8_A8>,
                 gfx_core::handle::ShaderResourceView<R, [f32; 4]>,
                 gfx_core::handle::RenderTargetView<R, Rgba8>);

type NormalMap<R> = (gfx_core::handle::Texture<R, gfx_core::format::R8_G8_B8_A8>,
                  gfx_core::handle::ShaderResourceView<R, [f32; 4]>,
                  gfx_core::handle::RenderTargetView<R, Rgba8>);

type ShadowMap<R> = (gfx_core::handle::Texture<R, gfx_core::format::R16>,
                  gfx_core::handle::ShaderResourceView<R, f32>,
                  gfx_core::handle::RenderTargetView<R, (R16, Unorm)>);

struct Clipmap <R> where R: gfx::Resources {
    side_length: i64,
    vertex_resolution: i64,
    texture_resolution: i64,

    pso: gfx::PipelineState<R, pipe::Meta>,
    slice: gfx::Slice<R>,

    layers: Vec<ClipmapLayer<R>>,
}

enum ClipmapLayer <R> where R: gfx::Resources {
    Precomputed {
        x: i64,
        y: i64,
        pipeline_data: pipe::Data<R>,
        colors: ColorMap<R>,
        heights: HeightMap<R>,
    },
    Static {
        x: i64,
        y: i64,
        pipeline_data: pipe::Data<R>,
        // heights: HeightMap<R>,
        normals: NormalMap<R>,
        shadows: ShadowMap<R>,
    },
    Generated {
        x: i64,
        y: i64,
        pipeline_data: pipe::Data<R>,
        heights: HeightMap<R>,
        normals: NormalMap<R>,
        shadows: ShadowMap<R>,
    }
}

impl<R> Clipmap <R> where R: gfx::Resources {
    pub fn generate_textures<F, C>(&mut self, factory: &mut F, encoder: &mut gfx::Encoder<R, C>)
        where C: gfx_core::command::Buffer<R>,
              F: gfx::Factory<R>
    {
        let slice = gfx::Slice {
            start: 0,
            end: 6,
            base_vertex: 0,
            instances: None,
            buffer: gfx::IndexBuffer::Auto,
        };

        for layer in self.layers.iter_mut() {
            match layer {
                &mut ClipmapLayer::Static {x, y,
                                      ref mut pipeline_data,
                                      ref mut normals,
                                      ref mut shadows} => {
                    let data = generate_textures::Data {
                        heights: pipeline_data.heights.clone(),
                        normals: normals.2.clone(),
                        shadows: shadows.2.clone(),
                        y_scale: 320.0,
                    };

                    let pso = factory.create_pipeline_simple(
                        include_str!("../assets/generate_textures.glslv").as_bytes(),
                        include_str!("../assets/generate_textures.glslf").as_bytes(),
                        generate_textures::new()
                    ).unwrap();

                    encoder.draw(&slice, &pso, &data);
                }
                _ => unimplemented!(),
            }
        }
    }
    pub fn update(&mut self, mvp_mat: vecmath::Matrix4<f32>) {
        for layer in self.layers.iter_mut() {
            match layer {
                &mut ClipmapLayer::Precomputed {ref mut pipeline_data, ..} =>
                    pipeline_data.model_view_projection = mvp_mat,
                &mut ClipmapLayer::Static {ref mut pipeline_data, ..} =>
                    pipeline_data.model_view_projection = mvp_mat,
                &mut ClipmapLayer::Generated {ref mut pipeline_data, ..} =>
                    pipeline_data.model_view_projection = mvp_mat,
            }
        }
    }
    pub fn render<C>(&self, encoder: &mut gfx::Encoder<R, C>)
        where C: gfx_core::command::Buffer<R>
    {
        for layer in self.layers.iter() {
            match layer {
                &ClipmapLayer::Precomputed {ref pipeline_data, ..} =>
                    encoder.draw(&self.slice, &self.pso, pipeline_data),
                &ClipmapLayer::Static {ref pipeline_data, ..} =>
                    encoder.draw(&self.slice, &self.pso, pipeline_data),
                &ClipmapLayer::Generated {ref pipeline_data, ..} =>
                    encoder.draw(&self.slice, &self.pso, pipeline_data),
            }
        }
    }

}

pub struct Terrain <R, F> where R: gfx::Resources, F: gfx::Factory<R>{
    factory: F,
    clipmap: Clipmap<R>,
}

impl<R, F> Terrain <R, F> where R: gfx::Resources, F: gfx::Factory<R> {
    pub fn new(heightmap: Heightmap<u16>,
               mut factory: F,
               out_color: <RenderTarget as gfx::pso::DataBind<R>>::Data,
               out_stencil: <DepthTarget as gfx::pso::DataBind<R>>::Data) -> Self
    {
        let resolution = 32;

        let slice = gfx::Slice {
            start: 0,
            end: resolution * resolution * 4,
            base_vertex: 0,
            instances: None,
            buffer: gfx::IndexBuffer::Auto,
        };

        let w = heightmap.width;
        let h = heightmap.height;
        let (_, texture_view) = factory.create_texture_immutable::<(R16, Unorm)>(
            gfx::texture::Kind::D2(w, h, gfx::texture::AaMode::Single),
            &[&heightmap.heights[..]]).unwrap();

        let sinfo = gfx::texture::SamplerInfo::new(
            gfx::texture::FilterMethod::Bilinear,
            gfx::texture::WrapMode::Clamp);
        let sampler = factory.create_sampler(sinfo);

        let shaders = gfx::ShaderSet::Tessellated(
            factory.create_shader_vertex(
                include_str!("../assets/clipmap.glslv").as_bytes()).unwrap(),
            factory.create_shader_hull(
                include_str!("../assets/clipmap.glslh").as_bytes()).unwrap(),
            factory.create_shader_domain(
                include_str!("../assets/clipmap.glsld").as_bytes()).unwrap(),
            factory.create_shader_pixel(
                include_str!("../assets/clipmap.glslf").as_bytes()).unwrap()
        );
        let rasterizer = gfx::state::Rasterizer {
            front_face: gfx::state::FrontFace::Clockwise,
            cull_face: gfx::state::CullFace::Nothing,
            method: gfx::state::RasterMethod::Line(1),
            offset: None,
            samples: None,
        };
        let pso = factory.create_pipeline_state(
            &shaders,
            gfx::Primitive::PatchList(4),
            rasterizer,
            pipe::new()
        ).unwrap();

        let mut clipmap = Clipmap {
            side_length: 1024,
            vertex_resolution: 64,
            texture_resolution: 1024,
            pso: pso,
            slice: slice,
            layers: Vec::new(),
        };

        let normals = factory.create_render_target::<Rgba8>(w, h).unwrap();
        let shadows = factory.create_render_target::<(R16, Unorm)>(w, h).unwrap();
        clipmap.layers.push(ClipmapLayer::Static{
            x: 0, y: 0,
            pipeline_data: pipe::Data {
                model_view_projection: [[0.0; 4]; 4],
                resolution: resolution as i32,
                heights: (texture_view, sampler.clone()),
                normals: (normals.1.clone(), sampler.clone()),
                shadows: (shadows.1.clone(), sampler),
                out_color: out_color,
                out_depth: out_stencil,
            },
            normals: normals.clone(),
            shadows: shadows.clone(),
        });

        Terrain {
            factory: factory,
            clipmap: clipmap,
        }
    }

    pub fn generate_textures<C>(&mut self, encoder: &mut gfx::Encoder<R, C>)
        where C: gfx_core::command::Buffer<R>
    {
        self.clipmap.generate_textures(&mut self.factory, encoder);
    }

    pub fn update(&mut self, mvp_mat: vecmath::Matrix4<f32>) {
        self.clipmap.update(mvp_mat);
    }

    pub fn render<C>(&self, encoder: &mut gfx::Encoder<R, C>)
        where C: gfx_core::command::Buffer<R>
    {
        self.clipmap.render(encoder);
    }
}
<|endoftext|>


<commit_before>//! An interpreter for the rust-installer package format.  Responsible
//! for installing from a directory or tarball to an installation
//! prefix, represented by a `Components` instance.

use crate::dist::component::components::*;
use crate::dist::component::transaction::*;

use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;

use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

/// The current metadata revision used by rust-installer
pub const INSTALLER_VERSION: &str = "3";
pub const VERSION_FILE: &str = "rust-installer-version";

pub trait Package: fmt::Debug {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool;
    fn install<'a>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>>;
    fn components(&self) -> Vec<String>;
}

#[derive(Debug)]
pub struct DirectoryPackage {
    path: PathBuf,
    components: HashSet<String>,
    copy: bool,
}

impl DirectoryPackage {
    pub fn new(path: PathBuf, copy: bool) -> Result<Self> {
        validate_installer_version(&path)?;

        let content = utils::read_file("package components", &path.join("components"))?;
        let components = content
            .lines()
            .map(std::borrow::ToOwned::to_owned)
            .collect();
        Ok(DirectoryPackage {
            path,
            components,
            copy,
        })
    }
}

fn validate_installer_version(path: &Path) -> Result<()> {
    let file = utils::read_file("installer version", &path.join(VERSION_FILE))?;
    let v = file.trim();
    if v == INSTALLER_VERSION {
        Ok(())
    } else {
        Err(ErrorKind::BadInstallerVersion(v.to_owned()).into())
    }
}

impl Package for DirectoryPackage {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.components.contains(component)
            || if let Some(n) = short_name {
                self.components.contains(n)
            } else {
                false
            }
    }
    fn install<'a>(
        &self,
        target: &Components,
        name: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>> {
        let actual_name = if self.components.contains(name) {
            name
        } else if let Some(n) = short_name {
            n
        } else {
            name
        };

        let root = self.path.join(actual_name);

        let manifest = utils::read_file("package manifest", &root.join("manifest.in"))?;
        let mut builder = target.add(name, tx);

        for l in manifest.lines() {
            let part = ComponentPart::decode(l)
                .ok_or_else(|| ErrorKind::CorruptComponent(name.to_owned()))?;

            let path = part.1;
            let src_path = root.join(&path);

            match &*part.0 {
                "file" => {
                    if self.copy {
                        builder.copy_file(path.clone(), &src_path)?
                    } else {
                        builder.move_file(path.clone(), &src_path)?
                    }
                }
                "dir" => {
                    if self.copy {
                        builder.copy_dir(path.clone(), &src_path)?
                    } else {
                        builder.move_dir(path.clone(), &src_path)?
                    }
                }
                _ => return Err(ErrorKind::CorruptComponent(name.to_owned()).into()),
            }

            set_file_perms(&target.prefix().path().join(path), &src_path)?;
        }

        let tx = builder.finish()?;

        Ok(tx)
    }

    fn components(&self) -> Vec<String> {
        self.components.iter().cloned().collect()
    }
}

// On Unix we need to set up the file permissions correctly so
// binaries are executable and directories readable. This shouldn't be
// necessary: the source files *should* have the right permissions,
// but due to rust-lang/rust#25479 they don't.
#[cfg(unix)]
fn set_file_perms(dest_path: &Path, src_path: &Path) -> Result<()> {
    use std::fs::{self, Metadata};
    use std::os::unix::fs::PermissionsExt;
    use walkdir::WalkDir;

    // Compute whether this entry needs the X bit
    fn needs_x(meta: &Metadata) -> bool {
        meta.is_dir() || // Directories need it
        meta.permissions().mode() & 0o700 == 0o700 // If it is rwx for the user, it gets the X bit
    }

    // By convention, anything in the bin/ directory of the package is a binary
    let is_bin = if let Some(p) = src_path.parent() {
        p.ends_with("bin")
    } else {
        false
    };

    let is_dir = utils::is_directory(dest_path);

    if is_dir {
        // Walk the directory setting everything
        for entry in WalkDir::new(dest_path) {
            let entry = entry.chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;
            let meta = entry
                .metadata()
                .chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;

            let mut perm = meta.permissions();
            perm.set_mode(if needs_x(&meta) { 0o755 } else { 0o644 });
            fs::set_permissions(entry.path(), perm)
                .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        }
    } else {
        let meta =
            fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        let mut perm = meta.permissions();
        perm.set_mode(if is_bin || needs_x(&meta) {
            0o755
        } else {
            0o644
        });
        fs::set_permissions(dest_path, perm)
            .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
    }

    Ok(())
}

#[cfg(windows)]
fn set_file_perms(_dest_path: &Path, _src_path: &Path) -> Result<()> {
    Ok(())
}

#[derive(Debug)]
pub struct TarPackage<'a>(DirectoryPackage, temp::Dir<'a>);

impl<'a> TarPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let temp_dir = temp_cfg.new_directory()?;
        let mut archive = tar::Archive::new(stream);
        // The rust-installer packages unpack to a directory called
        // $pkgname-$version-$target. Skip that directory when
        // unpacking.
        unpack_without_first_dir(&mut archive, &*temp_dir)?;

        Ok(TarPackage(
            DirectoryPackage::new(temp_dir.to_owned(), false)?,
            temp_dir,
        ))
    }
}

fn unpack_without_first_dir<R: Read>(archive: &mut tar::Archive<R>, path: &Path) -> Result<()> {
    let entries = archive
        .entries()
        .chain_err(|| ErrorKind::ExtractingPackage)?;
    let mut checked_parents: HashSet<PathBuf> = HashSet::new();
    for entry in entries {
        let mut entry = entry.chain_err(|| ErrorKind::ExtractingPackage)?;
        let relpath = {
            let path = entry.path();
            let path = path.chain_err(|| ErrorKind::ExtractingPackage)?;
            path.into_owned()
        };
        let mut components = relpath.components();
        // Throw away the first path component
        components.next();
        let full_path = path.join(&components.as_path());

        // Create the full path to the entry if it does not exist already
        match full_path.parent() {
            Some(parent) => {
                if !checked_parents.contains(parent) {
                    checked_parents.insert(parent.clone().to_owned());
                    // It would be nice to optimise this stat out, but the tar could be like so:
                    // a/deep/file.txt
                    // a/file.txt
                    // which would require tracking the segments rather than a simple hash.
                    // Until profile shows that one stat per dir is a problem (vs one stat per file)
                    // leave till later.

                    if !parent.exists() {
                        ::std::fs::create_dir_all(&parent)
                            .chain_err(|| ErrorKind::ExtractingPackage)?
                    }
                }
            }
            _ => (),
        };

        entry
            .unpack(&full_path)
            .chain_err(|| ErrorKind::ExtractingPackage)?;
    }

    Ok(())
}

impl<'a> Package for TarPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarGzPackage<'a>(TarPackage<'a>);

impl<'a> TarGzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = flate2::read::GzDecoder::new(stream);
        Ok(TarGzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarGzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarXzPackage<'a>(TarPackage<'a>);

impl<'a> TarXzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = xz2::read::XzDecoder::new(stream);
        Ok(TarXzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarXzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}
<commit_msg>Don't set mtime on toolchain files<commit_after>//! An interpreter for the rust-installer package format.  Responsible
//! for installing from a directory or tarball to an installation
//! prefix, represented by a `Components` instance.

use crate::dist::component::components::*;
use crate::dist::component::transaction::*;

use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;

use std::collections::HashSet;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

/// The current metadata revision used by rust-installer
pub const INSTALLER_VERSION: &str = "3";
pub const VERSION_FILE: &str = "rust-installer-version";

pub trait Package: fmt::Debug {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool;
    fn install<'a>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>>;
    fn components(&self) -> Vec<String>;
}

#[derive(Debug)]
pub struct DirectoryPackage {
    path: PathBuf,
    components: HashSet<String>,
    copy: bool,
}

impl DirectoryPackage {
    pub fn new(path: PathBuf, copy: bool) -> Result<Self> {
        validate_installer_version(&path)?;

        let content = utils::read_file("package components", &path.join("components"))?;
        let components = content
            .lines()
            .map(std::borrow::ToOwned::to_owned)
            .collect();
        Ok(DirectoryPackage {
            path,
            components,
            copy,
        })
    }
}

fn validate_installer_version(path: &Path) -> Result<()> {
    let file = utils::read_file("installer version", &path.join(VERSION_FILE))?;
    let v = file.trim();
    if v == INSTALLER_VERSION {
        Ok(())
    } else {
        Err(ErrorKind::BadInstallerVersion(v.to_owned()).into())
    }
}

impl Package for DirectoryPackage {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.components.contains(component)
            || if let Some(n) = short_name {
                self.components.contains(n)
            } else {
                false
            }
    }
    fn install<'a>(
        &self,
        target: &Components,
        name: &str,
        short_name: Option<&str>,
        tx: Transaction<'a>,
    ) -> Result<Transaction<'a>> {
        let actual_name = if self.components.contains(name) {
            name
        } else if let Some(n) = short_name {
            n
        } else {
            name
        };

        let root = self.path.join(actual_name);

        let manifest = utils::read_file("package manifest", &root.join("manifest.in"))?;
        let mut builder = target.add(name, tx);

        for l in manifest.lines() {
            let part = ComponentPart::decode(l)
                .ok_or_else(|| ErrorKind::CorruptComponent(name.to_owned()))?;

            let path = part.1;
            let src_path = root.join(&path);

            match &*part.0 {
                "file" => {
                    if self.copy {
                        builder.copy_file(path.clone(), &src_path)?
                    } else {
                        builder.move_file(path.clone(), &src_path)?
                    }
                }
                "dir" => {
                    if self.copy {
                        builder.copy_dir(path.clone(), &src_path)?
                    } else {
                        builder.move_dir(path.clone(), &src_path)?
                    }
                }
                _ => return Err(ErrorKind::CorruptComponent(name.to_owned()).into()),
            }

            set_file_perms(&target.prefix().path().join(path), &src_path)?;
        }

        let tx = builder.finish()?;

        Ok(tx)
    }

    fn components(&self) -> Vec<String> {
        self.components.iter().cloned().collect()
    }
}

// On Unix we need to set up the file permissions correctly so
// binaries are executable and directories readable. This shouldn't be
// necessary: the source files *should* have the right permissions,
// but due to rust-lang/rust#25479 they don't.
#[cfg(unix)]
fn set_file_perms(dest_path: &Path, src_path: &Path) -> Result<()> {
    use std::fs::{self, Metadata};
    use std::os::unix::fs::PermissionsExt;
    use walkdir::WalkDir;

    // Compute whether this entry needs the X bit
    fn needs_x(meta: &Metadata) -> bool {
        meta.is_dir() || // Directories need it
        meta.permissions().mode() & 0o700 == 0o700 // If it is rwx for the user, it gets the X bit
    }

    // By convention, anything in the bin/ directory of the package is a binary
    let is_bin = if let Some(p) = src_path.parent() {
        p.ends_with("bin")
    } else {
        false
    };

    let is_dir = utils::is_directory(dest_path);

    if is_dir {
        // Walk the directory setting everything
        for entry in WalkDir::new(dest_path) {
            let entry = entry.chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;
            let meta = entry
                .metadata()
                .chain_err(|| ErrorKind::ComponentDirPermissionsFailed)?;

            let mut perm = meta.permissions();
            perm.set_mode(if needs_x(&meta) { 0o755 } else { 0o644 });
            fs::set_permissions(entry.path(), perm)
                .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        }
    } else {
        let meta =
            fs::metadata(dest_path).chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
        let mut perm = meta.permissions();
        perm.set_mode(if is_bin || needs_x(&meta) {
            0o755
        } else {
            0o644
        });
        fs::set_permissions(dest_path, perm)
            .chain_err(|| ErrorKind::ComponentFilePermissionsFailed)?;
    }

    Ok(())
}

#[cfg(windows)]
fn set_file_perms(_dest_path: &Path, _src_path: &Path) -> Result<()> {
    Ok(())
}

#[derive(Debug)]
pub struct TarPackage<'a>(DirectoryPackage, temp::Dir<'a>);

impl<'a> TarPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let temp_dir = temp_cfg.new_directory()?;
        let mut archive = tar::Archive::new(stream);
        // The rust-installer packages unpack to a directory called
        // $pkgname-$version-$target. Skip that directory when
        // unpacking.
        unpack_without_first_dir(&mut archive, &*temp_dir)?;

        Ok(TarPackage(
            DirectoryPackage::new(temp_dir.to_owned(), false)?,
            temp_dir,
        ))
    }
}

fn unpack_without_first_dir<R: Read>(archive: &mut tar::Archive<R>, path: &Path) -> Result<()> {
    let entries = archive
        .entries()
        .chain_err(|| ErrorKind::ExtractingPackage)?;
    let mut checked_parents: HashSet<PathBuf> = HashSet::new();
    for entry in entries {
        let mut entry = entry.chain_err(|| ErrorKind::ExtractingPackage)?;
        let relpath = {
            let path = entry.path();
            let path = path.chain_err(|| ErrorKind::ExtractingPackage)?;
            path.into_owned()
        };
        let mut components = relpath.components();
        // Throw away the first path component
        components.next();
        let full_path = path.join(&components.as_path());

        // Create the full path to the entry if it does not exist already
        match full_path.parent() {
            Some(parent) => {
                if !checked_parents.contains(parent) {
                    checked_parents.insert(parent.clone().to_owned());
                    // It would be nice to optimise this stat out, but the tar could be like so:
                    // a/deep/file.txt
                    // a/file.txt
                    // which would require tracking the segments rather than a simple hash.
                    // Until profile shows that one stat per dir is a problem (vs one stat per file)
                    // leave till later.

                    if !parent.exists() {
                        ::std::fs::create_dir_all(&parent)
                            .chain_err(|| ErrorKind::ExtractingPackage)?
                    }
                }
            }
            _ => (),
        };
        entry.set_preserve_mtime(false);
        entry
            .unpack(&full_path)
            .chain_err(|| ErrorKind::ExtractingPackage)?;
    }

    Ok(())
}

impl<'a> Package for TarPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarGzPackage<'a>(TarPackage<'a>);

impl<'a> TarGzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = flate2::read::GzDecoder::new(stream);
        Ok(TarGzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarGzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}

#[derive(Debug)]
pub struct TarXzPackage<'a>(TarPackage<'a>);

impl<'a> TarXzPackage<'a> {
    pub fn new<R: Read>(stream: R, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let stream = xz2::read::XzDecoder::new(stream);
        Ok(TarXzPackage(TarPackage::new(stream, temp_cfg)?))
    }
    pub fn new_file(path: &Path, temp_cfg: &'a temp::Cfg) -> Result<Self> {
        let file = File::open(path).chain_err(|| ErrorKind::ExtractingPackage)?;
        Self::new(file, temp_cfg)
    }
}

impl<'a> Package for TarXzPackage<'a> {
    fn contains(&self, component: &str, short_name: Option<&str>) -> bool {
        self.0.contains(component, short_name)
    }
    fn install<'b>(
        &self,
        target: &Components,
        component: &str,
        short_name: Option<&str>,
        tx: Transaction<'b>,
    ) -> Result<Transaction<'b>> {
        self.0.install(target, component, short_name, tx)
    }
    fn components(&self) -> Vec<String> {
        self.0.components()
    }
}
<|endoftext|>


<commit_before>// 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.

use language_tags::LanguageTag;
use std::fmt;

use {Args, Message};

/// Contextual configuration data.
#[derive(Clone)]
pub struct Context {
    /// The language being localized for.
    pub language_tag: LanguageTag,
    /// The value to use in a `PlaceholderFormat`.
    pub placeholder_value: Option<i64>,
}

impl Context {
    /// Create a new instance of `Context`.
    pub fn new(language: LanguageTag, placeholder_value: Option<i64>) -> Self {
        Context {
            language_tag: language,
            placeholder_value: placeholder_value,
        }
    }

    /// Format a message, returning a string.
    pub fn format<'f>(&self, message: &Message, args: Option<&Args<'f>>) -> String {
        let mut output = String::new();
        let _ = message.write_message(self, &mut output, args);
        output
    }

    /// Write a message to a stream.
    pub fn write<'f>(&self,
                     message: &Message,
                     stream: &mut fmt::Write,
                     args: Option<&Args<'f>>)
                     -> fmt::Result {
        message.write_message(self, stream, args)
    }
}

impl Default for Context {
    fn default() -> Self {
        Context {
            language_tag: Default::default(),
            placeholder_value: None,
        }
    }
}
<commit_msg>Make Context derive Debug.<commit_after>// 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.

use language_tags::LanguageTag;
use std::fmt;

use {Args, Message};

/// Contextual configuration data.
#[derive(Clone, Debug)]
pub struct Context {
    /// The language being localized for.
    pub language_tag: LanguageTag,
    /// The value to use in a `PlaceholderFormat`.
    pub placeholder_value: Option<i64>,
}

impl Context {
    /// Create a new instance of `Context`.
    pub fn new(language: LanguageTag, placeholder_value: Option<i64>) -> Self {
        Context {
            language_tag: language,
            placeholder_value: placeholder_value,
        }
    }

    /// Format a message, returning a string.
    pub fn format<'f>(&self, message: &Message, args: Option<&Args<'f>>) -> String {
        let mut output = String::new();
        let _ = message.write_message(self, &mut output, args);
        output
    }

    /// Write a message to a stream.
    pub fn write<'f>(&self,
                     message: &Message,
                     stream: &mut fmt::Write,
                     args: Option<&Args<'f>>)
                     -> fmt::Result {
        message.write_message(self, stream, args)
    }
}

impl Default for Context {
    fn default() -> Self {
        Context {
            language_tag: Default::default(),
            placeholder_value: None,
        }
    }
}
<|endoftext|>