<commit_before>//! This is the documentation for the `parse_url` crate.
//!
//! The `parse_url` crate is used for breaking down a URL into indivdual parts.
//!
//! # Examples
//!
//! ```
//! use parse_url::*;
//! let url = String::from("https://www.example.com/en-US/page/sub/?pre=2&foo=bar#fuzz");
//! let parts = parse_url(&url);
//!
//! assert_eq!(Some("https"), parts.protocol);
//! assert_eq!(Some("www.example.com"), parts.host);
//! assert_eq!(Some("en-US/page/sub/"), parts.path);
//! assert_eq!(Some("pre=2&foo=bar"), parts.search);
//! assert_eq!(2, parts.params.len());
//! assert_eq!(("pre", "2"), parts.params[0]);
//! assert_eq!(("foo", "bar"), parts.params[1]);
//! assert_eq!(Some("fuzz"), parts.fragment);
//! ```

mod utils;

pub struct URLParts<'a> {
    pub protocol: Option<&'a str>,
    pub host: Option<&'a str>,
    pub path: Option<&'a str>,
    pub search: Option<&'a str>,
    pub fragment: Option<&'a str>,
    pub params: Vec<(&'a str, &'a str)>
}

pub fn get_protocol(url: &str) -> Option<&str> {
    let protocol: Option<&str> = utils::truncate(&url, "://", 0);

    if protocol.is_some() {
        let protocol_type = protocol.unwrap();
        match protocol_type {
            "http" | "https" | "ftp" => return protocol,
            _ => return None
        }
    }

    None
}

pub fn get_host(url: &str) -> Option<&str> {
    let result = utils::truncate(&url, "://", 1);

    if result.is_none() {
        return utils::truncate(&url, "/", 0)
    }

    utils::truncate(&result.unwrap(), "/", 0) // strip path
}

pub fn get_path(url: &str) -> Option<&str> {
    let result = utils::truncate(&url, "://", 1);
    let mut path: Option<&str>;

    if result.is_none() {
        path = utils::truncate(&url, "/", 1);
    } else {
        path = utils::truncate(&result.unwrap(), "/", 1);
    }

    if path.is_some() {
        path = utils::truncate(&path.unwrap(), "?", 0);
        return utils::truncate(&path.unwrap(), "#", 0);
    }

    None
}

pub fn get_search_string(url: &str) -> Option<&str> {
    let search: Option<&str> = utils::truncate(&url, "?", 1);

    if !search.is_none() {
        return utils::truncate(&search.unwrap(), "#", 0) // strip hash
    }

    None
}

pub fn get_fragment(url: &str) -> Option<&str> {
    utils::truncate(&url, "#", 1)
}

pub fn get_params(url: &str) -> Vec<(&str, &str)> {
    let search = get_search_string(&url);
    let mut result: Vec<(&str, &str)> = vec![];

    if !search.is_none() {
        let params: Vec<&str> = search.unwrap().split("&").collect();

        for p in params {
            let param: Vec<&str> = p.splitn(2, "=").collect();

            if param.len() == 2 {
                let k = param[0].clone();
                let v = param[1].clone();
                result.push((k, v));
            }
        }
    }

    result
}

pub fn parse_url(url: &str) -> URLParts {
    let url: &str = url.trim();

    URLParts {
        protocol: get_protocol(&url),
        host: get_host(&url),
        path: get_path(&url),
        search: get_search_string(&url),
        fragment: get_fragment(&url),
        params: get_params(&url)
    }
}

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

    #[test]
    fn url() {
        let url = String::from("https://www.example.com/en-US/page/sub/?pre=2&foo=bar#fuzz");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(Some("pre=2&foo=bar"), parts.search);
        assert_eq!(2, parts.params.len());
        assert_eq!(("pre", "2"), parts.params[0]);
        assert_eq!(("foo", "bar"), parts.params[1]);
        assert_eq!(Some("fuzz"), parts.fragment);
    }

    #[test]
    fn path() {
        let url = String::from("https://www.example.com/en-US/page/sub/");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn fragment() {
        let url = String::from("https://www.example.com/en-US/page/sub/#fuzz");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(Some("fuzz"), parts.fragment);
    }

    #[test]
    fn host() {
        let url = String::from("https://www.example.com/");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(None, parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn protocol_invalid() {
        let url = String::from("foo://www.example.com/en-US/page/sub/");
        let parts = parse_url(&url);

        assert_eq!(None, parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn protocol_none() {
        let url = String::from("www.example.com/en-US/page/sub/");
        let parts = parse_url(&url);

        assert_eq!(None, parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn params() {
        let url = String::from("https://www.example.com/en-US/page/sub/?pre=2&foo=bar#fuzz");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(Some("pre=2&foo=bar"), parts.search);
        assert_eq!(2, parts.params.len());
        assert_eq!(("pre", "2"), parts.params[0]);
        assert_eq!(("foo", "bar"), parts.params[1]);
        assert_eq!(Some("fuzz"), parts.fragment);
    }

    #[test]
    fn white_space() {
        let url = String::from(" https://www.example.com/en-US/page/sub/#fuzz  ");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(Some("fuzz"), parts.fragment);
    }
}
<commit_msg>Remove duplicate test<commit_after>//! This is the documentation for the `parse_url` crate.
//!
//! The `parse_url` crate is used for breaking down a URL into indivdual parts.
//!
//! # Examples
//!
//! ```
//! use parse_url::*;
//! let url = String::from("https://www.example.com/en-US/page/sub/?pre=2&foo=bar#fuzz");
//! let parts = parse_url(&url);
//!
//! assert_eq!(Some("https"), parts.protocol);
//! assert_eq!(Some("www.example.com"), parts.host);
//! assert_eq!(Some("en-US/page/sub/"), parts.path);
//! assert_eq!(Some("pre=2&foo=bar"), parts.search);
//! assert_eq!(2, parts.params.len());
//! assert_eq!(("pre", "2"), parts.params[0]);
//! assert_eq!(("foo", "bar"), parts.params[1]);
//! assert_eq!(Some("fuzz"), parts.fragment);
//! ```

mod utils;

pub struct URLParts<'a> {
    pub protocol: Option<&'a str>,
    pub host: Option<&'a str>,
    pub path: Option<&'a str>,
    pub search: Option<&'a str>,
    pub fragment: Option<&'a str>,
    pub params: Vec<(&'a str, &'a str)>
}

pub fn get_protocol(url: &str) -> Option<&str> {
    let protocol: Option<&str> = utils::truncate(&url, "://", 0);

    if protocol.is_some() {
        let protocol_type = protocol.unwrap();
        match protocol_type {
            "http" | "https" | "ftp" => return protocol,
            _ => return None
        }
    }

    None
}

pub fn get_host(url: &str) -> Option<&str> {
    let result = utils::truncate(&url, "://", 1);

    if result.is_none() {
        return utils::truncate(&url, "/", 0)
    }

    utils::truncate(&result.unwrap(), "/", 0) // strip path
}

pub fn get_path(url: &str) -> Option<&str> {
    let result = utils::truncate(&url, "://", 1);
    let mut path: Option<&str>;

    if result.is_none() {
        path = utils::truncate(&url, "/", 1);
    } else {
        path = utils::truncate(&result.unwrap(), "/", 1);
    }

    if path.is_some() {
        path = utils::truncate(&path.unwrap(), "?", 0);
        return utils::truncate(&path.unwrap(), "#", 0);
    }

    None
}

pub fn get_search_string(url: &str) -> Option<&str> {
    let search: Option<&str> = utils::truncate(&url, "?", 1);

    if !search.is_none() {
        return utils::truncate(&search.unwrap(), "#", 0) // strip hash
    }

    None
}

pub fn get_fragment(url: &str) -> Option<&str> {
    utils::truncate(&url, "#", 1)
}

pub fn get_params(url: &str) -> Vec<(&str, &str)> {
    let search = get_search_string(&url);
    let mut result: Vec<(&str, &str)> = vec![];

    if !search.is_none() {
        let params: Vec<&str> = search.unwrap().split("&").collect();

        for p in params {
            let param: Vec<&str> = p.splitn(2, "=").collect();

            if param.len() == 2 {
                let k = param[0].clone();
                let v = param[1].clone();
                result.push((k, v));
            }
        }
    }

    result
}

pub fn parse_url(url: &str) -> URLParts {
    let url: &str = url.trim();

    URLParts {
        protocol: get_protocol(&url),
        host: get_host(&url),
        path: get_path(&url),
        search: get_search_string(&url),
        fragment: get_fragment(&url),
        params: get_params(&url)
    }
}

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

    #[test]
    fn url() {
        let url = String::from("https://www.example.com/en-US/page/sub/?pre=2&foo=bar#fuzz");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(Some("pre=2&foo=bar"), parts.search);
        assert_eq!(2, parts.params.len());
        assert_eq!(("pre", "2"), parts.params[0]);
        assert_eq!(("foo", "bar"), parts.params[1]);
        assert_eq!(Some("fuzz"), parts.fragment);
    }

    #[test]
    fn path() {
        let url = String::from("https://www.example.com/en-US/page/sub/");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn fragment() {
        let url = String::from("https://www.example.com/en-US/page/sub/#fuzz");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(Some("fuzz"), parts.fragment);
    }

    #[test]
    fn host() {
        let url = String::from("https://www.example.com/");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(None, parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn protocol_invalid() {
        let url = String::from("foo://www.example.com/en-US/page/sub/");
        let parts = parse_url(&url);

        assert_eq!(None, parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn protocol_none() {
        let url = String::from("www.example.com/en-US/page/sub/");
        let parts = parse_url(&url);

        assert_eq!(None, parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(None, parts.fragment);
    }

    #[test]
    fn white_space() {
        let url = String::from(" https://www.example.com/en-US/page/sub/#fuzz  ");
        let parts = parse_url(&url);

        assert_eq!(Some("https"), parts.protocol);
        assert_eq!(Some("www.example.com"), parts.host);
        assert_eq!(Some("en-US/page/sub/"), parts.path);
        assert_eq!(None, parts.search);
        assert_eq!(0, parts.params.len());
        assert_eq!(Some("fuzz"), parts.fragment);
    }
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]
extern crate conduit;
extern crate route_recognizer as router;

use std::collections::hash_map::{Entry, HashMap};
use std::error::Error;
use std::fmt;

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use router::{Match, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone, Copy)]
pub struct RoutePattern(&'static str);

impl RoutePattern {
    pub fn pattern(&self) -> &str {
        self.0
    }
}

pub struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

impl conduit::Handler for WrappedHandler {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        self.handler.call(request)
    }
}

#[derive(Debug)]
pub struct RouterError(String);

impl RouteBuilder {
    pub fn new() -> RouteBuilder {
        RouteBuilder {
            routers: HashMap::new(),
        }
    }

    #[allow(clippy::borrowed_box)]
    pub fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path),
            None => Err(format!("No router found for {:?}", method)),
        }
        .map_err(RouterError)
    }

    pub fn map<H: Handler>(
        &mut self,
        method: Method,
        pattern: &'static str,
        handler: H,
    ) -> &mut RouteBuilder {
        {
            let router = match self.routers.entry(method) {
                Entry::Occupied(e) => e.into_mut(),
                Entry::Vacant(e) => e.insert(Router::new()),
            };
            let wrapped_handler = WrappedHandler {
                pattern: RoutePattern(pattern),
                handler: Box::new(handler),
            };
            router.add(pattern, wrapped_handler);
        }
        self
    }

    pub fn get<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => return Err(box_error(e)),
            }
        };

        {
            let extensions = request.mut_extensions();
            extensions.insert(m.handler().pattern);
            extensions.insert(m.params().clone());
        }

        m.handler().call(request)
    }
}

impl Error for RouterError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for RouterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a router::Params;
}

pub fn params(req: &dyn RequestExt) -> &router::Params {
    req.extensions()
        .find::<router::Params>()
        .expect("Missing params")
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a router::Params {
        params(self)
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test;

    use std::io;
    use std::net::SocketAddr;

    use {RequestParams, RouteBuilder, RoutePattern};

    use self::conduit_test::ResponseExt;
    use conduit::{
        Body, Extensions, Handler, HeaderMap, Host, Method, Response, Scheme, StatusCode, Version,
    };

    struct RequestSentinel {
        method: Method,
        path: String,
        extensions: conduit::Extensions,
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_string(),
                extensions: Extensions::new(),
                method,
            }
        }
    }

    impl conduit::RequestExt for RequestSentinel {
        fn http_version(&self) -> Version {
            unimplemented!()
        }
        fn method(&self) -> &Method {
            &self.method
        }
        fn scheme(&self) -> Scheme {
            unimplemented!()
        }
        fn host(&self) -> Host<'_> {
            unimplemented!()
        }
        fn virtual_root(&self) -> Option<&str> {
            unimplemented!()
        }
        fn path(&self) -> &str {
            &self.path
        }
        fn path_mut(&mut self) -> &mut String {
            &mut self.path
        }
        fn query_string(&self) -> Option<&str> {
            unimplemented!()
        }
        fn remote_addr(&self) -> SocketAddr {
            unimplemented!()
        }
        fn content_length(&self) -> Option<u64> {
            unimplemented!()
        }
        fn headers(&self) -> &HeaderMap {
            unimplemented!()
        }
        fn body(&mut self) -> &mut dyn io::Read {
            unimplemented!()
        }
        fn extensions(&self) -> &Extensions {
            &self.extensions
        }
        fn mut_extensions(&mut self) -> &mut Extensions {
            &mut self.extensions
        }
    }

    #[test]
    fn basic_get() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn nonexistent_route() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/nonexistent");
        router.call(&mut req).err().expect("No response");
    }

    #[test]
    fn catch_all() {
        let mut router = RouteBuilder::new();
        router.get("/*", test_handler);

        let mut req = RequestSentinel::new(Method::GET, "/foo");
        let res = router.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b", GET, /*"[..]);
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let mut res = vec![];
        res.push(req.params().find("id").unwrap_or("").to_string());
        res.push(format!("{:?}", req.method()));
        res.push(
            req.extensions()
                .find::<RoutePattern>()
                .unwrap()
                .pattern()
                .to_string(),
        );

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<commit_msg>Address clippy suggestion<commit_after>#![warn(rust_2018_idioms)]
extern crate conduit;
extern crate route_recognizer as router;

