<commit_before>use std::{
    fs,
    thread,
    path::{Path, PathBuf},
    sync::{mpsc, Arc},
    time::Duration,
};
use crossbeam_channel::{Receiver, Sender, unbounded, RecvError, select};
use relative_path::RelativePathBuf;
use thread_worker::WorkerHandle;
use walkdir::WalkDir;
use parking_lot::Mutex;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher};

use crate::{RootConfig, Roots, VfsRoot};

type Result<T> = std::result::Result<T, crossbeam_channel::SendError<TaskResult>>;

pub(crate) enum Task {
    AddRoot {
        root: VfsRoot,
        config: Arc<RootConfig>,
    },
}

#[derive(Debug)]
pub enum TaskResult {
    BulkLoadRoot {
        root: VfsRoot,
        files: Vec<(RelativePathBuf, String)>,
    },
    AddSingleFile {
        root: VfsRoot,
        path: RelativePathBuf,
        text: String,
    },
    ChangeSingleFile {
        root: VfsRoot,
        path: RelativePathBuf,
        text: String,
    },
    RemoveSingleFile {
        root: VfsRoot,
        path: RelativePathBuf,
    },
}

#[derive(Debug)]
enum ChangeKind {
    Create,
    Write,
    Remove,
}

const WATCHER_DELAY: Duration = Duration::from_millis(250);

pub(crate) struct Worker {
    worker: thread_worker::Worker<Task, TaskResult>,
    worker_handle: WorkerHandle,
}

impl Worker {
    pub(crate) fn start(roots: Arc<Roots>) -> Worker {
        // This is a pretty elaborate setup of threads & channels! It is
        // explained by the following concerns:

        //    * we need to burn a thread translating from notify's mpsc to
        //      crossbeam_channel.
        //    * we want to read all files from a single thread, to gurantee that
        //      we always get fresher versions and never go back in time.
        //    * we want to tear down everything neatly during shutdown.
        let (worker, worker_handle) = thread_worker::spawn(
            "vfs",
            128,
            // This are the channels we use to communicate with outside world.
            // If `input_receiver` is closed we need to tear ourselves down.
            // `output_sender` should not be closed unless the parent died.
            move |input_receiver, output_sender| {
                // These are `std` channels notify will send events to
                let (notify_sender, notify_receiver) = mpsc::channel();
                // These are the corresponding crossbeam channels
                let (watcher_sender, watcher_receiver) = unbounded();
                let watcher = notify::watcher(notify_sender, WATCHER_DELAY)
                    .map_err(|e| log::error!("failed to spawn notify {}", e))
                    .ok();
                let ctx = WatcherCtx {
                    roots,
                    watcher: Arc::new(Mutex::new(watcher)),
                    sender: output_sender,
                };
                let thread = thread::spawn(move || {
                    let _ = notify_receiver
                        .into_iter()
                        // forward relevant events only
                        .for_each(|event| convert_notify_event(event, &watcher_sender));
                });

                loop {
                    select! {
                        // Received request from the caller. If this channel is
                        // closed, we should shutdown everything.
                        recv(input_receiver) -> t => match t {
                            Err(RecvError) => break,
                            Ok(Task::AddRoot { root, config }) => watch_root(&ctx, root, Arc::clone(&config)),
                        },
                        // Watcher send us changes. If **this** channel is
                        // closed, the watcher has died, which indicates a bug
                        // -- escalate!
                        recv(watcher_receiver) -> event => match event {
                            Err(RecvError) => panic!("watcher is dead"),
                            Ok((path, change)) => WatcherCtx::handle_change(&ctx, path, change).unwrap(),
                        },
                    }
                }
                drop(ctx.watcher.lock().take());
                drop(ctx);
                let res2 = thread.join();
                match &res2 {
                    Ok(()) => log::info!("... Watcher terminated with ok"),
                    Err(_) => log::error!("... Watcher terminated with err"),
                }
                res2.unwrap();
            },
        );
        Worker {
            worker,
            worker_handle,
        }
    }

    pub(crate) fn sender(&self) -> &Sender<Task> {
        &self.worker.inp
    }

    pub(crate) fn receiver(&self) -> &Receiver<TaskResult> {
        &self.worker.out
    }

    pub(crate) fn shutdown(self) -> thread::Result<()> {
        let _ = self.worker.shutdown();
        self.worker_handle.shutdown()
    }
}

fn watch_root(woker: &WatcherCtx, root: VfsRoot, config: Arc<RootConfig>) {
    let mut guard = woker.watcher.lock();
    log::debug!("loading {} ...", config.root.as_path().display());
    let files = watch_recursive(guard.as_mut(), config.root.as_path(), &*config)
        .into_iter()
        .filter_map(|path| {
            let abs_path = path.to_path(&config.root);
            let text = read_to_string(&abs_path)?;
            Some((path, text))
        })
        .collect();
    woker
        .sender
        .send(TaskResult::BulkLoadRoot { root, files })
        .unwrap();
    log::debug!("... loaded {}", config.root.as_path().display());
}

#[derive(Clone)]
struct WatcherCtx {
    roots: Arc<Roots>,
    watcher: Arc<Mutex<Option<RecommendedWatcher>>>,
    sender: Sender<TaskResult>,
}

fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) {
    match event {
        DebouncedEvent::NoticeWrite(_)
        | DebouncedEvent::NoticeRemove(_)
        | DebouncedEvent::Chmod(_) => {
            // ignore
        }
        DebouncedEvent::Rescan => {
            // TODO rescan all roots
        }
        DebouncedEvent::Create(path) => {
            sender.send((path, ChangeKind::Create)).unwrap();
        }
        DebouncedEvent::Write(path) => {
            sender.send((path, ChangeKind::Write)).unwrap();
        }
        DebouncedEvent::Remove(path) => {
            sender.send((path, ChangeKind::Remove)).unwrap();
        }
        DebouncedEvent::Rename(src, dst) => {
            sender.send((src, ChangeKind::Remove)).unwrap();
            sender.send((dst, ChangeKind::Create)).unwrap();
        }
        DebouncedEvent::Error(err, path) => {
            // TODO should we reload the file contents?
            log::warn!("watcher error \"{}\", {:?}", err, path);
        }
    }
}

impl WatcherCtx {
    fn handle_change(&self, path: PathBuf, kind: ChangeKind) -> Result<()> {
        let (root, rel_path) = match self.roots.find(&path) {
            None => return Ok(()),
            Some(it) => it,
        };
        let config = &self.roots[root];
        match kind {
            ChangeKind::Create => {
                let mut paths = Vec::new();
                if path.is_dir() {
                    let mut guard = self.watcher.lock();
                    paths.extend(watch_recursive(guard.as_mut(), &path, &config));
                } else {
                    paths.push(rel_path);
                }
                paths
                    .into_iter()
                    .filter_map(|rel_path| {
                        let abs_path = rel_path.to_path(&config.root);
                        let text = read_to_string(&abs_path)?;
                        Some((rel_path, text))
                    })
                    .try_for_each(|(path, text)| {
                        self.sender
                            .send(TaskResult::AddSingleFile { root, path, text })
                    })?
            }
            ChangeKind::Write => {
                if let Some(text) = read_to_string(&path) {
                    self.sender.send(TaskResult::ChangeSingleFile {
                        root,
                        path: rel_path,
                        text,
                    })?;
                }
            }
            ChangeKind::Remove => self.sender.send(TaskResult::RemoveSingleFile {
                root,
                path: rel_path,
            })?,
        }
        Ok(())
    }
}

fn watch_recursive(
    mut watcher: Option<&mut RecommendedWatcher>,
    dir: &Path,
    config: &RootConfig,
) -> Vec<RelativePathBuf> {
    let mut files = Vec::new();
    for entry in WalkDir::new(dir)
        .into_iter()
        .filter_entry(|it| config.contains(it.path()).is_some())
        .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok())
    {
        if entry.file_type().is_dir() {
            if let Some(watcher) = &mut watcher {
                watch_one(watcher, entry.path());
            }
        } else {
            let path = config.contains(entry.path()).unwrap();
            files.push(path.to_owned());
        }
    }
    files
}

fn watch_one(watcher: &mut RecommendedWatcher, dir: &Path) {
    match watcher.watch(dir, RecursiveMode::NonRecursive) {
        Ok(()) => log::debug!("watching \"{}\"", dir.display()),
        Err(e) => log::warn!("could not watch \"{}\": {}", dir.display(), e),
    }
}

fn read_to_string(path: &Path) -> Option<String> {
    fs::read_to_string(&path)
        .map_err(|e| log::warn!("failed to read file {}", e))
        .ok()
}
<commit_msg>remove mutexes<commit_after>use std::{
    fs,
    thread,
    path::{Path, PathBuf},
    sync::{mpsc, Arc},
    time::Duration,
};
use crossbeam_channel::{Receiver, Sender, unbounded, RecvError, select};
use relative_path::RelativePathBuf;
use thread_worker::WorkerHandle;
use walkdir::WalkDir;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher as _Watcher};

use crate::{RootConfig, Roots, VfsRoot};

type Result<T> = std::result::Result<T, crossbeam_channel::SendError<TaskResult>>;

pub(crate) enum Task {
    AddRoot {
        root: VfsRoot,
        config: Arc<RootConfig>,
    },
}

#[derive(Debug)]
pub enum TaskResult {
    BulkLoadRoot {
        root: VfsRoot,
        files: Vec<(RelativePathBuf, String)>,
    },
    AddSingleFile {
        root: VfsRoot,
        path: RelativePathBuf,
        text: String,
    },
    ChangeSingleFile {
        root: VfsRoot,
        path: RelativePathBuf,
        text: String,
    },
    RemoveSingleFile {
        root: VfsRoot,
        path: RelativePathBuf,
    },
}

