<commit_before>use libc;
use ram::entry;
use ram::tombstone::{TOMBSTONE_SIZE_U32, Tombstone};
use ram::cell;
use ram::cell::Cell;
use ram::chunk::Chunk;
use ram::entry::EntryMeta;
use std::sync::atomic::{AtomicUsize, AtomicU32, AtomicI64, AtomicBool, Ordering};
use std::collections::BTreeSet;
use std::fs::{File, remove_file, copy};
use std::io::BufWriter;
use std::io::prelude::*;
use std::io;
use std::path::Path;
use crc32c::crc32c;
use bifrost::utils::async_locks::{RwLock, RwLockReadGuard};
use parking_lot;

use super::cell::CellHeader;

pub const MAX_SEGMENT_SIZE_U32: u32 = 8 * 1024 * 1024;
pub const MAX_SEGMENT_SIZE: usize = MAX_SEGMENT_SIZE_U32 as usize;

pub struct Segment {
    pub id: u64,
    pub addr: usize,
    pub bound: usize,
    pub append_header: AtomicUsize,
    pub dead_space: AtomicU32,
    pub tombstones: AtomicU32,
    pub dead_tombstones: AtomicU32,
    pub last_tombstones_scanned: AtomicI64,
    pub references: AtomicUsize,
    pub backup_file_name: Option<String>,
    pub wal_file: Option<parking_lot::Mutex<BufWriter<File>>>,
    pub wal_file_name: Option<String>,
    pub archived: AtomicBool,
    pub dropped: AtomicBool
}

impl Segment {
    pub fn new(id: u64, size: usize,
               backup_storage: &Option<String>,
               wal_storage: &Option<String>) -> Segment {
        let buffer_ptr = unsafe { libc::malloc(size) as usize };
        let mut wal_file_name = None;
        let mut wal_file = None;
        if let Some(wal_storage) = wal_storage {
            let file_name = format!("{}/{}.log", wal_storage, id);
            let file = BufWriter::with_capacity(
                4096, // most common disk block size
                File::open(&file_name).unwrap()); // fast fail
            wal_file_name = Some(file_name);
            wal_file = Some(parking_lot::Mutex::new(file));
        }
        debug!("Creating new segment with id {}, size {}, address {}", id, size, buffer_ptr);
        Segment {
            addr: buffer_ptr,
            id,
            bound: buffer_ptr + size,
            append_header: AtomicUsize::new(buffer_ptr),
            dead_space: AtomicU32::new(0),
            tombstones: AtomicU32::new(0),
            dead_tombstones: AtomicU32::new(0),
            last_tombstones_scanned: AtomicI64::new(0),
            references: AtomicUsize::new(0),
            backup_file_name: backup_storage.clone().map(|path| format!("{}/{}.backup", path, id)),
            archived: AtomicBool::new(false),
            dropped: AtomicBool::new(false),
            wal_file,
            wal_file_name
        }
    }

    pub fn try_acquire(&self, size: u32) -> Option<usize> {
        let size = size as usize;
        loop {
            let curr_last = self.append_header.load(Ordering::SeqCst);
            let exp_last = curr_last + size;
            if exp_last > self.bound {
                return None;
            } else {
                if self.append_header.compare_and_swap(curr_last, exp_last, Ordering::SeqCst) != curr_last {
                    continue;
                } else {
                    return Some(curr_last);
                }
            }
        }
    }

    fn append_header(&self) -> usize {
        self.append_header.load(Ordering::Relaxed)
    }

    pub fn entry_iter(&self) -> SegmentEntryIter {
        SegmentEntryIter {
            bound: self.append_header(),
            cursor: self.addr
        }
    }

    pub fn dead_space(&self) -> u32 {
        self.dead_space.load(Ordering::Relaxed)
    }

    // dead space plus tombstone spaces
    pub fn total_dead_space(&self) -> u32 {
        let dead_tombstones_space = self.dead_tombstones.load(Ordering::Relaxed) * TOMBSTONE_SIZE_U32;
        let dead_cells_space = self.dead_space();
        return dead_tombstones_space + dead_cells_space;
    }

    pub fn used_spaces(&self) -> u32 {
        return (self.append_header.load(Ordering::Relaxed) as usize - self.addr) as u32
    }

    pub fn living_space(&self) -> u32 {
        let total_dead_space = self.total_dead_space();
        let used_space = self.used_spaces();
        used_space - total_dead_space
    }

    pub fn valid_space(&self) -> u32 {
        return self.used_spaces() - self.dead_space();
    }

    pub fn living_rate(&self) -> f32 {
        let used_space = self.used_spaces() as f32;
        if used_space == 0f32 {
            // empty segment
            return 1f32;
        }
        return self.living_space() as f32 / used_space;
    }

    // archive this segment and write the data to backup storage
    pub fn archive(&self) -> Result<bool, io::Error> {
        if let &Some(ref backup_file) = &self.backup_file_name {
            while !self.no_references() { /* wait until all references released */}
            let backup_file_path = Path::new(backup_file);
            if backup_file_path.exists() {
                warn!("Segment backup {} exists and can't archive twice", backup_file);
                return Ok(false);
            }
            if let Some(ref wal_file) = self.wal_file_name {
                // if there is a WAL file ready, copy this file to backup
                if let Some(ref file_mutex) = self.wal_file {
                    // this should be redundant but I don't want to take the chance
                    // obtain the writer lock before continue
                    let _ = file_mutex.lock();
                    copy(wal_file, backup_file);
                    remove_file(wal_file);
                } else { panic!() }

            } else {
                let backup_file = File::open(backup_file_path)?;
                let seg_size = self.append_header.load(Ordering::Relaxed) - self.addr;
                let mut buffer = BufWriter::with_capacity(seg_size, backup_file);
                unsafe {
                    for offset in 0..seg_size {
                        let ptr = (self.addr + offset) as *const u8;
                        let byte = *ptr;
                        buffer.write(&[byte])?;
                    }
                }
                buffer.flush()?;
                return Ok(true);
            }
        }
        return Ok(false);
    }

    pub fn write_wal(&self, addr: usize, size: u32) -> io::Result<()> {
        if let Some(ref wal_file) = self.wal_file {
            let mut file = wal_file.lock();
            unsafe {
                for offset in 0..size as usize {
                    let ptr = (addr + offset) as *const u8;
                    let byte = *ptr;
                    file.write(&[byte])?;
                }
            }
            file.flush()?;
        }
        return Ok(());
    }

    pub fn no_references(&self) -> bool { self.references.load(Ordering::Relaxed) == 0 }

    pub fn mem_drop(&self) {
        if !self.dropped.compare_and_swap(false, true, Ordering::Relaxed) {
            debug!("disposing segment at {}", self.addr);
            unsafe {
                libc::free(self.addr as *mut libc::c_void)
            }
        }
    }

    // remove the backup if it have one
    pub fn dispense(&self) {
        debug!("dispense segment {}", self.id);
        if let &Some(ref backup_storage) = &self.backup_file_name {
            let path = Path::new(backup_storage);
            if path.exists() {
                if let Err(e) = remove_file(path) {
                    error!("cannot reclaim segment file on dispense {}", backup_storage)
                }
            } else {
                error!("cannot find segment backup {}", backup_storage)
            }
        }
    }
}

impl Drop for Segment {
    fn drop(&mut self) {
        debug!("Memory dropping segment {}", self.id);
        assert_eq!(self.references.load(Ordering::Relaxed), 0);
        self.mem_drop()
    }
}

pub struct SegmentEntryIter {
    bound: usize,
    cursor: usize
}

impl Iterator for SegmentEntryIter {
    type Item = EntryMeta;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        let cursor = self.cursor;
        if cursor >= self.bound {
            return None;
        }
        let (_, entry_meta) = entry::Entry::decode_from(
            cursor,
            |body_pos, header| {
                let entry_header_size = body_pos - cursor;
                let entry_size = entry_header_size + header.content_length as usize;
                debug!("Found body pos {}, entry header {:?}. Header size: {}, entry size: {}, entry pos: {}, content length {}, bound {}",
                       body_pos, header, entry_header_size, entry_size, cursor, header.content_length, self.bound);
                return EntryMeta {
                    body_pos, entry_header: header, entry_size, entry_pos: cursor
                };
            });
        self.cursor += entry_meta.entry_size;
        Some(entry_meta)
    }
}<commit_msg>overcome the overflow, agaion<commit_after>use libc;
use ram::entry;
use ram::tombstone::{TOMBSTONE_SIZE_U32, Tombstone};
use ram::cell;
use ram::cell::Cell;
use ram::chunk::Chunk;
use ram::entry::EntryMeta;
use std::sync::atomic::{AtomicUsize, AtomicU32, AtomicI64, AtomicBool, Ordering};
use std::collections::BTreeSet;
use std::fs::{File, remove_file, copy};
use std::io::BufWriter;
use std::io::prelude::*;
use std::io;
use std::path::Path;
use crc32c::crc32c;
use bifrost::utils::async_locks::{RwLock, RwLockReadGuard};
use parking_lot;

use super::cell::CellHeader;

pub const MAX_SEGMENT_SIZE_U32: u32 = 8 * 1024 * 1024;
pub const MAX_SEGMENT_SIZE: usize = MAX_SEGMENT_SIZE_U32 as usize;

pub struct Segment {
    pub id: u64,
    pub addr: usize,
    pub bound: usize,
    pub append_header: AtomicUsize,
    pub dead_space: AtomicU32,
    pub tombstones: AtomicU32,
    pub dead_tombstones: AtomicU32,
    pub last_tombstones_scanned: AtomicI64,
    pub references: AtomicUsize,
    pub backup_file_name: Option<String>,
    pub wal_file: Option<parking_lot::Mutex<BufWriter<File>>>,
    pub wal_file_name: Option<String>,
    pub archived: AtomicBool,
    pub dropped: AtomicBool
}