use std::collections::hash_map::{Entry, HashMap};
use std::error::Error;
use std::fmt;

use conduit::{box_error, Handler, HandlerResult, Method, RequestExt};
use router::{Match, Router};

#[derive(Default)]
pub struct RouteBuilder {
    routers: HashMap<Method, Router<WrappedHandler>>,
}

#[derive(Clone, Copy)]
pub struct RoutePattern(&'static str);

impl RoutePattern {
    pub fn pattern(&self) -> &str {
        self.0
    }
}

pub struct WrappedHandler {
    pattern: RoutePattern,
    handler: Box<dyn Handler>,
}

impl conduit::Handler for WrappedHandler {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        self.handler.call(request)
    }
}

#[derive(Debug)]
pub struct RouterError(String);

impl RouteBuilder {
    pub fn new() -> RouteBuilder {
        RouteBuilder {
            routers: HashMap::new(),
        }
    }

    #[allow(clippy::borrowed_box)]
    pub fn recognize<'a>(
        &'a self,
        method: &Method,
        path: &str,
    ) -> Result<Match<&WrappedHandler>, RouterError> {
        match self.routers.get(method) {
            Some(router) => router.recognize(path),
            None => Err(format!("No router found for {:?}", method)),
        }
        .map_err(RouterError)
    }

    pub fn map<H: Handler>(
        &mut self,
        method: Method,
        pattern: &'static str,
        handler: H,
    ) -> &mut RouteBuilder {
        {
            let router = match self.routers.entry(method) {
                Entry::Occupied(e) => e.into_mut(),
                Entry::Vacant(e) => e.insert(Router::new()),
            };
            let wrapped_handler = WrappedHandler {
                pattern: RoutePattern(pattern),
                handler: Box::new(handler),
            };
            router.add(pattern, wrapped_handler);
        }
        self
    }

    pub fn get<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::GET, pattern, handler)
    }

    pub fn post<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::POST, pattern, handler)
    }

    pub fn put<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::PUT, pattern, handler)
    }

    pub fn delete<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::DELETE, pattern, handler)
    }

    pub fn head<H: Handler>(&mut self, pattern: &'static str, handler: H) -> &mut RouteBuilder {
        self.map(Method::HEAD, pattern, handler)
    }
}

impl conduit::Handler for RouteBuilder {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        let m = {
            let method = request.method();
            let path = request.path();

            match self.recognize(&method, path) {
                Ok(m) => m,
                Err(e) => return Err(box_error(e)),
            }
        };

        {
            let extensions = request.mut_extensions();
            extensions.insert(m.handler().pattern);
            extensions.insert(m.params().clone());
        }

        m.handler().call(request)
    }
}

impl Error for RouterError {
    fn description(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for RouterError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

pub trait RequestParams<'a> {
    fn params(self) -> &'a router::Params;
}

pub fn params(req: &dyn RequestExt) -> &router::Params {
    req.extensions()
        .find::<router::Params>()
        .expect("Missing params")
}

impl<'a> RequestParams<'a> for &'a (dyn RequestExt + 'a) {
    fn params(self) -> &'a router::Params {
        params(self)
    }
}

#[cfg(test)]
mod tests {
    extern crate conduit_test;

    use std::io;
    use std::net::SocketAddr;

    use {RequestParams, RouteBuilder, RoutePattern};