#[derive(Debug)]
enum ChangeKind {
    Create,
    Write,
    Remove,
}

const WATCHER_DELAY: Duration = Duration::from_millis(250);

pub(crate) struct Worker {
    worker: thread_worker::Worker<Task, TaskResult>,
    worker_handle: WorkerHandle,
}

impl Worker {
    pub(crate) fn start(roots: Arc<Roots>) -> Worker {
        // This is a pretty elaborate setup of threads & channels! It is
        // explained by the following concerns:

        //    * we need to burn a thread translating from notify's mpsc to
        //      crossbeam_channel.
        //    * we want to read all files from a single thread, to gurantee that
        //      we always get fresher versions and never go back in time.
        //    * we want to tear down everything neatly during shutdown.
        let (worker, worker_handle) = thread_worker::spawn(
            "vfs",
            128,
            // This are the channels we use to communicate with outside world.
            // If `input_receiver` is closed we need to tear ourselves down.
            // `output_sender` should not be closed unless the parent died.
            move |input_receiver, output_sender| {
                // These are `std` channels notify will send events to
                let (notify_sender, notify_receiver) = mpsc::channel();
                // These are the corresponding crossbeam channels
                let (watcher_sender, watcher_receiver) = unbounded();
                let watcher = notify::watcher(notify_sender, WATCHER_DELAY)
                    .map_err(|e| log::error!("failed to spawn notify {}", e))
                    .ok();
                let mut ctx = WatcherCtx {
                    roots,
                    watcher,
                    sender: output_sender,
                };
                let thread = thread::spawn(move || {
                    let _ = notify_receiver
                        .into_iter()
                        // forward relevant events only
                        .for_each(|event| convert_notify_event(event, &watcher_sender));
                });

                loop {
                    select! {
                        // Received request from the caller. If this channel is
                        // closed, we should shutdown everything.
                        recv(input_receiver) -> t => match t {
                            Err(RecvError) => break,
                            Ok(Task::AddRoot { root, config }) => watch_root(&mut ctx, root, Arc::clone(&config)),
                        },
                        // Watcher send us changes. If **this** channel is
                        // closed, the watcher has died, which indicates a bug
                        // -- escalate!
                        recv(watcher_receiver) -> event => match event {
                            Err(RecvError) => panic!("watcher is dead"),
                            Ok((path, change)) => WatcherCtx::handle_change(&mut ctx, path, change).unwrap(),
                        },
                    }
                }
                drop(ctx.watcher.take());
                drop(ctx);
                let res2 = thread.join();
                match &res2 {
                    Ok(()) => log::info!("... Watcher terminated with ok"),
                    Err(_) => log::error!("... Watcher terminated with err"),
                }
                res2.unwrap();
            },
        );
        Worker {
            worker,
            worker_handle,
        }
    }

    pub(crate) fn sender(&self) -> &Sender<Task> {
        &self.worker.inp
    }

    pub(crate) fn receiver(&self) -> &Receiver<TaskResult> {
        &self.worker.out
    }

    pub(crate) fn shutdown(self) -> thread::Result<()> {
        let _ = self.worker.shutdown();
        self.worker_handle.shutdown()
    }
}

fn watch_root(woker: &mut WatcherCtx, root: VfsRoot, config: Arc<RootConfig>) {
    log::debug!("loading {} ...", config.root.as_path().display());
    let files = watch_recursive(woker.watcher.as_mut(), config.root.as_path(), &*config)
        .into_iter()
        .filter_map(|path| {
            let abs_path = path.to_path(&config.root);
            let text = read_to_string(&abs_path)?;
            Some((path, text))
        })
        .collect();
    woker
        .sender
        .send(TaskResult::BulkLoadRoot { root, files })
        .unwrap();
    log::debug!("... loaded {}", config.root.as_path().display());
}

struct WatcherCtx {
    roots: Arc<Roots>,
    watcher: Option<RecommendedWatcher>,
    sender: Sender<TaskResult>,
}

fn convert_notify_event(event: DebouncedEvent, sender: &Sender<(PathBuf, ChangeKind)>) {
    match event {
        DebouncedEvent::NoticeWrite(_)
        | DebouncedEvent::NoticeRemove(_)
        | DebouncedEvent::Chmod(_) => {
            // ignore
        }
        DebouncedEvent::Rescan => {
            // TODO rescan all roots
        }
        DebouncedEvent::Create(path) => {
            sender.send((path, ChangeKind::Create)).unwrap();
        }
        DebouncedEvent::Write(path) => {
            sender.send((path, ChangeKind::Write)).unwrap();
        }
        DebouncedEvent::Remove(path) => {
            sender.send((path, ChangeKind::Remove)).unwrap();
        }
        DebouncedEvent::Rename(src, dst) => {
            sender.send((src, ChangeKind::Remove)).unwrap();
            sender.send((dst, ChangeKind::Create)).unwrap();
        }
        DebouncedEvent::Error(err, path) => {
            // TODO should we reload the file contents?
            log::warn!("watcher error \"{}\", {:?}", err, path);
        }
    }
}

impl WatcherCtx {
    fn handle_change(&mut self, path: PathBuf, kind: ChangeKind) -> Result<()> {
        let (root, rel_path) = match self.roots.find(&path) {
            None => return Ok(()),
            Some(it) => it,
        };
        let config = &self.roots[root];
        match kind {
            ChangeKind::Create => {
                let mut paths = Vec::new();
                if path.is_dir() {
                    paths.extend(watch_recursive(self.watcher.as_mut(), &path, &config));
                } else {
                    paths.push(rel_path);
                }
                paths
                    .into_iter()
                    .filter_map(|rel_path| {
                        let abs_path = rel_path.to_path(&config.root);
                        let text = read_to_string(&abs_path)?;
                        Some((rel_path, text))
                    })
                    .try_for_each(|(path, text)| {
                        self.sender
                            .send(TaskResult::AddSingleFile { root, path, text })
                    })?
            }
            ChangeKind::Write => {
                if let Some(text) = read_to_string(&path) {
                    self.sender.send(TaskResult::ChangeSingleFile {
                        root,
                        path: rel_path,
                        text,
                    })?;
                }
            }
            ChangeKind::Remove => self.sender.send(TaskResult::RemoveSingleFile {
                root,
                path: rel_path,
            })?,
        }
        Ok(())
    }
}

fn watch_recursive(
    mut watcher: Option<&mut RecommendedWatcher>,
    dir: &Path,
    config: &RootConfig,
) -> Vec<RelativePathBuf> {
    let mut files = Vec::new();
    for entry in WalkDir::new(dir)
        .into_iter()
        .filter_entry(|it| config.contains(it.path()).is_some())
        .filter_map(|it| it.map_err(|e| log::warn!("watcher error: {}", e)).ok())
    {
        if entry.file_type().is_dir() {
            if let Some(watcher) = &mut watcher {
                watch_one(watcher, entry.path());
            }
        } else {
            let path = config.contains(entry.path()).unwrap();
            files.push(path.to_owned());
        }
    }
    files
}

fn watch_one(watcher: &mut RecommendedWatcher, dir: &Path) {
    match watcher.watch(dir, RecursiveMode::NonRecursive) {
        Ok(()) => log::debug!("watching \"{}\"", dir.display()),
        Err(e) => log::warn!("could not watch \"{}\": {}", dir.display(), e),
    }
}

fn read_to_string(path: &Path) -> Option<String> {
    fs::read_to_string(&path)
        .map_err(|e| log::warn!("failed to read file {}", e))
        .ok()
}
<|endoftext|>


<commit_before>use std::collections::RingBuf;

use gc_object::GCObject;
use line_allocator::LineAllocator;
use stack;

pub struct RCCollector {
    old_root_buffer: RingBuf<*mut GCObject>,
    decrement_buffer: RingBuf<*mut GCObject>,
    modified_buffer: RingBuf<*mut GCObject>,
}

impl RCCollector {
    pub fn new() -> RCCollector {
        return RCCollector {
            old_root_buffer: RingBuf::new(),
            decrement_buffer: RingBuf::new(),
            modified_buffer: RingBuf::new(),
        };
    }

    pub fn collect(&mut self, line_allocator: &mut LineAllocator) {
        self.process_old_roots();
        self.process_current_roots(line_allocator);
        self.process_mod_buffer(line_allocator);
        self.process_decrement_buffer(line_allocator);
    }

    pub fn write_barrier(&mut self, object: *mut GCObject) {
        debug!("Write barrier on object {}", object);
        self.modified(object);
        for child in unsafe{ (*object).children() }.into_iter() {
            self.decrement(child);
        }
        unsafe{ (*object).set_logged(true); }
    }

    fn modified(&mut self, object: *mut GCObject) {
        debug!("Push object{} into mod buffer", object);
        self.modified_buffer.push_back(object);
    }

    fn decrement(&mut self, object: *mut GCObject) {
        debug!("Push object {} into dec buffer", object);
        self.decrement_buffer.push_back(object);
    }

    fn increment(&mut self, line_allocator: &mut LineAllocator, object: *mut GCObject) {
        debug!("Increment object {}", object);
        if unsafe{ (*object).increment() } {
            line_allocator.increment_lines(object);
            self.modified(object);
        }
    }

    fn process_old_roots(&mut self) {
        debug!("Process old roots (size {})", self.old_root_buffer.len());
        self.decrement_buffer.extend(self.old_root_buffer.drain());
    }

