<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 map::red_black_tree_map;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt::Display;
use std::iter::FromIterator;
use RedBlackTreeMap;

// TODO Use impl trait instead of this when available.
pub type Iter<'a, T> = red_black_tree_map::IterKeys<'a, T, ()>;

/// Creates a [`RedBlackTreeSet`](set/red_black_tree_set/struct.RedBlackTreeSet.html) containing the
/// given arguments:
///
/// ```
/// # use rpds::*;
/// #
/// let s = RedBlackTreeSet::new()
///     .insert(1)
///     .insert(2)
///     .insert(3);
///
/// assert_eq!(rbt_set![1, 2, 3], s);
/// ```
#[macro_export]
macro_rules! rbt_set {
    ($($e:expr),*) => {
        {
            #[allow(unused_mut)]
            let mut s = $crate::RedBlackTreeSet::new();
            $(
                s.insert_mut($e);
            )*
            s
        }
    };
}

/// A persistent set with structural sharing.  This implementation uses a
/// [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree).
///
/// # Complexity
///
/// Let *n* be the number of elements in the set.
///
/// ## Temporal complexity
///
/// | Operation                  | Best case | Average   | Worst case  |
/// |:-------------------------- | ---------:| ---------:| -----------:|
/// | `new()`                    |      Θ(1) |      Θ(1) |        Θ(1) |
/// | `insert()`                 |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `remove()`                 |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `get()`                    |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `contains()`               |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `size()`                   |      Θ(1) |      Θ(1) |        Θ(1) |
/// | `clone()`                  |      Θ(1) |      Θ(1) |        Θ(1) |
/// | iterator creation          |      Θ(1) |      Θ(1) |        Θ(1) |
/// | iterator step              |      Θ(1) |      Θ(1) |   Θ(log(n)) |
/// | iterator full              |      Θ(n) |      Θ(n) |        Θ(n) |
///
/// # Implementation details
///
/// This is a thin wrapper around a [`RedBlackTreeMap`](../../map/red_black_tree_map/struct.RedBlackTreeMap.html).
#[derive(Debug)]
pub struct RedBlackTreeSet<T>
where
    T: Ord,
{
    map: RedBlackTreeMap<T, ()>,
}

impl<T> RedBlackTreeSet<T>
where
    T: Ord,
{
    pub fn new() -> RedBlackTreeSet<T> {
        RedBlackTreeSet {
            map: RedBlackTreeMap::new(),
        }
    }

    pub fn insert(&self, v: T) -> RedBlackTreeSet<T> {
        RedBlackTreeSet {
            map: self.map.insert(v, ()),
        }
    }

    pub fn insert_mut(&mut self, v: T) {
        self.map.insert_mut(v, ());
    }

    pub fn remove<V: ?Sized>(&self, v: &V) -> RedBlackTreeSet<T>
    where
        T: Borrow<V>,
        V: Ord,
    {
        RedBlackTreeSet {
            map: self.map.remove(v),
        }
    }

    pub fn remove_mut<V: ?Sized>(&mut self, v: &V) -> bool
    where
        T: Borrow<V>,
        V: Ord,
    {
        self.map.remove_mut(v)
    }

    pub fn contains<V: ?Sized>(&self, v: &V) -> bool
    where
        T: Borrow<V>,
        V: Ord,
    {
        self.map.contains_key(v)
    }

    pub fn first(&self) -> Option<&T> {
        self.map.first().map(|(k, _)| k)
    }

    pub fn last(&self) -> Option<&T> {
        self.map.last().map(|(k, _)| k)
    }

    pub fn is_disjoint(&self, other: &RedBlackTreeSet<T>) -> bool {
        let mut self_it = self.iter();
        let mut other_it = other.iter();

        let mut v_opt = self_it.next();
        let mut u_opt = other_it.next();

        while let (Some(v), Some(u)) = (v_opt, u_opt) {
            match v.cmp(u) {
                Ordering::Less => v_opt = self_it.next(),
                Ordering::Equal => return false,
                Ordering::Greater => u_opt = other_it.next(),
            }
        }

        true
    }

    pub fn is_subset(&self, other: &RedBlackTreeSet<T>) -> bool {
        let mut other_it = other.iter();

        for v in self.iter() {
            loop {
                match other_it.next() {
                    Some(u) => match u.cmp(v) {
                        Ordering::Less => (),
                        Ordering::Equal => break,
                        Ordering::Greater => return false,
                    },
                    None => return false,
                }
            }
        }

        true
    }

    pub fn is_superset(&self, other: &RedBlackTreeSet<T>) -> bool {
        other.is_subset(self)
    }

    #[inline]
    pub fn size(&self) -> usize {
        self.map.size()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.size() == 0
    }

    pub fn iter(&self) -> Iter<T> {
        self.map.keys()
    }
}

impl<T> Clone for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn clone(&self) -> RedBlackTreeSet<T> {
        RedBlackTreeSet {
            map: self.map.clone(),
        }
    }
}

impl<T> Default for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn default() -> RedBlackTreeSet<T> {
        RedBlackTreeSet::new()
    }
}

impl<T> PartialEq for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn eq(&self, other: &RedBlackTreeSet<T>) -> bool {
        self.map.eq(&other.map)
    }
}

impl<T> Eq for RedBlackTreeSet<T>
where
    T: Ord,
{
}

impl<T> Display for RedBlackTreeSet<T>
where
    T: Ord + Display,
{
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        let mut first = true;

        fmt.write_str("{")?;

        for v in self.iter() {
            if !first {
                fmt.write_str(", ")?;
            }
            v.fmt(fmt)?;
            first = false;
        }

        fmt.write_str("}")
    }
}

impl<'a, T> IntoIterator for &'a RedBlackTreeSet<T>
where
    T: Ord,
{
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.iter()
    }
}

// TODO This can be improved to create a perfectly balanced tree.
impl<T> FromIterator<T> for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn from_iter<I: IntoIterator<Item = T>>(into_iter: I) -> RedBlackTreeSet<T> {
        let mut map = RedBlackTreeSet::new();

        for v in into_iter {
            map = map.insert(v);
        }

        map
    }
}

#[cfg(feature = "serde")]
pub mod serde {
    use super::*;
    use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
    use serde::ser::{Serialize, Serializer};
    use std::fmt;
    use std::marker::PhantomData;

    impl<T> Serialize for RedBlackTreeSet<T>
    where
        T: Ord + Serialize,
    {
        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
            serializer.collect_seq(self)
        }
    }

    impl<'de, T> Deserialize<'de> for RedBlackTreeSet<T>
    where
        T: Ord + Deserialize<'de>,
    {
        fn deserialize<D: Deserializer<'de>>(
            deserializer: D,
        ) -> Result<RedBlackTreeSet<T>, D::Error> {
            deserializer.deserialize_seq(RedBlackTreeSetVisitor {
                phantom: PhantomData,
            })
        }
    }

    struct RedBlackTreeSetVisitor<T> {
        phantom: PhantomData<T>,
    }

    impl<'de, T> Visitor<'de> for RedBlackTreeSetVisitor<T>
    where
        T: Ord + Deserialize<'de>,
    {
        type Value = RedBlackTreeSet<T>;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a sequence")
        }

        fn visit_seq<A>(self, mut seq: A) -> Result<RedBlackTreeSet<T>, A::Error>
        where
            A: SeqAccess<'de>,
        {
            let mut set = RedBlackTreeSet::new();

            while let Some(value) = seq.next_element()? {
                set = set.insert(value);
            }

            Ok(set)
        }
    }
}

#[cfg(test)]
mod test;
<commit_msg>`RedBlackTreeSet` serialization and from_iter now uses `insert_mut()`.<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 map::red_black_tree_map;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt::Display;
use std::iter::FromIterator;
use RedBlackTreeMap;

// TODO Use impl trait instead of this when available.
pub type Iter<'a, T> = red_black_tree_map::IterKeys<'a, T, ()>;

/// Creates a [`RedBlackTreeSet`](set/red_black_tree_set/struct.RedBlackTreeSet.html) containing the
/// given arguments:
///
/// ```
/// # use rpds::*;
/// #
/// let s = RedBlackTreeSet::new()
///     .insert(1)
///     .insert(2)
///     .insert(3);
///
/// assert_eq!(rbt_set![1, 2, 3], s);
/// ```
#[macro_export]
macro_rules! rbt_set {
    ($($e:expr),*) => {
        {
            #[allow(unused_mut)]
            let mut s = $crate::RedBlackTreeSet::new();
            $(
                s.insert_mut($e);
            )*
            s
        }
    };
}

/// A persistent set with structural sharing.  This implementation uses a
/// [red-black tree](https://en.wikipedia.org/wiki/Red-Black_tree).
///
/// # Complexity
///
/// Let *n* be the number of elements in the set.
///
/// ## Temporal complexity
///
/// | Operation                  | Best case | Average   | Worst case  |
/// |:-------------------------- | ---------:| ---------:| -----------:|
/// | `new()`                    |      Θ(1) |      Θ(1) |        Θ(1) |
/// | `insert()`                 |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `remove()`                 |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `get()`                    |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `contains()`               |      Θ(1) | Θ(log(n)) |   Θ(log(n)) |
/// | `size()`                   |      Θ(1) |      Θ(1) |        Θ(1) |
/// | `clone()`                  |      Θ(1) |      Θ(1) |        Θ(1) |
/// | iterator creation          |      Θ(1) |      Θ(1) |        Θ(1) |
/// | iterator step              |      Θ(1) |      Θ(1) |   Θ(log(n)) |
/// | iterator full              |      Θ(n) |      Θ(n) |        Θ(n) |
///
/// # Implementation details
///
/// This is a thin wrapper around a [`RedBlackTreeMap`](../../map/red_black_tree_map/struct.RedBlackTreeMap.html).
#[derive(Debug)]
pub struct RedBlackTreeSet<T>
where
    T: Ord,
{
    map: RedBlackTreeMap<T, ()>,
}