impl Segment {
    pub fn new(id: u64, size: usize,
               backup_storage: &Option<String>,
               wal_storage: &Option<String>) -> Segment {
        let buffer_ptr = unsafe { libc::malloc(size) as usize };
        let mut wal_file_name = None;
        let mut wal_file = None;
        if let Some(wal_storage) = wal_storage {
            let file_name = format!("{}/{}.log", wal_storage, id);
            let file = BufWriter::with_capacity(
                4096, // most common disk block size
                File::open(&file_name).unwrap()); // fast fail
            wal_file_name = Some(file_name);
            wal_file = Some(parking_lot::Mutex::new(file));
        }
        debug!("Creating new segment with id {}, size {}, address {}", id, size, buffer_ptr);
        Segment {
            addr: buffer_ptr,
            id,
            bound: buffer_ptr + size,
            append_header: AtomicUsize::new(buffer_ptr),
            dead_space: AtomicU32::new(0),
            tombstones: AtomicU32::new(0),
            dead_tombstones: AtomicU32::new(0),
            last_tombstones_scanned: AtomicI64::new(0),
            references: AtomicUsize::new(0),
            backup_file_name: backup_storage.clone().map(|path| format!("{}/{}.backup", path, id)),
            archived: AtomicBool::new(false),
            dropped: AtomicBool::new(false),
            wal_file,
            wal_file_name
        }
    }

    pub fn try_acquire(&self, size: u32) -> Option<usize> {
        let size = size as usize;
        loop {
            let curr_last = self.append_header.load(Ordering::SeqCst);
            let exp_last = curr_last + size;
            if exp_last > self.bound {
                return None;
            } else {
                if self.append_header.compare_and_swap(curr_last, exp_last, Ordering::SeqCst) != curr_last {
                    continue;
                } else {
                    return Some(curr_last);
                }
            }
        }
    }

    fn append_header(&self) -> usize {
        self.append_header.load(Ordering::Relaxed)
    }

    pub fn entry_iter(&self) -> SegmentEntryIter {
        SegmentEntryIter {
            bound: self.append_header(),
            cursor: self.addr
        }
    }

    pub fn dead_space(&self) -> u32 {
        self.dead_space.load(Ordering::Relaxed)
    }

    // dead space plus tombstone spaces
    pub fn total_dead_space(&self) -> u32 {
        let total_dead_space = self.total_dead_space();
        let used_space = self.used_spaces();
        if total_dead_space <= used_space {
            used_space - total_dead_space
        } else {
            warn!("living space check error, used {}, dead {}", used_space, total_dead_space);
            0
        }
    }

    pub fn used_spaces(&self) -> u32 {
        return (self.append_header.load(Ordering::Relaxed) as usize - self.addr) as u32
    }

    pub fn living_space(&self) -> u32 {
        let total_dead_space = self.total_dead_space();
        let used_space = self.used_spaces();
        used_space - total_dead_space
    }

    pub fn valid_space(&self) -> u32 {
        return self.used_spaces() - self.dead_space();
    }

    pub fn living_rate(&self) -> f32 {
        let used_space = self.used_spaces() as f32;
        if used_space == 0f32 {
            // empty segment
            return 1f32;
        }
        return self.living_space() as f32 / used_space;
    }

    // archive this segment and write the data to backup storage
    pub fn archive(&self) -> Result<bool, io::Error> {
        if let &Some(ref backup_file) = &self.backup_file_name {
            while !self.no_references() { /* wait until all references released */}
            let backup_file_path = Path::new(backup_file);
            if backup_file_path.exists() {
                warn!("Segment backup {} exists and can't archive twice", backup_file);
                return Ok(false);
            }
            if let Some(ref wal_file) = self.wal_file_name {
                // if there is a WAL file ready, copy this file to backup
                if let Some(ref file_mutex) = self.wal_file {
                    // this should be redundant but I don't want to take the chance
                    // obtain the writer lock before continue
                    let _ = file_mutex.lock();
                    copy(wal_file, backup_file);
                    remove_file(wal_file);
                } else { panic!() }

            } else {
                let backup_file = File::open(backup_file_path)?;
                let seg_size = self.append_header.load(Ordering::Relaxed) - self.addr;
                let mut buffer = BufWriter::with_capacity(seg_size, backup_file);
                unsafe {
                    for offset in 0..seg_size {
                        let ptr = (self.addr + offset) as *const u8;
                        let byte = *ptr;
                        buffer.write(&[byte])?;
                    }
                }
                buffer.flush()?;
                return Ok(true);
            }
        }
        return Ok(false);
    }

    pub fn write_wal(&self, addr: usize, size: u32) -> io::Result<()> {
        if let Some(ref wal_file) = self.wal_file {
            let mut file = wal_file.lock();
            unsafe {
                for offset in 0..size as usize {
                    let ptr = (addr + offset) as *const u8;
                    let byte = *ptr;
                    file.write(&[byte])?;
                }
            }
            file.flush()?;
        }
        return Ok(());
    }

    pub fn no_references(&self) -> bool { self.references.load(Ordering::Relaxed) == 0 }

    pub fn mem_drop(&self) {
        if !self.dropped.compare_and_swap(false, true, Ordering::Relaxed) {
            debug!("disposing segment at {}", self.addr);
            unsafe {
                libc::free(self.addr as *mut libc::c_void)
            }
        }
    }

    // remove the backup if it have one
    pub fn dispense(&self) {
        debug!("dispense segment {}", self.id);
        if let &Some(ref backup_storage) = &self.backup_file_name {
            let path = Path::new(backup_storage);
            if path.exists() {
                if let Err(e) = remove_file(path) {
                    error!("cannot reclaim segment file on dispense {}", backup_storage)
                }
            } else {
                error!("cannot find segment backup {}", backup_storage)
            }
        }
    }
}

impl Drop for Segment {
    fn drop(&mut self) {
        debug!("Memory dropping segment {}", self.id);
        assert_eq!(self.references.load(Ordering::Relaxed), 0);
        self.mem_drop()
    }
}

pub struct SegmentEntryIter {
    bound: usize,
    cursor: usize
}

impl Iterator for SegmentEntryIter {
    type Item = EntryMeta;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        let cursor = self.cursor;
        if cursor >= self.bound {
            return None;
        }
        let (_, entry_meta) = entry::Entry::decode_from(
            cursor,
            |body_pos, header| {
                let entry_header_size = body_pos - cursor;
                let entry_size = entry_header_size + header.content_length as usize;
                debug!("Found body pos {}, entry header {:?}. Header size: {}, entry size: {}, entry pos: {}, content length {}, bound {}",
                       body_pos, header, entry_header_size, entry_size, cursor, header.content_length, self.bound);
                return EntryMeta {
                    body_pos, entry_header: header, entry_size, entry_pos: cursor
                };
            });
        self.cursor += entry_meta.entry_size;
        Some(entry_meta)
    }
}<|endoftext|>


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

use dom::audioparam::AudioParam;
use dom::baseaudiocontext::BaseAudioContext;
use dom::bindings::codegen::Bindings::AudioNodeBinding::{AudioNodeMethods, AudioNodeOptions};
use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation};
use dom::bindings::codegen::InheritTypes::{AudioNodeTypeId, EventTargetTypeId};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::root::{Dom, DomRoot};
use dom::eventtarget::EventTarget;
use dom_struct::dom_struct;
use servo_media::audio::graph::NodeId;
use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit};
use servo_media::audio::node::ChannelCountMode as ServoMediaChannelCountMode;
use servo_media::audio::node::ChannelInterpretation as ServoMediaChannelInterpretation;
use std::cell::Cell;

// 32 is the minimum required by the spec for createBuffer() and the deprecated
// createScriptProcessor() and matches what is used by Blink and Gecko.
// The limit protects against large memory allocations.
pub const MAX_CHANNEL_COUNT: u32 = 32;

#[dom_struct]
pub struct AudioNode {
    eventtarget: EventTarget,
    #[ignore_malloc_size_of = "servo_media"]
    node_id: NodeId,
    context: Dom<BaseAudioContext>,
    number_of_inputs: u32,
    number_of_outputs: u32,
    channel_count: Cell<u32>,
    channel_count_mode: Cell<ChannelCountMode>,
    channel_interpretation: Cell<ChannelInterpretation>,
}

impl AudioNode {
    pub fn new_inherited(
        node_type: AudioNodeInit,
        context: &BaseAudioContext,
        options: &AudioNodeOptions,
        number_of_inputs: u32,
        number_of_outputs: u32,
    ) -> AudioNode {
        let node_id = context.audio_context_impl().create_node(node_type);
        AudioNode::new_inherited_for_id(node_id, context, options, number_of_inputs, number_of_outputs)
    }

    pub fn new_inherited_for_id(
        node_id: NodeId,
        context: &BaseAudioContext,
        options: &AudioNodeOptions,
        number_of_inputs: u32,
        number_of_outputs: u32,
    ) -> AudioNode {
        AudioNode {
            eventtarget: EventTarget::new_inherited(),
            node_id,
            context: Dom::from_ref(context),
            number_of_inputs,
            number_of_outputs,
            channel_count: Cell::new(options.channelCount.unwrap_or(2)),
            channel_count_mode: Cell::new(options.channelCountMode.unwrap_or_default()),
            channel_interpretation: Cell::new(options.channelInterpretation.unwrap_or_default()),
        }
    }

    pub fn message(&self, message: AudioNodeMessage) {
        self.context
            .audio_context_impl()
            .message_node(self.node_id, message);
    }

    pub fn node_id(&self) -> NodeId {
        self.node_id
    }
}