    fn process_current_roots(&mut self, line_allocator: &mut LineAllocator) {
        let roots = stack::enumerate_roots(line_allocator);
        debug!("Process current roots (size {})", roots.len());
        for root in roots.into_iter() {
            debug!("Process root object: {}", root);
            self.increment(line_allocator, root);
            self.old_root_buffer.push_back(root);
        }
    }

    fn process_mod_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process mod buffer (size {})", self.modified_buffer.len());
        loop {
            match self.modified_buffer.pop_front() {
                None => break,
                Some(object) => {
                    debug!("Process object {} in mod buffer", object);
                    unsafe { (*object).set_logged(false); }
                    for child in unsafe{ (*object).children() }.into_iter() {
                        self.increment(line_allocator, child);
                    }
                }
            }
        }
    }

    fn process_decrement_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process dec buffer (size {})", self.decrement_buffer.len());
        loop {
            match self.decrement_buffer.pop_front() {
                None => break,
                Some(object) => {
                    debug!("Process object {} in dec buffer", object);
                    if unsafe{ (*object).decrement() } {
                        line_allocator.decrement_lines(object);
                        for child in unsafe{ (*object).children() }.into_iter() {
                            self.decrement(child);
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Fix: Remove dead object from object_map in RC-Collection<commit_after>use std::collections::RingBuf;

use gc_object::GCObject;
use line_allocator::LineAllocator;
use stack;

pub struct RCCollector {
    old_root_buffer: RingBuf<*mut GCObject>,
    decrement_buffer: RingBuf<*mut GCObject>,
    modified_buffer: RingBuf<*mut GCObject>,
}

impl RCCollector {
    pub fn new() -> RCCollector {
        return RCCollector {
            old_root_buffer: RingBuf::new(),
            decrement_buffer: RingBuf::new(),
            modified_buffer: RingBuf::new(),
        };
    }

    pub fn collect(&mut self, line_allocator: &mut LineAllocator) {
        self.process_old_roots();
        self.process_current_roots(line_allocator);
        self.process_mod_buffer(line_allocator);
        self.process_decrement_buffer(line_allocator);
    }

    pub fn write_barrier(&mut self, object: *mut GCObject) {
        debug!("Write barrier on object {}", object);
        self.modified(object);
        for child in unsafe{ (*object).children() }.into_iter() {
            self.decrement(child);
        }
        unsafe{ (*object).set_logged(true); }
    }

    fn modified(&mut self, object: *mut GCObject) {
        debug!("Push object{} into mod buffer", object);
        self.modified_buffer.push_back(object);
    }

    fn decrement(&mut self, object: *mut GCObject) {
        debug!("Push object {} into dec buffer", object);
        self.decrement_buffer.push_back(object);
    }

    fn increment(&mut self, line_allocator: &mut LineAllocator, object: *mut GCObject) {
        debug!("Increment object {}", object);
        if unsafe{ (*object).increment() } {
            line_allocator.increment_lines(object);
            self.modified(object);
        }
    }

    fn process_old_roots(&mut self) {
        debug!("Process old roots (size {})", self.old_root_buffer.len());
        self.decrement_buffer.extend(self.old_root_buffer.drain());
    }

    fn process_current_roots(&mut self, line_allocator: &mut LineAllocator) {
        let roots = stack::enumerate_roots(line_allocator);
        debug!("Process current roots (size {})", roots.len());
        for root in roots.into_iter() {
            debug!("Process root object: {}", root);
            self.increment(line_allocator, root);
            self.old_root_buffer.push_back(root);
        }
    }

    fn process_mod_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process mod buffer (size {})", self.modified_buffer.len());
        loop {
            match self.modified_buffer.pop_front() {
                None => break,
                Some(object) => {
                    debug!("Process object {} in mod buffer", object);
                    unsafe { (*object).set_logged(false); }
                    for child in unsafe{ (*object).children() }.into_iter() {
                        self.increment(line_allocator, child);
                    }
                }
            }
        }
    }

    fn process_decrement_buffer(&mut self, line_allocator: &mut LineAllocator) {
        debug!("Process dec buffer (size {})", self.decrement_buffer.len());
        loop {
            match self.decrement_buffer.pop_front() {
                None => break,
                Some(object) => {
                    debug!("Process object {} in dec buffer", object);
                    if unsafe{ (*object).decrement() } {
                        line_allocator.unset_gc_object(object);
                        line_allocator.decrement_lines(object);
                        for child in unsafe{ (*object).children() }.into_iter() {
                            self.decrement(child);
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Batches are structures containing all the data required for the draw call,
//! except for the target frame. Here we define the `Batch` trait as well as
//! `RefBatch` and `OwnedBatch` implementations.

use std::fmt;
use std::num::from_uint;
use std::cmp::Ordering;
use std::marker::PhantomData;
use device::{Resources, PrimitiveType, ProgramHandle};
use device::shade::ProgramInfo;
use render::mesh;
use render::mesh::ToSlice;
use shade::{ParameterError, ShaderParam};
use render::state::DrawState;

/// An error occurring at batch creation
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    /// Error connecting mesh attributes
    Mesh(mesh::Error),
    /// Error connecting shader parameters
    Parameters(ParameterError),
    /// Error context is full
    ContextFull,
}

/// Return type for `Batch::get_data()``
pub type BatchData<'a, R: Resources> = (&'a mesh::Mesh<R>, mesh::AttributeIter,
                                        &'a mesh::Slice<R>, &'a DrawState);

/// Abstract batch trait
#[allow(missing_docs)]
pub trait Batch {
    type Resources: Resources;
    /// Possible errors occurring at batch access
    type Error: fmt::Debug;
    /// Obtain information about the mesh, program, and state
    fn get_data(&self) -> Result<BatchData<Self::Resources>, Self::Error>;
    /// Fill shader parameter values
    fn fill_params(&self, ::shade::ParamValues<Self::Resources>)
                   -> Result<&ProgramHandle<Self::Resources>, Self::Error>;
}

impl<'a, T: ShaderParam> Batch for (&'a mesh::Mesh<T::Resources>, mesh::Slice<T::Resources>,
                                    &'a ProgramHandle<T::Resources>, &'a T, &'a DrawState) {
    type Resources = T::Resources;
    type Error = Error;

    fn get_data(&self) -> Result<BatchData<T::Resources>, Error> {
        let (mesh, ref slice, program, _, state) = *self;
        match mesh::Link::new(mesh, program.get_info()) {
            Ok(link) => Ok((mesh, link.to_iter(), &slice, state)),
            Err(e) => Err(Error::Mesh(e)),
        }
    }

    fn fill_params(&self, values: ::shade::ParamValues<T::Resources>)
                   -> Result<&ProgramHandle<T::Resources>, Error> {
        let (_, _, program, params, _) = *self;
        match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(link) => {
                params.fill_params(&link, values);
                Ok(program)
            },
            Err(e) => return Err(Error::Parameters(e)),
        }
    }
}

/// Owned batch - self-contained, but has heap-allocated data
pub struct OwnedBatch<T: ShaderParam> {
    mesh: mesh::Mesh<T::Resources>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<T::Resources>,
    /// Parameter data.
    pub param: T,
    program: ProgramHandle<T::Resources>,
    param_link: T::Link,
    /// Draw state
    pub state: DrawState,
}

impl<T: ShaderParam> OwnedBatch<T> {
    /// Create a new owned batch
    pub fn new(mesh: mesh::Mesh<T::Resources>, program: ProgramHandle<T::Resources>, param: T)
           -> Result<OwnedBatch<T>, Error> {
        let slice = mesh.to_slice(PrimitiveType::TriangleList);
        let mesh_link = match mesh::Link::new(&mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Mesh(e)),
        };
        let param_link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Parameters(e)),
        };
        Ok(OwnedBatch {
            mesh: mesh,
            mesh_link: mesh_link,
            slice: slice,
            program: program,
            param: param,
            param_link: param_link,
            state: DrawState::new(),
        })
    }
}

impl<T: ShaderParam> Batch for OwnedBatch<T> {
    type Resources = T::Resources;
    type Error = ();

    fn get_data(&self) -> Result<BatchData<T::Resources>, ()> {
        Ok((&self.mesh, self.mesh_link.to_iter(), &self.slice, &self.state))
    }

    fn fill_params(&self, values: ::shade::ParamValues<T::Resources>)
                   -> Result<&ProgramHandle<T::Resources>, ()> {
        self.param.fill_params(&self.param_link, values);
        Ok(&self.program)
    }
}

type Index = u16;

//#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Id<T>(Index, PhantomData<T>);

impl<T> Copy for Id<T> {}

impl<T> Id<T> {
    fn unwrap(&self) -> Index {
        let Id(i, _) = *self;
        i
    }
}

impl<T> fmt::Debug for Id<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Id(i, _) = *self;
        write!(f, "Id({})", i)
    }
}

impl<T> PartialEq for Id<T> {
    fn eq(&self, other: &Id<T>) -> bool {
        self.unwrap() == other.unwrap()
    }
}

impl<T> Eq for Id<T> {}

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

impl<T> Ord for Id<T> {
    fn cmp(&self, other: &Id<T>) -> Ordering {
        self.unwrap().cmp(&other.unwrap())
    }
}

struct Array<T> {
    data: Vec<T>,
    //generation: u16,
}

/// Error accessing outside of the array
#[derive(Debug)]
pub struct OutOfBounds(pub usize);

impl<T> Array<T> {
    fn new() -> Array<T> {
        Array {
            data: Vec::new(),
            //generation: 0,
        }
    }

    fn get(&self, id: Id<T>) -> Result<&T, OutOfBounds> {
        let Id(i, _) = id;
        if (i as usize) < self.data.len() {
            Ok(&self.data[i as usize])
        }else {
            Err(OutOfBounds(i as usize))
        }
    }
}

impl<T: Clone + PartialEq> Array<T> {
    fn find_or_insert(&mut self, value: &T) -> Option<Id<T>> {
        match self.data.iter().position(|v| v == value) {
            Some(i) => from_uint::<Index>(i).map(|id| Id(id, PhantomData)),
            None => {
                from_uint::<Index>(self.data.len()).map(|id| {
                    self.data.push(value.clone());
                    Id(id, PhantomData)
                })
            },
        }
    }
}

/// Ref batch - copyable and smaller, but depends on the `Context`.
/// It has references to the resources (mesh, program, state), that are held
/// by the context that created the batch, so these have to be used together.
pub struct RefBatch<T: ShaderParam> {
    mesh_id: Id<mesh::Mesh<T::Resources>>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<T::Resources>,
    program_id: Id<ProgramHandle<T::Resources>>,
    param_link: T::Link,
    state_id: Id<DrawState>,
}

impl<T: ShaderParam> Copy for RefBatch<T> where T::Link: Copy {}

impl<T: ShaderParam> fmt::Debug for RefBatch<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RefBatch(mesh: {:?}, slice: {:?}, program: {:?}, state: {:?})",
            self.mesh_id, self.slice, self.program_id, self.state_id)
    }
}