    use self::conduit_test::ResponseExt;
    use conduit::{
        Body, Extensions, Handler, HeaderMap, Host, Method, Response, Scheme, StatusCode, Version,
    };

    struct RequestSentinel {
        method: Method,
        path: String,
        extensions: conduit::Extensions,
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_string(),
                extensions: Extensions::new(),
                method,
            }
        }
    }

    impl conduit::RequestExt for RequestSentinel {
        fn http_version(&self) -> Version {
            unimplemented!()
        }
        fn method(&self) -> &Method {
            &self.method
        }
        fn scheme(&self) -> Scheme {
            unimplemented!()
        }
        fn host(&self) -> Host<'_> {
            unimplemented!()
        }
        fn virtual_root(&self) -> Option<&str> {
            unimplemented!()
        }
        fn path(&self) -> &str {
            &self.path
        }
        fn path_mut(&mut self) -> &mut String {
            &mut self.path
        }
        fn query_string(&self) -> Option<&str> {
            unimplemented!()
        }
        fn remote_addr(&self) -> SocketAddr {
            unimplemented!()
        }
        fn content_length(&self) -> Option<u64> {
            unimplemented!()
        }
        fn headers(&self) -> &HeaderMap {
            unimplemented!()
        }
        fn body(&mut self) -> &mut dyn io::Read {
            unimplemented!()
        }
        fn extensions(&self) -> &Extensions {
            &self.extensions
        }
        fn mut_extensions(&mut self) -> &mut Extensions {
            &mut self.extensions
        }
    }

    #[test]
    fn basic_get() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::GET, "/posts/1");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"1, GET, /posts/:id"[..]);
    }

    #[test]
    fn basic_post() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/posts/10");
        let res = router.call(&mut req).expect("No response");

        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b"10, POST, /posts/:id"[..]);
    }

    #[test]
    fn nonexistent_route() {
        let router = test_router();
        let mut req = RequestSentinel::new(Method::POST, "/nonexistent");
        router.call(&mut req).err().expect("No response");
    }

    #[test]
    fn catch_all() {
        let mut router = RouteBuilder::new();
        router.get("/*", test_handler);

        let mut req = RequestSentinel::new(Method::GET, "/foo");
        let res = router.call(&mut req).expect("No response");
        assert_eq!(res.status(), StatusCode::OK);
        assert_eq!(*res.into_cow(), b", GET, /*"[..]);
    }

    fn test_router() -> RouteBuilder {
        let mut router = RouteBuilder::new();
        router.post("/posts/:id", test_handler);
        router.get("/posts/:id", test_handler);
        router
    }

    fn test_handler(req: &mut dyn conduit::RequestExt) -> conduit::HttpResult {
        let res = vec![
            req.params().find("id").unwrap_or("").to_string(),
            format!("{:?}", req.method()),
            req.extensions()
                .find::<RoutePattern>()
                .unwrap()
                .pattern()
                .to_string(),
        ];

        let bytes = res.join(", ").into_bytes();
        Response::builder().body(Body::from_vec(bytes))
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::c_void;

mod ffi;

pub use self::ffi::Align;
pub use self::ffi::Dimensions;
pub use self::ffi::Direction;
pub use self::ffi::Edge;
pub use self::ffi::ExperimentalFeature;
pub use self::ffi::FlexDirection;
pub use self::ffi::FlexWrap;
pub use self::ffi::JustifyContent;
pub use self::ffi::LogLevel;
pub use self::ffi::MeasureMode;
pub use self::ffi::Overflow;
pub use self::ffi::PositionType;
pub use self::ffi::PrintOptions;
pub use self::ffi::Size;

pub struct Context<'a> {
    pub text: &'a str,
}

impl<'a> Context<'a> {
    pub fn new(text: &'a str) -> Context<'a> {
        Context { text: text }
    }

    pub fn get_text(&self) -> &'a str {
        self.text
    }
}

pub extern "C" fn measure(node: *mut ffi::Node,
                          width: f32,
                          width_mode: MeasureMode,
                          height: f32,
                          height_mode: MeasureMode)
                          -> Size {
    let n = Node { _node: node };
    Size {
        width: n.get_context().get_text().len() as f32,
        height: 1.0,
    }
}

#[derive(Debug)]
pub struct Node {
    _node: *mut ffi::Node,
}

impl Node {
    pub fn new() -> Node {
        Node { _node: unsafe { ffi::YGNodeNew() } }
    }

    pub fn get_instance_count() -> i32 {
        unsafe { ffi::YGNodeGetInstanceCount() }
    }

    pub fn reset(&mut self) {
        unsafe { ffi::YGNodeReset(self._node) }
    }

    pub fn free(self) {
        unsafe { ffi::YGNodeFree(self._node) }
    }

    pub fn free_recursive(self) {
        unsafe { ffi::YGNodeFreeRecursive(self._node) }
    }

    pub fn set_direction(&mut self, value: ffi::Direction) {
        unsafe { ffi::YGNodeStyleSetDirection(self._node, value) }
    }

    pub fn get_direction(&self) -> ffi::Direction {
        unsafe { ffi::YGNodeStyleGetDirection(self._node) }
    }

    pub fn set_flex_direction(&mut self, value: ffi::FlexDirection) {
        unsafe { ffi::YGNodeStyleSetFlexDirection(self._node, value) }
    }

    pub fn get_flex_direction(&self) -> ffi::FlexDirection {
        unsafe { ffi::YGNodeStyleGetFlexDirection(self._node) }
    }

    pub fn set_width(&mut self, width: f32) {
        unsafe { ffi::YGNodeStyleSetWidth(self._node, width) }
    }

    pub fn get_width(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetWidth(self._node) }
    }

    pub fn set_height(&mut self, height: f32) {
        unsafe { ffi::YGNodeStyleSetHeight(self._node, height) }
    }

    pub fn get_height(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetHeight(self._node) }
    }

    pub fn calculate_layout(&mut self) {
        unsafe {
            ffi::YGNodeCalculateLayout(self._node, std::f32::NAN, std::f32::NAN, Direction::LTR)
        }
    }

    pub fn get_layout_width(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetWidth(self._node) }
    }

    pub fn get_layout_height(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetHeight(self._node) }
    }

    pub fn get_layout_left(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetLeft(self._node) }
    }

    pub fn get_layout_top(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetTop(self._node) }
    }

    pub fn get_layout_direction(&self) -> ffi::Direction {
        unsafe { ffi::YGNodeLayoutGetDirection(self._node) }
    }

    pub fn insert_child(&mut self, child: &Node, index: u32) {
        unsafe { ffi::YGNodeInsertChild(self._node, child._node, index) }
    }

    pub fn remove_child(&mut self, child: &Node) {
        unsafe { ffi::YGNodeRemoveChild(self._node, child._node) }
    }

    pub fn get_child(&self, index: u32) -> Node {
        unsafe {
            let mut node = ffi::YGNodeGetChild(self._node, index);
            Node { _node: &mut node }
        }
    }

    pub fn get_child_count(&self) -> u32 {
        unsafe { ffi::YGNodeGetChildCount(self._node) }
    }

    pub fn get_parent(&self) -> Node {
        unsafe {
            let mut node = ffi::YGNodeGetParent(self._node);
            Node { _node: &mut node }
        }
    }

    pub fn set_justify_content(&mut self, value: ffi::JustifyContent) {
        unsafe { ffi::YGNodeStyleSetJustifyContent(self._node, value) }
    }

    pub fn get_justify_content(&self) -> ffi::JustifyContent {
        unsafe { ffi::YGNodeStyleGetJustifyContent(self._node) }
    }

    pub fn get_align_items(&self) -> ffi::Align {
        unsafe { ffi::YGNodeStyleGetAlignItems(self._node) }
    }

    pub fn set_align_items(&mut self, value: ffi::Align) {
        unsafe { ffi::YGNodeStyleSetAlignItems(self._node, value) }
    }

    pub fn get_align_self(&self) -> ffi::Align {
        unsafe { ffi::YGNodeStyleGetAlignSelf(self._node) }
    }

    pub fn set_align_self(&mut self, value: ffi::Align) {
        unsafe { ffi::YGNodeStyleSetAlignSelf(self._node, value) }
    }

    pub fn get_align_content(&self) -> ffi::Align {
        unsafe { ffi::YGNodeStyleGetAlignContent(self._node) }
    }

    pub fn set_align_content(&mut self, value: ffi::Align) {
        unsafe { ffi::YGNodeStyleSetAlignContent(self._node, value) }
    }

    pub fn get_position_type(&self) -> ffi::PositionType {
        unsafe { ffi::YGNodeStyleGetPositionType(self._node) }
    }

    pub fn set_position_type(&mut self, value: ffi::PositionType) {
        unsafe { ffi::YGNodeStyleSetPositionType(self._node, value) }
    }

    pub fn get_flex_wrap(&self) -> ffi::FlexWrap {
        unsafe { ffi::YGNodeStyleGetFlexWrap(self._node) }
    }

    pub fn set_flex_wrap(&mut self, value: ffi::FlexWrap) {
        unsafe { ffi::YGNodeStyleSetFlexWrap(self._node, value) }
    }

    pub fn get_overflow(&self) -> ffi::Overflow {
        unsafe { ffi::YGNodeStyleGetOverflow(self._node) }
    }

    pub fn set_overflow(&mut self, value: ffi::Overflow) {
        unsafe { ffi::YGNodeStyleSetOverflow(self._node, value) }
    }

    pub fn set_flex(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlex(self._node, value) }
    }

    pub fn get_flex_grow(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetFlexGrow(self._node) }
    }

    pub fn set_flex_grow(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlexGrow(self._node, value) }
    }

    pub fn get_flex_shrink(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetFlexShrink(self._node) }
    }

    pub fn set_flex_shrink(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlexShrink(self._node, value) }
    }

    pub fn get_flex_basis(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetFlexBasis(self._node) }
    }

    pub fn set_flex_basis(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlexBasis(self._node, value) }
    }

    pub fn get_margin(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetMargin(self._node, edge) }
    }

    pub fn set_margin(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetMargin(self._node, edge, value) }
    }

    pub fn get_padding(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetPadding(self._node, edge) }
    }

    pub fn set_padding(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetPadding(self._node, edge, value) }
    }

    pub fn get_border(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetBorder(self._node, edge) }
    }

    pub fn set_border(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetBorder(self._node, edge, value) }
    }

    pub fn get_position(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetPosition(self._node, edge) }
    }

    pub fn set_position(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetPosition(self._node, edge, value) }
    }

    pub fn set_measure_func(&mut self,
                            func: extern "C" fn(*mut ffi::Node, f32, MeasureMode, f32, MeasureMode)
                                                -> Size) {
        unsafe { ffi::YGNodeSetMeasureFunc(self._node, func) }
    }

    pub fn mark_dirty(&mut self) {
        unsafe { ffi::YGNodeMarkDirty(self._node) }
    }

    pub fn is_dirty(&self) -> bool {
        unsafe { ffi::YGNodeIsDirty(self._node) }
    }

    pub fn set_context(&mut self, context: *mut Context) {
        unsafe { ffi::YGNodeSetContext(self._node, context as *mut c_void) }
    }

    pub fn get_context(&self) -> &Context {
        unsafe {
            let context = ffi::YGNodeGetContext(self._node);
            let ref ctx: Context = *(context as *mut Context);
            ctx
        }
    }

    // boolean hasNewLayout();
    // void markLayoutSeen();
    // void copyStyle(YogaNodeType srcNode);
    // TODO int indexOf(YogaNodeType child);
    // TODO boolean isMeasureDefined();
}