impl<T> RedBlackTreeSet<T>
where
    T: Ord,
{
    pub fn new() -> RedBlackTreeSet<T> {
        RedBlackTreeSet {
            map: RedBlackTreeMap::new(),
        }
    }

    pub fn insert(&self, v: T) -> RedBlackTreeSet<T> {
        RedBlackTreeSet {
            map: self.map.insert(v, ()),
        }
    }

    pub fn insert_mut(&mut self, v: T) {
        self.map.insert_mut(v, ());
    }

    pub fn remove<V: ?Sized>(&self, v: &V) -> RedBlackTreeSet<T>
    where
        T: Borrow<V>,
        V: Ord,
    {
        RedBlackTreeSet {
            map: self.map.remove(v),
        }
    }

    pub fn remove_mut<V: ?Sized>(&mut self, v: &V) -> bool
    where
        T: Borrow<V>,
        V: Ord,
    {
        self.map.remove_mut(v)
    }

    pub fn contains<V: ?Sized>(&self, v: &V) -> bool
    where
        T: Borrow<V>,
        V: Ord,
    {
        self.map.contains_key(v)
    }

    pub fn first(&self) -> Option<&T> {
        self.map.first().map(|(k, _)| k)
    }

    pub fn last(&self) -> Option<&T> {
        self.map.last().map(|(k, _)| k)
    }

    pub fn is_disjoint(&self, other: &RedBlackTreeSet<T>) -> bool {
        let mut self_it = self.iter();
        let mut other_it = other.iter();

        let mut v_opt = self_it.next();
        let mut u_opt = other_it.next();

        while let (Some(v), Some(u)) = (v_opt, u_opt) {
            match v.cmp(u) {
                Ordering::Less => v_opt = self_it.next(),
                Ordering::Equal => return false,
                Ordering::Greater => u_opt = other_it.next(),
            }
        }

        true
    }

    pub fn is_subset(&self, other: &RedBlackTreeSet<T>) -> bool {
        let mut other_it = other.iter();

        for v in self.iter() {
            loop {
                match other_it.next() {
                    Some(u) => match u.cmp(v) {
                        Ordering::Less => (),
                        Ordering::Equal => break,
                        Ordering::Greater => return false,
                    },
                    None => return false,
                }
            }
        }

        true
    }

    pub fn is_superset(&self, other: &RedBlackTreeSet<T>) -> bool {
        other.is_subset(self)
    }

    #[inline]
    pub fn size(&self) -> usize {
        self.map.size()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.size() == 0
    }

    pub fn iter(&self) -> Iter<T> {
        self.map.keys()
    }
}

impl<T> Clone for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn clone(&self) -> RedBlackTreeSet<T> {
        RedBlackTreeSet {
            map: self.map.clone(),
        }
    }
}

impl<T> Default for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn default() -> RedBlackTreeSet<T> {
        RedBlackTreeSet::new()
    }
}

impl<T> PartialEq for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn eq(&self, other: &RedBlackTreeSet<T>) -> bool {
        self.map.eq(&other.map)
    }
}

impl<T> Eq for RedBlackTreeSet<T>
where
    T: Ord,
{
}

impl<T> Display for RedBlackTreeSet<T>
where
    T: Ord + Display,
{
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        let mut first = true;

        fmt.write_str("{")?;

        for v in self.iter() {
            if !first {
                fmt.write_str(", ")?;
            }
            v.fmt(fmt)?;
            first = false;
        }

        fmt.write_str("}")
    }
}

impl<'a, T> IntoIterator for &'a RedBlackTreeSet<T>
where
    T: Ord,
{
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.iter()
    }
}

// TODO This can be improved to create a perfectly balanced tree.
impl<T> FromIterator<T> for RedBlackTreeSet<T>
where
    T: Ord,
{
    fn from_iter<I: IntoIterator<Item = T>>(into_iter: I) -> RedBlackTreeSet<T> {
        let mut set = RedBlackTreeSet::new();

        for v in into_iter {
            set.insert_mut(v);
        }

        set
    }
}

#[cfg(feature = "serde")]
pub mod serde {
    use super::*;
    use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
    use serde::ser::{Serialize, Serializer};
    use std::fmt;
    use std::marker::PhantomData;

    impl<T> Serialize for RedBlackTreeSet<T>
    where
        T: Ord + Serialize,
    {
        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
            serializer.collect_seq(self)
        }
    }

    impl<'de, T> Deserialize<'de> for RedBlackTreeSet<T>
    where
        T: Ord + Deserialize<'de>,
    {
        fn deserialize<D: Deserializer<'de>>(
            deserializer: D,
        ) -> Result<RedBlackTreeSet<T>, D::Error> {
            deserializer.deserialize_seq(RedBlackTreeSetVisitor {
                phantom: PhantomData,
            })
        }
    }

    struct RedBlackTreeSetVisitor<T> {
        phantom: PhantomData<T>,
    }

    impl<'de, T> Visitor<'de> for RedBlackTreeSetVisitor<T>
    where
        T: Ord + Deserialize<'de>,
    {
        type Value = RedBlackTreeSet<T>;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a sequence")
        }

        fn visit_seq<A>(self, mut seq: A) -> Result<RedBlackTreeSet<T>, A::Error>
        where
            A: SeqAccess<'de>,
        {
            let mut set = RedBlackTreeSet::new();

            while let Some(value) = seq.next_element()? {
                set.insert_mut(value);
            }

            Ok(set)
        }
    }
}

#[cfg(test)]
mod test;
<|endoftext|>


<commit_before>use std::cell::{Ref, RefCell};
use std::ptr;

use cairo::{ImageSurface, Status};
use cairo_sys;
use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use gio::{Cancellable, File as GFile, InputStream};
use gio_sys;
use glib;
use glib::translate::*;
use glib_sys;
use libc;
use url::Url;

use allowed_url::AllowedUrl;
use css::{self, CssStyles};
use defs::{Defs, RsvgDefs};
use error::{set_gerror, LoadingError, RsvgError};
use io;
use surface_utils::shared_surface::SharedImageSurface;

pub enum RsvgHandle {}

pub enum RsvgHandleRust {}

struct Handle {
    base_url: RefCell<Option<Url>>,
}

impl Handle {
    fn new() -> Handle {
        Handle {
            base_url: RefCell::new(None),
        }
    }
}

#[allow(improper_ctypes)]
extern "C" {
    fn rsvg_handle_get_defs(handle: *const RsvgHandle) -> *const RsvgDefs;

    fn rsvg_handle_load_extern(
        handle: *const RsvgHandle,
        href: *const libc::c_char,
    ) -> *const RsvgHandle;

    fn rsvg_handle_keep_image_data(handle: *const RsvgHandle) -> glib_sys::gboolean;

    fn rsvg_load_handle_xml_xinclude(
        handle: *mut RsvgHandle,
        href: *const libc::c_char,
    ) -> glib_sys::gboolean;

    fn rsvg_handle_get_rust(handle: *const RsvgHandle) -> *mut RsvgHandleRust;

    fn rsvg_handle_get_cancellable(handle: *const RsvgHandle) -> *mut gio_sys::GCancellable;
}

pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a mut Defs {
    unsafe {
        let d = rsvg_handle_get_defs(handle);
        &mut *(d as *mut Defs)
    }
}

pub fn load_extern(handle: *const RsvgHandle, uri: &str) -> Result<*const RsvgHandle, ()> {
    unsafe {
        let res = rsvg_handle_load_extern(handle, uri.to_glib_none().0);

        if res.is_null() {
            Err(())
        } else {
            Ok(res)
        }
    }
}

pub fn get_base_url<'a>(handle: *const RsvgHandle) -> Ref<'a, Option<Url>> {
    let rhandle = get_rust_handle(handle);

    rhandle.base_url.borrow()
}

fn get_cancellable<'a>(handle: *const RsvgHandle) -> Option<Cancellable> {
    unsafe { from_glib_borrow(rsvg_handle_get_cancellable(handle)) }
}

pub struct BinaryData {
    pub data: Vec<u8>,
    pub content_type: Option<String>,
}

pub fn acquire_data(handle: *mut RsvgHandle, aurl: &AllowedUrl) -> Result<BinaryData, glib::Error> {
    io::acquire_data(aurl, get_cancellable(handle).as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))
}

pub fn acquire_stream(handle: *mut RsvgHandle, href: &str) -> Result<InputStream, glib::Error> {
    let rhandle = get_rust_handle(handle);

    let aurl = AllowedUrl::from_href(href, rhandle.base_url.borrow().as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))?;

    io::acquire_stream(&aurl, get_cancellable(handle).as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))
}

fn keep_image_data(handle: *const RsvgHandle) -> bool {
    unsafe { from_glib(rsvg_handle_keep_image_data(handle)) }
}

pub fn image_surface_new_from_href(
    handle: *mut RsvgHandle,
    href_str: &str,
) -> Result<ImageSurface, LoadingError> {
    let rhandle = get_rust_handle(handle);

    let aurl = AllowedUrl::from_href(href_str, rhandle.base_url.borrow().as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))?;

    let data = acquire_data(handle, &aurl)?;

    if data.data.len() == 0 {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = data.content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&data.data)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let surface = SharedImageSurface::from_pixbuf(&pixbuf)?.into_image_surface()?;

    if keep_image_data(handle) {
        if let Some(mime_type) = data.content_type {
            extern "C" {
                fn cairo_surface_set_mime_data(
                    surface: *mut cairo_sys::cairo_surface_t,
                    mime_type: *const libc::c_char,
                    data: *mut libc::c_char,
                    length: libc::c_ulong,
                    destroy: cairo_sys::cairo_destroy_func_t,
                    closure: *mut libc::c_void,
                ) -> Status;
            }

            let data_ptr = ToGlibContainerFromSlice::to_glib_full_from_slice(&data.data);

            unsafe {
                let status = cairo_surface_set_mime_data(
                    surface.to_glib_none().0,
                    mime_type.to_glib_none().0,
                    data_ptr as *mut _,
                    data.data.len() as libc::c_ulong,
                    Some(glib_sys::g_free),
                    data_ptr as *mut _,
                );

                if status != Status::Success {
                    return Err(LoadingError::Cairo(status));
                }
            }
        }
    }

    Ok(surface)
}

// FIXME: distinguish between "file not found" and "invalid XML"
pub fn load_xml_xinclude(handle: *mut RsvgHandle, aurl: &AllowedUrl) -> bool {
    let href = aurl.url().as_str();

    unsafe { from_glib(rsvg_load_handle_xml_xinclude(handle, href.to_glib_none().0)) }
}