impl<T: ShaderParam> PartialEq for RefBatch<T> {
    fn eq(&self, other: &RefBatch<T>) -> bool {
        self.program_id == other.program_id &&
        self.state_id == other.state_id &&
        self.mesh_id == other.mesh_id
    }
}

impl<T: ShaderParam> Eq for RefBatch<T> {}

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

impl<T: ShaderParam> Ord for RefBatch<T> {
    fn cmp(&self, other: &RefBatch<T>) -> Ordering {
        (&self.program_id, &self.state_id, &self.mesh_id).cmp(
        &(&other.program_id, &other.state_id, &other.mesh_id))
    }
}

impl<T: ShaderParam> RefBatch<T> {
    /// Compare meshes by Id
    pub fn cmp_mesh(&self, other: &RefBatch<T>) -> Ordering {
        self.mesh_id.cmp(&other.mesh_id)
    }
    /// Compare programs by Id
    pub fn cmp_program(&self, other: &RefBatch<T>) -> Ordering {
        self.program_id.cmp(&other.program_id)
    }
    /// Compare draw states by Id
    pub fn cmp_state(&self, other: &RefBatch<T>) -> Ordering {
        self.state_id.cmp(&other.state_id)
    }
}

/// Factory of ref batches, required to always be used with them.
pub struct Context<R: Resources> {
    meshes: Array<mesh::Mesh<R>>,
    programs: Array<ProgramHandle<R>>,
    states: Array<DrawState>,
}

/// A RefBatch completed by the shader parameters and a context
/// Implements `Batch` thus can be drawn.
/// It is meant to be a struct, but we have lots of lifetime issues
/// with associated resources, binding which looks nasty (#614)
pub type RefBatchFull<'a, T: ShaderParam> = (
    &'a RefBatch<T>,
    &'a T,
    &'a Context<T::Resources>
);

impl<R: Resources> Context<R> {
    /// Create a new empty `Context`
    pub fn new() -> Context<R> {
        Context {
            meshes: Array::new(),
            programs: Array::new(),
            states: Array::new(),
        }
    }
}

impl<R: Resources> Context<R> {
    /// Produce a new ref batch
    pub fn make_batch<T: ShaderParam<Resources = R>>(&mut self,
                      program: &ProgramHandle<R>,
                      mesh: &mesh::Mesh<R>,
                      slice: mesh::Slice<R>,
                      state: &DrawState)
                      -> Result<RefBatch<T>, Error> {
        let mesh_link = match mesh::Link::new(mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Mesh(e)),
        };
        let link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Parameters(e))
        };
        let mesh_id = match self.meshes.find_or_insert(mesh) {
            Some(id) => id,
            None => return Err(Error::ContextFull),
        };
        let program_id = match self.programs.find_or_insert(program) {
            Some(id) => id,
            None => return Err(Error::ContextFull),
        };
        let state_id = match self.states.find_or_insert(state) {
            Some(id) => id,
            None => return Err(Error::ContextFull),
        };

        Ok(RefBatch {
            mesh_id: mesh_id,
            mesh_link: mesh_link,
            slice: slice,
            program_id: program_id,
            param_link: link,
            state_id: state_id,
        })
    }

    /// Complete a RefBatch temporarily by turning it into RefBatchFull
    pub fn bind<'a, T: ShaderParam<Resources = R> + 'a>(&'a self,
                 batch: &'a RefBatch<T>, data: &'a T) -> RefBatchFull<'a, T> {
        (batch, data, self)
    }
}

impl<'a, T: ShaderParam + 'a> Batch for RefBatchFull<'a, T> {
    type Resources = T::Resources;
    type Error = OutOfBounds;

    fn get_data(&self) -> Result<BatchData<T::Resources>, OutOfBounds> {
        let (b, _, ctx) = *self;
        Ok((try!(ctx.meshes.get(b.mesh_id)),
            b.mesh_link.to_iter(),
            &b.slice,
            try!(ctx.states.get(b.state_id))
        ))
    }

    fn fill_params(&self, values: ::shade::ParamValues<T::Resources>)
                   -> Result<&ProgramHandle<T::Resources>, OutOfBounds> {
        let (b, data, ctx) = *self;
        data.fill_params(&b.param_link, values);
        ctx.programs.get(b.program_id)
    }
}
<commit_msg>Added ImplicitBatch<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Batches are structures containing all the data required for the draw call,
//! except for the target frame. Here we define the `Batch` trait as well as
//! `RefBatch` and `OwnedBatch` implementations.

use std::fmt;
use std::num::from_uint;
use std::cmp::Ordering;
use std::marker::PhantomData;
use device::{Resources, PrimitiveType, ProgramHandle};
use render::mesh;
use render::mesh::ToSlice;
use shade::{ParameterError, ShaderParam};
use render::state::DrawState;

/// An error occurring at batch creation
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    /// Error connecting mesh attributes
    Mesh(mesh::Error),
    /// Error connecting shader parameters
    Parameters(ParameterError),
    /// Error context is full
    ContextFull,
}

/// Return type for `Batch::get_data()``
pub type BatchData<'a, R: Resources> = (&'a mesh::Mesh<R>, mesh::AttributeIter,
                                        &'a mesh::Slice<R>, &'a DrawState);

/// Abstract batch trait
#[allow(missing_docs)]
pub trait Batch {
    type Resources: Resources;
    /// Possible errors occurring at batch access
    type Error: fmt::Debug;
    /// Obtain information about the mesh, program, and state
    fn get_data(&self) -> Result<BatchData<Self::Resources>, Self::Error>;
    /// Fill shader parameter values
    fn fill_params(&self, ::shade::ParamValues<Self::Resources>)
                   -> Result<&ProgramHandle<Self::Resources>, Self::Error>;
}

/// A batch that is constructed on the fly when rendering.
/// Meant to be a struct, blocked by #614
pub type ImplicitBatch<'a, T: ShaderParam> = (
    &'a mesh::Mesh<T::Resources>,
    mesh::Slice<T::Resources>,
    &'a ProgramHandle<T::Resources>,
    &'a T,
    &'a DrawState
);

impl DrawState {
    /// Create an implicit batch
    pub fn bind<'a, T: ShaderParam>(&'a self,
                 mesh: &'a mesh::Mesh<T::Resources>,
                 slice: mesh::Slice<T::Resources>,
                 program: &'a ProgramHandle<T::Resources>,
                 data: &'a T) -> ImplicitBatch<'a, T> {
        (mesh, slice, program, data, self)
    }
}

impl<'a, T: ShaderParam> Batch for ImplicitBatch<'a, T> {
    type Resources = T::Resources;
    type Error = Error;

    fn get_data(&self) -> Result<BatchData<T::Resources>, Error> {
        let (mesh, ref slice, program, _, state) = *self;
        match mesh::Link::new(mesh, program.get_info()) {
            Ok(link) => Ok((mesh, link.to_iter(), &slice, state)),
            Err(e) => Err(Error::Mesh(e)),
        }
    }

    fn fill_params(&self, values: ::shade::ParamValues<T::Resources>)
                   -> Result<&ProgramHandle<T::Resources>, Error> {
        let (_, _, program, params, _) = *self;
        match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(link) => {
                params.fill_params(&link, values);
                Ok(program)
            },
            Err(e) => return Err(Error::Parameters(e)),
        }
    }
}

/// Owned batch - self-contained, but has heap-allocated data
pub struct OwnedBatch<T: ShaderParam> {
    mesh: mesh::Mesh<T::Resources>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<T::Resources>,
    /// Parameter data.
    pub param: T,
    program: ProgramHandle<T::Resources>,
    param_link: T::Link,
    /// Draw state
    pub state: DrawState,
}

impl<T: ShaderParam> OwnedBatch<T> {
    /// Create a new owned batch
    pub fn new(mesh: mesh::Mesh<T::Resources>, program: ProgramHandle<T::Resources>, param: T)
           -> Result<OwnedBatch<T>, Error> {
        let slice = mesh.to_slice(PrimitiveType::TriangleList);
        let mesh_link = match mesh::Link::new(&mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Mesh(e)),
        };
        let param_link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Parameters(e)),
        };
        Ok(OwnedBatch {
            mesh: mesh,
            mesh_link: mesh_link,
            slice: slice,
            program: program,
            param: param,
            param_link: param_link,
            state: DrawState::new(),
        })
    }
}