#[cfg(test)]
mod tests {
    use Context;
    use Node;
    use measure;

    #[test]
    fn dirty_works() {
        let mut node = Node::new();
        node.set_measure_func(measure);
        assert!(!node.is_dirty());
        node.mark_dirty();
        assert!(node.is_dirty());
        node.free_recursive();
    }

    #[test]
    fn context_works() {
        let mut node = Node::new();
        node.set_context(&mut Context::new("Yo!"));
        {
            let context = node.get_context();
            assert!(context.get_text() == "Yo!");
        }
        node.free_recursive();
    }

    #[test]
    fn measure_works() {
        let mut node = Node::new();
        node.set_measure_func(measure);
        node.set_context(&mut Context::new("Yo!!"));
        node.calculate_layout();
        assert!(node.get_layout_width() == 4.0);
        assert!(node.get_layout_height() == 1.0);
        node.free_recursive();
    }

    #[test]
    fn get_instance_count() {
        assert!(Node::get_instance_count() == 0);
        let mut node = Node::new();
        assert!(Node::get_instance_count() == 1);
        node.free_recursive();
    }
}
<commit_msg>feat(Drop): impl Drop for Node; prevent Drop during measure; clean up tests<commit_after>extern crate libc;

use libc::c_void;

mod ffi;

pub use self::ffi::Align;
pub use self::ffi::Dimensions;
pub use self::ffi::Direction;
pub use self::ffi::Edge;
pub use self::ffi::ExperimentalFeature;
pub use self::ffi::FlexDirection;
pub use self::ffi::FlexWrap;
pub use self::ffi::JustifyContent;
pub use self::ffi::LogLevel;
pub use self::ffi::MeasureMode;
pub use self::ffi::Overflow;
pub use self::ffi::PositionType;
pub use self::ffi::PrintOptions;
pub use self::ffi::Size;

pub struct Context<'a> {
    pub text: &'a str,
}

impl<'a> Context<'a> {
    pub fn new(text: &'a str) -> Context<'a> {
        Context { text: text }
    }

    pub fn get_text(&self) -> &'a str {
        self.text
    }
}

pub extern "C" fn measure(node: *mut ffi::Node,
                          width: f32,
                          width_mode: MeasureMode,
                          height: f32,
                          height_mode: MeasureMode)
                          -> Size {
    let n = Node { _node: node };
    let width = n.get_context().get_text().len() as f32;

    // n is only made a Node to get at context.
    // we want to keep node passed in around
    // so don't run Drop at end of method
    // it'll Drop elsewhere
    std::mem::forget(n);

    Size {
        width: width,
        height: 1.0,
    }
}

#[derive(Debug)]
pub struct Node {
    _node: *mut ffi::Node,
}

impl Node {
    pub fn new() -> Node {
        Node { _node: unsafe { ffi::YGNodeNew() } }
    }

    pub fn get_instance_count() -> i32 {
        unsafe { ffi::YGNodeGetInstanceCount() }
    }

    pub fn reset(&mut self) {
        unsafe { ffi::YGNodeReset(self._node) }
    }

    pub fn free(self) {
        unsafe { ffi::YGNodeFree(self._node) }
    }

    pub fn free_recursive(self) {
        unsafe { ffi::YGNodeFreeRecursive(self._node) }
    }

    pub fn set_direction(&mut self, value: ffi::Direction) {
        unsafe { ffi::YGNodeStyleSetDirection(self._node, value) }
    }

    pub fn get_direction(&self) -> ffi::Direction {
        unsafe { ffi::YGNodeStyleGetDirection(self._node) }
    }

    pub fn set_flex_direction(&mut self, value: ffi::FlexDirection) {
        unsafe { ffi::YGNodeStyleSetFlexDirection(self._node, value) }
    }

    pub fn get_flex_direction(&self) -> ffi::FlexDirection {
        unsafe { ffi::YGNodeStyleGetFlexDirection(self._node) }
    }

    pub fn set_width(&mut self, width: f32) {
        unsafe { ffi::YGNodeStyleSetWidth(self._node, width) }
    }