// This function just slurps CSS data from a possibly-relative href
// and parses it.  We'll move it to a better place in the end.
pub fn load_css(css_styles: &mut CssStyles, handle: *mut RsvgHandle, href_str: &str) {
    let rhandle = get_rust_handle(handle);

    let aurl = match AllowedUrl::from_href(href_str, rhandle.base_url.borrow().as_ref()) {
        Ok(a) => a,
        Err(_) => {
            rsvg_log!("Could not load \"{}\" for CSS data", href_str);
            // FIXME: report errors; this should be a fatal error
            return;
        }
    };

    if let Ok(data) = acquire_data(handle, &aurl) {
        let BinaryData {
            data: bytes,
            content_type,
        } = data;

        if content_type.as_ref().map(String::as_ref) != Some("text/css") {
            rsvg_log!("\"{}\" is not of type text/css; ignoring", href_str);
            // FIXME: report errors
            return;
        }

        if let Ok(utf8) = String::from_utf8(bytes) {
            css::parse_into_css_styles(css_styles, handle, &utf8);
        } else {
            rsvg_log!(
                "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                href_str
            );
            // FIXME: report errors
            return;
        }
    } else {
        rsvg_log!("Could not load \"{}\" for CSS data", href_str);
        // FIXME: report errors from not being to acquire data; this should be a fatal error
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_new() -> *mut RsvgHandleRust {
    Box::into_raw(Box::new(Handle::new())) as *mut RsvgHandleRust
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_free(raw_handle: *mut RsvgHandleRust) {
    assert!(!raw_handle.is_null());

    Box::from_raw(raw_handle as *mut Handle);
}

fn get_rust_handle<'a>(handle: *const RsvgHandle) -> &'a mut Handle {
    unsafe { &mut *(rsvg_handle_get_rust(handle) as *mut Handle) }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_set_base_url(
    raw_handle: *const RsvgHandleRust,
    uri: *const libc::c_char,
) {
    let handle = &*(raw_handle as *const Handle);

    assert!(!uri.is_null());
    let uri: String = from_glib_none(uri);

    let url = match Url::parse(&uri) {
        Ok(u) => u,

        Err(e) => {
            rsvg_log!(
                "not setting base_uri to \"{}\" since it is invalid: {}",
                uri,
                e
            );
            return;
        }
    };

    rsvg_log!("setting base_uri to \"{}\"", url);
    *handle.base_url.borrow_mut() = Some(url);
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_get_base_gfile(
    raw_handle: *const RsvgHandleRust,
) -> *mut gio_sys::GFile {
    let handle = &*(raw_handle as *const Handle);

    match *handle.base_url.borrow() {
        None => ptr::null_mut(),

        Some(ref url) => GFile::new_for_uri(url.as_str()).to_glib_full(),
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_acquire_data(
    handle: *mut RsvgHandle,
    href_str: *const libc::c_char,
    out_len: *mut usize,
    error: *mut *mut glib_sys::GError,
) -> *mut libc::c_char {
    assert!(!href_str.is_null());
    assert!(!out_len.is_null());

    let href_str: String = from_glib_none(href_str);

    let rhandle = get_rust_handle(handle);

    let aurl = match AllowedUrl::from_href(&href_str, rhandle.base_url.borrow().as_ref()) {
        Ok(a) => a,
        Err(_) => {
            set_gerror(error, 0, "URL is not allowed");
            return ptr::null_mut();
        }
    };

    match acquire_data(handle, &aurl) {
        Ok(binary) => {
            if !error.is_null() {
                *error = ptr::null_mut();
            }

            *out_len = binary.data.len();
            io::binary_data_to_glib(&binary, ptr::null_mut(), out_len)
        }

        Err(_) => {
            set_gerror(error, 0, "Could not acquire data");
            *out_len = 0;
            ptr::null_mut()
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_acquire_stream(
    handle: *mut RsvgHandle,
    href: *const libc::c_char,
    error: *mut *mut glib_sys::GError,
) -> *mut gio_sys::GInputStream {
    assert!(!href.is_null());

    let href: String = from_glib_none(href);

    match acquire_stream(handle, &href) {
        Ok(stream) => {
            if !error.is_null() {
                *error = ptr::null_mut();
            }

            stream.to_glib_full()
        }

        Err(_) => {
            set_gerror(error, 0, "Could not acquire stream");
            ptr::null_mut()
        }
    }
}
<commit_msg>handle::acquire_stream(): Take an AllowedUrl, not a string href<commit_after>use std::cell::{Ref, RefCell};
use std::ptr;

use cairo::{ImageSurface, Status};
use cairo_sys;
use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use gio::{Cancellable, File as GFile, InputStream};
use gio_sys;
use glib;
use glib::translate::*;
use glib_sys;
use libc;
use url::Url;

use allowed_url::AllowedUrl;
use css::{self, CssStyles};
use defs::{Defs, RsvgDefs};
use error::{set_gerror, LoadingError, RsvgError};
use io;
use surface_utils::shared_surface::SharedImageSurface;

pub enum RsvgHandle {}

pub enum RsvgHandleRust {}

struct Handle {
    base_url: RefCell<Option<Url>>,
}

impl Handle {
    fn new() -> Handle {
        Handle {
            base_url: RefCell::new(None),
        }
    }
}

#[allow(improper_ctypes)]
extern "C" {
    fn rsvg_handle_get_defs(handle: *const RsvgHandle) -> *const RsvgDefs;

    fn rsvg_handle_load_extern(
        handle: *const RsvgHandle,
        href: *const libc::c_char,
    ) -> *const RsvgHandle;

    fn rsvg_handle_keep_image_data(handle: *const RsvgHandle) -> glib_sys::gboolean;

    fn rsvg_load_handle_xml_xinclude(
        handle: *mut RsvgHandle,
        href: *const libc::c_char,
    ) -> glib_sys::gboolean;

    fn rsvg_handle_get_rust(handle: *const RsvgHandle) -> *mut RsvgHandleRust;

    fn rsvg_handle_get_cancellable(handle: *const RsvgHandle) -> *mut gio_sys::GCancellable;
}

pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a mut Defs {
    unsafe {
        let d = rsvg_handle_get_defs(handle);
        &mut *(d as *mut Defs)
    }
}

pub fn load_extern(handle: *const RsvgHandle, uri: &str) -> Result<*const RsvgHandle, ()> {
    unsafe {
        let res = rsvg_handle_load_extern(handle, uri.to_glib_none().0);

        if res.is_null() {
            Err(())
        } else {
            Ok(res)
        }
    }
}

pub fn get_base_url<'a>(handle: *const RsvgHandle) -> Ref<'a, Option<Url>> {
    let rhandle = get_rust_handle(handle);

    rhandle.base_url.borrow()
}

fn get_cancellable<'a>(handle: *const RsvgHandle) -> Option<Cancellable> {
    unsafe { from_glib_borrow(rsvg_handle_get_cancellable(handle)) }
}

pub struct BinaryData {
    pub data: Vec<u8>,
    pub content_type: Option<String>,
}

pub fn acquire_data(handle: *mut RsvgHandle, aurl: &AllowedUrl) -> Result<BinaryData, glib::Error> {
    io::acquire_data(aurl, get_cancellable(handle).as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))
}

pub fn acquire_stream(
    handle: *mut RsvgHandle,
    aurl: &AllowedUrl,
) -> Result<InputStream, glib::Error> {
    io::acquire_stream(&aurl, get_cancellable(handle).as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))
}

fn keep_image_data(handle: *const RsvgHandle) -> bool {
    unsafe { from_glib(rsvg_handle_keep_image_data(handle)) }
}

pub fn image_surface_new_from_href(
    handle: *mut RsvgHandle,
    href_str: &str,
) -> Result<ImageSurface, LoadingError> {
    let rhandle = get_rust_handle(handle);

    let aurl = AllowedUrl::from_href(href_str, rhandle.base_url.borrow().as_ref())
        .map_err(|_| glib::Error::new(RsvgError, "FIXME"))?;

    let data = acquire_data(handle, &aurl)?;

    if data.data.len() == 0 {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = data.content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&data.data)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let surface = SharedImageSurface::from_pixbuf(&pixbuf)?.into_image_surface()?;

    if keep_image_data(handle) {
        if let Some(mime_type) = data.content_type {
            extern "C" {
                fn cairo_surface_set_mime_data(
                    surface: *mut cairo_sys::cairo_surface_t,
                    mime_type: *const libc::c_char,
                    data: *mut libc::c_char,
                    length: libc::c_ulong,
                    destroy: cairo_sys::cairo_destroy_func_t,
                    closure: *mut libc::c_void,
                ) -> Status;
            }

            let data_ptr = ToGlibContainerFromSlice::to_glib_full_from_slice(&data.data);

            unsafe {
                let status = cairo_surface_set_mime_data(
                    surface.to_glib_none().0,
                    mime_type.to_glib_none().0,
                    data_ptr as *mut _,
                    data.data.len() as libc::c_ulong,
                    Some(glib_sys::g_free),
                    data_ptr as *mut _,
                );

                if status != Status::Success {
                    return Err(LoadingError::Cairo(status));
                }
            }
        }
    }

    Ok(surface)
}

// FIXME: distinguish between "file not found" and "invalid XML"
pub fn load_xml_xinclude(handle: *mut RsvgHandle, aurl: &AllowedUrl) -> bool {
    let href = aurl.url().as_str();

    unsafe { from_glib(rsvg_load_handle_xml_xinclude(handle, href.to_glib_none().0)) }
}

// This function just slurps CSS data from a possibly-relative href
// and parses it.  We'll move it to a better place in the end.
pub fn load_css(css_styles: &mut CssStyles, handle: *mut RsvgHandle, href_str: &str) {
    let rhandle = get_rust_handle(handle);

    let aurl = match AllowedUrl::from_href(href_str, rhandle.base_url.borrow().as_ref()) {
        Ok(a) => a,
        Err(_) => {
            rsvg_log!("Could not load \"{}\" for CSS data", href_str);
            // FIXME: report errors; this should be a fatal error
            return;
        }
    };

    if let Ok(data) = acquire_data(handle, &aurl) {
        let BinaryData {
            data: bytes,
            content_type,
        } = data;

        if content_type.as_ref().map(String::as_ref) != Some("text/css") {
            rsvg_log!("\"{}\" is not of type text/css; ignoring", href_str);
            // FIXME: report errors
            return;
        }

        if let Ok(utf8) = String::from_utf8(bytes) {
            css::parse_into_css_styles(css_styles, handle, &utf8);
        } else {
            rsvg_log!(
                "\"{}\" does not contain valid UTF-8 CSS data; ignoring",
                href_str
            );
            // FIXME: report errors
            return;
        }
    } else {
        rsvg_log!("Could not load \"{}\" for CSS data", href_str);
        // FIXME: report errors from not being to acquire data; this should be a fatal error
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_new() -> *mut RsvgHandleRust {
    Box::into_raw(Box::new(Handle::new())) as *mut RsvgHandleRust
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_free(raw_handle: *mut RsvgHandleRust) {
    assert!(!raw_handle.is_null());

    Box::from_raw(raw_handle as *mut Handle);
}

fn get_rust_handle<'a>(handle: *const RsvgHandle) -> &'a mut Handle {
    unsafe { &mut *(rsvg_handle_get_rust(handle) as *mut Handle) }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_set_base_url(
    raw_handle: *const RsvgHandleRust,
    uri: *const libc::c_char,
) {
    let handle = &*(raw_handle as *const Handle);

    assert!(!uri.is_null());
    let uri: String = from_glib_none(uri);

    let url = match Url::parse(&uri) {
        Ok(u) => u,

        Err(e) => {
            rsvg_log!(
                "not setting base_uri to \"{}\" since it is invalid: {}",
                uri,
                e
            );
            return;
        }
    };

    rsvg_log!("setting base_uri to \"{}\"", url);
    *handle.base_url.borrow_mut() = Some(url);
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_rust_get_base_gfile(
    raw_handle: *const RsvgHandleRust,
) -> *mut gio_sys::GFile {
    let handle = &*(raw_handle as *const Handle);

    match *handle.base_url.borrow() {
        None => ptr::null_mut(),

        Some(ref url) => GFile::new_for_uri(url.as_str()).to_glib_full(),
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_acquire_data(
    handle: *mut RsvgHandle,
    href_str: *const libc::c_char,
    out_len: *mut usize,
    error: *mut *mut glib_sys::GError,
) -> *mut libc::c_char {
    assert!(!href_str.is_null());
    assert!(!out_len.is_null());

    let href_str: String = from_glib_none(href_str);

    let rhandle = get_rust_handle(handle);

    let aurl = match AllowedUrl::from_href(&href_str, rhandle.base_url.borrow().as_ref()) {
        Ok(a) => a,
        Err(_) => {
            set_gerror(error, 0, "URL is not allowed");
            return ptr::null_mut();
        }
    };

    match acquire_data(handle, &aurl) {
        Ok(binary) => {
            if !error.is_null() {
                *error = ptr::null_mut();
            }

            *out_len = binary.data.len();
            io::binary_data_to_glib(&binary, ptr::null_mut(), out_len)
        }

        Err(_) => {
            set_gerror(error, 0, "Could not acquire data");
            *out_len = 0;
            ptr::null_mut()
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn rsvg_handle_acquire_stream(
    handle: *mut RsvgHandle,
    href_str: *const libc::c_char,
    error: *mut *mut glib_sys::GError,
) -> *mut gio_sys::GInputStream {
    assert!(!href_str.is_null());

    let href_str: String = from_glib_none(href_str);

    let rhandle = get_rust_handle(handle);

    let aurl = match AllowedUrl::from_href(&href_str, rhandle.base_url.borrow().as_ref()) {
        Ok(a) => a,
        Err(_) => {
            set_gerror(error, 0, "URL is not allowed");
            return ptr::null_mut();
        }
    };

    match acquire_stream(handle, &aurl) {
        Ok(stream) => {
            if !error.is_null() {
                *error = ptr::null_mut();
            }

            stream.to_glib_full()
        }

        Err(_) => {
            set_gerror(error, 0, "Could not acquire stream");
            ptr::null_mut()
        }
    }
}
<|endoftext|>


<commit_before>use super::{
    ast_to_hir::AstToHir,
    cst::CstDb,
    cst_to_ast::CstToAst,
    hir::{self, Body, Expression},
    lir::{Chunk, ChunkIndex, Instruction, Lir, StackOffset},
};
use crate::input::Input;
use std::{collections::HashMap, sync::Arc};

#[salsa::query_group(HirToLirStorage)]
pub trait HirToLir: CstDb + AstToHir {
    fn lir(&self, input: Input) -> Option<Arc<Lir>>;
}

fn lir(db: &dyn HirToLir, input: Input) -> Option<Arc<Lir>> {
    let (hir, _) = db.hir(input.clone())?;
    let mut context = LoweringContext::new();
    context.compile_body(&hir);
    assert_eq!(context.stack_size, 0); // TODO
    assert!(context.ids.values().all(|it| it.is_empty())); // TODO
    Some(Arc::new(context.lir))
}

struct LoweringContext {
    stack_size: usize,
    /// Maps ids to stack indices where it appears.
    ids: HashMap<hir::Id, Vec<usize>>,
    lir: Lir,
    current_chunk_index: ChunkIndex,
}
impl LoweringContext {
    fn new() -> LoweringContext {
        LoweringContext {
            stack_size: 0,
            ids: HashMap::new(),
            lir: Lir {
                chunks: vec![Chunk::new()],
            },
            current_chunk_index: 0,
        }
    }

    fn compile_body(&mut self, body: &Body) {
        for (id, expression) in &body.expressions {
            self.compile_expression(id, expression);
        }

        self.add(Instruction::PopMultipleBelowTop(body.expressions.len() - 1));
        for (id, expression) in &body.expressions {
            self.notify_stack_entry_removed(id);
        }
    }
    fn compile_expression(&mut self, id: &hir::Id, expression: &Expression) {
        match expression {
            Expression::Int(int) => self.add(Instruction::CreateInt(*int)),
            Expression::Text(text) => self.add(Instruction::CreateText(text.clone())),
            Expression::Reference(id) => self.push_from_stack(id),
            Expression::Symbol(symbol) => self.add(Instruction::CreateSymbol(symbol.clone())),
            Expression::Struct(entries) => {
                for (key, value) in entries {
                    self.push_from_stack(key);
                    self.push_from_stack(value);
                }
                self.add(Instruction::CreateStruct {
                    num_entries: entries.len(),
                });
                for (key, value) in entries {
                    self.notify_stack_entry_removed(key);
                    self.notify_stack_entry_removed(value);
                }
            }
            Expression::Lambda(lambda) => {
                let old_chunk_index = self.current_chunk_index;
                self.lir.chunks.push(Chunk::new());
                self.current_chunk_index = self.lir.chunks.len() - 1;
                let chunk_index = self.current_chunk_index;

                for (i, argument) in lambda.parameters.iter().enumerate() {
                    self.notify_new_stack_entry(&(lambda.first_id.clone() + i));
                }

                self.compile_body(&lambda.body);

                self.add(Instruction::PopMultipleBelowTop(lambda.parameters.len()));
                for (i, argument) in lambda.parameters.iter().enumerate() {
                    self.notify_stack_entry_removed(&(lambda.first_id.clone() + i));
                }

                self.add(Instruction::Return);

                self.current_chunk_index = old_chunk_index;

                self.add(Instruction::CreateClosure(chunk_index))
            }
            Expression::Body(body) => self.compile_body(body),
            Expression::Call {
                function,
                arguments,
            } => {
                let builtin_function = if let &[builtin_function_index] = &function.local[..] {
                    if let Some(builtin_function) =
                        crate::builtin_functions::VALUES.get(builtin_function_index)
                    {
                        Some(*builtin_function)
                    } else {
                        None
                    }
                } else {
                    None
                };

                for argument in arguments {
                    self.push_from_stack(argument);
                }

                if let Some(builtin_function) = builtin_function {
                    self.add(Instruction::Builtin(builtin_function));
                } else {
                    self.push_from_stack(function);
                    self.add(Instruction::Call);
                }
                for argument in arguments {
                    self.notify_stack_entry_removed(argument);
                }
            }
            Expression::Error { child, errors } => self.add(Instruction::Error(id.to_owned())),
        };
        self.notify_new_stack_entry(id);
        self.add(Instruction::DebugValueEvaluated(id.clone()));
    }

    fn push_from_stack(&mut self, id: &hir::Id) {
        self.add(Instruction::PushFromStack(self.find_in_stack(id)));
        self.notify_new_stack_entry(id);
    }
    fn notify_new_stack_entry(&mut self, id: &hir::Id) {
        self.ids
            .entry(id.to_owned())
            .or_insert_with(|| vec![])
            .push(self.stack_size);
        self.stack_size += 1;
    }
    fn notify_stack_entry_removed(&mut self, id: &hir::Id) {
        self.ids.get_mut(id).unwrap().pop().unwrap();
        self.stack_size -= 1;
    }
    fn find_in_stack(&self, id: &hir::Id) -> StackOffset {
        dbg!(self.stack_size - 1) - dbg!(self.ids[id].last().unwrap())
    }

    fn add(&mut self, instruction: Instruction) {
        let mut chunk = self.lir.chunks.get_mut(self.current_chunk_index).unwrap();
        chunk.instructions.push(instruction);
    }
}
<commit_msg>Refactor and fix HIR to LIR lowering<commit_after>use super::{
    ast_to_hir::AstToHir,
    cst::CstDb,
    hir::{self, Body, Expression},
    lir::{Chunk, ChunkIndex, Instruction, Lir, StackOffset},
};
use crate::{builtin_functions::BuiltinFunction, input::Input};
use itertools::Itertools;
use std::{
    mem::{self, swap},
    sync::Arc,
};

#[salsa::query_group(HirToLirStorage)]
pub trait HirToLir: CstDb + AstToHir {
    fn lir(&self, input: Input) -> Option<Arc<Lir>>;
}

fn lir(db: &dyn HirToLir, input: Input) -> Option<Arc<Lir>> {
    let (hir, _) = db.hir(input.clone())?;

    let mut context = LoweringContext::default();
    context.compile_body(&hir);
    let lir = context.finalize();

    Some(Arc::new(lir))
}

#[derive(Default)]
struct ChunkRegistry {
    chunks: Vec<Chunk>,
}
impl ChunkRegistry {
    fn register_chunk(&mut self, chunk: Chunk) -> ChunkIndex {
        let index = self.chunks.len();
        self.chunks.push(chunk);
        index
    }
}

#[derive(Default)]
struct LoweringContext {
    registry: ChunkRegistry,
    stack: Vec<hir::Id>,
    instructions: Vec<Instruction>,
}
impl LoweringContext {
    fn finalize(mut self) -> Lir {
        self.stack.pop().unwrap(); // Top-level has no return value.
        assert!(dbg!(self.stack).is_empty());

        self.registry.register_chunk(Chunk {
            instructions: self.instructions,
        });
        Lir {
            chunks: self.registry.chunks,
        }
    }

    fn compile_body(&mut self, body: &Body) {
        for (id, expression) in &body.expressions {
            self.compile_expression(id, expression);
        }
        self.emit_pop_multiple_below_top(body.expressions.len() - 1);
    }
    fn compile_expression(&mut self, id: &hir::Id, expression: &Expression) {
        match expression {
            Expression::Int(int) => self.emit_create_int(id.clone(), *int),
            Expression::Text(text) => self.emit_create_text(id.clone(), text.clone()),
            Expression::Reference(reference) => {
                self.emit_push_from_stack(reference.clone());
                self.stack.replace_top_id(id.clone());
            }
            Expression::Symbol(symbol) => self.emit_create_symbol(id.clone(), symbol.clone()),
            Expression::Struct(entries) => {
                for (key, value) in entries {
                    self.emit_push_from_stack(key.clone());
                    self.emit_push_from_stack(value.clone());
                }
                self.emit_create_struct(id.clone(), entries.len());
            }
            Expression::Lambda(lambda) => {
                let mut registry = ChunkRegistry::default();
                swap(&mut self.registry, &mut registry);
                let mut lambda_context = LoweringContext {
                    registry,
                    stack: self.stack.clone(),
                    instructions: vec![],
                };
                for (i, argument) in lambda.parameters.iter().enumerate() {
                    lambda_context.stack.push(lambda.first_id.clone() + i);
                }
                lambda_context.compile_body(&lambda.body);
                lambda_context.emit_pop_multiple_below_top(lambda.parameters.len());
                lambda_context.emit_return();
                swap(&mut self.registry, &mut lambda_context.registry);

                let lambda_chunk = Chunk {
                    instructions: lambda_context.instructions,
                };
                let chunk_index = self.registry.register_chunk(lambda_chunk);
                self.emit_create_closure(id.clone(), chunk_index);
            }
            Expression::Body(body) => {
                self.compile_body(body);
                self.stack.replace_top_id(id.clone());
            }
            Expression::Call {
                function,
                arguments,
            } => {
                let builtin_function = if let &[builtin_function_index] = &function.local[..] {
                    crate::builtin_functions::VALUES
                        .get(builtin_function_index)
                        .map(|it| *it)
                } else {
                    None
                };

                for argument in arguments {
                    self.emit_push_from_stack(argument.clone());
                }

                if let Some(builtin_function) = builtin_function {
                    self.emit_builtin(id.clone(), builtin_function, arguments.len());
                } else {
                    self.emit_push_from_stack(function.clone());
                    self.emit_call(id.clone(), arguments.len());
                }
            }
            Expression::Error { child, errors } => self.emit_error(id.to_owned()),
        };
        self.emit_debug_value_evaluated(id.clone());
    }

    fn emit_create_int(&mut self, id: hir::Id, int: u64) {
        self.emit(Instruction::CreateInt(int));
        self.stack.push(id);
    }
    fn emit_create_text(&mut self, id: hir::Id, text: String) {
        self.emit(Instruction::CreateText(text));
        self.stack.push(id);
    }
    fn emit_create_symbol(&mut self, id: hir::Id, symbol: String) {
        self.emit(Instruction::CreateSymbol(symbol));
        self.stack.push(id);
    }
    fn emit_create_struct(&mut self, id: hir::Id, num_entries: usize) {
        self.emit(Instruction::CreateStruct { num_entries });
        self.stack.pop_multiple(2 * num_entries);
        self.stack.push(id);
    }
    fn emit_create_closure(&mut self, id: hir::Id, chunk: usize) {
        self.emit(Instruction::CreateClosure(chunk));
        self.stack.push(id);
    }
    fn emit_pop(&mut self) {
        self.emit(Instruction::Pop);
        self.stack.pop();
    }
    fn emit_pop_multiple_below_top(&mut self, n: usize) {
        self.emit(Instruction::PopMultipleBelowTop(n));
        let top = self.stack.pop().unwrap();
        self.stack.pop_multiple(n);
        self.stack.push(top);
    }
    fn emit_push_from_stack(&mut self, id: hir::Id) {
        let offset = self.stack.find_id(&id);
        self.emit(Instruction::PushFromStack(offset));
        self.stack.push(id);
    }
    fn emit_call(&mut self, id: hir::Id, num_args: usize) {
        self.emit(Instruction::Call);
        self.stack.pop(); // closure
        self.stack.pop_multiple(num_args);
        self.stack.push(id);
    }
    fn emit_return(&mut self) {
        self.emit(Instruction::Return);
    }
    fn emit_builtin(&mut self, id: hir::Id, builtin: BuiltinFunction, num_args: usize) {
        self.emit(Instruction::Builtin(builtin));
        self.stack.pop_multiple(num_args);
        self.stack.push(id);
    }
    fn emit_debug_value_evaluated(&mut self, id: hir::Id) {
        self.emit(Instruction::DebugValueEvaluated(id));
    }
    fn emit_error(&mut self, id: hir::Id) {
        self.emit(Instruction::Error(id));
    }

    fn emit(&mut self, instruction: Instruction) {
        self.instructions.push(instruction);
    }
}

trait StackExt {
    fn pop_multiple(&mut self, n: usize);
    fn find_id(&self, id: &hir::Id) -> StackOffset;
    fn replace_top_id(&mut self, id: hir::Id);
}
impl StackExt for Vec<hir::Id> {
    fn pop_multiple(&mut self, n: usize) {
        for _ in 0..n {
            self.pop();
        }
    }
    fn find_id(&self, id: &hir::Id) -> StackOffset {
        self.iter().rev().position(|it| it == id).expect(&format!(
            "Id {} not found in stack: {}",
            id,
            self.iter().join(" ")
        ))
    }
    fn replace_top_id(&mut self, id: hir::Id) {
        self.pop().unwrap();
        self.push(id);
    }
}
<|endoftext|>


<commit_before>
use wca::*;
use maplit::hashmap;
use wtest_basic::*;

//

fn _basic()
{
  let command = CommandOptions::default()
  .hint( "hint" )
  .long_hint( "long_hint" )
  .phrase( "phrase" )
  .subject_hint( "subject_hint" )
  .property_hint( "prop1", "hint of prop1" )
  .property_hint( "prop2", "hint of prop2" )
  .property_alias( "property_alias", "a1" )
  .property_alias( "property_alias", "a2" )
  .routine( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  dbg!( &command );

  assert_eq!( command.hint, "hint".to_string() );
  assert_eq!( command.long_hint, "long_hint".to_string() );
  assert_eq!( command.phrase, "phrase".to_string() );
  assert_eq!( command.subject_hint, "subject_hint".to_string() );

  let properties_hints = hashmap!
  {
    "prop1".to_string() => "hint of prop1".to_string(),
    "prop2".to_string() => "hint of prop2".to_string(),
  };
  assert_eq!( command.properties_hints, properties_hints );

  let properties_aliases = hashmap!
  {
    "property_alias".to_string() => vec![ "a1".to_string(), "a2".to_string() ],
  };
  assert_eq!( command.properties_aliases, properties_aliases );
}

//

fn _shortcut()
{
  let command = CommandOptions::default()
  .h( "hint2" )
  .lh( "long_hint2" )
  .ro( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  dbg!( &command );

  assert_eq!( command.hint, "hint2".to_string() );
  assert_eq!( command.long_hint, "long_hint2".to_string() );
  assert_eq!( command.phrase, "".to_string() );
  assert_eq!( command.subject_hint, "".to_string() );
}

//

fn _callback_trivial()
{
  let command = CommandOptions::default()
  .hint( "hint" )
  .long_hint( "long_hint" )
  .phrase( "phrase" )
  .subject_hint( "subject_hint" )
  .property_hint( "prop1", "hint of prop1" )
  .property_hint( "prop2", "hint of prop2" )
  .property_alias( "property_alias", "a1" )
  .property_alias( "property_alias", "a2" )
  .routine( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  let perform = command.perform( &instruction::Instruction::default() );
  assert!( perform.is_ok() );
}

//

test_suite!
{
  basic,
  shortcut,
  callback_trivial,
}

<commit_msg>Write test routine `perform_with_props`<commit_after>
use wca::*;
use maplit::hashmap;
use wtest_basic::*;

//

fn _basic()
{
  let command = CommandOptions::default()
  .hint( "hint" )
  .long_hint( "long_hint" )
  .phrase( "phrase" )
  .subject_hint( "subject_hint" )
  .property_hint( "prop1", "hint of prop1" )
  .property_hint( "prop2", "hint of prop2" )
  .property_alias( "property_alias", "a1" )
  .property_alias( "property_alias", "a2" )
  .routine( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  dbg!( &command );

  assert_eq!( command.hint, "hint".to_string() );
  assert_eq!( command.long_hint, "long_hint".to_string() );
  assert_eq!( command.phrase, "phrase".to_string() );
  assert_eq!( command.subject_hint, "subject_hint".to_string() );

  let properties_hints = hashmap!
  {
    "prop1".to_string() => "hint of prop1".to_string(),
    "prop2".to_string() => "hint of prop2".to_string(),
  };
  assert_eq!( command.properties_hints, properties_hints );

  let properties_aliases = hashmap!
  {
    "property_alias".to_string() => vec![ "a1".to_string(), "a2".to_string() ],
  };
  assert_eq!( command.properties_aliases, properties_aliases );
}

//

fn _shortcut()
{
  let command = CommandOptions::default()
  .h( "hint2" )
  .lh( "long_hint2" )
  .ro( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  dbg!( &command );

  assert_eq!( command.hint, "hint2".to_string() );
  assert_eq!( command.long_hint, "long_hint2".to_string() );
  assert_eq!( command.phrase, "".to_string() );
  assert_eq!( command.subject_hint, "".to_string() );
}

//

fn _callback_trivial()
{
  let command = CommandOptions::default()
  .hint( "hint" )
  .long_hint( "long_hint" )
  .phrase( "phrase" )
  .subject_hint( "subject_hint" )
  .property_hint( "prop1", "hint of prop1" )
  .property_hint( "prop2", "hint of prop2" )
  .property_alias( "property_alias", "a1" )
  .property_alias( "property_alias", "a2" )
  .routine( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  let instruction = instruction::instruction_parse()
  .instruction( "" )
  .perform();
  let perform = command.perform( &instruction );
  assert!( perform.is_ok() );
}

//

fn _callback_with_props()
{
  let command = CommandOptions::default()
  .hint( "hint" )
  .long_hint( "long_hint" )
  .phrase( "phrase" )
  .subject_hint( "subject_hint" )
  .property_hint( "prop1", "hint of prop1" )
  .property_hint( "prop2", "hint of prop2" )
  .property_alias( "property_alias", "a1" )
  .property_alias( "property_alias", "a2" )
  .routine( &| _i : &instruction::Instruction | { println!( "hello" ); Ok( () ) } )
  .form();

  let instruction = instruction::instruction_parse()
  .instruction( ".get subj prop1:1" )
  .perform();
  let perform = command.perform( &instruction );
  assert!( perform.is_ok() );

  let instruction = instruction::instruction_parse()
  .instruction( ".get subj unknown:1" )
  .perform();
  let perform = command.perform( &instruction );
  assert!( perform.is_err() );
}

//

test_suite!
{
  basic,
  shortcut,
  callback_trivial,
  callback_with_props,
}

<|endoftext|>


<commit_before>// Solution for http://rosettacode.org/wiki/Execute_a_Markov_algorithm

// Individual markov rule
struct MarkovRule {
    pattern: ~str,
    replacement: ~str,
    stop: bool
}

impl MarkovRule {
    fn new(pattern: ~str, replacement: ~str, stop: bool) -> MarkovRule {
        MarkovRule {pattern: pattern, replacement: replacement, stop: stop}
    }
}

// Struct for the complete markov algorithm
struct MarkovAlgorithm {
    rules: Vec<MarkovRule>
}

impl MarkovAlgorithm {

    // parse an algorithm description to build a markov algorithm
    pub fn build_from_string(s: &str) -> Option<MarkovAlgorithm> {
        let mut rules: Vec<MarkovRule> = vec!();
        for line in s.lines() {
            // skip comment lines
            if (line.char_len() > 0) && (line.char_at(0) == '#')
            {
                continue;
            }

            // check for -> (must be preceded by whitespace)
            // invalid ruleset if absent
            // whitespace rules mean there's 2 possible variations: " ->" and "\t->"
            let arrow_pos = line.find_str(" ->").or_else(||{line.find_str("\t->")});
            match arrow_pos {
                None => {
                    // Ruleset is invalid
                    println!("Invalid rule \"{}\"", line);
                    return None
                }
                Some(arrow) => {
                    // extract pattern (trim trailing whitespace)
                    let pattern = line.slice_to(arrow).trim_right();

                    // get the string after the arrow
                    // this adds 3 to skip the arrow itself
                    let line_end = line.slice_from(arrow + 3).trim_left();

                    // check for . (stop)
                    let stop = (line_end.char_len() > 0) && (line_end.char_at(0) == '.');

                    // extract replacement
                    let replacement = if stop {line_end.slice_from(1)} else {line_end};

                    // add to rules
                    let new_rule = MarkovRule::new(pattern.to_owned(), replacement.to_owned(), stop);
                    rules.push(new_rule);
                }
            }
        }
        let rule_set = MarkovAlgorithm{rules: rules};
        Some(rule_set)
    }

    // transform a text string by applying the markov algorithm
    pub fn apply(&self, input: &str) -> ~str {

        // get a writable version of the input to work with
        let mut state = input.into_owned();

        // Don't allow input to be used after this
        drop(input);

        // loop while operations are possible
        loop {
            // find the first rule that is applicable
            // (pattern string is in state)
            let mut rule_iterator = self.rules.iter();
            let possible_rule = rule_iterator.find(|rule|{
                state.find_str(rule.pattern).is_some()
            });

            match possible_rule {
                // stop if no rule found
                None => { break; }
                Some(rule) => {
                    // replace the first instnace (only) of the pattern
                    // Note: cannot use str::replace as that replaces all instances

                    // unwrap is safe here as the code for finding a rule
                    // already established that the pattern is present
                    let pos = state.find_str(rule.pattern).unwrap();
                    let width = rule.pattern.len();

                    // string parts
                    let left = state.slice_to(pos).to_owned();
                    let right = state.slice_from(pos + width).to_owned();

                    // construct new string
                    state = left + rule.replacement + right;

                    // stop if required
                    if rule.stop { break; }
                }
            }
        }

        state
    }
}

// Demonstration of rosetta code samples

struct RCSample {
    ruleset: ~str,
    input: ~str,
    expected_result: ~str
}

fn main() {
    // Sample markow algorithms from rosetta code
    // Left aligned to avoid extra whitespace in strings
    let samples = [
        RCSample{
            ruleset:
~"# This rules file is extracted from Wikipedia:
# http://en.wikipedia.org/wiki/Markov_Algorithm
A -> apple
B -> bag
S -> shop
T -> the
the shop -> my brother
a never used -> .terminating rule",
            input: ~"I bought a B of As from T S.",
            expected_result: ~"I bought a bag of apples from my brother."
        },
        RCSample{
            ruleset:
~"# Slightly modified from the rules on Wikipedia
A -> apple
B -> bag
S -> .shop
T -> the
the shop -> my brother
a never used -> .terminating rule",
            input: ~"I bought a B of As from T S.",
            expected_result: ~"I bought a bag of apples from T shop."
        },
        RCSample{
            ruleset:
~"# BNF Syntax testing rules
A -> apple
WWWW -> with
Bgage -> ->.*
B -> bag
->.* -> money
W -> WW
S -> .shop
T -> the
the shop -> my brother
a never used -> .terminating rule",
            input: ~"I bought a B of As W my Bgage from T S.",
            expected_result: ~"I bought a bag of apples with my money from T shop."
        },
        RCSample{
            ruleset:
~"### Unary Multiplication Engine, for testing Markov Algorithm implementations
### By Donal Fellows.
# Unary addition engine
_+1 -> _1+
1+1 -> 11+
# Pass for converting from the splitting of multiplication into ordinary
# addition
1! -> !1
,! -> !+
_! -> _
# Unary multiplication by duplicating left side, right side times
1*1 -> x,@y
1x -> xX
X, -> 1,1
X1 -> 1X
_x -> _X
,x -> ,X
y1 -> 1y
y_ -> _
# Next phase of applying
1@1 -> x,@y
1@_ -> @_
,@_ -> !_
++ -> +
# Termination cleanup for addition
_1 -> 1
1+_ -> 1
_+_ -> ",
            input: ~"_1111*11111_",
            expected_result: ~"11111111111111111111"
        },
        RCSample{
            ruleset:
~"# Turing machine: three-state busy beaver
#
# state A, symbol 0 => write 1, move right, new state B
A0 -> 1B
# state A, symbol 1 => write 1, move left, new state C
0A1 -> C01
1A1 -> C11
# state B, symbol 0 => write 1, move left, new state A
0B0 -> A01
1B0 -> A11
# state B, symbol 1 => write 1, move right, new state B
B1 -> 1B
# state C, symbol 0 => write 1, move left, new state B
0C0 -> B01
1C0 -> B11
# state C, symbol 1 => write 1, move left, halt
0C1 -> H01
1C1 -> H11",
            input: ~"000000A000000",
            expected_result: ~"00011H1111000"
        },
    ];

    for (index, sample) in samples.iter().enumerate() {
        let markov_algorithm = MarkovAlgorithm::build_from_string(sample.ruleset);

        let output = markov_algorithm.map(|algorithm|{
            algorithm.apply(sample.input)
        });

        output.map(|output_string|{
            println!("Sample {}", (index + 1))
            println!("Expected: {}", sample.expected_result)
            println!("Actual : {}", output_string);
            assert!(sample.expected_result == output_string);
        });
    }
}
<commit_msg>Fixed markov algorithm and added test<commit_after>// Solution for http://rosettacode.org/wiki/Execute_a_Markov_algorithm

// Individual markov rule
struct MarkovRule {
    pattern: ~str,
    replacement: ~str,
    stop: bool
}

impl MarkovRule {
    fn new(pattern: ~str, replacement: ~str, stop: bool) -> MarkovRule {
        MarkovRule {pattern: pattern, replacement: replacement, stop: stop}
    }
}

// The complete markov algorithm
struct MarkovAlgorithm {
    rules: Vec<MarkovRule>
}

impl MarkovAlgorithm {
    // Parse an algorithm description to build a markov algorithm
    pub fn from_str(s: &str) -> Result<MarkovAlgorithm, ~str> {
        let mut rules: Vec<MarkovRule> = vec!();
        for line in s.lines()
            .map(|l| l.trim()) // Ignore whitespace before and after
            .filter(|l| l.char_len() > 0 && l.char_at(0) != '#') { // Ignore comments

            // check for -> (must be preceded by whitespace)
            // invalid ruleset if absent
            // whitespace rules mean there's 2 possible variations: " ->" and "\t->"
            let arrow_pos = line.find_str(" ->").or_else(|| line.find_str("\t->"));
            match arrow_pos {
                None => {
                    // Ruleset is invalid
                    return Err(format!("Invalid rule \"{}\"", line));
                }
                Some(arrow) => {
                    // extract pattern (trim trailing whitespace)
                    let pattern = line.slice_to(arrow).trim_right();

                    // get the string after the arrow
                    // this adds 3 to skip the arrow itself
                    let line_end = line.slice_from(arrow + 3).trim_left();

                    // check for . (stop)
                    let stop = (line_end.char_len() > 0) && (line_end.char_at(0) == '.');

                    // extract replacement
                    let replacement = if stop {line_end.slice_from(1)} else {line_end};

                    // add to rules
                    let new_rule = MarkovRule::new(pattern.to_owned(), replacement.to_owned(), stop);
                    rules.push(new_rule);
                }
            }
        }
        let rule_set = MarkovAlgorithm{rules: rules};
        Ok(rule_set)
    }

    // Transform a text string by applying the markov algorithm
    pub fn apply(&self, input: &str) -> ~str {

        // get a writable version of the input to work with
        let mut state = input.into_owned();

        // Don't allow input to be used after this
        drop(input);

        // loop while operations are possible
        loop {
            // find the first rule that is applicable
            // (pattern string is in state)
            let mut rule_iterator = self.rules.iter();
            let possible_rule = rule_iterator.find(|rule|{
                state.find_str(rule.pattern).is_some()
            });

            match possible_rule {
                // stop if no rule found
                None => { break; }
                Some(rule) => {
                    // replace the first instnace (only) of the pattern
                    // Note: cannot use str::replace as that replaces all instances

                    // unwrap is safe here as the code for finding a rule
                    // already established that the pattern is present
                    let pos = state.find_str(rule.pattern).unwrap();
                    let width = rule.pattern.len();

                    // string parts
                    let left = state.slice_to(pos).to_owned();
                    let right = state.slice_from(pos + width).to_owned();

                    // construct new string
                    state = left + rule.replacement + right;

                    // stop if required
                    if rule.stop { break; }
                }
            }
        }

        state
    }
}

// A Rosetta Code sample
struct RCSample {
    ruleset: ~str,
    input: ~str,
    expected_result: ~str
}

// Sample markow algorithms from rosetta code
// The extra whitespaces are trimmed when MarkovAlgorithm::from_str is called
fn get_samples() -> [RCSample, ..5] {
    [
        RCSample {
            ruleset:
                "# This rules file is extracted from Wikipedia:
                # http://en.wikipedia.org/wiki/Markov_Algorithm
                A -> apple
                B -> bag
                S -> shop
                T -> the
                the shop -> my brother
                a never used -> .terminating rule".to_owned(),
            input: "I bought a B of As from T S.".to_owned(),
            expected_result: "I bought a bag of apples from my brother.".to_owned()
        },
        RCSample{
            ruleset:
                "# Slightly modified from the rules on Wikipedia
                A -> apple
                B -> bag
                S -> .shop
                T -> the
                the shop -> my brother
                a never used -> .terminating rule".to_owned(),
            input: "I bought a B of As from T S.".to_owned(),
            expected_result: "I bought a bag of apples from T shop.".to_owned()
        },
        RCSample{
            ruleset:
                "# BNF Syntax testing rules
                A -> apple
                WWWW -> with
                Bgage -> ->.*
                B -> bag
                ->.* -> money
                W -> WW
                S -> .shop
                T -> the
                the shop -> my brother
                a never used -> .terminating rule".to_owned(),
            input: "I bought a B of As W my Bgage from T S.".to_owned(),
            expected_result: "I bought a bag of apples with my money from T shop.".to_owned()
        },
        RCSample{
            ruleset:
                "### Unary Multiplication Engine, for testing Markov Algorithm implementations
                ### By Donal Fellows.
                # Unary addition engine
                _+1 -> _1+
                1+1 -> 11+
                # Pass for converting from the splitting of multiplication into ordinary
                # addition
                1! -> !1
                ,! -> !+
                _! -> _
                # Unary multiplication by duplicating left side, right side times
                1*1 -> x,@y
                1x -> xX
                X, -> 1,1
                X1 -> 1X
                _x -> _X
                ,x -> ,X
                y1 -> 1y
                y_ -> _
                # Next phase of applying
                1@1 -> x,@y
                1@_ -> @_
                ,@_ -> !_
                ++ -> +
                # Termination cleanup for addition
                _1 -> 1
                1+_ -> 1
                _+_ -> ".to_owned(),
            input: "_1111*11111_".to_owned(),
            expected_result: "11111111111111111111".to_owned()
        },
        RCSample{
            ruleset:
                "# Turing machine: three-state busy beaver
                #
                # state A, symbol 0 => write 1, move right, new state B
                A0 -> 1B
                # state A, symbol 1 => write 1, move left, new state C
                0A1 -> C01
                1A1 -> C11
                # state B, symbol 0 => write 1, move left, new state A
                0B0 -> A01
                1B0 -> A11
                # state B, symbol 1 => write 1, move right, new state B
                B1 -> 1B
                # state C, symbol 0 => write 1, move left, new state B
                0C0 -> B01
                1C0 -> B11
                # state C, symbol 1 => write 1, move left, halt
                0C1 -> H01
                1C1 -> H11".to_owned(),
            input: "000000A000000".to_owned(),
            expected_result: "00011H1111000".to_owned()
        },
    ]
}

#[cfg(not(test))]
fn main() {
    for (index, sample) in get_samples().iter().enumerate() {
        match MarkovAlgorithm::from_str(sample.ruleset) {
            Ok(algorithm) => {
                println!("Sample {}", (index + 1));
                println!("Output: {}", algorithm.apply(sample.input));
            }
            Err(message) => println!("{}", message)
        }
    }
}

#[test]
fn test_samples() {
    for sample in get_samples().iter() {
        match MarkovAlgorithm::from_str(sample.ruleset) {
            Ok(algorithm) => assert!(sample.expected_result == algorithm.apply(sample.input)),
            Err(message) => fail!("{}", message)
        }
    }
}
<|endoftext|>


<commit_before>use super::instance::*;
use super::module::*;
use super::register::*;
use super::signal::*;

use typed_arena::Arena;

use std::cell::RefCell;
use std::collections::BTreeMap;

/// A top-level container/owner object for a module graph.
///
/// A `Context` owns all parts of a module graph, and provides an API for creating `Module` objects.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
/// let m = c.module("my_module");
/// m.output("out", m.input("in", 1));
/// ```
#[must_use]
pub struct Context<'a> {
    module_arena: Arena<Module<'a>>,
    pub(super) signal_arena: Arena<Signal<'a>>,
    pub(super) register_data_arena: Arena<RegisterData<'a>>,
    pub(super) register_arena: Arena<Register<'a>>,
    pub(super) instance_arena: Arena<Instance<'a>>,

    pub(super) modules: RefCell<BTreeMap<String, &'a Module<'a>>>,
}

impl<'a> Context<'a> {
    /// Creates a new, empty `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    /// ```
    pub fn new() -> Context<'a> {
        Context {
            module_arena: Arena::new(),
            signal_arena: Arena::new(),
            register_data_arena: Arena::new(),
            register_arena: Arena::new(),
            instance_arena: Arena::new(),

            modules: RefCell::new(BTreeMap::new()),
        }
    }

    /// Creates a new `Module` called `name` in this `Context`.
    ///
    /// Conventionally, `name` should be `snake_case`, though this is not enforced.
    ///
    /// # Panics
    ///
    /// Panics if a `Module` with the same `name` already exists in this `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let my_module = c.module("my_module");
    /// let another_mod = c.module("another_mod");
    /// ```
    ///
    /// The following example panics by creating a `Module` with the same `name` as a previously-created `Module` in the same `Context`:
    ///
    /// ```should_panic
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let _ = c.module("a"); // Unique name, OK
    /// let _ = c.module("b"); // Unique name, OK
    ///
    /// let _ = c.module("a"); // Non-unique name, panic!
    /// ```
    pub fn module<S: Into<String>>(&'a self, name: S) -> &Module {
        let name = name.into();
        let mut modules = self.modules.borrow_mut();
        if modules.contains_key(&name) {
            panic!(
                "A module with the name \"{}\" already exists in this context.",
                name
            );
        }
        let module = self.module_arena.alloc(Module::new(self, name.clone()));
        modules.insert(name, module);
        module
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected = "A module with the name \"a\" already exists in this context.")]
    fn unique_module_names() {
        let c = Context::new();

        let _ = c.module("a"); // Unique name, OK
        let _ = c.module("b"); // Unique name, OK

        // Panic
        let _ = c.module("a");
    }
}
<commit_msg>Fix up links in Context docs<commit_after>use super::instance::*;
use super::module::*;
use super::register::*;
use super::signal::*;

use typed_arena::Arena;

use std::cell::RefCell;
use std::collections::BTreeMap;

/// A top-level container/owner object for a module graph.
///
/// A `Context` owns all parts of a module graph, and provides an API for creating [`Module`] objects.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
/// let m = c.module("my_module");
/// m.output("out", m.input("in", 1));
/// ```
///
/// [`Module`]: ./struct.Module.html
#[must_use]
pub struct Context<'a> {
    module_arena: Arena<Module<'a>>,
    pub(super) signal_arena: Arena<Signal<'a>>,
    pub(super) register_data_arena: Arena<RegisterData<'a>>,
    pub(super) register_arena: Arena<Register<'a>>,
    pub(super) instance_arena: Arena<Instance<'a>>,

    pub(super) modules: RefCell<BTreeMap<String, &'a Module<'a>>>,
}

impl<'a> Context<'a> {
    /// Creates a new, empty `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    /// ```
    pub fn new() -> Context<'a> {
        Context {
            module_arena: Arena::new(),
            signal_arena: Arena::new(),
            register_data_arena: Arena::new(),
            register_arena: Arena::new(),
            instance_arena: Arena::new(),

            modules: RefCell::new(BTreeMap::new()),
        }
    }

    /// Creates a new [`Module`] called `name` in this `Context`.
    ///
    /// Conventionally, `name` should be `snake_case`, though this is not enforced.
    ///
    /// # Panics
    ///
    /// Panics if a [`Module`] with the same `name` already exists in this `Context`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let my_module = c.module("my_module");
    /// let another_mod = c.module("another_mod");
    /// ```
    ///
    /// The following example panics by creating a `Module` with the same `name` as a previously-created `Module` in the same `Context`:
    ///
    /// ```should_panic
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let _ = c.module("a"); // Unique name, OK
    /// let _ = c.module("b"); // Unique name, OK
    ///
    /// let _ = c.module("a"); // Non-unique name, panic!
    /// ```
    ///
    /// [`Module`]: ./struct.Module.html
    pub fn module<S: Into<String>>(&'a self, name: S) -> &Module {
        let name = name.into();
        let mut modules = self.modules.borrow_mut();
        if modules.contains_key(&name) {
            panic!(
                "A module with the name \"{}\" already exists in this context.",
                name
            );
        }
        let module = self.module_arena.alloc(Module::new(self, name.clone()));
        modules.insert(name, module);
        module
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic(expected = "A module with the name \"a\" already exists in this context.")]
    fn unique_module_names() {
        let c = Context::new();

        let _ = c.module("a"); // Unique name, OK
        let _ = c.module("b"); // Unique name, OK

        // Panic
        let _ = c.module("a");
    }
}
<|endoftext|>


<commit_before>
/// Get the port from a string (ip)
pub fn parse_port(string: &str) -> &str {
    let a = match string.find(':') {
        Some(pos) => pos + 1,
        None => 0,
    };

    let mut b = 0;
    for c in string.chars().skip(a) {
        match c {
            '0' | '1' |
            '2' | '3' |
            '4' | '5' |
            '6' | '7' |
            '8' | '9' => b += 1,
            _ => break,
        }
    }

    &string[a..b + 1]
}

/// Get the host from a string (ip)
pub fn parse_host(string: &str) -> &str {
    // TODO: Username/Password syntax
    let pos = match string.find(':') {
        Some(pos) => pos + 1,
        None => match string.find('/') {
            Some(pos) => pos + 1,
            None => string.len(),
        },
    };

    &string[..pos]
}
<commit_msg>Optimize parse_host. And it's carefully reviewed for not being panicable.<commit_after>
/// Get the port from a string (ip)
pub fn parse_port(string: &str) -> &str {
    let a = match string.find(':') {
        Some(pos) => pos + 1,
        None => 0,
    };

    let mut b = 0;
    for c in string.chars().skip(a) {
        match c {
            '0' | '1' |
            '2' | '3' |
            '4' | '5' |
            '6' | '7' |
            '8' | '9' => b += 1,
            _ => break,
        }
    }

    &string[a..b + 1]
}

/// Get the host from a string (ip)
pub fn parse_host(string: &str) -> &str {
    let pos = match string.find(|c| c == ':' || c == '/') {
        Some(pos) => pos + 1,
        None => string.len(),
    };

    &string[..pos]
}
<|endoftext|>


<commit_before>use alloc::boxed::Box;

use core::cmp;

use common::string::{String, ToString};

use graphics::display::Display;

use schemes::{KScheme, Resource, ResourceSeek, URL};

pub struct DisplayScheme;

// Should there only be one display per session?
pub struct DisplayResource {
	pub display: Box<Display>,
	pub seek: usize,
}

impl Resource for DisplayResource {
	// can't think of when you would wish to duplicate a display
	fn dup(&self) -> Option<Box<Resource>> {
		None
	}

	/// Return the URL for display resource
	fn url(&self) -> URL {
		return URL::from_string(&("display://".to_string()));
	}

	// not sure what to return here
	fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
		None
	}

	fn write(&mut self, buf: &[u8]) -> Option<usize> {
		let display = &mut self.display;

		let size = cmp::min(display.size - self.seek, buf.len());
		unsafe {
			Display::copy_run(buf.as_ptr() as usize,
			                  display.offscreen + self.seek,
			                  size);
		}
		self.seek += size;
		return Some(size);
	}

	fn seek(&mut self, pos: ResourceSeek) -> Option<usize> {
		let end = self.display.size;

		self.seek = match pos {
			ResourceSeek::Start(offset) => cmp::min(end, cmp::max(0, offset)),
			ResourceSeek::Current(offset) => cmp::min(end, cmp::max(0, self.seek as isize + offset) as usize),
			ResourceSeek::End(offset) => cmp::min(end, cmp::max(0, end as isize + offset) as usize),
		};

		return Some(self.seek);
	}

	fn sync(&mut self) -> bool {
		self.display.flip();
		return true;
	}
}

impl KScheme for DisplayScheme {
	fn scheme(&self) -> String {
		return "display".to_string();
	}

	fn open(&mut self, url: &URL) -> Option<Box<Resource>> {
		// TODO: ponder these things:
		// - should display:// be the only only valid url
		//      for this scheme?
		// - maybe "read" should support displays at some other location
		//      like built in screen sharing capability or something
		unsafe {
			return Some(box DisplayResource {
			            display: Display::root(),
			           seek: 0,
			      });
		}
	}
}
<commit_msg>Convert tabs to spaces in `kernel::schemes::display`<commit_after>use alloc::boxed::Box;

use core::cmp;

use common::string::{String, ToString};

use graphics::display::Display;

use schemes::{KScheme, Resource, ResourceSeek, URL};

pub struct DisplayScheme;

// Should there only be one display per session?
pub struct DisplayResource {
    pub display: Box<Display>,
    pub seek: usize,
}

impl Resource for DisplayResource {
    // can't think of when you would wish to duplicate a display
    fn dup(&self) -> Option<Box<Resource>> {
        None
    }

    /// Return the URL for display resource
    fn url(&self) -> URL {
        return URL::from_string(&("display://".to_string()));
    }

    // not sure what to return here
    fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
        None
    }

    fn write(&mut self, buf: &[u8]) -> Option<usize> {
        let display = &mut self.display;

        let size = cmp::min(display.size - self.seek, buf.len());
        unsafe {
            Display::copy_run(buf.as_ptr() as usize,
                              display.offscreen + self.seek,
                              size);
        }
        self.seek += size;
        return Some(size);
    }

    fn seek(&mut self, pos: ResourceSeek) -> Option<usize> {
        let end = self.display.size;

        self.seek = match pos {
            ResourceSeek::Start(offset) => cmp::min(end, cmp::max(0, offset)),
            ResourceSeek::Current(offset) => cmp::min(end, cmp::max(0, self.seek as isize + offset) as usize),
            ResourceSeek::End(offset) => cmp::min(end, cmp::max(0, end as isize + offset) as usize),
        };

        return Some(self.seek);
    }

    fn sync(&mut self) -> bool {
        self.display.flip();
        return true;
    }
}

impl KScheme for DisplayScheme {
    fn scheme(&self) -> String {
        return "display".to_string();
    }

    fn open(&mut self, url: &URL) -> Option<Box<Resource>> {
        // TODO: ponder these things:
        // - should display:// be the only only valid url
        //      for this scheme?
        // - maybe "read" should support displays at some other location
        //      like built in screen sharing capability or something
        unsafe {
            return Some(box DisplayResource {
                        display: Display::root(),
                       seek: 0,
            });
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::os::raw;
use super::DelegateState;
use Event;
use events::{ Touch, TouchPhase };

use objc::runtime::{ Class, Object, Sel, BOOL, YES };
use objc::declare::{ ClassDecl };

use super::ffi::{
    longjmp,
    id,
    nil,
    CGRect,
    CGPoint,
    CGFloat,
    UIViewAutoresizingFlexibleWidth,
    UIViewAutoresizingFlexibleHeight
 };

use super::jmpbuf;

pub fn create_delegate_class() {
    extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
        unsafe {
            let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
            let bounds: CGRect = msg_send![main_screen, bounds];
            let scale: CGFloat = msg_send![main_screen, nativeScale];

            let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
            let window: id = msg_send![window, initWithFrame:bounds.clone()];

            let size = (bounds.size.width as u32, bounds.size.height as u32);

            let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
            let view_controller: id = msg_send![view_controller, init];

            let class = Class::get("MainView").unwrap();
            let view:id = msg_send![class, alloc];
            let view:id = msg_send![view, initForGl:&bounds];

            let _: () = msg_send![view_controller, setView:view];

            let _: () = msg_send![window, setRootViewController:view_controller];

            let _: () = msg_send![window, addSubview:view];
            let _: () = msg_send![window, makeKeyAndVisible];

            let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f32));
            let state_ptr: *mut DelegateState = mem::transmute(state);
            this.set_ivar("glutinState", state_ptr as *mut raw::c_void);

            let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
        }
        YES
    }

    extern fn post_launch(_: &Object, _: Sel, _: id) {
        unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
    }

    extern fn did_become_active(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Focused(true));
        }
    }

    extern fn will_resign_active(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Focused(false));
        }
    }

    extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Suspended(false));
        }
    }

    extern fn did_enter_background(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Suspended(true));
        }
    }

    extern fn will_terminate(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            // push event to the front to garantee that we'll process it
            // immidiatly after jump
            state.events_queue.push_front(Event::Closed);
            longjmp(mem::transmute(&mut jmpbuf),1);
        }
    }

    extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);

            let touches_enum: id = msg_send![touches, objectEnumerator];

            loop {
                let touch: id = msg_send![touches_enum, nextObject];
                if touch == nil {
                    break
                }
                let location: CGPoint = msg_send![touch, locationInView:nil];
                let touch_id = touch as u64;
                let phase: i32 = msg_send![touch, phase];

                state.events_queue.push_back(Event::Touch(Touch {
                    id: touch_id,
                    location: (location.x as f64, location.y as f64),
                    phase: match phase {
                        0 => TouchPhase::Started,
                        1 => TouchPhase::Moved,
                        // 2 is UITouchPhaseStationary and is not expected here
                        3 => TouchPhase::Ended,
                        4 => TouchPhase::Cancelled,
                        _ => panic!("unexpected touch phase: {:?}", phase)
                    }
                }));
            }
        }
    }

    let superclass = Class::get("UIResponder").unwrap();
    let mut decl = ClassDecl::new(superclass, "AppDelegate").unwrap();

    unsafe {
        decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
            did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);

        decl.add_method(sel!(applicationDidBecomeActive:),
            did_become_active as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationWillResignActive:),
            will_resign_active as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationWillEnterForeground:),
            will_enter_foreground as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationDidEnterBackground:),
            did_enter_background as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationWillTerminate:),
            will_terminate as extern fn(&Object, Sel, id));

        decl.add_method(sel!(touchesBegan:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(touchesMoved:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(touchesEnded:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(touchesCancelled:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(postLaunch:),
            post_launch as extern fn(&Object, Sel, id));

        decl.add_ivar::<*mut raw::c_void>("glutinState");;

        decl.register();
    }
}

pub fn create_view_class() {
    let superclass = Class::get("UIViewController").unwrap();
    let decl = ClassDecl::new(superclass, "MainViewController").unwrap();

    decl.register();

    extern fn init_for_gl(this: &Object, _: Sel, frame: *const raw::c_void) -> id {
        unsafe {
            let bounds: *const CGRect = mem::transmute(frame);
            let view: id = msg_send![this, initWithFrame:(*bounds).clone()];

            let _: () = msg_send![view, setAutoresizingMask: UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
            let _: () = msg_send![view, setAutoresizesSubviews:YES];

            let layer: id = msg_send![view, layer];
            let _ : () = msg_send![layer, setOpaque:YES];

            view
        }
    }

    extern fn layer_class(_: &Class, _: Sel) -> *const Class {
        unsafe { mem::transmute(Class::get("CAEAGLLayer").unwrap()) }
    }

    let superclass = Class::get("UIView").unwrap();
    let mut decl = ClassDecl::new(superclass, "MainView").unwrap();

    unsafe {
        decl.add_method(sel!(initForGl:),
            init_for_gl as extern fn(&Object, Sel, *const raw::c_void) -> id);

        decl.add_class_method(sel!(layerClass),
            layer_class as extern fn(&Class, Sel) -> *const Class);
        decl.register();
    }
}
<commit_msg>iOS: update for ClassDecl signature<commit_after>use std::mem;
use std::os::raw;
use super::DelegateState;
use Event;
use events::{ Touch, TouchPhase };

use objc::runtime::{ Class, Object, Sel, BOOL, YES };
use objc::declare::{ ClassDecl };

use super::ffi::{
    longjmp,
    id,
    nil,
    CGRect,
    CGPoint,
    CGFloat,
    UIViewAutoresizingFlexibleWidth,
    UIViewAutoresizingFlexibleHeight
 };

use super::jmpbuf;

pub fn create_delegate_class() {
    extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
        unsafe {
            let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
            let bounds: CGRect = msg_send![main_screen, bounds];
            let scale: CGFloat = msg_send![main_screen, nativeScale];

            let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
            let window: id = msg_send![window, initWithFrame:bounds.clone()];

            let size = (bounds.size.width as u32, bounds.size.height as u32);

            let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
            let view_controller: id = msg_send![view_controller, init];

            let class = Class::get("MainView").unwrap();
            let view:id = msg_send![class, alloc];
            let view:id = msg_send![view, initForGl:&bounds];

            let _: () = msg_send![view_controller, setView:view];

            let _: () = msg_send![window, setRootViewController:view_controller];

            let _: () = msg_send![window, addSubview:view];
            let _: () = msg_send![window, makeKeyAndVisible];

            let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f32));
            let state_ptr: *mut DelegateState = mem::transmute(state);
            this.set_ivar("glutinState", state_ptr as *mut raw::c_void);

            let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
        }
        YES
    }

    extern fn post_launch(_: &Object, _: Sel, _: id) {
        unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
    }

    extern fn did_become_active(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Focused(true));
        }
    }

    extern fn will_resign_active(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Focused(false));
        }
    }

    extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Suspended(false));
        }
    }

    extern fn did_enter_background(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            state.events_queue.push_back(Event::Suspended(true));
        }
    }

    extern fn will_terminate(this: &Object, _: Sel, _: id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);
            // push event to the front to garantee that we'll process it
            // immidiatly after jump
            state.events_queue.push_front(Event::Closed);
            longjmp(mem::transmute(&mut jmpbuf),1);
        }
    }

    extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
        unsafe {
            let state: *mut raw::c_void = *this.get_ivar("glutinState");
            let state = &mut *(state as *mut DelegateState);

            let touches_enum: id = msg_send![touches, objectEnumerator];

            loop {
                let touch: id = msg_send![touches_enum, nextObject];
                if touch == nil {
                    break
                }
                let location: CGPoint = msg_send![touch, locationInView:nil];
                let touch_id = touch as u64;
                let phase: i32 = msg_send![touch, phase];

                state.events_queue.push_back(Event::Touch(Touch {
                    id: touch_id,
                    location: (location.x as f64, location.y as f64),
                    phase: match phase {
                        0 => TouchPhase::Started,
                        1 => TouchPhase::Moved,
                        // 2 is UITouchPhaseStationary and is not expected here
                        3 => TouchPhase::Ended,
                        4 => TouchPhase::Cancelled,
                        _ => panic!("unexpected touch phase: {:?}", phase)
                    }
                }));
            }
        }
    }

    let superclass = Class::get("UIResponder").unwrap();
    let mut decl = ClassDecl::new("AppDelegate", superclass).unwrap();

    unsafe {
        decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
            did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);

        decl.add_method(sel!(applicationDidBecomeActive:),
            did_become_active as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationWillResignActive:),
            will_resign_active as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationWillEnterForeground:),
            will_enter_foreground as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationDidEnterBackground:),
            did_enter_background as extern fn(&Object, Sel, id));

        decl.add_method(sel!(applicationWillTerminate:),
            will_terminate as extern fn(&Object, Sel, id));

        decl.add_method(sel!(touchesBegan:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(touchesMoved:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(touchesEnded:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(touchesCancelled:withEvent:),
            handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));

        decl.add_method(sel!(postLaunch:),
            post_launch as extern fn(&Object, Sel, id));

        decl.add_ivar::<*mut raw::c_void>("glutinState");;

        decl.register();
    }
}