impl<T: ShaderParam> Batch for OwnedBatch<T> {
    type Resources = T::Resources;
    type Error = ();

    fn get_data(&self) -> Result<BatchData<T::Resources>, ()> {
        Ok((&self.mesh, self.mesh_link.to_iter(), &self.slice, &self.state))
    }

    fn fill_params(&self, values: ::shade::ParamValues<T::Resources>)
                   -> Result<&ProgramHandle<T::Resources>, ()> {
        self.param.fill_params(&self.param_link, values);
        Ok(&self.program)
    }
}

type Index = u16;

//#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Id<T>(Index, PhantomData<T>);

impl<T> Copy for Id<T> {}

impl<T> Id<T> {
    fn unwrap(&self) -> Index {
        let Id(i, _) = *self;
        i
    }
}

impl<T> fmt::Debug for Id<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Id(i, _) = *self;
        write!(f, "Id({})", i)
    }
}

impl<T> PartialEq for Id<T> {
    fn eq(&self, other: &Id<T>) -> bool {
        self.unwrap() == other.unwrap()
    }
}

impl<T> Eq for Id<T> {}

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

impl<T> Ord for Id<T> {
    fn cmp(&self, other: &Id<T>) -> Ordering {
        self.unwrap().cmp(&other.unwrap())
    }
}

struct Array<T> {
    data: Vec<T>,
    //generation: u16,
}

/// Error accessing outside of the array
#[derive(Debug)]
pub struct OutOfBounds(pub usize);

impl<T> Array<T> {
    fn new() -> Array<T> {
        Array {
            data: Vec::new(),
            //generation: 0,
        }
    }

    fn get(&self, id: Id<T>) -> Result<&T, OutOfBounds> {
        let Id(i, _) = id;
        if (i as usize) < self.data.len() {
            Ok(&self.data[i as usize])
        }else {
            Err(OutOfBounds(i as usize))
        }
    }
}

impl<T: Clone + PartialEq> Array<T> {
    fn find_or_insert(&mut self, value: &T) -> Option<Id<T>> {
        match self.data.iter().position(|v| v == value) {
            Some(i) => from_uint::<Index>(i).map(|id| Id(id, PhantomData)),
            None => {
                from_uint::<Index>(self.data.len()).map(|id| {
                    self.data.push(value.clone());
                    Id(id, PhantomData)
                })
            },
        }
    }
}

/// Ref batch - copyable and smaller, but depends on the `Context`.
/// It has references to the resources (mesh, program, state), that are held
/// by the context that created the batch, so these have to be used together.
pub struct RefBatch<T: ShaderParam> {
    mesh_id: Id<mesh::Mesh<T::Resources>>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<T::Resources>,
    program_id: Id<ProgramHandle<T::Resources>>,
    param_link: T::Link,
    state_id: Id<DrawState>,
}

impl<T: ShaderParam> Copy for RefBatch<T> where T::Link: Copy {}

impl<T: ShaderParam> fmt::Debug for RefBatch<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RefBatch(mesh: {:?}, slice: {:?}, program: {:?}, state: {:?})",
            self.mesh_id, self.slice, self.program_id, self.state_id)
    }
}

impl<T: ShaderParam> PartialEq for RefBatch<T> {
    fn eq(&self, other: &RefBatch<T>) -> bool {
        self.program_id == other.program_id &&
        self.state_id == other.state_id &&
        self.mesh_id == other.mesh_id
    }
}

impl<T: ShaderParam> Eq for RefBatch<T> {}

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

impl<T: ShaderParam> Ord for RefBatch<T> {
    fn cmp(&self, other: &RefBatch<T>) -> Ordering {
        (&self.program_id, &self.state_id, &self.mesh_id).cmp(
        &(&other.program_id, &other.state_id, &other.mesh_id))
    }
}

impl<T: ShaderParam> RefBatch<T> {
    /// Compare meshes by Id
    pub fn cmp_mesh(&self, other: &RefBatch<T>) -> Ordering {
        self.mesh_id.cmp(&other.mesh_id)
    }
    /// Compare programs by Id
    pub fn cmp_program(&self, other: &RefBatch<T>) -> Ordering {
        self.program_id.cmp(&other.program_id)
    }
    /// Compare draw states by Id
    pub fn cmp_state(&self, other: &RefBatch<T>) -> Ordering {
        self.state_id.cmp(&other.state_id)
    }
}

/// Factory of ref batches, required to always be used with them.
pub struct Context<R: Resources> {
    meshes: Array<mesh::Mesh<R>>,
    programs: Array<ProgramHandle<R>>,
    states: Array<DrawState>,
}

/// A RefBatch completed by the shader parameters and a context
/// Implements `Batch` thus can be drawn.
/// It is meant to be a struct, but we have lots of lifetime issues
/// with associated resources, binding which looks nasty (#614)
pub type RefBatchFull<'a, T: ShaderParam> = (
    &'a RefBatch<T>,
    &'a T,
    &'a Context<T::Resources>
);

impl<R: Resources> Context<R> {
    /// Create a new empty `Context`
    pub fn new() -> Context<R> {
        Context {
            meshes: Array::new(),
            programs: Array::new(),
            states: Array::new(),
        }
    }
}

impl<R: Resources> Context<R> {
    /// Produce a new ref batch
    pub fn make_batch<T: ShaderParam<Resources = R>>(&mut self,
                      program: &ProgramHandle<R>,
                      mesh: &mesh::Mesh<R>,
                      slice: mesh::Slice<R>,
                      state: &DrawState)
                      -> Result<RefBatch<T>, Error> {
        let mesh_link = match mesh::Link::new(mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Mesh(e)),
        };
        let link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(Error::Parameters(e))
        };
        let mesh_id = match self.meshes.find_or_insert(mesh) {
            Some(id) => id,
            None => return Err(Error::ContextFull),
        };
        let program_id = match self.programs.find_or_insert(program) {
            Some(id) => id,
            None => return Err(Error::ContextFull),
        };
        let state_id = match self.states.find_or_insert(state) {
            Some(id) => id,
            None => return Err(Error::ContextFull),
        };

        Ok(RefBatch {
            mesh_id: mesh_id,
            mesh_link: mesh_link,
            slice: slice,
            program_id: program_id,
            param_link: link,
            state_id: state_id,
        })
    }

    /// Complete a RefBatch temporarily by turning it into RefBatchFull
    pub fn bind<'a, T: ShaderParam<Resources = R> + 'a>(&'a self,
                 batch: &'a RefBatch<T>, data: &'a T) -> RefBatchFull<'a, T> {
        (batch, data, self)
    }
}

impl<'a, T: ShaderParam + 'a> Batch for RefBatchFull<'a, T> {
    type Resources = T::Resources;
    type Error = OutOfBounds;

    fn get_data(&self) -> Result<BatchData<T::Resources>, OutOfBounds> {
        let (b, _, ctx) = *self;
        Ok((try!(ctx.meshes.get(b.mesh_id)),
            b.mesh_link.to_iter(),
            &b.slice,
            try!(ctx.states.get(b.state_id))
        ))
    }

    fn fill_params(&self, values: ::shade::ParamValues<T::Resources>)
                   -> Result<&ProgramHandle<T::Resources>, OutOfBounds> {
        let (b, data, ctx) = *self;
        data.fill_params(&b.param_link, values);
        ctx.programs.get(b.program_id)
    }
}
<|endoftext|>


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

use diff;
use std::collections::VecDeque;
use std::io;
use term;
use utils::isatty;

#[derive(Debug, PartialEq)]
pub enum DiffLine {
    Context(String),
    Expected(String),
    Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
    pub line_number: u32,
    pub lines: Vec<DiffLine>,
}

impl Mismatch {
    fn new(line_number: u32) -> Mismatch {
        Mismatch {
            line_number: line_number,
            lines: Vec::new(),
        }
    }
}

// Produces a diff between the expected output and actual output of rustfmt.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
    let mut line_number = 1;
    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
    let mut lines_since_mismatch = context_size + 1;
    let mut results = Vec::new();
    let mut mismatch = Mismatch::new(0);

    for result in diff::lines(expected, actual) {
        match result {
            diff::Result::Left(str) => {
                if lines_since_mismatch >= context_size {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
                lines_since_mismatch = 0;
            }
            diff::Result::Right(str) => {
                if lines_since_mismatch >= context_size {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
                line_number += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Both(str, _) => {
                if context_queue.len() >= context_size {
                    let _ = context_queue.pop_front();
                }

                if lines_since_mismatch < context_size {
                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
                } else {
                    context_queue.push_back(str);
                }

                line_number += 1;
                lines_since_mismatch += 1;
            }
        }
    }

    results.push(mismatch);
    results.remove(0);

    results
}

pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    match term::stdout() {
        Some(ref t) if isatty() && t.supports_color() => {
            print_diff_fancy(diff, get_section_title, term::stdout().unwrap())
        }
        _ => print_diff_basic(diff, get_section_title),
    }
}

fn print_diff_fancy<F>(
    diff: Vec<Mismatch>,
    get_section_title: F,
    mut t: Box<term::Terminal<Output = io::Stdout>>,
) where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        writeln!(t, "{}", title).unwrap();

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    t.reset().unwrap();
                    writeln!(t, " {}⏎", str).unwrap();
                }
                DiffLine::Expected(ref str) => {
                    t.fg(term::color::GREEN).unwrap();
                    writeln!(t, "+{}⏎", str).unwrap();
                }
                DiffLine::Resulting(ref str) => {
                    t.fg(term::color::RED).unwrap();
                    writeln!(t, "-{}⏎", str).unwrap();
                }
            }
        }
        t.reset().unwrap();
    }
}