    pub fn get_width(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetWidth(self._node) }
    }

    pub fn set_height(&mut self, height: f32) {
        unsafe { ffi::YGNodeStyleSetHeight(self._node, height) }
    }

    pub fn get_height(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetHeight(self._node) }
    }

    pub fn calculate_layout(&mut self) {
        unsafe {
            ffi::YGNodeCalculateLayout(self._node, std::f32::NAN, std::f32::NAN, Direction::LTR)
        }
    }

    pub fn get_layout_width(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetWidth(self._node) }
    }

    pub fn get_layout_height(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetHeight(self._node) }
    }

    pub fn get_layout_left(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetLeft(self._node) }
    }

    pub fn get_layout_top(&self) -> f32 {
        unsafe { ffi::YGNodeLayoutGetTop(self._node) }
    }

    pub fn get_layout_direction(&self) -> ffi::Direction {
        unsafe { ffi::YGNodeLayoutGetDirection(self._node) }
    }

    pub fn insert_child(&mut self, child: &Node, index: u32) {
        unsafe { ffi::YGNodeInsertChild(self._node, child._node, index) }
    }

    pub fn remove_child(&mut self, child: &Node) {
        unsafe { ffi::YGNodeRemoveChild(self._node, child._node) }
    }

    pub fn get_child(&self, index: u32) -> Node {
        unsafe {
            let mut node = ffi::YGNodeGetChild(self._node, index);
            Node { _node: &mut node }
        }
    }

    pub fn get_child_count(&self) -> u32 {
        unsafe { ffi::YGNodeGetChildCount(self._node) }
    }

    pub fn get_parent(&self) -> Node {
        unsafe {
            let mut node = ffi::YGNodeGetParent(self._node);
            Node { _node: &mut node }
        }
    }

    pub fn set_justify_content(&mut self, value: ffi::JustifyContent) {
        unsafe { ffi::YGNodeStyleSetJustifyContent(self._node, value) }
    }

    pub fn get_justify_content(&self) -> ffi::JustifyContent {
        unsafe { ffi::YGNodeStyleGetJustifyContent(self._node) }
    }

    pub fn get_align_items(&self) -> ffi::Align {
        unsafe { ffi::YGNodeStyleGetAlignItems(self._node) }
    }

    pub fn set_align_items(&mut self, value: ffi::Align) {
        unsafe { ffi::YGNodeStyleSetAlignItems(self._node, value) }
    }

    pub fn get_align_self(&self) -> ffi::Align {
        unsafe { ffi::YGNodeStyleGetAlignSelf(self._node) }
    }

    pub fn set_align_self(&mut self, value: ffi::Align) {
        unsafe { ffi::YGNodeStyleSetAlignSelf(self._node, value) }
    }

    pub fn get_align_content(&self) -> ffi::Align {
        unsafe { ffi::YGNodeStyleGetAlignContent(self._node) }
    }

    pub fn set_align_content(&mut self, value: ffi::Align) {
        unsafe { ffi::YGNodeStyleSetAlignContent(self._node, value) }
    }

    pub fn get_position_type(&self) -> ffi::PositionType {
        unsafe { ffi::YGNodeStyleGetPositionType(self._node) }
    }

    pub fn set_position_type(&mut self, value: ffi::PositionType) {
        unsafe { ffi::YGNodeStyleSetPositionType(self._node, value) }
    }

    pub fn get_flex_wrap(&self) -> ffi::FlexWrap {
        unsafe { ffi::YGNodeStyleGetFlexWrap(self._node) }
    }

    pub fn set_flex_wrap(&mut self, value: ffi::FlexWrap) {
        unsafe { ffi::YGNodeStyleSetFlexWrap(self._node, value) }
    }

    pub fn get_overflow(&self) -> ffi::Overflow {
        unsafe { ffi::YGNodeStyleGetOverflow(self._node) }
    }

    pub fn set_overflow(&mut self, value: ffi::Overflow) {
        unsafe { ffi::YGNodeStyleSetOverflow(self._node, value) }
    }

    pub fn set_flex(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlex(self._node, value) }
    }

    pub fn get_flex_grow(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetFlexGrow(self._node) }
    }

    pub fn set_flex_grow(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlexGrow(self._node, value) }
    }

    pub fn get_flex_shrink(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetFlexShrink(self._node) }
    }

    pub fn set_flex_shrink(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlexShrink(self._node, value) }
    }

    pub fn get_flex_basis(&self) -> f32 {
        unsafe { ffi::YGNodeStyleGetFlexBasis(self._node) }
    }

    pub fn set_flex_basis(&mut self, value: f32) {
        unsafe { ffi::YGNodeStyleSetFlexBasis(self._node, value) }
    }

    pub fn get_margin(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetMargin(self._node, edge) }
    }

    pub fn set_margin(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetMargin(self._node, edge, value) }
    }

    pub fn get_padding(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetPadding(self._node, edge) }
    }

    pub fn set_padding(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetPadding(self._node, edge, value) }
    }

    pub fn get_border(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetBorder(self._node, edge) }
    }

    pub fn set_border(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetBorder(self._node, edge, value) }
    }

    pub fn get_position(&self, edge: ffi::Edge) -> f32 {
        unsafe { ffi::YGNodeStyleGetPosition(self._node, edge) }
    }

    pub fn set_position(&mut self, edge: ffi::Edge, value: f32) {
        unsafe { ffi::YGNodeStyleSetPosition(self._node, edge, value) }
    }

    pub fn set_measure_func(&mut self,
                            func: extern "C" fn(*mut ffi::Node,
                                                f32,
                                                MeasureMode,
                                                f32,
                                                MeasureMode)
                                                -> Size) {
        unsafe { ffi::YGNodeSetMeasureFunc(self._node, func) }
    }

    pub fn mark_dirty(&mut self) {
        unsafe { ffi::YGNodeMarkDirty(self._node) }
    }

    pub fn is_dirty(&self) -> bool {
        unsafe { ffi::YGNodeIsDirty(self._node) }
    }

    pub fn set_context(&mut self, context: *mut Context) {
        unsafe { ffi::YGNodeSetContext(self._node, context as *mut c_void) }
    }

    pub fn get_context(&self) -> &Context {
        unsafe {
            let context = ffi::YGNodeGetContext(self._node);
            let ref ctx: Context = *(context as *mut Context);
            ctx
        }
    }

    // boolean hasNewLayout();
    // void markLayoutSeen();
    // void copyStyle(YogaNodeType srcNode);
    // TODO int indexOf(YogaNodeType child);
    // TODO boolean isMeasureDefined();
}

impl std::ops::Drop for Node {
    fn drop(&mut self) {
        unsafe { ffi::YGNodeFree(self._node) }
    }
}

#[cfg(test)]
mod tests {
    use Context;
    use Node;
    use measure;

    #[test]
    fn dirty_works() {
        let mut node = Node::new();
        node.set_measure_func(measure);
        assert!(!node.is_dirty());
        node.mark_dirty();
        assert!(node.is_dirty());
    }

    #[test]
    fn context_works() {
        let mut node = Node::new();
        node.set_context(&mut Context::new("Yo!"));
        let context = node.get_context();
        assert!(context.get_text() == "Yo!");
    }

    #[test]
    fn measure_works() {
        let mut node = Node::new();
        node.set_measure_func(measure);
        node.set_context(&mut Context::new("Yo!!"));
        node.calculate_layout();
        assert!(node.get_layout_width() == 4.0);
        assert!(node.get_layout_height() == 1.0);
    }

    #[test]
    fn get_instance_count() {
        assert!(Node::get_instance_count() == 0);
        let mut node = Node::new();
        assert!(Node::get_instance_count() == 1);
    }
}
<|endoftext|>


<commit_before>// Filebuffer -- Fast and simple file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! Filebuffer, a library for fast and simple file reading.
//!
//! # Examples
//!
//! Map a file into memory and access it as a slice of bytes. This is simple and will generally
//! outperform `Read::read_to_end()`.
//!
//! ```
//! use filebuffer::FileBuffer;
//! let fbuffer = FileBuffer::open("src/lib.rs").unwrap();
//! assert_eq!(fbuffer[3..45], b"Filebuffer -- Fast and simple file reading"[..]);
//! ```
//!
//! TODO: More examples.

#![warn(missing_docs)]

use std::cmp;
use std::io;
use std::fs;
use std::ops::Deref;
use std::path::Path;
use std::ptr;
use std::slice;

#[cfg(unix)]
mod unix;

#[cfg(windows)]
mod windows;

#[cfg(unix)]
use unix::{PlatformData, get_resident, get_page_size, map_file, unmap_file, prefetch};

#[cfg(windows)]
use windows::{PlatformData, get_resident, get_page_size, map_file, unmap_file, prefetch};

/// A memory-mapped file.
pub struct FileBuffer {
    page_size: usize,
    buffer: *const u8,
    length: usize,

    #[allow(dead_code)] // This field is not dead, it might have an effectful destructor.
    platform_data: PlatformData,
}

/// Rounds `size` up to the nearest multiple of `power_of_two`.
fn round_up_to(size: usize, power_of_two: usize) -> usize {
    (size + (power_of_two - 1)) & !(power_of_two - 1)
}

#[test]
fn verify_round_up_to() {
    assert_eq!(1024, round_up_to(23, 1024));
    assert_eq!(1024, round_up_to(1024, 1024));
    assert_eq!(2048, round_up_to(1025, 1024));
}

/// Rounds `size` down to the nearest multiple of `power_of_two`.
fn round_down_to(size: usize, power_of_two: usize) -> usize {
    size & !(power_of_two - 1)
}

#[test]
fn verify_round_down_to() {
    assert_eq!(0, round_down_to(23, 1024));
    assert_eq!(1024, round_down_to(1024, 1024));
    assert_eq!(1024, round_down_to(1025, 1024));
}

impl FileBuffer {
    /// Maps the file at `path` into memory.
    ///
    /// TODO: Document what happens when the file is changed after opening.
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<FileBuffer> {
        // Open the `fs::File` so we get all of std's error handling for free, then use it to
        // extract the file descriptor. The file is closed again when `map_file` returns on
        // Unix-ish platforms, but `mmap` only requires the descriptor to be open for the `mmap`
        // call, so this is fine. On Windows, the file must be kept open for the lifetime of the
        // mapping, so `map_file` moves the file into the platform data.
        let mut open_opts = fs::OpenOptions::new();
        open_opts.read(true);

        // TODO: On Windows, set `share_mode()` to read-only. This requires the `open_options_ext`
        // feature that is currently unstable, but it is required to ensure that a different
        // process does not suddenly modify the contents of the file.

        let file = try!(open_opts.open(path));
        let (buffer, length, platform_data) = try!(map_file(file));
        let fbuffer = FileBuffer {
            page_size: get_page_size(),
            buffer: buffer,
            length: length,
            platform_data: platform_data
        };
        Ok(fbuffer)
    }