pub fn create_view_class() {
    let superclass = Class::get("UIViewController").unwrap();
    let decl = ClassDecl::new("MainViewController",superclass).unwrap();

    decl.register();

    extern fn init_for_gl(this: &Object, _: Sel, frame: *const raw::c_void) -> id {
        unsafe {
            let bounds: *const CGRect = mem::transmute(frame);
            let view: id = msg_send![this, initWithFrame:(*bounds).clone()];

            let _: () = msg_send![view, setAutoresizingMask: UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
            let _: () = msg_send![view, setAutoresizesSubviews:YES];

            let layer: id = msg_send![view, layer];
            let _ : () = msg_send![layer, setOpaque:YES];

            view
        }
    }

    extern fn layer_class(_: &Class, _: Sel) -> *const Class {
        unsafe { mem::transmute(Class::get("CAEAGLLayer").unwrap()) }
    }

    let superclass = Class::get("UIView").unwrap();
    let mut decl = ClassDecl::new("MainView", superclass).unwrap();

    unsafe {
        decl.add_method(sel!(initForGl:),
            init_for_gl as extern fn(&Object, Sel, *const raw::c_void) -> id);

        decl.add_class_method(sel!(layerClass),
            layer_class as extern fn(&Class, Sel) -> *const Class);
        decl.register();
    }
}
<|endoftext|>