pub fn print_diff_basic<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        println!("{}", title);

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    println!(" {}⏎", str);
                }
                DiffLine::Expected(ref str) => {
                    println!("+{}⏎", str);
                }
                DiffLine::Resulting(ref str) => {
                    println!("-{}⏎", str);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{make_diff,Mismatch};
    use super::DiffLine::*;

    #[test]
    fn diff_simple() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest= "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(diff, vec![Mismatch { line_number: 2,
                                         lines: vec![
                                             Context("two".into()),
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                             Context("four".into()),
                                         ] }]);
    }

    #[test]
    fn diff_simple2() {
        let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
        let dest= "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(diff, vec![Mismatch { line_number: 2,
                                         lines: vec![
                                             Context("two".into()),
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                             Context("four".into()),
                                         ] },
                              Mismatch { line_number: 5,
                                         lines: vec![
                                             Resulting("five".into()),
                                             Expected("cinq".into()),
                                             Context("six".into()),
                                         ] }]);
    }

    #[test]
    fn diff_zerocontext() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest= "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 0);
        assert_eq!(diff, vec![Mismatch { line_number: 3,
                                         lines: vec![
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                         ] }]);
    }
}<commit_msg>Add a couple of special cases which fix the zero-context diff case.<commit_after>// Copyright 2017 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.

use diff;
use std::collections::VecDeque;
use std::io;
use term;
use utils::isatty;

#[derive(Debug, PartialEq)]
pub enum DiffLine {
    Context(String),
    Expected(String),
    Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
    pub line_number: u32,
    pub lines: Vec<DiffLine>,
}

impl Mismatch {
    fn new(line_number: u32) -> Mismatch {
        Mismatch {
            line_number: line_number,
            lines: Vec::new(),
        }
    }
}

// Produces a diff between the expected output and actual output of rustfmt.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
    let mut line_number = 1;
    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
    let mut lines_since_mismatch = context_size + 1;
    let mut results = Vec::new();
    let mut mismatch = Mismatch::new(0);

    for result in diff::lines(expected, actual) {
        match result {
            diff::Result::Left(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
                lines_since_mismatch = 0;
            }
            diff::Result::Right(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(line_number - context_queue.len() as u32);
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
                line_number += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Both(str, _) => {
                if context_queue.len() >= context_size {
                    let _ = context_queue.pop_front();
                }

                if lines_since_mismatch < context_size {
                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
                } else if context_size > 0 {
                    context_queue.push_back(str);
                }

                line_number += 1;
                lines_since_mismatch += 1;
            }
        }
    }

    results.push(mismatch);
    results.remove(0);

    results
}

pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    match term::stdout() {
        Some(ref t) if isatty() && t.supports_color() => {
            print_diff_fancy(diff, get_section_title, term::stdout().unwrap())
        }
        _ => print_diff_basic(diff, get_section_title),
    }
}

fn print_diff_fancy<F>(
    diff: Vec<Mismatch>,
    get_section_title: F,
    mut t: Box<term::Terminal<Output = io::Stdout>>,
) where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        writeln!(t, "{}", title).unwrap();

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    t.reset().unwrap();
                    writeln!(t, " {}⏎", str).unwrap();
                }
                DiffLine::Expected(ref str) => {
                    t.fg(term::color::GREEN).unwrap();
                    writeln!(t, "+{}⏎", str).unwrap();
                }
                DiffLine::Resulting(ref str) => {
                    t.fg(term::color::RED).unwrap();
                    writeln!(t, "-{}⏎", str).unwrap();
                }
            }
        }
        t.reset().unwrap();
    }
}

pub fn print_diff_basic<F>(diff: Vec<Mismatch>, get_section_title: F)
where
    F: Fn(u32) -> String,
{
    for mismatch in diff {
        let title = get_section_title(mismatch.line_number);
        println!("{}", title);

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    println!(" {}⏎", str);
                }
                DiffLine::Expected(ref str) => {
                    println!("+{}⏎", str);
                }
                DiffLine::Resulting(ref str) => {
                    println!("-{}⏎", str);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{make_diff,Mismatch};
    use super::DiffLine::*;

    #[test]
    fn diff_simple() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest= "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(diff, vec![Mismatch { line_number: 2,
                                         lines: vec![
                                             Context("two".into()),
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                             Context("four".into()),
                                         ] }]);
    }

    #[test]
    fn diff_simple2() {
        let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
        let dest= "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(diff, vec![Mismatch { line_number: 2,
                                         lines: vec![
                                             Context("two".into()),
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                             Context("four".into()),
                                         ] },
                              Mismatch { line_number: 5,
                                         lines: vec![
                                             Resulting("five".into()),
                                             Expected("cinq".into()),
                                             Context("six".into()),
                                         ] }]);
    }

    #[test]
    fn diff_zerocontext() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest= "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 0);
        assert_eq!(diff, vec![Mismatch { line_number: 3,
                                         lines: vec![
                                             Resulting("three".into()),
                                             Expected("trois".into()),
                                         ] }]);
    }
}<|endoftext|>


<commit_before>pub struct Smp {
    // TODO: emulator
    reg_pc: u16,
    reg_a: u8,
    reg_x: u8,
    reg_y: u8,
    reg_sp: u8,

    psw_c: bool,
    psw_z: bool,
    psw_h: bool,
    psw_p: bool,
    psw_v: bool,
    psw_n: bool,

    // TODO: Look up some more behavior for I and B. Can't seem to find much
    // but some instructions DO set them.
    psw_i: bool,
    psw_b: bool,

    cycle_count: i32
}
<commit_msg>filled out lots of smp stuff<commit_after>const RESET_VECTOR: u16 = 0xffc0;
const RESET_REG_SP: u8 = 0xef;

pub struct Smp {
    // TODO: emulator
    reg_pc: u16,
    reg_a: u8,
    reg_x: u8,
    reg_y: u8,
    reg_sp: u8,

    psw_c: bool,
    psw_z: bool,
    psw_h: bool,
    psw_p: bool,
    psw_v: bool,
    psw_n: bool,

    // TODO: Look up some more behavior for I and B. Can't seem to find much
    // but some instructions DO set them.
    psw_i: bool,
    psw_b: bool,

    cycle_count: i32
}

impl Smp {
    pub fn new() -> Smp {
        let mut ret = Smp {
            reg_pc: RESET_VECTOR,
            reg_a: 0,
            reg_x: 0,
            reg_y: 0,
            reg_sp: RESET_REG_SP,

            psw_c: false,
            psw_z: true,
            psw_h: false,
            psw_p: false,
            psw_v: false,
            psw_n: false,
            psw_i: false,
            psw_b: false,

            cycle_count: 0
        };
        ret.reset();
        ret
    }

    pub fn reset(&mut self) {
        self.reg_pc = RESET_VECTOR;
        self.reg_a = 0;
        self.reg_x = 0;
        self.reg_y = 0;
        self.reg_sp = RESET_REG_SP;
        self.set_psw(0x02);
    }

    pub fn set_reg_ya(&mut self, value: u16) {
        self.reg_a = (value & 0xff) as u8;
        self.reg_y = ((value >> 8) & 0xff) as u8;
    }

    pub fn get_reg_ya(&self) -> u16 {
        ((self.reg_y as u16) << 8) | (self.reg_a as u16)
    }

    pub fn set_psw(&mut self, value: u8) {
        self.psw_c = (value & 0x01) != 0;
        self.psw_z = (value & 0x02) != 0;
        self.psw_h = (value & 0x08) != 0;
        self.psw_p = (value & 0x20) != 0;
        self.psw_v = (value & 0x40) != 0;
        self.psw_n = (value & 0x80) != 0;
    }

    pub fn get_psw(&self) -> u8 {
        ((if self.psw_n { 1 } else { 0 }) << 7) |
        ((if self.psw_v { 1 } else { 0 }) << 6) |
        ((if self.psw_p { 1 } else { 0 }) << 5) |
        ((if self.psw_h { 1 } else { 0 }) << 3) |
        ((if self.psw_z { 1 } else { 0 }) << 1) |
        (if self.psw_c { 1 } else { 0 })
    }

    fn is_negative(value: u32) -> bool {
        (value & 0x80) != 0
    }

    fn cycles(&mut self, num_cycles: i32) {
        // TODO: notify emulator!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        self.cycle_count += num_cycles;
    }
}
<|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 crypto::CryptoContext;
use error::{WebPushError, WebPushResult};
use hyper::header::{ContentEncoding, Encoding, Authorization};
use hyper::Client;
use hyper::client::Body;
use hyper::client::response::Response;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;
use std::cmp::max;

header! { (Encryption, "Encryption") => [String] }
header! { (EncryptionKey, "Encryption-Key") => [String] }
header! { (CryptoKey, "Crypto-Key") => [String] }
header! { (Ttl, "TTL") => [u32] }

pub struct SubscriptionManager
{
    gcm_api_key: Option<String>,
    crypto: CryptoContext,
}

impl SubscriptionManager {
    pub fn new(gcm_api_key: Option<String>) -> Self {
        SubscriptionManager {
            gcm_api_key: gcm_api_key,
            crypto: CryptoContext::new().unwrap(),
        }
    }

    pub fn with_crypto(crypto: CryptoContext, gcm_api_key: Option<String>) -> Self {
        SubscriptionManager {
            gcm_api_key: gcm_api_key,
            crypto: crypto,
        }
    }