    /// Returns the number of bytes resident in physical memory, starting from `offset`.
    ///
    /// The slice `[offset..offset + resident_len]` can be accessed without causing page faults or
    /// disk access. Note that this is only a snapshot, and the kernel might decide to evict pages
    /// or make them resident at any time.
    ///
    /// The returned resident length is at most `length`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    ///
    /// # Remarks
    ///
    /// Windows does not expose a mechanism to query which pages are resident in physical
    /// memory. Therefore this function optimistically claims that the entire range is resident
    /// on Windows.
    pub fn resident_len(&self, offset: usize, length: usize) -> usize {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        // This is a no-op for empty files.
        if self.buffer == ptr::null() { return 0; }

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);
        let num_pages = aligned_length / self.page_size;

        // There is a tradeoff here: to store residency information, we need an array of booleans.
        // The requested range can potentially be very large and it is only known at runtime. We
        // could allocate a vector here, but that requires a heap allocation just to get residency
        // information (which might in turn cause a page fault). Instead, check at most 32 pages at
        // once. This means more syscalls for large ranges, but it saves us the heap allocation,
        // and for ranges up to 32 pages (128 KiB typically) there is only one syscall.
        let mut residency = [false; 32];
        let mut pages_checked = 0;
        let mut pages_resident = 0;

        while pages_checked < num_pages {
            let pages_to_check = cmp::min(32, num_pages - pages_checked);
            let check_offset = (aligned_offset + pages_checked * self.page_size) as isize;
            let check_buffer = unsafe { self.buffer.offset(check_offset) };
            let check_length = pages_to_check * self.page_size;
            get_resident(check_buffer, check_length, &mut residency);

            // Count the number of resident pages.
            match residency[..pages_to_check].iter().position(|resident| !resident) {
                Some(non_resident) => {
                    // The index of the non-resident page is the number of resident pages.
                    pages_resident += non_resident;
                    break;
                }
                None => {
                    pages_resident += pages_to_check;
                    pages_checked += pages_to_check;
                }
            }
        }

        let resident_length = pages_resident * self.page_size + aligned_offset - offset;

        // Never return more than the requested length. The resident length might be larger than
        // the length of the buffer, because it is rounded up to the page size.
        cmp::min(length, resident_length)
    }

    /// Returns a good chunk length if the file is to be processed in chunks.
    ///
    /// This just returns the page size. Multiples of this are fine too.
    pub fn chunk_len_hint(&self) -> usize {
        self.page_size
    }

    /// Advises the kernel to make a slice of the file resident in physical memory.
    ///
    /// This method does not block, meaning that when the function returns, the slice is not
    /// necessarily resident. After this function returns, the kernel may read the requested slice
    /// from disk and make it resident. Note that this is only an advice, the kernel need not honor
    /// it.
    ///
    /// To check whether the slice is resident at a later time, use `resident_len()`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn prefetch(&self, offset: usize, length: usize) {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        // This is a no-op for empty files.
        if self.buffer == ptr::null() { return; }

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);

        let buffer = unsafe { self.buffer.offset(aligned_offset as isize) };
        prefetch(buffer, aligned_length);
    }
}

impl Drop for FileBuffer {
    fn drop(&mut self) {
        if self.buffer != ptr::null() { unmap_file(self.buffer, self.length); }
    }
}

impl Deref for FileBuffer {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        unsafe { slice::from_raw_parts(self.buffer, self.length) }
    }
}

#[test]
fn open_file() {
    let fbuffer = FileBuffer::open("src/lib.rs");
    assert!(fbuffer.is_ok());
}

#[test]
fn make_resident() {
    let fbuffer = FileBuffer::open("src/lib.rs").unwrap();

    // Touch the first page to make it resident.
    assert_eq!(fbuffer[3..13], b"Filebuffer"[..]);

    // Now at least that part should be resident.
    assert_eq!(fbuffer.resident_len(3, 10), 10);
}

#[test]
fn prefetch_is_not_harmful() {
    let fbuffer = FileBuffer::open("src/lib.rs").unwrap();

    // It is impossible to test that this actually works without root access to instruct the kernel
    // to drop its caches, but at least we can verify that calling `prefetch` is not harmful.
    fbuffer.prefetch(0, fbuffer.len());

    // Reading from the file should still work as normal.
    assert_eq!(fbuffer[3..13], b"Filebuffer"[..]);
}

#[test]
fn open_empty_file_is_fine() {
    FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
}

#[test]
fn empty_file_prefetch_is_fine() {
    let fbuffer = FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
    fbuffer.prefetch(0, 0);
}

#[test]
fn empty_file_deref_is_fine() {
    let fbuffer = FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
    assert_eq!(fbuffer.iter().any(|_| true), false);
}

#[test]
fn empty_file_has_zero_resident_len() {
    let fbuffer = FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
    assert_eq!(fbuffer.resident_len(0, 0), 0);
}

#[test]
fn page_size_at_least_4096() {
    // There is no reason why the page size cannot be smaller, it is just that in practice there
    // is no platform with a smaller page size, so this tests that `get_page_size()` returns
    // a plausible value.
    assert!(get_page_size() >= 4096);
}
<commit_msg>Improve documentation for chunk_len_hint()<commit_after>// Filebuffer -- Fast and simple file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! Filebuffer, a library for fast and simple file reading.
//!
//! # Examples
//!
//! Map a file into memory and access it as a slice of bytes. This is simple and will generally
//! outperform `Read::read_to_end()`.
//!
//! ```
//! use filebuffer::FileBuffer;
//! let fbuffer = FileBuffer::open("src/lib.rs").unwrap();
//! assert_eq!(fbuffer[3..45], b"Filebuffer -- Fast and simple file reading"[..]);
//! ```
//!
//! TODO: More examples.

#![warn(missing_docs)]

use std::cmp;
use std::io;
use std::fs;
use std::ops::Deref;
use std::path::Path;
use std::ptr;
use std::slice;

#[cfg(unix)]
mod unix;

#[cfg(windows)]
mod windows;

#[cfg(unix)]
use unix::{PlatformData, get_resident, get_page_size, map_file, unmap_file, prefetch};

#[cfg(windows)]
use windows::{PlatformData, get_resident, get_page_size, map_file, unmap_file, prefetch};

/// A memory-mapped file.
pub struct FileBuffer {
    page_size: usize,
    buffer: *const u8,
    length: usize,

    #[allow(dead_code)] // This field is not dead, it might have an effectful destructor.
    platform_data: PlatformData,
}

/// Rounds `size` up to the nearest multiple of `power_of_two`.
fn round_up_to(size: usize, power_of_two: usize) -> usize {
    (size + (power_of_two - 1)) & !(power_of_two - 1)
}

#[test]
fn verify_round_up_to() {
    assert_eq!(1024, round_up_to(23, 1024));
    assert_eq!(1024, round_up_to(1024, 1024));
    assert_eq!(2048, round_up_to(1025, 1024));
}

/// Rounds `size` down to the nearest multiple of `power_of_two`.
fn round_down_to(size: usize, power_of_two: usize) -> usize {
    size & !(power_of_two - 1)
}

#[test]
fn verify_round_down_to() {
    assert_eq!(0, round_down_to(23, 1024));
    assert_eq!(1024, round_down_to(1024, 1024));
    assert_eq!(1024, round_down_to(1025, 1024));
}

impl FileBuffer {
    /// Maps the file at `path` into memory.
    ///
    /// TODO: Document what happens when the file is changed after opening.
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<FileBuffer> {
        // Open the `fs::File` so we get all of std's error handling for free, then use it to
        // extract the file descriptor. The file is closed again when `map_file` returns on
        // Unix-ish platforms, but `mmap` only requires the descriptor to be open for the `mmap`
        // call, so this is fine. On Windows, the file must be kept open for the lifetime of the
        // mapping, so `map_file` moves the file into the platform data.
        let mut open_opts = fs::OpenOptions::new();
        open_opts.read(true);

        // TODO: On Windows, set `share_mode()` to read-only. This requires the `open_options_ext`
        // feature that is currently unstable, but it is required to ensure that a different
        // process does not suddenly modify the contents of the file.

        let file = try!(open_opts.open(path));
        let (buffer, length, platform_data) = try!(map_file(file));
        let fbuffer = FileBuffer {
            page_size: get_page_size(),
            buffer: buffer,
            length: length,
            platform_data: platform_data
        };
        Ok(fbuffer)
    }

    /// Returns the number of bytes resident in physical memory, starting from `offset`.
    ///
    /// The slice `[offset..offset + resident_len]` can be accessed without causing page faults or
    /// disk access. Note that this is only a snapshot, and the kernel might decide to evict pages
    /// or make them resident at any time.
    ///
    /// The returned resident length is at most `length`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    ///
    /// # Remarks
    ///
    /// Windows does not expose a mechanism to query which pages are resident in physical
    /// memory. Therefore this function optimistically claims that the entire range is resident
    /// on Windows.
    pub fn resident_len(&self, offset: usize, length: usize) -> usize {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        // This is a no-op for empty files.
        if self.buffer == ptr::null() { return 0; }

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);
        let num_pages = aligned_length / self.page_size;