impl AudioNodeMethods for AudioNode {
    // https://webaudio.github.io/web-audio-api/#dom-audionode-connect
    fn Connect(
        &self,
        destination: &AudioNode,
        output: u32,
        input: u32,
    ) -> Fallible<DomRoot<AudioNode>> {
        if self.context != destination.context {
            return Err(Error::InvalidAccess);
        }

        if output >= self.NumberOfOutputs() || input >= destination.NumberOfInputs() {
            return Err(Error::IndexSize);
        }

        // servo-media takes care of ignoring duplicated connections.

        self.context.audio_context_impl().connect_ports(
            self.node_id().output(output),
            destination.node_id().input(input),
        );

        Ok(DomRoot::from_ref(destination))
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output
    fn Connect_(&self, dest: &AudioParam, output: u32) -> Fallible<()> {
        if self.context != dest.context() {
            return Err(Error::InvalidAccess);
        }

        if output >= self.NumberOfOutputs() {
            return Err(Error::IndexSize);
        }

        // servo-media takes care of ignoring duplicated connections.

        self.context.audio_context_impl().connect_ports(
            self.node_id().output(output),
            dest.node_id().param(dest.param_type()),
        );

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect
    fn Disconnect(&self) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_all_from(self.node_id());
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-output
    fn Disconnect_(&self, out: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output(self.node_id().output(out));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode
    fn Disconnect__(&self, to: &AudioNode) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_between(self.node_id(), to.node_id());
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output
    fn Disconnect___(&self, to: &AudioNode, out: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output_between(self.node_id().output(out), to.node_id());
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output-input
    fn Disconnect____(&self, to: &AudioNode, out: u32, inp: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output_between_to(self.node_id().output(out), to.node_id().input(inp));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect
    fn Disconnect_____(&self, param: &AudioParam) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_to(self.node_id(),
                           param.node_id().param(param.param_type()));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect
    fn Disconnect______(&self, param: &AudioParam, out: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output_between_to(self.node_id().output(out),
                                          param.node_id().param(param.param_type()));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-context
    fn Context(&self) -> DomRoot<BaseAudioContext> {
        DomRoot::from_ref(&self.context)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-numberofinputs
    fn NumberOfInputs(&self) -> u32 {
        self.number_of_inputs
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-numberofoutputs
    fn NumberOfOutputs(&self) -> u32 {
        self.number_of_outputs
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount
    fn ChannelCount(&self) -> u32 {
        self.channel_count.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount
    fn SetChannelCount(&self, value: u32) -> ErrorResult {
        match self.upcast::<EventTarget>().type_id() {
            EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioDestinationNode) => {
                if self.context.is_offline() {
                    return Err(Error::InvalidState);
                } else if value < 1 || value > MAX_CHANNEL_COUNT {
                    return Err(Error::IndexSize);
                }
            },
            // XXX We do not support any of the other AudioNodes with
            // constraints yet. Add more cases here as we add support
            // for new AudioNodes.
            _ => (),
        };

        if value == 0 || value > MAX_CHANNEL_COUNT {
            return Err(Error::NotSupported);
        }

        self.channel_count.set(value);
        self.message(AudioNodeMessage::SetChannelCount(value as u8));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode
    fn ChannelCountMode(&self) -> ChannelCountMode {
        self.channel_count_mode.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode
    fn SetChannelCountMode(&self, value: ChannelCountMode) -> ErrorResult {
        // Channel count mode has no effect for nodes with no inputs.
        if self.number_of_inputs == 0 {
            return Ok(());
        }

        match self.upcast::<EventTarget>().type_id() {
            EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioDestinationNode) => {
                if self.context.is_offline() {
                    return Err(Error::InvalidState);
                }
            },
            // XXX We do not support any of the other AudioNodes with
            // constraints yet. Add more cases here as we add support
            // for new AudioNodes.
            _ => (),
        };

        self.channel_count_mode.set(value);
        self.message(AudioNodeMessage::SetChannelMode(value.into()));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelinterpretation
    fn ChannelInterpretation(&self) -> ChannelInterpretation {
        self.channel_interpretation.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelinterpretation
    fn SetChannelInterpretation(&self, value: ChannelInterpretation) {
        // Channel interpretation mode has no effect for nodes with no inputs.
        if self.number_of_inputs == 0 {
            return;
        }

        self.channel_interpretation.set(value);
        self.message(AudioNodeMessage::SetChannelInterpretation(value.into()));
    }
}

impl From<ChannelCountMode> for ServoMediaChannelCountMode {
    fn from(mode: ChannelCountMode) -> Self {
        match mode {
            ChannelCountMode::Max => ServoMediaChannelCountMode::Max,
            ChannelCountMode::Clamped_max => ServoMediaChannelCountMode::ClampedMax,
            ChannelCountMode::Explicit => ServoMediaChannelCountMode::Explicit,
        }
    }
}

impl From<ChannelInterpretation> for ServoMediaChannelInterpretation {
    fn from(interpretation: ChannelInterpretation) -> Self {
        match interpretation {
            ChannelInterpretation::Discrete => ServoMediaChannelInterpretation::Discrete,
            ChannelInterpretation::Speakers => ServoMediaChannelInterpretation::Speakers,
        }
    }
}
<commit_msg>Add constraints for PannerNode<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::audioparam::AudioParam;
use dom::baseaudiocontext::BaseAudioContext;
use dom::bindings::codegen::Bindings::AudioNodeBinding::{AudioNodeMethods, AudioNodeOptions};
use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation};
use dom::bindings::codegen::InheritTypes::{AudioNodeTypeId, EventTargetTypeId};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::root::{Dom, DomRoot};
use dom::eventtarget::EventTarget;
use dom_struct::dom_struct;
use servo_media::audio::graph::NodeId;
use servo_media::audio::node::{AudioNodeMessage, AudioNodeInit};
use servo_media::audio::node::ChannelCountMode as ServoMediaChannelCountMode;
use servo_media::audio::node::ChannelInterpretation as ServoMediaChannelInterpretation;
use std::cell::Cell;

// 32 is the minimum required by the spec for createBuffer() and the deprecated
// createScriptProcessor() and matches what is used by Blink and Gecko.
// The limit protects against large memory allocations.
pub const MAX_CHANNEL_COUNT: u32 = 32;

#[dom_struct]
pub struct AudioNode {
    eventtarget: EventTarget,
    #[ignore_malloc_size_of = "servo_media"]
    node_id: NodeId,
    context: Dom<BaseAudioContext>,
    number_of_inputs: u32,
    number_of_outputs: u32,
    channel_count: Cell<u32>,
    channel_count_mode: Cell<ChannelCountMode>,
    channel_interpretation: Cell<ChannelInterpretation>,
}

impl AudioNode {
    pub fn new_inherited(
        node_type: AudioNodeInit,
        context: &BaseAudioContext,
        options: &AudioNodeOptions,
        number_of_inputs: u32,
        number_of_outputs: u32,
    ) -> AudioNode {
        let node_id = context.audio_context_impl().create_node(node_type);
        AudioNode::new_inherited_for_id(node_id, context, options, number_of_inputs, number_of_outputs)
    }

    pub fn new_inherited_for_id(
        node_id: NodeId,
        context: &BaseAudioContext,
        options: &AudioNodeOptions,
        number_of_inputs: u32,
        number_of_outputs: u32,
    ) -> AudioNode {
        AudioNode {
            eventtarget: EventTarget::new_inherited(),
            node_id,
            context: Dom::from_ref(context),
            number_of_inputs,
            number_of_outputs,
            channel_count: Cell::new(options.channelCount.unwrap_or(2)),
            channel_count_mode: Cell::new(options.channelCountMode.unwrap_or_default()),
            channel_interpretation: Cell::new(options.channelInterpretation.unwrap_or_default()),
        }
    }

    pub fn message(&self, message: AudioNodeMessage) {
        self.context
            .audio_context_impl()
            .message_node(self.node_id, message);
    }

    pub fn node_id(&self) -> NodeId {
        self.node_id
    }
}

impl AudioNodeMethods for AudioNode {
    // https://webaudio.github.io/web-audio-api/#dom-audionode-connect
    fn Connect(
        &self,
        destination: &AudioNode,
        output: u32,
        input: u32,
    ) -> Fallible<DomRoot<AudioNode>> {
        if self.context != destination.context {
            return Err(Error::InvalidAccess);
        }

        if output >= self.NumberOfOutputs() || input >= destination.NumberOfInputs() {
            return Err(Error::IndexSize);
        }

        // servo-media takes care of ignoring duplicated connections.

        self.context.audio_context_impl().connect_ports(
            self.node_id().output(output),
            destination.node_id().input(input),
        );

        Ok(DomRoot::from_ref(destination))
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output
    fn Connect_(&self, dest: &AudioParam, output: u32) -> Fallible<()> {
        if self.context != dest.context() {
            return Err(Error::InvalidAccess);
        }

        if output >= self.NumberOfOutputs() {
            return Err(Error::IndexSize);
        }

        // servo-media takes care of ignoring duplicated connections.

        self.context.audio_context_impl().connect_ports(
            self.node_id().output(output),
            dest.node_id().param(dest.param_type()),
        );

        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect
    fn Disconnect(&self) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_all_from(self.node_id());
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-output
    fn Disconnect_(&self, out: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output(self.node_id().output(out));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode
    fn Disconnect__(&self, to: &AudioNode) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_between(self.node_id(), to.node_id());
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output
    fn Disconnect___(&self, to: &AudioNode, out: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output_between(self.node_id().output(out), to.node_id());
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-output-input
    fn Disconnect____(&self, to: &AudioNode, out: u32, inp: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output_between_to(self.node_id().output(out), to.node_id().input(inp));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect
    fn Disconnect_____(&self, param: &AudioParam) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_to(self.node_id(),
                           param.node_id().param(param.param_type()));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect
    fn Disconnect______(&self, param: &AudioParam, out: u32) -> ErrorResult {
        self.context
            .audio_context_impl()
            .disconnect_output_between_to(self.node_id().output(out),
                                          param.node_id().param(param.param_type()));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-context
    fn Context(&self) -> DomRoot<BaseAudioContext> {
        DomRoot::from_ref(&self.context)
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-numberofinputs
    fn NumberOfInputs(&self) -> u32 {
        self.number_of_inputs
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-numberofoutputs
    fn NumberOfOutputs(&self) -> u32 {
        self.number_of_outputs
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount
    fn ChannelCount(&self) -> u32 {
        self.channel_count.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcount
    fn SetChannelCount(&self, value: u32) -> ErrorResult {
        match self.upcast::<EventTarget>().type_id() {
            EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioDestinationNode) => {
                if self.context.is_offline() {
                    return Err(Error::InvalidState);
                } else if value < 1 || value > MAX_CHANNEL_COUNT {
                    return Err(Error::IndexSize);
                }
            },
            EventTargetTypeId::AudioNode(AudioNodeTypeId::PannerNode) => {
                if value > 2 {
                    return Err(Error::NotSupported)
                }
            }
            // XXX We do not support any of the other AudioNodes with
            // constraints yet. Add more cases here as we add support
            // for new AudioNodes.
            _ => (),
        };

        if value == 0 || value > MAX_CHANNEL_COUNT {
            return Err(Error::NotSupported);
        }

        self.channel_count.set(value);
        self.message(AudioNodeMessage::SetChannelCount(value as u8));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode
    fn ChannelCountMode(&self) -> ChannelCountMode {
        self.channel_count_mode.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelcountmode
    fn SetChannelCountMode(&self, value: ChannelCountMode) -> ErrorResult {
        // Channel count mode has no effect for nodes with no inputs.
        if self.number_of_inputs == 0 {
            return Ok(());
        }

        match self.upcast::<EventTarget>().type_id() {
            EventTargetTypeId::AudioNode(AudioNodeTypeId::AudioDestinationNode) => {
                if self.context.is_offline() {
                    return Err(Error::InvalidState);
                }
            },
            EventTargetTypeId::AudioNode(AudioNodeTypeId::PannerNode) => {
                if value == ChannelCountMode::Max {
                    return Err(Error::NotSupported)
                }
            }
            // XXX We do not support any of the other AudioNodes with
            // constraints yet. Add more cases here as we add support
            // for new AudioNodes.
            _ => (),
        };

        self.channel_count_mode.set(value);
        self.message(AudioNodeMessage::SetChannelMode(value.into()));
        Ok(())
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelinterpretation
    fn ChannelInterpretation(&self) -> ChannelInterpretation {
        self.channel_interpretation.get()
    }

    // https://webaudio.github.io/web-audio-api/#dom-audionode-channelinterpretation
    fn SetChannelInterpretation(&self, value: ChannelInterpretation) {
        // Channel interpretation mode has no effect for nodes with no inputs.
        if self.number_of_inputs == 0 {
            return;
        }

        self.channel_interpretation.set(value);
        self.message(AudioNodeMessage::SetChannelInterpretation(value.into()));
    }
}

impl From<ChannelCountMode> for ServoMediaChannelCountMode {
    fn from(mode: ChannelCountMode) -> Self {
        match mode {
            ChannelCountMode::Max => ServoMediaChannelCountMode::Max,
            ChannelCountMode::Clamped_max => ServoMediaChannelCountMode::ClampedMax,
            ChannelCountMode::Explicit => ServoMediaChannelCountMode::Explicit,
        }
    }
}

impl From<ChannelInterpretation> for ServoMediaChannelInterpretation {
    fn from(interpretation: ChannelInterpretation) -> Self {
        match interpretation {
            ChannelInterpretation::Discrete => ServoMediaChannelInterpretation::Discrete,
            ChannelInterpretation::Speakers => ServoMediaChannelInterpretation::Speakers,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright(C) 2016 Chris Liebert
extern crate glium;
extern crate image;
extern crate nalgebra;

use std::collections::HashMap;

use common;
use glium::backend::glutin_backend::GlutinFacade;
use glium::Program;
use glium::Surface;
use self::nalgebra::{Matrix4, PerspectiveMatrix3};

use common::{Scene, Vertex8f32};
implement_vertex!(Vertex8f32, position, normal, texcoord);

pub struct Renderer {
	pub index_buffer: glium::index::NoIndices,
	pub modelview_matrix_array: [[f32; 4]; 4],
	pub program: Program,
	pub projection_matrix: Matrix4<f32>,
    pub scene: Scene,
    pub textures: HashMap<String, glium::texture::CompressedSrgbTexture2d>,
    pub vertex_buffers: Vec<glium::vertex::VertexBuffer<common::Vertex8f32>>,
}

impl Renderer {
    pub fn new(display: &GlutinFacade, scene: Scene) -> Renderer {

    let mut vertex_buffers: Vec<glium::vertex::VertexBuffer<common::Vertex8f32>> = Vec::with_capacity(scene.meshes.len());

    for i in 0..scene.meshes.len() {
        vertex_buffers.push(glium::vertex::VertexBuffer::new(display, &scene.meshes[i].vertices).unwrap());
    }

    let index_buffer: glium::index::NoIndices =
        glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    if scene.shaders.len() == 0 {
        panic!("No shaders loaded");
    }

	let mut textures: HashMap<String, glium::texture::CompressedSrgbTexture2d> = HashMap::new();

	for i in 0..scene.images.len() {
		//TODO: determine image format by name extension if nessisary
		//let image = image::load_from_memory_with_format(&scene.images[i].image, image::PNG).unwrap().to_rgba();
		let image = image::load_from_memory(&scene.images[i].image).unwrap().to_rgba();

	    let image_dimensions = image.dimensions();
	    let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(),
	                                                                   image_dimensions);
	    let opengl_texture: glium::texture::CompressedSrgbTexture2d = glium::texture::CompressedSrgbTexture2d::new(display, image).unwrap();
		textures.insert(scene.images[i].name.clone(), opengl_texture);
	}

    // Load the first shader in the database by default
    // TODO: load from config settings table?
    let shader_index = 0;

    // println!("Using glsl version {}", glsl_version);

    let program: Program = program!(display,
    	/*
			110 => {
				vertex: &scene.shaders[shader_index].vertex_source_110,
				fragment: &scene.shaders[shader_index].fragment_source_110,
			},
			120 => {
				vertex: &scene.shaders[shader_index].vertex_source_120,
				fragment: &scene.shaders[shader_index].fragment_source_120,
			},
			130 => {
				vertex: &scene.shaders[shader_index].vertex_source_130,
				fragment: &scene.shaders[shader_index].fragment_source_130,
			},
			*/
			140 => {
				vertex: &scene.shaders[shader_index].vertex_source_140,
				fragment: &scene.shaders[shader_index].fragment_source_140,
			},
			/*
			150 => {
				vertex: &scene.shaders[shader_index].vertex_source_150,
				fragment: &scene.shaders[shader_index].fragment_source_150,
			},
			300 => {
				vertex: &scene.shaders[shader_index].vertex_source_300,
				fragment: &scene.shaders[shader_index].fragment_source_300,
			},
			330 => {
				vertex: &scene.shaders[shader_index].vertex_source_330,
				fragment: &scene.shaders[shader_index].fragment_source_330,
			},
			400 => {
				vertex: &scene.shaders[shader_index].vertex_source_400,
				fragment: &scene.shaders[shader_index].fragment_source_400,
			},
			410 => {
				vertex: &scene.shaders[shader_index].vertex_source_410,
				fragment: &scene.shaders[shader_index].fragment_source_410,
			},
			420 => {
				vertex: &scene.shaders[shader_index].vertex_source_420,
				fragment: &scene.shaders[shader_index].fragment_source_420,
			},
			430 => {
				vertex: &scene.shaders[shader_index].vertex_source_430,
				fragment: &scene.shaders[shader_index].fragment_source_430,
			},
			100 es => {
				vertex: &scene.shaders[shader_index].vertex_source_100es,
				fragment: &scene.shaders[shader_index].fragment_source_100es,
			},
			300 es => {
				vertex: &scene.shaders[shader_index].vertex_source_300es,
				fragment: &scene.shaders[shader_index].fragment_source_300es,
			}, */
		)
        .unwrap();

    // Shader compilation no longer required
    display.release_shader_compiler();

    //TODO get width and height from display
    let screen_width = 400;
    let screen_height = 300;

    // Set up camera
    let projection_matrix = PerspectiveMatrix3::new(screen_width as f32 / screen_height as f32,
                                              45.0,
                                              0.1,
                                              1000.0);
    let projection_matrix_array: Matrix4<f32> = projection_matrix.to_matrix();

    let modelview_matrix_array = [[1.0f32, -0.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 1.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 0.0f32, 1.0f32, 0.0f32],
                                  [-5.0f32, -3.0f32, -12.0f32, 1.0f32]];
        Renderer {
        	index_buffer: index_buffer,
        	modelview_matrix_array: modelview_matrix_array,
        	program: program,
        	projection_matrix: projection_matrix_array,
        	scene: scene,
        	textures: textures,
        	vertex_buffers: vertex_buffers,
        }
    }

    pub fn render(&self, display: &GlutinFacade) {
		 let mut target = display.draw();
	 	// TODO: generate this texture instead of loading from sqlite
		let default_blank_texture = &self.textures["DEFAULT_BLANK_TEXTURE.png"];
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        for i in 0..self.vertex_buffers.len() {
            let material_id: i32 = self.scene.meshes[i as usize].material_id.clone();
            let diffuse = self.scene.materials[material_id as usize].diffuse.clone();
            let diffuse_texname: String =
                self.scene.materials[material_id as usize].diffuse_texname.clone();
            let opengl_texture: &glium::texture::CompressedSrgbTexture2d;
            if diffuse_texname.len() == 0 {
                opengl_texture = &default_blank_texture;
            } else {
                opengl_texture = &self.textures[&diffuse_texname];
            }
            let uniforms: glium::uniforms::UniformsStorage<_, _> = uniform! {
		        projection: *self.projection_matrix.as_ref(),
		        modelview: self.modelview_matrix_array,
		        light_position_worldspace: [2.0, 10.0, 1.0f32],
		        diffuse: diffuse,
		        diffuse_texture: opengl_texture,
		    };

            target.draw(&self.vertex_buffers[i],
                      &self.index_buffer,
                      &self.program,
                      &uniforms,
                      &Default::default())
                .unwrap();
        }
        target.finish().unwrap();
    }
}
<commit_msg>changed material_id to material_index<commit_after>// Copyright(C) 2016 Chris Liebert
extern crate glium;
extern crate image;
extern crate nalgebra;

use std::collections::HashMap;

use common;
use glium::backend::glutin_backend::GlutinFacade;
use glium::Program;
use glium::Surface;
use self::nalgebra::{Matrix4, PerspectiveMatrix3};

use common::{Scene, Vertex8f32};
implement_vertex!(Vertex8f32, position, normal, texcoord);

pub struct Renderer {
	pub index_buffer: glium::index::NoIndices,
	pub modelview_matrix_array: [[f32; 4]; 4],
	pub program: Program,
	pub projection_matrix: Matrix4<f32>,
    pub scene: Scene,
    pub textures: HashMap<String, glium::texture::CompressedSrgbTexture2d>,
    pub vertex_buffers: Vec<glium::vertex::VertexBuffer<common::Vertex8f32>>,
}

impl Renderer {
    pub fn new(display: &GlutinFacade, scene: Scene) -> Renderer {

    let mut vertex_buffers: Vec<glium::vertex::VertexBuffer<common::Vertex8f32>> = Vec::with_capacity(scene.meshes.len());

    for i in 0..scene.meshes.len() {
        vertex_buffers.push(glium::vertex::VertexBuffer::new(display, &scene.meshes[i].vertices).unwrap());
    }

    let index_buffer: glium::index::NoIndices =
        glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    if scene.shaders.len() == 0 {
        panic!("No shaders loaded");
    }

	let mut textures: HashMap<String, glium::texture::CompressedSrgbTexture2d> = HashMap::new();

	for i in 0..scene.images.len() {
		//TODO: determine image format by name extension if nessisary
		//let image = image::load_from_memory_with_format(&scene.images[i].image, image::PNG).unwrap().to_rgba();
		let image = image::load_from_memory(&scene.images[i].image).unwrap().to_rgba();

	    let image_dimensions = image.dimensions();
	    let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(),
	                                                                   image_dimensions);
	    let opengl_texture: glium::texture::CompressedSrgbTexture2d = glium::texture::CompressedSrgbTexture2d::new(display, image).unwrap();
		textures.insert(scene.images[i].name.clone(), opengl_texture);
	}

    // Load the first shader in the database by default
    // TODO: load from config settings table?
    let shader_index = 0;

    // println!("Using glsl version {}", glsl_version);

    let program: Program = program!(display,
    	/*
			110 => {
				vertex: &scene.shaders[shader_index].vertex_source_110,
				fragment: &scene.shaders[shader_index].fragment_source_110,
			},
			120 => {
				vertex: &scene.shaders[shader_index].vertex_source_120,
				fragment: &scene.shaders[shader_index].fragment_source_120,
			},
			130 => {
				vertex: &scene.shaders[shader_index].vertex_source_130,
				fragment: &scene.shaders[shader_index].fragment_source_130,
			},
			*/
			140 => {
				vertex: &scene.shaders[shader_index].vertex_source_140,
				fragment: &scene.shaders[shader_index].fragment_source_140,
			},
			/*
			150 => {
				vertex: &scene.shaders[shader_index].vertex_source_150,
				fragment: &scene.shaders[shader_index].fragment_source_150,
			},
			300 => {
				vertex: &scene.shaders[shader_index].vertex_source_300,
				fragment: &scene.shaders[shader_index].fragment_source_300,
			},
			330 => {
				vertex: &scene.shaders[shader_index].vertex_source_330,
				fragment: &scene.shaders[shader_index].fragment_source_330,
			},
			400 => {
				vertex: &scene.shaders[shader_index].vertex_source_400,
				fragment: &scene.shaders[shader_index].fragment_source_400,
			},
			410 => {
				vertex: &scene.shaders[shader_index].vertex_source_410,
				fragment: &scene.shaders[shader_index].fragment_source_410,
			},
			420 => {
				vertex: &scene.shaders[shader_index].vertex_source_420,
				fragment: &scene.shaders[shader_index].fragment_source_420,
			},
			430 => {
				vertex: &scene.shaders[shader_index].vertex_source_430,
				fragment: &scene.shaders[shader_index].fragment_source_430,
			},
			100 es => {
				vertex: &scene.shaders[shader_index].vertex_source_100es,
				fragment: &scene.shaders[shader_index].fragment_source_100es,
			},
			300 es => {
				vertex: &scene.shaders[shader_index].vertex_source_300es,
				fragment: &scene.shaders[shader_index].fragment_source_300es,
			}, */
		)
        .unwrap();

    // Shader compilation no longer required
    display.release_shader_compiler();

    //TODO get width and height from display
    let screen_width = 400;
    let screen_height = 300;

    // Set up camera
    let projection_matrix = PerspectiveMatrix3::new(screen_width as f32 / screen_height as f32,
                                              45.0,
                                              0.1,
                                              1000.0);
    let projection_matrix_array: Matrix4<f32> = projection_matrix.to_matrix();

    let modelview_matrix_array = [[1.0f32, -0.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 1.0f32, -0.0f32, 0.0f32],
                                  [0.0f32, 0.0f32, 1.0f32, 0.0f32],
                                  [-5.0f32, -3.0f32, -12.0f32, 1.0f32]];
        Renderer {
        	index_buffer: index_buffer,
        	modelview_matrix_array: modelview_matrix_array,
        	program: program,
        	projection_matrix: projection_matrix_array,
        	scene: scene,
        	textures: textures,
        	vertex_buffers: vertex_buffers,
        }
    }

    pub fn render(&self, display: &GlutinFacade) {
		 let mut target = display.draw();
	 	// TODO: generate this texture instead of loading from sqlite
		let default_blank_texture = &self.textures["DEFAULT_BLANK_TEXTURE.png"];
        target.clear_color(0.0, 0.0, 0.0, 1.0);
        for i in 0..self.vertex_buffers.len() as usize {
            let material_index: usize = self.scene.meshes[i].material_index.clone();
            let diffuse = self.scene.materials[material_index].diffuse.clone();
            let diffuse_texname: String =
                self.scene.materials[material_index as usize].diffuse_texname.clone();
            let opengl_texture: &glium::texture::CompressedSrgbTexture2d;
            if diffuse_texname.len() == 0 {
                opengl_texture = &default_blank_texture;
            } else {
                opengl_texture = &self.textures[&diffuse_texname];
            }
            let uniforms: glium::uniforms::UniformsStorage<_, _> = uniform! {
		        projection: *self.projection_matrix.as_ref(),
		        modelview: self.modelview_matrix_array,
		        light_position_worldspace: [2.0, 10.0, 1.0f32],
		        diffuse: diffuse,
		        diffuse_texture: opengl_texture,
		    };

            target.draw(&self.vertex_buffers[i],
                      &self.index_buffer,
                      &self.program,
                      &uniforms,
                      &Default::default())
                .unwrap();
        }
        target.finish().unwrap();
    }
}
<|endoftext|>


<commit_before>use rustc_serialize::Decodable;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::operations::search::Sort;
use rs_es::operations::index::IndexResult;
use rs_es::operations::mapping::*;
use rs_es::error::EsError;

use params::*;

use std::fmt::Debug;

pub trait Resource : Decodable + Debug {
  fn search(mut es: &mut Client, default_index: &str, params: &Map) -> Vec<u32>;
  fn index(&self, mut es: &mut Client, index: &str) -> Result<IndexResult, EsError>;
  fn reset_index(mut es: &mut Client, index: &str) -> Result<MappingResult, EsError>;
  fn visibility_filters(epoch: &str, presented_talents: Vec<i32>) -> Vec<Filter>;
  fn search_filters(params: &Map, epoch: &str) -> Query;
  fn sorting_criteria() -> Sort;
}
<commit_msg>resource: require trait impls to implement Send + Sync + Any<commit_after>use rustc_serialize::Decodable;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::operations::search::Sort;
use rs_es::operations::index::IndexResult;
use rs_es::operations::mapping::*;
use rs_es::error::EsError;

use params::*;

use std::any::Any;
use std::fmt::Debug;

pub trait Resource : Send + Sync + Any + Decodable + Debug {
  fn search(mut es: &mut Client, default_index: &str, params: &Map) -> Vec<u32>;
  fn index(&self, mut es: &mut Client, index: &str) -> Result<IndexResult, EsError>;
  fn reset_index(mut es: &mut Client, index: &str) -> Result<MappingResult, EsError>;
  fn visibility_filters(epoch: &str, presented_talents: Vec<i32>) -> Vec<Filter>;
  fn search_filters(params: &Map, epoch: &str) -> Query;
  fn sorting_criteria() -> Sort;
}
<|endoftext|>


<commit_before>use seed::prelude::*;
use crate::Msg;

extern crate num_bigint as bigint;
extern crate num_traits;

use crate::utils::nth;
use regex::Regex;

mod numerics_to_text {
    use num_traits::{Num,ToPrimitive,Zero};
    use num_bigint::{BigUint,ToBigUint};

    pub fn convert(str_num: String) -> String{
        let units:Vec<&str> = vec![
            "",
            " one",
            " two",
            " three",
            " four",
            " five",
            " six",
            " seven",
            " eight",
            " nine",
            " ten",
            " eleven",
            " twelve",
            " thirteen",
            " fourteen",
            " fifteen",
            " sixteen",
            " seventeen",
            " eighteen",
            " nineteen"
        ];
        let tens:Vec<&str> = vec![
            "",
            "",
            " twenty",
            " thirty",
            " forty",
            " fifty",
            " sixty",
            " seventy",
            " eighty",
            " ninety"
        ];
        let triplets:Vec<&str> = vec![
            "",
            " thousand",
            " million",
            " billion",
            " trillion",
            " quadrillion",
            " quintillion",
            " sextillion",
            " septillion",
            " octillion",
            " nonillion",
            " decillion",
            " undecillion",
            " duodecillion",
            " tredecillion",
            " quattuordecillion",
            " quindecillion",
            " sexdecillion",
            " septendecillion",
            " octodecillion",
            " novemdecillion",
            " vigintillion",
            " unvigintillion",
            " duovigintillion",
            " tresvigintillion",
            " quattuorvigintillion",
            " quinquavigintillion",
            " sesvigintillion",
            " septemvigintillion",
            " octovigintillion",
            " novemvigintillion",
            " trigintillion",
            " untrigintillion",
            " duotrigintillion",
            " trestrigintillion",
            " quattuortrigintillion",
            " quinquatrigintillion",
            " sestrigintillion",
            " septentrigintillion",
            " octotrigintillion",
            " noventrigintillion",
            " quadragintillion",
            " ERROR ",
        ];

        let num:BigUint = num_bigint::BigUint::from_str_radix(&str_num, 10).unwrap();
        convert_tri(num, Zero::zero(), units, tens, triplets)
    }

    fn convert_tri(num:BigUint, tri:usize, units:Vec<&str>, tens:Vec<&str>, triplets:Vec<&str>) -> String {
        // chunk the number, ...rxyy
        let ten:BigUint = 10.to_biguint().unwrap();
        let hundred:BigUint = 100.to_biguint().unwrap();
        let thousand:BigUint = 1000.to_biguint().unwrap();
        let r = &num / &thousand; // this in theory is rounding down to an int
        let x:usize = ((&num / &hundred) % &ten).to_usize().unwrap();
        let y:usize = (&num % &hundred).to_usize().unwrap();
        // init the output string
        let mut string:String = "".to_owned();
        // do hundreds
        if x > 0 {
            string.push_str(units[x]);
            string.push_str(" hundred");
        }
        // do units and tens
        if y < 20 {
            string.push_str(units[y]);
        } else {
            string.push_str(tens[(y / 10)]);
            string.push_str(units[(y / 10)]);
        }
        // add triplet modifier only if there
        // is some output to be modified...
        if string != "" {
            match triplets[tri] {
                _s => string.push_str(&triplets[tri].to_string()),
            }
        }
        // continue recursing?
        if r > Zero::zero() {
            let mut string2:String = convert_tri(r, tri + 1, units, tens, triplets);
            string2.push_str(&string);
            string2
        } else {
            string
        }
    }

}

fn html_form() -> seed::dom_types::Node<Msg> {
    div![
        h1!["Number Cruncher"],
        "Welcome to the number cruncher.
        Type in a number in the box below and we will crunch it for you.",
        br![],
        br![],
        "You will be taken to a page that tells you the following information about your number:",
        ul![
            li!["Is it odd or even?"],
            li!["Is it a palindrome?"],
            li!["Is it a prime number? (Checks numbers upto 11 digits in length)"],
            li!["Is it a ",a!["mersenne prime", attrs!{At::Class => "link", At::Href => "https://en.wikipedia.org/wiki/Mersenne_prime"}],"? (Checks numbers upto 2993 digits in length)"],
            li!["Is it a ",a!["fermat prime", attrs!{At::Class => "link", At::Href => "https://www.fermatsearch.org/"}],"? (Checks numbers upto 155 digits in length"],
            li!["Is it a ",a!["perfect number", attrs!{At::Class => "link", At::Href => "https://en.wikipedia.org/wiki/Perfect_number"}],"? (Checks numbers upto 12003 digits in length)"],
            li!["Is it a triangle number? (Checks numbers upto 40 digits in length)"],
            li!["Is it a square number?"],
            li!["Is it a cube number? (Checks numbers upto 17 digits in length)"],
            li!["Is it a factorial number?"],
            br![],
            br![],
            li!["All factors of the number will be listed (for numbers upto 9 digits)"],
            li!["The page will also show a list of base conversions. e.g. binary, octal and hexadecimal (Numbers upto 500 digits in length)"],
            li!["The number will be converted to roman numerals (Upto 6 digits in length)"],
            li!["The number will be converted to egyptian numerals (Upto 7 digits in length)"],
            li!["The number will be converted to chinese numerals (Upto 6 digits in length)"],
            li!["The number will be converted to babylonian numerals (Upto 13 digits in length)"],
        ],
        br![],
        br![],
	    "Please type your number here:",
        form![attrs!{At::Name => "crunchy", At::Action => "/cruncher/", At::Method => "get", At::Target => "_top"},
            div![
                textarea![attrs!{At::Name => "number", At::Cols => "85", At::Rows => "10", At::OnKeyDown => "if (event.keyCode == 13){document.location='/cruncher/'+crunchy.number.value+'/'}"}],
                br![],
                input![attrs!{At::Type => "button", At::Value => "Crunch", At::OnClick => "document.location='/cruncher/'+crunchy.number.value+'/'"}],
            ],
        ],
    ]
}

fn html_crunched_number(slug:String) -> seed::dom_types::Node<Msg> {
    let table_style = style!{"border" => "1px #000 solid"};
    div![style!{"width" => "75%"; "padding" => "3px"},
        br![],
        br![],
        b!["The number you submitted to be crunched was:"],
        h1![slug.to_string()," - ",numerics_to_text::convert(slug.to_string())],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![
                        "It is an odd number.",
                        br![],
                        "It is the 4th prime number.",
                        br![],
                        "It is the 2nd ",a!["mersenne prime", attrs!{At::Class => "link", At::Href => "http://en.wikipedia.org/wiki/Mersenne_prime"}],".",
                        br![],
                        "It is not a ",a!["fermat prime", attrs!{At::Class => "link", At::Href => "https://www.fermatsearch.org/"}],".",
                        br![],
                        "It is not a ",a!["perfect number", attrs!{At::Class => "link", At::Href => "https://en.wikipedia.org/wiki/Perfect_number"}],".",
                        br![],
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![
                        "It is not a triangle number.",
                        br![],
                        "It is not a square number.",
                        br![],
                        "It is not a cube number.",
                        br![],
                        br![],
                        "It is not a factorial number.",
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![
                        "It it has no factors except itself and 1.",
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 2 (Binary):",
                    ],
                    td![
                        "111",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 3 (Ternary):",
                    ],
                    td![
                        "21",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 4 (Quaternary):",
                    ],
                    td![
                        "13",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 5 (Quinary):",
                    ],
                    td![
                        "12",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 8 (Octal):",
                    ],
                    td![
                        "7",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 10 (Denary):",
                    ],
                    td![
                        "7",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 16 (Hexadecimal):",
                    ],
                    td![
                        "7",
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![attrs!{At::Width => "200"},
                        "Roman Numerals:",
                    ],
                    td![attrs!{At::Width => "40"},
                        "VII",
                    ],
                ],
                tr![
                    td![
                        "Egyptian Numerals:",
                    ],
                    td![
                        "𓐀",
                    ]
                ],
                tr![
                    td![
                        "Chinese Numerals:",
                    ],
                    td![style!{"vertical-align" => "middle"; "background-color" => "#FFF"},
                        "柒",
                    ]
                ],
                tr![
                    td![
                        "Babylonian Numerals:",
                    ],
                    td![style!{"vertical-align" => "middle"; "background-color" => "#FFF"},
                        img![attrs!{At::Src => "https://static.bigprimes.net/imgs/babnumbers/bab_7.gif", At::Alt => "7"}],
                    ]
                ],
            ],
        ],
    ]
}

pub fn render(slug:String) -> seed::dom_types::Node<Msg> {
    let rgx = Regex::new(r"^([1-9]+[0-9]*)$").unwrap();

    match rgx.is_match(&slug) {
        true => html_crunched_number(slug),
        _ => html_form()
    }
}
<commit_msg>added call to nth() to prevent warning<commit_after>use seed::prelude::*;
use crate::Msg;

extern crate num_bigint as bigint;
extern crate num_traits;

use crate::utils::nth;
use regex::Regex;

mod numerics_to_text {
    use num_traits::{Num,ToPrimitive,Zero};
    use num_bigint::{BigUint,ToBigUint};

    pub fn convert(str_num: String) -> String{
        let units:Vec<&str> = vec![
            "",
            " one",
            " two",
            " three",
            " four",
            " five",
            " six",
            " seven",
            " eight",
            " nine",
            " ten",
            " eleven",
            " twelve",
            " thirteen",
            " fourteen",
            " fifteen",
            " sixteen",
            " seventeen",
            " eighteen",
            " nineteen"
        ];
        let tens:Vec<&str> = vec![
            "",
            "",
            " twenty",
            " thirty",
            " forty",
            " fifty",
            " sixty",
            " seventy",
            " eighty",
            " ninety"
        ];
        let triplets:Vec<&str> = vec![
            "",
            " thousand",
            " million",
            " billion",
            " trillion",
            " quadrillion",
            " quintillion",
            " sextillion",
            " septillion",
            " octillion",
            " nonillion",
            " decillion",
            " undecillion",
            " duodecillion",
            " tredecillion",
            " quattuordecillion",
            " quindecillion",
            " sexdecillion",
            " septendecillion",
            " octodecillion",
            " novemdecillion",
            " vigintillion",
            " unvigintillion",
            " duovigintillion",
            " tresvigintillion",
            " quattuorvigintillion",
            " quinquavigintillion",
            " sesvigintillion",
            " septemvigintillion",
            " octovigintillion",
            " novemvigintillion",
            " trigintillion",
            " untrigintillion",
            " duotrigintillion",
            " trestrigintillion",
            " quattuortrigintillion",
            " quinquatrigintillion",
            " sestrigintillion",
            " septentrigintillion",
            " octotrigintillion",
            " noventrigintillion",
            " quadragintillion",
            " ERROR ",
        ];

        let num:BigUint = num_bigint::BigUint::from_str_radix(&str_num, 10).unwrap();
        convert_tri(num, Zero::zero(), units, tens, triplets)
    }

    fn convert_tri(num:BigUint, tri:usize, units:Vec<&str>, tens:Vec<&str>, triplets:Vec<&str>) -> String {
        // chunk the number, ...rxyy
        let ten:BigUint = 10.to_biguint().unwrap();
        let hundred:BigUint = 100.to_biguint().unwrap();
        let thousand:BigUint = 1000.to_biguint().unwrap();
        let r = &num / &thousand; // this in theory is rounding down to an int
        let x:usize = ((&num / &hundred) % &ten).to_usize().unwrap();
        let y:usize = (&num % &hundred).to_usize().unwrap();
        // init the output string
        let mut string:String = "".to_owned();
        // do hundreds
        if x > 0 {
            string.push_str(units[x]);
            string.push_str(" hundred");
        }
        // do units and tens
        if y < 20 {
            string.push_str(units[y]);
        } else {
            string.push_str(tens[(y / 10)]);
            string.push_str(units[(y / 10)]);
        }
        // add triplet modifier only if there
        // is some output to be modified...
        if string != "" {
            match triplets[tri] {
                _s => string.push_str(&triplets[tri].to_string()),
            }
        }
        // continue recursing?
        if r > Zero::zero() {
            let mut string2:String = convert_tri(r, tri + 1, units, tens, triplets);
            string2.push_str(&string);
            string2
        } else {
            string
        }
    }

}

fn html_form() -> seed::dom_types::Node<Msg> {
    div![
        h1!["Number Cruncher"],
        "Welcome to the number cruncher.
        Type in a number in the box below and we will crunch it for you.",
        br![],
        br![],
        "You will be taken to a page that tells you the following information about your number:",
        ul![
            li!["Is it odd or even?"],
            li!["Is it a palindrome?"],
            li!["Is it a prime number? (Checks numbers upto 11 digits in length)"],
            li!["Is it a ",a!["mersenne prime", attrs!{At::Class => "link", At::Href => "https://en.wikipedia.org/wiki/Mersenne_prime"}],"? (Checks numbers upto 2993 digits in length)"],
            li!["Is it a ",a!["fermat prime", attrs!{At::Class => "link", At::Href => "https://www.fermatsearch.org/"}],"? (Checks numbers upto 155 digits in length"],
            li!["Is it a ",a!["perfect number", attrs!{At::Class => "link", At::Href => "https://en.wikipedia.org/wiki/Perfect_number"}],"? (Checks numbers upto 12003 digits in length)"],
            li!["Is it a triangle number? (Checks numbers upto 40 digits in length)"],
            li!["Is it a square number?"],
            li!["Is it a cube number? (Checks numbers upto 17 digits in length)"],
            li!["Is it a factorial number?"],
            br![],
            br![],
            li!["All factors of the number will be listed (for numbers upto 9 digits)"],
            li!["The page will also show a list of base conversions. e.g. binary, octal and hexadecimal (Numbers upto 500 digits in length)"],
            li!["The number will be converted to roman numerals (Upto 6 digits in length)"],
            li!["The number will be converted to egyptian numerals (Upto 7 digits in length)"],
            li!["The number will be converted to chinese numerals (Upto 6 digits in length)"],
            li!["The number will be converted to babylonian numerals (Upto 13 digits in length)"],
        ],
        br![],
        br![],
	    "Please type your number here:",
        form![attrs!{At::Name => "crunchy", At::Action => "/cruncher/", At::Method => "get", At::Target => "_top"},
            div![
                textarea![attrs!{At::Name => "number", At::Cols => "85", At::Rows => "10", At::OnKeyDown => "if (event.keyCode == 13){document.location='/cruncher/'+crunchy.number.value+'/'}"}],
                br![],
                input![attrs!{At::Type => "button", At::Value => "Crunch", At::OnClick => "document.location='/cruncher/'+crunchy.number.value+'/'"}],
            ],
        ],
    ]
}

fn html_crunched_number(slug:String) -> seed::dom_types::Node<Msg> {
    let table_style = style!{"border" => "1px #000 solid"};
    div![style!{"width" => "75%"; "padding" => "3px"},
        br![],
        br![],
        b!["The number you submitted to be crunched was:"],
        h1![slug.to_string()," - ",numerics_to_text::convert(slug.to_string())],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![
                        "It is an odd number.",
                        br![],
                        "It is the ",nth(4)," prime number.",
                        br![],
                        "It is the 2nd ",a!["mersenne prime", attrs!{At::Class => "link", At::Href => "http://en.wikipedia.org/wiki/Mersenne_prime"}],".",
                        br![],
                        "It is not a ",a!["fermat prime", attrs!{At::Class => "link", At::Href => "https://www.fermatsearch.org/"}],".",
                        br![],
                        "It is not a ",a!["perfect number", attrs!{At::Class => "link", At::Href => "https://en.wikipedia.org/wiki/Perfect_number"}],".",
                        br![],
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![
                        "It is not a triangle number.",
                        br![],
                        "It is not a square number.",
                        br![],
                        "It is not a cube number.",
                        br![],
                        br![],
                        "It is not a factorial number.",
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![
                        "It it has no factors except itself and 1.",
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 2 (Binary):",
                    ],
                    td![
                        "111",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 3 (Ternary):",
                    ],
                    td![
                        "21",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 4 (Quaternary):",
                    ],
                    td![
                        "13",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 5 (Quinary):",
                    ],
                    td![
                        "12",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 8 (Octal):",
                    ],
                    td![
                        "7",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 10 (Denary):",
                    ],
                    td![
                        "7",
                    ],
                ],
                tr![
                    td![attrs!{At::Width => "200"},
                        "Base 16 (Hexadecimal):",
                    ],
                    td![
                        "7",
                    ],
                ],
            ],
        ],
        br![],
        br![],
        table![attrs!{At::Class => "text", At::Width => "100%"}, &table_style,
            tbody![
                tr![
                    td![attrs!{At::Width => "200"},
                        "Roman Numerals:",
                    ],
                    td![attrs!{At::Width => "40"},
                        "VII",
                    ],
                ],
                tr![
                    td![
                        "Egyptian Numerals:",
                    ],
                    td![
                        "𓐀",
                    ]
                ],
                tr![
                    td![
                        "Chinese Numerals:",
                    ],
                    td![style!{"vertical-align" => "middle"; "background-color" => "#FFF"},
                        "柒",
                    ]
                ],
                tr![
                    td![
                        "Babylonian Numerals:",
                    ],
                    td![style!{"vertical-align" => "middle"; "background-color" => "#FFF"},
                        img![attrs!{At::Src => "https://static.bigprimes.net/imgs/babnumbers/bab_7.gif", At::Alt => "7"}],
                    ]
                ],
            ],
        ],
    ]
}

pub fn render(slug:String) -> seed::dom_types::Node<Msg> {
    let rgx = Regex::new(r"^([1-9]+[0-9]*)$").unwrap();

    match rgx.is_match(&slug) {
        true => html_crunched_number(slug),
        _ => html_form()
    }
}
<|endoftext|>


<commit_before>// FIXME: add the support of transient objects

use any_cache::{Cache, HashCache};
use notify::{self, RecommendedWatcher, Watcher};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Class of types that can be loaded.
pub trait Load: Sized {
  /// Arguments passed at loading.
  type Args: Clone;

  /// Static string representing the name of the type (used in filesystem caching).
  const TY_STR: &'static str;

  fn load<P>(path: P, cache: &mut ResCache, args: Self::Args) -> Result<Self> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload: Load {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<T> Reload for T where T: Load<Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

pub type Result<T> = ::std::result::Result<T, LoadError>;

/// Resources are wrapped in this type.
#[derive(Debug)]
pub struct Res<T>(Rc<RefCell<T>>);

impl<T> Res<T> {
  pub fn new(resource: T) -> Self {
    Res(Rc::new(RefCell::new(resource)))
  }

  pub fn as_rc(&self) -> &Rc<RefCell<T>> {
    &self.0
  }
}

impl<T> Clone for Res<T> {
  fn clone(&self) -> Self {
    Res(self.0.clone())
  }
}

impl<T> Deref for Res<T> {
  type Target = RefCell<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T> From<Rc<RefCell<T>>> for Res<T> {
  fn from(rc: Rc<RefCell<T>>) -> Self {
    Res(rc)
  }
}

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource cache. Responsible for caching resource.
pub struct ResCache {
  // contains all the typed-erased Rc<RefCell<T>>
  cache: HashCache<PathBuf>,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // vector of pair (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

struct ResMetaData {
  on_reload: Box<Fn(&mut ResCache)>,
  last_update_instant: Instant
}

impl ResCache {
  /// Create a new cache.
  pub fn new<P>(root: P) -> Self where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let (wsx, wrx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(root);

      for event in wrx.iter() {
        if let notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } = event {
          dirty_.lock().unwrap().push((path.clone(), Instant::now()));
        }
      }
    });

    ResCache {
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    }
  }

  /// Inject a new resource in the cache.
  fn inject<T>(&mut self, path_buf: &PathBuf, resource: T, args: T::Args) -> Res<T> where T: 'static + Any + Reload {
    let res = Res(Rc::new(RefCell::new(resource)));
    let res_ = res.clone();

    let path_buf_ = path_buf.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut ResCache)> = Box::new(move |cache_| {
      match T::load(&path_buf_, cache_, args.clone()) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = new_resource;
          deb!("reloaded resource from {:?}", path_buf_);
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path_buf_, e);
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now()
    };

    // cache the resource and its meta data
    self.cache.save(path_buf.clone(), res.clone());
    self.metadata.insert(path_buf.clone(), metadata);

    res
  }

  /// Get a resource from the cache.
  pub fn get<T>(&mut self, key: &str, args: T::Args) -> Option<Res<T>> where T: 'static + Any + Reload {
    let path_str = format!("data/{}/{}", T::TY_STR, key);
    let path = Path::new(&path_str);
    let path_buf = path.to_owned();

    match self.cache.get::<Res<T>>(&path_buf).cloned() {
      r@Some(..) => {
        deb!("cache hit for {} ({})", key, path_str);
        r
      },
      None => {
        deb!("cache miss for {} ({})", key, path_str);

        // specific loading
        if path.exists() {
          match T::load(&path, self, args.clone()) {
            Ok(resource) => {
              Some(self.inject(&path_buf, resource, args))
            },
            Err(e) => {
              err!("unable to load resource from {}:\n{:#?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("resource at {} cannot be found", path_str);
          None
        }
      }
    }
  }

  /// Get a resource from the cache. If it fails, a proxy version is used, which will get replaced
  /// by the resource once it’s available.
  pub fn get_proxied<T, P>(&mut self, key: &str, args: T::Args, proxy: P) -> Option<Res<T>>
      where T: 'static + Any + Reload,
            P: FnOnce() -> T {
    self.get::<T>(key, args.clone()).or_else(move || {
      let path_str = format!("data/{}/{}", T::TY_STR, key);
      let path = Path::new(&path_str);
      let path_buf = path.to_owned();

      deb!("proxying resource {}", key);
      Some(self.inject(&path_buf, proxy(), args))
    })
  }

  /// Synchronize the cache by updating the resource that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let mut metadata = self.metadata.remove(path).unwrap();

      if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
        (metadata.on_reload)(self);
      }

      metadata.last_update_instant = *instant;
      self.metadata.insert(path.clone(), metadata);
    }

    dirty_.clear();
  }
}
<commit_msg>Correct implementation of proxied resource.<commit_after>// FIXME: add the support of transient objects

use any_cache::{Cache, HashCache};
use notify::{self, RecommendedWatcher, Watcher};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Class of types that can be loaded.
pub trait Load: Sized {
  /// Arguments passed at loading.
  type Args: Clone;

  /// Static string representing the name of the type (used in filesystem caching).
  const TY_STR: &'static str;

  fn load<P>(path: P, cache: &mut ResCache, args: Self::Args) -> Result<Self> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload: Load {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<T> Reload for T where T: Load<Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

pub type Result<T> = ::std::result::Result<T, LoadError>;

/// Resources are wrapped in this type.
#[derive(Debug)]
pub struct Res<T>(Rc<RefCell<T>>);

impl<T> Res<T> {
  pub fn new(resource: T) -> Self {
    Res(Rc::new(RefCell::new(resource)))
  }

  pub fn as_rc(&self) -> &Rc<RefCell<T>> {
    &self.0
  }
}

impl<T> Clone for Res<T> {
  fn clone(&self) -> Self {
    Res(self.0.clone())
  }
}

impl<T> Deref for Res<T> {
  type Target = RefCell<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T> From<Rc<RefCell<T>>> for Res<T> {
  fn from(rc: Rc<RefCell<T>>) -> Self {
    Res(rc)
  }
}

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource cache. Responsible for caching resource.
pub struct ResCache {
  // contains all the typed-erased Rc<RefCell<T>>
  cache: HashCache<PathBuf>,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // vector of pair (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

struct ResMetaData {
  on_reload: Box<Fn(&mut ResCache)>,
  last_update_instant: Instant
}

impl ResCache {
  /// Create a new cache.
  pub fn new<P>(root: P) -> Self where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let (wsx, wrx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(root);

      for event in wrx.iter() {
        if let notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } = event {
          dirty_.lock().unwrap().push((path.clone(), Instant::now()));
        }
      }
    });

    ResCache {
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    }
  }

  /// Inject a new resource in the cache.
  fn inject<T>(&mut self, path_buf: &PathBuf, resource: T, args: T::Args) -> Res<T> where T: 'static + Any + Reload {
    let res = Res(Rc::new(RefCell::new(resource)));
    let res_ = res.clone();

    let path_buf_ = path_buf.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut ResCache)> = Box::new(move |cache_| {
      match T::load(&path_buf_, cache_, args.clone()) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = new_resource;
          deb!("reloaded resource from {:?}", path_buf_);
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path_buf_, e);
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now()
    };

    // cache the resource and its meta data
    self.cache.save(path_buf.clone(), res.clone());
    self.metadata.insert(path_buf.clone(), metadata);

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<T>(&mut self, key: &str, args: T::Args) -> Result<Res<T>> where T: 'static + Any + Reload {
    let path_str = format!("data/{}/{}", T::TY_STR, key);
    let path = Path::new(&path_str);
    let path_buf = path.to_owned();

    match self.cache.get::<Res<T>>(&path_buf).cloned() {
      Some(resource) => {
        deb!("cache hit for {} ({})", key, path_str);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {} ({})", key, path_str);

        // specific loading
        if path.exists() {
          let resource = T::load(&path, self, args.clone())?;
          Ok(self.inject(&path_buf, resource, args))
        } else {
          Err(LoadError::FileNotFound(path_buf, String::new()))
        }
      }
    }
  }

  /// Get a resource from the cache.
  pub fn get<T>(&mut self, key: &str, args: T::Args) -> Option<Res<T>> where T: 'static + Any + Reload {
    match self.get_(key, args) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("{:?}", e);
        None
      }
    }
  }

  /// Get a resource from the cache. If it fails, a proxy version is used, which will get replaced
  /// by the resource once it’s available.
  pub fn get_proxied<T, P>(&mut self, key: &str, args: T::Args, proxy: P) -> Option<Res<T>>
      where T: 'static + Any + Reload,
            P: FnOnce() -> T {
    match self.get_::<T>(key, args.clone()) {
      Ok(resource) => Some(resource),
      Err(e) => {

        let path_str = format!("data/{}/{}", T::TY_STR, key);
        let path = Path::new(&path_str);
        let path_buf = path.to_owned();

        warn!("proxied resource {} because: {:?}", key, e);

        Some(self.inject(&path_buf, proxy(), args))
      }
    }
  }

  /// Synchronize the cache by updating the resource that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let mut metadata = self.metadata.remove(path).unwrap();

      if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
        (metadata.on_reload)(self);
      }

      metadata.last_update_instant = *instant;
      self.metadata.insert(path.clone(), metadata);
    }

    dirty_.clear();
  }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

pub struct JsType {
    uid: u64,
    thing: Box<JsThing>,
}

impl PartialEq for JsType {
    fn eq(&self, other: &Self) -> bool {
        self.uid == other.uid
    }

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

impl Eq for JsType{}

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

impl Ord for JsType {
    fn cmp(&self, other: &Self) -> Ordering {
        self.uid.cmp(&other.uid)
    }
}

pub trait JsThing {}
<commit_msg>Make JsType Hash instead of Ord<commit_after>use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use uuid::Uuid;

pub struct JsType {
    uid: Uuid,
    thing: Box<JsThing>,
}

impl JsType {
    fn new(thing: Box<JsThing>) -> JsType {
        JsType {
            uid: Uuid::new_v4(),
            thing: thing,
        }
    }
}

impl PartialEq for JsType {
    fn eq(&self, other: &Self) -> bool {
        self.uid == other.uid
    }

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

impl Eq for JsType{}

impl Hash for JsType {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.uid.hash(state);
    }

    fn hash_slice<H>(data: &[Self], state: &mut H) where H: Hasher {
        for ref d in data {
            d.uid.hash(state);
        }
    }
}

pub trait JsThing {}
<|endoftext|>


<commit_before>#[doc = "Prunes branches of the document tree that contain no documentation"];

export mk_pass;

fn mk_pass() -> pass {
    run
}

type ctxt = {
    mutable have_docs: bool
};

fn run(
    _srv: astsrv::srv,
    doc: doc::cratedoc
) -> doc::cratedoc {
    let ctxt = {
        mutable have_docs: true
    };
    let fold = fold::fold({
        fold_fn: fold_fn,
        fold_fnlist: fold_fnlist
        with *fold::default_seq_fold(ctxt)
    });
    fold.fold_crate(fold, doc)
}

fn fold_fn(
    fold: fold::fold<ctxt>,
    doc: doc::fndoc
) -> doc::fndoc {
    fold.ctxt.have_docs =
        doc.brief != none
        || doc.desc != none
        || doc.return.desc != none;
    ret doc;
}

fn fold_fnlist(
    fold: fold::fold<ctxt>,
    list: doc::fnlist
) -> doc::fnlist {
    doc::fnlist(vec::filter_map(*list) {|doc|
        let doc = fold_fn(fold, doc);
        if fold.ctxt.have_docs {
            some(doc)
        } else {
            none
        }
    })
}

#[cfg(test)]
mod tests {
    #[test]
    fn should_elide_undocumented_fns() {
        let source = "fn a() { }";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = run(srv, doc);
        assert vec::is_empty(*doc.topmod.fns);
    }
}<commit_msg>rustdoc: Prune undocumented modules<commit_after>#[doc = "Prunes branches of the document tree that contain no documentation"];

export mk_pass;

fn mk_pass() -> pass {
    run
}

type ctxt = {
    mutable have_docs: bool
};

fn run(
    _srv: astsrv::srv,
    doc: doc::cratedoc
) -> doc::cratedoc {
    let ctxt = {
        mutable have_docs: true
    };
    let fold = fold::fold({
        fold_mod: fold_mod,
        fold_fn: fold_fn,
        fold_modlist: fold_modlist,
        fold_fnlist: fold_fnlist
        with *fold::default_seq_fold(ctxt)
    });
    fold.fold_crate(fold, doc)
}

fn fold_mod(
    fold: fold::fold<ctxt>,
    doc: doc::moddoc
) -> doc::moddoc {
    let doc = fold::default_seq_fold_mod(fold, doc);
    fold.ctxt.have_docs =
        doc.brief != none
        || doc.desc != none
        || vec::is_not_empty(*doc.mods)
        || vec::is_not_empty(*doc.fns);
    ret doc;
}

fn fold_fn(
    fold: fold::fold<ctxt>,
    doc: doc::fndoc
) -> doc::fndoc {
    fold.ctxt.have_docs =
        doc.brief != none
        || doc.desc != none
        || doc.return.desc != none;
    ret doc;
}

fn fold_modlist(
    fold: fold::fold<ctxt>,
    list: doc::modlist
) -> doc::modlist {
    doc::modlist(vec::filter_map(*list) {|doc|
        let doc = fold_mod(fold, doc);
        if fold.ctxt.have_docs {
            some(doc)
        } else {
            none
        }
    })
}

fn fold_fnlist(
    fold: fold::fold<ctxt>,
    list: doc::fnlist
) -> doc::fnlist {
    doc::fnlist(vec::filter_map(*list) {|doc|
        let doc = fold_fn(fold, doc);
        if fold.ctxt.have_docs {
            some(doc)
        } else {
            none
        }
    })
}

#[cfg(test)]
mod tests {
    #[test]
    fn should_elide_undocumented_mods() {
        let source = "mod a { }";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = run(srv, doc);
        assert vec::is_empty(*doc.topmod.mods);
    }

    #[test]
    fn should_not_elide_undocument_mods_with_documented_mods() {
        let source = "mod a { #[doc = \"b\"] mod b { } }";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = attr_pass::mk_pass()(srv, doc);
        let doc = run(srv, doc);
        assert vec::is_not_empty(*doc.topmod.mods);
    }

    #[test]
    fn should_not_elide_undocument_mods_with_documented_fns() {
        let source = "mod a { #[doc = \"b\"] fn b() { } }";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = attr_pass::mk_pass()(srv, doc);
        let doc = run(srv, doc);
        assert vec::is_not_empty(*doc.topmod.mods);
    }

    #[test]
    fn should_elide_undocumented_fns() {
        let source = "fn a() { }";
        let srv = astsrv::mk_srv_from_str(source);
        let doc = extract::from_srv(srv, "");
        let doc = run(srv, doc);
        assert vec::is_empty(*doc.topmod.fns);
    }
}<|endoftext|>