    pub fn post(&self, sub: &Subscription, message: &str) -> WebPushResult<Response> {
        sub.post(&self.crypto, &self.gcm_api_key, message)
    }
}

pub struct Subscription {
    push_uri: String,
    public_key: String,
    auth: Option<String>
}

impl Subscription {
    pub fn new(push_uri: &str, public_key: &str, auth: &str) -> Self {
        Subscription {
            push_uri: push_uri.to_owned(),
            public_key: public_key.to_owned(),
            auth: Some(auth.to_owned()),
        }
    }

    fn post(&self, crypto: &CryptoContext, gcm_api_key: &Option<String>, message: &str) -> WebPushResult<Response> {
        // Make the record size at least the size of the encrypted message. We must
        // add 16 bytes for the encryption tag, 1 byte for padding and 1 byte to
        // ensure we don't end on a record boundary.
        //
        // https://tools.ietf.org/html/draft-ietf-webpush-encryption-02#section-3.2
        //
        // "An application server MUST encrypt a push message with a single record.
        //  This allows for a minimal receiver implementation that handles a single
        //  record. If the message is 4096 octets or longer, the "rs" parameter MUST
        //  be set to a value that is longer than the encrypted push message length."
        //
        // The push service is not obligated to accept larger records however.
        //
        // "Note that a push service is not required to support more than 4096 octets
        // of payload body, which equates to 4080 octets of cleartext, so the "rs"
        // parameter can be omitted for messages that fit within this limit."
        //
        let record_size = max(4096, message.len() + 18);
        let enc = try!(crypto.encrypt(&self.public_key, message.to_owned(),
                                      &self.auth, record_size));

        // If using Google's push service, we need to replace the given endpoint URI
        // with one known to work with WebPush, as support has not yet rolled out to
        // all of its servers.
        //
        // https://github.com/GoogleChrome/web-push-encryption/blob/dd8c58c62b1846c481ceb066c52da0d695c8415b/src/push.js#L69
        let push_uri = self.push_uri.replace("https://android.googleapis.com/gcm/send",
                                             "https://gcm-http.googleapis.com/gcm");

        let ssl = NativeTlsClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        let client = Client::with_connector(connector);

        let has_auth = self.auth.is_some();
        let public_key = crypto.get_public_key(has_auth);
        let mut req = client.post(&push_uri)
            .header(Encryption(format!("keyid=p256dh;salt={};rs={}", enc.salt, record_size)))
            .body(Body::BufBody(&enc.output, enc.output.len()));

        // If using Google's push service, we need to provide an Authorization header
        // which provides an API key permitting us to send push notifications. This
        // should be provided in foxbox.conf as webpush/gcm_api_key in base64.
        //
        // https://github.com/GoogleChrome/web-push-encryption/blob/dd8c58c62b1846c481ceb066c52da0d695c8415b/src/push.js#L84
        if push_uri != self.push_uri {
            match *gcm_api_key {
                Some(ref key) => {
                    req = req.header(Authorization(format!("key={}", key)));
                },
                None => return Err(WebPushError::MissingGcmApiKey),
            };
        }

        req = if has_auth {
            req.header(ContentEncoding(vec![Encoding::EncodingExt(String::from("aesgcm"))]))
                .header(CryptoKey(format!("keyid=p256dh;dh={}", public_key)))

                // Set the TTL which controls how long the push service will wait before giving
                // up on delivery of the notification
                //
                // https://tools.ietf.org/html/draft-ietf-webpush-protocol-04#section-6.2
                //
                // "An application server MUST include the TTL (Time-To-Live) header
                //  field in its request for push message delivery.  The TTL header field
                //  contains a value in seconds that suggests how long a push message is
                //  retained by the push service.
                //
                //      TTL = 1*DIGIT
                //
                //  A push service MUST return a 400 (Bad Request) status code in
                //  response to requests that omit the TTL header field."
                //
                //  TODO: allow the notifier to control this; right now we default to 24 hours
                .header(Ttl(86400))
        } else {
            req.header(ContentEncoding(vec![Encoding::EncodingExt(String::from("aesgcm128"))]))
                .header(EncryptionKey(format!("keyid=p256dh;dh={}", public_key)))
        };

        // TODO: Add a retry mechanism if 429 Too Many Requests returned by push service
        Ok(try!(req.send()))
    }
}

/*
#[cfg(test)]
mod tests {
    use super::{Subscription, SubscriptionManager};

    #[test]
    fn try_push() {
        let manager = SubscriptionManager::new(None);
        let sub = Subscription::new("https://updates.push.services.mozilla.com/wpush/v1/gAAAAABY46XRjSweIO5v4Sedj5Rzwg-RR3iTGP5RCB2Qqdoul2zSey_vM3Nnt0x3x8XM9oMxYbs4qW1Bj6vxWO4WJpL_cXFTEXeQXc8fiMa1AjS8ZXiD2H-MYurEEL4AoNJLXlnqdA-K",
                                    "BLQMve4OpG2qgwsqemr_UL8m49fcQ8omZ-eZXeFnx-aiFOVzFDPenBt90sOQO_sIV9q-QeNNGTpJXfeEuGPiDQE",
                                    "eks8ehlEqlDMD3cV1NJuKQ");
        manager.post(&sub, "rust auto test");
    }
}*/

<commit_msg>Make WebPush specific hyper headers private.<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 crypto::CryptoContext;
use error::{WebPushError, WebPushResult};
use hyper::header::{ContentEncoding, Encoding, Authorization};
use hyper::Client;
use hyper::client::Body;
use hyper::client::response::Response;
use hyper::net::HttpsConnector;
use hyper_native_tls::NativeTlsClient;
use std::cmp::max;

// Place these headers inside a sub module to prevent them from getting
// exported by the crate, as the macro declares them pub.
mod hidden {
    header! { (CryptoKey, "Crypto-Key") => [String] }
    header! { (Encryption, "Encryption") => [String] }
    header! { (EncryptionKey, "Encryption-Key") => [String] }
    header! { (Ttl, "TTL") => [u32] }
}

use self::hidden::{CryptoKey, Encryption, EncryptionKey, Ttl};

pub struct SubscriptionManager
{
    gcm_api_key: Option<String>,
    crypto: CryptoContext,
}

impl SubscriptionManager {
    pub fn new(gcm_api_key: Option<String>) -> Self {
        SubscriptionManager {
            gcm_api_key: gcm_api_key,
            crypto: CryptoContext::new().unwrap(),
        }
    }

    pub fn with_crypto(crypto: CryptoContext, gcm_api_key: Option<String>) -> Self {
        SubscriptionManager {
            gcm_api_key: gcm_api_key,
            crypto: crypto,
        }
    }

    pub fn post(&self, sub: &Subscription, message: &str) -> WebPushResult<Response> {
        sub.post(&self.crypto, &self.gcm_api_key, message)
    }
}

pub struct Subscription {
    push_uri: String,
    public_key: String,
    auth: Option<String>
}

impl Subscription {
    pub fn new(push_uri: &str, public_key: &str, auth: &str) -> Self {
        Subscription {
            push_uri: push_uri.to_owned(),
            public_key: public_key.to_owned(),
            auth: Some(auth.to_owned()),
        }
    }

    fn post(&self, crypto: &CryptoContext, gcm_api_key: &Option<String>, message: &str) -> WebPushResult<Response> {
        // Make the record size at least the size of the encrypted message. We must
        // add 16 bytes for the encryption tag, 1 byte for padding and 1 byte to
        // ensure we don't end on a record boundary.
        //
        // https://tools.ietf.org/html/draft-ietf-webpush-encryption-02#section-3.2
        //
        // "An application server MUST encrypt a push message with a single record.
        //  This allows for a minimal receiver implementation that handles a single
        //  record. If the message is 4096 octets or longer, the "rs" parameter MUST
        //  be set to a value that is longer than the encrypted push message length."
        //
        // The push service is not obligated to accept larger records however.
        //
        // "Note that a push service is not required to support more than 4096 octets
        // of payload body, which equates to 4080 octets of cleartext, so the "rs"
        // parameter can be omitted for messages that fit within this limit."
        //
        let record_size = max(4096, message.len() + 18);
        let enc = try!(crypto.encrypt(&self.public_key, message.to_owned(),
                                      &self.auth, record_size));

        // If using Google's push service, we need to replace the given endpoint URI
        // with one known to work with WebPush, as support has not yet rolled out to
        // all of its servers.
        //
        // https://github.com/GoogleChrome/web-push-encryption/blob/dd8c58c62b1846c481ceb066c52da0d695c8415b/src/push.js#L69
        let push_uri = self.push_uri.replace("https://android.googleapis.com/gcm/send",
                                             "https://gcm-http.googleapis.com/gcm");

        let ssl = NativeTlsClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        let client = Client::with_connector(connector);

        let has_auth = self.auth.is_some();
        let public_key = crypto.get_public_key(has_auth);
        let mut req = client.post(&push_uri)
            .header(Encryption(format!("keyid=p256dh;salt={};rs={}", enc.salt, record_size)))
            .body(Body::BufBody(&enc.output, enc.output.len()));

        // If using Google's push service, we need to provide an Authorization header
        // which provides an API key permitting us to send push notifications. This
        // should be provided in foxbox.conf as webpush/gcm_api_key in base64.
        //
        // https://github.com/GoogleChrome/web-push-encryption/blob/dd8c58c62b1846c481ceb066c52da0d695c8415b/src/push.js#L84
        if push_uri != self.push_uri {
            match *gcm_api_key {
                Some(ref key) => {
                    req = req.header(Authorization(format!("key={}", key)));
                },
                None => return Err(WebPushError::MissingGcmApiKey),
            };
        }

        req = if has_auth {
            req.header(ContentEncoding(vec![Encoding::EncodingExt(String::from("aesgcm"))]))
                .header(CryptoKey(format!("keyid=p256dh;dh={}", public_key)))

                // Set the TTL which controls how long the push service will wait before giving
                // up on delivery of the notification
                //
                // https://tools.ietf.org/html/draft-ietf-webpush-protocol-04#section-6.2
                //
                // "An application server MUST include the TTL (Time-To-Live) header
                //  field in its request for push message delivery.  The TTL header field
                //  contains a value in seconds that suggests how long a push message is
                //  retained by the push service.
                //
                //      TTL = 1*DIGIT
                //
                //  A push service MUST return a 400 (Bad Request) status code in
                //  response to requests that omit the TTL header field."
                //
                //  TODO: allow the notifier to control this; right now we default to 24 hours
                .header(Ttl(86400))
        } else {
            req.header(ContentEncoding(vec![Encoding::EncodingExt(String::from("aesgcm128"))]))
                .header(EncryptionKey(format!("keyid=p256dh;dh={}", public_key)))
        };

        // TODO: Add a retry mechanism if 429 Too Many Requests returned by push service
        Ok(try!(req.send()))
    }
}