        // There is a tradeoff here: to store residency information, we need an array of booleans.
        // The requested range can potentially be very large and it is only known at runtime. We
        // could allocate a vector here, but that requires a heap allocation just to get residency
        // information (which might in turn cause a page fault). Instead, check at most 32 pages at
        // once. This means more syscalls for large ranges, but it saves us the heap allocation,
        // and for ranges up to 32 pages (128 KiB typically) there is only one syscall.
        let mut residency = [false; 32];
        let mut pages_checked = 0;
        let mut pages_resident = 0;

        while pages_checked < num_pages {
            let pages_to_check = cmp::min(32, num_pages - pages_checked);
            let check_offset = (aligned_offset + pages_checked * self.page_size) as isize;
            let check_buffer = unsafe { self.buffer.offset(check_offset) };
            let check_length = pages_to_check * self.page_size;
            get_resident(check_buffer, check_length, &mut residency);

            // Count the number of resident pages.
            match residency[..pages_to_check].iter().position(|resident| !resident) {
                Some(non_resident) => {
                    // The index of the non-resident page is the number of resident pages.
                    pages_resident += non_resident;
                    break;
                }
                None => {
                    pages_resident += pages_to_check;
                    pages_checked += pages_to_check;
                }
            }
        }

        let resident_length = pages_resident * self.page_size + aligned_offset - offset;

        // Never return more than the requested length. The resident length might be larger than
        // the length of the buffer, because it is rounded up to the page size.
        cmp::min(length, resident_length)
    }

    /// Returns the system page size.
    ///
    /// When the kernel makes the file resident in physical memory, it does so with page
    /// granularity. (In practice this happens in larger chunks, but still in multiples of
    /// the page size.) Therefore, when processing the file in chunks, this is a good chunk
    /// length.
    pub fn chunk_len_hint(&self) -> usize {
        self.page_size
    }

    /// Advises the kernel to make a slice of the file resident in physical memory.
    ///
    /// This method does not block, meaning that when the function returns, the slice is not
    /// necessarily resident. After this function returns, the kernel may read the requested slice
    /// from disk and make it resident. Note that this is only an advice, the kernel need not honor
    /// it.
    ///
    /// To check whether the slice is resident at a later time, use `resident_len()`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn prefetch(&self, offset: usize, length: usize) {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        // This is a no-op for empty files.
        if self.buffer == ptr::null() { return; }

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);

        let buffer = unsafe { self.buffer.offset(aligned_offset as isize) };
        prefetch(buffer, aligned_length);
    }
}

impl Drop for FileBuffer {
    fn drop(&mut self) {
        if self.buffer != ptr::null() { unmap_file(self.buffer, self.length); }
    }
}

impl Deref for FileBuffer {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        unsafe { slice::from_raw_parts(self.buffer, self.length) }
    }
}

#[test]
fn open_file() {
    let fbuffer = FileBuffer::open("src/lib.rs");
    assert!(fbuffer.is_ok());
}

#[test]
fn make_resident() {
    let fbuffer = FileBuffer::open("src/lib.rs").unwrap();

    // Touch the first page to make it resident.
    assert_eq!(fbuffer[3..13], b"Filebuffer"[..]);

    // Now at least that part should be resident.
    assert_eq!(fbuffer.resident_len(3, 10), 10);
}

#[test]
fn prefetch_is_not_harmful() {
    let fbuffer = FileBuffer::open("src/lib.rs").unwrap();

    // It is impossible to test that this actually works without root access to instruct the kernel
    // to drop its caches, but at least we can verify that calling `prefetch` is not harmful.
    fbuffer.prefetch(0, fbuffer.len());

    // Reading from the file should still work as normal.
    assert_eq!(fbuffer[3..13], b"Filebuffer"[..]);
}

#[test]
fn open_empty_file_is_fine() {
    FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
}

#[test]
fn empty_file_prefetch_is_fine() {
    let fbuffer = FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
    fbuffer.prefetch(0, 0);
}

#[test]
fn empty_file_deref_is_fine() {
    let fbuffer = FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
    assert_eq!(fbuffer.iter().any(|_| true), false);
}

#[test]
fn empty_file_has_zero_resident_len() {
    let fbuffer = FileBuffer::open("src/empty_file_for_testing.rs").unwrap();
    assert_eq!(fbuffer.resident_len(0, 0), 0);
}

#[test]
fn page_size_at_least_4096() {
    // There is no reason why the page size cannot be smaller, it is just that in practice there
    // is no platform with a smaller page size, so this tests that `get_page_size()` returns
    // a plausible value.
    assert!(get_page_size() >= 4096);
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;

// What does this do? You'll never guess!
type HeapId<'a> = PhantomData<std::cell::Cell<&'a mut ()>>;

// Pair is the only type that is actually allocated inside the heap (private)
struct Pair<'a> {
    mark: bool,
    head: XValue<'a>,
    tail: XValue<'a>
}

// Values inside the heap (GC heap-to-heap cross-references) (private)
enum XValue<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),
    Pair(*mut Pair<'a>, HeapId<'a>)
}

// The heap is (for now) just a big Vec of Pairs
pub struct Heap<'a> {
    id: HeapId<'a>,
    storage: Vec<Pair<'a>>,
    freelist: *mut Pair<'a>,
    pins: RefCell<HashMap<*mut Pair<'a>, usize>>
}

pub const HEAP_SIZE: usize = 10000;

pub fn with_heap<F, O>(f: F) -> O
    where F: for<'a> FnOnce(&mut Heap<'a>) -> O
{
    f(&mut Heap::new())
}

impl<'a> Heap<'a> {
    fn new() -> Heap<'a> {
        // Allocate with the full capacity so that allocated Pairs never move
        let mut h = Heap {
            id: PhantomData,
            storage: Vec::with_capacity(HEAP_SIZE),
            freelist: std::ptr::null_mut(),
            pins: RefCell::new(HashMap::new())
        };
        for i in 0 .. HEAP_SIZE {
            h.storage.push(Pair {
                mark: false,
                head: XValue::Null,
                tail: XValue::Null
            });
            let p = &mut h.storage[i] as *mut Pair<'a>;
            unsafe {
                h.add_to_free_list(p);
            }
        }
        h
    }

    fn pin(&self, p: *mut Pair<'a>) {
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    fn unpin(&self, p: *mut Pair<'a>) {
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    unsafe fn add_to_free_list(&mut self, p: *mut Pair<'a>) {
        let listp: *mut *mut Pair<'a> = std::mem::transmute(p);
        *listp = self.freelist;
        assert_eq!(*listp, self.freelist);
        self.freelist = p;
    }

    pub fn try_alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Option<PairRoot<'a>> {
        if self.freelist.is_null() {
            unsafe {
                self.gc();
            }
            if self.freelist.is_null() {
                return None;
            }
        }

        let p = self.freelist;
        unsafe {
            let listp: *mut *mut Pair<'a> = std::mem::transmute(p);
            self.freelist = *listp;

            let (h, t) = pair;
            *p = Pair {
                mark: false,
                head: value_to_heap(h),
                tail: value_to_heap(t)
            };
            Some(PairRoot::new(self, p))
        }
    }

    pub fn alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> PairRoot<'a> {
        self.try_alloc(pair).expect("out of memory (gc did not collect anything)")
    }

    pub fn alloc_null(&mut self) -> PairRoot<'a> {
        self.alloc((Value::Null, Value::Null))
    }

    unsafe fn mark(p: *mut Pair<'a>) {
        if !p.is_null() && !(*p).mark {
            (*p).mark = true;
            Heap::mark_value(&(*p).head);
            Heap::mark_value(&(*p).tail);
        }
    }

    unsafe fn mark_value(v: &XValue<'a>) {
        match v {
            &XValue::Pair(p, _) => Heap::mark(p),
            _ => {}
        }
    }

    unsafe fn gc(&mut self) {
        // clear mark bits
        for p in &mut self.storage {
            p.mark = false;
        }

        // mark phase
        for (&p, _) in self.pins.borrow().iter() {
            Heap::mark(p);
        }

        // sweep phase
        self.freelist = std::ptr::null_mut();
        for i in 0 .. HEAP_SIZE {
            let p = &mut self.storage[i] as *mut Pair<'a>;
            if !(*p).mark {
                self.add_to_free_list(p);
            }
        }
    }

    #[cfg(test)]
    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

// === Values outside the heap that may refer to heap values

// Handle to a Pair that lives in the heap.
#[allow(raw_pointer_derive)]
#[derive(Debug, PartialEq)]
pub struct PairRoot<'a> {
    ptr: *mut Pair<'a>,
    heap: *const Heap<'a>,
    heap_id: HeapId<'a>
}

impl<'a> Drop for PairRoot<'a> {
    fn drop(&mut self) {
        unsafe {
            (*self.heap).unpin(self.ptr);
        }
    }
}