<|endoftext|>


<commit_before>use std::f64::NAN;

use super::js_str::JsStrStruct;
use super::js_var::{JsPtrEnum, JsPtrTag, JsType, JsVar};
use super::js_var::JsType::*;
use super::native_var::NativeVar;

pub trait AsBool {
    fn as_bool(&self) -> bool;
}

impl AsBool for JsVar {
    fn as_bool(&self) -> bool {
        match self.t {
            JsBool(b) => b,
            JsUndef=> false,
            JsNull => false,
            JsNum(n) =>
                if n == 0.0f64 || n == -0.0f64 || n.is_nan() {
                    false
                } else {
                    true
                },
            JsPtr(_) => true, // TODO - this is incorrect
            //JsString(ref s) =>
            //    if s.len() == 0 {
            //        false
            //    } else {
            //        true
            //    },
            //JsSymbol(_) => true,
            //JsObject | JsError(_) | JsFunction(_, _, _) => true,
        }
    }
}

pub trait AsNumber {
    fn as_number(&self) -> f64;
}

impl AsNumber for JsVar {
    fn as_number(&self) -> f64 {
        match self.t {
            JsBool(b) => if b { 1f64 } else { 0f64 },
            JsUndef => NAN,
            JsNull => 0f64,
            JsNum(n) => n,
            JsPtr(_) => NAN,
        }
    }
}

impl AsNumber for JsPtrEnum {
    fn as_number(&self) -> f64 {
        match self {
            // TODO: Change this to throw a TypeError
            &JsPtrEnum::JsSym(_) => panic!("Cannot convert a Symbol to a number."),
            &JsPtrEnum::JsStr(JsStrStruct { ref text }) => {
                if text.len() == 0 {
                    return 0.0
                }

                text.parse().unwrap_or(NAN)
            }
            &JsPtrEnum::NativeVar(NativeVar { ref var, ref ptr, .. }) => match (var, ptr) {
                (_, &Some(ref ptr)) => ptr.as_number(),
                (ref var, &None) => var.as_number(),
            },
            _ => NAN
        }
    }
}

pub trait AsString {
    fn as_string(&self) -> String;
}

impl AsString for JsType {
    fn as_string(&self) -> String {
        let s = match *self {
            JsBool(true) => "true",
            JsBool(false) => "false",
            JsUndef => "undefined",
            JsNull => "null",
            JsNum(n) => return format!("{}", n),

            // NOTE: These cases should never actually be used; in the case that the variable is
            // a JsPtr, the corresponding JsPtrEnum's string coercion should be used.
            JsPtr(JsPtrTag::JsSym) => "Symbol(...)",
            JsPtr(JsPtrTag::JsStr) => "\"...\"",
            JsPtr(JsPtrTag::JsObj) => "{ ... }",
            JsPtr(JsPtrTag::JsFn{..}) => "function() { ... }",
            JsPtr(JsPtrTag::NativeFn{..}) => "function() { [native code] }",
            JsPtr(JsPtrTag::NativeVar{..}) => "[native code]",
        };

        String::from(s)
    }
}

impl AsString for JsPtrEnum {
    fn as_string(&self) -> String {
        match *self {
            JsPtrEnum::JsSym(ref s) => format!("Symbol({})", s),
            JsPtrEnum::JsStr(ref s) => s.text.to_owned(),

            // TODO: Check object's `toString` method
            JsPtrEnum::JsObj(_) => String::from("[object Object]"),

            // TODO: A function's string representation is apparently the string of source code that
            // created it; the AST doesn't currently support this, so we'll need to do some
            // restructuring before we can support this.
            JsPtrEnum::JsFn(_) => String::from("[function]"),
            JsPtrEnum::NativeFn(_) => String::from("[native function]"),
            JsPtrEnum::NativeVar(NativeVar { ref var, ref ptr, ..}) => ptr.as_ref().map(|p| p.as_string()).unwrap_or(var.t.as_string()),
        }
    }
}
<commit_msg>add AsBool for JsPtrEnum<commit_after>use std::f64::NAN;

use super::js_str::JsStrStruct;
use super::js_var::{JsPtrEnum, JsPtrTag, JsType, JsVar};
use super::js_var::JsType::*;
use super::native_var::NativeVar;

pub trait AsBool {
    fn as_bool(&self) -> bool;
}

impl AsBool for JsVar {
    fn as_bool(&self) -> bool {
        match self.t {
            JsBool(b) => b,
            JsUndef=> false,
            JsNull => false,
            JsNum(n) =>
                if n == 0.0f64 || n == -0.0f64 || n.is_nan() {
                    false
                } else {
                    true
                },
            JsPtr(_) => true, // TODO - this is incorrect
            //JsString(ref s) =>
            //    if s.len() == 0 {
            //        false
            //    } else {
            //        true
            //    },
            //JsSymbol(_) => true,
            //JsObject | JsError(_) | JsFunction(_, _, _) => true,
        }
    }
}

impl AsBool for JsPtrEnum {
    fn as_bool(&self) -> bool {
        match *self {
            JsPtrEnum::JsSym(_) |
            JsPtrEnum::JsFn(_) |
            JsPtrEnum::JsObj(_) |
            JsPtrEnum::NativeFn(_) => true,
            JsPtrEnum::JsStr(ref s) => s.text.len() != 0,
            JsPtrEnum::NativeVar(ref nv) => nv.ptr.as_ref().map(|ptr| ptr.as_bool()).unwrap_or(nv.var.as_bool()),
        }
    }
}

pub trait AsNumber {
    fn as_number(&self) -> f64;
}

impl AsNumber for JsVar {
    fn as_number(&self) -> f64 {
        match self.t {
            JsBool(b) => if b { 1f64 } else { 0f64 },
            JsUndef => NAN,
            JsNull => 0f64,
            JsNum(n) => n,
            JsPtr(_) => NAN,
        }
    }
}

impl AsNumber for JsPtrEnum {
    fn as_number(&self) -> f64 {
        match self {
            // TODO: Change this to throw a TypeError
            &JsPtrEnum::JsSym(_) => panic!("Cannot convert a Symbol to a number."),
            &JsPtrEnum::JsStr(JsStrStruct { ref text }) => {
                if text.len() == 0 {
                    return 0.0
                }

                text.parse().unwrap_or(NAN)
            }
            &JsPtrEnum::NativeVar(NativeVar { ref var, ref ptr, .. }) => match (var, ptr) {
                (_, &Some(ref ptr)) => ptr.as_number(),
                (ref var, &None) => var.as_number(),
            },
            _ => NAN
        }
    }
}

pub trait AsString {
    fn as_string(&self) -> String;
}

impl AsString for JsType {
    fn as_string(&self) -> String {
        let s = match *self {
            JsBool(true) => "true",
            JsBool(false) => "false",
            JsUndef => "undefined",
            JsNull => "null",
            JsNum(n) => return format!("{}", n),

            // NOTE: These cases should never actually be used; in the case that the variable is
            // a JsPtr, the corresponding JsPtrEnum's string coercion should be used.
            JsPtr(JsPtrTag::JsSym) => "Symbol(...)",
            JsPtr(JsPtrTag::JsStr) => "\"...\"",
            JsPtr(JsPtrTag::JsObj) => "{ ... }",
            JsPtr(JsPtrTag::JsFn{..}) => "function() { ... }",
            JsPtr(JsPtrTag::NativeFn{..}) => "function() { [native code] }",
            JsPtr(JsPtrTag::NativeVar{..}) => "[native code]",
        };

        String::from(s)
    }
}

impl AsString for JsPtrEnum {
    fn as_string(&self) -> String {
        match *self {
            JsPtrEnum::JsSym(ref s) => format!("Symbol({})", s),
            JsPtrEnum::JsStr(ref s) => s.text.to_owned(),

            // TODO: Check object's `toString` method
            JsPtrEnum::JsObj(_) => String::from("[object Object]"),

            // TODO: A function's string representation is apparently the string of source code that
            // created it; the AST doesn't currently support this, so we'll need to do some
            // restructuring before we can support this.
            JsPtrEnum::JsFn(_) => String::from("[function]"),
            JsPtrEnum::NativeFn(_) => String::from("[native function]"),
            JsPtrEnum::NativeVar(NativeVar { ref var, ref ptr, ..}) => ptr.as_ref().map(|p| p.as_string()).unwrap_or(var.t.as_string()),
        }
    }
}
<|endoftext|>