impl<'a> Clone for PairRoot<'a> {
    fn clone(&self) -> PairRoot<'a> {
        let &PairRoot { ptr, heap, heap_id } = self;
        unsafe {
            (*heap).pin(ptr);
        }
        PairRoot {
            ptr: ptr,
            heap: heap,
            heap_id: heap_id
        }
    }
}

impl<'a> PairRoot<'a> {
    unsafe fn new(heap: &Heap<'a>, p: *mut Pair<'a>) -> PairRoot<'a> {
        heap.pin(p);
        PairRoot {
            ptr: p,
            heap: heap as *const Heap<'a>,
            heap_id: heap.id
        }
    }

    pub fn head(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { value_from_heap(&*self.heap, &(*ptr).head) }
    }

    pub fn tail(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { value_from_heap(&*self.heap, &(*ptr).tail) }
    }

    pub fn set_head(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).head = value_to_heap(v); }
    }

    pub fn set_tail(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).tail = value_to_heap(v); }
    }

    #[cfg(test)]
    pub fn address(&self) -> usize {
        unsafe { std::mem::transmute(self.ptr) }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum Value<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),  // <-- equality is by value
    Pair(PairRoot<'a>)  // <-- equality is by pointer
}

// === Getting data into and out of the heap

fn value_to_heap<'a>(v: Value<'a>) -> XValue<'a> {
    match v {
        Value::Null => XValue::Null,
        Value::Int(n) => XValue::Int(n),
        Value::Str(rcstr) => XValue::Str(rcstr),
        Value::Pair(PairRoot{ptr, heap_id, ..}) => XValue::Pair(ptr, heap_id)
    }
}

unsafe fn value_from_heap<'a>(heap: &Heap<'a>, v: &XValue<'a>) -> Value<'a> {
    match v {
        &XValue::Null => Value::Null,
        &XValue::Int(n) => Value::Int(n),
        &XValue::Str(ref rcstr) => Value::Str(rcstr.clone()),
        &XValue::Pair(ptr, _) => Value::Pair(PairRoot::new(heap, ptr))
    }
}

#[cfg(test)]
mod test;
<commit_msg>Reorg.<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;

// What does this do? You'll never guess!
type HeapId<'a> = PhantomData<std::cell::Cell<&'a mut ()>>;

// The heap is (for now) just a big Vec of Pairs
pub struct Heap<'a> {
    id: HeapId<'a>,
    storage: Vec<Pair<'a>>,
    freelist: *mut Pair<'a>,
    pins: RefCell<HashMap<*mut Pair<'a>, usize>>
}

pub const HEAP_SIZE: usize = 10000;

pub fn with_heap<F, O>(f: F) -> O
    where F: for<'a> FnOnce(&mut Heap<'a>) -> O
{
    f(&mut Heap::new())
}

impl<'a> Heap<'a> {
    fn new() -> Heap<'a> {
        // Allocate with the full capacity so that allocated Pairs never move
        let mut h = Heap {
            id: PhantomData,
            storage: Vec::with_capacity(HEAP_SIZE),
            freelist: std::ptr::null_mut(),
            pins: RefCell::new(HashMap::new())
        };
        for i in 0 .. HEAP_SIZE {
            h.storage.push(Pair {
                mark: false,
                head: XValue::Null,
                tail: XValue::Null
            });
            let p = &mut h.storage[i] as *mut Pair<'a>;
            unsafe {
                h.add_to_free_list(p);
            }
        }
        h
    }

    fn pin(&self, p: *mut Pair<'a>) {
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    fn unpin(&self, p: *mut Pair<'a>) {
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    unsafe fn add_to_free_list(&mut self, p: *mut Pair<'a>) {
        let listp: *mut *mut Pair<'a> = std::mem::transmute(p);
        *listp = self.freelist;
        assert_eq!(*listp, self.freelist);
        self.freelist = p;
    }

    pub fn try_alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> Option<PairRoot<'a>> {
        if self.freelist.is_null() {
            unsafe {
                self.gc();
            }
            if self.freelist.is_null() {
                return None;
            }
        }

        let p = self.freelist;
        unsafe {
            let listp: *mut *mut Pair<'a> = std::mem::transmute(p);
            self.freelist = *listp;

            let (h, t) = pair;
            *p = Pair {
                mark: false,
                head: value_to_heap(h),
                tail: value_to_heap(t)
            };
            Some(PairRoot::new(self, p))
        }
    }

    pub fn alloc(&mut self, pair: (Value<'a>, Value<'a>)) -> PairRoot<'a> {
        self.try_alloc(pair).expect("out of memory (gc did not collect anything)")
    }

    pub fn alloc_null(&mut self) -> PairRoot<'a> {
        self.alloc((Value::Null, Value::Null))
    }

    unsafe fn mark(p: *mut Pair<'a>) {
        if !p.is_null() && !(*p).mark {
            (*p).mark = true;
            Heap::mark_value(&(*p).head);
            Heap::mark_value(&(*p).tail);
        }
    }

    unsafe fn mark_value(v: &XValue<'a>) {
        match v {
            &XValue::Pair(p, _) => Heap::mark(p),
            _ => {}
        }
    }

    unsafe fn gc(&mut self) {
        // clear mark bits
        for p in &mut self.storage {
            p.mark = false;
        }

        // mark phase
        for (&p, _) in self.pins.borrow().iter() {
            Heap::mark(p);
        }

        // sweep phase
        self.freelist = std::ptr::null_mut();
        for i in 0 .. HEAP_SIZE {
            let p = &mut self.storage[i] as *mut Pair<'a>;
            if !(*p).mark {
                self.add_to_free_list(p);
            }
        }
    }

    #[cfg(test)]
    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

// === Pair, the reference type

// Pair is the only type that is actually allocated inside the heap (private)
struct Pair<'a> {
    mark: bool,
    head: XValue<'a>,
    tail: XValue<'a>
}

// Handle to a Pair that lives in the heap.
#[allow(raw_pointer_derive)]
#[derive(Debug, PartialEq)]
pub struct PairRoot<'a> {
    ptr: *mut Pair<'a>,
    heap: *const Heap<'a>,
    heap_id: HeapId<'a>
}

impl<'a> Drop for PairRoot<'a> {
    fn drop(&mut self) {
        unsafe {
            (*self.heap).unpin(self.ptr);
        }
    }
}

impl<'a> Clone for PairRoot<'a> {
    fn clone(&self) -> PairRoot<'a> {
        let &PairRoot { ptr, heap, heap_id } = self;
        unsafe {
            (*heap).pin(ptr);
        }
        PairRoot {
            ptr: ptr,
            heap: heap,
            heap_id: heap_id
        }
    }
}

impl<'a> PairRoot<'a> {
    unsafe fn new(heap: &Heap<'a>, p: *mut Pair<'a>) -> PairRoot<'a> {
        heap.pin(p);
        PairRoot {
            ptr: p,
            heap: heap as *const Heap<'a>,
            heap_id: heap.id
        }
    }

    pub fn head(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { value_from_heap(&*self.heap, &(*ptr).head) }
    }

    pub fn tail(&self) -> Value<'a> {
        let ptr = self.ptr;
        unsafe { value_from_heap(&*self.heap, &(*ptr).tail) }
    }

    pub fn set_head(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).head = value_to_heap(v); }
    }

    pub fn set_tail(&self, v: Value<'a>) {
        let ptr = self.ptr;
        unsafe { (*ptr).tail = value_to_heap(v); }
    }

    #[cfg(test)]
    pub fn address(&self) -> usize {
        unsafe { std::mem::transmute(self.ptr) }
    }
}

// === Values (a heap-inline enum)

// Values inside the heap (GC heap-to-heap cross-references) (private)
enum XValue<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),
    Pair(*mut Pair<'a>, HeapId<'a>)
}

#[derive(Debug, Clone, PartialEq)]
pub enum Value<'a> {
    Null,
    Int(i32),
    Str(Rc<String>),  // <-- equality is by value
    Pair(PairRoot<'a>)  // <-- equality is by pointer
}

// === Getting data into and out of the heap

fn value_to_heap<'a>(v: Value<'a>) -> XValue<'a> {
    match v {
        Value::Null => XValue::Null,
        Value::Int(n) => XValue::Int(n),
        Value::Str(rcstr) => XValue::Str(rcstr),
        Value::Pair(PairRoot{ptr, heap_id, ..}) => XValue::Pair(ptr, heap_id)
    }
}

unsafe fn value_from_heap<'a>(heap: &Heap<'a>, v: &XValue<'a>) -> Value<'a> {
    match v {
        &XValue::Null => Value::Null,
        &XValue::Int(n) => Value::Int(n),
        &XValue::Str(ref rcstr) => Value::Str(rcstr.clone()),
        &XValue::Pair(ptr, _) => Value::Pair(PairRoot::new(heap, ptr))
    }
}

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